SDRAM初始化
对sdram进行初始化设计,并仿真验证
SDRAM即同步动态随机存储器,同步是指SDRAM时钟频率与控制器的时钟频率相同,动态是指存储阵列需要不断的刷新来保证数据不丢失,随机是指数据的读取和写入可以随机指定地址。
SDRAM存储数据是 利用电容能够保持电荷以及其充放电特性,对于一位数据的存取,首先需要打开行地址,然后打开列地址,电容的电平状态就可以呈现在数据线上。打开行地址需要一定的时间,经过tRCD时间才可以打开列地址,打开列地址同样需要经过列选通潜伏期CL之后才能进行读写。SDRAM所有信号在时钟上升沿被寄存。
本开发板上为128M的SDRAM,SDRAM拥有4个BANK,每个bank可以存储32M的数据,每个BANK有4096行512列,数据位宽为16位。
SDRAM的存储容量计算是 数据位宽X一个bank中的行数X一个bank中的列数Xbank数量
本项目主要是对SDRAM进行初始化,对SDRAM的操作命令,是通过CS_N,RAS_N,CAS_N,WE_N这四个控制信号组成来控制的。
{CS_N,RAS_N,CAS_N,WE_N} | 命令 | 命令说明 |
4‘b1xxx | 禁止命令执行。 | |
4‘b0111 | 空命令。NOP | 无需操作 |
4'b0000 | 加载模式寄存器。 | 只有在所有bank空闲状态才可以进行,执行此命令的响应周期为tMRD |
4‘b0011 | 激活命令。 |
激活一个特定的bank和行,BA0和BA1用来选择特定的bank,地址线A0-A12选择指定的行,如果需 要关闭该行需要执行一次预充电命令,该行才会被关闭。 |
4'b0101 | 读命令。 | 对一个已经激活的行突发读操作,A10控制是否在突发读取完成之后立即执行预充电,为高电平则读取完当前行之后立即执行预充电,若为低电平则不关闭该行。 |
4‘b0100 | 写命令。 | 对一个已经激活的行突发写入操作,A10控制是否在突发读取完成之后立即执行预充电,为高电平则读取完当前行之后立即执行预充电,若为低电平则不关闭该行。 |
4'b0010 | 预充电命令。 | 其作用就是关闭指定bank或全部bank中已经打开的行,执行此命令的响应周期为tRP。A10为高电平的时候对所有bank进行预充电,为低电平时只对指定bank中的行进行预充电。 |
4'b0001 | 自动刷新。 | 在执行自动刷新之前所有bank必须被预充电(关闭),间隔至少为tRP时间,对于128M的SDRAM每64ms要执行4096次自动刷新命令。 |
SDRAM上电必须被初始化,需要先延迟200us,在这期间只能赋值给禁止命令或空操作命令,然后对SDRAM执行一次预充电命令,使所有BANK(A10=1)都处于空闲状态,等待tRP之后(在这个过程中保持禁止命令或者空操作命令),执行两个周期的自动刷新命令,每个自动刷新命令执行之后需要等待tRFC才可执行下一个命令,最后发出装在模式寄存器命令设置模式寄存器,模式寄存器的值由A0-A11传输,等待tMRD之后就可以进行行激活等其他命令。
BA1 | BA0 | A11 | A10 | A9 | A8 | A7 | A6 | A5 | A4 | A3 | A2 | A1 | A0 |
写突发模式设置寄存器 | 运行模式设置寄存器 | 列选通潜伏期设置寄存器 | 突发类型寄存器 | 突发长度寄存器 | |||||||||
为0代表读写突发模式 | 0 | 0 | 011潜伏期为3,100M时钟 | 0连续型 | 011突发长度为8 |
根据以下流程图进行代码设计
将需要定义的参数在单独文件中进行定义
`define ASIZE 13//地址位宽
`define DSIZE 16//数据位宽
`define BSIZE 2 //bank位宽
//操作命令
//空操作
parameter C_NOP =4'b0111;
//预充电
parameter C_PRE =4'b0010;
//自动刷新
parameter C_AREF=4'b0001;
//加载模式寄存器
parameter C_MSET=4'b0000;
//激活
parameter C_ACT =4'b0011;
//读
parameter C_RD =4'b0101;
//写
parameter C_WR =4'b0100;
//采用100M的时钟
parameter INIT_PRE=20000;//初始化等待200us,100M时钟下
parameter REF_PRE =3; //预充电等待tRP 30ns
parameter REF_REF =10; //自动刷新等待tRFC 60ns
parameter AUTO_REF=1560; //自动刷新周期<64ms/4096=15625ns
parameter LMR_ACT =2; //加载模式寄存器到可激活的延迟
parameter WR_PRE =2; //写操作完成到预充电时间间隔
parameter SC_RCD =3; //激活到读写命令延时
//模式寄存器参数化表示
parameter SC_CL =3;//列选通潜伏期
parameter SC_BL =8;//突发长度设置
parameter OP_CODE=0;//写突发模式设置
parameter SDR_BL =(SC_BL==8)?3'b011: 3'b111;
parameter SDR_BT=0;//突发类型设置
parameter SDR_CL=(SC_CL==2)? 3'b10 : 3'b11 ;//写突发模式设置
SDRAM初始化设计,需要注意的是如果参数定义的不都是用define定义,包含的头文件必须写在模块内部,否则会报错,如果是用define定义的参数,包含头文件也可以写在模块外面,可查看笔者的lcd液晶显示项目。
module sdram_init(
clk,
rst_n,
command,//命令信号
saddr,//地址信号
init_done
);
`include "params.h"
input clk;
input rst_n;
output reg[3:0] command;//命令信号
output reg[`ASIZE-1:0]saddr;//地址信号
output init_done;
localparam init_PRE_TIME =INIT_PRE;//初始化等待200us,100M时钟下
localparam init_AREF1_TIME =INIT_PRE+REF_PRE;//初始化等待200us,然后预充电等待tRP预充电周期
localparam init_AREF2_TIME =INIT_PRE+REF_PRE+REF_REF;//等待第一个自动刷新周期
localparam init_LMR_TIME =INIT_PRE+REF_PRE+REF_REF*2;//等待第2个自动刷新周期
localparam init_END_TIME =INIT_PRE+REF_PRE+REF_REF*2+LMR_ACT;//初始化结束
//初始化过程计数器
reg [15:0] init_cnt;
always@(posedge clk or negedge rst_n)
if(!rst_n)
init_cnt<=0;
else if(init_cnt
仿真结果
在仿真之前,需要添加SDRAM的仿真模型,在镁光官网可以下载,需要用到sdr.v和sdr_parameter.h文件。
`timescale 1ns/1ns
`define clk_period 10
module sdram_init_tb;
`include "../dev/params.h"
reg clk;
reg rst_n;
wire[3:0] command;//命令信号
wire[`ASIZE-1:0]saddr;//地址信号
wire init_done;
wire sd_clk;
wire cs_n;
wire ras_n;
wire cas_n;
wire we_n;
assign command={cs_n,ras_n,cas_n,we_n};
assign sd_clk=~clk;//数据和时钟呈中心对齐
sdram_init sdram_init(
.clk(clk),
.rst_n(rst_n),
.command(command),//命令信号
.saddr(saddr),//地址信号
.init_done(init_done)
);
//sdram模型例化
sdr sdr(
.Dq(),
.Addr(saddr),
.Ba(),
.Clk(sd_clk),
.Cke(rst_n),
.Cs_n(cs_n),
.Ras_n(ras_n),
.Cas_n(cas_n),
.We_n(we_n),
.Dqm()
);
initial clk=0;
always #(`clk_period/2) clk=~clk;
initial begin
rst_n=0;
#(`clk_period*200+1)
rst_n=1;
@(posedge init_done)
#2000;
$stop;
end
endmodule
加载模式寄存器后{OP_CODE,2'b00,SDR_CL,SDR_BT,SDR_BL}=0_00_011 _0_011,这个代码调试了两个两个小时,应该是软件的问题,代码对着就是波形出来的数据一直是错的,至今还没找到具体的原因,感觉这种跨文件传输数据应该用宏定义比较保险吧。