520 likes | 740 Views
ipblock design in GEZEL. Patrick Schaumont Virginia Tech Dept. of Electrical and Computer Engineering September 2010. What are ipblock?. An ipblock is a black-box simulation entity for GEZEL. Name. Interface. ipblock M(in address : ns(5); in wr,rd : ns(1);
E N D
ipblock design in GEZEL Patrick Schaumont Virginia Tech Dept. of Electrical and Computer Engineering September 2010
What are ipblock? • An ipblock is a black-box simulation entity for GEZEL Name Interface ipblock M(in address : ns(5); in wr,rd : ns(1); in idata : ns(8); out odata : ns(8)) { iptype "ram"; ipparm "wl=8"; ipparm "size=32"; } Type Parameters
Applications of ipblock • Capture functionality not available as FSMD • FSMD does not exist: memory modules • FSMD unavailable: IP, complex modules, .. • Interfaces to other simulation engines • Instruction-set simulators: ARM, 8051 • Other languages/environments: SystemC,... • Extensions of GEZEL simulation capabilities • Collect signal statistics • Debug facility
Who develops ipblock? GEZEL Designers GEZEL Users ipblock with generic use design-specific
How are ipblock implemented? aipblock GEZEL class lib (data types, symbol table, ...) C++ ipblock statically linked dynamic-link library ld.so EXE fdlsim/gplatform
Operational Principle • ipblock are cycle-based simulation models • A cycle-based simulation has two phases per cycle: evaluate and (state) update R1 R2 R3 logic L1 logic L2
Operational Principle • ipblock are cycle-based simulation models • A cycle-based simulation has two phases per cycle: evaluate and (state) update R1 R1 logic L1 Evaluate: next_R1 = ... next_R2 = Logic_L1(R1); next_R3 = Logic_L2(R2); Update: R1 = next_R1; R2 = next_R2; R3 = next_R3; R2 R2 logic L2 R3 R3 Cycle N + 1 Cycle N
Operational Principle • ipblock are cycle-based simulation models • A cycle-based simulation has two phases per cycle: evaluate and (state) update • Evaluate and update can be called only once per cycle R1 R3 logic L1 logic L2 GEZEL ensures that all Evaluate( ) are properly sorted: Logic_L1(R1) will be called BEFORE Logic L2(L1_output)
Operational Principle • ipblock are cycle-based simulation models • A cycle-based simulation has two phases per cycle: evaluate and (state) update • Evaluate and update are called only once per cycle logic L1 logic L1 logic L2 If the execution order for Evaluate( ) cannot be determined, the simulator terminates ('combinatorial loop')
Combinational ipblock • By default, have a single evaluate function, called once per clock cycle fsmd OK ipblock Not OK ipblock ipblock
Simple ipblock: combinational GEZEL ipblock ablock(in a, b : ns(8); out c : ns(8)) { iptype "myblock"; } C++ class myblock : public aipblock { public: void run(); }; void myblock::run() { ioval[2]->assignulong(ioval[0]->toulong() + ioval[1]->toulong()); } +
Simple ipblock: combinational GEZEL ipblock ablock(in a, b : ns(8); out c : ns(8)) { iptype "myblock"; } a ioval[0] C++ b ioval[1] c ioval[2] class myblock : public aipblock { public: void run(); }; void myblock::run() { ioval[2]->assignulong(ioval[0]->toulong() + ioval[1]->toulong()); }
Simple ipblock: combinational GEZEL ipblock ablock(in a, b : ns(8); out c : ns(8)) { iptype "myblock"; } C++ class myblock : public aipblock { public: void run(); }; • run() is called once per clock cycle • When called, input ioval[ ] reflects proper value for that clock cycle • When called, output ioval[ ] must be assigned proper value for that clock cycle void myblock::run() { ioval[2]->assignulong(ioval[0]->toulong() + ioval[1]->toulong()); }
Adding parameters GEZEL ipblock ablock(in a, b : ns(8); out c : ns(8)) { iptype "myblock"; ipparm "thisparam=thatval"; } C++ class myblock : public aipblock { public: void run(); void setparam(char *); }; call constructor for each 'ipparm' call setparam void myblock::run() { ioval[2]->assignulong(ioval[0]->toulong() + ioval[1]->toulong()); } void myblock::setparam(char *n) { // ... } for each cycle call run
Terminal-check function GEZEL ipblock ablock(in a, b : ns(8); out c : ns(8)) { iptype "myblock"; ipparm "thisparam=thatval"; } • ipblock use positional port matching • checkterminal verifies that ioval[0] is an input name "a", ioval[1] is input "b", ... C++ class myblock : public aipblock { public: void run(); void setparam(char *); bool checkterminal(int n, char *tname, iodir d); }; bool myblock::checkterminal(int n, char *tname, iodir d) { switch(n) { case 0 : return (isinput(d) && isname(tname, "a")); break; ... } return false; }
Terminal-check function GEZEL ipblock ablock(in a, b : ns(8); out c : ns(8)) { iptype "myblock"; ipparm "thisparam=thatval"; } call constructor C++ class myblock : public aipblock { public: void run(); void setparam(char *); void checkterminal(..); }; for each 'ipparm' call setparam for each i/o port call checkterminal for each cycle call run
Completing the C++ myblock.h Access to GEZEL library functions(aipblock definition, arbitrary-precision data types, ..) #ifndef myblock_h #define myblock_h #include "ipblock.h" extern "C" aipblock *create_myblock(char *instname); class myblock : public aipblock { public: myblock(char *name); void setparm(char *); void run(); bool checkterminal(int n, char *tname, aipblock::iodir d); bool cannotSleepTest(); bool needsWakeupTest(); }; #endif Called by the dynamic-link facility, Function name depends on class name! simulator control (see part 2)
Completing the C++ myblock.cxx Called by dynamic-link facility #include "myblock.h" extern "C" aipblock *create_myblock(char *instname) { return new myblock(instname); } myblock::myblock(char *name) : aipblock(name) {..} void myblock::setparm(char *_name) {..} void myblock::run() {..} bool myblock::checkterminal(..) {..} bool myblock::cannotSleepTest(){ return false; } bool myblock::needsWakeupTest(){ return true; } Default implementation for combinational modules
Compilation-Simulation Example Compile: g++ -fPIC -O3 -I/opt/gezel-2.2/include/gezel -c myblock.cxx g++ -shared -O3 \ -Wl,--rpath -Wl,/home/schaum/gezel/devel/build/lib \ myblock.o \ /opt/gezel-2.2/lib/libipconfig.so \ /opt/gezel-2.2/lib/libfdl.so \ -lgmp -ldl -o libmyblock.so Create dynamic-link library
Compilation-Simulation Example position-independent code Compile: g++ -fPIC -O3 -I/opt/gezel-2.2/include/gezel -c myblock.cxx g++ -shared -O3 \ -Wl,--rpath -Wl,/home/schaum/gezel/devel/build/lib \ myblock.o \ /opt/gezel-2.2/lib/libipconfig.so \ /opt/gezel-2.2/lib/libfdl.so \ -lgmp -ldl -o libmyblock.so .so library Create dynamic-link library path to gezel lib gezel lib and predefined ipblocks arbitrary precision math lib dynamic link lib
GEZEL File ipblock ablock(in a, b : ns(8); out c : ns(8)) { iptype "myblock"; ipparm "thisparam=thatval"; } dp top { sig a, b, c : ns(8); reg a1 : ns(8); use ablock(a, b, c); always { a = a1; b = 2; $display("a ", a, " b ", b, " c ",c); a1 = a1 + 1; } } system S { top; }
Simulation 1. parse ipb.fdl 2. instantiate ipblock 2.1. link dynamic-link lib 3. simulate 5 cycles > /opt/gezel-2.2/bin/fdlsim ipb.fdl 5 set parm: thisparam=thatval a 0 b 2 c 2 a 1 b 2 c 3 a 2 b 2 c 4 a 3 b 2 c 5 a 4 b 2 c 6 Per clock cycle: output: define all FSM next-state evaluate output of each dp call ipblock with no output update dp registers
Summary #ifndef myblock_h #define myblock_h #include "ipblock.h" class myblock : public aipblock { public: myblock(char *name); void setparm(char *); void run(); bool checkterminal(int n, char *tname, aipblock::iodir d); bool cannotSleepTest(); bool needsWakeupTest(); }; #endif
Sleep Cycle Mechanism #ifndefmyblock_h #define myblock_h #include "ipblock.h" classmyblock : publicaipblock { public: myblock(char *name); voidsetparm(char *); void run(); boolcheckterminal(int n, char *tname, aipblock::iodir d); boolcannotSleepTest(); boolneedsWakeupTest(); }; #endif
Sleep Cycle Mechanism Software (typ 1Mc/s) Hardware (typ 10K/s) 100K cycles 100 cycles 100K cycles 100 cycles 100K cycles
Sleep Cycle Mechanism Software (typ 1Mc/s) Hardware (typ 10K/s) 100K cycles 100 cycles 100K cycles 100 cycles Without sleep-cycle:300,200 cycles @ 10K/s = 30.02 seconds 100K cycles
Sleep Cycle Mechanism Software (typ 1Mc/s) Hardware (typ 10K/s) 100K cycles 100 cycles 100K cycles With sleep-cycle:300K cycles @ 1M/s +300K cycles (testing_overhead) + 200 cycles @ 10K/s = less than 0.5 seconds! 100 cycles 100K cycles
Sleep Cycle Mechanism • GEZEL simulator has two states: • Active: • each clock cycle, evaluate all datapath outputs (and dependent signals), evaluate all registers, evaluate all ipblock • perform sleep test • Passive: • perform wakeup test
Sleep Cycle Mechanism • Transition from active to passive sleep_test = false Active sleep_test = true wakeup_test = true Passive wakeup_test = false
Sleep Test • Sleep test evaluates to false in cycle X if: • Any register changes state during a clock X • Any FSM changes state during clock cycle X • Any ipblock returns true in aipblock::cannotSleepTest() • GEZEL simulator will call cannotSleepTest () once per cycle in active mode, default value should be false • (why is my GEZEL cosimulation so slow?Might be because there is remaining HW activity in idle phases)
Wakeup Test • Wakeup test evaluates to true in cycle X if: • Any ipblock returns true in aipblock::needsWakeupTest() • GEZEL simulator will call needsWakeupTest () once per cycle in passive mode, default value should be false • If needsWakeupTest() never returns false, your simulation will never sleep (may be the cause of a slow cosimulation) • If needsWakeupTest() never returns true, your simulation will never wakeup (may be the cause of a ‘halted’ cosimulation)
Example: 8051 cosimulation interface 8051 4 bi-directional ports ipblock my8051 { iptype "i8051system"; ipparm "exec=driver.ihx"; ipparm "verbose=1"; ipparm "period=1"; } ipblock my8051_datain(out data : ns(8)) { iptype "i8051systemsource"; ipparm "core=my8051"; ipparm "port=P1"; } ipblock my8051_dataout(in data : ns(8)) { iptype "i8051systemsink"; ipparm "core=my8051"; ipparm "port=P2"; } Core Three GEZEL ipblock: Input port Output port
The Core void i8051system::run() { if (sim.IsRunning()) { period_cnt--; if (period_cnt == 0) { period_cnt = period; sim.ClockTick(); if (! sim.IsRunning()) glbRunningISS--; } } } bool i8051system::cannotSleepTest() { return false; // the ISS will continue to run in sleep mode // so we can also return 'OK to sleep' } bool i8051system::needsWakeupTest() { run(); return false; }
The Input Port void i8051systemsink::run() { if (hook) { hook->writeRAM(address, ioval[0]->toulong()); } } bool i8051systemsink::cannotSleepTest() { return false; } (If you do not specify needsWakeupTest(), default implementation is used, which returns false)
The Output Port void i8051systemsource::run() { } Obviously, it is the 8051 simulator that decides what the value of the output port should be. The 8051 simulator calls a functionexternalwrite(address, data) each time is performs a port write
The Output Port void i8051systemsource::run() { } Obviously, it is the 8051 simulator that decides what the value of the output port should be. The 8051 simulator calls a functionexternalwrite(address, data) each time is performs a port write void i8051externalwrite(int dev, unsigned char d) { i8051devmap[dev]->ioval[0]->assignulong((long) d); i8051devmap[dev]->touch(); } I8051devmap is an array of output port ipblocks that are implemented by GEZEL The result of the externalwrite( ) is that an ipblock output is updated
The Output Port void i8051systemsource::run() { } bool i8051systemsource::needsWakeupTest() { bool v = interfacewritten; interfacewritten = false; return v; } bool i8051systemsource::cannotSleepTest() { bool v = interfacewritten; interfacewritten = false; return v; } void i8051systemsource::touch() { interfacewritten = true; } As a result ofthe 8051 WRITING to the hardware,the cycle-simulation will wake-up void i8051externalwrite(int dev, unsigned char d) { i8051devmap[dev]->ioval[0]->assignulong((long) d); i8051devmap[dev]->touch(); }
Review: One C++ class #ifndefmyblock_h #define myblock_h #include "ipblock.h" classmyblock : publicaipblock { public: myblock(char *name); voidsetparm(char *); void run(); boolcheckterminal(int n, char *tname, aipblock::iodir d); boolcannotSleepTest(); boolneedsWakeupTest(); }; #endif OK!
Ipblock are combinational • By default, have a single evaluate function, run( ), called once per clock cycle fsmd OK ipblock Not OK ipblock ipblock
Support for sequential IP block • Define sequential IP block:a sequential ipblock is an ipblock with an output which is is known and stable at the start of a clock cycle. • Thus, ‘sequential’ is a property of an output, not an entire ipblock
Support for Sequential IPblock • For example, assume you develop a C++ class for this structure: ipblock Sequential output Comb Function Combinational output
Support for Sequential IP block • This will simulate fine ! ipblock Comb Function Comb Function 0
Sequential IP Block in C++ #ifndefmyblock_h #define myblock_h #include "ipblock.h" classmyblock : publicaipblock { public: myblock(char *name); voidsetparm(char *); void run(); boolcheckterminal(int n, char *tname, aipblock::iodir d); boolcannotSleepTest(); boolneedsWakeupTest(); voidregOutput();voidout_run(); }; #endif
regOutput • Used in constructor to define an ipblock output as sequential // ipblockmysfu(out d1 : ns(32); out d2 : ns(32); // in q1 : ns(32); in q2 : ns(32)) { // iptype "armsfu2x2"; // ipparm "core = mycore"; -- core to hook into // ipparm "device = 0"; -- 2 possible devices per sfu type // } armsfu2x1::armsfu2x1(char *name) : aipblock(name) { deviceid = 0; hook = 0; myarm = 0; interfacewritten = false; regOutput(0); // d1 and d2 are registered regOutput(1); }
regOutput • Used in constructor to define an ipblock output as sequential // ipblockmysfu(out d1 : ns(32); out d2 : ns(32); // in q1 : ns(32); in q2 : ns(32)) { // iptype "armsfu2x2"; // ipparm "core = mycore"; -- core to hook into // ipparm "device = 0"; -- 2 possible devices per sfu type // } Stable at the start of a clock cycle d1 d2 Custom Datapath(combinational) OK! armsfu2x2 q1 q2
out_run( ) • out_run( ) is called at the start of the clock cycle, before any other run( ) is called, and before any signal or register is evaluated. • Typically, out_run( ) is used to initialize regOutput outputs to a stable value for that clock cycle.
Example: accumulator ipblock ipblock add ipblock acc(in d : ns(8); out q : ns(8)) { iptype “accumulator”; } myacc::myacc(char *name) : aipblock(name) { regOutput(0); accvalue = 0; } myacc::out_run() { ioval[0]->assignulong(accvalue); } myacc::run() { accvalue = accvalue + ioval[1]->toulong();}
Final hints • ipblock are a powerful extension mechanism for GEZEL • Puts a designer in control of design environment • Allows to put hardware design in context • Other neat tool: icg (incremental code generator) • Generates ipblock C++ out of GEZEL code • Useful to speed up simulation (3X – 10X faster) • Useful to compile HW to SW nice open research problems left here