侧边栏壁纸
    • 累计撰写 302 篇文章
    • 累计收到 527 条评论
    使用Verilog实现与仿真AMBA--AHB总线协议(三)
    我的学记|刘航宇的博客

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

    刘航宇
    2023-03-16 / 0 评论 / 238 阅读 / 正在检测是否收录...

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

    下图为一个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仿真,打印信息如下:


    整体仿真波形如下:

    不喜欢用DVE的话,也可以选择用Verdi查看波形,这种比较简单的调试,DVE也足够用了。下面贴一张突发传输的细节图:

    1
    【优惠活动】25考研西北大学电子信息类专业全程班
    « 上一篇 2023-03-18
    Verilog-位宽计算的系统函数$clog2
    下一篇 » 2023-03-13

    评论 (0)

    取消