UVM RAL · Chapter 5 · Adapters
uvm_reg_adapter
The adapter is a surprisingly small class whose two methods carry all of a register environment's bus knowledge. The reg2bus method is the outbound translator: given a generic operation with an address, data, and a read or write kind, it builds the concrete bus transaction, setting APB fields like paddr, pwrite, and pwdata. The bus2reg method is the inbound translator: given an observed bus item, it reconstructs the generic operation, including the status that tells RAL whether the access succeeded. The two methods must be exact inverses, because the predictor uses bus2reg to keep the mirror honest and to return read data and status. Two configuration flags, one for byte-enable support and one for response handling, round out the class. This lesson walks a complete adapter and then breaks the bug where bus2reg forgets to translate a bus error into status, so every access reports success and real errors go undetected.
Foundation12 min readUVM RALuvm_reg_adapterreg2busbus2regstatussupports_byte_enable
Chapter 5 · Section 5.2 · Adapters
1. Why Should I Learn This?
The adapter is small but load-bearing: two methods hold all the bus knowledge, and a subtle error in either has whole-block consequences. A reg2bus that encodes a field wrong corrupts every access; a bus2reg that reconstructs the operation incompletely drifts the mirror (the predictor uses it) or, worst of all, drops the status so bus errors are silently reported as success. Because the adapter is shared by every register, getting it exactly right — and knowing the two methods must be inverses — is what makes the whole register environment trustworthy on a given bus.
Learning to write the adapter's reg2bus and bus2reg, set supports_byte_enable and provides_responses correctly, and recognize the missing-status failure is what lets you bring up a register environment on any bus and trust that its accesses — and its error reporting — are honest. It is where the portability of 5.1 is actually earned.
2. Industry Story — the adapter that never saw an error
A team writes an APB adapter for a new register environment. The reg2bus is correct — addresses and data land right, registers read and write. The bus2reg, written quickly, reconstructs the address, kind, and data from the observed APB item but never sets rw.status, leaving it at its default of UVM_IS_OK.
For a while nothing seems wrong, because the accesses are all to valid addresses that succeed. Then a refactor introduces a register access to an unmapped address, and the APB slave returns PSLVERR — a bus error. But because bus2reg never translates pslverr into rw.status, RAL receives UVM_IS_OK for the errored access, so read()/write() report success and hand back the (garbage) data as valid. Every register access in the environment is now incapable of reporting a bus error, and the specific one that should have failed passes. The bug is discovered only when a system-level test with independent checking catches the unmapped access, and the team realizes their entire register suite had been blind to bus errors from day one — not because tests ignored status (they checked it, per 4.1), but because the adapter never produced an accurate status for them to check. The lesson: bus2reg must reconstruct the complete generic operation, including translating the bus's error response into status; an adapter that leaves status at its default reports every access as OK, so no test downstream can see a bus error no matter how carefully it checks.
3. Concept — the two methods and the config
A uvm_reg_adapter subclass is defined by two methods and a small configuration:
reg2bus(const ref uvm_reg_bus_op rw)returnsuvm_sequence_item. The outbound translator: create the concrete bus item and set its fields from the generic operation. For APB:paddr = rw.addr,pwrite = (rw.kind == UVM_WRITE),pwdata = rw.data,pstrb = rw.byte_en. Returns the item RAL will send on the sequencer.bus2reg(uvm_sequence_item bus_item, ref uvm_reg_bus_op rw). The inbound translator: cast the observed item and fill the generic operation —rw.addr,rw.kind,rw.data(read data on a read, write data on a write), andrw.status(translate the bus's response:pslverr->UVM_NOT_OK, elseUVM_IS_OK). Used by the predictor to update the mirror from monitored traffic, and to return read data and status to RAL.supports_byte_enable(bit). Set to 1 if the bus can write a sub-word (byte enables /pstrb), which is the capability behindindividually_accessible(3.3). If 0, RAL knows a field write must be a read-modify-write.provides_responses(bit). Set to 1 if the driver returns a response item per transfer (so RAL reads status from responses); 0 if status is carried on the request item itself. It must match how the bus agent's driver actually works.
The two methods must be exact inverses: whatever encoding reg2bus produces, bus2reg must decode identically, or the predictor (which runs bus2reg on observed traffic) will reconstruct a different operation than the one reg2bus sent, and the mirror drifts. Here is the field mapping both directions:
4. Mental Model — two translators that must be exact inverses
5. Working Example — a complete APB adapter
The whole bus knowledge of an APB register environment is this one small class:
class reg2apb_adapter extends uvm_reg_adapter;
`uvm_object_utils(reg2apb_adapter)
function new(string name = "reg2apb_adapter");
super.new(name);
supports_byte_enable = 1; // APB4 has PSTRB (byte enables) -> sub-word writes possible (3.3)
provides_responses = 0; // status carried on the request item, not a separate response
endfunction
// OUTBOUND: generic op -> concrete APB item.
virtual function uvm_sequence_item reg2bus(const ref uvm_reg_bus_op rw);
apb_item item = apb_item::type_id::create("item");
item.paddr = rw.addr;
item.pwrite = (rw.kind == UVM_WRITE);
item.pwdata = rw.data;
item.pstrb = rw.byte_en; // byte enables for a sub-word write
return item;
endfunction
// INBOUND: observed APB item -> generic op (address, kind, data, AND status).
virtual function void bus2reg(uvm_sequence_item bus_item, ref uvm_reg_bus_op rw);
apb_item item;
if (!$cast(item, bus_item)) begin
`uvm_fatal("ADPT", "bus2reg got a non-apb_item")
return;
end
rw.addr = item.paddr;
rw.kind = item.pwrite ? UVM_WRITE : UVM_READ;
rw.data = item.pwrite ? item.pwdata : item.prdata; // write data out, read data in
rw.status = item.pslverr ? UVM_NOT_OK : UVM_IS_OK; // TRANSLATE the bus error response
endfunction
endclassBind it to the map, and the whole generic register environment now drives real APB — with honest status:
// In connect_phase:
reg_model.default_map.set_sequencer(apb_agent.sequencer, reg2apb_adapter);
// Now a frontdoor access uses reg2bus to build the APB item and bus2reg to read the result:
cfg.write(s, 32'h13);
if (s != UVM_IS_OK) `uvm_error("REG", "APB write returned an error") // status is HONEST nowEvery field is encoded by reg2bus and decoded identically by bus2reg, and bus2reg translates pslverr into status — so a bus error becomes a UVM_NOT_OK a test can catch. The next section drops that one line.
6. Debugging Session — the bus2reg that dropped status
A bus2reg that never sets rw.status leaves it at UVM_IS_OK, so every access reports success and a bus error is masked
MISSING STATUS TRANSLATIONvirtual function void bus2reg(uvm_sequence_item bus_item, ref uvm_reg_bus_op rw);
apb_item item;
void'($cast(item, bus_item));
rw.addr = item.paddr;
rw.kind = item.pwrite ? UVM_WRITE : UVM_READ;
rw.data = item.pwrite ? item.pwdata : item.prdata;
// BUG: rw.status never set -> stays at its default UVM_IS_OK, even on PSLVERR.
endfunction
// An access to an unmapped address returns PSLVERR, but RAL sees UVM_IS_OK.
cfg.read(s, got); // s == UVM_IS_OK despite the bus error; 'got' is garbage treated as validRegister accesses never report a bus error — even a read or write to an unmapped address, which the APB slave answers with PSLVERR, comes back with status == UVM_IS_OK. Tests that dutifully check status (per 4.1) are satisfied every time, because the status they check is always OK, and they use the errored access's garbage data as valid. The whole environment is blind to bus errors, so a genuinely broken access passes, and the failure surfaces only when some independent, non-RAL check happens to catch the bad access much later. Nothing in the tests looks wrong — they check status correctly; the status they are handed is simply always a lie.
bus2reg reconstructs the generic operation from the observed bus item, and this one fails to translate the bus's error response into rw.status, leaving it at its default UVM_IS_OK. So no matter what the bus actually returned — success or PSLVERR — RAL is told the access succeeded. This is distinct from a test ignoring status (4.1): here the tests check status faithfully, but the adapter never produced an accurate one, so the check is against a value that is always OK. The status is the part of the operation bus2reg is responsible for reconstructing, and dropping it makes every access incapable of reporting failure at the source, upstream of any test.
Translate the bus response into status in bus2reg: rw.status = item.pslverr ? UVM_NOT_OK : UVM_IS_OK; (as in Section 5). Now an errored access produces a UVM_NOT_OK that a status-checking test catches. More generally, treat bus2reg as owing a complete reconstruction — address, kind, data, and status — and round-trip-test the adapter: bus2reg(reg2bus(op)) should return an operation equal to op, including status for both success and error responses. The rule the bug teaches: bus2reg must reconstruct the full generic operation, including translating the bus error response into status; an adapter that leaves status at its default reports every access as OK, so no downstream test can see a bus error however carefully it checks. When accesses never fail even on a known-bad address, look at bus2reg's status.
7. Common Mistakes
bus2regnot settingstatus. It defaults to OK, so every access reports success and bus errors are masked.reg2busandbus2regnot being exact inverses. The predictor reconstructs a different operation than was sent, and the mirror drifts.- Wrong
supports_byte_enable. Claiming byte enables the bus lacks makesindividually_accessiblea false promise (3.3); denying ones it has forces needless RMWs. - Wrong
provides_responses. If it does not match the driver's response behaviour, RAL reads status from the wrong place. - Reading write data from the read-data field (or vice versa) in
bus2reg. Reconstructdataby kind — write data out, read data in.
8. Industry Best Practices
- Reconstruct the complete operation in
bus2reg. Address, kind, data, and status — status translated from the bus response, always. - Round-trip-test the adapter.
bus2reg(reg2bus(op))must equalop, including status for success and error, so the two methods are proven inverses. - Set
supports_byte_enablefrom the real bus. It governsindividually_accessibleand RMW behaviour (3.3). - Match
provides_responsesto the driver. So RAL reads status from where the agent actually puts it. - Write one solid adapter per bus and reuse it. Test it hard once; every register environment on that bus inherits correct translation.
9. Interview / Review Questions
10. Key Takeaways
- A
uvm_reg_adapteris defined by two methods:reg2bus(rw)builds the concrete bus item from the generic operation (outbound), andbus2reg(item, rw)reconstructs the generic operation — address, kind, data, and status — from an observed bus item (inbound). - The two methods must be exact inverses, because the predictor runs
bus2regon monitored traffic to update the mirror; a mismatch drifts the mirror. Round-trip test:bus2reg(reg2bus(op)) == op. bus2regmust translate the bus error response intostatus(pslverr->UVM_NOT_OK); leaving it at its default reports every access as OK and masks bus errors upstream of any test.supports_byte_enable(the byte-enable capability behindindividually_accessible, 3.3) andprovides_responses(how the driver returns status) must be set from the real bus and driver.- The adapter is small but load-bearing — two methods hold all the bus knowledge — so write one solid adapter per bus, round-trip-test it, and reuse it across every register environment on that bus.