Leçon 9 : ALU

Vous allez réaliser une ALU proposant un maximum de 16 fonctions différentes. Pour l’instant juste certaines seront implantées. Cette ALU s’intercale entre les registres Rsrc1, Rsrc2 et Rdest. Les transferts de registres en amont doivent donc amener les données à manipuler par l’ALU dans les deux registres source puis le résultat sera récupéré par un autre transfert de donnée depuis le registre destination. En conséquence il n’y a pas d’opérande pour vos fonctions ALU ce qui va vous simplifier grandement la réalisation de la FSM.

Transférer des données n’a d’intérêt que si d’une façon ou d’un autre le système permet de les modifier. C’est l’ALU (Arithmetic & Logic Unit) qui va jouer ce rôle.

Connaissances requises

Registre, bus, nombre entier, complément à 2.

Objectif

Vous allez réaliser une ALU proposant un maximum de 16 fonctions différentes. Pour l’instant juste certaines seront implantées. Cette ALU s’intercale entre les registres Rsrc1, Rsrc2 et Rdest. Les transferts de registres en amont doivent donc amener les données à manipuler par l’ALU dans les deux registres source puis le résultat sera récupéré par un autre transfert de donnée depuis le registre destination. En conséquence il n’y a pas d’opérande pour vos fonctions ALU ce qui va vous simplifier grandement la réalisation de la FSM.

Le projet

Vous allez instancier un squelette d’ALU qui peut instancier jusque 16 fonctions élémentaires. Pour chacune de ces fonctions un code binaire doit être associé afin de pouvoir réaliser le contrôle par la FSM lors du décodage de l’instruction. Le mini assembleur devra lui aussi supporter ces nouvelles instructions.

L’ALU

Ouvrez le projet TP9 issu de la copie du TP8 précèdent. Ajoutez une nouvelle source schematic nommée ALU. Le fonctionnement de l’ALU reste simple. Vous placerez en parallèle tous les circuits qui exécutent une fonction, ici vous allez construire une ALU avec 16 fonctions au maximum. L’ALU produit à tout moment 16 résultats en parallèles, il convient ensuite de sélectionner le bon résultat en fonction du code de la fonction. Pour faire fonctionner l’ALU, la FSM devra générer les signaux de contrôle lorsque qu’une instruction ALU sera décodée. Commençons par définir le format d’une instruction ALU. Je vous propose comme premier champ b0001 qui signifie une opération ALU. Il nous reste alors 12 bits soit la possibilité de choisir une fonction parmi 2048. Seul le second champ de 4 bits sera utilisé. Il va vous permettre de sélectionner une fonction parmi un maximum de 16, c’est celle qui produira le résultat sur la sortie de l’ALU. Vous modifierez plus tard la FSM en fonction de ce codage.

Utilisez les mux2x16 que vous avez déjà définis. Placez deux bus a(15:0) et b(15:0) avec leur I/O Marker verticalement sur le côté gauche, et un bus OP(3:0) en haut, ces 4 bits codent le numéro de la fonction à réaliser. Placer une première colonne de 8 mux2x16 puis une colonne de 4 puis une colonne de 2 et enfin une colonne de 1. Reliez les sorties avec les entrées pour créer votre arbre de mux2x16. De suite supprimez le sous arbre de 3 mux2x16 en haut à gauche, ils ne serviront pas. Puis avec des Bus Tap, OP(0) est connecté aux sélecteurs de la première colonne, OP(1) pour la seconde etc… en sortie du dernier mux2x16 vous pouvez placer un bus et son I/O Marker nommé s(15:0)

Votre squelette d’ALU devrait ressemblait à ce schéma, il ne manque que les deux bus en entrée a et b.

Figure 102 Sélecteur 1 parmi 16 en fonction de OP(3:0)

Il vous reste à placer de haut en bas, et donc avec des codes de fonction de b0000 à b1111, les fonctions que vous voulez instancier. Voici une première liste de 11 fonctions que vous pourrez compléter vous-même par la suite.

SUBC, ADD, SUB, ADDC : ces 4 fonctions sont réalisées par le composant ADSU16 de la bibliothèque Arithmetic. C’est pour cela que vous avez supprimé 3 mux2x16. Pointez sur ce composant et lisez Symbol Info. Ce composant permet de réaliser à lui seul 4 fonctions différentes dont voici les codes S3.

