一、初始化模块
SDRAM 初始化是芯片上电后必须进行的操作,只进行初始化操作 SDRAM 芯片只能正常使用。SDRAM 除此之外,初始化是一套预定义的过程 他的操作会导致 SDRAM 出现不可预知的后果。
CK:工作时钟,具体时钟频率取决于不同的芯片
CKE:在整个初始化过程中,需要提高时钟使能
COMMAND:SDRAM命令由四根线拼接而成CS#(片选信号),RAS#(行选通信号),CAS#(列通信号),WE#(写使能信号),结合这四条命令线SDRAM地址、输入输出数据等SDRAM各种命令操作
DQM/DQML,DQMU:数据掩码可以通过数据掩码掩埋输入或输出数据的某个人,即使某个人失败
A[9:0],A[12:11]:数据地址线也可用于设置模式寄存器
A10:数据地址线也可用于控制自动预充电、预充电等具体操作bank数量
BA[1:0]:bank地址
DQ数据:无数据输出在初始化过程中保持高阻态
1.上电后保持时钟稳定至少1000us(芯片时间不同)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关闭当前工作行,准备打开新行的操作是预充电。 预充电可以由独立的命令控制,也可以在每次发送读写命令时使用A线路控制自动预充电。事实上,预充电是一种重写工作中所有存储阵列的数据,并行地址进行复位,为新行做准备。
除初始化过程中使用预充电指令外,还将在自动刷新和读写操作中使用。
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:CAS1、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:发送模式寄存器设置指令状态,只维持一个时钟周期,下一个时钟跳转到状态INITTMRD,在此状态发送模式寄存器设置指令 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