AXI4总线学习
IP核选择:AXI BRAM Controller
IP文档:pg078
AXI总线介绍
ARM AMBA(Advance Microcontroller Bus Architecture),是ARM在1996年提出的一个微控制器的片上总线协议,AHB和APB等总线协议都是AMBA的一部分
AXI总线类型:AXI4、AXI4-Lite、AXI-Stream
- AXI4用于高性能内存映射(Menory-Mapped)型的通信,主要用于大块数据的读写,如DDR、BRAM的数据搬移
- AXI4-Lite用于简单低吞吐量的内存映射型通信上(比如简单的寄存器读写),主要用于外设模块的配置,状态寄存器的读写等
- AXI4-Stream用于高速的数据流传输,无需地址管理,主要用于高速AD数据、视频数据等的传输
关键特点:
1.分离的地址 / 控制、数据相位
2.使用字节线来支持非对齐的数据传输
3.使用基于burst的传输,只需要传输首地址
4.分离的读、写数据通道,提供低功耗DMA
5.支持多种寻址方式
6.支持乱序传输
7.允许容易的添加寄存器级来进行时序收敛
-
AXI总线是点对点的传输协议。总线两端发起读写事务的一端称为主设备,响应事务的一端是从设备。点对点传输意味着本条总线只能用于两个设备间通信,和SPI(图一)等接口不一样,不存在片选从设备这样的机制。中央互联(Interconnect)提供了设备间一对多,多对多的访问能力,可以理解为路由器的作用,AXI可以用在三种接口之间,master/slave,mater/interconnect,master/interconnect
-
AXI协议是一个读写通道分离的总线协议,数据总线分离,地址和控制总线分分离,这样可以在总线上全双工的读写数据,提高总线带宽。(写事务并不一定非要等地址和控制信号发送以后再发送写数据,AXI允许先发送写数据再发送写地址和写控制;AXI也允许先发送多个写地址和写控制,再慢慢发送写数据,而不是一次地址一次数据这样的传统总线模式。)
-
AXI采用握手机制,各个通道都有一对握手信号,valid和ready,只有两个信号都有效时,通道的数据才开始传输,这样AXI总线上的主从设备都能根据自己的情况4控制开启和中断通道的数据传输,双向流控机制
时钟和复位
信号名 | 来源 | 描述 |
---|---|---|
ACLK | system clk | 系统时钟 |
ARESETN | system rst | 复位信号低有效,释放的时候要和aclk上升沿同步 |
AXI的五组独立通道
Write Address Channel 写地址通道
突发传输指的是传输一次起始地址后,进行多次地址上连续的读写操作。
突发方式BURST:
FIXED(固定突发模式):每次突发传输的地址都相同
INCR(增量突发模式):突发传输地址递增,地增量与突发尺寸相关
WRAP(回卷突发模式):突发传输的地址可溢出性递增,突发长度仅支持2、4、8、16.地址空间被划分为长度[ SIZE * LEN ]的块,传输地址不会超出起始地址所在的块,一旦递增超出,则回到该块的起始地址
信号名 | 来源 | 描述 |
---|---|---|
AWID | m | 写地址ID(用于区分该地址属于哪个写地址组) |
AWADDR | m | 写地址 |
AWLEN | m | 突发长度(一次突发传输中数据传输的个数) |
AWSIZE | m | 突发尺寸(每次突发传输中数据的位宽) |
AWBURST | m | 突发方式(2’b00 FIXED,2’b01 INCR,2’b10 WRAP) |
AWCACHE | m | 存储类型(标记系统需要的传输类型) |
AWPROT | m | 保护模式 |
AWQOS | m | QoS标识符 |
AWREGION | m | region标识符(当slave有多种逻辑接口时标识使用的逻辑接口) |
AWUSER | m | 用户自定义信号 |
AWVALID | m | 写地址有效信号(有效时表示AWADDR上地址有效) |
AWREADY | s | 写从机就绪信号(有效时表示从机准备好接收地址) |
Write Data Channel 写数据通道
写通道比读通道多了WSTRB信号,用于标识写数据中有效传输的字节,告知从机不需要接收。一次突发传输开启后就不能提前终止,主机可以通过控制WSTRB信号使能部分有效字节,来减少写传输的数量
信号名 | 来源 | 描述 |
---|---|---|
WDATA | m | 写数据 |
WSTRB | m | 数据段有效(标记写数据有效字节) |
WLAST | m | last信号(有效时表示当前为突发传输最后一个数据) |
WUSER | m | 用户自定义信号 |
WVALID | m | 写有效信号(有效时表示WDATA上数据有效) |
WREADY | s | 写ready信号(有效时表示从机准备好接收数据) |
Write Response Channel 写响应信号
从机将写完成情况回复给主机,针对一次突发传输,而不每一次的写数据操作,一次突发传输可以包括多次写操作
为啥写有响应通道,读没有?
主机在读取数据的时候,数据在读数据通道由从机流向主机,读响应信号也是由从机流向主机,可以合并在一起。主机在写数据的时候,数据在写数据通道由主机流向从机,写回复信号是报告从机写操作的完成情况,由从机流向主机,没办法合并,就单独来一个写响应通道
响应信号RESP:
OKAY(2’b00):正常访问成功
EXOKAY(2’b01):独占访问成功
SLVERR(2’b10):从机错误。表明访问已经成功到了从机,但从机希望返回一个错误的情况给主机。
DECERR(2’b11):译码错误。一般由互联组件给出,表明没有对应的从机地址
信号名 | 来源 | 描述 |
---|---|---|
BID | s | 响应ID |
BRESP | s | 写响应 |
BUSER | s | 用户自定义信号 |
BVALID | s | 写响应信号有效 |
BREADY | m | 写响应ready(主机准备好接受写响应信号) |
Read Address Channel 读地址信号
信号名 | 来源 | 描述 |
---|---|---|
ARID | m | 读地址ID |
ARADDR | m | 读地址 |
ARLEN | m | 突发长度 |
ARSIZE | m | 突发尺寸(每次突发传输的byte数) |
ARBURST | m | 突发类型(FIXED,INCR,WRAP) |
ARCACHE | m | 存储类型 |
ARPROT | m | 保护类型 |
ARQOS | m | QoS标识符 |
ARREGION | m | 区域标识符 |
ARUSER | m | 用户自定义 |
ARVALID | m | 读地址有效(有效时表示ARADDR上地址有效) |
ARREADY | s | 写有效信号(有效时表示从机准备好接收读地址) |
Read Data Channel 读数据信号
信号名 | 来源 | 描述 |
---|---|---|
RID | s | 读ID标签 |
RDATA | s | 读数据 |
RRESP | s | 读响应 |
RLAST | s | 有效时表示为突发传输的最后一个 |
RUSER | s | 用户自定义 |
RVALID | s | 读数据有效信号 |
RREADY | m | 主机就绪信号(有效时表示) |
读写传输
握手机制
AXI使用基于valid/ready的握手机制数据传输协议。传输源端使用valid表明地址/控制信号、数据是有效的,目的端使用ready表明自己能够接收信息
valid和ready的先后关系有三种:
- valid线有效,等待ready有效后完成传输(valid一旦有效后在传输完成前不可取消)
- ready先有效,等待valid有效后完成传输(ready可以在valid有效前撤销)
- valid和ready同时有效,立刻完成传输
写地址通道:当主机驱动有效的地址和控制信号时,主机拉高AWVALID,保持拉高状态,直到时钟上升沿采样到从机的AWREDY。AWREADY信号默认可高可低(如果为低,一次传输至少需要两个周期,一个用来断言AWVALID,一个用来断言AWREADY);其他通道类似的
通道顺序
为了防止死锁,传输通道规定了先后顺序
- 写响应通道传输必须在写操作完成以后进行
- 读数据通道传输必须在读地址通道传输后进行
- 必须遵循一系列的状态依赖关系
valid信号不能等待ready信号
AXI接口可以检测到valid信号后再拉高ready,也可以检测到valid之前就拉高ready
数据通道的传输
使用STRB字节选择信号,标志数据中的对应字节是否有效,位宽为[DATA_WITTH / 8 : 0]
支持窄带传输(narrow transfers)。当传输的数据位宽小于xDATA总线带宽时,为窄带传输,每次使用的数据位数不同:
- 固定地址的突发下,使用同一段数据信号线
- 递增地址的突发下,使用不同段信号线
举例:
- 5次突发传,起始地址为0,每次传输为8bit,数据总线为32bit,突发类型为INCR
- 3次突发,起始地址为4,每次传输32bit,数据总线为64bit,突发类型为INCR
支持不对齐传输(Unaligned transfers)。
举例
指定控制为内部Intermal
`timescale 1ns / 1ps
/*
data width: 32
memory depth:262166
memory width:20
*/
module tb_axi4 ( );
parameter DATA_WIDTH = 32;
parameter ADDR_WIDTH = 20;
// global
reg aclk ;
reg aresetn ; // 异步复位,低有效
// write address channel
wire m_axi_awid ;
wire [ADDR_WIDTH - 1: 0] m_axi_awaddr ;
wire [ 7: 0] m_axi_awlen ; // Burst Length:突发传输长度 1~256 len[7:0]+1
wire [ 2: 0] m_axi_awsize ; // Brust Size:突发尺寸,单次突发传输byte数
wire [ 1: 0] m_axi_awburst ; // Brust Type:突发类型,FIXED(2'b00),INCR(2'b01),WRAP(2'b10)
wire m_axi_awlock ; // 写地址锁信号,提供原子操作传输和屏障事务的信息
wire [ 3: 0] m_axi_awcache ;
wire [ 2: 0] m_axi_awprot ;
wire [ 3: 0] m_axi_awqos ;
wire m_axi_awuser ;
wire m_axi_awvalid ;
wire m_axi_awready ;
// write data channel
wire [DATA_WIDTH - 1: 0] m_axi_wdata ;
wire [ 3: 0] m_axi_wstrb ; // 写字节选择
wire m_axi_wlast ; // 标志突发传输最后一个数据
wire m_axi_wuser ;
wire m_axi_wvalid ;
wire m_axi_wready ;
// wire response channel
wire m_axi_bid ;
wire [ 1: 0] m_axi_bresp ;
wire m_axi_buser ;
wire m_axi_bvalid ;
wire m_axi_bready ;
// read address channel
wire m_axi_arid ;
wire [ADDR_WIDTH - 1: 0] m_axi_araddr ;
wire [ 7: 0] m_axi_arlen ;
wire [ 2: 0] m_axi_arsize ;
wire [ 1: 0] m_axi_arburst ;
wire m_axi_arlock ;
wire [ 3: 0] m_axi_arcache ;
wire [ 2: 0] m_axi_arprot ;
wire [ 3: 0] m_axi_arqos ;
wire m_axi_aruser ;
wire m_axi_arvalid ;
wire m_axi_arready ;
// read data channel
wire m_axi_rid ;
wire [DATA_WIDTH - 1: 0] s_axi_rdata ;
wire [ 1: 0] m_axi_rresp ;
wire m_axi_rlast ;
wire m_axi_ruser ;
wire m_axi_rvalid ;
wire m_axi_rready ;
// user insterface
reg master_rst ;
reg wr_start ;
reg [ADDR_WIDTH - 1: 0] wr_addrs ;
reg [ 7: 0] wr_len ;
reg [DATA_WIDTH - 1: 0] wr_data ;
wire wr_handshake;
wire wr_busy ;
wire wr_done ;
reg rd_start ;
reg [ADDR_WIDTH - 1: 0] rd_addrs ;
reg [ 7: 0] rd_len ;
wire [DATA_WIDTH - 1: 0] rd_data ;
wire rd_handshake;
wire rd_busy ;
wire rd_done ;
initial begin
aclk = 0;
aresetn = 0;
#100
aresetn = 1;
end
always # (5) aclk = ~aclk;
initial begin
master_rst = 0;
wr_start = 0; //
wr_addrs = 0;
wr_len = 0;
wr_data = 0;
#100
@(posedge aclk)
// aresetn <= 1;
@(posedge aclk)
wr_addrs = 0;
wr_len = 15;
@(posedge aclk)
if (wr_busy == 1'b0) // 当前没有写事务
wr_start = 1; // 开启一次写事务
@(posedge aclk)
wr_start = 0;
repeat ( 30) @(posedge aclk);
wr_addrs = 1024;
wr_len = 255;
@(posedge aclk)
if (wr_busy == 1'b0)
wr_start <= 1;
// @(posedge aclk) // 不太理解这里wr_start拉高一拍,不能正常开启写传输.....明明跟第一次写一样,但是这里在时钟上升沿采样不到
// 其他三次突发开启,start都是拉高一拍,就只有这里偶尔可以偶尔不行
#11
wr_start = 0;
end
initial begin
rd_start = 0;
rd_addrs = 0;
rd_len = 0;
repeat (500) @(posedge aclk)
@(posedge aclk)
rd_addrs = 1024;
rd_len = 255;
@(posedge aclk)
if (rd_busy == 1'b0)
rd_start = 1;
@(posedge aclk)
rd_start = 0;
repeat (300) @(posedge aclk)
@(posedge aclk)
rd_addrs = 0;
rd_len = 15;
@(posedge aclk)
if (rd_busy == 1'b0)
rd_start = 1;
@(posedge aclk)
rd_start = 0;
end
always @ (posedge aclk)
if (wr_handshake)
wr_data = wr_data + 1;
/* AXI WRITE */
parameter W_IDLE = 3'd0,
WA_WAIT = 3'd1, // write address
WA_START= 3'd2,
WD_WAIT = 3'd3, // wrte data
WD_PROC = 3'd4,
WR_WAIT = 3'd5, // write response
WR_DONE = 3'd6;
reg [ 2: 0] wr_state ;
reg [ADDR_WIDTH - 1: 0] reg_wr_addrs; // 写地址
reg [ 7: 0] reg_wr_len ; // 突发长度
reg reg_awvalid ; // 写地址有效
reg reg_wvalid ; // 写数据有效
reg reg_w_last ;
// write state
always @ (posedge aclk)
if (aresetn == 1'b0)
begin
wr_state <= W_IDLE;
reg_wr_addrs<= 0;
reg_wr_len <= 0;
reg_awvalid <= 0;
reg_wvalid <= 0;
reg_w_last <= 0;
end
else
begin
if (master_rst) // 主机复位
wr_state <= W_IDLE;
else
begin
case (wr_state)
W_IDLE: begin
if (wr_start) // 开启一次写事务
begin
wr_state <= WA_WAIT;
reg_wr_addrs<= wr_addrs;
reg_wr_len <= wr_len;
end
reg_awvalid <= 0;
reg_wvalid <= 0;
reg_w_last <= 0;
end
WA_WAIT: begin
wr_state <= WA_START;
end
WA_START: begin // 拉高写地址有效信号
wr_state <= WD_WAIT;
reg_awvalid <= 1;
end
WD_WAIT: begin // 从机响应写地址,拉低写地址有效信号,拉高数据有效信号
if (m_axi_awvalid && m_axi_awready)
begin
wr_state <= WD_PROC;
reg_awvalid <= 0;
reg_wvalid <= 1;
end
end
WD_PROC: begin // 突发传输len次
if (m_axi_wvalid && m_axi_wready)
begin
if (reg_wr_len == 0)
begin
wr_state <= WR_WAIT;
reg_wvalid <= 0;
end
else
reg_wr_len <= reg_wr_len - 1;
if (reg_wr_len == 1)
reg_w_last <= 1;
else
reg_w_last <= 0;
end
end
WR_WAIT: begin // 等待写相应通道信号握手
if (m_axi_bvalid && m_axi_bready)
wr_state <= WR_DONE;
end
WR_DONE: begin
wr_state <= W_IDLE;
end
default: begin
wr_state <= W_IDLE;
end
endcase
end
end
// assign m-AXI interface
// aw写地址
assign m_axi_awid = 1'b0; // default value
assign m_axi_awaddr = reg_wr_addrs;
assign m_axi_awlen = reg_wr_len;
assign m_axi_awsize = $clog2(DATA_WIDTH / 8); // 4byte
assign m_axi_awburst = 2'b01; // default value 增量突发
assign m_axi_awlock = 1'b0; // default value
assign m_axi_awcache = 4'b0011; // default value
assign m_axi_awprot = 3'b000; // default value
assign m_axi_awqos = 4'b0000; // default value QOS标识符
assign m_axi_awuser = 1'b0; // default value 用户自定义
assign m_axi_awvalid = reg_awvalid;
// w写数据
assign m_axi_wdata = wr_data;
assign m_axi_wstrb = {$clog2(DATA_WIDTH / 8){1'b1}};
assign m_axi_wlast = reg_w_last;
assign m_axi_wuser = 1'b0; // default value
assign m_axi_wvalid = reg_wvalid;
// b写响应
assign m_axi_bready = m_axi_bvalid;
// user interface
assign wr_handshake = m_axi_wvalid & m_axi_wready;
assign wr_busy = (wr_state != W_IDLE);
assign wr_done = (wr_state == WR_DONE);
/* ASI READ */
parameter R_IDLE = 3'd0,
RA_WAIT = 3'd1,
RA_START= 3'd2,
RD_WAIT = 3'd3,
RD_PROC = 3'd4,
RD_DONE = 3'd5;
reg [ 2: 0] rd_state ;
reg [ADDR_WIDTH - 1: 0] reg_rd_addrs;
reg [ 7: 0] reg_rd_len ;
reg reg_arvalid ;
reg reg_r_last ;
// read state
always @ (posedge aclk or negedge aresetn)
if (aresetn == 1'b0)
begin
rd_state <= R_IDLE;
reg_rd_addrs<= 0;
reg_rd_len <= 0;
reg_arvalid <= 0;
end
else
begin
case (rd_state)
R_IDLE: begin
if (rd_start)
begin
rd_state <= RA_WAIT;
reg_rd_addrs<= rd_addrs;
reg_rd_len <= rd_len;
end
reg_arvalid <= 0;
end
RA_WAIT: begin
rd_state <= RA_START;
end
RA_START: begin
rd_state <= RD_WAIT;
reg_arvalid <=1;
end
RD_WAIT: begin
if (m_axi_arvalid && m_axi_arready)
begin
rd_state <= RD_PROC;
reg_arvalid <= 0;
end
end
RD_PROC: begin
if (m_axi_rvalid && m_axi_rready)
begin
if (m_axi_rlast && reg_r_last)
rd_state <= RD_DONE;
else
reg_rd_len <= reg_rd_len - 1;
if (reg_rd_len == 1)
reg_r_last = 1;
else
reg_r_last = 0;
end
end
RD_DONE: begin
rd_state <= R_IDLE;
end
default: begin
rd_state <= R_IDLE;
end
endcase
end
// assign m-AXI interface
// ar读地址
assign m_axi_arid = 1'b0; // default value
assign m_axi_araddr = reg_rd_addrs;
assign m_axi_arlen = reg_rd_len;
assign m_axi_arsize = $clog2(DATA_WIDTH / 8);
assign m_axi_arburst = 2'b01; // default value
assign m_axi_arlock = 1'b0; // default value
assign m_axi_arcache = 4'b0011; // default value
assign m_axi_arprot = 3'b000; // default value
assign m_axi_arqos = 4'b0000; // default value
assign m_axi_aruser = 1'b0; // default value
assign m_axi_arvalid = reg_arvalid;
// r读数据
assign m_axi_rready = m_axi_rvalid;
// user interface
assign rd_handshake = m_axi_rvalid && m_axi_rready;
assign rd_busy = (rd_state != R_IDLE);
assign rd_done = (rd_state == RD_DONE);
// inst
// io相对于从机来说 //
axi_bram_ctrl_0m axi4_inst (
// global
.s_axi_aclk (aclk ), // input wire s_axi_aclk
.s_axi_aresetn (aresetn ), // input wire s_axi_aresetn
// write address channel
.s_axi_awaddr (m_axi_awaddr ), // i
.s_axi_awlen (m_axi_awlen ), // i
.s_axi_awsize (m_axi_awsize ), // i
.s_axi_awburst (m_axi_awburst ), // i
.s_axi_awlock (m_axi_awlock ), // i
.s_axi_awcache (m_axi_awcache ), // i
.s_axi_awprot (m_axi_awprot ), // i
.s_axi_awvalid (m_axi_awvalid ), // i
.s_axi_awready (m_axi_awready ), // o
// write data channel
.s_axi_wdata (m_axi_wdata ), // i
.s_axi_wstrb (m_axi_wstrb ), // i
.s_axi_wlast (m_axi_wlast ), // i
.s_axi_wvalid (m_axi_wvalid ), // i
.s_axi_wready (m_axi_wready ), // o
// write response channel
.s_axi_bresp (m_axi_bresp ), // o
.s_axi_bvalid (m_axi_bvalid ), // o
.s_axi_bready (m_axi_bready ), // i
// read address channel
.s_axi_araddr (m_axi_araddr ), // i
.s_axi_arlen (m_axi_arlen ), // i
.s_axi_arsize (m_axi_arsize ), // i
.s_axi_arburst (m_axi_arburst ), // i
.s_axi_arlock (m_axi_arlock ), // i
.s_axi_arcache (m_axi_arcache ), // i
.s_axi_arprot (m_axi_arprot ), // i
.s_axi_arvalid (m_axi_arvalid ), // i
.s_axi_arready (m_axi_arready ), // o
// read data channel
.s_axi_rdata (s_axi_rdata ), // o
.s_axi_rresp (m_axi_rresp ), // o
.s_axi_rlast (m_axi_rlast ), // o
.s_axi_rvalid (m_axi_rvalid ), // o
.s_axi_rready (m_axi_rready ) // i
);
endmodule
对比文档的写事务时序,是对应的