第二章 DDR控制器架构:内部模块划分、命令调度器、数据通路、地址映射
好,咱们接着聊DDR控制器。上一章我大概说了说DDR的基本概念,这一章咱们深入内部,看看控制器到底是怎么工作的。
说实话,我刚接触DDR控制器那会儿,觉得这东西就是个黑盒子——给个地址,数据就出来了。后来自己动手做项目,才发现里面的门道多着呢。你想想看,CPU和DDR颗粒之间,速度差了好几个数量级,中间没个精密的调度系统,那不乱套了?
2.1 控制器内部模块划分
一个典型的DDR控制器,我习惯把它拆成这么几大块:
- 前端接口模块:负责和CPU、DMA或者咱们的卷积加速器打交道。说白了,就是接收读写请求的地方。
- 命令调度器:这是核心中的核心。它决定哪个请求先执行,哪个后执行。
- 数据通路:负责数据的搬移、对齐、缓冲。
- 地址映射模块:把逻辑地址翻译成DDR颗粒能懂的物理地址。
- PHY接口:物理层,直接和DDR颗粒的引脚相连。
我在项目中遇到过一种情况:前端接口设计得太复杂,结果调度器天天在等数据,效率低得可怜。后来我学乖了,前端接口尽量简单,把复杂的逻辑扔给调度器去处理。
核心要点:模块划分的原则是“高内聚、低耦合”。每个模块只干一件事,干好一件事。
2.2 命令调度器
命令调度器,说白了就是个“交通警察”。它手里握着多个请求,得决定先放谁过去。
为什么会需要调度?因为DDR颗粒有各种时序约束——比如行激活到列读写之间要等tRCD,读写切换要等tWTR。如果调度器不懂这些,乱发命令,DDR颗粒就会罢工。
我常用的调度策略有这么几种:
- 先来先服务(FCFS):最简单,但效率最低。你想想看,如果先来的请求要读Bank0,后来的请求要读Bank1,但Bank0正在预充电,那后面的请求就得干等着。
- 优先级调度:给读写请求打上优先级标签。比如卷积加速器的权重读取,优先级就比普通数据高。
- Bank交错调度:这是我最喜欢的一种。它利用DDR颗粒的多Bank特性,当一个Bank在预充电时,调度器可以切换到另一个Bank去操作。
个人经验:我曾经在一个项目中,单纯用FCFS调度,DDR带宽利用率只有30%。后来改成Bank交错调度,直接飙到85%。差距就这么大。
调度器的内部结构,我画了个图,你一看就明白:
2.3 数据通路
数据通路,说白了就是数据从A点到B点的“高速公路”。但这条路上有很多收费站和检查站。
数据通路主要干这几件事:
- 数据缓冲:用FIFO或者BRAM暂存数据。我一般会在读路径和写路径各放一个FIFO,深度至少是DDR突发长度的两倍。
- 数据对齐:DDR颗粒的数据位宽通常是16位或32位,但CPU可能是64位甚至128位。不对齐的话,数据就串位了。
- 字节掩码:写操作时,有时候只想写某个字节,其他字节保持不变。这时候就需要字节掩码信号。
注意:数据通路的宽度要和DDR颗粒匹配。我曾经见过有人把64位的数据通路接到16位的DDR颗粒上,结果数据一直出错,查了三天才发现是位宽没对齐。
数据通路的时序,我一般这么处理:
// 写数据通路示例
always @(posedge clk) begin
if (wr_en) begin
wr_fifo <= {wr_data, wr_mask}; // 数据和掩码一起存
end
end
// 读数据通路示例
always @(posedge clk) begin
if (rd_valid) begin
rd_data <= rd_fifo;
// 注意:读数据有延迟,需要对齐
end
end
2.4 地址映射
地址映射,嗯,这里要特别注意。很多新手在这里栽跟头。
DDR颗粒的物理地址是分层的:Bank -> Row -> Column。但CPU或者加速器看到的地址是连续的线性地址。怎么把线性地址映射到物理地址,这就是地址映射干的事。
我常用的映射方式有两种:
| 映射方式 | 说明 | 适用场景 |
|---|---|---|
| 顺序映射 | 先Bank,再Row,最后Column | 连续大块数据读写 |
| 交错映射 | 先Column,再Bank,最后Row | 随机小数据访问 |
举个例子,假设地址是32位:
// 顺序映射
bit[31:28] = Bank地址
bit[27:16] = Row地址
bit[15:0] = Column地址
// 交错映射
bit[31:20] = Row地址
bit[19:16] = Bank地址
bit[15:0] = Column地址
我的建议:做卷积加速器的时候,用交错映射效果更好。因为卷积核的权重访问是随机的,交错映射能充分利用Bank并行性。
地址映射还有一个坑——地址对齐。DDR的突发长度通常是8,也就是说一次读写至少传输8个数据。如果你只读1个数据,DDR也会读8个,浪费带宽。所以,我一般建议把地址对齐到突发边界。
好了,这一章的内容就这些。DDR控制器的内部结构,说白了就是这几个模块的协同工作。下一章咱们聊聊怎么用FPGA实现一个简单的DDR控制器。
本章小结:
- 控制器分五大模块:前端接口、命令调度器、数据通路、地址映射、PHY接口
- 命令调度器是核心,Bank交错调度效率最高
- 数据通路要注意位宽对齐和缓冲深度
- 地址映射选交错方式,对卷积加速器更友好
可可精品资料库