AMBA 2.0 学习记录

  • 第一周学习内容

利用寒假时间学习一些数字ic相关的内容,每周记录一下。
基本都是现有资料文章的整理,最后标明了出处。
欢迎大家指点、交流。

https://img-blog.csdnimg.cn/a02c7272b1f1413abe2fd642bdd300d7.png


学习路径

  1. 百科上搜索了解AMBA的是什么
  2. eetop上下载了arm公司官方的 AMBA2.0总线规范
  3. csdn上moshanghongfeng大佬的翻译的AMBA总线规范也很好,有些部分有中英对照看着比较清楚。链接: https://blog.csdn.net/moshanghongfeng/article/details/108931201
  4. 寻找代码例程,很少有专门这方面的仓库,最后在知乎上“托管小弟”的专栏“AMBA协议详解与Verilog实现”找到了,本文是复现的他的代码,一些地方进行了改动。注释基本都是我自己添的,可能有理解错误的地方。

提示:以下是本篇文章正文内容,下面案例可供参考

一、AMBA是什么?

AMBA(Advanced Microcontroller Bus Architecture)总线协议是一种面向高性能嵌入式微控制器设计的片上联接标准。

AMBA总线(截至AMBA2.0)确定了三种总线标准:

    AHB:(Advanced High-performance Bus)高级高性能总线  用于高性能、高时钟频率的系统模块。

    ASB:(Advanced System Bus)高级系统总线  用于高性能的系统模块之间

    APB:(Advanced Peripheral Bus)高级外设总线  用于低功耗外设

[典型 AMBA 系统]

二、APB总线

总概

APB具备以下特性:
(1)低功耗;
(2)接口协议简单;
(3)总线传输使用时钟上升沿进行,便于时序分析;
(4)应用广泛,支持多种外设;
(5)所有的APB模块均是APB从机。

APB 桥是 AMBA APB 中的唯一总线主机。另外,APB 桥也是高级系统总线中的一个
从机。
所以我从最简单的APB总线学起

以下状态图表示了APB的三个状态,即外设总线的活动性
状态图

写传输时序:(注意看虚线的位置)

在这里插入图片描述
写传输开始于T2时刻,在改时钟上升沿时刻,地址、写信号、PSEL、写数据信号同时发生变化,T2时钟,即传输的第一个时钟被称为SETUP周期。在下个时钟上升沿T3,PENABLE信号拉高,表示ENABLE周期,在该周期内,数据、地址以及控制信号都必须保持有效。整个写传输在这个周期结束时完成。

读传输时序:

在这里插入图片描述

地址、写、选择和选通信号的时序都和写传输一样。在读传输的情况下,从机必须在
EANBLE 周期提供数据。数据在 ENABLE 周期末尾的时钟上升沿被采样。

一个通过apb总线连接的sram外设实现:

