AHB Lite protocol Verification

Hi,

I am trying understand the verification process of AHB lite protocol. I am trying build my resume to add a project to build a Full UVM testbench. I know the concept of AHB lite protocol I have gone through the whole spec twice.

But i want to understand the process of verifying it, so lets say when someone says verification of AHB lite protocol what should it have,

is it just read and write transactions between a single master and one or more slave?
or will there be any inclusions of BFM or should i verify a protocol in such a way that it acts as a bridge between two components and AHB lite protocol will act as a bus for communication between Component A and Component B.

Can someone please guide me bottom line i want to build a Full uvm testbench to verify AHB lite protocol. I want to know what to and how to do , what are all the components should i include in to verify it if necessary.

Thanks,
Teja

AHB - Lite is a single Master and Multi Slave protocol. To verify this protocol, follow below steps.

  1. Design a simple DUT which does memory read and write using AHB - Lite interface. Which means reads and writes to memory should obey AHB - Lite protocol.
  2. Write UVM TB which contains all necessary components required like tb_top, test, env,master_agent(drives the data on AHB - Lite interface) ref model, Scoreboard etc.
  3. Code the master agent which should support all burst features like INCRs, WRAPs.
  4. Write the assertions to check the timing relationship of the AHB - Lite protocol signals
  5. Write functional coverage model so as to check you have exercised all possible input combinations or not.

Hope this helps.

1 Like

sir
may you review my code is it in the right direction

// Code your design here
//
// AHB-Lite Memory Controller (DUT)

// Memory Size: 1KB (256 x 32-bit words)

module ahb_lite_memory #(
parameter ADDR_WIDTH = 32,
parameter DATA_WIDTH = 32,
parameter MEM_DEPTH  = 256  // 1KB / 4 bytes = 256 words
)(
// Global signals
input  logic                    HCLK,
input  logic                    HRESETn,

// AHB-Lite Master Interface
input  logic [ADDR_WIDTH-1:0]  HADDR,
input  logic [1:0]              HTRANS,
input  logic                    HWRITE,
input  logic [2:0]              HSIZE,
input  logic [2:0]              HBURST,
input  logic [DATA_WIDTH-1:0]  HWDATA,

// AHB-Lite Slave Interface
output logic [DATA_WIDTH-1:0]  HRDATA,
output logic                    HREADY,
output logic                    HRESP

);

// Local Parameters
// 

// HTRANS encoding
localparam [1:0] IDLE   = 2'b00;
localparam [1:0] BUSY   = 2'b01;
localparam [1:0] NONSEQ = 2'b10;
localparam [1:0] SEQ    = 2'b11;

// HSIZE encoding
localparam [2:0] SIZE_BYTE = 3'b000;  // 8-bit
localparam [2:0] SIZE_HALF = 3'b001;  // 16-bit
localparam [2:0] SIZE_WORD = 3'b010;  // 32-bit

// HRESP encoding
localparam RESP_OKAY  = 1'b0;
localparam RESP_ERROR = 1'b1;

// Memory address range
localparam [ADDR_WIDTH-1:0] MEM_BASE = 32'h0000_0000;
localparam [ADDR_WIDTH-1:0] MEM_TOP  = 32'h0000_03FF;  // 1KB


// Internal Signals


// Memory array
logic [DATA_WIDTH-1:0] memory [0:MEM_DEPTH-1];

// Pipeline registers for address phase
logic [ADDR_WIDTH-1:0] addr_reg;
logic [1:0]            trans_reg;
logic                  write_reg;
logic [2:0]            size_reg;

// Control signals
logic                  valid_addr;
logic                  addr_error;
logic [7:0]            word_addr;


// Address Phase - Capture control signals

always_ff @(posedge HCLK or negedge HRESETn) begin
    if (!HRESETn) begin
        addr_reg  <= '0;
        trans_reg <= IDLE;
        write_reg <= 1'b0;
        size_reg  <= SIZE_WORD;
    end else if (HREADY) begin
        addr_reg  <= HADDR;
        trans_reg <= HTRANS;
        write_reg <= HWRITE;
        size_reg  <= HSIZE;
    end
