2
votes

I want to buffer a single-bit signal "done" with two single-bit flip-flops. The done signal will rise for only one clock cycle in my design. So I wrote the following code.

//first level buffer done signal for one cycle to get ciphertext_reg ready
always @(posedge clk or posedge rst) begin
    if(rst)
        done_buf_1 = 1'b0;
    else
        done_buf_1 = done;
end

//second level buffer
always @(posedge clk or posedge rst) begin
    if(rst)
        done_buf_2 = 1'b0;
    else
        done_buf_2 = done_buf_1;
end

In functional simulation, I discover the done_buf_1 rises one cycle after done, but done_buf_2 rises at the same time as done_buf_1.

What is the explanation for this?

Thank you!

3
I hardly know any verilog and I'm far from certain about this but do you need to use <= instead of = in the assigments to ensure the correct timing herejcoder
See here for a good explanation of blocking and non-blocking statements: sutherland-hdl.com/papers/…Tim
@Tim Thank you for the reference!drdot

3 Answers

3
votes

You've already got answers with the solution ("use non-blocking assignments"), but here's an attempt at why you need to do that.

Both of your always statements have the same event, so they could run in any order. What seems to be happening is that the first one is running first. When the line...

done_buf_1 = done;

... is hit, it will block until the assignment is complete (it's a "blocking" assignment). Therefore done_buf_1 takes the new value immediately. This differs from the non-blocking version...

done_buf_1 <= done;

... which says 'give done_buf_1 the value of done (which I'll evaluate now) at the end of the time slice'.

Now we move on, and done_buf_2 is assigned.

done_buf_2 = done_buf_1;

Now, if done_buf_1 was updated with a blocking assignment it already has the current value of done, and you'll see both signal rise at the same time. If it was a non-blocking assignment then done_buf_1 still has the previous value of done, as it won't be updated until the end of the time-slice, the result being a 2 cycle delay for done_buf_2.

There's also another problem though. Remember that I said that the always statements could be run in either order because the events were the same? Well if the second one was executed first the code would appear to work as intended (db2 = db1; db1 = done; No problem). So it's worth knowing that using blocking assignments like this gives erratic results especially between tools. That can lead to some subtle bugs.

2
votes

You're using blocking assignments = to model synchronous logic. You need to use non-blocking assignments <=.

1
votes

As others have said: don't use blocking assignments (=) for this.

The key point is that "this" is the job of communicating between different processes. The race conditions inherent in blocking assignments make this unpredictable. VHDL takes this so seriously that it separates these types of assignment such that you can't use the wrong one (as long as you keep away from shared variables).

Some interesting writings on the subject from Jan Decaluwe: