目录
AXI4系列总线简介
AXI4-lite总线
通道信号时序要求
读写的时序图
写时序的可综合程序
读时序的可综合程序
JESD的AXI配置调试心得
Advanced eXtensible Interface (AXI)是ARM的Advanced Micro controller Bus Architecture(AMBA)总线的一部分,第一个版本的AXI在2003年的AMBA3.0中被提出,在2010年的AMBA4.0中提出了第二个版本AXI4。
AXI4包含三个类型:
AXI4:主要面向高性能地址映射通信的需求;
AXI4-Lite(axi):是AXI的简化版本,例如可以用来进行控制与状态寄存器的配置与读取。
AXI4-stream(axis):是ARM公司和Xilinx公司一起提出,主要用在FPGA进行以数据为主导的大量数据的传输应用。在vivado的IPcatalog里可以看到很多IP核的数据传输是基于AXI4或AXI4-stream。
参考文档:
ug1037-vivado-axi-reference-guide.pdf:介绍AXI对xilinx的IP核以及Device的支持特征。
IHI0022D_amba_axi_protocol_spec.pdf: AXI4、AXI4-lite的协议内容。(在http://www.amba.com可以下载到)
IHI0051A_amba4_axi4_stream_v1_0_protocol_spec.pdf:AXI4-stream的协议内容。(在http://www.amba.com可以下载到)
其包含了5个独立的传输通道:读地址通道、读数据通道、写地址通道、写数据通道、写响应通道。如Table B1-1。
注:我在使用vivado 的JESD204B的jesd core时配置IP核的参数需要使用AXI4-lite的配置接口(Table B1-1中的awport,arport没有用到),但是其example design只给出仿真时序,因此我又写出来了可综合的Verilog设计,主要思想是状态机控制。
AXI是基于VALID/READY的握手机制数据传输协议,传输源端使用VALID表明地址/控制信号、数据是有效的,目的端使用READY表明自己已经接受信息。
通道握手信号:每个通道有自己的xVALID/xREADY握手信号对。
写地址通道:当主机驱动有效awaddr与awvalid时,保持有效状态,直到主机在时钟上升沿采样到从机的awready有效后,可以将awaddr与awvalid无效。
写数据通道:当主机驱动有效wdata与wvalid时,保持有效状态,直到主机在时钟上升沿采样到从机的wready有效后,可以将wdata与wvalid无效。
写响应通道:在写地址与写数据均传输后且生效后,从机有效写响应信号bresp与bvalid,直到时钟上升沿采样到主机的bready信号。
读地址通道:当主机驱动有效araddr与arvalid时,保持有效状态,直到主机在时钟上升沿采样到从机的arready有效后,可以将araddr与arvalid无效。
读数据通道:主机传输完地址后就将rready有效表示准备好接收读数据,从机接收到地址后传输所读地址的数据,从机驱动有效rdata与rvalid、rresp,从机在时钟上升沿采样到主机的rready有效后,可以将rdata与rvalid、rresp无效,同时主机采样到有效的rdata与rvalid、rresp后下一个时钟也会使rready无效。
综上可以用状态机来控制读写的过程。
module axi_write(
input s_axi_aclk,
input s_axi_aresetn,
input s_axi_awready,
input s_axi_wready,
input s_axi_bvalid,
input [1:0] s_axi_bresp,
output reg [31:0] s_axi_awaddr,
output reg s_axi_awvalid,
output reg [31:0] s_axi_wdata,
output reg s_axi_wvalid,
output reg s_axi_bready
);
localparam pLanes = 2;
// F = 1 K = 20
localparam pF = 1;///
localparam pK = 20;///
// Setup the link configuration parameters.
localparam [7:0] pDID = 8'h55;//不变
localparam [3:0] pADJCNT = 4'h0;//不变
localparam [3:0] pBID = 4'hA;//不变
localparam pADJDIR = 1'b0;//不变
localparam pPHADJ = 1'b0;//不变
localparam pSCR = 1'b0;/扰码
localparam [4:0] pL = (pLanes-1);///
localparam [7:0] pM = 3;///
localparam [1:0] pCS = 2'd0;
localparam [4:0] pN = 5'd15;//
localparam [4:0] pNt = 5'd15;/
localparam [2:0] pSUBCV = 3'b001;//不变
localparam [2:0] pJESDV = 3'b001;//不变
localparam [4:0] pS = 5'd0;/
localparam pHD = 1'b0;/
localparam [4:0] pCF = 5'd0;
localparam [7:0] pRES1 = 8'h5A;//不变
localparam [7:0] pRES2 = 8'hA5;//不变
localparam [4:0] IDLEW= 5'b00001;
localparam [4:0] DRIVEW= 5'b00010;
localparam [4:0] ADD_RES= 5'b00100;
localparam [4:0] DAT_RES= 5'b01000;
localparam [4:0] BRES= 5'b10000;
localparam WRITE_NUM = 14;///配置
reg [4:0] curr_ws,next_ws;
reg write_over;
reg [31:0] wadd;
reg [9:0] w_cnt;/对写入的数据个数进行计数,配置完成将write_over置高
reg [31:0] wdata;
reg [1:0] resp;
always@(posedge s_axi_aclk or negedge s_axi_aresetn)
if(!s_axi_aresetn) curr_ws <= IDLEW;
else curr_ws <= next_ws;
always@(*)
begin
next_ws = 'dx;
case(curr_ws)
IDLEW: if(write_over==0) next_ws = DRIVEW; else next_ws = IDLEW;
DRIVEW: if(s_axi_awready==1) next_ws = ADD_RES; else next_ws = DRIVEW;
ADD_RES: if(s_axi_wready==1) next_ws = DAT_RES; else next_ws = ADD_RES;
DAT_RES: if(s_axi_bvalid==1) next_ws = BRES; else next_ws = DAT_RES;
BRES: if(write_over==1) next_ws = IDLEW; else next_ws = DRIVEW;
default: next_ws = IDLEW;
endcase
end
always@(posedge s_axi_aclk or negedge s_axi_aresetn)
if(!s_axi_aresetn)
begin
s_axi_awaddr <= 0; s_axi_awvalid <= 0;
s_axi_wdata <= 0; s_axi_wvalid <= 0;
s_axi_bready <= 0; w_cnt <= 0; resp <= 0;
end
else
case(curr_ws)
IDLEW:
begin
s_axi_awaddr <= 0; s_axi_awvalid <= 0;
s_axi_wdata <= 0; s_axi_wvalid <= 0;
s_axi_bready <= 0; w_cnt <= w_cnt; resp <= 0;
end
DRIVEW:
begin
if(s_axi_awready==1)
begin s_axi_awaddr <= 0; s_axi_awvalid <= 0; end
else
begin s_axi_awaddr <= wadd; s_axi_awvalid <= 1; end
s_axi_wdata <= wdata; s_axi_wvalid <= 1;
s_axi_bready <= 0; w_cnt <= w_cnt; resp <= 0;
end
ADD_RES:
begin
s_axi_awaddr <= 0; s_axi_awvalid <= 0;
if(s_axi_wready==1)
begin s_axi_wdata <= 0; s_axi_wvalid <= 0; w_cnt <= w_cnt+1;end //else keep//每传输一个数据加1
s_axi_bready <= 0; resp <= 0;
end
DAT_RES:
begin
s_axi_awaddr <= 0; s_axi_awvalid <= 0;
s_axi_wdata <= 0; s_axi_wvalid <= 0;
if(s_axi_bvalid==1)
begin s_axi_bready <= 1; resp <= s_axi_bresp;end //else keep
w_cnt <= w_cnt;
end
BRES:
begin
s_axi_awaddr <= 0; s_axi_awvalid <= 0;
s_axi_wdata <= 0; s_axi_wvalid <= 0; w_cnt <= w_cnt;
if(s_axi_bready)
begin s_axi_bready <= 0; resp <= 0; end//只持续一个高电平
else
begin s_axi_bready <= 1; resp <= s_axi_bresp;end
end
default:
begin
s_axi_awaddr <= 0; s_axi_awvalid <= 0;
s_axi_wdata <= 0; s_axi_wvalid <= 0;
s_axi_bready <= 0; w_cnt <= 0;resp <= 0;
end
endcase
always@(posedge s_axi_aclk or negedge s_axi_aresetn)
if(!s_axi_aresetn) write_over <= 0;
else if( w_cnt == WRITE_NUM) write_over <= 1;//else keep
always@(posedge s_axi_aclk or negedge s_axi_aresetn)
if(!s_axi_aresetn)
begin
wadd <= 0; wdata <= 0;
end
else
case( w_cnt )
0:begin wadd <=32'h008 ;wdata <= 32'h00000001 ; end
1:begin wadd <=32'h00C ;wdata <= {31'b0,pSCR} ; end
2:begin wadd <=32'h010 ;wdata <= {31'b0,1'b1} ; end
3:begin wadd <=32'h014 ;wdata <= 32'h00000003 ; end
4:begin wadd <=32'h018 ;wdata <= 32'h00000000 ; end
5:begin wadd <=32'h020 ;wdata <= {24'b0,8'b0} ; end
6:begin wadd <=32'h024 ;wdata <= {27'b0,5'd19} ; end
7:begin wadd <=32'h028 ;wdata <= {24'b0,8'hff} ; end
8:begin wadd <=32'h02C ;wdata <= 32'h00000001 ; end
9:begin wadd <=0 ;wdata <= 0 ; end //begin wadd <=32'h80C ;wdata <= {3'b0, pL, 12'b0, pBID, pDID} ; end
10:begin wadd <=32'h810 ;wdata <= {6'b0, pCS, 3'b0, pNt, 3'b0, pN, pM} ; end
11:begin wadd <=32'h814 ;wdata <= {3'b0, pCF, 7'b0, pHD, 3'b0, pS, 7'b0, pSCR} ; end
12:begin wadd <=0 ;wdata <= 0 ; end //begin wadd <=32'h818 ;wdata <= {16'b0, pRES2, pRES1} ; end
13:begin wadd <=32'h004 ;wdata <= 32'h00000001 ; end
default:begin wadd <=32'h008 ;wdata <= 32'h00000001 ; end
endcase
endmodule
//test bench
module axi_sim;
parameter CLK_PERIOD = 10; //仿真周期ns
parameter RST_CYCLE = 20; //复位周期数
parameter RST_TIME = RST_CYCLE * CLK_PERIOD;
reg sim_clk;
reg sim_rstn;
initial
begin
sim_clk = 0;
sim_rstn = 0;
#RST_TIME sim_rstn = 1;
end
always #(CLK_PERIOD/2) sim_clk = ~sim_clk;
reg s_axi_awready;
reg s_axi_wready;
reg s_axi_bvalid;
reg [1:0] s_axi_bresp;
wire [31:0] s_axi_awaddr;
wire s_axi_awvalid;
wire [31:0] s_axi_wdata;
wire s_axi_wvalid;
wire s_axi_bready;
task virtual_rx;
begin
while(!s_axi_awvalid)
@(posedge sim_clk);
#1 s_axi_awready = 1;
@(posedge sim_clk);#1 s_axi_awready = 0;
while(s_axi_awvalid)
@(posedge sim_clk);
@(posedge sim_clk);#1 s_axi_wready = 1;
@(posedge sim_clk);#1 s_axi_wready = 0;s_axi_bvalid = 1;s_axi_bresp = 2'b11;
@(posedge sim_clk);
@(posedge sim_clk); #1 s_axi_bvalid = 0;s_axi_bresp = 0;//2个周期
end
endtask
//always@(posedge sim_clk)
integer i;
initial
begin
s_axi_awready=0;
s_axi_wready =0;
s_axi_bvalid =0;
s_axi_bresp =0;
#RST_TIME
for(i=0;i<14;i=i+1)
begin
virtual_rx; //task
end
end
/***********************************************************/
axi_write axi_write_u (
.s_axi_aclk(sim_clk ),
.s_axi_aresetn(sim_rstn ),
.s_axi_awready(s_axi_awready ),
.s_axi_wready(s_axi_wready ),
.s_axi_bvalid(s_axi_bvalid ),
.s_axi_bresp(s_axi_bresp ),
.s_axi_awaddr(s_axi_awaddr),
.s_axi_awvalid(s_axi_awvalid),
.s_axi_wdata(s_axi_wdata),
.s_axi_wvalid(s_axi_wvalid),
.s_axi_bready(s_axi_bready)
);
endmodule
module axi_read(
input s_axi_aclk,
input s_axi_aresetn,
input s_axi_arready,
input s_axi_rvalid,
input [31:0] s_axi_rdata,
input [1:0]s_axi_rresp,
output reg [31:0] s_axi_araddr,
output reg s_axi_arvalid,
output reg s_axi_rready
);
localparam [2:0] IDLER= 3'b001;
localparam [2:0] DRIVER= 3'b010;
localparam [2:0] RDATA= 3'b100;
localparam READ_NUM= 14;
reg [4:0] curr_rs,next_rs;
reg read_over;
reg [31:0] radd;
reg [9:0] r_cnt;
reg [31:0] rdata;
reg [1:0] rresp;
always@(posedge s_axi_aclk or negedge s_axi_aresetn)
if(!s_axi_aresetn) curr_rs <= IDLER;
else curr_rs <= next_rs;
always@(*)
begin
next_rs = 'dx;
case(curr_rs)
IDLER: if(read_over==0 && s_axi_arready==1) next_rs = DRIVER; else next_rs = IDLER;
DRIVER: if(s_axi_arvalid==1) next_rs = RDATA; else next_rs = DRIVER;
RDATA: if(s_axi_rvalid==1) next_rs = IDLER; else next_rs = RDATA;
default: next_rs = IDLER;
endcase
end
always@(posedge s_axi_aclk or negedge s_axi_aresetn)
if(!s_axi_aresetn)
begin
s_axi_araddr <= 0; s_axi_arvalid <= 0;
s_axi_rready <= 0;
rdata <= 0; rresp <= 0; r_cnt <= 0;
end
else
case(curr_rs)
IDLER:
begin
rdata <= 0;rresp <= 0;
if(read_over==0 && s_axi_arready==1)
begin
s_axi_araddr <= radd; s_axi_arvalid <= 1;
s_axi_rready <= 0; r_cnt <= r_cnt+1; //控制个数
end
else
begin
s_axi_araddr <= 0; s_axi_arvalid <= 0;
s_axi_rready <= 0; r_cnt <= r_cnt;
end
end
DRIVER:
begin
rdata <= 0;rresp <= 0;r_cnt <= r_cnt;
if(s_axi_arvalid==1)
begin
s_axi_araddr <= 0; s_axi_arvalid <= 0;
s_axi_rready <= 1;
end//else keep
end
RDATA:
begin
s_axi_araddr <= 0; s_axi_arvalid <= 0; r_cnt <= r_cnt;
if(s_axi_rvalid==1)
begin
s_axi_rready <= 0;
rdata <= s_axi_rdata; rresp <= s_axi_rresp;
end //else keep
end
default:
begin
s_axi_araddr <= 0; s_axi_arvalid <= 0;
s_axi_rready <= 0;
rdata <= 0;rresp <= 0; r_cnt <= r_cnt;
end
endcase
always@(posedge s_axi_aclk or negedge s_axi_aresetn)
if(!s_axi_aresetn)
read_over <= 0;
else if( r_cnt == READ_NUM)
read_over <= 1;//else keep
always@(posedge s_axi_aclk or negedge s_axi_aresetn)
if(!s_axi_aresetn)
radd <= 0;
else
case( r_cnt )
0: radd <=32'h008 ;
1: radd <=32'h00C ;
2: radd <=32'h010 ;
3: radd <=32'h014 ;
4: radd <=32'h018 ;
5: radd <=32'h020 ;
6: radd <=32'h024 ;
7: radd <=32'h028 ;
8: radd <=32'h02C ;
9: radd <=32'h80C ;
10: radd <=32'h810 ;
11: radd <=32'h814 ;
12: radd <=32'h818 ;
13: radd <=32'h004 ;
default: radd <=32'h004;
endcase
endmodule
/
//test bench
module axi_read_sim;
parameter CLK_PERIOD = 10; //仿真周期ns
parameter RST_CYCLE = 20; //复位周期数
parameter RST_TIME = RST_CYCLE * CLK_PERIOD;
reg sim_clk;
reg sim_rstn;
initial
begin
sim_clk = 0;
sim_rstn = 0;
#RST_TIME sim_rstn = 1;
end
always #(CLK_PERIOD/2) sim_clk = ~sim_clk;
/*************************************************************/
wire [31:0] s_axi_araddr;
wire s_axi_arvalid;
wire s_axi_rready;
reg s_axi_arready;
reg s_axi_rvalid;
reg [31:0] s_axi_rdata;
reg [1:0] s_axi_rresp;
task virtual_rx;
input [31:0] rdata;
input [1:0] resp;
begin
@(posedge sim_clk);#1 s_axi_arready = 1;
while(!s_axi_arvalid)
@(posedge sim_clk);//接收到有效的地址后,拉低
//@(posedge sim_clk);
#1 s_axi_arready = 0;
#(CLK_PERIOD*4)
#1 s_axi_rvalid = 1;
s_axi_rdata = rdata;
s_axi_rresp = resp;
while(!s_axi_rready)
@(posedge sim_clk); //对方收到后拉低
@(posedge sim_clk);
#1 s_axi_rvalid = 0;
s_axi_rdata = 0;
s_axi_rresp = 0;
end
endtask
integer i;
initial
begin
s_axi_arready =0;
s_axi_rvalid =0;
s_axi_rdata =0;
s_axi_rresp =0;
#RST_TIME
#CLK_PERIOD
virtual_rx('h23,3);
virtual_rx('h48,2);
virtual_rx('h7d,1); virtual_rx('h48,2);virtual_rx('h23,0);
virtual_rx('h7d,1); virtual_rx('h48,2);virtual_rx('h23,0);
virtual_rx('h7d,1); virtual_rx('h48,2);virtual_rx('h23,0);
virtual_rx('h7d,1); virtual_rx('h48,2);virtual_rx('h23,0);
virtual_rx('h7d,1); virtual_rx('h48,2);virtual_rx('h23,0);
end
axi_read axi_read_u (
.s_axi_aclk(sim_clk ),
.s_axi_aresetn(sim_rstn ),
.s_axi_arready(s_axi_arready ),
.s_axi_rvalid(s_axi_rvalid ),
.s_axi_rdata(s_axi_rdata ),
.s_axi_rresp(s_axi_rresp ),
.s_axi_araddr(s_axi_araddr),
.s_axi_arvalid(s_axi_arvalid),
.s_axi_rready(s_axi_rready)
);
endmodule
注:我的testbench其中有些:#1 信号x =0;其实是为了使得非阻塞时序在判断信号x变化后动作的时间点在下一个时钟沿,若直接:信号x =0;在仿真时会在当前时钟沿动作,这不是我们期望的。
vivado的JESD204Bv7.2核的example design 发射端tx给出的demo_tb.v中有关于AXI4-lite的配置寄存器的细节,但注意的是寄存器0x80C和0x818不能像仿真里那样配置,配置了会出大事!因为PG066手册里写到是不需要配置的:[RX only, not writeable for TX]。
我一开始没有注意就把上面的所有的寄存器放到可综合里面去了,这样仿真可以通过,且在FPGA内部用jesd_core_tx---jesdphy---jesd_core_rx内部自环下载到板子上也通过了,然后在FPGA上jesd_core_tx---jesdphy---带有jesd功能的的DAC芯片就无法成功,主要是jesd的关键信号tx_sync一直无法变高,即无法同步,然后痛苦的来了,我就各种找问题,找JESD核的配置界面是否出问题,以及SYSREF信号与参数K的关系,以及DAC芯片的配置的问题,xdc的管教约束等等,后来搞到了一个FPGA核与DA正常同步的例程,虽然可以直接把例子套到我的工程上去,但是我本着debug的心态想找出我失败的原因,一顿折腾之后终于发现了上面所说的原因,0x80c与0x818不该配置。
这前前后后已经用了接近两周的时间,问题发现了感觉也不是很复杂的问题,但你不发现就是解决不了。最后反思一下,第一是不够细心,若是早早看一下寄存器配置的就好了,第二是问题把握的不透彻,总是怀疑这怀疑那的最后找不到问题的重点,第三要与人多交流,自己一人调试是真的有点难受(事实上我们实验室就我一个人在做FPGA(哭)好在我对这个还挺感兴趣)。
最后,如果有用就点个赞吧~