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