Behaviour of Blocking Assignments inside Tasks called from within always_ff blocks - verilog

Have looked for an answer to this question online everywhere but I haven't managed to find an answer yet.
I've got a SystemVerilog project at the moment where I've implemented a circular buffer in a separate module to the main module. The queue module itself has a synchronous portion that acquires data from a set of signals but it also has a combinatorial section that responds to an input. Now when I want to query the state of this queue in my main module a task, inside an always_ff block sets the input using a blocking assignment, then the next statement reads the output and acts on that.
An example would look something like this in almost SystemVerilog:
module foo(clk, ...)
queue = queue(clk, ...)
always_ff#(posedge clk)
begin
check_queue(...)
end
task check_queue();
begin
query_in = 3;
if (query_out == 5)
begin
<<THINGS HAPPEN>>
end
end
endtask
endmodule
module queue(clk, query_in, query_out)
always_comb
begin
query_out = query_in + 2;
end
endmodule
My question essentially comes down to, does this idea work? In my head because the queue is combinatorial it should respond as soon as the input stimulus is applied it should be fine but because it's within a task within an always_ff block I'm a bit concerned about the use of blocking assignments.
Can someone help? If you need more information then let me know and I can give some clarifications.

This creates a race condition and most likely will not work. It has nothing to do with your use of a task. You are trying to read the value of a signal (queue_out) that is being assigned in another concurrent process. Whether it gets updated or not by the time you get to the If statement is a race. U?se a non-blocking assignment to all variable that go outside of the always_ff block and it guarantees you get the previous value.

in order to figure out the stuff, you can just mentally inline the task inside the always_ff. BTW, it really looks like a function in your case. Now, remember that execution of any always block must finish before any other is executed. So, the following will never evaluate to '5' at the same clock edge:
query_in = 3;
if (query_out == 5)
query_out will become 5 after this block (your task) is evaluated and will be ready at the next clock edge only. So, you are supposed to get a one cycle delay.
You need to split it into several always blocks.

Related

SystemVerilog calculations right before writing to clocking block

I have a task, whose job it is to drive data onto a bus via a clocking block. See snippet:
task effects_driver::ReadQueueData();
stream_intf_.cycles(); // clocking block event
if (sample_q_.size() >= 2) // check to see if there is enough data in the queue
begin
automatic bit [31:0] sample0, sample1;
sample0 = sample_q_.pop_front(); // read from queue
sample1 = sample_q_.pop_front(); // read from queue
stream_intf_.cb.AXIS_TDATA <= {sample1, sample0}; // drive two samples at once
stream_intf_.cb.AXIS_TVALID <= 1;
end
else
...
endtask
You'll notice that I need to read a couple of items out of a queue before writing it to the clocking block. Is this the correct way to do it? Am I guaranteed that the simulator will perform these blocking assignments to the automatic variable before writing it to the clocking block?
P.S. I run into this scenario semi-frequently--where I need to do some quick calculations on the fly right before writing to the clocking block.
I believe you meant to ask "Am I guaranteed that the simulator will perform these blocking assignments to the automatic variable before writing it to the clocking block?" because that is what your code is doing.
The answer to that is yes, blocking assignments are guaranteed to complete before executing the statement following it.
Also note that there is no need to declare sample0 and sample1 with automatic lifetimes because class methods always have automatic lifetimes. Variables declared within them are implicitly automatic.

Verilog task based TB issue

