2022-2024
La figure suivante, montre des transactions simple d’écriture puis de lecture utilisant le protocole Avalon.
Transactions d’écriture:
write
de façon synchrone.waitrequest
.write
est haut et que le signal waitrequest
est bas.Transactions de lecture:
read
.waitrequest
.read
est haut et que le signal waitrequest
est
bas.Les signaux read
et write
peuvent passer à
1 dans le même cycle. Dans ce cas, nous avons des transactions de
lecture et d’écriture simultanées.
Si ni read
ni write
sont à 1, l’interface
est inactive (IDLE).
L’un des avantages de protocole Avalon (au moins de la séparation des
signaux read
et write
) est qu’il est trivial
d’associer à ses signaux de contrôle les signaux de contrôle d’un
registre (sans logique supplémentaire).
La figure suivante représente donc une cible (Agent) tout à fait valide.
Comme les signaux waitrequest
et read
sont
optionnels, nous pouvons tout simplement associer le signal
write
à l’entrée Enable
qui valide l’écriture
dans le registre.
Nous avons donc la correspondance suivante:
writedata
données entrant dans le registre,write
l’activation du registre (EN
in the
figure),readdata
données sortant du registre.Comme un registre peut être mis à jour en un cycle quand son signal
d’activation (EN
) est haut et sa sortie est valide à chaque
cycle, le signal waitrequest
peut être omis (ce qui revient
à le mettre systématiquement à 0). Le signal read
peut
aussi être omis, car les données sont systématiquement disponibles à la
sortie du registre (ce qui revient à le mettre systématiquement à 1)
Nous allons décrire un registre qui sera connecté aux sorties LEDs de la carte. Ce registre replacera le module PIO utilisé dans plateforme précédemment construite.
simple_mm_register.sv
.Ce fichier contiendra un module du même nom
(simple_mm_register
). Avoir le même nom pour le module
n’est pas une obligation, juste une bonne pratique.
L’interface du module devra être comme suit:
clk
: l’horloge principalereset_n
: un signal de remise à zéro
asynchrone, actif sur niveau basavs_write
: le signal write
du
protocole Avalonavs_writedata
: sur 32 bits, correspondant
au signal writedata
du protocole Avalonavs_readdata
: sur 32 bits, correspondant au
signal readdata
du protocole Avalonleds
: sur 10 bits, connecté au 10 bits de
poids faible du registremodule simple_mm_register(
input clk,
input reset_n,
input avs_write,
input [31:0] avs_writedata,
output [31:0] avs_readdata,
output [9:0] leds
);
// Ajoutez à partir de là la description RTL
endmodule
Bien que seulement 10 bits soient utilisés, nous vous demandons d’écrire le code d’un registre 32 bits. Au reset les 32 bits seront forcés à la valeur 0.
Si le signal write
est actif, la valeur du registre sera
chargée à partir de l’entrée writedata
.
La description du comportement devrait ressembler au code suivant:
logic[31:0] R;
always_ff @(posedge clk or negedge reset_n)
if(!reset_n)
0;
R <= 'else if(avs_write)
R <= avs_writedata;
La sortie vers l’interface Avalon (readdata
) doit être
connectée à la sortie du registre. Ceci permettra de relire la valeur
contenue dans le registre (par le processeur par exemple ou durant le
test).
Les 10 bits de poids faible seront aussi connectés à la sortie
leds
pour permettre de les visualiser sur la maquette.
Le code de connexion devrait ressembler au code suivant:
assign avs_readdata = R;
assign leds = R[9:0];
Platform Designer permet de générer automatiquement l’interconnect de notre système. Pour cela, il a besoin de connaitre le protocole utilisé par notre module ainsi que l’association entre les signaux de ce protocole est les signaux de l’interface de notre module RTL. Donc, pour pouvoir intégrer le module, il faut lui fournir des informations supplémentaires.
Dans le panneau IP Catalog
vous pouvez cliquer sur
new component
. Un assistant vous guidera pour ajouter votre
module RTL à la liste des modules disponibles.
Il faut choisir un nom ainsi qu’un nom d’affichage (name et display name . Le nom ne doit pas contenir d’espaces ou de caractères spéciaux.
Ensuite, vous pouvez associer vos sources RTL. En cliquant sur
Analyse Synthesis Files
l’outil analysera vos fichiers et
tentera de “deviner” le protocole utilisé par votre module en se basant
sur les noms des signaux de son interface. Au-delà du protocole Avalon,
Platform Designer supporte d’autres protocoles de communication
sur puce comme AXI par exemple. Aussi, il peut identifier les paramètres
d’un module pour les exposer dans l’interface de l’outil.
Après cette étape, vous aurez éventuellement des erreurs (par
exemple, il se peut que l’assistant associe la sortie leds
à un second signal readdata
). Nous corrigerons ces erreurs
dans les étapes suivantes.
Vous pouvez passer directement à l’onglet
Signals & Interface
. Supprimer le signal
leds
de l’interface avalon_slave
et ajouter
une interface de type conduit
. Ce type d’interface permet
d’indiquer que nous avons juste un ensemble d’entrées/sorties et nous ne
voulons pas que Platform Designer leur donne un sens
particulier.
Ajoutez le signal leds
comme une sortie sur 10 bits de
ce conduit (comme le montre la figure suivante).
Ici le nom du signal DOIT correspondre au nom du signal dans le module RTL
Une fois complété, vous pouvez appuyer sur Finish
. Le
nouveau composant devrait apparaitre dans la liste des composants
utilisable dans le panneau IP Catalog
.
Note
L’assistant de création de composant génère un script TCL (
xxxx_hw.tcl
) associé au composant. Platform Designer analysera systématiquement tous les fichiers avec suffixe_hw.tcl
dans le répertoire du projet (ainsi que dans une liste de répertoire correspondant à l’installation de l’outil et les préférences de l’utilisateur).Ce fichier contient la description du module (au sens Platform Designer), la liste des fichiers RTL, des paramètres et la description des différentes interfaces.
La façon de construire ces scripts est documentée dans la documentation officielle de Platform Designer. Il est donc possible de construire ce fichier de description directement, sans passer par l’assistant d’ajout de modules.
Supprimez le module PIO
précédemment connecté aux LEDs
et remplacez-le par le nouveau composant que vous venez d’ajouter à la
bibliothèque des composants. Le système mis à jour devrait ressembler à
la figure suivante:
Une fois fait, vous pouvez régénérer le RTL de la plateforme, refaire la synthèse dans Quartus II et tester le comportement sur la carte.
N’oubliez pas de modifier le programme en C de test pour prendre en compte vos modifications.
Nous allons faire évoluer notre module GPIO vers un contrôleur programmable d’entrées sorties.
Il devra permettre les fonctionnalités suivantes:
Le schéma suivant montre l’architecture globale de ce contrôleur.
Les entrées/sorties (pio_i
/pio_o
) seront
sur 32 bits et permettront de se connecter au monde exterieur (les leds
et interrupteurs par exemple) Ces signaux seront connectés en interne à
deux registres (data_in
/data_out
).
D’un point de vue logiciel, nous auront accès aux registres 32 bits suivants:
adresse | nom | taille | fonction |
---|---|---|---|
0 |
data |
32 bits | en écriture modifie le registre de sortie
(data_out ) |
en lecture récupère la valeur du registre d’entrée
(data_in ) |
|||
4 |
enable |
32 bits | chaque bit permet de dire si la pin correspondante est utilisée |
en lecture on peut lire son état | |||
8 |
irq mask |
32 bits | pour chaque pin (en entrée), si le bit correspondant est à
1 , une interruption peut être générée |
en lecture on peut lire son état | |||
12 |
irq pol |
32 bits | pour chaque pin (en entrée), si le bit correspondant est à
1 , l’interruption est générée sur niveau
bas |
en lecture on peut lire son état | |||
16 |
irq ack |
32 bits | une écriture (quelle que soit la valeur) remet à zéro la sortie d’interruption |
en lecture aucun effet |
Note
- Les adresses sont des adresses d’octets
- L’état initial (au reset) de tous ces registres est
0
En partant du module précédent, créer un nouveau module
prog_gpio
. Vous pouvez aussi copier, puis, modifier le
fichier de description (_hw.tcl
) pour qu’il soit reconnu
par Platform Designer.
Comme ici nous avons besoin d’adresser plusieurs registres, il faut
ajouter une entrée pour l’adresse venant du bus Avalon. Si on respecte
la convention précédente, on peut la nommer avs_address
.
Cette nouvelle entrée doit être suffisamment grande pour représenter la
plage d’adresses du contrôleur (de 0 à 16 inclue).
Ajouter aussi les entrées/sorties suivantes:
pio_i
/pio_o
entrées/sorties 32 bits qui
seront connectées à des conduits,irq
une sortie d’interruption sur 1 bitNous voulons des adresses d’octets, vérifiez que dans la configuration de l’interface esclave, les adresses sont bien des adresses d’octets (Address Units : SYMBOLS et Bits per symbol : 8). Cette information est utilisée à la génération de l’interconnect pour ne transmettre que les bits d’adresse nécessaires.
Note nous aurions pu ici numéroter les registres 0,1,2,3 et 4 et indiquer que nous voulons une adresse mot (WORD). Dans ce cas, la correspondance avec les adresses vues par le logiciel ne seraient pas directes.
Notez qu’il est possible de donner explicitement la plage d’adresse
utilisée par le périphérique (Explicite Adress Span
),
sinon, elle est déduite de la taille du bus
adresse.
pio_*
.Ici, vous pouvez prévoir deux conduits séparés ou un conduit unique permettant d’exporter les deux signaux. Attention aussi à la direction des signaux.
irq
.Vérifiez que le fichier source (synthesis File) correspond bien au nouveau module.
Comme nous faisons un simple registre et que nous n’utilisons pas le
signal waitrequest
, les transactions Avalon devrait
ressembler à ce qui est montré sur la figure suivante:
write
passe à
1.read
, la donnée peut être présentée
combinatoirement pour être prise en compte au front
suivant.Ceci est autorisé par le protocole Avalon, mais, pour un système plus complexe, dans lequel cette augmentation du chemin combinatoire influe négativement sur la fréquence de fonctionnement, nous pouvons répondre en deux cycles en utilisant
waitrequest
. Ainsi, la sortie de notre module viendrait directement d’un registre.
NOTE Bien que nous ne l’utilisions pas, le signal
read
est émis par l’initiateur. Ici il faut comprendre que quand l’adresse change, nous devons renvoyer la donnée dans le même cycle (i.e. combinatoirement).
Écrivez le code des différents registres et du décodage d’adresse permettant d’y accéder par l’interface Avalon.
Connectez les registres de données aux entrées/sorties
pio_*
.
enable
Tel que décrit dans la spécification, seules les pins pour lesquels
nous avons écrit 1
dans le registre enable
doivent être pris en compte/modifiés.
Ça commence à se complexifier, pour identifier d’éventuels problèmes, nous vous proposons d’utiliser l’environnement de simulation disponible dans l’archive suivante.
Décompressez là dans le répertoire où se trouve votre fichier SystemVerilog.
Elle contient un module de test qui peut générer des transactions
Avalon ainsi qu’un testbench (testbench.sv
) avec quelques
exemples de son utilisation.
Le fichier filelist
content la liste des fichiers qui
seront compilés.
Le makefile permet de:
make
),make sim
)Lancez donc la simulation et vérifiez que le comportement de votre module est correct (modifiez éventuellement le testbench pour tester progressivement votre module).
Le schéma suivant montre comment un signal d’interruption interne au module peut être généré, en fonction de l’activation, la polarité et le masque, à partir des entrées.
Ce signal doit permettre de générer l’interruption qui remonte vers
le processeur. S’il passe à 1, l’information doit être conservée et
n’est remise à zéro que si on écrit à l’adresse du registre
irq ack
.
Pour tester sur la maquette, écrivez un programme de test en C permettant de vérifier que tout fonctionne.
Pour tester les interruptions, la fonction RegisterISR
permet d’associer un handler à l’une des entrées d’interruption
su processeur. La fonction irq_enable
autorise globalement
les interruptions.
Attention si vous voulez utiliser la fonction
printf
vous devrez augmenter la taille de la mémoire embarquée (64K par exemple). La fonctionsimple_printf
nécessite moins de mémoire, mais, elle n’implémente pas tous les formats.
Si vous avez tout testé, vous pouvez ajouter les fonctionnalités suivantes: