Regarding Race Condition

Hi ,

I want to know why and how race condition will Happen in systemverilog . What are solutions … for those ?

In reply to hemaja:
A race condition occurs when two or more threads are using the same resources. There are many examples of race conditions. A few are listed below.

  1. The same signal is driven and sampled at the same time.
    => To avoid this race condition, a clocking block in interface is used as it provides an input and output skews to sample and drive, respectively.
  2. Race condition between testbench and design.
    => The program block is used to avoid this race, as module executes on an active region, while program block executes in reactive region.
  3. Race condition in event.
    => This race condition occurs when one thread is waiting for triggering an event using @(event) syntax and at the same time event is triggered from another thread. This race condition could be avoided by using wait statement and triggered method like wait(event.triggered);

Please refer clocking block, program block, SV regions and events for more details.

In reply to bdreku:

Program blocks do not elimnate all race conditions and I do not recommend using them.

In reply to hemaja:

A race condition in SystemVerilog is an simulation modeling artifact where the order of execution between two different constructs cannot be guaranteed. The most common race condition is when there are multiple processes reading and writing the same variable synchronized to the same event region (like the edge of a clock). SystemVerilog does not guarantee the order of execution between the reading and writing, so there is no way to know if the “read” captures the old or the new value of that variable. (bdreku, this can happen just as easily within a program block or module)

The solution to this problem is to move either the reading or writing of that variable to a different event region. The most common way to do this is to use a non-blocking assignment (q <= d)for the writing of that variable. That postpones actual update of that variable to a later region. Now all the other process that try to read that variable in the current event region are guaranteed to the “old” value of that variable regardless of the writing process executes before or after the reading process. This technique is the way Verilog RTL designers have been coding for years, and works equally as well for testbench verification.

Testbench writers have a few other options, including using a different event for writing and reading (like the inverse edge of the clock), and clocking blocks to offset the reading and writing from a synchronous clock edge.

Real hardware does not have the same kind of problem with race conditions. Propagation delays through registers usually prevent simultaneous reading and writing, but physical issues like clock skew and setup/hold requirements can produce timing problems where it is difficult to predict reading the old or new value of a signal.

In reply to dave_59:
Thank you Dave.

I have read your article “Are Program Blocks Necessary?”. It’s really a useful article and it has cleared my doubts except one, it’s a point number 3 in your article as I have quoted below.

Unless you’re an experienced Vera user, there is the unexpected surprise that your simulation exits immediately after the thread in your program block ends. Again this is an issue with mixing legacy testbenches, or mixed-language testbenches.

I’ve never used Vera, but I am seeing the termination of simulation as a feature of program block because it allows to terminate your testcase in the case of testcase hang.

In reply to dave_59:
In the case of a SV testbench (without using any methodology), the termination feature is useful.

Let’s say, in some scenario, the testcase should be completed (done with configuration, link establishment, stimulus generation and transmission, capturing response, etc.) within 25ms. But if the testbench hangs in any step like capturing response, then the initial block with 25ms delay could help to terminate the testcase. initial #25ms.

Yes, $finish is an alternative in the case of program block is not used.

In reply to bdreku:
If the testcase is encapsulated in a program block, then a testbench hang will hang the program block

In reply to dave_59:
Yes, Dave. I have got your point.
Thank you :-)

In reply to bdreku:

Hi,

How 2nd point is different than the 1st one ?

  1. The same signal is driven and sampled at the same time.
    => To avoid this race condition, a clocking block in interface is used as it provides an input and output skews to sample and drive, respectively.
  2. Race condition between testbench and design.
    => The program block is used to avoid this race, as module executes on an active region, while program block executes in reactive region.

Regards,
Saket

How do we avoid race condition between driver and monitor ?

In reply to dave_59:

In reply to bdreku:
Program blocks do not elimnate all race conditions and I do not recommend using them.
In reply to hemaja:
A race condition in SystemVerilog is a simulation modeling artifact where the order of execution between two different constructs cannot be guaranteed. The most common race condition is when there are multiple processes reading and writing the same variable synchronized to the same event region (like the edge of a clock). SystemVerilog does not guarantee the order of execution between the reading and writing, so there is no way to know if the “read” captures the old or the new value of that variable. (bdreku, this can happen just as easily within a program block or module)
The solution to this problem is to move either the reading or writing of that variable to a different event region. The most common way to do this is to use a non-blocking assignment (q <= d)for the writing of that variable. That postpones actual update of that variable to a later region. Now all the other process that try to read that variable in the current event region are guaranteed to the “old” value of that variable regardless of the writing process executes before or after the reading process. This technique is the way Verilog RTL designers have been coding for years, and works equally as well for testbench verification.
Testbench writers have a few other options, including using a different event for writing and reading (like the inverse edge of the clock), and clocking blocks to offset the reading and writing from a synchronous clock edge.
Real hardware does not have the same kind of problem with race conditions. Propagation delays through registers usually prevent simultaneous reading and writing, but physical issues like clock skew and setup/hold requirements can produce timing problems where it is difficult to predict reading the old or new value of a signal.

Hi Dave,
Can you give more detail on the later region in which update is done when doing non-blocking write ?

In reply to NC22:

module DUT(input bit clk, int A,
           output int B);
  always @(posedge clk) begin
    B <= A + 1;

  end
endmodule

module testbench;
  int a,b;
  bit clk;
  DUT dut(.clk,.A(a),.B(b));
  initial #5 repeat(100) #5 clk = ! clk; // posedge clk at 10,20,30,...
  
  initial begin // avoiding race
    @(posedge clk)
    a <= 1; // using NBA
    @(posedge clk)
    a <= 3; // using NBA
    @(negedge clk) //using negedge
    a = 5;         // NBA does not matter
    @(negedge clk) //using negedge
    a = 7;         // NBA does not matter
  end
endmodule