FPGA:自定义AXI-FIFO主机接口(含versal读写工程)

日常·唠嗑

最近在操作AXI接口的DDR控制器,参考了ALINX的做法,相当好用,做法很玄妙,AXI被封装成类FIFO的简易接口,不用懂AXI也可以操作(当然了解原理才知道具体含义,不会一头雾水),在这里分享给大家。

一、代码框架

代码框架很简单,总共三个module:

数据留向:

现有代码用的计数器,所以fifo只是信号名,各位如果想读写其他数据,可以同个FIFO缓冲一下,怼到mem_test接口上就好。

这样设计有个好处,代码的第 96-120 行定义的 Local Bus 接口,它的作用是屏蔽 AXI 协议的复杂性。

如果不使用这个 Local Bus 接口,你的用户逻辑代码就需要自己去处理 AXI 的时序(比如判断 AWREADY 何时变高,计算 WLAST 信号,处理 4KB 边界等)。

这个模块将 AXI Master 变成了一个简单的"命令-数据流"模式:

控制信号 (WR_START, WR_ADRS, WR_LEN 等):

用户只需要给出一个"开始写"信号、起始地址和长度,这个模块内部的状态机(wr_state)就会自动产生符合 AXI 规范的一系列时序。

它会自动把长数据传输切分成多个 AXI Burst 传输(代码 215-223 行处理了长度递减和 Burst 拆分)。

FIFO 接口 (WR_FIFO_DATA, WR_FIFO_RE 等):

对于数据传输,它通过 FIFO 接口与用户逻辑交互。

写操作时:用户逻辑只需要把数据写入一个 FIFO,这个模块会自动从 FIFO 读数据 (WR_FIFO_RE) 并发送到 AXI 总线 (M_AXI_WDATA)。

读操作时:这个模块从 AXI 总线收到数据 (M_AXI_RDATA) 后,会自动写使能 (RD_FIFO_WE) 将数据推送到用户的 FIFO 中。

总结 "Local Bus" 的作用

Local Bus 的作用是充当"用户逻辑"和"AXI 总线"之间的桥梁(适配器)。

输入端 (Local Bus): 简单的握手(Start/Done)、地址/长度配置、FIFO 数据流接口。

输出端 (AXI Master): 复杂的 AXI4 协议时序。

这样设计的好处是,你在编写其余的逻辑代码(比如 mem_test.v)时,完全不需要了解 AXI 协议的细节,只需要操作简单的 FIFO 和寄存器即可完成对 DDR 等高速存储器的读写。

二、程序设计:

三个模块:请各位尊重他人成果,保留代码作者信息及相关信息。

1、top

