VTOC compiler


First draft manual. Feb 99.

Function and Purpose

The Verilog to C compiler takes a synthesisable Verilog module or set of modules and generates an equivalent ANSI C code section. Parts of the Verilog language which are not normally compilable to hardware are not compiled to C by the VTOC compiler, although there is limited support for the metacommands including $display.

The purpose of the VTOC compiler is to enable sections of Verilog to be used by non-hardware engineers in functional emulations of that hardware. Typically, the output from VTOC will be included as one of the modules in a system simulator or emulator.

Approach

The approach is to use standard Verilog compilation algorithms as embodied in Verilog hardware compilers to generate a set of C language assignments between integer C variables in the target C environment. These correspond to the reg and wire variables found in behavioural Verilog and to the D-type registers normally generated by Verilog compilation.

The generated assignments are of two forms: clocked and continuous. Clocked assignments are normally enclosed inside ` if' statements so that they are only executed when a clock edge is being simulated.

The left-hand side of an assignment is an integer variable in the target C environment and the right-hand side is an expression made up of these variables and constants combined with the normal operators found in C, including bitwise and, or and xor and bitwise negation.

Owing to the serial thread of execution found in C, assignments which take place in parallel in the Verilog implementation must either be placed in an order such that the same overall effect is achieved or else an intermediate variable must be introduced to model the parallelism. The intermediate variable acts like the master portion of a D-type flip-flop in a hardware environment.

Verilog expressions can be scalarised in the compiler or left in vector form. The choice is normally determined by a command line flag to the compiler. The flag is -scalarise , which, if present, causes all vector nets to be scalarised. If left vectored, broadside registers of up to 32 bits in width are held in one C variable. If scalarised, then each C variable holds just one bit. In the vectored form, the right-hand sides of the generated expressions will include further C operators, including addition, subtraction and left and right shifts. If a register or bus is wider than 32 bits then it is forced to be scalarised into a number of single bit locations, regardless of the command line flag.

Compilation Semantics

A fact of Verilog that is not well known is that certain constructs have different behaviour when synthesised from when simulated. The VTOC compiler will indicate warnings when such constructs are used. Examples are multiple always blocks using blocking assignment in the same module and side effects propagated via continuous assignments not being visable (see examples).

Source Verilog Structure

VTOC compiler can handle either a single module or a heirarchy where a top level module includes instances of sub-modules. Where a heirarchy of modules is used, the lowest, leaf modules are implemented only in RTL or behavioural Verilog. Specify blocks are not supported. The compiler flattens such heirachies to give a single module whose signature is the same as the original top level module, but where the RTL and behavioural code from all the intermediate modules has been combined.

Preprocessor

There is a standard Verilog preprocessor built in.

Case varients

The parallel_case modifier is respected. There is no logic minimiser the current release, so full_case has not effect.

Use of x as don't care

There is no logic minimiser the current release, so using an x just introduces a zero, unless -xisone is given.

Characteristic Examples

In these characteristic examples, the Verilog on the left is converted to the C on the right.

Here is an example of behavioural elaboration. Notice that the assignment to v has been re-ordered to be place it after v is used to set up d. Note that b is an input to the top level module, so is actually a reference and hence has a star in front of it, whereas the other variables are static locals.

       always @(posedge clk) begin          {
           a = ~b & c;                        a = ~(*b) & c;
           v <= a;                            d = v | c;
           d = v  | c;                        v = a;
           end                              }

An example of compiling an addition in vectored form is as follows. All examples will be in vectored form unless otherwise stated.

     reg [1:0] sum;                    {
     always @(posedge clk)                sum = (sum + 1) & 3;
           sum <= sum + 1;             }
           end

Here is the same example in sclared form

     reg [1:0] sum;                    {
     always @(posedge clk)                sum_1 = sum_0 ^ sum_1;
           sum <= sum + 1;                sum_0 = ~sum_0;
           end                         }

Here is an example where an intermediate variable is needed in the C version to implement a swap

     always @(posedge clk)             {
           a <= b;                        t = b;
           b <= a;                        b = a;
           end                            a = t;
                                       }

Bit extracts and selective assigns are handled with shifts and masks as shown in this example:

   reg [7:0] p, q, s;                  {
                                         unsigned static int  p;
                                         unsigned static int  q;
   always @(posedge c) begin             unsigned static int  s;
           p[7:4] <= q[3:0];             p = ((240 & p) | (15 & (15 & (s>>4))));
           p[3:0] <= s[7:4];           }
	end

In this next example, a signal feeds from one always block to another in the same module. In Verilog, this is bad practice since the relative timing of the updates is not defined. The compiler generates the code on the right and a warning message. When the same two always blocks are in different Verilog modules, the behaviour is defined, there is no warning and the same code is generated. The order of the assignmnets in the C generated code is correct, given that Verilog has an implied hash zero delay as a signal leaves one module, on its way to the other.

     always @(posedge clk) b = c;      {
                                         a = b;
     always @(posedge clk) a = b;        b = c;
                                       }

Where a continuous assignment causes an internal `side effect' to a block of Verilog code, whether this is honoured or not is not implemented correctly in all Verilog tools (the original language sematics were not clearly defined). Therefore there are two resulting sections of C code.

     assign c = b;                     {          {
     always @(posedge clk) begin          c = a;    d = c;
              b = a;                      b = a;    c = a;
              d = c;                      d = a;    b = a;
              end                      }          }
VTOC generates a warning on this example unless either the command line flag -continuous_immediate is used to dictate the left result or -continuous_delayed is used to dictate the right hand result.

If the net c had been defined and initialised in a single wire statment, without a net delay in that statement, as follows, then the side effect is honoured and the left-hand C code is generated:

     wire c = b;                       {          
     always @(posedge clk) begin          c = a;  
              b = a;                      b = a;  
              d = c;                      d = a; 
              end                      }         

Clocking and Calling

Mode 1

Many designs have a single master clock. Other designs have a single clock input, but internally divide this down to generate internal clocks of a lower rate. In either case, only a single clock signal is present in the top level module. The -mode1 command line flag takes an argument which is the name of an identifier present in the top level module which is to be treated as a clock input. When this mode is used, the actual clock input signal is unused. Instead, each time the `ttv' routine is called by a C thread, the system emulates the advance which would occur at the next active edge on that clock net. Therefore calls to the routine are clock cycles. All event control statements in the Verilog which are sensitive to this clock must have the same polarity (i.e. be all positive or all negative edge triggered).

Asynchronous reset inputs to top level modules in mode 1 do not have any effect for event control and essentially become synchronous values. Initial values generated by the initial statement in Verilog are honoured.

Mode 0

If the -mode0 command line flag is used for compilation, the system will update all internal state and outputs each time the `ttv' routine is called. Reset inputs in mode 0 behave normally and require no special treatment.

Combinatorial Paths and Loops

Top Level Combinatorial Paths

When combinatorial paths through the top level module are present, the outputs will only be updated to reflect input changes each time the `ttv' routine is called. Therefore, such paths might appear to have transparent latches in series with them, which are activated only as frequently as the `ttv' routine is called.

Internal Combinatorial Loops

Internal combinatorial loops are fully supported provided they would not oscillate in reality, which would be when the path is open and inverting overall. The compiler prints a warning for each combinatorial path found, regardless of polarity. An oscillating path will generate a half cycle for each call to `ttv'.

Input files needed

VTOC will read in any number of Verilog files. One of these should contain a root module whose name is given in the VTOC commmand line with -root . This module and all modules instantiated below it will be compiled to C in a single output file. If instantiated modules are not found, an error is given.

Mode 0

If the -mode0 command line flag is used for compilation, the system will update all internal state and outputs each time the `ttv' routine is called. If top level inputs are clocks or asynchronous resets, they will have the expected effect provided the call to the `ttv' routine is at least as fast as each change of such an input.

Divided down clocks

Many designs will generate their own internal clocks from a divided down version of the external clock. The normal rules of hardware design should apply, meaning that delay in generating the derived clock is unpredictable and so the design should guarantee that all other signals entering the lower speed domain will be stable on the derived clock edge.

The command line flag -clockbefore will cause the derrived clock to change before any of the inputs to it have changed, whereas by default, the reverse order is true. As stated above, this does not matter for sensible hardware designs.

