SDRAM 内部存储体是利用电容能够保持电荷以及可充放电的特 性制成,而电容所存储的电荷会随时间不断流失,会造成存储数据的丢失。为保证 SDRAM 中数据的可靠性,需要对 SDRAM 进行不断刷新。
SDRAM 的刷新方式分为自刷新和自动刷新两种。自动刷新模式:作用是在 SDRAM 的正常操作过程中,保证数据不丢失,自动刷新过 程需要外部时钟的参与,但刷新行地址由内部刷新计数器控制,无需外部写入。 自刷新模式则主要用于休眠模式低功耗状态下的数据保存,自刷新过程无需外部时钟 参与,与自动刷新相同的是,刷新行地址由内部刷新计算器控制,无需外部写入。两者的操作命令相同,当 CKE 信号保持高电平时,写入刷新指令,进入自动刷新模 式;当 CKE 信号为低电平时,写入刷新指令,进入自刷新模式。 自刷新模式下,除 CKE 之外的其他外部信号均无效,当 CKE 再次拉高时,退出自刷 新模式,进入正常操作状态。
自动刷新大致流程如下:
(1) 写入预充电命令,A10 设置为高电平,对所有 L-Bank 进行预充电; (2) 预充电指令写入后,等待 tRP时间,此过程中操作命令保持为空操作命令; (3) tRP等待时间结束后,写入自动刷新命令; (4) 自动刷新命令写入后,等待 tRC时间,此过程中操作命令保持为空操作命令; (5) tRC等待时间结束后,再次写入自动刷新命令; (6) 自动刷新命令写入后,等待 tRC时间,此过程中操作命令保持为空操作命令; (7) tRC等待时间结束后,自动刷新操作完成。不同的等待时间与芯片有关。
本模块的输入信号:init_end 为初始化模块传入的初始化完成标志信号,表示初始化完成, SDRAM 进入正常模式,可进行其他操作;aref_en 为自动刷新使能信号,表示仲裁模块响 应自动刷新请求,自动刷新模块可以开始自动刷新操作。 输出信号:aref_cmd、aref_ba、aref_addr 分别为自动刷新阶段的指令信号、逻辑 Bank 地址和地址总线;aref_req 为自动刷新请求信号,向仲裁模块请求执行自动刷新操作; aref_end 为自动刷新结束标志信号,告知仲裁模块自动刷新操作完成,SDRAM 可进行其他 操作。
SDRAM 的自动刷新操作是周期性执行,这里的时钟为 100MHz,周期计数器的计数范围设置为 0-749,计数时间为 7.5us。
代码如下:
//SDRAM自动刷新模块sdram_aref
module sdram_aref
(
input wire sys_clk , //100MHz
input wire sys_rst_n,
input wire init_end, //初始化结束标志
input wire aref_en, //自动刷新使能信号(由仲裁模块传入,只有为高时才能执行刷新)
output reg aref_req, //自动刷新请求
output reg [3:0] aref_cmd, //输出指令
output reg [1:0] aref_ba, //输出bank地址
output reg [12:0] aref_addr, //地址总线A12-A0
output wire aref_end //自动刷新结束标志
);
parameter P_CHARGE = 4'b0010; //预充电指令
parameter A_REF = 4'b0001; //自动刷新指令
parameter NOP = 4'b0111; //空操作指令
parameter AREF_IDLE = 3'b000 ; //初始状态,等待自动刷新使能
parameter AREF_PCHA = 3'b001 ; //预充电状态
parameter AREF_TRP = 3'b011 ; //预充电等待状态
parameter AUTO_REF = 3'b010 ; //自动刷新状态
parameter AREF_TRF = 3'b100 ; //自动刷新等待状态
parameter AREF_END = 3'b101 ; //自动刷新结束状态
parameter MAX_ref = 'd750 ; //计数的最大值(每7.5us刷新一次)
reg [2:0] STATE_NOW; //现态
reg [2:0] STATE_NEXT; //次态
reg [9:0] cnt_ref ; //计数器位宽
reg [3:0] cnt_state ; //计数器位宽
reg [3:0] MAX_state; //等待状态时需要等待的周期
reg cnt_AUTO_REF = 0; //计数自动刷新状态经过了几次
wire add_cnt_ref ; //计数器开始条件
wire end_cnt_ref ; //计数器结束条件
wire add_cnt_state ; //计数器开始条件
wire end_cnt_state ; //计数器结束条件
//自动刷新计数器--cnt_ref
always@(posedge sys_clk or negedge sys_rst_n)begin
if(!sys_rst_n)begin
cnt_ref <= 0;
end
else if(add_cnt_ref)begin
if(end_cnt_ref)
cnt_ref <= 0;
else
cnt_ref <= cnt_ref + 1;
end
else
cnt_ref <= cnt_ref;
end
assign add_cnt_ref = init_end;//初始化结束后开始计数
assign end_cnt_ref = (add_cnt_ref && (cnt_ref>=MAX_ref-1));
//等待状态计数器--cnt_state
always@(posedge sys_clk or negedge sys_rst_n)begin
if(!sys_rst_n)begin
cnt_state <= 0;
end
else if(add_cnt_state)begin
if(end_cnt_state)
cnt_state <= 0;
else
cnt_state <= cnt_state + 1;
end
else
cnt_state <= cnt_state;
end
assign add_cnt_state = (STATE_NOW==3'b011 || STATE_NOW==3'b100)?1:0;//只有在等待状态才计数
assign end_cnt_state = (add_cnt_state && (cnt_state>=MAX_state-1));
//三段式状态机
//(1)状态切换
always@(posedge sys_clk or negedge sys_rst_n)begin
if(!sys_rst_n)begin
STATE_NOW <= 3'b000; //初始状态,等待自动刷新使能
//STATE_NEXT <= 'd0;
end
else
STATE_NOW <= STATE_NEXT;
end
//(2)状态转移
always@(*)begin
case(STATE_NOW)
3'b000: //初始状态,等待自动刷新使能
begin
if(aref_en)
STATE_NEXT<=3'b001;
else
STATE_NEXT<=STATE_NOW;
end
3'b001: //预充电状态
begin
STATE_NEXT<=3'b011; //直接跳转
end
3'b011: //预充电等待状态
begin
if(end_cnt_state) //等待两个时钟周期
STATE_NEXT<=3'b010;
else
STATE_NEXT<=STATE_NOW;
end
3'b010: //自动刷新状态(需要刷新2次)
begin
STATE_NEXT<=3'b100; //直接跳转
end
3'b100: //自动刷新等待状态
begin
if(end_cnt_state && cnt_AUTO_REF<'d1)begin
STATE_NEXT<=3'b010; //跳回自动刷新状态
//cnt_AUTO_REF<=cnt_AUTO_REF+1;
end
else if(end_cnt_state) //等待7个时钟周期
STATE_NEXT<=3'b101;
else
STATE_NEXT<=STATE_NOW;
end
3'b101: //自动刷新结束状态
begin
STATE_NEXT<=3'b000; //直接跳转
end
default:
STATE_NEXT<=3'b000; //回到空闲态
endcase
end
//(3)输出控制
always@(posedge sys_clk or negedge sys_rst_n)begin
if(!sys_rst_n)begin
MAX_state <= 'd0;
aref_cmd <= NOP; //空操作指令
aref_ba <= 2'b11;
aref_addr <= 13'h1fff; //本阶段无需操作bank和地址,输出全1
end
else
case(STATE_NOW)
3'b000: //初始状态,等待自动刷新使能
begin
MAX_state <= 'd0;
aref_cmd <= NOP; //空操作指令
aref_ba <= 2'b11;
aref_addr <= 13'h1fff;
end
3'b001: //预充电状态
begin
MAX_state <= 'd0;
aref_cmd <= P_CHARGE; //预充电指令
aref_ba <= 2'b11;
aref_addr <= 13'h1fff;
end
3'b011: //预充电等待状态
begin
MAX_state <= 'd2; //等待两个时钟周期
aref_cmd <= NOP; //空操作指令
aref_ba <= 2'b11;
aref_addr <= 13'h1fff;
end
3'b010: //自动刷新状态(需要刷新2次)
begin
MAX_state <= 'd0;
aref_cmd <= A_REF; //自动刷新指令
aref_ba <= 2'b11;
aref_addr <= 13'h1fff;
end
3'b100: //自动刷新等待状态(同样有2次)
begin
MAX_state <= 'd7; //等待7个时钟周期
aref_cmd <= NOP; //空操作指令
aref_ba <= 2'b11;
aref_addr <= 13'h1fff;
cnt_AUTO_REF<=cnt_AUTO_REF+1;
end
3'b101: //自动刷新结束状态
begin
MAX_state <= 'd0;
aref_cmd <= NOP; //空操作指令
aref_ba <= 2'b11;
aref_addr <= 13'h1fff;
end
default:
begin
MAX_state <= 'd0;
aref_cmd <= NOP; //空操作指令
aref_ba <= 2'b11;
aref_addr <= 13'h1fff;
end
endcase
end
always@(posedge sys_clk or negedge sys_rst_n)begin
if(!sys_rst_n)
aref_req <= 0;
else if(STATE_NOW!=3'b000) //当不在空闲态时,说明正处于自动刷新的过程中,因此将自动刷新请求拉低
aref_req <= 0; //自动刷新请求信号拉低
else if(end_cnt_ref) //自动刷新周期到了
aref_req <= 1;
else
aref_req <= aref_req;
end
assign aref_end=(STATE_NOW==3'b101)?1:0; //在自动刷新结束状态产生一个周期的脉冲
endmodule
仿真结果如下:可以看到,执行了两次刷新指令。