SUBC : A – B – 1 code 0000

ADD : A + B code 0001

SUB : A – B code 0010

ADDC : A + B + 1 code 0011

Vous remarquerez dans le codage proposé que le bit de poids faible permet de sélectionner soit ADD soit SUB. Le bit suivant permet quant à lui la sélection de la fonctionnalité avec ou sans retenue à 1 pour les deux types d’opération. Les deux bits suivant à b00 sélectionnent votre ADSU16 comme producteur de la sortie finale de l’ALU, ils vont chacun contrôler un multiplexeur 2x1 sur 16 bits de largeur. Donc instanciez un composant ADSU16, reliez les bus a et b aux entrées A et B du composant. Sur le fil ADD du composant il suffit de connecter le fil OP(0) pour sélectionner le type ADD ou SUB. Sur le fil CI vous connectez le fil OP(1) afin de sélectionner avec ou sans retenue. La sortie de ADDSU16 est directement connectée au D0 du mux2x16 de la troisième couche.

Figure 103 4 fonctions ALU en un seul composant

INV : cette fonction transforme bit à bit les 16 bits de 0 => 1 et de 1=> 0. Placez un Inv16 sur le bus a. Reliez la sortie de l’inverseur sur le premier mux2x16 de la couche 0, sur son port D0. Par ce positionnement son code de fonction correspond effectivement à b0100.

Figure 104 L'inverseur sur le bus a

AND, OR : Ces opérateurs réalisent un AND ou un OR bit à bit sur les 16 bits des entrées. Créez le schématique andor. Placez-y au centre deux bus verticaux en entrée a(15:0) et b(15:0) et deux bus verticaux en sortie de chaque côté des entrées. Nommez-les sand(15:0) et sor(15:0). Placez 16 AND2 du côté du bus sand et 16 OR2 du côté du bus sor légèrement décalés. Placez les Bus Tap entre les bus a et b et les entrées des portes, puis vers sand et sor depuis les sorties de portes. Créez le symbole et placez-le dans l’ALU.

Figure 105 AND et OR sur des mots d 16 bits

Figure 106 AND et OR dans l'ALU

INC : Vous pourriez réaliser un incrément par une addition avec retenue en plaçant x0000 sur un des deux registres sources de l’ALU. Afin de gagner un transfert de registre et donc un cycle, vous allez instancier le symbole inc que vous avez créé pour incrémenter le registre CO. Il suffit ensuite de relier l’entrée de inc au bus a, sa sortie sera reliée au D1 suivant. Le code fonction de INC correspond à b0111

CPL2 : Pour calculer le complément à deux d’un nombre il suffit de l’inverser et d’y ajouter 1. Placez ces deux composants à la suite entre le bus a et l’entrée D0 du sélecteur suivant. Son code correspondant à la sélection b1000.

Figure 107 Incrément et complément à 2

CONCAT : Les switches permettent de saisir des mots de 8 bits. Pour saisir un mot de 16 bits, il faudrait 2 saisies successives sur les switches puis il faudrait concaténer les 8 bits de la première saisie avec les 8 bits de la seconde, l’ordre doit être choisi par exemple d’abord les bits de poids faible puis les bits de poids fort. Créez une nouvelle source schematic nommée concat. Placez y deux bus et I/O Marker respectifs a(15:0) et b(15:0). Placez un bus nommé s(15 :0) à droite verticalement et avec son I/O Marker. Il vous reste à placer 16 BUF les 8 premiers sont connectés à a(7:0), les 8 second à b(7:0). Les 16 BUF sont connectés à s(15 :0) en sortie. Respectez les indices !!

Figure 108 Concaténation de 2 mots de 8 bits vers un mot de 16 bits

Vous pouvez générer le symbole et venir le placer dans ALU. La sortie se connecte au port suivant des mux2x16.

Figure 109 Concaténation dans l'ALU

ID : La fonction identité recopie l'entrée a(15:0) sur la sortie s(15:0). C'est la plus simple il suffit de connecter le bus a(15:0) sur le mux2x16 suivant.

