DDR2/3 SDRAM学习笔记

本文详细介绍了DDR、DDR2、DDR3内存的工作原理,如随路时钟、片内终结、预读概念,以及DDR3与DDR2的区别,包括时钟频率、OCD、AL等参数。同时,提到了DDR3的时序设计、引脚功能,以及在VivadoIP核中的设置和DDR3读写实验的相关内容。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

随路时钟概念

Dq(数据总线)与Dqs(时钟信号)构成随路时钟。
在Dqs上升或下降沿翻转Dq数据总线信号。
注意:dq与dqs为双线信号。两个信号都由发送方发送。dqs为数据同步信号。
在这里插入图片描述
ODT引脚:片上终端电阻,数据线接上拉电阻。

DDR与DDR2区别

时钟

DDR外部IO时钟频率与内部存储时钟频率一致。
DDR2外部IO时钟是内部存储时钟频率的两倍。

片外驱动调校(OCD)

DDR2在初始化过程中配置模式寄存器前添加新EMRS选项:可选OCD功能。
目的是让DQS与DQ数据信号之间的偏差降低到最小。

片内终结(ODT)

DDR2:ODT功能让信号被电路的终端吸收,不会在电路上形成反射对后面信号造成影响。ODT将终结电阻移植到芯片内部。
在这里插入图片描述

前置CAS、附加潜伏期、写入潜伏期

DDR2中读写指令可以紧随行激活发送,但读写操作没有提前,为了避免造成命令冲突,引入附加潜伏期。

RL(读潜伏期)= AL(附加潜伏期)+CL(列选通潜伏期)
从写入命令发出到第一个数据输入潜伏期  WL(写入潜伏期)=RL-1=AL+CL-1;
SDRAM无AL。

在这里插入图片描述
tDQSS:发出写指令到写数据之间的时间。
在这里插入图片描述

DDR2与DDR3区别

DDR3可以根据温度变化的自刷新频率。

参数

CL(寻址时序):DDR2 CL:2~5	  AL(附加延迟):0 ~4
			 DDR3 CL:5~11    AL(附加延迟): 0、CL-1、CL-2   CWD(写入延迟):由具体的工作频率而定

引脚

DDR3新增重置(Reset)功能: 初始化、重新配置模式寄存器。
ZQ: 该引脚通过命令集指令用相应的时钟周期对导通电阻和ODT电阻进行重新校准。

参考电压信号VREF

DDR3中,使用两个参考电压。VREFCA(命令与地址信号)、VREFDQ(数据总线)。

预读概念

bit (位)

			           突发长度		
DDR_SDRAM(2bit预读): 2   4   8	   内部结构导致,从内部总线32位中一次读出至少216位数据
DDR2_SDRAM(4bit预读) 4       8     一次至少读出416位,2个完整的时钟周期读出
DDR3_SDRAM(8bit预读)         8

使用突发长度时读写只能顺序地址;不使用读写长度时,读写地址可随机但是要连续写指令。
在这里插入图片描述

在这里插入图片描述

DDR3主要引脚

CK,CK_n:	差分信号时钟。
CKE:	高电平使能操作指令,低电平进入休眠自刷新状态。
CS_n:	片选使能信号。高电平使能芯片。 	 
RAS_n\CAS_n\WE_n:	指令输入。
DM(8位)/DMU,DML(16位):	遮掩输入数据使能信号。
BA0-BA2:	bank地址。
A10/AP:	使能在读写操作后自动预充电,高电平使能。
A0-A14(8位宽)/A0-A13(16位宽):	地址总线。
A21/BC_n:	默认高电平8bit突发长度,低电平使用突发突变模式(4bit读,4bit写)。
ODT: 	高/低电平使能校准功能。由芯片手册决定。  
RESET_n:	初始化或稳定工作中复位使能,重新配置模式寄存器。
DQ:		输入输出引脚,数据总线。
DQS,DQS_n(8位宽)/DQSL,DQSL_n,DQSU,DQSU_n(16位宽):	输入输出引脚,数据同步信号。
TDQS,TDQS_n(8位宽):		DRAMs输出引脚。数据遮掩信号,与DQS/DQS_n信号相同,在A11引脚控制下DM/TDQS_n可以使用数据遮掩,此时TDQS_n无效。
Vdd.Vddq:	电压引脚。
Vssq,Vss:	接地。
Vrefca:		CA参考电压。
 Vrefdq:	DQ参考电压。
 ZQ: 		该引脚通过命令集指令用相应的时钟周期对导通电阻和ODT电阻进行重新校准。

vivado IP核DDR3 SDRAM设置

在这里插入图片描述
在这里插入图片描述

PHY to Controller Clock Ratio:在这里插入图片描述

DDR3读写实验

DDR3模块设计

在这里插入图片描述

DDR3时序设计

在这里插入图片描述

DDR3顶层模块代码

`timescale 1ns / 1ps

module ddr_top(
    input                 sys_clk,        //系统时钟						
    // DDR3			
    inout   [7:0]         ddr3_dq      ,  //DDR3 数据			
    inout   [0:0]         ddr3_dqs_n   ,  //DDR3 dqs负			
    inout   [0:0]         ddr3_dqs_p   ,  //DDR3 dqs正  			
    output  [14:0]        ddr3_addr    ,  //DDR3 地址   			
    output  [2:0]         ddr3_ba      ,  //DDR3 banck 选			
    output                ddr3_ras_n   ,  //DDR3 行选择			
    output                ddr3_cas_n   ,  //DDR3 列选择			
    output                ddr3_we_n    ,  //DDR3 读写选择			
    output                ddr3_reset_n ,  //DDR3 复位			
    output  [0:0]         ddr3_ck_p    ,  //DDR3 时钟正			
    output  [0:0]         ddr3_ck_n    ,  //DDR3 时钟负			
    output  [0:0]         ddr3_cke     ,  //DDR3 时钟使能			
    output  [0:0]         ddr3_cs_n    ,  //DDR3 片选			
    output  [0:0]         ddr3_dm      ,  //DDR3_dm			
    output  [0:0]         ddr3_odt        //DDR3_odt			
    );
	
wire					ui_clk				;	
wire					clk_200				;
wire					ui_clk_sync_rst		;
wire					init_done			;
wire		[28:0]		app_addr			;
wire		[2:0]		app_cmd				;
wire					app_en				;
wire		[63:0]		app_wdf_data		;
wire					app_wdf_end			;
wire					app_wdf_wren		;
wire		[63:0]		app_rd_data			;
wire					app_rd_data_end		;  
wire					app_rd_data_valid	;
wire					app_rdy				;
wire					app_wdf_rdy			;
wire		[31:0]		rd_addr_cnt			;
wire		[31:0]      wr_addr_cnt			;
wire					error_flog			;
wire					pll_locked			;
wire					data_state			;

	
reg						sys_rst				;
reg			[31:0]		time_cnt			;
wire					wr_en				;
wire                    rd_en               ;
wire					rst_en				;

always @(posedge clk_200)
	if(!pll_locked)
		time_cnt	<= 32'b0;
	else if(time_cnt == 32'd600_0)
		time_cnt <= time_cnt ;
	else 
		time_cnt <= time_cnt + 1'b1;

always @(posedge clk_200)
	if(!pll_locked)
		sys_rst	<= 1'b1;
	else if(time_cnt == 32'd600_0)
		sys_rst	<= 1'b0;
	else
		sys_rst	<= sys_rst;

  clk_wiz_0 PLL
   (

    .clk_out1(clk_200),     // output clk_out1

    .locked(pll_locked),       // output locked

    .clk_in1(sys_clk)
	);      // input clk_in1

vio_0 vio_0_inst (
  .clk(ui_clk),                // input wire clk
  .probe_in0(),    // input wire [0 : 0] probe_in0
  .probe_out0(wr_en),  // output wire [0 : 0] probe_out0
  .probe_out1(rd_en), // output wire [0 : 0] probe_out1
  .probe_out2(rst_en)  // output wire [0 : 0] probe_out2
);



ddr3_rw  ddr3_rw_inst(

    .ui_clk				(ui_clk)		,		//用户时钟 参考时钟		IP设置Clock Period 800M, 4:1比例ui_clk=200M (FPGA与DDR3传输数据时钟200M)
	.sys_rst			(ui_clk_sync_rst)         ,
	.rst_en				(rst_en),
	.init_done			(init_done)		,		//DDR3初始化完成
	.app_rdy			(app_rdy)			,		//IP核命令接收准备完成信号
	.app_wr_data_rdy	(app_wdf_rdy)		,		//IP核数据接受准备完成信号
	.app_rd_data_valid	(app_rd_data_valid)		,		//IP读数据有效
	.app_rd_data		(app_rd_data)			,		//mig读出的数据 		[63:0]	
	.wr_en				(wr_en)		,		//写使能
	.rd_en				(rd_en)		,		//读使能
                                             
	.app_addr			(app_addr)		,		//地址                  [28:0]	
	.app_en				(app_en)		,		//IP发送命令信号使能									
	.app_wd_wren		(app_wdf_wren)		 	,		//写数据使能
	.app_wd_end			(app_wdf_end)		,		//写突发当前时钟最后一个数据
    .app_cmd			(app_cmd)			,		//读写命令
    .app_data_wr		(app_wdf_data)			,		//写数据			[63:0]
    .rd_addr_cnt		(rd_addr_cnt)			,		//用户读地址计数	[31:0]
    .wr_addr_cnt		(wr_addr_cnt)			,	    //用户写地址计数    [31:0]
	.error_flog			(error_flog)		 	,    //读写错误标志
	.data_state			(data_state)	

);
 mig_7series_0 mig_7series_0_inst (

    .ddr3_addr                      (ddr3_addr),  // output [14:0]	
    .ddr3_ba                        (ddr3_ba),  // output [2:0]		
    .ddr3_cas_n                     (ddr3_cas_n),  // output		
    .ddr3_ck_n                      (ddr3_ck_n),  // output [0:0]	
    .ddr3_ck_p                      (ddr3_ck_p),  // output [0:0]	
    .ddr3_cke                       (ddr3_cke),  // output [0:0]	
    .ddr3_ras_n                     (ddr3_ras_n),  // output		
    .ddr3_reset_n                   (ddr3_reset_n),  // output		
    .ddr3_we_n                      (ddr3_we_n),  // output			
    .ddr3_dq                        (ddr3_dq),  // inout [7:0]		
    .ddr3_dqs_n                     (ddr3_dqs_n),  // inout [0:0]	
    .ddr3_dqs_p                     (ddr3_dqs_p),  // inout [0:0]	
    .init_calib_complete            (init_done),  // output			
	.ddr3_cs_n                      (ddr3_cs_n),  // output [0:0]	
    .ddr3_dm                        (ddr3_dm),  // output [0:0]		
    .ddr3_odt                       (ddr3_odt),  // output [0:0]		
    // Application interface ports
    .app_addr                       (app_addr),  // input [28:0]		
    .app_cmd                        (app_cmd),  // input [2:0]		
    .app_en                         (app_en),  // input				
    .app_wdf_data                   (app_wdf_data),  // input [63:0]		
    .app_wdf_end                    (app_wdf_end),  // input				
    .app_wdf_wren                   (app_wdf_wren),  // input				
    .app_rd_data                    (app_rd_data),  // output [63:0]		
    .app_rd_data_end                (app_rd_data_end),  // output			//突发读当前时钟最后一个数据 
    .app_rd_data_valid              (app_rd_data_valid),  // output			
    .app_rdy                        (app_rdy),  // output			
    .app_wdf_rdy                    (app_wdf_rdy),  // output			
    .app_sr_req                     (1'b0),  // input			该输入被保留并且应该被绑定到0。
    .app_ref_req                    (1'b0),  // input			该高电平有效输入请求向DRAM发出刷新命令。
    .app_zq_req                     (1'b0),  // input			该高电平有效输入请求向DRAM发出ZQ校准命令。
    .app_sr_active                  (),  // output			该输出保留。
    .app_ref_ack                    (),  // output			该输出保留。
    .app_zq_ack                     (),  // output			该输出保留。
    .ui_clk                         (ui_clk),  // output		200M	
    .ui_clk_sync_rst                (ui_clk_sync_rst),  // output			
    .app_wdf_mask                   (8'b0),  // input [7:0]		
    // System Clock Ports
    .sys_clk_i						(clk_200),			//Input Clock Period设置200M,ip核输入200M时钟。	
    .sys_rst                        (!sys_rst) // input 
	
);

endmodule

DDR3读写模块代码

`timescale 1ns / 1ps
module	ddr3_rw(
    input								ui_clk						,		//用户时钟 参考时钟
	input								sys_rst                         ,
	input								rst_en						,
	input								init_done					,		//DDR3初始化完成
	input								app_rdy						,		//IP核命令接收准备完成信号
	input								app_wr_data_rdy				,		//IP核数据接受准备完成信号
	input								app_rd_data_valid			,		//IP读数据有效
	input			[63:0]				app_rd_data					,		//mig读出的数据 		[63:0]	
	input								wr_en						,		//写使能
	input								rd_en						,		//读使能
	
				                                                                                            
	output		reg	[28:0]				app_addr					,		//地址                  [28:0]	
	output								app_en						,		//IP发送命令信号使能									
	output								app_wd_wren				 	,		//写数据使能
	output								app_wd_end					,		//写突发当前时钟最后一个数据
    output								app_cmd						,		//读写命令
    output		reg	[63:0]				app_data_wr					,		//写数据			[63:0]
    output		reg	[31:0]				rd_addr_cnt					,		//用户读地址计数	[31:0]
    output		reg	[31:0]				wr_addr_cnt					,	    //用户写地址计数    [31:0]
	output		reg						error_flog					,	    //读写错误标志
	output		reg						data_state
    );
	parameter DATA_LENGTH = 32'd1000;
	parameter IDLE =	3'b000;
	parameter WRITE = 3'b001;
	parameter WAIT = 3'b010;
	parameter READ = 3'b100;
	
	reg				[2:0]			state	;								//读写状态
	reg				[25:0]			rd_cnt	;								//实际读数据标记
	wire			rst						;
	reg				error					;
	
ila_0 ila_0_inst (
	.clk(ui_clk), // input wire clk
.probe0({init_done,app_rdy,app_rd_data_valid,app_rd_data,app_addr,app_en,app_wd_wren,app_wd_end,error_flog,data_state,app_data_wr,rd_cnt,app_wr_data_rdy,app_cmd,state,wr_en,rd_en,wr_addr_cnt,rd_addr_cnt,rst_en,sys_rst,rst,error}) // input wire [399:0] probe0
);

assign	rst	= rst_en | sys_rst;

assign	app_en	= (((state == WRITE)&&(app_wr_data_rdy&&app_rdy)) ||((state == READ)&&app_rdy)) ? 1'b1:1'b0;
assign	app_wd_wren	= ((state == WRITE)&&(app_wr_data_rdy&&app_rdy)) ? 1'b1:1'b0;
assign	app_wd_end	= app_wd_wren	;
assign	app_cmd	= (state == READ) ? 1'b1:1'b0;

always @(posedge ui_clk or posedge rst)
	if(rst || error_flog)
		begin
			state	<= IDLE;
			wr_addr_cnt	<= 	32'd0;
			rd_addr_cnt	<= 	32'd0;
			app_addr	<=	29'd0;
			app_data_wr	<=  64'b0;
			data_state	<=	1'b0 ;
		end
	else	if(init_done)
		begin
			case(state)
			
				IDLE	:
					begin
						if(wr_en)
							begin
								state	<=	WRITE	;													
							end
						else 
							begin
								state	<= IDLE	;
								wr_addr_cnt	<= 	32'd0;
								rd_addr_cnt	<= 	32'd0;
								app_addr	<=	29'd0;
								app_data_wr	<=  64'b0;
								data_state	<=	1'b0 ;								
							end

					end
					
				WRITE	:
					begin
						if(wr_addr_cnt == DATA_LENGTH - 1'b1)
							state <= WAIT	;
					else
						if(app_wr_data_rdy && app_rdy)
							begin
								wr_addr_cnt <= wr_addr_cnt + 1'b1;
								app_addr	<= app_addr + 29'd8;
								app_data_wr	<= app_data_wr	+ 1'b1;
								data_state	<=	1'b1 ;
							end
					else
						begin
							wr_addr_cnt <= wr_addr_cnt;
						    app_addr	<= app_addr ;
						    app_data_wr	<= app_data_wr;
							data_state	<=	data_state ;
						
						end					
					end
					
				WAIT	:
					begin
						if(rd_en)
							begin
								state <= READ;
								app_addr <=  29'b0;							
							end
						else
							begin
								state <= WAIT;
							    app_addr <=  app_addr;
							
							end
					end
					
				READ	:
					begin
						if(rd_addr_cnt == DATA_LENGTH - 1'b1)
							state <= IDLE ;
						else if(app_rdy)
							begin
								rd_addr_cnt <= rd_addr_cnt + 1'b1;
								app_addr	<= app_addr	+ 29'd8;
							end
						else
							begin
								rd_addr_cnt <= rd_addr_cnt ;
								app_addr	<= app_addr	;							
							end
					end
	
				default	:
					begin
						state	<= IDLE;
						wr_addr_cnt	<= 	32'd0;
						rd_addr_cnt	<= 	32'd0;
						app_addr	<=	29'd0;
						data_state	<=	data_state ;
					end
			endcase

		end
		
always @(posedge ui_clk or posedge rst)
	if(rst)
		rd_cnt <= 26'd0;
	else if(app_rd_data_valid)
		begin
			if(rd_cnt == DATA_LENGTH - 1'b1)
				rd_cnt <= 26'd0;
			else
				rd_cnt <= rd_cnt + 1'b1;		
		end
	else
		rd_cnt <= rd_cnt;	
		
always @(posedge ui_clk or posedge rst)
	if(rst)
		error_flog <= 1'b0;
	else if((state == READ) && app_rd_data_valid)
		begin
			if(rd_cnt != app_rd_data)
				error_flog <= 1'b1;
			else
				error_flog <= 1'b0;		
		end
	else
		error_flog <= 1'b0;	

always @(posedge ui_clk or posedge rst)
	if(rst)
		error <= 1'b0;
	else if(error_flog)
		error <= 1'b1;
	else 
		error <= error;
		
endmodule

DDR3读写实际波形

使用逻辑分析仪查看DDR3写时序。
在这里插入图片描述使用逻辑分析仪查看DDR3读时序。
在这里插入图片描述

DDR4 IP 时钟

在这里插入图片描述

vivado IP核PLL设置

请添加图片描述
请添加图片描述
在这里插入图片描述

X16 DDR4芯片引脚选址

在这里插入图片描述
地址/控制引脚:cs_n,ras_n,we_n等。
字节通道分为T0,T1,T2,T3。每个通道分两小组U和L。
在这里插入图片描述
在这里插入图片描述

X16和X8型号的dqs:必须在U通道上,必须在N6/N7。
dq:不能在N1,N12,但与dps在同一通道。
在这里插入图片描述
dm/dbi:与dqs在同一通道线且只能在N0。
在这里插入图片描述

在这里插入图片描述
一个Bank只能绑定一组DQ。
在这里插入图片描述
reset_n:任何引脚但IO standard必须满足LVCMOS12。
dq低位对应P。高位对应N。dq[0]:p dq[1]:N

### Vivado 中配置和使用随路时钟 #### 配置随路时钟的基本概念 在 FPGA 设计中,随路时钟(也称为伴随时钟)是指与时序路径上的数据信号一起传输的时钟信号。这种机制确保接收端能够正确采样发送的数据流。对于跨时钟域的设计,通常会遇到不同模块由不同的时钟源驱动的情况。 为了处理这种情况,在 Vivado 工具链里推荐的方法不是通过 `set_clock_groups` 来定义逻辑上独立工作的多个时钟区域[^2],而是利用最大延迟(`set_max_delay`)命令来控制跨越两个异步时钟边界的信号线路上的最大传播时间。具体来说,应该设定从源头触发器到目标触发器之间的时间不超过目的时钟的一个周期长度。 #### 实际操作指南 ##### 创建并管理输入时钟资源 当项目涉及到外部提供的参考时钟比如来自 KC705 开发板,则可以通过特定参数调整生成所需的内部工作频率。例如,如果初始输入为 25MHz 的晶振输出,经过适当比例因子设置后可以派生出一对分别为 120MHz 和约 3.9MHz 的同步时钟供后续电路部分选用[^3]。 ##### 设置 I/O 管脚属性 针对具体的硬件平台,依据其物理连接布局图完成对外部接口如复位按钮、LED 显示灯以及最重要的全局/局部时钟输入引脚的选择与绑定是非常必要的步骤之一。这一步骤可以在 Vivado IDE 内部直观地完成,并支持保存自定义映射方案以便重复应用[^4]。 ```tcl # Example Tcl script to assign clock pin and reset pin in Vivado create_project my_design ./my_design -part xc7k325tffg676-2 read_verilog top.v add_files constraints.xdc set_property PACKAGE_PIN E18 [get_ports clk_100mhz] set_property IOSTANDARD LVCMOS33 [get_ports clk_100mhz] set_property PACKAGE_PIN C19 [get_ports rst_btn] set_property IOSTANDARD LVCMOS33 [get_ports rst_btn] ``` 以上代码片段展示了如何在一个基于 Xilinx Kintex-7 系列器件的新工程中指定时钟和重置按键对应的 PCB 上实际焊盘位置,并指定了相应的电气特性标准。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值