4
\$\begingroup\$

Please refer to the following Verilog module:

module counter(
input  wire clk,
input  wire rstn
);

reg [4:0] counter;

// Powered by non-blocking assignments
reg  enable1;
reg  enable2;
reg  enable3;

// Powered by blocking assignments
reg  go_high_r1;
reg  go_low_r1;

// Powered by non-blocking assignments
reg  go_high_r2;    
reg  go_low_r2;

// Powered by continuous assignments
wire go_high_3;
wire go_low_3;

assign go_high_3 = (counter == 'd17);
assign go_low_3  = (counter == 'd28);

always @ (posedge clk or negedge rstn) begin
    if (!rstn) begin 
        counter <= 'd0;
    end else begin 
        // let it overflow
        counter <= counter + 'd1;
    end 
end 

// interaction b/w these signals below and enable1 signal is NOT clear
always @ (posedge clk or negedge rstn) begin
    if (!rstn) begin 
        go_high_r1 = 'd0;
        go_low_r1  = 'd0;
    end else begin 
        go_high_r1 = (counter == 'd17);
        go_low_r1  = (counter == 'd28);
    end 
end 


always @ (posedge clk or negedge rstn) begin    
    if (!rstn) begin 
        go_high_r2 <= 'd0;
        go_low_r2  <= 'd0;
    end else begin 
        go_high_r2 <= (counter == 'd16);
        go_low_r2  <= (counter == 'd27);
    end 
end 

always @ (posedge clk or negedge rstn) begin
    if (!rstn) begin 
        enable1 <= 'd0;
        enable2 <= 'd0;
        enable3 <= 'd0;
    end else begin 
        enable1 <= (go_high_r1) ? 'd1 : (go_low_r1) ? 'd0 : enable1;
        enable2 <= (go_high_r2) ? 'd1 : (go_low_r2) ? 'd0 : enable2;
        enable3 <= (go_high_3)  ? 'd1 : (go_low_3)  ? 'd0 : enable3;
    end 
end  

endmodule

Waveform:

wave

I am able to understand the interaction between enable_2 and enable_3 and corresponding control signals.

What I am not able to understand is that how enable1 gets its value without a single clock cycle delay (source: ans1, ans2) from the point of time when either go_high_r1 or go_low_r1 gets its value from the procedural block.

\$\endgroup\$

2 Answers 2

3
\$\begingroup\$

Although people tend to generalize by saying use nonblocking assignments for sequential logic and blocking assignments for combinational logic, the more precise rule is:

Use nonblocking assignments when one process writes to a variable, and another process reads the same variable, and both processes are synchronized to the same clock edge to prevent race conditions. This guarantees the reading process always uses the old value of the variable.

In your example it might be the ordering of the way you have written your 3 always blocks that determines whether the assignment to enable1 sees old or updated values of go_high_r1 and go_low_r1.

Synthesizable, combinational logic should always be written with blocking assignments. There are certain exceptions for modeling transport delays where you can use nonblocking assignments.

Within an edge triggered block, variables that are read before written within the same process will always become sequential logic regardless of which kind of assignment is used. Any variable written in one edge triggered process and read outside that process becomes sequential logic. That precise rule above still applies.

\$\endgroup\$
5
  • \$\begingroup\$ Thank You Dave for the answer, based on your explanation, I think what is happening in the code is that it is written before it is read. The reason why I am thinking of using clocked procedural block instead of always @ * is that the former might result in a circuit having combinational delay distributed over a clock edge. \$\endgroup\$
    – lousycoder
    Commented Mar 9 at 16:37
  • \$\begingroup\$ the read before write rules apply to within the same process. I've update my last paragraph. \$\endgroup\$
    – dave_59
    Commented Mar 9 at 17:29
  • \$\begingroup\$ Quoting: "Any variable written in one edge triggered process and read outside that process becomes sequential logic." I've corrected a mistake in the code of question details, Now referring to the signals in second always block from the top and enable1 signals. I'm able to digest the time at which 1go_high_r1 & go_low_r1 are "1". Not able to get is that how enable1 goes "1" without a clock cycle delay? As per quote, such a read becomes seq logic, which means that the value read from preceding FF cannot appear at the output of succeeding FF within a cycle, right? \$\endgroup\$
    – lousycoder
    Commented Mar 10 at 12:52
  • 1
    \$\begingroup\$ That is the definition of a simulation race condition. A simulator will execute each always block serially, whereas synthesis and true hardware executes them concurrently. Changing the order of execution of a read versus a write process changes whether the now or old value is seen by the read process when using blocking assignments. \$\endgroup\$
    – dave_59
    Commented Mar 10 at 17:14
  • \$\begingroup\$ moral of the story: do not use blocking assignment in procedural blocks while writing synthesizable Verilog code. BTW, I recall your comment somewhere on the EESE where you said something similar to "it is expected from the synthesizer to produce a circuit which behaves exactly like the Verilog code responded in the simulation". \$\endgroup\$
    – lousycoder
    Commented Mar 10 at 17:43
3
\$\begingroup\$

The recommended Verilog coding practice is to always use non-blocking assignments to describe sequential logic. This means that assignments to signals inside an always procedural block which has a posegde clk (or similar) in the sensitivity list should use <=, not =. Doing so leads to predictable simulation results, both before and after synthesis.

If you don't follow this simple recommendation, you get results that are difficult to explain, and others who use your code will find it difficult to understand.

Try not to dwell too much on why it is this way. There are many discussions on the Verilog event queue out there to read up on, but sometimes that just gets in the way of designing what you want to design.

\$\endgroup\$
1
  • \$\begingroup\$ If I use always @ *, I think that can save me from this confusion. Is there any other way of creating a control signal without using NBA? In this case, the signal might be dependent upon a lot of factors, so I cannot use assign statement and I don't want to deal with the 1 cycle delay behavior of NBA (at least for control signals). If you'd like to share some of the discussions on the Verilog event queue, please do, as I suppose that these semantics remain same for SV too. \$\endgroup\$
    – lousycoder
    Commented Mar 9 at 13:40

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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