Skip to content
DenseChip
Learn
PracticeCode labInterview

DenseChip

Navigate

Menu

LearnPracticeCode labInterview

Tools

All toolsSV editorCode repoResume
DenseChip

Practice, courses, and chip-design tools in one focused workspace.

LearnPracticeCode labInterviewToolsLearning paths

© 2026 DenseChip

AboutContactPrivacyTerms
Skip to lesson

Course contents

  • Start Here6
  • SystemVerilog Language Foundations28
  • Object-Oriented SystemVerilog for Verification30
  • Constraint Randomization — Interview-Critical Deep Dive36
  • SystemVerilog Assertions — Interview-Critical Deep Dive40
  • Functional Coverage in SystemVerilog22
  • Testbench Architecture in SystemVerilog23
  • Advanced Language & Integration25
    • DPI-C: Calling C from SystemVerilog
    • bind: Attaching Verification to RTL
    • Macros, Defines & Compilation
    • Plusargs, Files & Runtime Configuration
    • Simulation Performance & TB Memory
    • RTL/TB Boundaries, Races & Timing
      • Anatomy of a TB/RTL Race
      • Clocking Blocks as the Cure
      • Scheduling Regions: the Full Map
      • Hierarchical References & Force/Release
  • Senior & Interview Preparation21
  • Principal-Level Verification28
Loading lesson

‹RTL/TB Boundaries, Races & Timing
  1. Topics
  2. SystemVerilog
  3. Advanced Language & Integration
  4. RTL/TB Boundaries, Races & Timing
  5. Anatomy of a TB/RTL Race

Part 7 · Advanced & Integration · Intermediate

Anatomy of a TB/RTL Race

A same-edge drive and sample race walked cycle by cycle, with the off-by-one checklist.

On this page

    Two processes, one edge

    A race needs two processes in the same scheduling region touching one signal. The usual pair is a testbench drive or sample with blocking semantics and an RTL block triggered by the same clock edge.

    The LRM leaves same-region process order to the simulator. That unspecified order is the whole race.

    pipe8.sv
    1
    2
    3
    4
    5
    6
    7
    8
    // RTL: a registered pipeline stage
    module pipe8 (
      input logic clk,
      input logic [7:0] data_in,
      output logic [7:0] data_q
    );
      always_ff @(posedge clk) data_q <= data_in;
    endmodule

    The RTL above is internally race-free. All of its reads happen before any of its updates.

    Side

    Blocking semantic

    Nonblocking semantic

    Testbench drive at the edge

    Updates the signal mid-region; RTL may read old or new

    Lands after RTL reads; RTL always sees the previous value

    Testbench sample at the edge

    May run before or after the drive

    N/A: sampling discipline comes from clocking blocks

    RTL update at the edge

    N/A: RTL uses nonblocking updates

    Reads settle first, left-hand sides update in the NBA region

    Order A versus Order B

    Diagram
    SAME-EDGE DRIVE/SAMPLE RACE - time step at posedge clk
    
      ACTIVE REGION (process order is SIMULATOR'S CHOICE)
      +--------------------------------------------------------+
      |  runnable: { always_ff (RTL), driver, monitor }        |
      |                                                        |
      |  ORDER A (driver first):                               |
      |    1. driver:    data_in = 8'hA5    (blocking, now)    |
      |    2. always_ff: reads data_in = A5, schedules NBA     |
      |    3. monitor:   captured = A5                         |
      |                                                        |
      |  ORDER B (RTL first):                                  |
      |    1. always_ff: reads data_in = OLD, schedules NBA    |
      |    2. driver:    data_in = 8'hA5                       |
      |    3. monitor:   captured = A5 (or OLD if the monitor  |
      |                                  ran before the driver)|
      +--------------------------------------------------------+
                              |
                              v
      NBA REGION
      +--------------------------------------------------------+
      |  data_q <= (whatever always_ff READ above)             |
      |  ORDER A: data_q becomes A5 - DUT saw the new value   |
      |  ORDER B: data_q becomes OLD - DUT saw the old value  |
      +--------------------------------------------------------+
    
      Same source code. Two legal outcomes. THAT is the race.

    Both orders are legal per the LRM. Declaration order, flags, or tool version can flip the choice without touching the source.

    The bottom row, data_q, holds the previous slot's data_in. This is the NBA-driven behavior both orders must converge to once the race is removed.

    • Slot 0: data_in is a5, so data_q still holds 00 (the pre-edge value).

    • Slot 1: data_in is 3c, so data_q takes a5.

    • Slot 2: data_in holds 3c, so data_q takes 3c.

    • Slot 3: data_in is f0, so data_q still holds 3c.

    • Slot 4: data_in is 07, so data_q takes f0.

    • Slot 5: data_in holds 07, so data_q takes 07.

    The off-by-one checklist

    A race announces itself with a fixed signature. A value is off by exactly one cycle, intermittently or per tool, and behavior moves with the simulator, its flags, or unrelated edits.

    • A testbench blocking assignment or immediate read sits on the same edge the RTL flops use.

    • A #0 or #1 patch sits in the history near the driver.

    • The failure follows the tool or the optimization level, never the stimulus.

    Common pitfall

    Declaring victory because the test passes on one simulator mistakes ordering luck for correctness. A delay patch hides the race and leaves it for the next flag change to rediscover.

    Interview

    Likely question: “What is a testbench race and how do you prevent it?” Answer with the region model: two same-region processes, LRM-legal orders, different results. Prevention is region separation, covered in the next lesson.

    Check yourself Two processes run at the same edge and the result differs per tool. What is the race, precisely?

    Two processes in the same region of the same time step touch one signal, and the LRM leaves their order to the simulator. Each legal order gives a different result.

    Check yourself Why does a passing test not prove the boundary is clean?

    A pass proves the simulator picked the friendly order today. Any unrelated edit or flag can flip the order tomorrow.

    Testbench

    Two flops stage the two LRM-legal outcomes deterministically. An early blocking drive settles before the edge and is captured, while a drive landing just after the edge is missed until the next one. A drive sitting exactly on the edge is a coin flip between these two outcomes, and that flip is the race.

    flop8.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    timeunit 1ns; timeprecision 1ps;
    
    module flop8 (
      input logic clk,
      input logic [7:0] d,
      output logic [7:0] q
    );
      always_ff @(posedge clk) q <= d;
    endmodule
    

    Related topics

    • Testbench synchronization

    Previous

    207 / 259

    Next: Clocking Blocks as the Cure

    Up next

    Up nextClocking Blocks as the Cure

    Sign in to save progress

    Practice this lessonReport an error

    See also

    • Scheduling Regions: the Full Map
    • Hierarchical References & Force/Release
    clkdata_ina53cf07data_q0a53cf07
    A disciplined drive: data_q trails data_in by one cycle