当前位置:   article > 正文

DDR3(MIG核配置&官方demo&FPGA代码实现及仿真)_ddr3的fpga设计

ddr3的fpga设计

  由于直接对 DDR3 进行控制很复杂,因此一般使用 MIG IP 来实现,同时为了更简单地使用 MIG IP,我们采用 AXI4 总线协议进行控制。下面首先介绍 MIG IP 的配置,然后看看官方 demo (里面包含一个仿真要用到的 DDR3 模型)及其仿真结果,最后进行我们自己的控制代码实现。

MIG IP 生成

  在 IP Catalog 里搜索 MIG,如下

在这里插入图片描述

第一步里,勾选 AXI4 Interface 选项,使用 AXI4 接口

在这里插入图片描述

下一步是选择 FPGA 型号,读者自行选择自己的 FPGA 型号;下一步是选择控制器类型,我们直接选择 DDR3 SDRAM;这几步很简单,就不放图了。再下一步,Controller Options,这里要重点介绍下

在这里插入图片描述

首先配置 DDR3 的工作时钟,这个时钟也就是 DDR3_CLK_P/N,下一个 PHY to Controller Clock Ratio 是 FPGA 与 MIG 间通信使用的时钟(ui_clk)与 DDR3 时钟的比值,笔者使用 200MHz,因此这里设置为 2:1;Memory Type 需读者按照自己的 DDR 型号进行选择;Data Width 是 DDR3 用到的数据位宽,比如笔者用到 4 片 DDR,每片位宽 16bit,菊花链连接,因此这里需要设为 64。

  下一步,这里的 Data Width 是 FPGA 与 MIG 通信用到的数据位宽,为方便起见,我使用了与 DDR 数据同位宽;下一个是设置读写优先级的,TDM 是读写同级,也可修改为读/写优先、循环读写等。

在这里插入图片描述

  下一步,Input Clock Period 是设置 sys_clk_i 的频率,MIG IP 会使用这个时钟生成 DDR3_CK 信号以及供用户使用的 ui_clk,所有关于 MIG 的用户操作均应在 ui_clk 下进行;最下面的 Memory Address Mapping Selection 是 MIG 与 DDR 进行读写通信时的地址写入顺序。

在这里插入图片描述

下一步,System Clock 设为 No Buffer,因为我们用到的 sys_clk_i 是从 FPGA 内部提供的,如果是从外部引脚提供,则需要修改这里(有单引脚时钟,也有差分时钟信号);参考时钟直接使用 sys_clk_i;System Reset Polarity 设置 sys_rst 的极性,这里我设为低电平有效。

在这里插入图片描述

下一步是设置引脚阻抗;再下一步,勾选 Fixed Pin Out;再下一步,这里配置 DDR3 的引脚,可以手动配置也可以 read XDC 来配置,然后 Validate 一下,如果通过了,就可以下一步了(不 validate 那个 next 无法点击)。

在这里插入图片描述

  下一步,由于这三个信号我均做为 FPGA 内部信号,因此都 No connect

在这里插入图片描述

再之后就是 summary 啥的,一路 next 到底就行。

  • 注意事项

  在使用 MIG IP 时,mmcm_locked 信号指示是否稳定给出 ui_clk,为高表示给出了稳定的 ui_clk;init_calib_complete 信号表示会否完成 DDR3 的初始化,为高表示完成初始化,在这之后才可以进行 DDR3 的读写。

  注意 MIG IP 的 app_sr_req、app_ref_req、app_zq_req 要置零,否则无法完成 DDR 初始化,init_calib_complete 将无法拉高。

  此外,MIG IP 有三个 reset 信号,分别是 sys_rst、ui_clk_sync_rst 和 aresetn 。ui_clk_sync_rst 是同步于 ui_clk 的 sys_rst,高电平有效;但 MIG 并不会因为 sys_rst 而 reset,而是通过 aresetn 进行 reset 的,且 aresetn 应当同步于 ui_clk。因此应当如下书写三者的关系

always @(posedge ui_clk) begin
	aresetn				<= ~ui_clk_sync_rst;
end
  • 1
  • 2
  • 3

官方 demo

  在生成的 MIG IP 上右击,Open IP Example Design,会新建一个名为 mig_7series_0_ex.xpr 的工程,打开工程,可以看到一个顶层文件 example_top.v 和一个仿真文件 sim_tb_top.v

在这里插入图片描述

可以直接运行仿真,结果如下

在这里插入图片描述

在工程文件夹下, ./imports 文件夹中,有两个文件,ddr3_model.sv 和 ddr3_model_parameters.vh,前者是 ddr3 的模型,引用了后边这个 parameter 文件,parameter 文件里是关于 ddr3 模型的参数配置,在 ddr3_model 中修改 `define 值,可以配置 ddr3_model 的各类参数。后面仿真我们自己写的控制代码就要用到这个 DDR3 模型文件。

自己的FPGA实现

  MIG IP 用到 AXI 总线协议,因此建议读者首先对该总线协议进行初步了解,具体可见我之前的文章。这里也推荐一个知乎大佬写的 AXI 协议介绍,链接在此

  其余不多说,代码如下,该模块自动完成从 W_FIFO 向 DDR 的写入,以及从 DDR 读数据到 R_FIFO,用户只需要读写两个 FIFO 即可

/* 
 * file			: DDR3_top.v
 * author		: 今朝无言
 * Lab			: WHU-EIS-LMSWE
 * date			: 2023-04-21
 * version		: v1.0
 * description	: DDR3控制模块
 */
module DDR3_top(
input				clk_200M,
input				rst_n,
output				rst_busy,

//--------------------ddr3------------------------
inout		[63:0]	ddr3_dq,				//DQ,4片 x16 DDR的数据线

output		[14:0]	ddr3_addr,				//Address
output		[2:0]	ddr3_ba,				//Bank Address

inout		[7:0]	ddr3_dqs_p,				//DQ Select,0,2,4,6分别为四片DDR的DQSL(Lower Byte),
inout		[7:0]	ddr3_dqs_n,				//1,3,5,7分别为四片DDR的DQSU(Upper Byte)
//Output with read data. Edge-aligned with read data.
//Input with write data. Center-aligned to write data.

output		[0:0]	ddr3_ck_p,				//4片DR3共用ck、cke、odt、cs信号,故[0:0]
output		[0:0]	ddr3_ck_n,
//differential clock inputs. All control and address input signals are sampled 
//on the crossing of the positive edge of CK and the negative edge of CK#

output		[7:0]	ddr3_dm,				//Input Data Mask,0,2,4,6为DML,1,3,5,7为DMU
output		[0:0]	ddr3_cke,				//Clock Enable
output		[0:0]	ddr3_cs_n,				//Chip Select
output				ddr3_ras_n,				//Row Address Enable
output				ddr3_cas_n,				//Column Address Enable
output				ddr3_we_n,				//Write Enable
output		[0:0]	ddr3_odt,				//On-die termination enable
output				ddr3_reset_n,

//-------------FPGA FIFO Control-------------------
input				wr_en,					//高电平有效
input		[63:0]	wrdat,
output				full,					//fifo_w full

input				rd_en,					//高电平有效
output		[63:0]	rddat,
output				empty					//fifo_r empty
);
//本方案采用4片 MT41K256M16TW-107 芯片,每片 256M * 16bit = 512MB 容量
//8个Bank,故 BA 为 3 位;行地址用到全部的 15 根地址线,列地址用到 log2(256M/2^15/2^3)=10 根地址线

parameter	AXI_ID		= 4'h00;	//读写事务ID
parameter	DATA_NUM	= 32;		//一次突发传输的数据个数,1~256

localparam	AXI_LEN		= DATA_NUM - 1'b1;
localparam	FIFO_LEN	= 1024;		//w_fifo、r_fifo的长度

//----------------------------------state define-----------------------------------------
localparam	S_IDLE		= 8'h01;	//等待初始化
localparam	S_ARB		= 8'h02;	//判决接下来进行WR还是RD
localparam	S_WR_ADDR	= 8'h04;	//写地址
localparam	S_WR_DATA	= 8'h08;	//写数据
localparam	S_WR_RESP	= 8'h10;	//写回复
localparam	S_RD_ADDR	= 8'h20;	//读地址
localparam	S_RD_DATA	= 8'h40;	//读数据
localparam	S_RD_RESP	= 8'h80;	//读回复

//---------------------------------------------------------------------------------------
reg		[7:0]	state		= S_IDLE;
reg		[7:0]	next_state	= S_IDLE;
//尽管 AXI 的读、写可以独立,然而 DDR 读写是分时复用,因此这里的读写也必须分时进行

//FIFO
wire			w_fifo_full;
wire			w_fifo_empty;
wire			r_fifo_full;
wire			r_fifo_empty;

reg				w_fifo_rden		= 1'b0;
reg				r_fifo_wren		= 1'b0;

wire			fifo_rst;

wire	[9:0]	w_fifo_rddat_cnt;
wire	[9:0]	r_fifo_wrdat_cnt;

wire			w_fifo_wr_rst_busy;
wire			w_fifo_rd_rst_busy;
wire			r_fifo_wr_rst_busy;
wire			r_fifo_rd_rst_busy;

wire			w_fifo_rst_busy;
wire			r_fifo_rst_busy;

//Application interface ports
wire			ui_clk;					//MIG输出的时钟,一切关于mig的操作均应在此时钟域下进行
wire			ui_clk_sync_rst;		//mig输出的rst信号,同步于ui_clk,高电平有效
wire			mmcm_locked;
reg				aresetn;
wire			init_calib_complete;	//指示MIG是否完成初始化

wire			app_sr_req;
wire			app_ref_req;
wire			app_zq_req;
wire			app_sr_active;
wire			app_ref_ack;
wire			app_zq_ack;

//Slave Interface Write Address Ports
wire	[3:0]	s_axi_awid;
reg		[30:0]	s_axi_awaddr;			//4*512MB=2GB,对应 2^31 Byte,故 AXI 地址线位宽为 31
wire	[7:0]	s_axi_awlen;
wire	[2:0]	s_axi_awsize;
wire	[1:0]	s_axi_awburst;
wire	[0:0]	s_axi_awlock;
wire	[3:0]	s_axi_awcache;
wire	[2:0]	s_axi_awprot;
wire	[3:0]	s_axi_awqos;
reg				s_axi_awvalid;
wire			s_axi_awready;

//Slave Interface Write Data Ports
wire	[63:0]	s_axi_wdata;
wire	[7:0]	s_axi_wstrb;
reg				s_axi_wlast;
reg				s_axi_wvalid;
wire			s_axi_wready;

//Slave Interface Write Response Ports
wire	[3:0]	s_axi_bid;
wire	[1:0]	s_axi_bresp;
wire			s_axi_bvalid;
reg				s_axi_bready;

//Slave Interface Read Address Ports
wire	[3:0]	s_axi_arid;
reg		[30:0]	s_axi_araddr;
wire	[7:0]	s_axi_arlen;
wire	[2:0]	s_axi_arsize;
wire	[1:0]	s_axi_arburst;
wire	[0:0]	s_axi_arlock;
wire	[3:0]	s_axi_arcache;
wire	[2:0]	s_axi_arprot;
wire	[3:0]	s_axi_arqos;
reg				s_axi_arvalid;
wire			s_axi_arready;

//Slave Interface Read Data Ports
wire	[3:0]	s_axi_rid;
wire	[63:0]	s_axi_rdata;
wire	[1:0]	s_axi_rresp;
wire			s_axi_rlast;
wire			s_axi_rvalid;
reg				s_axi_rready;

//ddr3读写请求
reg				wr_ddr3_req;
reg				rd_ddr3_req;

//突发读写的已传输数据计数
reg		[8:0]	data_cnt;

//------------------------------------write FIFO------------------------------------------
//异步FIFO    采用 First Word Fall Through 模式    1024*64
fifo_generator_DDR_W fifo_w(
	.rst			(fifo_rst),
	.wr_clk			(clk_200M),
	.rd_clk			(ui_clk),
	.din			(wrdat),
	.wr_en			(wr_en),
	.rd_en			(w_fifo_rden),
	.dout			(s_axi_wdata),
	.full			(w_fifo_full),
	.empty			(w_fifo_empty),
	.rd_data_count	(w_fifo_rddat_cnt),		//仿真显示,w_fifo_rddat_cnt 和 r_fifo_wrdat_cnt 计数不可信,...IP 在做什么!!!
	.wr_data_count	(),
	.wr_rst_busy	(w_fifo_wr_rst_busy),
	.rd_rst_busy	(w_fifo_rd_rst_busy)
);

//------------------------------------read FIFO-------------------------------------------
//异步FIFO
fifo_generator_DDR_R fifo_r(
	.rst			(fifo_rst),
	.wr_clk			(ui_clk),
	.rd_clk			(clk_200M),
	.din			(s_axi_rdata),
	.wr_en			(r_fifo_wren),
	.rd_en			(rd_en),
	.dout			(rddat),
	.full			(r_fifo_full),
	.empty			(r_fifo_empty),
	.rd_data_count	(),
	.wr_data_count	(r_fifo_wrdat_cnt),
	.wr_rst_busy	(r_fifo_wr_rst_busy),
	.rd_rst_busy	(r_fifo_rd_rst_busy)
);

//----------------------------------MIG IP, AXI4-----------------------------------------
mig_7series_0 u_mig_7series_0 (
	// Memory interface ports
	.ddr3_addr				(ddr3_addr),			// output [14:0]	ddr3_addr
	.ddr3_ba				(ddr3_ba),				// output [2:0]		ddr3_ba
	.ddr3_cas_n				(ddr3_cas_n),			// output			ddr3_cas_n
	.ddr3_ck_n				(ddr3_ck_n),			// output [0:0]		ddr3_ck_n
	.ddr3_ck_p				(ddr3_ck_p),			// output [0:0]		ddr3_ck_p
	.ddr3_cke				(ddr3_cke),				// output [0:0]		ddr3_cke
	.ddr3_ras_n				(ddr3_ras_n),			// output			ddr3_ras_n
	.ddr3_reset_n			(ddr3_reset_n),			// output			ddr3_reset_n
	.ddr3_we_n				(ddr3_we_n),			// output			ddr3_we_n
	.ddr3_dq				(ddr3_dq),				// inout [63:0]		ddr3_dq
	.ddr3_dqs_n				(ddr3_dqs_n),			// inout [7:0]		ddr3_dqs_n
	.ddr3_dqs_p				(ddr3_dqs_p),			// inout [7:0]		ddr3_dqs_p
	.ddr3_cs_n				(ddr3_cs_n), 			// output [0:0]		ddr3_cs_n
	.ddr3_dm				(ddr3_dm),  			// output [7:0]		ddr3_dm
	.ddr3_odt				(ddr3_odt),  			// output [0:0]		ddr3_odt

	// Application interface ports
	.ui_clk					(ui_clk),				// output			ui_clk
	.ui_clk_sync_rst		(ui_clk_sync_rst),		// output			ui_clk_sync_rst
	.mmcm_locked			(mmcm_locked),			// output			mmcm_locked
	.aresetn				(aresetn),				// input			aresetn
	.init_calib_complete	(init_calib_complete),	// output			init_calib_complete
	.app_sr_req				(app_sr_req),			// input			app_sr_req
	.app_ref_req			(app_ref_req),			// input			app_ref_req
	.app_zq_req				(app_zq_req),			// input			app_zq_req
	.app_sr_active			(app_sr_active),		// output			app_sr_active
	.app_ref_ack			(app_ref_ack),			// output			app_ref_ack
	.app_zq_ack				(app_zq_ack),			// output			app_zq_ack

	// Slave Interface Write Address Ports
	.s_axi_awid				(s_axi_awid),			// input [3:0]		s_axi_awid
	.s_axi_awaddr			(s_axi_awaddr),			// input [30:0]		s_axi_awaddr
	.s_axi_awlen			(s_axi_awlen),			// input [7:0]		s_axi_awlen
	.s_axi_awsize			(s_axi_awsize),			// input [2:0]		s_axi_awsize
	.s_axi_awburst			(s_axi_awburst),		// input [1:0]		s_axi_awburst
	.s_axi_awlock			(s_axi_awlock),			// input [0:0]		s_axi_awlock
	.s_axi_awcache			(s_axi_awcache),		// input [3:0]		s_axi_awcache
	.s_axi_awprot			(s_axi_awprot),			// input [2:0]		s_axi_awprot
	.s_axi_awqos			(s_axi_awqos),			// input [3:0]		s_axi_awqos
	.s_axi_awvalid			(s_axi_awvalid),		// input			s_axi_awvalid
	.s_axi_awready			(s_axi_awready),		// output			s_axi_awready

	// Slave Interface Write Data Ports
	.s_axi_wdata			(s_axi_wdata),			// input [63:0]		s_axi_wdata
	.s_axi_wstrb			(s_axi_wstrb),			// input [7:0]		s_axi_wstrb
	.s_axi_wlast			(s_axi_wlast),			// input			s_axi_wlast
	.s_axi_wvalid			(s_axi_wvalid),			// input			s_axi_wvalid
	.s_axi_wready			(s_axi_wready),			// output			s_axi_wready

	// Slave Interface Write Response Ports
	.s_axi_bid				(s_axi_bid),			// output [3:0]		s_axi_bid
	.s_axi_bresp			(s_axi_bresp),			// output [1:0]		s_axi_bresp
	.s_axi_bvalid			(s_axi_bvalid),			// output			s_axi_bvalid
	.s_axi_bready			(s_axi_bready),			// input			s_axi_bready

	// Slave Interface Read Address Ports
	.s_axi_arid				(s_axi_arid),			// input [3:0]		s_axi_arid
	.s_axi_araddr			(s_axi_araddr),			// input [30:0]		s_axi_araddr
	.s_axi_arlen			(s_axi_arlen),			// input [7:0]		s_axi_arlen
	.s_axi_arsize			(s_axi_arsize),			// input [2:0]		s_axi_arsize
	.s_axi_arburst			(s_axi_arburst),		// input [1:0]		s_axi_arburst
	.s_axi_arlock			(s_axi_arlock),			// input [0:0]		s_axi_arlock
	.s_axi_arcache			(s_axi_arcache),		// input [3:0]		s_axi_arcache
	.s_axi_arprot			(s_axi_arprot),			// input [2:0]		s_axi_arprot
	.s_axi_arqos			(s_axi_arqos),			// input [3:0]		s_axi_arqos
	.s_axi_arvalid			(s_axi_arvalid),		// input			s_axi_arvalid
	.s_axi_arready			(s_axi_arready),		// output			s_axi_arready

	// Slave Interface Read Data Ports
	.s_axi_rid				(s_axi_rid),			// output [3:0]		s_axi_rid
	.s_axi_rdata			(s_axi_rdata),			// output [63:0]	s_axi_rdata
	.s_axi_rresp			(s_axi_rresp),			// output [1:0]		s_axi_rresp
	.s_axi_rlast			(s_axi_rlast),			// output			s_axi_rlast
	.s_axi_rvalid			(s_axi_rvalid),			// output			s_axi_rvalid
	.s_axi_rready			(s_axi_rready),			// input			s_axi_rready

	// System Clock Ports
	.sys_clk_i				(clk_200M),				// input sys_clk_i 200M
	.sys_rst				(rst_n)					// input sys_rst
);

//---------------------------------------FSM----------------------------------------------
always @(posedge ui_clk or posedge ui_clk_sync_rst) begin
	if(ui_clk_sync_rst) begin
		state <= S_IDLE;
	end
	else begin
		state <= next_state;
	end
end

always @(*) begin
	case(state)
	S_IDLE: begin
		if(mmcm_locked && init_calib_complete) begin
			next_state	<= S_ARB;
		end
		else begin
			next_state	<= S_IDLE;
		end
	end
	S_ARB: begin
		if(wr_ddr3_req) begin
			next_state	<= S_WR_ADDR;
		end
		else if(rd_ddr3_req && (s_axi_araddr != s_axi_awaddr)) begin	//DDR3不空,才可读取
			next_state	<= S_RD_ADDR;
		end
		else begin
			next_state	<= S_ARB;
		end
	end
	S_WR_ADDR: begin
		if(s_axi_awvalid && s_axi_awready) begin
			next_state	<= S_WR_DATA;
		end
		else begin
			next_state	<= S_WR_ADDR;
		end
	end
	S_WR_DATA: begin
		if(data_cnt >= DATA_NUM) begin
			next_state	<= S_WR_RESP;
		end
		else begin
			next_state	<= S_WR_DATA;
		end
	end
	S_WR_RESP: begin
		if(s_axi_bvalid && s_axi_bready) begin
			next_state	<= S_ARB;
		end
		else begin
			next_state	<= S_WR_RESP;
		end
	end
	S_RD_ADDR: begin
		if(s_axi_arvalid && s_axi_arready) begin
			next_state	<= S_RD_DATA;
		end
		else begin
			next_state	<= S_RD_ADDR;
		end
	end
	S_RD_DATA: begin
		if(data_cnt >= DATA_NUM) begin	//m_axi_rready && m_axi_rvalid && m_axi_rlast
			next_state	<= S_RD_RESP;
		end
		else begin
			next_state	<= S_RD_DATA;
		end
	end
	S_RD_RESP: begin
		next_state	<= S_ARB;
	end
	default: begin
		next_state	<= S_IDLE;
	end
	endcase
end

//-------------------------------------Control--------------------------------------------
assign	s_axi_awid		= AXI_ID;
assign	s_axi_arid		= AXI_ID;

assign	s_axi_awlen		= AXI_LEN;
assign	s_axi_arlen		= AXI_LEN;

assign	s_axi_awsize	= 3'd3;		//每个数据的大小为2^awsize = 2^3 = 8Bytes = 64bit
assign	s_axi_awburst	= 2'b01;	//突发传输模式为INCR,地址一直增加,递增的值为(2^Burst_size)
assign	s_axi_awlock	= 1'b0; 	//正常传输,非独占传输
assign	s_axi_awcache	= 4'b0000;	//指明了总线中的存储类型
assign	s_axi_awprot	= 3'b000;	//指明访问是否被允许的信号
assign	s_axi_awqos		= 4'b0000;
assign	s_axi_wstrb		= 8'hff;	//写字节通道选通

assign	s_axi_arsize	= 3'd3;
assign	s_axi_arburst	= 2'b01;	//突发传输模式为INCR
assign	s_axi_arlock	= 1'b0;
assign	s_axi_arcache	= 4'b0000;
assign	s_axi_arprot	= 3'b000;
assign	s_axi_arqos		= 4'b0000;

assign	full			= w_fifo_full;
assign	empty			= r_fifo_empty;

always @(posedge ui_clk) begin
	aresetn				<= ~ui_clk_sync_rst;
end

assign	fifo_rst		= ~mmcm_locked;
//fifo的rst必须同时存在rd_clk和wr_clk,否则将初始化失败,一直处在rst_busy阶段

assign	app_sr_req		= 1'b0;		//此三者要置零,否则 refresh DDR
assign	app_ref_req		= 1'b0;
assign	app_zq_req		= 1'b0;

assign	w_fifo_rst_busy	= w_fifo_wr_rst_busy | w_fifo_rd_rst_busy;
assign	r_fifo_rst_busy	= r_fifo_wr_rst_busy | r_fifo_rd_rst_busy;

assign	rst_busy		= w_fifo_rst_busy | r_fifo_rst_busy | (~init_calib_complete);

//-----------s_axi_awvalid--------------
always @(*) begin
	case(state)
	S_WR_ADDR: begin
		s_axi_awvalid	<= 1'b1;
	end
	default: begin
		s_axi_awvalid	<= 1'b0;
	end
	endcase
end

//-----------s_axi_wvalid---------------
always @(*) begin
	case(state)
	S_WR_DATA: begin
		if(~w_fifo_empty && data_cnt < DATA_NUM) begin
			s_axi_wvalid	<= 1'b1;
		end
		else begin
			s_axi_wvalid	<= 1'b0;
		end
	end
	default: begin
		s_axi_wvalid	<= 1'b0;
	end
	endcase
end

//-----------s_axi_wlast---------------
always @(*) begin
	case(state)
	S_WR_DATA: begin
		if(data_cnt == DATA_NUM - 1) begin
			s_axi_wlast	<= 1'b1;
		end
		else begin
			s_axi_wlast	<= 1'b0;
		end
	end
	default: begin
		s_axi_wlast	<= 1'b0;
	end
	endcase
end

//-----------s_axi_bready---------------
always @(*) begin
	case(state)
	S_WR_RESP: begin
		s_axi_bready	<= 1'b1;
	end
	default: begin
		s_axi_bready	<= 1'b0;
	end
	endcase
end

//-----------s_axi_arvalid--------------
always @(*) begin
	case(state)
	S_RD_ADDR: begin
		s_axi_arvalid	<= 1'b1;
	end
	default: begin
		s_axi_arvalid	<= 1'b0;
	end
	endcase
end

//-----------s_axi_rready---------------
always @(*) begin
	case(state)
	S_RD_DATA: begin
		if(~r_fifo_full && data_cnt < DATA_NUM) begin
			s_axi_rready	<= 1'b1;
		end
		else begin
			s_axi_rready	<= 1'b0;
		end
	end
	S_RD_RESP: begin
		s_axi_rready	<= 1'b1;
	end
	default: begin
		s_axi_rready	<= 1'b0;
	end
	endcase
end

//------------w_fifo_rden---------------
always @(*) begin
	case(state)
	S_WR_DATA: begin
		w_fifo_rden		<= s_axi_wvalid & s_axi_wready;
	end
	default: begin
		w_fifo_rden		<= 1'b0;
	end
	endcase
end

//------------r_fifo_wren---------------
always @(*) begin
	case(state)
	S_RD_DATA: begin
		r_fifo_wren		<= s_axi_rready & s_axi_rvalid;
	end
	default: begin
		r_fifo_wren		<= 1'b0;
	end
	endcase
end

//-------------data_cnt-----------------
always @(posedge ui_clk) begin
	case(state)
	S_IDLE, S_ARB: begin
		data_cnt	<= 9'd0;
	end
	S_WR_DATA: begin
		if(s_axi_wvalid && s_axi_wready) begin
			data_cnt	<= data_cnt + 1'b1;
		end
		else begin
			data_cnt	<= data_cnt;
		end
	end
	S_RD_DATA: begin
		if(s_axi_rvalid && s_axi_rready) begin
			data_cnt	<= data_cnt + 1'b1;
		end
		else begin
			data_cnt	<= data_cnt;
		end
	end
	default: begin
		data_cnt	<= data_cnt;
	end
	endcase
end

//------------s_axi_awaddr--------------
always @(posedge ui_clk) begin
	case(state)
	S_IDLE: begin
		s_axi_awaddr	<= 31'd0;
	end
	S_WR_RESP: begin
		if(s_axi_bvalid && s_axi_bready) begin
			s_axi_awaddr	<= s_axi_awaddr + DATA_NUM * 8;		//s_axi_awaddr为字节地址,每次写NUM个64bit数据
		end														//超出最大范围后自动回到0
		else begin
			s_axi_awaddr	<= s_axi_awaddr;
		end
	end
	default: begin
		s_axi_awaddr	<= s_axi_awaddr;
	end
	endcase
end

//------------s_axi_araddr--------------
always @(posedge ui_clk) begin
	case(state)
	S_IDLE: begin
		s_axi_araddr	<= 31'd0;
	end
	S_RD_RESP: begin
		if(s_axi_rvalid && s_axi_rready) begin
			s_axi_araddr	<= s_axi_araddr + DATA_NUM * 8;
		end
		else begin
			s_axi_awaddr	<= s_axi_awaddr;
		end
	end
	default: begin
		s_axi_araddr	<= s_axi_araddr;
	end
	endcase
end

//------------wr_ddr3_req---------------
always @(posedge ui_clk) begin
	case(state)
	S_ARB: begin
		if(w_fifo_rddat_cnt >= DATA_NUM) begin
			wr_ddr3_req		<= 1'b1;			//w_FIFO中数据足够一次突发传输,发起写请求
		end
		else begin
			wr_ddr3_req		<= 1'b0;
		end
	end
	default: begin
		wr_ddr3_req		<= 1'b0;
	end
	endcase
end

//------------rd_ddr3_req---------------
always @(posedge ui_clk) begin
	case(state)
	S_ARB: begin
		if((FIFO_LEN - r_fifo_wrdat_cnt) > DATA_NUM) begin
			rd_ddr3_req		<= 1'b1;			//r_FIFO中空闲位置足够一次突发传输,发起读请求
		end
		else begin
			rd_ddr3_req		<= 1'b0;
		end
	end
	default: begin
		rd_ddr3_req		<= 1'b0;
	end
	endcase
end

endmodule
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
  • 234
  • 235
  • 236
  • 237
  • 238
  • 239
  • 240
  • 241
  • 242
  • 243
  • 244
  • 245
  • 246
  • 247
  • 248
  • 249
  • 250
  • 251
  • 252
  • 253
  • 254
  • 255
  • 256
  • 257
  • 258
  • 259
  • 260
  • 261
  • 262
  • 263
  • 264
  • 265
  • 266
  • 267
  • 268
  • 269
  • 270
  • 271
  • 272
  • 273
  • 274
  • 275
  • 276
  • 277
  • 278
  • 279
  • 280
  • 281
  • 282
  • 283
  • 284
  • 285
  • 286
  • 287
  • 288
  • 289
  • 290
  • 291
  • 292
  • 293
  • 294
  • 295
  • 296
  • 297
  • 298
  • 299
  • 300
  • 301
  • 302
  • 303
  • 304
  • 305
  • 306
  • 307
  • 308
  • 309
  • 310
  • 311
  • 312
  • 313
  • 314
  • 315
  • 316
  • 317
  • 318
  • 319
  • 320
  • 321
  • 322
  • 323
  • 324
  • 325
  • 326
  • 327
  • 328
  • 329
  • 330
  • 331
  • 332
  • 333
  • 334
  • 335
  • 336
  • 337
  • 338
  • 339
  • 340
  • 341
  • 342
  • 343
  • 344
  • 345
  • 346
  • 347
  • 348
  • 349
  • 350
  • 351
  • 352
  • 353
  • 354
  • 355
  • 356
  • 357
  • 358
  • 359
  • 360
  • 361
  • 362
  • 363
  • 364
  • 365
  • 366
  • 367
  • 368
  • 369
  • 370
  • 371
  • 372
  • 373
  • 374
  • 375
  • 376
  • 377
  • 378
  • 379
  • 380
  • 381
  • 382
  • 383
  • 384
  • 385
  • 386
  • 387
  • 388
  • 389
  • 390
  • 391
  • 392
  • 393
  • 394
  • 395
  • 396
  • 397
  • 398
  • 399
  • 400
  • 401
  • 402
  • 403
  • 404
  • 405
  • 406
  • 407
  • 408
  • 409
  • 410
  • 411
  • 412
  • 413
  • 414
  • 415
  • 416
  • 417
  • 418
  • 419
  • 420
  • 421
  • 422
  • 423
  • 424
  • 425
  • 426
  • 427
  • 428
  • 429
  • 430
  • 431
  • 432
  • 433
  • 434
  • 435
  • 436
  • 437
  • 438
  • 439
  • 440
  • 441
  • 442
  • 443
  • 444
  • 445
  • 446
  • 447
  • 448
  • 449
  • 450
  • 451
  • 452
  • 453
  • 454
  • 455
  • 456
  • 457
  • 458
  • 459
  • 460
  • 461
  • 462
  • 463
  • 464
  • 465
  • 466
  • 467
  • 468
  • 469
  • 470
  • 471
  • 472
  • 473
  • 474
  • 475
  • 476
  • 477
  • 478
  • 479
  • 480
  • 481
  • 482
  • 483
  • 484
  • 485
  • 486
  • 487
  • 488
  • 489
  • 490
  • 491
  • 492
  • 493
  • 494
  • 495
  • 496
  • 497
  • 498
  • 499
  • 500
  • 501
  • 502
  • 503
  • 504
  • 505
  • 506
  • 507
  • 508
  • 509
  • 510
  • 511
  • 512
  • 513
  • 514
  • 515
  • 516
  • 517
  • 518
  • 519
  • 520
  • 521
  • 522
  • 523
  • 524
  • 525
  • 526
  • 527
  • 528
  • 529
  • 530
  • 531
  • 532
  • 533
  • 534
  • 535
  • 536
  • 537
  • 538
  • 539
  • 540
  • 541
  • 542
  • 543
  • 544
  • 545
  • 546
  • 547
  • 548
  • 549
  • 550
  • 551
  • 552
  • 553
  • 554
  • 555
  • 556
  • 557
  • 558
  • 559
  • 560
  • 561
  • 562
  • 563
  • 564
  • 565
  • 566
  • 567
  • 568
  • 569
  • 570
  • 571
  • 572
  • 573
  • 574
  • 575
  • 576
  • 577
  • 578
  • 579
  • 580
  • 581
  • 582
  • 583
  • 584
  • 585
  • 586
  • 587
  • 588
  • 589
  • 590
  • 591
  • 592
  • 593
  • 594
  • 595
  • 596
  • 597
  • 598
  • 599
  • 600
  • 601
  • 602
  • 603
  • 604
  • 605
  • 606
  • 607
  • 608
  • 609
  • 610
  • 611
  • 612
  • 613
  • 614
  • 615
  • 616
  • 617
  • 618
  • 619

testbench 如下

`timescale 100ps/100ps

module DDR3_tb();

reg		clk_200M	= 1'b1;
always #25 begin
	clk_200M	<= ~clk_200M;
end

reg					rst_n;
wire				rst_busy;

//--------------------ddr3------------------------
wire		[63:0]	ddr3_dq;			//DQ,4片 x16 DDR的数据线

wire		[14:0]	ddr3_addr;			//Address
wire		[2:0]	ddr3_ba;			//Bank Address

wire		[7:0]	ddr3_dqs_p;			//DQ Select,0,2,4,6分别为四片DDR的DQSL(Lower Byte),
wire		[7:0]	ddr3_dqs_n;			//1,3,5,7分别为四片DDR的DQSU(Upper Byte)
//Output with read data. Edge-aligned with read data.
//Input with write data. Center-aligned to write data.

wire		[0:0]	ddr3_ck_p;			//4片DR3共用ck、cke、odt、cs信号,故[0:0]
wire		[0:0]	ddr3_ck_n;
//differential clock inputs. All control and address input signals are sampled 
//on the crossing of the positive edge of CK and the negative edge of CK#

wire		[7:0]	ddr3_dm;			//Input Data Mask,0,2,4,6为DML,1,3,5,7为DMU
wire		[0:0]	ddr3_cke;			//Clock Enable
wire		[0:0]	ddr3_cs_n;			//Chip Select
wire				ddr3_ras_n;			//Row Address Enable
wire				ddr3_cas_n;			//Column Address Enable
wire				ddr3_we_n;			//Write Enable
wire		[0:0]	ddr3_odt;			//On-die termination enable
wire				ddr3_reset_n;

//-------------FPGA FIFO Control-------------------
reg					wr_en;				//高电平有效
reg			[63:0]	wrdat	= 64'd0;
wire				full;				//fifo_w full

reg					rd_en;				//高电平有效
wire		[63:0]	rddat;
wire				empty;				//fifo_r empty

//--------------------DDR3 Control-----------------------------------
DDR3_top DDR3_top_inst(
	.clk_200M				(clk_200M),
	.rst_n					(rst_n),
	.rst_busy				(rst_busy),

	//--------------------ddr3------------------------
	.ddr3_dq				(ddr3_dq),

	.ddr3_addr				(ddr3_addr),
	.ddr3_ba				(ddr3_ba),

	.ddr3_dqs_p				(ddr3_dqs_p),
	.ddr3_dqs_n				(ddr3_dqs_n),

	.ddr3_ck_p				(ddr3_ck_p),
	.ddr3_ck_n				(ddr3_ck_n),

	.ddr3_dm				(ddr3_dm),
	.ddr3_cke				(ddr3_cke),
	.ddr3_cs_n				(ddr3_cs_n),
	.ddr3_ras_n				(ddr3_ras_n),
	.ddr3_cas_n				(ddr3_cas_n),
	.ddr3_we_n				(ddr3_we_n),
	.ddr3_odt				(ddr3_odt),
	.ddr3_reset_n			(ddr3_reset_n),

	//-------------FPGA FIFO Control-------------------
	.wr_en					(wr_en),
	.wrdat					(wrdat),
	.full					(full),

	.rd_en					(rd_en),
	.rddat					(rddat),
	.empty					(empty)
);

