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的分享

相关推荐
可编程芯片开发15 小时前
基于FPGA的数字示波器设计和硬件调试,将正弦波形通过HDMI接口在屏幕上动态显示
fpga开发·hdmi·数字示波器·正弦波形
风_峰1 天前
AMD FPGA产品系列讲解
fpga开发
碎碎思1 天前
FINN:FPGA AI 推理新范式 —— 定制化、高性能、量化神经网络编译器框架
人工智能·深度学习·神经网络·机器学习·fpga开发
ShiMetaPi1 天前
GM-3568JHF丨ARM+FPGA异构开发板系列教程:外设教程 05 蓝牙
arm开发·fpga开发·fpga·rk3568
无情的8862 天前
硬件中的端接设计
fpga开发·硬件工程
快乐的划水a2 天前
存储器介绍(2)
fpga开发
leixj0252 天前
MultiBoot中的两个WatchDog时间设置及跳转调试
fpga开发
博览鸿蒙2 天前
FPGA 经典面试题目及答案汇总
fpga开发·面试·职场和发展
步达硬件3 天前
【FPGA】DP、HDMI、USB4、GPMI、eDP、LVDS等音视频协议及性能对比
fpga开发