一.理论知识
sdram芯片上电后,执行读写操作之前,首先要做的第一件事就是初始化,这里解释下初始化的目的。
1.在使用sdram资源的时候,要先给出芯片的地址,这个地址就是行/列地址。初始化第一步,首先就是要关闭所有bank中激活的行。虽然系统上电之后,sdram还没开始使用,没有被激活的行,但是依然要执行预充电这一步操作,确保在使用之前所有bank中没有激活的行。
2.sdram是动态存储器,物理层面是靠电容来维持电平信号,需要不断地给它刷新来给电容充电。在使用sdram资源之前,要给sdram的所有bank刷新。为了确保刷新成功,在初始化期间,要刷新多次。
3.刷新完之后,要给sdram发送模式寄存器配置指令,模式寄存器主要控制芯片的读写方式,突发方式和突发长度等配置信息。为后面的读写操作提前设置参数信息。
二.解读数据手册
1.初始化操作时序
2.延迟时间
3.配置寄存器的值
上面的时序图是从镁光公司提供的sdram芯片数据手册中截选出来的。时序图介绍:
1.sdram上电后,加载稳定的时钟信号,然后设置CKE为高电平。芯片手册要求sdram的时钟频率最高不超过166M,这里我们选用100M时钟频率。sdram要求在时钟上升沿到来的时候,所有的信号数据要准备好,sdram会在上升沿采集这些信号。我们在fpga内部定义一个100M时钟信号,所有信号都在100M时钟的上升沿赋值。但是fpga的信号传输到sdram芯片中间会有延迟,所以我们另外再定义一个100M时钟信号,比前面的100M时钟信号偏移一定的角度,新的100M时钟上升沿比前面的100M时钟早到一会儿,然后把这个新的100M时钟信号发送给sdram。这样就能确保sdram能在时钟上升沿的时候采集到稳定的信号。
2.等待至少T=100us的时间,我们这里取200us,此过程中操作命令保持为空操作命令。
3.200us等待结束后,写入预充电命令,A10设置为高电平,对所有Bank进行预充电。
4.预充电指令写入后,等待tRP时间,从上图中找到tRP最少为20ns,sdram的时钟是100M,所以我们这里设置为2个时钟周期,此过程中操作命令保持为空操作命令。
5.等待tRP时间之后,写入自动刷新命令。
6.自动刷新命令写入后,等待 tRFC时间,从上图中找到tRFC最少为66纳秒,我们设置为7个时钟周期,此过程中操作命令保持为空操作命令。
7.tRFC等待时间结束后,再次写入自动刷新命令。
8.自动刷新命令写入后,等待 tRFC时间,此过程中操作命令保持为空操作命令。
9.数据手册中要求自动刷新的次数不少于2,我们这里设定自动刷新的次数等于8次。
10.自动刷新结束后,写入模式寄存器配置指令,具体模式寄存器配置的数据值是通过在地址总线 A0-A12赋予不同的信号来给出的。
11.模式寄存器配置指令写入后,等待 tMRD 时间,从上图中找到tMRD最少是2个时钟周期,我们这里设置为3个时钟周期,此过程中操作命令保持为空操作命令。
12.tMRD等待时间结束后,sdram芯片初始化完成,才能进行后续的读写和刷新操作。
三.设计verilog时序图
四.verilog代码
module sdram_init
(
input wire sys_clk,
input wire sys_rst_n,
output reg [3:0] init_cmd, // {CS_N,RAS_N,CAS_N,WE_N}
output reg [1:0] init_ba, // BA[1:0]
output reg [12:0] init_addr, // A[12:0]
output reg init_end // 初始化结束信号
);
// 状态
parameter ST_IDLE = 0,
ST_PRE = 1,
ST_PRE_WAIT = 2,
ST_AUTO = 3,
ST_AUTO_WAIT = 4,
ST_MODE = 5,
ST_MODE_WAIT = 6,
ST_END = 7;
// 延迟计数器的值
parameter CNT_IDLE = 20_000,
CNT_PRE_WAIT = 2,
CNT_AUTO_WAIT = 7,
CNT_MODE_WAIT = 3;
// 自动刷新的次数
parameter CNT_AUTO = 8;
// 命令
parameter CMD_NOP = 4'b0111, // 无操作命令也叫空命令,控制指令为{CS_N,RAS_N,CAS_N,WE_N} = 4'b0111
CMD_PRE = 4'b0010, // 预充电命令,分为全部预充电和指定L-Bank预充电两种,控制指令均为{CS_N,RAS_N,CAS_N,WE_N} = 4'b0010
CMD_AUTO = 4'b0001, // 自动刷新命令,分为自动刷新命令和自刷新,控制指令均为{CS_N,RAS_N,CAS_N,WE_N} = 4'b0001
CMD_MODE = 4'b0000; // 此命令只有所有L-Bank均处于空闲状态时才可被写入,否则配置出错,控制指令为{CS_N,RAS_N,CAS_N,WE_N} = 4'b0000
reg rst_n_reg; // 对sys_rst_n寄存一拍
reg [3:0] state; // 状态机
reg [14:0] cnt_wait; // 延迟计数器
reg cnt_wait_rst; // 延迟计数器复位信号
reg pre_flag; // 预充电状态标志信号
reg auto_flag; // 自动刷新状态标志信号
reg mode_flag; // 模式寄存器状态标志信号
reg end_flag; // 结束状态标志信号
reg [3:0] cnt_auto; // 自动刷新次数计数器
// rst_n_reg信号
always@(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n)
rst_n_reg <= 0;
else
rst_n_reg <= sys_rst_n;
end
// state信号
always@(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n)
state <= ST_IDLE;
else case(state)
ST_IDLE :
if(pre_flag == 1)
state <= ST_PRE;
else
state <= ST_IDLE;
ST_PRE :
state <= ST_PRE_WAIT;
ST_PRE_WAIT :
if(auto_flag == 1)
state <= ST_AUTO;
else
state <= ST_PRE_WAIT;
ST_AUTO :
state <= ST_AUTO_WAIT;
ST_AUTO_WAIT:
if(auto_flag == 1)
state <= ST_AUTO;
else if(mode_flag == 1)
state <= ST_MODE;
else
state <= ST_AUTO_WAIT;
ST_MODE :
state <= ST_MODE_WAIT;
ST_MODE_WAIT:
if(end_flag == 1)
state <= ST_END;
else
state <= ST_MODE_WAIT;
ST_END :
state <= ST_END;
default :
state <= ST_IDLE;
endcase
end
// cnt_wait信号
always@(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n)
cnt_wait <= 0;
else if(cnt_wait_rst == 0)
cnt_wait <= cnt_wait + 1;
else if(cnt_wait_rst == 1)
cnt_wait <= 0;
end
// cnt_wait_rst信号
always@(*) begin
if(!sys_rst_n)
cnt_wait_rst <= 1;
else if(pre_flag == 1 || auto_flag == 1 || mode_flag == 1 || end_flag == 1)
cnt_wait_rst <= 1;
else if(state == ST_END)
cnt_wait_rst <= 1;
else if(rst_n_reg == 1)
cnt_wait_rst <= 0;
end
// pre_flag信号
always@(*) begin
if(!sys_rst_n)
pre_flag <= 0;
else if(state == ST_IDLE && cnt_wait == CNT_IDLE)
pre_flag <= 1;
else
pre_flag <= 0;
end
// auto_flag信号
always@(*) begin
if(!sys_rst_n)
auto_flag <= 0;
else if(state == ST_PRE_WAIT && cnt_wait == CNT_PRE_WAIT)
auto_flag <= 1;
else if(state == ST_AUTO_WAIT && cnt_wait == CNT_AUTO_WAIT && cnt_auto < 8)
auto_flag <= 1;
else
auto_flag <= 0;
end
// mode_flag信号
always@(*) begin
if(!sys_rst_n)
mode_flag <= 0;
else if(state == ST_AUTO_WAIT && cnt_wait == CNT_AUTO_WAIT && cnt_auto == 8)
mode_flag <= 1;
else
mode_flag <= 0;
end
// end_flag信号
always@(*) begin
if(!sys_rst_n)
end_flag <= 0;
else if(state == ST_MODE_WAIT && cnt_wait == CNT_MODE_WAIT && cnt_auto == CNT_AUTO)
end_flag <= 1;
else
end_flag <= 0;
end
// cnt_auto信号
always@(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n)
cnt_auto <= 0;
else if(state == ST_AUTO)
cnt_auto <= cnt_auto + 1;
else
cnt_auto <= cnt_auto;
end
// init_cmd[3:0]信号
always@(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n)
init_cmd <= CMD_NOP;
else if(state == ST_PRE)
init_cmd <= CMD_PRE;
else if(state == ST_AUTO)
init_cmd <= CMD_AUTO;
else if(state == ST_MODE)
init_cmd <= CMD_MODE;
else
init_cmd <= CMD_NOP;
end
// init_ba[1:0]信号
always@(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n)
init_ba <= 2'b11;
else if(state == ST_MODE)
init_ba <= 2'b00;
else
init_ba <= 2'b11;
end
// init_addr[12:0]信号
always@(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n)
init_addr <= 13'h1fff;
else if(state == ST_MODE)
init_addr <= {3'b000,1'b0,2'b00,3'b011,1'b0,3'b111};
else
init_addr <= 13'h1fff;
end
// init_end信号
always@(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n)
init_end <= 0;
else if(end_flag == 1)
init_end <= 1;
else
init_end <= init_end;
end
endmodule
五.仿真代码
`timescale 1ns/1ns
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_100m;
wire clk_100m_shift;
wire clk_locked;
wire rst_n;
assign rst_n = sys_rst_n & clk_locked;
initial begin
sys_clk = 0;
sys_rst_n <= 0;
#100
sys_rst_n <= 1;
end
always #10 sys_clk = ~sys_clk;
clk_100m clk_100m_inst
(
.areset (~sys_rst_n ),
.inclk0 (sys_clk ),
.c0 (clk_100m ),
.c1 (clk_100m_shift ),
.locked (clk_locked)
);
sdram_init sdram_init_inst
(
.sys_clk (clk_100m ),
.sys_rst_n (rst_n ),
.init_cmd (init_cmd ),
.init_ba (init_ba ),
.init_addr (init_addr ),
.init_end (init_end )
);
defparam sdram_model_plus_inst.addr_bits = 13; // 行地址13位
defparam sdram_model_plus_inst.data_bits = 16; // 一次读写数据16位
defparam sdram_model_plus_inst.col_bits = 9; // 列地址9位
defparam sdram_model_plus_inst.mem_sizes = 2*1024*1024; // L-Bank容量
sdram_model_plus sdram_model_plus_inst
(
.Dq ( ), // 数据输入输出端口
.Addr (init_addr ), // 地址端口
.Ba (init_ba ), // Bank地址
.Clk (clk_100m_shift ), // 带有偏移的时钟信号
.Cke (1 ), // 时钟使能信号
.Cs_n (init_cmd[3]), // 命令第4位
.Ras_n (init_cmd[2]), // 命令第3位
.Cas_n (init_cmd[1]), // 命令第2位
.We_n (init_cmd[0]), // 命令第1位
.Dqm (2'b00 ), // 数据掩码
.Debug (1 ) // 开启Debug模式
);
endmodule