So I'm trying to build a master-slave d flipflop in verilog,
module my_d_ff (output out, out_bar, input clk, in);
my_not clk_not0(clk_bar2, clk); //doesn't work
// my_not clk_not2(clk_bar2, clk); //does work
my_not clk_not1(clk_bar1, clk);
my_d_ff_helper m(mid, mid_bar, clk, clk_bar1, in);
my_d_ff_helper o(out, out_bar, clk_bar2, clk, mid);
endmodule
module my_d_ff_helper (output out, out_bar, input clk, input clk_bar, in);
my_2x1mux_helper m(out, clk, clk_bar, out_bar_bar, in);
my_not q_not(out_bar, out);
my_not q(out_bar_bar, out_bar);
endmodule
module my_2x1mux_helper(output out, input sel, input sel_bar, in0, in1);
For the parts that I omitted, my_not is obvious and my_2x1mux_helper is a mux that takes the negation of the selector recalculated.
When I compile and run, during the high half cycle of the clock, the slave outputs x rather than the old value. Experimentation shows that if I change the line, then the slave performs properly.
I do recognize that I'm negating clk two separate times but if I pass clk_bar into both my_d_ff_helper instances, then I get the same x behavior.
Is this an issue with my verilog compiler? Or am I misunderstanding mutability of input parameters?
-
\$\begingroup\$ Looks like a race condition. Try adding delay between the latches \$\endgroup\$Greg– Greg2019年01月16日 05:26:06 +00:00Commented Jan 16, 2019 at 5:26
-
\$\begingroup\$ @Greg how does changing the name of a part affect race conditions? \$\endgroup\$Donneaux– Donneaux2019年01月16日 13:35:40 +00:00Commented Jan 16, 2019 at 13:35
1 Answer 1
As I said in my comment, it looks like a race condition. Changing the name will not cause a race condition; but it could expose one. Per the IEEE standard, Verilog can have nondeterministic execution within a scheduler event region (Verilog has 4 event regions: Active Events, Inactive Events, NBA Update, Monitored Events).
All versions of the Verilog and SystemVerilog LRMs has an example of a race condition: (IEEE1364-1995 § 5.5, IEEE1364-2001 § 5.5, ..., IEEE IEEE1800-2012 § 4.8, IEEE-800-2017 § 4.8)
assign p = q;
intial begin
q = 1;
#1 q = 0;
$display(p);
end
$display(p)
can output 0 or 1 and both are allowed. It is up to the compiler/simulator to decide update p
first or display it first. In your case, changing the name seems to make the simulator choose different order of execution. The intention of the nondeterminism is an attempt to reflect the real hardware without talking delays into consideration.
Trying to pick names to get the order you want is not a solution. Any other changes to to the code, a different simulator or version, or ran on a different computer could expose the race condition again. The solution is to add intentional a well placed delay.
I set the timescale as `` timescale 1ns/100ps
(the precision smaller) then defined my_not as:
module my_not #(parameter D=0.0)(output out, input in);
assign #(D) out = ~in;
endmodule
Then changed: my_not q_not(out_bar, out);
to my_not #(0.1) q_not(out_bar, out);
to add a tiny delay. This creates a timing gap that allows all other evaluations to resolve to a deterministic value regardless of the evaluation order.
You can see my full code and run yourself with different simulations and version here: https://www.edaplayground.com/x/5eAp
I added the display statement in the my_2x1mux_helper
definition to get an idea how often evaluations occur (there are noticeable differences between some simulators and versions).
Normal a flop is written as RTL with non-blocking assignment; like below. The non-blocking assignment postpones the update to q
which prevents a race condition. With low level gate logic, delays need to be added to prevent zero-time feedback and race conditions.
always @(posedge clk)
q <= d;
For more detail, I recommend reading http://www.sunburst-design.com/papers/CummingsSNUG2006Boston_SystemVerilog_Events.pdf