//--------------------DDR3 Model-----------------------------------
ddr3_model ddr3_b1 (
	.rst_n		(ddr3_reset_n),
	.ck			(ddr3_ck_p),
	.ck_n		(ddr3_ck_n),
	.cke		(ddr3_cke),
	.cs_n		(ddr3_cs_n),
	.ras_n		(ddr3_ras_n),
	.cas_n		(ddr3_cas_n),
	.we_n		(ddr3_we_n),
	.dm_tdqs	(ddr3_dm[1:0]),
	.ba			(ddr3_ba),
	.addr		(ddr3_addr),
	.dq			(ddr3_dq[15:0]),
	.dqs		(ddr3_dqs_p[1:0]),
	.dqs_n		(ddr3_dqs_n[1:0]),
	.tdqs_n		(),
	.odt		(ddr3_odt)
);

ddr3_model ddr3_b2 (
	.rst_n		(ddr3_reset_n),
	.ck			(ddr3_ck_p),
	.ck_n		(ddr3_ck_n),
	.cke		(ddr3_cke),
	.cs_n		(ddr3_cs_n),
	.ras_n		(ddr3_ras_n),
	.cas_n		(ddr3_cas_n),
	.we_n		(ddr3_we_n),
	.dm_tdqs	(ddr3_dm[3:2]),
	.ba			(ddr3_ba),
	.addr		(ddr3_addr),
	.dq			(ddr3_dq[31:16]),
	.dqs		(ddr3_dqs_p[3:2]),
	.dqs_n		(ddr3_dqs_n[3:2]),
	.tdqs_n		(),
	.odt		(ddr3_odt)
);

