Part 5 · Sequences · Intermediate

Layered & Derived Items: Constraint Inheritance

Extend base items for write-only variants, add fields, tighten constraints without rewriting the base class.

Why derive items

A single monolithic item class with every field and constraint for every scenario becomes unmaintainable. Derived items add fields and tighten constraints for specific transaction types — write-only beats, burst descriptors, encrypted payloads — while inheriting base protocol rules from the parent class.

Layering items mirrors layering sequences: the base item defines the protocol's common address/data/write fields; derived items add strb, burst attributes, or security flags. Sequences typed to a derived item get stronger guarantees from randomize() without duplicate constraint code.

Factory overrides work on derived types independently — tests can swap axi_wr_item for axi_stress_wr_item without touching the base axi_item or the driver, as long as the driver accepts the base type.

diagram
[ITEM] derivation stack

  axi_item          addr, data, write, burst_len — base protocol
    │
    ├── axi_wr_item     write==1, strb — write-only variant
    ├── axi_rd_item     write==0, rdata focus — read variant
    └── axi_burst_item  data[], len — multi-beat descriptor

Base item — shared protocol fields

systemverilog
class axi_item extends uvm_sequence_item;
  rand bit [31:0] addr, data;
  rand bit        write;
  rand bit [2:0]  burst_len;

  constraint legal_addr { addr[1:0] == 2'b00; }

  `uvm_object_utils_begin(axi_item)
    `uvm_field_int(addr,      UVM_ALL_ON)
    `uvm_field_int(data,      UVM_ALL_ON)
    `uvm_field_int(write,     UVM_ALL_ON)
    `uvm_field_int(burst_len, UVM_ALL_ON)
  `uvm_object_utils_end

  function new(string name = "axi_item");
    super.new(name);
  endfunction
endclass

Note: derived classes need their own uvm_object_utils_begin/end — field macros do not inherit automatically. Re-register base fields in derived macros or use uvm_field_utils if your flow supports it.


Derived write item — tightened constraints

axi_wr_item adds strobe and forces write==1. Sequences typed to axi_wr_item cannot accidentally randomize a read — the constraint solver enforces transaction kind at the type level.

systemverilog
class axi_wr_item extends axi_item;
  rand bit [3:0] strb;

  constraint wr_only   { write == 1; }
  constraint full_strb { strb == 4'hF; }

  `uvm_object_utils_begin(axi_wr_item)
    `uvm_field_int(addr,      UVM_ALL_ON)
    `uvm_field_int(data,      UVM_ALL_ON)
    `uvm_field_int(write,     UVM_ALL_ON)
    `uvm_field_int(burst_len, UVM_ALL_ON)
    `uvm_field_int(strb,      UVM_ALL_ON)
  `uvm_object_utils_end

  function new(string name = "axi_wr_item");
    super.new(name);
  endfunction
endclass
diagram
[ITEM] constraint layering on derive

  BASE axi_item:     legal_addr { addr[1:0]==0; }     ← inherited
  DERIVED axi_wr_item: wr_only { write==1; }           ← added
                       full_strb { strb==4'hF; }       ← added

  randomize() on axi_wr_item: BOTH base and derived constraints apply
  Conflict if with-block says write==0 on axi_wr_item  FAIL
  • Derived constraints add to base — solver sees full constraint set.

  • Type-specific sequences: uvm_sequence #(axi_wr_item) cannot randomize reads.

  • Driver still uvm_driver #(axi_item) — polymorphism via base type handle.


APB read/write item split

APB agents often split read and write items for clarity. Each derived class removes randomization freedom that the sequence should not have:

systemverilog
class apb_wr_item extends apb_item;
  constraint c_wr { write == 1; }
  `uvm_object_utils(apb_wr_item)
endclass

class apb_rd_item extends apb_item;
  constraint c_rd { write == 0; }
  `uvm_object_utils(apb_rd_item)
endclass

class apb_rd_seq extends uvm_sequence #(apb_rd_item);
  task body();
    apb_rd_item req;
    repeat (5) begin
      req = apb_rd_item::type_id::create("req");
      start_item(req);
      assert(req.randomize() with { addr == 32'h4000; });
      finish_item(req);
      `uvm_info("RD", $sformatf("rdata=0x%08x", req.rdata), UVM_MEDIUM)
    end
  endtask
endclass
diagram
[SEQ] typed sequence + [ITEM] derived item

  apb_rd_seq  apb_rd_item  write==0 guaranteed
  apb_wr_seq  apb_wr_item  write==1 guaranteed

  [DRV] apb_driver #(apb_item) accepts both — reads req.write to branch

Constraint override with factory

Replace constraint blocks in derived items via factory without editing VIP — useful for errata workarounds or project-specific address maps:

systemverilog
class apb_item_wide_map extends apb_item;
  constraint addr_map {
    addr inside {[32'h0000:32'hFFFF_FFFF]};  // full 32-bit map
  }
  `uvm_object_utils(apb_item_wide_map)
endclass

// In test build_phase:
set_type_override_by_type(apb_item::get_type(),
                          apb_item_wide_map::get_type());

Layering guidelines

  • Base item = protocol-common fields + legality constraints.

  • Derived item = transaction-kind constraints (read vs write) + extra fields.

  • Do not deep-chain more than 2–3 levels — flat hierarchy is easier to debug.

  • Re-register all fields in derived uvm_object_utils for print/compare/copy.


post_randomize for computed fields

When a field depends on other randomized fields, compute it in post_randomize() rather than constraining it — checksums, derived lengths, encoded sizes:

systemverilog
class pkt_item extends uvm_sequence_item;
  rand bit [7:0]  payload[];
  rand bit [15:0] pkt_len;
  bit [15:0]      checksum;

  function void post_randomize();
    checksum = calc_checksum(payload);
  endfunction
endclass

Key takeaways

  • Derive items to tighten constraints per transaction type — not copy-paste base classes.

  • Base + derived constraints compose — solver sees the full set.

  • Type sequences to derived items for compile-time transaction kind safety.

Common pitfalls

  • Forgetting field macros in derived class — compare skips new fields.

  • Conflicting derived constraint vs with-block — randomize returns 0, unchecked.

  • Deep inheritance tower — hard to predict which constraint block failed.

  • Derived item with different driver type — keep driver at base #(apb_item).