4. DRAM调度基础:DRAM架构(DDR4/DDR5/LPDDR)、行缓冲策略、预充电与激活
各位同学,今天我们聊聊DRAM调度。说实话,很多做芯片的同学对DRAM的理解停留在「就是个存数据的大池子」。但如果你真的去调过QoS,你会发现——DRAM的脾气比你想的复杂得多。
我个人习惯把DRAM比作一个图书馆。你想想看,你要找一本书,得先走到对应的书架(激活行),然后从书架上抽出来(列访问),看完还得放回去(预充电)。这一套流程,就是DRAM调度的核心。
4.1 DDR4/DDR5/LPDDR:三代架构的演进
先说说这三代架构的区别。我在做上一款AI芯片时,DDR4还是主流,现在DDR5已经铺开了,LPDDR则在移动端大杀四方。
| 参数 | DDR4 | DDR5 | LPDDR5 |
|---|---|---|---|
| 数据速率 | 1600-3200 MT/s | 4800-6400 MT/s | 3200-6400 MT/s |
| Bank数量 | 16 | 32(分两组) | 8-16 |
| 预取宽度 | 8n | 16n | 16n |
| 工作电压 | 1.2V | 1.1V | 0.5-0.9V |
| 命令总线 | 单端 | 差分 | 单端 |
DDR5最大的变化是什么?我个人觉得是Bank Group的引入。DDR5把32个Bank分成两组,每组16个。这样做的好处是——你可以同时激活两个不同组的Bank,并行度直接翻倍。
我曾经在一个项目中,把DDR4的调度器直接搬到DDR5上,结果性能反而下降了。为什么?因为DDR5的Bank Group特性没利用起来,调度器还在串行处理。后来改了策略,把请求按Bank Group分散,吞吐量提升了30%。
核心要点:DDR5的Bank Group是性能关键。调度时尽量把请求分散到不同Group,别挤在一个Group里死磕。
LPDDR呢?它更注重功耗。LPDDR5有个叫「深度睡眠」的模式,功耗可以降到微瓦级。但代价是唤醒延迟很大——大概几百纳秒。如果你的系统频繁唤醒,性能会很难看。
4.2 行缓冲策略:命中就是王道
行缓冲(Row Buffer)是DRAM性能的命门。说白了,DRAM每次访问都要先打开一行,把数据读到行缓冲里。如果下一次访问还是同一行,那就直接命中,省掉激活时间。
我给你们算笔账:
- 行命中:延迟约15-20ns
- 行冲突(先预充电再激活):延迟约40-50ns
- 行未命中(直接激活新行):延迟约30-35ns
你看,行命中和行冲突差了将近3倍。所以调度器的核心任务就是——尽量让访问落在同一行。
怎么做到?我分享几个实战经验:
- 地址映射要合理:把连续地址映射到同一行。我见过一个项目,地址映射没做好,导致连续访问都在不同行,性能直接腰斩。
- 利用Bank级并行:如果一个Bank正在预充电,赶紧去访问另一个Bank。别傻等着。
- 关闭策略要谨慎:DDR4默认是「自动预充电」,每次访问完就关闭行。但如果你知道下一笔访问还是同一行,手动关闭就是浪费。
小技巧:在调度器里加一个「行命中预测器」。如果最近几次都命中同一行,就别急着预充电,等一等。我试过,命中率能提高10-15%。
4.3 预充电与激活:时间就是金钱
预充电(Precharge)和激活(Activate)是DRAM最耗时的操作。激活需要打开一行,预充电需要关闭一行。这两个操作都不能被中断,而且有严格的时序约束。
DDR4的时序参数大概是这样:
| 参数 | 含义 | 典型值(DDR4-3200) |
|---|---|---|
| tRCD | 激活到列访问 | 13.75ns |
| tRP | 预充电时间 | 13.75ns |
| tRAS | 激活到预充电最小间隔 | 32ns |
| tRC | 两次激活最小间隔 | 45.75ns |
嗯,这里要注意。tRAS这个参数经常被忽略。它规定了激活后至少要等多久才能预充电。如果你提前预充电,数据可能还没读完,那就出错了。
我曾经踩过一个坑:为了追求低延迟,我让调度器在读完数据后立刻预充电。结果发现某些场景下数据读出来是错的。查了半天,原来是tRAS没满足。从那以后,我每次写调度器都会把时序参数表贴在墙上。
警告:不要为了性能牺牲时序。DRAM的时序参数是物理限制,不是建议值。违反时序会导致数据损坏,而且很难复现。
再说说激活策略。我个人习惯用「提前激活」——在数据真正需要之前,先把行打开。比如,你正在处理第N行,调度器可以预测第N+1行,提前激活。这样当请求到来时,行已经准备好了。
但提前激活也有风险:如果预测错了,你白白浪费了激活时间和功耗。所以预测算法要保守一点,别太激进。
4.4 知识体系总览
下面这张图是我自己画的,把DRAM调度的核心逻辑串起来了。你们可以保存下来,写代码时对照着看。
这张图的核心逻辑就是:请求进来,先判断行命中情况,然后选择最优路径。别忘了最后一步——反馈。把命中率统计出来,用来优化预测算法。
好了,DRAM调度的基础就讲到这里。记住三件事:行命中是王道,时序不能省,Bank Group要利用好。下一节我们聊聊更具体的调度算法,到时候我会拿实际项目的代码出来分析。
课后思考:如果你的系统里同时有DDR5和LPDDR5,调度器该怎么统一管理?两种架构的时序参数差异很大,你会怎么设计一个通用的调度框架?