I am quite new to verilog, specially using it for TB coding and I am facing the following issue -
I am working on verifying round-robin (RR) arbiter and want to check the DUT grant with a TB modeled grant. I am writing a task to implement the TB model and then calling the task at every posedge of clock as follows -
always #(posedge clk or posedge reset) begin
if(reset) reset_state;//task to reset state
else begin
//Initialising req vector every cycle
req_vc = {req4, req3, req2, req1, req0};
for (index=0; index<5; index=index+1) begin
temp[index] = rr_pri_state[index];
end
chk_grant; //task that gives out checker grant
**if(gnt_id_checker != gnt_id)** begin//logic to compare checker grant with DUT grant (gnt_id) and end test if false
$display ("\tERROR!! Model grant and DUT grant IDs MISMATCH.\n\t\tModel grant = %d\n\t\tDUT grant = %d ", gnt_id_checker, gnt_id);
#1 $finish;
end
end
end
All my variables are globally defined
The issue is with the if comparison just after chk_grant task is called. When I simulate this I see that the values for gnt_id and gnt_id_checker used for comparison are not being updated to the latest one for that cycle. It is still performing comparison on old values. If i introduce #1 delay before if statement then everything works as expected with the correct values.
So I want to understand if this is expected or if I have approached this incorrectly?
Here is how my chk_grant task looks like for your reference. It basically finds out highest priority requestor and updated memory to reflect the new priority order
//task to calculate checker grant AND update priority state for next cycle
task chk_grant;
begin
i=0;
while (i<5) begin
req_index = rr_pri_state[i];
if(req_vc[req_index]) begin //we have found the req getting grant this cycle
gnt_id_checker = req_index;
k = 5-(i+1); //i+1 req will have the highest priority next cycle
for (j=0; j<k; j=j+1) begin
rr_pri_state[j] = temp[i+j+1];
end
for (j=0; j<=i; j=j+1) begin
rr_pri_state[j+k] = temp[j];
end
i=5;
end //end of if
i=i+1;
end //end of while loop
end
endtask
PS - I have no particular reason for using task here. If you think an event/function is better here please do let me know how and why so. Any other inputs for improving this would be much appreciated. Thanks!
Not sure if theres anything wrong with the approach you are taking (ie, doing the work in a separate task, which in my mind makes more sense than a function as you are updating a few globals, not just getting the gnt_id_checker. If it was just getting a single value out, I would use a function).
The reason you need a #1 is that when the design run, I assume gnt_id comes from a NBA (<=) from some register. Thus you are updating your gnt_id_checker in the current process right away (blocking assignment, =) but the assignment of gnt_id is delayed into the postponed region and thus not being used by the if-statement in performing the check. If it happens you are updating gnt_id combinationally, then theres a race condition between this block and that assigning gnt_id. Im assuming theres another block actually providing the stimulus to the design which is hard to tell without more information on your variables. If this same block is providing the stimulus, you definitely not giving your design a chance to run before checking the result (which it basically comes down to either way).
So to answer you directly, yes, this is expected and no, your general approach looks fine. Though for stylistic comments, I would suggest calling your checker a golden model as that is what you are doing. So, the task might be called run_golden instead (checkers are a thing in SystemVerilog as a separate concept). And would move temp into the task so the golden model is entirely contained within the task.

register inferred by blocking assignment and races

I am new to verilog and have a doubt concerning the race conditions in the following code which is taken from FPGA Prototyping by Veriloog Examples by Pong P. Chu. The code is:
always #(posedge clk)
a = b;
always #(posedge clk)
b = a;
This will infer races depending on which always block gets executed first. But always blocks should get executed in parallel. Correct me if I am wrong. I know there is blocking assignment but how does it affect the first statement of the block, which is the always statement?
The second code using the non-blocking assignment is:-
always #(posedge clk)
begin //b(entry) = b
a <= b; //a(exit) = b(entry)
end //a = a(exit)
always #(posedge clk)
begin //a(entry) = a
b <= a; //b(exit) = a(entry)
end //b = b(exit)
This will work fine according to the book but I couldn't understand why? Is it because the always blocks are executed in parallel in this case because of non-blocking assignment?
Because the Verilog "stratified event queue" has different regions, the type of assignment operator used makes a difference in how the code is executed. The details can be found in section 5 of the IEEE Verilog standard, but in terms of your question, it boils roughly down to this:
The blocking assignments (in your first example) are evaluated immediately, i.e. when the always block they're in is activated. Since both blocks are parallel, order of their activation is undefined.
The non-blocking assignments are not executed immediately but rather scheduled after all blocks of the same time step have finished executing.
So when encountering a rising clk edge, in your first example a simulator would
Pick at random one of the always blocks
Find that the assignment is a blocking one and execute it immediately, therefore changing the left-hand variable in that assignment.
Pick the other always block and do the same, at which point the value of the first variable was already changed.
In your second example, the simulator would
Pick at random one of the always blocks
Find that the assignment is a non-blocking one and therefore
evaluate the right-hand side of the = sign and schedule the value it found there as a non-blocking assign update event to the variable left of the =.
Pick the other always block and do the same. Note that both variables still have their values from before the rising clk edge.
Since all blocks are done executing, update all variables which were scheduled for non-blocking assign update events, effectively swapping their values.

Non-blocking and blocking assignments don't work as expected

