Articles

Projet SoC avec Vivado

Introduction

Dans ce projet, on exploite à la fois la partie processeur du Zynq (PS pour Processing System) et le réseau de portes configurable (PL pour Programmable Logic).

La communication entre la partie processeur et le réseau de portes configurable s'effectue grâce au bus AXI, ressource interne à la partie PS.

Pour ce premier exemple, on met en oeuvre un coeur de processeur dont la programmation s'effectuera en langage C capable de communiquer avec un périphérique d'entrées/sorties permettant d'accéder aux LEDs de la carte Zybo.

Le processeur comme le périphérique d'entrées/sorties sont des modules IP prééxistants dans Vivado.

Projet SoC version 1

Réalisation d'un projet avec IPs

Première étape : créer le projet

Créer un projet de type RTL (comme dans l'exemple précédent) et le nommer ProjetSoC1.

Note : le projet créé est totalement vierge et doit être configuré pour cibler une carte Zybo comme dans le projet VHDL vu précédemment.

Seconde étape : créer la structure du système à partir des IP du catalogue d'IP Xilinx

Ce projet repose sur un schéma hiérarchique intégrant des IPs. Pour le créer, cliquer sur Create Block Design dans le Flow Navigator.

Saisir ProjetSoC1 pour le nom du design et valider par OK.

Design du projet SoC version 1

Cette étape aboutit à la création d'un diagramme vierge.

Design vierge du projet SoC version 1

On commence par ajouter l'IP du processeur à notre projet en cliquant sur le bouton + (soit dans la palette d'outils de la section Diagram; soit en cliquant sur le + présent au centre du diagram).

Choix d'une IP

Dans la liste qui apparaît, sélectionner l'IP ZYNQ7 Processing System qui englobe donc les coeurs ARM Cortex A9 ainsi que toute la logique d'interconnexion AXI vers le réseau de portes configurables puis valider par Entrer ou par un double clic sur le nom de l'IP à intégrer.

Résultat :

IP Zynq7 en place

Lancement du magicien de configuration des IP

Vivado facilite largement l'usage des IP grâce à des assistants qui automatisent les tâches de configuration des IPs. Cliquer sur le texte Run Block Automation qui apparaît sous la barre d'outils de la fenêtre Diagram (un clic droit sur l'espace de travail du diagramme permet aussi d'accéder à cet outil).

Valider par OK pour utiliser la configuration par défaut.

L'automate fournit toute la connectivité nécessaire entre la mémoire DDR de la carte Zybo et les coeurs ARM du Zynq mais aussi entre les coeurs ARM et différents périphériques de la carte Zybo (interface Ethernet, mémoire Flash ...) par l'intermédiaire du bus FIXED_IO.

Connectivité entre PS et DDR

Pour que le Processing System puisse accéder aux LEDs, ajouter un IP AXI_GPIO au projet (bouton + comme précédemment) puis lancer le processus d'automatisation pour connecter l'IP AXI GPIO au PS.

Connectivité entre PS et AXI GPIO

