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

一、AXI总线简介

对于axi总线的学习我主要是参考了赛灵思的ug1037文档以及arm的INI0022D手册,对其中的内容做了总结。FPGA开发(4)——AXI_LITE总线协议_第1张图片
AXI是amba总线的一种,包含三种,axi full、axi lite和axi stream。
FPGA开发(4)——AXI_LITE总线协议_第2张图片
FPGA开发(4)——AXI_LITE总线协议_第3张图片
AXI工作:axi接口包含了五组通道,分别是读地址、写地址、读数据、写数据以及写响应。数据可以在主机和从机中双向传输,AXI4支持最大256突发读写,AXI-lite只不支持突发读写。
FPGA开发(4)——AXI_LITE总线协议_第4张图片
FPGA开发(4)——AXI_LITE总线协议_第5张图片
1、axi支持数据突发传输,读和写通道可同时工作。Axi-lite则不支持突发传输,axi-stream可支持任意突发长度传输
2、其次,axi和axi-lite是地址映射的,axi-stream不是地址映射。Axi和axi-stream还可以结合起来,例如DMA等。

FPGA开发(4)——AXI_LITE总线协议_第6张图片
FPGA开发(4)——AXI_LITE总线协议_第7张图片
FPGA开发(4)——AXI_LITE总线协议_第8张图片
AXI接口:
1、赛灵思官方本身提供很多带axi接口的ip,有axi、axistream等
2、自行封装带有axi接口的ip,axi协议自动生成,添加DIY代码即可
3、外部添加axi-接口IP
4、HLS
5、axi for dsp
……
FPGA开发(4)——AXI_LITE总线协议_第9张图片
Vivado中axi互联
主要有interconnect和smartconnect,其中smart更精密,inter适用于所有。两种都作为axi ip核与arm axi接口之间转换的功能接口。
Inter和smart都是地址映射,axi-stream无法连接,但是axi-stream可以连接axis-inter,然后转换为地址映射
FPGA开发(4)——AXI_LITE总线协议_第10张图片
Axi互联核心提供1对多、多对1和多对多的接口。
FPGA开发(4)——AXI_LITE总线协议_第11张图片
FPGA开发(4)——AXI_LITE总线协议_第12张图片
FPGA开发(4)——AXI_LITE总线协议_第13张图片
包含了五组信号,写通道和读通道可以同时进行数据的传输
FPGA开发(4)——AXI_LITE总线协议_第14张图片
1、每一个独立的通道都包含valid和ready这样的握手机制
2、读通道和写通道数据传输最后会有一个last信号
3、读地址和写地址
4、读数据通道,数据支持多种位宽,然后读响应说明读的状态。
5、写数据通道,多种带宽,每个字节信号有一个频闪信号,用来说明数据有效。
6、写响应通道,从机用写响应通道反馈写的状态

二、AXI总线五组接口介绍

FPGA开发(4)——AXI_LITE总线协议_第15张图片
对于写地址通道,有这么一些关键的接口
1、写地址
2、写长度
3、写大小
4、写突发,有三种模式,固定、自增、回环
5、valid
6、ready
FPGA开发(4)——AXI_LITE总线协议_第16张图片
对于写数据通道,有这么一些关键的接口
1、写数据
2、频闪信号,表示数据有效
3、last,表示传输数据最后一个的标志
4、valid
5、ready
FPGA开发(4)——AXI_LITE总线协议_第17张图片
对于写响应通道,有这么一些关键的接口
1、bresp,代表了从机对主机写的回应,判断是否写入成功
2、valid
3、ready
FPGA开发(4)——AXI_LITE总线协议_第18张图片
FPGA开发(4)——AXI_LITE总线协议_第19张图片
读地址和读数据通道,与写相似

三、AXI信号约束

FPGA开发(4)——AXI_LITE总线协议_第20张图片
FPGA开发(4)——AXI_LITE总线协议_第21张图片
FPGA开发(4)——AXI_LITE总线协议_第22张图片

信号约束
共用时钟线,上升沿数据采样,在上升沿之后数据才可以变化。
低电平复位,复位时主机控制写地址、读地址和写数据的valid,从机控制读数据和写响应的valid。
FPGA开发(4)——AXI_LITE总线协议_第23张图片
FPGA开发(4)——AXI_LITE总线协议_第24张图片
信号约束
说明了五组信号中valid和ready信号的关系
当源端的数据或者地址或者控制信号有效时,拉高valid信号。然后这个valid信号要一直保持高电平直到ready信号为高并采样为止。
FPGA开发(4)——AXI_LITE总线协议_第25张图片
读地址和读数据的信号先后关系
1、主机arvaild信号不必等从机的arready
2、从机的arready可以等主机的arvalid
3、从机的arready可以先于主机的arvalid
4、从机的rvalid一定要在arvalid和arready之后才出现(重要)
5、从机的rvalid不必等主机的rready
6、主机的rready可以等从机的rvalid
7、主机的rready可以先于从机的rvalid
FPGA开发(4)——AXI_LITE总线协议_第26张图片
写地址和写通道类似前面读的过程
要注意一点,主机valid不要等待从机ready,否则有可能会引发死锁的产生。
FPGA开发(4)——AXI_LITE总线协议_第27张图片
FPGA开发(4)——AXI_LITE总线协议_第28张图片
FPGA开发(4)——AXI_LITE总线协议_第29张图片
在非高带宽应用场景,axi-lite接口是很常用的,比axi4少了很多信号。其有以下的特点:
1、不支持突发传输
2、传输的位宽位32或者64位
3、传输无缓存,不可配置
4、不支持独立访问
这边主要来学习axi-lite协议。

四、AXI-LITE信号时序图

FPGA开发(4)——AXI_LITE总线协议_第30张图片
首先上面这张图是输入信号的时序图。
FPGA开发(4)——AXI_LITE总线协议_第31张图片FPGA开发(4)——AXI_LITE总线协议_第32张图片
上面两张图是我根据看的两个手册画的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寄存器的值。
FPGA开发(4)——AXI_LITE总线协议_第33张图片
下面是我的ps端的C代码,主要完成功能就是对两个IP分别读写寄存器,来控制呼吸灯。还有另外一部分不想关,是我对是个数据的读取个判断,是第三个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之间的互通。
FPGA开发(4)——AXI_LITE总线协议_第34张图片

你可能感兴趣的:(fpga开发)