c 复制代码
`timescale 1ns / 1ps

module top(  
  output [0:0]	DDR4_act_n,
  output [16:0]	DDR4_adr,
  output [1:0]	DDR4_ba,
  output [0:0]	DDR4_bg,
  output [0:0]	DDR4_ck_c,
  output [0:0]	DDR4_ck_t,
  output [0:0]	DDR4_cke,
  output [0:0]	DDR4_cs_n,
  inout [7:0]	DDR4_dm_n,
  inout [63:0]	DDR4_dq,
  inout [7:0]	DDR4_dqs_c,
  inout [7:0]	DDR4_dqs_t,
  output [0:0]	DDR4_odt,
  output [0:0]	DDR4_reset_n,
  input [0:0]	sys_clk_n,
  input [0:0]	sys_clk_p
    );
	
	
(*mark_debug="true"*) wire error;	
	
wire rst_n;	
wire M_AXI_ACLK;
// Master Write Address
wire [0:0]  M_AXI_AWID;
(*mark_debug="true"*)wire [31:0] M_AXI_AWADDR;
(*mark_debug="true"*)wire [7:0]  M_AXI_AWLEN;    // Burst Length: 0-255
(*mark_debug="true"*)wire [2:0]  M_AXI_AWSIZE;   // Burst Size: Fixed 2'b011
(*mark_debug="true"*)wire [1:0]  M_AXI_AWBURST;  // Burst Type: Fixed 2'b01(Incremental Burst)
wire        M_AXI_AWLOCK;   // Lock: Fixed 2'b00
wire [3:0]  M_AXI_AWCACHE;  // Cache: Fiex 2'b0011
wire [2:0]  M_AXI_AWPROT;   // Protect: Fixed 2'b000
wire [3:0]  M_AXI_AWQOS;    // QoS: Fixed 2'b0000
wire [0:0]  M_AXI_AWUSER;   // User: Fixed 32'd0
(*mark_debug="true"*)wire        M_AXI_AWVALID;
(*mark_debug="true"*)wire        M_AXI_AWREADY;

// Master Write Data
(*mark_debug="true"*)wire [63:0] M_AXI_WDATA;
(*mark_debug="true"*)wire [7:0]  M_AXI_WSTRB;
(*mark_debug="true"*)wire        M_AXI_WLAST;
(*mark_debug="true"*)wire [0:0]  M_AXI_WUSER;
(*mark_debug="true"*)wire        M_AXI_WVALID;
(*mark_debug="true"*)wire        M_AXI_WREADY;

// Master Write Response
wire [0:0]   M_AXI_BID;
(*mark_debug="true"*)wire [1:0]   M_AXI_BRESP;
(*mark_debug="true"*)wire [0:0]   M_AXI_BUSER;
(*mark_debug="true"*)wire         M_AXI_BVALID;
(*mark_debug="true"*)wire         M_AXI_BREADY;
    
// Master Read Address
wire [0:0]  M_AXI_ARID;
(*mark_debug="true"*)wire [31:0] M_AXI_ARADDR;
(*mark_debug="true"*)wire [7:0]  M_AXI_ARLEN;
(*mark_debug="true"*)wire [2:0]  M_AXI_ARSIZE;
(*mark_debug="true"*)wire [1:0]  M_AXI_ARBURST;
wire        M_AXI_ARLOCK;
wire [3:0]  M_AXI_ARCACHE;
wire [2:0]  M_AXI_ARPROT;
wire [3:0]  M_AXI_ARQOS;
wire [0:0]  M_AXI_ARUSER;
(*mark_debug="true"*)wire        M_AXI_ARVALID;
(*mark_debug="true"*)wire        M_AXI_ARREADY;
    
// Master Read Data 
wire [0:0]   M_AXI_RID;
(*mark_debug="true"*)wire [63:0]  M_AXI_RDATA;
(*mark_debug="true"*)wire [1:0]   M_AXI_RRESP;
(*mark_debug="true"*)wire         M_AXI_RLAST;
(*mark_debug="true"*)wire [0:0]   M_AXI_RUSER;
(*mark_debug="true"*)wire         M_AXI_RVALID;
(*mark_debug="true"*)wire         M_AXI_RREADY;

(*mark_debug="true"*)wire wr_burst_data_req;
(*mark_debug="true"*)wire wr_burst_finish;
(*mark_debug="true"*)wire rd_burst_finish;
(*mark_debug="true"*)wire rd_burst_req;
(*mark_debug="true"*)wire wr_burst_req;
(*mark_debug="true"*)wire[9:0] rd_burst_len;
(*mark_debug="true"*)wire[9:0] wr_burst_len;
(*mark_debug="true"*)wire[31:0] rd_burst_addr;
(*mark_debug="true"*)wire[31:0] wr_burst_addr;
(*mark_debug="true"*)wire rd_burst_data_valid;
(*mark_debug="true"*)wire[63 : 0] rd_burst_data;
(*mark_debug="true"*)wire[63 : 0] wr_burst_data;

mem_test
#(
	.MEM_DATA_BITS(64),
	.ADDR_BITS(27)
)
mem_test_m0
(
	.rst(~rst_n),                                 
	.mem_clk(M_AXI_ACLK),                             
	.rd_burst_req(rd_burst_req),               
	.wr_burst_req(wr_burst_req),               
	.rd_burst_len(rd_burst_len),               
	.wr_burst_len(wr_burst_len),               
	.rd_burst_addr(rd_burst_addr),        
	.wr_burst_addr(wr_burst_addr),        
	.rd_burst_data_valid(rd_burst_data_valid),  
	.wr_burst_data_req(wr_burst_data_req),  
	.rd_burst_data(rd_burst_data),  
	.wr_burst_data(wr_burst_data),    
	.rd_burst_finish(rd_burst_finish),   
	.wr_burst_finish(wr_burst_finish),

	.error(error)
); 
aq_axi_master u_aq_axi_master
(
	.ARESETN(rst_n),
	.ACLK(M_AXI_ACLK),
	
	.M_AXI_AWID(M_AXI_AWID),
	.M_AXI_AWADDR(M_AXI_AWADDR),     
	.M_AXI_AWLEN(M_AXI_AWLEN),
	.M_AXI_AWSIZE(M_AXI_AWSIZE),
	.M_AXI_AWBURST(M_AXI_AWBURST),
	.M_AXI_AWLOCK(M_AXI_AWLOCK),
	.M_AXI_AWCACHE(M_AXI_AWCACHE),
	.M_AXI_AWPROT(M_AXI_AWPROT),
	.M_AXI_AWQOS(M_AXI_AWQOS),
	.M_AXI_AWUSER(M_AXI_AWUSER),
	.M_AXI_AWVALID(M_AXI_AWVALID),
	.M_AXI_AWREADY(M_AXI_AWREADY),
	
	.M_AXI_WDATA(M_AXI_WDATA),
	.M_AXI_WSTRB(M_AXI_WSTRB),
	.M_AXI_WLAST(M_AXI_WLAST),
	.M_AXI_WUSER(M_AXI_WUSER),
	.M_AXI_WVALID(M_AXI_WVALID),
	.M_AXI_WREADY(M_AXI_WREADY),
	
	.M_AXI_BID(M_AXI_BID),
	.M_AXI_BRESP(M_AXI_BRESP),
	.M_AXI_BUSER(M_AXI_BUSER),
	.M_AXI_BVALID(M_AXI_BVALID),
	.M_AXI_BREADY(M_AXI_BREADY),
	
	.M_AXI_ARID(M_AXI_ARID),
	.M_AXI_ARADDR(M_AXI_ARADDR),
	.M_AXI_ARLEN(M_AXI_ARLEN),
	.M_AXI_ARSIZE(M_AXI_ARSIZE),
	.M_AXI_ARBURST(M_AXI_ARBURST),
	.M_AXI_ARLOCK(M_AXI_ARLOCK),
	.M_AXI_ARCACHE(M_AXI_ARCACHE),
	.M_AXI_ARPROT(M_AXI_ARPROT),
	.M_AXI_ARQOS(M_AXI_ARQOS),
	.M_AXI_ARUSER(M_AXI_ARUSER),
	.M_AXI_ARVALID(M_AXI_ARVALID),
	.M_AXI_ARREADY(M_AXI_ARREADY),
	
	.M_AXI_RID(M_AXI_RID),
	.M_AXI_RDATA(M_AXI_RDATA),
	.M_AXI_RRESP(M_AXI_RRESP),
	.M_AXI_RLAST(M_AXI_RLAST),
	.M_AXI_RUSER(M_AXI_RUSER),
	.M_AXI_RVALID(M_AXI_RVALID),
	.M_AXI_RREADY(M_AXI_RREADY),
	
	.MASTER_RST(~rst_n),
	
	.WR_START(wr_burst_req),
	.WR_ADRS({wr_burst_addr[28:0],3'd0}),
	.WR_LEN({wr_burst_len,3'd0}), 
	.WR_READY(),
	.WR_FIFO_RE(wr_burst_data_req),
	.WR_FIFO_EMPTY(1'b0),
	.WR_FIFO_AEMPTY(1'b0),
	.WR_FIFO_DATA(wr_burst_data),
	.WR_DONE(wr_burst_finish),
	
	.RD_START(rd_burst_req),
	.RD_ADRS({rd_burst_addr[28:0],3'd0}),
	.RD_LEN({rd_burst_len,3'd0}), 
	.RD_READY(),
	.RD_FIFO_WE(rd_burst_data_valid),
	.RD_FIFO_FULL(1'b0),
	.RD_FIFO_AFULL(1'b0),
	.RD_FIFO_DATA(rd_burst_data),
	.RD_DONE(rd_burst_finish),
	.DEBUG()                                         
);

	
design_1_wrapper ps_block
(
    	
	.S_AXI_araddr       (M_AXI_ARADDR          ),
	.S_AXI_arburst      (M_AXI_ARBURST         ),
	.S_AXI_arcache      (M_AXI_ARCACHE         ),
	.S_AXI_arid         (M_AXI_ARID            ),
	.S_AXI_arlen        (M_AXI_ARLEN           ),
	.S_AXI_arlock       (M_AXI_ARLOCK          ),
	.S_AXI_arprot       (M_AXI_ARPROT          ),
	.S_AXI_arqos        (M_AXI_ARQOS           ),
	.S_AXI_arready      (M_AXI_ARREADY         ),
	.S_AXI_arsize       (M_AXI_ARSIZE          ),
	.S_AXI_arvalid      (M_AXI_ARVALID         ),
	.S_AXI_rdata        (M_AXI_RDATA           ),
	.S_AXI_rid          (M_AXI_RID             ),
	.S_AXI_rlast        (M_AXI_RLAST           ),
	.S_AXI_rready       (M_AXI_RREADY          ),
	.S_AXI_rresp        (M_AXI_RRESP           ),
	.S_AXI_rvalid       (M_AXI_RVALID          ),		
	.S_AXI_awaddr       (M_AXI_AWADDR          ),
	.S_AXI_awburst      (M_AXI_AWBURST         ),
	.S_AXI_awcache      (M_AXI_AWCACHE         ),
	.S_AXI_awid         (M_AXI_AWID            ),
	.S_AXI_awlen        (M_AXI_AWLEN           ),
	.S_AXI_awlock       (M_AXI_AWLOCK          ),
	.S_AXI_awprot       (M_AXI_AWPROT          ),
	.S_AXI_awqos        (M_AXI_AWQOS           ),
	.S_AXI_awready      (M_AXI_AWREADY         ),
	.S_AXI_awsize       (M_AXI_AWSIZE          ),
	.S_AXI_awvalid      (M_AXI_AWVALID         ),
	.S_AXI_bid          (M_AXI_BID             ),
	.S_AXI_bready       (M_AXI_BREADY          ),
	.S_AXI_bresp        (M_AXI_BRESP           ),
	.S_AXI_bvalid       (M_AXI_BVALID          ),
	.S_AXI_wdata        (M_AXI_WDATA           ),
	.S_AXI_wlast        (M_AXI_WLAST           ),
	.S_AXI_wready       (M_AXI_WREADY          ),
	.S_AXI_wstrb        (M_AXI_WSTRB           ),
	.S_AXI_wvalid       (M_AXI_WVALID          ),
	
	.DDR4_act_n         (DDR4_act_n    ),
	.DDR4_adr           (DDR4_adr      ),
	.DDR4_ba            (DDR4_ba       ),
	.DDR4_bg            (DDR4_bg       ),
	.DDR4_ck_c          (DDR4_ck_c     ),
	.DDR4_ck_t          (DDR4_ck_t     ),
	.DDR4_cke           (DDR4_cke      ),
	.DDR4_cs_n          (DDR4_cs_n     ),
	.DDR4_dm_n          (DDR4_dm_n     ),
	.DDR4_dq            (DDR4_dq       ),
	.DDR4_dqs_c         (DDR4_dqs_c    ),
	.DDR4_dqs_t         (DDR4_dqs_t    ),
	.DDR4_odt           (DDR4_odt      ),
	.DDR4_reset_n       (DDR4_reset_n  ),
	.sys_clk_p(sys_clk_p),
	.sys_clk_n(sys_clk_n),
	.axi_resetn(rst_n),
	.axi_clk(M_AXI_ACLK)
);
endmodule

2、mem_test

c 复制代码
//////////////////////////////////////////////////////////////////////////////////
// Company: ALINX黑金
// 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 mem_test
#(
	parameter MEM_DATA_BITS = 64,
	parameter ADDR_BITS = 32
)
(
	input rst,                                 /*复位*/
	input mem_clk,                               /*接口时钟*/
	output reg rd_burst_req,                          /*读请求*/
	output reg wr_burst_req,                          /*写请求*/
	output reg[9:0] rd_burst_len,                     /*读数据长度*/
	output reg[9:0] wr_burst_len,                     /*写数据长度*/
	output reg[ADDR_BITS - 1:0] rd_burst_addr,        /*读首地址*/
	output reg[ADDR_BITS - 1:0] wr_burst_addr,        /*写首地址*/
	input rd_burst_data_valid,                  /*读出数据有效*/
	input wr_burst_data_req,                    /*写数据信号*/
	input[MEM_DATA_BITS - 1:0] rd_burst_data,   /*读出的数据*/
	output[MEM_DATA_BITS - 1:0] wr_burst_data,    /*写入的数据*/
	input rd_burst_finish,                      /*读完成*/
	input wr_burst_finish,                      /*写完成*/

	output reg error
);
localparam IDLE = 3'd0;
localparam MEM_READ = 3'd1;
localparam MEM_WRITE  = 3'd2;
localparam BURST_LEN = 128;

reg[2:0] state;
reg[7:0] wr_cnt;
reg[MEM_DATA_BITS - 1:0] wr_burst_data_reg;
assign wr_burst_data = wr_burst_data_reg;
reg[7:0] rd_cnt;
reg[31:0] write_read_len;
//assign error = (state == MEM_READ) && rd_burst_data_valid && (rd_burst_data != {(MEM_DATA_BITS/8){rd_cnt}});

always@(posedge mem_clk or posedge rst)
begin
	if(rst)
		error <= 1'b0;
	else if(state == MEM_READ && rd_burst_data_valid && rd_burst_data != {(MEM_DATA_BITS/8){rd_cnt}})
		error <= 1'b1;
end
always@(posedge mem_clk or posedge rst)
begin
	if(rst)
	begin
		wr_burst_data_reg <= {MEM_DATA_BITS{1'b0}};
		wr_cnt <= 8'd0;
	end
	else if(state == MEM_WRITE)
	begin
		if(wr_burst_data_req)
			begin
				wr_burst_data_reg <= {(MEM_DATA_BITS/8){wr_cnt}};
				wr_cnt <= wr_cnt + 8'd1;
			end
		else if(wr_burst_finish)
			wr_cnt <= 8'd0;
	end
end

always@(posedge mem_clk or posedge rst)
begin
	if(rst)
	begin
		rd_cnt <= 8'd0;
	end
	else if(state == MEM_READ)
	begin
		if(rd_burst_data_valid)
			begin
				rd_cnt <= rd_cnt + 8'd1;
			end
		else if(rd_burst_finish)
			rd_cnt <= 8'd0;
	end
	else
		rd_cnt <= 8'd0;
end

always@(posedge mem_clk or posedge rst)
begin
	if(rst)
	begin
		state <= IDLE;
		wr_burst_req <= 1'b0;
		rd_burst_req <= 1'b0;
		rd_burst_len <= BURST_LEN;
		wr_burst_len <= BURST_LEN;
		rd_burst_addr <= 0;
		wr_burst_addr <= 0;
		write_read_len <= 32'd0;
	end
	else
	begin
		case(state)
			IDLE:
			begin
				state <= MEM_WRITE;
				wr_burst_req <= 1'b1;
				wr_burst_len <= BURST_LEN;
				wr_burst_addr <='h2000000;
				write_read_len <= 32'd0;
			end
			MEM_WRITE:
			begin
				if(wr_burst_finish)
				begin
					state <= MEM_READ;
					wr_burst_req <= 1'b0;
					rd_burst_req <= 1'b1;
					rd_burst_len <= BURST_LEN;
					rd_burst_addr <= wr_burst_addr;
					write_read_len <= write_read_len + BURST_LEN;
				end
			end
			MEM_READ:
			begin
				if(rd_burst_finish)
				begin
				    if(write_read_len == 32'h2000000)
				    begin
						rd_burst_req <= 1'b0;
						state <= IDLE;
				    end
				    else
				    begin
						state <= MEM_WRITE;
						wr_burst_req <= 1'b1;
						wr_burst_len <= BURST_LEN;
						rd_burst_req <= 1'b0;
						wr_burst_addr <= wr_burst_addr + BURST_LEN;
					end
				end
			end
			default:
				state <= IDLE;
		endcase
	end
end

endmodule

3、aq_axi_master

c 复制代码
/*
 * 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: ALINX榛戦噾
// 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 or negedge ARESETN)
begin
	if(!ARESETN)
		rd_fifo_cnt <= 32'd0;
	else if(WR_FIFO_RE)
		rd_fifo_cnt <= rd_fifo_cnt + 32'd1;
	else if(wr_state == S_WR_IDLE)
		rd_fifo_cnt <= 32'd0;	
end

always @(posedge ACLK or negedge ARESETN)
begin
	if(!ARESETN)
		rd_fifo_enable <= 1'b0;
	else if(wr_state == S_WR_IDLE && WR_START)
		rd_fifo_enable <= 1'b1;
	else if(WR_FIFO_RE && (rd_fifo_cnt == RD_LEN[31:3] - 32'd1) )
		rd_fifo_enable <= 1'b0;		
end
  // Write State
  always @(posedge ACLK or negedge 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;
  end else begin
    if(MASTER_RST) begin
      wr_state <= S_WR_IDLE;
    end else begin
      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;
          end else begin
            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;
            end else begin
              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;
            end else begin
              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 or negedge 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;
    end else begin
      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;
          end else begin
            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;
              end else begin
                rd_state          <= S_RA_WAIT;
                reg_rd_adrs[31:0] <= reg_rd_adrs[31:0] + 32'd2048;
              end
            end else begin
              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

三、工程下载

通过网盘分享的文件:02_pl_rw_ddr

链接: https://pan.baidu.com/s/1brcFxnT4RLFIq6HwDRPfdw?pwd=jwmf 提取码: jwmf

--来自百度网盘超级会员v8的分享

相关推荐
仲南音17 小时前
Xilinx FPGA——时钟IP核MMCM、PLL
fpga开发
I'm a winner1 天前
基于Xilinx FPGA的LVDS高速串行通信系统(三)--数据训练及握手机制【文末源码】
fpga开发
upper20201 天前
coremark移植坑之-nostartfiles -nostdlib参数
学习·fpga开发·coremark移植·riscv设计
Eloudy2 天前
Vivado 纯命令行构建 FPGA 项目教程
fpga开发
Rambo.xia2 天前
AXI跨时钟域与数据一致性——CDC FIFO深度不够、写后读冒险、多Master仲裁、乱序返回,出了Bug根本查不出来
fpga开发·bug
upper20202 天前
FPGA部署卷积神经网络识别MINST图片
fpga开发·卷积神经网络·minst·minst识别·部署神经网络·神经网络识别图片
你是我的解忧王子2 天前
嵌入式 ADC采样 原始数据分析上层(平均值 波动率 正态分布 线性补偿 系统误差)
fpga开发·数据挖掘·数据分析
9527华安3 天前
FPGA纯verilog代码ISP图像处理培训教程,基于IMX214,提供工程源码+视频教程+FPGA开发板
图像处理·fpga开发·isp·imx214·mipi
千寻xun3 天前
二、实战篇-NVME SSD控制之ZYNQ实现(六)-读写NVME SSD硬盘数据
fpga开发·nvme·nvme ssd
千寻xun3 天前
二、实战篇-NVME SSD控制之ZYNQ实现(三)
fpga开发·nvme ssd·nvme协议