Au cours de ce TP, vous allez :
Conseils :
Bien que le sujet du TP concerne davantage le coeur d’exécution du microprocesseur que son environnement d’utilisation, nous présentons ici le système complet implanté sur la maquette de test. Le système proposé se compose :
Quelques autres périphériques ne sont pas représentés sur le schéma 1:
le générateur d’horloge sclk et la génération de la remise à zéro (reset_n),

Fig. 1: Schéma global du processeur et de ses périphériques
|
Nom |
Description |
Entrée ou Sortie (vue du processeur) |
|
sclk |
Horloge générale du processeur |
Entrée |
|
reset_n |
Remise à zéro, asynchrone et active au niveau bas |
Entrée |
|
BZ |
Port pouvant être positionné à ’1’ ou ’0’ |
Sortie |
|
ram_addr[7..0] |
Adresse de la RAM |
Sorties |
|
accu[7..0] |
Sortie de l’ACCUmulateur. La valeur de ce bus sera mémorisée dans la RAM à l’adresse ram_addr si ram_write vaut 1 (accès à la RAM en écriture) |
Sorties |
|
ram_data[7..0] |
Données issues de la RAM, à l’adresse ram_addr (accès à la RAM en lecture) |
Entrées |
|
ram_write |
Signal demandant l'écriture à la mémoire |
Sortie |
Tab. 1 : Entrées-sorties du processeur
Remarque : il y a quelques différences par rapport à la leçon sur le nanoprocesseur, portant sur les noms des signaux :
Les caractéristiques principales de notre processeur sont les suivantes :
L’adresse de l’instruction courante en RAM est notée PC ("Program Counter").
Z si ce résultat est nul
C si une retenue sortante existe (dans le cas des opérations arithmétiques et de rotation)
Le tableau 2 résume le jeu d’instruction du microprocesseur.
|
Mnémonique |
Instruction |
Effet sur les données |
Effet sur le PC |
code (binaire 8bits) |
code (décimal) |
|
NOP |
No OP: pas d'opération |
A inchangé |
PC ⇐ PC + 2 |
0 |
0 |
|
XOR |
XOR bit à bit |
A ⇐ A xor (AD) |
PC ⇐ PC + 2 |
1 |
1 |
|
AND |
AND bit à bit |
A ⇐ A and (AD) |
PC ⇐ PC + 2 |
10 |
2 |
|
OR |
OR bit à bit |
A ⇐ A or (AD) |
PC ⇐ PC + 2 |
11 |
3 |
|
ADD |
ADDition sans retenue |
A ⇐ A + (AD) |
PC ⇐ PC + 2 |
100 |
4 |
|
ADC |
ADDition avec retenue |
A ⇐ A + (AD) + C |
PC ⇐ PC + 2 |
101 |
5 |
|
SUB |
Soustraction sans retenue |
A ⇐ A - (AD) |
PC ⇐ PC + 2 |
110 |
6 |
|
SBC |
Soustraction avec retenue |
A ⇐ A - (AD) - C |
PC ⇐ PC + 2 |
111 |
7 |
|
ROL |
Rotation à gauche |
A ⇐ A[6..0]A[7] |
PC ⇐ PC + 2 |
1000 |
8 |
|
ROR |
Rotation à droite |
A ⇐ A[0]A[7..1] |
PC ⇐ PC + 2 |
1001 |
9 |
|
LDA |
LoaD Acc: charger l'accu. depuis la mémoire |
A ⇐ (AD) |
PC ⇐ PC + 2 |
1010 |
10 |
|
STA |
STore Acc: ranger l'accu. dans la mémoire |
(AD) ⇐ A A inchangé |
PC ⇐ PC + 2 |
1011 |
11 |
|
OUT |
OUTput: sortie vers BZ |
BZ ⇐ (AD)(0) A inchangé |
PC ⇐ PC + 2 |
1100 |
12 |
|
JMP |
JuMP: saut inconditionnel |
A inchangé |
PC ⇐ AD |
1101 |
13 |
|
JNC |
Sauter si pas de retenue |
A inchangé |
PC ⇐ AD si C=0, PC + 2 sinon |
1110 |
14 |
|
JNZ |
Sauter si non nul |
A inchangé |
PC ⇐ AD si Z=0, PC + 2 sinon |
1111 |
15 |
Tab. 2 : Jeu d’instructions du processeur
Nous vous conseillons de vous familiariser avec le jeu d’instructions et avec les codes associés au processeur en étudiant le programme test.s qui servira de jeu de test de simulation pour le processeur.
Le coeur, comme illustré à la figure 2 est composé de quelques registres, d’une unité de calcul, d’un compteur de programme et d’une unité de contrôle. Tous les registres du microprocesseur sont pilotés par l’horloge sclk (comme horloge système, system clock en anglais) et remis à zéro par l’état bas du signal reset_n.

