-1

I implemented a very simple counter with preset functionality (code reproduced below).

module counter
#(
    parameter mod = 4
) (
    input wire clk,
    input wire rst,
    input wire pst,
    input wire en,
    input wire [mod - 1:0] data,
    output reg [mod - 1:0] out,
    output reg rco
);

    parameter max = (2 ** mod) - 1;

    always @* begin
        if(out == max) begin
            rco = 1;
        end else begin
            rco = 0;
        end
    end

    always @(posedge clk) begin
        if(rst) begin
            out <= 0;
        end else if(pst) begin
            out <= data;
        end else if(en) begin
            out <= out + 1;
        end else begin
            out <= out;
        end
    end

endmodule

I am having trouble understanding the following simulation result. With pst asserted and data set to 7 on a rising clock edge, the counter's out is set to data, as expected (first image below. out is the last signal, data is the signal just above, and above that is pst.). On the next rising edge, I kept preset asserted and set data to 0. However, out does not follow data this time. What is the cause of this behavior?

Simulation Result

My thoughts

On the rising clock edge where I set data to 0, I notice that out stays at 7, and doesn't increment to 8. So I believe that the counter is presetting, but with the value 7, not 0. If I move the data transition from 7 to 0 up in time, out gets set to 0 as expected (image below). Am I encountering a race condition?

Second Simulation Result

Testbenches

My initial testbench code that produced the first image is reproduced below. I show the changes I made to get coherent results as comments.

parameter mod = 4;
// ...
reg pst;
reg [mod - 1:0] data;
// ...
@(posedge clk); // ==> @(negedge clk)
data = 7;
pst = 1;
@(posedge clk); // ==> @(negedge clk)
data 0;
pst = 1;
@(posedge clk); // ==> @(negedge clk)
pst = 0;
@(posedge clk);
// ...
RayaneCTX
  • 573
  • 4
  • 13
  • Apparently at the second clock edge `data` is still 7 and only changes to 0 *after* the clock edge. – mkrieger1 Dec 19 '21 at 21:36
  • @mkrieger1 It does look like it. But then how come the first preset cycle sees `data` as 7 at the rising clock edge and not 0 (`data` is 0 before then)? – RayaneCTX Dec 19 '21 at 21:39
  • Because at the first clock edge `data` apparently is already 7. – mkrieger1 Dec 19 '21 at 21:41
  • @mkrieger1 I am trying to understand why the behavior is different from one clock edge to the next. In my testbench, I am simply doing this: `data = 7; @(posedge clk); data = 0; @(posedge clk)` to get the first image. – RayaneCTX Dec 19 '21 at 21:45
  • Okay. I don't know why it behaves that way then. – mkrieger1 Dec 19 '21 at 21:48
  • 1
    most likely your problem is in a data race. For example, at *posedge clk* you use blocking assignment in the test bench for *data* and nba in your rtl to assign its value to *out*. The result now depends on the order of evaluation of *data* in relation to the nba. To make the simulation predictable and stable, you should use nbas in the test bench as well at least for *data*. You can also think about using #delays in your test bench. – Serge Dec 22 '21 at 03:51

2 Answers2

1

You have a race condition test bench. The Verilog scheduler is allowed to evaluate any @ triggered in the time step in any order it chooses. All code after the granted @ will execute until it hits another time blocking statement. In your waveform it looks like data and pst from the from the test bench are sometimes being assigned before the design samples them and sometimes after.

The solution is simple, use non-blocking assignments (<=). Refer to What is the difference between = and <= in Verilog?

@(posedge clk);
data <= 7;
pst <= 1;
@(posedge clk);
data <= 0;
pst <= 1;
@(posedge clk);
pst <= 0;
@(posedge clk);

enter image description here

Greg
  • 18,111
  • 5
  • 46
  • 68
-1

I am able to obtain correct, predictable behavior if I modify my testbench to only modify input signals to my counter on falling clock edges rather than on rising clock edges (as it should be anyways). My best guess as to why the above behavior was occurring is that changing input signals at the same time the counter module is programmed to sample its inputs leads to undefined simulator behavior.

simulation

RayaneCTX
  • 573
  • 4
  • 13