本文主要目的是
1.手撕简易AXI4-FULL协议;
2. 通过反复查看spec加深对spec的理解,学习AXI4协议各个端口信号的意义,以及怎么定义该信号的拉高和拉低;
3.进一步学习xilinx的vivado中的AXI4-FULL IP核,逐行阅读代码,从而完全掌握AXI4-FULL协议的原理和实现过程(在下一篇博文会通过展现代码和注释来进一步解读AXI4-FULL协议)。
下面是rtl和tb文件代码展示:
分别为top_axi_full、axi_full_master、axi_full_slave、tb_top_axi_full
module top_axi_full(
input clk ,
input rst_n ,
input txn
);
parameter C_M_TARGET_SLAVE_BASE_ADDR = 32'h40000000 ;
parameter integer C_M_AXI_BURST_LEN = 16 ;
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 = 16 ;
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 INIT_AXI_TXN ;
assign INIT_AXI_TXN = txn;
wire TXN_DONE ;
wire ERROR ;
//全局信号
wire M_AXI_ACLK ;
assign M_AXI_ACLK = clk;
wire M_AXI_ARESETN ;
assign M_AXI_ARESETN = rst_n;
//写地址通道
wire [C_M_AXI_ID_WIDTH-1 : 0] M_AXI_AWID ; //写地址ID标签。地址组的识别标签。
//outstanding超前传输,带id连续传输写地址和写数据,写完数据第一个id回复后,再传输第二个地址的写数据。造成前向死锁
//out of order无序传输,带多个id写,不同id回复,如id=2先写,id=1后写,回复的可能是id=1的控制信号。造成反向死锁
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 ; //*锁类型。AXI4没有
wire [3 : 0] M_AXI_AWCACHE ; //*存储器类型。此信号指示如何通过系统进行事务。Device Non-bufferable,还有很多模式
wire [2 : 0] M_AXI_AWPROT ; //*保护类型。该信号指示事务的权限和安全级别,以及该事务是数据访问还是指令访问
wire [3 : 0] M_AXI_AWQOS ; //*服务质量。为每个写事务发送的QoS标识符
//output wire [3 : 0] M_AXI_AWREGION , //*区域标识符。允许从机上的单个物理接口用于多个逻辑接口。
wire [C_M_AXI_AWUSER_WIDTH-1 : 0] M_AXI_AWUSER ; //*用户信号。在写地址通道中,可选的用户自定义信号。
wire M_AXI_AWVALID ; //写地址有效。该信号表示信道正在发出有效的写地址和控制信息
wire M_AXI_AWREADY ; //写地址准备。该信号表示从设备已准备好接受地址和相关的控制信号
//写数据通道
//output wire [C_M_AXI_DATA_WIDTH-1 : 0] M_AXI_WID , //写数据ID标签。写数据传输的识别标签。AXI3才有
wire [C_M_AXI_DATA_WIDTH-1 : 0] M_AXI_WDATA ; //写数据
wire [C_M_AXI_DATA_WIDTH/8-1 : 0] M_AXI_WSTRB ; //*写入strobes。此信号指示哪些字节道保存了有效的数据。写数据总线的每8位都有一个写频闪位。
wire M_AXI_WLAST ; //最后写入。该信号表示写突发中的最后一次传输
wire [C_M_AXI_WUSER_WIDTH-1 : 0] M_AXI_WUSER ; //*用户信号。在写数据通道中,可选的用户自定义信号。
wire M_AXI_WVALID ; //写数据有效。该信号表示有效的写入数据和strobes(M_AXI_WSTRB)可用。
wire M_AXI_WREADY ; //写数据准备。该信号表示从设备已准备好接受写入数据
//写响应通道
wire [C_M_AXI_ID_WIDTH-1 : 0] M_AXI_BID ; //写响应ID标签。写响应的识别标签。
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 ; //读地址ID。读地址组的识别标签。
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 ; //*锁类型。AXI4没有
wire [3 : 0] M_AXI_ARCACHE ; //*存储器类型。此信号指示如何通过系统进行事务。Device Non-bufferable,还有很多模式
wire [2 : 0] M_AXI_ARPROT ; //*保护类型。该信号指示事务的权限和安全级别,以及该事务是数据访问还是指令访问
wire [3 : 0] M_AXI_ARQOS ; //*服务质量。为每个读事务发送的QoS标识符
//output wire [3 : 0] M_AXI_ARREGION , //*区域标识符。允许从机上的单个物理接口用于多个逻辑接口。
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 ; //读数据ID标签。读数据传输的识别标签。
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 ; //读数据准备。该信号表示主设备可以接受读取的数据和响应信息
axi_full_master#
(
.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_master(
.INIT_AXI_TXN (INIT_AXI_TXN ),
.TXN_DONE (TXN_DONE ),
.ERROR (ERROR ),
.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 ), //突发类型。突发类型和大小信息,确定如何计算突发中每个传输的地址
.M_AXI_AWCACHE (M_AXI_AWCACHE ), //*存储器类型。此信号指示如何通过系统进行事务。Device Non-bufferable,还有很多模式
.M_AXI_AWPROT (M_AXI_AWPROT ), //*保护类型。该信号指示事务的权限和安全级别,以及该事务是数据访问还是指令访问
.M_AXI_AWQOS (M_AXI_AWQOS ), //*服务质量。为每个写事务发送的QoS标识符
//.M_AXI_AWREGION (M_AXI_AWREGION ), //*区域标识符。允许从机上的单个物理接口用于多个逻辑接口。
.M_AXI_AWUSER (M_AXI_AWUSER ), //*用户信号。在写地址通道中,可选的用户自定义信号。
.M_AXI_AWVALID (M_AXI_AWVALID ), //写地址有效。该信号表示信道正在发出有效的写地址和控制信息
.M_AXI_AWREADY (M_AXI_AWREADY ), //写地址准备。该信号表示从设备已准备好接受地址和相关的控制信号
//.M_AXI_WID (M_AXI_WID ), //写数据ID标签。写数据传输的识别标签。AXI3才有
.M_AXI_WDATA (M_AXI_WDATA ) , //写数据
.M_AXI_WSTRB (M_AXI_WSTRB ) , //*写入strobes。此信号指示哪些字节道保存了有效的数据。写数据总线的每8位都有一个写频闪位。
.M_AXI_WLAST (M_AXI_WLAST ) , //最后写入。该信号表示写突发中的最后一次传输
.M_AXI_WUSER (M_AXI_WUSER ) , //*用户信号。在写数据通道中,可选的用户自定义信号。
.M_AXI_WVALID (M_AXI_WVALID ), //写数据有效。该信号表示有效的写入数据和strobes(M_AXI_WSTRB)可用。
.M_AXI_WREADY (M_AXI_WREADY ), //写数据准备。该信号表示从设备已准备好接受写入数据
.M_AXI_BID (M_AXI_BID ), //写响应ID标签。写响应的识别标签。
.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 ), //突发类型。突发类型和大小信息,确定如何计算突发中每个传输的地址
.M_AXI_ARCACHE (M_AXI_ARCACHE ), //*存储器类型。此信号指示如何通过系统进行事务。Device Non-bufferable,还有很多模式
.M_AXI_ARPROT (M_AXI_ARPROT ), //*保护类型。该信号指示事务的权限和安全级别,以及该事务是数据访问还是指令访问
.M_AXI_ARQOS (M_AXI_ARQOS ), //*服务质量。为每个读事务发送的QoS标识符
//.M_AXI_ARREGION (M_AXI_ARREGION ), //*区域标识符。允许从机上的单个物理接口用于多个逻辑接口。
.M_AXI_ARUSER (M_AXI_ARUSER ), //*用户信号。在读地址通道中,可选的用户自定义信号。
.M_AXI_ARVALID (M_AXI_ARVALID ), //读地址有效。该信号表示信道正在发出有效的读地址和控制信息
.M_AXI_ARREADY (M_AXI_ARREADY ), //读地址准备。该信号表示从设备已准备好接受地址和相关的控制信号
.M_AXI_RID (M_AXI_RID ), //读数据ID标签。读数据传输的识别标签。
.M_AXI_RDATA (M_AXI_RDATA ) , //读数据
.M_AXI_RRESP (M_AXI_RRESP ) , //读响应。该信号表示读传输的状态。
.M_AXI_RLAST (M_AXI_RLAST ) , //最后读取。该信号表示读突发中的最后一次传输
.M_AXI_RUSER (M_AXI_RUSER ) , //*用户信号。在读数据通道中,可选的用户自定义信号。
.M_AXI_RVALID (M_AXI_RVALID ), //读数据有效。该信号表示信道正在发送所需的读取数据。
.M_AXI_RREADY (M_AXI_RREADY ) //读数据准备。该信号表示主设备可以接受读取的数据和响应信息
);
axi_full_slave #
(
.C_S_AXI_ID_WIDTH (C_S_AXI_ID_WIDTH ),
.C_S_AXI_DATA_WIDTH (C_S_AXI_DATA_WIDTH ),
.C_S_AXI_ADDR_WIDTH (C_S_AXI_ADDR_WIDTH ),
.C_S_AXI_AWUSER_WIDTH (C_S_AXI_AWUSER_WIDTH ),
.C_S_AXI_ARUSER_WIDTH (C_S_AXI_ARUSER_WIDTH ),
.C_S_AXI_WUSER_WIDTH (C_S_AXI_WUSER_WIDTH ),
.C_S_AXI_RUSER_WIDTH (C_S_AXI_RUSER_WIDTH ),
.C_S_AXI_BUSER_WIDTH (C_S_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 (4'b0),
.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 (4'b0),
.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
module axi_full_master#
(
parameter C_M_TARGET_SLAVE_BASE_ADDR = 32'h40000000 ,
parameter integer C_M_AXI_BURST_LEN = 16 ,
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标签。地址组的识别标签。
//outstanding超前传输,带id连续传输写地址和写数据,写完数据第一个id回复后,再传输第二个地址的写数据。造成前向死锁
//out of order无序传输,带多个id写,不同id回复,如id=2先写,id=1后写,回复的可能是id=1的控制信号。造成反向死锁
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 , //突发类型。突发类型和大小信息,确定如何计算突发中每个传输的地址
//output wire M_AXI_AWLOCK , //*锁类型。AXI4没有
output wire [3 : 0] M_AXI_AWCACHE , //*存储器类型。此信号指示如何通过系统进行事务。Device Non-bufferable,还有很多模式
output wire [2 : 0] M_AXI_AWPROT , //*保护类型。该信号指示事务的权限和安全级别,以及该事务是数据访问还是指令访问
output wire [3 : 0] M_AXI_AWQOS , //*服务质量。为每个写事务发送的QoS标识符
//output wire [3 : 0] M_AXI_AWREGION , //*区域标识符。允许从机上的单个物理接口用于多个逻辑接口。
output wire [C_M_AXI_AWUSER_WIDTH-1 : 0] M_AXI_AWUSER , //*用户信号。在写地址通道中,可选的用户自定义信号。
output wire M_AXI_AWVALID , //写地址有效。该信号表示信道正在发出有效的写地址和控制信息。当主机驱动有效的地址和控制信息时拉高,然后保持,直到检测到M_AXI_AWREADY为高电平拉低
//The master can assert the AWVALID signal only when it drives valid address and control information. When asserted, AWVALID must remain
//asserted until the rising clock edge after the slave asserts AWREADY.
input wire M_AXI_AWREADY , //写地址准备。该信号表示从设备已准备好接受地址和相关的控制信号。规格书推荐默认为高电平,当从机的S_AXI_AWREADY拉高,必须要有有效的地址已提供
//The default state of AWREADY can be either HIGH or LOW. This specification recommends a default state of
//HIGH. When AWREADY is HIGH the slave must be able to accept any valid address that is presented to it.
//写数据通道
//output wire [C_M_AXI_DATA_WIDTH-1 : 0] M_AXI_WID , //写数据ID标签。写数据传输的识别标签。AXI3才有
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 , //写入strobes。此信号指示哪些字节道保存了有效的数据。写数据总线的每8位都有一个写频闪位。
output wire M_AXI_WLAST , //最后写入。该信号表示写数据通道正在传输最后一个写数据。当传输最后一个写数据时拉高一个时钟周期。
//The master must assert the WLAST signal while it is driving the final write transfer in the burst.
output wire [C_M_AXI_WUSER_WIDTH-1 : 0] M_AXI_WUSER , //*用户信号。在写数据通道中,可选的用户自定义信号。
output wire M_AXI_WVALID , //写数据有效。该信号表示有效的写入数据和strobes(M_AXI_WSTRB)可用。当主机驱动有效的写数据时拉高,然后保持,直到检测到S_AXI_WREADY为高电平拉低
//During a write burst, the master can assert the WVALID signal only when it drives valid write data. When asserted,
//WVALID must remain asserted until the rising clock edge after the slave asserts WREADY.
input wire M_AXI_WREADY , //写数据准备。该信号表示从设备已准备好接受写入数据。规格书推荐默认为高电平,前提从机必须在一个周期内接收有效的写数据。
//The default state of WREADY can be HIGH, but only if the slave can always accept write data in a single cycle.
//写响应通道
input wire [C_M_AXI_ID_WIDTH-1 : 0] M_AXI_BID , //写响应ID标签。写响应的识别标签。
input wire [1 : 0] M_AXI_BRESP , //写响应。该信号表示写事务的状态。
input wire [C_M_AXI_BUSER_WIDTH-1 : 0] M_AXI_BUSER , //*用户信号。在写响应通道中,可选的用户自定义信号。
input wire M_AXI_BVALID , //写响应有效。该信号表示信道正在发出有效的写响应信号。当从机驱动有效的写响应时拉高,然后保持,直到检测到S_AXI_BREADY为高电平拉低
//The slave can assert the BVALID signal only when it drives a valid write response. When asserted, BVALID must
//remain asserted until the rising clock edge after the master asserts BREADY.
output wire M_AXI_BREADY , //写响应准备。该信号表示主设备可以接受写响应。规格书推荐默认为高电平,前提主机必须在一个周期内接收有效的写响应。
//The default state of BREADY can be HIGH, but only if the master can always accept a write response in a single cycle.
//读地址通道
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 , //突发类型。突发类型和大小信息,确定如何计算突发中每个传输的地址
//output wire M_AXI_ARLOCK , //*锁类型。AXI4没有
output wire [3 : 0] M_AXI_ARCACHE , //*存储器类型。此信号指示如何通过系统进行事务。Device Non-bufferable,还有很多模式
output wire [2 : 0] M_AXI_ARPROT , //*保护类型。该信号指示事务的权限和安全级别,以及该事务是数据访问还是指令访问
output wire [3 : 0] M_AXI_ARQOS , //*服务质量。为每个读事务发送的QoS标识符
//output wire [3 : 0] M_AXI_ARREGION , //*区域标识符。允许从机上的单个物理接口用于多个逻辑接口。
output wire [C_M_AXI_ARUSER_WIDTH-1 : 0] M_AXI_ARUSER , //*用户信号。在读地址通道中,可选的用户自定义信号。
output wire M_AXI_ARVALID , //读地址有效。该信号表示信道正在发出有效的读地址和控制信息。当主机驱动有效的地址和控制信息时拉高,然后保持,直到检测到S_AXI_ARREADY为高电平拉低
//The master can assert the ARVALID signal only when it drives valid address and control information. When
//asserted, ARVALID must remain asserted until the rising clock edge after the slave asserts the ARREADY signal.
input wire M_AXI_ARREADY , //读地址准备。该信号表示从设备已准备好接受地址和相关的控制信号。规格书推荐默认为高电平,当从机的S_AXI_ARREADY拉高,必须要有有效的地址已提供
//The default state of ARREADY can be either HIGH or LOW. This specification recommends a default state of HIGH.
//If ARREADY is HIGH then the slave must be able to accept any valid address that is presented to it.
//读数据通道
input wire [C_M_AXI_ID_WIDTH-1 : 0] M_AXI_RID , //读数据ID标签。读数据传输的识别标签。
input wire [C_M_AXI_DATA_WIDTH-1 : 0] M_AXI_RDATA , //读数据
input wire [1 : 0] M_AXI_RRESP , //读响应。该信号表示读传输的状态。
input wire M_AXI_RLAST , //最后读取。该信号表示读突发中的最后一次传输。当传输最后一个读数据时拉高一个时钟周期。
//The slave must assert the RLAST signal when it is driving the final read transfer in the burst.
input wire [C_M_AXI_RUSER_WIDTH-1 : 0] M_AXI_RUSER , //*用户信号。在读数据通道中,可选的用户自定义信号。
input wire M_AXI_RVALID , //读数据有效。该信号表示信道正在发送所需的读取数据。当主机驱动有效的读数据时拉高,然后保持,直到检测到S_AXI_WREADY为高电平拉低
//The slave can assert the RVALID signal only when it drives valid read data. When asserted, RVALID must remain
//asserted until the rising clock edge after the master asserts RREADY. Even if a slave has only one source of read
//data, it must assert the RVALID signal only in response to a request for data.
output wire M_AXI_RREADY //读数据准备。该信号表示主设备可以接受读取的数据和响应信息。规格书推荐默认为高电平,前提主机必须立即接收有效的读数据。assign语句?
//The master interface uses the RREADY signal to indicate that it accepts the data. The default state of RREADY
//can be HIGH, but only if the master is able to accept read data immediately, whenever it starts a read transaction.
);
/*******************************function*******************************/
function integer clog2(input integer number);
for(clog2 = 0; number > 0; clog2 = clog2 + 1)
number = number >> 1;
endfunction
/*********************************reg*********************************/
reg r_txn_ff1 ;
reg r_txn_ff2 ;
reg [C_M_AXI_ADDR_WIDTH-1 : 0] r_m_awaddr ;
reg r_m_awvalid ;
reg [C_M_AXI_DATA_WIDTH-1 : 0] r_m_wdata ;
reg r_m_wlast ;
reg r_m_wvalid ;
reg r_m_bready ;
reg r_error ;
reg [C_M_AXI_ADDR_WIDTH-1 : 0] r_m_araddr ;
reg r_m_arvalid ;
reg r_m_rready ;
reg r_read_ff1 ;
reg r_read_ff2 ;
//写入数据计数器
reg [clog2(C_M_AXI_BURST_LEN - 1) : 0] r_m_wdata_cnt ;
/********************************wire********************************/
wire w_m_read_en ;
/******************************parameter******************************/
//写地址递增
parameter AWADDR_PLUS = C_M_AXI_BURST_LEN * (C_M_AXI_ADDR_WIDTH / 8);
/*********************************port*********************************/
/*******************************machine*******************************/
/*****************************component*****************************/
/*******************************assign*******************************/
assign M_AXI_AWID = 'b0 ;
assign M_AXI_AWADDR = r_m_awaddr ;
assign M_AXI_AWLEN = C_M_AXI_BURST_LEN ;
assign M_AXI_AWSIZE = 3'b010 ;
assign M_AXI_AWBURST = 2'b01 ;
//00为固定。在一个固定的突发中:对于突发中的每个传输,地址都是相同的。有效的字节道对于突发中的所有节拍都是常量的。
//然而,在这些字节通道中,断言了WSTRB的实际字节可以因突发中的每个节拍而有所不同。
//这种突发类型用于重复访问相同的位置,例如在加载或清空FIFO时。
//01为增加。在递增突发中,突发中每个传输的地址是前一个传输地址的增量。增量值取决于传输的大小。
//例如,大小为4字节的突发中每个传输的地址是前一个地址加上4。此突发类型用于访问正常的顺序内存。
//02为包装。包装突发类似于递增突发,只是如果达到地址上限,则地址环绕到较低的地址。
//以下限制适用于包装突发:起始地址必须与每个传输的大小对齐,突发的长度必须为2、4、8或16次传输。
//包装突发的行为是:突发使用的最低地址与要传输的数据的总大小对齐,即((突发中每个传输的大小)×(突发中的传输数量))。
//此地址被定义为包装边界。每次传输后,地址以与INCR突发相同的方式递增。
//但是,如果此增量的地址为((行边界)+(要传输数据的总大小)),则地址将环绕到行边界。
//突发中的第一个传输可以使用高于包装边界的地址,这受适用于包装突发的限制。
//这意味着地址包装了第一个地址高于包装边界的任何WRAP突发。此突发类型用于高速缓存行访问。
assign M_AXI_AWCACHE = 'd2 ;
assign M_AXI_AWPROT = 'b0 ;
assign M_AXI_AWQOS = 'b0 ;
assign M_AXI_AWUSER = 'b1 ;
assign M_AXI_AWVALID = r_m_awvalid ;
assign M_AXI_WDATA = r_m_wdata ;
assign M_AXI_WSTRB = '1 ;
assign M_AXI_WLAST = r_m_wlast ;
assign M_AXI_WUSER = 'b1 ;
assign M_AXI_WVALID = r_m_wvalid ;
assign M_AXI_BREADY = r_m_bready ;
assign M_AXI_ARID = 'b0 ;
assign M_AXI_ARADDR = r_m_araddr ;
assign M_AXI_ARLEN = C_M_AXI_BURST_LEN ;
assign M_AXI_ARSIZE = 3'b010 ;
assign M_AXI_ARBURST = 2'b01 ;
assign M_AXI_ARCACHE = 'd2 ;
assign M_AXI_ARPROT = 'b0 ;
assign M_AXI_ARQOS = 'b0 ;
assign M_AXI_ARUSER = 'b1 ;
assign M_AXI_ARVALID = r_m_arvalid ;
assign w_m_read_en = (M_AXI_BVALID && M_AXI_BREADY && (M_AXI_BRESP == 2'b00))? 1'b1 : 1'b0; //改成状态机可变成多次读写,条件也需要适当做修改
assign M_AXI_RREADY = r_m_rready ;
/*******************************always*******************************/
/*******************************写地址通道*******************************/
//同步+延迟
always@(posedge M_AXI_ACLK)
if(M_AXI_ARESETN == 1'b0)begin
r_txn_ff1 <= 'b0;
r_txn_ff2 <= 'b0;
end
else begin
r_txn_ff1 <= INIT_AXI_TXN;
r_txn_ff2 <= r_txn_ff1;
end
//写地址有效,该信号表示信道正在发出有效的写地址和控制信息。
always@(posedge M_AXI_ACLK)
if(M_AXI_ARESETN == 1'b0 || (M_AXI_AWVALID && M_AXI_AWREADY))
r_m_awvalid <= 'b0;
else if(r_txn_ff2)
r_m_awvalid <= 1'b1;
else
r_m_awvalid <= r_m_awvalid;
//写地址
always@(posedge M_AXI_ACLK)
if(M_AXI_ARESETN == 1'b0 || r_txn_ff2) //与写地址同一拍,符合信号定义
r_m_awaddr <= C_M_TARGET_SLAVE_BASE_ADDR;
else if(M_AXI_AWVALID && M_AXI_AWREADY)
r_m_awaddr <= r_m_awaddr + AWADDR_PLUS;
else
r_m_awaddr <= r_m_awaddr;
/*******************************写数据通道*******************************/
//写数据有效
always@(posedge M_AXI_ACLK)
if(M_AXI_ARESETN == 1'b0 || r_m_wlast)
r_m_wvalid <= 1'b0;
else if(r_txn_ff2)
r_m_wvalid <= 1'b1;
else
r_m_wvalid <= r_m_wvalid;
//写数据
always@(posedge M_AXI_ACLK)
if(M_AXI_ARESETN == 1'b0 || r_txn_ff2)
r_m_wdata <= 'd1;
else if(M_AXI_WVALID && M_AXI_WREADY)
r_m_wdata <= r_m_wdata + 1'b1;
else
r_m_wdata <= r_m_wdata;
//C_M_AXI_BURST_LEN < 2需要另作处理
//最后写入
always@(posedge M_AXI_ACLK)
if(M_AXI_ARESETN == 1'b0)
r_m_wlast <= 'd0;
//倒数第二位,即提前一拍拉高,后一拍直接拉低有效信号
else if(r_m_wdata_cnt == C_M_AXI_BURST_LEN - 2)
r_m_wlast <= 1'b1;
else
r_m_wlast <= 1'b0;
//写入数据计数器
always@(posedge M_AXI_ACLK)
if(M_AXI_ARESETN == 1'b0)
r_m_wdata_cnt <= 'b0;
else if(r_m_wdata_cnt == C_M_AXI_BURST_LEN - 1)
r_m_wdata_cnt <= 1'b0;
else if(M_AXI_WVALID && M_AXI_WREADY)
r_m_wdata_cnt <= r_m_wdata_cnt + 1'b1;
else
r_m_wdata_cnt <= r_m_wdata_cnt;
/*******************************写响应通道*******************************/
//写响应准备
always@(posedge M_AXI_ACLK)
if(M_AXI_ARESETN == 1'b0)
r_m_bready <= 1'b0;
else if(M_AXI_BVALID)
r_m_bready <= 1'b1;
else
r_m_bready <= 1'b0;
//错误
always@(posedge M_AXI_ACLK)
if(M_AXI_ARESETN == 1'b0)
ERROR <= 1'b0;
else if(M_AXI_BVALID && M_AXI_BREADY && (M_AXI_BRESP != 2'b00))
ERROR <= 1'b1;
else
ERROR <= ERROR;
/*******************************读地址通道*******************************/
//同步+延迟
always@(posedge M_AXI_ACLK)
if(M_AXI_ARESETN == 1'b0)begin
r_read_ff1 <= 'b0;
r_read_ff2 <= 'b0;
end
else begin
r_read_ff1 <= w_m_read_en;
r_read_ff2 <= r_read_ff1;
end
//读地址有效
always@(posedge M_AXI_ACLK)
if(M_AXI_ARESETN == 1'b0 || (M_AXI_ARVALID && M_AXI_ARREADY))
r_m_arvalid <= 'b0;
else if(r_read_ff2)
r_m_arvalid <= 1'b1;
else
r_m_arvalid <= r_m_arvalid;
//读地址
always@(posedge M_AXI_ACLK)
if(M_AXI_ARESETN == 1'b0 || r_read_ff2)
r_m_araddr <= C_M_TARGET_SLAVE_BASE_ADDR;
else if(M_AXI_ARVALID && M_AXI_ARREADY)
r_m_araddr <= r_m_araddr + AWADDR_PLUS;
else
r_m_araddr <= r_m_araddr;
/*******************************读数据通道*******************************/
//读数据准备
always@(posedge M_AXI_ACLK)
if(M_AXI_ARESETN == 1'b0 || M_AXI_RLAST)
r_m_rready <= 1'b0;
else if(M_AXI_RVALID)
r_m_rready <= 1'b1;
else
r_m_rready <= r_m_rready;
endmodule
module axi_full_slave #
(
parameter integer C_S_AXI_ID_WIDTH = 1 ,
parameter integer C_S_AXI_DATA_WIDTH = 32 ,
parameter integer C_S_AXI_ADDR_WIDTH = 16 ,
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
)
(
input wire S_AXI_ACLK ,
input wire S_AXI_ARESETN ,
input wire [C_S_AXI_ID_WIDTH-1 : 0] S_AXI_AWID ,
input wire [C_S_AXI_ADDR_WIDTH-1 : 0] S_AXI_AWADDR ,
input wire [7 : 0] S_AXI_AWLEN ,
input wire [2 : 0] S_AXI_AWSIZE ,
input wire [1 : 0] S_AXI_AWBURST ,
input wire S_AXI_AWLOCK ,
input wire [3 : 0] S_AXI_AWCACHE ,
input wire [2 : 0] S_AXI_AWPROT ,
input wire [3 : 0] S_AXI_AWQOS ,
input wire [3 : 0] S_AXI_AWREGION ,
input wire [C_S_AXI_AWUSER_WIDTH-1 : 0] S_AXI_AWUSER ,
input wire S_AXI_AWVALID ,
output wire S_AXI_AWREADY ,
input wire [C_S_AXI_DATA_WIDTH-1 : 0] S_AXI_WDATA ,
input wire [(C_S_AXI_DATA_WIDTH/8)-1 : 0] S_AXI_WSTRB ,
input wire S_AXI_WLAST ,
input wire [C_S_AXI_WUSER_WIDTH-1 : 0] S_AXI_WUSER ,
input wire S_AXI_WVALID ,
output wire S_AXI_WREADY ,
output wire [C_S_AXI_ID_WIDTH-1 : 0] S_AXI_BID ,
output wire [1 : 0] S_AXI_BRESP ,
output wire [C_S_AXI_BUSER_WIDTH-1 : 0] S_AXI_BUSER ,
output wire S_AXI_BVALID ,
input wire S_AXI_BREADY ,
input wire [C_S_AXI_ID_WIDTH-1 : 0] S_AXI_ARID ,
input wire [C_S_AXI_ADDR_WIDTH-1 : 0] S_AXI_ARADDR ,
input wire [7 : 0] S_AXI_ARLEN ,
input wire [2 : 0] S_AXI_ARSIZE ,
input wire [1 : 0] S_AXI_ARBURST ,
input wire S_AXI_ARLOCK ,
input wire [3 : 0] S_AXI_ARCACHE ,
input wire [2 : 0] S_AXI_ARPROT ,
input wire [3 : 0] S_AXI_ARQOS ,
input wire [3 : 0] S_AXI_ARREGION ,
input wire [C_S_AXI_ARUSER_WIDTH-1 : 0] S_AXI_ARUSER ,
input wire S_AXI_ARVALID ,
output wire S_AXI_ARREADY ,
output wire [C_S_AXI_ID_WIDTH-1 : 0] S_AXI_RID ,
output wire [C_S_AXI_DATA_WIDTH-1 : 0] S_AXI_RDATA ,
output wire [1 : 0] S_AXI_RRESP ,
output wire S_AXI_RLAST ,
output wire [C_S_AXI_RUSER_WIDTH-1 : 0] S_AXI_RUSER ,
output wire S_AXI_RVALID ,
input wire S_AXI_RREADY
);
/*******************************function*******************************/
function integer clog2(input integer number);
for(clog2=0;number>0;clog2=clog2+1)
number = number >> 1;
endfunction
/*********************************reg*********************************/
reg r_s_awready ;
reg r_s_wready ;
reg [3:0] data_cnt ;
reg [1:0] r_s_bresp ;
reg r_s_bvalid ;
reg r_s_arready ;
reg [C_S_AXI_DATA_WIDTH-1 : 0] r_s_rdata ;
reg r_s_rlast ;
reg r_s_rvalid ;
reg [C_S_AXI_DATA_WIDTH-1 : 0] mem [0 : C_S_AXI_ADDR_WIDTH-1] ;
/********************************wire********************************/
/******************************parameter******************************/
/*********************************port*********************************/
/*******************************machine*******************************/
/*****************************component*****************************/
integer i;
/*******************************assign*******************************/
assign S_AXI_AWREADY = r_s_awready;
assign S_AXI_WREADY = r_s_wready;
assign S_AXI_BID = 'b0;
assign S_AXI_BRESP = r_s_bresp;
assign S_AXI_BUSER = 'b1;
assign S_AXI_BVALID = r_s_bvalid;
assign S_AXI_ARREADY = r_s_arready;
assign S_AXI_RID = 'b0;
assign S_AXI_RDATA = r_s_rdata;
assign S_AXI_RRESP = 'b0;
assign S_AXI_RLAST = r_s_rlast;
assign S_AXI_RUSER = 'b1;
assign S_AXI_RVALID = r_s_rvalid;
/*******************************always*******************************/
/*****************************写地址通道*****************************/
always@(posedge S_AXI_ACLK)
if(S_AXI_ARESETN == 1'b0 || (S_AXI_AWREADY && S_AXI_AWVALID))
r_s_awready <= 1'b0;
else if(S_AXI_AWVALID == 1'b1)
r_s_awready <= 1'b1;
else
r_s_awready <= 1'b0;
/*****************************写数据通道*****************************/
always@(posedge S_AXI_ACLK)
if(S_AXI_ARESETN == 1'b0 || S_AXI_WLAST)
r_s_wready <= 1'b0;
else if(S_AXI_WVALID == 1'b1)
r_s_wready <= 1'b1;
else
r_s_wready <= r_s_wready;
always@(posedge S_AXI_ACLK)
if(S_AXI_ARESETN == 1'b0)
data_cnt <= 'b0;
else if(data_cnt == 15)
data_cnt <= 'b0;
else if((S_AXI_WREADY && S_AXI_WVALID) || (S_AXI_RVALID && S_AXI_RREADY))
data_cnt <= data_cnt + 1'b1;
else
data_cnt <= data_cnt;
always@(posedge S_AXI_ACLK)
if(S_AXI_ARESETN == 1'b0)
for(i=0;i<=(C_S_AXI_ADDR_WIDTH-1);i=i+1)
mem[i] <= 'b0;
else if(S_AXI_WREADY && S_AXI_WVALID)
mem[data_cnt] <= S_AXI_WDATA;
/*****************************写响应通道*****************************/
always@(posedge S_AXI_ACLK)
if(S_AXI_ARESETN == 1'b0 || (S_AXI_BREADY && S_AXI_BVALID)) begin
r_s_bresp <= 'b0;
r_s_bvalid<= 'b0;
end
else if(S_AXI_WVALID && S_AXI_WREADY && S_AXI_WLAST) begin
r_s_bresp <= 'b0;
r_s_bvalid<= 'b1;
end
else begin
r_s_bresp <= 'b0;
r_s_bvalid<= r_s_bvalid;
end
/*****************************读地址通道*****************************/
always@(posedge S_AXI_ACLK)
if(S_AXI_ARESETN == 1'b0 || (S_AXI_ARREADY && S_AXI_ARVALID))
r_s_arready <= 1'b0;
else if(S_AXI_ARVALID == 1'b1)
r_s_arready <= 1'b1;
else
r_s_arready <= 1'b0;
/*****************************读数据通道*****************************/
always@(posedge S_AXI_ACLK)
if(S_AXI_ARESETN == 1'b0 || S_AXI_RLAST)
r_s_rvalid <= 1'b0;
else if(S_AXI_ARREADY && S_AXI_ARVALID)
r_s_rvalid <= 1'b1;
else
r_s_rvalid <= r_s_rvalid;
always@(posedge S_AXI_ACLK)
if(S_AXI_ARESETN == 1'b0)
r_s_rlast <= 1'b0;
else if(data_cnt == 15)
r_s_rlast <= 1'b1;
else
r_s_rlast <= 1'b0;
always@(posedge S_AXI_ACLK)
if(S_AXI_ARESETN == 1'b0)
r_s_rdata <= 'b0;
else if(S_AXI_RVALID && S_AXI_RREADY)
r_s_rdata <= mem[data_cnt];
else
r_s_rdata <= r_s_rdata;
endmodule
`timescale 1ns/1ns
module tb_top_axi_full();
reg clk;
reg rst_n;
reg txn;
top_axi_full u_top_axi_full
( .clk (clk ),
.rst_n (rst_n ),
.txn (txn )
);
always #10 clk = ~clk;
initial begin
rst_n = 1'b1;
clk = 1'b0;
txn = 1'b0;
#20
rst_n = 1'b0;
#20
rst_n = 1'b1;
#20
txn = 1'b1;
#20
txn = 1'b0;
end
endmodule