22 | | Mais l'exploitation de cette bibliothèque de modèles de simulation pose (au moins) deux problèmes : |
23 | | |
24 | | 1. Il faut identifier et localiser tous les fichiers nécessaires pour générer le simulateur d'une architecture particulière. L' archive qui vous a été fournie pour le TP3 rassemblait dans un seul répertoire la centaine de fichiers nécessaires, et le Makefile vous était fourni. Mais dans le cas général, l'identification des fichiers nécessaires à la compilation est un travail non négligeable, à cause des dépendances entre composants logiciels (le fichier A fait référence à des objets définis dans le fichier B, qui lui-même fait appel au fichier C, etc.). De ce fait, la construction du Makefile est généralement un exercice laborieux. |
25 | | |
26 | | 2. Par ailleurs, la plupart des modèles ont des paramètres templates (puisque la plupart des composants ont des interfaces VCI, et que les largeurs des champs VCI sont paramétrables). Pour chaque composant possédant un (ou plusieurs) paramètre(s) template, il faut donc modifier le fichier ''.cpp'' pour préciser la valeur des paramètres template avant de lancer la compilation de ce composant (on dit qu'on instancie les ''template''). Vous avez fait ce travail dans le TP2, et c'est un travail très fastidieux dès que les architectures modélisées deviennent complexes. |
27 | | |
28 | | La chaîne de compilation '''soclib-cc''' a pour but de résoudre ces deux problèmes dans le cas général, |
29 | | en automatisant la recherche des dépendances, l'instanciation des templates, et l'appel du compilateur. |
30 | | |
31 | | Pour permettre cette automatisation, tout composant logiciel de SoCLib doit être accompagné d'un fichier |
32 | | de ''metadata'' (fichier possédant le suffixe {{{.sd}}}) qui contient les informations suivantes: |
33 | | * le nom de la classe C++ |
34 | | * les paramètres templates associés, avec leurs types et les valeurs par défaut (si applicable) |
35 | | * les chemins d'accès aux fichiers d'en-tête (.h) et d'implémentation (.cpp) |
36 | | * la liste des ports d'interface du composant |
37 | | * la liste des dépendances vers d'autres composants |
38 | | * les paramètres du constructeur, avec leurs types |
39 | | Ce fichier est écrit en un langage ad-hoc (mais que Python peut parser nativement), et on trouvera ci-dessous, à titre d'exemple, le fichier ''vci_simple_ram.sd'': |
40 | | {{{ |
41 | | |
42 | | Module('caba:vci_simple_ram', |
43 | | classname = 'soclib::caba::VciSimpleRam', |
44 | | tmpl_parameters = [parameter.Module('vci_param', default = 'caba:vci_param')], |
45 | | header_files = ['../source/include/vci_simple_ram.h',], |
46 | | implementation_files = ['../source/src/vci_simple_ram.cpp'], |
47 | | ports = [ |
48 | | Port('caba:vci_target', 'p_vci'), |
49 | | Port('caba:bit_in', 'p_resetn', auto = 'resetn') |
50 | | Port('caba:clock_in', 'p_clk', auto = 'clock')], |
51 | | uses = [ |
52 | | Uses('caba:base_module'), |
53 | | Uses('common:linked_access_buffer', |
54 | | addr_t = parameter.StringExt('sc_dt::sc_uint<%d>', parameter.Reference('addr_size')), |
55 | | id_t = parameter.StringExt('sc_dt::sc_uint<%d>', parameter.Reference('srcid_size'))), |
56 | | Uses('common:loader'), |
57 | | Uses('common:mapping_table',], |
58 | | instance_parameters = [ |
59 | | parameter.IntTab('ident'), |
60 | | parameter.Module('mt', 'common:mapping_table', auto='env:mapping_table'), |
61 | | parameter.Module('loader', 'common:loader', auto='env:loader'), |
62 | | parameter.Int('latency')], |
63 | | extensions = [ |
64 | | 'dsx:addressable=ident', |
65 | | 'dsx:get_ident=ident:p_vci', |
66 | | 'dsx:mapping_type=memory'], |
67 | | ) |
68 | | }}} |
69 | | |
70 | | Il faut par ailleurs définir les caractéristiques de la top-cell dans un fichier de directives pour soclib-cc. |
71 | | Ce fichier est habituellement nommé '''platform.desc''', mais le nom n'est pas imposé. |
72 | | Ce fichier est également en langage parsable par Python, et contient : le nom de fichier de la top-cell SystemC, la liste des modèles des composants instanciés et les valeurs des paramètres template VCI. |
73 | | Vous trouverez ci-dessous, à titre d'exemple, le fichier '''tp3.desc''' décrivant l'architecture du TP3: |
74 | | |
75 | | {{{ |
76 | | todo = Platform('caba', 'tp3_top.cpp', |
77 | | uses = [ |
78 | | Uses('caba:vci_xcache_wrapper', iss_t = 'common:mips32el'), |
79 | | Uses('caba:vci_simple_ram'), |
80 | | Uses('caba:vci_multi_tty'), |
81 | | Uses('caba:vci_vgsb'), |
82 | | Uses('caba:vci_gcd_coprocessor'), |
83 | | Uses('common:mapping_table'), |
84 | | Uses('common:elf_file_loader')], |
85 | | cell_size = 4, |
86 | | plen_size = 8, |
87 | | addr_size = 32, |
88 | | rerror_size = 1, |
89 | | clen_size = 1, |
90 | | rflag_size = 1, |
91 | | srcid_size = 12, |
92 | | pktid_size = 1, |
93 | | trdid_size = 1, |
94 | | wrplen_size = 1 |
95 | | ) |
96 | | }}} |
97 | | |
98 | | = 3 Communications par interruption = |
| 23 | = 2 Communications par interruption = |
121 | | Pour communiquer avec un périphérique, un programme utilisateur peut donc utiliser |
122 | | un tampon mémoire partagé DATA, protégé par une variable de synchronisation SYNC. |
123 | | Supposons qu'un programme utilisateur souhaite lire un caractère sur un terminal TTY. |
124 | | Plutôt que d'effectuer un appel système bloquant (qui effectue une scrutation directement sur le registre STATUS |
125 | | du TTY), le programme utilisateur va appeler une fonction de communication qui s'exécute en mode ''user'', et qui |
126 | | effectue une scrutation sur la variable SYNC. Le tampon est partagé entre le périphérique TTY et le programme |
127 | | utilisateur : |
128 | | * Le périphérique TTY écrit dans le tampon DATA et active la variable SYNC (en déclenchant l'exécution de la routine d'interruption). |
129 | | * Le programme utilisateur lit dans le tampon DATA et désactive la variable SYNC. |
| 46 | Pour communiquer avec un périphérique, un programme utilisateur utilise |
| 47 | un tampon mémoire partagé '''_tty_get_buf''', protégé par une variable de synchronisation '''-tty_get_full'''. |
| 48 | Ces deux variables appartiennent au système d'exploitation et sont stockées dans le segment ''seg_kunc'', |
| 49 | qui est à la fois protégé (non accessible par les programmes utilisateur) et non cachable. |
| 50 | |
| 51 | Si un programme utilisateur souhaite lire un caractère sur un terminal TTY, il utilise l'appel système |
| 52 | ''tty_getc_irq()'', défini dans le fichier ''stdio.c'' du GIET. |
| 53 | Plutôt que d'effectuer une scrutation sur le registre STATUS du contrôleur TTY, cet appel système |
| 54 | va tester la variable '''_tty_get_full''', ce qui permet (en principe) au système d'exploitation d'attribuer le processeur |
| 55 | à un autre programme utilisateur si te tampon est vide. C'est la routine d'interruption (ISR) associée au |
| 56 | terminal TTY qui se charge d'écrire le code ASCII du caractère dans le tampon '''_tty_get_buf''', et de |
| 57 | forcer à 1 la variable de synchronsation '''_tty_get_full'''. Cette variable de synchronisation est remise à 0 |
| 58 | par l'appel système ''tty_getc_irq()'' lorsque le caractère est transféré du tampon système '''tty_get_buf''' |
| 59 | vers le tampon mémoire défini par l'utilisateur. |