使用Verilog实现与仿真AMBA--AHB总线协议(三)
我的学记|刘航宇的博客

使用Verilog实现与仿真AMBA--AHB总线协议(三)

刘航宇
2年前发布 /正在检测是否收录...
温馨提示:
本文最后更新于2023年03月16日,已超过708天没有更新,若内容或图片失效,请留言反馈。

1、AHB从机
AHB从机应答来自系统主主机发起的传输。从机使用从译码器输出的 HSELx 信号来决定它什么时候作应答。其它传输需要的信号,如地址与控制信息由主机产生。

下图为一个AHB从机的接口框图:
图片[1] - 使用Verilog实现与仿真AMBA--AHB总线协议(三) - 我的学记|刘航宇的博客
下面是一个AHB接口的SRAM控制器的程序:

//------------------------------------------------------------------------------
// File                     : ahb_sram.v
// Editor                   : VSCode, Tab Size(4)
// Description              : Top Module of AHB SRAM Controller.
//
//------------------------------------------------------------------------------
`timescale 1ns / 1ps

module ahb_sram #( 
    parameter                       SYNC_RESET = 1,
    parameter                       AHB_DWIDTH = 32,
    parameter                       AHB_AWIDTH = 32,
    parameter                     SIZE_IN_BYTES = 2048,
    parameter                       ADD_WIDTH = $clog2(SIZE_IN_BYTES)   
)
(
    //----------------------------------
    // IO Declarations
    //----------------------------------
    // Inputs
    input                           HCLK,
    input                           HRESETN,
    input                           HSEL,
    input                           HREADYIN,
    input [1:0]                     HTRANS,
    input [2:0]                     HBURST,
    input [2:0]                     HSIZE,
    input [AHB_DWIDTH-1:0]          HWDATA,
    input [AHB_AWIDTH-1:0]          HADDR,
    input                           HWRITE,
    // Outputs
    output [AHB_DWIDTH-1:0]         HRDATA,
    output [1:0]                    HRESP,
    output                          HREADYOUT
);

    //----------------------------------
    // Variable Declarations
    //----------------------------------
    wire [ADD_WIDTH-1:0]            HADDR_cal;
    wire [2:0]                      ahbsram_size;
    wire [ADD_WIDTH-1:0]            ahbsram_addr;
    wire [31:0]                     ahbsram_wdata;
    wire                            ahbsram_write;
    wire [31:0]                     sramahb_rdata;
    wire                            sramahb_ack;

    //----------------------------------
    // Start of Main Code
    //----------------------------------
    assign HADDR_cal = HADDR[ADD_WIDTH-1:0];

    // Instantiations
    ahb_sram_if #(
        .AHB_DWIDTH                 (AHB_DWIDTH),
        .AHB_AWIDTH                 (AHB_AWIDTH),
        .ADD_WIDTH                  (ADD_WIDTH), 
        .SYNC_RESET                 (SYNC_RESET)
    )       
    u_ahb_sram_if (        
        .HCLK                       (HCLK),
        .HRESETN                    (HRESETN),
        .HSEL                       (HSEL),
        .HTRANS                     (HTRANS),
        .HBURST                     (HBURST),
        .HWRITE                     (HWRITE),
        .HWDATA                     (HWDATA),
        .HSIZE                      (HSIZE),
        .HADDR                      (HADDR_cal),
        .HREADYIN                   (HREADYIN),
        // From SRAM Control signals
        .sramahb_ack                (sramahb_ack),
        .sramahb_rdata              (sramahb_rdata),
        // Outputs      
        .HREADYOUT                  (HREADYOUT),
        .HRESP                      (HRESP),
        // To SRAM Control signals
        .ahbsram_req                (ahbsram_req),
        .ahbsram_write              (ahbsram_write),
        .ahbsram_wdata              (ahbsram_wdata),
        .ahbsram_size               (ahbsram_size),
        .ahbsram_addr               (ahbsram_addr),
        .HRDATA                     (HRDATA)
    );

    sram_ctrl_if #(
        .ADD_WIDTH                  (ADD_WIDTH),
        .SYNC_RESET                 (SYNC_RESET)
    )       
    u_sram_ctrl_if (     
        .HCLK                       (HCLK),
        .HRESETN                    (HRESETN),
        // From AHB Interface signals
        .ahbsram_req                (ahbsram_req),
        .ahbsram_write              (ahbsram_write),
        .ahbsram_wdata              (ahbsram_wdata),
        .ahbsram_size               (ahbsram_size),
        .ahbsram_addr               (ahbsram_addr),
        // Outputs
        // To AHB Interface signals
        .sramahb_ack                (sramahb_ack),
        .sramahb_rdata              (sramahb_rdata)
    );

endmodule

//------------------------------------------------------------------------------
// File                     : ahb_sram_if.v
// Editor                   : VSCode, Tab Size(4)
// Description              :
//
//------------------------------------------------------------------------------
`timescale 1ns / 1ps