ddr3_model ddr3_b3 (
	.rst_n		(ddr3_reset_n),
	.ck			(ddr3_ck_p),
	.ck_n		(ddr3_ck_n),
	.cke		(ddr3_cke),
	.cs_n		(ddr3_cs_n),
	.ras_n		(ddr3_ras_n),
	.cas_n		(ddr3_cas_n),
	.we_n		(ddr3_we_n),
	.dm_tdqs	(ddr3_dm[5:4]),
	.ba			(ddr3_ba),
	.addr		(ddr3_addr),
	.dq			(ddr3_dq[47:32]),
	.dqs		(ddr3_dqs_p[5:4]),
	.dqs_n		(ddr3_dqs_n[5:4]),
	.tdqs_n		(),
	.odt		(ddr3_odt)
);

ddr3_model ddr3_b4 (
	.rst_n		(ddr3_reset_n),
	.ck			(ddr3_ck_p),
	.ck_n		(ddr3_ck_n),
	.cke		(ddr3_cke),
	.cs_n		(ddr3_cs_n),
	.ras_n		(ddr3_ras_n),
	.cas_n		(ddr3_cas_n),
	.we_n		(ddr3_we_n),
	.dm_tdqs	(ddr3_dm[7:6]),
	.ba			(ddr3_ba),
	.addr		(ddr3_addr),
	.dq			(ddr3_dq[63:48]),
	.dqs		(ddr3_dqs_p[7:6]),
	.dqs_n		(ddr3_dqs_n[7:6]),
	.tdqs_n		(),
	.odt		(ddr3_odt)
);

