Part 3 · Factory & Configuration · Intermediate

Liskov Substitution Safety: Override Classes That Actually Fit

Structural contracts for override derivatives, port/parameter compatibility, behavioral specialization boundaries, and connect_phase safety.

Substitution contract

An override class must be a true subtype of the requested base: same parameters, same public ports, compatible behavior . Factory substitution does not relax connect-time or TLM contracts.

diagram
[FACTORY][UVM] Liskov checklist for override class B replacing A

inheritance:
  class B extends A  (required)

structure:
  same #(T) params on uvm_driver/uvm_agent generics
  no removed uvm_*_port / export / imp members

behavior:
  specialize run/body tasks, not connection topology
  honor base handshake expectations
systemverilog
class err_driver extends base_driver;
  `uvm_component_utils(err_driver)

  task run_phase(uvm_phase phase);
    forever begin
      seq_item_port.get_next_item(req);
      if (req.inject_err)
        drive_error(req);
      else
        drive_item(req);
      seq_item_port.item_done();
    end
  endtask
endclass

Key takeaways

  • Override classes extend base — never parallel unrelated classes.

  • Keep TLM topology stable so env connect_phase remains unchanged.

  • Specialize behavior, not structural role or interface surface.

Common pitfalls

  • Derivative drops a port used by env connect_phase.

  • Changing parameter types so analysis port write signatures mismatch.

  • Throwing new errors in derivative for valid base sequences.


Structural and behavioral review

Review override classes as API contracts, not just behavior tweaks.

Structural review matrix

  • Same sequence item type on driver/sequencer path.

  • Same analysis transaction type on monitor subscribers.

  • Config object handles still consumed via config_db as expected.

systemverilog
class verbose_monitor extends base_monitor;
  `uvm_component_utils(verbose_monitor)

  task run_phase(uvm_phase phase);
    forever begin
      @(posedge vif.clk);
      collect_packet();
      `uvm_info("PKT", pkt.sprint(), UVM_HIGH)
      ap.write(pkt);  // same analysis path as base
    end
  endtask
endclass

Behavioral boundaries

diagram
[FACTORY] allowed vs risky specialization

allowed:
  inject protocol errors under cfg control
  add non-invasive logging/metrics
  alter timing within protocol legal bounds

risky:
  reject/legalize different transaction classes
  change reset/phase assumptions of env
  consume transactions without item_done/handshake completion
systemverilog
function void end_of_elaboration_phase(uvm_phase phase);
  super.end_of_elaboration_phase(phase);
  assert (env.agt_tx.drv.seq_item_port != null);
  assert (env.agt_tx.mon.ap != null);
endfunction

Common pitfalls

  • Override class requiring new connect_phase wiring in env.

  • Changing randomization constraints that invalidate base sequences.

  • Using override to bypass protocol checks instead of extend them.