TP2 : Simulation d'une C.P.U. et d'un système

>>> CE TP EST A RENDRE.
Les modalités sont précisées à la fin de ce document.

Présentation de la machine

Pour suivre ce TP vous devez récupérer le prototype du simulateur qui est mis à votre disposition.

Mémoire centrale

La mémoire centrale de notre machine est composée de cellules. Une cellule peut contenir soit entier de 32 bits (la taille des entiers sur une architecture PC) soit une instruction. La mémoire contient quelques dizaines de cellules simulées par un simple tableau. Les adresses physiques sont contiguës et varient de zéro à 127.

typedef union {    /* une cellule memoire contient  */
    int  value;    /* soit un entier 32 bits signe  */
    INST inst;     /* soit une instruction.         */
} CELL;

CELL mem[128];     /* memoire                       */

Instructions de la C.P.U.

Notre C.P.U. exécute des instructions à taille fixe (32 bits). Une instruction est composée d'un code opération, de deux numéros de registre et d'un argument. Vous pouvez voir les détails ci-dessous :

typedef struct {
    unsigned OP: 10;  /* code operation (10 bits)  */
    unsigned i:   3;  /* nu 1er registre (3 bits)  */
    unsigned j:   3;  /* nu 2eme registre (3 bits) */
    short    ARG;     /* argument (16 bits)        */
} INST;

Une instruction va ressembler à ceci : « ADD R1, R2, 1000 ». Elle va effectuer l'affectation R1=R1+R2+1000. Afin de ne pas rendre la création d'un programme trop pénible une fonction est fournie pour implanter une instruction en mémoire.

void make_inst(int adr, unsigned code, unsigned i, unsigned j, short arg) {
    INST inst;
    inst.OP  = code;
    inst.i  = i;
    inst.j  = j;
    inst.ARG = arg;
    mem[adr].inst = inst;
}

Structure de la C.P.U.

Le mot d'état du processeur est défini comme suit

typedef struct PSW {    /* Processor Status Word */
        int PC;         /* Program Counter */
        int SB;         /* Segment Base */
        int SS;         /* Segment Size */
        int IN;         /* Interrupt number */
        int DR[8];      /* Data Registers */
        int AC;         /* Accumulateur */
        INST RI;        /* Registre instruction */
} PSW;

Adressage logique et physique

Durant l'exécution, la CPU adresse la mémoire en utilisant des adresses logiques c'est à dire des entiers compris entre 0 et la taille de la zone mémoire allouée à ce processus (Segment). Le début de cette zone mémoire est pointé par le registre SB (Segment Base) tandis que le registre SS en donne la taille (Segment Size).

       <--------------  SS  -------------->
    ---+---+---+------------------+---+---+---
...    |   |   |       ....       |   |   |    ...
    ---+---+---+------------------+---+---+---
         ^
         SB

pour chaque adresse logique « a » la CPU calcule l'adresse physique « p » en appliquant l'algorithme suivant :

lire(a) :
  | si (a < 0) ou (a >= SS) <erreur adressage>
  | p = (SB + a)
  | renvoyer mem[p]

Simulation de la machine

Nous avons vu en cours que la CPU passe son temps à alterner des cycles ou elle exécute du code utilisateur et des cycles ou elle exécute du code système. Elle passe du code utilisateur au code système par une interruption et du code système au code utilisateur par un chargement du mot d'état processeur (ou processor Status Word). On peut donc simuler ce comportement par le code ci-dessous :

mep.IN = INT_INIT; /* interruption INIT */      
while (1) {
    mep = systeme(mep);
    mep = cpu(mep);
}

La fonction cpu() simule l'exécution du code utilisateur jusqu'à l'apparition d'une interruption. La fonction systeme() reprends la main, traite l'interruption et redonne la main au code utilisateur.

Nouvelles fonctions à réaliser

Tracer les interruptions

  1. Pour l'instant le simulateur boucle sans rien afficher. Faites en sorte que le système indique les numéros d'interruption reçus.
  2. Faites en sorte que les interruptions d'erreur (instruction inconnue et erreur d'adressage) provoque l'arrêt du simulateur. Modifiez le programme exécuté pour faire apparaître ces deux interruptions.
  3. Modifiez le code du système pour que l'interruption TRACE provoque l'affichage des registres (pas tous, seulement PC et les DR).

Améliorer la C.P.U.

  1. Nous allons maintenant ajouter de nouvelles instructions à la C.P.U. afin de pouvoir exécuter la simple boucle ci-dessous :
     0 : SUB R1, R1, 0          | R1 = 0
     1 : SUB R2, R2, -1000      | R2 = 1000
     2 : SUB R3, R3, -10        | R3 = 10
     3 : CMP R1, R2, 0          | AC = (R1 - R2)
     4 : IFGT 10                | if (AC > 0) PC = 10
     5 : NOP                    | no operation
     6 : NOP                    | no operation
     7 : NOP                    | no operation
     8 : ADD R1, R3, 0          | R1 += R3
     9 : JUMP 3                 | PC = 3
    10 : HALT                   | HALT
    
    Pour pouvoir coder ce programme, il faut ajouter quatre instructions à notre CPU (IFGT, NOP, JUMP et HALT).
  2. Pour diminuer le nombre d'interruptions (et donc le nombre d'affichages réalisés par le système), faites en sorte que la C.P.U. exécute trois instructions avant de générer une nouvelle interruption de fin de tranche de temps (INT_CLOCK). L'interruption de trace n'est donc plus nécessaire.

Appels au système

Pour l'instant les affichages de notre simulateur sont réalisés par les traces du PSW faites par le système. Il est temps maintenant d'ajouter une nouvelle instruction, que nous appellerons SYSC, dont le but est de générer une interruption afin de donner la main au système. La partie argument de cette instruction indiquera au système l'action voulue et les registres indiqueront les éventuels paramètres de cette action.

  1. Commencez par implanter l'appel système SYSC_EXIT qui provoque l'arrêt du processus demandeur et donc du système puisque nous avons, pour l'instant, un seul processus.
  2. Continuez avec l'appel SYSC_PUTI qui affiche l'entier stocké dans le premier registre de l'instruction SYSC. Vous pouvez maintenant placer cette instruction au coeur de la boucle et voir le déroulement de la boucle.
  3. Pour implanter le calcul de la taille mémoire alloué à un processus, il nous manque l'instruction de lecture mémoire que nous appellerons LOAD :
    instruction       | action réalisée
    ------------------|----------------------------------------------
    LOAD Ri, Rj, k    | AC = Rj + k
                      | si (AC < 0) ou (AC >= SS) <erreur adressage>
                      | AC = mem[SB + AC]
                      | Ri = AC
                      | PC += 1
    

Comment rendre ce TP ?

IMPORTANT : à la fin de la séance (ou quelques jours après) envoyez vos fichiers en utilisant la commande ci-dessous :

/home/massat/public/envoyer-tps-systeme noms-des-fichiers-a-envoyer