Xilinx官方DDR3调试总结

DDR3调试总结

一、开发板介绍

本实验所用开发板为xc7a35t-2fgg484I
在这里插入图片描述
FPGA设备名称命名规则如上图所示
XC7A:代表FPGA的家族系列 (X表示XILINX产品,C表示商用(Commercial),7A表示7系列产品)
35t:代表拥有的Logic Cells的数目
-2:表示速度等级
fgg484:表示封装类型和引脚数目

二、DDR3介绍

DDR SDRAM 全称为 Double Data Rate SDRAM,中文名为“双倍数据流 SDRAM”。DDR SDRAM 在原有的 SDRAM 的基础上改进而来。
存储器分类:
在这里插入图片描述
RAM:即随机存储内存,就是计算机的内存,这种存储器在断电时将丢失其存储内容,故主要用于存储短时间使用的程序。
ROM:即只读内存,是一种只能读出事先所存储数据的固态半导体存储器。
FLASH:它结合了ROM和RAM的长处,不仅具备电子可擦出可编程(EEPROM)的性能,还不会断电丢失数据同时可以快速读取数据(NVRAM的优势), 用作存储Bootloader以及操作系统或者程序代码或者直接当硬盘使用(U盘).

AX7035开发板上搭载1个Micron DDR3的颗MT41J128M16HA,芯片容量为2Gb,即256MB。在 DDR SDRAM 中,突发长度只有 2、4、8 三种选择,没有了随机存取的操作(突发长度为 1)和全页式突发。这是因为L-Bank 一次就存取两倍于芯片位宽的数据,所以芯片至少也要进行两次传输才可以。

三、DDR3设计

MIG IP 控制器是 Xilinx 为用户提供的一个 DDR 控制的 IP,用户可以不需要了解DDR3的控制和读写时序,只需要通过DDR3控制器对DDR3的读写进行控制。
7系列的DDR3控制器接口如下:
在这里插入图片描述
DDR3控制器包含3个部分:用户接口模块(User interface Block),存储器控制模块(Memory Controller),DDR3的物理接口(Physical Layer)。

DDR3使用流程:
①打开IP Catalog界面
在这里插入图片描述
②在 IP Catalog 界面里双击 Memories & Storage Elements\ Memory Interface Generators 下的Memory Interface Generator (MIG 7 Series)。
在这里插入图片描述
③点击next
在这里插入图片描述
④修改 Component Name 为你想要命名的IP名字,点击 Next。
在这里插入图片描述
⑤这里可以选择兼容芯片,不需要直接点击next。
在这里插入图片描述
⑥选择默认的DDR3
在这里插入图片描述
⑦DDR3设置
7035 开发板 Memory Part 选择开发板的型号"MT41J128m16xx-125", Data Width 数据宽度选择 16 位。
在这里插入图片描述
⑧选择 PLL 输入时钟的频率为 200Mhz, 这个时钟需要跟开发板上的时钟频率一致,其它设置输出阻抗值和内部的 ODT 内部上拉电阻值来改善 DDR3 的信号完整性,一般不需要修改。
在这里插入图片描述
⑨System Clock 选择差分"No Buffer", Reference Clock 因为开发板上没有提供单独的 DDR 参考时钟,所以选择"Use System Clock"。System Reset Polarity 选择"ACTIVE LOW",AX7035 还需勾选 Internal Verf,其它保留默认配置。在这里插入图片描述
⑩之后按照默认值进行设定。在最后的界面需要设置DDR3的数据、地址和控制信号的FPGA管脚分配和IO电平,用户可以根据开发板原理图进行手动配置或者使用Read XDC/UCF按键导入.xdc或者.ucf文件进行自动配置。最后validate成功即可正确生成DDR3的IP核。

DDR3的时钟分析总结:
步骤⑦中:
Clock_Period:表示MIG核对DDR3接口的速率,乘以2则表示双边沿传输。AX7035开发板设置成400MHz,根据不同开发板设置。
PHY to Controller Clock Ratio:一个比值,表示MIG输出到app接口上的时钟ui_clk。AX7035开发板设置成4:1,表明ui_clk为100MHz。
步骤⑧中:
Input Clock Period:表示DDR3中top层输入时钟,即用户需要输入到DDR3的时钟。AX7035设置为200MHz。
步骤⑨中:
reference clock:use system clock,与步骤⑧中的Input Clock Period表示同一个时钟,如果不设置此时钟,最后在工程中MIG生成.v会生成一个接口向我们索要该200MHz的时钟。

DDR3用户接口时序图分析:
写数据时序图:
在这里插入图片描述
add_cmd:输入信号。DDR3命令端口,0为写入,1为读出。
app_addr:输入信号。DDR3的地址信号。
app_en:DDR3使能信号。
app_rdy:输出端口。高电平有效,表示MIG准备好接收命令和数据了。
app_wdf_mask:高电平有效,将其置1可以屏蔽某些字节。可以指示哪些字节写入外部存储器,哪些保持其当前状态。
app_wdf_rdy:输出端口,表示mig准备好接收写数据和写命令。
app_wdf_data:写入的数据。
app_wdf_wren:写入数据接口app_wdf_data的使能信号。
app_wdf_end:指示当前周期数据已经写结束了。

提示:只有当app_en和app_rdy同时为高电平时add_cmd和app_addr才有效。app_name和app_wdf_name是分开的,若app_wdf_rdy有效,而app_rdy无效,数据也会被存入fifo中,只有app_rdy有效了,数据才会写入到DDR3中。由时序图可知,写数据和写控制信号可以早于写命令、写当前地址、其他写控制信号一个时钟周期或迟于两个时钟周期之内都可。

读数据时序:只要当读命令(app_cmd)和当前读地址(app_addr)以及读控制信号(app_en,app_rdy)同时有效时,等待读数据有效信号(app_rd_data_valid)有效时读数据(app_rd_data)有效。

DDR3用户接口驱动代码:

module mem_burst
#(
	parameter MEM_DATA_BITS = 128,
	parameter ADDR_BITS = 28
)
(
    //用户控制接口
	input rst,                                   /*复位*/
	input mem_clk,                               /*接口时钟*/
	input rd_burst_req,                          /*读请求*/
	input wr_burst_req,                          /*写请求*/
	input[15:0] rd_burst_len,                     /*读数据长度*/
	input[15:0] wr_burst_len,                     /*写数据长度*/
	input[ADDR_BITS - 1:0] rd_burst_addr,        /*读首地址*/
	input[ADDR_BITS - 1:0] wr_burst_addr,        /*写首地址*/
	output rd_burst_data_valid,                  /*读出数据有效*/
	output wr_burst_data_req,                    /*写数据信号*/
	output[MEM_DATA_BITS - 1:0] rd_burst_data,   /*读出的数据*/
	input[MEM_DATA_BITS - 1:0] wr_burst_data,    /*写入的数据*/
	output rd_burst_finish,                      /*读完成*/
	output wr_burst_finish,                      /*写完成*/
	output burst_finish,                         /*读或写完成*/
	
	//DDR接口
   output[ADDR_BITS-1:0]                       app_addr,
   output[2:0]                                 app_cmd,
   output                                      app_en,
   output [MEM_DATA_BITS-1:0]                  app_wdf_data,
   output                                      app_wdf_end,
   output [MEM_DATA_BITS/8-1:0]                app_wdf_mask,
   output                                      app_wdf_wren,
   input [MEM_DATA_BITS-1:0]                   app_rd_data,
   input                                       app_rd_data_end,
   input                                       app_rd_data_valid,
   input                                       app_rdy,
   input                                       app_wdf_rdy,
   input                                       ui_clk_sync_rst,  
   input                                       init_calib_complete
);

