SDRAM 控制器(三)——自动刷新模块

1、知识点

1.1 刷新

        SDRAM 内部存储体是利用电容能够保持电荷以及可充放电的特性制成,而电容所存储的电荷会随时间不断流失,会造成存储数据的丢失。为保证 SDRAM 中数据的可靠性,需要对 SDRAM 进行不断刷新。SDRAM 的刷新方式分为自刷新和自动刷新两种,这两种刷新方式,在实现和作用上存在差异。

        自动刷新模式:作用是在 SDRAM 的正常操作过程中,保证数据不丢失,自动刷新过程需要外部时钟的参与,但刷新行地址由内部刷新计数器控制,无需外部写入。
        自刷新模式则主要用于休眠模式低功耗状态下的数据保存,自刷新过程无需外部时钟参与,与自动刷新相同的是,刷新行地址由内部刷新计算器控制,无需外部写入。
        两者的操作命令相同,当 CKE信号保持高电平时,写入刷新指令,进入自动刷新模式;当CKE 信号为低电平时,写入刷新指令,进入自刷新模式。

        目前国际公认的标准是,存储体中电容的数据有效保存期上限是 64ms, 也就是说每一行刷新的循环周期最大为 64ms,那么刷新速度就是:行数/64ms。我们在 SDRAM 的数据手册中经常会看到 4096 Refresh Cycles/64ms 或 8192 Refresh Cycles/64ms 的相关介绍, 这里的 4096 与 8192 就代表SDRAM 芯片中单个 L-Bank 的行数。刷新命令一次对一行有效,发送间隔也是随总行数而变化, 当单个 L-Bank 为 4096 行时,刷新间隔最大为 15.625μs,单个 L-Bank 为 8192 行时,刷新间隔最大为 7.8125μs。(但是通常不会在边缘刷新,所以我们采用7.5us刷新一次)

1.2 自动刷新

        SDRAM的所有操作都需要在完成初始化操作后才能进行,自动刷新操作自然也不例外。下图是MT48LC64M4A2的自动刷新时序图:

各信号说明如下:

        CK:工作时钟,具体时钟频率视不同的芯片而不同 

        CKE:时钟使能,在整个初始化过程中都需要拉高

        COMMAND:SDRAM命令,由4根线拼接而成,分别是CS#(片选信号),RAS#(行选通信号),CAS#(列选通信号),WE#(写使能信号),通过这4根命令线,再结合SDRAM的地址、输入输出数据等,就可以对SDRAM进行各种命令操作

        DQM/DQML,DQMU:数据掩码,通过数据掩码可以实现对输入或输出数据的某一位进行“掩埋”,也就是使某一位失效

        A[9:0],A[12:11]:数据地址线,同时也可用来设置模式寄存器

        A10:数据地址线,同时也可以用来使能一些具体操作,比如控制自动预充电使能、使能预充电bank数量

        BA[1:0]:bank地址

        DQ数据:在初始化过程中无数据输出,保持高阻态就行

补充:

        1.对所有BANK进行预充电操作,A10拉高即是选中所有BANK

        2.进行预充电操作后需要等待一定的时间,即tRP,在此期间同样需要发送NOP空指令(发送空指令是为了防止对SDRAM进行误操作)

        3.等待结束后发送自动刷新指令

        4.进行自动刷新操作后需要等待一定的时间,即tRC,在此期间同样需要发送NOP空指令(发送空指令是为了防止对SDRAM进行误操作)

        5.重复进行发送自动刷新指令与等待tRFC

        6.tRFC等待时间结束后,SDRAM 自动刷新完成

2、设计

模块图及端口:

在这里插入图片描述

时序图:

波形:

①完成初始化

 ②寄存器cnt_ref 最大时479,然后拉高aref_req,接着一个时钟后拉高aref_en,下一个时钟拉高aref_ack,下一个时钟拉低aref_req

 ③状态机

总结:

1、所有的操作都需要在完成初始化后进行,即init_end信号为高电平,才可以完成其他操作;

2、周期性自动刷新,需要定义一个寄存器cnt_ref,对刷新时间间隔进行计数;