`timescale 1ns / 1ps

module apb_sram #(
    parameter                           SIZE_IN_BYTES = 1024
)
(
    //----------------------------------
    // IO Declarations
    //----------------------------------
    input                               PRESETn,
    /*APB 总线复位信号为低有效并且通常将该信号直接连接到系统总线复位信号。*/
    input                               PCLK,
    //PCLK 的上升沿用作所有 APB 传输的时基
    input                               PSEL,
    /*来自二级译码器的信号,从外设总线桥接单元内到每个外设总线从机 x。
该信号表示从机设备被选中并且要求一次数据传输。每个总线从机都有
一个 PSELx 信号*/
    input [31:0]                        PADDR,
    //这是 APB 地址总线,可高达 32 位宽度并由外设总线桥接单元驱动。
    input                               PENABLE,
    /*这个选通信号用来给外设总线上的所有访问提供时间。使能信号用来表
示一次 APB 传输的第二个周期。PENABLE 的上升沿出现在 APB 传输
的中间。*/
    input                               PWRITE,
    //该信号为高表示一次 APB 写访问而为低表示一次读访问。
    input [31:0]                        PWDATA,
    /*读数据总线由被选中的从机在读周期(PWRITE 为低)期间驱动。读数
据总线可达到 32 位宽度。*/
    output reg [31:0]                   PRDATA
    /*读数据总线由被选中的从机在读周期(PWRITE 为低)期间驱动。读数
据总线可达到 32 位宽度。*/
);

    //----------------------------------
    // Local Parameter Declarations
    //----------------------------------
    localparam                          A_WIDTH = clogb2(SIZE_IN_BYTES);

    //----------------------------------
    // Variable Declarations
    //----------------------------------
    reg [31:0]                          mem[0:SIZE_IN_BYTES/4-1];
    //AMBA2.0最多可以传输高达 32 位宽度的地址,这里是地址只有30位,所以除以四
    wire                                wren;
    wire                                rden;
    wire [A_WIDTH-1:2]                  addr; 
 
    //----------------------------------
    // Function Declarations
    //----------------------------------
    function integer clogb2;
    //判断value-1用二进制表示有几位
    //也就是value取2的对数再向上取整
    //原理:二进制n位数最大是2的n次方减一,所以先减一,再判断他有几位,就求出了n的上整数
    //类似于C艹里面的31 - __builtin_clz(x);
        input [31:0]                    value; 
        reg [31:0]                      tmp; 
        reg [31:0]                      rt;
    begin
        tmp = value - 1;
        for (rt = 0; tmp > 0; rt = rt + 1) 
            tmp = tmp >> 1;
        clogb2 = rt;
    end
    endfunction

    //----------------------------------
    // Start of Main Code
    //----------------------------------
    // Create read and write enable signals using APB control signals
    assign wren = PWRITE && PENABLE && PSEL; // Enable Period 写使能
    assign rden = ~PWRITE && ~PENABLE && PSEL; // Setup Period 读使能
/*写时序中, 从机在ENABLE周期结束的时钟上升沿里从总线采样数据, 
    因此在ENABLE周期中用PENABLE作为输入进行判断即可; 
    而读时序中, 从机要在SETUP周期结束时的上升沿里就把数据送到总线, 
    主机在ENABLE周期结束的上升沿里取数据, 
    这就要求从机用SETUP周期里的PENABLE(此时为1'b0)来作为输入, 判断是否需要送数据.*/
    //详见AMBA 总线规范 P101
    assign addr = PADDR[A_WIDTH-1:2];
    
    // Write mem
    always @(posedge PCLK)
    begin
        if (wren)
            mem[addr] <= PWDATA;
    end

    // Read mem
    always @(posedge PCLK)
    begin
        if (rden)
            PRDATA <= mem[addr];
        else
            PRDATA <= 'h0;
    end

endmodule

我的理解都写在注释里了,就不讲了。

验证平台:

`timescale 1ns / 1ps

`ifndef CLK_FREQ
`define CLK_FREQ 50_000_000 //50MHz
`endif
//使用 ifndef 、define 和endif的目的:为了防止同一个文件在编译时被重复编译,引起多重定义的问题。
//ifndef 的含义:即 “if not defined”,也就是说,当文件编译到这一行,如果这个文件还没有被编译过,也就是首次编译,就会执行后续的 `define xxx这句话,把后续的代码定义一次。反之,则不会再重复编译。
//ifdef 的含义:即"if defined",与 ifndef 的作用相反,如果已经编译过,那么则继续执行后面的代码。
//enif 的含义:出现 ifndef 或者 ifdef 作为开头,程序块的末尾就需要有 endif 作为结束的标识。

