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);


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);

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 Jan 16 '19 at 5:26
  • \$\begingroup\$ @Greg how does changing the name of a part affect race conditions? \$\endgroup\$ – Donneaux Jan 16 '19 at 13:35

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) 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;

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


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.