3、cnt_ref计数到最大值max,完成一次计数后,输出自动刷新的请求信号aref_req,并发送给仲裁模块,仲裁模块同意请求后发送自动刷新aref_en;

4、内部收到aref_en后产生一个响应信号aref_ack,保持一段时间高电平,响应信号有效时拉低请求信号;

5、利用状态机(6个状态)实现        

 AREF_IDLE:初始状态,直到初始化完成后且仲裁模块发送自动刷新使能信号后,跳转至下一状态AREF_PCH,在此状态发送NOP指令
AREF_PCH:发送预充电指令状态、只维持一个时钟周期、下个时钟就跳转到状态AREF_TRP,在此状态发送预充电指令
AREF_TRP:预充电指令等待状态、在此状态等待时间满足TRP后就跳转到下一个状态AUTO_REF,在此状态发送NOP指令  
AUTO_REF:发送自动刷新指令状态、只维持一个时钟周期、下个时钟就跳转到状态AREF_TRF,在此状态发送自动刷新指令    
AREF_TRF:自动刷新指令等待状态、在此状态等待时间满足TRF时进行判断,若自动刷新次数满足要求(2次)后就跳转到下一个状态AREF_END,在此状态发送NOP指令,若不满足自动刷新次数要求就继续进行自动刷新操作,跳转到状态AUTO_REF
AREF_END:自动刷新结束状态,完成自动刷新后停留在这个状态一个周期;在此状态发送NOP指令,并发送一个自动刷新完成脉冲信号以通知仲裁模块自动刷新结束

代码:

//----------------------------------------------------------------------------------------------------
//--SDRAM自动刷新模块
//----------------------------------------------------------------------------------------------------
module SDRAM_AR
(
			input                 sys_clk			,
			input	              sys_rst_n			,
			input	              init_end			,
			input                 aref_en			,
			output	reg  [3:0]    aref_cmd			,
			output	reg  [1:0]	  aref_ba			,
			output  reg  [12:0]	  aref_addr			,
			output				  aref_end			,
			output	reg			  aref_req			
);

parameter   CNT_REF_MAX  =  10'd749		;

parameter   AREF_IDLE   =    3'b000		,
			AREF_PCH    =    3'b001		,
			AREF_TRP    =    3'b011		,
			AUTO_REF    =    3'b010		,
			AREF_TRF    =    3'b110		,
			AREF_END    =    3'b111		;

parameter     TRP    =  3'd2,
			  TRFC   =  3'd7;
			  
parameter     NOP      = 4'b0111,
			  P_CHARGE = 4'b0010,
			  A_REF    = 4'b0001;



wire 			aref_ack				;
wire 			TRP_end					;
wire			TRFC_end					;

reg     [9:0]	cnt_ref					;
reg 	[2:0] 	aref_state				;
reg 	[2:0]	cnt_clk					;
reg 			cnt_clk_rst				;

reg 	[1:0]   cnt_aref				;