Vous pourriez encore placer 5 fonctions dans cette ALU. Pour en mettre plus de 16, il faudrait augmenter la taille du code fonctions sur 8 bits par exemple avec ainsi 256 fonctions possibles. C’est assez facile à mettre en œuvre. Pour l’instant çà suffira.

Figure 110 Vue d'ensemble de l'ALU

Votre ALU est prête, générez le symbole et venez l’instancier dans S3 à gauche du registre Rdest. Il vous suffit de connecter l’entrée a à la sortie du registre Rsrc1, l’entrée b à la sortie du registre Rsrc2, la sortie s à l’entrée du registre Rdest. Ajoutez un fil sur le port OP(3:0) et donnez-lui le même nom. Il vous faut également ajouter le signal Rdestload sur la sortie 11 du décodeur D4_16E des destinations et sur le CE du registre Rdest.

Figure 111 Prise en compte du signal Rdestload

Figure 112 Insertion de l'ALU dans le processeur S3

Modification de la FSM

Ajoutez un port de sortie OP(3:0) dans l’entity afin de sélectionner la bonne fonction au sein de l’ALU.

Déclarez un signal interne OP_I(3:0).

Modifiez le process Next_output en initialisant le signal interne OP_i. Puis si l’instruction n’est pas un MOV, testez s’il s’agit d’une fonction ALU. Si tel est le cas, modifiez le décodage de l’instruction afin d’identifier la fonction ALU de l’instruction. Dans ce cas, il vous faut aussi déclencher le chargement du registre Rdest par activation du port dest(3:0) avec la valeur associée à Rdest soit b1011.

Ajoutez dans le process Synchro la validation du signal de sortie OP depuis OP_i .

Vous pouvez alors recréer le symbole FSM et le mettre à jour dans le schéma S3. Il suffit maintenant de connecter la sortie OP de la FSM avec l’entrée OP de l’ALU. Pour cela créez un fil en sortie avec le même nom OP(3:0), par association des noms les ports sont connectés. Vous avez toujours la possibilité d’éditer le symbole pour par exemple déplacer un port sur le symbole.

Figure 113 FSM contrôlant l'ALU

Afin de tester votre processeur, je vous propose le programme suivant : saisie d’une valeur sur les switches, addition de cette valeur avec elle-même, affichage du résultat sur l’afficheur.

Voici le programme pour générer un fichier d’initialisation de la mémoire d’instruction insmem, modifiez le fichier prog.coe en remplaçant les instructions en fonction du code hexadécimal suivant.

Vous devez ensuite le recharger dans la ROM en rouvrant l’IP_Core et sur le troisième écran sélectionnez Generate une fois le fichier chargé et visualisé. Si IP_core ne trouve pas l’ancien fichier prog.coe il risque de vous refuser l’ouverture, je vous conseille de supprimer insmem (clic droit>remove) de votre projet et de le recréer.

Mettez à jour les instances des symboles modifiés dans S3. Recréez le symbole S3 et mettez à jour dans toplevel. Lancez une simulation sur toplevel_tb. En observant la sortie Q(15:0) du registre R7seg, vous devriez obtenir la valeur x24 puisque dans toplevel_tb vous aviez initialisé les switches à x12. (les q(15:0) dans l’ordre R7seg, CO, RI , Rsrc1, Rsrc2, Rdest)

Vous pouvez générer le toplevel.bit et le tester sur la carte. Pour finaliser la leçon, faites une copie du projet sur TP10 pour la suite.

A vous de jouer

Question 1 : Remplacez l’instruction ADD par toutes les autres instructions de l’ALU une à une et recompilez à chaque changement. Vérifiez ainsi la sémantique de votre ALU.

Question 2 : Mettre à jour le fichier S3asm.bat afin de supporter les nouvelles instructions ADD … CONCAT.

Question 3 : Construire une ALU qui fait la même chose mais en VHDL. Vous verrez que parfois c’est plus simple d’utiliser VHDL ( Une alu 16 bits en VHDL ) et ici en particulier. Vous pourrez par la suite utiliser l’un ou l’autre. Vous aurez la possibilité d’ajouter par la suite des fonctions sur les codes non utilisés.

Question 4 : Ajoutez la fonction DEC pour décrément.

Question 5 : Ajoutez la fonction MUL qui multiplie deux nombres de 8 bits non signés.

Réponses: