trsimManuals › Verilog Support

trsim Verilog Support

The Tuwa RTL Simulator, version 1.0

What the language front end accepts, and where it falls short. This manual is deliberately blunt about the gaps: meeting them here is cheaper than meeting them in a debugging session.


Scope

trsim targets Verilog-2001 as it is written for synthesis and for the testbenches around it. It has been driven hardest by a real system-on-chip — 1361 signals and 598 processes, with a CPU, cache, block RAM and a UART — whose output it reproduces exactly against a commercial simulator.

What is supported

Structure

Data

Behaviour

Operators

All the Verilog operators, including === and !==, the reduction operators, concatenation and replication ({4{1'b0}}), the conditional operator, shifts, and $signed / $unsigned.

System tasks

The parser recognises 123 system task and function names, including the timing checks ($setup, $hold, $width ...) and the random distributions ($dist_normal and friends). Recognised is not the same as implemented: the ones below are the ones in regular use and verified.

Output. $display, $write, $strobe, $monitor and their b, o and h variants.

Files. $fopen, $fclose, $fdisplay, $fwrite, $fstrobe, $fmonitor, and the read side $fgets, $fgetc, $feof, $fscanf.

Memory images. $readmemh, $readmemb, $writememh, $writememb.

Time. $time, $stime, $realtime, $timeformat, $printtimescale.

Control. $finish, $stop, $exit.

Waveforms. $dumpfile, $dumpvars, $dumpon, $dumpoff, $dumpall, $dumplimit, $dumpflush.

Command line. $test$plusargs, $value$plusargs.

Other. $random (with and without a seed), $signed, $unsigned, $itor, $rtoi, $bitstoreal, $system.

Format specifiers: %d %b %o %h %c %s %t %f %e %g %m, with widths, %0d-style zero-width, and correct x/z propagation into the printed digits — a partly-unknown vector prints x only in the digits that are actually unknown.

Known not to work: $sformat is recognised but does not write anything into its target string. Build the string with $display directly, or with $swrite to a file.

Known limitations

These are real and worth knowing before you write a lot of code.

A task containing a timing control does not suspend its caller

task tick;                       // does NOT work as written
    begin @(posedge clk); end
endtask

The call returns immediately without waiting. Keep @() and # waits in the block that needs them:

for (i = 0; i < 8; i = i + 1) begin
    @(negedge clk);
    din  = data[i];
    push = 1'b1;
    @(negedge clk);
    push = 1'b0;
end

A task with no timing control in it &mdash; a checker, a formatter &mdash; is fine and is used throughout the examples.

localparam derived from an overridden parameter

module m #(parameter W = 8) (...);
    localparam MASK = (1 << W) - 1;   // folds using W = 8 even if overridden

The localparam is evaluated before instance parameter overrides are applied, so it keeps the default. Use the parameter directly in expressions, or pass the derived value in as its own parameter.

reg signed is not accepted

reg signed [7:0] x;      // not accepted

Use integer, which is signed, or apply $signed() at the point of use.

A string parameter in a numeric context reads as 0

parameter NAME = "abc";
$display("%h", NAME);    // prints 0
$display("%s", NAME);    // prints abc, correctly

Passing a string parameter to $readmemh as a filename works.

VCD does not record array elements inside a submodule

A memory declared inside an instantiated module appears in the waveform file with its contents at time 0 and then a flat line. Scalars and vectors inside a module are recorded correctly. To watch a memory location, assign it to a scalar and watch that.

The compiled backend covers less than the interpreter

-native does not compile task calls, among other things. It reports what it cannot handle and stops rather than generating something subtly wrong. See the Compiled Backend manual.

Coding guidance

None of this is unusual style; it is what the examples do.

Write the combinational block before the register. Either order is legal Verilog and both work, but the conventional order reads better and is what has been exercised most:

always @(*) begin
    case (state)
        S_IDLE: next = start ? S_RUN : S_IDLE;
        ...
    endcase
end

always @(posedge clk or negedge rst_n) begin
    if (!rst_n) state <= S_IDLE;
    else        state <= next;
end

Compare with !== in checks. != returns x when either side has an x bit, and if (x) takes the false branch, so a genuine mismatch can slip past. !== compares x and z exactly.

Give every reg a reset or an initial value. trsim starts variables at x and propagates it faithfully, which is what you want &mdash; but it means an unreset register shows as x until something writes it, and any arithmetic on it stays x.

Keep waits out of tasks &mdash; see above.

Reporting a gap

The most useful bug report is the smallest .v file that shows the problem, what trsim prints, and what another simulator prints for the same file. That is exactly how the limitations above were found and how most of them were fixed.

← All trsim manuals