Fig. 2 : Intérieur du processeur
Le compteur de programme (8 bits) sert à stocker l’adresse de l’instruction courante.
Son mode "standard" de fonctionnement est de s’auto-incrémenter lors des phases IF et AF pour aller chercher en séquence les différents octets des instructions à exécuter. L’incrémentation se fait au moyen du signal inc_PC.
Le signal load_PC est prioritaire par rapport au signal inc_PC.
Cette unité contient la machine à états permettant de gérer les unités du processeur. Elle est donc en charge de séquencer les différents cycles d’une instruction (chargement d’une instruction, chargement de l’opérande, puis exécution) et de générer les signaux de contrôle des différents blocs, sans oublier, bien sûr, le signal ram_write de validation d’écriture en mémoire.
Ce bloc regroupe en fait les quatre registres suivants :
Tous ces registres fonctionnent de la même façon : la valeur présente à l’entrée du registre xxx est enregistrée sur le front montant de l’horloge, si le signal de validation (load_xxx) est à 1.
Ce multiplexeur permet de choisir qui, de l’adresse "pointeur programme" PC ou de l’adresse de données AD, doit être envoyé sur le bus d’adresse ram_addr de la mémoire. Le choix de l’un ou de l’autre dépend de l’état courant du microprocesseur. De manière générale, lorsque le microprocesseur va chercher une instruction en mémoire, PC est sélectionné, et lorsque le microprocesseur va traiter une donnée en mémoire, AD est sélectionné. Le choix est contrôlé par le signal sel_adr. La table de vérité de cette fonction est :
|
sel_adr |
ram_addr |
|
0 |
PC |
|
1 |
AD |
Ce bloc prend comme opérandes le contenu de l’accumulateur et le bus de donnée de la RAM, et effectue l’opération indiquée par I. Selon le type d’opération:
une retenue entrante peut éventuellement être prise en compte (C)
Z qui indique si le résultat de l’opération effectuée est nul
C qui indique si une éventuelle retenue sortante est générée.
Le graphe de la machine à états est rappelé dans la figure 3 (toutes les sorties ne sont pas indiquées).

Fig. 3 : Graphe d’état du contrôleur CTR
Pendant le premier cycle (IF pour "instruction fetch") deux opérations sont réalisées simultanément.
Pendant le deuxième cycle (AF pour "address fetch") deux opérations sont réalisées simultanément.
Remarque : pour simplifier, inc_PC est systématiquement mis à 1. En cas de saut, le bloc PC considérera le signal load_PC comme prioritaire.
Le troisième cycle correspond à l’exécution proprement dite de l’instruction. L’opération réalisée dépend de l’instruction stockée dans le registre d’instruction I. Dans tous les cas, le contenu de la mémoire à l’adresse déterminée par le pointeur stocké dans AD au cycle précédent, est lu de la mémoire et placé sur le bus d’entrée ram_data (sel_adr = 1).
Le chronograme 4 permet de résumer les différents cas.

Fig. 4 : Chronogrammes des cycles du processeur
Vous venez de lire les spécifications d’un petit micro-processeur, appelé ici "Nanoprocesseur". Comme tout projet bien spécifié, il a été possible de partager le travail de conception entre différentes équipes qui ont pu travailler de façon indépendante à la conception des différents modules. Ainsi, la carte de circuit imprimé contenant les différents composants est prête, un programme de test est déjà écrit (en assembleur), le schéma global est terminé ainsi que les sous-blocs ALU, REG, MEMOIRE.
Hélas, lors de la phase finale d’intégration, le chef de projet réalise, tardivement il est vrai, que les deux équipes chargées des blocs PC et CTR ont fait un travail de tellement piètre qualité, qu’il juge préférable de les reconcevoir complètement.
Il se tourne vers vous et vous confie la tâche de concevoir ces deux blocs. A vous de jouer...
Puis traduisez ce schéma en System-vérilog, et complétez le fichier pc.sv (en respectant les noms déjà définis).
Nous vous recommandons d'utiliser la syntaxe case déjà employée dans les TP précédents.
Vérifiez le fonctionnement de PC en tapant la commande make simu_pc

Fig. 5 : Chronogrammes du PC
Si le simulateur vous indique une erreur, c’est que votre code Verilog comporte une erreur de syntaxe. Fermez le simulateur, corrigez le code source, et relancez la simulation.
Si les chronogrammes ne sont pas assez grands, vous pouvez zoomer à l’aide des icônes
. Vous pouvez aussi zoomer sur une partie précise avec l’icône
.

Fig. 6 : Bloc CTR
De nouveau, il faut commencer par lire les spécifications pour en extraire le comportement de la machine à états, puis en déduire une architecture (en faisant un schéma), puis le coder en system-vérilog. Faites attention, la machine à état est un peu spéciale:
Complétez le fichier ctr.sv. Pour cela nous vous conseillons :
D'utiliser les méthodes proposées dans le TP séquentiel : Codage des machines à états,
De traiter séparément chaque signal de sortie (un always@(*) par signal de sortie) en utilisant ce complément de syntaxe sur les expressions booléennes en System Verilog.
Vous pouvez vérifier le bon fonctionnement de CTR en simulant directement le système complet (nanoprocesseur + mémoire) avec un programme de test. Ce programme, nommé verif_globale permet de tester l’exécution de toutes les instructions du nanoprocesseur.
Pour cela, vous avez deux options : la simulation (virtuelle) et l’exécution pas-à-pas sur la maquette.
Lancez la simulation de l’ensemble avec le programme de verification globale : make simu.
Les chronogrammes doivent ressembler à ceux de la figure 7.

Fig. 7 : Chronogrammes de la simulation du programme de test
Ces chronogrammes sont disponibles en version plus grande en cliquant ici.
Nous avons inclus dans ces chronogrammes les signaux nécessaires à la compréhension de ce qui se passe dans le processeur :
Pour suivre les chronogrammes, il est conseillé de se rapporter à la page du programme de test, et de suivre le déroulement du programme cycle par cycle.
Il est également possible de comparer vos chronogrammes à des chronogrammes de référence. Pour cela :
Dans Tools -> Waveform Compare -> Comparison Wizard,
Par exemple, ces chronogrammes mettent en évidence un dysfonctionnement
Lorsque la simulation est correcte, vous pouvez suivre le déroulement du programme "en vrai", sur la maquette de test.
Synthétisez le code correspondant : make verif
Si la synthèse s’est terminée sans erreur, programmez le FPGA : make prg_verif
Il y a deux modes d’exécution de ce programme de test :
pas-à-pas, si l’interrupteur SW0 est à 1 (en position vers le haut) C’est le bouton KEY3 qui donne alors l’horloge (un appui = un coup d’horloge)
automatique, si l’interrupteur SW0 est à 0 (en position vers le bas) L’horloge est alors générée automatiquement, et le programme s’exécute à vitesse normale (25MHz).
Vous pouvez ré-initialiser votre microprocesseur en appuyant sur le bouton KEY0.
Vous pouvez suivre l’état de certains signaux du processeur :
sur les deux afficheurs 7 segments de droite (HEX1,HEX0) : ram_addr[7:0]
sur les deux afficheurs 7 segments de gauche (HEX7,HEX6) : accu[7:0]
Suivez l’exécution du programme pas à pas, en vérifiant chaque étape à l’aide de la page du programme de test .
Pour finir le test de votre processeur, nous vous proposons de lui faire jouer un petit morceau de musique. Pour cela:
Suivant le temps disponible, traitez dans l'ordre indiqué les questions suivantes: