UVM RAL · Chapter 6 · Predictors
Passive & Multi-Bus Prediction
The predictor's real power shows in two scenarios beyond a single active bus. In passive prediction, RAL drives nothing at all. The register traffic comes from embedded software or an external master, and the environment only watches, so the predictor is the sole mechanism keeping the mirror current. Watching the bus, it maintains the whole model from observation, so the model reflects what software configured without RAL driving a cycle. In multi-bus prediction, a block reached through more than one bus needs one predictor per bus, each subscribed to its own monitor and given its own map, all updating the single shared model. This page shows how to set up passive prediction and wire a predictor per bus, then breaks a two-bus block given only one predictor, so accesses on the second bus are never observed and the mirror drifts while the first bus looks fine.
Foundation12 min readUVM RALPassive PredictionMulti-BusPredictorMulti-MapMonitor
Chapter 6 · Section 6.4 · Predictors
1. Why Should I Learn This?
Passive and multi-bus prediction are exactly the scenarios where register verification is hardest and most valuable — embedded-software bring-up, where the model must track what firmware does over the bus without driving anything, and multi-bus SoC blocks, where a register is reachable from a fast path and a debug path. In both, a single-predictor mental model fails silently: passive needs the predictor to be the whole mirror mechanism, and multi-bus needs a predictor per bus or the mirror drifts for the untracked bus. Getting the predictor topology right is what keeps the mirror honest in the environments that matter most.
Learning these two scenarios extends the predictor from 'the thing that catches the second master' (6.1) to the general principle: the mirror must be fed by a witness on every bus that can change a register, and when RAL drives nothing, the witnesses are all there is. It is the predictor scaled to real SoC topologies.
2. Industry Story — the debug bus nobody predicted
An SoC block's registers are reachable from two buses: a fast functional bus for normal operation and a separate low-speed debug bus. The register model has two maps (3.2), one per bus. The verification team, moving to multi-master, connects a predictor to the functional bus's monitor with the functional map — and stops there, assuming one predictor covers the block.
Debug-bus accesses are never tracked. A test (or an embedded debug routine) programs a register over the debug bus, and because no predictor watches that bus, RAL's mirror never learns about the write. A later RAL read (over the functional bus) of that register mismatches: the DUT holds the debug-written value, the mirror holds the old one. Functional-bus accesses are all fine, so the environment looks correct and the drift seems to strike at random — until someone notices every drifting access was driven over the debug bus. The fix is a second predictor, subscribed to the debug bus's monitor and given the debug map, updating the same shared register model. The lesson: a block reached through multiple buses needs one predictor per bus — each watching its own bus's monitor with its own map, all updating the single shared model — and a single predictor tracks only its one bus, leaving accesses on every other bus to drift the mirror.
3. Concept — passive prediction and per-bus predictors
Two scenarios, each with a rule:
- Passive prediction (RAL drives nothing). The stimulus is embedded software or an external master; RAL only monitors. The predictor is the sole mirror maintainer — it watches the bus and updates the whole register model from observation, so the model tracks live register state without any frontdoor accesses. Auto prediction is moot (there is nothing RAL drives to auto-predict from), so the setup is simply: connect the predictor (map, adapter,
bus_in), and the model is passive. This is what makes RAL usable for software-driven register bring-up: the model reflects what firmware configured, purely by watching. - Multi-bus prediction (one block, several buses). A block reached through several buses has several maps (3.2), and each bus has its own monitor and address view. Keeping the mirror honest requires one predictor per bus: each
uvm_reg_predictoris parameterized by that bus's item type, given that bus's map, and subscribed to that bus's monitor. All the predictors update the same shared register model — the registers are one set of objects with one mirror, and each predictor is a witness on one road, all reporting to the one ledger. A predictor covers exactly the one bus it watches; another bus needs its own.
The unifying principle from 6.1 taken to its conclusion: the mirror must be fed by a witness on every bus that can change a register. Here is a two-bus block, one predictor per bus, one shared model:
4. Mental Model — one ledger, a witness on every road
5. Working Example — passive setup and per-bus predictors
Passive prediction is just the predictor with no active RAL driving — the model tracks observed software traffic:
// PASSIVE: RAL drives nothing (embedded software drives the bus). The predictor is the
// SOLE mirror maintainer; the model tracks live register state from observation.
apb_predictor.map = reg_model.default_map;
apb_predictor.adapter = apb_adapter;
apb_agent.monitor.ap.connect(apb_predictor.bus_in); // watch the software-driven bus
reg_model.default_map.set_auto_predict(0); // explicit prediction (nothing to auto anyway)
// No sequences drive the model; get_mirrored_value() reflects what SOFTWARE configured.Multi-bus prediction wires one predictor per bus, all updating the one shared model:
// MULTI-BUS: one predictor per bus, each with ITS bus's item type, map, and monitor.
fast_predictor = uvm_reg_predictor#(fast_item)::type_id::create("fast_predictor", this);
dbg_predictor = uvm_reg_predictor#(dbg_item) ::type_id::create("dbg_predictor", this);
// Functional bus predictor:
fast_predictor.map = reg_model.fast_map; // fast bus's map (3.2)
fast_predictor.adapter = fast_adapter;
fast_agent.monitor.ap.connect(fast_predictor.bus_in); // watches the functional bus
// Debug bus predictor — SAME shared model, its own map/adapter/monitor:
dbg_predictor.map = reg_model.dbg_map; // debug bus's map
dbg_predictor.adapter = dbg_adapter;
dbg_agent.monitor.ap.connect(dbg_predictor.bus_in); // watches the debug bus
reg_model.fast_map.set_auto_predict(0);
reg_model.dbg_map.set_auto_predict(0);
// Now a register written over EITHER bus updates the one shared mirror, so it stays coherent.Each predictor watches its own bus and updates the shared model, so a register written over the debug bus is tracked by dbg_predictor and a functional-bus read of it is coherent. Drop the debug predictor and debug-bus accesses go untracked — the next section.
6. Debugging Session — one predictor for a two-bus block
A block reached by two buses but given only one predictor never tracks the second bus, so the mirror drifts for everything driven over it
MISSING PER-BUS PREDICTOR// The block is reached by a functional bus AND a debug bus (two maps, 3.2).
// Only the FUNCTIONAL bus gets a predictor:
fast_predictor.map = reg_model.fast_map; fast_predictor.adapter = fast_adapter;
fast_agent.monitor.ap.connect(fast_predictor.bus_in);
// BUG: no predictor on the DEBUG bus. Its monitor is never watched.
// A register is written over the DEBUG bus (a test or debug routine drives dbg_agent):
// ... dbg_agent drives a write of 0xABCD to CTRL over the debug bus ...
ctrl.read(s, got); // functional-bus read: DUT = 0xABCD, but mirror is stale (debug write unseen)Functional-bus accesses are all coherent, but reads mismatch after a register is changed over the debug bus — DUT correct, mirror stale — and the drift seems random until you notice that every drifting access was driven over the debug bus. It looks like intermittent register coherency corruption, and because the functional-bus predictor is present and working, the environment appears to have prediction covered. The tell is the correlation: the mismatches track one specific bus (the unwatched one), while the other bus is always fine.
The block is reached by two buses, but only one has a predictor. A predictor watches exactly one bus (the monitor it is subscribed to) with one map; it has no visibility into any other bus. So the debug bus, with no predictor on its monitor, is unobserved: a register written over it changes the DUT, but no witness reports the change to the mirror, which stays at its last-known value. The functional-bus predictor is correct and covers functional-bus traffic perfectly — it simply cannot see the debug bus, because that is a different road with a different monitor. The mirror drifts for exactly the accesses driven over the unwatched bus, which is why the drift correlates with one bus.
Add a predictor for the second bus: a uvm_reg_predictor#(dbg_item) given the debug map (reg_model.dbg_map) and the debug adapter, subscribed to the debug bus's monitor (Section 5). Now both buses are watched, both feed the one shared model, and a register written over either bus is tracked. The rule the bug teaches: a block reached through multiple buses needs one predictor per bus — each with its own monitor and map, all updating the single shared model — because a predictor watches only its one bus; every other bus that can change a register needs its own witness, or the mirror drifts for accesses driven over it. When mirror mismatches correlate with one specific bus, that bus is missing its predictor.
7. Common Mistakes
- One predictor for a multi-bus block. It tracks only its bus; accesses over other buses drift the mirror.
- Wrong map on a per-bus predictor. Each predictor must use its own bus's map to resolve addresses (6.3).
- Assuming auto prediction helps in a passive scenario. RAL drives nothing to auto-predict from; the predictor is the whole mechanism.
- Leaving auto-predict on with per-bus predictors. Turn it off on each map (6.2); explicit prediction is in use.
- Not sharing one model across the bus predictors. All predictors update the same register model (one mirror), not separate copies.
8. Industry Best Practices
- One predictor per bus, all updating the shared model. Each with its own item type, map, and monitor; the registers are one set with one mirror.
- Use passive prediction for software-driven register verification. RAL drives nothing; the predictor maintains the model from observation.
- Turn off auto-predict on every map when predictors are used. Explicit prediction across all buses (6.2).
- Diagnose bus-correlated drift as a missing per-bus predictor. Mismatches that track one bus mean that bus is unwatched.
- Verify each bus's predictor with a real access over that bus. Confirm the shared mirror advances for traffic on every bus.
9. Interview / Review Questions
10. Key Takeaways
- Passive prediction: RAL drives nothing (embedded software / external stimulus), so the predictor is the sole mirror-maintenance mechanism — the model tracks live register state purely from observation.
- Multi-bus prediction: a block reached through several buses needs one predictor per bus, each with its own item type, map, adapter, and monitor, all updating the single shared register model (one set of registers, one mirror).
- The unifying principle: the mirror must be fed by a witness on every bus that can change a register — one ledger, a witness on every road, all reporting to the one ledger.
- The signature bug is one predictor for a multi-bus block: it tracks only its bus, so the mirror drifts for accesses over every other bus — and the drift correlates with the unwatched bus.
- Turn auto-predict off on every map when predictors are used (6.2), and verify each bus's predictor with a real access over that bus.