Leçon 11 : Assembleur

Il est temps de mettre en œuvre quelques programmes en langage machine S3 afin de bénéficier de tous les développements qui vous ont amené ici. La programmation d’un processeur en langage machine demande une bonne compréhension de son architecture. C’est vous qui avez créé ce processeur S3. Vous en connaissez tous les rouages. Il vous reste à associer une suite d’instructions supportées par le processeur S3 pour en faire un programme exécutable.

Votre processeur S3 est réalisé à 95%. Il vous reste à placer une mémoire de données, ce que vous ferez dans la dernière leçon. Il est temps de mettre en œuvre quelques programmes en langage machine S3 afin de bénéficier de tous les développements qui vous ont amené ici. La programmation d’un processeur en langage machine demande une bonne compréhension de son architecture. C’est vous qui avez créé ce processeur S3. Vous en connaissez tous les rouages. Il vous reste à associer une suite d’instructions supportées par le processeur S3 pour en faire un programme exécutable.

Connaissance requise

Eléments de programmation. Pour exécuter un programme S3 sur la carte ou le simuler sous ISE vous devez :

    1. Ecrire le code assembleur dans un fichier prog.S3.
    2. Compiler le code avec S3asm.bat vers un fichier prog.coe.
    3. Charger le code prog.coe dans la mémoire d’instructions insmem en cliquant sur le symbole dans la hiérarchie de votre projet. Le troisième écran permet de sélectionner le fichier et aussi de le visualiser.
    4. Générer insmem avec cette nouvelle initialisation.
    5. Mettre à jour le symbole dans S3.sch et sauver S3.sch.
    6. Créer le symbole de S3.
    7. Mettre à jour le symbole S3 dans toplevel et sauver.
    8. Simuler ou synthétiser.

Les instructions de contrôle

Par construction chaque phase de chargement d’une instruction déclenche l’incrémentation du CO afin de le faire pointer vers la prochaine instruction à exécuter, c’est à dire la suivante dans le modèle Von Neumann. La rupture de séquence est obtenue seulement par le chargement explicite d’une autre adresse dans le CO. Pour cela vous disposez de 6 instructions dans le processeur S3.

  • MOV registre_source CO : Une valeur de 16 bits peut être produite par programme dans un registre. Ainsi vous pouvez écrire un programme qui calcule la prochaine instruction à exécuter, voire la saisir depuis les switches via le registre Rsw.
  • MVZ registre_source CO : La mise à jour du CO ne se fera que si le contenu de Rdest est égal à x0000. Par calcul on peut ainsi mettre en place une rupture de séquence conditionnée par le résultat de la dernière opération ALU.
  • MVNZ registre_source CO : Idem mais la mise à jour du CO est effective seulement si Rdest est différent de x0000.
  • MVI 8_bits CO : Ici avec l’adressage immédiat on peut ranger une adresse explicite dans le CO . Cette adresse est comprise entre x00 et xFF.
  • MIZ 8_bits CO : Fonctionne comme le MVI mais sous la condition que Rdest soit égal à x0000
  • MINZ 8_bits CO : Avec Rdest différent de x0000 cette fois.

Avant tout il faut comprendre le fonctionnement pipeline du processeur. Lorsque l’instruction qui modifie la valeur du CO s’exécute, le pipeline a déjà chargé l’instruction suivante et commence à la décoder. La prise en compte du déroutement du programme ne sera effective qu’avec un retard de un cycle donc d’une instruction. On parle de branchement retardé.

Dans cet exemple l’instruction NEXT sera toujours exécutée, le saut étant effective au cycle suivant.

Je vous propose de tester cette caractéristique sur votre carte et par simulation. Il suffit de tester ce programme.

La simulation avec affichage des q(15:0) des registres CO, RI et R7seg vous donnera ceci. Vous observez la prise en compte effective du MVI 02 R7seg avant la rupture de séquence. L’exécution affichera x0002 sur l’afficheur et non pas x0001. La valeur X0003 ne sera jamais affichée.

Figure 125 Simulation branchement retardé

Ce n’est pas toujours possible de trouver une dernière instruction à placer après la mise à jour du CO. Au pire vous pourrez toujours placer un NOP. Vous aurez dans ce cas un cycle de perdu lors du branchement au pire des cas.

Calcul de prédicat

Les instructions de transfert de registres conditionnées ne testent que la valeur du registre Rdest égale ou non à x0000.

Les booléens sont une codification de l'information, en général définie pour le langage que vous utilisez. Pour le processeur S3, si vous décidez que VRAI = x0000 et FAUX = xFFFF et les autres valeurs restant indéfinies, les opérateurs de votre ALU vous permettent directement de réaliser des AND, OR ou INV. Les résultats produits dans Rdest servent alors à déclencher ou non des branchements.

