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.
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
endclassReview findings
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).
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.
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.
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).
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.
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
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
endclassKey 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.