VII. Tesztkörnyezet felépítése 1
VII.1. Szimuláció folyamatát vezérlő eszközök SystemC-ben sc_start sc_stop sc_time_stamp sc_simulation_time sc_clock sc_trace sc_cycle és az sc_initialize sc_time 2
sc_start Az sc_start metódus feladata a szimuláció elindítása majd a paraméterként megadott idő eltelte után a szimulációs folyamat megállítása. Tehát nem szükséges külön utasítás a szimuláció leállítására, az a paraméterként megadott idő eltelte után automatikusan leáll. Az alábbi függvény elindítja a szimulációt és utasítja a szimulációt vezérlő kernelt, hogy 100 mili-szekundumig futassa azt. A második függvény, pedig azt jelenti a szimulációs kernel számára, hogy a szimulációt korlátlan ideig futassa. 3
Például: sc_start(100,sc_ms); sc_start(-1); 4
sc_stop Az sc_stop metódus bármely folyamatban felhasználható, feladata az aktuálisan futó szimuláció leállítása. A metódus nem vesz át semmilyen paramétert sem az őt meghívó függvénytől. A használata, pedig az alábbiakban látható: Például: sc_stop(); 5
sc_time_stamp Ezzel a metódussal le lehet kérdezni egy adott időpillanatban, hogy hol tart a szimuláció folyamata az alapértelmezett időegységekben mérve. cout << Most éppen a << sc_time_stamp() << időpillanatban járunk ; Eredményül pedig azt írja ki erre a futó programra, hogy például Most éppen a 15ns időpillanatban járunk 6
sc_simulation_time Ez a metódus, az aktuális szimulációs időt szolgáltatja egész szám formájában, amelynek a típusa double, dupla pontosságú lebegőpontos szám. double aktualis_ido=sc_simulation_time(); 7
sc_clock Az sc_clock típus lehetővé teszi speciális órajel generátor objektumok létrehozását, amivel egy időzített négyszög jelalak hozható létre. Az órajel deklarálását a következő utasítással lehet végrehajtani. sc_clock jel1 ( jel1,20,sc_ns); A fenti deklarációs utasítás létrehoz egy 20 nano szekundum periódusú négyszögjelet, amelynek első fél periódusa (50%) felső, a második fél periódusa, pedig alsó jelállapotban van. Alapértelmezésben a négyszögjel felső és alsó állapotainak felosztása 50%-50% és az órajel felfutó éllel (egyes, 1, true) indul. Az órajel objektum neve jel1. 8
jel1 0 10 20 30 40 50 ns t sc_clock jel1 ( jel1,20,sc_ns); 9
A második példa egy valamivel összetettebb órajel alakot definiál. sc_clock jel2 ( jel2,10,sc_ns, 0.3,5,SC_NS,false); jel2 0 t 12 15 22 25ns 5 10
Az előző órajel generátor deklaráció létrehoz egy 10 nano szekundum periódusú négyszögjelet, amelynek periódusa olyan módon van felosztva, hogy az első 70%-a alsó, a periódus további 30%-a pedig felső jelállapotban van. Az első él 5ns késleltetéssel indul, azaz egy 5ns idejű felsőállapotbeli bevezető szakasza van az órajelnek és az első él egy lefutó él lesz. A jel kezdeti állapotát határozza meg az utolsó paraméterként megadott false kulcsszó, ami most azt jelenti, hogy nincsen felfutó él a t=0ns időpillanatban. Az órajel objektum neve jel2. 11
sc_trace Az sc_trace metódus feladata, hogy a szimuláció során előálló numerikus eredményeket egy meghatározott állomány formátumban a háttértárakon tárolni tudjuk további feldolgozás céljából. A SystemC három különféle formátumú állomány típust támogat a szimulációs eredmények mentésére. Ezek a következők: VCD (Value Change Dump) WIF (Waveform Interchange Format) ISDB (Integrated Signal Data Base) 12
Az egyes állomány formátumok kezelése nagyon hasonlít a C/C++-ban megismert állománykezelési technikára, azaz meg kell nyitnunk/létre kell hoznunk az adott állományt, majd abba az eredményeket el kell mentenünk, valamint a használat után az állományokat le kell zárnunk az erre a célra szolgáló metódusokkal. A megnyitás során a megnyitást végző függvény elsősorban a C program nyelvben megszokottakhoz hasonlóan egy file mutatóval tér vissza, amit aztán az állományunk azonosítására tudunk a továbbiakban, a kódban felhasználni. 13
Az egyes formátumok használatához az alábbi függvények meghívása szükséges. VCD sc_create_vcd_trace_file() WIF sc_create_wif_trace_file() ISDB sc_create_isdb_trace_file() Az egyes kiterjesztéseket (*.vcd, *.awif, *.isdb) automatikusan hozzáfűzi a megnyitást/létrehozást végző függvény a létrejövő állományok nevéhez. 14
sc_trace_file *f= sc_create_vcd_trace_file( eredmenyek ); A fenti deklaráció létrehoz egy VCD típusú állományt, aminek a neve eredmenyek.vcd és ezt az állományt az f nevű pointerrel tudjuk a továbbiakban azonosítani a további programlépésekben. 15
Ezután, már van arra lehetőség, hogy már létező jelek értékeit kimentsük az sc_trace() függvény segítségével ebbe az állományba. Erre az alábbiakban lehet látni egy példát. sc_trace(f, jel_neve_azonosítoja, jel_neve_azonosítoja ); A harmadik paraméter célja, hogy az állományba kiírt numerikus értékek könnyebben azonosíthatóak legyenek, mivel az, ebben a szöveges alakban fog az állományban megjelenni. 16
sc_cycle, sc_initialize Ez a két metódus a ciklus-szintű szimulációk végrehajtásának elősegítésére alkalmas. Ha például a kidolgozott modellnek 10 időegységenként szeretnék szimulálni a működését, akkor praktikus ciklus-szintű szimulációt végrehajtani. Ebben az esetben nem az sc_start() metódust kell használni, hanem az sc_initialize() és az sc_cycle() metódusokat. sc_initialize(); sc_cycle(20,sc_us); A fenti utasítás páros minden olyan folyamatot, amely készen áll a futtatásra végrehajt, majd a szimuláció idejét előre lépteti 20 mikro-szekundummal. 17
sc_time Az sc_time segítségével különböző idő objektumokat deklarálhatunk, amelyeket a továbbiakban a szimulációt vezérlő metódusoknál, mint például az sc_clock vagy az sc_start() fel lehet használni. sc_time t1 (77, SC_NS); sc_time t2 (33, SC_US); Az első esetben a t1-hez a 77 nano-szekundum a második esetben a t2-höz a 33 mikro-szekundum lett hozzárendelve. sc_start (77, SC_NS); sc_start (t1); 18
Az idő objektum további alkalmazása látható a következő példában sc_clock jel2 ( jel2,10,sc_ns, 0.3,5,SC_NS,false); sc_time periodus (10,SC_NS); sc_time indito_periodus (5,SC_NS); sc_clock jel3 ( jel3,periodus, 0.3,indito_periodus,false); A fentiekben deklarált jel2 és jel3 teljes mértékben megegyezik. 19
A SystemC-ben a következő időállandók kerültek deklarálásra. SC_FS, SC_PS, SC_NS, SC_US, SC_MS, SC_SEC, amelyek rendre a femto, piko, nano, mikro, mili másodperceket jelenti a legutolsó, pedig természetesen az egy másodpercet azonosítja. Az alapértelmezett időegység az 1ps azaz 1 piko-szekundum. Ez az sc_set_time_resolution() függvénnyel módosítható, mint például: sc_set_time_resolution (10, SC_NS); beállítja az alapértelmezett időegységet 10 nanoszekundumra. Az alapértelmezett időegység kizárólag 10 egészkitevőjű hatványa lehet. 20
VII.2. Jelalakok generálása SystemC-ben Komplex nem periodikus jelalak létrehozása Egyszerű szabályos jelalakok az sc_clock segítségével könnyedén és gyorsan létrehozhatóak, ellenben komplexebb jelformák előállítására létezik SystemC környezetben egy másik lehetőség is. Ezt a SC_THREAD típusú folyamatok és a wait utasítás segítségével lehet megoldani. Tekintsük például az alábbi általános nem periodikus jelalakot. 21
jel 0 3 7 17 22 27ns t A fenti jelalak megvalósítására külön modult kell írni, amelyben egy folyamat kerül deklarálásra és a folyamat SC_THREAD típusú lesz. 22
#include systemc.h SC_MODULE (jel) { sc_out<bool> x; void jel_gen(); SC_CTOR(jel) { SC_THREAD (jel_gen); } }; 23
void jel::jel_gen() { x=0; wait(3, SC_NS); x=1; wait(4, SC_NS); x=0; wait(10, SC_NS); x=1; wait(5, SC_NS); x=0; } 24
Felmerül a kérdés, hogy a jel_gen() folyamat mikor kezdi el végrehajtást. Erre a kérdésre a válasz az, hogy a szimuláció megkezdése előtt, egy úgynevezett inicializáló fázisban minden egyes folyamat legyen az akár SC_METHOD vagy SC_THREAD típusú, egyszer végrehajtásra kerül. 25
A fenti jelgeneráló modulban a modul egyetlen kimeneti portal x rendelkezik, aminek értékét a jel_gen() nevű, SC_THREAD típusú folyamat állítja be, majd módosítja az egyes wait utasításoknak megfelelő időzítéssel. Először az x értékét beállítja a folyamat nullára, majd felfüggeszti a folyamat futási idejét 3 ns ideig, majd az x értékét 1-re állítja be, aztán a folyamat futási idejét újra felfüggeszti 4 ns ideig, majd ezt folytatja a további utasításoknak megfelelően. 26
Komplex periodikus jelalak létrehozása Ha a komplex jelalakot egy megadott időtartományon (perióduson) túl, ismételni szeretnénk, akkor egy while utasítás segítségével egy végtelen ciklust hozunk létre a jelet előállító folyamatban p_jel_gen() és ezt a szimulációs kernel folyamatosan végre fogja hajtani, mindaddig, amíg a szimuláció futni fog. A következő példában 45 ns-os időablakokban ismétlődik az előző példában deklarált komplex jelforma. 27
#include systemc.h SC_MODULE (p_jel) { sc_out<bool> x; void p_jel_gen(); SC_CTOR(p_jel) { SC_THREAD (p_jel_gen); } }; 28
void p_jel::p_jel_gen() { while(1) { x=0; wait(3, SC_NS); x=1; wait(4, SC_NS); x=0; wait(10, SC_NS); x=1; wait(5, SC_NS); x=0; wait(23, SC_NS); } } 29
Az előző példához képest a folyamat definíciós részében van egy alapvető különbség, még pedig az, hogy a p_jel_gen() nevű folyamat egy végtelen ciklust tartalmaz, while(1){} ami a hagyományos programfejlesztésben eléggé ritkán alkalmazott megoldás. Természetesen a ciklus a valódi értelemben csak látszólag lesz végtelen, mivel a while(1){} ciklus csak a későbbiek folyamán rögzített szimulációs időtartomány által meghatározott ideig fog működni, a szimuláció befejeződésével maga a ciklus is leáll. 30
Szinkronizált jelalak generálása A harmadik a gyakorlatban igen fontos jelgenerálási probléma, amikor egy alap órajelhez kell egy másik órajelet igazítani (szinkronizálni). Erre mutat példát az alábbi ábrán látható két különböző négyszögjel. A két jel közül a felső az alap órajel, amelyhez az alatta látható hullám alakot kell generálni, szinkronban az alap órajellel. 31
órajel 0 7 10 20 30 40 9 17 19 27 29 37 39ns t szinkron_jel 32
Az alap órajel 10 ns szekundumos periódus idejű négyszög jel, amelynek első fele nulla (low), a második fele egyes (high) állapotban van. Ehhez az alapjelhez kell egy másik órajelet generálnunk, amelyre a következő feltételeknek kell fenn állnia. 33
A szinkronizált órajelnek az alap órajel felfutó éléhez kell igazodnia, a felfutó él időpontját tekintjük egyfajta bázis időnek, amihez a szinkron órajel igazodni fog. Az alap órajel felfutó éle után 2 ns-al kell megjelennie a szinkronizált órajelben a felfutó élnek A szinkronizált jelet 2 ns ideig tartjuk egyes (high) jelállapotban, aztán visszahúzzuk nullába. 34
#include systemc.h SC_MODULE (main_signal) { sc_out<bool> x; void main_signal_gen(); SC_CTOR(main_signal) { SC_THREAD (main_signal_gen); } }; 35
void main_signal::main_signal_gen() { while(1) { x=0; wait(5, SC_NS); x=1; wait(5, SC_NS); } } 36
Első lépésben az alap órajelet generáló modult kell létrehozni, amit a main_signal nevű modul és azon belül egy SC_THREAD típusú main_signal_gen() nevű folyamat hajt majd végre. Ennek a modulnak egyetlen logikai típusú bool kimeneti portja x van, amin keresztül a generált órajelet lehet a többi modul számára elérhetővé tenni. A main_signal_gen() nevű folyamat egy végtelen ciklust tartalmaz, ami lehetővé teszi, hogy a periodikus alap órajelet a szimuláció futásának végéig folyamatosan generálhassuk. 37
SC_MODULE (jel) { sc_out<bool> x; sc_in<bool> ora; void jel_gen(); SC_CTOR(jel) { SC_THREAD (jel_gen); sensitive_pos << ora; } }; 38
void jel::jel_gen() { x=0; while(1) { wait(); wait(2, SC_NS); x=1; wait(2, SC_NS); x=0; } } 39
A második lépésben egy jel nevű modult és azon belül egy SC_THREAD típusú jel_gen() nevű folyamatot deklarálunk. A modulnak egy bemenete van, amin keresztül az alap órajel érkezik be a modulba, és egy kimenetet deklaráltunk, ami a szinkronizált órajelet bocsátja ki a modulból. Fontos megjegyezni, hogy a jel_gen() nevű folyamatnak van érzékenységi listája. Még pedig a sensitive_pos << ora; utasítás sor adja meg, hogy a jel_gen() nevű folyamat milyen változásokra reagáljon. 40
A sensitive_pos ebben az esetben azt jelenti, hogy kizárólag a felfutó élre legyen érzékeny, erre induljon csak el a jel_gen() nevű folyamatban a while ciklus belsejében az első wait() után található többi utasítás végrehajtása. Ugyanis a wait() utasítás paraméter nélküli alakjában egy olyan várakozási állapotot hoz létre jel_gen() nevű folyamatban megadott végtelen ciklusban, ami addig várakozik amíg, az érzékenységi listában megadott esemény be nem következik. Akkor befejezi a várakozást és a következő utasításra lép, amelytől kezdődően már csak korlátozott időtartamú várakozások vannak az adott ciklus végéig, majd a következő cikluslépésben újra belefut a paraméter nélküli wait() utasításba. 41
int sc_main(int argc, char *argv[]) { sc_signal<bool> jel_fv, clock; sc_trace_file *f; int ch; main_signal ora_jel( alap_jel ); ora_jel.x(clock); jel s( szink_jel ); s.ora(clock); s.x(jel_fv); 42
f= sc_create_vcd_trace_file( jel_eredmenyek ); sc_trace(f, clock, alap_orajel ); sc_trace(f, jel_fv, szinkron_jel ); sc_start(100,sc_ns); sc_close_vcd_trace_file(f); cout << A szimulációs folyamat végének ideje: << sc_time_stamp() << endl; cout << Nyomjon le a q/q és aztán Enter ; cin >> ch; return 0; } 43
A szinkronjel.cpp program fordítása és futtatása után a következő.vcd kiterjesztésű szöveges állomány jön létre a háttértáron. A jel_eredmenyek.vcd nevű állomány tartalma és az állomány szerkezete részletesen ismertetésre kerül, mivel az állomány struktúrája első látásra nem triviális és könnyen olvasható. 44
$date Jun 14, 2012 22:24:03 az állomány létrehozási dátuma $end $version SystemC 2.2.0 --- May 31 2012 14:58:00 a SystemC verzió száma $end $timescale 1 ps az alapértelmezett időskála $end 45
$scope module SystemC $end $var wire 1 aaa alap_orajel $end $var wire 1 aab szinkron_jel $end az állományba elmentett változók listája $upscope $end $enddefinitions $end $comment All initial values are dumped below at time 0 sec = 0 timescale units. $end 46
$dumpvars 0aaa 0aab az elmentett jelek kezdeti értékei a szimuláció indulásakor $end mindkét jel (aaa, aab) értéke a szimuláció indulásakor 0. 47
#5000 1aaa az alapjel értéke a 5 ns=5000 ps-ban 0-ról 1-re vált #7000 1aab a szinkronjel értéke a 7 ns -ban 0-ról 1-re vált #9000 0aab #10000 0aaa #15000 1aaa aaa aab 0 5 10 15 7 t 9 17ns az alapjel értéke a 15 ns -ban 0-ról 1-re vált 48