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

    二、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 
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228
    • 229
    • 230
    • 231
    • 232
    • 233
    • 234
    • 235
    • 236
    • 237
    • 238
    • 239
    • 240
    • 241
    • 242
    • 243
    • 244
    • 245
    • 246
    • 247
    • 248
    • 249
    • 250
    • 251
    • 252
    • 253
    • 254
    • 255
    • 256
    • 257
    • 258
    • 259
    • 260
    • 261
    • 262
    • 263
    • 264
    • 265
    • 266
    • 267
    • 268
    • 269
    • 270
    • 271
    • 272
    • 273
    • 274
    • 275
    • 276
    • 277
    • 278
    • 279
    • 280
    • 281
    • 282
    • 283
    • 284

    六、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;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103

    最终通过串口也可以得到,两个IP可以接收到的数据是一样的。说明自己写的axi-lite接口可以实现ps与pl之间的互通。
    在这里插入图片描述

  • 相关阅读:
    5分钟搭建图片压缩应用
    大数据与云计算实验一
    【开发工具的那些故事】Git跨代码仓库合并代码
    @Async可以和@Transactional结合使用吗?
    自定义卷积核的分组转置卷积如何实现?
    利用百度竞价排名推广增加转化率-华媒舍
    SPRINGBOOT04_配置文件、三种读取配置文件方式
    OSINT技术情报精选·2024年6月第2周
    Jenkins+webhooks-多分支参数化构建-
    【C++】静态成员变量 ( 静态成员变量概念 | 静态成员变量声明 | 静态成员变量初始化 | 静态成员变量访问 | 静态成员变量生命周期 )
  • 原文地址:https://blog.csdn.net/qq_40995480/article/details/128007792