UVM RAL · Chapter 5 · Adapters
Why Adapters Exist
RAL deliberately speaks a generic language: every register operation is simply read or write this data at this address. A real interconnect speaks a concrete language of signals and phases that differs from one bus to the next. These two languages cannot talk directly, and if RAL tried to speak every bus protocol itself, the register model would be welded to one bus. The adapter is the translator that sits between them. Its reg2bus method turns a generic operation into a concrete bus transaction on the way out, and its bus2reg method turns an observed transaction back into a generic operation on the way in. Because the adapter is the only bus-specific component, everything else stays portable, so moving a block from APB to AXI-Lite means writing one new adapter while the tests run unchanged. This lesson explains why the adapter exists and breaks a setup with no adapter, where every frontdoor access fails.
Foundation11 min readUVM RALAdapterreg2busbus2regPortabilityBus
Chapter 5 · Section 5.1 · Adapters
1. Why Should I Learn This?
The adapter is the hinge that makes RAL worth using across a real project. Without understanding why it exists, engineers make one of two mistakes: they hand-code register tests in bus terms (losing all portability and reintroducing the rot RAL removes), or they treat the adapter as incidental boilerplate and are baffled when a missing or wrong one makes the whole register environment fail. Grasping that the adapter is the single bus-specific piece — the one place the concrete protocol lives — is what lets you reason about what is portable (everything else) and where a bus problem must be (the adapter).
Learning why adapters exist sets up the rest of the chapter: the adapter's methods (5.2), how it plugs in, and the byte-enable and response subtleties. It is the conceptual foundation for the one component that lets a register model built once run on any bus.
2. Industry Story — the tests that could not move buses
A team verifies a peripheral behind an APB interface by writing register checks directly in APB terms: each check drives an APB transaction to a hard-coded address and inspects the response. It works, and the block ships. A year later the same IP is reused in a new SoC, but on an AXI-Lite interconnect instead of APB.
Every register test has to be rewritten. Because the tests were written in the bus's concrete language — APB signals, APB sequences — none of them can run against AXI-Lite; the addresses, the transaction types, the response handling are all APB-specific. The team re-derives the entire register test suite for AXI, re-introducing every transcription risk, and the reuse the IP promised evaporates at the register layer. A neighbouring team that had used RAL moved the same IP to AXI by writing one new adapter — perhaps a hundred lines — and ran their existing register tests unchanged, because their tests never spoke APB in the first place; they spoke generic RAL operations that the adapter translated. The post-mortem lesson: RAL keeps register tests bus-agnostic by confining the bus-specific protocol to one component, the adapter — so a bus change is one new adapter, not a rewritten suite. Hand-coding tests in bus terms throws that portability away.
3. Concept — two languages and the translator between them
The adapter exists because two languages meet at the register interface:
- RAL's generic language. Every register operation RAL produces is a
uvm_reg_bus_op: an address, a data value, a kind (read/write), a width, and byte enables. It is deliberately abstract — it describes what to do to a register, not how any particular bus does it. The register model, maps, sequences, and tests all speak only this. - The bus's concrete language. A real interconnect has its own transaction with its own signals and phases — APB's
paddr/pwrite/pwdata/pready/pslverr, AXI-Lite's channels, a custom bus's protocol. A bus agent's sequencer and driver speak only this. - The adapter — the translator.
reg2bus(rw)takes a genericuvm_reg_bus_opand produces the concrete bus sequence item (outbound).bus2reg(item, rw)takes an observed concrete bus item and fills in a genericuvm_reg_bus_op(inbound), used by the predictor to update the mirror and to return read data. The adapter is the one place the concrete protocol is known.
The consequence is the whole point of the design: the adapter is the only bus-specific component, so everything else is portable. Here is that portability — one generic model, a swappable adapter, any bus:
4. Mental Model — the adapter is the interpreter; swap it and keep the conversation
5. Working Example — the same tests, a swapped adapter
The register model and a test are pure register-ese — no bus anywhere:
// A register test — entirely generic. It knows nothing about APB or AXI.
class cfg_test_seq extends uvm_reg_sequence;
`uvm_object_utils(cfg_test_seq)
my_reg_block model;
virtual task body();
uvm_status_e s; logic [31:0] got;
model.cfg.write(s, 32'h0000_0013); // generic: write a value to a register
model.cfg.read (s, got); // generic: read it back (auto-checked)
// ... more register-ese; not a single bus signal named ...
endtask
endclassOnly the adapter — and which one the map is bound to — carries the bus:
// APB environment: bind the map to the APB agent's sequencer via an APB adapter.
reg_model.default_map.set_sequencer(apb_agent.sequencer, apb_adapter);
// Move the SAME IP to AXI-Lite: the ONLY change is the adapter (and its agent).
reg_model.default_map.set_sequencer(axi_agent.sequencer, axil_adapter);
// The cfg_test_seq above runs UNCHANGED against AXI-Lite — it never spoke APB.The test, the model, and the maps did not change one line; the bus port was a single new adapter bound in place of the old one. That is the portability the adapter's existence buys — and the next section shows what happens when there is no adapter at all.
6. Debugging Session — no adapter, no translation
Binding a map with no adapter leaves RAL unable to build a bus transaction, so every frontdoor access fails
MISSING ADAPTER// The map is given a sequencer but NO adapter (null):
reg_model.default_map.set_sequencer(apb_agent.sequencer, null); // BUG: adapter is null
// A frontdoor access needs the adapter to translate the generic op into an APB item:
cfg.write(s, 32'h13); // RAL has no reg2bus() to call -> cannot build a bus transaction
// UVM_FATAL/ERROR: map has no adapter; frontdoor access cannot proceedEvery frontdoor access fails — an error about the map having no adapter, or the access simply cannot build a transaction and returns not-OK with no bus activity. It affects the whole block uniformly (the map is shared), so it looks like a broad, catastrophic RAL failure, and because the sequencer was provided, it is tempting to think the connection is complete. Backdoor access, if HDL paths are set, still works — which is a clue: the storage is reachable, but nothing can be driven on the bus, because the piece that speaks the bus is missing.
The map has a sequencer but no adapter, and the adapter is the component that translates a generic uvm_reg_bus_op into a concrete bus transaction. Without it, RAL has no reg2bus() to call, so it literally cannot construct the bus item a frontdoor access must drive — there is a sequencer to send on, but nothing to build what to send. This is the adapter's reason for existing made visible: RAL speaks only register-ese, and with no interpreter it cannot say anything on the bus at all. The register model, the map's addresses, and the sequencer are all fine; the one piece that speaks the bus is absent.
Provide the adapter when binding the map: reg_model.default_map.set_sequencer(apb_agent.sequencer, apb_adapter). Now RAL can call the adapter's reg2bus() to build the APB transaction and bus2reg() to interpret the result, and frontdoor access works. The rule the bug teaches is the chapter's thesis stated as a failure: RAL cannot speak any bus without an adapter — it is the mandatory translator between RAL's generic operations and the concrete bus protocol, so a map with no adapter can address registers but cannot drive a single frontdoor transaction. When every frontdoor access fails uniformly but the sequencer is connected, check the adapter.
7. Common Mistakes
- Hand-coding register tests in bus terms. They cannot move buses; write them as generic RAL operations so the adapter carries the bus.
- Binding a map with no adapter. RAL cannot build a bus transaction; every frontdoor access fails.
- Letting protocol knowledge leak into the model or tests. Any bus-specific decision belongs in the adapter, the one place the protocol lives.
- Assuming the sequencer alone completes the connection. The map needs both a sequencer to send on and an adapter to build what to send.
- Writing a new model for a new bus. The model is bus-agnostic; a bus port is a new adapter, not a new model.
8. Industry Best Practices
- Confine the bus to the adapter. One bus-specific component; everything else stays generic and portable.
- Write register tests as RAL operations. Register-ese, never bus transactions — portable by construction.
- Port to a new bus by writing one adapter. Reuse the model, maps, sequences, and tests unchanged.
- Bind the map with both sequencer and adapter. In
connect_phase, alwaysset_sequencer(sequencer, adapter)— both are required. - Reuse a proven adapter per bus. Write the APB/AXI adapter once, test it hard (5.2), and every register environment on that bus inherits it.
9. Interview / Review Questions
10. Key Takeaways
- The adapter exists because two languages meet: RAL's generic operation (
uvm_reg_bus_op— read/write an address) and a bus's concrete protocol (APB/AXI signals and phases). - The adapter is the translator:
reg2bus()turns a generic operation into a bus transaction (outbound);bus2reg()turns an observed transaction back into a generic operation (inbound). - Because the adapter is the only bus-specific component, the register model, maps, sequences, and tests are bus-agnostic and portable — a bus port is one new adapter, not a rewritten suite.
- Write register tests as generic RAL operations (register-ese), never as bus transactions, so they move buses by construction.
- The signature bug is a map with no adapter: RAL has no
reg2bus()to call, so it cannot build a bus transaction and every frontdoor access fails — the adapter is the mandatory translator without which RAL cannot speak any bus.