-
Notifications
You must be signed in to change notification settings - Fork 523
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Incorrect always_comb
behavior, possible sensitivity issue
#872
Comments
I'm not sure this is not undefined behavior. This looks almost exactly like the example given in the TRM in section 4.8 "Race conditions". |
That section you describe appears to be describing race conditions between a hardware |
Or if you meant a race between the mini-testbench and the hardware, I think the delay after assignment and before the check should resolve that |
I don't think there is any differences in the rules for execution of blocking statements regardless of whether is |
What is the race you think might exist in this example? And why does the first example pass, and the second example fail? |
SystemVerilog on icarus verilog still recent work till i remember , you could try use verilog declarations to combinational e clocked pulse and compare results to give some clean view about problem if possible. |
The first example has a single process. Within a single process the order of execution is defined. So there is fully deterministic. The second example has two processes. These are running concurrently and the order of execution is non-deterministic. The TRM says that when running a blocking statement "execution may then continue with |
Don't the
I would interpret this to mean that when the
Effectively, this causes Am I misunderstanding something here? |
Maybe my understanding is wrong, I don't know. But |
I feel like an interpretation implying that the above design can have multiple behavioral interpretations opens the concern that any mixing of In 10.3.2 on continuous assignments:
I read this to imply that it should functionally behave like a constantly present assignment of combinational logic. In 4.9.2 on procedural continuous assignments:
This section sounds like your explanation of it being just placed in a region, however, in section 4.3:
I think this could imply that the simulator could instead implement the order in the functionally equivalent following way which still permits the arbitrary execution order? Though something doesn't fully add up here either since it seems like we could get an infinite loop with this approach. Perhaps the interpretation of "previous value" is key here, since I think you could perhaps argue the "previous value" of // first line of always_comb executes
_GEN_0 = 8'hF;
// _GEN_0 change triggers assignment to _GEN, which is scheduled in the region "using current values", thus save _GEN_0=8'hF, a=15'hFF for later
// TODO #1: assign _GEN = a & _GEN_0;
// second line of always_comb executes, _GEN_1 = 0
_GEN_1 = _GEN
// _GEN_1 triggers assignment to b, which is scheduled with saved value _GEN_1=0 for later
// TODO #2: assign b = _GEN_1;
// final line of always_comb executes, _GEN_0 = 0
_GEN_0 = 8'h0;
// ?? does this re-trigger `assign _GEN = a & _GEN_0;` again? cause an infinite loop? or is there something stopping that?
// execute TODO #1 with then-current value, _GEN=8'hF
assign _GEN = a & _GEN_0;
// execute TODO #2 with then-current value, _GEN_1=0
assign b = _GEN_1;
// always_comb is re-triggered via an update event since _GEN is a sensitivity
_GEN_0 = 8'hF;
// again, trigger the assignment, values are the same as last time
// TODO #3: assign _GEN = a & _GEN_0;
// second line of always_comb executes, _GEN_1 = 8'hF, a new value
_GEN_1 = _GEN;
// _GEN_1 triggers assignment to b, which is scheduled with saved value _GEN_1=8'hF for later
// TODO #4: assign b = _GEN_1;
// final line of always_comb executes, _GEN_0 = 0
_GEN_0 = 8'h0;
// ?? again, does this retrigger `assign _GEN = a & _GEN_0;`?
// execute TODO #3 with then-current value, but there is no change, no re-trigger of always_comb
assign _GEN = a & _GEN_0;
// execute TODO #4 with then-current value, update b to 8'hF
assign b = _GEN_1;
|
The with (System)Verilog is that it very easy to create code that has non-deterministic behavior in simulation. There is also this discussion that suggest that there is a unwritten rule to not interrupt a begin end block as long as there are no explicit delay instructions as that will create a whole new set of issues. |
From that discussion
vvp does exactly that. However, depending on the complexity of the RHS expression in the continuous assignment, it may suspend the execution of the continuous assignment before it updates the target net. This is an optimisation that avoids (fully) evaluating the expression multiple times if more than one of the expression's primary operands changes in the same time step. |
I ran a couple more experiments and it seems like the Keeping the testbench the same: module tb;
logic [7:0] a;
logic [7:0] b;
SimplerExample dut(.a(a), .b(b));
initial begin
#1
a = 255;
#2
if(b !== 15) $error($sformatf("Expected b=0xf, but found b=0x%x with inputs {a: 255}", b));
#8
$finish;
end
endmodule I added a module SimplerExample( // tmp_circt/tmp_circt388006263.mlir:7:1
input [7:0] a, // tmp_circt/tmp_circt388006263.mlir:7:27
output [7:0] b // tmp_circt/tmp_circt388006263.mlir:7:39
);
wire [7:0] _GEN; // tmp_circt/tmp_circt388006263.mlir:22:17
reg [7:0] _GEN_0; // tmp_circt/tmp_circt388006263.mlir:12:6
reg [7:0] _GEN_1; // tmp_circt/tmp_circt388006263.mlir:13:6
always_comb begin // tmp_circt/tmp_circt388006263.mlir:14:1
$display($sformatf("@%t: _GEN=%x", $time, _GEN));
_GEN_0 = 8'hF; // tmp_circt/tmp_circt388006263.mlir:9:6, :15:1
_GEN_1 = _GEN; // tmp_circt/tmp_circt388006263.mlir:16:1, :22:17
_GEN_0 = 8'h0; // tmp_circt/tmp_circt388006263.mlir:10:6, :17:1
end // always_comb
assign _GEN = a & _GEN_0; // tmp_circt/tmp_circt388006263.mlir:19:10, :22:17
assign b = _GEN_1; // tmp_circt/tmp_circt388006263.mlir:20:6, :23:1
endmodule Results:
Note that there's only execution at time 0, even though the TB drives Module: module SimplerExample( // tmp_circt/tmp_circt388006263.mlir:7:1
input [7:0] a, // tmp_circt/tmp_circt388006263.mlir:7:27
output [7:0] b // tmp_circt/tmp_circt388006263.mlir:7:39
);
wire [7:0] _GEN; // tmp_circt/tmp_circt388006263.mlir:22:17
reg [7:0] _GEN_0; // tmp_circt/tmp_circt388006263.mlir:12:6
reg [7:0] _GEN_1; // tmp_circt/tmp_circt388006263.mlir:13:6
always_comb begin // tmp_circt/tmp_circt388006263.mlir:14:1
$display($sformatf("@%t: _GEN=%x", $time, _GEN));
_GEN_0 = 8'hF; // tmp_circt/tmp_circt388006263.mlir:9:6, :15:1
_GEN_1 = _GEN; // tmp_circt/tmp_circt388006263.mlir:16:1, :22:17
// _GEN_0 = 8'h0; // tmp_circt/tmp_circt388006263.mlir:10:6, :17:1
end // always_comb
assign _GEN = a & _GEN_0; // tmp_circt/tmp_circt388006263.mlir:19:10, :22:17
assign b = _GEN_1; // tmp_circt/tmp_circt388006263.mlir:20:6, :23:1
endmodule Results:
Now at time 1 the Doesn't this imply that this is a sensitivity issue rather than a race or non-determinism between processes? |
No, because What is happening is:
I believe this behaviour is entirely consistent with the scheduling semantics given in the IEEE standard. You might want to test your code at https://www.edaplayground.com/. I think you'll find that other simulators behave the same way in this case. |
Thank you for your detailed explanation. I'm pretty surprised and I haven't been able to find a contrasting point in the spec so far. I'm also having a hard time coming up with a code generation solution which can treat logic feeding into an |
As the current behaviour both conforms to the IEEE standard and matches the behaviour of other simulators, closing as invalid. |
I'm working on finalizing another generator for ROHD that uses CIRCT instead of the native ROHD SystemVerilog generator. I've used both generators to create the same design in SystemVerilog to be run through iverilog. The original implementation passes, and the new one generated by CIRCT fails. I don't see an obvious reason why and so I'm suspecting a bug in iverilog.
I'm running on:
Icarus Verilog version 11.0 (stable) (s20150603-1127-gcf0bf4d9)
The original passing version:
The new failing version, which should be equivalent:
The failing version prints the following:
This is based on a unit test from ROHD found here: https://github.com/intel/rohd/blob/main/test/comb_math_test.dart
The text was updated successfully, but these errors were encountered: