CentraleSupélecDépartement informatique
Plateau de Moulon
3 rue Joliot-Curie
F-91192 Gif-sur-Yvette cedex
Etude de Laboratoire 1 : Réalisation d'un microprocesseur

L'objectif de cette étude de laboratoire est de completer la conception du microprocesseur MiniArm que vous avez commencée à l'occasion du premier bureau d'étude. Il s'agit de définir le tableau des temps, d'en dériver les équations logiques et de définir l'instruction bl (Branch and Link).

Compétences visées:

  • Déterminer les séquences d'opérations élémentaires nécessaires à l'exécution des instructions d'un microprocesseur.
  • Synthétiser les équations du séquenceur permettant d'exécuter ces instructions.
  • Modifier ces équations pour ajouter une nouvelle instruction.
  • Expliquer les mécanismes mis en œuvre lors d'un appel de fonction.

Il est préférable de travailler sous Linux pour utiliser l'assembleur de code MiniARM (assembler.py).

Si vous souhaitez poursuivre le séquencement manuel que vous avez commencé lors du premier bureau d'étude, vous pouvez télécharger le kit correspondant.

1. Préliminaires

Téléchargez dans votre répertoire personnel le kit qui vous est fourni et décompressez-le. Ce kit contient :

  • Le fichier miniARM.circ, contenant le chemin de données du processeur.
  • Le fichier assembler.py, un assembleur pour le processeur MiniArm permettant de traduire en langage machine un programme écrit dans le langage d'assemblage de MiniArm.
  • Le fichier test.txt, un programme de test écrit dans le langage d'assemblage de MiniArm, vous permettant de vérifier le bon fonctionnement du processeur après avoir défini les équations du séquenceur.
  • Le fichier ese1010.jar, une librairies de composants utilisés pour réaliser le chemin de données de MiniArm.

