Blocking assignment and non blocking assignment in simulation

Posted by SilentQ-noob- on Mon, 01 Nov 2021 03:10:53 +0100

Problem background

Today, I encountered a problem when writing uart serial port code and simulating. Two signals send in uart module_ EN and tx_state. The original expectation was send_en a clock high level appears, TX_ The high state indicates that the serial port is transmitting data. The code is as follows:

//tx_state signal
    always@(posedge clk or negedge rst_n)begin
        if(!rst_n)
            tx_state <= 1'b0;
        else if(send_en)
            tx_state <= 1'b1;
        else if(baud_clk_cnt == 4'd11)
            tx_state <= 1'b0;
        else 
            tx_state <= tx_state;
    end

But there is a problem in the simulation: send_ The en signal appears before the high level of a clock, TX_ The state signal has been pulled up.
The simulation is shown in the figure below:

As can be seen from the figure, send_ The en signal is not high, but TX_ The state signal has changed.

reason

This is because there is no special distinction between blocking assignment and non blocking assignment in the simulation code.
The simulation code is as follows:

initial clk = 1;
    always #(`clock_period/2) clk = ~clk;
 
    initial begin
        rst_n = 0;
        send_en = 0;
        tx_data = 8'h55;
        baud_set = 3'b001;
        #(`clock_period*50);
        rst_n = 1;
        #(`clock_period*500);
        send_en = 1;
        #(`clock_period);
        send_en = 0;
        @(posedge tx_done);
        #(`clock_period);
        send_en = 1;
        tx_data = 8'haa;
        #`clock_period;
        send_en = 0;
        #(`clock_period*500);
        @(posedge tx_done);
        #(`clock_period*50000);
        $stop;
    end

It can be seen from the above code that all simulation statements use non blocking assignment, and all signals are generated by non blocking assignment. At this time, clk sampling will extract the signal value after the rising edge if it encounters the rising edge of the signal. The previous problem is that in the figure, it can be seen that the signal extracted by the clk signal on the rising edge is send_ The value after the rising edge of the en signal is the high level, and TX can be driven at this time_ The state signal becomes high, so send appears_ The change of en did not cause TX_ The state signal changes.

resolvent

Here you just need to send_ The assignment of en signal can be changed from non blocking assignment to blocking assignment. At the same time, we also turn other signals other than the clock signal into blocking assignment.

initial clk = 1;
    always #(`clock_period/2) clk = ~clk;
 
    initial begin
        rst_n <= 0;
        send_en <= 0;
        tx_data <= 8'h55;
        baud_set <= 3'b001;
        #(`clock_period*50);
        rst_n <= 1;
        #(`clock_period*500);
        send_en <= 1;
        #(`clock_period);
        send_en <= 0;
        @(posedge tx_done);
        #(`clock_period);
        send_en <= 1;
        tx_data <= 8'haa;
        #`clock_period;
        send_en <= 0;
        #(`clock_period*500);
        @(posedge tx_done);
        #(`clock_period*50000);
        $stop;
    end

The simulation results are shown in the figure below

reflection

This problem makes us notice that blocking assignment and non blocking assignment also need to be considered in the simulation, otherwise the code may be correct but inconsistent with the simulation.
Here I also specially write a counter to determine rst_n signal uses the difference between blocking assignment and non blocking assignment in simulation.
cnt_test.v

module cnt_test(
    clk,
    rst_n,
    cnt
);
    input clk;
    input rst_n;
    output reg [7:0]cnt;

    always@(posedge clk or negedge rst_n)begin
        if(~rst_n)
            cnt <= 8'd0;
        else if(cnt == 8'd99)
                cnt <= 8'd0;
        else
            cnt <= cnt + 1'b1;
    end
endmodule

Simulation code
cnt_test_tb.v

`timescale 1ns/1ns
`define clock_period 20
module test_tb;

    reg clk;
    reg rst_n;
    wire [7:0]cnt;

    test u_test(
        .clk    ( clk    ),
        .rst_n  ( rst_n  ),
        .cnt    ( cnt    )
    );

    initial begin
        clk = 1'b1;
        forever begin
            #10 clk = ~clk;
        end
    end

    initial begin
        rst_n <= 1'b0;
        #(`clock_period*10);
        rst_n <= 1'b1;
        #(`clock_period*500);
        $stop;
    end

endmodule 

Interested friends can take a look at changing the blocking assignment in the simulation code to non blocking assignment, where RST_ The influence of N on the counter cnt.

Topics: Verilog FPGA