一、AXI总线简介
对于axi总线的学习我主要是参考了赛灵思的ug1037文档以及arm的INI0022D手册,对其中的内容做了总结。
AXI是amba总线的一种,包含三种,axi full、axi lite和axi stream。
AXI工作:axi接口包含了五组通道,分别是读地址、写地址、读数据、写数据以及写响应。数据可以在主机和从机中双向传输,AXI4支持最大256突发读写,AXI-lite只不支持突发读写。
1、axi支持数据突发传输,读和写通道可同时工作。Axi-lite则不支持突发传输,axi-stream可支持任意突发长度传输
2、其次,axi和axi-lite是地址映射的,axi-stream不是地址映射。Axi和axi-stream还可以结合起来,例如DMA等。
二、AXI总线五组接口介绍
对于写地址通道,有这么一些关键的接口
1、写地址
2、写长度
3、写大小
4、写突发,有三种模式,固定、自增、回环
5、valid
6、ready
对于写数据通道,有这么一些关键的接口
1、写数据
2、频闪信号,表示数据有效
3、last,表示传输数据最后一个的标志
4、valid
5、ready
对于写响应通道,有这么一些关键的接口
1、bresp,代表了从机对主机写的回应,判断是否写入成功
2、valid
3、ready
读地址和读数据通道,与写相似
三、AXI信号约束
信号约束
共用时钟线,上升沿数据采样,在上升沿之后数据才可以变化。
低电平复位,复位时主机控制写地址、读地址和写数据的valid,从机控制读数据和写响应的valid。
信号约束
说明了五组信号中valid和ready信号的关系
当源端的数据或者地址或者控制信号有效时,拉高valid信号。然后这个valid信号要一直保持高电平直到ready信号为高并采样为止。
读地址和读数据的信号先后关系
1、主机arvaild信号不必等从机的arready
2、从机的arready可以等主机的arvalid
3、从机的arready可以先于主机的arvalid
4、从机的rvalid一定要在arvalid和arready之后才出现(重要)
5、从机的rvalid不必等主机的rready
6、主机的rready可以等从机的rvalid
7、主机的rready可以先于从机的rvalid
写地址和写通道类似前面读的过程
要注意一点,主机valid不要等待从机ready,否则有可能会引发死锁的产生。
在非高带宽应用场景,axi-lite接口是很常用的,比axi4少了很多信号。其有以下的特点:
1、不支持突发传输
2、传输的位宽位32或者64位
3、传输无缓存,不可配置
4、不支持独立访问
这边主要来学习axi-lite协议。
四、AXI-LITE信号时序图
首先上面这张图是输入信号的时序图。
上面两张图是我根据看的两个手册画的axi-lite协议的时序图。
五、AXI-LITE的verilog代码
根据上面的时序图写了下述的代码,其内容也很简单就是定义了19组信号。这19个信号分为五组,分别是写地址、写数据、读地址、读数据以及写响应。每一组数据都含有valid和ready信号,基于握手机制传输数据。代码并不复杂,根据时序图就能很快写出来。
module axi_test(
//aclk and reset_n
input s_axi_aclk,
input s_axi_aresetn,
//write addr
input [3:0] s_axi_awaddr,
input [2:0] s_axi_awprot,
input s_axi_awvalid,
output s_axi_awready,
//write data
input [31:0] s_axi_wdata,
input [3:0] s_axi_wstrb,
input s_axi_wvalid,
output s_axi_wready,
//write response
output [1:0] s_axi_bresp,
output s_axi_bvalid,
input s_axi_bready,
//read addr
input [3:0] s_axi_araddr,
input [2:0] s_axi_arprot,
input s_axi_arvalid,
output s_axi_arready,
//read data
output [31:0] s_axi_rdata,
output [1:0] s_axi_rresp,
output s_axi_rvalid,
input s_axi_rready,
//output signal
output axi_test
);
//axi4_lite signal
reg [3:0] axi_awaddr;
reg [3:0] axi_araddr;
reg axi_awready;
reg axi_wready;
reg axi_bvalid;
reg [1:0] axi_bresp;
reg axi_arready;
reg axi_rvalid;
reg [31:0] axi_rdata;
reg [1:0] axi_rresp;
//slave registers to save data
reg [31:0] slv_reg0;
reg [31:0] slv_reg1;
reg [31:0] slv_reg2;
reg [31:0] slv_reg3;
reg [31:0] reg_data_out;
reg aw_en;
integer byte_index;
wire slv_reg_rden;
wire slv_reg_wren;
//axi4-lite signal define
assign s_axi_awready=axi_awready;
assign s_axi_wready=axi_wready;
assign s_axi_bvalid=axi_bvalid;
assign s_axi_bresp=axi_bresp;
assign s_axi_arready=axi_arready;
assign s_axi_rdata=axi_rdata;
assign s_axi_rvalid=axi_rvalid;
assign s_axi_rresp=axi_rresp;
//awready signal
//aresetn:0
//~awready & awvalid & wvalid & awen:1
//bready & bvalide:0
//other:0
always @(posedge s_axi_aclk) begin
if(~s_axi_aresetn)begin
aw_en<=1'b1;
axi_awready<=1'b0;
end
else begin
if(~axi_awready && aw_en && s_axi_awvalid && s_axi_wvalid)begin
aw_en<=1'b0;
axi_awready<=1'b1;
end
else if(s_axi_bready && axi_bvalid)begin
aw_en<=1'b1;
axi_awready<=1'b0;
end
else begin
axi_awready<=1'b0;
end
end
end
//awaddr signal
//aresetn:0
//~awready & awvalid & wvalid & awen:1
//other:same
always @(posedge s_axi_aclk) begin
if(~s_axi_aresetn)begin
axi_awaddr<=4'd0;
end
else begin
if(~axi_awready && aw_en && s_axi_awvalid && s_axi_wvalid)begin
axi_awaddr<=s_axi_awaddr;
end
end
end
//axi_wready signal
//aresetn:0
//~awready & awvalid & wvalid & awen:1
//other:0
always @(posedge s_axi_aclk) begin
if(~s_axi_aresetn)begin
axi_wready<=1'b0;
end
else begin
if(~axi_awready && aw_en && s_axi_awvalid && s_axi_wvalid)begin
axi_wready<=1'b1;
end
else begin
axi_wready<=1'b0;
end
end
end
//wren signal
//awready && awvalid && wready && wvalid:1
//other:0
assign slv_reg_wren=s_axi_awvalid && s_axi_wvalid && axi_awready && axi_wready;
//write data to reg0 to reg3
always @(posedge s_axi_aclk) begin
if(~s_axi_aresetn)begin
slv_reg0<=32'd0;
slv_reg1<=32'd0;
slv_reg2<=32'd0;
slv_reg3<=32'd0;
end
else begin
if(slv_reg_wren)begin
case(axi_awaddr[3:2])
//reg0
2'h0:begin
for(byte_index=0;byte_index<=3;byte_index=byte_index+1)begin
if(s_axi_wstrb[byte_index]==1'b1)begin
slv_reg0[(byte_index*8) +: 8]<=s_axi_wdata[(byte_index*8) +: 8];
end
end
end
//reg1
2'h1:begin
for(byte_index=0;byte_index<=3;byte_index=byte_index+1)begin
if(s_axi_wstrb[byte_index]==1'b1)begin
slv_reg1[(byte_index*8) +: 8]<=s_axi_wdata[(byte_index*8) +: 8];
end
end
end
//reg2
2'h2:begin
for(byte_index=0;byte_index<=3;byte_index=byte_index+1)begin
if(s_axi_wstrb[byte_index]==1'b1)begin
slv_reg2[(byte_index*8) +: 8]<=s_axi_wdata[(byte_index*8) +: 8];
end
end
end
//reg3
2'h3:begin
for(byte_index=0;byte_index<=3;byte_index=byte_index+1)begin
if(s_axi_wstrb[byte_index]==1'b1)begin
slv_reg3[(byte_index*8) +: 8]<=s_axi_wdata[(byte_index*8) +: 8];
end
end
end
//no reg
default:begin
slv_reg0<=slv_reg0;
slv_reg1<=slv_reg1;
slv_reg2<=slv_reg2;
slv_reg3<=slv_reg3;
end
endcase
end
end
end
//breasp and bvalid signal
//aresetn:0
//awready && wready && awvalid && wvalid && ~bvalid:1
//bvalid && bready:1
always @(posedge s_axi_aclk) begin
if(~s_axi_aresetn)begin
axi_bvalid<=1'b0;
axi_bresp<=2'd0;
end
else begin
if(axi_awready && axi_wready && s_axi_wvalid && s_axi_awvalid && ~axi_bvalid)begin
axi_bvalid<=1'b1;
axi_bresp<=2'd0;
end
else if(s_axi_bready && axi_bvalid)begin
axi_bvalid<=1'b0;
end
end
end
//arready signal
//araddr save
//arsetn:0
//~arready && arvalid:1
//other:0
always @(posedge s_axi_aclk) begin
if(~s_axi_aresetn)begin
axi_arready<=1'b0;
axi_araddr<=4'd0;
end
else begin
if(~axi_arready && s_axi_arvalid)begin
axi_arready<=1'b1;
axi_araddr<=s_axi_araddr;
end
else begin
axi_arready<=1'b0;
end
end
end
//rvalid and rresp signal
//aresetn:0
//arready && arvalid && ~rvalid:1
//rvalid & rready:0
//other:0
always @(posedge s_axi_aclk) begin
if(~s_axi_aresetn)begin
axi_rvalid<=1'b0;
axi_rresp<=2'd0;
end
else begin
if(axi_arready && s_axi_arvalid && ~axi_rvalid)begin
axi_rvalid<=1'b1;
axi_rresp<=2'd0;
end
else if(axi_rvalid && s_axi_rready)begin
axi_rvalid<=1'b0;
end
end
end
//read data from reg0 to reg3
assign slv_reg_rden=axi_arready && s_axi_arvalid && ~axi_rvalid;
always @(*) begin
case(axi_araddr[3:2])
2'h0:reg_data_out<=slv_reg0;
2'h1:reg_data_out<=slv_reg1;
2'h2:reg_data_out<=slv_reg2;
2'h3:reg_data_out<=slv_reg3;
default:reg_data_out<=32'd0;
endcase
end
//output data
always @(posedge s_axi_aclk) begin
if(~s_axi_aresetn)begin
axi_rdata<=32'd0;
end
else begin
if(slv_reg_rden)begin
axi_rdata<=reg_data_out;
end
end
end
//axi_test
breath_led u_breath_led(
.sys_clk ( s_axi_aclk ),
.sys_rst_n ( s_axi_aresetn ),
.sw_ctrl ( slv_reg0[0] ),
.set_en ( slv_reg1[31] ),
.set_freq_step ( slv_reg1[9:0] ),
.led ( axi_test )
);
endmodule
六、AXI-LITE测试——ps与pl互通
下面是我的block design,作为参照,我还使用vivado自带封装AXI接口的ip核封装了一个呼吸灯的代码,然后两个IP完成的功能是一样的,都是可以控制呼吸灯的闪烁,然后都可以从ps读取pl寄存器的值。
下面是我的ps端的C代码,主要完成功能就是对两个IP分别读写寄存器,来控制呼吸灯。还有另外一一个ip是number_recog是神经网络识别mnist的,这里不相关。只需看breath led和axi test这两个IP就可以。
#include "math.h"
#include "stdio.h"
#include "breathLED.h"
#include "xparameters.h"
#include "xil_io.h"
//baseaddr define
#define breath_led_baseaddr XPAR_BREATHLED_0_S00_AXI_BASEADDR
#define axi_test_baseaddr XPAR_AXI_TEST_0_BASEADDR
#define axi_num_recog_baseaddr XPAR_AXI_NUM_RECOG_TEST_0_BASEADDR
int main()
{
//led test signal
int a1;
int a2;
int a1_1;
int a2_1;
//data read from PL
int data[10];
//data change to double
double data_lf[10];
//find max data and cnt
u32 i;
u32 cnt;
double max;
//calculate the number probability
double pro_all=0;
double pro_num[10];
//led control test
Xil_Out32(breath_led_baseaddr+BREATHLED_S00_AXI_SLV_REG0_OFFSET,0x00000001);
Xil_Out32(breath_led_baseaddr+BREATHLED_S00_AXI_SLV_REG1_OFFSET,0x80000050);
a1=Xil_In32(breath_led_baseaddr+BREATHLED_S00_AXI_SLV_REG0_OFFSET);
a2=Xil_In32(breath_led_baseaddr+BREATHLED_S00_AXI_SLV_REG1_OFFSET);
Xil_Out32(axi_test_baseaddr+0,0x00000001);
Xil_Out32(axi_test_baseaddr+4,0x80000050);
a1_1=Xil_In32(axi_test_baseaddr+0);
a2_1=Xil_In32(axi_test_baseaddr+4);
//data read
for(i=0;i<10;i++)
{
data[i]=Xil_In32(axi_num_recog_baseaddr+i*4);
}
//data change
for(i=0;i<10;i++)
{
data_lf[i]=data[i];
}
//find max data and cnt
max=data_lf[0];
cnt=0;
for(i=0;i<10;i++)
{
if(max<=data_lf[i])
{
max=data_lf[i];
cnt=i;
}
}
Xil_Out32(axi_num_recog_baseaddr+10*4,cnt);
//calculate the probability
for(i=0;i<10;i++)
{
data_lf[i]=data_lf[i]/max*10;
}
for(i=0;i<10;i++)
{
pro_all=pro_all+exp(data_lf[i]);
};
for(i=0;i<10;i++)
{
pro_num[i]=exp(data_lf[i])/pro_all;
}
printf("breath led test\n");
printf("a1=%d\n",a1);
printf("a2=%d\n",a2);
printf("\n");
printf("breath led DIY test\n");
printf("a1_1=%d\n",a1_1);
printf("a2_1=%d\n",a2_1);
printf("\n");
printf("read num data test\n");
for(i=0;i<10;i++)
{
printf("data_%d=%d\n",i,data[i]);
}
printf("\n");
printf("output number and probability\n");
for(i=0;i<10;i++)
{
printf("The probability of the number %d is %f\n",i,pro_num[i]);
}
printf("the number is %d\n",cnt);
return 0;
}
最终通过串口也可以得到,两个IP可以接收到的数据是一样的。说明自己写的axi-lite接口可以实现ps与pl之间的互通。
文章出处登录后可见!