DDR2/3 SDRAM学习笔记

随路时钟概念

Dq(数据总线)与Dqs(时钟信号)构成随路时钟。
在Dqs上升或下降沿翻转Dq数据总线信号。
注意:dq与dqs为双线信号。两个信号都由发送方发送。dqs为数据同步信号。
DDR2/3 SDRAM学习笔记_第1张图片
ODT引脚:片上终端电阻,数据线接上拉电阻。

DDR与DDR2区别

时钟

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

片外驱动调校(OCD)

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

片内终结(ODT)

DDR2:ODT功能让信号被电路的终端吸收,不会在电路上形成反射对后面信号造成影响。ODT将终结电阻移植到芯片内部。
DDR2/3 SDRAM学习笔记_第2张图片

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

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

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

DDR2/3 SDRAM学习笔记_第3张图片
tDQSS:发出写指令到写数据之间的时间。
DDR2/3 SDRAM学习笔记_第4张图片

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

使用突发长度时读写只能顺序地址;不使用读写长度时,读写地址可随机但是要连续写指令。
DDR2/3 SDRAM学习笔记_第5张图片

DDR2/3 SDRAM学习笔记_第6张图片

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设置

DDR2/3 SDRAM学习笔记_第7张图片
DDR2/3 SDRAM学习笔记_第8张图片

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

DDR3读写实验

DDR3模块设计

DDR2/3 SDRAM学习笔记_第9张图片

DDR3时序设计

DDR2/3 SDRAM学习笔记_第10张图片

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写时序。
DDR2/3 SDRAM学习笔记_第11张图片使用逻辑分析仪查看DDR3读时序。
DDR2/3 SDRAM学习笔记_第12张图片

DDR4 IP 时钟

DDR2/3 SDRAM学习笔记_第13张图片

vivado IP核PLL设置

DDR2/3 SDRAM学习笔记_第14张图片
DDR2/3 SDRAM学习笔记_第15张图片
DDR2/3 SDRAM学习笔记_第16张图片

你可能感兴趣的:(学习,fpga开发)