AMBA 2.0 学习记录

article/2025/4/19 21:06:09
  • 第一周学习内容

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

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

文章目录

  • 学习路径
  • 一、AMBA是什么?
  • 二、APB总线
      • 总概
      • 写传输时序:(注意看虚线的位置)
      • 读传输时序:
      • 一个通过apb总线连接的sram外设实现:
      • 仿真波形
      • 总结与疑问
  • 三、AHB总线
    • 简介
    • 传输类型
    • 传输流程
      • response中split和retry的区别
    • 虚拟主机和默认主机
    • 地址译码
    • 互联
      • 主设备接口
      • 从设备接口
    • 一个ahpSram的实现
      • code
      • 突发传输
      • 单个传输:
      • 总结与疑问
  • 四、ASB总线
  • 五、APB桥
  • 参考链接


学习路径

  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 / 1psmodule 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;begintmp = value - 1;for (rt = 0; tmp > 0; rt = rt + 1) tmp = tmp >> 1;clogb2 = rt;endendfunction//----------------------------------// Start of Main Code//----------------------------------// Create read and write enable signals using APB control signalsassign wren = PWRITE && PENABLE && PSEL; // Enable Period 写使能assign rden = ~PWRITE && ~PENABLE && PSEL; // Setup Period 读使能
/*写时序中, 从机在ENABLE周期结束的时钟上升沿里从总线采样数据, 因此在ENABLE周期中用PENABLE作为输入进行判断即可; 而读时序中, 从机要在SETUP周期结束时的上升沿里就把数据送到总线, 主机在ENABLE周期结束的上升沿里取数据, 这就要求从机用SETUP周期里的PENABLE(此时为1'b0)来作为输入, 判断是否需要送数据.*///详见AMBA 总线规范 P101assign addr = PADDR[A_WIDTH-1:2];// Write memalways @(posedge PCLK)beginif (wren)mem[addr] <= PWDATA;end// Read memalways @(posedge PCLK)beginif (rden)PRDATA <= mem[addr];elsePRDATA <= 'h0;endendmodule

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

验证平台:

`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 PCLKalways #CLK_PERIOD_HALF beginPCLK <= ~PCLK;end // generate PRESETninitial beginPRESETn <= 1'b0;repeat(5) @(posedge PCLK);//复位5个周期后再把PRESETn置1。敏感量列表后面直接加空是代表空操作。PRESETn <= 1'b1;//如果是#加时间,有可能会吞波形。end// test memoryinitial beginPSEL = 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 tasktask memory_test;// starting addressinput [31:0]                    start;// ending address, inclusiveinput [31:0]                    finish; reg [31:0]                      dataW;reg [31:0]                      dataR;integer                         a; integer                         b; integer                         err;beginerr = 0;// read-after-write testfor (a = start; a <= finish; a = a + 1) begindataW = $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);endendif (err == 0) $display($time,,"%m Read after Write 0x%x-%x test OK", start, finish);err = 0;// read_all-after-write_all testfor (a = start; a <= finish; a = a + 1) beginb = a - start;reposit[b] = $random;apb_write(4*a, reposit[b]);endfor (a = start; a <= finish; a = a + 1) beginb = a - start;apb_read(4*a, dataR);if (dataR !== reposit[b]) beginerr = 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);endendif (err == 0) $display($time,"%m Read all after Write all 0x%x-%x test OK", start, finish);endendtask// APB write tasktask 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;endendtask// APB read tasktask 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 我猜测是因为阻塞赋值可以保证这一步数据读取完毕了才会进行后续操作endendtask`ifdef VCSinitial begin$fsdbDumpfile("apb_sram_tb.fsdb");$fsdbDumpvars;endinitial begin`ifdef DUMP_VPD$vcdpluson();`endifend
`endifendmodule

仿真波形

在这里插入图片描述写传输:两蓝线之间是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//----------------------------------// Inputsinput                           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,// Outputsoutput                          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 parameterslocalparam                      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 definitionparameter                       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 Machineassign validahbcmd = HREADYIN & HSEL & (HTRANS == TRN_NONSEQ);// Generation of HRESPassign HRESP = RESP_OKAY;always @(*) beginHWDATA_cal = HWDATA;end// Latch all the AHB signalsalways @(posedge HCLK or negedge aresetn) beginif ((aresetn == 1'b0) || (sresetn == 1'b0)) beginHADDR_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;endelse if (HREADYIN == 1'b1 & HSEL == 1'b1 & HREADYOUT == 1'b1) beginHADDR_d    <= HADDR;HTRANS_d   <= HTRANS;HSIZE_d    <= HSIZE;HBURST_d   <= HBURST;HWRITE_d   <= HWRITE;HWDATA_d   <= HWDATA_cal;         HSEL_d     <= HSEL;HREADYIN_d <= HREADYIN;endend// Current State generationalways @(posedge HCLK or negedge aresetn) beginif ((aresetn == 1'b0) || (sresetn == 1'b0)) beginahbcurr_state <= IDLE;endelse beginahbcurr_state <= ahbnext_state;endend// Next State and output decoder logicalways @(*) beginlatchahbcmd = 1'b0;ahbsram_req_int = 1'b0;ahbnext_state = ahbcurr_state;case (ahbcurr_state)IDLE : beginif (HREADYIN == 1'b1 && HSEL == 1'b1 && ((HTRANS == TRN_NONSEQ) || HTRANS == TRN_SEQ)) beginlatchahbcmd = 1'b1;if (HWRITE == 1'b1) beginahbnext_state = AHB_WR;           end else beginahbnext_state = AHB_RD;           endend else beginahbnext_state = IDLE;           endendAHB_WR : beginlatchahbcmd = 1'b0;ahbsram_req_int = 1'b1;if (sramahb_ack == 1'b1) beginif (count == burst_count_reg) beginahbnext_state = IDLE;end else beginahbsram_req_int = 1'b0;endendendAHB_RD : beginlatchahbcmd = 1'b0;ahbsram_req_int = 1'b1;if (sramahb_ack == 1'b1) beginahbnext_state = IDLE;endenddefault : beginahbnext_state = IDLE;endendcase  end// LOGIC FOR BURST COUNTalways @(*) beginburst_count = burst_count_reg;if (HSEL == 1'b1  && HTRANS == TRN_NONSEQ && HREADYIN == 1'b1 && HREADYOUT == 1'b1) begincase (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;endcaseendendalways @(posedge HCLK or negedge aresetn) beginif ((aresetn == 1'b0) || (sresetn == 1'b0)) beginburst_count_reg <= 'h0;end else beginburst_count_reg <= burst_count;endendalways @(posedge HCLK or negedge aresetn) beginif ((aresetn == 1'b0) || (sresetn == 1'b0)) begincount <= 5'h0;end else beginif (count == burst_count_reg) begincount <= 5'h0;end else if (ahbsram_req == 1'b1) begincount <= count + 1'b1;end else begincount <= count;endendendassign HREADYOUT = !ahbsram_req_int;// Generation of signals required for SRAMassign 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) beginif ((aresetn == 1'b0) || (sresetn == 1'b0)) beginahbsram_req_d1 <= 1'b0;endelse beginahbsram_req_d1 <= ahbsram_req_int;endend// Generate the request to the SRAM contol logic when there is AHB read or write requestassign ahbsram_req = ahbsram_req_int & !ahbsram_req_d1; // HRDATA generation   always @(*) beginif (HREADYOUT && HREADYIN) beginHRDATA = sramahb_rdata;end  else beginHRDATA = sramahb_rdata;endendendmodule

sram控制信号接口定义

`timescale 1ns / 1psmodule sram_ctrl_if #( parameter                       AHB_DWIDTH = 32,parameter                       ADD_WIDTH = 11,parameter                       SYNC_RESET = 0
)
(//----------------------------------// IO Declarations//----------------------------------// Inputsinput                           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,// Outputsoutput                          sramahb_ack,output reg [AHB_DWIDTH-1: 0]    sramahb_rdata
);//----------------------------------// Local Parameter Declarations//----------------------------------// State Machine parameterslocalparam                      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 generationalways @(posedge HCLK or negedge aresetn) beginif ((aresetn == 1'b0) || (sresetn == 1'b0)) beginsramcurr_state <= S_IDLE;endelse beginsramcurr_state <= sramnext_state;endend// Next State and output decoder logicalways @(*) beginsramahb_ack_int = 1'b0;sram_wen = 1'b0;sram_ren = 1'b0;sramnext_state = sramcurr_state;case (sramcurr_state)S_IDLE : beginif (ahbsram_req == 1'b1) beginif (ahbsram_write == 1'b1) beginsramnext_state = S_WR;           sram_wen = 1'b1;endelse beginsram_ren = 1'b1;sramnext_state = S_RD;           endendendS_WR : beginif (sram_done == 1'b1) beginsramnext_state = S_IDLE;sramahb_ack_int = 1'b1;endendS_RD : beginif (sram_done == 1'b1) beginsramnext_state = S_IDLE;sramahb_ack_int = 1'b1;endenddefault : beginsramnext_state = S_IDLE;endendcase     endalways @(*) beginsram_wen_mem = 4'b0000;if (ahbsram_size == 3'b010) beginsram_wen_mem = {4{sram_wen}};endelse if (ahbsram_size == 3'b001) begincase (ahbsram_addr[1])1'b0 : beginsram_wen_mem[0] = sram_wen;sram_wen_mem[1] = sram_wen;sram_wen_mem[2] = 1'b0;sram_wen_mem[3] = 1'b0;end1'b1 : beginsram_wen_mem[0] = 1'b0;sram_wen_mem[1] = 1'b0;sram_wen_mem[2] = sram_wen;sram_wen_mem[3] = sram_wen;endendcase      end     else if (ahbsram_size == 3'b000) begincase (ahbsram_addr[1:0])2'b00 : beginsram_wen_mem[0] = sram_wen;sram_wen_mem[1] = 1'b0;sram_wen_mem[2] = 1'b0;sram_wen_mem[3] = 1'b0;end2'b01 : beginsram_wen_mem[0] = 1'b0;sram_wen_mem[1] = sram_wen;sram_wen_mem[2] = 1'b0;sram_wen_mem[3] = 1'b0;end2'b10 : beginsram_wen_mem[0] = 1'b0;sram_wen_mem[1] = 1'b0;sram_wen_mem[2] = sram_wen;sram_wen_mem[3] = 1'b0;end2'b11 : beginsram_wen_mem[0] = 1'b0;sram_wen_mem[1] = 1'b0;sram_wen_mem[2] = 1'b0;sram_wen_mem[3] = sram_wen;endendcase       endelse beginsram_wen_mem = {4{sram_wen}};end             end// SRAM Instantiationssram_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) beginif ((aresetn == 1'b0) || (sresetn == 1'b0)) beginsramahb_rdata <= 32'h0;endelse if (sram_ren_d == 1'b1) beginsramahb_rdata <= ram_rdata;endelse beginsramahb_rdata <= sramahb_rdata;endendalways @(posedge HCLK or negedge aresetn) beginif ((aresetn == 1'b0) || (sresetn == 1'b0)) beginsram_ren_d <= 32'h0;endelse beginsram_ren_d <= sram_ren;endend// Generate the SRAM done when the SRAM wren/rden is donealways @(posedge HCLK or negedge aresetn) beginif ((aresetn == 1'b0) || (sresetn == 1'b0)) beginsram_done <= 1'b0;endelse if (sram_wen || sram_ren) beginsram_done <= 1'b1;endelse beginsram_done <= 1'b0;endend// Generate the SRAM ack assign sramahb_ack = sramahb_ack_int;endmodule

