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
  • Senior & Interview Preparation21
    • Senior Debug Playbook
    • Reviewing Verification Code
      • Reviewing Transactions & Constraints
      • Reviewing TB Components
      • Reviewing Assertions & Coverage
      • Review Process & Checklist
    • Verification Strategy: Spec to Sign-Off
    • SystemVerilog Interview Q&A Bank
    • Capstone: Verify a Parameterized FIFO
  • Principal-Level Verification28
Loading lesson

‹Reviewing Verification Code
  1. Topics
  2. SystemVerilog
  3. Senior & Interview Preparation
  4. Reviewing Verification Code
  5. Reviewing Transactions & Constraints

Part 8 · Senior & Interview Prep · Intermediate

Reviewing Transactions & Constraints

Review constraints for reachability: legality versus typicality, checked randomize, full copy and compare.

On this page

    Legality versus typicality

    Constraints define the reachable stimulus space. A bug here silently shrinks what the whole environment can ever test.

    The review question is never whether the class randomizes. It is which legal stimulus the class can never produce.

    Kind

    Content

    Form

    Legality

    What the protocol forbids

    Hard constraints, never disabled

    Typicality

    What traffic is merely common

    Soft or named blocks, tests may relax them

    A base class that hard-codes typical traffic makes corner cases unreachable for tests, unless a subclass overrides the whole named block and restates the legal rules. Typicality belongs in soft or test-layer constraints.

    Worked review: an over-constrained class

    The listing below is not run in this course's checks: it needs the UVM library, which neither course tool (Icarus 13, Verilator 5.048) runs here.

    design.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    // not-run: needs the UVM library (uvm_sequence_item, macros); neither Icarus 13 nor Verilator 5.048 here runs uvm_pkg
    class bus_txn extends uvm_sequence_item;
      rand bit [31:0] addr;
      rand bit [7:0]  len;
      rand bit        write;
      rand bit        slverr;          // response status
      bit [31:0]      data[];
    
      constraint c_addr {
        addr[1:0] == 2'b00;            // keep it aligned
        addr < 32'h0001_0000;          // stay in the test RAM
        len  <= 8;                     // big bursts are slow
        !slverr;
      }
    
      `uvm_object_utils(bus_txn)
      function new(string name = "bus_txn");
        super.new(name);
      endfunction
    
      function void do_copy(uvm_object rhs);
        bus_txn t;
        $cast(t, rhs);
        addr  = t.addr;
        len   = t.len;
        write = t.write;
      endfunction
    
      function bit do_compare(uvm_object rhs, uvm_comparer comparer);
        bus_txn t;
        $cast(t, rhs);
        return (addr == t.addr) && (write == t.write);
      endfunction
    endclass
    1. BLOCK — legality and typicality share one block. Alignment and the address cap may encode real rules, but len <= 8 is pure typicality. No test can relax burst length without losing the rest. Split legality from typicality into named blocks.

    2. BLOCK — slverr is rand on a request object. Response status belongs to the responder. Constraining it to zero makes error-response paths unreachable, unless a subclass overrides the whole block and restates the legal rules.

    3. BLOCK — do_copy drops fields and skips super.do_copy. Payload, status, and base-class fields stop copying. Scoreboards that compare copies then pass on corrupted data.

    4. BLOCK — do_compare ignores len and data. A fault in any data beat passes comparison. Every behavioral field belongs in the compare, with a comparer policy for intentional exclusions.

    5. NOTE — task-form $cast in both functions. If rhs is the wrong type, the task form raises a run-time error at the cast (IEEE 1800 6.24.2), so it is not silent, but the message names nothing. The function form gives a named failure: if (!$cast(t, rhs)) $fatal(1, "bad cast");.

    6. NOTE — data has no size tied to len. Generation must happen in a hand-written loop elsewhere. Prefer rand data with data.size() == len.

    7. NOTE — the address cap hard-codes the map. A literal memory-map fact rots when the map grows. Such facts belong in config-derived constraints.

    Common pitfall

    Approving a constraint block because it randomizes without complaint. Solvability is not the question; reachability is. Ask which legal value can never come out.

    Corrected class and testbench

    The corrected shape below is plain SystemVerilog without UVM. It runs under Verilator, which supports classes. The audit counts every length value instead of sampling the solver, so the reachability verdict is exact rather than statistical.

    The testbench proves two things. Sweeping all 256 length values shows the typical cap hides 56 of the 64 legal burst lengths. Copy and compare track every field, including status.

    design.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    class pkt;
      rand bit [31:0] addr;
      rand bit [7:0]  len;
      rand bit        write;
      bit             slverr;   // response: not rand, set by the responder
    
      // Legality only: what the protocol forbids. Never disabled.
      constraint c_legal {
        addr[1:0] == 2'b00;    // bus needs word alignment
        len inside {[1:64]};   // spec maximum burst
      }
    
      // Typicality: named, so tests may relax it with constraint_mode(0).
      constraint c_typical {
        len <= 8;
      }
    
      // Solver-free mirrors of the two blocks, for the audit loop below.
      // Each reads exactly like its constraint, so the counts transfer.
      function bit legal_ok(bit [7:0] l);
        return (l >= 8'd1 && l <= 8'd64);
      endfunction
      function bit typical_ok(bit [7:0] l);
        return (l <= 8'd8);
      endfunction
    
      function void copy_from(pkt src);
        addr   = src.addr;
        len    = src.len;
        write  = src.write;
        slverr = src.slverr;
      endfunction
    
      function bit same_as(pkt other);
        if (addr   != other.addr)   return 1'b0;
        if (len    != other.len)    return 1'b0;
        if (write  != other.write)  return 1'b0;
        if (slverr != other.slverr)  return 1'b0;
        return 1'b1;
      endfunction
    endclass
    

    Every real randomize call site checks the return value. The idiom is one line, shown partial here:

    design.sv
    1
    2
    3
    4
    // snippet
    if (!req.randomize()) $fatal(1, "rand failed");
    // A failed randomize keeps stale values, so an unchecked call lets the
    // test run on garbage and stay green. The return value is the check.

    In UVM the same compare should route through the comparer argument. That keeps the exclusion policy for intentionally skipped fields in one place.

    Common mistakes

    • Letting keep it aligned style comments substitute for citing the spec rule being encoded.

    • Missing a skipped super.do_copy: base-class fields silently stop copying.

    • Treating a compare gap as a nit. It is the highest-severity finding in a transaction review.

    • Leaving randomize() unchecked: a failed call keeps stale values and the test runs on them silently.

    Interview

    Likely question: “a directed test for maximum bursts never fires — where do you look”. The answer starts with the base-class constraints and constraint_mode, not with the test.

    Check yourself A test disables the typical block and still cannot produce a 64-beat burst. What do you check next?

    Whether a second hard constraint still caps length, and whether the randomize call carries an inline with block that re-adds the cap. Then whether the call site checks the return value at all.

    Check yourself A new response field is added to a transaction but to neither do_copy nor do_compare. What escapes?

    Copies silently lose the field, and compares pass when the field disagrees. Both failures are silent by construction.

    Related topics

    • Randomization debug

    • Constraint quickfire Q&A

    Previous

    215 / 259

    Next: Reviewing TB Components

    Up next

    Up nextReviewing TB Components

    Sign in to save progress

    Practice this lessonReport an error

    See also

    • Reviewing Assertions & Coverage
    • Review Process & Checklist