赞
踩
AXI4 相对复杂,但 SOC 开发者必须掌握。AXI 协议的具体内容可参考 Xilinx UG761 AXI Reference Guide。
在这里我们简单了解一下。
AXI4 所采用的是一种 READY,VALID 握手通信机制,即主从模块进行数据通信前,先根
据操作对各所用到的数据、地址通道进行握手。主要操作包括传输发送者 A 等到传输接受者 B
的 READY 信号后,A 将数据与 VALID 信号同时发送给 B,这是一种典型的握手机制。
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 代表成功,其他为错误。
读操作顺序为主与从进行读地址通道握手并传输地址内容,然后在读数据通道握手并传输
所读内容以及读取操作的回应,时钟上升沿有效。如图所示:
写操作顺序为主与从进行写地址通道握手并传输地址内容,然后在写数据通道握手并传输
所读内容,最后再写回应通道握手,并传输写回应数据,时钟上升沿有效。如图所示:
在我们不擅长写 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的测试代码。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。