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:

FeatureAHB (Source)APB (Destination)
PerformanceHigh-bandwidth, pipelinedLow-power, simple
Burst SupportYes (INCR, WRAP)No
Wait StatesHREADY signalPREADY signal
Data Width32/64/128-bit8/16/32-bit
Typical UseCPU, DMA, MemoryUART, 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:

  1. Protocol Compliance: Both AHB and APB protocols must be followed correctly
  2. Burst Handling: INCR, INCR4/8/16, WRAP4/8/16 bursts must decompose correctly
  3. Back-pressure: Bridge must handle slow APB peripherals (PREADY delays)
  4. Error Handling: PSLVERR must propagate to HRESP correctly
  5. Concurrent Access: Multiple masters accessing bridge (if arbitrated)
  6. Address Boundary: 1KB address boundary crossing in bursts

Verification Testplan

Functional Coverage Goals

CategoryFeaturesPriority
Transfer TypesREAD, WRITEP1
Burst TypesSINGLE, INCR, INCR4/8/16, WRAP4/8/16P1
Transfer SizesBYTE, HALFWORD, WORDP1
Wait States0, 1, 2, random delaysP1
Error ResponseOKAY, ERROR (PSLVERR)P1
Address RangesEach APB peripheral selectP2
Back-to-backConsecutive transfersP2
Boundary Cross1KB boundary in burstP2

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:

BugSymptomTest to Catch
Burst address miscalculationWrong peripheral accessedWRAP burst crossing boundary
PREADY not sampled correctlyData corruptionRandom wait states
HREADY held too longAHB bus stallBack-to-back transfers
PSLVERR not propagatedError lostError injection test
Write data latchingWrong data writtenConsecutive writes
Burst early terminationIncomplete transferLong 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

Related Topics

Author
Mayur Kubavat
VLSI Design and Verification Engineer sharing knowledge about SystemVerilog, UVM, and hardware verification methodologies.

Comments (0)

Leave a Comment