sram顶层

`timescale 1ns / 1psmodule 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//----------------------------------// Inputsinput                           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,/*当该信号为高时表示一个写传输,为低的时候表示一个读传输。*/// Outputsoutput [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];// Instantiationsahb_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 beginHADDR = 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_TESTrepeat(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_TESTrepeat(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 addressinput [31:0]                finish; // end addressinput [2:0]                 size; // data size: 1, 2, 4integer                     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) begingen = $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;endendif (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) begingen = {$random} & ~32'b0;data = align(i, gen, size);reposit[i] = data;ahb_write(i, size, data);endfor (i = start; i < (finish-size+1); i = i + size) begindata = 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;endendif (error == 0)$display("[%10d] %m OK: from %x to %x", $time, start, finish);endendtask//-----------------------------------------------------------------------// Burst transfer test //-----------------------------------------------------------------------task memory_test_burst;input [31:0]                start; // start addressinput [31:0]                finish; // end addressinput [7:0]                 leng; // burst lengthinteger                     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)) beginfor (i = start; i < (finish-(leng*4)+1); i = i + leng*4) beginfor (j = 0; j < leng; j = j + 1) begindata_burst[j] = $random;reposit[j+k*leng] = data_burst[j];end@(posedge HCLK);ahb_write_burst(i, leng);k = k + 1;endgen = $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) beginif (data_burst[j] != reposit[j+k*leng]) beginerror = error+1;$display("%m A=%hh D=%hh, but %hh expected",i+j*leng, data_burst[j], reposit[j+k*leng]);endendk = k + 1;r = $random & 8'h0F;repeat(r) @(posedge HCLK);endif (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);endendendtask//-----------------------------------------------------------------------// 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 bytesbegin`ifdef BIG_ENDIANcase (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;endcase4 : align = data&32'hFFFF_FFFF;default : $display($time,,"%m ERROR %d-byte not supported for size", size);endcase`elsecase (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;endcase2 : case (addr[1])0 : align = data & 32'h0000_FFFF;1 : align = data & 32'hFFFF_0000;endcase4 : align = data&32'hFFFF_FFFF;default : $display($time,,"%m ERROR %d-byte not supported for size", size);endcase`endifendendfunction`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 blockingif (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; // NONSEQHBURST <= #1 3'b000; // SINGLEHWRITE <= #1 1'b1; // WRITEcase (size)1 : HSIZE <= #1 3'b000; // BYTE2 : HSIZE <= #1 3'b001; // HWORD4 : HSIZE <= #1 3'b010; // WORDdefault : $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;
begink = 0;@(posedge HCLK);HADDR <= #1 addr; addr = addr + 4;HTRANS <= #1 2'b10; // NONSEQif (leng >= 16) begin HBURST <= #1 3'b111; // INCR16ln = 16; endelse if (leng >= 8) begin HBURST <= #1 3'b101; // INCR8ln = 8; endelse if (leng >= 4) begin HBURST <= #1 3'b011; // INCR4ln = 4; end else begin HBURST <= #1 3'b001; // INCRln = leng; end HWRITE <= #1 1'b0; // READHSIZE <= #1 3'b010; // WORD@(posedge HCLK);while (HREADY == 1'b0) @(posedge HCLK);while (leng > 0) beginfor (i = 0; i < ln-1; i = i + 1) beginHADDR <= #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;endleng = leng - ln;if (leng == 0) beginHADDR <= #1 0;HTRANS <= #1 0;HBURST <= #1 0;HWRITE <= #1 0;HSIZE <= #1 0;end else beginHADDR <= #1 addr; addr = addr + 4;HTRANS <= #1 2'b10; // NONSEQif (leng >= 16) begin HBURST <= #1 3'b111; // INCR16ln = 16; end else if (leng >= 8) begin HBURST <= #1 3'b101; // INCR8ln = 8; end else if (leng >= 4) begin HBURST <= #1 3'b011; // INCR4ln = 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 blockingk = k + 1;endend@(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;
beginj = 0;ln = 0;@(posedge HCLK);while (leng > 0) beginHADDR <= #1 addr; addr = addr + 4;HTRANS <= #1 2'b10; // NONSEQif (leng >= 16) begin HBURST <= #1 3'b111; // INCR16ln = 16; endelse if (leng >= 8) begin HBURST <= #1 3'b101; // INCR8ln = 8; endelse if (leng >= 4) begin HBURST <= #1 3'b011; // INCR4ln = 4; endelse begin HBURST <= #1 3'b001; // INCRln = leng; endHWRITE <= #1 1'b1; // WRITEHSIZE <= #1 3'b010; // WORDfor (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) beginHADDR <= #1 0;HTRANS <= #1 0;HBURST <= #1 0;HWRITE <= #1 0;HSIZE <= #1 0;endleng = 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);
`endifHWDATA <= #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 blockingif (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; // NONSEQHBURST <= #1 3'b000; // SINGLEHWRITE <= #1 1'b1; // WRITEcase (size)1 : HSIZE <= #1 3'b000; // BYTE2 : HSIZE <= #1 3'b001; // HWORD4 : HSIZE <= #1 3'b010; // WORDdefault : $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;
begink = 0;@(posedge HCLK);HADDR <= #1 addr; addr = addr + 4;HTRANS <= #1 2'b10; // NONSEQif (leng >= 16) begin HBURST <= #1 3'b111; // INCR16ln = 16; endelse if (leng >= 8) begin HBURST <= #1 3'b101; // INCR8ln = 8; endelse if (leng >= 4) begin HBURST <= #1 3'b011; // INCR4ln = 4; end else begin HBURST <= #1 3'b001; // INCRln = leng; end HWRITE <= #1 1'b0; // READHSIZE <= #1 3'b010; // WORD@(posedge HCLK);while (HREADY == 1'b0) @(posedge HCLK);while (leng > 0) beginfor (i = 0; i < ln-1; i = i + 1) beginHADDR <= #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;endleng = leng - ln;if (leng == 0) beginHADDR <= #1 0;HTRANS <= #1 0;HBURST <= #1 0;HWRITE <= #1 0;HSIZE <= #1 0;end else beginHADDR <= #1 addr; addr = addr + 4;HTRANS <= #1 2'b10; // NONSEQif (leng >= 16) begin HBURST <= #1 3'b111; // INCR16ln = 16; end else if (leng >= 8) begin HBURST <= #1 3'b101; // INCR8ln = 8; end else if (leng >= 4) begin HBURST <= #1 3'b011; // INCR4ln = 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 blockingk = k + 1;endend@(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;
beginj = 0;ln = 0;@(posedge HCLK);while (leng > 0) beginHADDR <= #1 addr; addr = addr + 4;HTRANS <= #1 2'b10; // NONSEQif (leng >= 16) begin HBURST <= #1 3'b111; // INCR16ln = 16; endelse if (leng >= 8) begin HBURST <= #1 3'b101; // INCR8ln = 8; endelse if (leng >= 4) begin HBURST <= #1 3'b011; // INCR4ln = 4; endelse begin HBURST <= #1 3'b001; // INCRln = leng; endHWRITE <= #1 1'b1; // WRITEHSIZE <= #1 3'b010; // WORDfor (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) beginHADDR <= #1 0;HTRANS <= #1 0;HBURST <= #1 0;HWRITE <= #1 0;HSIZE <= #1 0;endleng = 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);
`endifHWDATA <= #1 0;@(posedge HCLK);
end
endtask`endif
`timescale 1ns / 1psmodule 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)beginrden_r <= rden;end// Infer Block RAM always @(posedge clk)beginif (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];endalways @(posedge clk)beginaddr_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

http://chatgpt.dhexx.cn/article/7J6fh46L.shtml

相关文章

什么软件可以听学业水平测试网课,免费听网课app推荐

免费听网课app推荐2020-10-17 15:41:06文/张孟影 小编为大家找来了一些可以免费学习的网课软件&#xff0c;大家赶快来挑选适合自己的app吧。 网课app有哪些 宇学教育 安博教育 京教通 小七学伴 名华在线 燃天网校 钉钉学生版 华远学成教育 华数钉钉课堂 简单学习网 网课软件简…

UE4网课学习笔记

学习资源&#xff1a;https://www.bilibili.com/video/av52017180?p22 BSP&#xff1a;二进制空间划分让门可旋转&#xff0c;需要将属性改为“Movable”选中一个物体后&#xff0c;在Level BluePrint中右键会自动出现对应物体的函数TimeLine&#xff1a; 其中&#xff1a;Pla…

超星学习通小助手多线程工具Python

话不多说&#xff0c;直接开始&#xff0c;不会安转的直接使用后面两款&#xff0c;下载直接打开exe运行 第一款&#xff1a;网课小助手python&#xff0c;需要自行安装Python环境&#xff08;支持Windows、Mac、Linux各种环境&#xff09; https://wwiv.lanzoul.com/ifVrC0vk…

[脚本] 如果电脑不会自己看网氪,就应该用Python教会它

很多时候需要看网课完成一下学习任务&#xff0c;但在某些平台在播完一条视频后并不会自动切播到下一条。所以这里简单用python写了一个脚本&#xff0c;利用PyAutoGui库自动完成一些如鼠标移动点击来快速切换的操作。 源码地址 现已将代码上传至GitHub仓库&#xff08;其中包…

计算机网络第六章学习通题目及答案

目录 DNS FTP 第十一次练兵 WWW 电子邮件 第十二次练兵 DNS 3 【简答题】 域名系统的查询方式有哪两类&#xff1f;最常见的是哪一种&#xff1f; 我的答案&#xff1a; 递归查询和迭代查询&#xff0c;迭代查询。 域名系统DNS是因特网使用的命名系统&#xff0c;用来…

kafka命令行操作(for Linux)

kafka命令行操作主要分为三大类&#xff1a; 主题命令行操作、生产者命令行操作、消费者命令行操作。 注意&#xff1a; 命令行操作前提&#xff0c;启动kafka集群。 1. 主题命令行操作 1.1 查看主题命令行参数 a) 查询命令 [roothadoop102 ~]# /opt/module/kafka/bin/kafka…

【Kafka】kafka命令kafka-console-consumer.sh

文章目录 1.概述2.消息消费3.从开始位置消费4.显示key消费5.其他参数6.案例7.源码分析1.概述 转载:转载并且补充 kafka-console-consumer.sh 脚本是一个简易的消费者控制台。该 shell 脚本的功能通过调用 kafka.tools 包下的 ConsoleConsumer 类,并将提供的命令行参数全部传…

【Kafka】kafka命令 kafka-console-producer

文章目录 1.概述2.输入单挑数据3.批量导入数据3.无key型消息4.有key型消息1.概述 消费者:【Kafka】kafka命令kafka-console-consumer.sh kafka-console-producer 命令的常用参数如下 参数值类型说明有效值--bootstrap-serverString要连接的服务器必需(除非指定--broker-list…

kafka 命令、API

日萌社 人工智能AI&#xff1a;Keras PyTorch MXNet TensorFlow PaddlePaddle 深度学习实战&#xff08;不定时更新&#xff09; 大数据组件使用 总文章 kafka 生产/消费API、offset管理/原理、kafka命令kafka 命令、APIKafka 安装、原理、使用mapreduce 实时消费 kafka 数据…

kafka命令行使用

kafka使用 查看Kafka当前的主题列表创建一个主题查看主题信息修改分区信息删除一个主题生成者推送消息消费者接收查看分组信息查看特定consumer group 详情 基于Kafka 2.13版本的操作 查看Kafka当前的主题列表 ./kafka-topics.sh --zookeeper 127.0.0.1:2181 --list创建一个主…

简单的kafka命令行操作

目录 一、主题topic命令行操作 1.查看操作主题的命令参数 2.连接kafka地址&#xff0c;创建名为kaf的主题&#xff0c;指定分区和副本数量 3.查看所有主题的名称 4.查看主题的详细信息 5.修改主题&#xff08;修改分区数&#xff09; 二、生产者命令行操作 1.查看操作生…

【kafka】三、kafka命令行操作

kafka命令行操作 kafka的相关操作命令脚本文件在bin目录下 查看所有的topic kafka-topics.sh --zookeeper hll1:2181 --list 或 kafka-topics.sh --zookeeper 192.168.171.132:2181 --listkafka-topics.sh&#xff1a;topic执行脚本 --zookeeper hll1:2181&#xff1a;需要的…

Kafka的命令行操作

一、topic命令 下面Windows命令需要把cmd路径切换到bin/windows下。 而Linux命令只需要在控制台切换到bin目录下即可。 下面都以Windows下的操作为例&#xff0c;在Linux下也是一样的。 1.1 查看主题命令的参数 kafka-topics.bat # Windows kafka-topics.sh # Linux输…

Kafka 命令行操作

1&#xff09;查看当前服务器中的所有 topic bin/kafka-topics.sh --zookeeper backup01:2181 使用命令 bin/kafka-topics.sh --list 报异常&#xff0c;提示必须依赖zookeeper 前面我们就讲过kafka是依赖于zookeeper 连上zookeeper什么都没有输出&#xff0c;因为我们什…

kafka命令行操作大全

最近利用flink使用一个流式SQL处理平台&#xff0c;利用kafka, mysql, hive等组件比较多&#xff0c;命令行突然间需要操作一次记不住命令很麻烦&#xff0c;索性直接整理成笔记。 在 0.9.0.0 之后的 Kafka&#xff0c;出现了几个新变动&#xff0c;一个是在 Server 端增加了…

Kafka命令大全

kafka 脚本 connect-distributed.sh connect-mirror-maker.sh connect-standalone.sh kafka-acls.sh kafka-broker-api-versions.sh kafka-configs.sh kafka-console-consumer.sh kafka-console-producer.sh kafka-consumer-groups.sh kafka-consumer-perf-test.sh kafka-dele…

Kafka常用命令行命令

文章目录 Kafka常用命令kafka的基本操作&#xff08;命令行操作&#xff09;1.启动集群&#xff1a;2.查看当前服务器中的所有topic&#xff08;在kafka目录下&#xff09;3.创建主题topic&#xff08;在kafka目录下&#xff09;4.删除topic&#xff08;在kafka目录下&#xff…

美国Stripe支付Android端集成流程

上家公司想要拓展自己在新加坡的市场,打算做一个新加坡本地的生活服务应用,其中少不了的就是支付了。国外支付这块一直是个头疼的问题。想用Google Wallet吧,但它是采用NFC接触式交易,想要进行线上服务时没法进行,后来就去整个贝宝PayPal支付。在这里想吐槽一下,PayPal支付做起…

zencart1.55手把手教你开发stripe支付插件

第一步&#xff1a;在includes/modules/payment目录下创建名称为c_stripe的文件夹&#xff0c;用于存放stripe支付logo 第二步&#xff1a;在同includes/modules/payment目录下创建c_stripe.php文件&#xff0c;这个文件就是用于编写zencart支付插件,代码如下 <?php // /…

JAVA接入STRIPE支付教程(测试环境),STRIPE支付的调用以及STRIPE WEBHOOK回调

一、环境准备 1.注册 2.密钥 3.WEBHOOK回调 二、核心代码 1.配置API.key以及webhook.key 2.支付demo 3.WEBHOOK回调 一、环境准备 1.注册 STRIPE官网自行注册账号 2.两个重要的密钥 首先在STRIPE官网注册账号之后进入首页&#xff0c;点击API密钥&#xff0c;查看账号对应的…