Travaillez dans le répertoire sim/pipe (au lieu du répertoire sim/seq auquel vous êtes habitué). Téléchargez le programme loop.ys dans ce répertoire ; le code est volontairement assez stupide.
Compilez le programme : make loop.yo et lancez le simulateur : ./psim -g loop.yo &
Les quatre barres bleues représentent des circuits combinatoires (fetch, decode, execute, memory) dont le détail est donné sur ces images. Chacun de ces étages est alimenté par un registre d'état (blanc) et alimente à son tour le registre d'état suivant ; par exemple l'étage Execute est un circuit combinatoire qui prend en entrée les signaux lus dans le registre d'état E (en blanc, situé en-dessous de la barre bleue) ; les sorties de cet étage sont affichées en gris au-dessus de la barre bleue et seront chargées dans le registre d'état blanc M au prochain signal d'horloge (d'où le terme Input affiché par le simulateur à côté de ces valeurs).
Sur les images détaillées des circuits et dans les fichiers hcl (Hardware Control Language) les champs d'un registre d'état sont préfixés par le nom du registre écrit en majuscule : cette convention permet par exemple de distinguer E_valA et M_valA. Les valeurs situées sur les lignes Input sont préfixées par le nom de l'étage qui les a calculées, écrit en minuscule : par exemple d_srcA est chargé dans E_srcA à chaque top d'horloge.
Exécutez pas à pas quelques instructions pour bien examiner comment les résultats produits par les circuits combinatoires sont chargés dans les registres correspondants à chaque cycle d'horloge (c'est-à-dire ici chaque fois que vous appuyez sur le bouton Step). Repérez en particulier les quatre signaux d_dstE, d_srcA, d_srcB, et comparez leurs valeurs avec celles de E_dstE, E_srcA, E_srcB.
Suivez aussi la progression des instructions dans le pipeline, à la fois dans la fenêtre principale du simulateur et dans la fenêtre où est affiché le code.
Lorsque vous avez assimilé ces principes de fonctionnement du simulateur, réinitialisez l'exécution en appuyant sur Reset.
Observez le fonctionnement de l'instruction addl: remarquez que les valeurs des registres lus sont propagées, puis ignorées une fois le résultat du calcul obtenu. (Ici, il n'y a pas de problème de dépendance de données)
Observez jmp l0: le processeur sait qu'il faut de toutes façons brancher et ne se trompe donc pas.
Observez la boucle end: le pipeline est complètement rempli par 5 instances consécutives de la même instruction !
Modifiez le programme pour observer la boucle 1: le processeur suppose que le branchement conditionnel doit être pris, et a raison 2 fois. La 3e fois, il ne faut pas brancher puisque la boucle est terminée, mais le processeur ne s'en rend compte que trop tard et est obligé d'annuler les instructions qu'il a commencé à exécuter par prédiction. Étudiez dans le code l'injection de bulles qui correspond à cela (Mispredicted branch dans le fichiers HCL pipe-std.hcl), observez combien les étages auxquels on récupère les valeurs sont soigneusement choisis. Comment le processeur fait-il pour savoir à quelle adresse il devait en fait brancher ?
Observez la boucle 2: le processeur suppose encore que le branchement conditionnel doit être pris, mais cette fois-ci a quasiment toujours tort !
Téléchargez le programme call.ys dans le répertoire pipe.
Exécutez jusqu'à l'instruction call. Puisque l'on doit de toutes façons prendre le branchement, le processeur ne se trompe pas. Observez le moment où l'adresse de retour de la fonction est effectivement écrite en mémoire sur la pile.
De même, observez le moment où push écrit effectivement en mémoire.
Observez le retour de f: le processeur n'a aucune idée de l'endroit où il faut retourner, et est donc obligé d'injecter des bulles pour attendre que la lecture mémoire soit effectuée avant de pouvoir continuer à exécuter des instructions. Étudiez soigneusement le code HCL pour l'instruction RET.
Téléchargez le programme fibo.ys dans le répertoire pipe ; ce programme calcule et écrit en mémoire les éléments de la suite de Fibonacci, on a juste remplacé les termes initiaux 1, 1 par 3, 5 pour qu'ils ne risquent pas d'être confondus. Ce programme n'est pas une fonction, il n'y a donc pas d'instruction call ou ret, ni d'instruction de manipulation de la pile push ou pop pour empiler ou dépiler les paramètres.
Appuyez trois fois sur Step, vous devez obtenir la situation suivante :
La première instruction vient d'être traitée par l'étage Execute, et sans surprise la principale valeur de sortie de cet étage ( valE ) vaut 100 (représentation hexadécimale) qui est la valeur de l'étiquette F ; cette valeur n'est pas encore chargée dans le registre d'état M et est désignée par e_valE sur les circuits détaillés, ou dans le fichier hcl.
Sur la même ligne dstE est le numéro du registre edi, copié directement depuis le registre d'état E, et son nom est donc E_dstE.
Questions :
Examinez la partie Decode Stage du fichier pipe-std.hcl et expliquez comment y a été calculée la valeur valB (new_E_valB dans le code HCL).
Combien de cycles sont nécessaires pour que le registre edi soit mis à jour par la première instruction ?
Faites la liste de toutes les dépendances de données dans ce programme, c'est-à-dire de tous les cas où une instruction a besoin de lire un registre dont la valeur n'est pas à jour (une instruction précédente est en train de la calculer). Dans chaque cas indiquez exactement comment le problème est résolu, c'est-à-dire comment est récupérée la valeur en cours de calcul. Vérifiez la solution en consultant le fichier pipe-std.hcl.
Déplacez la cinquième instruction (initialisation du compteur) en tête du programme, recompilez et rechargez le simulateur. Examinez alors soigneusement la première exécution de la première instruction de la boucle et expliquez ce qui se passe ! Où ce cas est-il traité dans le fichier pipe-std.hcl ?
Examinez (et expliquez) le traitement de l'instruction jne boucle lorsque le compteur %esi devient nul.
Téléchargez le programme mem.ys dans le répertoire pipe.
Téléchargez le programme cpi.ys dans le répertoire pipe.
Augmentez la vitesse d'exécution au maximum, laissez le programme tourner complètement. Observez la valeur CPI en bas à droite: c'est le nombre moyen de cycles par instruction, i.e. le nombre de cycles nécessaires pour exécuter le programme divisé par le nombre d'instructions exécutées. Il est supérieur à 1 à cause des bulles qui ont dû être insérées pour que l'exécution soit correcte.
Corrigez ce programme pour améliorer le nombre de cycles nécessaires pour exécuter le programme entier.