GEZEL SystemC Cosimulation

From Gezel2

Jump to: navigation, search

Contents

Cosimulating GEZEL with SystemC

GEZEL supports cosimulation with SystemC, a C++ library for hardware modeling as well as system level simulation created by the Open SystemC Initiative. SystemC can be downloaded from http://www.systemc.org. A detailed reference manual for SystemC is included in the release available from that website. This chapter presents the cosimulation interfaces between GEZEL and SystemC. This cosimulation interface allows users with legacy SystemC code to try out GEZEL modeling without leaving their existing system level environment.

Cosimulation Setup

The coupling between GEZEL and SystemC is done by integrating GEZEL as one or more modules (SC_MODULE) in SystemC.

  • A single module is required to couple the GEZEL kernel to the SystemC kernel. In the resulting simulation, GEZEL is a slave simulator attached to a SystemC clock.
  • One additional module is required for each data channel from GEZEL to SystemC or the other direction.

The same remarks as made in Section 5.1 on page 40 hold: the cosimulation interfaces for data exchange do not guarantee synchronization of the GEZEL application with the SystemC application. A synchronization protocol might still be required.

GEZEL uses a scripted approach for designs, while SystemC uses a compiled approach. Therefore, running a cosimulation must be done in two separate steps.

  • A SystemC program must be written that uses the GEZEL cosimulation interface, included in the C++ library libgzlsysc.a. This library is part of the standard GEZEL release, provided it has been configured with SystemC support (See GEZEL Installation). This SystemC program must be compiled and linked into an executable. The resulting program will have a module, the GEZEL model, which is defined by means of a filename, say mygezel.fdl.
  • When running the SystemC executable, the GEZEL description included in mygezel.fdl will be parsed in before the SystemC simulation starts, as part of the simulator initialization.

Once the SystemC executable is created, step 2 can be taken many times and each time the GEZEL description can be changed. Typically, the GEZEL parse times are only a fraction of the SystemC compilation times. When a module is interactively being debugged, GEZEL offers a more responsive way of working than the compiled approach.

GEZEL/SystemC Cosimulation Interfaces

As with C-based cosimulation discussed in Section 5.2 on page 41, a cosimulation interface has two sides: one side in GEZEL and one side in the cosimulated environment. The cosimulation interfaces on the GEZEL side will be captured in an ipblock. The cosimulation interfaces on the SystemC side will be captured in SC_MODULE.

GEZEL interfaces are unidrectional, and must specify the symbolic name of the interface variable they capture. An interface that transports data from GEZEL to SystemC is captured in a systemcsink. An interface that transports data from SystemC to GEZEL is captured in a systemcsource.

This example presents an interface to transport 32-bit signed numbers from SystemC to GEZEL. The symbolic interface name is sample, this is the name that SystemC will refer to.

 ipblock systemc_sample(out data : tc(32)) { 
   iptype "systemcsource";
   ipparm "var=sample"; 
 }

This example presents an interface to transport 1-bit numbers from GEZEL to SystemC. The symbolic interface name is output_data_ready.

 ipblock systemc_output_data_ready(in data : ns(1)) {
   iptype "systemcsink";
   ipparm "var=output_data_ready"; 
 }

The cosimulation interfaces at the SystemC side are complementary to the above ones. They are represented as SC_MODULE of the type gezel_inport or gezel_outport. These module types are declared in an include file systemc_itf.h, which is part of the standard GEZEL installation. The counterparts for the above interfaces in SystemC are defined as follows.

 #include “systemc_itf.h�?
 gezel_inport block1("block1","sample");
 block1.datain(sample_int);
 gezel_outport block2 ("block2","output_data_ready");
 block2.dataout(output_data_ready_int);

The SystemC modules accept two parameters, the first being the SystemC module name, and the second the name of the interface variable. The SystemC modules each define also an input- resp output-port as datain resp dataout. In the current version of the GEZEL/SystemC cosimulation interface, the type of these ports is fixed to sc_int<32>.

The SystemC/GEZEL cosimulation gives SystemC control over the GEZEL file that should be used, as well as over the clock that should be used for the simulation. A SystemC module called gezel_module is used for this purpose. This module is declared in systemc_itf.h as well.

