自动刷新模块
由于电容存储电荷会随时间流逝,所以要对电容进行刷新也就是充电,防止数据丢失。
时序分析
自动刷新: 为了保证数据不丢失,自动刷新需要外部时钟参与,刷新行地址是由内部刷新计数器控制。
自刷新是sdram休眠模式,低功耗下数据保存,不需要外部时钟。
刷新命令都为0001。 时钟使能信号为高电平 再写入命令进入自动刷新,时钟使能信号为低电平,写入命令进入自刷新模式。
开始自刷新时,要对所有bank进行预充电,写入预充电指令后等待一段时间tRP,写入刷新指令,这时候时钟使能信号为低电平,进入自刷新模式,时钟信号拉高,退出自刷新模式。随后就可以进行正常操作。
自动刷新时序:
要对所有bank进行预充电,A10为高电平对所有bank进行预充电。等待一段时间tRP,时钟使能信号一直为高电平,并在等待过程中写入NOP空操作指令,防止其他指令写入。结束后写入自动刷新命令,等待tRFC时间写入NOP,需要自动刷新两次,完成两次操作。
所有正常的操作都需要在SDRAM完成初始化后进行,所以引入init_end信号。
init_end高电平时,才可以进行其他操作。
计数器cnt_ref对刷新时间进行间隔,计数器初值为0,当完成初始化操作后就可以开始计数,计数值为1-MAX。
完成计数后发出自动刷新请求信号,发送到仲裁模块,告知仲裁模块。aref_req。 从0开始计数,在MAX-1开始拉高。
随后仲裁模块接收到aref_req,会做出判断,发送给自动刷新模块aref_en信号。
接收到有效自动刷新使能信号aref_en,作出响应,aref_ack信号保持一个周期高电平。
状态机来实现自动刷新的实现。
定义状态变量aref_state
初始状态 IDLE
预充电状态AREF_PCH 随后进行等待AREF_TRP
自动刷新AUTO_PCH 等到AREF_TRF
自动刷新AUTO_PCH 等到AREF_TRF
AREF_END 回到IDLE状态
等待时间需要计数器来实现,cnt_clk来计数周期,参考技术手册查看计数周期。
计数使能信号cnt_clk_rst,高电平清零,低电平计数,完成计数后清零。
trp_end 等待时间做判断
trf_end
cnt_tref 对自动刷新次数做判断,每完成一次归零
四路输出信号波形图的绘制:
aref_cmd 命令,一开始写入nop,写入预充电指令,等待过程中写入nop,自动刷新命令,等待过程中写入nop,写入自动刷新命令,写入nop。
aref_ba bank地址一直为全1就行 2'b11
aref_addr 地址总线也为全1 , 13'h1fff
aref_end 自动刷新结束时,拉高一个时钟周期。
编写模块代码:
分析:
刷新时间间隔cnt_ref
//cnt_ref为刷新的时间间隔 13位宽 2^13 = 8192行,刷新时间间隔为64ms //64ms/8192 = 7812.5ns. 当前时钟信号为100mhz,每个周期10ns,也就是781.25个周期刷新一次 //当仲裁模块发送仲裁请求信号后还需要等待一段时间,所以7500/10,完成750个周期的计数即可。
程序:
input wire sys_clk,
input wire sys_rst_n,
input wire init_end,
input wire aref_en,
output reg [3:0] aref_cmd,
output reg [1:0] aref_ba,
output reg [12:0] aref_addr,
output wire aref_end,
output reg aref_req
);
parameter CNT_REF_MAX = 10'd749;
parameter TRP = 3'd2,
TRF = 3'd7;
//声明状态机状态,使用格雷码进行定义
parameter AREF_IDLE = 3'd000,
AREF_PCH = 3'd001,
AREF_TRP = 3'd011,
AREF_REF = 3'd010,
AREF_TRF = 3'd110,
AREF_END = 3'd111;
//四个命令 空命令 预充电 自动刷新 模式寄存器配置
parameter NOP = 4'b0111,
P_CHARGE =4'b0010,
AUTO_REF =4'b0001;
reg [9:0] cnt_ref;
wire aref_ack;
reg [2:0] aref_state;
reg [2:0] cnt_clk;
reg cnt_clk_rst;
wire trp_end;
wire trf_end;
reg [1:0]cnt_aref;
//cnt_ref为刷新的时间间隔 13位宽 2^13 = 8192行,刷新时间间隔为64ms
//64ms/8192 = 7812.5ns. 当前时钟信号为100mhz,每个周期10ns,也就是781.25个周期刷新一次
//当仲裁模块发送仲裁请求信号后还需要等待一段时间,所以7500/10,完成750个周期的计数即可。
//初始化结束后,开始计数
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt_ref <= 10'd0;
else if(cnt_ref >= CNT_REF_MAX)
cnt_ref <= 10'd0;
else if(aref_end == 1'b1)
cnt_ref <= cnt_ref + 1'b1;
// 自动刷新请求信号 当自动刷新响应信号为高电平拉低该信号
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
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 == 1'b0)
aref_state <= AREF_IDLE;
else
case(aref_state)
AREF_IDLE:
//完成初始化,并且仲裁模块同意
if(aref_end == 1'b1 && aref_en == 1'b1)
aref_state <= AREF_PCH;
else
aref_state <= aref_state;
AREF_PCH:
aref_state <= AREF_TRP;
AREF_TRP:
if(trp_end == 1'b1)
aref_state <= AREF_REF;
else
aref_state <= aref_state;
AREF_REF:
aref_state <= AREF_TRF;
AREF_TRF:
if(trf_end == 1'b1)
if(cnt_aref == 2'd2)
aref_state <= AREF_END;
else
aref_state <= AREF_REF;
else
aref_state <= aref_state;
AREF_END:
aref_state <= AREF_IDLE;
default:
aref_state <= AREF_IDLE;
endcase
//cnt_clk 计数器的赋值
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt_clk <= 3'd0;
else if(cnt_clk_rst == 1'b1)
cnt_clk <= 3'd0;
else
cnt_clk<= cnt_clk +1'b1;
//复位信号的赋值 组合逻辑赋值 case 语句状态跳转条件
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 == TRF))? 1'b1:1'b0;
//自动刷新次数计数器赋值
//自动刷新次数计数器 cnt_aref ,当状态机处于初始状态进行清零
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt_aref <= 2'd0;
else if(aref_state == AREF_IDLE)
cnt_aref <= 2'd0;
else if(aref_state == AREF_REF)
cnt_aref <= cnt_aref + 1'b1;
else
cnt_aref <= cnt_aref;
//命令和结束信号的赋值
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
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 <= AUTO_REF;
aref_ba <= 2'b11;
aref_addr <= 13'h1fff;
end
//模式寄存器下地址总线和bank地址都要改一下
// 地址总线高三位为0 ,第三位为突发长度选择
AREF_END:
begin
aref_cmd <= NOP;
aref_ba <= 2'b11;
aref_addr <= 13'h1fff;
end
default:
begin
aref_cmd <= NOP;
aref_ba <= 2'b11;
aref_addr <= 13'h1fff;
end
endcase
// 结束信号 aref_end 用组合逻辑赋值
assign aref_end = (aref_state == AREF_END)?1'b1:1'b0;
endmodule
部分仿真程序:
module tb_sdtam_aref();
wire clk_50m;
wire clk_100m;
wire clk_100m_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 aref_en;
wire [3:0] aref_cmd ;
wire [1:0] aref_ba ;
wire [12:0]aref_addr ;
wire aref_end;
wire aref_req;
reg sys_clk;
reg sys_rst_n;
//整合命令
wire [3:0] sdram_cmd;
wire [1:0] sdram_ba;
wire [12:0]sdram_addr;
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 clk_100m or negedge rst_n)
if(rst_n == 1'b0)
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 ;
//pll锁相环 复位信号为高电平有效,
dram_init dram_init_inst(
.sys_clk(sys_clk),
.sys_rst_n(rst_n),
.init_cmd(init_cmd),
.init_ba(init_ba),
.init_addr(init_addr),
.init_end(init_end)
);
//仿真需要用到sdram仿真模型。 这个模块我没有 可以自己在网上找找
clk_gen clk_gen_inst (
.areset ( sys_clk ),
.inclk0 ( ~sys_rst_n ),
.c0 ( clk_50m ),
.c1 ( clk_100m ),
.c2 ( clk_100m_shift ),
.locked ( locked )
);
sdram_aref sdram_aref_inst(
.sys_clk(sdram_aref),
.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)
);
endmodule
```