La description du processeur est disponible sur cette page. La documentation des composants du chemin de données (y compris les codes des opérations de l'UAL) est disponible sur cette page.

2. Tableau des temps

Afin de définir les équations logiques qui sont à la base du fonctionnement du séquenceur, il faut concevoir un tableau des temps permettant de déterminer les signaux à activer pour l'exécution des instructions.

Exercice 1 :

  1. Ecrivez le tableau des temps du séquenceur dans un fichier Excel nommé tableau-temps-miniarm.xls.
  2. Faites valider ce tableau par votre encadrant(e).

Dans la description des actions effectuées à chaque temps, utilisez des noms symboliques pour les opérations de l'UAL (par exemple UAL=A plutôt que UAL=0000). Cela vous permettra de détecter plus facilement les erreurs. Vous n'utiliserez les valeurs numériques qu'au moment d'écrire les équations des 4 signaux de commande de l'UAL.

Il est conseillé de regrouper dans un même temps les actions similaires, même si cela impose de ne rien faire à certains temps pour certaines instructions. En regroupant les actions similaires, vous obtiendrez des équations plus simples et plus faciles à comprendre et à corriger.

Cette approche doit vous amener à concevoir un séquenceur en 6 ou 7 temps sans optimisation particulière.

3. Equations du séquenceur.

Une fois le tableau des temps déterminé, il faut définir les équations du séquenceur permettant de donner une valeur aux sorties du séquenceur. Ces valeurs, qui pilotent le fonctionnement du chemin de données, seront une fonction des entrées (notamment, le code de l'instruction à exécuter et les drapeaux {$N$} et {$Z$}) et les signaux d'horloge {$T_i$} et {$P_i$}.

Exercice 2 :

Dérivez les équations du séquenceur et éditez-les dans Logisim.

  • Pour ce faire, cliquez sur le séquenceur et ensuite sur le texte (click to edit) de l'attribut Equations.
  • Consultez, si besoin, la documentation.

Attention : ne pas modifier l'ordre des signaux dans les équations du séquenceur, car cela modifierait la disposition des pattes du composant !

4. Test

Maintenant que les équations du séquenceur sont définies, il faut tester le bon fonctionnement de MiniArm. Dans ce qui suit, vous devrez observer l'exécution du programme test.txt qui vous est fourni, afin de vérifier que toutes les instructions soient exécutées correctement.

Ouvrez un terminal et déplacez-vous au répertoire el-1 en utilisant la commande cd :

cd .../home-directory/el-1

Assemblez le code contenu dans le fichier test.txt en utilisant l'assembleur assembler.py comme suit :

./assembler.py test.txt

Cela créera dans votre répertoire un fichier nommé test.mem contenant le code machine du programme de test. Si vous ouvrez ce fichier avec un éditeur de texte, vous verrez une suite d'instructions codées en hexadécimal. Un fichier test.lst est également généré pour indiquer comment le programme est placé en mémoire. Il indique pour chaque instruction du programme le code généré et l'adresse mémoire à laquelle il se trouve.

Le code en langage d'assemblage du programme de test est le suivant.

%
% Program for testing the mini ARM
%
% It should stop by looping at address 0x0020 (label 'end')
% If it stops at another address, there is a bug...
%
        mov r0,#5       % load 5 into r0
        str r0, var     % store 5 in var
        ldr r1, var     % load var into r2
        cmp r1,r0       % did we read the same value?
        beq okls
@errls  b errls         % no => loop here
@okls   add r2,r1,r1    % r2 <- 2*r1
        cmp r2,r1       % is r2 < r1
        blt err1        % yes => error
        b ok1           % no, continue
@err1   b err1          % loop here
@ok1    cmp r1,#5       % is r1 = 5
        beq ok2         % yes, it's ok
@err2   b err2          % no => error, loop here
@ok2    sub r3,r1,r2    % r3 <- r1-r2
        cmp r3,#0       % is r3 < 0
        blt end         % yes => ok
@err3   b err3          % no => error, loop here
@end    b end           % end of program, loop here

@var    rmw 1           % reserve a word for a variable

Si tout se passe bien, ce programme doit boucler sur sa dernière instruction (label @end, à l'adresse 0x0020). S'il s'arrête à un label de la forme @err..., c'est que votre processeur ne fonctionne pas correctement.

Exercice 3 :

  • Chargez le programme test.mem dans la mémoire RAM.
    • Pour ce faire, faites un clic droit sur le composant mémoire, choisissez Load image, puis sélectionnez le fichier test.mem.
  • Lancez la simulation et vérifiez que le résultat de chaque instruction soit conforme avec les commentaires du code ci-dessus.
    • cochez Simulation Enabled ;
    • cochez Ticks Enabled ;
    • dans Tick Frequency, choisissez une fréquence suffisamment rapide (par ex., 128Hz).
    • pour avancer dans le temps, utilisez le bouton {$C$} qui se trouve en bas du composant séquenceur. Cela vous permettra de voir le résultat de l'exécution d'une instruction.
  • Si le résultat n'est pas celui attendu, redémarrez l'exécution du programme et testez l'instruction qui a produit l'erreur en utilisant les boutons {$S$} ou {$T$} du séquenceur, qui vous permettent d'avancer plus lentement dans le temps afin de détecter l'erreur.
    • Avec {$S$}, le séquenceur avance d'un tiers de temps (par exemple, passe de {$T_1$} à {$T_1+P_1$}), puis s'arrête.
    • Avec {$T$}, le séquenceur avance d'un temps (par exemple, de {$T_1$} à {$T_2$}), puis s'arrête.

Il est utile d'ouvrir le fichier test.lst dans un éditeur de texte pour suivre plus facilement l'exécution du programme.

5. Branch and Link

La possibilité de définir des fonctions est un aspect très important d'un langage de programmation, car ça permet la réutilisation d'une séquence d'instructions réalisant un calcul. Dans la mémoire, une fonction est mémorisée à partir d'une certaine adresse. L'appel d'une fonction se traduit par une instruction de saut vers l'adresse de sa première instruction. En plus, le processeur a besoin de mémoriser l'adresse de retour, c'est à dire l'adresse de l'instruction qui suit l'appel de fonction, afin de pouvoir continuer le programme une fois que la fonction a terminé sa computation.

Dans notre processeur, on utilise les registres r0, ..., r5 pour passer des valeurs à une fonction (les arguments), le registre r7 pour mémoriser l'adresse de retour et le registre r0 pour stocker le résultat de la fonction. Le programme suivant montre les instructions nécessaires afin d'appeler la fonction func.

@main   mov r0, #5       % passer la valeur 5 en argument dans r0
        mov r7, #retour  % sauvegarder l'adresse de retour dans r7
        b func           % sauter à l'adresse de la fonction
@retour mov r1, r0       % utiliser le résultat (r0)
        ...

@func   add r0, r0, #1   % cette fonction rend son argument + 1
        b r7             % sauter à l'adresse de retour

Remarquez qu'afin d'appeler la fonction, il faut deux instructions, une instruction mov pour sauvegarder l'adresse de retour dans r7 et une instruction b pour sauter à l'adresse de la première instruction de la fonction.

@main   ...
        mov r7, #retour  % sauvegarder l'adresse de retour dans r7
        b func           % sauter à l'adresse de la fonction
        ...
        ...

On peut simplifier l'appel d'une fonction par la définition d'une nouvelle instruction que nous appelons bl (Branch and Link) :

instructioncodeimmrxryrz 
 4 bits1 bit3 bits3 bits3 bits2 bits
bl rx fonction10011rx 
fonction

L'instruction bl rx fonction est définie comme suit :

  1. rx <-- PC (par convention, rx sera toujours r7).
  2. PC <-- fonction

Exercice 4 :

Modifiez le jeu d'instructions du processeur pour ajouter l'instruction bl. Pour cela il faut :

  1. Modifier le tableau des temps pour rajouter la nouvelle instruction.
  2. Modifier les équations du séquenceur. Rappelez vous d'introduire un nouveau signal interne BL au séquenceur.

6. Appel de fonction

Vous allez maintenant tester le bon fonctionnement de l'instruction bl. Dans ce but, nous allons considérer la suite de Fibonacci, définie comme suit :

{$F(0) = 0$}

{$F(1) = 1$}

{$F(n) = F(n-1) + F(n-2)$}

Ci-dessous vous trouvez une possible implémentation en Python de l'algorithme permettant de calculer le terme de rang {$n$} de la suite de Fibonacci

def fibonacci(n):
    if n <= 1:
        return n
    else:
        fibo = 1
        fiboPrev = 1
        i = 2
        while i < n :
            temp = fibo;
            fibo += fiboPrev
            fiboPrev = temp
            i += 1
        return fibo

Exercice 5 :

  1. Traduisez le code ci-dessus dans le language d'assemblage de MiniArm. Sauvegardez le code dans un fichier nommé fibonacci.txt.
    • Rajoutez les instructions nécessaires pour appeler cette fonction dans votre code.
  2. Assemblez fibonacci.txt en utilisant l'assembleur fourni, ce qui créera le fichier fibonacci.mem.
  3. Importez fibonacci.mem dans la mémoire et testez-le.

7. Livraison

  • Une seule livraison est demandée par binôme.
  • Compressez le répertoire el-1 dans un fichier de livraison appelé nom1-nom2.zip, où nom1 et nom2 correspondent aux noms des composants du binôme.
  • Livrez le fichier de livraison sur le serveur de livraison du département informatique.
    • Rentrez votre nom utilisateur et mot de passe (les mêmes que vous lire vos mails).
    • Rentrez le code de livraison qui vous est donné par votre encadrant(e).