目录
Xilinx的绝大多数IP Core的寄存器读写,都是使用的AXI4 Lite协议。另外,Xilinx的Zynq系列器件,PS读写PL的寄存器也是使用的AXI4 Lite协议。以至于,AXI4 Lite的使用十分广泛。
主要的参考手册:
- ARM,IHI0022E-AMBA AXI and ACE Protocol Specification
- Xilinx,UG1037-AXI Reference Guide
1.1. 信号概述
AXI4-Lite协议,分为5个Channel:其中写相关的有3个Channel,读有2个Channel,总计21个信号线。
注意:在Xilinx IP Core的寄存器读写接口中,信号线的数量不定,17根、18根、19根、21根的都有。其中,差异如下:
- 19根信号线,相比于21根信号线,少ARPROT和AWPROT
- 18根信号线,相比于19根信号线,少WSTRB
1.2. 信号详述
1.2.1. 时钟和复位
对于时钟信号,所有的信号在ACLK的上升沿采样。在Xilinx的应用中,AXI-Lite协议ACLK一般都是100MHz。
对于复位信号,协议的描述主要包括三点:
- 复位低有效
- 第二话的意思是,必须满足异步复位同步释放的要求
- 所有的VALID信号在复位阶段都需要拉低
需要注意得是,Xilinx的IP Core对复位信号的要求不同的。如:在AXI DMA中的复位信号至少需要保持16个时钟周期,且同步到ACLK信号的时钟域。
1.2.2. Read address & Read data Channel
Slave端的信号,如下:
//read address channel
.s_axi_lite_arvalid(s_axi_lite_arvalid), // input wire s_axi_lite_arvalid
.s_axi_lite_arready(s_axi_lite_arready), // output wire s_axi_lite_arready
.s_axi_lite_araddr(s_axi_lite_araddr), // input wire [9 : 0] s_axi_lite_araddr
.s_axi_lite_arprot(3'b0 ), // input wire [2 : 0] s_axi_lite_arprot
//read data channel
.s_axi_lite_rvalid(s_axi_lite_rvalid), // output wire s_axi_lite_rvalid
.s_axi_lite_rready(s_axi_lite_rready), // input wire s_axi_lite_rready
.s_axi_lite_rdata(s_axi_lite_rdata), // output wire [31 : 0] s_axi_lite_rdata
.s_axi_lite_rresp(s_axi_lite_rresp), // output wire [1 : 0] s_axi_lite_rresp
注:s_axi_lite_arprot为保护类型,暂时不用,Master直接赋0即可。详见,IHI0022E-Pg73。
(上图中,单箭头-can,双箭头-must)
根据协议中的说明:
- ARVALID有效,可以在ARREADY有效前(对应上图第1句)
- ARREADY有效,可以在ARVALID有效之前,也可以在其有效之后(2,3)
- RVALID有效,必须在ARREADY和ARVALID同时有效之后。此时,写读地址成功,之后Slave才能拉高RVALID,以指示读数据有效(4),也没必须等待BREADY有效(5)
- RREADY有效,可以在RVALID有效之前,也可以在其有效之后(6,7)
上述存在很多情况,一般情况,满足必须得条件即可。存在多种可能的时序,满足其中一种即可。下面是Master端时序为例进行说明:
Step.1 Master拉高ARVALID信号,同时给ARADDR信号赋值;
Step.2 等到Slave的响应,当Slave将ARREADY拉高,即ARVALID和ARREADY信号同时有效时,读地址ARADDR写入;
Step.3 下一个上升沿时,将ARVALID拉低(写地址完成),同时ARADDR赋值为0(也可以不赋0,直到下一次写时再变化也可以),并将RREADY拉高(非此时刻必须);
Step.4 等待RVALID到来,此时,RREADY和RVALID同时有效,获取有效的RDATA和RRESP;
Step.5 判断RRESP是否为0(0代表OK),如果OK,就拉低RREADY拉低,读操作完成;
仿真实例:
以下代码,是我直接从Xilinx JESD MAC的官方例程tb中截取出来的。与我上述描述的步骤完全一致,可以借用到在自己的仿真工程中,如下:
// AXI-Lite Read task
task axi_read;
//input [29:0] offset;
output [31:0] data;
input [31:0] addr;
reg [1:0] resp;
begin
// shift offset to account for AXI byte addressing
//addr = {offset, 2'b00};
// Drive Address valid
@(posedge s_axi_aclk);
#1000;
s_axi_araddr = addr;
s_axi_arvalid = 1;
s_axi_rready = 0;
// Address Response Phase
@(negedge s_axi_aclk);
while (s_axi_arready == 1'b0)
@(negedge s_axi_aclk);
@(posedge s_axi_aclk);
#1000;
s_axi_araddr = 0;
s_axi_arvalid = 0;
s_axi_rready = 1;
// Read Data Phase
@(negedge s_axi_aclk);
while (s_axi_rvalid == 1'b0)
@(negedge s_axi_aclk);
@(posedge s_axi_aclk);
data = s_axi_rdata;
resp = s_axi_rresp;
if (resp != 0) $display ("Error AXI RRESP not equal 0");
#1000;
s_axi_rready = 0;
end
endtask // axi_read
1.2.3. Write address /Write data /Write response Channel
//write address channel
.s_axi_lite_awvalid(s_axi_lite_awvalid), // input wire s_axi_lite_awvalid
.s_axi_lite_awready(s_axi_lite_awready), // output wire s_axi_lite_awready
.s_axi_lite_awaddr(s_axi_lite_awaddr), // input wire [9 : 0] s_axi_lite_awaddr
.s_axi_lite_awprot(3'b0 ), // input wire [2 : 0] s_axi_lite_awprot
//write data channel
.s_axi_lite_wvalid(s_axi_lite_wvalid), // input wire s_axi_lite_wvalid
.s_axi_lite_wready(s_axi_lite_wready), // output wire s_axi_lite_wready
.s_axi_lite_wdata(s_axi_lite_wdata), // input wire [31 : 0] s_axi_lite_wdata
.s_axi_lite_wstrb(4'hF ), // input wire [3 : 0] s_axi_lite_wstrb
//write response channel
.s_axi_lite_bresp(s_axi_lite_bresp), // output wire [1 : 0] s_axi_lite_bresp
.s_axi_lite_bvalid(s_axi_lite_bvalid), // output wire s_axi_lite_bvalid
.s_axi_lite_bready(s_axi_lite_bready), // input wire s_axi_lite_bready
注:s_axi_lite_arprot为保护类型,暂时不用,Master直接赋0即可。详见,IHI0022E-Pg73。
注:s_axi_lite_wstrb为选通脉冲,类似于tkeep信号,每一位指示1Byte,读取32bit数据时,Master直接赋值4’hF。详见,IHI0022E-Pg52。
根据协议中的说明:
- AWREADY信号有效,在AWVALID or WVALID有效之前,之后都可以(对应上图1,2句)
- WREADY信号有效,在AWVALID or WVALID有效之前,之后都可以(3,4)
- BVALID有效,必须是在AWVALID 和 WVALID均有效之后(5),也没必须等待BREADY有效(6)
- BREADY有效,可以在BVALID有效之前,之后也可以(7,8)
注意:上述写写地址通道和写数据通道,可以理解为相互独立的。可以同时进行写操作。当两者同时生效之后,SLAVE才可以反馈是否写成功。
上述存在很多情况,一般情况,满足必须得条件即可。存在多种可能的时序,满足其中一种即可。下面是Master端时序为例进行说明:
Step.1 Master拉高AWVALID和WVALID信号,同时给AWADDR和ADATA信号赋值;(上述理解写写地址和写写数据时独立的,那么就可以同时进行)
Step.2 等到Slave的响应,当Slave将AWREADY和WREADY拉高,即AWVALID和AWREADY信号同时有效时,和WVALID和WREADY信号同时有效时,写地址AWADDR和WDATA写入;
Step.3 下一个上升沿时,将AWVALID和WREADY拉低(写地址和写数据完成),同时AWADDR和WDATA赋值为0(也可以不赋0,直到下一次写时再变化也可以),写入完成
Step.4 等待BVALID到来,获取有效的BRESP;
Step.5 判断BRESP是否为0(0代表OK),如果OK,就使BREADY有效一个时钟周期,读操作完成;
注:上述,读写操作中的响应RREADY和BREADY的拉高是有区别的,这里就可以体现响应READY信号可以在VALID信号的之前,也可以之后。
仿真实例:
以下代码,是我直接从Xilinx JESD MAC的官方例程tb中截取出来的。与我上述描述的步骤完全一致,可以借用到在自己的仿真工程中,如下:
// AXI-Lite Write task
task axi_write;
//input [29:0] offset;
input [31:0] data;
input [31:0] addr;
reg [1:0] resp;
begin
// shift offset to account for AXI byte addressing
//addr = {offset, 2'b00};
// Drive Address & Data valid
@(posedge s_axi_aclk);
#1000;
s_axi_awaddr = addr;
s_axi_awvalid = 1;
s_axi_wdata = data;
s_axi_wvalid = 1;
s_axi_bready = 0;
// Address Response Phase
@(negedge s_axi_aclk);
while (s_axi_awready == 1'b0)
@(negedge s_axi_aclk);
@(posedge s_axi_aclk);
#1000;
s_axi_awaddr = 0;
s_axi_awvalid = 0;
// Data Response Phase
@(negedge s_axi_aclk);
while (s_axi_wready == 1'b0)
@(negedge s_axi_aclk);
@(posedge s_axi_aclk);
#1000;
s_axi_wdata = 0;
s_axi_wvalid = 0;
// BRESP phase
@(negedge s_axi_aclk);
while (s_axi_bvalid == 1'b0)
@(negedge s_axi_aclk);
@(posedge s_axi_aclk);
resp = s_axi_bresp;
if (resp != 0) $display ("Error AXI BRESP not equal 0");
#1000;
s_axi_bready = 1;
@(posedge s_axi_aclk);
#1000;
s_axi_bready = 0;
end
endtask // axi_write
上述代码虽然是从Xilinx官方JESD MAC示例中截取出来的,实际上,这段代码是有问题的。我在进行JESD仿真时,是没有问题的。但在仿真AXI DMA实例时,就出现了问题。
在写AXI DMA的AXI4 Lite接口时出现了问题,仿真一直卡在了while (s_axi_wready == 1'b0)
这里。原因如下:
JESD MAC IP Core写数据时响应的时序如下:
而AXI DMA写数据时,时序如下:
可以看出:在AXI DMA这个IP CORE中,AWREADY和WREADY信号是同时来的,而JESD MAC的这两个信号不是同时来的。
而上述代码的TASK中判断这两个信号是有先后顺序的,就不支持两个信号同时来的情况。
对代码进行如下修改,让两个条件支持同时判断。
注:看来代码还是不能直接随便借用。233333
// AXI-Lite Write task
task axi_write;
//input [29:0] offset;
input [31:0] data;
input [31:0] addr;
reg [1:0] resp;
begin
// shift offset to account for AXI byte addressing
//addr = {offset, 2'b00};
// Drive Address & Data valid
@(posedge s_axi_aclk);
#1000;
s_axi_awaddr = addr;
s_axi_awvalid = 1;
s_axi_wdata = data;
s_axi_wvalid = 1;
s_axi_bready = 0;
fork
begin
// Address Response Phase
@(negedge s_axi_aclk);
while (s_axi_awready == 1'b0)
@(negedge s_axi_aclk);
@(posedge s_axi_aclk);
#1000;
s_axi_awaddr = 0;
s_axi_awvalid = 0;
end
begin
// Data Response Phase
@(negedge s_axi_aclk);
while (s_axi_wready == 1'b0)
@(negedge s_axi_aclk);
@(posedge s_axi_aclk);
#1000;
s_axi_wdata = 0;
s_axi_wvalid = 0;
end
join
// BRESP phase
@(negedge s_axi_aclk);
while (s_axi_bvalid == 1'b0)
@(negedge s_axi_aclk);
@(posedge s_axi_aclk);
resp = s_axi_bresp;
if (resp != 0) $display ("Error AXI BRESP not equal 0");
#1000;
s_axi_bready = 1;
@(posedge s_axi_aclk);
#1000;
s_axi_bready = 0;
end
endtask // axi_write