SDRAM 控制器(二)——初始化模块

1、初始化模块

        SDRAM 的初始化是芯片上电后必须进行的一项操作,只有进行了初始化操作的 SDRAM 芯片才可被正常使用。SDRAM 的初始化是一套预先定义好的流程,除此之外的其 他操作会导致 SDRAM 出现不可预知的后果。

初始化时序图:

        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.上电后需保持时钟稳定状态至少100us(各芯片时间不同),同时CKE需拉高;同时需发送NOP空指令(发送空指令是为了防止对SDRAM进行误操作)

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

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

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

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

        6.重复进行发送自动刷新指令与等待tRFC,芯片不同刷新次数不同

        7.发送模式寄存器设置指令,地址总线 A0-A11 参数不同 辅助模式寄存器不同模式的设置

        8.发送模式寄存器设置指令后需要等待一定的时间、即tMRD,在此期间同样需要发送NOP空指令(发送空指令是为了防止对SDRAM进行误操作)

        9.tMRD等待时间结束后,SDRAM 初始化完成

补充:

预充电
        SDRAM的寻址具有独占性,所以在进行完读写操作后,如果要对同一个Bank的另一行进行寻址,就要将原来有效(ACTIVE)的行关闭,重新发送行/列地址。Bank关闭当前工作行,准备打开新行的操作就是预充电。 预充电可以通过独立的命令控制,也可以在每次发送读写命令的同时使用“A10”线控制自动进行预充电。实际上,预充电是一种对工作中所有存储阵列进行数据重写,并对行地址进行复位,以准备新行的工作。

        预充电指令除了在初始化过程中用到外,在自动刷新以及读写操作中也都会被用到。

自动刷新
        SDRAM 内部存储体是利用电容能够保持电荷以及可充放电的特性制成,而电容所存储的电荷会随时间不断流失,会造成存储数据的丢失。为保证 SDRAM 中数据的可靠性,需要对 SDRAM 进行不断刷新。

        刷新操作分两种:“自动刷新”和“自刷新”。发送命令后CKE时钟为有效时(低电平),使用自动刷新操作,否则使用自我刷新操作。不论使用何种刷新方式,都不需要外部提供地址信息,因为这个是内部操作。

        对于“自动刷新”,SDRAM内部有一个行地址生成器(也称刷新计数器)用来自动地依次生成行地址,每收到一次命令刷新一行。在刷新过程中,所有的Bank都停止工作,而每次刷新所占用的时间为N个时钟周期。刷新结束后才可进入正常的工作状态,也就是说在这N个时钟期间内,所有工作指令只能等待而无法执行。一次次的按行刷新,刷新完所有行后,将再一次对第一行重新进行刷新操作,这个对同一行刷新操作的时间间隔,成为SDRAM的刷新周期,通常为64ms。显然,刷新会对SDRAM的性能造成影响,但这是SDRAM的特性决定的,也是SDRAM相对于SRAM取得成本优势的同时所付出的代价。

         “自刷新”则主要用于休眠模式低功耗状态下的数据保存,也就是说即使外部控制器件不工作了,SDRAM都能自己确保数据正常。在发出“自我刷新”命令后,将CKE置于无效状态(低电平),就进入自我刷新模式。此时不再依靠外部时钟工作,而是根据SDRAM内部的时钟进行刷新操作。在自我刷新期间,除了CKE之外的所有外部信号都是无效的,只有重新使CKE有效才能退出自我刷新模式并进入正常操作状态。
        因为我们平常控制SDRAM都是在正常工作状态下使用,所以一般是对SDRAM进行自动刷新操作。

模式寄存器配置:

        通过对SDRAM模式寄存器的配置可以实现对其各种工作方式、参数的控制,如突发长度BL、读潜伏期CL等。

        A12-A10:预留
        A9:读写方式,0:突发读&突发写;1:突发读&单写
        A8,A7:00:标准模式,默认
        A6,A5,A4:CAS潜伏期,分别为1、2、3、保留
        A3:突发传输方式,0:顺序;1:隔行
        A2,A1,A0:突发长度,000:1、2、4、8、全页

等待时间参数:
以下时间参数根据芯片的不同可能存在差异:

        tRP:PRECHARGE  command  period,发送预充电指令后进行下一个操作需要等待的时间

        tRFC:AUTO  REFRESH  period,发送自动刷新指令后进行下一个操作需要等待的时间

        tMRD:LOAD MODE REGISTER command to ACTIVE or REFRESH command,发送设置模式寄存器指令后进行下一个操作需要等待的时间