module apb_sram_tb();

    //----------------------------------
    // Local Parameter Declarations
    //----------------------------------
    parameter                           SIZE_IN_BYTES = 1024;

    localparam                          CLK_FREQ = `CLK_FREQ;
    localparam                          CLK_PERIOD_HALF = 1e9/CLK_FREQ/2;//1s==1e9ns

    //----------------------------------
    // Variable Declarations
    //----------------------------------
    reg                                 PRESETn = 1'b0;
    reg                                 PCLK = 1'b0;
    reg                                 PSEL;
    reg [31:0]                          PADDR;
    reg                                 PENABLE;
    reg                                 PWRITE;
    reg [31:0]                          PWDATA;   
    wire [31:0]                         PRDATA;

    reg [31:0]                          reposit[0:1023];//这是模拟给sram传数据的仓库

    //----------------------------------
    // Start of Main Code
    //----------------------------------
    apb_sram #(
        .SIZE_IN_BYTES                  (SIZE_IN_BYTES)
    )
    u_apb_sram (
        .PRESETn                        (PRESETn),
        .PCLK                           (PCLK),
        .PSEL                           (PSEL),
        .PADDR                          (PADDR),
        .PENABLE                        (PENABLE),
        .PWRITE                         (PWRITE),
        .PWDATA                         (PWDATA),
        .PRDATA                         (PRDATA)
    );
    
    // generate PCLK
    always #CLK_PERIOD_HALF 
    begin
        PCLK <= ~PCLK;
    end 

    // generate PRESETn
    initial begin
        PRESETn <= 1'b0;
        repeat(5) @(posedge PCLK);//复位5个周期后再把PRESETn置1。敏感量列表后面直接加空是代表空操作。
        PRESETn <= 1'b1;//如果是#加时间,有可能会吞波形。
    end

    // test memory
    initial begin
        PSEL = 1'b0;
        PADDR = ~32'h0;
        PENABLE = 1'b0;
        PWRITE = 1'b0;
        PWDATA = 32'hffff_ffff;
        wait(PRESETn == 1'b0);
        wait(PRESETn == 1'b1);
        repeat(3) @(posedge PCLK);
        memory_test(0, SIZE_IN_BYTES/4-1);
        repeat(5) @(posedge PCLK);
        $finish(2);
        /*
    0:不输出任何信息;
    1:输出当前仿真时刻和位置;
    2:输出当前仿真时刻、位置和仿真过程中所用的memory及CPU时间的统计。

        当$finish后面不带参数时,则默认参数为1。
        */
    end

    // memory test task
    task memory_test;
        // starting address
        input [31:0]                    start;
        // ending address, inclusive
        input [31:0]                    finish; 
        reg [31:0]                      dataW;
        reg [31:0]                      dataR;
        integer                         a; 
        integer                         b; 
        integer                         err;
    begin
        err = 0;
        // read-after-write test
        for (a = start; a <= finish; a = a + 1) begin
            dataW = $random;
            apb_write(4*a, dataW);
            apb_read (4*a, dataR);
            if (dataR !== dataW) begin//使用不全等,防止有高阻态或者不定态导致不好判断
                err = err + 1;
                $display($time,,"%m Read after Write error at A:0x%08x D:0x%x, but 0x%x expected", a, dataR, dataW);
            end
        end
        if (err == 0) 
            $display($time,,"%m Read after Write 0x%x-%x test OK", start, finish);
        err = 0;
        // read_all-after-write_all test
        for (a = start; a <= finish; a = a + 1) begin
            b = a - start;
            reposit[b] = $random;
            apb_write(4*a, reposit[b]);
        end
        for (a = start; a <= finish; a = a + 1) begin
            b = a - start;
            apb_read(4*a, dataR);
            if (dataR !== reposit[b]) begin
                err = err + 1;
                $display("***%m***: Read after Write error at A:0x%08x D:0x%x, but 0x%x expected,happend at %d", a, dataR, dataW,$time);
            end
        end
        if (err == 0) 
            $display($time,"%m Read all after Write all 0x%x-%x test OK", start, finish);
    end
    endtask

    // APB write task
    task apb_write;
        input [31:0]                    addr;
        input [31:0]                    data;
    begin
        @(posedge PCLK);
        PADDR <= #1 addr;
        PWRITE <= #1 1'b1;
        PSEL <= #1 1'b1;
        PWDATA <= #1 data;
        @(posedge PCLK);
        PENABLE <= #1 1'b1;
        @(posedge PCLK);
        PSEL <= #1 1'b0;
        PENABLE <= #1 1'b0;
    end
    endtask

    // APB read task
    task apb_read;
        input [31:0]                     addr;
        output [31:0]                    data;
    begin
        @(posedge PCLK);
        PADDR <= #1 addr;
        PWRITE <= #1 1'b0;
        PSEL <= #1 1'b1;
        @(posedge PCLK);
        PENABLE <= #1 1'b1;
        @(posedge PCLK);
        PSEL <= #1 1'b0;
        PENABLE <= #1 1'b0;
        data = PRDATA; // it should be blocking 我猜测是因为阻塞赋值可以保证这一步数据读取完毕了才会进行后续操作
    end
    endtask

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

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

endmodule

仿真波形

在这里插入图片描述写传输:两蓝线之间是setup周期,sel信号将该sram外设选中,写地址、写数据在此周期准备好。蓝黄线之间是enable周期,此周期内PENABLE信号被拉高,写地址、写数据此周期保持有效。数据在黄线时刻传输。

在这里插入图片描述读传输:两蓝线之间是setup周期,sel信号将该sram外设选中,读地址、读数据在此周期准备好。蓝黄线之间是enable周期,此周期内PENABLE信号被拉高,读地址、读数据此周期保持有效。从机要在SETUP周期结束时的上升沿里就把数据送到总线, 主机在ENABLE周期结束的上升沿里取数据。

总结与疑问

  1. 为什么apb读数据要在enable周期结束时完成?在enable周期开始时数据明明就准备好了。或许其实就是在enable周期开始时传输的?因为我将代码
    assign wren = PWRITE && PENABLE && PSEL; // Enable Period 写使能
    assign rden = ~PWRITE && ~PENABLE && PSEL; // Setup Period 读使能

改成:

    assign wren = PWRITE && ~PENABLE && PSEL; // Enable Period 写使能
    assign rden = ~PWRITE && ~PENABLE && PSEL; // Setup Period 读使能

功能同样正确

  1. 为什么要加个penable信号,这信号完全是根据时钟赋值的,也就是说其实并不能体现setup周期的数据准备好了没有,那为何不直接根据时钟第一周期setup第二周期enable?我想到的原因或许是这信号是主机给的,无论apb接多少个从机,这penable占用的资源只有一个(无论是综合出buffer还是counter),从机也更纯粹便于调试。当然也可能就是协议的统一标准,为了规范,没有什么特别的原因。

三、AHB总线

简介

典型的 AMBA AHB 系统设计包含以下的成分:

  1. AHB 主机 总线主机能够通过提供地址和控制信息发起读写操作。任何时候只允许一
    个总线主机处于有效状态并能使用总线。
  2. AHB 从机 总线从机在给定的地址空间范围内响应读写操作。总线从机将成功、失败
    或者等待数据传输的信号返回给有效的主机。
  3. AHB 仲裁器 总线仲裁器确保每次只有一个总线主机被允许发起数据传输。即使仲裁协
    议已经固定,任何一种仲裁算法,比如最高优先级或者公平访问都能够根据应用要求而得到执行。
    在这里插入图片描述
    部分仲裁器信号名:
    在这里插入图片描述
注意:
AHB 必须只包含一个仲裁器,尽管在单总线主机系统中这显得并不重要。
AHB 译码器 AHB 译码器用来对每次传输进行地址译码并且在传输中包含一个从机选
择信号。
所有 AHB 执行都必须仅要求有一个中央译码器。

传输类型

在这里插入图片描述

传输流程

  1. master发起一个请求给仲裁器
  2. arbiter运行某个主设备控制总线
  3. master驱动地址和控制信号
  4. 仅选中的从设备响应地址/控制信号
  5. 传输完成后slave拉高HREADY信号,总线传输完成

两个阶段

■地址周期,只有一个cycle

■数据周期,由HREADY信号决定需要几个cycle流水线传送


先是地址周期,然后是数据周期

一次无须等待的简单传输只需要两拍
可以使用流水线传输
在这里插入图片描述
突发传输
在这里插入图片描述
下图是一次突发传输的示例:
在这里插入图片描述
T2时主机busy没有准备好传输,HADDR必须保持直到busy状态取消;T5时,HREADY未被拉高,从机没有准备好,data也必须等待HREADY被拉高再开始传输。

下图是一次从机retry的示例
在这里插入图片描述

response中split和retry的区别

主要区别在于仲裁的方式
. RETRY: arbiter会继续使用通常的优先级
. SPLIT: arbiter会调整优先级方案以便其他请求总线的主设备可以访问总线
总线主设备应该用同样的方式处理RETRY响应和SPLIT响应

虚拟主机和默认主机

在这里插入图片描述

地址译码

地址译码AHB总线中,会使用一个中央译码器来产生HSELx信号,用于选择从机,选择信号由地址高位信号组合译码产生。建议使用简单的译码方案来保证高速操作。从机只能在HREADY信号为高时,采样地址和控制信号以及HSELx,当HREADY信号为高时,表示传输完成。AHB有几个特别的规定:
(1)每个从机至少都有 1KB 的内存空间。
(2)每个主机每次存取的空间不可超过 1KB。
(3)如果在 NONSEQ 或 SEQ 型态下存取到不存在的地址时,会有一个预设的从机发出 ERROR 的响应信号。
(4)如果在 IDLE 或 BUSY 型态下存取到不存在的地址时,会有 OKAY 的响应信号。
(5)预设的从机是中央译码器的一部分。
(6)根据系统设计,使用地址的高位来产生从机选择信号。
(7)地址的低位送给从机以寻址其的内部存储器或缓存器

互联

AHB信号以字母H作为前缀,下表为部分信号名称:
在这里插入图片描述

主设备接口

在这里插入图片描述

从设备接口

在这里插入图片描述

一个ahpSram的实现

code

ahbSRAM接口定义:

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,
    (* DONT_TOUCH= "TRUE" *)  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

sram控制信号接口定义

`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

sram顶层

`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,
    //时钟为所有总线传输提供时基。所有信号时序都和HCLK 的上升沿相关
    input                           HRESETN,
    //总线复位信号低有效并用来复位系统和总线。这是唯一的低有效的信号
    input                           HSEL,
    /*来源:译码器 每个 AHB 从机都有自己独立的从机选择信号并且用该
信号来表示当前传输是否是打算送给选中的从机。该
信号是地址总线的简单组合译码。*/
    input                           HREADYIN,
    /*当 HREADY 为高时表示总线上的传输已经完成。在扩
    展传输时该信号可能会被拉低。  注意:总线上的从机要求 HREADY 作为输入输出信号。*/
    input [1:0]                     HTRANS,
    //表示当前传输的类型,可以是不连续、连续、空闲和忙。
    input [2:0]                     HBURST,
    //表示传输是否组成了突发的一部分。支持四个、八个
    //或者 16 个节拍的突发传输并且突发传输可以是增量或者是回环。
    input [2:0]                     HSIZE,
    //表示传输的大小,典型情况是字节(8 位)、半字(16
    //位)或者是字(32 位)。协议允许最大的传输大小可以达到 1024 位。
    input [AHB_DWIDTH-1:0]          HWDATA,
    /*写数据总线用来在写操作期间从主机到总线从机传输
数据。建议最小的数据总线宽度为 32 位。然而,在要
求高带宽运行时扩展(数据总线)还是很容易的。*/
    input [AHB_AWIDTH-1:0]          HADDR,
    /*32 位系统地址总线。*/
    input                           HWRITE,
    /*当该信号为高时表示一个写传输,为低的时候表示一个读传输。*/
    // Outputs
    output [AHB_DWIDTH-1:0]         HRDATA,
    /*读数据总线用来在读操作期间从总线从机向总线主机
传输数据。建议最小的数据总线宽度为 32 位。然而,
在要求高带宽运行时扩展(数据总线)还是很容易的。*/
    output [1:0]                    HRESP,
    /*传输响应给传输状态提供了附加信息。提供四种不同的响应:OKEY、ERROR、RETRY 和 SPLIT。*/
    output                          HREADYOUT
    /*当 HREADY 为高时表示总线上的传输已经完成。在扩
展传输时该信号可能会被拉低*/
);

    //----------------------------------
    // 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

主机功能模型


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
`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
`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
`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