Configurer la boite de dialogue comme ci-dessus (mode All Automation et sélectionner leds_4bits dans les options GPIO Select Board Part Interface. Valider par OK.

Effectuer un clic droit dans la zone de travail du diagramme pour Regénerer le Layout et obtenir un diagramme semblable à ceci :

Système complet avec un GPIO

Sauvegarder le diagramme et valider sa structure en exécutant l'outil du menu Tools->Validate Design.

Dernière étape : finalisation du projet et export de la configuration matérielle

Pour pouvoir générer le bitstream du projet, Vivado a besoin que le diagramme réalisé soit maintenant exploitable par une approche VHDL. La commande Create HDL Wrapper permet cette exploitation du projet à un niveau VHDL en générant une netliste du diagramme en VHDL. Pour la lancer, sélectionner le ProjetSoC1 dans l'onglet Sources et choisir Create HDL Wrapper dans le menu contextuel.

Création du wrapper VHDL

Création du wrapper VHDL

Valider par OK après avoir sélectionner l'option Let Vivado manage wrapper and auto-update.

Pour clore toute cette partie, commencer par générer le bitstream destiné à la configuration du Zynq en cliquant sur Generate Bitstream dans la catégorie PROGRAM AND DEBUG de la section Flow Navigator.

Note : il faut valider le lancement de l'implémentation qui elle-même exécutera l'opération de Synthèse (2 boites de dialogue de confirmation).

La suite propose de programmer le processeur ARM du Zynq en langage C. Pour rendre cette étape possible, on termine par l'export des fichiers matériels : menu File->Export->Export Hardware...

Export de la configuration matérielle

Penser à cocher la case à cocher Include bitstream et valider par OK.

Export de la configuration matérielle

La création du système est maintenant achevée.

Programmer avec le SDK

Création d'une application

Lancer le SDK à partir de Vivado HLx : menu File->Lauch SDK...

Lancement du SDK

Valider par OK.

La configuration matérielle créé dans Vivado est le premier élément chargé dans le SDK.

Caractéristique de la platforme matérielle

Note : la première partie de ce fichier contient un tableau qui renseigne sur les plages d'adresses attribuées à chacun des composants de la structure matérielle.

Créer l'application à partir du menu File->New->Application Project.

Menu pour créer l'application

Compléter la boite de dialogue comme suit :

Création de l'application

Cliquer Next pour choisir un application de base et sélectionner Empty Application comme ci-dessous :

Création d'une application vide

Cette action a créé deux dossiers. Un dossier pour le codage de notre projet et dans lequel on placera les fichiers sources. Le second dossier possède le suffixe _bsp, pour Board support package. Ce dossier fournit un ensemble de bibliothèques et pilotes constituant la couche de plus bas niveau de l'application.

Les bibliothèques et pilotes sont parfaitement documentés dans le dossier d'information BSP Documentation. Un clic sur l'intitulé gpio_vx_y permet d'accéder à l'ensemble de la documentation générée par doxygen pour l'IP GPIO sur internet.

Dossier BSP

Pour ce premier projet, on part d'un code source existant proposé par Xilinx pour le test d'un GPIO.

Effectuer un clic droit avec la souris sur le code source ci-dessous pour l'enregistrer dans le dossier ProjetSoC1\ProjetSoC1.sdk\testLEDs\src de votre projet.

Note : pour en savoir plus sur l'usage de l'IP GPIO dans un programme en C sous le SDK, penser à consulter la documentation dont le lien est présent dans le dossier BSP du projet..



/******************************************************************************
*
* Copyright (C) 2005 - 2015 Xilinx, Inc.  All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* Use of the Software is limited solely to applications:
* (a) running on a Xilinx device, or
* (b) that interact with a Xilinx device through a bus or interconnect.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* XILINX  BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* Except as contained in this notice, the name of the Xilinx shall not be used
* in advertising or otherwise to promote the sale, use or other dealings in
* this Software without prior written authorization from Xilinx.
*
******************************************************************************/
/*****************************************************************************/
/**
* @file xgpio_tapp_example.c
*
* This file contains a example for using AXI GPIO hardware and driver.
* This example assumes that there is a UART Device or STDIO Device in the
* hardware system.
*
* @note
*
* None
*
* <pre>
* MODIFICATION HISTORY:
*
* Ver	Who  Date	  Changes
* ----- ---- -------- -----------------------------------------------
* 1.00a sv   04/15/05 Initial release for TestApp integration.
* 3.00a sv   11/21/09 Updated to use HAL Processor APIs.
* 3.01a bss  04/18/13 Removed incorrect Documentation lines.(CR #701641)
* 4.1   lks  11/18/15 Updated to use canonical xparameters and
*		      clean up of the comments and code for CR 900381
* 4.3   ms   01/23/17 Added xil_printf statement in main function to
*                     ensure that "Successfully ran" and "Failed" strings
*                     are available in all examples. This is a fix for
*                     CR-965028.
* </pre>
*
*****************************************************************************/

/***************************** Include Files ********************************/

#include "xparameters.h"
#include "xgpio.h"
#include "stdio.h"
#include "xstatus.h"
#include "xil_printf.h"

/************************** Constant Definitions ****************************/

/*
 * The following constant is used to wait after an LED is turned on to make
 * sure that it is visible to the human eye.  This constant might need to be
 * tuned for faster or slower processor speeds.
 */
#define LED_DELAY	  10000000

/* The following constant is used to determine which channel of the GPIO is
 * used if there are 2 channels supported in the GPIO.
 */
#define LED_CHANNEL 1

#define LED_MAX_BLINK	20	/* Number of times the LED Blinks */

#define GPIO_BITWIDTH	4	/* This is the width of the GPIO */

#define printf xil_printf	/* A smaller footprint printf */

/*
 * The following constants map to the XPAR parameters created in the
 * xparameters.h file. They are defined here such that a user can easily
 * change all the needed parameters in one place.
 */
#ifndef TESTAPP_GEN
#define GPIO_OUTPUT_DEVICE_ID	XPAR_GPIO_0_DEVICE_ID
#define GPIO_INPUT_DEVICE_ID	XPAR_GPIO_0_DEVICE_ID
#endif /* TESTAPP_GEN */

/**************************** Type Definitions ******************************/


/***************** Macros (Inline Functions) Definitions *******************/


/************************** Function Prototypes ****************************/

int GpioOutputExample(u16 DeviceId, u32 GpioWidth);

int GpioInputExample(u16 DeviceId, u32 *DataRead);

void GpioDriverHandler(void *CallBackRef);


/************************** Variable Definitions **************************/

/*
 * The following are declared globally so they are zeroed and so they are
 * easily accessible from a debugger
 */
XGpio GpioOutput; /* The driver instance for GPIO Device configured as O/P */
XGpio GpioInput;  /* The driver instance for GPIO Device configured as I/P */

/*****************************************************************************/
/**
* Main function to call the example. This function is not included if the
* example is generated from the Peripheral Tests in SDK.
*
* @param	None
*
* @return
*		- XST_SUCCESS if successful
*		- XST_FAILURE if unsuccessful
*
* @note		None
*
******************************************************************************/
#ifndef TESTAPP_GEN
int main(void)
{
	int Status;
	u32 InputData;

	Status = GpioOutputExample(GPIO_OUTPUT_DEVICE_ID, GPIO_BITWIDTH);
	if (Status != XST_SUCCESS) {
		xil_printf("Gpio tapp Example Failed\r\n");
		  return XST_FAILURE;
	}

	/*Status = GpioInputExample(GPIO_INPUT_DEVICE_ID, &InputData);
	if (Status != XST_SUCCESS) {
		xil_printf("Gpio tapp Example Failed\r\n");
		  return XST_FAILURE;
	}
	*/

	printf("Data read from GPIO Input is  0x%x \n\r", (int)InputData);

	xil_printf("Successfully ran Gpio tapp Example\r\n");
	return XST_SUCCESS;
}
#endif


/*****************************************************************************/
/**
*
* This function does a minimal test on the GPIO device configured as OUTPUT
* and driver as a example.
*
*
* @param	DeviceId is the XPAR_<GPIO_instance>_DEVICE_ID value from
*		xparameters.h
* @param	GpioWidth is the width of the GPIO
*
* @return
*		- XST_SUCCESS if successful
*		- XST_FAILURE if unsuccessful
*
* @note		None
*
****************************************************************************/
int GpioOutputExample(u16 DeviceId, u32 GpioWidth)
{
	volatile int Delay;
	u32 LedBit;
	u32 LedLoop;
	int Status;

	/*
	 * Initialize the GPIO driver so that it's ready to use,
	 * specify the device ID that is generated in xparameters.h
	 */
	 Status = XGpio_Initialize(&GpioOutput, DeviceId);
	 if (Status != XST_SUCCESS)  {
		  return XST_FAILURE;
	 }

	 /* Set the direction for all signals to be outputs */
	 XGpio_SetDataDirection(&GpioOutput, LED_CHANNEL, 0x0);

	 /* Set the GPIO outputs to low */
	 XGpio_DiscreteWrite(&GpioOutput, LED_CHANNEL, 0x0);

	 for (LedBit = 0x0; LedBit < GpioWidth; LedBit++)  {

		for (LedLoop = 0; LedLoop < LED_MAX_BLINK; LedLoop++) {

			/* Set the GPIO Output to High */
			XGpio_DiscreteWrite(&GpioOutput, LED_CHANNEL,
						1 << LedBit);

#ifndef __SIM__
			/* Wait a small amount of time so the LED is visible */
			for (Delay = 0; Delay < LED_DELAY; Delay++);

#endif
			/* Clear the GPIO Output */
			XGpio_DiscreteClear(&GpioOutput, LED_CHANNEL,
						1 << LedBit);


#ifndef __SIM__
			/* Wait a small amount of time so the LED is visible */
			for (Delay = 0; Delay < LED_DELAY; Delay++);
#endif

		  }

	 }

	 return XST_SUCCESS;

}


/******************************************************************************/
/**
*
* This function  performs a test on the GPIO driver/device with the GPIO
* configured as INPUT
*
* @param	DeviceId is the XPAR_<GPIO_instance>_DEVICE_ID value from
*		xparameters.h
* @param	DataRead is the pointer where the data read from GPIO Input is
*		returned
*
* @return
*		- XST_SUCCESS if the Test is successful
*		- XST_FAILURE if the test is not successful
*
* @note	  	None.
*
******************************************************************************/
int GpioInputExample(u16 DeviceId, u32 *DataRead)
{
	 int Status;

	 /*
	  * Initialize the GPIO driver so that it's ready to use,
	  * specify the device ID that is generated in xparameters.h
	  */
	 Status = XGpio_Initialize(&GpioInput, DeviceId);
	 if (Status != XST_SUCCESS) {
		  return XST_FAILURE;
	 }

	 /* Set the direction for all signals to be inputs */
	 XGpio_SetDataDirection(&GpioInput, LED_CHANNEL, 0xFFFFFFFF);

	 /* Read the state of the data so that it can be  verified */
	 *DataRead = XGpio_DiscreteRead(&GpioInput, LED_CHANNEL);

	 return XST_SUCCESS;

}

Etude du code source

Parcourir le code source et identifier les différentes fonctions permettant d'utiliser l'IP GPIO.

Décrire ce qui s'affichera sur les LEDs durant l'exécution du programme.

Exécution

La première exécution requiert de programmer la configuration dans le FPGA : menu Xilinx->Program FPGA

Programmation de configuration matérielle dans le FPGA

Penser à vérifier que le nom du bitstream, ProjetSoC1_wrapper.bit, est bien présent dans la boite de dialogue puis cliquer sur Program.

A noter que cette configuration sera perdue à l'extinction de la carte Zybo.

Avant d'exécuter le programme proposé ci-dessus, on construit l'exécutable en sélectionnant le dossier testLEDs dans le Project Explorer :

Sélection du projet actif

puis en cliquant sur Build All dans le menu Project.

Noter qu'un clic droit de la souris dont le curseur est positionné sur le dossier du projet permet d'accéder à la commande Build ou, qu'en fonction de la configuration du SDK, la sauvegarde des fichiers sources du programme lance le processus de construction de l'application.

L'exécution peut ensuite être lancée :

  • soit par un Run As -> Launch on Hardware (System Debugger),
  • soit par un Debug As -> Launch on Hardware (System Debugger) pour bénéficier des fonctionnalités de mise au point.

Exercice

Créer un projet en repartant de 0 pour lequel vous activerez le port GPIO2 de l'IP GPIO. Le premier port restera lié aux LEDs de la carte Zybo. Le second port sera mappé sur les boutons poussoirs.

La partie logicielle sera effectuée avec comme base le fichier source précédent, modifié pour que les LEDs reflètent l'états des boutons poussoirs.

Noter que l'intitulé LED_CHANNEL est défini pour permettre l'accès au port 1 de l'IP GPIO et qu'il sera judicieux de créer une définition PB_CHANNEL valant 2 pour accéder au second port de l'IP GPIO.