Part 8 · Senior & Interview Prep · Intermediate

Reviewing Transactions & Constraints

Over-constrained base classes, legality vs typicality separation, unchecked randomize, copy/compare completeness — with a worked review of a flawed transaction class.

The transaction checklist

Transactions and their constraints define the entire reachable stimulus space — a bug here silently shrinks what the whole environment can ever test. The review question is never 'does this randomize?' but 'what legal stimulus can this class never produce?'

  • Over-constrained base class: hard constraints in the base that encode typical traffic (aligned addresses, small bursts) make corner cases unreachable by ANY derived class — soft or test-layer constraints belong there instead. Ask: can a derived class reach every spec-legal value?

  • Legality vs typicality separation: one constraint block for what the protocol forbids (keep, hard), separate named blocks for what is merely common (disable-able via constraint_mode). Mixed blocks force tests to rewrite legality when they only wanted weird traffic.

  • randomize() return checked at every call site — a failed randomize keeps stale values and the test runs on garbage.

  • Copy/compare/convert2string completeness: every new field must appear in all three; a field missing from compare() makes mismatches in that field invisible forever.

  • rand on the right fields: response/status fields should NOT be rand on request objects; conversely a field that tests need to vary but is not rand forces hacks.

  • Naming and units: addr_bytes vs addr_words class bugs start here; names must carry units and direction.


Worked review: a flawed transaction class

Review the following class as submitted. Find the problems before reading the verdict — there are at least five, ranging from blocking to note-level.

systemverilog
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

Review findings

  1. BLOCK — c_addr mixes legality and typicality in one anonymous block: alignment and the 64KB limit may be legal-traffic-only assumptions; len <= 8 is pure typicality. No test can relax 'big bursts are slow' without losing alignment legality. Split into c_legal (hard protocol rules only) and c_typical (named, disable-able).

  2. BLOCK — slverr is rand and force-constrained to 0 on the request object: response status does not belong to the requester, and !slverr in a hard constraint means error-response paths are unreachable from any derived class.

  3. BLOCK — do_copy drops data, slverr, and skips super.do_copy(rhs); copied transactions silently lose payload — scoreboards comparing copies will pass on corrupted data.

  4. BLOCK — do_compare ignores len and data entirely: a DUT that corrupts every data beat passes comparison. Compare must cover every field that defines behavior (with a comparer policy for intentionally excluded ones).

  5. NOTE — data[] is not rand and has no size constraint tied to len; generation likely happens in a hand-written loop somewhere — fragile, should be rand with constraint data.size() == len.

  6. NOTE — addr < 32'h0001_0000 hard-codes a memory map fact; belongs in a config-derived constraint or test layer, or it silently breaks when the map grows.

Corrected version

systemverilog
class bus_txn extends uvm_sequence_item;
  rand bit [31:0] addr;
  rand bit [7:0]  len;
  rand bit        write;
  rand bit [31:0] data[];
  bit             slverr;          // response: not rand, set by responder

  // Legality only — what the protocol forbids. Never disabled.
  constraint c_legal {
    addr[1:0] == 2'b00;            // bus requires word alignment (spec 3.2)
    len inside {[1:64]};           // spec max burst
    data.size() == len;
  }

  // Typicality — named, tests may constraint_mode(0) this.
  constraint c_typical {
    soft len <= 8;
    soft addr < 32'h0001_0000;
  }

  `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;
    super.do_copy(rhs);
    $cast(t, rhs);
    addr   = t.addr;  len  = t.len;  write = t.write;
    data   = t.data;  slverr = t.slverr;
  endfunction

  function bit do_compare(uvm_object rhs, uvm_comparer comparer);
    bus_txn t;
    $cast(t, rhs);
    return (addr == t.addr) && (write == t.write) &&
           (len  == t.len)  && (data  == t.data)  &&
           (slverr == t.slverr);
  endfunction
endclass

Key takeaways

  • Review constraints by asking what legal stimulus becomes unreachable — that is the silent damage.

  • Hard constraints encode legality only; typicality is soft or in named disable-able blocks.

  • copy/compare completeness is a checking-integrity issue, not style — a missed field hides mismatches forever.

  • Response fields are not rand on request objects; constraining them to 'no error' deletes error testing.

Common pitfalls

  • Approving a constraint block because it 'randomizes fine' — reachability, not solvability, is the question.

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

  • Missing the skipped super.do_copy() — base-class fields silently stop copying.

  • Treating compare gaps as a nit — they are the highest-severity finding in a transaction review.