02 AXI4总线axi-lite-slave

发布时间 2023-12-30 14:32:20作者: 米联客(milianke)

软件版本:vitis2021.1(vivado2021.1)

操作系统:WIN10 64bit

硬件平台:适用XILINX A7/K7/Z7/ZU/KU系列FPGA

登录"米联客"FPGA社区-www.uisrc.com视频课程、答疑解惑!

1概述

使用XILINX 的软件工具VIVADO以及XILINX7代以上的FPGA或者SOC掌握AXI-4总线协议,并且可以灵活使用AXI-4总线技术完成数据的交换,可以让我们在构建强大的FPGA内部总线数据互联通信方面取得高效、高速、标准化的优势。

本文实验目的:

1:学习AXI总线协议包括AXI-FULLAXI-Lite

2:掌握基于VIVADO工具产生AXI协议模板

3:掌握通过VIVADO工具产生AXI-lite-Slave代码,并且会修改寄存器

4:理解AXI-lite-Slave中自定义寄存器的地址分配

5:掌握通过VIVADO封装AXI-LITE-SLAVE 图形化IP

6:通过仿真验证AXI-LITE IP的工作是否正常。

2 axi-lite介绍

AXI_lite是轻量级的AXI协议,它每次传输的数据和地址的突发长度只有1,也就是burst=1。常用与较少数据量的存储映射通信,比如配置寄存器。

下面把AXI_lite的所有信号罗列出来:

写地址

AW_ADDR

ADDR_WIDTH-1 0

 

AW_VALID

  

AW_READY

  

AW_PORT

1 : 0

写通道保护信号

写数据

W_DATA

DATA_WIDTH-1 : 0

 

W_STRB

(DATA_WIDTH/8)-1 : 0

写字节有效位控制

W_VALID

  

W_READY

  

写回应

B_RESP

10

 

B_VALID

  

B_READY

  

读地址

AR_ADDR

ADDR_WIDTH-1 : 0

 

AR_VALID

  

AR_READY

  

AR_PORT

10

读通道保护信号

读数据

R_DATA

  

R_RESP

10

 

R_VALID

  

R_READY

  

介绍一下AW_PORT和AR_PORT,是写/读通道保护信号,[0]表示正常或特权,[1]表示安全或非安全,[2]表示指令或数据。这个信号需要用户在使用中根据需要自行配置,我们在本次实现的AXI_lite中不考虑这个信号。

W_STRB信号是写字节有效位控制,在高速通信协议中,都会有这个信号来代表传输的字节是否有效,PCIE、GTX等协议中都有,1代表有效,0代表无效。如果数据为32位,则STRB = 32/8 = 4位。

3 axi-lite信号之间的依赖关系

图中的单头箭头表示:其指向的信号可以在箭头起始信号置起之前或之后置起(无依赖)

图中的双头箭头表示:其指向的信号必须在箭头起始信号置起之后置起(指向信号依赖起始信号)

读顺序:先传输完毕读地址后(arvalid+arready),slave再给出读数据(rvalid)。

读通道顺序(单箭头:无依赖;双箭头:有依赖)

写顺序:写地址和写数据同时传输,然后才能给出bvalid。

写通道顺序(单箭头:无依赖;双箭头:有依赖)

4创建axi4-lite-slave总线接口IP

新建fpga工程,过程省略

新建完成工程后,单击菜单栏Tools->Create and Package New IP,开始创建一个AXI4-Lite接口总线IP

选择使用vivado自带的AXI总线模板创建一个AXI4-Lite接口IP

设置IP的名字为saxi_lite

 

模板支持3种协议,分别是AXI4-Full AXI4-Lite AXI4-Stream

总线包括MasterSlave两种模式,这里选择Slave模式

这里选择Verify Peripheral IP using AXI4 VIP 可以对AXI4-Lite快速验证

单击Finish 后展开VIVADO自动产生的demo,单击Block Design的工程,可以看到如下2IP。其中saxi_lite_0就是我们自定义的IP,另外一个master_0是用来读写我们自定义的saxi_lite_0,以此验证我们的IP正确性。

继续再看代码看看里面有什么东西

右击Generate Output Products

路径uisrc/03_ip/saxi_lite_1.0/hdl路径下的saxi_lite_v_0_S00_AXI.v就是我们的源码。另外一个saxi_lite_v1_0.v是软件产生了一个接口文件,如果我们自己定义IP可有可无。

4程序分析

axi总线信号的关键无非是地址和数据,而写地址的有效取决于AXI_AWVALIDAXI_AWREADY,写数据的有效取决于S_AXI_WVALIDS_AXI_WREADY。同理,读地址的有效取决于AXI_ARVALIDAXI_ARREADY,读数据的有效取决于S_AXI_RVALIDS_AXI_RREADY。所以以下代码的阅读分析注意也是围绕以上4个信号的有效时序。

