Electronique et Télécommunication – 5ème Année
2021-2022
TP SoC
Prise en main du
PicoBlaze, le processeur embarqué
Dans ce premier TP, le but recherché est de pouvoir créer un circuit SoC contenant un
microprocesseur, la mémoire du programme et des interfaces.
Nous avons à notre disposition le programme VHDL du PicoBlaze (appelé KCPSM6) et les
outils logiciels nécessaires pour écrire des programmes en langages assembleurs. Ensuite, on
crée les codes objets pour générer la mémoire ROM qui contiendra ces codes opératoires (les
codes objets) interprétables par KCPSM6.
1 Première exercice, lecture d’un port d’entrée et le recopier dans un
port de sortie 1
- Télécharger à partir de http://www.unilim.fr/pages_perso/vahid/kcpsm6.html, le fichier
zippé qui contient les fichiers nécessaires pour travailler avec PicoBlaze. Dé-zipper ce
fichier dans un dossier, qu’on appellera ci-après le « dossier de référence ». Ce dossier
contiendra, entre-autres, le guide d’utilisation de KCPSM6 et le programme VHDL qui
décrit le MicroProc.
- Créer un nouveau projet avec les bonnes références du FPGA disponible sur la carte Basys3
(Artix7, xc7a35ticpg236).
- Copier, depuis le dossier de référence, les fichiers suivants vers le répertoire de votre projet:
o kcpsm6.vhd
o kcpsm6_design_template.vhd
o ROM_form.vhd
1
Une vidéo sur community détaille ce premier exercice.
Vahid Meghdadi 1 ENSIL / ELT5
o kcpsm6.exe
- Créer un module VHDL nommé "main" avec une entrée horloge (clk), une entrée sw (sur 8
bits) et une sortie led (sur 8 bits).
- Dans ce fichier "main.vhd" il va falloir ajouter au programme main, deux components : le
PicoBlaze et la mémoire. Ouvrez le fichier "kcpsm6_design_template.vhd" que vous avez
copié dans le répertoire de travail. Ce fichier contient un ensemble de lignes de codes que
l’on peut utiliser (en faisant copier-coller dans vos programmes à vous). Copier les lignes
suivantes dans votre programme main.vhd :
o Component "kcpsm6" (lignes 71-89) à coller avant le mot clé « begin » dans main;
c’est la déclaration de PicoBlaze,
o Component "<your_program>" (lignes 97-106) à coller juste après le component
précédent, c’est la déclaration de la mémoire du programme contenant votre
programme assembleur que nous devons créer plus tard. Renommer le component
en remplaçant "<your_program>" par "prog".
o Toutes les déclarations des signaux (ligne 119-145), à coller à la suite du précédent
o Instanciation du kcpsm6 (ligne 163-180) à coller après « begin » ; c’est à ce
moment-là que PicoBlaze sera instancié, on s’occupera de son câblage plus tard.
o Instanciation du mémoire de programme <your_program> (213-221), à coller à la
suite. Le renommer en remplaçant "<your_program>" par "prog". Sachant que
nous utiliserons le FPGA Artix-7 (série 7) et non pas Virtex 6, remplacer donc
« V6 » par « 7S » dans C_FAMILY. Choisir aussi 1K de cases mémoires pour
C_RAM_SIZE_KWORDS, et avec "JTAG Loader" (la valeur 1) :
C_FAMILY => "7S",
C_RAM_SIZE_KWORDS => 1,
C_JTAG_LOADER_ENABLE => 1)
- Réalisation du câblage entre PicoBlaze et la mémoire. On utilisera le câblage présenté
sur la figure ci-dessous avec des noms des signaux internes donnés sur le schéma. Ceci
implique éventuellement de renommer des signaux et de modifier l'instanciation de
KCPSM6 et de la mémoire (prog) :
Vahid Meghdadi 2 ENSIL / ELT5
Figure 1- connexion de PicoBlaze et mémoire
Pour kcpsm6 et pour la mémoire prog :
port map(address => instr_adr,
instruction => instr,
...
Création de la mémoire « prog »
Nous écrivons le programme en assembleur dans un fichier texte. Sous vivado, faire file-> text
editor -> new file. C’est dans ce fichier que nous écrivons notre programme. Sauvegarder avec
le nom « prog.psm » (c’est la raison pour laquelle nous avions renommé <your_program> en
« prog »).
Ecriture du programme assembleur : Dans ce premier exercice, le but est d’afficher l'état des
SW sur les LED. Pour cela on lit, dans une boucle infinie, l’état des switches et on écrit cet état
sur les LEDs. On écrit donc le programme ci-dessous :
BOUCLE:
INPUT S0,00
OUTPUT S0,00
JUMP BOUCLE
Dans une boucle infinie, ce programme lit le port zéro et écrit la data lue, sur le port 0.
Génération des codes objet et la mémoire de programme
Pour créer les codes objets on va « assembler » ce programme, et ensuite on créa un fichier
VHDL qui réalise la mémoire ROM correspondante :
- Exécutez le fichier kcpsm6.exe que vous aviez copié dans le répertoire du projet. Il vous
demandera le nom du fichier de programme : prog. Il créera la mémoire (prog.vhd).
- Le message « writing VHDL file … prog.vhd » confirme la création de la ROM : prog.vhd.
Ajouter le PicoBlaze (kcpsm6.vhd) et la mémoire (prog.vhd) à votre projet.
Mise en place du port d'entrée : Puisqu'il n'y qu'une seule entrée, nous pouvons définitivement
connecter in_port du µP à notre signal SW (ainsi le signal port_id n'est pas exploité).
Vahid Meghdadi 3 ENSIL / ELT5
- Ceci se fait directement dans l'instanciation du KCPSM6 :
in_port => SW,
(Le signal read_strobe ne sera pas utilisé: read_strobe => open)
- Mise en place du port de sortie : la data se trouvant sur le port de sortie (out_port) est
valide uniquement quand write_strobe = '1' (voir le timing de la page 74 de la présentation
KCPSM6; voir aussi sur cette page le process qui peut être utilisé)2. Dans notre cas, puisqu'il
n'y a qu'un seul port de sortie, on n'exploite pas le "port_id", comme présenté sur la figure
(port_id => open).
Figure 2- le circuit fonctionnel du port de sortie
- Donc le câblage de kcpsm6 contiendra :
…
port_id => open,
write_strobe => wr_en,
k_write_strobe => open,
out_port => out_port,
read_strobe => open,
in_port => SW,
interrupt => '0',
interrupt_ack => open,
sleep => '0',
...
Il faudra que vous modifiiez les déclarations des signaux en conséquence (write_strobe en
wr_en par exemple)
- Créer et ajouter le fichier des contraintes "Basys3_Master.xdc".
o Aller le chercher sur la page web du TP (Resources → carte basys 3 → Fichier Xdc
de la carte), l’enregistrer dans votre répertoire du projet et décommenter les broches
que vous utiliserez.
o L’ajouter à votre projet en tant que constraints
2
Quand le processeur exécute l’instruction « OUTPUT "sur un port","une data" », la data sera mise sur out_port
et une impulsion sera générée sur write_strobe.
Vahid Meghdadi 4 ENSIL / ELT5
- Synthétiser, implantez, et générer le fichier binaire à télécharger sur FPGA. Programmez le
FPGA et vérifier son fonctionnement.
Essai : On modifie légèrement le programme comme ci-dessous. Une fois ces modifications
faites, reconstruire la mémoire utilisant kcpsm6.exe, et ensuite régénérer le fichier main.bit.
DISABLE INTERRUPT
BOUCLE:
INPUT S0,00
ADD S0,01
OUTPUT S0,00
JUMP BOUCLE
Comme vous voyez, le moindre changement du programme assembleur nous oblige à
resynthétiser tout le projet, ce qui est très ennuyeux. Cependant, on peut appliquer une
procédure particulière afin de ne pas tout resynthétiser, et retester le programme en quelques
secondes. Pour cela, aller voir la procédure sue le site web de cours :
Ressources => PicoBlaze(KCPSM6) => JTAG_LOADER
2 Ajout d’un deuxième port
Nous souhaitons pouvoir afficher la valeur de "SW", non seulement sur les 8 LED, mais aussi
sur les deux 7-seg de droite de la carte nexys-3. Sachant que nous avons 8 SW, ils peuvent
représenter une valeur hexadécimale sur 2 digits. C’est ces deux digits que nous souhaitons
afficher. Nous réaliserons le hardware nécessaire par la suite. Une fois hardware implanté, le
programmeur n’a qu’à envoyer l’octet correspondant aux deux digits sur un port, disons le port
1. Le programme à écrire par le programmeur assembleur ressemblera donc à :
DISABLE INTERUPT
BOUCLE:
INPUT S0,00
OUTPUT S0,00 ; pour afficher sur les LED
OUTPUT S0,01 ; pour afficher sur les 7-SEG
JUMP BOUCLE
Remarque :
Nous mettrons ainsi à la disposition des utilisateurs de notre SoC deux API, un pour afficher
une donnée de 8 bits sur les LED et un autre pour afficher cette donnée sur les deux 7-segments.
Il est toujours préférable d’accompagner nos API d’une aide qui détaille l’utilisation de notre
API.
Exemple : API à utiliser pour écrire sur les LED de la carte Basys3
OUTPUT sX,00
sX contient la donnée à afficher.
Vahid Meghdadi 5 ENSIL / ELT5
Exemple : API à utiliser pour écrire une valeur hexadécimale sur les deux
7-seg de la carte Basys3
OUTPUT sx,01
sX contient la donnée à afficher.
Vous avez forcément remarqué combien le circuit (hardware) a pu faciliter la tâche d’affichage
pour celui qui programmera ce "micro-controleur".
Conception hardware
Dans un premier temps, créer la mémoire ROM correspondant à ce nouveau programme
assembleur en utilisant la commande kcpsm6.exe. Ceci génère le fichier prog.vhd qui
contiendra les codes opératoires.
On conçoit maintenant la partie hardware en VHDL. Pour cela, nous utiliserons le programme
VHDL correspondant à l’affichage que nous avons écrit l’an dernier.
- Mise en place de l'afficheur : récupérez le programme "afficheur.vhd" sur la page web du
cours → Ressources → Afficheurs sept-segments, et le sauvegarder dans le dossier courant
de votre projet Puis l’ajouter au projet. C'est un programme qui permet d'afficher un nombre
en hexadécimal à 4 digits sur les 7-segmnts.
- Déclarer et instancier ce component dans main
- Ce component a besoin d'un signal à son entrée (le signal "enable") que vous devez créer
par un process dans votre programme main.vhd. Pour des raison de réutiliser le timing pour
le port UART, on fixe le temps de rafraichissement des segments à 1/19200.
Rappel :
process(CLK)
begin
if CLK'event and CLK='1' then
cmp <= cmp+1; en <= '0';
if cmp = 5207 then
cmp <= 0; en <= '1';
end if;
end if;
end process;
- Effectuez le câblage dans l’instanciation de l’afficheur comme présente la Figure 3.
Vahid Meghdadi 6 ENSIL / ELT5
Figure 3- Mise en place du component « afficheur »
Comme on peut constater, les 8 MSB du signal "hex" sont connectés à la masse ; les 8 autres
doivent provenir du port 01 de PicoBlaze.
- Mise en place des ports de sortie : Nous avons deux ports aux adresses 00 (pour les LED)
et 01 (pour les 7-segments). Utilisant un décodage partiel, le LSB du port_id pourra
départager les LED et les 7-seg. Si, sur le front montant de CLK, wr_en est actif et
port_id(0)='0', out_port est copié sur LED, par contre si port_id(0) ='1', out_port est copié
sur hex(7 downto 0). Ceci est réalisable par un process synchrone. La Figure 4 schématise
ce principe. Pour plus de détails sur les chronogrammes de la commande output, consultez
la page 74 du guide d’utilisation de KCPSM6.
Figure 4- Mise en place des ports de sortie
Configurer le FPGA et tester le programme.
3 Ajout d’un émetteur UART
Nous allons donner à l’utilisateur de notre SoC la possibilité d’écrire sur le port série. Pour cela,
récupérer le programme "rs232_tx" depuis la page du TP: Ressource → Port série (émetteur).
Ajouter ce programme à votre projet. Ajouter la déclaration et l'instanciation de ce component
à votre programme main.vhd. Cette interface, une fois intégré sur le FPGA, permettra au
programmeur, moyennant l’appel d’une fonction, d’envoyer un caractère ASCII au PC par
l’intermédiaire du port série.
Vahid Meghdadi 7 ENSIL / ELT5
Fonction de cette interface : L’interface RS232_tx (voir la Figure 5) reçoit une donnée 8 bits
sur son entrée DATA, le signal STRB valide cette donnée. Dès que l’activation de STRB est
sentie par l’interface, la donnée est copiée dans l’interface et elle commence à sérialiser cet
octet, en suivant la norme RS232 et au rythme imposé par son entrée EN_19200.
La sortie sérialisée doit être connectée à la broche TX de la carte. Pendant la sérialisation de la
donnée, l’interface ne pourra pas accepter une nouvelle donnée, ceci est signalé par activation
(niveau haut) de la sortie BUSY. Le programmeur, avant d’écrire son caractère sur l’interface
UART, doit s’assurer que l’interface est prête (BUSY vaut ’0’). Vous devez donc donner cette
possibilité au programmeur de pouvoir lire l’état de la ligne BUSY. Pour cela, vous allez
connecter le signal « BUSY » au port d’entrée 02 du microprocesseur.
L’API ci-dessous sevra être appelé par l’utilisateur pour envoyer un caractère vers le PC :
; le registre S0 est modifié au cours de sous-programme
; le registre SF est envoyé au port série
UART_TX:
INPUT S0,02 ; verification de BUSY
TEST S0,01
JUMP NZ,UART_TX ; Boucle d’attente si BUSY = '1'
OUTPUT SF,02
RETURN
Attention : ce sous-programme modifie le contenu de S0 !
L’utilisateur, pour envoyer le caractère ‘A’ par exemple, doit écrire :
LOAD SF,”A”
CALL UART_TX
BCL:
JUMP BCL
La Figure 5 présente les différents connexion des entrées/sorties du component rs232_tx aux
signaux déclarés dans votre programme "main.vhd".
Figure 5- Mise en place du component rs232_tx
Dans un premier temps, on s’occupe de créer la possibilité de lecture de busy sur le port 02.
Mise en place du port d'entrée pour la lecture du BUSY
Vahid Meghdadi 8 ENSIL / ELT5
On garde le SW connecté au port 00, et on y ajoute un port supplémentaire. La Figure 6 présente
les détails du circuit conçu.
Figure 6- Mise en place du port d’entrée
Le process de la figure gère les ports d’entrée au µP. Le circuit logique à créer est un circuit qui
copie sur in_port, soit SW, soit le signal ("0000000"&busy). En fait, busy étant un seul bit, les
7 autres bits du port sont connectés à la masse.
Remarque importante : nous avions connecté le signal sw directement à l’entrée in_port du
µP. Il faudra le modifier pour le connecter à un signal interne que nous appelons aussi in_port.
La mise en place du port de sortie UART_TX
La Figure 7 montre la structure à mettre en place.
Figure 7- Mise en place du port de sortie
Pour réaliser ce dispositif, modifier le process output_ports pour tenir compte de ce nouveau
port.
Remarque importante : Pour que l’interface rs232_tx prenne la donnée, il faudra aussi lui
envoyer le signal STRB, chose à prévoir dans votre process. C’est un signal strobe classique
qui ne doit pas durer plus d’un seul cycle d’horloge.
Pour tester votre hardware, nous allons écrire un programme assembleur qui écrit un mot sur le
port série (visible sur PC par l’intermédiaire de "hercules"). Essayer le programme ci-dessous :
LOAD SF,"V"
CALL UART_TX
LOAD SF,"H"
CALL UART_TX
Vahid Meghdadi 9 ENSIL / ELT5
LOAD SF,"D"
CALL UART_TX
LOAD SF,"L"
CALL UART_TX
LOAD SF,0A
CALL UART_TX
LOAD SF,0D
CALL UART_TX
FIN:
INPUT S0,00
OUTPUT S0,00
OUTPUT S0,01
JUMP FIN
Pour vérifier le fonctionnement de notre hardware, on connecte la carte au PC par le port série,
puis on lance "hercules" (avec les bons paramètres de configuration : le baud rate à 19200, 1 bit
de stop, pas de parité).
Remarque : l’API développé pour écrire sur le port série est un sous-programme à appeler :
UART_TX. Dans l’aide, on marquera qu’il faudra mettre le code ascii à envoyer dans le registre
SF avant d’appeler le sous-programme. On marquera aussi qu’il détruit le contenu du registre
S0. Une solution pour ne pas écraser S0 est d’utiliser la banque B.
UART_TX:
STAR S1,SF
REGBANK B
INPUT S0,02 ; verification de BUSY
TEST S0,01
JUMP NZ,UART_TX ; Boucle d’attente si BUSY = '1'
OUTPUT S1,02
REGBANK A
RETURN
4 Ajout d’un récepteur UART (utilisation de l’interruption)
Nous allons ajouter à notre processeur, une interface UART en réception pour nous permettre
de recevoir un caractère ASCII venant du PC. On affiche ensuite ce code ASCII sur les 2
segments de gauche. L'interface (à récupérer sur le site du TP) écoute le PC en permanence.
Dès qu'elle reçoit un caractère, elle envoie le caractère reçu sur ces lignes D_OUT et puis
elle donne une impulsion sur sa sortie STRB. Notre programme main, dès la réception de
STRB, doit sauvegarder ce caractère dans un registre pour que le processeur le lise quand il
aura le temps. Nous devons signaler la présence d’un caractère au processeur. On va le faire
moyennant une interruption.
Vahid Meghdadi 10 ENSIL / ELT5
Figure 8- Mise en place du component uart_rx
Pour signaler au processeur la présence d’un nouveau caractère, on utilise le signal rx_strb
comme une interruption. Cette demande d’interruption doit être effacé en utilisant
l’acquittement du processeur. Ecrire donc les deux process de la Figure 8 ci-dessus.
Mise en place du port d'entrée
Figure 9- Mise en place des ports d’entrée
La donnée de uart_rx doit être connecté au processeur comme un port d’entrée (voir la Figure
9). On réserve l'adresse 03 pour ce nouveau port qui donnera à notre utilisateur la possibilité
d’une lecture du caractère reçu.
Mise en place du port de sortie:
Figure 10- Mise en place des ports de sortie
Vahid Meghdadi 11 ENSIL / ELT5
Nous n’avons pas encore rentabilisé les deux 7-seg de gauche. Nous faisons de tel sort qu’à
chaque réception d’un caractère ascii, le code soit écrit sur ces deux 7-seg. On utilisera donc le
port de sortie à l'adresse 03 pour écrire sur les 8 bits du poids fort du signal hex, où nous allons
afficher le caractère reçu.
Une fois la synthèse s’effectue sans erreur, on s’occupe de la partie software pour tester le tout.
Software (programme assembleur)
A la réception de l’interruption, le µP saute à l’adresse 3FF où il voit une instruction de
branchement vers la routine d’interruption. Dans la routine d’interruption, il fera une lecture du
port 03 pour lire le caractère reçu et ensuite une écriture sur le port 03 pour écrire sur les deux
7-seg de gauche :
ISR:
REGBANK B
INPUT S0,03 ; lecture UART_RX
OUTPUT S0,03 ; Ecriture 7-SEG de gauche
REGBANK A
RETURNI ENABLE
ADDRESS 3FF
JUMP ISR
Remarque important:
N'oubliez pas d'activer l'interruption en mettant, au début de votre programme, la ligne
ENABLE INTERUPT
Résumé des adresses utilisées:
Port de sortie Adresse Port d'entrée Adresse
LED 00 SW 00
7-SEG (LSD) 01 BUSY 02
7-SEG (MSD) 03 UART_RX 03
UART TX 02
Vahid Meghdadi 12 ENSIL / ELT5