GEZEL Standalone Simulation

From Gezel2

Jump to: navigation, search

This chapter covers GEZEL simulations. Besides the use of the simulation tool, the use of simulation directives is discussed as well as the use of the debug flag.

Contents

The simulation algorithm

GEZEL uses a cycle-true simulation algorithm, with an evaluate phase and a register-update phase. For each simulated clock cycle, the GEZEL kernel takes the following actions in sequence.

  • In each of the controllers in the system (hardwired, sequencer, FSM), select the control step to execute. Selection of the control step also chooses which sfg should be executed, and as a result, which expressions should be executed in the evaluate phase of this clock cycle.
  • For each datapath module, evaluate the outputs and the inputs of the registers contained in that datapath. The evaluation process makes use of all expressions which are enabled according to the active control step. Also, the evaluation process obeys data precedences between signals and can trigger evaluation of dependent expressions if needed.
  • Evaluate all ipblock (library blocks). The concept of library blocks is treated in GEZEL Library Blocks.
  • Evalute all $display, $trace and $finish directives that appear inside of an sfg that is currently being executed. Directives are discussed in later in this chapter.
  • Update all registers in the simulation by copying the next-value to the current-value.

This simulation algorithm shows the sequence of activities while the GEZEL simulator is in awake mode. As this name suggests, there is an alternate mode called sleep mode. At runtime, the GEZEL simulator switches automatically between these two modes, based on the activities in your design. In sleep mode, none of the steps 1 to 5 discussed above are executed; the simulator is effectively inactive. Sleep mode is triggered by the occurence of three runtime conditions:

  • During clock cycle N, none of the datapath registers has changed state.
  • During clock cycle N, none of the controllers has changed state.
  • During clock cycle N, none of the library blocks has indicated a change-of-state.

When all of these conditions are simultaneously true, it is easy to see that the simulation result of clock cycle N+1 cannot produce a result that is different from clock cycle N. Consequently, the GEZEL simulator enters sleep mode. If this happens in standalone simulation mode, then the simulation might as well terminate because the simulator cannot leave sleep-mode. However, the sleep-mode will be useful in cosimulations, in which a processor (instruction set simulator) can wakeup the GEZEL simulation dynamically.

The fdlsim tool

The standalone simulator for GEZEL is call fdlsim. The command line for fdlsim is as follows:

 fdlsim [-d] [<design.fdl>] <cyclecount>

Parameters in between square brackets are optional. When the design filename is not provided, fdlsim will read the design from standard input until an end-of-file is encountered. The -d is a debug flag. When it is enabled, the simulator provides a more detailed account of the activities during simulation.

When the command line executes, the GEZEL kernel will first parse the design. If any parsing errors are encountered, the simulation will be aborted. If the design is parsed successfully, the simulation will run. It will terminate on one of the following conditions (a) the target cycle count is reached (b) a runtime error occurs or (c) the $finish directive is executed.

The target cyclecount is a positive number (long), or the value -1 to indicate infinity (i.e. the target cycle count will never be reached).

There are three alternative methods by which the GEZEL simulator can parse and simulate GEZEL designs. Let us consider the following code, which simply counts clock cycles and prints them on the screen:

 dp mydp {
   always {$display("Cycle ", $cycle);}
 }
 
 system S {
   mydp;
 }

A first method of simulation is to use the command line. The program can be run as follows to execute 3 clock cycles:

 >fdlsim listing9.fdl 3
 Cycle 1
 Cycle 2
 Cycle 3

A second method is use standard input, to The first is to use the command line, as shown above. The second is to use standard input, and pipe the design into the simulator:

 cat listing9.fdl | fdlsim 3
 Cycle 1
 Cycle 2
 Cycle 3

