aboutsummaryrefslogtreecommitdiffstats
path: root/example/explanation.org
blob: 793c8902c8f98bd8358bcfb38bce5e29234f031f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
#+title: Interesting edge case in Simulation and Synthesis Mismatch

I have always known about situations where the simulation might not match the synthesised result, for example when blocking assignment is used in one always block and is read from another.  However, the following mismatch is slightly surprising to me because I feel like simulators should be giving the same result as synthesis tools, as it seems to be quite common.  Otherwise, it also seems fair that the synthesis tool should implement what the simulator is showing, as it is strange to get such different results.

I tested the following with the Quartus 20.1 and Yosys 0.9+2406 synthesis tool (which both gave the same result) and the icarus verilog, verilator and ModelSim simulators which also agreed with each other.

Assuming we want to implement the following design.  My expectation would be that it just basically assigns ~a~ to ~x~ whenever ~a~ changes.

#+begin_src verilog
module top(a, x);
   reg [3:0] tmp;
   input [3:0] a;
   output reg [3:0] x;

   always @* begin
      x = tmp;
      tmp = a;
   end
endmodule // top
#+end_src

This seems to be correct when looking at the synthesised design printed by Yosys.

#+begin_src verilog
/* Generated by Yosys 0.9+2406 (git sha1 000fd08198, clang++ 7.1.0 -fPIC -Os) */
module top_synth(a, x);
  input [3:0] a;
  wire [3:0] tmp;
  output [3:0] x;
  assign tmp = a;
  assign x = a;
endmodule
#+end_src

However, when simulating it with the following testbench, it instead acts like a shift register.

#+begin_src verilog
module main;
   reg [3:0] a;
   wire [3:0] x, x_synth;

   top top(a, x);
   top_synth top_synth(a, x_synth);

   initial begin
      a = 0;
      #10 a = 1;
      #10 $display("x: %d\nx_synth: %d", x, x_synth);
      $finish;
   end
endmodule
#+end_src

The test bench above prints

#+begin_src text
x:  0
x_synth:  1
#+end_src

showing that the synthesised design does not match the simulated design.

Is the test bench that I wrote slightly broken?  Or is this expected to be this different.  In the latter case, how come simulators don't implement the correct behaviour?  This could be done by recursing and reevaluating the always block when an element in the sensitivity list changed, even if that was in the same always block.

Even in simulation I would expect these two snippets to act the same

#+begin_src verilog
always @* begin
   x = tmp;
   tmp = a;
end
#+end_src

#+begin_src verilog
always @* begin
   tmp = a;
   x = tmp;
end
#+end_src

Because ~a~ and ~tmp~ are both in the sensitivity list, meaning in the first code snippet, it should reevaluate the always block and update the ~x~ register with the correct value which is ~a~.