Changes between Version 14 and Version 15 of Archi-1-TD9


Ignore:
Timestamp:
Nov 18, 2022, 10:07:54 AM (3 years ago)
Author:
franck
Comment:

--

Legend:

Unmodified
Added
Removed
Modified
  • Archi-1-TD9

    v14 v15  
    131131ori $r, $r, 0x4321
    132132}}}
    133  Réécrivez le code de la question précédente en utilisant `la` et `li`
    134 {{{#!protected ------------------------------------------------------------------------------------
    135 ''
    136 Cours 9 / slide 18\\\\
    137 Il suffit de remplacer les instructions `lui` et `ori` par `la` et `li`.
     133 Réécrivez le code de la question précédente en utilisant `la` et `li` (C9 S18)
     134{{{#!protected ------------------------------------------------------------------------------------
     135''
     136* Il suffit de remplacer les instructions `lui` et `ori` par `la` et `li`.
    138137{{{#!asm
    139138la    $4, __tty_regs_map
     
    146145{{{#!protected ------------------------------------------------------------------------------------
    147146''
    148 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.
     147* 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.
    149148* `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.
    150149* A l'inverse, `jr $r` effectue un saut absolu puisque cette instruction réalise `PC ← $r`
     
    159158{{{#!protected ------------------------------------------------------------------------------------
    160159''
    161 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`).
     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`).
    162161{{{#!as
    163162 .section .mytext,"ax"
     
    166165''
    167166}}}
    168 1. À quoi sert la directive `.globl label` ?
    169 {{{#!protected ------------------------------------------------------------------------------------
    170 ''
    171 Cours 9 / slide 18\\\\
    172 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`.
     1671. À 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`.
    173171* `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 fichier C du programme.
    174172''
     
    177175{{{#!protected ------------------------------------------------------------------------------------
    178176''
    179 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.\\
    180 Si les étudiants ne démarrent pas alors on peut donner la section `.data` et l'initialisation des registres `$4` et `$5`.
     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`.
    181179{{{#!asm
    182180.data
     
    197195{{{#!protected ------------------------------------------------------------------------------------
    198196''
    199 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 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.
     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 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.
    200198* 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 pour allouer le contexte de la fonction.
    201199* 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.
     
    209207
    210208
    211 = 3. Programmation en C
    212 
    213 
    214 
    215 
    216 Vous savez déjà programmer en C, mais vous allez voir des syntaxes ou des cas d'usage que vous ne connaissez peut-être pas encore.
    217 Les questions qui sont posées ici n'ont pas toutes été vues en cours, mais vous connaissez peut-être les réponses, sinon ce sera l'occasion d'apprendre.
    218 
    219 **Questions
    220 
    221 1. Quels sont les usages du mot clé `static` en C ?  (c'est une directive que l'on donne au compilateur C)
    222 {{{#!protected ------------------------------------------------------------------------------------
    223 ''
    224 Le cours 9 n'en parle pas, mais dans le code vous trouverez cette directive un peu partout. Il y a deux usages, on a déjà parlé du premier, mais pas encore du second.
    225 1. Déclarer `static` une variable globale ou une fonction en faisant précéder leur définition du mot clé `static` permet de limiter la visibilité de cette variable ou de cette fonction au seul fichier de déclaration. Notez que par défaut les variables et les fonctions du C ne sont pas `static`, il faut le demander explicitement. C'est exactement l'inverse en assembleur où tout label est implicitement  `static` ; il faut demander avec la directive `.globl` de le rendre visible.
    226 1. Déclarer `static` une variable locale permet de la rendre persistante, c'est-à-dire qu'elle conserve sa valeur entre deux appels. Cette variable locale n'est pas dans le contexte de la fonction (c'est-à-dire qu'elle n'est pas dans la pile parce que le contexte est libéré en sortie de fonction). Une variable locale `static` est en fait allouée comme une variable globale mais son usage est limité à la seule fonction où elle est définie.
    227 ''
    228 }}}
    229 1. Pourquoi déclarer des fonctions ou des variables `extern` ?
    230 {{{#!protected ------------------------------------------------------------------------------------
    231 ''
    232 Ça n'ont plus ce n'est pas dit dans le cours mais c'est sensé être connu, sinon c'est qu'il y a des choses à apprendre. Notez que la directive externe est implicite en C et qu'on peut donc ne pas l'écrire. On la met pour la lisibilité du code.
    233 * Les déclarations `extern` permettent d'informer que le compilateur qu'une variable ou qu'une fonction existe et est définie ailleurs. Le compilateur connaît ainsi le type de la variable ou du prototype des fonctions, il sait donc comment les utiliser. En C, par défaut, les variables et les fonctions doivent être déclarées / leur existence et type doit être connus avant leur utilisation.
    234 * Il n'y a pas de déclaration `extern` en assembleur parce que ce n'est pas un langage typé. Pour l'assembleur, un label c'est juste une adresse donc un nombre.
    235 ''
    236 }}}
    237 1. Comment déclarer un tableau de structures en variable globale ? La structure est nommée `test_s`, elle a deux champs `int` nommés `a` et `b`. Le tableau est nommé `tab` et a 2 cases.
    238 {{{#!protected ------------------------------------------------------------------------------------
    239 ''
    240 Là encore, ce sont des connaissances censées être connues, mais c'est important parce qu'on a besoin de le comprendre pour la déclaration des registres du TTY.
    241 {{{#!c
    242 struct test_s {
    243   int a;
    244   int b;
    245 };
    246 struct test_s tab[2];
    247 }}}
    248 ''
    249 }}}
    250 1. Supposons que la structure `tty_s` et le tableau de registres de `TTY` soient définis comme suit. Écrivez une fonction C `int getchar(void)` bloquante qui attend un caractère tapé au clavier sur le `TTY0`. Nous vous rappelons qu'il faut attendre que le registre `TTY_STATUS` soit différent de 0 avant de lire `TTY_READ`. `NTTYS` est un `#define` définit dans le Makefile de compilation avec le nombre de terminaux du SoC (en utilisant l'option `-D` de gcc).
    251 {{{#!c
    252 struct tty_s {
    253     int write;          // tty's output
    254     int status;         // tty's status something to read if not null)
    255     int read;           // tty's input
    256     int unused;         // unused
    257 };
    258 extern volatile struct tty_s __tty_regs_map[NTTYS];
    259 }}}
    260 {{{#!protected ------------------------------------------------------------------------------------
    261 ''
    262 Cours 9 / slide 10\\\\
    263 En principe, cela ne devrait pas poser de difficulté, mais cela nécessite d'avoir compris comment fonctionne le TTY et de savoir écrire une fonction en C. Pour aider, au cas où, on peut donner la description de ce qui est attendu:\\Tant que le registre `status` est à 0 alors attendre, puis lire le registre `read`.\\Notez que cela nécessite de savoir accéder aux champs d'une structure.
    264 {{{#!c
    265 int getchar(void)
    266 {
    267    while (__tty_regs_map[0].status == 0);
    268    return __tty_regs_map[0].read;
    269 }
    270 }}}
    271 ''
    272 }}}
    273 1. Savez-vous à quoi sert le mot clé `volatile` ? Nous n'en avons pas parlé en cours, mais c'est nécessaire pour les adresses des registres de périphérique, une idée ... ?
    274 {{{#!protected ------------------------------------------------------------------------------------
    275 ''
    276 Ce n'est pas dit dans le cours, mais c'est un concept important. Quand le programme doit aller chercher une donnée dans la mémoire puis faire plusieurs calculs dessus, le compilateur optimise en réservant un registre du processeur pour cette variable afin de ne pas être obligé d'aller lire la mémoire à chaque fois. Mais, il y a des cas où ce comportement n'est pas souhaitable (il est même interdit). C'est le cas pour les données qui se trouvent dans les registres de périphériques. Ces données peuvent être changées par le périphérique sans que le processeur le sache, de sorte qu'une valeur lue par le processeur à l'instant `t` n'est plus la même (dans le registre du périphérique) à l'instant `t+1`. Le compilateur ne doit pas optimiser, il doit aller chercher la donnée en mémoire à chaque fois que le programme le demande.
    277 * `volatile` permet de dire à `gcc` que la variable en mémoire peut changer à tout moment, elle est volatile. Ainsi quand le programme demande de lire une variable `volatile` le compilateur doit toujours aller la lire en mémoire. Il ne doit jamais chercher à optimiser en utilisant un registre afin de réduire le nombre de lecture mémoire (load). De même, quand le programme écrit dans une variable `volatile`, cela doit toujours provoquer une écriture dans la mémoire (store).
    278 * Ainsi, les registres de périphériques doivent toujours être impérativement lus ou écrits à chaque fois que le programme le demande, parce que c'est justement ces lectures et ces écritures qui commandent le périphérique.
    279 ''
    280 }}}
    281 
    282 
    283 
    284 
    285 = 4. Compilation
     209= 3. Compilation
    286210
    287211
     
    290214Pour obtenir le programme exécutable, nous allons utiliser :
    291215* `gcc -o file.o -c file.c`
    292   - Appel du compilateur avec l'option `-c` qui demande à `gcc` de faire le préprocessing puis la compilation c pour produire le fichier objet `file.o`
     216  - Appel du compilateur avec l'option `-c` qui demande à `gcc` de faire le ''préprocessing'' puis la compilation C pour produire le fichier objet `file.o`
    293217* `ld -o bin.x -Tkernel.ld files.o ...`
    294218  - Appel de l'éditeur de liens pour produire l'exécutable `bin.x` en assemblant tous les fichiers objets `.o`, en les plaçant dans l'espace d'adressage et résolvant les liens entre eux. \\Autrement dit, quand un fichier `h.o` utilise une fonction `fg()` ou une variable `vg` définie dans un autre fichier `g.o` (`h` et `g` sont là pour illustrer), alors l'éditeur de liens place dans l'espace d'adressage les sections `.text` et `.data` des fichiers `h.o` et `g.o`, puis il détermine alors quelles sont les adresses de `fg()` et `vg` en mémoire et il complètent les instructions de `h` qui utilisent ces adresses.
     
    296220  - Appel du désassembleur qui prend les fichiers binaires (`.o` ou `.x`) pour retrouver le code produit par le compilateur à des fins de debug ou de curiosité.
    297221
    298 **Questions**
     222**Questions sur l'édition de lien**
    299223
    300224Le fichier `kernel.ld` décrit l'espace d'adressage et la manière de remplir les sections dans le programme exécutable. Ce fichier est utilisé par l'éditeur de lien. C'est un `ldscript`, c'est-à-dire un [https://www.wikiwand.com/fr/Langage_de_script `script`] pour `ld`.
     
    325249}}}
    326250
    327 1. Le fichier `kernel.ld` commence par la déclaration des variables donnant des informations sur les adresses et les tailles des régions de mémoire. Ces symboles n'ont pas de type et ils sont visibles de tous les programmes C, il faut juste leur donner un type pour que le compilateur puisse les exploiter, c'est ce que nous avons fait pour `extern volatile struct tty_s __tty_regs_map[NTTYS]`. En regardant dans le dessin de la représentation de l'espace d'adressage, complétez les lignes de déclaration des variables pour la région `kdata_region`
    328 {{{#!protected ------------------------------------------------------------------------------------
    329 ''
    330 Cours 9 / slides 22 et 23\\\\
    331 Pour répondre, il faut savoir interpréter le dessin représentant l'espace d'adressage. Si ça semble difficile, il faut revoir ce qu'est l'espace d'adressage.
     2511. Le fichier `kernel.ld` commence par la déclaration des variables donnant des informations sur les adresses et les tailles des régions de mémoire. Ces symboles n'ont pas de type et ils sont visibles de tous les programmes C, il faut juste leur donner un type pour que le compilateur puisse les exploiter, c'est ce que nous avons fait pour `extern volatile struct tty_s __tty_regs_map[NTTYS]`. En regardant dans le dessin de la représentation de l'espace d'adressage, complétez les lignes de déclaration des variables pour la région `kdata_region`. Pour répondre, il faut savoir interpréter le dessin représentant l'espace d'adressage. (C9 S7+S22+S23)
     252{{{#!protected ------------------------------------------------------------------------------------
     253''
     254* Si ça semble difficile, il faut revoir ce qu'est l'espace d'adressage.
    332255{{{#!c
    333256__kdata_origin   = 0x80020000 ;
     
    336259''
    337260}}}
    338 1. Le fichier contient ensuite la déclaration des régions (dans `MEMORY{...}`) qui seront remplies par l'éditeur de lien avec les sections trouvées dans les fichiers objets selon un ordre décrit dans la partie `SECTIONS{}` du `ldscript`.  Complétez cette partie (la zone `[... question 2 ...]`) pour ajouter les lignes correspondant à la déclaration de la région `kdata_region` ?
    339 {{{#!protected ------------------------------------------------------------------------------------
    340 ''
    341 Cours 9 / slides 23 et 24\\\\
    342 La syntaxe est assez explicite, cela ne devrait pas poser de problème.
     2611. Le fichier contient ensuite la déclaration des régions (dans `MEMORY{...}`) qui seront remplies par l'éditeur de lien avec les sections trouvées dans les fichiers objets selon un ordre décrit dans la partie `SECTIONS{}` du `ldscript`.  Complétez cette partie (la zone `[... question 2 ...]`) pour ajouter les lignes correspondant à la déclaration de la région `kdata_region` ? (C9 S22+S23)
     262{{{#!protected ------------------------------------------------------------------------------------
     263''
     264* La syntaxe est assez explicite, cela ne devrait pas poser de problème.
    343265{{{#!c
    344266kdata_region : ORIGIN = __kdata_origin, LENGTH = __kdata_length
     
    346268''
    347269}}}
    348 1.  Enfin le fichier contient comment sont remplies les régions avec les sections. Complétez les lignes correspondant à la description du remplissage de la région `ktext_region`. Vous devez la remplir avec les sections `.text` issus de tous les fichiers.
    349 {{{#!protected ------------------------------------------------------------------------------------
    350 ''
    351 Cours 9 / slides 22 et 23\\\\
    352 Il faut bien comprendre que `.ktext` est une section produite par l'éditeur de liens. C'est ce que l'on appelle une section de sortie. `.text` est une section que l'éditeur de liens trouve dans un fichier objet `.o`, c'est ce que l'on appelle une section d'entrée. Comme il y a plusieurs fichiers objet, on doit dire à l'éditeur de lien de prendre toutes les sections `.text` de tous les fichiers qu'on lui donne. Le `*` devant `(.text)` est une expression régulière permettant de dire à l'éditeur de liens quels fichiers sont concernés, ici avec `*` c'est tous les fichiers. Les expressions régulières sont celles qu'on utilise avec le `shell`.
     2701.  Enfin le fichier contient comment sont remplies les régions avec les sections. Complétez les lignes correspondant à la description du remplissage de la région `ktext_region`. Vous devez la remplir avec les sections `.text` issus de tous les fichiers.\\Il faut bien comprendre que `.ktext` est une section produite par l'éditeur de liens. C'est ce que l'on appelle une section de sortie. `.text` est une section que l'éditeur de liens trouve dans un fichier objet `.o`, c'est ce que l'on appelle une section d'entrée. Comme il y a plusieurs fichiers objet, on doit dire à l'éditeur de lien de prendre toutes les sections `.text` de tous les fichiers qu'on lui donne. (C9 S22+S23)
     271{{{#!protected ------------------------------------------------------------------------------------
     272''
     273* Le `*` devant `(.text)` est une expression régulière permettant de dire à l'éditeur de liens quels fichiers sont concernés, ici avec `*` c'est tous les fichiers. Les expressions régulières sont celles qu'on utilise avec le `shell`.
    353274{{{#!c
    354275.ktext : {
     
    358279''
    359280}}}
     281
     282**Questions sur les Makefiles**
    360283 
    361284Nous allons systématiquement utiliser des Makefiles pour la compilation du code, mais aussi pour lancer le simulateur du prototype **almo1**. Pour cette première séance, les Makefiles ne permettent pas de faire des recompilations partielles de fichiers. Les Makefiles sont utilisés pour agréger toutes les actions que nous voulons faire sur les fichiers, c'est-à-dire : compiler, exécuter avec ou sans trace, nettoyer le répertoire. Nous avons recopié partiellement le premier Makefile pour montrer sa forme et poser quelques questions, auxquels vous savez certainement répondre.
     285
     286La syntaxe des `Makefile`s peut-être très complexe (c'est un vieux langage), ici nous ne verrons qu'une petite partie. Notez que le Makefile `voit` les variables du `shell` comme s'il les avait définies lui même.
    362287{{{#!make
    363288# Tools and parameters definitions
     
    402327    -rm *.o* *.x* *~ *.log.* proc?_term? 2> /dev/null || true
    403328}}}
    404 4. Au début du fichier se trouve la déclaration des variables du Makefile, quelle est la différence entre `=`, `?=` et `+=` ?
    405 {{{#!protected ------------------------------------------------------------------------------------
    406 ''
    407 Ce n'est pas expliqué dans le cours, mais c'est utilisé dans les `Makefile`s. La syntaxe des `Makefile`s peut-être très complexe (c'est un vieux langage), ici nous ne verrons qu'une partie. La question sur la déclaration des variables optionnelles est intéressante si on fait le parallèle avec ce qu'il faut faire pour avoir le même comportement avec le préprocesseur et ses `#ifndef` (on a une autre méthode encore pour le `shell`.\\A notez que le Makefile `voit` les variables du `shell` comme s'il les avait définies lui même.
     3294. Où est utilisé `CFLAGS` ? Que fait `-DNTTYS=$(NTTY)` et pourquoi est-ce utile ici ? (C9 annexe S8)
     330{{{#!protected ------------------------------------------------------------------------------------
     331''
     332* Le compilateur C peut avoir beaucoup de paramètres. Définir une variable `CFLAGS` permet de les déclarer une fois au début et d'utiliser cette variable plusieurs fois dans le `Makefile`. Si on veut changer un argument, il suffit de le faire une seule fois. Ce genre de choses est nécessaire si on veut faire des `Makefile`s facile à lire et à faire évoluer.
     333* La variable `CFLAGS` est utilisée par `gcc`, il y a ici toutes les options indispensables pour compiler mais il en existe beaucoup, ce qui fait des tonnes de combinaison d'options !
     334* `-DNTTYS=$(NTTY)` permet de définir et donner une valeur à une macro (ici définition `NTTYS` avec la valeur `$(NNTY)` comme le fait un `#define` dans un fichier C. Cette commande éviter donc d'ouvrir les codes pour les changer.
     335''
     336}}}
     3371. Si on exécute `make` sans cible, que se passe-t-il ? (C9 annexe S6)
     338{{{#!protected ------------------------------------------------------------------------------------
     339''
     340* Mettre une règle `help` comme règle par défaut permet de documenter l'usage du `Makefile`, ce qui est plutôt une bonne pratique quand il y a beaucoup de cible et de paramètres. C'est d'autant plus vrai qu'on utilise les Makefiles comme des ensembles de `shell script`.
     341* C'est la première cible qui est choisie, donc ici c'est équivalent à `make help`. Cela affiche l'usage pour connaître les cibles disponibles.
     342''
     343}}}
     344Réponses non présentes dans les slides, mais utiles à savoir.
     3456. à quoi servent  `@` et `-` au début de certaines commandes ?
     346{{{#!protected ------------------------------------------------------------------------------------
     347''
     348* `@` permet de ne pas afficher la commande avant son exécution. On peut rendre ce comportement systématique en ajoutant la règle `.SILENT:` n'importe où dans le fichier.
     349* `-`  permet de ne pas stopper l'exécution des commandes même si elles rendent une erreur, c'est-à-dire une valeur de sortie différente de 0.
     350''
     351}}}
     3527. Au début du fichier se trouve la déclaration des variables du Makefile, quelle est la différence entre `=`, `?=` et `+=` ?
     353{{{#!protected ------------------------------------------------------------------------------------
     354''
    408355* `=`  fait une affectation simple
    409356* `?=` fait une affection de la variable si elle n'est pas déjà définie comme variable d'environnement du shell ou dans la ligne de commande de make, par exemple avec `FROM`
     
    411358''
    412359}}}
    413 1. Où est utilisé `CFLAGS` ? Que fait `-DNTTYS=$(NTTY)` et pourquoi est-ce utile ici ?
    414 {{{#!protected ------------------------------------------------------------------------------------
    415 ''
    416 Cours 9 / Slide 8 annexe\\\\
    417 Le compilateur C peut avoir beaucoup de paramètres. Définir une variable `CFLAGS` permet de les déclarer une fois au début et d'utiliser cette variable plusieurs fois dans le `Makefile`. Si on veut changer un argument, il suffit de le faire une seule fois. Ce genre de choses est nécessaire si on veut faire des `Makefile`s facile à lire et à faire évoluer.
    418 * La variable `CFLAGS` est utilisée par `gcc`, il y a ici toutes les options indispensables pour compiler mais il en existe beaucoup, ce qui fait des tonnes de combinaison d'options !
    419 * `-DNTTYS=$(NTTY)` permet de définir et donner une valeur à une macro (ici définition `NTTYS` avec la valeur `$(NNTY)` comme le fait un `#define` dans un fichier C. Cette commande éviter donc d'ouvrir les codes pour les changer.
    420 ''
    421 }}}
    422 1. Si on exécute `make` sans cible, que se passe-t-il ?
    423 {{{#!protected ------------------------------------------------------------------------------------
    424 ''
    425 Cours 9 / slides 6 annexe\\\\
    426 Mettre une règle `help` comme règle par défaut permet de documenter l'usage du `Makefile`, ce qui est plutôt une bonne pratique quand il y a beaucoup de cible et de paramètres. C'est d'autant plus vrai qu'on utilise les Makefiles comme des ensembles de `shell script`.
    427 * C'est la première cible qui est choisie, donc ici c'est équivalent à `make help`. Cela affiche l'usage pour connaître les cibles disponibles.
    428 ''
    429 }}}
    430 1. à quoi servent  `@` et `-` au début de certaines commandes ?
    431 {{{#!protected ------------------------------------------------------------------------------------
    432 ''
    433 Ce n'est pas dit en cours, mais comme c'est utilisé, il n'est pas inutile de le savoir.
    434 * `@` permet de ne pas afficher la commande avant son exécution. On peut rendre ce comportement systématique en ajoutant la règle `.SILENT:` n'importe où dans le fichier.
    435 * `-`  permet de ne pas stopper l'exécution des commandes même si elles rendent une erreur, c'est-à-dire une valeur de sortie différente de 0.
    436 ''
    437 }}}
     360
     361
     362
     363
     364
     365= 4. Programmation en C
     366
     367
     368
     369
     370Vous savez déjà programmer en C, mais vous allez voir des syntaxes ou des cas d'usage que vous ne connaissez peut-être pas encore.
     371Les questions qui sont posées ici n'ont pas toutes été vues en cours, mais vous connaissez peut-être les réponses, sinon ce sera l'occasion d'apprendre.
     372
     373**Questions
     374
     3751. Quels sont les usages du mot clé `static` en C ?  (c'est une directive que l'on donne au compilateur C)
     376{{{#!protected ------------------------------------------------------------------------------------
     377''
     378* Le cours 9 n'en parle pas, mais dans le code vous trouverez cette directive un peu partout. Il y a deux usages.
     379 1. Déclarer `static` une variable globale ou une fonction en faisant précéder leur définition du mot clé `static` permet de limiter la visibilité de cette variable ou de cette fonction au seul fichier de déclaration. Notez que par défaut les variables et les fonctions du C ne sont pas `static`, il faut le demander explicitement. C'est exactement l'inverse en assembleur où tout label est implicitement  `static` ; il faut demander avec la directive `.globl` de le rendre visible.
     380 1. Déclarer `static` une variable locale permet de la rendre persistante, c'est-à-dire qu'elle conserve sa valeur entre deux appels. Cette variable locale n'est pas dans le contexte de la fonction (c'est-à-dire qu'elle n'est pas dans la pile parce que le contexte est libéré en sortie de fonction). Une variable locale `static` est en fait allouée comme une variable globale mais son usage est limité à la seule fonction où elle est définie.
     381''
     382}}}
     3831. Pourquoi déclarer des fonctions ou des variables `extern` ?
     384{{{#!protected ------------------------------------------------------------------------------------
     385''
     386* Ça n'ont plus ce n'est pas dit dans le cours mais c'est sensé être connu, sinon c'est qu'il y a des choses à apprendre. Notez que la directive externe est implicite en C et qu'on peut donc ne pas l'écrire. On la met pour la lisibilité du code.
     387* Les déclarations `extern` permettent d'informer que le compilateur qu'une variable ou qu'une fonction existe et est définie ailleurs. Le compilateur connaît ainsi le type de la variable ou du prototype des fonctions, il sait donc comment les utiliser. En C, par défaut, les variables et les fonctions doivent être déclarées / leur existence et type doit être connus avant leur utilisation.
     388* Il n'y a pas de déclaration `extern` en assembleur parce que ce n'est pas un langage typé. Pour l'assembleur, un label c'est juste une adresse donc un nombre.
     389''
     390}}}
     3911. Comment déclarer un tableau de structures en variable globale ? La structure est nommée `test_s`, elle a deux champs `int` nommés `a` et `b`. Le tableau est nommé `tab` et a 2 cases.
     392{{{#!protected ------------------------------------------------------------------------------------
     393''
     394* Là encore, ce sont des connaissances censées être connues, mais c'est important parce qu'on a besoin de le comprendre pour la déclaration des registres du TTY.
     395{{{#!c
     396struct test_s {
     397  int a;
     398  int b;
     399};
     400struct test_s tab[2];
     401}}}
     402''
     403}}}
     4041. Supposons que la structure `tty_s` et le tableau de registres de `TTY` soient définis comme suit. Écrivez une fonction C `int getchar(void)` bloquante qui attend un caractère tapé au clavier sur le `TTY0`. Nous vous rappelons qu'il faut attendre que le registre `TTY_STATUS` soit différent de 0 avant de lire `TTY_READ`. `NTTYS` est un `#define` définit dans le Makefile de compilation avec le nombre de terminaux du SoC (en utilisant l'option `-D` de gcc). (C9 S10)
     405{{{#!c
     406struct tty_s {
     407    int write;          // tty's output
     408    int status;         // tty's status something to read if not null)
     409    int read;           // tty's input
     410    int unused;         // unused
     411};
     412extern volatile struct tty_s __tty_regs_map[NTTYS];
     413}}}
     414{{{#!protected ------------------------------------------------------------------------------------
     415''
     416* En principe, cela ne devrait pas poser de difficulté, mais cela nécessite d'avoir compris comment fonctionne le TTY et de savoir écrire une fonction en C. Pour aider, au cas où, on peut donner la description de ce qui est attendu:\\Tant que le registre `status` est à 0 alors attendre, puis lire le registre `read`.\\Notez que cela nécessite de savoir accéder aux champs d'une structure.
     417{{{#!c
     418int getchar(void)
     419{
     420   while (__tty_regs_map[0].status == 0);
     421   return __tty_regs_map[0].read;
     422}
     423}}}
     424''
     425}}}
     4261. Savez-vous à quoi sert le mot clé `volatile` ? Nous n'en avons pas parlé en cours, mais c'est nécessaire pour les adresses des registres de périphérique, une idée ... ?
     427{{{#!protected ------------------------------------------------------------------------------------
     428''
     429* Ce n'est pas dit dans le cours, mais c'est un concept important. Quand le programme doit aller chercher une donnée dans la mémoire puis faire plusieurs calculs dessus, le compilateur optimise en réservant un registre du processeur pour cette variable afin de ne pas être obligé d'aller lire la mémoire à chaque fois. Mais, il y a des cas où ce comportement n'est pas souhaitable (il est même interdit). C'est le cas pour les données qui se trouvent dans les registres de périphériques. Ces données peuvent être changées par le périphérique sans que le processeur le sache, de sorte qu'une valeur lue par le processeur à l'instant `t` n'est plus la même (dans le registre du périphérique) à l'instant `t+1`. Le compilateur ne doit pas optimiser, il doit aller chercher la donnée en mémoire à chaque fois que le programme le demande.
     430* `volatile` permet de dire à `gcc` que la variable en mémoire peut changer à tout moment, elle est volatile. Ainsi quand le programme demande de lire une variable `volatile` le compilateur doit toujours aller la lire en mémoire. Il ne doit jamais chercher à optimiser en utilisant un registre afin de réduire le nombre de lecture mémoire (load). De même, quand le programme écrit dans une variable `volatile`, cela doit toujours provoquer une écriture dans la mémoire (store).
     431* Ainsi, les registres de périphériques doivent toujours être impérativement lus ou écrits à chaque fois que le programme le demande, parce que c'est justement ces lectures et ces écritures qui commandent le périphérique.
     432''
     433}}}