Part 9 · Register Model (RAL) · Intermediate

Integration Reference Environment: Full reg_env Walkthrough

Complete reference reg_env code with build, connect, reset sequencing hooks, and detailed integration walkthrough.

Complete reference reg_env source

This reference environment shows a production-grade baseline: one shared model, explicit predictor, backdoor-ready model setup, and deterministic diagnostics.

systemverilog
class reg_env extends uvm_env;
  `uvm_component_utils(reg_env)

  // ------------------------------------------------------------
  // Infrastructure
  // ------------------------------------------------------------
  apb_agent                     apb;
  apb_reg_adapter               adapter;
  uvm_reg_predictor #(apb_item) predictor;
  my_reg_block                  reg_model;

  // optional helper components
  reg_window_filter             reg_filter;
  bit                           use_addr_filter = 0;
  uvm_reg_addr_t                reg_base = 'h4000_0000;
  uvm_reg_addr_t                reg_last = 'h4000_0FFF;

  function new(string name = "reg_env", uvm_component parent = null);
    super.new(name, parent);
  endfunction

  // ------------------------------------------------------------
  // build_phase: create everything structural
  // ------------------------------------------------------------
  function void build_phase(uvm_phase phase);
    super.build_phase(phase);

    // Pull optional knobs first
    void'(uvm_config_db#(bit)::get(this, "", "use_addr_filter", use_addr_filter));
    void'(uvm_config_db#(uvm_reg_addr_t)::get(this, "", "reg_base", reg_base));
    void'(uvm_config_db#(uvm_reg_addr_t)::get(this, "", "reg_last", reg_last));

    // Agent + adapter + predictor
    apb       = apb_agent::type_id::create("apb", this);
    adapter   = apb_reg_adapter::type_id::create("adapter");
    predictor = uvm_reg_predictor#(apb_item)::type_id::create("predictor", this);

    adapter.base_addr = reg_base;

    if (use_addr_filter) begin
      reg_filter = reg_window_filter::type_id::create("reg_filter", this);
      reg_filter.base  = reg_base;
      reg_filter.limit = reg_last;
    end

    // Try to get shared model first
    if (!uvm_config_db#(my_reg_block)::get(this, "", "reg_model", reg_model)) begin
      `uvm_info("RAL_CFG", "No external reg_model found; creating local model", UVM_LOW)
      reg_model = my_reg_block::type_id::create("reg_model");
      reg_model.build();
      reg_model.lock_model();
    end

    // Ensure lock even for externally provided model
    if (!reg_model.is_locked())
      reg_model.lock_model();

    // Optional: if your generated model exposes explicit HDL root setup
    // reg_model.set_hdl_root("tb_top.dut");

    // Publish same handle for children and sequences
    uvm_config_db#(my_reg_block)::set(this, "*", "reg_model", reg_model);
    uvm_config_db#(my_reg_block)::set(null, {get_full_name(), ".*"}, "reg_model", reg_model);

    `uvm_info("RAL_CFG", $sformatf("reg_model handle=%0p", reg_model), UVM_LOW)
  endfunction

  // ------------------------------------------------------------
  // connect_phase: bind transport and prediction
  // ------------------------------------------------------------
  function void connect_phase(uvm_phase phase);
    super.connect_phase(phase);

    // Frontdoor route
    reg_model.default_map.set_sequencer(apb.sqr, adapter);
    reg_model.default_map.set_auto_predict(0); // explicit predictor only

    // Predictor core binding
    predictor.map     = reg_model.default_map;
    predictor.adapter = adapter;

    // Monitor feed: direct or filtered
    if (use_addr_filter) begin
      apb.mon.ap.connect(reg_filter.analysis_export);
      reg_filter.out_ap.connect(predictor.bus_in);
    end
    else begin
      apb.mon.ap.connect(predictor.bus_in);
    end

    `uvm_info("RAL_CFG", "set_sequencer + predictor wiring complete", UVM_LOW)
  endfunction

  // ------------------------------------------------------------
  // start_of_simulation diagnostics
  // ------------------------------------------------------------
  function void start_of_simulation_phase(uvm_phase phase);
    super.start_of_simulation_phase(phase);
    `uvm_info("RAL_CFG",
      $sformatf("auto_predict=%0d base=0x%0h filter=%0d",
                reg_model.default_map.get_auto_predict(),
                adapter.base_addr,
                use_addr_filter),
      UVM_LOW)
  endfunction

  // ------------------------------------------------------------
  // Utility: quick sanity check callable from tests
  // ------------------------------------------------------------
  task ral_sanity_check(uvm_object parent = null);
    uvm_status_e   status;
    uvm_reg_data_t rdata;
    uvm_reg        rg;

    // Pick one known register from model
    rg = reg_model.ctrl;
    if (rg == null) begin
      `uvm_error("RAL_SANITY", "reg_model.ctrl is null")
      return;
    end

    rg.write(status, 32'h1, UVM_FRONTDOOR, .parent(parent));
    if (status != UVM_IS_OK) begin
      `uvm_error("RAL_SANITY", "frontdoor write failed")
      return;
    end

    rg.read(status, rdata, UVM_FRONTDOOR, .parent(parent));
    if (status != UVM_IS_OK) begin
      `uvm_error("RAL_SANITY", "frontdoor read failed")
      return;
    end

    rg.mirror(status, UVM_CHECK, UVM_FRONTDOOR, .parent(parent));
    if (status != UVM_IS_OK)
      `uvm_error("RAL_SANITY", "mirror check failed")
  endtask

  // ------------------------------------------------------------
  // Utility: show predictor callback chain quickly
  // ------------------------------------------------------------
  function void report_phase(uvm_phase phase);
    super.report_phase(phase);
    `uvm_info("RAL_CFG", "reg_env report complete", UVM_LOW)
  endfunction

endclass

Walkthrough by region

Infrastructure and knobs

  1. Declare one adapter and one predictor for the map to keep semantics aligned.

  2. Expose optional base address and address-window filter knobs through config_db.

  3. Use explicit handles for agent, map, and model to make debug prints meaningful.

build_phase responsibilities

  1. Read knobs before object creation so components initialize with final config.

  2. Create agent/adapter/predictor deterministically.

  3. Get shared model or create local fallback with explicit info log.

  4. Always ensure lock_model before any frontdoor run.

  5. Publish model handle to children and sequences via config_db.

connect_phase responsibilities

  1. Bind map transport using set_sequencer with the same adapter used for predictor.

  2. Disable auto_predict when explicit prediction is expected.

  3. Bind predictor.map and predictor.adapter before analysis connections.

  4. Connect monitor directly or via filter based on address scope.

diagram
[UVM][RAL] region-to-risk map

Code region                     Primary risk if wrong
--------------------------------------------------------------
adapter.base_addr               writes to wrong register offsets
reg_model get/create            duplicate model instances
lock_model guard                late structural mutation errors
set_sequencer binding           frontdoor unable to route
predictor.map/adapter           mirror not updated correctly
monitor.ap connect              no explicit prediction path
  • Treat each region as a separate checkpoint during bring-up.

  • Log key handles and knob values at UVM_LOW for first-run visibility.

  • Keep set_sequencer and predictor setup physically close in code.


Minimal companion test using the reference env

systemverilog
class reg_env_smoke_test extends uvm_test;
  `uvm_component_utils(reg_env_smoke_test)
  reg_env env;

  function void build_phase(uvm_phase phase);
    super.build_phase(phase);
    env = reg_env::type_id::create("env", this);
  endfunction

  task run_phase(uvm_phase phase);
    phase.raise_objection(this);
    env.ral_sanity_check(this);
    phase.drop_objection(this);
  endtask
endclass

Expected logs in healthy run

diagram
[UVM][RAL] expected log sketch

UVM_INFO ... RAL_CFG ... reg_model handle=0x...
UVM_INFO ... RAL_CFG ... set_sequencer + predictor wiring complete
UVM_INFO ... RAL_CFG ... auto_predict=0 base=0x40000000 filter=0
UVM_INFO ... RAL_SANITY ... write/read/mirror completed
UVM_INFO ... report summary ...

Key takeaways

  • A full reference env should include knobs, diagnostics, and one sanity task.

  • Keep ownership clear: build creates/binds; connect wires flow; tests drive intent.

  • Co-locating code and walkthrough accelerates onboarding for new team members.

Common pitfalls

  • Using reference code but removing handle logs, then losing debug visibility.

  • Over-customizing before first sanity check passes.

  • Adding predictors per sub-agent without clear map ownership.