I'm having problems with understanding such a simply looking thing: blocking and non-blocking assignments.
I created a small test bench just to simulate the behavior of this code:
module ATest(clk, out);
input wire clk;
output reg [7:0] out;
reg [7:0] A;
initial begin
A <= 8'b0;
end
always #(posedge clk) begin
A = A + 1;
out = A;
end
endmodule
After simulation, I got this wave:
I expected the same value under both A and out, as I assigned values to them sequentially. Why is out "don't care" during the first clock?
Then I tried to use non-blocking assignment. I changed a part of my code into:
always #(posedge clk) begin
A <= A + 1;
out <= A;
end
And I got this wave:
I didn't expect anything here, because non-blocking statements are kind of mystery to me. Why is both A and out set to "don't care"?
Also, I found different names on every page I got to, so please help me out:
Are blocking and non-blocking interchangeable with sequential and concurrent as terms? Which one is right: non-blocking statement or concurrent statement?
Without diving too deep into the simulation cycles used by Verilog Simulators, you can think of non-blocking vs blocking assignment simply as this:
Blocking assignment happens inline at the time the given assignment is executed, so that means if I have a line like A = A + 1, that means we take the present value of A, add 1 and assign A that new value. So, the assignment "blocks" execution until it is done.
Non-blocking assignment (NBA) happens at a time slightly later than while the line is executed. You can think of non-blocking assignments as lines telling the simulator to schedule this assignment for a little bit later (note, later is still with the same simulation time step, so all of this is still happening in simtime t). So, if you have something like A <= A + 1, this means take the value of A at the time of executing this line, add 1 and schedule A to be updated to that value in a little bit, but keep moving on with the lines following that one. So, if the next line after is out = (A == 1) ? 1 : 0, this line will execute using the old value of A, not the incremented one. Once the simulator finished with the active code, it can move on to perform all the non-blocking assignments. Now, A will get the incremented value and all other non-blocking assignments will take effect.
So, to your examples. In case one, we see the delayed effect of NBA. In the initial block, A is assigned to 0, which means A will take on the value of 0 a little bit later (still within sim time 0 remember); ie the assignment is scheduled to take place after all blocking assignments have run (Not strictly true but it works in this case). Also, you have the clock's posedge happen so the always block runs. Here, A takes on the value A + 1, but remember, the assignment of A to 0 hasnt happened, so A still has its initial value of 8'bx. so, A + 1 is also 8'bx. And since this is a blocking assignment, it happens right away. So, A doesnt change from don't care. Continuing on, out gets the current value of A, which is 8'bx. So, we get the don't cares on out. After these and other blocking assignments are done, now we finish up the NBAs, in this case, A to become 0. So, still within sim time 0, A becomes 0 and we are done. At the next posedge of the clock, A is 0, out is don't care and your always block runs as expected, incrementing A and assignment out to the same value.
If you change the always block to use NBA (which it should if it is suppose to be a register), things change slightly. The initial block still results in a NBA scheduled for A to become 0. But now, the always block does something different. Now, A <= A + 1 instead of assigning A to don't cares right away, it schedules A to become 8'bx (remember, the right-hand side expression for what value to assign is evaluated inline, so A + 1 still uses A as don't care just as before; whats changed is when A takes on this new value) and this is scheduled after A to become 0. So, both the NBAs of A are set up, but the one telling A to be 0 happens first and is wiped out by the later assignment of A to 8'bx. out is similarly scheduled to take on 8'bx but now, A never becomes 0. As such, both A and out get stuck at 8'bx.
You can look through the Verilog or SystemVerilog LRM's to get a better understanding of sim cycles and what really goes on, but I hope this helps you better understand the difference!
Your issue come from using a non-blocking assignment in your initial block. Use initial A = 8'b0; instead.
The casue for this is likely how the two assignments are processed. = assignments are done incrementally, with any new values being available to subsequent assignments. Changes made via <= assignments are only available once all assignments have been processed.
Because your first edge is at t = 0 (when intial blocks are processed), in the first example A is assigned 0, but that 0 isn't available to out until after it is processed. That's while the first cycle looks weird, but everything else is OK. In the second, A is assigned both 0 and A+1, so the simulator uses the always block instead of the initial, going with A+1, when A is still an unknown value. As such, the values for A and out are never known.
The terms are equivalent. "Blocking" is the same as "sequential" because "blocking" means that the assignment must be done before the simulator moves to the next line (in sequence). "Non-Blocking" means that all the lines may be done at once. As everything with Verilog, it helps to imagine the hardware intended, so you may think of it as "parallel" vs. "serial" sometimes.
Is there a positive clock edge at time 0 in your simulation?

About verilog flip flop delay

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!
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.
You're using blocking assignments = to model synchronous logic. You need to use non-blocking assignments <=.
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:
Verilog's major flaw
VHDL's crown jewel

Resources