trsim › Manuals › 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
- Modules, ports in both the ANSI (
module m (input wire [7:0] d);) and the Verilog-1995 styles - Parameters,
#(.NAME(value))overrides at instantiation,defparam - Hierarchy to any depth; hierarchical references such as
dut.core.state `include,`definewith arguments,`ifdef/`ifndef/`else/`endif,`timescalegenerateblocks andgenvar
Data
wire,reg,integer,time,real, and the net typestri,wand,wor,triand,trior,tri0,tri1,supply0,supply1- Vectors of any width. Values wider than 64 bits are held and printed in full
- Memories —
reg [7:0] mem [0:1023]— with bit and part selects on their elements - Four-state values throughout: 0, 1, x and z, per bit
Behaviour
always,initial,begin/end,fork/join- Blocking and non-blocking assignment, including intra-assignment delay (
x <= #5 y) if,case,casex,casez,for,while,repeat,forever,do- Event control:
@(posedge clk),@(negedge clk),@(*),@(a or b), named events and-> - Delays, including fractional ones such as
#2.5 wait (expression)- Tasks and functions, with automatic and static storage
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 — a checker, a formatter — 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 — 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 — 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.