PAGE 1 PETITPA
Intégration d’un processeur spécialisé et d’un périphérique Personnalisable sur un IPCORE
1) Problématique et objectifs
Lors du TP Précédent nous avons mis en place un processeur spécialisé PS associé à de la logique
programmable PL. Nous garderons pour ce TP le processeur spécialisé Microblaze mais nous allons intégrer à celui-ci un périphérique. Ce périphérique sera couplé à l’un des bus internes du processeur l’AXI4LITE (voir dans le dossier annexe l’architecture des bus du processeur Microblaze). Le périphérique sera un temporisateur qui nous permettra de façon simple de piloter la LED LDO du KIT FPGA ATLYS. Lors de la création du périphérique il faudra modéliser de façon comportementale sa logique interne par du code VHDL. Par la suite on développera un driver en langage C afin de lire ou écrire dans les différents registres du périphérique que nous avons créé. Un diviseur de fréquence nous fournira une horloge de 1 ms au temporisateur
PAGE 2 PETITPA
2) Méthodologie
La méthodologie est la suivante :
XPS Xilinx platform
studio
ISE
Integrated system editor
SDK software development
kit
Définir la plateforme du système microprogrammé CPU microblaze, Intégrez un
nouveau périphérique au processeur
- Créez notre schematic sous ISE afin d’intégrer notre CPU des
IPCORE propriétaires le tout synthétisé sur un FPGA -Simulez le cas échéant, avec lsim
le comportement du système - Programmez avec impact la partie matérielle du FPGA Développez en langage C
notre driver pour le cpu microblaze afin de piloter
notre périphérique, programmez la partie
logicielle FPGA
On aura besoin de créer un répertoire projet et trois sous-répertoires pour nos 3 étapes : ISE pour l’intégration RTL VHDL
XPS pour la création et la configuration du processeur spécialisé PS SDK pour le développement logiciel du SOPC
dds8
PAGE 3 PETITPA
3) Partie 1 :définir et configurer le processeur spécialisé et intégrer notre périphérique Timer
3.1) Configuration du PS
Lancez le logiciel XPS
On définit par la suite un board support package (BSB) qui sera fonction de l’architecture matérielle du FPGA et du kit ATLYS
La Prise en compte matériel du board support package de la carte ATLYS XILINX DIGILENT
PAGE 4 PETITPA
La configuration du PS et les IPCORE intégrables du BSP s’effectue sur la fenêtre suivante :
Cliquez sur next
PAGE 5 PETITPA
On peut intégrer dans le PS, des IPCORE compatibles avec le CPU microblaze. La fenêtre de droite nous donne tous les IPCORE qui constituent l’architecture actuelle du PS
Dans notre application on va Intégrer dans le PS, notre propre IPcore qui sera un périphérique du type
temporisateur programmable. Ce périphérique sera constitué de 2 registres accessibles en lecture ou en écriture intégré dans le bloc user_logic.
L’interface AXI LITE sera gérée automatiquement par la suite logicielle XPS. Nous aurons à définir le bloc user_logic.vhd (broche d’entrée-sortie, configuration des registres, fonctionnement comportemental)
On intégrera 2 registres :
Un registre de préchargement des compteurs du temporisateur nommée slv_reg1 Un registre de configuration du temporisateur nommée slv_reg0
PAGE 6 PETITPA
Registre de configuration slv_reg0 32bits Registre de préchargement
des compteurs slv_reg1 32bits
Adresse @
XPAR_TIMER_0_BASEADDR
XPAR_TIMER_0_BASEADDR+0x04 Cartographie mémoire du
processeur Microblaze
Chaque registre sera accessible en lecture ou en écriture. Ce temporisateur permettra aux compteurs internes de décompter de la valeur contenue dans le registre de préchargement slv_reg1 jusqu’à la valeur 0. L’entrée
d’horloge sera externe au processeur spécialisé et baptisé Timer_0_H1_pin.
Le registre de configuration fonctionnera de la suivante :
slv_reg0=0x00 le compteur évolue naturellement de reg_slav1 jusqu’à la valeur 0 avec une horloge Timer_0_H1_pin de 1ms.
slv_reg0=0x01 le compteur est chargé avec le contenu de reg_slav1 et il reste figé à cette valeur. la LED LD0 du kit FPGA est éteinte.
La LED LD0 changera d’état à chaque nouveau cycle de décomptage du décompteur. Cette LED sera connectée sur la sortie du temporisateur Timer_0_LED_PIN.
Nous allons créer un IPCORE personnalisable qui correspond au périphérique timer du processeur spécialisé Microblaze.
3.2) Intégration de notre périphérique grâce à un IPCORE personnalisable
Allez dans le menu hardware et sélectionnez Create or import peripheral
PAGE 7 PETITPA
Faire Next
PAGE 8 PETITPA
Donnez un nom à notre périphérique Timer dans notre cas
PAGE 9 PETITPA
Choisir une connexion du périphérique en utilisant le bus interne du PS nommé AXI4-lite
L’Intégration de l’IPCORE gpio dans le PS se fait par le bus AXI4lite du CPU Microblaze
PAGE 10 PETITPA
On peut définir si l’interface de notre périphérique IPIF du Timer est du type maitre ou esclave et si les registres sont programmables logiciellement .
PAGE 11 PETITPA
Le nombre de registre accessible est configurable. On choisit 2 registres accessibles
PAGE 12 PETITPA
Les signaux de l’interface IPIF vers notre user_logic sont aussi paramétrables
PAGE 13 PETITPA
Il est possible de faire du debuggage logiciel de notre périphérique en intégrant un master Lite BFM (non sélectionné en raison des ressources matérielles monopolisées).
PAGE 14 PETITPA
Différents fichiers peuvent être créés pour les autres outils de la suite ISE. On ne demandera que la création du fichier source C du périphérique.
PAGE 15 PETITPA
La fenêtre suivante nous donne l’emplacement des fichiers créés par XPS
PAGE 16 PETITPA
La suite de la fenêtre :
PAGE 17 PETITPA
A ce stade il faut intégrer notre périphérique sur le bus AXI4 du processeur Microblaze :
Les fenêtres suivantes nous donnent la configuration possible du périphérique. Ne changez rien XPS fera le travail de configuration à notre place.
PAGE 18 PETITPA
En cliquant sur ok notre périphérique va s’intégrer dans notre processeur spécialisé :
Ses différents ports que l’on configurera un peu plus tard
PAGE 19 PETITPA
Le logiciel XPS a donc créé sur le disque dur notre IPCORE suivant l’arborescence ci-dessous :
Le fichier MPD est un fichier de description du microprocesseur contenant les informations des périphériques afin que l’outil EDK puisse reconnaitre ton périphérique. On l’utilisera pour déclarer des nouveaux ports.
Le fichier PAO définit l’ordre d’analyse de tous les fichiers sources HDL qui sont utilisé pour synthétiser ton périphérique
Le fichier timer_ip.vhd décrit en VHDL l’entité de ton périphérique avec l’intégration de l’interface IPIF et son lien avec la logique utilisateur user_logic. On l’utilisera si on veut additionner des ports en code VHDL dans l’entité.
Le fichier user_logic.vhd va décrire l’entité du composant user_logic. Le code VHDL décrire le comportement fonctionnel de notre périphérique.
L’adresse du périphérique dans la cartographie mémoire du PS est donnée ci-dessous :
PAGE 20 PETITPA
On peut accéder aux fichiers de définition de la manière suivante :
On doit rajouter la description matérielle de nos deux ports d’entrée- sorties du timer
PAGE 21 PETITPA
On rajoute nos deux déclarations matérielles :
On peut accéder à la description VHDL de notre périphérique
PAGE 22 PETITPA
Dans le fichier timer.vhd, on déclarera dans l’entité du PS les ports d’entrées sorties de notre périphérique :
Dans l’entité user_logic_i du fichier timer.vhdl on effectuera le câblage des ports
PAGE 23 PETITPA
Dans le fichier user_logic.vhd il faudra dans les ports à nouveau déclarer nos ports d’entrée-sorties :
Repérez les 2 registres slv_reg0 et slv_reg1 créé par XPS
PAGE 24 PETITPA
Une fois la déclaration des ports effectués il faudra les connecter vers l’extérieur :
La dernière partie est de décrire le comportement fonctionnel du périphérique en code VHDL dans le fichier user_logic.vhd :
slv_reg_write_sel <= Bus2IP_WrCE(1 downto 0);-- registre de sélection d'écriture
slv_reg_read_sel <= Bus2IP_RdCE(1 downto 0);-- registre de sélection de lecture
slv_write_ack <= Bus2IP_WrCE(0) or Bus2IP_WrCE(1);
slv_read_ack <= Bus2IP_RdCE(0) or Bus2IP_RdCE(1);
consigne<=slv_reg1(31 downto 0);-- mise à jour de la consigne avec la programmation du registre
-- implement slave model software accessible register(s)
compteur : process (H1) begin
if (H1'event and H1 = '1') then
if (Bus2IP_Resetn = '0' or slv_reg0 = X"00000001") then count <= consigne(31 downto 0);
LED <='0';
else count <=count-1;
end if;
end if;
if (count=0) then
count <= consigne(31 downto 0);
LED <= not LED;
end if;
end process compteur;
PAGE 25 PETITPA
SLAVE_REG_WRITE_PROC : process( Bus2IP_Clk ) is begin
if Bus2IP_Clk'event and Bus2IP_Clk = '1' then if Bus2IP_Resetn = '0' then
slv_reg0 <= (others => '0');-- remise à 0 des 2 registres de travail lorsque Bus2IP_Resetn = '0'
slv_reg1 <= (others => '0');-- remise à 0 des 2 registres de travail else
case slv_reg_write_sel is
when "10" => -- si le registre de selection d'écriture est à 10 affectez octets par octets slv_reg0 avec Bus2IP_Data
for byte_index in 0 to (C_SLV_DWIDTH/8)-1 loop
if ( Bus2IP_BE(byte_index) = '1' ) then -- si bus validé slv_reg0(byte_index*8+7 downto byte_index*8) <=
Bus2IP_Data(byte_index*8+7 downto byte_index*8);
end if;
end loop;
when "01" => -- si le registre de selection d'écriture est à 01 affectez octets par octets slv_reg1 avec Bus2IP_Data
for byte_index in 0 to (C_SLV_DWIDTH/8)-1 loop
if ( Bus2IP_BE(byte_index) = '1' ) then --si bus est validé slv_reg1(byte_index*8+7 downto byte_index*8) <=
Bus2IP_Data(byte_index*8+7 downto byte_index*8);
end if;
end loop;
when others => null;
end case;
end if;
end if;
end process SLAVE_REG_WRITE_PROC;
-- implement slave model software accessible register(s) read mux
SLAVE_REG_READ_PROC : process( slv_reg_read_sel, slv_reg0, slv_reg1 ) is begin
case slv_reg_read_sel is
when "10" => slv_ip2bus_data <= slv_reg0;-- slv_reg_read_sel=10 alors lv_ip2bus_data = slv_reg0;
when "01" => slv_ip2bus_data <= slv_reg1;-- slv_reg_read_sel=01 alors lv_ip2bus_data = slv_reg1;
when others => slv_ip2bus_data <= (others => '0');
end case;
end process SLAVE_REG_READ_PROC;
PAGE 26 PETITPA ---
-- Example code to drive IP to Bus signals ---
IP2Bus_Data <= slv_ip2bus_data when slv_read_ack = '1' else (others => '0');
IP2Bus_WrAck <= slv_write_ack;
IP2Bus_RdAck <= slv_read_ack;
IP2Bus_Error <= '0';
end IMP;
Lancez à nouveau un DRC (design rule check ) et Sauvegardez le projet
A ce stade notre processeur spécialisé a été façonné, on va donc intégrer ce PS dans un environnement matériel plus complexe sous ISE
3.3) Intégration de notre processeur spécialisé sous ISE
Les différentes phases de conception qui suivent ont été effectuées lors des TPS précédent :
On réalisera la schématique page suivante.
On réalisera le fichier de contrainte .ucf sur Plan_ahead afin de connecter l’IPCORE clocking wizard sur la broche L15 du FPGA et la sortie du timer sur la LED LD0 du kit
NET "clk_EX" LOC = L15;
NET "timer_0_LED_pin_ex" LOC = U18;
On programmera le FPGA avec le logiciel impact
Lancez l’outil SDK sans exporter le bitstream
PAGE 27 PETITPA
3.4) Développement du pilote pour le périphérique timer du processeur spécialisé
Créez un nouveau projet
PAGE 28 PETITPA
PAGE 29 PETITPA
PAGE 30 PETITPA
L'utilisation des composants matériels nécessitent des drivers pour accéder aux systèmes
Comme tous les matériels sont mappés en mémoire dans l'espace adressable le driver peut directement accéder aux registres d'interfaces.
L'adresse de base de chaque système est accessible par le fichier “xparameters.h”.
XPAR_TIMER_0_BASEADDR
Il faut modifier le fichier test_periph.c
#include <stdio.h>
#include "xparameters.h"
#include "xil_cache.h"
#include "uartlite_header.h"
#include "xbasic_types.h"
#include "xgpio.h"
//#include "platform.h"
#include "gpio_header.h"
#include "xspi.h"
#include "spi_header.h"
int main() {
u32 *compteur;
u32 *reg_select;
u32 DataRead;
u32 status;
reg_select = (u32 *)XPAR_TIMER_0_BASEADDR;// registre de configuration du timer
compteur=(u32 *)(XPAR_TIMER_0_BASEADDR+0x04);// registre de prépositionnement du timer
Xil_ICacheEnable();
Xil_DCacheEnable();
//init_platform();
xil_printf("essai timer\n");
status = GpioInputExample(XPAR_DIP_SWITCHES_8BITS_DEVICE_ID, &DataRead);//
récupération état des switches
xil_printf("GpioInputExample PASSED. Read data:0x%X\r\n", DataRead);
*compteur=1000; // initialisation du timer *reg_select=0x00000001;// chargement
*reg_select=0x00000000;// evolution libre du timer while(1){
status = GpioInputExample(XPAR_DIP_SWITCHES_8BITS_DEVICE_ID, &DataRead);
if ((DataRead&0x00000002)==0x00000002) {
*compteur=1000;
}
if ((DataRead&0x00000004)==0x00000004) {
PAGE 31 PETITPA *compteur=10000;
}
if ((DataRead&0x00000008)==0x00000008) {
*compteur=100;
}
if ((DataRead&0x00000001)==0x00000001) {
*reg_select=0x00000001;// charger le timer }
else *reg_select=0x00000000;// laisser évoluer le timer usleep(1000000);
xil_printf("la valeur de reg_slv0 est 0x%X\r\n",*reg_select);
xil_printf("la valeur de reg_slv1 est 0x%X\r\n", *compteur);
}
//status = UartLiteSelfTestExample(XPAR_DEBUG_MODULE_DEVICE_ID);
Xil_DCacheDisable();
Xil_ICacheDisable();
return 0;
}
void usleep(unsigned int useconds) {
int i,j;
for (j=0;j<useconds;j++)
for (i=0;i<15;i++) asm("nop");
}
PAGE 32 PETITPA
Annexe : description des bus internes du processeur Microblaze
AXI fait partie de ARM AMBA, une famille de bus pour microcontrôleurs crée en 1996. L’interface AXI permet au PS de communiquer avec les IP de la partie PL en mode maître ou esclave. Il connecte un ou plusieurs device esclaves (jusqu’à 64 bits d’adresse) dans la PL, ayant une zone mémoire (registre) connu du PS. Il se conforme aux spécifications AMBA®. AXI4 de ARM® et inclut le subset AXI4-Lite control register interface ;
Il y a 3 types d’interfaces AXI4 :
AXI4-STREAM Pour du streaming high-speed de données.
AXI4-Lite Il n’y a pas de limite de taille pour la rafale de données sans interruptions (burst).
AXI4 Pour du streaming high-speed de données.
La gestion du protocole de communication des 3 interfaces AXI4 sera gérée par le logiciel XPS. La génération de code automatique de XPS rendra transparent pour l’utilisateur l’écriture du code VHDL responsable de gérer les trois protocoles de l’AXI4.
PAGE 33 PETITPA IPIF : AXI4-Lite IP InterFace
Met à disposition une interface bidirectionnelle, point à point, entre l’IP utilisateur et le bus AXI interconnect. Optimisée pour des opérations esclaves sur l’AXI. Elle n’apporte pas de support pour le Direct Access Memory DMA.
User_Logic
Le fichier user_logic permet une liaison entre le PS et le PL ; c’est ce que l’on nomme stub. Il permet la communication entre le PS et la PL via le modèle de registres. Il fait partie du User IP. C’est sur ce bloc que l’on viendra créer l’IPcore précédemment écrit en vhdl.
PAGE 34 PETITPA