errors · simdewfpga sim, after iverilog compiled the design
note [sim-starts-as-board]: 3 registers without a start value start as the board does, not at x: a at 4'b0101 and b at 4'b1010 (their reset values), c at 0 (no reset). Vivado's simulator shows x there until the first reset; a start value in the declaration (logic [3:0] c = 0;) is honoured by every tool (UG901 v2022.2 Ch.4 p.79).
dewfpga sim on a design with four registers read before the first clock edge: a with a synchronous reset to 5, b with an asynchronous reset to 4'b1010, c with no reset, d declared as logic [3:0] d = 4'd9;:
iverilog -g2012 -o top_sim design.sv tb.sv
note [sim-starts-as-board]: 3 registers without a start value start as the board does, not at x: a at 4'b0101 and b at 4'b1010 (their reset values), c at 0 (no reset). Vivado's simulator shows x there until the first reset; a start value in the declaration (logic [3:0] c = 0;) is honoured by every tool (UG901 v2022.2 Ch.4 p.79). https://nosey-dewdrop.github.io/dewfpga/errors/sim-starts-as-board/
PASS
tb.sv:15: $finish called at 21 (1s)
Before #35 the same run printed FAIL: before the first edge a=xxxx b=xxxx c=xxxx d=1001: the three registers without a start value were x until their first reset, and d was 9 from its declaration.
Why does it happen?
Icarus Verilog starts every 4-state variable at x, as IEEE 1800 says. A register with a synchronous reset and no start value stays x until the first clock edge with the reset high. Before that, case (state) on an x takes the default item, if (state == RUN) takes the else branch, x + 1 stays x: the simulation shows a definite value, and the board contradicts it. On the board every flip-flop starts at a known value, and since #34 the netlist dewfpga bit writes does too. Measured on the netlist and the FASM of the design above (16 flip-flops, 10 ZINI bits): a register with a reset starts at its reset value bit by bit (yosys maps a reset-to-1 bit to FDSE or FDPE and nextpnr programs INIT 1; a reset-to-0 bit to FDRE or FDCE at 0), a register with no reset at 0, a LUT RAM word at 0, a declared start value as declared: a=0101 b=1010 c=0000 d=1001 before the first edge. A state machine without a start value therefore starts in its reset state on the board and moves on at the first edge, while the simulation sat in default with every output off: 9 of the 11 random designs the fuzz test still found silent-wrong after #34 were this.
So dewfpga sim now lists the design's registers and memories with yosys (read, proc, flatten, memory_collect, opt_dff; yosys-slang when yosys' own reader refuses the code) and compiles one more module with the testbench, dewfpga_start, that at time 0, after the design's own declaration values and the testbench's time-0 statements ran, sets every register bit that is still x to the board's value and every x bit of every memory word to 0, through the testbench's instance (tb.dut.state). A declared start value is never x and stays. The note names what was started and how; nothing to start (blink: both registers have declared values), no note. When both readers refuse the design, or iverilog cannot write a variable this way, the simulation runs exactly as before, without a note. The listing costs one yosys run: 0.04 s on this design, inside a dewfpga sim of 0.3 s.
The listing runs opt_clean before opt_dff: yosys-slang leaves $buf cells between a reset mux and its flip-flop, and opt_dff does not see through them (measured: every register of a design read by yosys-slang listed as "no reset" without it, and the note said m at 0 and p at 0 (no reset) where the netlist starts them at 6 and 8'b10100101). A bit that opt_dff folds to a constant (written one constant under the reset and held otherwise: m2[1] <= 2 in the reset branch, nothing else) is no flip-flop any more; the netlist and the board hardwire it, so the simulation starts it at that constant too, and the note shows the whole register (m2 at 8'b00100001, not 8'b00000001).
When iverilog refuses the design and the simulation runs on the design as the build reads it (note [sim-from-build]), the same module is written for that elaborated design (listed from the build's own .il, every variable written whole, since an enum is a plain reg there) and the note prints after the sim-from-build note. Measured on a packed struct written with an assignment pattern, which iverilog refuses: before, FAIL: before the first edge led=xx3x; after, p at 8'b10100101 and m at 4'b0110 (their reset values) and PASS, the values the netlist shows.
What differs from Vivado's simulator?
Vivado's simulator (xsim) shows x for a register without a start value until its first reset, as iverilog did: a testbench that checks an output before the first reset sees x there and a definite value here. What Vivado programs into the bitstream for such a register is not in UG901; the values here are what the open chain programs (nextpnr-xilinx's default INIT for each primitive yosys picked), measured on the design's own netlist and FASM. UG901 v2022.2 Ch.4 p.79 lists FDRE, FDSE, FDCE, FDPE and says: "To initialize the content of a Register at circuit power-up, specify a default value for the signal during declaration".
What is the fix?
Nothing: the note says what ran. To make every tool, Vivado's simulator included, start the register the same way, give it a start value in its declaration; it is honoured by iverilog, xsim and the bitstream (UG901 v2022.2 Ch.4 p.79):
logic [3:0] c = 0;
state_t state = IDLE;
A memory gets the same through an initial loop (initial for (int i = 0; i < 4; i++) mem[i] = 0;), which synthesis reads as the RAM's initial content.
What about a table only partly loaded?
Only a memory with no value at all is started. A table that $readmemh fills in part ($readmemh("rom.hex", rom, 0, 3) on a 16-word rom, or a file shorter than the table) keeps x in the words it did not load, and the note says so after the started names:
note [sim-starts-as-board]: 1 register without a start value starts as the board does, not at x: q at 0 (no reset). Vivado's simulator shows x there until the first reset; a start value in the declaration (logic [7:0] q = 0;) is honoured by every tool (UG901 v2022.2 Ch.4 p.79). rom: 12 of its 16 words were given no value and stay x; the build treats such a word as a free choice (the netlist and the board read some value there, not 0), so load every word, or fill the table first (initial begin for (int i = 0; i < 16; i++) rom[i] = 0; $readmemh(...); end). https://nosey-dewdrop.github.io/dewfpga/errors/sim-starts-as-board/
Measured on that design: yosys folds the 16-word table into one LUT with the 12 unloaded words as don't-care, and the netlist reads CC (the value of the loaded word 2) at rom[10], not 0; the first version of #35 started those words at 0 and said the board does the same, which the netlist it built contradicted. Now rom[10] reads x in the simulation, as it did before #35 (nobody gave it a value), and the note names the gap and the fix. A design whose only memory is such a table and whose registers all have start values gets no note: the simulation is iverilog's alone.
Which command prints it?
Which probes in test/sv print this code?
- four 4-bit registers read before the first clock edge: a synchronous reset to 5, an asynchronous reset to 4'b1010, no reset, a declared start value (logic [3:0] d = 4'd9)
- a 3-state enum FSM with a synchronous reset and no start value, read at the first clock edge before any reset (the board starts in IDLE and moves to RUN)
- a 4-word LUT RAM read before any write (mem[0] on the first edge)
Every probe, with what Vivado does and what each stage does here: the probes, stage by stage.