Assuming that clock is an sc_clock, then the following SystemC definition will read in the GEZEL file fir.fdl upon simulation startup, and connect the GEZEL simulator to clock. This means that each upgoing positive edge in clock will result in a single cycle of GEZEL simulation.

 gezel_module G("gezel_block", "fir.fdl");
 G.clk(clock.signal());

In the current version of the GEZEL/SystemC cosimulation interface, only a single GEZEL file can be read in a SystemC simulation.

A FIR filter

To illustrate the use of the interface, design a FIR filter starting from the FIR filter included in the current SystemC 2.0.1 release. This example is a 16-bit FIR filter included under examples/systemc/fir. The goal is to substitute the FIR core in SystemC with an identical FIR in GEZEL, while keeping the surrounding testbench identical. First, look at the way the SystemC version of the filter is integrated (file main_rtl.cpp) in sc_main:

 sc_clock        clock;
 sc_signal<bool> reset;
 sc_signal<bool> input_valid;
 sc_signal<int>  sample;
 sc_signal<bool> output_data_ready;
 sc_signal<int>  result;
 fir_top   fir_top1    ( "process_body");
 fir_top1.RESET(reset); 
 fir_top1.IN_VALID(input_valid); 
 fir_top1.SAMPLE(sample); 
 fir_top1.OUTPUT_DATA_READY(output_data_ready); 
 fir_top1.RESULT(result); 
 fir_top1.CLK(clock.signal());

The block has three input ports and two output ports. Each of these ports will map to either a gezel_inport or a gezel_outport. In addition, a gezel_module will be required to hook up the GEZEL simulator into SystemC.

However, the signal types of the testbench do no correpond to the types supported by the cosimulation interfaces. A possible solution is to insert type translation modules in the SystemC simulation. For example, the following block converts bool (such as needed for input_valid) into an sc_int<32>.

 SC_MODULE(bool2int32) {
   sc_in< bool > din;
   sc_out< sc_int<32> > dout;
 
   SC_CTOR(bool2int32) { 
     SC_METHOD(run);
     sensitive << din;
   }
   void run() { 
     if (din.read()) dout.write(1); else dout.write(0); 
   }
 };

While not particularly compact nor fast, this glue code will do the job until the cosimulation interfaces will support a wider range of types. Now substitute the original fir_top in main_rtl.cpp with a GEZEL version as follows:

 #include "systemc_itf.h"
 
 int sc_main (int argc , char *argv[]) {
   ...
   sc_signal<sc_int<32> > reset_int;
   sc_signal<sc_int<32> > input_valid_int;
   sc_signal<sc_int<32> > output_data_ready_int;
   sc_signal<sc_int<32> > sample_int;  	    
   sc_signal<sc_int<32> > result_int;
   bool2int32 b1("b1"); 
         b1.din(reset); b1.dout(reset_int);
   bool2int32 b2("b2"); 
         b2.din(input_valid); b2.dout(input_valid_int);
   int322bool b3("b3");
         b3.din(output_data_ready_int); b3.dout(output_data_ready);
   int2int32  b4("b4");
         b4.din(sample); b4.dout(sample_int);
   int322int  b5("b5");
         b5.din(result_int); b5.dout(result);
   gezel_module  G ("gezel_block","fir.fdl");
         G.clk(clock.signal());
   gezel_inport  fir_reset("fir_reset","reset");
         fir_reset.datain(reset_int);
   gezel_inport  fir_in_valid("fir_in_valid","input_data_valid");
         fir_in_valid.datain(input_valid_int);
   gezel_inport  fir_sample("fir_sample","sample");
         fir_sample.datain(sample_int);
   gezel_outport fir_output_data_ready (
            "fir_output_data_ready","output_data_ready");
   fir_output_data_ready.dataout(output_data_ready_int);
   gezel_outport fir_result("fir_result", "result");
   fir_result.dataout(result_int);
   ...
 }

Five extra signals are created of type sc_int<32>. These are connected to the GEZEL interfaces and conversion blocks to resolve the type conversion issues discussed earlier. The fir_top1 module will be replaced by a GEZEL file defined in fir.fdl. This file will replace fir_data.cpp and fir_fsm.cpp.

