3
\$\begingroup\$

I have a very simple module that waits for the valid signal to become 1 and then sets the data signal to 1 as below:

module test2(
    input clk,
    input valid,
    output reg data = 0
    );
    
    always @(posedge clk) begin
        if(valid) begin
            data <= 1;
        end
    end
      
endmodule

The problem is with my testbench. When I use the following testbench, the result is weird:

module test( );
    reg clk = 1;
    reg valid = 0;
    wire data;
    
    always 
        clk = #5 ~clk;
        
    initial begin
        @(posedge clk);
        @(posedge clk);
        @(posedge clk);
        valid =  1;
    end

    test2 uut(
        .clk(clk),
        .valid(valid),
        .data(data)
    );
    
endmodule

This leads to the following result:

enter image description here

As is obvious, the data signal and valid signal have changed their value at the same time (same posedge clk). Since we declared our design module sequentially, it should make a flipflop, but the result is not as flipflop behavior.

Furthermore, if I change the line valid = 1; to valid <= 1; in my testbench, the output is modified:

enter image description here

What is the problem here, and why does blocking or nonblocking assignment in the initial block in the testbench affect the design behavior?

I am using Vivado2023 for simulation.

\$\endgroup\$
0

2 Answers 2

0
\$\begingroup\$

With the blocking assignment in the testbench (=), you have a simulation race condition.

The proper way to drive synchrounous inputs from the testbench in Verilog is to use nonblocking assignments (<=). You should drive valid from the testbench in the same way you drive data from the design, namely using:

  • The same edge of the clock: @(posedge clk)
  • Nonblocking assignments

Using the nonblocking assignment in the testbench guarantees correct simulation behavior because it eliminates the simulation race condition.

It doesn't matter whether you are driving signals from an initial block or an always block. Both types of blocks are used for procedural assignments. You need to be consistent.

\$\endgroup\$
0
0
\$\begingroup\$

The issue is that you have a race condition. There are two @(posedge clk) conditions: one in the always block and one in the initial block. They can execute in any order.

When you do a blocking assignment, it takes effect immediately. This the behavior depends on which order the simulator procesess events.

A non-blocking assignment waits until all pending events have cleared before taking effect, so it doesn't matter which order the simulator processes things. All of the actions triggered by a single clock edge happen before any of the variables are updated.

This is why in general it's problematic to use blocking assignments in a synchronous process. Some people think this is specifically a problem for synthesis, but it's actually fundamental to the verilog execution model.

\$\endgroup\$

Your Answer

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.