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, andlock_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 inconnect_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 scoreboardgetit (or reach it viap_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:
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:
// 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// 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// 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
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// 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.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.
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.
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 —
getthe shared model, never re-create it. - Not distributing the model via config_db. If it is not
set, users cannotgetit and are tempted to create their own — alwayssetthe one model in the env. - Silently tolerating a failed
get. A failedgetshoulduvm_fatal, not fall back to creating a model — catch a missing distribution loudly. - Connecting map/predictor in the wrong phase.
set_sequencerand predictor-to-monitor connections belong inconnect_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
getthe 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 viaconfig_dbso 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,
setin config_db, and every componentgets 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.