记录了SDRAM控制器进行初始化配置后的刷新模块。
SDRAM要进行不停地刷新保证数据的存在,因为SDRAM类似于电容,每次放电结束后数据都会丢失,所以要进行不断地刷新,刷新间隔为15us。
控制器状态从开始的idle状态到仲裁状态,在仲裁状态下进行刷新,读,写状态的跳转。
在仲裁状态下接收到刷新请求,发送刷新使能,状态转到刷新状态,当刷新结束后发送刷新结束标志,状态跳转到仲裁状态。
首先进行precharge命令,trp时间后进行auto refresh命令(20ns,1个周期),文档中要进行两次刷新才能进行act命令,但是实际上只用一次就行了。
1.每个15us以后自动进行刷新
1.输出刷新请求,仲裁阶段接收到刷新请求,仲裁同意刷新才可以刷新。
2.输出刷新结束,状态跳转回到仲裁
3.刷新命令command(cs,cas,ras,we)
4.addr(a0-a11),文档中显示A0-A9,A11,A12不关心。
输出:
ref_req:只有在每个15us拉高(每隔15us刷新一次)
flag_ref_end:刷新结束,当命令寄存器发送aref命令结束后拉高
cmd_reg:发送命令(precharge—cmd_cnt计数1和auto refcmd_cnt计数2)
ref_addr:A0-A11
内部变量:
falg_ref:在刷新阶段为高电平
ref_cnt_15us:计数15us,初始化结束后自动开始计数
cmd_cnt:首先是precharge,一个周期后aref,在刷新阶段内部开始计数(flag_ref为高)
module sdram_aref(
input sclk,
input srst,
//commmunicate with arbit
input ref_en,
output wire ref_req,
output wire flag_ref_end,
//others
input flag_init_end,
output reg [3:0] cmd_reg,
output wire [11:0] ref_addr
);
//==========================================================
//======= define parameter and internal signal ========
//==========================================================
localparam delay_15us = 749;
localparam cmd_aref = 4'b0001;
localparam cmd_nop = 4'b0111;
localparam cmd_pre = 4'b0010;
reg flag_ref;
reg [9:0] ref_cnt_15us;
reg [3:0] cmd_cnt;
//==========================================================
//==================== main code ====================
//==========================================================
always@(posedge sclk or negedge srst)begin
if(srst == 1'b0)
ref_cnt_15us <= 'd0;
else if(ref_cnt_15us >= delay_15us)
ref_cnt_15us <= 'd0;
else if(flag_init_end == 'b1)
ref_cnt_15us <= ref_cnt_15us +1'b1;
end
always@(posedge sclk or negedge srst)begin
if(srst == 1'b0)
flag_ref <= 1'b0;
else if(flag_ref_end == 1'b1)
flag_ref <= 1'b0;
else if(ref_en == 1'b1)
flag_ref <= 1'b1;
end
always@(posedge sclk or negedge srst)begin
if(srst == 1'b0)
cmd_cnt <= 'd0;
else if(flag_ref == 1'b1)
cmd_cnt <= cmd_cnt +1'b1;
else
cmd_cnt <= 'd0;
end
always@(posedge sclk or negedge srst) begin
if(srst == 1'b0)
cmd_reg <= cmd_nop;
else case(cmd_cnt)
1: cmd_reg <= cmd_pre;
2: cmd_reg <= cmd_aref;
default:
cmd_reg <= cmd_nop;
endcase
end
assign flag_ref_end = (cmd_cnt == 'd3)? 1'b1:1'b0;
assign ref_addr = 12'b0100_0000_0000;
assign ref_req = (ref_cnt_15us >= delay_15us)?1'b1:1'b0;
endmodule
顶层模块:
module sdram_top(
input sclk,
input srst,
//sdram interface
output wire sdram_clk,
output wire sdram_cke,
output wire sdram_cs_n,
output wire sdram_cas_n,
output wire sdram_ras_n,
output wire sdram_we_n,
output wire [1:0] sdram_bank,
output wire [11:0] sdram_addr,
output wire [1:0] sdram_dqm,
inout [15:0] sdram_dq
);
//==========================================================
//======= define parameter and internal signal ========
//==========================================================
//define state machine
reg [4:0] state;
localparam idle = 5'b00001;
localparam arbit = 5'b00010;
localparam aref = 5'b00100;
//init module
wire flag_init_end;
wire [3:0] init_cmd;
wire [11:0] init_addr;
//refresh module
reg ref_en;
wire ref_req;
wire flag_ref_end;
wire [3:0] cmd_reg;
wire [11:0] ref_addr;
//==========================================================
//==================== main code ====================
//==========================================================
always@(posedge sclk or negedge srst) begin
if(srst == 1'b0)
state <= idle;
else
case(state)
idle:
if(flag_init_end == 1'b1)
state <= arbit;
else
state <= idle;
arbit:
if(ref_en == 1'b1)
state <= aref;
else
state <= arbit;
aref:
if(flag_ref_end == 1'b1)
state <= idle;
else
state <= aref;
default:
state <= idle;
endcase
end
//ref_en
always@(posedge sclk or negedge srst)begin
if(srst == 1'b0)
ref_en <= 1'b0;
else if(state == arbit && ref_req == 1'b1)
ref_en <= 1'b1;
else
ref_en <= 1'b0;
end
assign sdram_cke = 1'b1;
assign sdram_addr = (state == idle) ?init_addr:ref_addr;
assign {sdram_cs_n, sdram_ras_n, sdram_cas_n, sdram_we_n} = (state == idle) ?init_cmd:cmd_reg;
assign sdram_dqm = 2'b00;
assign sdram_clk = ~sclk; //内部时钟上升沿采集命令,命令又是由系统时钟上升沿产生的??(为了保证采样时刻处在数据中间时刻)
sdram_init sdram_init_inst(
.sclk (sclk) ,
.srst (srst) ,
.cmd_reg (init_cmd) ,
.sdram_addr (init_addr) ,
.flag_init_end (flag_init_end)
);
sdram_aref sdram_aref(
.sclk (sclk),
.srst (srst),
//commmunicate with arbit
.ref_en (ref_en),
.ref_req (ref_req),
.flag_ref_end (flag_ref_end),
//others
.flag_init_end (flag_init_end),
.cmd_reg (cmd_reg),
.ref_addr (ref_addr)
);
endmodule
测试代码:
`timescale 1ns/1ns
module tb_sdram_top;
reg sclk;
reg srst;
//----------------------------------------
wire sdram_clk;
wire sdram_cke;
wire sdram_cs_n;
wire sdram_cas_n;
wire sdram_ras_n;
wire sdram_we_n;
wire [1:0] sdram_bank;
wire [11:0] sdram_addr;
wire [1:0] sdram_dqm;
wire [15:0] sdram_dq;
//----------------------------------------
initial begin
sclk = 1;
srst <= 0;
#100
srst <=1;
end
always #10 sclk = ~sclk;
defparam sdram_model_plus_inst.addr_bits = 12;
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;//1 M
sdram_top sdram_top_inst(
.sclk (sclk ),
.srst (srst ),
.sdram_clk (sdram_clk ),
.sdram_cke (sdram_cke ),
.sdram_cs_n (sdram_cs_n ),
.sdram_cas_n (sdram_cas_n),
.sdram_ras_n (sdram_ras_n),
.sdram_we_n (sdram_we_n ),
.sdram_bank (sdram_bank ),
.sdram_addr (sdram_addr ),
.sdram_dqm (sdram_dqm ),
.sdram_dq (sdram_dq )
);
sdram_model_plus sdram_model_plus_inst(
.Dq (sdram_dq) ,
.Addr (sdram_addr),
.Ba (sdram_bank),
.Clk (sdram_clk),
.Cke (sdram_cke),
.Cs_n (sdram_cs_n),
.Ras_n (sdram_ras_n),
.Cas_n (sdram_cas_n),
.We_n (sdram_we_n),
.Dqm (sdram_dqm),
.Debug (1'b1)
);
endmodule