//--------------------wrdat, rddat-----------------------------------
always @(posedge clk_200M) begin
	if(wr_en) begin
		wrdat	<= wrdat + 1'b1;
	end
	else begin
		wrdat	<= wrdat;
	end
end

//-----------------------tb----------------------------------------
initial begin
	wr_en	<= 1'b0;
	rd_en	<= 1'b0;

	rst_n	<= 1'b1;
	#100;
	rst_n	<= 1'b0;
	#100;
	rst_n	<= 1'b1;

	wait(~rst_busy);
	#200;

	fork 
		begin: w_r
			write(16);
			#100;

			write(16);
			#100;

			write(16);
			#100;

			// #10000;
			read(16);
			#100;

			write(16);
			#100;

			read(64);

			#2000;
			disable shut_down;
			$stop;
		end

		begin: shut_down
			#100000;
			disable w_r;
			$stop;
		end
	join
end

task write;
	input	[7:0]	num;

	integer 		i;
	begin
		for(i=0; i<num; i=i) begin
			wait(clk_200M);
			if(~full) begin
				wr_en	<= 1'b1;
				i		<= i+1;
			end
			else begin
				wr_en	<= 1'b0;
			end
			wait(~clk_200M);
		end
		wait(clk_200M);
		wr_en	<= 1'b0;
	end
endtask

task read;
	input	[7:0]	num;

	integer			i;
	begin
		for(i=0; i<num; i=i) begin
			wait(clk_200M);
			if(~empty) begin
				rd_en	<= 1'b1;
				i		<= i+1;
			end
			else begin
				rd_en	<= 1'b0;
			end
			wait(~clk_200M);
		end
		wait(clk_200M);
		rd_en	<= 1'b0;
	end
endtask

endmodule
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
  • 234
  • 235
  • 236
  • 237
  • 238
  • 239
  • 240
  • 241
  • 242
  • 243
  • 244
  • 245
  • 246
  • 247
  • 248
  • 249
  • 250
  • 251
  • 252
  • 253
  • 254
  • 255
  • 256
  • 257
  • 258
  • 259
  • 260

仿真结果如下

在这里插入图片描述

放大 FIFO 读写部分的结果如下

在这里插入图片描述

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/IT小白/article/detail/484197
推荐阅读
相关标签
  

闽ICP备14008679号