A FIR algorithm in GEZEL

 dp fir(in  reset             : ns(1);
        in  input_valid       : ns(1);
        in  sample            : tc(32);
        out output_data_ready : ns(1);
        out result            : tc(32)) {
    lookup coefs : tc(9) = {  -6,  -4,  13,  16, 
                             -18, -41,  23, 154, 
                             222, 154,  23, -41, 
                             -18,  13,  -4, -6};
   reg rdy     : ns(1);
   reg rreset  : ns(1);
   reg rsample : tc(32);
   reg acc     : tc(19);
   reg shft0,  shft1,  shft2,   shft3 : tc(6);
   reg shft4,  shft5,  shft6,   shft7 : tc(6);
   reg shft8,  shft9, shft10,  shft11 : tc(6);
   reg shft12, shft13, shft14, shft15 : tc(6);
   sfg read {
     rsample = sample; rdy = input_valid; rreset = reset;
   }
   sfg rst  {
     acc=0;   shft0=0; shft1=0;  shft2=0;  shft3=0;
     shft4=0; shft5=0; shft6=0;  shft7=0;
     shft8=0; shft9=0; shft10=0; shft11=0;
     shft12=0;shft13=0;shft14=0; shft15=0; 
   }
   sfg shft {
     shft0=rsample; shft1=shft0;   shft2=shft1;   shft3=shft2;
     shft4=shft3;   shft5=shft4;   shft6=shft5;   shft7=shft6;
     shft8=shft7;   shft9=shft8;   shft10=shft9;  shft11=shft10;
     shft12=shft11; shft13=shft12; shft14=shft13; shft15=shft14;
   }
   sfg phi0 {
     acc = shft14 * coefs(15) + shft13 * coefs(14) + 
           shft12 * coefs(13) + shft11 * coefs(12) + 
           sample * coefs(0);
   }
   sfg phi1 {
     acc = acc + shft10 * coefs(11) + shft9 * coefs(10) 
               +  shft8 * coefs(9)  + shft7 * coefs(8);
   }
   sfg phi2 {
     acc = acc + shft6 * coefs(7)   + shft5 * coefs(6)  
               + shft4 * coefs(5)   + shft3 * coefs(4);
   }
   sfg phi3 {
     result = acc + shft2 * coefs(3) + shft1 * coefs(2) 
                  + shft0 * coefs(1);
      output_data_ready = 1; 
   }
   sfg noout { result = 0; output_data_ready  = 0;}
 }
 fsm cfir(fir) {
   initial s0;
   state s1, s2, s3, s4;
 
   @s0 (read, noout, phi0) -> s1;
   @s1 if (rreset)   then (read, rst, noout)  -> s1;
       else if (rdy) then (read, noout, phi1) -> s2;
            else (read, noout, phi0)          -> s1;
   @s2 (noout, phi2)      -> s3;
   @s3 (phi3, shft, read) -> s1;
 }
 
 // interfaces
 ipblock systemc_reset(out data : ns(1)) { 
   iptype "systemcsource";  ipparm "var=reset";
 }
 ipblock systemc_input_valid(out data : ns(1)) { 
   iptype "systemcsource";  ipparm "var=input_data_valid"; 
 }
 ipblock systemc_sample(out data : tc(32)) { 
   iptype "systemcsource";  ipparm "var=sample";
 }
 ipblock systemc_output_data_ready(in data : ns(1)) {
   iptype "systemcsink";  ipparm "var=output_data_ready";
 }
 ipblock systemc_result(in data : tc(32)) {
   iptype "systemcsink";  ipparm "var=result"; 
 }
 
 dp sysfir {
   sig reset, input_valid, output_data_ready : ns(1);
   sig sample, result : tc(32);
   use fir(reset, input_valid, sample, output_data_ready, result);
   use systemc_reset(reset);
   use systemc_input_valid(input_valid);
   use systemc_sample(sample);
   use systemc_output_data_ready(output_data_ready);
   use systemc_result(result);
 }
 system S {
   sysfir;
 }

