FPGA中的SDRAM有缓存容量大的特点,在本篇博客中,将重点介绍SDRAM的初始化操作。
首先看下面初始化的时序图
可知初始化SDRSAM需要如下操作。
首先在上电和clk使能之后sdram要等待一段时间,根据手册可知最小等待时间位100us。这里我们取200us。此时的COMMAND填充的使NOP命令,下面几条初始化所需要的命令
之后进行预充电状态,一个时钟周期内完成,而且因为此时是对所有的bank进行充电,则要进行如下设置
将BA0, BA1设置为2‘b11即可; 当预充电完成之后,需要等待一段时间,预充电的等待时间为tRP。在预充电等待这段时间内,执行的命令依然为NOP。tRP结束后。后面会进入一个周期的自动刷新状态。自动刷新状态之后,也需要尽量自动刷新的等待时间,为tRC。因为SDRAM的自动刷新一般要维持至少两次以上,所以我设置的是8次。设置好之后,接下来进入一周期的配置寄存器模式的状态。具体的配置可以根据自己的要求进行配置。
最后就是当寄存器配置好之后,就最后等待一个寄存器配置的时间。设置为tMRD。
最后先提出的一点是:SDRAM的工作时钟要仔细看下,比如我这款芯片,时钟频率配置如下
最后是我的verilog代码设计和仿真波形
`timescale 1ns / 1ps
module sdram_init(
input wire sys_clk ,
input wire sys_rst_n ,
output reg [3:0] init_cmd ,
output reg [1:0] init_ba ,
output reg [12:0] init_addr ,
output wire init_end
);
parameter INIT_IDLE = 3'b000,
INIT_PRE = 3'b001,
INIT_TRP = 3'b011,
INIT_AR = 3'b010,
INIT_TRF = 3'b110,
INIT_MRS = 3'b111,
INIT_TMRD = 3'b101,
INIT_END = 3'b100;
parameter WAIT_MAX = 20000 ;
parameter TPR = 3'd2 ,
TRF = 3'd7 ,
TMRD = 3'd2 ;
parameter NOP = 4'b0111,
P_CHARGE = 4'b0010,
AUTO_REF = 4'b0001,
M_REG_SET = 4'b0000;
reg [2:0] init_state ;
reg [14:0] cnt_200us ;
wire wait_end ;
reg [2:0] cnt_clk ;
reg cnt_clk_rst ;
wire trp_end ;
wire trfc_end ;
wire trmd_end ;
reg [3:0] cnt_aref ;
always @(posedge sys_clk or negedge sys_rst_n)
if(!sys_rst_n)
init_state <= INIT_IDLE;
else begin
case (init_state)
INIT_IDLE: init_state <= (wait_end) ? INIT_PRE : INIT_IDLE;
INIT_PRE : init_state <= INIT_TRP;
INIT_TRP : init_state <= (trp_end) ? INIT_AR : INIT_TRP;
INIT_AR : init_state <= INIT_TRF;
INIT_TRF : init_state <= (trfc_end) ? ((cnt_aref == 4'd8) ? INIT_MRS : INIT_AR) : INIT_TRF;
INIT_MRS : init_state <= INIT_TMRD;
INIT_TMRD: init_state <= (trmd_end) ? INIT_END : INIT_TMRD;
INIT_END : init_state <= INIT_END;
default: init_state <= INIT_IDLE;
endcase
end
/* 200us counter*/
always @(posedge sys_clk or negedge sys_rst_n)
if(!sys_rst_n)
cnt_200us <= 15'd0;
else if(cnt_200us == WAIT_MAX)
cnt_200us <= WAIT_MAX;
else
cnt_200us <= cnt_200us + 1'b1;
/* wait_end*/
assign wait_end = (cnt_200us == WAIT_MAX - 1) ? 1'b1 : 1'b0;
/* cnt_clk*/
always @(posedge sys_clk or negedge sys_rst_n)
if(!sys_rst_n)
cnt_clk <= 3'd0;
else if(cnt_clk_rst)
cnt_clk <= 3'd0;
else
cnt_clk <= cnt_clk + 1'b1;
/* cnt_clk_rst*/
always @(*)
begin
case(init_state)
INIT_IDLE: cnt_clk_rst <= 1'b1 ;
INIT_TRP: cnt_clk_rst <= (trp_end) ? 1'b1 : 1'b0;
INIT_TRF: cnt_clk_rst <= (trfc_end) ? 1'b1 : 1'b0;
INIT_TMRD: cnt_clk_rst <= (trmd_end) ? 1'b1 : 1'b0;
INIT_END: cnt_clk_rst <= 1'b1;
default: cnt_clk_rst <= 1'b0;
endcase
end
/*end signal */
assign trp_end = ((init_state == INIT_TRP) && cnt_clk == TPR) ? 1'b1 : 1'b0;
assign trfc_end = ((init_state == INIT_TRF) &&cnt_clk == TRF) ? 1'b1 : 1'b0;
assign trmd_end = ((init_state == INIT_TMRD) && cnt_clk == TMRD) ? 1'b1 : 1'b0;
/* cnt_aref */
always @(posedge sys_clk or negedge sys_rst_n)
if(!sys_rst_n)
cnt_aref <= 4'd0;
else if(init_state == INIT_IDLE)
cnt_aref <= 4'd0;
else if(init_state == INIT_AR)
cnt_aref <= cnt_aref + 1'b1;
always @(posedge sys_clk or negedge sys_rst_n)
if(!sys_rst_n)
begin
init_cmd <= NOP;
init_ba <= 2'b11;
init_addr <= 13'h1fff;
end
else begin
case(init_state)
INIT_IDLE : begin
init_cmd <= NOP;
init_ba <= 2'b11;
init_addr <= 13'h1fff;
end
INIT_TRP : begin
init_cmd <= NOP;
init_ba <= 2'b11;
init_addr <= 13'h1fff;
end
INIT_TRF : begin
init_cmd <= NOP;
init_ba <= 2'b11;
init_addr <= 13'h1fff;
end
INIT_TMRD : begin
init_cmd <= NOP;
init_ba <= 2'b11;
init_addr <= 13'h1fff;
end
INIT_END : begin
init_cmd <= NOP;
init_ba <= 2'b11;
init_addr <= 13'h1fff;
end
INIT_PRE :
begin
init_cmd <= P_CHARGE ;
init_ba <= 2'b11;
init_addr <= 13'h1fff;
end
INIT_AR :
begin
init_cmd <= AUTO_REF ;
init_ba <= 2'b11;
init_addr <= 13'h1fff;
end
INIT_MRS :
begin
init_cmd <= M_REG_SET ;
init_ba <= 2'b00;
init_addr <= {3'b0, 1'b0, 2'b00, 3'b011, 1'b0, 3'b111};
end
default :
begin
init_cmd <= NOP;
init_ba <= 2'b11;
init_addr <= 13'h1fff;
end
endcase
end
assign init_end = (init_state == INIT_END) ? 1'b1 : 1'b0;
endmodule
testbench 如下
`timescale 1ns / 1ps
//
// Company:
// Engineer:
//
// Create Date: 2022/05/27 15:48:44
// Design Name:
// Module Name: tb_sdram_init
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//
module tb_sdram_init();
reg sys_clk ;
reg sys_rst_n ;
wire [3:0] init_cmd ;
wire [1:0] init_ba ;
wire [12:0] init_addr ;
wire init_end ;
wire clk_50m ;
wire clk_100m ;
wire clk_100m_shift ;
wire locked ;
wire rst_n ;
initial begin
sys_clk = 1'b1;
sys_rst_n <= 1'b0;
$display("yes");
#30
sys_rst_n <= 1'b1;
end
always #10 sys_clk = ~sys_clk;
assign rst_n = (sys_rst_n) & locked;
defparam sdram_model_plus_inst.addr_bits = 13 ;
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;
clk_wiz_0 u_clk_wiz_0
(
.clk_in1 (sys_clk) ,
.reset (sys_rst_n ) ,
.clk_50 (clk_50m) ,
.clk_100 (clk_100m) ,
.clk_100_shift(clk_100m_shift),
.locked (locked)
);
sdram_init u_sdram_init(
. sys_clk (sys_clk ) ,
. sys_rst_n (sys_rst_n ) ,
. init_cmd (init_cmd ) ,
. init_ba (init_ba ) ,
. init_addr (init_addr ) ,
. init_end (init_end )
);
sdram_model_plus sdram_model_plus_inst
( .Dq () ,
.Addr (init_addr) ,
.Ba (init_ba) ,
.Clk (clk_100m_shift) ,
.Cke (1'b1) ,
.Cs_n (init_cmd[3]) ,
.Ras_n (init_cmd[2]) ,
.Cas_n (init_cmd[1]) ,
.We_n (init_cmd[0]) ,
.Dqm (2'b00) ,
.Debug (1'b1)
);
endmodule
波形如下 可以知道成功进行了初始化