FPGA开发(4)——AXI_LITE总线协议

一、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之间的互通。

文章出处登录后可见!

已经登录?立即刷新

共计人评分,平均

到目前为止还没有投票!成为第一位评论此文章。

(0)
青葱年少的头像青葱年少普通用户
上一篇 2023年12月6日
下一篇 2023年12月6日

相关推荐