Changes between Initial Version and Version 1 of CaoCourseTme7


Ignore:
Timestamp:
Mar 22, 2007, 11:47:47 AM (18 years ago)
Author:
alain
Comment:

--

Legend:

Unmodified
Added
Removed
Modified
  • CaoCourseTme7

    v1 v1  
     1{{{
     2#!html
     3<h1>TME 7 : Simulation logici-temporelle</h1>
     4}}}
     5[[PageOutline]]
     6
     7= Objectif =
     8
     9On souhaite réaliser dans ce tme un petit simulateur logico-temporel, permettant de simuler
     10un réseau Booléen temporisé, où les expressions Booléennes sont représentées par des arbres ABL (voir TME5).
     11
     12Les simulateurs à événements discrets permette de simuler des systèmes matériels constitués d'un ensemble
     13de composants matériels interconnectés par des signaux. Les signaux électriques véhiculent fondamentatement
     14deux tensions VSS et VDD représentant respectivement les valeurs Booléennes 0 et 1, mais le simulateur doit traiter
     15plus de valeurs pour gérer les cas spéciaux comme les signaux en haute impédance, les conflits électriques, ou les
     16valeurs indéfinies.  A titre indicatif, le VHDL standard utilise 9 valeurs pour les signaux. 
     17Pour simplifier nous nous limiterons dans ce TME aux trois valeurs  0, 1, et U (indéfini).
     18
     19Dans le cas général, Chaque composant est modélisé par un processus séquentiel, et tous les processus s'exécutent en parallèle.
     20Chaque processus utilise les valeurs de ses signaux d'entrées pour calculer les valeurs de ses signaux de sortie.
     21Les signaux sont donc orientés, et un signal possède un seul émetteur, mais peut avoir plusieurs destinataires.
     22On définit, pour chaque processus, un sous-ensemble des signaux d'entrée, appelé liste de sensibilité du processus :
     23un changement de valeur sur un signal appartenant à la liste de sensibilité peut entraîner un changement de valeur
     24sur un signal de sortie du processus. Un processus doit donc être évalué à chaque fois que l'un des signaux de la liste de sensibilité
     25change de valeur. Par exemple, la liste de sensibilité d'un processus représentant un automate de Moore ne comporte
     26qu'un seul signal, qui est le signal d'horloge.
     27
     28On s'intéresse dans ce TME au cas particulier des réseaux Booléens, où un processus correspond à une expression Booléenn
     29multi-niveaux, représentée par un arbre ABL. Dans ce cas particulier, un processus possède donc un seul signal de sortie,
     30et la liste de sensibilité contient tous les signaux d'entrée. Un réseau Booléen peut être représenté par un graphe biparti
     31comportant deux types de noeuds : des processus et des signaux. Les noeuds à la périphérie du réseau sont toujours des signaux.
     32En d'autres termes, un processus a toujours au moins un signal entrant et un signal sortant.  Les signaux qui n'ont pas d'arrête
     33entrante sont les entrées primaires du réseau, les signaux qui n'ont pas d'arrête sortante sont les sorties primaires du réseau. 
     34Les autres signaux sont appelés signaux internes.
     35
     36On appelle événement le changement de valeur d'un signal à un certain instant. Un événement est donc défini par un triplet (signal, date, valeur).
     37
     38Il faut évidemment respecter le principe de causalité, c'est-à-dire qu'il ne peut y avoir d'événement sur le signal de sortie  d'un processus que
     39s'il y a eu préalablement au moins un événement sur un signal d'entrée du processus. Simuler le fonctionnement d'un circuit consiste donc
     40à calculer pour chaque signal la succession des événements pour chaque signal, appelée forme d'onde.
     41L'ensemble des formes d'ondes de tous les signaux constitue un chronogramme.
     42
     43La simulation suppose que l'on possède une fonction d'évaluation qui calcule la valeur du signal de sortie du processus en fonction
     44de la valeur des signaux d'entrée. Dans notre cas, il faudra disposer d’une fonction eval_abl() capable de traiter les trois valeurs (0,1,U).
     45 
     46== B) Structures de données
     47On utilise deux structures de données :
     48 * structure représentant le réseau Booléen, c'est à dire le graphe biparti des processus et des signaux,
     49 * structure représentant l'échéancier, c'est à dire l'ensemble ordonné des événements.
     50 
     51= B1) Le réseau Booléen
     52
     53Le réseau Booléen est constitué d'un ensemble de signaux et d'un ensemble de processus.
     54Les signaux sont regroupés dans trois sous-ensembles disjoints suivant leur type (IN, OUT, INTERNAL).
     55{{{
     56typedef struct boolnet_t {
     57char                    * NAME;         // nom du circuit modélisé
     58signal_t        * IN ;                  // ensemble des signaux IN
     59signal_t                * OUT ;         // ensemble des signaux OUT
     60signal_t                * INTERNAL;     // ensemble des signaux INTERNAL
     61process_t       * PROCESS;      // ensemble des proceessus
     62} boolnet_t;
     63}}}
     64L’ordre dans les listes des signaux et des processus n'est pas significatif. Ils sont chaînés dans l'ordre de leur création.
     65
     66Un signal possède un type, un pointeur sur une variable Booleenne (var_t), qui définit son nom et sa valeur (0,1 ou U).
     67Il possède également un pointeur sur le processus dont il est la sortie, et un pointeur sur la liste chaînée des processus dont il est une entrée.
     68{{{
     69typedef struct signal_t {
     70boolnet_t               * BOOLNET;              // pointeur sur le réseau Booléen
     71sigtype_t       TYPE;                   // type du signal
     72var_t                   * VAR;                  // variable Booléenne associée
     73bip_t                   * TO_PROCESS;           // liste des process destinataires
     74process_t               * FROM_PROCESS; // process source
     75struct signal_t         * NEXT;                 // signal suivant de même type
     76} signal_t;
     77}}}
     78Le type pourra prendre une des valeurs du type enuméré suivant :
     79{{{
     80enum sigtype_t {
     81IN = 0,
     82OUT = 1,
     83INTERNAL = 2};
     84}}}           
     85Un processus est une expression Booléenne qui définit la valeur du signal de sortie, en fonction des valeurs des signaux d’entrée.
     86Il est caractérisé par un temps de propagation qui est le retard entre un événement sur une entrée et l'événement sur la sortie
     87qui en est la conséquence. Dans le cas général, les temps de propagation dépendent de l'entrée qui commute,
     88mais pour simplifier le simulateur, on suppose que le temps de propagation ne dépend pas de l'entrée qui commute.
     89{{{
     90typedef struct process_t {
     91boolnet_t               * BOOLNET;              // pointeur sur le réseau Booléen
     92signal_t                        * SIGNAL;               // pointeur sur le signal de sortie
     93bip_t                           * ABL;          // pointeur sur l’arbre ABL associé
     94long                            * DELAY;                // retard entrée -> sortie (en ps)
     95bip_t                           * SUPPORT;      // liste des signaux d’entrée
     96struct process_t        * NEXT;         // processus suivant
     97} process_t
     98}}}
     99
     100B2) L’échéancier
     101
     102L'échéancier permet d'enregistrer  et d'ordonner les événements dans le temps. Ceux-ci sont donc rangés dans une liste doublement chaînée, par dates croissantes. Un événement représente une affection de valeur à un signal à une certaine date.
     103 
     104typedef struct event_t {
     105signal_t                * SIGNAL;               // signal modifié
     106unsigned                  VALUE;                // nouvelle valeur
     107long                      DATE;         // date de la modification
     108struct event_t  * PREV ;                // événement précédent dans l’echéancier
     109struct event_t  * NEXT;         // événement suivnt dans l’echéancier
     110} event_t;
     111
     112L'échéancier peut contenir plusieurs événements à la même DATE, mais portant sur des signaux différents. Ces événements synchrones sont rangés consécutivement dans la liste chaînée, mais l'ordre entre ces événements synchrones n'est pas  significatif. L'échéancier représente la liste ordonnée des événements passés, présents, et futurs. Il contient donc en particulier la variable TC qui représente la date courante.
     113
     114 typedef struct scheduler_t {
     115 long             TC;                   // Temps Courant
     116 event_t        * CURRENT;              // pointeur sur le premier événement à TC
     117 event_t        * FIRST;                // pointeur sur le premier événement de la liste
     118 } scheduler_t
     119
     120C/ Fonctions d'accès aux structures de données
     121
     122Ce paragraphe définit les différentes fonctions permettant d’accéder aux structures de données définies ci-dessus. Dans un premier temps le code binaire exécutable de ces fonction vous est fourni, et vous pourrez utiliser ces fonctions pour construire en mémoire le réseau Booléen, construire et initialiser l’échéancier, et écrire la boucle principale de simulation qui a été présentée en cours. Dans un deuxième temps, il vous sera demandé d’écrire vous-même le code de ces fonctions.
     123
     124C1) Création d'un réseau Booléen "vide"
     125
     126boolnet_t * cons_boolnet (char * name)
     127
     128Les pointeurs sur les listes de signaux et de processus sont initialisés à NULL.
     129 
     130C2) Création d’un signal
     131
     132signal_t * cons_signal (boolnet_t * bn, sigtype_t type, var_t * var)
     133
     134Cette fonction prend pour argument une variable Booléenne préalablement créée. Elle crée un signal et l’insère dans la liste des signaux du réseau correspondant à son type. Elle initialise à NULL les pointeurs FROM_PROCESS et TO_PROCESS. La valeur de la variable Booléenne est forcée à U.
     135   
     136C3) Création d’un processus
     137 
     138process_t * cons_process (boolnet_t * bn, signal_t * sig, char * abl, long delay)
     139
     140Cette fonction prend pour argument un pointeur sur le signal de sortie, un pointeur sur un arbre ABL préalablement créé (en utilisant par exemple  la fonction parse_abl du TME5), et la valeur du temps de propagation.
     141Elle crée une structure process_t, calcule le support de l'abl (sous forme d’une liste de signaux d’entrée), et met à jour les pointeurs contenus dans les structures représentant les signaux impliqués. Cette fonction vérifie le typage des signaux, c'est-à-dire qu'un signal de type IN ne peut avoir de processus générateur et qu'un signal de type OUT ne peut apparaître dans le support d'une fonction.
     142   
     143C4) Création d’un échéancier « vide »
     144 
     145scheduler_t * cons_scheduler()
     146
     147Cette fonction fabrique un échéancier « vide ». La date courante  TC est initialisée à une valeur négative, et les pointeurs FIRST et CURRENT sont initialisés à NULL.
     148
     149C5) Création d’un événement
     150
     151event_t * cons_event(scheduler_t * sch, signal_t sig, long date, unsigned val)
     152
     153Cette  fonction construit un événement  sur le signal sig, à un certaine date , avec la valeur val, et introduit cet événement dans l’échéancier. Cette fonction met à jour les deux pointeurs FIRST et CURRENT dans l’échéancier, si cela est nécessaire.
     154Cette fonction ne doit être utilisée que dans la phase d’initialisation, pour introduire dans l’échéancier les événements portant sur les signaux d’entrée du circuit (stimuli).
     155 
     156C6) Insertion d’un événement
     157
     158void add_event (scheduler_t * sch, signal_t sig, long delta, unsigned val)
     159
     160Cette fonction construit un événement portant sur le signal sig, à la date TC + delta, avec la valeur val, et le range dans l'échéancier. A la différence de la précédente, cette fonction utilise un temps relatif (delta), et ne modifie pas le les pointeurs FIRST et CURRENT. C’est cette fonction qui doit être utilisée dans la boucle de simulation.
     161
     162Dans le cas général, cette fonction doit en principe vérifier qu’il n’existe pas un autre événement postérieur sur le même signal, et retirer cet événement parasite s’il y a lieu. Dans notre cas, cela n’est pas nécessaire. Cette importante simplification est dûe à l'hypothèse qu'un processus est caractérisé par un unique temps de propagation, qui ne dépend pas de l'entrée qui commute. Avec cette hypothèse, tous les événements prévus se produisent effectivement.
     163   
     164C7) Récupération des événements au temps TC
     165
     166bip_t * get_events (scheduler_t * sch)
     167
     168Cette fonction recherche dans l'échéancier les événements prévus au temps TC. Il peut y avoir plusieurs événements à la même date. Elle renvoie ces événements sous forme d'une liste chaînée de bi-pointeurs (qu'il faut penser à libérer quand ils ne sont plus utiles).
     169
     170C8) Progression du temps simulé
     171
     172unsigned update_tc(scheduler_t * sch)
     173
     174Cette fonction modifie la valeur de la variable TC en lui donnant la valeur de la date du premier événement strictement postérieur au temps courant. Elle met à jour le pointeur CURRENT de l’échéancier. Cette fonction renvoie la valeur 0 quand elle ne trouve pas d’événement postérieur au temps courant, ce qui correspond à la fin de la simulation. Cette valeur doit donc être testée à chaque itération de la boucle de simulation.
     175
     176C9) Mise à jour des signaux
     177
     178bip_t * update_signals ( bip_t * events)
     179
     180Cette fonction prend pour argument un pointeur sur une liste de signaux (liste de bipointeurs). Elle modifie la valeur des variables Booléennes associées à chacun des signaux pour lesquels il existe un événement. Elle rend la liste des signaux qui ont été modifiés (une autre liste de bipointeurs, qu’il faudra également libérer quand ils ne seront plus utilisés).
     181
     182C10) Récupération des processus à évaluer
     183
     184bip_t * get_processes (boolnet_t * bn, bip_t * signals)
     185
     186Cette fonction prend comme argument un pointeur sur le réseau Booléen, et une liste de signaux, et elle renvoie un pointeur sur une autre liste contenant l’ensemble de tous les processus ayant au moins un signal d’entrée appartenant à l’ensemble signals. (ici encore, il faut penser à libérer la mémoire occupée par les bi-pointeurs quand ils ne sont plus utiles).
     187
     188C11) Evaluation d’un process
     189
     190void eval_processes( scheduler_t * sch, bip_t * p)
     191
     192Cette fonction prend pour argument une liste de processus, ainsi qu’un pointeur sur le scheduler, et évalue la valeur du signal de sortie pour chacun des processus de la liste,  en fonction des valeurs des signaux d’entrée. Elle utilise pour cela une version de la fonction eval_abl() modifiée pour traiter une logique à trois valeurs (0,1,U). Elle compare la valeur future du signal de sortie à sa valeur présente, et insère un événement dans  l’échéancier si cette valeur change (en utilisant la fonction add_event).
     193
     194 D/ Travail à réaliser
     195
     196On se propose de simuler le schéma suivant, qui réalise un additionneur 2 bits.
     197A chaque porte est associé un processus modélisé par un arbre ABL.
     198
     199
     200
     201
     202
     203
     204
     205
     206
     207
     208
     209
     210
     211
     212
     213
     214
     215
     216
     217
     218
     219D1) Construction du réseau Booléen
     220
     221Utiliser les fonctions cons_boolnet(), cons_process() et cons_signal() pour construire en mémoire le réseau Booléen correspondant au circuit-ci-dessus. On prendra une valeur de 1 ns pour le temps de propagation de la porte NAND2, et de 2 ns pour la porte XOR2. Ecrire, compiler et exécuter le programme main() qui construit le réseau Booléen. Pour vérifier la structure du réseau Booléen, on pourra utiliser la fonction :
     222
     223void drive_boolnet(dotfmt_t format, boolnet_t *bn)
     224
     225Cette fonction construit une représentation graphique du réseau Booléen, et la sauvegarde dans un fichier au format .gif ou .ps.
     226
     227enum dotfmt_t { GIF , PS }
     228
     229D2) Construction et initialisation de l’échéancier
     230
     231Complêter le programme main() de la question D1 pour créer l’échéancier et initialiser les événements sur les signaux d’ entrée a0, b0, c0, a1 et b1 de façon à respecter le chronogramme suivant . On utilisera les fonctions cons-scheduler() et add_event(). Attention : le passage de la valeur U (indéfinie) à une valeur 0 ou 1 constitue un événement : Dans ce chronogramme, il y a donc un événement sur tous les signaux d’entrée au temps T = 0.
     232
     233
     234Pour vérifier que l’échéancier est correctement initialisé, on pourra utiliser la fonction:
     235
     236void drive_scheduler (boolnet_t * bn, scheduler_t * sch)
     237
     238Cette fonction génére un fichier au format .pat que vous pouvez visualiser avec l’outil XPAT que vous connaissez déjà.
     239
     240
     241
     242
     243
     244
     245
     246
     247
     248
     249
     250
     251
     252
     253
     254
     255
     256
     257
     258
     259
     260
     261
     262
     263D3) Ecriture de la boucle de simulation
     264
     265Ecrire la boucle fonction principale de simulation qui enchaîne les phases “update” et “exécute” présentées en cours.
     266
     267void simulate (boolnet_t * bn, scheduler_t * sch)
     268
     269On utilisera pour cela les fonctions get_events(), update_tc(), update_signals(), get_process() et eval_process().
     270
     271On sauvera le contenu de l’échéancier en sortie de la boucle de simulation en utilisant la fonction drive_scheduler(), de façon à visualiser le chronogramme avec l’outil XPAT.
     272
     273D4) Ecriture des fonctions d’accès
     274
     275Ecrivez vous-même le code des 11 fonctions d’accès aux structures de données du simulateur décrites dans la section C, et introduire progressivement votre code à la place des fichiers .o qui vous ont été fournis.