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