SDRAM--Verilog及仿真(2)

(2)仲裁和刷新

这篇博客需要在(1)的基础上进一步学习。

在刷新、写、读三者之间仲裁。这里只设计仲裁和刷新两个模块。

SDRAM需要不断的刷新来给SDRAM中存储数据的电容充电来达到数据不丢失的目的。

 

从官方手册中可以知道,SDRAM在64ms里刷新4096次,因此64000us/4096=15.625us,即每15us,SDRAM就会刷新一次。

其中tRP和tRC分别是20ns和63ns。

仿真代码设计:

由于SDRAM器件复杂,所以借用了别人已经设计好的SDRAM模型sdram_model_plus.v,并且改动其中内容如下:

激励文件top_tb.v:

`timescale 1ns/1ns
module top_tb;
reg sys_clk;
reg sys_rst_n;
wire sdram_clk;
wire sdram_cke;
wire sdram_cs_n;
wire sdram_ras_n;
wire sdram_cas_n;
wire sdram_we_n;
wire [1:0]sdram_ba;
wire [11:0]sdram_addr;
wire [1:0]sdram_dqm;
wire [15:0]sdram_data;

top u_top(
.sys_clk     (sys_clk),
.sys_rst_n   (sys_rst_n),
.sdram_clk   (sdram_clk),
.sdram_cke   (sdram_cke),
.sdram_cs_n  (sdram_cs_n),
.sdram_ras_n (sdram_ras_n),
.sdram_cas_n (sdram_cas_n),
.sdram_we_n  (sdram_we_n),
.sdram_ba    (sdram_ba),
.sdram_addr  (sdram_addr),
.sdram_dqm   (sdram_dqm),
.sdram_data  (sdram_data)
);

sdram_model_plus u_model_plus(
.Dq      (sdram_data),
.Addr    (sdram_addr),
.Ba      (sdram_ba),
.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)
);

initial begin
sys_clk=1;
sys_rst_n=0;
#30 sys_rst_n=1;
end
always #10 sys_clk=~sys_clk;

endmodule

综合代码设计:

sdram_init.v模块:保持不变

module sdram_init(
input sys_clk,
input sys_rst_n,

output reg [3:0]cmd,
output [11:0]sdram_addr,
output init_end_flag
);

localparam NOP=4'b0111;
localparam PRE=4'b0010;
localparam REF=4'b0001;
localparam MSET=4'b0000;

reg [13:0]cnt_200us;
reg [3:0]cmd_cnt;

always @(posedge sys_clk or negedge sys_rst_n)begin
if(!sys_rst_n)
cnt_200us<=14'd0;
else if(cnt_200us < 14'd10000)
cnt_200us<=cnt_200us+1'b1;
end

always @(posedge sys_clk or negedge sys_rst_n)begin
if(!sys_rst_n)
cmd_cnt<=4'd0;
else if(cnt_200us == 14'd10000 && init_end_flag == 1'b0)
cmd_cnt<=cmd_cnt+1'b1;
end

always @(posedge sys_clk or negedge sys_rst_n)begin
if(!sys_rst_n)
cmd<=NOP;
else if(cnt_200us == 14'd10000)
case(cmd_cnt)
0:cmd<=PRE;
1:cmd<=REF;
5:cmd<=REF;
9:cmd<=MSET;
default:cmd<=NOP;
endcase
end

assign init_end_flag=(cmd_cnt>=4'd10)?1'b1:1'b0;
assign sdram_addr=(cmd == MSET)?12'b00000_011_0010:12'b0100_0000_0000;
endmodule

刷新模块sdram_ref.v:

module sdram_ref(
input sys_clk,
input sys_rst_n,

input ref_en,
input init_end_flag,
output ref_req,
output ref_end_flag,
output reg [3:0]ref_cmd,
output [11:0]ref_addr
);
//==========================================================
//*********Define Parameter and Internal Signals************
//==========================================================
localparam DELAY_15us=750;
localparam CMD_AREF=4'b0001;
localparam CMD_NOP=4'b0111;
localparam CMD_PRE=4'b0010;

reg [3:0]cmd_cnt;
reg [9:0]cnt_15us;
reg ref_flag;
//==========================================================
//*****************Main Code******************
//==========================================================
always @(posedge sys_clk or negedge sys_rst_n)begin
if(!sys_rst_n)
cnt_15us<=10'd0;
else if(cnt_15us >= DELAY_15us)
cnt_15us<=10'd0;
else if(init_end_flag == 1'b1)
cnt_15us<=cnt_15us+1'b1;
end

always @(posedge sys_clk or negedge sys_rst_n)begin
if(!sys_rst_n)
ref_flag<=1'b0;
else if(ref_end_flag == 1'b1)
ref_flag<=1'b0;
else if(ref_en)
ref_flag<=1'b1;
end

always @(posedge sys_clk or negedge sys_rst_n)begin
if(!sys_rst_n)
cmd_cnt<=4'd0;
else if(ref_flag == 1'b1)
cmd_cnt<=cmd_cnt+1'b1;
else
cmd_cnt<=4'd0;
end

always @(posedge sys_clk or negedge sys_rst_n)begin
if(!sys_rst_n)
ref_cmd<=CMD_NOP;      
else case(cmd_cnt)
1:ref_cmd<=CMD_PRE;    
2:ref_cmd<=CMD_AREF;  
default:ref_cmd<=CMD_NOP;
endcase
end

assign ref_end_flag=(cmd_cnt>=2'd3)?1'b1:1'b0;
assign ref_addr=12'b0100_0000_0000;
assign ref_req=(cnt_15us >= DELAY_15us)?1'b1:1'b0;

endmodule 

顶层文件top.v:

module top(
input sys_clk,
input sys_rst_n,

output sdram_clk,
output sdram_cke,
output sdram_cs_n,
output sdram_ras_n,
output sdram_cas_n,
output sdram_we_n,
output [1:0]sdram_ba,
output [11:0]sdram_addr,
output [1:0]sdram_dqm,
inout [15:0]sdram_data
);

localparam IDLE=5'b00001;
localparam ARBIT=5'b00010;
localparam AREF=5'b00100;

//init module 
wire [11:0]init_addr;
wire init_end_flag;
wire [3:0]init_cmd;

//
reg [4:0]state;

//refresh module 
wire ref_req;
reg ref_en;
wire ref_end_flag;
wire [3:0]ref_cmd;
wire [11:0]ref_addr;

always @(posedge sys_clk or negedge sys_rst_n)begin
if(!sys_rst_n)
state<=IDLE;
else case(state)
IDLE:
    if(init_end_flag == 1'b1)
    state<=ARBIT;
    else
    state<=IDLE;
ARBIT:
    if(ref_en == 1'b1)
    state<=AREF;
    else 
    state<=ARBIT;
AREF:
    if(ref_end_flag == 1'b1)
    state<=ARBIT;
    else
    state<=AREF;
default:state<=IDLE;
endcase
end

//ref_en
always @(posedge sys_clk or negedge sys_rst_n)begin
if(!sys_rst_n)
ref_en<=1'b0;
else if(state == ARBIT && ref_req == 1'b1)
ref_en<=1'b1;
else
ref_en<=1'b0;
end

assign {sdram_cs_n,sdram_ras_n,sdram_cas_n,sdram_we_n}=(state == IDLE)?init_cmd:ref_cmd;
assign sdram_clk=~sys_clk;
assign sdram_cke=1'b1;
assign sdram_ba=2'b00;
assign sdram_addr=(state == IDLE)?init_addr:ref_addr;
assign sdram_dqm=2'b00;

sdram_init u_sdram_init(
.sys_clk        (sys_clk),
.sys_rst_n      (sys_rst_n),
.cmd            (init_cmd),
.sdram_addr     (init_addr),
.init_end_flag  (init_end_flag)
);

sdram_ref u_sdram_ref(
.sys_clk          (sys_clk),
.sys_rst_n        (sys_rst_n),

.ref_en           (ref_en),
.init_end_flag    (init_end_flag),
.ref_req          (ref_req),
.ref_end_flag     (ref_end_flag),
.ref_cmd          (ref_cmd),
.ref_addr          (ref_addr)
);
endmodule

结果:

尝试跑200us去掉稳定期后,继续跑1us检验初始化命令是否正确,跑30us,15us刷新一次,那么如果有两个预充电PRE和两个自动刷新AREF,说明刷新模块设计成功。

对应波形:

 

posted @ 2020-11-21 21:48  LiYiRui  阅读(353)  评论(0编辑  收藏  举报