//自动刷新计数器,每计数到一次最大值清零,表示需要进行一次刷新操作
always @(posedge sys_clk or negedge sys_rst_n)
	if(!sys_rst_n)
		cnt_ref <= 10'd0;
	else if(cnt_ref >= CNT_REF_MAX )
		cnt_ref <= 10'd0;
	else if(init_end == 1'b1)
		cnt_ref <= cnt_ref + 1'b1;
		
//自动刷新请求信号,每次计数到时间就发起自动刷新信号,发送出自动刷新指令后拉低	
always @(posedge sys_clk or negedge sys_rst_n)
	if(!sys_rst_n)
		aref_req <= 1'b0;
	else if(cnt_ref == CNT_REF_MAX - 1'b1) //提前一个时钟周期拉高
		aref_req <= 1'b1;
	else if(aref_ack == 1'b1)
		aref_req <= 1'b0;

//发送预充电时,即代表自动刷新模块响应了仲裁模块给出的自动刷新使能,所以拉高aref_ack	
assign  aref_ack = (aref_state == AREF_PCH)	? 1'b1 :1'b0;


always @(posedge sys_clk or negedge sys_rst_n)
	if(!sys_rst_n)
		aref_state <= AREF_IDLE;
	else
			case(aref_state)
				AREF_IDLE :
					if(init_end == 1'b1 && aref_en == 1'b1)
						aref_state <= AREF_PCH;
					else;
			    AREF_PCH  :
					aref_state <= AREF_TRP;
	            AREF_TRP  :
					if(TRP_end == 1'b1 )
						aref_state <= AUTO_REF;
					else;
                AUTO_REF   :
					aref_state <= AREF_TRF;
                AREF_TRF  :
					if(TRFC_end == 1'b1)
						if(cnt_aref == 2'd2)
							aref_state <= AREF_END;
						else
							aref_state <= AUTO_REF;
					else;
                AREF_END :
					aref_state <= AREF_IDLE;
				default : aref_state <= AREF_IDLE;
			endcase
			
//用于计数各个状态以实现状态跳转,计数复位信号cnt_clk_rst有效时复位,其他时间累加
always @(posedge sys_clk or negedge sys_rst_n)
	if(!sys_rst_n)
		cnt_clk <= 3'd0;
	else if(cnt_clk_rst == 1'b1)
		cnt_clk <= 3'd0;
	else
		cnt_clk <= cnt_clk + 1'b1;

//工作状态计数器的复位信号
always @(*)begin 
	case(aref_state)
		AREF_IDLE : cnt_clk_rst <= 1'b1;
		AREF_TRP  : cnt_clk_rst <= (TRP_end == 1'b1)? 1'b1 : 1'b0 ;
	    AREF_TRF : cnt_clk_rst <= (TRFC_end == 1'b1)? 1'b1 : 1'b0  ;
		AREF_END : cnt_clk_rst <= 1'b1;
		default : cnt_clk_rst <= 1'b0  ;
	endcase
end
//因为状态跳转是时序逻辑,所以在前一个周期拉高时间等待参数的标志信号,用来进行状态跳转
assign  TRP_end  = (aref_state == AREF_TRP && cnt_clk == TRP) ? 1'b1 : 1'b0;
assign  TRFC_end = (aref_state == AREF_TRF && cnt_clk == TRFC)? 1'b1 : 1'b0;		

//刷新次数计数器,每进一次状态(INIT_AR,自动刷新指令)则+1,
always @(posedge sys_clk or negedge sys_rst_n)
	if(!sys_rst_n)
		cnt_aref <= 2'd0;
	else if(aref_state == AREF_IDLE)
		cnt_aref <= 2'd0;
	else if(aref_state == AUTO_REF)
		cnt_aref <= cnt_aref + 1'b1;
	else;


always @(posedge sys_clk or negedge sys_rst_n)
	if(!sys_rst_n)
		begin
			aref_cmd    <=  NOP		;
			aref_ba     <= 2'b11	;
			aref_addr   <= 13'h1fff ;
		end
	else
		case(aref_state)
			AREF_IDLE,AREF_TRP,AREF_TRF: 
				begin 
					aref_cmd    <=  NOP		 ;
					aref_ba     <=  2'b11	 ;
					aref_addr   <=  13'h1fff ;
				end
			AREF_PCH :
				begin 
					aref_cmd    <=  P_CHARGE	 ;
					aref_ba     <=  2'b11	 ;
					aref_addr   <=  13'h1fff ;
				end
			AUTO_REF  :
				begin 
					aref_cmd    <=  A_REF ;
					aref_ba     <=  2'b11	 ;
					aref_addr   <=  13'h1fff ;
				end			
			AREF_END :
				begin 
					aref_cmd    <=  NOP ;
					aref_ba     <=  2'b00	 ;
					aref_addr   <=  13'h1fff;
				end
			default : 
				begin 
					aref_cmd    <=  NOP		 ;
					aref_ba     <=  2'b11	 ;
					aref_addr   <=  13'h1fff ;
				end				
			endcase
assign  aref_end = (aref_state == AREF_END) ? 1'b1 : 1'b0;

endmodule

仿真代码:

//----------------------------------------------------------------------------------------------------
//--SDRAM自动刷新模块仿真测试
//----------------------------------------------------------------------------------------------------
`timescale 1ns/1ns
module SDRAM_AR_tb();

wire   	clk50m			;
wire    clk100m			;
wire    clk100m_shift	;
wire    locked			;
wire    rst_n  			;

wire [3:0]  init_cmd	;
wire [1:0]	init_ba		;
wire [12:0] init_addr	;
wire		init_end	;

wire  [3:0]        aref_cmd	    ;
wire  [1:0]	       aref_ba	    ;
wire  [12:0]       aref_addr	;
wire      		   aref_end	    ;
wire       		   aref_req	    ;

wire  [3:0]        sdram_cmd	;
wire  [1:0]	       sdram_ba	    ;
wire  [12:0]       sdram_addr	;


reg   sys_clk 		;
reg   sys_rst_n		;
reg   aref_en		;


initial 
	begin 
		sys_clk  = 1'b1;
		sys_rst_n 	<= 1'b0;
		#30
		sys_rst_n	<= 1'b1;
	end
always #10  sys_clk <= ~sys_clk;	
		 
assign rst_n = sys_rst_n & locked ;	

//产生自动刷新使能信号
always @(posedge clk100m or negedge rst_n)
	if(!rst_n)
		aref_en <= 1'b0;
	else if(init_end == 1'b1 && aref_req == 1'b1)
		aref_en <= 1'b1;
	else if(aref_end == 1'b1)
		aref_en <= 1'b0;
		
assign  	sdram_cmd = (init_end == 1'b1)? aref_cmd : init_cmd	;
assign  	sdram_ba = (init_end == 1'b1)? aref_ba : init_ba	;	
assign  	sdram_addr = (init_end == 1'b1)? aref_addr : init_addr	;		
		

//重定义仿真模型中的相关参数
defparam    sdram_model_plus_inst.addr_bits = 13;
defparam    sdram_model_plus_inst.data_bits = 16;
defparam    sdram_model_plus_inst.col_bits = 9;
defparam    sdram_model_plus_inst.mem_sizes = 2*1024*1024;

//------------<例化被测试模块>----------------------------------------
clk_gen	clk_gen_inst
(
	.areset ( ~sys_rst_n),
	.inclk0 ( sys_clk ),
	.c0 ( clk50m	 ),
	.c1 ( clk100m	 ),
	.c2 ( clk100m_shift ),
	.locked	( locked )
);


SDRAM_INIT SDRAM_INIT_inst(
		.  sys_clk		(clk100m)	,
		.  sys_rst_n 	(rst_n)		,
		.  init_cmd    	(init_cmd)	,
		.  init_ba		(init_ba)	,
		.  init_addr	(init_addr)	,
		.  init_end		(init_end)
);      

SDRAM_AR SDRAM_AR_inst
(
			 .sys_clk		( clk100m)	,
			 .sys_rst_n		( rst_n)	,
			 .init_end		(init_end )	,
			 .aref_en		( aref_en)	,
			 .aref_cmd	    (aref_cmd )	,
			 .aref_ba	    (aref_ba )	,
			 .aref_addr	    (aref_addr)	,
			 .aref_end		(aref_end)	,
			 .aref_req		(aref_req)	
);




sdram_model_plus  sdram_model_plus_inst
(				
				.Dq     ()       			 , 
				.Addr	(sdram_addr)       	 , 
				.Ba		(sdram_ba)		 	 , 
				.Clk	(clk100m_shift)		 , 
				.Cke	(1'b1)				 , 
				.Cs_n	(sdram_cmd[3])	     , 
				.Ras_n	(sdram_cmd[2])	     , 
				.Cas_n	(sdram_cmd[1])	     , 
				.We_n	(sdram_cmd[0])       , 
				.Dqm	(2'b00)		  		 ,
				.Debug  (1'b1)
);                      


endmodule

  • 2
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

发光中请勿扰

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

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

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

打赏作者

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

抵扣说明:

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

余额充值