Pour un langage comme le shell, on peut décider de choisir la codification suivante où les valeurs booléennes sont : VRAI= x0000 et FAUX toutes les autres valeurs (En C c'est l'inverse!). Dans ce cas voici les codes pour tester les prédicats suivants : A, NOT A, A and B, A NAND B et un code qui réalise B := NOT A. Les formules de De Morgan seront bien utiles…[1] Le code if A=B est lui utile pour tous les types de données. On considère dans ces exemples que l’adresse de branchement est rangée dans R3, A dans R1 et B dans R2. Vous pourriez faire la même chose avec MIZ, MINZ et un adressage immédiat.

Je vous propose d’écrire le programme suivant manipulant des booléens avec cette codification et un OR.

Entrer R1

Entrer R2

si (R1 ou R2) alors

Rdest := x0001

sinon

Rdest := x0002

fsi

Le programme affiche la valeur x0001 si l’une au moins des deux valeurs saisies est égale à x00 et x0002 sinon. Appliquons la formule de De Morgan A OR B est égal à NAND ( Not A , Not B ). Vous pouvez analyser et tester le programme suivant (pour la simulation remplacer les PAUSE par des NOP).

Le branchement relatif

Les instructions de rupture de séquence forcent la valeur du CO à une nouvelle adresse. Parfois il est souhaitable d’effectuer un saut relatif à la valeur du CO. Par exemple on peut vouloir reculer de 3 instructions ou encore sauter au-dessus des 5 suivantes. Il faut dans ce cas recalculer la valeur du CO par rapport à sa valeur actuelle. Cette technique de rupture de séquence vous évitera d’avoir à calculer et recalculer les adresses de branchement chaque fois que la portion de code change de place dans la mémoire d’instructions.

Pour simuler ce comportement il faut inclure les instructions qui recalculeront la valeur du CO. Cette façon de procédé a bien sûr un coût par rapport à l’adressage direct. Il faut bien se souvenir que le CO pointe vers l’instruction suivante. Voici un code générique que vous pourrez réutiliser par la suite.

Voici un code avec retour en arrière.

:

La simulation avec les registres CO, RI et R7seg que vous devriez obtenir.

Figure 126 La boucle infinie avec branchement relatif

On peut faire la même chose pour un saut en avant.

Figure 127 Saut en avant

La simulation pour ce programme vous permet d’observer le saut en avant sur l’adresse x0008. R7seg ne reçoit pas la valeur x04.

La construction d’un IF Then ELSE

Je vous propose de construire un programme qui utilise un IF THEN ELSE. Vous allez concevoir un programme qui affiche la liste

3 4 7 8 11 12 15 … ou 3 4 7 8 B C F 10 … en hexadécimal

N := 1

Loop :

IF N pair THEN

dest := 2N

ElSE

dest := 2N + 1

ENDIF

Afficher dest

Pause

N := N+ 1

GOTO Loop

Voici le programme assembleur qui pourrait être produit par un compilateur depuis la description avec un IF THEN ELSE. Ici le branchement retardé a bien été utilisé puisque des instructions ont trouvé leur place derrière la rupture de séquence. Ce code est moins efficace que celui que vous avez écrit en séance de TD, mais il pourrait être produit automatiquement à partir d'un langage de plus haut niveau. Testez les deux programmes!!!

Vous pouvez exécuter le programme sur la carte. Vous pouvez aussi le simuler sur Isim afin de suivre l’évolution du CO des registres Rsrc1, Rsrc2, Rdest et R1. Pour la simulation il est préférable de remplacer le PAUSE par un NOP, ça vous évitera d’attendre la pression du bouton btn0.

La construction d’un Tant Que

Vous allez tester ce programme qui affiche le Iième élément de la suite de fibonacci. I est un nombre compris entre 0 et 255 saisi sur les switches, néanmoins sur 16 bits on n'ira pas très loin. Le programme dans un pseudo langage serait :

Loop:

N := 0

R1 := 0

R2 : = 1

-- Positionner les switches

Pause

Tantque N<> Rsw faire

N := N + 1

Dest := R1 + R2

R1 := R2

R2 := Dest

FTQ

Afficher R1

Goto loop

Tester le programme sur la carte. C’est un bon test pour votre processeur S3 ! Testez aussi le programme obtenu en TD.

Ensuite pour le simuler il faudrait mieux remplacer le PAUSE par un NOP et n’oubliez pas de positionner une valeur initiale sur les switches dans toplevel_tb.

A vous de jouer

Question 1 :

Pour la suite 3 4 7 8 11 12 15, on peut remarquer que l'on ajoute alternativement +1 et +3. Ecrivez ce code avec une boucle qui produit un élément de la série à la fois

Question 2

Produisez un autre code qui déroule la boucle et produit deux éléments à chaque itération! Ce code a été proposé en TD.

Réponses

[1] Attention votre ALU implémente un AND sur 16 bits : x0010 and x1101 vous produira x0000 alors que les valeurs "booléennes" sont indéfinies. Vous pouvez utiliser ce AND tel que seulement avec la codification choisie.