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

AboutRoadmapCareersInternshipsFAQContactPrivacyTerms
Skip to lesson

Course contents

  • Start Here5
  • SystemVerilog Language Foundations28
  • Object-Oriented SystemVerilog for Verification30
  • Constraint Randomization — Interview-Critical Deep Dive39
  • SystemVerilog Assertions — Interview-Critical Deep Dive41
  • Functional Coverage in SystemVerilog25
  • Testbench Architecture in SystemVerilog24
  • Advanced Language & Integration26
  • Senior & Interview Preparation23
  • Principal-Level Verification34
    • Reusable Testbench Architecture
    • Checking Out-of-Order Traffic
    • Assertion Depth
    • Coverage Modelling for Real Features
    • Capstone: Out-of-Order Memory Controller
      • Spec and Plan
      • Executable Spec: One Bench per Rule
      • Environment
      • Environment Self-Test
      • Checking
      • Testing the Checkers
      • Coverage and Closure
      • Merging Runs and Reading Holes
      • Bug Hunt: Six Bugs and the Full Bench
      • Bug Hunt Results and Sign-Off
    • Principal Judgment
Loading lesson

←Capstone: Out-of-Order Memory Controller
  1. Topics
  2. SystemVerilog
  3. Principal-Level Verification
  4. Capstone: Out-of-Order Memory Controller
  5. Environment
On this page

    Part 9 of 9, lesson 22 of 34Part 9 of 9/Principal-Level Verification/Lesson 22 of 34Advanced

    Environment

    Policy layers and test phases, abstract BFMs that drive through clocking blocks, reset-aware bounded drivers, and a self-test that proves the plumbing before any checker is trusted.

    The structure

    The environment has four parts. Stimulus is a policy-layered item. Each port has an abstract BFM whose implementation lives inside the port's interface. One monitor samples all three ports at the rising edge and feeds the checkers and coverage. The test is a sequence of phases, each setting the policy knobs and the back-pressure.

    Diagram
    test phases A to D ─── knobs ──► cap_item (policy layers)    sink: ready_pct
                                            │ randomize()                  │
                                            ▼                              ▼
                          req_bfm[0], req_bfm[1] (abstract)          rsp_bfm (abstract)
                          bfm_impl inside mc_req_if #(PORT)    bfm_impl inside mc_rsp_if
                                            │ clocking drv @(negedge clk)  │
                                            ▼                              ▼
                                      ooo_memctl #(BUG) pins ◄─────────────┘
                                            │ sampled at posedge
                                            ▼
                          monitor ─► scoreboard + memory model, coverage, watchdog

    Stimulus: policy layers and phases

    The item uses the compile-time policy layers from Policy Classes: id_focus #(write_mix #(hot_window #(mc_item))). (That lesson is optional. All you need here: each layer is a class that extends the layer inside it and adds one constraint, so the item carries the address window, the write share and the ID focus together.) The runtime policy-list form is not used because Verilator 5.048 does not honour constraints reached through a back-reference. Each layer adds one rule with its own knob, so a phase changes knobs, not classes.

    Phase

    Window

    Writes

    ID focus

    Response ready

    Plan rows it targets

    A: random mix

    words 0-7

    40%

    off

    70%

    P1, P2 (cross-ID reorder, strobes, read-after-write)

    B: bursts under stall

    words 0-3

    30%

    70% on one ID per port

    25%

    P1 same-ID depth, P3 table full, P4 reset

    C: after reset

    words 0-15

    45%

    50%

    60%

    P4 ID reuse after reset, P5 contention

    D: directed empty table

    words 0-15

    0%

    50%

    60%

    P3 empty table: wait until nothing is outstanding, then one read, five times

    Phase B is where the risky states live. A narrow window forces reads right behind writes; focusing each port on one ID builds same-ID queues; a 25% ready rate fills the table. The mid-traffic reset is applied 200 cycles into phase B, when the table is busy.

    Abstract BFMs with clocking blocks

    The env holds req_bfm and rsp_bfm handles, the abstract-BFM pattern from Abstract BFM. The implementations sit inside the interfaces, so the env never names a virtual interface. Two details of this capstone are driven by tool behaviour we measured:

    • Drive through a clocking block. In this bench on Verilator 5.048, when the class inside each interface wrote the request pins with plain nonblocking assignments and the interface had no clocking block, the controller's combinational ready, which depends on both ports' valid, was not re-evaluated. Both ports were accepted in the same cycle. That is a Verilator scheduling bug, not a language rule: nonblocking writes from a class to interface variables are legal and must wake the dependent always_comb. A 40-line reduction did not reproduce it, so the trigger is narrower than this description. Declaring clocking drv @(negedge clk) in the interface and driving drv.valid <= ... removed the problem. A clocking block is the right style anyway: it states the drive edge in one place.

    • Give each port interface a different parameter. With two instances of the same interface specialization, the class inside the second instance drove the first instance's pins in a Verilator 5.048 test. That is also a tool bug: each interface instance has its own items. The PORT parameter makes mc_req_if #(0) and mc_req_if #(1) different specializations, which avoids that and also lets each BFM report its port number.

    Neither issue appeared in the Abstract BFM lesson, because its memory is sequential and its two interfaces already had different widths. They show up when the design has combinational ready logic, which most real handshakes do.

    Drivers that cannot hang the test

    Each driver call waits for ready at the rising edge, and returns early in two cases: reset (the request is dropped by the spec, so the phase stops) and 400 cycles without acceptance (a driver failure is reported by name). A driver that waits forever turns a DUT hang into a simulator timeout with no message, which is the failure mode the drain lesson warns about.

    The monitor, not the driver, decides what was accepted. Expected reads enter the scoreboard when the monitor sees the handshake, so a request dropped by reset never becomes an expectation.

    In UVM terms, req_bfm is what the driver and monitor call instead of holding a virtual interface, the phases are sequences, and the knobs are sequence fields or config objects.

    Check yourself Why does the phase B reset come from a fixed cycle count rather than waiting for a condition such as "six reads outstanding"?

    A wait on a condition can wait forever if a bug or a short test never reaches it, and then the test hangs. A fixed time always fires; coverage row CV8 then records whether the table was busy at that moment, so a weak reset shows up as a hole instead of a hang.

    Check yourself The env never holds a virtual interface. Where does the bus width or port number come from when it needs them?

    From the abstract API, implemented inside each interface where the parameters are in scope. req_bfm::port_num() returns PORT; a width() method would work the same way.

    Related topics

    • Previous: Executable Spec

    • Next: Environment Self-Test

    • Abstract BFM

    • Driving & Sampling via Clocking Blocks

    Previous

    263 / 275

    Next: Environment Self-Test

    Up next

    Up nextEnvironment Self-Test

    Sign in to save progress

    Practice this lessonPrevious lessonReport an error

    See also

    • Spec and Plan
    • Checking
    • Testing the Checkers