--- tags: - sorbonne - informatique - architecture-des-ordinateurs semestre: 3 --- ## Registres Un registre de $n$ bits est un composant capable de mémoriser un mot binaire de $n$ bits |> changement de valeur possible uniquement lors de front montant/descendant du signal de l’horloge |> émission de la valeur contenue dans le registre en continu |> les registres dépendent du processeur -> contient toutes les informations utilisées par le processeur Tous les registres du Mips font 32 bits et en possèdent 32 |> les registres sont nommés par leur numéro PC (Program Counter) = adresse de l’instruction en cours d’exécution (ou la suivante) |> modifié après l’exécution de chaque instruction IR (Instruction Register) = instruction en cours de traitement HI/LO (High/Low) = les registres contenant le résultat d’opérations de multiplication ou de division Il y a d'autres registres qu'on n'utilisera pas en cours > [!info] L'architecture moderne du Mips est le RISC-V Processus d'exécution : 1. Lire une instruction en mémoire (dans IR) 2. Décoder l'instruction 3. Exécuter l'instruction 4. Calculer l'adresse de l'instruction suivante : mettre à jour le PC Utilisation des registres : - `$0` contient la valeur 0 -> est le générateur du 0 - `$1` registre réservé à l'assembleur (programme qui génère le binaire) - `$2 - $3` contiennent les résultats des appels de fonction - `$2` peut aussi contenir le numéro d'appel système - `$4 - $7` (est aussi appelé de `a0` à `a3`) utilisés pour le passage d'arguments lors des appels de fonctions ou appels systèmes - `$8 - $15` contiennent les valeurs non persistantes (libre) - `$16 - $25` contiennent les valeurs persistantes (libre) - `$26 - $27` contiennent les valeurs OS -> voir les diapos pour les autres Les valeurs persistantes gardent les valeurs avant les appels > [!NOTE] Notation des registres > En Mips, on peut les notés avec `R`, `r` ou `$`, ainsi `R1 = r1 = $1` > [!danger] On **doit** respecter les règles d'utilisation ## Jeu d'instruction La vue externe d'un CPU peut être définie par l'ensemble des instructions qu'il est capable de traiter Jeu d'instruction d'un CPU (aussi appelé ISA) est la donnée : - de l'ensemble des instructions qu'il peut effectuer - le codage de ces instructions en binaire Une instruction, c'est une commande définissant le traitement à effectuer et quelle sera la prochaine instruction à exécuter -> le traitement séquentiel est implicite On peut spécifier quelle autre ligne à utiliser après l'instruction en cours -> c'est un « saut » Le code d'opération définit quelle opération utiliser |> elle porte sur les opérandes |> opérandes immédiates sont codées dans l'instruction |> les autres opérandes sont dans des registres indiqués `add $4, $2, $5` signifie `$4 <- $2 + $5` `ori $4, $2, 0xABCF` signifie `$4 <- $2 | (0x0000 ABCF)` `addi $4, $2, 0xABCF`signifie `$4 <- $2 + (0xFFFF ABCF)` car, par défaut, les entiers sont considérés comme relatifs `mult $3, $4` signifie `(HI/LO) <- $3 × $4` `div $3, $4` signifie `(HI/LO) <- $3 ÷ $4` (`HI` contient le quotient et `LO` le reste) On peut définir un label pour savoir où sauter 4 classes d'instructions : - arithmétique et logique -> addition, and... - transfert mémoire -> lire la mémoire... - rupture de séquence -> faire un saut... - appels systèmes -> lire un caractère, écrire un entier sur l'écran Voir le memento pour la liste des instructions L'instruction `ori` permet de placer une certaine valeur dans un registre |> `ori $2, $0, 0x1234` place `0x1234` dans `$2` Les instructions en Mips possèdent 3 formats : - R -> quand on utilise 3 registres - I -> quand on fait des calculs avec des immédiats - J -> quand on fait des sauts | Nom\n° de bit | 31 - 26 | 25 - 21 | 20 - 16 | 15 - 11 | 10 - 6 | 5 - 0 | | ------------- | ------- | ------- | ------- | ------- | ------ | ----- | | **R** | OPCODE | RS | RT | RD | SH | FUNC | | **I** | OPCODE | RS | RT | IMM | IMM | IMM | | **J** | OPCODE | JUMP | JUMP | JUMP | JUMP | JUMP | OPCODE est spécifié dans un codage normé |> détecte le format utilisé en fonction de l'OPCODE Le codage normé ne contient pas tous les OPCODE |> s'il n'est pas dedans, l'OPCODE est le "special" et l'opération est dans la case FUNC |> l'opération dans FUNC est aussi dans un codage normé On regarde le memento pour savoir ce que signifie RS, RT et RD |> j'ai l'impression que le registre contenant le résultat est toujours le dernier affiché, mais c'est à vérifier SH permet d'utiliser le shift Langage haut niveau : - $\forall$ ISA - notions de type - peut créer des variables - structure les traitements - gestions d'erreurs Assembleur : - Allocation des données et gestion mémoire - Suite d'instructions spécifiques - Présence d'étiquettes pour désigner les adresses (données ou instructions) Un programme de haut niveau peut être : - natif, i.e. il est compilé pour être exécuté sur la machine cible - interprété, i.e. un programme natif interprète le programme et l'exécuté Nous, on ne regarde que les programmes natifs Assemblage = assembleur -> binaire Désassemblage = binaire -> assembleur Un label (ou étiquette) s'écrit comme : `nom: add $4, $4, $3` |> ici le label `nom` désigne la ligne `add $4, $4, $3` -> elles ne sont pas conservées par lors de l'assemblage En Mips, toujours deux sections différentes : 1. les données du programme 2. la section de code Directive `.data` permet de dire que la suite sera des données Directive `.text` indique que la suite sera des instructions On met toujours `.data`, y compris si c'est vide (dans le cadre de cette UE) Pour exécuter un programme, on a besoin de le charger |> le mettre en mémoire |> mettre dans PC la première adresse à exécuter En Mips, les syscall se font à l'aide de `syscall` |> il cherche toujours le numéro de l'appel dans `$2` -> se finit donc toujours par ```asm ori $2, $0, 10 # place 10 dans $2 syscall # syscall dans $2, i.e. syscall 10, i.e. fin du programme ``` On utilise le simulateur Mars pour écrire / exécuter des programmes ## Lancer un programme Pour exécuter un programme, on a besoin de le lancer |> besoin de le charger en mémoire -> souvent, le *loader* s'en occupe |> chargé en RAM depuis l'adresse de base contenant le début du programme |> modifie PC pour contenir l'adresse du début (adresse en RAM) ## System call Un appel système est une demande de service fourni par le système |> est un garde fou |> `syscall` appelle ces services |> chaque service possède son numéro en Mips |> on met le numéro du service dans `$2` |> pour passer des argument à `syscall`, on doit les mettre dans les registres `$4` à `$7` Pour afficher 125 : ```asm ori $4, $0, 125 ori $2, $0, 1 syscall ``` `syscall` est un déroutement -> est gérer par l'OS ## Données en mémoire Variables globales et partie `.data` On peut voir la mémoire en mode "mot" (32 bits) ou en "octet" (8 bits) |> représente la même mémoire |> "mot" est 4 fois plus grand qu'un octet Mips ne peut lire que 4 octets (ou un mot, ou 32 bits) à la fois |> adresse mémoire d'un mot est l'adresse la plus petite des 4 octets |> octet est l'unité adressable -> pas possible d'être plus petit que ça On utilise toujours les notations en Kio Mémoire non réinscriptible = ROM, lente d'accès, contient le code de démarrage ou l'ensemble du code dans certains systèmes embarqués Mémoire vive = RAM, volatile et rapide Mémoire flash (USB, SD Card) = non volatile, peut écrire (mais pas beaucoup) RAM garde les données en mémoire et ne les supprime pas jusqu'à ce qu'on écrit dessus |> lecture est non destructive (pas vraie partout !) Écriture RAM -> *store* Lecture RAM -> *load* Type de stockage en RAM : - Big Endian (grand boutien) -> l'octet de poids fort est rangé à l'adresse la plus petite - Little Endian (petit boutien) -> l'octet de poids faible est rangé à l'adresse la plus petite -> avec les mêmes bits en RAM, on n'obtient pas le même mot ! Mips est en Little Endian Après avoir écrit `0xAABBCCDD` à `0x4`, on obtient | Adresse | `0x0` | `0x1` | `0x2` | `0x3` | `0x4` | `0x5` | `0x6` | `0x7` | `0x8` | | ------- | ----- | ----- | ----- | ----- | ------ | ------ | ------ | ------ | ----- | | Contenu | ? | ? | ? | ? | `0xDD` | `0xCC` | `0xBB` | `0xAA` | ? | On peut écrire en mot, demi-mot ou octet Mémoire est structuré pour éviter d'avoir des problèmes des sécurités |> programme ne peut pas utiliser la RAM de l'OS par exemple -> elle est d'abord coupé en deux |> une partie utilisateur et une autre système |> est séparée par une adresse (tous ceux qui sont inférieures sont dans l'utilisateur en Mips) Mémoire utilisateur, c'est : 1. segment de code (le code du programme) 2. segment de données (données et variables globales) 3. segment de pile (variables locales et contextes d'appels) -> stack 4. (le tas dans la pile, mais on n'en a pas ici) **Besoin de lire le diapo 22/48** (et un peu avant) Le transfert RAM -> CPU en Mips doit être sur la même ligne ! Les contraintes d'alignement sont respectées par défaut quand utilise les données globales |> quand utilise `.space`, on a besoin d'utiliser `.align` pour aligner correctement les zones libres Quand on utilise la mémoire, on a besoin de copier les valeurs dans les registres et d'après la mettre à jour Signature des instructions d'accès mémoire : `Codop Rt, Imm16(Rs)` |> `Rt` est la destination ou le registre source |> `Imm16(Rs)` est la zone mémoire avec `Rs` étant le décallage On se place tjs du côté du processeur pour les opcodes |> `l.` servent à *load* |> `s.` servent à *store* |> `.w` gèrent les mots (4 octets) |> `.h` gèrent les half-words (2 octets) |> `.b` gèrent les octets (1 octet) ```asm lh $4, 4($3) # load le half-word contenu dans l'adresse 4 + $3 dans $4 lw $4, -2(3) # load le word contenu dans l'adresse -2 + 3 dans $4 ``` Par défaut, tout est signé, si on veut être en non signé, on rajoute `u`, i.e. `lhu` pour récupérer un `uint16`