Leçon 10 : Immédiat-condition

Dans cette leçon, vous allez enrichir votre processeur avec une instruction d’entrée/sortie qui vous permettra de contrôler l’avancement de votre CO en mode attente active. Une instruction supplémentaire vous permettra de manipuler les adressages immédiats : une valeur est donnée explicitement dans l’instruction elle-même comme source d’un transfert de registre, très utile pour manipuler des constantes. Enfin les transferts de registres seront sous condition calculée par l’ALU. Cette façon originale de conditionner un transfert de registre procure toute la simplicité recherchée pour le processeur S3 dans la réalisation de rupture de séquence pour lesquelles il n’existera pas d’instruction spécifique (pas de JUMP, Branch, etc). Le jeu d’instruction est ultra réduit !

Un processeur doit être capable de dérouler un programme. Un programme est composé d’instructions de calcul exécutées les unes après les autres. Le processeur S3 implémente une ALU et la FSM déroule le programme en séquence. Il doit aussi être capable de réaliser des portions de code sous condition comme un IF THEN ELSE. Enfin il faut pouvoir répéter plusieurs fois le même code sous condition de type TANT QUE. Le processeur S3 doit être compléter pour devenir programmable.

Connaissances requises

Adressage immédiat

Objectif

Dans cette leçon, vous allez enrichir votre processeur avec une instruction d’entrée/sortie qui vous permettra de contrôler l’avancement de votre CO en mode attente active. Une instruction supplémentaire vous permettra de manipuler les adressages immédiats : une valeur est donnée explicitement dans l’instruction elle-même comme source d’un transfert de registre, très utile pour manipuler des constantes. Enfin les transferts de registres seront sous condition calculée par l’ALU. Cette façon originale de conditionner un transfert de registre procure toute la simplicité recherchée pour le processeur S3 dans la réalisation de rupture de séquence pour lesquelles il n’existera pas d’instruction spécifique (pas de JUMP, Branch, etc). Le jeu d’instruction est ultra réduit !

Le projet

Trois instructions à développer. L’instruction PAUSE permet de bloquer l’avancement de l’automate de votre FSM. L’instruction MVI permet de copier dans un registre destination une valeur constante. Les instructions MVZ , MVNZ, MIZ et MINZ réaliseront des transferts de registres ou non suivant la valeur présente à cet instant dans le registre Rdest. Vous verrez que cet enrichissement du jeu d’instructions n’entraîne que des modifications mineures dans l’architecture du S3.

L’instruction PAUSE

Afin de pouvoir saisir, ou afficher deux nombres successifs, il faut introduire un mécanisme matériel fonctionnant comme le « retour chariot/return » d’un clavier. Sur votre carte, vous disposez de 4 ou 5 boutons poussoir, l’un d’entre eux peut jouer ce rôle. Après le positionnement des switches ou la lecture des led ou de l’afficheur 7 segments, une pression sur le bouton devrait permettre la reprise du programme. L’instruction PAUSE associée fonctionne comme une attente active : le processeur S3 reste sur cette instruction à chaque top d’horloge jusqu’à ce que le bouton déclenche un signal de continuation. Alors le processeur passe à l’instruction suivante. Vous choisirez xF comme code opération de l’instruction PAUSE. Toutes les instructions xFzzz sont des PAUSE.

Je vous propose d’utiliser le bouton Est, le plus à droite. Ce que vous devez détecter c’est le changement d’état de votre bouton : le passage de 0 à 1. Cette détection ne doit être active que pendant 1 cycle alors que la pression manuelle du bouton va activer un signal pendant quelques dixièmes de seconde, la vitesse de votre doigt ! La détection du changement d’état du bouton correspond à produire une impulsion. Pour 2 instructions PAUSE successives, il faut bien appuyer deux fois sur le bouton. Je vous propose de réaliser un petit circuit qui assure la génération de cette impulsion à l’aide de six bascules D. D’abord il faut éviter les rebonds des boutons. Un bouton, c’est de la mécanique et le signal produit n’est pas toujours propre.

