Xilinx AXI4 协议

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

本文链接:https://blog.csdn.net/Calvin790704/article/details/128588714 

一、AXI4协议介绍

AXI4 相对复杂,但 SOC 开发者必须掌握。AXI 协议的具体内容可参考 Xilinx UG761 AXI Reference Guide。在这里我们简单了解一下。

AXI4 所采用的是一种 READY,VALID 握手通信机制,即主从模块进行数据通信前,先根据操作对各所用到的数据、地址通道进行握手。主要操作包括传输发送者 A 等到传输接受者 B的 READY 信号后,A 将数据与 VALID 信号同时发送给 B,这是一种典型的握手机制。

01.png

AXI 总线分为五个通道:

 读地址通道,包含 ARVALID, ARADDR, ARREADY 信号;

 写地址通道,包含 AWVALID,AWADDR, AWREADY 信号;

 读数据通道,包含 RVALID, RDATA, RREADY, RRESP 信号;

 写数据通道,包含 WVALID, WDATA,WSTRB, WREADY 信号;

 写应答通道,包含 BVALID, BRESP, BREADY 信号;

 系统通道,包含:ACLK,ARESETN 信号;

其中 ACLK 为 axi 总线时钟,ARESETN 是 axi 总线复位信号,低电平有效;读写数据与读写地址类信号宽度都为 32bit;READY 与 VALID 是对应的通道握手信号;WSTRB 信号为 1 的bit 对应 WDATA 有效数据字节,WSTRB 宽度是 32bit/8=4bit;BRESP 与 RRESP 分别为写回应信号,读回应信号,宽度都为 2bit,‘h0 代表成功,其他为错误。

读操作顺序为主与从进行读地址通道握手并传输地址内容,然后在读数据通道握手并传输所读内容以及读取操作的回应,时钟上升沿有效。如图所示:

02.png

写操作顺序为主与从进行写地址通道握手并传输地址内容,然后在写数据通道握手并传输

所读内容,最后再写回应通道握手,并传输写回应数据,时钟上升沿有效。如图所示:

03.png

二、axi协议应用举例

在我们不擅长写 FPGA 的一些代码时我们往往要借鉴别人的代码或者使用 IP core。在这里笔者从 github 上找到一个 AXI master 的代码,地址是https://github.com/aquaxis/IPCORE/tree/master/aq_axi_vdma  。这个工程是一个自己写的VDMA,里面包含了大量可参考的代码。笔者这里主要使用了 aq_axi_master.v 这个代码用于AXI master 读写操作。借鉴别人代码有时会节省很多时间,但如果不能理解的去借鉴,出现问题了很难解决。aq_axi_master.v 代码如下,有部分修改。

/* 
 * Copyright (C)2014-2015 AQUAXIS TECHNOLOGY. 
 *  Don't remove this header.  
 * When you use this source, there is a need to inherit this header. 
 * 
 * License 
 *  For no commercial - 
 *   License:     The Open Software License 3.0 
 *   License URI: http://www.opensource.org/licenses/OSL-3.0 
 * 
 *  For commmercial - 
 *   License:     AQUAXIS License 1.0 
 *   License URI: http://www.aquaxis.com/licenses 
 * 
 * For further information please contact. 
 *  URI:    http://www.aquaxis.com/ 
 *  E-Mail: info(at)aquaxis.com 
 */ 
 
 
// 
// Company:  
// Engineer: 
//  
// Create Date: 2016/11/17 10:27:06 
// Design Name:  
// Module Name: mem_test 
// Project Name:  
// Target Devices:  
// Tool Versions:  
// Description:  
//  
// Dependencies:  
//  
// Revision: 
// Revision 0.01 - File Created 
// Additional Comments: 
//  
 
// 
module aq_axi_master( 
// Reset, Clock 
input           ARESETN, 
input           ACLK, 
 
// Master Write Address 
output[0:0]  M_AXI_AWID, 
output[31:0] M_AXI_AWADDR, 
output[7:0]  M_AXI_AWLEN,// Burst Length: 0-255 
output[2:0]  M_AXI_AWSIZE,// Burst Size: Fixed 2'b011 
output[1:0]  M_AXI_AWBURST,// Burst Type: Fixed 2'b01(Incremental Burst) 
output        M_AXI_AWLOCK,// Lock: Fixed 2'b00 
output[3:0]  M_AXI_AWCACHE,// Cache: Fiex 2'b0011 
output[2:0]  M_AXI_AWPROT,// Protect: Fixed 2'b000 
output[3:0]  M_AXI_AWQOS,// QoS: Fixed 2'b0000 
output[0:0]  M_AXI_AWUSER,// User: Fixed 32'd0 
output        M_AXI_AWVALID, 
input         M_AXI_AWREADY, 
 
// Master Write Data 
output[63:0] M_AXI_WDATA, 
output[7:0]  M_AXI_WSTRB, 
output        M_AXI_WLAST, 
output[0:0]  M_AXI_WUSER, 
output        M_AXI_WVALID, 
input         M_AXI_WREADY, 
 
// Master Write Response 
input[0:0]   M_AXI_BID, 
input[1:0]   M_AXI_BRESP, 
input[0:0]   M_AXI_BUSER, 
input         M_AXI_BVALID, 
output        M_AXI_BREADY, 
 
// Master Read Address 
output[0:0]  M_AXI_ARID, 
output[31:0] M_AXI_ARADDR, 
output[7:0]  M_AXI_ARLEN, 
output[2:0]  M_AXI_ARSIZE, 
output[1:0]  M_AXI_ARBURST, 
output[1:0]  M_AXI_ARLOCK, 
output[3:0]  M_AXI_ARCACHE, 
output[2:0]  M_AXI_ARPROT, 
output[3:0]  M_AXI_ARQOS, 
output[0:0]  M_AXI_ARUSER, 
output        M_AXI_ARVALID, 
input         M_AXI_ARREADY, 
 
// Master Read Data  
input[0:0]   M_AXI_RID, 
input[63:0]  M_AXI_RDATA, 
input[1:0]   M_AXI_RRESP, 
input         M_AXI_RLAST, 
input[0:0]   M_AXI_RUSER, 
input         M_AXI_RVALID, 
output        M_AXI_RREADY, 
 
// Local Bus 
input         MASTER_RST, 
 
input         WR_START, 
input[31:0]  WR_ADRS, 
input[31:0]  WR_LEN, 
output        WR_READY, 
output        WR_FIFO_RE, 
input         WR_FIFO_EMPTY, 
input         WR_FIFO_AEMPTY, 
input[63:0]  WR_FIFO_DATA, 
output        WR_DONE, 
 
input         RD_START, 
input[31:0]  RD_ADRS, 
input[31:0]  RD_LEN, 
output        RD_READY, 
output        RD_FIFO_WE, 
input         RD_FIFO_FULL, 
input         RD_FIFO_AFULL, 
output[63:0] RD_FIFO_DATA, 
output        RD_DONE, 
 
output[31:0] DEBUG 
); 
 
localparam S_WR_IDLE  =3'd0; 
localparam S_WA_WAIT  =3'd1; 
localparam S_WA_START =3'd2; 
localparam S_WD_WAIT  =3'd3; 
localparam S_WD_PROC  =3'd4; 
localparam S_WR_WAIT  =3'd5; 
localparam S_WR_DONE  =3'd6; 
 
reg[2:0]   wr_state; 
reg[31:0]  reg_wr_adrs; 
reg[31:0]  reg_wr_len; 
reg         reg_awvalid, reg_wvalid, reg_w_last; 
reg[7:0]   reg_w_len; 
reg[7:0]   reg_w_stb; 
reg[1:0]   reg_wr_status; 
reg[3:0]   reg_w_count, reg_r_count; 
 
reg[7:0]   rd_chkdata, wr_chkdata; 
reg[1:0]   resp; 
reg rd_first_data; 
reg rd_fifo_enable; 
reg[31:0] rd_fifo_cnt; 
assign WR_DONE =(wr_state == S_WR_DONE); 
 
 
 
assign WR_FIFO_RE         = rd_first_data |(reg_wvalid &~WR_FIFO_EMPTY & 
M_AXI_WREADY & rd_fifo_enable); 
//assign WR_FIFO_RE         = reg_wvalid & ~WR_FIFO_EMPTY & M_AXI_WREADY; 
always@(posedge ACLK ornegedge ARESETN) 
begin 
  if(!ARESETN) 
    rd_fifo_cnt <=32'd0; 
  elseif(WR_FIFO_RE) 
    rd_fifo_cnt <= rd_fifo_cnt +32'd1; 
  elseif(wr_state == S_WR_IDLE) 
    rd_fifo_cnt <=32'd0;  
