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.
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.
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.
There is a standard Verilog preprocessor built in.
The parallel_case modifier is respected. There is no logic minimiser the current release, so full_case has not effect.
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 }
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.
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.
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.
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.
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.
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.
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.
-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
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.outThe 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 ...