初始化模块可以使用状态机实现:

INIT_IDLE:上电等待状态,等待时间满足100us要求后,跳转至下一状态INIT_PRE,在此状态发送NOP指令
INIT_PRE:发送预充电指令状态、只维持一个时钟周期、下个时钟就跳转到状态INIT_TRP ,在此状态发送预充电指令
INIT_TRP:预充电指令等待状态、在此状态等待时间满足TRP后就跳转到下一个状态INIT_AR,在此状态发送NOP指令  
INIT_AR:发送自动刷新指令状态、只维持一个时钟周期、下个时钟就跳转到状态INIT_TRFC,在此状态发送自动刷新指令    
INIT_TRFC:自动刷新指令等待状态、在此状态等待时间满足TRFC时进行判断,若自动刷新次数满足要求(2次或其他手册要求)后就跳转到下一个状态INIT_MRS,在此状态发送NOP指令,若不满足自动刷新次数要求就继续进行自动刷新操作,跳转到状态INIT_AR
INIT_MRS:发送模式寄存器设置指令状态、只维持一个时钟周期、下个时钟就跳转到状态INIT_TMRD,在此状态发送模式寄存器设置指令     
INIT_TMRD:模式寄存器设置指令等待状态、在此状态等待时间满足TMRD后就跳转到下一个状态INIT_END,在此状态发送NOP指令    
INIT_END:初始化结束状态,完成初始化后一直停留在这个状态;在此状态发送NOP指令,并将初始化完成信号拉高以通知其他模块开始进行工作

时序图:

 仿真结果:

端口:

信号名称         位宽       属性            描述
init_clk              1           输入      100M时钟信号
init_rst_n          1           输入       复位信号,低电平有效
init_addr         13           输出       SDRAM地址总线
init_cmd           4           输出       SDRAM命令,组成{CS#,RAS#,CAS#,WE#}
init_bank          2           输出       BANK地址,共4个BANK
init_end            1           输出       初始化完成信号,初始化完成后拉高,其他时间保持低电平

代码:

//----------------------------------------------------------------------------------------------------
//--SDRAM初始化模块
//----------------------------------------------------------------------------------------------------
module SDRAM_INIT(
		input              sys_clk				,
		input		       sys_rst_n 			,
		output reg [3:0]   init_cmd    			,
		output reg [1:0]   init_ba				,
		output reg [12:0]  init_addr			,
		output		       init_end		
);

parameter  		INIT_IDLE = 3'b000,
				INIT_PRE  = 3'b001,
				INIT_TRP  = 3'b011,
				INIT_AR   = 3'b010,
				INIT_TRFC = 3'b110,
				INIT_MRS  = 3'b111,
				INIT_TMRD = 3'b101,
				INIT_END  = 3'b100;
			  
parameter 	  WAIT_MAX = 15'd20_000;

parameter     TRP   =  3'd2,
			  TRFC  =  3'd7,
			  TMRD  =  3'd3;

parameter     NOP        = 4'b0111,
			  P_CHARGE = 4'b0010,
			  AUTO_REF   = 4'b0001,
			  M_REG_SET = 4'b0000;


wire				wait_end			;
wire				TRP_end				;
wire				TRFC_end			;
wire				TMRD_end			;


reg 	[2:0]       init_state			;
reg     [14:0]		cnt_200us			;
reg		[2:0]		cnt_clk				;
reg					cnt_clk_rst			;
reg		[3:0]		cnt_aref			;
		
always @(posedge sys_clk or negedge sys_rst_n)
	if(!sys_rst_n)
		init_state <= INIT_IDLE;
	else  begin
		case(init_state)
			INIT_IDLE  :
				if(wait_end == 1'b1)
					init_state <= INIT_PRE   ;
				else;
			INIT_PRE   :   
				init_state <= INIT_TRP		 ;
            INIT_TRP   :
				if(TRP_end == 1'b1)
					init_state <= INIT_AR    ;
				else;
            INIT_AR    :
				init_state <= INIT_TRFC		 ;
            INIT_TRFC  :
				if(TRFC_end == 1'b1 )
					if(cnt_aref == 4'd8)
						init_state <= INIT_MRS	 ;
					else
						init_state <= INIT_AR	 ;
				else;
            INIT_MRS   :
				init_state <= INIT_TMRD		 ;
            INIT_TMRD  :
				if(TMRD_end == 1'b1)
					init_state <= INIT_END	 ;
				else;
            INIT_END   :
				init_state <= INIT_END		 ;
			default : init_state <= INIT_IDLE;
		endcase
	end
//上电等待200us
always @(posedge sys_clk or negedge sys_rst_n)
	if(!sys_rst_n)
		cnt_200us <= 15'd0;
	else if(cnt_200us == WAIT_MAX)
		cnt_200us <= WAIT_MAX;
	else
		cnt_200us <= cnt_200us + 1'b1;

//200us标志信号计数器
assign  wait_end = (cnt_200us == WAIT_MAX - 1'b1)? 1'b1 : 1'b0;


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(init_state)
		INIT_IDLE : cnt_clk_rst <= 1'b1;
		INIT_TRP  : cnt_clk_rst <= (TRP_end == 1'b1)? 1'b1 : 1'b0 ;
	    INIT_TRFC : cnt_clk_rst <= (TRFC_end == 1'b1)? 1'b1 : 1'b0;
		INIT_TMRD : cnt_clk_rst <= (TMRD_end == 1'b1)? 1'b1 : 1'b0;
		INIT_END  : cnt_clk_rst <= 1'b1;
		default : cnt_clk_rst <= 1'b0  ;
	endcase
end
//如何判断先后顺序?不看状态只看cnt_clk计数?应该是在各自的状态下才进行计数?是的,后边修改了
assign  TRP_end  = (init_state == INIT_TRP && cnt_clk == TRP) ? 1'b1 : 1'b0;
assign  TRFC_end = (init_state == INIT_TRFC && cnt_clk == TRFC)? 1'b1 : 1'b0;
assign	TMRD_end = (init_state == INIT_TMRD && cnt_clk == TMRD)? 1'b1 : 1'b0;

//自动刷新次数计数  刷新次数为8    1,2,3,4,5,6,7,8共8次。
always @(posedge sys_clk or negedge sys_rst_n)
	if(!sys_rst_n)
		cnt_aref <= 4'd0;
	else if(init_state == INIT_IDLE)
		cnt_aref <= 4'd0;
	else if(init_state == INIT_AR)
		cnt_aref <= cnt_aref + 1'b1;
	else
		cnt_aref <= cnt_aref;

always @(posedge sys_clk or negedge sys_rst_n)
	if(!sys_rst_n)
		begin
			init_cmd    <=  NOP		;
			init_ba     <= 2'b11	;
			init_addr   <= 13'h1fff ;
		end
	else
		case(init_state)
			INIT_IDLE,INIT_TRP,INIT_TRFC,INIT_TMRD,INIT_END : 
				begin 
					init_cmd    <=  NOP		 ;
					init_ba     <=  2'b11	 ;
					init_addr   <=  13'h1fff ;
				end
			INIT_PRE :
				begin 
					init_cmd    <=  P_CHARGE	 ;
					init_ba     <=  2'b11	 ;
					init_addr   <=  13'h1fff ;
				end
			INIT_AR  :
				begin 
					init_cmd    <=  AUTO_REF ;
					init_ba     <=  2'b11	 ;
					init_addr   <=  13'h1fff ;
				end			
			INIT_MRS :
				begin 
					init_cmd    <=  M_REG_SET ;
					init_ba     <=  2'b00	 ;
					init_addr   <=  {3'b000,1'b0,2'b00,3'b011,1'b0,3'b111} ;
				end
			default : 
				begin 
					init_cmd    <=  NOP		 ;
					init_ba     <=  2'b11	 ;
					init_addr   <=  13'h1fff ;
				end				
			endcase
assign  init_end = (init_state == INIT_END) ? 1'b1 : 1'b0;

endmodule

仿真代码:

//----------------------------------------------------------------------------------------------------
//--SDRAM初始化仿真测试
//----------------------------------------------------------------------------------------------------
`timescale 1ns/1ns
module SDRAM_INIT_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	;




reg   sys_clk 			;
reg   sys_rst_n			;



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 ;	

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_model_plus  sdram_model_plus_inst
(				
				.Dq     ()     		  	  , 
				.Addr	(init_addr)       , 
				.Ba		(init_ba)		  , 
				.Clk	(clk100m_shift)	  , 
				.Cke	(1'b1)		 	  , 
				.Cs_n	(init_cmd[3])     , 
				.Ras_n	(init_cmd[2])	  , 
				.Cas_n	(init_cmd[1])     , 
				.We_n	(init_cmd[0])     , 
				.Dqm	(2'b00)		 	  ,
				.Debug  (1'b1)
);                      


endmodule

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

发光中请勿扰

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

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

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

打赏作者

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

抵扣说明:

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

余额充值