Skip to content

UVM RAL · Chapter 13 · Production RAL

Integrating RAL with UVM Environments

A register model is inert until it is wired into the live testbench, and integration is that wiring. First create the model once in the environment and build then lock it. Second, connect its map to the bus agent's sequencer with the adapter, so a frontdoor access is driven on the real bus. Third, connect the predictor to the agent's monitor, so the mirror tracks observed traffic. Fourth, distribute the one model to sequences and the scoreboard through the config database, so everyone shares the same instance and one mirror. This lesson covers that wiring and the phases it happens in, then breaks the signature bug: a sequence or scoreboard that cannot get the model from the config database creates its own, so two models with independent mirrors never agree.

Foundation12 min readUVM RALintegrationconfig_dbshared modelconnect

Chapter 13 · Section 13.4 · Production RAL

1. Why Should I Learn This?

A correct register model does nothing until it is connected to the agent (for the bus), the monitor (for prediction), and the sequences/scoreboard (to use it) — and it only works if everyone shares the same instance. Knowing the integration steps (create-once, map→sequencer+adapter, predictor→monitor, distribute via config_db) and, above all, that there must be one shared model is what turns an inert model into a working RAL environment instead of two models with mirrors that never agree.

Learning RAL integration connects the pieces the earlier chapters built — adapters (Chapter 5), prediction (Chapter 6/11.2), the lifecycle (build/lock), and sequences (Chapter 7) — into a wired environment, and it is the practical bridge from a generated model (13.1) to a running testbench.

2. Industry Story — the sequence with its own private model

An env creates one register model, builds and locks it, connects its map to the agent's sequencer with the adapter, and connects the predictor to the monitor — all correct. But a register sequence, needing the model, could not get it from the config database (it was never set, or the key/type was wrong), so the sequence created its own fresh register model to work with.

Now there were two models. The env's model was the one the predictor kept in step from bus traffic; the sequence's model was a separate object the predictor never touched. So every access the sequence made through its model updated its (private) mirror, while the env's mirror — the one the scoreboard and predictor used — went its own way; and the sequence's mirror, never predicted, drifted immediately. Checks through the sequence's model mismatched, or its handle behaviour was subtly wrong, and it looked like a mirror/prediction bug — but the mirror and prediction were fine for the env's model; the sequence was simply looking at a different model. The fix was to set the one model in the config database in the env and have the sequence get it — never create its own. The post-mortem lesson: a register model's mirror is only meaningful if everyone shares the same instance — if a sequence or scoreboard creates its own model instead of get-ing the shared one from the config database, there are two models with independent mirrors, and only the env's model is predicted, so the other's mirror never agrees; create one model, distribute it via config_db, and ensure every component gets it rather than re-creating it.

3. Concept — create once, connect map + predictor, distribute one model

Integrating RAL is four wiring steps, in the right phases:

  • 1. Create the model once (env), build + lock. The environment instantiates one register model, build()s it, and lock_model()s it (lifecycle). This is the single instance everyone will share.
  • 2. Connect the map to the agent (frontdoor). map.set_sequencer(agent.sequencer, adapter) — so a frontdoor access is translated by the adapter and driven on the real bus via the agent's sequencer (Chapter 5). Done in connect_phase.
  • 3. Connect the predictor to the monitor (prediction). Connect the predictor's analysis input to the agent's monitor analysis port, and give the predictor the map and adapter (Chapter 6, 11.2) — so the mirror tracks observed bus traffic. Done in connect_phase.
  • 4. Distribute the one model (config_db). uvm_config_db#(reg_block)::set(...) the single model, and have sequences and the scoreboard get it (or reach it via p_sequencer) — so everyone shares the same instance and mirror.
  • The crux: one shared model. The mirror is only meaningful if all users share one model. A component that creates its own instead of get-ing the shared one produces two models with independent mirrors — only the env's is predicted, so the other never agrees.

Here is the integration wiring:

RAL integration steps: create+lock in env, connect map to sequencer+adapter, connect predictor to monitor, distribute one model via config_dbget the shared modelre-create instead of getre-createinstead of…1. ENV createsthe model ONCE-> build() +lock_model()2. map.set_sequencer(agent.sequencer, adapter) -> frontdoor drives the bus (Ch5)2.map.set_sequencer(agent.sequencer,adapter) ->frontdoor drives th…3. connect predictor to the agent's MONITOR + give it map/adapter -> mirror tracks traffic (Ch6/11.2)3. connect predictorto the agent'sMONITOR + give itmap/adapter ->…4. uvm_config_db::set the ONE model -> sequences + scoreboard GET it4.uvm_config_db::setthe ONE model ->sequences +…everyone sharesONE model + ONEmirrorcomponent CREATES its own instead of GET -> TWO models, independent mirrors (only env's predicted)componentCREATES its owninstead of GET-> TWO models,…
Figure 1 — integrating RAL into a UVM env, in order. (1) The ENV creates the register model ONCE, build() + lock_model(). (2) Connect its map to the bus AGENT: map.set_sequencer(agent.sequencer, adapter) so frontdoor accesses drive the real bus (Ch5). (3) Connect the PREDICTOR to the agent's MONITOR (give it the map+adapter) so the mirror tracks observed traffic (Ch6/11.2). (4) DISTRIBUTE the ONE model via uvm_config_db so sequences and the scoreboard GET the same instance (one shared mirror). The crux is step 4: everyone must share ONE model — a component that creates its OWN instead of getting the shared one yields two models with independent mirrors, only one of which is predicted.

4. Mental Model — one model wired to the bus, the monitor, and every user; the mirror is a shared truth

5. Working Example — wiring the model and distributing the one instance

Create the model once, connect map + predictor, and distribute the single instance via config_db:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// In the ENV: create the model ONCE, build + lock, connect map to the agent, predictor to the monitor.
class my_env extends uvm_env;
  my_reg_block reg_model;
  reg_predictor#(bus_item) predictor;
  bus_agent agent;
  bus_adapter adapter;
 
  virtual function void build_phase(uvm_phase phase);
    reg_model = my_reg_block::type_id::create("reg_model");
    reg_model.build(); reg_model.lock_model();                 // (1) create ONCE + lock
    // ... create agent, adapter, predictor ...
    uvm_config_db#(my_reg_block)::set(this, "*", "reg_model", reg_model);  // (4) DISTRIBUTE the ONE model
  endfunction
 
  virtual function void connect_phase(uvm_phase phase);
    reg_model.default_map.set_sequencer(agent.sequencer, adapter);   // (2) frontdoor -> real bus (Ch5)
    predictor.map = reg_model.default_map; predictor.adapter = adapter;
    agent.monitor.ap.connect(predictor.bus_in);                      // (3) predictor <- monitor (Ch6/11.2)
  endfunction
endclass
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// In a SEQUENCE / scoreboard: GET the shared model — NEVER create one.
class cfg_seq extends uvm_reg_sequence#(uvm_sequence#(uvm_reg_item));
  my_reg_block reg_model;
  virtual task body();
    if (!uvm_config_db#(my_reg_block)::get(null, get_full_name(), "reg_model", reg_model))
      `uvm_fatal("RALINT", "could not GET the shared reg_model — it must be set by the env, not re-created")
    // Use reg_model — the SAME instance the predictor maintains, so its mirror is the real shared one.
  endtask
endclass
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// The crux: ONE instance, distributed. Everyone GETs the same object -> one shared mirror.
//   env set(reg_model) + sequence/scoreboard get(reg_model)   -> ONE model, ONE mirror.        CORRECT.
//   sequence creates my_reg_block::type_id::create(...)        -> TWO models, TWO mirrors.       BUG (next).

Distributing the one model and having every user get it means everyone shares the predictor-maintained mirror. A component that creates its own — the bug of the next section — forks the mirror.

6. Debugging Session — a sequence that creates its own register model

1

A sequence that creates its own register model instead of getting the shared one ends up with a separate, unpredicted mirror, so its view of the registers never agrees with the env's

ONE SHARED MODEL — GET, NEVER RE-CREATE
Buggy Code
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// The env set the ONE model in config_db, but the sequence couldn't get it (or didn't try) and CREATED its own:
class cfg_seq extends uvm_reg_sequence#(uvm_sequence#(uvm_reg_item));
  my_reg_block reg_model;
  virtual task body();
    reg_model = my_reg_block::type_id::create("reg_model");  // BUG: creates its OWN model, not the shared one
    reg_model.build(); reg_model.lock_model();
    // This is a SEPARATE object from the env's. The predictor updates the ENV's model, NOT this one.
  endtask
endclass
// -> TWO register models with INDEPENDENT mirrors; the sequence's mirror is never predicted.
Symptom

The sequence's view of the registers is perpetually out of step: its mirror does not reflect what bus traffic (via the predictor) has established, so accesses/checks through its model mismatch, while the scoreboard (using the env's model) sees correct, predicted values. It looks like a mirror or prediction bug — the sequence's mirror is 'wrong' — but the env's mirror is perfectly maintained. The tell: the sequence's model disagrees with the env's model, and the sequence's mirror behaves as if no prediction ever happens to it (because none does) — two views of 'the registers' that never agree, which only makes sense if they are different models.

Root Cause

The sequence created its own register model instead of get-ing the shared one from the config database. So there are two register models: the env's (which the predictor keeps in step from monitor traffic) and the sequence's (a separate object). Nothing connects the sequence's model to the predictor — the predictor was wired to update the env's map/mirror — so the sequence's mirror is never predicted: it reflects only what the sequence itself does to it and drifts immediately from the real, shared, predicted mirror. The mirror and prediction machinery are not broken; they work correctly for the env's model. The bug is that the sequence is consulting the wrong model — a private duplicate whose mirror no one maintains. This is not a null-handle bug within one model (7.1), nor a prediction-pipeline break (11.2 — the pipeline is intact for the env's model); it is an integration bug: the single-shared-model invariant was violated by re-creating the model in a user instead of distributing and getting the one instance.

Fix

Have the sequence get the shared model from the config database (set by the env) rather than creating its own: uvm_config_db#(my_reg_block)::get(null, get_full_name(), "reg_model", reg_model) — and fatal if the get fails, so a missing distribution is caught loudly rather than papered over by re-creation. Ensure the env sets the model with a key/scope the sequence's get matches. Then the sequence uses the same instance the predictor maintains, and its view agrees with the env's. The rule the bug teaches: a register model's mirror is a shared truth — there must be one model instance, created once in the env, connected to the bus and the monitor, and distributed via config_db so every user gets it; a component that creates its own instead forks the mirror into a private, unpredicted copy that never agrees with the env's. The tell: a sequence's or scoreboard's register view disagreeing with the env's while the env's prediction is fine — check that they hold the same model instance, and replace any re-creation with a get.

7. Common Mistakes

  • Creating the model in a sequence/scoreboard instead of getting it. Forks the mirror into a private, unpredicted copy — get the shared model, never re-create it.
  • Not distributing the model via config_db. If it is not set, users cannot get it and are tempted to create their own — always set the one model in the env.
  • Silently tolerating a failed get. A failed get should uvm_fatal, not fall back to creating a model — catch a missing distribution loudly.
  • Connecting map/predictor in the wrong phase. set_sequencer and predictor-to-monitor connections belong in connect_phase, after components exist.
  • Forgetting to give the predictor the map/adapter. The predictor needs both to update the right register with the right data (11.2), in addition to the monitor connection.

8. Industry Best Practices

  • Create the model once in the env, build + lock. One instance is the single shared source of register state.
  • Wire map→agent and predictor→monitor in connect_phase. Frontdoor to the real bus via the adapter (Ch5); mirror tracking via the monitor (Ch6/11.2).
  • Distribute the one model via config_db; everyone gets it. Sequences and scoreboard get the shared instance — never create their own.
  • Fatal on a failed get. A missing distribution should stop loudly, not be masked by re-creating a model.
  • Diagnose disagreeing register views as a duplicated model. If a component is out of step while the env's prediction is fine, check it holds the same instance.

9. Interview / Review Questions

10. Key Takeaways

  • Integrating RAL is four wiring steps: (1) create the model once in the env (build + lock); (2) map.set_sequencer(agent.sequencer, adapter) so frontdoor drives the real bus (Ch5); (3) connect the predictor to the monitor (with map+adapter) so the mirror tracks observed traffic (Ch6/11.2); (4) distribute the one model via config_db so sequences/scoreboard get it.
  • The crux is one shared model: the mirror is a shared truth, meaningful only if every user holds the same instance — the predictor maintains one model's mirror.
  • The signature integration bug is a component (sequence/scoreboard) that creates its own model instead of get-ing the shared one — producing two models with independent mirrors, only the env's being predicted, so the other never agrees.
  • It runs fine but disagrees (unlike a null handle 7.1, which crashes immediately) and affects only the duplicating component while the env's mirror stays correct (unlike a prediction break 11.2, which makes the shared mirror wrong for everyone).
  • Create once, set in config_db, and every component gets it — never re-create; fatal on a failed get so a missing distribution is caught loudly, and diagnose a component's disagreeing register view by checking it holds the same model instance as the env.