The third method is to make use of the scripting feature of the shell. In that case, the location of fdlsim must be provided in the code. Assume fdlsim is located in /home/guest/bin/fdlsim, then the scripting feature (#!) is used as:

 #!/home/guest/bin/fdlsim
 
 dp mydp {
   always {$display("Cycle ", $cycle);}
 }
 
 system S {
   mydp;
 }

The GEZEL parser will treat line 1 (starting with #!) as a comment. To run this GEZEL program directly from the command line, first turn on the executable flag of listing10.fdl:

 >chmod +x listing10.fdl

After that we can run 3 cycles as

 >listing10.fdl 3
 Cycle 1
 Cycle 2
 Cycle 3

A line that starts with ‘#’ is considered as a comment line in GEZEL. This way, it is possible to use the C preprocessor on your GEZEL code before simulation. The C preprocessor enables the use of macro’s and include files. To run the simulator together with the C preprocessor, use the command line:

 >cpp -P listing10.fdl | fdlsim

Simulation directives

The simulation output can be modified with the use of simulation directives. These directives are embedded in the GEZEL datapath and controller descriptions.

Display directives print out variable values and messages during simulation. A display directive is embedded inside of an sfg, and will be executed when the sfg executes. The format of the display directive is

 $display(arg, arg, arg, ...);

The arguments of a display directive can be expressions or strings. For example, if the variables a and b are defined and available in the datapath, we can write

 $display(“The value of a + b is “, a + b);

The default printing format of a and b is hexadecimal. This format can be changed using a modifier directive. Values can be printed in hexadecimal ($hex), decimal ($dec) or binary ($bin). After a modifier directive is used, it stays in effect until a new one is applied. Thus, for the following three values, the first two will be hex, while the second two will be in binary:

 $display(a, b, $bin, a+b, a-b);

Apart from strings and expressions, also a number of meta-variables are available. Such variables cannot interact with registers or signals, but they can be printed to reveal runtime-dependend information. For this purpose, meta-variables are formatted as directives. $cycle returns the current cycle count. $dp returns the name of the datapath in which the display directive is used. And $sfg returns the name of the sfg in which the display directive is used.

Control directives affect the course of the simulation. There is one control directive: $finish. It can be used inside of a signal flow graph’s definition, and will terminate the simulation when this sfg is executed. Trace directives are used record stimuli files. Such a directive is used to create test vector files for future simulations. The format for a trace directive is

 $trace(expression, “filename.txt�?);

This directive must be placed just after the signal and register definitions inside of a datapath. The default output format for a trace directive is binary. There can be multiple trace directives active at the same time. In that case, each of them should write to a different file.

Another format of the trace directive is to use it in the state transition definition of an FSM. In this case, a message will be printed to standard output when the state transition is executed. An example of a trace directive in a state transition is

 @s0 (sfg1, sfg2, $trace) -> s1;

A summary of all the directives is as follows.

$display(arg, ..) Used inside of an sfg. Prints strings, expressions and meta-variables.
$cycle Returns current clock cycle (first cycle = 1).
$toggle Returns current overall toggle count.
$sfg Used as a $display argument.Returns the name of the current sfg.
$dp Used as a $display argument. Returns the name of the current datapath.
$hex, $bin, $dec Used as a $display argument. Changes the base of the next argument to hexadecimal, binary, or decimal.
$finish Used inside of an sfg. Aborts the simulation.
$trace(expression, “file.txt�?); Used after register/signal definitions in dp. Records value of expression each clock cycle in file.txt
$trace Used as an instruction in an FSM state transition. Echoes the state transision to the screen.
$option “string�? Includes optional simulator profiling. string is one of debug, vcd, profile_toggle_upedge, profile_toggle_alledge, profile_display_cycles, profile_display_operations, toggle_include, toggle_weights. (See below for details)

The debug flag

The main use of the GEZEL standalone simulator is validation of your GEZEL designs. In fact, before taking any GEZEL code into a cosimulation (as will be discussed later), it is a good idea to verify the design first in a small standalone simulation.

fdlsim provides a debug mode-of-operation that can be enabled using the -d flag on the command line. The effect of the -d flag is twofold: It will print out the GEZEL symbol table in a file called fdl.symbols. For each clock cycle, it will print out all register changes in the datapaths and controllers using a value-change format. This means that, if a register is not changing in a particular clock cycle, it will not be printed. The use of the debug flag will be illustrated on the Galois Field Multiplier discussed earlier. A small testbench was added after line 50 to multiply (1101) with (1001). Also, various directives were added in the simulation, such as on lines 7 (trace), 20 and 25 (display), 26 (finish) and 42 (trace).

 dp gfmul( in fp, i1, i2 : ns(4); out mul: ns(4); 
           in mul_st: ns(1); 
           out mul_done : ns(1)) {
   reg acc, sr2, fpr, r1 : ns(4);
   reg mul_st_cmd : ns(1);
 
   $trace(acc, "acc.txt");
 
   sfg ini { // initialization
     fpr        = fp;
     r1         = i1;
     sr2        = i2;
     acc        = 0;
     mul_st_cmd = mul_st;
   }
   sfg calc { // calculation
     sr2 = (sr2 << 1);
     acc = (acc << 1) ^ 
           (r1 & (tc(1))  sr2[3]) ^ (fpr & (tc(1)) acc[3]);
     $display("acc ", $bin, acc);
   }
   sfg omul { // output inactive
     mul      = acc;
     mul_done = 1;
     $display("done. mul=", mul);
     $finish;
   }
   sfg noout { // output active
     mul      = 0;
     mul_done = 0;
   }
 }
 fsm gfmul_ctl(gfmul) {
   state   s1, s2, s3, s4, s5;
   initial s0;
   @s0 (ini,  noout) -> s1;
   @s1 if (mul_st_cmd) then (calc, noout) -> s2; 
                       else (ini, noout)  -> s1;
   @s2 (calc, noout) -> s3;
   @s3 (calc, noout) -> s4;
   @s4 (calc, noout) -> s5;
   @s5 (ini,  $trace, omul ) -> s1;
 }
 
 dp TB( out fp, i1, i2 : ns(4); out mul_st: ns(1)) {
   reg ctl : ns(4);
   always {
     ctl = ctl + 1;
     fp  = 0b0011;
     i1  = 0b1101; 
     i2  = 0b1001;
     mul_st = (ctl == 0) ? 1 : 0;
   }
 }
 
 dp sysgfmul {
   sig fp, i1, i2, mul : ns(4);
   sig mul_done, mul_st: ns(1);
   use gfmul(fp, i1, i2, mul, mul_st, mul_done);
   use TB   (fp, i1, i2, mul_st);
 }
 
 system S {
   sysgfmul;
 }

The output of the simulation is:

 >fdlsim listing11.fdl 10
 acc 0000/1101
 acc 1101/1001
 acc 1001/0001
 acc 0001/1111
 gfmul_ctl: gfmul_ctl.s5 -> gfmul_ctl.s1
 done. mul=f
 finish reached !

The output of the first four lines was generated by the display directive in line 20. The next line originates from a $trace (line 42) telling that the controller gfmul_ctl makes a transition from state s5 to state s1. The result is displayed with another $display and finally the simulation is terminated as a result of the $finish directive. During the simulation, a tracefile is created for the acc register in acc.txt. The content of this file shows that the simulation ran 6 clock cycles. The file of acc is stored, as binary ASCII numbers.

 >cat acc.txt 
 0000
 0000
 1101
 1001
 0001
 1111

Now run the simulation again, commenting out all $display and $trace directives, but enabling the debug mode. The simulation can be monitored clock cycle by clock cycle. Lines indicating register changes include the previous register value, the new register value, and the register name including a pathname that identifies the datapath where the register is located. For example, we can see that in clock cycle 3, the acc register in datapath gfmul changes value from 0xd to 0x9. Or, in cycle 5, the FSM of gfmul_ctl moves from state s4 to state s5.

 >fdlsim -d listing11.fdl 10
 > Cycle 1
 gfmul_ctl: gfmul_ctl.s0 -> gfmul_ctl.s1
                    0                   9 gfmul.sr2
                    0                   3 gfmul.fpr
                    0                   d gfmul.r1
                    0                   1 gfmul.mul_st_cmd
                    0                   1 TB.ctl
 >  Cycle 2
 gfmul_ctl: gfmul_ctl.s1 -> gfmul_ctl.s2
                    0                   d gfmul.acc
                    9                   2 gfmul.sr2
                    1                   2 TB.ctl
 > Cycle 3
 gfmul_ctl: gfmul_ctl.s2 -> gfmul_ctl.s3
                    d                   9 gfmul.acc
                    2                   4 gfmul.sr2
                    2                   3 TB.ctl
 > Cycle 4
 gfmul_ctl: gfmul_ctl.s3 -> gfmul_ctl.s4
                    9                   1 gfmul.acc
                    4                   8 gfmul.sr2
                    3                   4 TB.ctl
 > Cycle 5
 gfmul_ctl: gfmul_ctl.s4 -> gfmul_ctl.s5
                    1                   f gfmul.acc
                    8                   0 gfmul.sr2
                    4                   5 TB.ctl
 > Cycle 6
 gfmul_ctl: gfmul_ctl.s5 -> gfmul_ctl.s1
 finish reached !

Value-Change Dump (VCD) files

When there are $trace statements in your code, and you run the simulator in debug mode (-d flag), then a VCD trace file will be generated as well. This file, called TRACE.vcd, can be read by digital waveform viewing tools such as GTKWave (http://www.geda.seul.org/tools/gtkwave/). The VCD file will contain only the signals for which you have written $trace statements. The debug mode generates a lot of output on the terminal. For this reason, it is also possible to enable/disable the debug mode and the VCD file generation independently. The statement

 $option "debug"

at the top of your file will enable only the debug mode that prints values of registers as they change to the terminal. The statement

 $option "vcd"

at the top of your file will enable only the VCD mode that creates the TRACE.vcd file.

Operation profiling and toggle counting

GEZEL provides a simple facility for operation profiling and toggle counting. Operation profiling returns the number of times each operation kind has executed over the course of a simulation. This is useful to estimate the computational complexity of a design. Toggle counting returns an estimate on the number of signal transitions that occur per clock cycle in a particular simulation. This is useful to estimate the immediate dynamic power consumption of a design.

Operation profiling and toggle counting are enabled with the $option directive. This directive is given at the top of a GEZEL file, before all datapath definitions. Consider the following small example.

 $option "profile_toggle_alledge“
 $option "profile_display_operations"
 dp counter(out v : ns(4)) {
   reg r : ns(4);
   always {
     r = r - 1;
     v = r;
   }
 }
 dp adder(in v : ns(4); out w : ns(4)) {
   always {
     w = v + 3;
   }
 }
 dp top {
   sig v, w : ns(4);
   use counter(v);
   use adder(v,w);
   always {
     $display("Cycle ", $cycle, " Toggle ", $toggle, " Counter ", v);
   }
 }
 system S {
   top;
 }

The directive “profile_toggle_alledge“ tells the simulator to collect toggle counts over the entire design. The directive "profile_display_operations" tells the simulator to display the resulting operations profile when the simulation terminates. Note that the $display statement in the datapath 'top' also contains a $toggle directive. This meta-variable will return the toggle count in the current clock cycle.

The listing above returns the following output.

 > fdlsim toggle.fdl 10
 Cycle 0 Toggle 12 Counter 0
 Cycle 1 Toggle 8 Counter f
 Cycle 2 Toggle 3 Counter e
 Cycle 3 Toggle 2 Counter d
 Cycle 4 Toggle 6 Counter c
 Cycle 5 Toggle 4 Counter b
 Cycle 6 Toggle 3 Counter a
 Cycle 7 Toggle 2 Counter 9
 Cycle 8 Toggle 9 Counter 8
 Cycle 9 Toggle 6 Counter 7
          Type          Evals       Toggles
      dpoutput             30             11
           reg             10             11
     assign_op             20             22
        sub_op             10             11

The output shows the number of evaluations and the number of net togglecounts per operation type. For example, in the 10 clock cycles, there have been 30 assignments (assign_op) and those 30 assignments have contributed to 22 0->1 and 1->0 transitions. These toggles are evaluates at the bit-level. Thus, given a current-value and a new-value, the overall toggle-count is defined by:

  overall_toggle = pop_count(current-value xor new-value)

with pop_count the population count function. Upgoing transitions can be evaluated as

  up_toggle = pop_count((current-value xor new-value) and new-value)

The toggle counting mechanism can be specialized in three different ways.

  • The toggle counting can be restricted to up-going transitions only. This is important to differentiate energy-consumption into the circuit (caused by the power supply charging parasitic capacitors) from energy-dissipation into the circuit (caused by charged parasitic capacitors discharging to ground). To count up-going transitions only, use the directive $profile_toggle_upedge instead of $profile_toggle_alledge.
  • The toggle counting can be restricted to specific data-paths. This can be done by adding the directive "$option toggle_include <datapath>" at the top of the file. This directive can be added multiple times, once for each data-path that must be included into the overall toggle count. toggle_include does not extend into the structural hierarchy of datapaths. Thus, if multiple lower-level datapaths are included into a single top-level datapath, then a toggle_include on the top-level datapath does not include the lower-level datapaths.
  • The toggle counting can be weighted such that the lsb of all words have a different weight then the lsb+1, lsb+2, ..., msb. For example, a circuit in differential logic can be simulated with different weights on the direct and differential nets. The directive "$option toggle_weight value1 value2 value3 ..." expresses these weights. The weight of a toggle on the lsb is value1, the weight of toggle on the second bit is value2, and so forth.

Because a GEZEL program is technology-independent, the toggle counting mechanisms are approximate values. The have a relative meaning rather then an absolute one. The options for toggle counting and their syntax is summarized below.

  • Enable toggle counting of upgoing and downgoing transitions.
 $option "profile_toggle_alledge"
  • Enable toggle counting of upgoing transitions only. This option is mutually exclusive with the previous one.
 $option "profile_toggle_upedge"
  • Display a summary of operation counts and toggle counts each clock cycle.
 $option "profile_display_cycles"
  • Display a summary of operation counts and toggle counts at the end of the simulation.
 $option "profile_display_operations"
  • Display the toggle count of the current cycle (can be included as part of any $display statement).
 $display("The current toggle count is ", $toggle);
  • Include a specific datapath in the toggle counting and operation profiling. By default, all datapaths will be included unless this directive appears at least once.
 $option "toggle_include my_dp"
 // my_dp must be the name of a datapath
  • Specific the weight for each bit in toggle counts. By default, all weights are 1.
 $option "toggle_weights 500 501"