end 
 
always@(posedge ACLK ornegedge ARESETN) 
begin 
  if(!ARESETN) 
    rd_fifo_enable <=1'b0; 
  elseif(wr_state == S_WR_IDLE && WR_START) 
    rd_fifo_enable <=1'b1; 
  elseif(WR_FIFO_RE &&(rd_fifo_cnt == RD_LEN[31:3]-32'd1)) 
    rd_fifo_enable <=1'b0;     
end 
// Write State 
always@(posedge ACLK ornegedge ARESETN)begin 
if(!ARESETN)begin 
      wr_state            <= S_WR_IDLE; 
      reg_wr_adrs[31:0]<=32'd0; 
      reg_wr_len[31:0]<=32'd0; 
      reg_awvalid         <=1'b0; 
      reg_wvalid          <=1'b0; 
      reg_w_last          <=1'b0; 
      reg_w_len[7:0]<=8'd0; 
      reg_w_stb[7:0]<=8'd0; 
      reg_wr_status[1:0]<=2'd0; 
      reg_w_count[3:0]<=4'd0; 
      reg_r_count[3:0]<=4'd0; 
      wr_chkdata          <=8'd0; 
      rd_chkdata <=8'd0; 
      resp <=2'd0; 
    rd_first_data <=1'b0; 
endelsebegin 
if(MASTER_RST)begin 
      wr_state <= S_WR_IDLE; 
endelsebegin 
case(wr_state) 
        S_WR_IDLE:begin 
if(WR_START)begin 
            wr_state          <= S_WA_WAIT; 
            reg_wr_adrs[31:0]<= WR_ADRS[31:0]; 
            reg_wr_len[31:0]<= WR_LEN[31:0]-32'd1; 
      rd_first_data <=1'b1; 
end 
          reg_awvalid         <=1'b0; 
          reg_wvalid          <=1'b0; 
          reg_w_last          <=1'b0; 
          reg_w_len[7:0]<=8'd0; 
          reg_w_stb[7:0]<=8'd0; 
          reg_wr_status[1:0]<=2'd0; 
end 
        S_WA_WAIT:begin 
if(!WR_FIFO_AEMPTY |(reg_wr_len[31:11]==21'd0))begin 
            wr_state          <= S_WA_START; 
end 
      rd_first_data <=1'b0; 
end 
        S_WA_START:begin 
          wr_state            <= S_WD_WAIT; 
          reg_awvalid         <=1'b1; 
          reg_wr_len[31:11]<= reg_wr_len[31:11]-21'd1; 
if(reg_wr_len[31:11]!=21'd0)begin 
            reg_w_len[7:0]<=8'hFF; 
            reg_w_last      <=1'b0; 
            reg_w_stb[7:0]<=8'hFF; 
endelsebegin 
            reg_w_len[7:0]<= reg_wr_len[10:3]; 
            reg_w_last      <=1'b1; 
            reg_w_stb[7:0]<=8'hFF; 
/* 
            case(reg_wr_len[2:0]) begin 
              case 3'd0: reg_w_stb[7:0]  <= 8'b0000_0000; 
              case 3'd1: reg_w_stb[7:0]  <= 8'b0000_0001; 
              case 3'd2: reg_w_stb[7:0]  <= 8'b0000_0011; 
              case 3'd3: reg_w_stb[7:0]  <= 8'b0000_0111; 
              case 3'd4: reg_w_stb[7:0]  <= 8'b0000_1111; 
              case 3'd5: reg_w_stb[7:0]  <= 8'b0001_1111; 
              case 3'd6: reg_w_stb[7:0]  <= 8'b0011_1111; 
              case 3'd7: reg_w_stb[7:0]  <= 8'b0111_1111; 
              default:   reg_w_stb[7:0]  <= 8'b1111_1111; 
            endcase 
*/ 
end 
end 
        S_WD_WAIT:begin 
if(M_AXI_AWREADY)begin 
            wr_state        <= S_WD_PROC; 
            reg_awvalid     <=1'b0; 
            reg_wvalid      <=1'b1; 
end 
end 
        S_WD_PROC:begin 
if(M_AXI_WREADY &~WR_FIFO_EMPTY)begin 
if(reg_w_len[7:0]==8'd0)begin 
              wr_state        <= S_WR_WAIT; 
              reg_wvalid      <=1'b0; 
              reg_w_stb[7:0]<=8'h00; 
endelsebegin 
              reg_w_len[7:0]<= reg_w_len[7:0]-8'd1; 
end 
end 
end 
        S_WR_WAIT:begin 
if(M_AXI_BVALID)begin 
            reg_wr_status[1:0]<= reg_wr_status[1:0]| M_AXI_BRESP[1:0]; 
if(reg_w_last)begin 
              wr_state          <= S_WR_DONE; 
endelsebegin 
              wr_state          <= S_WA_WAIT; 
              reg_wr_adrs[31:0]<= reg_wr_adrs[31:0]+32'd2048; 
end 
end 
end 
        S_WR_DONE:begin 
            wr_state <= S_WR_IDLE; 
end 
default:begin 
          wr_state <= S_WR_IDLE; 
end 
endcase 
/* 
      if(WR_FIFO_RE) begin 
        reg_w_count[3:0]  <= reg_w_count[3:0] + 4'd1; 
      end 
      if(RD_FIFO_WE)begin 
        reg_r_count[3:0]  <= reg_r_count[3:0] + 4'd1; 
      end 
      if(M_AXI_AWREADY & M_AXI_AWVALID) begin 
        wr_chkdata <= 8'hEE; 
      end else if(M_AXI_WSTRB[7] & M_AXI_WVALID) begin 
        wr_chkdata <= WR_FIFO_DATA[63:56]; 
      end 
      if(M_AXI_AWREADY & M_AXI_AWVALID) begin 
        rd_chkdata <= 8'hDD; 
      end else if(M_AXI_WSTRB[7] & M_AXI_WREADY) begin 
        rd_chkdata <= WR_FIFO_DATA[63:56]; 
      end 
      if(M_AXI_BVALID & M_AXI_BREADY) begin 
        resp <= M_AXI_BRESP; 
      end 
*/ 
end 
end 
end 
 
assign M_AXI_AWID         =1'b0; 
assign M_AXI_AWADDR[31:0]= reg_wr_adrs[31:0]; 
assign M_AXI_AWLEN[7:0]= reg_w_len[7:0]; 
assign M_AXI_AWSIZE[2:0]=2'b011; 
assign M_AXI_AWBURST[1:0]=2'b01; 
assign M_AXI_AWLOCK       =1'b0; 
assign M_AXI_AWCACHE[3:0]=4'b0011; 
assign M_AXI_AWPROT[2:0]=3'b000; 
assign M_AXI_AWQOS[3:0]=4'b0000; 
assign M_AXI_AWUSER[0]=1'b1; 
assign M_AXI_AWVALID      = reg_awvalid; 
 
assign M_AXI_WDATA[63:0]= WR_FIFO_DATA[63:0]; 
//  assign M_AXI_WSTRB[7:0]   = (reg_w_len[7:0] == 
8'd0)?reg_w_stb[7:0]:8'hFF; 
//  assign M_AXI_WSTRB[7:0]   = (wr_state == S_WD_PROC)?8'hFF:8'h00; 
assign M_AXI_WSTRB[7:0]=(reg_wvalid &~WR_FIFO_EMPTY)?8'hFF:8'h00; 
assign M_AXI_WLAST        =(reg_w_len[7:0]==8'd0)?1'b1:1'b0; 
assign M_AXI_WUSER        =1; 
assign M_AXI_WVALID       = reg_wvalid &~WR_FIFO_EMPTY; 
//  assign M_AXI_WVALID       = (wr_state == S_WD_PROC)?1'b1:1'b0; 
 
assign M_AXI_BREADY       = M_AXI_BVALID; 
 
assign WR_READY           =(wr_state == S_WR_IDLE)?1'b1:1'b0; 
 
//  assign WR_FIFO_RE         = (wr_state == S_WD_PROC)?M_AXI_WREADY:1'b0; 
 
localparam S_RD_IDLE  =3'd0; 
localparam S_RA_WAIT  =3'd1; 
localparam S_RA_START =3'd2; 
localparam S_RD_WAIT  =3'd3; 
localparam S_RD_PROC  =3'd4; 
localparam S_RD_DONE  =3'd5; 
 
reg[2:0]   rd_state; 
reg[31:0]  reg_rd_adrs; 
reg[31:0]  reg_rd_len; 
reg         reg_arvalid, reg_r_last; 
reg[7:0]   reg_r_len; 
assign RD_DONE =(rd_state == S_RD_DONE); 
// Read State 
always@(posedge ACLK ornegedge ARESETN)begin 
if(!ARESETN)begin 
      rd_state          <= S_RD_IDLE; 
      reg_rd_adrs[31:0]<=32'd0; 
      reg_rd_len[31:0]<=32'd0; 
      reg_arvalid       <=1'b0; 
      reg_r_len[7:0]<=8'd0; 
endelsebegin 
case(rd_state) 
        S_RD_IDLE:begin 
if(RD_START)begin 
            rd_state          <= S_RA_WAIT; 
            reg_rd_adrs[31:0]<= RD_ADRS[31:0]; 
            reg_rd_len[31:0]<= RD_LEN[31:0]-32'd1; 
end 
          reg_arvalid     <=1'b0; 
          reg_r_len[7:0]<=8'd0; 
end 
        S_RA_WAIT:begin 
if(~RD_FIFO_AFULL)begin 
            rd_state          <= S_RA_START; 
end 
end 
        S_RA_START:begin 
          rd_state          <= S_RD_WAIT; 
          reg_arvalid       <=1'b1; 
          reg_rd_len[31:11]<= reg_rd_len[31:11]-21'd1; 
if(reg_rd_len[31:11]!=21'd0)begin 
            reg_r_last      <=1'b0; 
            reg_r_len[7:0]<=8'd255; 
endelsebegin 
            reg_r_last      <=1'b1; 
            reg_r_len[7:0]<= reg_rd_len[10:3]; 
end 
end 
        S_RD_WAIT:begin 
if(M_AXI_ARREADY)begin 
            rd_state        <= S_RD_PROC; 
            reg_arvalid     <=1'b0; 
end 
end 
        S_RD_PROC:begin 
if(M_AXI_RVALID)begin 
if(M_AXI_RLAST)begin 
if(reg_r_last)begin 
                rd_state          <= S_RD_DONE; 
endelsebegin 
                rd_state          <= S_RA_WAIT; 
                reg_rd_adrs[31:0]<= reg_rd_adrs[31:0]+32'd2048; 
end 
endelsebegin 
              reg_r_len[7:0]<= reg_r_len[7:0]-8'd1; 
end 
end 
end 
    S_RD_DONE:begin 
      rd_state          <= S_RD_IDLE; 
    end 
       
  endcase 
end 
end 
 
// Master Read Address 
assign M_AXI_ARID         =1'b0; 
assign M_AXI_ARADDR[31:0]= reg_rd_adrs[31:0]; 
assign M_AXI_ARLEN[7:0]= reg_r_len[7:0]; 
assign M_AXI_ARSIZE[2:0]=3'b011; 
assign M_AXI_ARBURST[1:0]=2'b01; 
assign M_AXI_ARLOCK       =1'b0; 
assign M_AXI_ARCACHE[3:0]=4'b0011; 
assign M_AXI_ARPROT[2:0]=3'b000; 
assign M_AXI_ARQOS[3:0]=4'b0000; 
assign M_AXI_ARUSER[0]=1'b1; 
assign M_AXI_ARVALID      = reg_arvalid; 
 
assign M_AXI_RREADY       = M_AXI_RVALID &~RD_FIFO_FULL; 
 
assign RD_READY           =(rd_state == S_RD_IDLE)?1'b1:1'b0; 
assign RD_FIFO_WE         = M_AXI_RVALID; 
assign RD_FIFO_DATA[63:0]= M_AXI_RDATA[63:0]; 
 
assign DEBUG[31:0]={reg_wr_len[31:8], 
1'd0, wr_state[2:0],1'd0, rd_state[2:0]}; 
 
endmodule

三、工程下载

下面有一个关于 PL 读写 PS 端 DDR 数据的真实工程,本人已经完全验证过了,即包括了ZYNQ处理器的搭建,逻辑封装,又包括PS端SDK的测试代码。

PL 读写PS 端 DDR 数据

最新文章

最新文章