Part 9 · Register Model (RAL) · Intermediate

Environment Build Checklist: Numbered Integration Steps

Deterministic step-by-step order for building and validating a RAL-enabled environment.

Why strict ordering matters

Register integration fails when objects are created in an ad-hoc order. A deterministic checklist makes build failures reproducible and keeps bring-up time low.

diagram
[UVM][RAL] build order timeline

  1) create bus agent
  2) create adapter
  3) create predictor
  4) get or create reg_model
  5) reg_model.build()
  6) reg_model.lock_model()
  7) optional HDL path setup
  8) publish reg_model via config_db
  9) connect map/sequencer/adapter in connect_phase
 10) connect monitor to predictor
 11) run smoke register write + mirror check

Numbered integration steps

  1. Create protocol agent(s) first so sequencer handles exist before connect.

  2. Create one adapter instance and keep it for both set_sequencer and predictor.adapter.

  3. Create predictor in build_phase, assign map/adapter in connect_phase.

  4. Try config_db get for reg_model before creating a new model.

  5. If get fails, create model, call build(), then lock_model().

  6. Set HDL paths in model build when backdoor is required.

  7. Publish the same model handle with config_db::set for downstream components.

  8. In connect_phase, bind default_map to sequencer using set_sequencer().

  9. Disable auto_predict when explicit predictor flow is used.

  10. Connect monitor analysis port to predictor.bus_in.

  11. Run one known write/read/mirror smoke test immediately after bring-up.

  • If any step is skipped, later errors often appear far from the root cause.

  • Do not defer model build/lock to run_phase; keep structural setup in build_phase.

  • Keep checklist visible in comments or project docs for new engineers.


Reference build_phase skeleton

systemverilog
class reg_env extends uvm_env;
  `uvm_component_utils(reg_env)

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

  function void build_phase(uvm_phase phase);
    super.build_phase(phase);

    // 1-3: core infrastructure
    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);

    // 4-6: obtain or create one shared model
    if (!uvm_config_db#(my_reg_block)::get(this, "", "reg_model", reg_model)) begin
      reg_model = my_reg_block::type_id::create("reg_model");
      reg_model.build();
      reg_model.lock_model();
    end

    // 7: optional model-level setup
    // reg_model.add_hdl_path("tb_top.dut");

    // 8: publish for children
    uvm_config_db#(my_reg_block)::set(this, "*", "reg_model", reg_model);
  endfunction
endclass

Checklist assertions you can add

systemverilog
function void end_of_elaboration_phase(uvm_phase phase);
  super.end_of_elaboration_phase(phase);
  if (reg_model == null)
    `uvm_fatal("RAL_CFG", "reg_model is null after build_phase")

  if (!reg_model.is_locked())
    `uvm_fatal("RAL_CFG", "reg_model must be lock_model()'d before run")
endfunction
  • Use early fatal checks for null model and unlocked model.

  • Publishing reg_model in build_phase allows agents/sub-envs to consume it immediately.

  • Avoid lazy creation in connect_phase; structural ownership becomes unclear.


Bring-up smoke sequence

Run a minimal smoke immediately after environment creation to validate the checklist end-to-end.

systemverilog
task run_phase(uvm_phase phase);
  uvm_status_e   status;
  uvm_reg_data_t data;
  phase.raise_objection(this);

  // frontdoor write through sequencer+adapter
  env.reg_model.ctrl.write(status, 32'h0000_0001, UVM_FRONTDOOR, .parent(this));
  if (status != UVM_IS_OK)
    `uvm_fatal("RAL_SMOKE", "frontdoor write failed")

  // frontdoor read should return same value
  env.reg_model.ctrl.read(status, data, UVM_FRONTDOOR, .parent(this));
  if (status != UVM_IS_OK)
    `uvm_fatal("RAL_SMOKE", "frontdoor read failed")

  // mirror check proves predictor flow
  env.reg_model.ctrl.mirror(status, UVM_CHECK, UVM_FRONTDOOR, .parent(this));
  if (status != UVM_IS_OK)
    `uvm_error("RAL_SMOKE", "mirror check mismatch in smoke")

  phase.drop_objection(this);
endtask

Key takeaways

  • A numbered checklist converts RAL bring-up from guesswork to repeatable setup.

  • Create/fetch model, build, lock, bind map, wire predictor, then smoke test.

  • Smoke mirror check should run on day one of integration.

Common pitfalls

  • Skipping smoke and discovering setup bugs only during long regressions.

  • Calling lock_model too late after sequences already started.

  • Creating adapter/predictor after connect_phase and missing bindings.