TP5 : Analyse du fonctionnement d’un processeur MIPS sur FPGA Travail Préparatoire : Faire l’exercice 1 Le but de ce TP est de comprendre le fonctionnement d’un processeur (ici le processeur MIPS vu en TD12) et plus précisément : – comment fonctionne sa mécanique interne (PC et PO), – comment le jeu d’instructions permet de le contrôler, – comment il s’intégre dans un système. Ex. 1 : Travail préparatoire En plus de la mémoire déjà intégrée en TD, l’environnement du processeur est enrichi avec des périphériques (LED, interrupteurs, afficheurs 7 segments. . .) pour nous permettre de communiquer avec lui. Le processeur peut accéder à ses périphériques en utilisant les instructions d’accès mémoire classique (lw, sw) dans une plage d’adresses dédiée aux périphériques. Ainsi pour les adresses entre 0x4000 et 0x4FFF, le module périphérique est activé et l’accès à la mémoire ne sera pas pris en compte. La table d’allocation des périphériques donnée en annexe, permet de savoir les adresses à utiliser pour communiquer avec un périphérique et le type d’accès pouvant y être réalisé. L’environnement du processeur utilisé dans ce TP est représenté ci-dessous. Il contient un bloc DCM (Digital Clock Manager) utilisé pour fournir un reset et une horloge à 25MHz 1 au reste du circuit. La partie opérative du processeur et le jeu d’instruction sont rappelés en annexe. 1. Le processeur fournit ne peut pas fonctionner à 50MHz Question 1 Ecrire en langage d’assemblage un programme recopiant en boucle sur les LED la valeur présente sur les interrupteurs. Puis, traduire ce programme en langage machine (en hexadécimal évidemment). Le programme débute à l’adresse 0. Un timer est un composant matériel donnant au processeur des informations sur le temps écoulé. Le timer fourni s’active à une fréquence d’environ 1,5Hz par une impulsion au niveau bas dont la durée est fixée au nombre de cycles pris par le processeur pour exécuter un lw, un beq et un j. Afin de détecter l’activation de ce timer, le processeur doit faire une boucle de scrutation. Pour ne manquer aucune impulsion du timer, il faut que la durée de l’impulsion du timer soit exactement égale à celle de la boucle de scrutation. Question 2 Avec les 3 instructions proposées, écrire une boucle de scrutation sautant vers fonction lorsque le timer s’active. Écrire ensuite la fonction correspondante pour que le programme fasse clignoter toutes les LED. Au cours du TP, on vous demandera d’exécuter un programme permettant d’afficher un chenillard sur les LED de la carte. C’est-à-dire que les LED allumées se décalent d’une position à chaque activation du timer. L’algorithme ci-contre permet de réaliser un chenillard sur un afficheur à 8 LED : Question 3 motif ← 0x03; while true do while timer = 1 do timer ← lire_timer(); end while af f icher(motif ); motif ← motif << 1; if (motif and 0x100) 6= 0 then motif ← motif and 0xFF; motif ← motif or 0x01; end if end while Écrire un programme assembleur réalisant cet algorithme. Ex. 2 : Simulation comportementale du processeur Récupérez les sources fournies sur l’Ensiwiki et décompressez les où bon vous semble. Ces dernières contiennent un Makefile réalisant à votre place toutes les actions sur l’environnement ISE de Xilinx. Pour permettre à votre processeur d’exécuter un programme, il suffit d’écrire ce dernier en langage binaire MIPS dans un fichier ayant une extension .mem dans le répertoire src. Le fichier pgcd.mem vous est donné à titre d’exemple. Il correspond au programme assembleur pgcd.s vu en TD. Question 1 Ouvrez les deux fichiers pgcd et vérifiez bien que le fichier .mem est la traduction fidèle du fichier .s. Dans un terminal, tapez « source /Xilinx/env-12.2.sh » pour positionner les variables d’environnement nécessaires aux outils Xilinx, puis déplacez-vous dans le répertoire du projet. Pour lancer une simulation comportementale de votre processeur et de son environnement sur un fichier <nom_du_prog>.mem, il suffit de taper : make run_simu TP5=<nom_du_prog> Cette commande va mettre à jour le contenu de la mémoire du système simulé, puis va recompiler le testbench (src/tb_MMIPS_simple.vhd) en prenant en compte les fichiers déjà compilés pour lesquels nous n’avons pas fourni les sources 2 . Finalement, la commande lance le simulateur de ISE. Question 2 Lancez une simulation sur le programme pgcd fourni. Par défaut, le simulateur ne fournit que les entrées-sorties du module simulé. Quelles informations peut-on obtenir du chronogramme obtenu ? Pour obtenir plus d’informations, il faut ajouter des signaux à votre simulation. Pour ça, il faut sélectionner dans l’onglet « Instances and Processes » le composant contenant le signal recherché. Tous les signaux de ce composant apparaissent dans l’onglet « Objects ». Il suffit alors de faire glisser le signal désiré vers le chronogramme. Pour conserver un chronogramme lisible, pensez à regrouper les signaux par thématique, à utiliser des séparateurs (clic droit dans le chronogramme et « New Divider ») et à demander la bonne interprétation des vecteurs de bits (clic droit sur le signal dans le chronogramme, puis prendre l’interprétation voulue dans le champs Radix). Question 3 Conservez uniquement les signaux utiles de la simulation initiale. Ajoutez ensuite les signaux suivants : – clk25 et rst du composant C_MMIPS_simple, pour avoir les signaux de synchronisation utilisé par le processeur ; – les signaux du composant C_RAM, pour savoir ce qui se passe au niveau de la mémoire (attention les 2 bits d’adresses de poids faible ne sont pas utilisés par le module mémoire) ; – le groupement de signaux cmd du composant C_PROC pour voir les commandes données par la PC à la PO ; – le signal d’état de la PC (state_q) et les signaux vous paraissant pertinent dans la PO. Le banc de registre est appelé rf_q. Enfin, relancez la simulation. Question 4 Trouvez dans la simulation l’instant où le processeur commence à travailler, puis vérifier que l’état du registre PC évolue bien comme prévu par les instructions du programme (en s’aidant des registres PC et IR). Vérifier que le contenu des registres évolue aussi comme désiré. Trouvez la première instruction subu et suivre sur les signaux de la PO et de la RAM l’impact des ordres donnés dans la PC dans l’état avant le fetch, dans l’état fetch, dans l’état decode et dans l’état d’exécution. Pour gagner du temps et éviter de refaire ces fichiers de configuration de vos simulations, il est possible de les sauvegarder. Le répertoire wcfg est là à cet usage. Vous y retrouverez notre fichier de configuration pour le pgcd. Si vous voulez lancer automatiquement vos simulations avec vos fichiers de configuration, sauvegardez dans ce répertoire votre fichier de configuration et remplacer TP5 par WCFG dans l’appel à make. Ex : make run_simu WCFG=pgcd. Question 5 Ecrire dans un fichier src/copy.mem le programme en hexadécimal trouvé à la question 1 du travail préparatoire en respectant la syntaxe des fichiers mem, puis lancer une simulation pour vérifier votre travail : make run_simu TP5=copy 2. Vu que ca sera votre boulot de les écrire dans le module CEP. Ex. 3 : Exécution du programme sur la carte FPGA Pour faire les exécutions sur carte, il suffit de taper la commande : make run_FPGA TP5=<nom du fichier mem> Le makefile modifiera alors le contenu du fichier de configuration du FPGA et programmera ce dernier 3 . En cas d’erreur, le makefile fourni ne donne aucune indication. Afin d’avoir plus d’informations, il suffit de mettre dans la ligne de commande la variable VERB à 1 : make run_FPGA TP5=<nom du fichier mem> VERB=1 Question 1 Implantez sur la carte le circuit avec les programmes pgcd et copy des questions précédentes et vérifiez leur bon fonctionnement. Pensez à initialiser avec le bouton poussoir « user reset » le circuit pour charger votre programme. Comme il est fastidieux de traduire à la main un programme du langage d’assemblage au langage machine, on vous fournit un assembleur réalisant cette tâche pour vous à condition de respecter la syntaxe introduite dans le fichier pgcd.s. Question 2 Recopiez les solutions trouvées durant les questions 2 et 3 de l’exercice préparatoire dans les fichiers src/cligno.s et src/chenillard.s. Vérifiez directement sur carte leur fonctionnement. Si ça ne marche pas comme vous l’espériez, vérifiez en simulation que votre programme est correct. Question 3 Ouvrez le fichier mystere.mem fourni. A votre avis, que fait-il ? Implantez le sur la carte et vérifiez. Question 4 Il vous reste du temps. Pourquoi ne pas s’amuser avec vos propres programmes ? Quelques idées en vrac : – Coder la multiplication vue au TD10. – Coder le LFSR vu en TP1. – Ecrire un programme PGCD "interactif". Ce programme peut lire A puis B sur les interrupteurs, en utilisant un bouton poussoir pour valider une valeur, puis affiche le résultat soit sur les LED, soit sur l’afficheur 7 segments. 3. à condition qu’il soit correctement branché bien sur ! ! Ex. 4 : Annexes Table des périphériques : Périphérique LEDs Accès écriture Adresse 0x4000 afficheurs 7 segments interrupteurs écriture 0x4004 lecture 0x4008 boutons poussoir timer lecture lecture 0x400C 0x4010 Action Affiche l’octet de poids faible écrit par le processeur sur les LED Affiche en hexa les 2 octets de poids faible récupère la valeur sur les 8 interrupteurs et met des 0 sur les bits de poids forts récupère la valeur sur les 3 boutons poussoirs récupère la valeur du timer (actif à 0) Partie Opérative du processeur : Jeux d’instructions : Le MIPS a un jeu d’instructions à 3 opérandes et ses instructions sont réparties dans 3 formats différents : 31 2625 2120 1615 Format R : opcode RS RT Format I : opcode RS RT Format J : opcode 1110 RD 65 SH IMM16 IMM26 0 FUNC Dans ce TP, nous considérerons les instructions suivantes : Nmémonic/ syntaxe addiu $rt, $rs, imm Format I Opcode/ Func 0x9 addu $rd, $rs, $rt and $rd, $rs, $rt andi $rt, $rs, imm beq $rs, $rt, label R R I I 0x0/0x21 0x0/0x24 0xC 0x4 bne $rs, $rt, label I 0x5 j label lui $rt, imm lw $rt, imm($rs) J I I 0x2 0xF 0x23 or $rd, $rs, $rt ori $rt, $rs, imm slt $rd, $rs, $rt slti $rt, $rs, imm R I R I 0x0/0x25 0xD 0x0/0x2A 0xA sll $rd, $rt, sh srl $rd, $rt, sh subu $rd, $rs, $rt R R R 0x0/0x0 0x0/0x2 0x0/0x23 sw $rt, imm($rs) I 0x2B xori $rt, $rs, imm I 0xE 1. 2. 3. 4. SignExtImm = IM M 1616 15 |IM M 16 ZeroExtImm = 016 |IM M 16 2 BranchAddr = IM M 1614 15 |IM M 16|0 JumpAddr = (P C + 4)31···28 |IM M 26|02 Opération R[rt]=R[rs]+SignExtImm 1 R[rd]=R[rs]+R[rt] R[rd]=R[rs] & R[rt] R[rt]=R[rs] & ZeroExtImm 2 if(R[rs]== R[rt]) PC=PC+4+BranchAddr 3 if(R[rs] != R[rt]) PC=PC+4+BranchAddr 3 PC = JumpAddr 4 R[rt] = IM M 16|016 R[rt] = mem[R[rs]+SignExtImm 1 ] R[rd] = R[rs] | R[rt] R[rt] = R[rs] | ZeroExtImm 2 R[rd] = (R[rs] < R[rt]) ? 1 : 0 R[rt] = (R[rs] < SignExtImm 1 ) ? 1 : 0 R[rd] = R[rt] « SH R[rd] = R[rt] » SH R[rd]=R[rs]-R[rt] mem[R[rs]+SignExtImm 1 ] = R[rt] R[rt] = R[rs] XOR ZeroExtImm Nom Complet addition non signée avec un immédiat addition non signée entre registres ET bit à bit entre registres ET bit à bit avec un immédiat Branchement si égalité Branchement si inégalité Saut inconditionnel Chargement immédiat haut chargement registre OU bit à bit registre à registre OU bit à bit avec immédiat Set Less Than registre à registre Set Less Than avec immédiat décalage à gauche décalage logique à droite soustraction non signée entre registres stockage registre OU exclusif bit à bit avec un immédiat