Top Level Combinatorial Paths

When combinatorial paths through the top level module are present, the outputs will only be updated to reflect input changes each time the `ttv' routine is called. Therefore, such paths might appear to have transparent latches in series with them, which are activated only as frequently as the `ttv' routine is called.

Internal Combinatorial Loops

Internal combinatorial loops are fully supported provided they would not oscillate in reality, which would be when the path is open and inverting overall. The compiler prints a warning for each combinatorial path found, regardless of polarity. An oscillating path will generate a half cycle for each call to `ttv'.

Installation and operation

The program executable binary is normally called cv3core . A soft link to this file with the name vtoc is needed. If such a link is not available, we can use that the program can take its identity from the first argument and so can be invoked with fullpathname/cv3core vtoc args .

The Verilog files to be loaded must normally be found on the list of colon separated directories held in the CVPATH environment variable. This setting can also be provided from the command line via -I .

The VTOC system data files must also be online. These mostly have suffix .sml and are accessed via the CVMETAPATH environment variable. This setting can also be provided from the command line via -M .

For compiling the C code, the library header ttvtoc.h must be on the C compiler search path.

For linking the C code, the library header ttvtoclib.c must be lined with the auto-generated code.

Running the VTOC compiler

From the command line the compiler can be run quite simply. It just takes a list of Verilog file names each of which can have any number of modules in. One of the modules must be named as per the root command line flag. It is also sensible to specify the output file name, otherwise the output is the screen (stdout).

   $ vtoc -root TOPMOD -o outpufile.c file1.v file2.v ...

The command can also be run from shellscripts and makefiles etc.. The -f and -files commands can be read to import a long command line.

Command Line Flag Summary

    -root rootname    specifiy name of top level Verilog module

    -parsetree        dump the source code parsetree (ML syntax) 

    -o fn             specify output filename

    -E                preprocessor only

    -f  filename      read further command arguments from file

    -files            short for -f files

    -mode1 clk        a cycle of this clk is implied per call to `ttv'

    -mode0            no implied clock

    -I dirpath        override CVPATH directory list
    -Idirpath         same thing

    -M dirpath        override CVMETAPATH directory list
    -Mdirpath         ditto

    -id name          use this name instead of `ttv' in the output

    -o fn             output file name

    -root rootmod     give name of top level module

    -Dname            defines name as a preprocessor macro

    -verbose 	   verbose style progress reports

A Simple Full Example

A minimal example is as follows. The following Verilog is compiled with flags -root TEST -mode1 clk :
       module TEST(out, in, clk);
	 input in, clk;
	 output [3:0] out;
	 reg [3:0] out;
	 initial out = 0;
	 always @(posedge clk) begin
	      if (in) out <= out + 1;
	      #0 $display("Out is %h\n", out);
	      end
       endmodule


This generates the following C module. Note that the clk input is not used since -mode1 clk was given as a command line flag.

      /* output from CBG TT VTOC */

      #include "ttvtoc.h" 

      static int ttvtoc_ticks = 0; /*used for $time */ 

      void ttv(unsigned int *out, unsigned int *in, unsigned int *clk)
      {
          ttvtoc_ticks++;
	  *out = (*in) ? ((*out)+1) & 15):(*out);
	  ttvtoc_display("Out is %h\n", *out);
      }

      /* end of tt generated C code */
The routines for the metafunctions such as ttvtoc_display are found in the library ttvtoclib.c .

An example of the required C code to be linked with this is

       /* testwrapper.c */
       #include "ttvtoc.h" 

       int main()
       {
	 unsigned int clk, in, out; 
	 in = 1;
	 while (1)
	  { 
	    ttv(&out, &in, &clk);
	    printf("Out is now %i\n", out);
	  }
	}

The C code is written to the file specified with the -o command line flag: in this case `ttmyfile.c'. The command to compile and link the testwrapper with the generated C code and the runtime library is then:

	gcc -Wall ttvmyfile.c ttvtoclib.o testwrapper.c
	a.out
The output generated from executing this is
       Out is 0
       Out is now 0
       Out is 1
       Out is now 1
       Out is 2
       Out is now 2
       ...

(C) 1999 TT. TT home .