En première ligne, vous placez 3 bascules D en série à une fréquence de 190 Hz afin de valider la pression sur le bouton pour un échantillonnage de 15 ms environ. Si le signal est à 1 pendant ces 15ms il est validé en sortie par un AND3. Les trois bascules en série fonctionnent comme un registre à décalage. Pour les trois autres bascules en série, à chaque top d’horloge clk, celle du processeur cette fois, les trois bascules mémorisent les trois dernières valeurs du signal échantillonné. Si ces trois valeurs sont 110, il y a une nouvelle pression sur le bouton vous générez alors l’impulsion. Toute autre combinaison sur les 3 dernières valeurs du signal bouton ne relâchera pas l’instruction PAUSE.

Ouvrez le projet TP10 copié en fin de leçon 9. Ajoutez un schematic de nom pulse. Placez-y 6 bascules D sur deux lignes de 3. Ajoutez deux fils clk et clk190 avec leur I/O Marker. Clk190 est relié au C des 3 premières bascules, clk aux 3 dernières. Un fil inp et son I/O Marker est relié à l’entrée D de la première bascule. Les sorties Q de la première et deuxième bascules se connectent à l’entrée D des bascules suivantes. Les trois sorties Q sont reliées par un AND3. La sortie de ce AND3 devient l’entrée de la 4ième bascule.

Les 3 dernières bascules sont en cascade elles aussi. Ici la sortie de la dernière passe par un INV avant d’être reliée au AND3 afin de détecter la suite b110. Enfin la sortie de ce AND3 est nommée outp. Ajoutez-y un I/O Marker. Nommez vos fils pour la simulation : q0, q1, q2, q3, q4, q5 et o1 pour la sortie un premier AND3. Voici le schéma pour vous guider.

Figure 114 Schéma de votre pulse

En simulant ce composant, vous obtiendrez le chronogramme suivant qui garantit effectivement la création du signal d’impulsion. Pour simuler vous pouvez créer un pulse_tb avec un stimuli pour clk, clk190 et pour le bouton à 1 après un certain temps. Vous pouvez aussi lancer Isim sur pulse.sch, dans ce cas il faut utiliser Force constant et Force clock pour stimuler vos entrées. Pour réduire la durée de simulation, j’ai choisi une clk190 deux fois plus lente que clk. Vous pouvez créer le symbole correspondant.

Figure 115 Simulation de pulse

Il faut ensuite modifier la FSM pour prendre en compte cette nouvelle instruction. L’automate va devoir ajouter un nœud sur lequel la FSM bouclera tant que l’impulsion ne sera pas détectée.

Figure 116 Idée de l'automate de la FSM

    1. Ajoutez un port en entrée que je vous propose de nommer continue.
    1. Ajoutez une valeur pause dans la définition de type state_type.
    1. Modifiez le process Next_node afin de prendre en compte le nouvel état pause de votre automate. Si une instruction pause est détecté lors du décodage, votre automate se positionnera dans l’état pause. Il quittera cet état lorsque le signal continue sera actif. Ce signal sera produit par votre pulse. Il faut ajouter continue dans la liste de sensibilité car il apparaît dans un test de votre process.
    1. Il faut encore modifier le process Next_Output puisque l’instruction PAUSE ne doit pas déclencher le passage à l’instruction suivante tant que le signal continue n’est pas actif, on décodera l’instruction PAUSE tant que le registre CO n’a pas été incrémenté. Continue apparaissant dans un prédicat, il faut aussi le placer dans la liste de sensibilité de ce process.

Vous pouvez sauvegarder et créer le symbole de fsm.vhd. Venez le mettre à jour dans S3. Il vous suffit maintenant de placez un fil sur la sortie continue, nommez le continue et placez-y un I/O Marker. Sauvez et recréez le symbole S3. Le reste se passe dans toplevel.

Figure 117 Connexion de pulse sur la nouvelle FSM

Mettez à jour l'instance de S3 dans toplevel. Placez sous S3 une instance de pulse. Connectez la sortie du pulse sur l’entrée continue de votre S3. Il vous reste à connecter l’entrée clk au fil de sortie du BUFG. Récupérez la sortie clk190 du clkdiv (ou testclk) pour alimenter l'entrée clk190 du pulse. Enfin placez un fil et son I/O Marker sur l’entrée inp de pulse, nommez-le btn0.

