AMBA AHB APB Bridge UVM Verification Environment
A comprehensive guide to building a UVM-based verification environment for the AMBA AHB-to-APB Bridge. This post covers the bridge functionality, verification challenges, testplan development, and complete UVM architecture.
What is an AHB-APB Bridge?
The AHB-APB Bridge is a critical component in AMBA-based SoC designs that translates transactions between two different bus protocols:
| Feature | AHB (Source) | APB (Destination) |
|---|---|---|
| Performance | High-bandwidth, pipelined | Low-power, simple |
| Burst Support | Yes (INCR, WRAP) | No |
| Wait States | HREADY signal | PREADY signal |
| Data Width | 32/64/128-bit | 8/16/32-bit |
| Typical Use | CPU, DMA, Memory | UART, GPIO, Timers |
Bridge Responsibilities
- Protocol Translation: Convert pipelined AHB to non-pipelined APB
- Burst Decomposition: Break AHB bursts into individual APB transfers
- Address Decoding: Route transactions to correct APB peripheral
- Wait State Handling: Manage HREADY/PREADY synchronization
- Error Propagation: Convert PSLVERR to HRESP
Bridge State Machine
The bridge operates through distinct states to manage the protocol conversion:
%%{init: {'theme': 'base', 'themeVariables': {'primaryColor': '#e0f2fe', 'primaryTextColor': '#0f172a', 'primaryBorderColor': '#0066cc', 'lineColor': '#475569'}}}%%
stateDiagram-v2
[*] --> IDLE
IDLE --> SETUP: AHB Transfer
SETUP --> ACCESS: Always
ACCESS --> IDLE: PREADY & Last
ACCESS --> SETUP: PREADY & Burst
ACCESS --> ACCESS: !PREADY
note right of IDLE: Wait for AHB request
note right of SETUP: APB Setup Phase\nPSEL=1, PENABLE=0
note right of ACCESS: APB Access Phase\nPSEL=1, PENABLE=1
Verification Challenges
Verifying the AHB-APB bridge requires testing several complex scenarios:
- Protocol Compliance: Both AHB and APB protocols must be followed correctly
- Burst Handling: INCR, INCR4/8/16, WRAP4/8/16 bursts must decompose correctly
- Back-pressure: Bridge must handle slow APB peripherals (PREADY delays)
- Error Handling: PSLVERR must propagate to HRESP correctly
- Concurrent Access: Multiple masters accessing bridge (if arbitrated)
- Address Boundary: 1KB address boundary crossing in bursts
Verification Testplan
Functional Coverage Goals
| Category | Features | Priority |
|---|---|---|
| Transfer Types | READ, WRITE | P1 |
| Burst Types | SINGLE, INCR, INCR4/8/16, WRAP4/8/16 | P1 |
| Transfer Sizes | BYTE, HALFWORD, WORD | P1 |
| Wait States | 0, 1, 2, random delays | P1 |
| Error Response | OKAY, ERROR (PSLVERR) | P1 |
| Address Ranges | Each APB peripheral select | P2 |
| Back-to-back | Consecutive transfers | P2 |
| Boundary Cross | 1KB boundary in burst | P2 |
Test Scenarios
TEST_SINGLE_READ - Basic single read transaction
TEST_SINGLE_WRITE - Basic single write transaction
TEST_BURST_INCR4 - 4-beat incrementing burst
TEST_BURST_WRAP8 - 8-beat wrapping burst
TEST_MIXED_RW - Alternating read/write
TEST_BACK_TO_BACK - No idle cycles between transfers
TEST_WAIT_STATES - APB slave with random PREADY delays
TEST_ERROR_RESPONSE - PSLVERR injection and HRESP check
TEST_ALL_PERIPHERALS - Access each address range
TEST_BOUNDARY_CROSS - Burst crossing 1KB boundary
TEST_RANDOM_STRESS - Random transactions with constraints
UVM Environment Architecture
%%{init: {'theme': 'base', 'themeVariables': {'primaryColor': '#e0f2fe', 'primaryTextColor': '#0f172a', 'primaryBorderColor': '#0066cc', 'lineColor': '#475569', 'secondaryColor': '#f8fafc'}}}%%
flowchart TB
subgraph TEST["Test"]
SEQ[Virtual Sequence]
end
subgraph ENV["Environment"]
subgraph AHB_AGT["AHB Agent (Active)"]
AHB_DRV[Driver]
AHB_MON[Monitor]
AHB_SEQR[Sequencer]
end
subgraph APB_AGT["APB Agent (Reactive)"]
APB_DRV[Responder]
APB_MON[Monitor]
end
SCB[Scoreboard]
COV[Coverage]
PRED[Predictor]
end
subgraph DUT["AHB-APB Bridge DUT"]
BRIDGE{{Bridge}}
end
SEQ --> AHB_SEQR
AHB_SEQR --> AHB_DRV
AHB_DRV -->|AHB IF| BRIDGE
BRIDGE -->|APB IF| APB_DRV
APB_DRV -->|Response| BRIDGE
AHB_MON -.-> PRED
APB_MON -.-> SCB
PRED -.-> SCB
AHB_MON -.-> COV
APB_MON -.-> COV
style BRIDGE fill:#d1fae5,stroke:#10b981,stroke-width:2px
style SCB fill:#fef3c7,stroke:#f59e0b,stroke-width:2px
style COV fill:#e0e7ff,stroke:#6366f1,stroke-width:2px
Key UVM Components
AHB Sequence Item
class ahb_seq_item extends uvm_sequence_item;
`uvm_object_utils(ahb_seq_item)
// Transaction fields
rand bit [31:0] haddr;
rand bit [31:0] hwdata;
rand bit hwrite;
rand bit [2:0] hsize; // BYTE, HALFWORD, WORD
rand bit [2:0] hburst; // SINGLE, INCR, WRAP4, etc.
rand bit [3:0] hprot;
// Response
bit [31:0] hrdata;
bit hresp;
// Constraints
constraint c_size {
hsize inside {3'b000, 3'b001, 3'b010}; // BYTE, HALF, WORD
}
constraint c_burst {
hburst inside {3'b000, 3'b001, 3'b010, 3'b011, // SINGLE, INCR, WRAP4, INCR4
3'b100, 3'b101, 3'b110, 3'b111}; // WRAP8, INCR8, WRAP16, INCR16
}
constraint c_align {
// Address alignment based on size
(hsize == 3'b001) -> (haddr[0] == 0); // Halfword aligned
(hsize == 3'b010) -> (haddr[1:0] == 0); // Word aligned
}
function new(string name = "ahb_seq_item");
super.new(name);
endfunction
endclass
AHB Driver
class ahb_driver extends uvm_driver #(ahb_seq_item);
`uvm_component_utils(ahb_driver)
virtual ahb_if vif;
task run_phase(uvm_phase phase);
forever begin
seq_item_port.get_next_item(req);
drive_transfer(req);
seq_item_port.item_done();
end
endtask
task drive_transfer(ahb_seq_item item);
// Address phase
@(posedge vif.hclk);
vif.haddr <= item.haddr;
vif.hwrite <= item.hwrite;
vif.hsize <= item.hsize;
vif.hburst <= item.hburst;
vif.htrans <= 2'b10; // NONSEQ
// Wait for HREADY
do @(posedge vif.hclk);
while (!vif.hready);
// Data phase
if (item.hwrite) begin
vif.hwdata <= item.hwdata;
end
// Wait for completion
do @(posedge vif.hclk);
while (!vif.hready);
// Capture response
if (!item.hwrite) begin
item.hrdata = vif.hrdata;
end
item.hresp = vif.hresp;
vif.htrans <= 2'b00; // IDLE
endtask
endclass
APB Responder (Reactive Agent)
class apb_responder extends uvm_driver #(apb_seq_item);
`uvm_component_utils(apb_responder)
virtual apb_if vif;
// Memory model for read data
bit [31:0] mem[bit[31:0]];
// Configuration
int unsigned min_wait_cycles = 0;
int unsigned max_wait_cycles = 3;
bit inject_error = 0;
task run_phase(uvm_phase phase);
vif.pready <= 1'b0;
vif.pslverr <= 1'b0;
vif.prdata <= 32'h0;
forever begin
@(posedge vif.pclk);
// Detect APB access phase
if (vif.psel && vif.penable) begin
// Random wait states
repeat ($urandom_range(min_wait_cycles, max_wait_cycles))
@(posedge vif.pclk);
// Handle transaction
if (vif.pwrite) begin
// Write: store data
mem[vif.paddr] = vif.pwdata;
end else begin
// Read: return data
vif.prdata <= mem.exists(vif.paddr) ? mem[vif.paddr] : 32'hDEAD_BEEF;
end
// Error injection
vif.pslverr <= inject_error;
vif.pready <= 1'b1;
@(posedge vif.pclk);
vif.pready <= 1'b0;
vif.pslverr <= 1'b0;
end
end
endtask
endclass
Scoreboard with Predictor
class bridge_scoreboard extends uvm_scoreboard;
`uvm_component_utils(bridge_scoreboard)
uvm_analysis_imp #(ahb_seq_item, bridge_scoreboard) ahb_export;
uvm_analysis_imp #(apb_seq_item, bridge_scoreboard) apb_export;
// Expected APB transactions (predicted from AHB)
apb_seq_item expected_queue[$];
int match_count = 0;
int mismatch_count = 0;
// Predict APB transaction from AHB
function void write_ahb(ahb_seq_item ahb_txn);
apb_seq_item predicted;
// For bursts, predict multiple APB transactions
int burst_len = get_burst_length(ahb_txn.hburst);
for (int i = 0; i < burst_len; i++) begin
predicted = apb_seq_item::type_id::create("predicted");
predicted.paddr = calculate_burst_addr(ahb_txn, i);
predicted.pwrite = ahb_txn.hwrite;
predicted.pwdata = ahb_txn.hwdata; // Simplified
expected_queue.push_back(predicted);
end
endfunction
// Compare actual APB transaction
function void write_apb(apb_seq_item apb_txn);
apb_seq_item expected;
if (expected_queue.size() == 0) begin
`uvm_error("SCB", "Unexpected APB transaction")
return;
end
expected = expected_queue.pop_front();
if (apb_txn.paddr !== expected.paddr ||
apb_txn.pwrite !== expected.pwrite) begin
`uvm_error("SCB", $sformatf(
"Mismatch! Expected addr=0x%h write=%b, Got addr=0x%h write=%b",
expected.paddr, expected.pwrite, apb_txn.paddr, apb_txn.pwrite))
mismatch_count++;
end else begin
`uvm_info("SCB", $sformatf("Match: addr=0x%h", apb_txn.paddr), UVM_HIGH)
match_count++;
end
endfunction
function void report_phase(uvm_phase phase);
`uvm_info("SCB", $sformatf(
"Scoreboard Summary: %0d matches, %0d mismatches",
match_count, mismatch_count), UVM_NONE)
endfunction
endclass
Functional Coverage
class bridge_coverage extends uvm_subscriber #(ahb_seq_item);
`uvm_component_utils(bridge_coverage)
ahb_seq_item txn;
covergroup ahb_cg;
option.per_instance = 1;
// Transfer type
cp_write: coverpoint txn.hwrite {
bins READ = {0};
bins WRITE = {1};
}
// Burst type
cp_burst: coverpoint txn.hburst {
bins SINGLE = {3'b000};
bins INCR = {3'b001};
bins WRAP4 = {3'b010};
bins INCR4 = {3'b011};
bins WRAP8 = {3'b100};
bins INCR8 = {3'b101};
bins WRAP16 = {3'b110};
bins INCR16 = {3'b111};
}
// Transfer size
cp_size: coverpoint txn.hsize {
bins BYTE = {3'b000};
bins HALFWORD = {3'b001};
bins WORD = {3'b010};
}
// Response
cp_resp: coverpoint txn.hresp {
bins OKAY = {0};
bins ERROR = {1};
}
// Cross coverage
cx_burst_write: cross cp_burst, cp_write;
cx_burst_size: cross cp_burst, cp_size;
cx_write_resp: cross cp_write, cp_resp;
endgroup
function new(string name, uvm_component parent);
super.new(name, parent);
ahb_cg = new();
endfunction
function void write(ahb_seq_item t);
txn = t;
ahb_cg.sample();
endfunction
endclass
Test Example
class burst_test extends base_test;
`uvm_component_utils(burst_test)
task run_phase(uvm_phase phase);
burst_sequence seq;
phase.raise_objection(this);
seq = burst_sequence::type_id::create("seq");
// Configure sequence
seq.num_transactions = 100;
seq.burst_types = {INCR4, INCR8, WRAP4, WRAP8};
// Run on virtual sequencer
seq.start(env.v_seqr);
// Drain time
#1000;
phase.drop_objection(this);
endtask
endclass
Common Bugs Found
Typical bugs discovered during AHB-APB bridge verification:
| Bug | Symptom | Test to Catch |
|---|---|---|
| Burst address miscalculation | Wrong peripheral accessed | WRAP burst crossing boundary |
| PREADY not sampled correctly | Data corruption | Random wait states |
| HREADY held too long | AHB bus stall | Back-to-back transfers |
| PSLVERR not propagated | Error lost | Error injection test |
| Write data latching | Wrong data written | Consecutive writes |
| Burst early termination | Incomplete transfer | Long burst with errors |
Assertions for Protocol Checking
module bridge_assertions (input clk, psel, penable, pready);
// APB Setup phase: PSEL rises, PENABLE must be low
property apb_setup;
@(posedge clk) $rose(psel) |-> !penable;
endproperty
assert property (apb_setup) else $error("APB setup violation");
// APB Access phase: PENABLE rises after PSEL
property apb_access;
@(posedge clk) (psel && !penable) |=> penable;
endproperty
assert property (apb_access) else $error("APB access violation");
// PSEL must stay high until PREADY
property psel_stable;
@(posedge clk) (psel && penable && !pready) |=> psel;
endproperty
assert property (psel_stable) else $error("PSEL deasserted early");
endmodule
Source Code
The complete SystemVerilog UVM environment with all components is available on GitHub:
View on GitHub: AHB-APB Bridge UVM Testbench
Comments (0)
Leave a Comment