schematic:
在这里插入图片描述

突发传输

代码里添一句宏定义即可开始仿真

`define SINGLE_TEST

console输出:
在这里插入图片描述功能完全正确。

波形图:
在这里插入图片描述HREADYIN 高阻,查看代码和schematic,我认为是因为在top_tb中他直接被拉高所以被优化掉了,造成了HREADYIN空驱动的情形;
在这里插入图片描述在这里插入图片描述

试着给HREADYIN加了一句(* DONT_TOUCH= “TRUE” *) ,还是一样的。那可能不是HREADYIN而是HREADYIN的驱动被优化了,或者直接接到HREADYIN下一级去了。
不过看代码原作者“托管小弟”截取的图HREADYIN是被成功拉高的。
在这里插入图片描述难道vcs综合的和vivado不一样?

往虚拟机上下了套edatools,自己用vcs跑了一下,还是高阻。。。
总的schematic看着没啥问题,但点这个信号的schematic或者showpath,直接没反应,看来确实是被优化了。
在这里插入图片描述在这里插入图片描述
可能原作者用了些禁止优化、限制优化的源语或者vcs命令吧。想来这就是个testbench,写的master也是功能模型,这个仿真信号再深究也没有意义。

单个传输:

在这里插入图片描述

在这里插入图片描述

总结与疑问

vcs&verdi或者vcs加dve比使用vivado进行前仿好用多了

  1. 编译、仿真的速度快得多
  2. 可以直接在vcs命令中进行宏定义+define+SINGLE_TEST 改为+define+DUMP_VPD就可以改变宏定义,而不需要去修改代码
  3. 使用脚本运行比在vivado上使用鼠标点效率更高
  4. 对文件的管理更直观,自由度更高

不能使用DUMP函数的问题

  1. 最开始报错Undefined System Task call to $fsdbDumpfile ,于是加上了p操作。
  2. 由于一直找不到pli文件,采用-debug_pp -fsdb_old自动调用pli文件
  3. 提示没有配置novasHOME,到bashrc里配置好了并source一下重置
  4. 还是无法打开,多方查找办法,可能是版本问题

四、ASB总线

asb总线由于其总线带宽最高只支持到32bits、三态双向特性不适合DFT等缺点,已经几乎被淘汰了,现在很少有asb的设计。所以这里我也没有进行学习、实现。

五、APB桥

简单来说,apb桥就是将ahb上的高速转化为apb上的低速信号,再传递给apb上对应的外设。apb桥是apb外设的唯一主机。
桥接单元将系统总线传输转换成 APB 传输并实现下列功能:
z 锁存地址并使之在整个传输期间有效;
z 译码地址和产生一个外设选择信号,PSELx。在一个传输期间只有一个选择信号有
效;
z 对于写传输驱动数据到 APB 上;
z 对于读传输驱动 APB 数据到系统总线上;
z 为传输产生一个时序选通信号,PENABLE。
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述apb桥的传输示意图:
在这里插入图片描述


参考链接

  1. eetop.cn_AMBA总线规范_cn_20.pdf
  2. moshanghongfeng翻译的AMBA总线规范。链接: https://blog.csdn.net/moshanghongfeng/article/details/108931201
  3. “托管小弟”的专栏“AMBA协议详解与Verilog实现”
  4. “e网课”《数字ic入门》17到19讲,amba总线介绍。链接:https://www.bilibili.com/video/BV1K34y1B7hR/?spm_id_from=333.788&vd_source=2848d4df68e7c5d5183ab1c8dedb5f96
  • 4
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值