Figure 119 Toplevel avec un bouton

Pareil pour toplevel_tb, il faut ajouter un port btn0 dans la déclaration du composant toplevel, un signal interne btn0, dans l’instanciation du composant il faut prendre en compte le port btn0 et enfin il faut stimuler ce signal interne. Voici un exemple de stimulation de signal en mode simulation avec la commande after.

Il faut ajouter le bouton btn0 dans le fichier S3.ucf. Il faut aussi modifier l’assembleur S3asm.bat en ajoutant le code de PAUSE.

Vous pouvez maintenant réaliser un programme qui ajoute deux nombres de 8 bits saisis successivement sur les switches et affiche la valeur sur l’afficheur. Voici le programme assembleur et son code binaire généré. A vous de générer insmem avec ce code. Visualisez le code dans le troisième écran avant de cliquer sur generate.

Voici le chronogramme que vous obtiendrez avec les signaux clk, le stimuli BTN0 et les q(15:0) des registres CO puis RI et enfin les trois q(15:0) des registres Rsrc1, Rsrc2 et R7seg. Vous pouvez observer qu’il faut bien une quinzaine de ms avant que l’impulsion ne soit générée.

Vous pouvez tester ce programme sur la carte. Si vous voulez tester les autres fonctions de votre ALU, en particulier réaliser un CONCAT après la saisie de deux mots de 8 bits, il suffit de changer le code de ADD et de recréer le toplevel.bit après les mises à jour de insmem et S3.

L’instruction MVI

L’adressage immédiat permet de manipuler une valeur constante rangée dans l’instruction elle-même. L’exécution de cette instruction est réalisée par le transfert d’une partie du registre RI contenant l’instruction en cours d’exécution, et donc la valeur immédiate, vers un registre destination. La largeur des instructions étant de 16 bits, on peut garder 8 bits pour codifier cette valeur immédiate, 4 bits pour le code opération et 4 bits pour la destination. Afin de garder le format global de vos instructions, je vous propose le format suivant : code opération = b0010, 8 bits pour la valeur, 4 bits pour le numéro du registre destination. Le registre source RI est implicite pour cette instruction MVI, il n’y a pas de codage du numéro de la source. Avec ce format, il va falloir extraire du registre RI les 8 bits du milieu Q(11:4) et forcer les autres bits à 0 avant de les placer sur le bus de données. Pour cela supprimez dans S3.sch le bus entre la sortie Q(15:0) du registre RI et l’entrée R(15:0) de son connecteur16 CRI. Vous allez créer une nouvelle source de type schématique appelé immediat. Placez-y un bus vertical a(15:0) avec son I/O Marker à gauche. Placez un bus b(15:0) avec son I/O Marker à droite.

Instanciez 8 BUF entre les deux bus. Avec des Bus Tap connectez les entrées des BUF aux fils a(11:4) puis les sorties des BUF aux fils b(7:0). Placez une constante en changeant sa valeur à x00 puis en changeant sa longueur à 8. Reliez la constante par un Bus Tap sur b(15:8) . Sauvez et créez le symbole associé

.

Figure 120 Immédiat de 8 bits à 16 bits

De retour sur S3, placez une instance de immediat sous le connecteur16 CRI. Reliez sa sortie avec l’entrée R du connecteur. Connectez la sortie q(15:0) et l’entrée de immediat. Chaque ouverture du registre RI sur le bus (RI2B actif) produira un mot de 16 bits construit avec les 8 bits de la valeur immédiate sur les poids faibles et complétée par b00000000 sur les poids forts.

Figure 121 Connexion de immediat entre RI et CRI

Il ne reste plus qu’à modifier la FSM afin de décoder et contrôler cette instruction MVI. Cette instruction est donc codée par b0010 vvvv vvvv dddd : les 8 v vous donnent la valeur de l’immédiat et les 4 d le numéro du registre destination. Il n’y a pas de port supplémentaire sur la FSM. Il suffit de compléter le décodage des instructions dans le process Next_Output pour le nœud chargement.