assign app_wdf_mask = {MEM_DATA_BITS/8{1'b0}};

localparam IDLE = 3'd0;
localparam MEM_READ = 3'd1;
localparam MEM_READ_WAIT = 3'd2;
localparam MEM_WRITE  = 3'd3;
localparam MEM_WRITE_WAIT = 3'd4;
localparam READ_END = 3'd5;
localparam WRITE_END = 3'd6;
localparam MEM_WRITE_FIRST_READ = 3'd7;

reg[2:0] state;	
reg[9:0] rd_addr_cnt;
reg[9:0] rd_data_cnt;
reg[9:0] wr_addr_cnt;
reg[9:0] wr_data_cnt;

reg[2:0] app_cmd_r;
reg[ADDR_BITS-1:0] app_addr_r;
reg app_en_r;
reg app_wdf_end_r;
reg app_wdf_end_r_m0;
reg app_wdf_wren_r;
assign app_cmd = app_cmd_r;
assign app_addr = app_addr_r;
assign app_en = app_en_r;
assign app_wdf_end = app_wdf_end_r_m0;  //延迟了一个时钟周期
assign app_wdf_data = wr_burst_data;
assign app_wdf_wren = app_wdf_wren_r & app_wdf_rdy;
assign rd_burst_finish = (state == READ_END);
assign wr_burst_finish = (state == WRITE_END);
assign burst_finish = rd_burst_finish | wr_burst_finish;

assign rd_burst_data = app_rd_data;
assign rd_burst_data_valid = app_rd_data_valid;

assign wr_burst_data_req = (state == MEM_WRITE) & app_wdf_rdy ;

always@(posedge mem_clk or posedge rst)
begin
	if(rst)
	begin
		app_wdf_wren_r <= 1'b0;
	end
	else if(app_wdf_rdy)
		app_wdf_wren_r <= wr_burst_data_req;
end

always@(posedge mem_clk or posedge rst)
begin
	if(rst)
		app_wdf_end_r_m0 <= 1'b0;
	else 
		app_wdf_end_r_m0 <= app_wdf_end_r;
end

always@(posedge mem_clk or posedge rst)
begin
	if(rst)
	begin
		state <= IDLE;
		app_cmd_r <= 3'b000;
		app_addr_r <= 0;
		app_en_r <= 1'b0;
		rd_addr_cnt <= 0;
		rd_data_cnt <= 0;
		wr_addr_cnt <= 0;
		wr_data_cnt <= 0;
		app_wdf_end_r <= 1'b0;
	end
	else if(init_calib_complete == 1'b1)
	begin
		case(state)
			IDLE:
			begin
				if(rd_burst_req)
				begin
					state <= MEM_READ;
					app_cmd_r <= 3'b001;
					app_addr_r <= {rd_burst_addr,3'd0};
					app_en_r <= 1'b1;
				end
				else if(wr_burst_req)
				begin
					state <= MEM_WRITE;
					app_cmd_r <= 3'b000;
					app_addr_r <= {wr_burst_addr,3'd0};
					app_en_r <= 1'b1;
					wr_addr_cnt <= 0;
					app_wdf_end_r <= 1'b1;
					wr_data_cnt <= 0;
				end
			end
			MEM_READ:
			begin
				if(app_rdy)
				begin
					app_addr_r <= app_addr_r + 8;
					if(rd_addr_cnt == rd_burst_len - 1)
					begin
						state <= MEM_READ_WAIT;
						rd_addr_cnt <= 0;
						app_en_r <= 1'b0;
					end
					else
						rd_addr_cnt <= rd_addr_cnt + 1;
				end
				
				if(app_rd_data_valid)
				begin
					if(rd_data_cnt == rd_burst_len - 1)
					begin
						rd_data_cnt <= 0;
						state <= READ_END;
					end
					else
					begin
						rd_data_cnt <= rd_data_cnt + 1;
					end
				end
			end
			MEM_READ_WAIT:
			begin
				if(app_rd_data_valid)
				begin
					if(rd_data_cnt == rd_burst_len - 1)
					begin
						rd_data_cnt <= 0;
						state <= READ_END;
					end
					else
					begin
						rd_data_cnt <= rd_data_cnt + 1;
					end
				end
			end
			MEM_WRITE_FIRST_READ:
			begin
				app_en_r <= 1'b1;
				state <= MEM_WRITE;
				wr_addr_cnt <= 0;
			end
			MEM_WRITE:
			begin
				if(app_rdy)
				begin
					app_addr_r <= app_addr_r + 8;
					if(wr_addr_cnt == wr_burst_len - 1)
					begin
						app_wdf_end_r <= 1'b0;
						app_en_r <= 1'b0;
					end
					else
					begin
						wr_addr_cnt <= wr_addr_cnt + 1;
					end
				end
					
				if(wr_burst_data_req)
				begin
//					app_addr_r <= app_addr_r + 8;
					if(wr_data_cnt == wr_burst_len - 1)
					begin	
						state <= MEM_WRITE_WAIT;
					end
					else
					begin
						wr_data_cnt <= wr_data_cnt + 1;
					end
				end
				
			end
			READ_END:
				state <= IDLE;
			MEM_WRITE_WAIT:
			begin
				if(app_rdy)
				begin
					app_addr_r <= app_addr_r + 'b1000;
					if(wr_addr_cnt == wr_burst_len - 1)
					begin
						app_wdf_end_r <= 1'b0;
						app_en_r <= 1'b0;
						if(app_wdf_rdy) 
							state <= WRITE_END;
					end
					else
					begin
						wr_addr_cnt <= wr_addr_cnt + 1;
					end
				end
				else if(~app_en_r & app_wdf_rdy)
					state <= WRITE_END;
				
			end
			WRITE_END:
				state <= IDLE;
			default:
				state <= IDLE;
		endcase
	end
end
endmodule 

注意:wr_burst_addr,rd_burst_addr和app_addr的区别,DDR3的突发长度为8,wr_burst_addr或者rd_burst_addr地址以1递增,而DDR3的内部地址以8递增。
app_addr_r <= {rd_burst_addr,3’d0}; //ddr3每8个数据进行传输一次,因此每次读写的时候起始地址为用户操纵地址的8倍,因此左移三位。
app_addr_r <= app_addr_r + 8; //ddr3每读写一次其地址自增8

  • 0
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Xilinx平台的DDR3设计教程中的仿真篇主要介绍了如何使用Xilinx Vivado软件进行DDR3控制器的仿真。 首先,我们需要创建一个新的Vivado项目,并导入DDR3控制器的IP核。在新建的项目中,我们可以根据对DDR3控制器的需求进行配置,包括时钟频率、时序参数等。配置完成后,我们可以生成设计文件和约束文件,并准备进行仿真。 接下来,我们需要在Vivado中设置仿真环境。我们可以使用ModelSim或其他仿真工具来执行仿真,但需要确保与Vivado兼容。在设置仿真环境时,我们需要指定仿真的输入文件和仿真的时钟频率等参数。 然后,我们可以运行仿真。在仿真过程中,可以通过观察信号波形和输出消息来验证DDR3控制器是否正常工作。我们可以检查控制器的读写操作是否正确,时序是否满足要求,数据是否被正确传输等。 如果在仿真过程中发现了问题,我们可以使用Vivado提供的调试工具来帮助我们分析和排查问题。可以使用逐步仿真功能,逐步跟踪代码的执行过程,并观察信号的变化情况。还可以使用信号探查器来监视关键信号的数值,以便发现问题所在。 最后,如果仿真通过并且DDR3控制器按照预期工作,我们可以将设计文件导出到硬件平台进行验证和部署。可以生成比特流文件,直接加载到FPGA中进行硬件验证。 综上所述,Xilinx平台DDR3设计教程中的仿真篇详细介绍了使用Vivado软件进行DDR3控制器仿真的过程,并提供了一些调试工具来帮助我们分析和排查问题。通过仿真,我们可以验证DDR3控制器的功能和性能,并在实际硬件平台上进行验证和部署。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

三岁囍

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值