SDRAM控制器(初始化)
文章目录
初始化模块
SDRAM 在上电之后,执行正常操作之前需要被初始化,实际上就是对上文提到的SDRAM 内部逻辑控制单元进行初始化,初始化成功的 SDRAM 才可进行后续的其他操作。接下来我们将要学习掌握初始化操作时序,设计、实现并仿真验证初始化模块功能。
初始化操作时序
SDRAM 的初始化是芯片上电后必须进行的一项操作,只有进行了初始化操作的SDRAM 芯片才可被正常使用。 SDRAM 的初始化是一套预先定义好的流程,除此之外的其他操作会导致 SDRAM 出现不可预知的后果。 SDRAM 初始化操作时序图,具体见图53-23。
结合 SDRAM 初始化时序图, 列出 SDRAM 初始化参考流程如下:
(1) 对 SDRAM 上电,加载稳定时钟信号, CKE 设置为高电平;
(2) 等待至少 T=100us 的时间,此过程中操作命令保持为空操作命令;
(3) 100us 等待结束后,写入预充电命令, A10 设置为高电平,对所有 L-Bank 进行预充电;
(4) 预充电指令写入后,等待 tRP时间,此过程中操作命令保持为空操作命令;
(5) tRP等待时间结束后,写入自动刷新命令;
(6) 自动刷新命令写入后,等待 tRC时间,此过程中操作命令保持为空操作命令;
(7) tRC等待时间结束后,再次写入自动刷新命令;
(8) 自动刷新命令写入后,等待 tRC时间,此过程中操作命令保持为空操作命令;
(9) tRC 等待时间结束后,写入模式寄存器配置指令,地址总线 A0-A11 参数不同辅助模式寄存器不同模式的设置;
(10) 模式寄存器配置指令写入后,等待 tMRD 时间,此过程中操作命令保持为空操作命令;
(11) tMRD等待时间结束后, SDRAM 初始化完成。
注: 1.对于 tRP、 tRC、 tMRD 等时间参数,不同芯片或速度等级可能存在差异,读者需查阅
芯片对应数据手册进行参数设置;
2.T=100us 为最小等待时间,我们在使用 SDRAM 时,等待时间 T 可适当延长;
3.初始化过程中,至少进行两次自动刷新,也可适当增加刷新次数。
时序逻辑图
(1)因为对于时间等待参数,我们可以设置成时钟的倍数,所以我们可以用计数器控制 (当然也可以用状态机,因为状态机很适合设计接口) 对于预充电,自刷新,寄存器配置的指令只需要一个cycle即可写入,所以有用的控制命令只保持一个时钟 ,对于trp我们设为3个时钟周期,trp是指写入预充电指令与自动刷新指令之间的时间, 所以在逻辑图里我们只给了trp2个时钟周期,因为还要加上PRE的时钟周期,刚好三个。 类似的不写了。
代码
`timescale 1ns / 1ps
//
// Company:
// Engineer:
//
// Create Date: 2021/04/22 17:28:14
// Design Name: 超级无敌大猫猫
// Module Name: SDRAM_INIT
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//
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 reg init_end
);
reg [14:0] DELAY_CNT;
reg [4:0] command_cnt;
wire delay_flag;
always@(posedge sys_clk)
begin
if(sys_rst_n == 1'b0)
DELAY_CNT <= 'b0;
else if(DELAY_CNT == 15'd20000)
DELAY_CNT <= DELAY_CNT;
else
DELAY_CNT <= DELAY_CNT + 1'b1;
end
assign delay_flag = (DELAY_CNT == 15'd20000)? 1'b1:1'b0;
always@(posedge sys_clk)
begin
if(sys_rst_n == 1'b0)
command_cnt <= 'b0;
else if(command_cnt == 5'd31&&delay_flag)
command_cnt <= command_cnt;
else if(delay_flag)
command_cnt <= command_cnt + 1'b1;
end
always@(posedge sys_clk)
begin
if(sys_rst_n == 1'b0)
init_cmd <= 4'b0111;
else if(DELAY_CNT == 15'd19999)
init_cmd <= 4'b0010;
else if(delay_flag == 1'b1)
begin
case(command_cnt)
0 : init_cmd <= 4'b0111;
2 : init_cmd <= 4'b0001;
3 : init_cmd <= 4'b0111;
10 :init_cmd <= 4'b0001;
11 :init_cmd <= 4'b0111;
18 :init_cmd <= 4'b0001;
19 :init_cmd <= 4'b0111;
26 :init_cmd <= 4'b0000;
27 :init_cmd <= 4'b0111;
default : init_cmd <= 4'b0111;
endcase
end
else
init_cmd <= 4'b0111;
end
always@(posedge sys_clk)
begin
if(sys_rst_n == 1'b0)
init_end <= 1'b0;
else if(command_cnt == 5'd30)
init_end <= 1'b1;
end
always@(posedge sys_clk)
begin
if(sys_rst_n == 1'b0)
init_ba <= 2'b11;
else if(command_cnt == 5'd26)
init_ba <= 2'b00;
else
init_ba <= 2'b11;
end
always@(posedge sys_clk)
begin
if(sys_rst_n == 1'b0)
init_addr <= 13'h1fff;
else if(command_cnt == 5'd26)
init_addr <= {3'b000,1'b0,2'b00,3'b011,1'b0,3'b111};
else
init_addr <= 13'h1fff;
end
endmodule
仿真结果
记住一定要足够的仿真时间长能出数据,因为我们的上电是200us,所以仿真时间最好设置大于200us才出数据。