AXI4-Lite总线读写BRAM
博主参考和学习的博客
- AXI协议基础知识 。这篇博客比较详细地介绍了AXI总线,并且罗列了所有的通道和端口,写代码的时候可以方便地进行查表。
- AXI总线,AXI_BRAM读写仿真测试 。 这篇文章为代码的书写提供大致的思路,比如状态机和时序的控制问题,可以参考。
valid-ready双向握手机制
- 双向握手机制的实质是:
数据接收方R告诉数据发送方T“我准备好接收数据了”,并拉高ready;同样的,数据发送方T告诉数据接收方R“我准备好发送数据了”,并拉高valid。
数据发送方给出valid,数据接收方给出ready
重点:只有在valid和ready同时拉高时,表面成功握手,数据才得以传输。
【例】比如下图,当前clk上升沿检测到awvalid和awready都拉高,aw握手成功,此时32位的awaddr地址得以写入bram。
【例】比如下图,当前clk上升沿检测到wvalid和wready都拉高,w握手成功,此时32位的wdata数据得以写入bram。
读写BRAM需要用到四个的AXI通道。每一个通道下都有很多端口,包括常规的数据线和valid-ready线:
① 写地址通道AW(address write)
//写地址通道AW
reg [31:0] awaddr; //in(视角:bram)
reg awvalid; //in
wire awready; //out
② 写数据通道W(write)
//写数据通道W
reg [31:0] wdata; //in
reg wvalid; //in
wire wready; //out
reg [3:0] wstrb; //in
③ 写响应通道B
//写响应通道B
reg bready; //in:表示主机准备好接收bram的数据
wire bvalid; //out:表示bram可以给master发送数据
wire [1:0] bresp; //out
④ 读地址通道AR(address read)
//读地址通道AR
reg [31:0] araddr; //in
reg arvalid; //in
wire arready; //out
⑤ 读数据通道R(read)
//读数据通道R
wire [31:0] rdata; //out
wire rvalid; //out
reg rready; //in
wire [1:0] rresp; //out
博主画了一张图方便理解和查询:
这是BRAM的IP核设置界面,可以清晰地看到各个通道。
突发读和突发写时序
有关AXI进行BRAM读写的细节问题
【主从valid-ready互不干扰】
正常工作情况下valid不等待ready,ready也不等待valid。因为主机和从机的逻辑判断是独立的,都是根据自己的情况拉高valid或者ready,换句话说就是:“主机想发数据就把自己的valid拉高,从机能接收数据就把自己的ready拉高”。正好握上了数据就开始传。
【有一边出现繁忙,才会出现等待握手的情况】
如果现在主机可以发送数据,拉高了valid,但是从机处于繁忙状态不能接收数据,没有拉高ready。这个时候主机valid信号就要等待从机退出繁忙并将ready拉高。
代码详解
我们书写代码,是站在“主机master”的视角,去操控bram的。
我们的程序中关于valid-ready双向握手协议的部分,只需要操纵主机的三个valid信号(awvalid,wvalid,arvalid)和一个ready信号(rready),从机bram的ready不需要我们操心。如果主从握手成功,数据便会成功传输,即对于数据传输的过程只要握手成功便会进行,也不需要我们操心。
- 状态机——状态空间:
由于AXI读写时序较为复杂,为了更加好地操控时序和debug,博主使用状态机进行书写。
reg [3:0] STATE;
parameter wr_addr = 4'b0000;
parameter wr_addr_shakehand = 4'b0001;
parameter wr_data = 4'b0010;
parameter wr_data_shakehand = 4'b0011;
parameter write = 4'b0100;
parameter rd_addr = 4'b0101;
parameter rd_addr_shakehand = 4'b0110;
parameter rd_data = 4'b0111;
parameter rd_data_shakehand = 4'b1000;
parameter read = 4'b1001;
parameter init = 4'b1010;
parameter buffer = 4'b1011;
parameter stop = 4'b1111;
- 状态机——状态跳转:
always@(posedge clk or negedge rst_n)
beginif(!rst_n) STATE <= init;else begincase(STATE)init: beginif(init_cnt == 5) STATE <= wr_addr;else STATE <= init; endwr_addr: STATE <= wr_addr_shakehand;wr_addr_shakehand: beginif(awvalid == 1'b1 && awready == 1'b1) STATE <= wr_data;else STATE <= wr_addr_shakehand; endwr_data: STATE <= wr_data_shakehand;wr_data_shakehand: beginif(wvalid == 1'b1 && wready == 1'b1) STATE <= write;else STATE <= wr_data_shakehand;endwrite: STATE <= buffer;buffer: STATE <= rd_addr;rd_addr: STATE <= rd_addr_shakehand;rd_addr_shakehand: beginif(arvalid == 1'b1 && arready == 1'b1) STATE <= rd_data;else STATE <= rd_addr_shakehand;endrd_data: STATE <= rd_data_shakehand;rd_data_shakehand: beginif(rready == 1'b1 && rvalid == 1'b1) STATE <= read;else STATE <= rd_data_shakehand;endread: STATE <= stop;stop: STATE <= stop;default: STATE <= init;endcaseend
end
- 写地址通道AW:
① 给出要写入的地址ADDRESS,并将awvalid拉高,等待bram拉高awready进行握手。
(准确来说不是等待,因为bram只要满足条件就会拉高awready,主从逻辑独立。此处为方便表达。)
② awvalid-awready握手后,数据awaddr传输完成,此时主机要主动拉低awvalid。
//写地址通道AW
parameter ADDRESS = 114;
always@(posedge clk or negedge rst_n)
beginif(!rst_n) beginawaddr <= 32'b0;awvalid <= 1'b0;end else if(STATE == wr_addr) beginawaddr <= ADDRESS;awvalid <= 1'b1;endelse beginawaddr <= awaddr;awvalid <= awvalid;end
end
always@(posedge clk or negedge rst_n)
beginif(STATE == wr_addr_shakehand) beginif(awvalid == 1'b1 && awready == 1'b1) beginawvalid <= 1'b0; //成功握手,valid拉低awaddr <= 32'b0;endend
end
- 写数据通道W:
① 给出要写入的数据WR_DATA,同时拉高wvalid,等待bram拉高wready进行握手
② wvalid-wready握手后,数据wdata传输完成,此时主机要主动拉低wvalid。
③ 对于wstrb,在数据传输时赋为 4’b1111,表示wdata32位数据均有效。
(wstrb:写数据有效的字节线,用来表明哪8bits数据是有效的。)
//写数据通道W
parameter WR_DATA = 514;
always@(posedge clk or negedge rst_n)
beginif(!rst_n) beginwdata <= 32'b0;wvalid <= 1'b0;wstrb <= 4'b0;endelse if(STATE == wr_data) beginwdata <= WR_DATA;wvalid <= 1'b1;wstrb <= 4'b1111;end else beginwdata <= wdata;wvalid <= wvalid;wstrb <= wstrb;end
end
always@(posedge clk or negedge rst_n)
beginif(STATE == wr_data_shakehand) beginif(wvalid == 1'b1 && wready == 1'b1) beginwvalid <= 1'b0;wstrb <= 4'b0000;wdata <= 0;endelse beginwvalid <= wvalid;wstrb <= wstrb;endend
end
- 写响应通道B:
在AXI-Lite中,我们只需要管bready这一个端口就行。我们只需要一直拉高bready就行。
(bready:主机接受响应就绪信号 。该信号表示主机是否能够接受响应信息。1 = 主机就绪,0 = 主机未就绪。)
对于bvalid和bresp不需要我们操心,因为那是bram给到主机的,不由我们控制。
//写响应通道B
always@(posedge clk or negedge rst_n)
beginif(!rst_n) beginbready <= 1'b1;endelse if(STATE == write || STATE == read) beginbready <= 1'b1;end
end
- 读地址通道AR:
① 给出要读取的地址ADDRESS,并将arvalid拉高,等待bram拉高arready进行握手。
② arvalid-arready握手后,数据araddr传输完成,此时主机要主动拉低arvalid。
//读地址通道AR
always@(posedge clk or negedge rst_n)
beginif(!rst_n) beginaraddr <= 32'b0;arvalid <= 1'b0;endelse if (STATE == rd_addr) beginaraddr <= ADDRESS;arvalid <= 1'b1;endelse beginaraddr <= araddr;arvalid <= arvalid;end
end
always@(posedge clk or negedge rst_n)
beginif(STATE == rd_addr_shakehand) beginif(arvalid == 1'b1 && arready == 1'b1) beginarvalid <= 1'b0;endelse arvalid <= arvalid;end
end
- 读数据通道R:
① 将rready拉高,等待bram拉高rvalid进行握手。
② rready-rvalid握手后,数据rdata传输完成,此时主机要主动拉低rready。
(注意:此时的数据流向是bram→主机,因此主机端为ready信号,bram端为valid信号。)
//读数据通道R
always@(posedge clk or negedge rst_n)
beginif(!rst_n) beginrready <= 1'b0;endelse if(STATE == rd_data) beginrready <= 1'b1;end
end
always@(posedge clk or negedge rst_n)
beginif(STATE == rd_data_shakehand) beginif(rready == 1'b1 && rvalid == 1'b1) beginrready <= 1'b0;endelse rready <= rready;end
end
- 实例化block memory generator IP核模块:
blk_mem_gen_0 mybram(.s_aclk(clk),.s_aresetn(rst_n),.s_axi_awaddr(awaddr),.s_axi_awvalid(awvalid),.s_axi_awready(awready),.s_axi_wdata(wdata),.s_axi_wvalid(wvalid),.s_axi_wready(wready),.s_axi_wstrb(wstrb),.s_axi_bready(bready),.s_axi_bvalid(bvalid),.s_axi_bresp(bresp),.s_axi_araddr(araddr),.s_axi_arvalid(arvalid),.s_axi_arready(arready),.s_axi_rready(rready),.s_axi_rvalid(rvalid),.s_axi_rdata(rdata));
完整代码(包含Testbench)
module axi4_light_bram(
input wire clk,
input wire rst_n);reg [3:0] STATE;
parameter wr_addr = 4'b0000;
parameter wr_addr_shakehand = 4'b0001;
parameter wr_data = 4'b0010;
parameter wr_data_shakehand = 4'b0011;
parameter write = 4'b0100;
parameter rd_addr = 4'b0101;
parameter rd_addr_shakehand = 4'b0110;
parameter rd_data = 4'b0111;
parameter rd_data_shakehand = 4'b1000;
parameter read = 4'b1001;
parameter init = 4'b1010;
parameter buffer = 4'b1011;
parameter stop = 4'b1111;//写地址通道AW
reg [31:0] awaddr; //in(视角:bram)
reg awvalid; //in
wire awready; //out
//写数据通道W
reg [31:0] wdata; //in
reg wvalid; //in
wire wready; //out
reg [3:0] wstrb; //in
//写响应通道B
reg bready; //in:表示主机准备好接收bram的数据
wire bvalid; //out:表示bram可以给master发送数据
wire [1:0] bresp; //out
//读地址通道AR
reg [31:0] araddr; //in
reg arvalid; //in
wire arready; //out
//读数据通道R
wire [31:0] rdata; //out
wire rvalid; //out
reg rready; //in
wire [1:0] rresp; //out//初始缓冲init
reg [3:0] init_cnt;
always@(posedge clk or negedge rst_n)
beginif(!rst_n) init_cnt <= 0;else init_cnt <= (init_cnt == 5) ? 0 : (init_cnt + 1);
endalways@(posedge clk or negedge rst_n)
beginif(!rst_n) STATE <= init;else begincase(STATE)init: beginif(init_cnt == 5) STATE <= wr_addr;else STATE <= init; endwr_addr: STATE <= wr_addr_shakehand;wr_addr_shakehand: beginif(awvalid == 1'b1 && awready == 1'b1) STATE <= wr_data;else STATE <= wr_addr_shakehand; endwr_data: STATE <= wr_data_shakehand;wr_data_shakehand: beginif(wvalid == 1'b1 && wready == 1'b1) STATE <= write;else STATE <= wr_data_shakehand;endwrite: STATE <= buffer;buffer: STATE <= rd_addr;rd_addr: STATE <= rd_addr_shakehand;rd_addr_shakehand: beginif(arvalid == 1'b1 && arready == 1'b1) STATE <= rd_data;else STATE <= rd_addr_shakehand;endrd_data: STATE <= rd_data_shakehand;rd_data_shakehand: beginif(rready == 1'b1 && rvalid == 1'b1) STATE <= read;else STATE <= rd_data_shakehand;endread: STATE <= stop;stop: STATE <= stop;default: STATE <= init;endcaseend
end//写地址通道AW
parameter ADDRESS = 114;
always@(posedge clk or negedge rst_n)
beginif(!rst_n) beginawaddr <= 32'b0;awvalid <= 1'b0;end else if(STATE == wr_addr) beginawaddr <= ADDRESS;awvalid <= 1'b1;endelse beginawaddr <= awaddr;awvalid <= awvalid;end
end
always@(posedge clk or negedge rst_n)
beginif(STATE == wr_addr_shakehand) beginif(awvalid == 1'b1 && awready == 1'b1) beginawvalid <= 1'b0; //成功握手,valid拉低awaddr <= 32'b0;endend
end//写数据通道W
parameter WR_DATA = 514;
always@(posedge clk or negedge rst_n)
beginif(!rst_n) beginwdata <= 32'b0;wvalid <= 1'b0;wstrb <= 4'b0;endelse if(STATE == wr_data) beginwdata <= WR_DATA;wvalid <= 1'b1;wstrb <= 4'b1111;end else beginwdata <= wdata;wvalid <= wvalid;wstrb <= wstrb;end
end
always@(posedge clk or negedge rst_n)
beginif(STATE == wr_data_shakehand) beginif(wvalid == 1'b1 && wready == 1'b1) beginwvalid <= 1'b0;wstrb <= 4'b0000;wdata <= 0;endelse beginwvalid <= wvalid;wstrb <= wstrb;endend
end//写响应通道B
always@(posedge clk or negedge rst_n)
beginif(!rst_n) beginbready <= 1'b1;endelse if(STATE == write || STATE == read) beginbready <= 1'b1;end
end//读地址通道AR
always@(posedge clk or negedge rst_n)
beginif(!rst_n) beginaraddr <= 32'b0;arvalid <= 1'b0;endelse if (STATE == rd_addr) beginaraddr <= ADDRESS;arvalid <= 1'b1;endelse beginaraddr <= araddr;arvalid <= arvalid;end
end
always@(posedge clk or negedge rst_n)
beginif(STATE == rd_addr_shakehand) beginif(arvalid == 1'b1 && arready == 1'b1) beginarvalid <= 1'b0;endelse arvalid <= arvalid;end
end//读数据通道R
always@(posedge clk or negedge rst_n)
beginif(!rst_n) beginrready <= 1'b0;endelse if(STATE == rd_data) beginrready <= 1'b1;end
end
always@(posedge clk or negedge rst_n)
beginif(STATE == rd_data_shakehand) beginif(rready == 1'b1 && rvalid == 1'b1) beginrready <= 1'b0;endelse rready <= rready;end
endblk_mem_gen_0 mybram(.s_aclk(clk),.s_aresetn(rst_n),.s_axi_awaddr(awaddr),.s_axi_awvalid(awvalid),.s_axi_awready(awready),.s_axi_wdata(wdata),.s_axi_wvalid(wvalid),.s_axi_wready(wready),.s_axi_wstrb(wstrb),.s_axi_bready(bready),.s_axi_bvalid(bvalid),.s_axi_bresp(bresp),.s_axi_araddr(araddr),.s_axi_arvalid(arvalid),.s_axi_arready(arready),.s_axi_rready(rready),.s_axi_rvalid(rvalid),.s_axi_rdata(rdata));endmodule
Testbench如下:
`timescale 1ns / 1ps
module axi4_light_bram_tb();reg clk;
reg rst_n;initial beginclk = 1'b0;rst_n = 1'b0;#150rst_n = 1'b1;
endalways # 10 clk <= ~clk;axi4_light_bram axi4_light_bram_uut(.clk(clk),.rst_n(rst_n)
);endmodule
仿真结果分析
心得和后续工作
- AXI总线算是比较复杂的总线,虽然后续开发的过程中大多不需要自己去写读写时序,但是经过这么一个过程能加深我对AXI协议的理解。“握手”是协议的精髓所在,它有效地控制着整个时序,并且保证主从机互相了解对方的状态,方便数据传输的开始和终止。
- 本程序只是最简单的AXI-Lite,如果书写常规的AXI协议,将多出一些端口需要配置(比如id,len,size),后续博主会在这个代码的基础上写常规AXI协议。
- 本程序只进行了一次数据的读和写。但是使用AXI协议可以同时写很多数据,然后用last指示最后一个数据,此类云云。后续我会去写这样的代码的。
AXI4-Lite总线读写BRAM相关推荐
- 编写AXI4协议读写BRAM并仿真验证
前篇博文我们试验了AXI4-Lite协议读写BRAM,这里我们试验一下完整的AXI4协议.流程跟AXI4-Lite是一样的.省略的部分请参考:编写AXI4-Lite协议读写BRAM并仿真验证 一. 建 ...
- iic获取salve设备地址_Linux下使用IIC总线读写EEPROM(读写i2c从设备通用程序)
Linux 下使用IIC总线 读写 EEPROM by 韩大卫 @吉林师范大学 handawei@jusontech.com 转载请务必表明出处 ******************* ******* ...
- Xilinx-Spartan6-学习笔记(24):通过SPI总线读写FLASH
Xilinx-Spartan6-学习笔记(24):通过SPI总线读写FLASH 利用SPI总线实现对FLASH进行读写,写入255个数据再读出255个数据.(这里为了模拟SDO信号,随机生成了0~1信 ...
- DDR3 AXI4 IP核读写仿真实验(2)
上篇blog中记录了DDR3 AXI4接口的IP配置详情,这一文章则是记录自己在项目工程以及学习中对于DDR3的读写测试.先讲一下大概的工程架构:产生16位的自加数写进写FIFO中,当FIFO中的数达 ...
- stm32如何读取并口_CH374T STM32模拟并口总线读写U盘
| |-- ARM32: 32位指令ARM单片机 | | |-- LIB9: U盘文件级子程序库,ADS V1.2,小端数据格式 | | | |-- EXAM1: C示例源程序,查询方式 | | | ...
- [architecture]-ARM AMBA/AXI/ACE/LITE总线介绍
快速链接: .
- PCIe的XDMA应用
之前介绍的PCIe实物模型为PIO模式,可编程PIO模式,软件控制CPU在主机总线上发起一个存储器或IO读写总线周期,并以映射在PCIe设备地址空间的一个地址为目标,根据PCIe总线宽度的区别,在每个 ...
- FPGA控制DDR读写(AXI4总线接口)
FPGA控制DDR读写(AXI4总线接口) 范围 本文适用于FPGA控制DDR读写 MIG核 MIG信号注释 DDR型号为 MT41K256M16TW-107 下面是MIG IP核的相关信号 图2.1 ...
- 深入 AXI4总线 (四):RAM 读取实战
光说不练,云玩家.这篇文章中我们就通过访问一个 AXI4 接口的 RAM 的实际操作,加深我们对 AXI4 总线的理解. 我们的实验平台是 ISE 14.7 以及 modelsim 10.2, RAM ...
最新文章
- java 原子量_Java原子量 - Rickxue的个人空间 - OSCHINA - 中文开源技术交流社区
- java 商城 jfinal,Jfinal开发农副产品电子商城系统,完整前后台系统
- 20.27分发系统介绍;20.28expect脚本远程登录;20.29expect脚本远程执行命令;20.30expect脚本传递参数...
- Android启动过程深入解析
- EJB3.0开发环境的搭建
- 什么是 SAP UI5 的 Hybrid Web Containers
- 字节、编码、字符、字符集 专题
- 【MFC系列-第18天】企业信息管理软件开发
- 2021快手电商数据报告
- 杭电2098--分拆素数和
- (附源码)APP+springboot订餐APP 毕业设计 190711
- VoIP服务器Asterisk安装及部署
- WPS文字标题级别的设置和调整——多种方法任选
- android系统9有OTG功能吗,随身HiFi 安卓OTG功能在音频上的妙用
- 红灯停绿灯行c语言编程,C语言 实验三C语 实验三.doc
- 编译工具:XMake 和 CMake对比分析
- Redis(九)Redis的过期时间操作以及部分常用命令
- MATLAB 基础与通信系统仿真
- 英文论文写作中的词汇句式总结
- 国际标准电话和手机号码的正确写法
热门文章
- NAC配置与管理——3
- HDU2067——小兔的棋盘(迷宫,动态规划,卡特兰数)
- java设计模式--1.单例模式
- 以太坊椭圆曲线数字签名
- Box2D v2.1.0用户手册(4)——碰撞模块(Collision Module)
- china-pub网上书店计算机图书近周销售排行top5
- 软件本地化外包测试流程分析
- python做计算器_python实现计算器功能
- 如何以正确的顺序重新安装驱动程序 | Dell 中国
- 苹果7全网通经常显示无服务器,抖音教会我8个iPhone隐藏技巧,就连苹果老用户也未必全知道...