Part 8 · Checking & Coverage · Intermediate
Transaction-Level Reference Models
Memory models, predict() functions, and protocol response generation without cycle timing.
Protocol and memory state without cycle timing
Transaction-level models maintain logical state — memory arrays, register shadows, protocol response codes — and produce expected bus transactions in zero simulation time. They model what the DUT should return, not when cycles elapse. Use them when the verification plan checks data integrity, memory contents, and response semantics at the transaction boundary.
[CHECK] transaction-level model scope
MODELS: DOES NOT MODEL:
• Memory read/write data • Pipeline latency (N cycles)
• Response codes (OKAY) • Credit/debit counters
• Byte-enable semantics • Arbitration priority timing
• Ordering at txn boundary • Clock-domain crossing delays[UVM] memory ref model in check path
[UVM] dut_mon.ap ──► mem_ref_model.req_imp.write(req)
│
│ update mem[addr] on write
│ lookup mem[addr] on read
│
▼
mem_ref_model.ap.write(exp)
│
▼
[UVM] scb.exp_impSparse memory ref model
SRAM controllers, cache backends, and register files often need a backing memory model. A sparse associative array (bit [31:0] mem [bit [31:0]]) avoids allocating full address space. The model updates state on write, returns stored data or spec default on read.
class mem_ref_model extends uvm_component;
`uvm_component_utils(mem_ref_model)
bit [31:0] mem [bit [31:0]];
uvm_analysis_imp #(bus_txn, mem_ref_model) req_imp;
uvm_analysis_port #(bus_txn) ap;
function new(string name, uvm_component parent);
super.new(name, parent);
req_imp = new("req_imp", this);
ap = new("ap", this);
endfunction
function void write(bus_txn req);
bus_txn exp = bus_txn::type_id::create("exp");
exp.copy(req);
exp.kind = RESPONSE;
if (req.write) begin
if (req.strb == 4'hF)
mem[req.addr] = req.data;
else
mem[req.addr] = apply_strobe(mem.exists(req.addr) ? mem[req.addr] : 32'h0,
req.data, req.strb);
exp.resp = addr_mapped(req.addr) ? OKAY : SLVERR;
end else begin
if (!addr_mapped(req.addr)) begin
exp.resp = SLVERR;
exp.data = 32'h0;
end else begin
exp.data = mem.exists(req.addr) ? mem[req.addr] : 32'h0;
exp.resp = OKAY;
end
end
ap.write(exp);
endfunction
function bit addr_mapped(bit [31:0] addr);
return (addr >= 32'h0000_0000 && addr < 32'h0010_0000);
endfunction
endclassSparse array — only stores written addresses; unmapped reads return spec default.
Byte strobe handling — partial writes merge into existing word per spec.
Address map function encodes spec decode rules, not RTL decoder logic.
Response code (OKAY/SLVERR) from spec — document assumption in verification plan.
Clone req into exp and flip kind to RESPONSE for scoreboard pairing.
Standalone predict() function
For simpler blocks, a standalone predict() function outside the component hierarchy suffices. The component's write() delegates to it — same logic, two entry points for unit testing.
// Shared state and predict — testable without UVM
bit [31:0] mem [bit [31:0]];
function bus_txn predict(bus_txn req);
bus_txn exp = bus_txn::type_id::create("exp");
exp.copy(req);
exp.kind = RESPONSE;
if (req.write) begin
mem[req.addr] = req.data;
exp.resp = OKAY;
end else begin
exp.data = mem.exists(req.addr) ? mem[req.addr] : 32'h0;
exp.resp = OKAY;
end
return exp;
endfunction
// Component wrapper
function void write(bus_txn req);
ap.write(predict(req));
endfunctionProtocol outcome examples
APB slave — OKAY on mapped addr, SLVERR on decode miss; PREADY always 1 at transaction level.
AXI read — RDATA from memory model, RRESP=OKAY; ID preserved for out-of-order scoreboard.
DMA descriptor fetch — model returns linked-list next pointer from memory state.
[CHECK] transaction-level vs cycle-approximate boundary
Transaction-level: 'read addr 0x100 returns 0xDEADBEEF with OKAY'
(immediate, no wait states)
Cycle-approximate: 'read addr 0x100 returns 0xDEADBEEF after 3 cycles
when read credit available'
(requires credit model — see fidelity-matrix lesson)Key takeaways
Transaction models: memory + protocol outcomes, no cycle delays.
Sparse associative arrays for memory; document unmapped-address behavior.
predict() as package function enables unit tests before env wiring.
Common pitfalls
Full memory array for 32-bit address space — simulation memory blow-up; use sparse map.
Ignoring byte strobes — false mismatches on partial-word writes.
Modeling wait states at transaction level — belongs in cycle-approximate fidelity.