Changes between Initial Version and Version 1 of 2009/CaoCourseTme7


Ignore:
Timestamp:
Jan 11, 2010, 12:03:37 AM (15 years ago)
Author:
franck
Comment:

--

Legend:

Unmodified
Added
Removed
Modified
  • 2009/CaoCourseTme7

    v1 v1  
     1{{{
     2#!html
     3<h1>TME 7 : Simulation logico-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 permettent de simuler des systèmes matériels constitués d'un ensemble
     13de composants matériels interconnectés par des signaux. Les signaux 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  logiques 0, 1, et U (indéfini).
     18
     19Dans le cas général, Chaque composant est modélisé par un processus, 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.
     21Un 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
     25de sensibilité change de valeur. Par exemple, la liste de sensibilité d'un processus représentant un automate de Moore
     26ne comporte qu'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: un processus correspond à une expression Booléenne
     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. Le 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.
     32
     33[[Image(reseau_booleen.png, nolink)]]
     34
     35En 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
     36entrante 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. 
     37Les autres signaux sont appelés signaux internes.
     38
     39On 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).
     40
     41Simuler le fonctionnement d'un circuit consiste donc
     42à calculer pour chaque signal la succession des événements, appelée forme d'onde.
     43L'ensemble des formes d'ondes de tous les signaux constitue un chronogramme.
     44
     45La simulation suppose que l'on possède une fonction d'évaluation qui calcule la valeur du signal de sortie du processus en fonction
     46de 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).
     47
     48Créez un répertoire de travail TME7, et copiez dans ce répertoire tous les fichiers et répertoires utiles pour ce TME.
     49{{{
     50cp -rp /users/enseig/encadr/cao/tme7 .
     51}}}
     52
     53= A) Structures de données =
     54
     55On utilise deux structures de données pour représenter :
     56 * le réseau Booléen, c'est à dire le graphe biparti des processus et des signaux,
     57 * l'échéancier, c'est à dire l'ensemble ordonné des événements.
     58
     59'''A1) réseau Booléen'''
     60
     61Le réseau Booléen est constitué d'un ensemble de signaux et d'un ensemble de processus.
     62Les signaux sont regroupés dans trois sous-ensembles disjoints suivant leur type (IN, OUT, INTERNAL).
     63{{{
     64typedef struct boolnet_t {
     65char            * NAME;         // nom du circuit modélisé
     66signal_t        * IN ;          // ensemble des signaux IN
     67signal_t        * OUT ;         // ensemble des signaux OUT
     68signal_t        * INTERNAL;     // ensemble des signaux INTERNAL
     69process_t       * PROCESS;      // ensemble des proceessus
     70} boolnet_t;
     71}}}
     72L’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.
     73
     74Un 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).
     75Il 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.
     76{{{
     77typedef struct signal_t {
     78boolnet_t       * BOOLNET;      // pointeur sur le réseau Booléen
     79sigtype_t         TYPE;         // type du signal
     80var_t           * VAR;          // variable Booléenne associée
     81bip_t           * TO_PROCESS;   // liste des process destinataires
     82process_t       * FROM_PROCESS; // process source
     83signal_t        * NEXT;         // signal suivant de même type
     84} signal_t;
     85}}}
     86Le type pourra prendre une des valeurs du type enuméré suivant :
     87{{{
     88enum sigtype_t {
     89IN              = 0,
     90OUT             = 1,
     91INTERNAL        = 2};
     92}}}           
     93Un processus est une expression Booléenne qui définit la valeur du signal de sortie, en fonction des valeurs des signaux d’entrée.
     94Il 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
     95qui en est la conséquence. Dans le cas général, les temps de propagation dépendent de l'entrée qui commute,
     96mais pour simplifier le simulateur, on suppose que le temps de propagation ne dépend pas de l'entrée qui commute.
     97{{{
     98typedef struct process_t {
     99boolnet_t       * BOOLNET;      // pointeur sur le réseau Booléen
     100signal_t        * SIGNAL;       // pointeur sur le signal de sortie
     101bip_t           * ABL;          // pointeur sur l’arbre ABL associé
     102long            DELAY;          // retard entrée -> sortie (en ps)
     103bip_t           * SUPPORT;      // liste des signaux d’entrée
     104process_t       * NEXT;         // processus suivant
     105} process_t
     106}}}
     107
     108'''A2) échéancier'''
     109
     110L'échéancier permet d'enregistrer  et d'ordonner les événements dans le temps.
     111Ceux-ci sont donc rangés dans une liste doublement chaînée, par dates croissantes.
     112Un événement représente une affection de valeur à un signal à une certaine date.
     113{{{
     114#!c
     115typedef struct event_t {
     116  signal_t      * SIGNAL;       // signal modifié
     117  unsigned        VALUE;        // nouvelle valeur
     118  long            DATE;         // date de la modification
     119  event_t       * PREV ;        // événement précédent dans l’echéancier
     120  event_t       * NEXT;         // événement suivant dans l’echéancier
     121} event_t;
     122}}}
     123L'échéancier peut contenir plusieurs événements à la même DATE, mais portant sur des signaux différents.
     124Ces événements synchrones sont rangés consécutivement dans la liste chaînée,
     125mais l'ordre entre ces événements synchrones n'est pas  significatif.
     126L'échéancier représente la liste ordonnée des événements passés, présents, et futurs.
     127Il contient donc en particulier la variable TC qui représente la date courante.
     128{{{
     129#!c
     130typedef struct scheduler_t {
     131  long            TC;           // Temps Courant
     132  event_t       * CURRENT;      // pointeur sur le premier événement à a date TC
     133  event_t       * FIRST;        // pointeur sur le premier événement de l'échéancier
     134} scheduler_t;
     135}}}
     136
     137= B) Fonctions d'accès aux structures de données =
     138
     139On présente ici les différentes fonctions permettant d’accéder aux structures de données définies ci-dessus.
     140
     141Dans un premier temps le code binaire exécutable de ces fonction vous est fourni,
     142et vous pourrez utiliser ces fonctions pour construire en mémoire le réseau Booléen,
     143construire et initialiser l’échéancier, et écrire la boucle principale de simulation qui a été présentée en cours.
     144
     145Dans un deuxième temps, il vous sera demandé d’écrire vous-même le code de ces fonctions.
     146
     147'''B1) construction réseau Booléen'''
     148
     149{{{
     150boolnet_t * cons_boolnet (char * name)
     151}}}
     152Cette fonction construit un réseau Booléen "vide" de nom ''name''.
     153Les pointeurs sur les listes de signaux et de processus sont initialisés à NULL.
     154{{{
     155signal_t * cons_signal (boolnet_t * bn, sigtype_t type, var_t * var)
     156}}}
     157Cette fonction prend pour argument une variable Booléenne préalablement créée.
     158Elle crée un signal et l’insère dans la liste des signaux du réseau correspondant à son type.
     159Elle initialise à NULL les pointeurs FROM_PROCESS et TO_PROCESS. La valeur de la variable Booléenne est forcée à U.
     160{{{
     161process_t * cons_process (boolnet_t * bn, signal_t * sig, char * abl, long delay)
     162}}}
     163Cette fonction prend pour argument un pointeur sur le signal de sortie, un pointeur sur un arbre ABL préalablement créé
     164(en utilisant par exemple  la fonction parse_abl du TME5), et la valeur du temps de propagation.
     165Elle crée une structure process_t, calcule le support de l'abl (sous forme d’une liste de signaux d’entrée),
     166et met à jour les pointeurs contenus dans les structures représentant les signaux impliqués.
     167
     168'''Attention :''' La fonction support_abl du TME5 retourne une liste de variables, mais le champs SUPPORT de l'objet
     169process_t doit pointer sur une liste de signaux!
     170
     171Cette fonction doit  vérifier le typage des signaux, c'est-à-dire qu'un signal de type IN ne peut avoir de processus générateur
     172et qu'un signal de type OUT ne peut apparaître dans le support d'une fonction.
     173
     174'''B2) construction échéancier'''
     175{{{
     176scheduler_t * cons_scheduler()
     177}}}
     178Cette fonction fabrique un échéancier « vide ».
     179La date courante  TC est initialisée à une valeur négative, et les pointeurs FIRST et CURRENT sont initialisés à NULL.
     180{{{
     181event_t * cons_event(scheduler_t * sch, signal_t * sig, long date, unsigned val)
     182}}}
     183Cette  fonction construit un événement  sur le signal sig, à un certaine date,
     184avec la valeur val, et introduit cet événement dans l’échéancier.
     185Cette fonction met à jour les deux pointeurs FIRST et CURRENT dans l’échéancier, si cela est nécessaire.
     186'''Attention''' : Cette fonction ne doit être utilisée que dans la phase d’initialisation, pour introduire dans l’échéancier
     187les événements portant sur les signaux d’entrée du circuit (stimuli).
     188
     189'''B3) simulation'''
     190{{{
     191void add_event (scheduler_t * sch, signal_t * sig, long delta, unsigned val)
     192}}}
     193Cette 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.
     194A 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.
     195C’est cette fonction qui doit être utilisée dans la boucle de simulation.
     196Dans 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,
     197et retirer cet événement parasite s’il y a lieu. Dans notre cas, cela n’est pas nécessaire.
     198Cette importante simplification est dûe à l'hypothèse qu'un processus est caractérisé par un unique temps de propagation,
     199qui ne dépend pas de l'entrée qui commute. Avec cette hypothèse, tous les événements prévus se produisent effectivement.
     200{{{   
     201bip_t * get_events (scheduler_t * sch)
     202}}}
     203Cette fonction recherche dans l'échéancier les événements prévus au temps TC.
     204Il 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
     205(qu'il faut penser à libérer quand ils ne sont plus utiles).
     206{{{
     207unsigned update_tc(scheduler_t * sch)
     208}}}
     209Cette 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.
     210Elle met à jour le pointeur CURRENT de l’échéancier.
     211Cette fonction renvoie la valeur 0 quand elle ne trouve pas d’événement postérieur au temps courant,
     212ce qui correspond à la fin de la simulation. Cette valeur doit donc être testée à chaque itération de la boucle de simulation.
     213{{{
     214bip_t * update_signals ( bip_t * events)
     215}}}
     216Cette fonction prend pour argument un pointeur sur une liste d'événements (liste de bipointeurs).
     217Elle modifie la valeur des variables Booléennes associées à chacun des signaux pour lesquels il existe un événement.
     218Elle rend la liste des signaux qui ont été modifiés (une autre liste de bipointeurs, qu’il faut également libérer quand ils ne sont plus utilisés).
     219{{{
     220bip_t * get_processes (boolnet_t * bn, bip_t * signals)
     221}}}
     222Cette fonction prend comme argument un pointeur sur le réseau Booléen, et une liste de signaux,
     223et elle renvoie un pointeur sur une autre liste contenant l’ensemble de tous les processus ayant
     224au moins un signal d’entrée appartenant à l’ensemble ''signals''.
     225(ici encore, il faut penser à libérer la mémoire occupée par les bi-pointeurs quand ils ne sont plus utiles).
     226{{{
     227void eval_processes( scheduler_t * sch, bip_t * p)
     228}}}
     229Cette fonction prend pour argument une liste de processus, ainsi qu’un pointeur sur le scheduler,
     230et évalue la valeur du signal de sortie pour chacun des processus de la liste,  en fonction des valeurs des signaux d’entrée.
     231Elle utilise pour cela une version de la fonction eval_abl() modifiée pour traiter une logique à trois valeurs (0,1,U).
     232Elle compare la valeur future du signal de sortie à sa valeur présente, et insère un événement dans  l’échéancier
     233si cette valeur change (en utilisant la fonction add_event).
     234
     235= C) Travail à réaliser =
     236
     237On se propose de simuler le schéma suivant, qui réalise un additionneur 2 bits.
     238A chaque porte est associé une expression Booléenne représentée par un arbre ABL.
     239
     240[[Image(schema_portes.png, nolink)]]
     241
     242== C1) simulation du circuit ''etou'' ==
     243
     244Le fichier ''etou.c'' contient un tout petit réseau Booléen ne contenant que deux noeuds,
     245et 4 signaux. Compilez ce fihier, et exécutez la simulation.
     246{{{
     247> make
     248}}}
     249Lancez l'exécution duprogramme (construction du réseau Booléen et simulation).
     250{{{
     251> ./etou
     252}}}
     253Vous pouvez visualiser le réseau Booléen avec la commande:
     254{{{
     255> xv etou.gif
     256}}}
     257Vous pouvez visualiser le chronogramme résulat de la simulation avec la commande:
     258{{{
     259> xpat -l etou
     260}}}
     261
     262== C2) Construction réseau Booléen de l'additionneur ==
     263
     264En vous inspirant du fichier ''etou.c'', écrivez le fichier ''adder.c'' qui construit en mémoire
     265le réseau Booléen correspondant au circuit additionneur 2 bits décrit ci-dessus.
     266On utilisera pour cela les fonctions cons_boolnet(), cons_process() et cons_signal().
     267On prendra une valeur de 1 ns pour le temps de propagation de la porte NAND2, et de 2 ns pour la porte XOR2.
     268Pour vérifier la structure du réseau Booléen, on utilisera la fonction ''drive_boolnet()''.
     269Cette fonction construit une représentation graphique du réseau Booléen, et la sauvegarde dans un fichier
     270au format .gif ou .ps.
     271
     272Modifiez le Makefile permettant de compiler ce programme ''adder.c'', et exécutez-le.
     273
     274== C3) Construction et initialisation de l’échéancier ==
     275
     276Complêter le fichier ''adder.c'' main() pour créer l’échéancier
     277et initialiser les événements sur les signaux d’ entrée a0, b0, c0, a1 et b1 de façon à respecter le chronogramme ci-dessous.
     278On utilisera les fonctions cons-scheduler() et add_event(). Attention : le passage de la valeur U (indéfinie) à une valeur 0 ou 1
     279constitue un événement : Dans ce chronogramme, il y a donc un événement sur tous les signaux d’entrée au temps T = 0.
     280
     281[[Image(chronogramme.png, nolink)]]
     282
     283Pour vérifier que l’échéancier est correctement initialisé, on pourra utiliser la fonction ''drive_scheduler ()''.
     284Cette fonction peut être utilisée avant même l'exécution de la fonction de simulation, pour générer un fichier au format .pat
     285qui décrit le chronogramme des signaux d'entrée. Vous pouvez visualiser ce chronogramme avec l’outil XPAT.
     286
     287== C4) Simulation effective du circuit additionneur ==
     288
     289Introduisez dans le fichier ''adder.c'' la fonction ''simulate()'' qui effectue la simulation du réseau Booléen,
     290jusqu'à ce qu'il n'y ait plus aucun événement à traiter dans l'échéancier. Compilez ce programme,
     291et analysez le chronogramme résultant
     292
     293== C5) Ecriture de la boucle de simulation ==
     294
     295Ecrire en langage C la fonction ''simulate()''. Cette fonction contient une boucle ''while'' qui enchaîne les
     296deux phases “update” et “exécute” de l'algorithme de simulation ''event-driven'' présenté en cours.
     297{{{
     298void simulate (boolnet_t * bn, scheduler_t * sch)
     299}}}
     300On utilisera pour cela les fonctions get_events(), update_tc(), update_signals(), get_process() et eval_process().
     301On sauvera sur disque le contenu de l’échéancier en sortie de la boucle de simulation en utilisant la fonction drive_scheduler(),
     302de façon à visualiser le chronogramme avec l’outil XPAT.
     303
     304== C6) Ecriture des fonctions d’accès ==
     305
     306Bien que le texte de cette question soit très court, cette question est  évidemment la plus importante du TME :
     307Ecrivez vous-même le code des 11 fonctions d’accès aux structures de données du simulateur décrites
     308dans la section B, et introduire progressivement votre code à la place des fichiers .o qui vous ont été fournis.
     309
     310= Compte-Rendu =
     311
     312Il ne vous est pas demandé de compte-rendu écrit pour ce TME, mais vous devrez faire une démonstration
     313de votre code au début du prochain TME.
     314