UVM RAL · Chapter 14 · Industry Case Studies
Case Study: AXI-Lite Peripheral RAL
This capstone builds a peripheral's RAL on AXI-Lite instead of APB, so the focus shifts to the adapter and the shape of a multi-channel protocol. For a register access, a write drives the write-address and write-data channels and the subordinate returns a response on a separate B channel, while a read drives the read-address channel and returns data and a response together on the R channel. The adapter's reg2bus builds those phases, and its bus2reg reads back data and, critically, must map the response to the register status: okay to is-ok, and a slave error to not-ok. Because the response lives on its own channel, it is easy to build the adapter around address and data and forget it. This case builds the adapter and model, then breaks a bus2reg that ignores the response channel so a slave error is silently reported as ok.
Foundation14 min readUVM RALcase studyAXI-LiteadapterBRESP status
Chapter 14 · Section 14.5 · Industry Case Studies · Capstone
1. The Protocol Shape — a register access across AXI-Lite channels
Unlike APB's single two-phase transfer, an AXI-Lite register access is split across channels, and the response is on its own channel — the fact the adapter must not forget.
- Write:
AW(AWADDR) +W(WDATA,WSTRB), thenB(BRESP) — the write response. - Read:
AR(ARADDR), thenR(RDATA+RRESP) — data and response together. - Status mapping:
BRESP/RRESP=OKAY→UVM_IS_OK;SLVERR/DECERR→UVM_NOT_OK.
AXI-Lite register write — AW, W, then B (BRESP) response channel
12 cycles2. Industry Context — why AXI-Lite makes the status bug easy
APB delivers its error on the same transfer (PSLVERR), so mapping status is right next to the data. AXI-Lite delivers its error on a separate channel (B for writes, R for reads), after the address and data phases — so an engineer writing reg2bus/bus2reg naturally focuses on address and data (the obvious payload) and can forget the response channel entirely, especially for writes where the response arrives on a channel (B) with no data. The result is a status-fidelity bug (11.3) that is more likely on AXI-Lite than APB precisely because of the channel separation. Building an AXI-Lite peripheral RAL is the canonical place to internalize 'map the response channel to status' — and to verify it by forcing an error.
3. The AXI-Lite Adapter — reg2bus and bus2reg with response mapping
Build the adapter so reg2bus drives the address/data phases and bus2reg maps the response channel to status:
// AXI-Lite adapter: reg2bus builds AW/W (or AR); bus2reg reads data AND maps BRESP/RRESP to status.
class axil_adapter extends uvm_reg_adapter;
`uvm_object_utils(axil_adapter)
function new(string name="axil_adapter"); super.new(name); supports_byte_enable=1; provides_responses=1; endfunction
virtual function uvm_sequence_item reg2bus(const ref uvm_reg_bus_op rw);
axil_item t = axil_item::type_id::create("t");
t.addr = rw.addr;
t.data = rw.data; // W channel data (WDATA) for writes
t.strb = '1; // WSTRB
t.write = (rw.kind == UVM_WRITE); // AW+W path vs AR path
return t;
endfunction
virtual function void bus2reg(uvm_sequence_item bus_item, ref uvm_reg_bus_op rw);
axil_item t; $cast(t, bus_item);
rw.addr = t.addr;
rw.data = t.data; // R channel RDATA (for reads)
rw.kind = t.write ? UVM_WRITE : UVM_READ;
// MAP THE RESPONSE CHANNEL to status — this is the AXI-Lite-critical step:
rw.status = (t.resp == AXI_OKAY) ? UVM_IS_OK : UVM_NOT_OK; // SLVERR/DECERR -> UVM_NOT_OK (11.3)
endfunction
endclass4. Wiring and a Register Access
Connect the map to the AXI-Lite agent via this adapter, and drive a normal register access:
// Env: map -> AXI-Lite sequencer via the AXI-Lite adapter; predictor <- monitor (13.4).
reg_model.default_map.set_sequencer(axil_agent.sequencer, axil_adapter);
predictor.map = reg_model.default_map; predictor.adapter = axil_adapter;
axil_agent.monitor.ap.connect(predictor.bus_in);// A register write/read over AXI-Lite — the multi-channel access is hidden behind the RAL API:
uvm_status_e s; uvm_reg_data_t v;
ctrl_reg.write(s, 32'h0000_5A5A); // AW + W, then B (BRESP) -> status
ctrl_reg.read(s, v); // AR, then R (RDATA + RRESP) -> data + status
if (s != UVM_IS_OK) `uvm_error("AXIL", "access returned NOT_OK — a slave error on B/R")5. Verifying Status Fidelity — force a slave error
The status mapping is invisible until an error occurs, so force a SLVERR/DECERR and confirm UVM_NOT_OK (11.3):
// Drive an access the subordinate ERRORS on (illegal/protected address) and CHECK the status:
uvm_status_e s;
bad_reg.write(s, 32'h0); // subordinate returns SLVERR/DECERR on the B channel
if (s != UVM_NOT_OK)
`uvm_error("AXIL", "forced slave error not reported as UVM_NOT_OK -> bus2reg ignores BRESP/RRESP (11.3)")
// This forced-error check is the ONLY way to catch a status-fidelity bug — it has no wrong-value symptom.6. Debugging Walkthrough — the slave error that reported OK
The AXI-Lite adapter's bus2reg reads data but ignores the response channel, so a SLVERR/DECERR is reported as UVM_IS_OK and error accesses silently pass
MAP BRESP/RRESP TO STATUS// bus2reg reads the R-channel data but IGNORES the response (BRESP/RRESP), hardcoding OK:
virtual function void bus2reg(uvm_sequence_item bus_item, ref uvm_reg_bus_op rw);
axil_item t; $cast(t, bus_item);
rw.addr = t.addr;
rw.data = t.data;
rw.kind = t.write ? UVM_WRITE : UVM_READ;
rw.status = UVM_IS_OK; // BUG: never reads t.resp (BRESP/RRESP) — SLVERR/DECERR dropped
endfunction
// Every access — including ones the subordinate ERRORS on the B/R channel — returns UVM_IS_OK.Negative tests — driving an illegal/protected access and expecting UVM_NOT_OK — all pass, even against a subordinate that does return SLVERR/DECERR, because the register layer never sees a NOT_OK. Real slave errors in positive tests are silently masked — an access that errored on the B/R channel reports success, with no wrong-value symptom (the data may be valid). There is no loud failure; the only tell is that no access ever returns UVM_NOT_OK, visible only if you force an error and check.
A status-fidelity bug (11.3) made more likely by AXI-Lite's channel structure. bus2reg's job is to translate the bus transaction — including its response — back into a register result, and for AXI-Lite the response arrives on a separate channel (BRESP on the B channel for writes, RRESP on the R channel for reads). Here bus2reg read the data (RDATA) but never read the response field (t.resp), hardcoding rw.status = UVM_IS_OK — so every access, errored or not, returned OK to the register layer. The channel separation is what made the omission natural: the engineer built bus2reg around the obvious payload (address and data) and forgot the response, which lives on its own channel (and, for writes, on a channel with no data at all, making it easy to overlook). Negative tests, asserting UVM_NOT_OK, could never observe one, so they passed vacuously; genuine errors were swallowed. It is not a register, map, or data bug (the data round-trips fine); it is the response channel dropped in translation — the AXI-Lite form of the swallowed-error adapter (11.3), which is silent because a dropped error produces no wrong value.
Map the response channel to status in bus2reg: rw.status = (t.resp == AXI_OKAY) ? UVM_IS_OK : UVM_NOT_OK (translating SLVERR/DECERR to UVM_NOT_OK), for both B (writes) and R (reads). Then verify it by forcing a slave error and confirming UVM_NOT_OK — the forced-error check is the only way to catch this, since it has no wrong-value symptom. The lesson the AXI-Lite case teaches: an AXI-Lite adapter's bus2reg must map the response channel (BRESP/RRESP) to the register status — the channel separation makes it easy to build the adapter around address/data and forget the response, silently swallowing slave errors (SLVERR/DECERR reported as UVM_IS_OK); map the response to UVM_NOT_OK and verify by forcing an error (11.3). AXI-Lite makes the status-fidelity bug (11.3) more likely than APB precisely because the response is on a separate channel — so the forced-error verification is essential.
7. Common Mistakes
bus2regignoringBRESP/RRESP. The response is on a separate channel and easy to forget — map it to status (SLVERR/DECERR→UVM_NOT_OK, 11.3).- Mapping only the read response. The write response (
BRESPon theBchannel) also carries errors — map bothBandR. - Not forcing an error to verify status. A swallowed error has no wrong-value symptom — drive a
SLVERR/DECERRand confirmUVM_NOT_OK. - Trusting negative tests that never fail. Negative tests incapable of producing
NOT_OKare a status-fidelity red flag, not good news. - Ignoring
WSTRB/byte enables. For sub-word writes, driveWSTRBcorrectly inreg2bus(data-fidelity, 11.3).
8. Industry Best Practices
- Map the response channel to status.
BRESP/RRESP→UVM_IS_OK/UVM_NOT_OKinbus2reg, for both writes (B) and reads (R). - Force a slave error at bring-up. The only way to verify AXI-Lite status fidelity — drive
SLVERR/DECERRand assertUVM_NOT_OK(11.3). - Build
reg2bus/bus2regchannel-by-channel. Address (AW/AR), data (W/R), and response (B/R) — do not stop at address+data. - Distrust always-passing negative tests. A negative test that cannot produce
NOT_OKis not verifying error handling. - Verify the whole round trip. Data (bit-exact), address (right register), status (response mapped) across the AXI-Lite channels (11.3).
9. Interview / Review Questions
10. Key Takeaways
- An AXI-Lite register access is split across channels: a write uses
AW(address) +W(data/WSTRB) then theBchannel (BRESP); a read usesARthen theRchannel (RDATA+RRESP) — the response is on its own channel. - The adapter's
reg2busbuilds the address/data phases; itsbus2regreads data and must map the response channel (BRESP/RRESP) to status —OKAY→UVM_IS_OK,SLVERR/DECERR→UVM_NOT_OK(Ch5, 11.3). - The signature bug — more likely on AXI-Lite than APB because the response is on a separate channel that is easy to forget — is
bus2regignoringBRESP/RRESP, so a slave error is silently reported asUVM_IS_OK: negative tests pass wrongly, real errors masked, no wrong-value symptom. - Map the response for both
B(writes) andR(reads), and verify by forcing aSLVERR/DECERRand assertingUVM_NOT_OK— the only way to catch a status-fidelity bug (11.3). - Build
reg2bus/bus2regchannel-by-channel (address, data, and response), distrust always-passing negative tests, and make the forced-error status check a bring-up gate (11.6) — AXI-Lite reinforces the adapter status-fidelity lesson (11.3) in a multi-channel context.