(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, output [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,说明刷新模块设计成功。
对应波形:
原文:https://www.cnblogs.com/FPGAer/p/14017476.html