Changes between Version 19 and Version 20 of Archi-1-TD9


Ignore:
Timestamp:
Nov 6, 2023, 3:46:28 PM (19 months ago)
Author:
franck
Comment:

--

Legend:

Unmodified
Added
Removed
Modified
  • Archi-1-TD9

    v19 v20  
    7272''
    7373}}}
    74 1. Il y a 4 registres dans le contrôleur de `TTY`, à quelles adresses sont-ils placés dans l'espace d'adressage ?\\Comme ce sont des registres, est-ce que le MIPS peut les utiliser comme opérandes pour ses instructions (comme add, or, etc.) ?\\Dans quel registre faut-il écrire pour envoyer un caractère sur l'écran du terminal (implicitement à la position du curseur) ?\\Que contiennent les registres `TTY_STATUS` et `TTY_READ` ?\\Quelle est l'adresse de `TTY_WRITE` dans l'espace d'adressage ? (C9 S10)
     741. Il y a 4 registres de commande dans le contrôleur de `TTY`, à quelles adresses sont-ils placés dans l'espace d'adressage ?\\Comme ce sont des registres, est-ce que le MIPS peut les utiliser comme opérandes pour ses instructions (comme add, or, etc.) ?\\Dans quel registre faut-il écrire pour envoyer un caractère sur l'écran du terminal (implicitement à la position du curseur) ?\\Que contiennent les registres `TTY_STATUS` et `TTY_READ` ?\\Quelle est l'adresse de `TTY_WRITE` dans l'espace d'adressage ? (C9 S10)
    7575{{{#!protected ------------------------------------------------------------------------------------
    7676''
     
    8787''
    8888}}}
    89 1. Que représentent les flèches bleues sur le schéma ? Pourquoi ne vont-elles que dans une seule direction ? (C9 S11)
    90 {{{#!protected ------------------------------------------------------------------------------------
    91 ''
    92 * Ces flèches représentent les requêtes d'accès à la mémoire, c'est-à-dire les ''loads'' et les ''stores'' qui sont émis par le MIPS lors de l'exécution des instructions `lw`, `sw`, etc. Les requêtes sont émises par le MIPS et reçues par les composants mémoires ou périphériques.
     891. Que représentent les flèches bleues sur le schéma ci-dessus ? Pourquoi ne vont-elles que dans une seule direction ? (C9 S11)
     90{{{#!protected ------------------------------------------------------------------------------------
     91''
     92* Ces flèches représentent le sens des requêtes d'accès à la mémoire, c'est-à-dire les ''loads'' et les ''stores''. Ces requêtes sont émises par le MIPS lors de l'exécution des instructions `lw`, `sw`, etc. Les requêtes sont émises par le MIPS et reçues par les composants mémoires ou les périphériques.
    9393* On ne représente pas les données qui circulent, mais juste les requêtes, pour ne pas alourdir inutilement le schéma. Implicitement, si le MIPS envoie une requête de lecture alors il y aura une donnée qui va revenir, c'est obligatoire, alors on ne la dessine pas, car ce n'est pas intéressant. En revanche, le fait que le MIPS soit le seul composant à émettre des requêtes est une information intéressante.
    9494''
     
    111111''
    112112{{{#!asm
    113 lui   $4, 0xD020
    114 ori   $4, $4, 0x0000   // cette instruction ne sert a rien puisqu on ajoute 0, mais je la mets pour le cas general
    115 ori   $5, $0, 'x'
    116 sb    $5, 0($4)        // Notez que l immediat 0 devant ($4) n est pas obligatoire mais on s obligera als le mettre
     113lui   $4, 0xD020       // $4 <- 0xD0200000
     114ori   $5, $0, 'x'      // $5 <- code ascii de x
     115sb    $5, 0($4)        // TTY0.TTY_WRITE <- x
    117116}}}
    118117En principe, entre chaque écriture, il faut attendre un peu que le caractère précédent soit parti, parce que le débit du port de sortie matériel (USB par exemple) est beaucoup plus lent que ce que peut faire le processeur. Dans notre cas, c'est un simulateur de SoC et les caractères sont envoyés vers un terminal sans délai. Dans ce cas, il n'est pas nécessaire d'attendre.
    119118''
    120119}}}
    121 1. Un problème avec le code précédent est que l'adresse du `TTY` est un choix de l'architecte du prototype et s'il décide de placer le `TTY` ailleurs dans l'espace d'adressage, il faudra réécrire le code. Il est préférable d'utiliser une étiquette pour désigner cette adresse : on suppose désormais que l'adresse du premier registre du `TTY`  se nomme `__tty_regs_map`. Le code assembleur ne connait pas l'adresse, mais il ne connaît que le symbole. Ainsi, pour écrire `'x'` sur le terminal 0, nous devons utiliser la macro instruction `la $r, label`. Cette macro-instruction est remplacée lors de l'assemblage du code par une suite composée de deux instructions `lui` et `ori`. Il existe aussi la macro instruction `li` qui demande de charger une valeur sur 32 bits dans un registre. Pour être plus précis, les macro-instructions
     1201. Dans la question précédente, on connaissait l'adresse absolue du registre TTY_WRTIE. On suppose désormais que l'adresse du premier registre du `TTY`  se nomme `__tty_regs_map`. Le programmeur ne connait pas l'adresse, il ne connaît que le symbole. Ainsi, pour écrire `'x'` sur le terminal 0, nous devons utiliser la macro instruction `la $r, label`. Cette macro-instruction est remplacée lors de l'assemblage du code par les instructions `lui` et `ori`. Il existe aussi la macro instruction `li` qui demande de charger une valeur sur 32 bits dans un registre.\\\\Pour être plus précis, les macro-instructions
    122121{{{#!asm
    123122la $r, label
     
    126125 sont remplacées par
    127126{{{#!asm
    128 lui $r, label>>16
    129 ori $r, $r, label & 0xFFFF
    130 lui $r, 0x8765
    131 ori $r, $r, 0x4321
     127lui $r, label>>16              // chargement des 16 bits de poids forts de label
     128ori $r, $r, label & 0xFFFF     // chargement des 16 bits de poids faible de label
     129lui $r, 0x8765                 // chargement des 16 bits de poids fort de 0x8765431
     130ori $r, $r, 0x4321             // chargement des 16 bits de poids faible de 0x8765431
    132131}}}
    133132 Réécrivez le code de la question précédente en utilisant `la` et `li` (C9 S18)
     
    146145''
    147146* Il faut avoir compris que l'instruction `j` et toutes les branchements (`bne`, `beq`, etc.) sont relatives au PC. Il n'est pas possible d'aller n'importe où dans l'espace d'adressage.
    148 * `j label` malgré sa forme assembleur effectue un saut relativement au `PC` puisque le `label` n'est pas entièrement encodé dans l'instruction binaire (cf. cours sur les sauts). Cette instruction réalise :\\`PC ← (PC & 0xF0000000) | (ZeroExtended(label, 32) << 2)`\\Les 4 bits de poids forts du `PC` sont conservés, le saut est bien relatif au PC\\(`ZeroExtended` désigne ici le fait d'étendre le label sur 32 bits en ajoutant des zéros en tête).\\ Autrement dit, si `j label` est à l'adresse `PC`, l'adresse `label` doit avoir le même chiffre de poids fort (en hexa décimal), c'est-à-dire les mêmes 4 bits de poids fort en binaire). Sinon l'assembleur provoque une erreur lors du codage.
     147* `j label` effectue un saut relativement au `PC` puisque le `label` n'est pas entièrement encodé dans l'instruction binaire.\\Cette instruction réalise :\\`PC ← (PC & 0xF0000000) + (ZeroExtended(IMD26, 32) << 2)`\\Les 4 bits de poids forts du `PC` sont conservés, le saut est bien relatif au PC\\(`ZeroExtended` désigne ici le fait d'étendre le label sur 32 bits en ajoutant des zéros en tête).\\ Autrement dit, si l'instruction `j label` est à l'adresse `PC`, l'adresse `label` doit avoir le même chiffre de poids fort (en hexadécimal), c'est-à-dire les mêmes 4 bits de poids fort (b31 à b28) en binaire, sinon l'assembleur provoque une erreur lors du codage.
    149148* A l'inverse, `jr $r` effectue un saut absolu puisque cette instruction réalise `PC ← $r`
    150 
    151 Autrement dit, si l’on veut aller exécuter du code n'importe où en mémoire, il faut utiliser `jr`.
     149* En conséquence, si l’on veut sauter n'importe où en mémoire, il faut utiliser l'instruction `jr`.
    152150''
    153151}}}
     
    158156{{{#!protected ------------------------------------------------------------------------------------
    159157''
    160 * Pour répondre, il faut avoir compris l'explication donnée dans la question. L'intérêt de cette question est de revenir sur la notion de section. Une section est un segment d'adresses ayant un but spécifique. On définit des segments d'adresses pour le code, pour les données (il y a d'ailleurs plusieurs types de sections en fonction du type de données). Ces segments sont placés dans l'espace d'adressage par l'éditeur de liens (`ld`) et la manière dont ils sont placés est définie dans un fichier donné en paramètre de l'éditeur de lien, ce fichier de description de placement est le `ldscript`).
     158* Pour répondre, il faut avoir compris l'explication donnée dans la question. L'intérêt de cette question est de revenir sur la notion de section. Une section est un segment d'adresses ayant un but spécifique. On définit des sections pour le code, pour les différents types de données (initialisées, non initialisées, en lecture-seule, etc.). Ces sections sont placées dans l'espace d'adressage par l'éditeur de liens (`ld`) et la manière dont elles sont placées est définie dans un fichier nommé 'ldscript` donné en paramètre de l'éditeur de lien.
    161159{{{#!as
    162160 .section .mytext,"ax"
     
    165163''
    166164}}}
    167 1. À quoi sert la directive `.globl label` ? (C9 S18)
    168 {{{#!protected ------------------------------------------------------------------------------------
    169 ''
    170 * Ce qu'il faut comprendre, c'est que les comportements du `C` et de l'assembleur sont inversés vis-à-vis des labels. Dans un fichier `.c`, quand on définit un label (une fonction ou variable), ce label est par défaut `extern`, c'est-à-dire qu'il est utilisable dans un autre fichier `.c`. Si on veut que le label ne soit utilisable que dans le fichier dans lequel il est défini, il faut utiliser le mot clé `static` lors de sa déclaration. En assembleur, c'est l'inverse, les labels sont par défaut `static`, c'est-à-dire utilisable uniquement dans le fichier où ils sont définis. Si on veut qu'ils soient utilisables dans les autres fichiers, il faut le dire avec la directive `.globl`.
     1651. À quoi sert la directive `.globl label` dans un programme assembleur ? (C9 S18)
     166{{{#!protected ------------------------------------------------------------------------------------
     167''
    171168* `globl` signifie `glob`al `l`abel. Cette directive permet de dire que le `label` est visible en dehors de son fichier de définition. Ainsi il est utilisable dans les autres fichiers assembleur ou les autres fichiers C du programme.
    172 ''
    173 }}}
    174 1. Écrivez une séquence de code qui affiche la chaîne de caractère `"Hello"` sur `TTY0`. Ce n'est pas une fonction et vous pouvez utiliser tous les registres que vous voulez. Vous supposez que `__tty_regs_maps` est déjà défini.
    175 {{{#!protected ------------------------------------------------------------------------------------
    176 ''
    177 * Il faut laisser le temps d'écrire ce programme. Si la notion de section est comprise, si les macros `li` et `la` sont comprises, si l'écriture d'une boucle en assembleur est comprise, si l'usage du registre `write` du TTY est compris, alors c'est très facile, et si on donne la correction trop vite alors même ceux qui n'ont pas tout compris vont trouver ça évident.
    178 * Si les étudiants ne démarrent pas alors on peut donner la section `.data` et l'initialisation des registres `$4` et `$5`.
     169* Ce qu'il faut comprendre, c'est que les comportements du `C` et de l'assembleur vis-à-vis des labels sont inversés. Dans un fichier `C`, quand on définit un label (une fonction ou variable), ce label est par défaut `extern`, c'est-à-dire qu'il est utilisable dans un autre fichier `C`. Si on veut que le label ne soit utilisable que dans le fichier dans lequel il est défini, il faut utiliser le mot clé `static` lors de sa déclaration. En assembleur, c'est l'inverse, les labels sont par défaut `static`, c'est-à-dire utilisable uniquement dans le fichier où ils sont définis. Si on veut qu'ils soient utilisables dans les autres fichiers, il faut le dire avec la directive `.globl`.
     170''
     171}}}
     1721. Écrivez une séquence de code qui affiche la chaîne de caractère `"Hello"` sur `TTY0`.\\Vous devez déclarer la chaine de caractère `"Hello"` dans la section `.data`, puis écrire le code dans la section `.text`. Vous pouvez utiliser tous les registres que vous voulez. Vous supposez que le label `__tty_regs_maps` est déjà défini et qu'il désigne le premier registre de commande du `TTY0` qui est `TTY_WRITE`.\\C'est une boucle qui écrit chaque caractère de la chaîne dans le registre TTY_WRITE du TTY0, jusqu'à trouver le `0` de fin de chaîne.
     173{{{#!protected ------------------------------------------------------------------------------------
     174''
     175* On pourrait choisir d'autres registres.
    179176{{{#!asm
    180177.data
     
    195192{{{#!protected ------------------------------------------------------------------------------------
    196193''
    197 * On parle ici du pointeur de pile pour le `kernel`, on doit mettre le pointeur de pile dans la section des données du kernel. Ici, il n'y en a qu'une c'est `.kdata`. La pile est mise en haut de cette section aux adresses les plus grandes. Les adresses du bas de la section sont occupées par les données globales du kernel.
     194* On parle ici du pointeur de pile pour le `kernel`, on doit mettre le pointeur de pile dans la section des données du kernel. Ici, il n'y en a qu'une et c'est `.kdata`. La pile est mise en haut de cette section aux adresses les plus grandes. Les adresses du bas de la section sont occupées par les données globales du kernel.
    198195* La pile va être initialisée juste à la première adresse au-delà de la zone kdata donc\\`__kdata_end = 0x80020000` + `0x003E0000` = `0x80400000`\\Oui, on initialise le pointeur de pile a une adresse qui est en dehors du segment! Ce n'est pas un problème parce que le compilateur C n'écrira jamais à cette adresse, car elle n'est pas dans le contexte de la fonction C. La première chose qu'il fera, c'est décrémenter le pointeur de pile pour allouer le contexte de la fonction.
    199196* En effet, la première chose que fait une fonction, c'est décrémenter le pointeur de pile pour écrire `$31`, etc. Il faut donc que le pointeur ait été défini avant d'entrer dans la fonction.