The filter has the same data I/O as the set of interfaces in the SystemC main function (lines 2—5). The filter coeffcients are included as a lookup array (line 6—9). The design also includes registers for condition flags as well as for an accumulator and filter taps for the FIR (lines 10—17). The datapath is composed of a series of instructions that can evaluate the 16 filter taps in four clock cycles (lines 18—52). Thus, there are four multiply-accumulates per instruction.

The filter controller description starts from line 53. The sequencing is dependent on the reset input (which will reset the set of filter taps) and the input_available input. As soon as this control signal is asserted, the filter will go into one iteration of the algorithm and evaluated the 16 taps of the filter in four subsequent instructions.

The SystemC interfaces are expressed in lines 65—80. These interfaces are simply the couterparts of the ones we have added in main_rtl.cpp. Finally, a system block connects the filter core to the interfaces. Now you can compile the SystemC description, link the cosimulator and start the simulation. The compile command for main_rtl.cpp, assuming an installation under /home/guest looks as follows:

 g++ -O3 -Wall -c -I/home/guest/systemc-2.0.1/include \
                    -I/home/guest/gezel/build/include/gezel \
                   main_rtl.cpp -o main_rtl.o

The link command that creates the cosimulator is as follows.

 g++ -g stimulus.o display.o fir_fsm.o fir_data.o \
       main_rtl.o -L/home/guest/gezel/build/lib/ \
       -lgzlsysc -lfdl -lgzlsysc \
       -L/home/guest/systemc-2.0.1/lib-linux -lsystemc \
      -lgmp -o systemc_cosim

Why GEZEL with SystemC ?

The goals of GEZEL and SystemC are not the same. GEZEL focuses on easy modeling of cycle-true micro-architectures. SystemC focuses on solving system integration problems. Both have their place in the design of a complete system. GEZEL will be particularly useful when any of the following is an issue or critical requirement for you.

  • Compilation Time. GEZEL does not need to be compiled; it is parsed and interpreted. Compiling a model over and over again for each modification takes time, even if it’s only a few seconds for each iteration. For example, in the above FIR filter example, if we make a small modification inside of the SystemC model (in fir_data.cpp), then recompiling that file and relinking the SystemC model takes 2.5 seconds on a 3GHz Pentium PC. Making a modification to fir.fdl on the other hand and reloading the file takes less then 0.1 seconds.

In addition, the authors have shown in their research that GEZEL achieves the same simulation speed at cycle-true level as SystemC. So, interpreted does not have to mean slow.

  • Code Generation. GEZEL has a build-in path to VHDL implementation.
  • Error Messages. GEZEL generates error messages directly upon parsing, and directly in terms of the FSMD model. A SystemC design generates C++ error messages. This difference has important consequences on readability, and is most easy to demonstrate by means of an example. Next are two error messages for a similar type of error. The first one is from SystemC:
 main_gezel.cpp:95: 
 error: no match for call to `(sc_out<sc_dt::sc_int<32> >) (
   sc_signal<bool>&)'
 /home/schaum/systemc-2.0.1/include/systemc/communication/sc_port.h:230: error: candidates
    are: void sc_port_b<IF>::operator()(IF&) 
         [with IF = sc_signal_inout_if<sc_dt::sc_int<32> >]
 /home/schaum/systemc-2.0.1/include/systemc/communication/sc_port.h:239:   error:  
                void sc_port_b<IF>::operator()(sc_port_b<IF>&)
                [with IF = sc_signal_inout_if<sc_dt::sc_int<32> >]
 
 make: *** [main_gezel.o] Error 1

And here is the error message for a similar offense from GEZEL:

 *** Error: Signal has no driver S.reset
 
 Context:
 (96)     }
 (97)     
 (98)     system S {
 (99) >>>   fir(reset,input_valid,sample,output_data_ready,result);
 (100)      systemc_reset(output_data_ready);
 (101)      systemc_input_valid(input_valid);

Without going into the details of the exact error cause, the issue we are pointing at is easy to see.

  • Simplicity. GEZEL has simple FSMD semantics, easy to learn and to remember. Simple is not always better, but if the task is well defined as the creation of a micro-architecture using FSMD, then GEZEL may be just the tool you need.
  • Deterministic Simulation. GEZEL hardware does not (and cannot) generate race conditions. A semantically correct GEZEL model will never generate an ‘X’ during hardware simulation.