第四节:DDR基本操作命令——ACTIVATE、READ、WRITE、PRECHARGE、REFRESH
DDR的操作,说白了就是一套固定的“指令集”。你给DDR芯片发什么命令,它就干什么活。我刚开始接触DDR时,看着那堆命令真值表,头都大了。后来发现,其实核心命令就那么几个:激活、读、写、预充电、刷新。把这五个搞明白,DDR控制器的设计就入门了。
4.1 命令真值表——DDR的“摩斯密码”
DDR的命令不是靠一根线传的,而是靠一组信号组合编码。CS_n、RAS_n、CAS_n、WE_n,这四个信号的不同组合,就对应了不同的操作。嗯,这里要注意,这些信号都是低电平有效,所以看真值表时别搞反了。
| 命令 | CS_n | RAS_n | CAS_n | WE_n |
|---|---|---|---|---|
| NOP(空操作) | H | X | X | X |
| ACTIVATE(激活) | L | L | H | H |
| READ(读) | L | H | L | H |
| WRITE(写) | L | H | L | L |
| PRECHARGE(预充电) | L | L | H | L |
| REFRESH(刷新) | L | L | L | H |
你看,RAS_n和CAS_n就像两个开关,WE_n决定读写方向。我习惯把这四个信号画成一张小卡片贴在显示器边上,调试时一眼就能扫到。
4.2 ACTIVATE——先开门,再干活
DDR内部是按Bank组织的,每个Bank又有很多行。你要读写某个地址,必须先激活它所在的行。ACTIVATE命令就是干这个的——把目标Bank的某一行打开,数据才能被访问。
ACTIVATE命令需要带上Bank地址(BA)和行地址(Row Address)。发完ACTIVATE后,不能立刻读写,得等tRCD(RAS to CAS Delay)时间。这个时间一般是几个时钟周期,具体看DDR的速率等级。
关键参数:tRCD
从ACTIVATE发出到可以发READ/WRITE的最小间隔。DDR3-1600的tRCD通常是11-13个时钟周期。设计控制器时,这个参数决定了你的状态机跳转时机。
我在项目中遇到过一个问题:连续激活同一Bank的不同行,结果发现性能上不去。后来一查,是因为tRC(Row Cycle Time)没满足,两次激活之间必须隔开一定时间。说白了,你不能频繁开关同一Bank的门,得给它点喘息时间。
4.3 READ——把数据从DDR里拽出来
行激活之后,就可以发READ命令了。READ命令需要带上Bank地址和列地址(Column Address)。DDR有个特点:一次READ命令会连续读出多个数据(Burst Length,通常是8)。
发完READ命令后,数据不会立刻出现在DQ引脚上。得等CL(CAS Latency)个时钟周期。CL是DDR最重要的时序参数之一,它决定了读命令到第一个数据输出之间的延迟。
个人经验:CL值可以在初始化时通过模式寄存器配置。我一般选中间值,既不太快(容易出错)也不太慢(浪费性能)。比如DDR3-1600,CL=11是个稳妥的选择。
READ命令还有个变种——带预充电的读(Read with Auto-Precharge)。发命令时带上A10=1,读完数据后自动执行预充电,省得你再发一条PRECHARGE命令。这个技巧在随机访问场景下特别好用。
4.4 WRITE——把数据写进DDR
WRITE命令和READ类似,也需要先激活行。区别在于,WRITE命令发出时,数据要同时出现在DQ引脚上。也就是说,写数据是跟着命令一起送进去的。
WRITE命令也有延迟参数——WL(Write Latency)。WL = CL - 1(对于DDR3/DDR4)。所以写数据比读数据早一个周期到达。这个细节在设计控制器时很容易忽略,我刚开始就踩过这个坑。
避坑指南:我曾经在写控制器时,把读和写的时序混在一起处理,结果写操作总是丢数据。后来发现,写数据必须和WRITE命令严格对齐,差一个周期都不行。建议在状态机里把读和写的路径分开设计。
WRITE命令同样支持带预充电的写(Write with Auto-Precharge)。如果你连续写同一行,就别用这个功能,手动控制预充电时机更灵活。
4.5 PRECHARGE——关门,准备下一次
读写操作完成后,当前行必须关闭,才能激活下一行。PRECHARGE命令就是干这个的。它把Bank的位线预充到VDD/2,为下一次激活做准备。
PRECHARGE可以针对单个Bank,也可以针对所有Bank(通过A10=1)。发完PRECHARGE后,需要等tRP(Row Precharge Time)时间,才能再次激活该Bank。
你想想看,如果不做预充电,直接激活另一行,会发生什么?两个行的数据会在位线上打架,读出来的数据全是错的。所以预充电不是可选项,是必选项。
4.6 REFRESH——DDR的“续命”操作
DDR的存储单元是电容,电容会漏电。如果不定期刷新,数据就丢了。REFRESH命令就是给这些电容“充电续命”的。
DDR芯片内部有刷新计数器,你只需要发REFRESH命令,它自己会刷新所有行。刷新间隔是tREFI,通常是7.8微秒(对于温度低于85°C的场景)。每次刷新需要tRFC时间,DDR3-1600的tRFC大约是110个时钟周期。
关键点:刷新期间,整个DDR芯片都不能做读写操作。所以刷新命令的插入时机很讲究。我一般用“分散刷新”策略——每隔一段时间插一条REFRESH,而不是攒到一块儿刷。这样对读写性能的影响最小。
我曾经在一个项目中,因为刷新间隔算错了,导致高温下数据频繁出错。后来用示波器抓波形才发现,刷新命令发得太慢了,电容都漏光了才补电。嗯,从那以后,我每次设计控制器都会把刷新逻辑单独拿出来验证。
4.7 命令执行流程——一张图看懂
下面这张图展示了DDR基本操作的完整流程。从激活到读写,再到预充电或刷新,每一步都不能少。
这张图里,我特意把REFRESH画成了旁路。为什么?因为刷新是定时触发的,和读写操作是并行的。你可以在任何空闲时刻插入REFRESH,但一旦开始刷新,所有Bank都得等着。
4.8 小结
ACTIVATE、READ、WRITE、PRECHARGE、REFRESH,这五个命令构成了DDR操作的基础。设计控制器时,你只需要记住三件事:
- 先激活,再读写——ACTIVATE是读写的前提,别跳步。
- 读写完要关门——PRECHARGE不是可选项,是必选项。
- 刷新不能忘——7.8微秒刷一次,漏了数据就没了。
我个人习惯把这五个命令的时序参数(tRCD、CL、WL、tRP、tRFC)做成一个表格,放在代码注释里。调试时对照着看,能省不少时间。好了,这一节就到这里,下一节我们聊聊DDR控制器的状态机设计。