module ahb_sram_if #( 
    parameter                       AHB_DWIDTH = 32,
    parameter                       AHB_AWIDTH = 32,
    parameter                       ADD_WIDTH = 11,
    parameter                       SYNC_RESET = 0
)
(
    //----------------------------------
    // IO Declarations
    //----------------------------------
    // Inputs
    input                           HCLK,
    input                           HRESETN,
    input                           HSEL,
    input                           HREADYIN,
    input [1:0]                     HTRANS,
    input [2:0]                     HBURST,
    input [2:0]                     HSIZE,
    input [ADD_WIDTH-1:0]           HADDR,
    input [AHB_DWIDTH-1:0]          HWDATA,
    input                           HWRITE,
    input                           sramahb_ack,
    input [AHB_DWIDTH-1:0]          sramahb_rdata,
    // Outputs
    output                          HREADYOUT,
    output [1:0]                    HRESP,
    output reg [AHB_DWIDTH-1:0]     HRDATA,
    
    output                          ahbsram_req,
    output                          ahbsram_write,
    output [AHB_AWIDTH-1:0]         ahbsram_wdata,
    
    output [2:0]                    ahbsram_size,
    output [ADD_WIDTH-1:0]          ahbsram_addr
);

    //----------------------------------
    // Local Parameter Declarations
    //----------------------------------
    // State Machine parameters
    localparam                      IDLE = 2'b00;
    localparam                      AHB_WR = 2'b01;
    localparam                      AHB_RD = 2'b10;

    parameter                       RESP_OKAY = 2'b00;
    parameter                       RESP_ERROR = 2'b01;

    // AHB HTRANS definition
    parameter                       TRN_IDLE = 2'b00;
    parameter                       TRN_BUSY = 2'b01;
    parameter                       TRN_SEQ = 2'b11;
    parameter                       TRN_NONSEQ = 2'b10;

    parameter                       SINGLE = 3'b000;
    parameter                       INCR = 3'b001;
    parameter                       WRAP4 = 3'b010;
    parameter                       INCR4 = 3'b011;
    parameter                       WRAP8 = 3'b100;
    parameter                       INCR8 = 3'b101;
    parameter                       WRAP16 = 3'b110;
    parameter                       INCR16 = 3'b111;

    //----------------------------------
    // Variable Declarations
    //----------------------------------
    reg [1:0]                       HTRANS_d;
    reg [2:0]                       HBURST_d;
    reg [2:0]                       HSIZE_d;
    reg [ADD_WIDTH-1:0]             HADDR_d;
    reg [AHB_DWIDTH-1:0]            HWDATA_d;
    reg                             HWRITE_d;
    reg                             HSEL_d;
    reg                             HREADYIN_d;
    reg [1:0]                       ahbcurr_state;
    reg [1:0]                       ahbnext_state;

    reg                             latchahbcmd;
    reg                             ahbsram_req_int;
    reg                             ahbsram_req_d1;   
    reg [AHB_DWIDTH-1:0]            HWDATA_cal;

    reg [4:0]                       burst_count;
    reg [4:0]                       burst_count_reg;
    reg [4:0]                       count;

    wire                            aresetn;
    wire                            sresetn;

    //----------------------------------
    // Start of Main Code
    //----------------------------------
    assign aresetn = (SYNC_RESET==1) ? 1'b1 : HRESETN;
    assign sresetn = (SYNC_RESET==1) ? HRESETN : 1'b1;

    // Generation of valid AHB Command which triggers the AHB Slave State Machine
    assign validahbcmd = HREADYIN & HSEL & (HTRANS == TRN_NONSEQ);

    // Generation of HRESP
    assign HRESP = RESP_OKAY;

    always @(*) 
    begin
        HWDATA_cal = HWDATA;
    end
    
    // Latch all the AHB signals
    always @(posedge HCLK or negedge aresetn) 
    begin
        if ((aresetn == 1'b0) || (sresetn == 1'b0)) begin
            HADDR_d    <= {20{1'b0}};
            HWDATA_d   <= {32{1'b0}};
            HTRANS_d   <= 2'b00;
            HSIZE_d    <= 2'b00;
            HBURST_d   <= 3'b000;
            HWRITE_d   <= 1'b0;
            HSEL_d     <= 1'b0;
            HREADYIN_d <= 1'b0;
        end
        else if (HREADYIN == 1'b1 & HSEL == 1'b1 & HREADYOUT == 1'b1) begin
            HADDR_d    <= HADDR;
            HTRANS_d   <= HTRANS;
            HSIZE_d    <= HSIZE;
            HBURST_d   <= HBURST;
            HWRITE_d   <= HWRITE;
            HWDATA_d   <= HWDATA_cal;         
            HSEL_d     <= HSEL;
            HREADYIN_d <= HREADYIN;
        end
    end
    
    // Current State generation
    always @(posedge HCLK or negedge aresetn) 
    begin
        if ((aresetn == 1'b0) || (sresetn == 1'b0)) begin
            ahbcurr_state <= IDLE;
        end
        else begin
            ahbcurr_state <= ahbnext_state;
        end
    end
    
    // Next State and output decoder logic
    always @(*) 
    begin
        latchahbcmd = 1'b0;
        ahbsram_req_int = 1'b0;
        ahbnext_state = ahbcurr_state;
        
        case (ahbcurr_state)
            IDLE : begin
                if (HREADYIN == 1'b1 && HSEL == 1'b1 && ((HTRANS == TRN_NONSEQ) || HTRANS == TRN_SEQ)) begin
                    latchahbcmd = 1'b1;
                    if (HWRITE == 1'b1) begin
                        ahbnext_state = AHB_WR;           
                    end 
                    else begin
                        ahbnext_state = AHB_RD;           
                    end
                end 
                else begin
                    ahbnext_state = IDLE;           
                end
            end

            AHB_WR : begin
                latchahbcmd = 1'b0;
                ahbsram_req_int = 1'b1;
                
                if (sramahb_ack == 1'b1) begin
                    if (count == burst_count_reg) begin
                        ahbnext_state = IDLE;
                    end 
                    else begin
                        ahbsram_req_int = 1'b0;
                    end
                end

            end
            
            AHB_RD : begin
                latchahbcmd = 1'b0;
                ahbsram_req_int = 1'b1;
                if (sramahb_ack == 1'b1) begin
                    ahbnext_state = IDLE;
                end
            end

            default : begin
                ahbnext_state = IDLE;
            end
        endcase  
    end

    // LOGIC FOR BURST COUNT
    always @(*) 
    begin
        burst_count = burst_count_reg;
        if (HSEL == 1'b1  && HTRANS == TRN_NONSEQ && HREADYIN == 1'b1 && HREADYOUT == 1'b1) begin
            case (HBURST)
                SINGLE : 
                    burst_count = 5'b00001;
                WRAP4,INCR4 : 
                    burst_count = 5'b00100;
                WRAP8,INCR8 : 
                    burst_count = 5'b01000;
                WRAP16,INCR16 : 
                    burst_count = 5'b10000;
                default : 
                    burst_count = 4'b0001;
            endcase
        end
    end

    always @(posedge HCLK or negedge aresetn) 
    begin
        if ((aresetn == 1'b0) || (sresetn == 1'b0)) begin
            burst_count_reg <= 'h0;
        end 
        else begin
            burst_count_reg <= burst_count;
        end
    end

    always @(posedge HCLK or negedge aresetn) 
    begin
        if ((aresetn == 1'b0) || (sresetn == 1'b0)) begin
            count <= 5'h0;
        end 
        else begin
            if (count == burst_count_reg) begin
                count <= 5'h0;
            end 
            else if (ahbsram_req == 1'b1) begin
                count <= count + 1'b1;
            end 
            else begin
                count <= count;
            end
        end
    end

    assign HREADYOUT = !ahbsram_req_int;
    
    // Generation of signals required for SRAM
    assign ahbsram_write = ahbsram_req ? HWRITE_d : 1'b0;   
    assign ahbsram_wdata = HWDATA;     
    assign ahbsram_addr = ahbsram_req ? HADDR_d : HADDR_d;     
    assign ahbsram_size = ahbsram_req ? HSIZE_d : HSIZE_d;  
 
    always @(posedge HCLK or negedge aresetn) 
    begin
        if ((aresetn == 1'b0) || (sresetn == 1'b0)) begin
            ahbsram_req_d1 <= 1'b0;
        end
        else begin
            ahbsram_req_d1 <= ahbsram_req_int;
        end
    end

    // Generate the request to the SRAM contol logic when there is AHB read or write request
    assign ahbsram_req = ahbsram_req_int & !ahbsram_req_d1; 

    // HRDATA generation   
    always @(*) 
    begin
        if (HREADYOUT && HREADYIN) begin
            HRDATA = sramahb_rdata;
        end  
        else begin
            HRDATA = sramahb_rdata;
        end
    end

endmodule

//------------------------------------------------------------------------------
// File                     : sram_ctrl_if.v
// Editor                   : VSCode, Tab Size(4)
// Description              :
//
//------------------------------------------------------------------------------
`timescale 1ns / 1ps

module sram_ctrl_if #( 
    parameter                       AHB_DWIDTH = 32,
    parameter                       ADD_WIDTH = 11,
    parameter                       SYNC_RESET = 0
)
(
    //----------------------------------
    // IO Declarations
    //----------------------------------
    // Inputs
    input                           HCLK,
    input                           HRESETN,

    input                           ahbsram_req,
    input                           ahbsram_write,
    input [2:0]                     ahbsram_size,
    input [ADD_WIDTH-1:0]           ahbsram_addr,
    input [AHB_DWIDTH-1:0]          ahbsram_wdata,
    
    // Outputs
    output                          sramahb_ack,
    output reg [AHB_DWIDTH-1: 0]    sramahb_rdata
);

    //----------------------------------
    // Local Parameter Declarations
    //----------------------------------
    // State Machine parameters
    localparam                      S_IDLE = 2'b00;
    localparam                      S_WR = 2'b01;
    localparam                      S_RD = 2'b10;

    //----------------------------------
    // Variable Declarations
    //----------------------------------
    reg [3:0]                       sram_wen_mem;
    reg [1:0]                       sramcurr_state;
    reg [1:0]                       sramnext_state;
    reg                             sram_wen;
    reg                             sram_ren;
    reg                             sramahb_ack_int;
    reg                             sram_ren_d;
    reg                             sram_done;   
    wire [AHB_DWIDTH-1:0]           ram_rdata;

    wire                            aresetn;
    wire                            sresetn; 

    //----------------------------------
    // Start of Main Code
    //----------------------------------
    assign aresetn = (SYNC_RESET == 1) ? 1'b1 : HRESETN;
    assign sresetn = (SYNC_RESET == 1) ? HRESETN : 1'b1;

    // Current State generation
    always @(posedge HCLK or negedge aresetn) 
    begin
        if ((aresetn == 1'b0) || (sresetn == 1'b0)) begin
            sramcurr_state <= S_IDLE;
        end
        else begin
            sramcurr_state <= sramnext_state;
        end
    end
    
    // Next State and output decoder logic
    always @(*) 
    begin
        sramahb_ack_int = 1'b0;
        sram_wen = 1'b0;
        sram_ren = 1'b0;
        sramnext_state = sramcurr_state;
        case (sramcurr_state)
            S_IDLE : begin
                if (ahbsram_req == 1'b1) begin
                    if (ahbsram_write == 1'b1) begin
                        sramnext_state = S_WR;           
                        sram_wen = 1'b1;
                    end
                    else begin
                        sram_ren = 1'b1;
                        sramnext_state = S_RD;           
                    end
                end
            end

            S_WR : begin
                if (sram_done == 1'b1) begin
                    sramnext_state = S_IDLE;
                    sramahb_ack_int = 1'b1;
                end
            end
            
            S_RD : begin
                if (sram_done == 1'b1) begin
                    sramnext_state = S_IDLE;
                    sramahb_ack_int = 1'b1;
                end
            end

            default : begin
                sramnext_state = S_IDLE;
            end
        endcase     
    end
    
    always @(*) 
    begin
        sram_wen_mem = 4'b0000;
        if (ahbsram_size == 3'b010) begin
            sram_wen_mem = {4{sram_wen}};
        end
        else if (ahbsram_size == 3'b001) begin
            case (ahbsram_addr[1])
                1'b0 : begin
                    sram_wen_mem[0] = sram_wen;
                    sram_wen_mem[1] = sram_wen;
                    sram_wen_mem[2] = 1'b0;
                    sram_wen_mem[3] = 1'b0;
                end
                1'b1 : begin
                    sram_wen_mem[0] = 1'b0;
                    sram_wen_mem[1] = 1'b0;
                    sram_wen_mem[2] = sram_wen;
                    sram_wen_mem[3] = sram_wen;
                end
            endcase      
        end     
        else if (ahbsram_size == 3'b000) begin
            case (ahbsram_addr[1:0])
                2'b00 : begin
                    sram_wen_mem[0] = sram_wen;
                    sram_wen_mem[1] = 1'b0;
                    sram_wen_mem[2] = 1'b0;
                    sram_wen_mem[3] = 1'b0;
                end
                2'b01 : begin
                    sram_wen_mem[0] = 1'b0;
                    sram_wen_mem[1] = sram_wen;
                    sram_wen_mem[2] = 1'b0;
                    sram_wen_mem[3] = 1'b0;
                end
                2'b10 : begin
                    sram_wen_mem[0] = 1'b0;
                    sram_wen_mem[1] = 1'b0;
                    sram_wen_mem[2] = sram_wen;
                    sram_wen_mem[3] = 1'b0;
                end
                2'b11 : begin
                    sram_wen_mem[0] = 1'b0;
                    sram_wen_mem[1] = 1'b0;
                    sram_wen_mem[2] = 1'b0;
                    sram_wen_mem[3] = sram_wen;
                end
            endcase       
        end
        else begin
            sram_wen_mem = {4{sram_wen}};
        end             
    end

    // SRAM Instantiations
    sram_model #(
        .SYNC_RESET                 (SYNC_RESET),
        .ADDR_WIDTH                 (ADD_WIDTH)
    )
    u_sram_model (
        .writedata                  (ahbsram_wdata),
        .readdata                   (ram_rdata[31:0]),
        .wren                       (sram_wen_mem),
        .rden                       (sram_ren),
        .writeaddr                  (ahbsram_addr[ADD_WIDTH-1:2]),
        .readaddr                   (ahbsram_addr[ADD_WIDTH-1:2]),
        .clk                        (HCLK),
        .resetn                     (HRESETN)
    );

    always @(posedge HCLK or negedge aresetn) 
    begin
        if ((aresetn == 1'b0) || (sresetn == 1'b0)) begin
            sramahb_rdata <= 32'h0;
        end
        else if (sram_ren_d == 1'b1) begin
            sramahb_rdata <= ram_rdata;
        end
        else begin
            sramahb_rdata <= sramahb_rdata;
        end
    end

    always @(posedge HCLK or negedge aresetn) 
    begin
        if ((aresetn == 1'b0) || (sresetn == 1'b0)) begin
            sram_ren_d <= 32'h0;
        end
        else begin
            sram_ren_d <= sram_ren;
        end
    end

    // Generate the SRAM done when the SRAM wren/rden is done
    always @(posedge HCLK or negedge aresetn) 
    begin
        if ((aresetn == 1'b0) || (sresetn == 1'b0)) begin
            sram_done <= 1'b0;
        end
        else if (sram_wen || sram_ren) begin
            sram_done <= 1'b1;
        end
        else begin
            sram_done <= 1'b0;
        end
    end

    // Generate the SRAM ack 
    assign sramahb_ack = sramahb_ack_int;

endmodule

//------------------------------------------------------------------------------
// File                     : sram_model.v
// Editor                   : GVIM, Tab Size(4)
// Description              : FPGA Block Ram/Onchip SRAM.
//  
//------------------------------------------------------------------------------
`timescale 1ns / 1ps

module sram_model #(
    parameter                   SYNC_RESET = 0,
    parameter                   ADDR_WIDTH = 16
)
(
    //----------------------------------
    // IO Declarations
    //----------------------------------
    input [31:0]                writedata,
    output [31:0]               readdata,
    input [3:0]                 wren,
    input                       rden,
    input [ADDR_WIDTH-1:2]      writeaddr,
    input [ADDR_WIDTH-1:2]      readaddr,
    input                       clk,
    input                       resetn
);

    //----------------------------------
    //--Local Parameter Declarations
    //----------------------------------
    localparam                  AWT = ((1<<(ADDR_WIDTH-2))-1);

    //----------------------------------
    // Variable Declarations
    //----------------------------------
    reg [7:0]                   bram0[AWT:0];
    reg [7:0]                   bram1[AWT:0];
    reg [7:0]                   bram2[AWT:0];
    reg [7:0]                   bram3[AWT:0];

    reg [ADDR_WIDTH-3:0]        addr_q1;
    reg                         rden_r = 1'b0;
    wire [31:0]                 readdata_i;

    wire                        aresetn;
    wire                        sresetn;

    //----------------------------------
    // Start of Main Code
    //----------------------------------
    assign aresetn = (SYNC_RESET == 1) ? 1'b1 : resetn;
    assign sresetn = (SYNC_RESET == 1) ? resetn : 1'b1;

    always @(posedge clk)
    begin
        rden_r <= rden;
    end

    // Infer Block RAM 
    always @(posedge clk)
    begin
        if (wren[0])
            bram0[writeaddr] <= writedata[7:0];
        if (wren[1])
            bram1[writeaddr] <= writedata[15:8];
        if (wren[2])
            bram2[writeaddr] <= writedata[23:16];
        if (wren[3])
            bram3[writeaddr] <= writedata[31:24];
    end

    always @(posedge clk)
    begin
        addr_q1 <= readaddr[ADDR_WIDTH-1:2];
    end 

    assign readdata_i = {bram3[addr_q1],bram2[addr_q1],bram1[addr_q1],bram0[addr_q1]};

    assign readdata = rden_r ? readdata_i : {32{1'b0}};

endmodule

下面是测试用例代码:

//------------------------------------------------------------------------------
// File                     : top_tb.v
// Editor                   : VSCode, Tab Size(4)
// Description              : Testbench of AHB SRAM Controller.
//
//------------------------------------------------------------------------------
`timescale 1ns / 1ps

module top_tb();
    //----------------------------------
    // Local Parameter Declarations
    //----------------------------------
    localparam                      AHB_CLK_PERIOD = 5; // Assuming AHB CLK to be 100MHz

    localparam                      SIZE_IN_BYTES =2048;

    //----------------------------------
    // Variable Declarations
    //----------------------------------
    reg                             HCLK = 0;
    wire                            HWRITE;
    wire [1:0]                      HTRANS;
    wire [2:0]                      HSIZE;
    wire [2:0]                      HBURST;
    wire                            HREADYIN;
    wire [31:0]                     HADDR;
    wire [31:0]                     HWDATA;
    wire                            HREADYOUT;
    wire [1:0]                      HRESP;
    wire [31:0]                     HRDATA;
    reg                             HRESETn;

    //----------------------------------
    // Start of Main Code
    //----------------------------------

    //-----------------------------------------------------------------------
    // Generate HCLK
    //-----------------------------------------------------------------------
    always #AHB_CLK_PERIOD
        HCLK <= ~HCLK;
    
    //-----------------------------------------------------------------------
    // Generate HRESETn
    //-----------------------------------------------------------------------    
    initial begin
        HRESETn = 1'b0;
        repeat(5) @(posedge HCLK);
        HRESETn = 1'b1;
    end

    ahb_master #(
        .START_ADDR                 (32'h0),
        .DEPTH_IN_BYTES             (SIZE_IN_BYTES)
    )       
    u_ahb_master (     
        .HRESETn                    (HRESETn),
        .HCLK                       (HCLK),
        .HADDR                      (HADDR),
        .HTRANS                     (HTRANS),
        .HWRITE                     (HWRITE),
        .HSIZE                      (HSIZE),
        .HBURST                     (HBURST),
        .HWDATA                     (HWDATA),
        .HRDATA                     (HRDATA),
        .HRESP                      (HRESP),
        .HREADY                     (HREADYOUT)
    );

    ahb_sram # (
        .AHB_AWIDTH                 (32),
        .AHB_DWIDTH                 (32),
        .SIZE_IN_BYTES              (SIZE_IN_BYTES),
        .SYNC_RESET                 (1)
    ) 
    u_ahb_sram (
        .HCLK                       (HCLK),    
        .HRESETN                    (HRESETn),
        .HSEL                       (1'b1),
        .HWRITE                     (HWRITE),
        .HADDR                      (HADDR),
        .HWDATA                     (HWDATA),
        .HRDATA                     (HRDATA),
        .HSIZE                      (HSIZE),
        .HTRANS                     (HTRANS),
        .HBURST                     (HBURST),
        .HRESP                      (HRESP),
        .HREADYIN                   (1'b1),
        .HREADYOUT                  (HREADYOUT)
    );

`ifdef VCS
    initial begin
        $fsdbDumpfile("top_tb.fsdb");
        $fsdbDumpvars;
    end

    initial begin
    `ifdef DUMP_VPD
        $vcdpluson();
    `endif
    end
`endif

endmodule

这个模块主要是产生了时钟和复位信号,例化了待测模块,以及一个AHB Master的功能模型。具体见下面的代码:

//------------------------------------------------------------------------------
// File                     : ahb_master.v
// Editor                   : VSCode, Tab Size(4)
// Description              : AHB Master Simulation Model.
//
//------------------------------------------------------------------------------
`timescale 1ns / 1ps

`define SINGLE_TEST
`define BURST_TEST

module ahb_master #( 
    //----------------------------------
    // Paramter Declarations
    //----------------------------------
    parameter                           START_ADDR = 0,
    parameter                           DEPTH_IN_BYTES = 32'h100,
    parameter                           END_ADDR = START_ADDR+DEPTH_IN_BYTES-1
)
(
    //----------------------------------
    // IO Declarations
    //----------------------------------
    input wire                          HRESETn,        
    input wire                          HCLK,
    output reg [31:0]                   HADDR,
    output reg [1:0]                    HTRANS,
    output reg                          HWRITE,
    output reg [2:0]                    HSIZE,
    output reg [2:0]                    HBURST,
    output reg [31:0]                   HWDATA,
    input wire [31:0]                   HRDATA,
    input wire [1:0]                    HRESP,
    input wire                          HREADY
);

    //----------------------------------
    // Variable Declarations
    //----------------------------------
    reg [31:0]                          data_burst[0:1023];

    //----------------------------------
    // Start of Main Code
    //----------------------------------
    initial begin
        HADDR = 0;
        HTRANS = 0;
        HWRITE = 0;
        HSIZE = 0;
        HBURST = 0;
        HWDATA = 0;
        while(HRESETn === 1'bx) @(posedge HCLK);
        while(HRESETn === 1'b1) @(posedge HCLK);
        while(HRESETn === 1'b0) @(posedge HCLK);
    `ifdef SINGLE_TEST
        repeat(3) @(posedge HCLK);
        memory_test(START_ADDR, END_ADDR, 1);
        memory_test(START_ADDR, END_ADDR, 2);
        memory_test(START_ADDR, END_ADDR, 4);
    `endif
    `ifdef BURST_TEST
        repeat(5) @(posedge HCLK);
        memory_test_burst(START_ADDR, END_ADDR, 1);
        memory_test_burst(START_ADDR, END_ADDR, 2);
        memory_test_burst(START_ADDR, END_ADDR, 4);
        memory_test_burst(START_ADDR, END_ADDR, 6);
        memory_test_burst(START_ADDR, END_ADDR, 8);
        memory_test_burst(START_ADDR, END_ADDR, 10);
        memory_test_burst(START_ADDR, END_ADDR, 16);
        memory_test_burst(START_ADDR, END_ADDR, 32);
        memory_test_burst(START_ADDR, END_ADDR, 64);
        memory_test_burst(START_ADDR, END_ADDR, 128);
        memory_test_burst(START_ADDR, END_ADDR, 255);
        repeat(5) @(posedge HCLK);
    `endif
        $finish(2);
    end
    
    //-----------------------------------------------------------------------
    // Single transfer test 
    //-----------------------------------------------------------------------
    task memory_test;
        input [31:0]                start; // start address
        input [31:0]                finish; // end address
        input [2:0]                 size; // data size: 1, 2, 4
        
        integer                     i; 
        integer                     error;
        reg [31:0]                  data; 
        reg [31:0]                  gen; 
        reg [31:0]                  got;
        reg [31:0]                  reposit[START_ADDR:END_ADDR];
    begin
        $display("%m: read-after-write test with %d-byte access", size);
        error = 0;
        gen = $random(7);
        for (i = start; i < (finish-size+1); i = i + size) begin
            gen = $random & ~32'b0;
            data = align(i, gen, size);
            ahb_write(i, size, data);
            ahb_read(i, size, got);
            got = align(i, got, size);
            if (got !== data) begin
                $display("[%10d] %m A:%x D:%x, but %x expected", $time, i, got, data);
                error = error + 1;
            end
        end
        if (error == 0)
            $display("[%10d] %m OK: from %x to %x", $time, start, finish);

        $display("%m read-all-after-write-all with %d-byte access", size);
        error = 0;
        gen = $random(1);
        for (i = start; i < (finish-size+1); i = i + size) begin
            gen = {$random} & ~32'b0;
            data = align(i, gen, size);
            reposit[i] = data;
            ahb_write(i, size, data);
        end
        for (i = start; i < (finish-size+1); i = i + size) begin
            data = reposit[i];
            ahb_read(i, size, got);
            got = align(i, got, size);
            if (got !== data) begin
                $display("[%10d] %m A:%x D:%x, but %x expected", $time, i, got, data);
                error = error + 1;
            end
        end
        if (error == 0)
            $display("[%10d] %m OK: from %x to %x", $time, start, finish);
    end
    endtask
    
    //-----------------------------------------------------------------------
    // Burst transfer test 
    //-----------------------------------------------------------------------
    task memory_test_burst;
        input [31:0]                start; // start address
        input [31:0]                finish; // end address
        input [7:0]                 leng; // burst length
        
        integer                     i; 
        integer                     j; 
        integer                     k; 
        integer                     r; 
        integer                     error;
        reg [31:0]                  data; 
        reg [31:0]                  gen; 
        reg [31:0]                  got;
        reg [31:0]                  reposit[0:1023];
        integer                     seed;
    begin
        $display("%m: read-all-after-write-all burst test with %d-beat access", leng);
        error = 0;
        seed  = 111;
        gen = $random(seed);
        k = 0;
        if (finish > (start+leng*4)) begin
            for (i = start; i < (finish-(leng*4)+1); i = i + leng*4) begin
                for (j = 0; j < leng; j = j + 1) begin
                    data_burst[j] = $random;
                    reposit[j+k*leng] = data_burst[j];
                end
                @(posedge HCLK);
                ahb_write_burst(i, leng);
                k = k + 1;
            end
            gen = $random(seed);
            k = 0;
            for (i = start; i < (finish-(leng*4)+1); i = i + leng*4) begin
                @(posedge HCLK);
                ahb_read_burst(i, leng);
                for (j = 0; j < leng; j = j + 1) begin
                    if (data_burst[j] != reposit[j+k*leng]) begin
                        error = error+1;
                        $display("%m A=%hh D=%hh, but %hh expected",i+j*leng, data_burst[j], reposit[j+k*leng]);
                    end
                end
                k = k + 1;
                r = $random & 8'h0F;
                repeat(r) @(posedge HCLK);
            end
            if (error == 0)
                $display("%m %d-length burst read-after-write OK: from %hh to %hh",leng, start, finish);
        end 
        else begin
            $display("%m %d-length burst read-after-write from %hh to %hh ???",leng, start, finish);
        end
    end
    endtask
    
    //-----------------------------------------------------------------------
    // As AMBA AHB bus uses non-justified data bus scheme, data should be
    // aligned according to the address.
    //-----------------------------------------------------------------------
    function [31:0] align;
        input [ 1:0]                addr;
        input [31:0]                data;
        input [ 2:0]                size; // num of bytes
    begin
    `ifdef BIG_ENDIAN
        case (size)
            1 : 
                case (addr[1:0])
                    0 : align = data & 32'hFF00_0000;
                    1 : align = data & 32'h00FF_0000;
                    2 : align = data & 32'h0000_FF00;
                    3 : align = data & 32'h0000_00FF;
                endcase                
                                       
            2 :                        
                case (addr[1])         
                    0 : align = data & 32'hFFFF_0000;
                    1 : align = data & 32'h0000_FFFF;
                endcase
                
            4 : 
                align = data&32'hFFFF_FFFF;
            default : 
                $display($time,,"%m ERROR %d-byte not supported for size", size);
        endcase
    `else
        case (size)
            1 : 
                case (addr[1:0])
                    0 : align = data & 32'h0000_00FF;
                    1 : align = data & 32'h0000_FF00;
                    2 : align = data & 32'h00FF_0000;
                    3 : align = data & 32'hFF00_0000;
                endcase
                
            2 : 
                case (addr[1])
                    0 : align = data & 32'h0000_FFFF;
                    1 : align = data & 32'hFFFF_0000;
                endcase
                
            4 : 
                align = data&32'hFFFF_FFFF;
            default : 
                $display($time,,"%m ERROR %d-byte not supported for size", size);
        endcase
    `endif
    end
    endfunction

    `include "ahb_transaction_tasks.v"

endmodule

这个模块主要是实现了两个testcase,一个是测试single传输,另一个测试burst传输。AHB读写传输任务被封装在ahb_transaction_tasks.v文件中:

//------------------------------------------------------------------------------
// File                     : ahb_transaction_tasks.v
// Editor                   : VSCode, Tab Size(4)
// Description              : AHB Transaction Tasks.
//
//------------------------------------------------------------------------------
`ifndef __AHB_TRANSACTION_TASKS_V__
`define __AHB_TRANSACTION_TASKS_V__

//-----------------------------------------------------------------------
// AHB Read Task
//-----------------------------------------------------------------------
task ahb_read;
    input [31:0]                address;
    input [2:0]                 size;
    output [31:0]               data;
begin
    @(posedge HCLK);
    HADDR <= #1 address;
    HTRANS <= #1 2'b10; // NONSEQ;
    HBURST <= #1 3'b000; // SINGLE;
    HWRITE <= #1 1'b0; // READ;
    case (size)
        1 : HSIZE <= #1 3'b000; // BYTE;
        2 : HSIZE <= #1 3'b001; // HWORD;
        4 : HSIZE <= #1 3'b010; // WORD;
        default : 
            $display($time,, "ERROR: unsupported transfer size: %d-byte", size);
    endcase
    
    @(posedge HCLK);
    while (HREADY !== 1'b1) @(posedge HCLK);
    HTRANS <= #1 2'b0; // IDLE
    @(posedge HCLK);
    while (HREADY === 0) @(posedge HCLK);
    data = HRDATA; // must be blocking
    if (HRESP != 2'b00) 
        $display($time,, "ERROR: non OK response for read");
    @(posedge HCLK);
end
endtask

//-----------------------------------------------------------------------
// AHB Write Task
//-----------------------------------------------------------------------
task ahb_write;
    input [31:0]                address;
    input [2:0]                 size;
    input [31:0]                data;
begin
    @(posedge HCLK);
    HADDR <= #1 address;
    HTRANS <= #1 2'b10; // NONSEQ
    HBURST <= #1 3'b000; // SINGLE
    HWRITE <= #1 1'b1; // WRITE
    case (size)
        1 : HSIZE <= #1 3'b000; // BYTE
        2 : HSIZE <= #1 3'b001; // HWORD
        4 : HSIZE <= #1 3'b010; // WORD
        default : 
            $display($time,, "ERROR: unsupported transfer size: %d-byte", size);
    endcase
    
    @(posedge HCLK);
    while (HREADY !== 1) @(posedge HCLK);
    HWDATA <= #1 data;
    HTRANS <= #1 2'b0; // IDLE
    @(posedge HCLK);
    while (HREADY === 0) @(posedge HCLK);
    if (HRESP != 2'b00) 
        $display($time,, "ERROR: non OK response write");
    @(posedge HCLK);
end
endtask

//-----------------------------------------------------------------------
// AHB Read Burst Task
//-----------------------------------------------------------------------
task ahb_read_burst;
    input [31:0]                addr;
    input [31:0]                leng;
    
    integer                     i; 
    integer                     ln; 
    integer                     k;
begin
    k = 0;
    @(posedge HCLK);
    HADDR <= #1 addr; 
    addr = addr + 4;
    HTRANS <= #1 2'b10; // NONSEQ
    if (leng >= 16) begin 
        HBURST <= #1 3'b111; // INCR16
        ln = 16; 
    end
    else if (leng >= 8) begin 
        HBURST <= #1 3'b101; // INCR8
        ln = 8; 
    end
    else if (leng >= 4) begin 
        HBURST <= #1 3'b011; // INCR4
        ln = 4; 
    end 
    else begin 
        HBURST <= #1 3'b001; // INCR
        ln = leng; 
    end 
    HWRITE <= #1 1'b0; // READ
    HSIZE <= #1 3'b010; // WORD
    @(posedge HCLK);
    while (HREADY == 1'b0) @(posedge HCLK);
    while (leng > 0) begin
        for (i = 0; i < ln-1; i = i + 1) begin
            HADDR <= #1 addr; 
            addr = addr + 4;
            HTRANS <= #1 2'b11; // SEQ;
            @(posedge HCLK);
            while (HREADY == 1'b0) @(posedge HCLK);
            data_burst[k%1024] <= HRDATA;
            k = k + 1;
        end
        leng = leng - ln;
        if (leng == 0) begin
            HADDR <= #1 0;
            HTRANS <= #1 0;
            HBURST <= #1 0;
            HWRITE <= #1 0;
            HSIZE <= #1 0;
        end 
        else begin
            HADDR <= #1 addr; 
            addr = addr + 4;
            HTRANS <= #1 2'b10; // NONSEQ
            if (leng >= 16) begin 
                HBURST <= #1 3'b111; // INCR16
                ln = 16; 
            end 
            else if (leng >= 8) begin 
                HBURST <= #1 3'b101; // INCR8
                ln = 8; 
            end 
            else if (leng >= 4) begin 
                HBURST <= #1 3'b011; // INCR4
                ln = 4; 
            end 
            else begin 
                HBURST <= #1 3'b001; // INCR1 
                ln = leng; 
            end
            @(posedge HCLK);
            while (HREADY == 0) @(posedge HCLK);
            data_burst[k%1024] = HRDATA; // must be blocking
            k = k + 1;
        end
    end
    @(posedge HCLK);
    while (HREADY == 0) @(posedge HCLK);
    data_burst[k%1024] = HRDATA; // must be blocking
end
endtask

//-----------------------------------------------------------------------
// AHB Write Burst Task
// It takes suitable burst first and then incremental.
//-----------------------------------------------------------------------
task ahb_write_burst;
    input [31:0]                addr;
    input [31:0]                leng;
    integer                     i; 
    integer                     j; 
    integer                     ln;
begin
    j = 0;
    ln = 0;
    @(posedge HCLK);
    while (leng > 0) begin
        HADDR <= #1 addr; 
        addr = addr + 4;
        HTRANS <= #1 2'b10; // NONSEQ
        if (leng >= 16) begin 
            HBURST <= #1 3'b111; // INCR16
            ln = 16; 
        end
        else if (leng >= 8) begin 
            HBURST <= #1 3'b101; // INCR8
            ln = 8; 
        end
        else if (leng >= 4) begin 
            HBURST <= #1 3'b011; // INCR4
            ln = 4; 
        end
        else begin 
            HBURST <= #1 3'b001; // INCR
            ln = leng; 
        end
        HWRITE <= #1 1'b1; // WRITE
        HSIZE <= #1 3'b010; // WORD
        for (i = 0; i < ln-1; i = i + 1) begin
            @(posedge HCLK);
            while (HREADY == 1'b0) @(posedge HCLK);
            HWDATA <= #1 data_burst[(j+i)%1024];
            HADDR <= #1 addr; 
            addr = addr + 4;
            HTRANS <= #1 2'b11; // SEQ;
            while (HREADY == 1'b0) @(posedge HCLK);
        end
        @(posedge HCLK);
        while (HREADY == 0) @(posedge HCLK);
        HWDATA <= #1 data_burst[(j+i)%1024];
        if (ln == leng) begin
            HADDR <= #1 0;
            HTRANS <= #1 0;
            HBURST <= #1 0;
            HWRITE <= #1 0;
            HSIZE <= #1 0;
        end
        leng = leng - ln;
        j = j + ln;
    end
    @(posedge HCLK);
    while (HREADY == 0) @(posedge HCLK);
    if (HRESP != 2'b00) begin // OKAY
        $display($time,, "ERROR: non OK response write");
    end
`ifdef DEBUG
    $display($time,, "INFO: write(%x, %d, %x)", addr, size, data);
`endif
    HWDATA <= #1 0;
    @(posedge HCLK);
end
endtask

`endif

使用VCS仿真,打印信息如下:
图片[2] - 使用Verilog实现与仿真AMBA--AHB总线协议(三) - 我的学记|刘航宇的博客
图片[3] - 使用Verilog实现与仿真AMBA--AHB总线协议(三) - 我的学记|刘航宇的博客
整体仿真波形如下:
图片[4] - 使用Verilog实现与仿真AMBA--AHB总线协议(三) - 我的学记|刘航宇的博客
不喜欢用DVE的话,也可以选择用Verdi查看波形,这种比较简单的调试,DVE也足够用了。下面贴一张突发传输的细节图:
图片[5] - 使用Verilog实现与仿真AMBA--AHB总线协议(三) - 我的学记|刘航宇的博客
图片[6] - 使用Verilog实现与仿真AMBA--AHB总线协议(三) - 我的学记|刘航宇的博客

© 版权声明
THE END
喜欢就支持一下吧
点赞 1 分享 赞赏
评论 抢沙发
取消