一、AXI介绍
AXI全称Advanced eXtensible Interface,属于AMBA总线中的一种,由ARM公司制定。目前主流的包括AXI3和AXI4,其中AXI4又包括AXI4_Lite、AXI4_Full以及AXI4_Stream。本文是基于Xilinx AXI4 IP实现AXI4_FULL Master控制接口。
AXI协议是基于突发传输的,意味着只需要告诉首地址以及突发大小等信息即可实现数据传输。AXI_Full包括五个独立的通道:
1)读地址通道;
2)读数据通道;
3)写地址通道;
4)写数据通道;
5)写响应通道。
具体的通道信号可以参考ARM官网文档,此处不列举,重点在Verilog实现,有需要补充预备知识的提前看看。
二、Verilog实现
最终设计支持Burst_len为1、2、4、8、16、32、64、128、256的传输,在地址0处写入1-16,产生Wlast,然后又在地址0处读出1-16,接收Rlast。重点在控制每个通道的Valid和Ready信号,产生写地址AWADDR、写数据WDATA、读地址ARADDR。
1)接口信号
参考Xilinx的IP,端口信号基本不变,全部复制即可,这里就不放了。
2)时序逻辑
这部分代码主要就是控制寄存器类型的Valid和Ready信号的产生,以及Waddr、Raddr和Wdata、Wlast的产生。这里需要重点关注WLAST的产生,为了支持Burst_len长度从1~256,这里对Burst_len进行判断,最终真正的WLAST信号由组合逻辑部分选择产生。
always@(posedge M_AXI_ACLK)
if(!M_AXI_ARESETN || (M_AXI_AWVALID && M_AXI_AWREADY))
r_m_axi_awvalid <= 'd0;
else if(r_m_axi_wstart)
r_m_axi_awvalid <= 'd1;
else
r_m_axi_awvalid <= r_m_axi_awvalid;
always@(posedge M_AXI_ACLK)
if(!M_AXI_ARESETN)
r_m_axi_awaddr <= 'd0;
else if(r_m_axi_wstart)
r_m_axi_awaddr <= 'd0;
else
r_m_axi_awaddr <= 'd0;
always@(posedge M_AXI_ACLK)
if(!M_AXI_ARESETN || M_AXI_WLAST)
r_m_axi_wvalid <= 'd0;
else if(M_AXI_AWVALID && M_AXI_AWREADY)
r_m_axi_wvalid <= 'd1;
else
r_m_axi_wvalid <= r_m_axi_wvalid;
always@(posedge M_AXI_ACLK)
if(!M_AXI_ARESETN || M_AXI_WLAST)
r_m_axi_wdata <= 'd1;
else if(M_AXI_WVALID && M_AXI_WREADY)
r_m_axi_wdata <= r_m_axi_wdata + 1;
else
r_m_axi_wdata <= r_m_axi_wdata;
always@(posedge M_AXI_ACLK)
if(!M_AXI_ARESETN)
r_m_axi_wlast <= 'd0;
else if(C_M_AXI_BURST_LEN == 1)
r_m_axi_wlast <= 'd0;
else if(C_M_AXI_BURST_LEN == 2 && (M_AXI_WVALID && M_AXI_WREADY && !r_m_axi_wlast))
r_m_axi_wlast <= M_AXI_WVALID && M_AXI_WREADY;
else if(C_M_AXI_BURST_LEN > 2 && r_burst_cnt == C_M_AXI_BURST_LEN-2)
r_m_axi_wlast <= 'd1;
else
r_m_axi_wlast <= 'd0;
always@(posedge M_AXI_ACLK)
if(!M_AXI_ARESETN || M_AXI_WLAST)
r_burst_cnt <= 'd0;
else if(M_AXI_WVALID && M_AXI_WREADY)
r_burst_cnt <= r_burst_cnt + 1;
else
r_m_axi_wlast <= r_burst_cnt;
always@(posedge M_AXI_ACLK)
if(!M_AXI_ARESETN || (M_AXI_ARVALID && M_AXI_ARREADY))
r_m_axi_arvalid <= 'd0;
else if(r_m_axi_rstart)
r_m_axi_arvalid <= 'd1;
else
r_m_axi_arvalid <= 'd0;
always@(posedge M_AXI_ACLK)
if(!M_AXI_ARESETN)
r_m_axi_araddr <= 'd0;
else if(r_m_axi_rstart)
r_m_axi_araddr <= 'd0;
else
r_m_axi_araddr <= 'd0;
always@(posedge M_AXI_ACLK)
if(!M_AXI_ARESETN || M_AXI_RLAST)
r_m_axi_rready <= 'd0;
else if(M_AXI_ARVALID && M_AXI_ARREADY)
r_m_axi_rready <= 'd1;
else
r_m_axi_rready <= r_m_axi_rready;
always@(posedge M_AXI_ACLK)
if(!M_AXI_ARESETN)
r_m_axi_rdata <= 'd0;
else if(M_AXI_RVALID && M_AXI_RREADY)
r_m_axi_rdata <= M_AXI_RDATA;
else
r_m_axi_rdata <= r_m_axi_rdata;
3)组合逻辑
这部分就是第一部分接口信号的输出逻辑。这里的clog2b为自己写的求数据位宽的函数。注意真正写入的地址是要加上一个基地址的。
assign M_AXI_AWID = 'd0 ;
assign M_AXI_AWLEN = C_M_AXI_BURST_LEN ;
assign M_AXI_AWSIZE = clog2b(C_M_AXI_DATA_WIDTH/8 - 1) ;
assign M_AXI_AWBURST = 2'b01 ;
assign M_AXI_AWLOCK = 'd0 ;
assign M_AXI_AWCACHE = 4'b0010 ;
assign M_AXI_AWPROT = 'd0 ;
assign M_AXI_AWQOS = 'd0 ;
assign M_AXI_AWUSER = 'd0 ;
assign M_AXI_AWADDR = r_m_axi_awaddr + C_M_TARGET_SLAVE_BASE_ADDR ;
assign M_AXI_AWVALID = r_m_axi_awvalid ;
assign M_AXI_WSTRB = {C_M_AXI_DATA_WIDTH/8{1'b1}} ;
assign M_AXI_WUSER = 'd0 ;
assign M_AXI_WDATA = r_m_axi_wdata ;
assign M_AXI_WLAST = (C_M_AXI_BURST_LEN == 1) ? w_write_last : r_m_axi_wlast ;
assign M_AXI_WVALID = r_m_axi_wvalid ;
assign M_AXI_BREADY = 1'b1 ;
assign M_AXI_ARID = 'd0 ;
assign M_AXI_ARADDR = r_m_axi_araddr + C_M_TARGET_SLAVE_BASE_ADDR ;
assign M_AXI_ARLEN = C_M_AXI_BURST_LEN ;
assign M_AXI_ARSIZE = clog2b(C_M_AXI_DATA_WIDTH/8 - 1) ;
assign M_AXI_ARBURST = 2'b01 ;
assign M_AXI_ARLOCK = 'd0 ;
assign M_AXI_ARCACHE = 4'b0010 ;
assign M_AXI_ARPROT = 'd0 ;
assign M_AXI_ARQOS = 'd0 ;
assign M_AXI_ARUSER = 'd0 ;
assign M_AXI_ARVALID = r_m_axi_arvalid ;
assign M_AXI_RREADY = r_m_axi_rready ;
assign w_write_last = M_AXI_WVALID && M_AXI_WREADY ;
4)状态机
这里对写逻辑和读逻辑采用了两个FSM进行控制,产生写开始Wstart和读开始Rstart信号。写读共有7个状态。
/************************状态机*************************/
always@(posedge M_AXI_ACLK)
if(!M_AXI_ARESETN)
r_m_write_current <= P_ST_IDLE;
else
r_m_write_current <= r_m_write_next;
always@(*)
case(r_m_write_current)
P_ST_IDLE : r_m_write_next = P_ST_WRITE_START;
P_ST_WRITE_START: r_m_write_next = r_m_axi_wstart ? P_ST_WRITE_TRANS : P_ST_WRITE_START;
P_ST_WRITE_TRANS: r_m_write_next = M_AXI_WLAST ? P_ST_WRITE_END : P_ST_WRITE_TRANS;
P_ST_WRITE_END : r_m_write_next = (r_m_read_current == P_ST_READ_END) ? P_ST_IDLE : P_ST_WRITE_END;
default : r_m_write_next = P_ST_IDLE;
endcase
always@(posedge M_AXI_ACLK)
if(!M_AXI_ARESETN)
r_m_axi_wstart <= 'd0;
else if(r_m_write_current == P_ST_WRITE_START)
r_m_axi_wstart <= 'd1;
else
r_m_axi_wstart <= 'd0;
/************************状态机*************************/
always@(posedge M_AXI_ACLK)
if(!M_AXI_ARESETN)
r_m_read_current <= P_ST_IDLE;
else
r_m_read_current <= r_m_read_next;
always@(*)
case(r_m_read_current)
P_ST_IDLE : r_m_read_next = (r_m_write_current == P_ST_WRITE_END) ? P_ST_READ_START : P_ST_IDLE;
P_ST_READ_START: r_m_read_next = r_m_axi_rstart ? P_ST_READ_TRANS : P_ST_READ_START;
P_ST_READ_TRANS: r_m_read_next = M_AXI_RLAST ? P_ST_READ_END : P_ST_READ_TRANS;
P_ST_READ_END : r_m_read_next = P_ST_IDLE;
default : r_m_read_next = P_ST_IDLE;
endcase
always@(posedge M_AXI_ACLK)
if(!M_AXI_ARESETN)
r_m_axi_rstart <= 'd0;
else if(r_m_read_current == P_ST_READ_START)
r_m_axi_rstart <= 'd1;
else
r_m_axi_rstart <= 'd0;
三、仿真验证
验证采用在TOP模块里例化一个Xilinx 的AXI4_Full IP以及咱们自己写的Master接口,将咱们的信号连接到AXI4_Full IP 里的Slave口,大概如下图。
然后对TOP编写tb仿真,tb里主要产生clk,rst即可,过于简单,这里就不放了。直接看波形。文章来源:https://www.toymoban.com/news/detail-805002.html
先看与写相关的通道:可以看到在设置Burst_len为16时,满足握手要求,当AWVALID和AWREADY握手,成功写入1~16,并在最后一个数据处产生WLAST信号,成功后产生BVALID,且BRESP为Okay。
再看与读相关的通道:当ARVALID和ARREADY握手,且RVALID和RREADY握手时,成功读出1~16,并在最后一个数据处产生RLAST信号。但这里发现1)RVALID和RREADY握手时总是会产生间断,这是由于Xilinx 的IP自身问题产生的,因为RVALID对于咱们的接口来说是输入。2)最后读出的数据多了一个,这应该也是由于Xilinx 的IP自身问题产生的。总之咱们的Master接口实现是没有问题的。
最后验证对于其他的Burst_len都是成功的。文章来源地址https://www.toymoban.com/news/detail-805002.html
到了这里,关于手撕AXI-Full总线接口,实现AXI_Full Master接口的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!