Part 9 · Register Model (RAL) · Intermediate
RAL Data Flow Overview: TEST -> map -> adapter -> bus
End-to-end RAL transaction pipeline from abstract test API to bus traffic and prediction feedback into the mirror.
Pipeline intent
RAL data flow turns intent-driven register APIs into protocol traffic, then feeds observed bus behavior back into the mirror. Understanding each boundary makes debug deterministic and prevents false blame between test, adapter, and DUT.
The key path is TEST -> RAL map -> adapter -> sequencer/driver -> BUS -> monitor -> predictor -> mirror . Every mismatch can be localized by checking this chain in order.
[UVM][RAL][BUS] full frontdoor pipeline
[UVM] test sequence
ral.ctrl.write(status, data, UVM_FRONTDOOR)
│
▼
[RAL] uvm_reg / uvm_reg_map
- resolve address + access policy
- create uvm_reg_bus_op
│
▼
[RAL] uvm_reg_adapter.reg2bus()
- produce bus sequence item
│
▼
[BUS] sequencer -> driver -> DUT
│
▼
[BUS] monitor captures response/activity
│
▼
[RAL] uvm_reg_predictor.bus2reg() + predict()
│
▼
[RAL] mirror now reflects observed hardware state[UVM][RAL][BUS] backdoor side path
[UVM] test calls write/read with UVM_BACKDOOR
│
▼
[RAL] register uses HDL path slices
│
▼
uvm_hdl_deposit / uvm_hdl_read (no [BUS] driver activity)
│
▼
mirror updates from backdoor transaction resultFrontdoor proves bus integration; backdoor accelerates setup and debug.
Adapter is the protocol bridge and must preserve semantic meaning of reg ops.
Predictor closes loop so mirror state tracks real observed bus behavior.
Walkthrough: one write transaction
Trace a single register write from API call to bus waveform to mirror update. This walkthrough is the fastest way to train new engineers on RAL debugging boundaries.
task write_ctrl_enable();
uvm_status_e status;
uvm_reg_data_t data;
data = 32'h0000_0001;
env.reg_model.ctrl.write(status, data, UVM_FRONTDOOR, .parent(this));
endtaskclass apb_reg_adapter extends uvm_reg_adapter;
`uvm_object_utils(apb_reg_adapter)
virtual function uvm_sequence_item reg2bus(const ref uvm_reg_bus_op rw);
apb_item item = apb_item::type_id::create("item");
item.addr = rw.addr;
item.write = (rw.kind == UVM_WRITE);
item.data = rw.data;
return item;
endfunction
endclass[BUS] one-write timeline
t0 [UVM] test issues ctrl.write(0x1)
t1 [RAL] map resolves addr=0x0000_0000
t2 [RAL] adapter emits apb_item(addr=0x0, write=1, data=0x1)
t3 [BUS] driver drives PADDR/PWDATA
t4 [BUS] monitor captures transfer
t5 [RAL] predictor bus2reg + predict()
t6 [RAL] ctrl mirror == 0x1[UVM] where to instrument logs
- before reg.write call (test intent)
- inside adapter reg2bus (resolved op)
- driver send point (actual bus item)
- monitor capture point (what DUT saw)
- predictor update (mirror transition)Use one transaction trace across all layers to diagnose mismatches quickly.
Log both register full name and resolved numeric address in same record.
Confirm predictor receives monitor traffic for all frontdoor domains.
Read path, status handling, and bus2reg conversion
Reads add response semantics: adapter and driver must route returned data correctly, and bus2reg must populate rw.status/rw.data consistently so RAL APIs and mirror checks stay trustworthy.
virtual function void bus2reg(uvm_sequence_item bus_item, ref uvm_reg_bus_op rw);
apb_item item;
if (!$cast(item, bus_item)) begin
rw.status = UVM_NOT_OK;
return;
end
rw.kind = item.write ? UVM_WRITE : UVM_READ;
rw.addr = item.addr;
rw.data = item.data;
rw.status = item.slverr ? UVM_NOT_OK : UVM_IS_OK;
endfunction[RAL][BUS] read pipeline specifics
[UVM] reg.read(...)
-> map resolves address
-> adapter creates read bus item
-> driver issues read transaction
-> monitor captures response data
-> predictor bus2reg fills rw.data/status
-> mirror updates with observed read value[UVM] status semantics
UVM_IS_OK:
transaction successful; mirror can trust data
UVM_NOT_OK:
bus/protocol error; do not treat mirror update as valid data proof
regression policy:
fail fast on unexpected NOT_OK in bring-up suitesbus2reg must carry both data and status; dropping status hides protocol issues.
Read path debug requires monitor visibility of returned data channel.
Use explicit status checks in tests before trusting readback values.
End-to-end debug playbook
When expected and observed values diverge, isolate faults by boundary: intent, map resolution, adapter translation, bus execution, monitor observation, predictor update, mirror state.
[UVM][RAL][BUS] divergence triage matrix
symptom likely boundary
---------------------------------------------------------------
wrong address on bus map offset/submap config
right address, wrong data byte lanes endian/n_bytes mismatch
bus looks correct, mirror stale predictor not connected
mirror updated, scoreboard still fail policy/field semantics mismatch
intermittent mismatch race/ordering or shared model misuse[RAL] quick isolation sequence
1) run one deterministic seed + one register transaction
2) print resolved map address and kind
3) print adapter bus item
4) capture monitor item
5) print predictor rw op
6) compare mirror before/after
first mismatch stage identifies root-cause zonetask trace_single_access(uvm_reg rg, uvm_reg_data_t d);
uvm_status_e st;
`uvm_info("TRACE", {"start ", rg.get_full_name()}, UVM_MEDIUM)
rg.write(st, d, UVM_FRONTDOOR, .parent(this));
`uvm_info("TRACE", $sformatf("done status=%s", st.name()), UVM_MEDIUM)
endtaskKey takeaways
The RAL pipeline is closed-loop: abstract API out to bus, observed bus back to mirror.
Map and adapter are the most common root causes of address/data mismatches.
Predictor connectivity determines whether mirror-based checks are trustworthy.
A boundary-by-boundary trace method turns complex RAL bugs into deterministic fixes.
Common pitfalls
Assuming reg.write success implies bus correctness without monitor validation.
Using mirror comparisons with no predictor on frontdoor traffic.
Ignoring rw.status and treating all reads as valid.
Debugging with multi-transaction noise instead of isolating one deterministic access.