我是跟着正点原子例程中的SDRAM部分学习的,虽然有视频教程,但前前后后还是拖了很久,现在做完了之后写个博客记录一下,整理中途遇到的问题和不会的点,同时也方便之后如果忘记了好找。
这一部分主要是记录的是SDRAM的管脚描述,需要了解的英文缩写以及对SDRAM的初始化操作。
目录
SDRAM管脚描述

管脚名称 | 位宽 | 管脚描述 | 其他 |
CLK | 1 | 系统时钟 | 边沿有效 |
CKE | 1 | 时钟使能 | 高电平有效 |
BA | 2 | Bank地址 | |
WE | 1 | 写使能 | 低电平为写,高电平为读 |
CAS | 1 | 列地址选通 | 低电平有效 |
RAS | 1 | 行地址选通 | 低电平有效 |
CS | 1 | 片选 | 低电平有效 |
A0-A12 | 13 | 地址线 | 地址线复用,行地址A0-A12,列地址,列地址A0-A8 |
DQ0-DQ15 | 16 | 数据线 | 数据输入和输出 |
LDQM | 1 | 输入输出掩码 | 每个DQM信号线对应一个字节,控制低八位,高电平有效(下同) |
UDQM | 1 | 输入输出掩码 | 控制高八位 |
用到的英文缩写
英文缩写 | 含义 | 补充 |
row | 行 | |
column | 列 | |
Precharge | 预充电 | |
tRP(Precharge command Period) | 预充电有效周期 | 发送预充电命令后,经过一段时间才能发送行激活命令打开新的工作行 |
AR (auto refresh) | 自动刷新 | |
SR (self refresh) | 自刷新 | |
tRC | 自动刷新周期 | 在自动刷新指令发出后等待tRC才能发送其他指令 |
MRS (mode register set) | 模式寄存器设置 | |
tRSC(Register Set Cycle) | 模式寄存器设置周期 | 模式寄存器指令发出后,需要等待一段时间才能向SDRAM发送新的指令 |
tRCD(RAS to CAS Delay) | 行激活与列读写的时间间隔 | 行激活命令发出后,元件响应需要时间 |
CL(CAS Latency) | CAS潜伏期(读数据出现) | CAS/读指令发出后,到数据输出间的时间 |
Write Back | 写回操作 | |
tWR (Write Recovery Time) | 写入校正时间(写数据出现) | 为了保证数据的可靠写入,留出足够的时间 |
Burst | 突发 | |
BL(Burst Lengths) | 突发长度 | 1、2、4、8、全页(一整行数据量) 一行有9列,2^9=512 |
DQM (Data I/O Mask) | 数据掩码技术 |
初始化操作
对各个状态、状态跳转条件、输出进行说明:
I_NOP:初始化的初始状态,等到200us稳定期结束后跳转到发送预充电指令状态,在此状态发送NOP指令。
I_PRE:发送预充电指令状态,只维持一个时钟周期,下一个时钟周期跳转到预充电等待状态,在此状态发送预充电指令。
I_TRP:预充电指令等待状态,在此状态等待时间满足TRP后就跳转到发送自动刷新指令状态,在此状态发送NOP指令(防止对SDRAM进行误操作)。
I_AR:发送自动刷新指令状态,只维持一个周期,下一个时钟周期跳转到自动刷新等待状态,在此期间发送自动刷新指令。
I_TRF:自动刷新等待状态,在此状态等待时间满足TRC时进行判断,若自动刷新次数满足要求(8次)后就跳转到下一个发送模式寄存器设置指令状态,在此状态发送NOP指令,若不满足自动刷新次数要求就继续进行自动刷新操作,跳转到状态I_AR。
I_MRS:发送模式寄存器设置指令状态,只维持一个时钟周期,下一个时钟周期跳转到模式寄存器设置指令等待状态,在此期间发送模式寄存器设置指令。
I_TRSC:模式寄存器设置指令等待状态,在此状态等待时间满足TRSC后就跳转到初始化完成状态,在此状态发送NOP指令。
I_DONE:初始化结束状态,完成初始化后一直停留在这个状态;在此状态发送NOP指令,并将初始化完成信号拉高以通知其他模块开始进行工作。
根据状态转移图写初始化状态机,其中需要记录200us稳定时间的计数器,记录8个自动刷新周期的计数器和控制等待时间的延时计数器。
//延时参数
`define end_trp cnt_clk == TRP_CLK //预充电有效周期结束
`define end_trfc cnt_clk == TRC_CLK //自动刷新周期结束
`define end_trsc cnt_clk == TRSC_CLK //模式寄存器设置时钟周期结束//parameter define
//查芯片手册
parameter TRP_CLK = 10'd4; //预充电有效周期
parameter TRC_CLK = 10'd6; //自动刷新周期
parameter TRSC_CLK = 10'd6; //模式寄存器设置时钟周期
关于如何根据芯片手册设置参数:
举个栗子:我用的芯片型号是W9825G6DH-6,看的就是第二列【-6】,tRC要求的最小值是60ns,如果驱动时钟用的是100MHz,那么周期就是10ns,将参数定义为6,6*10ns=60ns,就能刚好满足要求。
初始化状态机代码(部分)
wire done_200us; //稳定期结束标志
reg [14:0] cnt_200us; //初始化200us稳定期
reg [3:0] init_ref_cnt; //初始化8次刷新计数器
//稳定期计数器
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
cnt_200us <= 15'b0;
end
else begin
cnt_200us <= (cnt_200us < 15'd20_000) ? cnt_200us + 1'b1 : cnt_200us;
end
end
//稳定期结束后,将标志信号拉高
assign done_200us = (cnt_200us == 15'd20_000);
//延时计数器对时钟计数,实现不同延时
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
cnt_clk <= 10'b0;
end
else if(!cnt_rst_n) begin //延时计数器复位信号,低电平有效
cnt_clk <= 10'b0;
end
else begin
cnt_clk <= cnt_clk + 1'b1;
end
end
//初始化过程中连续刷新(8次) 刷新计数器
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
init_ref_cnt <= 4'b0;
end
else if(init_state == `I_NOP) begin
init_ref_cnt <= 4'b0;
end
else if(init_state == `I_AR) begin //处于自动刷新状态时才开始计数
init_ref_cnt <= init_ref_cnt + 1'b1;
end
else begin
init_ref_cnt <= init_ref_cnt;
end
end
//初始化状态机
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
init_state <= `I_NOP;
end
else begin
case(init_state)
`I_NOP : init_state <= (done_200us) ? `I_PRE : `I_NOP;
`I_PRE : init_state <= `I_TRP;
`I_TRP : init_state <= (`end_trp) ? `I_AR : `I_TRP;
`I_AR : init_state <= `I_TRF;
`I_TRF : init_state <= (`end_trfc) ? ((init_ref_cnt == 4'd8) ? `I_MRS : `I_TRF) : `I_AR;
`I_MRS : init_state <= `I_TRSC;
`I_TRSC : init_state <= (`end_trsc) ? `I_DONE : `I_TRSC;
`I_DONE : init_state <= `I_DONE;
default : init_state <= `I_NOP;
endcase
end
end
//SDRAM初始化完成标志
assign sdram_init_done = (init_state == `I_DONE);
这位博主写的SDRAM教程很详细,解析也非常全面,我中间写的状态机跳转说明也参考了他博客的内容,在这里贴个链接,大家可以去看看。
「孤独的单刀」相信我,SDRAM真的不难(二)----初始化操作
原文链接:https://blog.csdn.net/wuzhikaidetb/article/details/119973580