0.序言
AXI是一种高频率,高带宽,低延迟的总线协议,是一种突发传输协议,即相邻存储器连续进行数据传输。是由ARM公司推出的,后被用于FPGA。主要分为三种类型:AXI_FULL(全功能版),AXI_LITE(简化版),AXI_STREAM(面向数据流的)。本文详细说明AXI_FULL类型,后面两种类型是FULL型的简化。
1.数据手册详解
(1)读写数据时序总图
AXI协议总共有5个数据通道:写数据地址通道,写数据通道,写应答通道,读数据地址通道,读数据通道。
1)在进行写数据时:
主机先通过写数据地址通道给从机传输写数据的地址和写数据模式等信息;
然后主机紧接着,通过写数据通道,给从机连续传输写入的数据;
完成后,从机通过写应答通道,给主机反馈写完成应答信号;
2)在进行读数据时:
主机先通过读数据地址通道给从机传输读数据的地址和读数据模式等信息;
然后从机紧接着,通过读数据通道,给主机连续传输读出的数据;
(2)5个通道
1)写数据地址通道
需要通过通过13个接口发送或接收13个数据:
信号 | 源 | 说明 |
---|---|---|
AWID[C_M_AXI_ID_WIDTH-1:0] | 主机 | 写地址ID,这个信号是写地址信号组的ID tag |
AWADDR[C_M_AXI_ADDR_WIDTH-1:0] | 主机 | 数据的地址,当是突发传输时,只用给首地址,后面数据的地址存储时会自动累加; |
AWLEN[7:0] | 主机 | 突发传输长度,即单次连续写数据个数,实际的突发长度burst_len = AWLEN+1,所以AWLEN为连续写数据的个数减1。要求实际的突发长度burst_len应该为2的指数次幂,即1,2,4,8,16,32,64,128,256…; |
AWSIZE[3:0] | 主机 | 每次写入数据的字节宽度,数据的宽度为2(AWSIZE)个字节。例:AWSIZE=0,数据位宽为20字节,即8位宽;AWSIZE=1,数据位宽为21字节,即16位宽; |
AWBURST[1:0] | 主机 | 突发模式。FIXWED:固定模式,突发多次,每一次传输的地址都不变;ICCR:自增模式,突发多次,地址依次自增;WRAP:包装式突发读写跟增值式突发读写类似。包装式突发读写的地址是包数据的低地址当到达一个包边界。三种模式分别对应:0’b00,0’b01,0b10 |
AWLOCK | 主机 | AXI不支持锁,直接给0; |
AWCACHE[3:0] | 主机 | 缓存模式:有多种模式,一般使用无缓存模式; |
AWPROT[2:0] | 主机 | 保护编码(详见后图):有的AXI可以特权访问/安全访问/指明数据访问还是指令访问这里设置全零模式; |
AWQOS[3:0] | 主机 | AXI协议这个信号没用,给全0; |
AWREGION[3:0] | 主机 | AXI协议这个信号一般没用,给全0; |
AWUSER[C_M_AXI_AWUSER_WIDTH-1 :0] | 主机 | AXI协议这个信号一般没用,给全0; |
AWVALID | 主机 | 重要,主机给从机的写数据地址准备好标志信号; |
AWREADY | 从机 | 重要,从机给主机反馈的接收数据地址准备好标志信号; |
注意:当写数据地址信息准备好后,AWVALID和AWREADY同时有效时,从机才接收写数据地址信息。
下图是对数据手册中AWPROT信号部分的翻译:
2)写数据通道
信号 | 源 | 说明 |
---|---|---|
WID[C_M_AXI_ID_WIDTH-1:0] | 主机 | 写数据ID,这个信号是时写地址信号组的ID tag,必须与AWID匹配; |
WDATA[C_M_AXI_DATA_WIDTH-1 : 0] | 主机 | 写的数据 |
WSTRB[C_M_AXI_DATA_WIDTH/8-1 : 0] | 主机 | 指示写的数据哪些字节是有效的。例子:数据32‘H1234_5678; 掩码:4’b0111;表示第一个字节数据12无效,后三个字节数据34_5678有效; |
WUSER[C_M_AXI_WUSER_WIDTH-1 : 0] | 主机 | 一般不用,给默认0值 |
WLAST | 主机 | 突发写数据最后一个数据的标志信号,在给最后一个数据时,要将此信号拉高一个时钟周期; |
WVALID | 主机 | 重要,主机给从机的写数据准备好标志信号; |
WREADY | 从机 | 重要,从机给主机反馈的接收数据准备好标志信号; |
注意:当写数据信息准备好后,WVALID和WREADY同时有效时,从机才接收写数据信息。
3)写数据响应通道
信号 | 源 | 说明 |
---|---|---|
BID[C_M_AXI_ID_WIDTH-1:0] | 从机 | 响应ID,这个信号是响应信号组的ID tag,必须与AWID匹配; |
BRESP[1:0] | 从机 | 写操作响应信号,指明写数据操作的状态,可能的响应有OKAY、EXOKAY、SLVERR、DECERR |
BUSER[C_M_AXI_WUSER_WIDTH-1 : 0] | 从机 | 一般不用,给默认0值 |
BVALID | 从机 | 重要,从机给主机反馈的写操作响应准备好标志; |
WREADY | 主机 | 重要,主机给从机的接收写操作响应准备好信号; |
注意:当从机的写数据响应信号准备好后,BVALID和BREADY同时有效时,主机才接收写数据响应信号。
4)读数据地址通道
基本同写数据地址通道一致,取值方式也一样。
5)读数据通道
基本信号同写数据通道一致,不同的是读数据通道数据基本是读入的,不用写,只需要在读数据准备好时给从机发送读数据准备好信号,即可读取数据。
信号 | 源 | 说明 |
---|---|---|
RID[C_M_AXI_ID_WIDTH-1:0] | 从机 | 从机输入的读数据ID,这个信号是时写地址信号组的ID tag,必须与ARID匹配; |
RDATA[C_M_AXI_DATA_WIDTH-1 : 0] | 从机 | 从机输入的读取的数据; |
RUSER[C_M_AXI_WUSER_WIDTH-1 : 0] | 从机 | 从机输入的一般不用; |
RLAST | 从机 | 从机输入的突发读数据最后一个数据的标志信号,当此信号拉高时表示读到了最后一个数据; |
RVALID | 从机 | 重要,从机给主机的读数据准备好标志信号; |
RREADY | 主机 | 重要,主机给从机发送的接收数据准备好标志信号; |
注意:当读数据信息准备好后,RVALID和RREADY同时有效时,从机才发送给主机读出的数据。
2.例子
(1)目标
基于AXI4协议向从机存入11到26共16个数据(突发长度为16),起始地址为11。然后,将16个数据,从从机中以突发的形式全部读出。
(2)实现步骤
A 写数据操作
发送写数据地址信息->突发写数据(最后一个数据要拉高写LAST信号)->接收从机应答信号
B 读数据操作
发送读数据地址信息->突发接收数据(输入最后一个数据时,读数据LAST信号会被拉高)
(3)具体实现
(A)AXI接口协议驱动模块
a 数据位宽计算:
在前面读/写数据地址通道时说道,AXI接口协议的主机给从机的数据位宽信息(M_AXI_AWSIZE)是以字节为单位的,比如:M_AXI_AWSIZE=0,表示传输的数据位宽为20个字节,即8位宽;M_AXI_AWSIZE=1,表示传输的数据位宽为21个字节,即16位宽。而在给参数时,我们传参的数据位宽用的是二进制位宽,所以在传给从机时,要将二进制位宽换算成以字节为单位的形式。使用了函数:
function integer bit_wide;
input integer data_in;
for(bit_wide = 0;data_in > 0;bit_wide = bit_wide + 1'b1)
data_in = data_in >> 1;
endfunction
调用函数计算位宽如下:
M_AXI_AWSIZE = bit_wide((C_M_AXI_DATA_WIDTH/8)-1) ;
使用举例:
当数据实际位宽为32位,传参C_M_AXI_DATA_WIDTH=32,则(C_M_AXI_DATA_WIDTH/8)-1=3,在函数中会for循环两次,返回值为2,所以为22个字节位宽,所以给从机穿的位宽信息为M_AXI_AWSIZE,等于4个字节。
b 参数赋值
AXI4的工作模式一旦确定,很多接口的数据就会确定。对于需要变化的数据,我们在后续用端口名字_r的形式在后续进行产生,在这一部分也统一进行赋值。
/*********************寄存器信号*********************/
//
//写地址操作
//reg [C_M_AXI_ADDR_WIDTH - 1 : 0] M_AXI_AWADDR_r ; //写地址赋值寄存
reg M_AXI_AWVALID_r ; //写地址有效标志寄存
//写数据操作
//reg [C_M_AXI_DATA_WIDTH - 1 : 0] M_AXI_WDATA_r ; //写数据赋值寄存器
reg M_AXI_WLAST_r ; //写数据最后一位标志寄存器
reg M_AXI_WVALID_r ; //写数据有效标志
//读地址操作
//reg [C_M_AXI_ADDR_WIDTH - 1 : 0] M_AXI_ARADDR_r ; //写地址赋值寄存
reg M_AXI_ARVALID_r ; //写地址有效标志寄存
//读数据操作
reg M_AXI_RREADY_r ; //主机读数据准备好信号
/************************参数************************/
//写地址参数的常量
assign M_AXI_AWID = 'd0 ; //写地址ID,一半不用,设为0
assign M_AXI_AWLEN = C_M_AXI_BURST_LEN ; //突发长度,连续写数据的个数
assign M_AXI_AWSIZE = bit_wide((C_M_AXI_DATA_WIDTH/8)-1) ; //数据位宽,多少个字节
assign M_AXI_AWBURST = 2'b01 ; //自增模式突发
assign M_AXI_AWLOCK = 1'b0 ; //锁信号无效
assign M_AXI_AWCACHE = 4'b0010 ; //无缓存模式
assign M_AXI_AWPROT = 3'b000 ; //默认保护编码:非特权访问,安全访问,数据存取
assign M_AXI_AWQOS = 4'b0000 ; //Axi4协议的Qos信号没用,默认4位0值
assign M_AXI_AWREGION = 4'b0000 ; //Axi4协议的REGION一般没用,默认4位0值
assign M_AXI_AWUSER = 'd0 ; //区域标识符,未用,设置全0,(位宽不定,设置0即可)
//写地址参数需要控制的量
//assign M_AXI_AWADDR = M_AXI_AWADDR_r + C_M_TARGET_SLAVE_BASE_ADDR ; //写地址赋值,输出的地址=数据写地址+基址;
assign M_AXI_AWADDR = M_AXI_AWADDR_r ; //写地址赋值
assign M_AXI_AWVALID = M_AXI_AWVALID_r ; //写地址有效标志赋值
//写数据参数的常量
assign M_AXI_WSTRB = {(C_M_AXI_DATA_WIDTH/8){1'b1}} ; //每8位数据一位掩码,拉高所有掩码,数据有效传输
assign M_AXI_WUSER = 'd0 ; //没有用户自定义信号,设为全0
//写数据需要控制的信号
assign M_AXI_WDATA = M_AXI_WDATA_r ; //写数据赋值
assign M_AXI_WLAST = M_AXI_WLAST_r & M_AXI_WREADY ; //最后一个写数据标志信号赋值
assign M_AXI_WVALID = M_AXI_WVALID_r ; //写数据有效标志赋值
//写响应参数的常量
assign M_AXI_BREADY = 1'b1 ; //直接拉高,表示主机一直准备接收从机的写响应信号
//读地址通道参数的常量
assign M_AXI_ARID = 'd0 ; //读地址ID,一半不用,设为0
assign M_AXI_ARLEN = C_M_AXI_BURST_LEN ; //突发长度,连续读数据的个数
assign M_AXI_ARSIZE = bit_wide((C_M_AXI_DATA_WIDTH/8)-1) ; //数据位宽,多少个字节
assign M_AXI_ARBURST = 2'b01 ; //自增模式突发
assign M_AXI_ARLOCK = 1'b0 ; //锁信号无效
assign M_AXI_ARCACHE = 4'b0010 ; //无缓存模式
assign M_AXI_ARPROT = 3'b000 ; //默认保护编码:非特权访问,安全访问,数据存取
assign M_AXI_ARQOS = 4'b0000 ; //Axi4协议的Qos信号没用,默认4位0值
assign M_AXI_ARREGION = 4'b0000 ; //Axi4协议的REGION信号没用,默认4位0值
assign M_AXI_ARUSER = 'd0 ; //区域标识符,未用,设置全0,(位宽不定,设置0即可)
//读地址参数需要控制的量
//assign M_AXI_ARADDR = M_AXI_ARADDR_r + C_M_TARGET_SLAVE_BASE_ADDR ; //读地址赋值,输出的地址=数据读取地址+基址;
assign M_AXI_ARADDR = M_AXI_ARADDR_r ; //读地址赋值
assign M_AXI_ARVALID = M_AXI_ARVALID_r ; //读地址有效标志赋值
//读数据通道
assign M_AXI_RREADY = M_AXI_RREADY_r ; //主机准备好从从机读入数据
c 写数据地址通道数据产生
always@(posedge M_AXI_ACLK)begin
if(!M_AXI_ARESETN)
M_AXI_AWVALID_r <= 1'b0;
else if(write_flag)
M_AXI_AWVALID_r <= 1'b1;
else if(M_AXI_AWVALID_r&&M_AXI_AWREADY)
M_AXI_AWVALID_r <= 1'b0;
else
M_AXI_AWVALID_r <=M_AXI_AWVALID_r;
end
当读写控制模块(详见后续说明,用于产生AXI接口需要写的数据和地址,并接收AXI接口读出数据的模块)给了写操作开始标志(write_flag)时(同时会给数据存储的起始地址M_AXI_AWADDR_r),主机立刻产生写数据地址有效信号M_AXI_AWVALID_r,从机接收到这个有效信号一个时钟后,会反馈准备好接收写数据地址M_AXI_AWREADY信号,写数据地址有效信号M_AXI_AWVALID_r需要在准备好接收写数据地址M_AXI_AWREADY信号为高的情况下,保持一个时钟的高电平,所以在两个信号同时为高时,将写数据地址有效信号M_AXI_AWVALID_r拉低。
特别注意:5个通道在进行传输时,主机都会有一个有效信号,从机都会有一个准备好接收信号。在主机准备好数据后,都需要将有效信号拉高,从机一般会在收到有效信号一个时钟周期后反馈准备好接收信号。因为从机会在有效信号和准备好就收信号同时为高的情况下采集数据,所以有效信号需要在准备好接收信号为高的情况下保持一个时钟的高电平。
d 写数据通道数据产生
always@(posedge M_AXI_ACLK)begin
if(!M_AXI_ARESETN)
M_AXI_WVALID_r <= 1'b0;
else if(M_AXI_AWVALID_r && M_AXI_AWREADY)
M_AXI_WVALID_r <= 1'b1;
else if(M_AXI_WLAST_r)
M_AXI_WVALID_r <= 1'b0;
else
M_AXI_WVALID_r <=M_AXI_WVALID_r;
end
一般传输了地址后,立马开始突发传输数据。于是在写数据地址有效信号M_AXI_AWVALID_r拉低的同时,将写数据有效信号M_AXI_WVALID_r拉高,所以公用了同一个判断条件**(M_AXI_AWVALID_r && M_AXI_AWREADY),且这个写数据有效信号M_AXI_WVALID_r会一直保持到突发传输完成后再拉低,所以判断标志为写数据的LAST信号M_AXI_WLAST_r**为高。
注意:读/写操作都会有这个LAST标志信号,这个信号会在突发传输的最后一个数据进行拉高,即最后一个数据的标志信号。
//LAST信号产生计数器
reg [7:0] W_LAST_cnt;
always@(posedge M_AXI_ACLK)begin
if(!M_AXI_ARESETN)
W_LAST_cnt <= 'd0;
else if(M_AXI_WVALID_r && M_AXI_WREADY)
if(W_LAST_cnt == C_M_AXI_BURST_LEN)
W_LAST_cnt <= 'd0;
else
W_LAST_cnt <= W_LAST_cnt + 1'b1;
else
W_LAST_cnt <= W_LAST_cnt;
end
//试配所有突发长度的写LAST信号
always@(posedge M_AXI_ACLK)begin
if(!M_AXI_ARESETN)
M_AXI_WLAST_r <= 1'b0;
else begin
if(C_M_AXI_BURST_LEN == 0) //突发长度为0+1=1,只写一个数据,跟随写数据地址有效且从机写数据地址准备好信号(因为突发长度只能是2的幂次方)
M_AXI_WLAST_r <= M_AXI_WVALID;
else if(C_M_AXI_BURST_LEN == 1) //突发长度为1+1=2,只写2个数据,即写数据有效且从机写数据地址准备好信号
M_AXI_WLAST_r <= M_AXI_WVALID_r & M_AXI_WREADY;
else if(W_LAST_cnt == C_M_AXI_BURST_LEN - 1)
M_AXI_WLAST_r <= 1'b1;
else
M_AXI_WLAST_r <= 1'b0;
end
end
在产生写数据LAST信号时需要分情况讨论:
a)第一种情况是突发长度n>2,所以我们会用一个n状态计数器,共存在0到n-1共n个状态,要保证LAST信号在最后一个状态(n-1)为高电平,所以每次计数到(n-2)时将写数据LAST信号M_AXI_WLAST_r拉高。
(显然,在突发长度n<=2时,每次计数到(n-2)时将写数据LAST信号M_AXI_WLAST_r拉高这种方式是不对的,所以需要额外讨论)
b)第二种情况是突发长度n=2,我们将第一个数据有效传输为标志,即写数据有效信号M_AXI_WVALID_r和准备好接收写数据信号M_AXI_WREADY同时为高作为写数据LAST信号M_AXI_WLAST_r拉高的标志;
c)第三种情况是突发长度n=1,我们需要保证写数据LAST信号M_AXI_WLAST_r在从机反馈的准备好接收写数据信号M_AXI_WREADY为高时同时为高。我们知道,从机反馈的准备好接收写数据信号M_AXI_WREADY会在写数据有效信号M_AXI_WVALID_r拉高一个时钟周期后拉高,所以我们将写数据有效信号M_AXI_WVALID_r直接赋值给写数据LAST信号M_AXI_WLAST_r。因为写数据有效信号M_AXI_WVALID_r会比突发长度多一个时钟周期,所以此时写数据LAST信号M_AXI_WLAST_r会比正常情况多了一个周期,所以我们还在数据还在给端口赋值时,将写数据LAST信号M_AXI_WLAST_r与上了准备好接收写数据信号M_AXI_WREADY。
注意:情况二和情况三只适用于,从机反馈的准备好信号相较于主机给的有效信号延迟一个时钟周期的情况,若不满足这种情况,这两种情况就还需要修改。
e 读数据地址通道数据产生
//读地址有效标志
always@(posedge M_AXI_ACLK)begin
if(!M_AXI_ARESETN)
M_AXI_ARVALID_r <= 1'b0;
else if(read_flag)
M_AXI_ARVALID_r <= 1'b1;
else if(M_AXI_ARVALID_r&&M_AXI_ARREADY)
M_AXI_ARVALID_r <= 1'b0;
else
M_AXI_ARVALID_r <=M_AXI_ARVALID_r;
end
同写数据地址通道一样,当读写控制模块给了读操作开始标志(read_flag)时(同时会给数据读取的起始地址M_AXI_ARADDR_r),主机立刻产生读数据地址有效信号M_AXI_ARVALID_r,从机接收到这个有效信号一个时钟后,会反馈准备好接收读数据地址M_AXI_ARREADY信号,读数据地址有效信号M_AXI_ARVALID_r需要在准备好接收读数据地址M_AXI_ARREADY信号为高的情况下,保持一个时钟的高电平,所以在两个信号同时为高时,将读数据地址有效信号M_AXI_ARVALID_r拉低。
f 读数据通道数据产生
//读数据准备好标志
always@(posedge M_AXI_ACLK)begin
if(!M_AXI_ARESETN)
M_AXI_RREADY_r <= 1'b0;
else if(M_AXI_ARVALID_r&&M_AXI_ARREADY)
M_AXI_RREADY_r <= 1'b1;
else if(M_AXI_RLAST)
M_AXI_RREADY_r <= 1'b0;
else
M_AXI_RREADY_r <=M_AXI_RREADY_r;
end
读数据通道的读入数据M_AXI_RDATA和读数据LAST信号M_AXI_RLAST由从机产生,所以主机只需要在传输完读数据地址后,给从机一个读数据准备好信号M_AXI_RREADY_r。且在收到读数据LAST信号M_AXI_RLAST后将准备好信号拉低。
g AXI接口协议驱动模块完整代码
`timescale 1ns / 1ps
module AXI_full_M
#(
//parameter C_M_TARGET_SLAVE_BASE_ADDR = 32'h4000_0000 ,
parameter integer C_M_AXI_BURST_LEN = 15 ,
parameter integer C_M_AXI_ID_WIDTH = 1 ,
parameter integer C_M_AXI_ADDR_WIDTH = 32 ,
parameter integer C_M_AXI_DATA_WIDTH = 32 ,
parameter integer C_M_AXI_AWUSER_WIDTH = 0 ,
parameter integer C_M_AXI_ARUSER_WIDTH = 0 ,
parameter integer C_M_AXI_WUSER_WIDTH = 0 ,
parameter integer C_M_AXI_RUSER_WIDTH = 0 ,
parameter integer C_M_AXI_BUSER_WIDTH = 0
)
(
input wire INIT_AXI_TXN ,
output wire TXN_DONE ,
output reg ERROR ,
input wire M_AXI_ACLK ,
input wire M_AXI_ARESETN ,
//写地址信号
output wire [C_M_AXI_ID_WIDTH-1 : 0] M_AXI_AWID , //写地址ID,一半不用
output wire [C_M_AXI_ADDR_WIDTH-1 : 0] M_AXI_AWADDR , //写数据的地址
output wire [7 : 0] M_AXI_AWLEN , //突发长度
output wire [2 : 0] M_AXI_AWSIZE , //数据宽度
output wire [1 : 0] M_AXI_AWBURST , //突发模式选择,00为固定地址,01为自增地址突发
output wire M_AXI_AWLOCK , //锁信号一般不用,保持为0
output wire [3 : 0] M_AXI_AWCACHE , //缓存模式,有多种,这里使用无缓存模式
output wire [2 : 0] M_AXI_AWPROT , //保护编码,指示特权访问/安全访问/指明数据访问还是指令访问,这里设置全0
output wire [3 : 0] M_AXI_AWQOS , //Qos信号,此协议的Qos信号不起作用,默认0值即可
output wire [3 : 0] M_AXI_AWREGION ,
output wire [C_M_AXI_AWUSER_WIDTH-1 : 0] M_AXI_AWUSER , //区域标识符,这里没用,默认0值
output wire M_AXI_AWVALID , //写地址有效信号
input wire M_AXI_AWREADY , //应答信号是从机输入的不需要控制
//写数据通道
output wire [C_M_AXI_DATA_WIDTH-1 : 0] M_AXI_WDATA , //写的数据
output wire [C_M_AXI_DATA_WIDTH/8-1 : 0] M_AXI_WSTRB , //掩码信号,每8位数据就对应一位掩码信号,掩码信号为高时,数据才能正常传输
output wire M_AXI_WLAST , //写数据最后一个数据标志信号
output wire [C_M_AXI_WUSER_WIDTH-1 : 0] M_AXI_WUSER , //用户自定义信号
output wire M_AXI_WVALID , //写数据有效信号
input wire M_AXI_WREADY , //从机准备好信号是从从机输入的
//写响应通道
input wire [C_M_AXI_ID_WIDTH-1 : 0] M_AXI_BID ,
input wire [1 : 0] M_AXI_BRESP ,
input wire [C_M_AXI_BUSER_WIDTH-1 : 0] M_AXI_BUSER ,
input wire M_AXI_BVALID ,
output wire M_AXI_BREADY , //写响应信号,主机给从机的响应准备好信号
//读地址通道
output wire [C_M_AXI_ID_WIDTH-1 : 0] M_AXI_ARID , //读地址ID,一半不用
output wire [C_M_AXI_ADDR_WIDTH-1 : 0] M_AXI_ARADDR , //读数据的地址
output wire [7 : 0] M_AXI_ARLEN , //突发长度
output wire [2 : 0] M_AXI_ARSIZE , //数据宽度
output wire [1 : 0] M_AXI_ARBURST , //突发模式选择,00为固定地址,01为自增地址突发
output wire M_AXI_ARLOCK , //锁信号一般不用,保持为0
output wire [3 : 0] M_AXI_ARCACHE , //缓存模式,有多种,这里使用无缓存模式
output wire [2 : 0] M_AXI_ARPROT , //保护编码,指示特权访问/安全访问/指明数据访问还是指令访问,这里设置全0
output wire [3 : 0] M_AXI_ARQOS , //Qos信号,此协议的Qos信号不起作用,默认0值即可
output wire [3 : 0] M_AXI_ARREGION , //REGION,一般不用,赋4'b0000
output wire [C_M_AXI_ARUSER_WIDTH-1 : 0] M_AXI_ARUSER , //区域标识符,这里没用,默认0值
output wire M_AXI_ARVALID , //读地址有效信号
input wire M_AXI_ARREADY , //应答信号是从机输入的不需要控制
//读数据通道
input wire [C_M_AXI_ID_WIDTH-1 : 0] M_AXI_RID ,
input wire [C_M_AXI_DATA_WIDTH-1 : 0] M_AXI_RDATA , //读入数据
input wire [1 : 0] M_AXI_RRESP ,
input wire M_AXI_RLAST , //从机数据的读数据LAST信号
input wire [C_M_AXI_RUSER_WIDTH-1 : 0] M_AXI_RUSER ,
input wire M_AXI_RVALID , //从机输入的读数据准备好信号
output wire M_AXI_RREADY , //读数据通道主机准备好信号
//读写数据控制端口
input wire write_flag , //写操作标志信号
input wire read_flag , //读操作标志信号
input wire [C_M_AXI_DATA_WIDTH - 1 : 0] M_AXI_AWADDR_r , //写数据地址寄存
input wire [C_M_AXI_DATA_WIDTH - 1 : 0] M_AXI_WDATA_r , //写数据赋值寄存器
input wire [C_M_AXI_DATA_WIDTH - 1 : 0] M_AXI_ARADDR_r //读数据地址寄存
);
/***************计算输入数据的二进制位宽***************/
function integer bit_wide;
input integer data_in;
for(bit_wide = 0;data_in > 0;bit_wide = bit_wide + 1'b1)
data_in = data_in >> 1;
endfunction
/*********************寄存器信号*********************/
//
//写地址操作
//reg [C_M_AXI_ADDR_WIDTH - 1 : 0] M_AXI_AWADDR_r ; //写地址赋值寄存
reg M_AXI_AWVALID_r ; //写地址有效标志寄存
//写数据操作
//reg [C_M_AXI_DATA_WIDTH - 1 : 0] M_AXI_WDATA_r ; //写数据赋值寄存器
reg M_AXI_WLAST_r ; //写数据最后一位标志寄存器
reg M_AXI_WVALID_r ; //写数据有效标志
//读地址操作
//reg [C_M_AXI_ADDR_WIDTH - 1 : 0] M_AXI_ARADDR_r ; //写地址赋值寄存
reg M_AXI_ARVALID_r ; //写地址有效标志寄存
//读数据操作
reg M_AXI_RREADY_r ; //主机读数据准备好信号
/************************参数************************/
//写地址参数的常量
assign M_AXI_AWID = 'd0 ; //写地址ID,一半不用,设为0
assign M_AXI_AWLEN = C_M_AXI_BURST_LEN ; //突发长度,连续写数据的个数
assign M_AXI_AWSIZE = bit_wide((C_M_AXI_DATA_WIDTH/8)-1) ; //数据位宽,多少个字节
assign M_AXI_AWBURST = 2'b01 ; //自增模式突发
assign M_AXI_AWLOCK = 1'b0 ; //锁信号无效
assign M_AXI_AWCACHE = 4'b0010 ; //无缓存模式
assign M_AXI_AWPROT = 3'b000 ; //默认保护编码:非特权访问,安全访问,数据存取
assign M_AXI_AWQOS = 4'b0000 ; //Axi4协议的Qos信号没用,默认4位0值
assign M_AXI_AWREGION = 4'b0000 ; //Axi4协议的REGION一般没用,默认4位0值
assign M_AXI_AWUSER = 'd0 ; //区域标识符,未用,设置全0,(位宽不定,设置0即可)
//写地址参数需要控制的量
//assign M_AXI_AWADDR = M_AXI_AWADDR_r + C_M_TARGET_SLAVE_BASE_ADDR ; //写地址赋值,输出的地址=数据写地址+基址;
assign M_AXI_AWADDR = M_AXI_AWADDR_r ; //写地址赋值
assign M_AXI_AWVALID = M_AXI_AWVALID_r ; //写地址有效标志赋值
//写数据参数的常量
assign M_AXI_WSTRB = {(C_M_AXI_DATA_WIDTH/8){1'b1}} ; //每8位数据一位掩码,拉高所有掩码,数据有效传输
assign M_AXI_WUSER = 'd0 ; //没有用户自定义信号,设为全0
//写数据需要控制的信号
assign M_AXI_WDATA = M_AXI_WDATA_r ; //写数据赋值
assign M_AXI_WLAST = M_AXI_WLAST_r & M_AXI_WREADY ; //最后一个写数据标志信号赋值
assign M_AXI_WVALID = M_AXI_WVALID_r ; //写数据有效标志赋值
//写响应参数的常量
assign M_AXI_BREADY = 1'b1 ; //直接拉高,表示主机一直准备接收从机的写响应信号
//读地址通道参数的常量
assign M_AXI_ARID = 'd0 ; //读地址ID,一半不用,设为0
assign M_AXI_ARLEN = C_M_AXI_BURST_LEN ; //突发长度,连续读数据的个数
assign M_AXI_ARSIZE = bit_wide((C_M_AXI_DATA_WIDTH/8)-1) ; //数据位宽,多少个字节
assign M_AXI_ARBURST = 2'b01 ; //自增模式突发
assign M_AXI_ARLOCK = 1'b0 ; //锁信号无效
assign M_AXI_ARCACHE = 4'b0010 ; //无缓存模式
assign M_AXI_ARPROT = 3'b000 ; //默认保护编码:非特权访问,安全访问,数据存取
assign M_AXI_ARQOS = 4'b0000 ; //Axi4协议的Qos信号没用,默认4位0值
assign M_AXI_ARREGION = 4'b0000 ; //Axi4协议的REGION信号没用,默认4位0值
assign M_AXI_ARUSER = 'd0 ; //区域标识符,未用,设置全0,(位宽不定,设置0即可)
//读地址参数需要控制的量
//assign M_AXI_ARADDR = M_AXI_ARADDR_r + C_M_TARGET_SLAVE_BASE_ADDR ; //读地址赋值,输出的地址=数据读取地址+基址;
assign M_AXI_ARADDR = M_AXI_ARADDR_r ; //读地址赋值
assign M_AXI_ARVALID = M_AXI_ARVALID_r ; //读地址有效标志赋值
//读数据通道
assign M_AXI_RREADY = M_AXI_RREADY_r ; //主机准备好从从机读入数据
/******************AXI_读写数据实现******************/
// always@(posedge M_AXI_ACLK)begin
// if(!M_AXI_ARESETN)
// else
// end
//写地址通道:写地址有效标志,写地址
//写地址有效标志
always@(posedge M_AXI_ACLK)begin
if(!M_AXI_ARESETN)
M_AXI_AWVALID_r <= 1'b0;
else if(write_flag)
M_AXI_AWVALID_r <= 1'b1;
else if(M_AXI_AWVALID_r&&M_AXI_AWREADY)
M_AXI_AWVALID_r <= 1'b0;
else
M_AXI_AWVALID_r <=M_AXI_AWVALID_r;
end
//写地址:由控制模块产生
/*
实现的功能是向11地址写入数据11~20,起始地址为11
*/
// always@(posedge M_AXI_ACLK)begin
// if(!M_AXI_ARESETN)
// M_AXI_AWADDR_r <= 'd0;
// else if(write_flag)
// M_AXI_AWADDR_r <= 'd11;
// else
// M_AXI_AWADDR_r <= M_AXI_AWADDR_r;
// end
//写数据通道:写数据有效标志,写数据,最后一个写数据标志信号
//写数据有效标志
always@(posedge M_AXI_ACLK)begin
if(!M_AXI_ARESETN)
M_AXI_WVALID_r <= 1'b0;
else if(M_AXI_AWVALID_r && M_AXI_AWREADY)
M_AXI_WVALID_r <= 1'b1;
else if(M_AXI_WLAST_r)
M_AXI_WVALID_r <= 1'b0;
else
M_AXI_WVALID_r <=M_AXI_WVALID_r;
end
// //写的数据
// always@(posedge M_AXI_ACLK)begin
// if(!M_AXI_ARESETN)
// M_AXI_WDATA_r <= 'd0;
// else if(M_AXI_AWVALID_r && M_AXI_AWREADY) //给第一个数据
// M_AXI_WDATA_r <= 'd11;
// else if((M_AXI_WVALID_r && M_AXI_WREADY) && (!M_AXI_WLAST_r))
// M_AXI_WDATA_r <= M_AXI_WDATA_r+1'b1;
// else
// M_AXI_WDATA_r <= 'd0;
// end
//LAST信号产生计数器
reg [7:0] W_LAST_cnt;
always@(posedge M_AXI_ACLK)begin
if(!M_AXI_ARESETN)
W_LAST_cnt <= 'd0;
else if(M_AXI_WVALID_r && M_AXI_WREADY)
if(W_LAST_cnt == C_M_AXI_BURST_LEN)
W_LAST_cnt <= 'd0;
else
W_LAST_cnt <= W_LAST_cnt + 1'b1;
else
W_LAST_cnt <= W_LAST_cnt;
end
//试配所有突发长度的写LAST信号
always@(posedge M_AXI_ACLK)begin
if(!M_AXI_ARESETN)
M_AXI_WLAST_r <= 1'b0;
else begin
if(C_M_AXI_BURST_LEN == 0) //突发长度为0+1=1,只写一个数据,跟随写数据地址有效且从机写数据地址准备好信号(因为突发长度只能是2的幂次方)
M_AXI_WLAST_r <= M_AXI_WVALID;
else if(C_M_AXI_BURST_LEN == 1) //突发长度为1+1=2,只写2个数据,即写数据有效且从机写数据地址准备好信号
M_AXI_WLAST_r <= M_AXI_WVALID_r & M_AXI_WREADY;
else if(W_LAST_cnt == C_M_AXI_BURST_LEN - 1)
M_AXI_WLAST_r <= 1'b1;
else
M_AXI_WLAST_r <= 1'b0;
end
end
// always@(posedge M_AXI_ACLK)begin
// if(!M_AXI_ARESETN)
// M_AXI_WLAST_r <= 1'b0;
// else if(W_LAST_cnt == C_M_AXI_BURST_LEN - 2'd2)
// M_AXI_WLAST_r <= 1'b1;
// else
// M_AXI_WLAST_r <= 1'b0;
// end
//读地址通道:读数据地址有效标志,读数据地址
//读地址有效标志
always@(posedge M_AXI_ACLK)begin
if(!M_AXI_ARESETN)
M_AXI_ARVALID_r <= 1'b0;
else if(read_flag)
M_AXI_ARVALID_r <= 1'b1;
else if(M_AXI_ARVALID_r&&M_AXI_ARREADY)
M_AXI_ARVALID_r <= 1'b0;
else
M_AXI_ARVALID_r <=M_AXI_ARVALID_r;
end
// //读首地址
// /*
// 实现的功能是从11地址读出数据
// */
// always@(posedge M_AXI_ACLK)begin
// if(!M_AXI_ARESETN)
// M_AXI_ARADDR_r <= 'd0;
// else if(read_flag)
// M_AXI_ARADDR_r <= 'd11;
// else
// M_AXI_ARADDR_r <= M_AXI_ARADDR_r;
// end
//读数据通道
//读数据准备好标志
always@(posedge M_AXI_ACLK)begin
if(!M_AXI_ARESETN)
M_AXI_RREADY_r <= 1'b0;
else if(M_AXI_ARVALID_r&&M_AXI_ARREADY)
M_AXI_RREADY_r <= 1'b1;
else if(M_AXI_RLAST)
M_AXI_RREADY_r <= 1'b0;
else
M_AXI_RREADY_r <=M_AXI_RREADY_r;
end
endmodule
(B)读写控制模块
当读写控制模块用于产生AXI接口需要写的数据和地址,并接收AXI接口读出数据的模块。
a 状态机
使用一个三状态的有限状态机(空闲状态,写数据状态和读数据状态),默认空闲状态,当操作开始读写信号WR_START有效时开始进入写数据状态;当写数据完成(写到最后一个数据,即写数据LAST信号M_AXI_WLAST为高)进入读数据状态;当读数据完成(读到最后一个数据,即读数据LAST信号M_AXI_RLAST为高)进入空闲状态;在空闲状态等待下一个开始读写信号。
b 写/读操作开始标志(write_flag/read_flag)
将开始读写信号WR_START作为写操作开始标志信号write_flag触发标志;
将写数据LAST信号M_AXI_WLAST作为读操作开始标志信号read_flag触发标志。
c 写数据首地址及数据产生
在写操作开始标志信号write_flag有效时给写数据首地址M_AXI_AWADDR_r;
因为写数据有效信号M_AXI_WVALID和写数据准备好信号M_AXI_WREADY同时有效时,从机采集数据,所以要保证此时数据已经准备好,所以在写完地址时就给第一个数据。其后保证前一个数据写成功,即写数据有效信号M_AXI_WVALID和写数据准备好信号M_AXI_WREADY同时有效时,递增产生后续数据。
d 读数据首地址产生
在读操作开始标志信号read_flag有效时给读数据首地址M_AXI_ARADDR_r;
e 读写控制模块完整代码
//读写数据控制模块:写数据产生模块,读数据接收模块
module write_read_data
#(
parameter integer C_M_AXI_BURST_LEN = 16 , //突发长度
parameter integer C_M_AXI_ADDR_WIDTH = 32 , //数据地址位宽
parameter integer C_M_AXI_DATA_WIDTH = 32 //数据位宽
)
(
input M_AXI_ACLK ,
input M_AXI_ARESETN ,
//读写总开始标志信号
input wire WR_START ,
//写数据地址端口
output reg write_flag , //写数据开始标志信号
output reg [C_M_AXI_DATA_WIDTH - 1 : 0] M_AXI_AWADDR_r , //写的数据首地址
input M_AXI_AWVALID , //主机写数据地址有效标志
input M_AXI_AWREADY , //从机写数据地址准备好信号
//写数据端口
input M_AXI_WVALID , //主机写数据有效信号
input M_AXI_WREADY , //从机写数据准备好信号
input M_AXI_WLAST , //主机写数据最后一个标志信号
output reg [C_M_AXI_DATA_WIDTH - 1 : 0] M_AXI_WDATA_r ,
//读数据地址端口
output reg read_flag , //读数据开始标志信号
output reg [C_M_AXI_DATA_WIDTH - 1 : 0] M_AXI_ARADDR_r , //读的数据首地址
//读数据端口
input wire M_AXI_RLAST
);
//有限状态机状态
parameter STATE_IDLE = 3'b001;
parameter STATE_WRITE = 3'b010;
parameter STATE_READ = 3'b100;
reg [2 : 0] current_state ;
reg [2 : 0] next_state ;
//第一段:时序always过程块,格式化地将次态赋值当前状态
always@(posedge M_AXI_ACLK)begin
if(!M_AXI_ARESETN)
current_state <= STATE_IDLE;
else
current_state <= next_state;
end
//第二段:组合always过程块,状态转移条件判断
always@(*)begin
case(current_state)
STATE_IDLE :begin
if(WR_START)
next_state = STATE_WRITE;
else
next_state = STATE_IDLE;
end
STATE_WRITE :begin
if(M_AXI_WLAST)
next_state = STATE_READ;
else
next_state = STATE_WRITE;
end
STATE_READ :begin
if(M_AXI_RLAST)
next_state = STATE_IDLE;
else
next_state = STATE_READ;
end
default:
next_state = STATE_IDLE;
endcase
end
//第三段:赋值,产生写数据和读数据使能信号
//写/读操作开始标志
always@(posedge M_AXI_ACLK)begin
if(!M_AXI_ARESETN)
write_flag <= 1'b0;
else if(WR_START)
write_flag <= 1'b1;
else
write_flag <= 1'b0;
end
always@(posedge M_AXI_ACLK)begin
if(!M_AXI_ARESETN)
read_flag <= 1'b0;
else if(M_AXI_WLAST)
read_flag <= 1'b1;
else
read_flag <= 1'b0;
end
/******************读写操作数据及首地址产生******************/
//写数据地址:产生首地址
/*
实现的功能是向11地址写入数据11~20,起始地址为11
*/
always@(posedge M_AXI_ACLK)begin
if(!M_AXI_ARESETN)
M_AXI_AWADDR_r <= 'd0;
else if(write_flag)
M_AXI_AWADDR_r <= 'd11;
else
M_AXI_AWADDR_r <= M_AXI_AWADDR_r;
end
//写数据产生:
always@(posedge M_AXI_ACLK)begin
if(!M_AXI_ARESETN)
M_AXI_WDATA_r <= 'd0;
else if(M_AXI_AWVALID && M_AXI_AWREADY) //给第一个数据
M_AXI_WDATA_r <= 'd11;
else if((M_AXI_WVALID && M_AXI_WREADY) && (!M_AXI_WLAST))
M_AXI_WDATA_r <= M_AXI_WDATA_r+1'b1;
else
M_AXI_WDATA_r <= M_AXI_WDATA_r;
end
//读数据地址:产生首地址
always@(posedge M_AXI_ACLK)begin
if(!M_AXI_ARESETN)
M_AXI_ARADDR_r <= 'd0;
else if(read_flag)
M_AXI_ARADDR_r <= 'd11;
else
M_AXI_ARADDR_r <= M_AXI_ARADDR_r;
end
endmodule
(C)从机(这里给出了xilinx例程中给的从机代码:后续会出这个代码的讲解)
`timescale 1 ns / 1 ps
module AXI_full_slave #
(
// Users to add parameters here
// User parameters ends
// Do not modify the parameters beyond this line
// Width of ID for for write address, write data, read address and read data
parameter integer C_S_AXI_ID_WIDTH = 1,
// Width of S_AXI data bus
parameter integer C_S_AXI_DATA_WIDTH = 32,
// Width of S_AXI address bus
parameter integer C_S_AXI_ADDR_WIDTH = 6,
// Width of optional user defined signal in write address channel
parameter integer C_S_AXI_AWUSER_WIDTH = 0,
// Width of optional user defined signal in read address channel
parameter integer C_S_AXI_ARUSER_WIDTH = 0,
// Width of optional user defined signal in write data channel
parameter integer C_S_AXI_WUSER_WIDTH = 0,
// Width of optional user defined signal in read data channel
parameter integer C_S_AXI_RUSER_WIDTH = 0,
// Width of optional user defined signal in write response channel
parameter integer C_S_AXI_BUSER_WIDTH = 0
)
(
// Users to add ports here
// User ports ends
// Do not modify the ports beyond this line
// Global Clock Signal
input wire S_AXI_ACLK,
// Global Reset Signal. This Signal is Active LOW
input wire S_AXI_ARESETN,
// Write Address ID
input wire [C_S_AXI_ID_WIDTH-1 : 0] S_AXI_AWID,
// Write address
input wire [C_S_AXI_ADDR_WIDTH-1 : 0] S_AXI_AWADDR,
// Burst length. The burst length gives the exact number of transfers in a burst
input wire [7 : 0] S_AXI_AWLEN,
// Burst size. This signal indicates the size of each transfer in the burst
input wire [2 : 0] S_AXI_AWSIZE,
// Burst type. The burst type and the size information,
// determine how the address for each transfer within the burst is calculated.
input wire [1 : 0] S_AXI_AWBURST,
// Lock type. Provides additional information about the
// atomic characteristics of the transfer.
input wire S_AXI_AWLOCK,
// Memory type. This signal indicates how transactions
// are required to progress through a system.
input wire [3 : 0] S_AXI_AWCACHE,
// Protection type. This signal indicates the privilege
// and security level of the transaction, and whether
// the transaction is a data access or an instruction access.
input wire [2 : 0] S_AXI_AWPROT,
// Quality of Service, QoS identifier sent for each
// write transaction.
input wire [3 : 0] S_AXI_AWQOS,
// Region identifier. Permits a single physical interface
// on a slave to be used for multiple logical interfaces.
input wire [3 : 0] S_AXI_AWREGION,
// Optional User-defined signal in the write address channel.
input wire [C_S_AXI_AWUSER_WIDTH-1 : 0] S_AXI_AWUSER,
// Write address valid. This signal indicates that
// the channel is signaling valid write address and
// control information.
input wire S_AXI_AWVALID,
// Write address ready. This signal indicates that
// the slave is ready to accept an address and associated
// control signals.
output wire S_AXI_AWREADY,
// Write Data
input wire [C_S_AXI_DATA_WIDTH-1 : 0] S_AXI_WDATA,
// Write strobes. This signal indicates which byte
// lanes hold valid data. There is one write strobe
// bit for each eight bits of the write data bus.
input wire [(C_S_AXI_DATA_WIDTH/8)-1 : 0] S_AXI_WSTRB,
// Write last. This signal indicates the last transfer
// in a write burst.
input wire S_AXI_WLAST,
// Optional User-defined signal in the write data channel.
input wire [C_S_AXI_WUSER_WIDTH-1 : 0] S_AXI_WUSER,
// Write valid. This signal indicates that valid write
// data and strobes are available.
input wire S_AXI_WVALID,
// Write ready. This signal indicates that the slave
// can accept the write data.
output wire S_AXI_WREADY,
// Response ID tag. This signal is the ID tag of the
// write response.
output wire [C_S_AXI_ID_WIDTH-1 : 0] S_AXI_BID,
// Write response. This signal indicates the status
// of the write transaction.
output wire [1 : 0] S_AXI_BRESP,
// Optional User-defined signal in the write response channel.
output wire [C_S_AXI_BUSER_WIDTH-1 : 0] S_AXI_BUSER,
// Write response valid. This signal indicates that the
// channel is signaling a valid write response.
output wire S_AXI_BVALID,
// Response ready. This signal indicates that the master
// can accept a write response.
input wire S_AXI_BREADY,
// Read address ID. This signal is the identification
// tag for the read address group of signals.
input wire [C_S_AXI_ID_WIDTH-1 : 0] S_AXI_ARID,
// Read address. This signal indicates the initial
// address of a read burst transaction.
input wire [C_S_AXI_ADDR_WIDTH-1 : 0] S_AXI_ARADDR,
// Burst length. The burst length gives the exact number of transfers in a burst
input wire [7 : 0] S_AXI_ARLEN,
// Burst size. This signal indicates the size of each transfer in the burst
input wire [2 : 0] S_AXI_ARSIZE,
// Burst type. The burst type and the size information,
// determine how the address for each transfer within the burst is calculated.
input wire [1 : 0] S_AXI_ARBURST,
// Lock type. Provides additional information about the
// atomic characteristics of the transfer.
input wire S_AXI_ARLOCK,
// Memory type. This signal indicates how transactions
// are required to progress through a system.
input wire [3 : 0] S_AXI_ARCACHE,
// Protection type. This signal indicates the privilege
// and security level of the transaction, and whether
// the transaction is a data access or an instruction access.
input wire [2 : 0] S_AXI_ARPROT,
// Quality of Service, QoS identifier sent for each
// read transaction.
input wire [3 : 0] S_AXI_ARQOS,
// Region identifier. Permits a single physical interface
// on a slave to be used for multiple logical interfaces.
input wire [3 : 0] S_AXI_ARREGION,
// Optional User-defined signal in the read address channel.
input wire [C_S_AXI_ARUSER_WIDTH-1 : 0] S_AXI_ARUSER,
// Write address valid. This signal indicates that
// the channel is signaling valid read address and
// control information.
input wire S_AXI_ARVALID,
// Read address ready. This signal indicates that
// the slave is ready to accept an address and associated
// control signals.
output wire S_AXI_ARREADY,
// Read ID tag. This signal is the identification tag
// for the read data group of signals generated by the slave.
output wire [C_S_AXI_ID_WIDTH-1 : 0] S_AXI_RID,
// Read Data
output wire [C_S_AXI_DATA_WIDTH-1 : 0] S_AXI_RDATA,
// Read response. This signal indicates the status of
// the read transfer.
output wire [1 : 0] S_AXI_RRESP,
// Read last. This signal indicates the last transfer
// in a read burst.
output wire S_AXI_RLAST,
// Optional User-defined signal in the read address channel.
output wire [C_S_AXI_RUSER_WIDTH-1 : 0] S_AXI_RUSER,
// Read valid. This signal indicates that the channel
// is signaling the required read data.
output wire S_AXI_RVALID,
// Read ready. This signal indicates that the master can
// accept the read data and response information.
input wire S_AXI_RREADY
);
// AXI4FULL signals
reg [C_S_AXI_ADDR_WIDTH-1 : 0] axi_awaddr;
reg axi_awready;
reg axi_wready;
reg [1 : 0] axi_bresp;
reg [C_S_AXI_BUSER_WIDTH-1 : 0] axi_buser;
reg axi_bvalid;
reg [C_S_AXI_ADDR_WIDTH-1 : 0] axi_araddr;
reg axi_arready;
reg [C_S_AXI_DATA_WIDTH-1 : 0] axi_rdata;
reg [1 : 0] axi_rresp;
reg axi_rlast;
reg [C_S_AXI_RUSER_WIDTH-1 : 0] axi_ruser;
reg axi_rvalid;
// aw_wrap_en determines wrap boundary and enables wrapping
wire aw_wrap_en;
// ar_wrap_en determines wrap boundary and enables wrapping
wire ar_wrap_en;
// aw_wrap_size is the size of the write transfer, the
// write address wraps to a lower address if upper address
// limit is reached
wire [31:0] aw_wrap_size ;
// ar_wrap_size is the size of the read transfer, the
// read address wraps to a lower address if upper address
// limit is reached
wire [31:0] ar_wrap_size ;
// The axi_awv_awr_flag flag marks the presence of write address valid
reg axi_awv_awr_flag;
//The axi_arv_arr_flag flag marks the presence of read address valid
reg axi_arv_arr_flag;
// The axi_awlen_cntr internal write address counter to keep track of beats in a burst transaction
reg [7:0] axi_awlen_cntr;
//The axi_arlen_cntr internal read address counter to keep track of beats in a burst transaction
reg [7:0] axi_arlen_cntr;
reg [1:0] axi_arburst;
reg [1:0] axi_awburst;
reg [7:0] axi_arlen;
reg [7:0] axi_awlen;
//local parameter for addressing 32 bit / 64 bit C_S_AXI_DATA_WIDTH
//ADDR_LSB is used for addressing 32/64 bit registers/memories
//ADDR_LSB = 2 for 32 bits (n downto 2)
//ADDR_LSB = 3 for 42 bits (n downto 3)
localparam integer ADDR_LSB = (C_S_AXI_DATA_WIDTH/32)+ 1;
localparam integer OPT_MEM_ADDR_BITS = 3;
localparam integer USER_NUM_MEM = 1;
//----------------------------------------------
//-- Signals for user logic memory space example
//------------------------------------------------
wire [OPT_MEM_ADDR_BITS:0] mem_address;
wire [USER_NUM_MEM-1:0] mem_select;
reg [C_S_AXI_DATA_WIDTH-1:0] mem_data_out[0 : USER_NUM_MEM-1];
genvar i;
genvar j;
genvar mem_byte_index;
// I/O Connections assignments
assign S_AXI_AWREADY = axi_awready;
assign S_AXI_WREADY = axi_wready;
assign S_AXI_BRESP = axi_bresp;
assign S_AXI_BUSER = axi_buser;
assign S_AXI_BVALID = axi_bvalid;
assign S_AXI_ARREADY = axi_arready;
assign S_AXI_RDATA = axi_rdata;
assign S_AXI_RRESP = axi_rresp;
assign S_AXI_RLAST = axi_rlast;
assign S_AXI_RUSER = axi_ruser;
assign S_AXI_RVALID = axi_rvalid;
assign S_AXI_BID = S_AXI_AWID;
assign S_AXI_RID = S_AXI_ARID;
assign aw_wrap_size = (C_S_AXI_DATA_WIDTH/8 * (axi_awlen));
assign ar_wrap_size = (C_S_AXI_DATA_WIDTH/8 * (axi_arlen));
assign aw_wrap_en = ((axi_awaddr & aw_wrap_size) == aw_wrap_size)? 1'b1: 1'b0;
assign ar_wrap_en = ((axi_araddr & ar_wrap_size) == ar_wrap_size)? 1'b1: 1'b0;
// Implement axi_awready generation
// axi_awready is asserted for one S_AXI_ACLK clock cycle when both
// S_AXI_AWVALID and S_AXI_WVALID are asserted. axi_awready is
// de-asserted when reset is low.
always @( posedge S_AXI_ACLK )
begin
if ( S_AXI_ARESETN == 1'b0 )
begin
axi_awready <= 1'b0;
axi_awv_awr_flag <= 1'b0;
end
else
begin
if (~axi_awready && S_AXI_AWVALID && ~axi_awv_awr_flag && ~axi_arv_arr_flag)
begin
// slave is ready to accept an address and
// associated control signals
axi_awready <= 1'b1;
axi_awv_awr_flag <= 1'b1;
// used for generation of bresp() and bvalid
end
else if (S_AXI_WLAST && axi_wready)
// preparing to accept next address after current write burst tx completion
begin
axi_awv_awr_flag <= 1'b0;
end
else
begin
axi_awready <= 1'b0;
end
end
end
// Implement axi_awaddr latching
// This process is used to latch the address when both
// S_AXI_AWVALID and S_AXI_WVALID are valid.
always @( posedge S_AXI_ACLK )
begin
if ( S_AXI_ARESETN == 1'b0 )
begin
axi_awaddr <= 0;
axi_awlen_cntr <= 0;
axi_awburst <= 0;
axi_awlen <= 0;
end
else
begin
if (~axi_awready && S_AXI_AWVALID && ~axi_awv_awr_flag)
begin
// address latching
axi_awaddr <= S_AXI_AWADDR[C_S_AXI_ADDR_WIDTH - 1:0];
axi_awburst <= S_AXI_AWBURST;
axi_awlen <= S_AXI_AWLEN;
// start address of transfer
axi_awlen_cntr <= 0;
end
else if((axi_awlen_cntr <= axi_awlen) && axi_wready && S_AXI_WVALID)
begin
axi_awlen_cntr <= axi_awlen_cntr + 1;
case (axi_awburst)
2'b00: // fixed burst
// The write address for all the beats in the transaction are fixed
begin
axi_awaddr <= axi_awaddr;
//for awsize = 4 bytes (010)
end
2'b01: //incremental burst
// The write address for all the beats in the transaction are increments by awsize
begin
axi_awaddr[C_S_AXI_ADDR_WIDTH - 1:ADDR_LSB] <= axi_awaddr[C_S_AXI_ADDR_WIDTH - 1:ADDR_LSB] + 1;
//awaddr aligned to 4 byte boundary
axi_awaddr[ADDR_LSB-1:0] <= {ADDR_LSB{1'b0}};
//for awsize = 4 bytes (010)
end
2'b10: //Wrapping burst
// The write address wraps when the address reaches wrap boundary
if (aw_wrap_en)
begin
axi_awaddr <= (axi_awaddr - aw_wrap_size);
end
else
begin
axi_awaddr[C_S_AXI_ADDR_WIDTH - 1:ADDR_LSB] <= axi_awaddr[C_S_AXI_ADDR_WIDTH - 1:ADDR_LSB] + 1;
axi_awaddr[ADDR_LSB-1:0] <= {ADDR_LSB{1'b0}};
end
default: //reserved (incremental burst for example)
begin
axi_awaddr <= axi_awaddr[C_S_AXI_ADDR_WIDTH - 1:ADDR_LSB] + 1;
//for awsize = 4 bytes (010)
end
endcase
end
end
end
// Implement axi_wready generation
// axi_wready is asserted for one S_AXI_ACLK clock cycle when both
// S_AXI_AWVALID and S_AXI_WVALID are asserted. axi_wready is
// de-asserted when reset is low.
always @( posedge S_AXI_ACLK )
begin
if ( S_AXI_ARESETN == 1'b0 )
begin
axi_wready <= 1'b0;
end
else
begin
if ( ~axi_wready && S_AXI_WVALID && axi_awv_awr_flag)
begin
// slave can accept the write data
axi_wready <= 1'b1;
end
//else if (~axi_awv_awr_flag)
else if (S_AXI_WLAST && axi_wready)
begin
axi_wready <= 1'b0;
end
end
end
// Implement write response logic generation
// The write response and response valid signals are asserted by the slave
// when axi_wready, S_AXI_WVALID, axi_wready and S_AXI_WVALID are asserted.
// This marks the acceptance of address and indicates the status of
// write transaction.
always @( posedge S_AXI_ACLK )
begin
if ( S_AXI_ARESETN == 1'b0 )
begin
axi_bvalid <= 0;
axi_bresp <= 2'b0;
axi_buser <= 0;
end
else
begin
if (axi_awv_awr_flag && axi_wready && S_AXI_WVALID && ~axi_bvalid && S_AXI_WLAST )
begin
axi_bvalid <= 1'b1;
axi_bresp <= 2'b0;
// 'OKAY' response
end
else
begin
if (S_AXI_BREADY && axi_bvalid)
//check if bready is asserted while bvalid is high)
//(there is a possibility that bready is always asserted high)
begin
axi_bvalid <= 1'b0;
end
end
end
end
// Implement axi_arready generation
// axi_arready is asserted for one S_AXI_ACLK clock cycle when
// S_AXI_ARVALID is asserted. axi_awready is
// de-asserted when reset (active low) is asserted.
// The read address is also latched when S_AXI_ARVALID is
// asserted. axi_araddr is reset to zero on reset assertion.
always @( posedge S_AXI_ACLK )
begin
if ( S_AXI_ARESETN == 1'b0 )
begin
axi_arready <= 1'b0;
axi_arv_arr_flag <= 1'b0;
end
else
begin
if (~axi_arready && S_AXI_ARVALID && ~axi_awv_awr_flag && ~axi_arv_arr_flag)
begin
axi_arready <= 1'b1;
axi_arv_arr_flag <= 1'b1;
end
else if (axi_rvalid && S_AXI_RREADY && axi_arlen_cntr == axi_arlen)
// preparing to accept next address after current read completion
begin
axi_arv_arr_flag <= 1'b0;
end
else
begin
axi_arready <= 1'b0;
end
end
end
// Implement axi_araddr latching
//This process is used to latch the address when both
//S_AXI_ARVALID and S_AXI_RVALID are valid.
always @( posedge S_AXI_ACLK )
begin
if ( S_AXI_ARESETN == 1'b0 )
begin
axi_araddr <= 0;
axi_arlen_cntr <= 0;
axi_arburst <= 0;
axi_arlen <= 0;
axi_rlast <= 1'b0;
axi_ruser <= 0;
end
else
begin
if (~axi_arready && S_AXI_ARVALID && ~axi_arv_arr_flag)
begin
// address latching
axi_araddr <= S_AXI_ARADDR[C_S_AXI_ADDR_WIDTH - 1:0];
axi_arburst <= S_AXI_ARBURST;
axi_arlen <= S_AXI_ARLEN;
// start address of transfer
axi_arlen_cntr <= 0;
axi_rlast <= 1'b0;
end
else if((axi_arlen_cntr <= axi_arlen) && axi_rvalid && S_AXI_RREADY)
begin
axi_arlen_cntr <= axi_arlen_cntr + 1;
axi_rlast <= 1'b0;
case (axi_arburst)
2'b00: // fixed burst
// The read address for all the beats in the transaction are fixed
begin
axi_araddr <= axi_araddr;
//for arsize = 4 bytes (010)
end
2'b01: //incremental burst
// The read address for all the beats in the transaction are increments by awsize
begin
axi_araddr[C_S_AXI_ADDR_WIDTH - 1:ADDR_LSB] <= axi_araddr[C_S_AXI_ADDR_WIDTH - 1:ADDR_LSB] + 1;
//araddr aligned to 4 byte boundary
axi_araddr[ADDR_LSB-1:0] <= {ADDR_LSB{1'b0}};
//for awsize = 4 bytes (010)
end
2'b10: //Wrapping burst
// The read address wraps when the address reaches wrap boundary
if (ar_wrap_en)
begin
axi_araddr <= (axi_araddr - ar_wrap_size);
end
else
begin
axi_araddr[C_S_AXI_ADDR_WIDTH - 1:ADDR_LSB] <= axi_araddr[C_S_AXI_ADDR_WIDTH - 1:ADDR_LSB] + 1;
//araddr aligned to 4 byte boundary
axi_araddr[ADDR_LSB-1:0] <= {ADDR_LSB{1'b0}};
end
default: //reserved (incremental burst for example)
begin
axi_araddr <= axi_araddr[C_S_AXI_ADDR_WIDTH - 1:ADDR_LSB]+1;
//for arsize = 4 bytes (010)
end
endcase
end
else if((axi_arlen_cntr == axi_arlen) && ~axi_rlast && axi_arv_arr_flag )
begin
axi_rlast <= 1'b1;
end
else if (S_AXI_RREADY)
begin
axi_rlast <= 1'b0;
end
end
end
// Implement axi_arvalid generation
// axi_rvalid is asserted for one S_AXI_ACLK clock cycle when both
// S_AXI_ARVALID and axi_arready are asserted. The slave registers
// data are available on the axi_rdata bus at this instance. The
// assertion of axi_rvalid marks the validity of read data on the
// bus and axi_rresp indicates the status of read transaction.axi_rvalid
// is deasserted on reset (active low). axi_rresp and axi_rdata are
// cleared to zero on reset (active low).
always @( posedge S_AXI_ACLK )
begin
if ( S_AXI_ARESETN == 1'b0 )
begin
axi_rvalid <= 0;
axi_rresp <= 0;
end
else
begin
if (axi_arv_arr_flag && ~axi_rvalid)
begin
axi_rvalid <= 1'b1;
axi_rresp <= 2'b0;
// 'OKAY' response
end
else if (axi_rvalid && S_AXI_RREADY)
begin
axi_rvalid <= 1'b0;
end
end
end
// ------------------------------------------
// -- Example code to access user logic memory region
// ------------------------------------------
generate
if (USER_NUM_MEM >= 1)
begin
assign mem_select = 1;
assign mem_address = (axi_arv_arr_flag? axi_araddr[ADDR_LSB+OPT_MEM_ADDR_BITS:ADDR_LSB]:(axi_awv_awr_flag? axi_awaddr[ADDR_LSB+OPT_MEM_ADDR_BITS:ADDR_LSB]:0));
end
endgenerate
// implement Block RAM(s)
generate
for(i=0; i<= USER_NUM_MEM-1; i=i+1)
begin:BRAM_GEN
wire mem_rden;
wire mem_wren;
assign mem_wren = axi_wready && S_AXI_WVALID ;
assign mem_rden = axi_arv_arr_flag ; //& ~axi_rvalid
for(mem_byte_index=0; mem_byte_index<= (C_S_AXI_DATA_WIDTH/8-1); mem_byte_index=mem_byte_index+1)
begin:BYTE_BRAM_GEN
wire [8-1:0] data_in ;
wire [8-1:0] data_out;
reg [8-1:0] byte_ram [0 : 15];
integer j;
//assigning 8 bit data
assign data_in = S_AXI_WDATA[(mem_byte_index*8+7) -: 8];
assign data_out = byte_ram[mem_address];
always @( posedge S_AXI_ACLK )
begin
if (mem_wren && S_AXI_WSTRB[mem_byte_index])
begin
byte_ram[mem_address] <= data_in;
end
end
always @( posedge S_AXI_ACLK )
begin
if (mem_rden)
begin
mem_data_out[i][(mem_byte_index*8+7) -: 8] <= data_out;
end
end
end
end
endgenerate
//Output register or memory read data
always @( mem_data_out, axi_rvalid)
begin
if (axi_rvalid)
begin
// Read address mux
axi_rdata <= mem_data_out[0];
end
else
begin
axi_rdata <= 32'h00000000;
end
end
// Add user logic here
// User logic ends
endmodule
(D)顶层模块
作用:例化读写控制模块,主机和从机代码。
module AXI_full_top(
input M_AXI_ACLK ,
input M_AXI_ARESETN ,
input WR_START
);
//主机参数
//parameter C_M_TARGET_SLAVE_BASE_ADDR = 32'h4000_0000 ;
parameter integer C_M_AXI_BURST_LEN = 15 ; //突发长度要大于2,且为2的幂次方4,8,16,32,64,256,512
parameter integer C_M_AXI_ID_WIDTH = 1 ;
parameter integer C_M_AXI_ADDR_WIDTH = 32 ;
parameter integer C_M_AXI_DATA_WIDTH = 32 ;
parameter integer C_M_AXI_AWUSER_WIDTH = 0 ;
parameter integer C_M_AXI_ARUSER_WIDTH = 0 ;
parameter integer C_M_AXI_WUSER_WIDTH = 0 ;
parameter integer C_M_AXI_RUSER_WIDTH = 0 ;
parameter integer C_M_AXI_BUSER_WIDTH = 0 ;
//从机参数
parameter integer C_S_AXI_ID_WIDTH = 1 ;
parameter integer C_S_AXI_DATA_WIDTH = 32 ;
parameter integer C_S_AXI_ADDR_WIDTH = 32 ;
parameter integer C_S_AXI_AWUSER_WIDTH = 0 ;
parameter integer C_S_AXI_ARUSER_WIDTH = 0 ;
parameter integer C_S_AXI_WUSER_WIDTH = 0 ;
parameter integer C_S_AXI_RUSER_WIDTH = 0 ;
parameter integer C_S_AXI_BUSER_WIDTH = 0 ;
//写地址信号
wire [C_M_AXI_ID_WIDTH-1 : 0] M_AXI_AWID ;
wire [C_M_AXI_ADDR_WIDTH-1 : 0] M_AXI_AWADDR ;
wire [7 : 0] M_AXI_AWLEN ;
wire [2 : 0] M_AXI_AWSIZE ;
wire [1 : 0] M_AXI_AWBURST ;
wire M_AXI_AWLOCK ;
wire [3 : 0] M_AXI_AWCACHE ;
wire [2 : 0] M_AXI_AWPROT ;
wire [3 : 0] M_AXI_AWQOS ;
wire [C_M_AXI_AWUSER_WIDTH-1 : 0] M_AXI_AWUSER ;
wire M_AXI_AWVALID ;
wire M_AXI_AWREADY ;
//写数据通道
wire [C_M_AXI_DATA_WIDTH-1 : 0] M_AXI_WDATA ;
wire [C_M_AXI_DATA_WIDTH/8-1 : 0] M_AXI_WSTRB ;
wire M_AXI_WLAST ;
wire [C_M_AXI_WUSER_WIDTH-1 : 0] M_AXI_WUSER ;
wire M_AXI_WVALID ;
wire M_AXI_WREADY ;
//写相应通道
wire [C_M_AXI_ID_WIDTH-1 : 0] M_AXI_BID ;
wire [1 : 0] M_AXI_BRESP ;
wire [C_M_AXI_BUSER_WIDTH-1 : 0] M_AXI_BUSER ;
wire M_AXI_BVALID ;
wire M_AXI_BREADY ;
//读地址通道
wire [C_M_AXI_ID_WIDTH-1 : 0] M_AXI_ARID ;
wire [C_M_AXI_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 [C_M_AXI_ARUSER_WIDTH-1 : 0] M_AXI_ARUSER ;
wire M_AXI_ARVALID ;
wire M_AXI_ARREADY ;
//读数据通道
wire [C_M_AXI_ID_WIDTH-1 : 0] M_AXI_RID ;
wire [C_M_AXI_DATA_WIDTH-1 : 0] M_AXI_RDATA ;
wire [1 : 0] M_AXI_RRESP ;
wire M_AXI_RLAST ;
wire [C_M_AXI_RUSER_WIDTH-1 : 0] M_AXI_RUSER ;
wire M_AXI_RVALID ;
wire M_AXI_RREADY ;
//读写数据控制端口
wire write_flag ;
wire read_flag ;
wire [C_M_AXI_DATA_WIDTH - 1 : 0] M_AXI_AWADDR_r ;
wire [C_M_AXI_DATA_WIDTH - 1 : 0] M_AXI_WDATA_r ;
wire [C_M_AXI_DATA_WIDTH - 1 : 0] M_AXI_ARADDR_r ;
/*********************例化读写控制模块*********************/
write_read_data
#(
.C_M_AXI_BURST_LEN (C_M_AXI_BURST_LEN ), //突发长度
.C_M_AXI_ADDR_WIDTH (C_M_AXI_ADDR_WIDTH ), //数据地址位宽
.C_M_AXI_DATA_WIDTH (C_M_AXI_DATA_WIDTH ) //数据位宽
)
u_write_read_data
(
.M_AXI_ACLK (M_AXI_ACLK ),
.M_AXI_ARESETN (M_AXI_ARESETN ),
//读写总开始标志信号
.WR_START (WR_START ),
//写数据地址端口
.write_flag (write_flag ), //写数据开始标志信号
.M_AXI_AWADDR_r (M_AXI_AWADDR_r ), //写的数据首地址
.M_AXI_AWVALID (M_AXI_AWVALID ), //主机写数据地址有效标志
.M_AXI_AWREADY (M_AXI_AWREADY ), //从机写数据地址准备好信号
//写数据端口
.M_AXI_WVALID (M_AXI_WVALID ), //主机写数据有效信号
.M_AXI_WREADY (M_AXI_WREADY ), //从机写数据准备好信号
.M_AXI_WLAST (M_AXI_WLAST ), //主机写数据最后一个标志信号
.M_AXI_WDATA_r (M_AXI_WDATA_r ),
//读数据地址端口
.read_flag (read_flag ), //读数据开始标志信号
.M_AXI_ARADDR_r (M_AXI_ARADDR_r ), //读的数据首地址
.M_AXI_RLAST (M_AXI_RLAST )
);
/********************例化Full_axi_主机********************/
AXI_full_M
#(
//.C_M_TARGET_SLAVE_BASE_ADDR (C_M_TARGET_SLAVE_BASE_ADDR ),
.C_M_AXI_BURST_LEN (C_M_AXI_BURST_LEN ),
.C_M_AXI_ID_WIDTH (C_M_AXI_ID_WIDTH ),
.C_M_AXI_ADDR_WIDTH (C_M_AXI_ADDR_WIDTH ),
.C_M_AXI_DATA_WIDTH (C_M_AXI_DATA_WIDTH ),
.C_M_AXI_AWUSER_WIDTH (C_M_AXI_AWUSER_WIDTH ),
.C_M_AXI_ARUSER_WIDTH (C_M_AXI_ARUSER_WIDTH ),
.C_M_AXI_WUSER_WIDTH (C_M_AXI_WUSER_WIDTH ),
.C_M_AXI_RUSER_WIDTH (C_M_AXI_RUSER_WIDTH ),
.C_M_AXI_BUSER_WIDTH (C_M_AXI_BUSER_WIDTH )
)
u_AXI_full_M
(
.M_AXI_ACLK (M_AXI_ACLK ),
.M_AXI_ARESETN (M_AXI_ARESETN ),
//写地址信号
.M_AXI_AWID (M_AXI_AWID ), //写地址ID,一半不用
.M_AXI_AWADDR (M_AXI_AWADDR ), //写数据的地址
.M_AXI_AWLEN (M_AXI_AWLEN ), //突发长度
.M_AXI_AWSIZE (M_AXI_AWSIZE ), //数据宽度
.M_AXI_AWBURST (M_AXI_AWBURST ), //突发模式选择,00为固定地址,01为自增地址突发
.M_AXI_AWLOCK (M_AXI_AWLOCK ), //锁信号一般不用,保持为0
.M_AXI_AWCACHE (M_AXI_AWCACHE ), //缓存模式,有多种,这里使用无缓存模式
.M_AXI_AWPROT (M_AXI_AWPROT ), //保护编码,指示特权访问/安全访问/指明数据访问还是指令访问,这里设置全0
.M_AXI_AWQOS (M_AXI_AWQOS ), //Qos信号,此协议的Qos信号不起作用,默认0值即可
.M_AXI_AWREGION (M_AXI_AWREGION ),
.M_AXI_AWUSER (M_AXI_AWUSER ), //区域标识符,这里没用,默认0值
.M_AXI_AWVALID (M_AXI_AWVALID ), //写地址有效信号
.M_AXI_AWREADY (M_AXI_AWREADY ), //应答信号是从机输入的不需要控制
//写数据通道
.M_AXI_WDATA (M_AXI_WDATA ), //写的数据
.M_AXI_WSTRB (M_AXI_WSTRB ), //掩码信号,每8位数据就对应一位掩码信号,掩码信号为高时,数据才能正常传输
.M_AXI_WLAST (M_AXI_WLAST ), //写数据最后一个数据标志信号
.M_AXI_WUSER (M_AXI_WUSER ), //用户自定义信号
.M_AXI_WVALID (M_AXI_WVALID ), //写数据有效信号
.M_AXI_WREADY (M_AXI_WREADY ), //从机准备好信号是从从机输入的
//写相应通道
.M_AXI_BID (M_AXI_BID ),
.M_AXI_BRESP (M_AXI_BRESP ),
.M_AXI_BUSER (M_AXI_BUSER ),
.M_AXI_BVALID (M_AXI_BVALID ),
.M_AXI_BREADY (M_AXI_BREADY ), //写响应信号,主机给从机的响应准备好信号
//读地址通道
.M_AXI_ARID (M_AXI_ARID ), //读地址ID,一半不用
.M_AXI_ARADDR (M_AXI_ARADDR ), //读数据的地址
.M_AXI_ARLEN (M_AXI_ARLEN ), //突发长度
.M_AXI_ARSIZE (M_AXI_ARSIZE ), //数据宽度
.M_AXI_ARBURST (M_AXI_ARBURST ), //突发模式选择,00为固定地址,01为自增地址突发
.M_AXI_ARLOCK (M_AXI_ARLOCK ), //锁信号一般不用,保持为0
.M_AXI_ARCACHE (M_AXI_ARCACHE ), //缓存模式,有多种,这里使用无缓存模式
.M_AXI_ARPROT (M_AXI_ARPROT ), //保护编码,指示特权访问/安全访问/指明数据访问还是指令访问,这里设置全0
.M_AXI_ARQOS (M_AXI_ARQOS ), //Qos信号,此协议的Qos信号不起作用,默认0值即可
.M_AXI_ARREGION (M_AXI_ARREGION ),
.M_AXI_ARUSER (M_AXI_ARUSER ), //区域标识符,这里没用,默认0值
.M_AXI_ARVALID (M_AXI_ARVALID ), //读地址有效信号
.M_AXI_ARREADY (M_AXI_ARREADY ), //应答信号是从机输入的不需要控制
//读数据通道
.M_AXI_RID (M_AXI_RID ),
.M_AXI_RDATA (M_AXI_RDATA ), //读入数据
.M_AXI_RRESP (M_AXI_RRESP ),
.M_AXI_RLAST (M_AXI_RLAST ), //从机数据的读数据LAST信号
.M_AXI_RUSER (M_AXI_RUSER ),
.M_AXI_RVALID (M_AXI_RVALID ), //从机输入的读数据准备好信号
.M_AXI_RREADY (M_AXI_RREADY ), //读数据通道主机准备好信号
//读写数据控制端口
.write_flag (write_flag ), //写操作标志信号
.read_flag (read_flag ), //读操作标志信号
.M_AXI_AWADDR_r (M_AXI_AWADDR_r ), //写数据地址寄存
.M_AXI_WDATA_r (M_AXI_WDATA_r ), //写数据赋值寄存器
.M_AXI_ARADDR_r (M_AXI_ARADDR_r ) //读数据地址寄存
);
//例化AXI从机
AXI_full_slave
#(
.C_S_AXI_ID_WIDTH (C_M_AXI_ID_WIDTH ),
.C_S_AXI_DATA_WIDTH (C_M_AXI_DATA_WIDTH ),
.C_S_AXI_ADDR_WIDTH (C_M_AXI_ADDR_WIDTH ),
.C_S_AXI_AWUSER_WIDTH (C_M_AXI_AWUSER_WIDTH ),
.C_S_AXI_ARUSER_WIDTH (C_M_AXI_ARUSER_WIDTH ),
.C_S_AXI_WUSER_WIDTH (C_M_AXI_WUSER_WIDTH ),
.C_S_AXI_RUSER_WIDTH (C_M_AXI_RUSER_WIDTH ),
.C_S_AXI_BUSER_WIDTH (C_M_AXI_BUSER_WIDTH )
)
u_AXI_full_slave
(
//从机时钟和复位
.S_AXI_ACLK (M_AXI_ACLK ),
.S_AXI_ARESETN (M_AXI_ARESETN ),
//从机写数据地址参数
.S_AXI_AWID (M_AXI_AWID ),
.S_AXI_AWADDR (M_AXI_AWADDR ),
.S_AXI_AWLEN (M_AXI_AWLEN ),
.S_AXI_AWSIZE (M_AXI_AWSIZE ),
.S_AXI_AWBURST (M_AXI_AWBURST ),
.S_AXI_AWLOCK (M_AXI_AWLOCK ),
.S_AXI_AWCACHE (M_AXI_AWCACHE ),
.S_AXI_AWPROT (M_AXI_AWPROT ),
.S_AXI_AWQOS (M_AXI_AWQOS ),
.S_AXI_AWREGION (M_AXI_AWREGION ),
.S_AXI_AWUSER (M_AXI_AWUSER ),
.S_AXI_AWVALID (M_AXI_AWVALID ),
.S_AXI_AWREADY (M_AXI_AWREADY ),
//从机写数据
.S_AXI_WDATA (M_AXI_WDATA ),
.S_AXI_WSTRB (M_AXI_WSTRB ),
.S_AXI_WLAST (M_AXI_WLAST ),
.S_AXI_WUSER (M_AXI_WUSER ),
.S_AXI_WVALID (M_AXI_WVALID ),
.S_AXI_WREADY (M_AXI_WREADY ),
//从机写数据响应通道参数
.S_AXI_BID (M_AXI_BID ),
.S_AXI_BRESP (M_AXI_BRESP ),
.S_AXI_BUSER (M_AXI_BUSER ),
.S_AXI_BVALID (M_AXI_BVALID ),
.S_AXI_BREADY (M_AXI_BREADY ),
//从机读数据地址参数
.S_AXI_ARID (M_AXI_ARID ),
.S_AXI_ARADDR (M_AXI_ARADDR ),
.S_AXI_ARLEN (M_AXI_ARLEN ),
.S_AXI_ARSIZE (M_AXI_ARSIZE ),
.S_AXI_ARBURST (M_AXI_ARBURST ),
.S_AXI_ARLOCK (M_AXI_ARLOCK ),
.S_AXI_ARCACHE (M_AXI_ARCACHE ),
.S_AXI_ARPROT (M_AXI_ARPROT ),
.S_AXI_ARQOS (M_AXI_ARQOS ),
.S_AXI_ARREGION (M_AXI_ARREGION ),
.S_AXI_ARUSER (M_AXI_ARUSER ),
.S_AXI_ARVALID (M_AXI_ARVALID ),
.S_AXI_ARREADY (M_AXI_ARREADY ),
.S_AXI_RID (M_AXI_RID ),
.S_AXI_RDATA (M_AXI_RDATA ),
.S_AXI_RRESP (M_AXI_RRESP ),
.S_AXI_RLAST (M_AXI_RLAST ),
.S_AXI_RUSER (M_AXI_RUSER ),
.S_AXI_RVALID (M_AXI_RVALID ),
.S_AXI_RREADY (M_AXI_RREADY )
);
endmodule
(E)testbench
作用:产生时钟,复位和总的读写开始信号WR_START,并例化顶层模块。文章来源:https://www.toymoban.com/news/detail-830154.html
`timescale 1ns / 1ps
module AXI_full_tb;
reg M_AXI_ACLK ;
reg M_AXI_ARESETN ;
reg WR_START ;
parameter T = 20;
initial begin
M_AXI_ACLK = 1'b0;
M_AXI_ARESETN = 1'b0;
WR_START = 1'b0;
#(2*T);
M_AXI_ARESETN = 1'b1;
#(10*T);
WR_START = 1'b1;
#(T);
WR_START = 1'b0;
end
always#(T/2) M_AXI_ACLK = !M_AXI_ACLK;
AXI_full_top u_AXI_full_top(
.M_AXI_ACLK (M_AXI_ACLK ),
.M_AXI_ARESETN (M_AXI_ARESETN ),
.WR_START (WR_START )
);
endmodule
(4)仿真结果
写数据时序:
读数据时序:
文章来源地址https://www.toymoban.com/news/detail-830154.html
到了这里,关于【接口协议】FPGA AXI接口协议详解的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!