Changes between Version 44 and Version 45 of AS6-TME-B1
- Timestamp:
- Jan 31, 2023, 6:37:21 PM (17 months ago)
Legend:
- Unmodified
- Added
- Removed
- Modified
-
AS6-TME-B1
v44 v45 1156 1156 1157 1157 1158 Le prototype de SoC que nous utilisons pour les TP est configurable. Il est possible par exemple de choisir le nombre de terminaux texte (TTY). Par défaut, il y en a un mais, nous pouvons en avoir jusqu'à 4. Nous allons modifier le code du noyau pour s'adapter à cette variabilité. En outre, nous allons ajouter un niveau d'abstraction qui représente un début de pilote de périphérique (device driver). Ce pilote, même tout petit constitue une couche logicielle avec une API.1158 Le prototype de SoC que nous utilisons pour les TP est configurable. Il est possible par exemple de choisir le nombre de terminaux texte (TTY). Par défaut, il y en a un mais, nous pouvons en avoir jusqu'à 4. Nous allons modifier le code du noyau pour s'adapter à cette variabilité. En outre, nous allons ajouter un niveau d'abstraction qui représente un début de pilote de périphérique (device driver). Ce pilote, même tout petit, constitue une couche logicielle avec une API. 1159 1159 1160 1160 **Objectifs** … … 1188 1188 {{{#!protected ------------------------------------------------------------------------------------ 1189 1189 ''''''''''''''' 1190 * La fonction qui lit ce registre (`$9` qui ne désigne pas un registre GPR du processeur !) est nécessairement en assembleur car elle utilise des instructions particulières et dépend du matériel, elle est donc mise dans hcpua.S.1190 * La fonction qui lit ce registre (`$9` qui ne désigne pas un registre GPR du processeur !) est nécessairement en assembleur car elle utilise des instructions particulières et dépend du matériel, elle est donc mise dans `hcpua.S`. 1191 1191 * `.globl clock` permet de faire en sorte que la fonction soit visible par les autres fichiers C. 1192 1192 ''''''''''''''' 1193 1193 }}} 1194 1. Compilez et exécutez le code avec `make exec`. Observez. Ensuite ouvrez le fichier `kernel.x.s` et regardez où a été placée la fonction `clock()`.\\Est-ce un problème si `kinit()` n'est plus au début du segment `ktext` ? Po sez-vous la question de qui a besoin de connaître l'adresse de `kinit()`1195 {{{#!protected ------------------------------------------------------------------------------------ 1196 ''''''''''''''' 1197 * Non, ce n'est pas un problème puisque ça fonctionne. Le code de boot a besoin de l'adresse de `kinit()` mais on l'obtient avec la macro `la` - c'est l'éditeur de lien qui fera en sorte que dans les code binaire l'adresse de `kinit()` mise dans le registre `$26` soit la bonne.1194 1. Compilez et exécutez le code avec `make exec`. Observez. Ensuite ouvrez le fichier `kernel.x.s` et regardez où a été placée la fonction `clock()`.\\Est-ce un problème si `kinit()` n'est plus au début du segment `ktext` ? Pour répondre, posez-vous la question de qui a besoin de connaître l'adresse de `kinit()` 1195 {{{#!protected ------------------------------------------------------------------------------------ 1196 ''''''''''''''' 1197 * Non, ce n'est pas un problème puisque ça fonctionne. Le code de boot a besoin de l'adresse de `kinit()` mais on l'obtient avec la macro `la` . C'est l'éditeur de lien qui fera en sorte que dans les code binaire l'adresse de `kinit()` mise dans le registre `$26` soit la bonne. Notez que cela fonctionne parce qu'on fait l'édition de lien du noyau et du code de boot pour fabrique un seul binaire (`kernel.x`), ce qui n'est pas le cas dans un vrai système, savez-vous pourquoi ? 1198 1198 ''''''''''''''' 1199 1199 }}} … … 1202 1202 1203 1203 - Ecrire une fonction `void Capitalize(void)` appelée par la fonction `kinit()` qui lit une phrase terminée par un `\n` et la réécrit en ayant mis en majuscule la première lettre de chaque mot. Vous mettrez cette nouvelle fonction dans le fichier `kinit.c` (ce ne devrait pas être sa place mais c'est juste un exercice). Notez que vous ne pouvez pas utiliser la fonction [http://manpagesfr.free.fr/man/man3/toupper.3.html `toupper()`] parce que c'est une fonction de la `glibc` (la bibliothèque de la librairie de fonctions standards) et que là vous ne l'avez pas. Vous n'êtes pas sur Linux :-) 1204 {{{#!protected ------------------------------------------------------------------------------------ 1205 ''''''''''''' 1206 - Dans cette fonction `void Capitalize(void)`, pour chaque caractère lu au clavier, vous devez tester s'il est compris entre 'a' et 'z' et si oui, ajouter ('A' - 'a') ... 1207 ''''''''''''' 1208 }}} 1204 1209 1205 1210 … … 1274 1279 1275 1280 * Le numéro du processeur est dans les 12 bits de poids faible du registre $15 (`c0_cpuid`) du coprocesseur système (à côté des registres `c0_epc`, `c0_sr`, etc.). Ajoutez la fonction `int cpuid(void)` qui lit le registre `c0_cpuid` et qui rend un entier contenant juste les 12 bits de poids faible.\\Vous pouvez vous inspirez fortement de la fonction `int clock(void)`. Comme il n'y a qu'un seul processeur dans cette architecture, `cpuid` rend toujours `0`.\\Ecrivez un programme de test (vous devrez modifier les fichiers `hcpu.h`, `hcpua.S` et `kinit.c`) 1276 1277 1281 {{{#!protected 1278 1282 **hcpua.S** … … 1303 1307 1304 1308 1305 L'application utilisateur n'est pas censée utiliser directement les appels système. Elle utilise une librairie de fonctions standards (la `libc` POSIX, mais pas seulement) et ce sont ces fonctions qui réalisent les appels système. Toutes les fonctions de la `libc` n'utilisent pas les appels système. Par exemple, les fonctions `int rand(void)` ou `int strlen(char *)` (rendent, respectivement, un nombre pseudoaléatoire et la longueur d'une chaîne de caractères) n'ont pas besoin du noyau. Les librairies font partie du système d'exploitation mais elles ne sont pas dans le noyau.1309 L'application utilisateur n'est pas censée utiliser directement les appels système. Elle utilise une librairie de fonctions standards (la `libc` POSIX, mais également d'autres) et ce sont ces fonctions qui réalisent les appels système. Toutes les fonctions de la `libc` n'utilisent pas les appels système. Par exemple, les fonctions `int rand(void)` ou `int strlen(char *)` (rendent, respectivement, un nombre pseudoaléatoire et la longueur d'une chaîne de caractères) n'ont pas besoin du noyau. Les librairies font partie du système d'exploitation mais elles ne sont pas dans le noyau. 1306 1310 1307 1311 ''Le terme « librairie » vient de l'anglais « library » qui signifie bibliothèque. On utilise souvent le mot librairie même si le sens en français n'est pas le même que celui en anglais. Disons que, dans notre contexte, les deux mots sont synonymes.'' … … 1310 1314 1311 1315 L'exécutable de l'application utilisateur est donc composé de deux parties : d'un côté, le code de l'application et, de l'autre, le code de la librairie `libc` (+ `crt0`). Nous allons répartir le code dans deux répertoires `uapp` pour les fichiers de l'application et `ulib` pour les fichiers qui ne sont pas l'application, c'est-à-dire la `libc`, le fichier `crt0.c` mais aussi le fichier ldscript `user.ld`. 1316 1317 On rappelle que le fichier `crt0.c` contient le code d'entrée dans l'application avec la fonction `_start()` appelée par la fonction `kinit()`. C'est aussi, dans ce fichier que l'on met le code assembleur de la fonction `syscall_fct()` permettant de revenir dans le noyau. En conséquence, `crt0.c`, c'est le pont entre le noyau et l'application. 1312 1318 1313 1319 … … 1350 1356 ''''''''''''''' 1351 1357 - Ils sont tous dans le fichier `libc.h`. 1352 - Non, pour POSIX, les prototypes de fonctions de la libc sont répart is dans plusieurs fichiers suivant leur rôle. Il y `stdio.h`, `string.h`, `stdlib.h`, etc. Nous n'avons pas voulu ajouter cette complexité.1358 - Non, pour POSIX, les prototypes de fonctions de la libc sont répart:q:qis dans plusieurs fichiers suivant leur rôle. Il y `stdio.h`, `string.h`, `stdlib.h`, etc. Nous n'avons pas voulu ajouter cette complexité. 1353 1359 ''''''''''''''' 1354 1360 }}} … … 1375 1381 **Exercice** 1376 1382 1383 1384 Pour finir ce TME (un peu long 🤪 ), vous allez juste ajouter une boucle d'affichage des caractères ASCII au début de la fonction `main()` en utilisant la fonction de la libc `fputc(tty,c)` (avec `tty` à 0 pour un affichage sur le terminal `0`, et `c` la variable contenant le caractère à afficher, qui prendra toutes les valeurs entre 32 et 127. 1385 1386 Je vous donne le code dans le corrigé, mais ça fait seulement 2 lignes, alors je pense que vous n'en aurez pas besoin ! 🙂 1387 {{{#!protected ------------------------------------------------------------------------------------ 1388 ''''''''''''''' 1389 {{{#!c 1390 for (int c = 32; c < 127; c++) { 1391 fputc (0, c); 1392 } 1393 }}} 1394 ''''''''''''''' 1395 }}} 1396 - Ensuite, quand ça marche, exécutez le programme en mode débug (`make debug` au lieu de `make exec`) et ouvrez le fichier `trace0.s`. A quel cycle, commence la fonction `main()` ? 1397 {{{#!protected ------------------------------------------------------------------------------------ 1398 ''''''''''''''' 1399 - 7351 (sur ma machine, peut-être que cela peut varier si, entre temps, j'ai mis une autre version du simulateur) 1400 ''''''''''''''' 1401 }}} 1402 - Recompilez le kernel en utilisant le mode `-O0`(lettre 0 suivie du chiffre zéro), réexécutez l'application en mode debug et regardez à nouveau à quelle cycle commence la fonction `main()` ? 1403 {{{#!protected ------------------------------------------------------------------------------------ 1404 ''''''''''''''' 1405 - 20603 (sur ma machine) c'est presque 3 fois plus lent !!! 1406 ''''''''''''''' 1407 }}} 1408 - Pour finir, recompilez à nouveau le noyau en utilisant le mode `-O3`, réexécutez encore l'application en mode debug et regardez combien de cycles sont nécessaire pour exécuter la fonction `fputc()`. Pour ça, vous ouvrez la fonction 1409 {{{#!comment 1377 1410 Vous allez utiliser le composant DMA. En cours, nous avons vu que le DMA réalise un copie de mémoire à partir de l'adresse contenu dans le registre DMA_SRC vers l'adresse contenu dans le registre DMA_DST du nombre d'octet contenu dans le registre DMA_LEN. 1378 1411 Dans l'ordre, on commence par écrire les adresses SRC, DST et IRQ_DISABLE (si besoin), puis on écrit LEN, ce qui provoque le démarrage de la copie par le DMA