以下程序我们把关键信号的代码拆分阅读

1:axi-lite-slaveaxi_awready

    always @( posedge S_AXI_ACLK )

    begin

      if ( S_AXI_ARESETN == 1'b0 )

        begin

          axi_awready <= 1'b0;

          aw_en <= 1'b1;

        end

      else

        begin    

          if (~axi_awready && S_AXI_AWVALID && S_AXI_WVALID && aw_en)

            begin

              // 当在写地址和数据总线上存在有效的写地址和写数据时,从设备准备好接受写地址。

              // 此设计不需要未完成的事务。

              axi_awready <= 1'b1;

              aw_en <= 1'b0;

            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

2:axi-lite-slaveaxi_awaddr

    always @( posedge S_AXI_ACLK )

    begin

      if ( S_AXI_ARESETN == 1'b0 )

        begin

          axi_awaddr <= 0;

        end

      else

        begin    

          if (~axi_awready && S_AXI_AWVALID && S_AXI_WVALID && aw_en)

            begin

              // 写入地址锁存

              axi_awaddr <= S_AXI_AWADDR;

            end

        end

    end

3:axi-lite-slaveaxi_wready

当满足(~axi_wready && S_AXI_WVALID && S_AXI_AWVALID && aw_en )==1条件,设置axi_wready有效。

    always @( posedge S_AXI_ACLK )

    begin

      if ( S_AXI_ARESETN == 1'b0 )

        begin

          axi_wready <= 1'b0;

        end

      else

        begin    

          if (~axi_wready && S_AXI_WVALID && S_AXI_AWVALID && aw_en )

            begin

              // 当在写地址和数据总线上存在有效的写地址和写数据时,从设备准备好接受写数据。

              // 此设计不需要未完成的事务。

              axi_wready <= 1'b1;

            end

          else

            begin

              axi_wready <= 1'b0;

            end

        end

    end

4:axi-lite-slave的写数据寄存器

axi-lite-slave很重要一点功能就是配合SOC的处理器部分完成一些低速外设,或者寄存器的控制。需要使用多寄存器或者外设,一般在ip代码里面就已经设置好了。前面用vivado的模板产生自定义ip的时候,我们选择了432bits寄存器,以下的模板中slv_reg0~ slv_reg3共计432bits寄存器。

    assign slv_reg_wren = axi_wready && S_AXI_WVALID && axi_awready && S_AXI_AWVALID;

 

    always @( posedge S_AXI_ACLK )

    begin

      if ( S_AXI_ARESETN == 1'b0 )

        begin

          slv_reg0 <= 0;

          slv_reg1 <= 0;

          slv_reg2 <= 0;

          slv_reg3 <= 0;

        end

      else begin

        if (slv_reg_wren)

          begin

            case ( axi_awaddr[ADDR_LSB+OPT_MEM_ADDR_BITS:ADDR_LSB] )

              2'h0:

                for ( byte_index = 0; byte_index <= (C_S_AXI_DATA_WIDTH/8)-1; byte_index = byte_index+1 )

                  if ( S_AXI_WSTRB[byte_index] == 1 ) begin

                    // 根据写入选通断言相应的字节使能

                    // 从属寄存器0

                    slv_reg0[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8];

                  end  

              2'h1:

                for ( byte_index = 0; byte_index <= (C_S_AXI_DATA_WIDTH/8)-1; byte_index = byte_index+1 )

                  if ( S_AXI_WSTRB[byte_index] == 1 ) begin

                    // 根据写入选通断言相应的字节使能

                    // 从寄存器1

                    slv_reg1[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8];

                  end  

              2'h2:

                for ( byte_index = 0; byte_index <= (C_S_AXI_DATA_WIDTH/8)-1; byte_index = byte_index+1 )

                  if ( S_AXI_WSTRB[byte_index] == 1 ) begin

                    // 根据写入选通断言相应的字节使能

                    // 从寄存器2

                    slv_reg2[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8];

                  end  

              2'h3:

                for ( byte_index = 0; byte_index <= (C_S_AXI_DATA_WIDTH/8)-1; byte_index = byte_index+1 )

                  if ( S_AXI_WSTRB[byte_index] == 1 ) begin

                    // 根据写入选通断言相应的字节使能

                    // 从寄存器3

                    slv_reg3[(byte_index*8) +: 8] <= S_AXI_WDATA[(byte_index*8) +: 8];

                  end  

              default : begin

                          slv_reg0 <= slv_reg0;

                          slv_reg1 <= slv_reg1;

                          slv_reg2 <= slv_reg2;

                          slv_reg3 <= slv_reg3;

                        end

            endcase

          end

      end

    end    

5:axi-lite-slaveaxi_bvalid信号

axi_bvalid用于告知axi master axi-slave端已经完成数据接收了

    always @( posedge S_AXI_ACLK )

    begin

      if ( S_AXI_ARESETN == 1'b0 )

        begin

          axi_bvalid  <= 0;

          axi_bresp   <= 2'b0;

        end

      else

        begin    

          if (axi_awready && S_AXI_AWVALID && ~axi_bvalid && axi_wready && S_AXI_WVALID)

            begin

              // indicates a valid write response is available

              axi_bvalid <= 1'b1;

              axi_bresp  <= 2'b0; // 'OKAY' response

            end                   // work error responses in future

          else

            begin

              if (S_AXI_BREADY && axi_bvalid)

                //check if bready is asserted while bvalid is high)

                //(there is a possibility that bready is always asserted high)  

                begin

                  axi_bvalid <= 1'b0;

                end  

            end

        end

    end  

6:axi-lite-slaveaxi_arready

当条件满足(~axi_arready && S_AXI_ARVALID)==1设置axi_arready有效,并且寄存住总线上的地址axi_araddr <= S_AXI_ARADDR

    always @( posedge S_AXI_ACLK )

    begin

      if ( S_AXI_ARESETN == 1'b0 )

        begin

          axi_arready <= 1'b0;

          axi_araddr  <= 32'b0;

        end

      else

        begin    

          if (~axi_arready && S_AXI_ARVALID)

            begin

              // indicates that the slave has acceped the valid read address

              axi_arready <= 1'b1;

              // Read address latching

              axi_araddr  <= S_AXI_ARADDR;

            end

          else

            begin

              axi_arready <= 1'b0;

            end

        end

    end    

7:axi-lite-slaveaxi_araddr

    always @( posedge S_AXI_ACLK )

    begin

      if ( S_AXI_ARESETN == 1'b0 )

        begin

          axi_arready <= 1'b0;

          axi_araddr  <= 32'b0;

        end

      else

        begin    

          if (~axi_arready && S_AXI_ARVALID)

            begin

              // indicates that the slave has acceped the valid read address

              axi_arready <= 1'b1;

              // Read address latching

              axi_araddr  <= S_AXI_ARADDR;

            end

          else

            begin

              axi_arready <= 1'b0;

            end

        end

    end

8:axi-lite-slaveaxi_rvalid信号

当条件满足(axi_arready && S_AXI_ARVALID && ~axi_rvalid)==1的时候设置axi_rvalid有效,表示axi-lite-slave总线上的数据是有效的。

    always @( posedge S_AXI_ACLK )

    begin

      if ( S_AXI_ARESETN == 1'b0 )

        begin

          axi_rvalid <= 0;

          axi_rresp  <= 0;

        end

      else

        begin    

          if (axi_arready && S_AXI_ARVALID && ~axi_rvalid)

            begin

              // Valid read data is available at the read data bus

              axi_rvalid <= 1'b1;

              axi_rresp  <= 2'b0; // 'OKAY' response

            end  

          else if (axi_rvalid && S_AXI_RREADY)

            begin

              // Read data is accepted by the master

              axi_rvalid <= 1'b0;

            end                

        end

    end  

9:axi-lite-slave的读数据寄存器

本文实验中,axi-master写入4个寄存器数据,然后读出,通过查看数据是否一致可以确认axi-lite-slave工作是否正常。当slv_reg_rden有效的时候,数据被读入寄存器axi_rdata,当axi_rvalid有效的时候,数据被锁存。

    assign slv_reg_rden = axi_arready & S_AXI_ARVALID & ~axi_rvalid;

    always @(*)

    begin

          // Address decoding for reading registers

          case ( axi_araddr[ADDR_LSB+OPT_MEM_ADDR_BITS:ADDR_LSB] )

            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 <= 0;

          endcase

    end

 

    // Output register or memory read data

    always @( posedge S_AXI_ACLK )

    begin

      if ( S_AXI_ARESETN == 1'b0 )

        begin

          axi_rdata  <= 0;

        end

      else

        begin    

          // When there is a valid read address (S_AXI_ARVALID) with

          // acceptance of read address by the slave (axi_arready),

          // output the read dada

          if (slv_reg_rden)

            begin

              axi_rdata <= reg_data_out;     // register read data

            end  

        end

    end

当我们阅读后分析完以上代码后,可以发现,axi-lite-slave的代码中没有突发长度的处理,每次只处理一个地址的一个数据。并且也没有WLASTRLAST信号,说明axi-lite-slave适合一些低速的数据交互,但是可以节省一些FPGA的逻辑资源。

5实验结果

单击仿真

 

添加观察信号

AXI总线依次写入1 2 3 4slv_reg0~slv_reg3完成数据寄存

 

读数据