Sauvez, créez le symbole et le mettre à jour dans S3. Vous devez également ajouter cette instruction dans votre assembleur, il suffit d’ajouter le mot clef MVI . Vous pouvez reconstruire un programme exemple pour tester votre nouvelle instruction, il génère le fichier .coe à charger dans insmem. Mettez à jour les symboles dans S3 puis dans toplevel. Vous êtes prêt pour la simulation et la synthèse.

Les instructions MZ, MNZ

Pour mettre en place des conditions dans le processeur S3, il faut être en mesure de tester le résultat d’une opération. Ce résultat est nécessairement dans Rdest. Une solution générique et simple consiste à valider les transferts de registres sous condition liée au contenu de Rdest. Les deux instructions que je vous propose MZ et MNZ, fonctionnent comme le MOV, à la différence que le transfert ne sera valide que si respectivement la valeur de Rdest lors de l’exécution vaut 0 ou est différente de 0. Ça va être très simple à mettre en œuvre. La FSM décode ces deux instructions comme elle le faisait pour le MOV mais lors de l’exécution proprement dite de l’instruction la destination dest(3:0) est validée si est seulement la condition associée est vraie à cet instant. Le décodeur D4_16E dispose d’une entrée E pour Enable. Il suffit d’invalider cette entrée quand la condition est fausse lors de cette exécution. Pour cela créez un schématique nommé testeur qui renvoie la valeur vrai si :

  • Instruction MZ et tous les bits de Rdest à 0.
  • Instruction MNZ et au moins un bit de Rdest à 1.
  • Toute autre instruction.

Dans ce schéma, placez y un bus a(15:0) avec son I/O Marker. Placez un OR16 devant ce bus et avec 16 Bus Tap successifs connectez les 16 fils du bus au OR16. Placez deux fils avec I/O Marker nommés test_Z et test_NZ. Il vous suffit de placer le circuit réalisant la fonction logique suivante. Une fois réalisé, sauvez et générez le symbole.

Le circuit final ressemble à celui-ci.

Figure 122 Circuit testeur de Rdest

Il vous faut maintenant modifier la FSM. Deux ports de sortie doivent être ajoutés, nommez-les test_Z et test_NZ.

    1. Déclarez les ports et les signaux internes associés test_Z_i et test_NZ_i.
    1. Dans le process Next_Output après avoir initialiser les deux signaux internes, ajoutez le décodage des deux instructions MZ et MNZ derrière le décodage de MVI. Les codes opération sont respectivement choisis à b0011 et b0100.
    1. Sur le process Synchro, il faut mettre à jour les deux ports de sortie à partir des signaux internes.

Vous pouvez sauvegarder et créer le symbole associé. Mettez à jour ce symbole dans S3. Le composant possède 2 ports de sortie en plus. Vous pouvez éditer le symbole pour la clarté du schéma si besoin. Placez deux bouts de fil nommés test_Z et test_NZ sur les ports correspondants. Placez un symbole testeur sous insmem. Sur les trois ports d’entrée placez des fils nommés respectivement dest_bus(15:0), test_Z et test_NZ. Il vous reste à supprimer le VCC sur l’entrée E du décodeur D4_16E associé au bus dest(3:0). Vous le remplacer par une connexion avec la sortie s de votre testeur.

Figure 123 Insertion du testeur avec la nouvelle FSM

Enfin nommez le bus en sortie du registre Rdest en dest_bus(15:0). Sauvez S3 recréez le symbole et valider la mise à jour dans toplevel.

Figure 124 Renommage du bus en sortie de Rdest

Il vous reste à modifier l’assembleur S3asm.bat puis à tester un programme. Je vous propose le programme suivant. Ce programme est un jeu du nombre caché. Entrez un nombre sur les switches (1 chance sur 256), si le nombre est trouvé le processeur affiche 0000 sinon il affiche 00FF.

Les instructions MIZ et MINZ

Comme pour le MOV, le MVI peut être conditionné par le résultat contenu dans Rdest. Il suffit de modifier la FSM pour réaliser le décodage de ces deux nouvelles instructions. Choisissez les codes b0101 et b0110. Le code à modifier se trouve dans Process Next_Ouput.

Ajoutez également ces deux codes dans votre assembleur S3asm.bat.

A vous de jouer

Question 1 : Réécrire le jeu du nombre caché en utilisant les instructions MIZ et MINZ.

Réponses