end


// Address Validation


assign valid_addr = (trans_reg == NONSEQ || trans_reg == SEQ);
assign addr_error = valid_addr && (addr_reg < MEM_BASE || addr_reg > MEM_TOP);
assign word_addr  = addr_reg[9:2];  // Convert byte address to word address


// Data Phase - Memory Read/Write Operations


always_ff @(posedge HCLK or negedge HRESETn) begin
    if (!HRESETn) begin
        for (int i = 0; i < MEM_DEPTH; i++) begin
            memory[i] <= '0;
        end
    end else if (HREADY && valid_addr && write_reg && !addr_error) begin
        // Write operation
        case (size_reg)
            SIZE_BYTE: begin
                // Byte write - use byte offset
                case (addr_reg[1:0])
                    2'b00: memory[word_addr][7:0]   <= HWDATA[7:0];
                    2'b01: memory[word_addr][15:8]  <= HWDATA[7:0];
                    2'b10: memory[word_addr][23:16] <= HWDATA[7:0];
                    2'b11: memory[word_addr][31:24] <= HWDATA[7:0];
                endcase
            end
            SIZE_HALF: begin
                // Halfword write - use halfword offset
                case (addr_reg[1])
                    1'b0: memory[word_addr][15:0]  <= HWDATA[15:0];
                    1'b1: memory[word_addr][31:16] <= HWDATA[15:0];
                endcase
            end
            SIZE_WORD: begin
                // Word write
                memory[word_addr] <= HWDATA;
            end
            default: memory[word_addr] <= HWDATA;
        endcase
    end
end


// Memory Read Operation


always_ff @(posedge HCLK or negedge HRESETn) begin
    if (!HRESETn) begin
        HRDATA <= '0;
    end else if (HREADY && valid_addr && !write_reg && !addr_error) begin
        // Read operation
        case (size_reg)
            SIZE_BYTE: begin
                // Byte read - replicate byte to all positions
                case (addr_reg[1:0])
                    2'b00: HRDATA <= {4{memory[word_addr][7:0]}};
                    2'b01: HRDATA <= {4{memory[word_addr][15:8]}};
                    2'b10: HRDATA <= {4{memory[word_addr][23:16]}};
                    2'b11: HRDATA <= {4{memory[word_addr][31:24]}};
                endcase
            end
            SIZE_HALF: begin
                // Halfword read - replicate halfword
                case (addr_reg[1])
                    1'b0: HRDATA <= {2{memory[word_addr][15:0]}};
                    1'b1: HRDATA <= {2{memory[word_addr][31:16]}};
                endcase
            end
            SIZE_WORD: begin
                // Word read
                HRDATA <= memory[word_addr];
            end
            default: HRDATA <= memory[word_addr];
        endcase
    end
end


// Response Generation
   always_ff @(posedge HCLK or negedge HRESETn) begin
    if (!HRESETn) begin
        HRESP <= RESP_OKAY;
    end else if (HREADY) begin
        HRESP <= addr_error ? RESP_ERROR : RESP_OKAY;
    end
end


// Ready Signal - Always ready in this simple implementation
// Can be modified to insert wait states


assign HREADY = 1'b1;


// Assertions for internal checking
// 

`ifdef SIMULATION

// Check for X or Z on critical signals
property no_x_on_addr;
    @(posedge HCLK) disable iff (!HRESETn)
    (HTRANS != IDLE) |-> !$isunknown(HADDR);
endproperty

property no_x_on_wdata;
    @(posedge HCLK) disable iff (!HRESETn)
    (write_reg && valid_addr) |-> !$isunknown(HWDATA);
endproperty

assert property (no_x_on_addr) else 
    $error("X or Z detected on HADDR during valid transfer");

assert property (no_x_on_wdata) else 
    $error("X or Z detected on HWDATA during write");

`endif

endmodule