第二章 DDR控制器架构:内部模块划分、命令调度器、数据通路、地址映射

好,咱们接着聊DDR控制器。上一章我大概说了说DDR的基本概念,这一章咱们深入内部,看看控制器到底是怎么工作的。

说实话,我刚接触DDR控制器那会儿,觉得这东西就是个黑盒子——给个地址,数据就出来了。后来自己动手做项目,才发现里面的门道多着呢。你想想看,CPU和DDR颗粒之间,速度差了好几个数量级,中间没个精密的调度系统,那不乱套了?

2.1 控制器内部模块划分

一个典型的DDR控制器,我习惯把它拆成这么几大块:

  • 前端接口模块:负责和CPU、DMA或者咱们的卷积加速器打交道。说白了,就是接收读写请求的地方。
  • 命令调度器:这是核心中的核心。它决定哪个请求先执行,哪个后执行。
  • 数据通路:负责数据的搬移、对齐、缓冲。
  • 地址映射模块:把逻辑地址翻译成DDR颗粒能懂的物理地址。
  • PHY接口:物理层,直接和DDR颗粒的引脚相连。

我在项目中遇到过一种情况:前端接口设计得太复杂,结果调度器天天在等数据,效率低得可怜。后来我学乖了,前端接口尽量简单,把复杂的逻辑扔给调度器去处理。

核心要点:模块划分的原则是“高内聚、低耦合”。每个模块只干一件事,干好一件事。

2.2 命令调度器

命令调度器,说白了就是个“交通警察”。它手里握着多个请求,得决定先放谁过去。

为什么会需要调度?因为DDR颗粒有各种时序约束——比如行激活到列读写之间要等tRCD,读写切换要等tWTR。如果调度器不懂这些,乱发命令,DDR颗粒就会罢工。

我常用的调度策略有这么几种:

  1. 先来先服务(FCFS):最简单,但效率最低。你想想看,如果先来的请求要读Bank0,后来的请求要读Bank1,但Bank0正在预充电,那后面的请求就得干等着。
  2. 优先级调度:给读写请求打上优先级标签。比如卷积加速器的权重读取,优先级就比普通数据高。
  3. Bank交错调度:这是我最喜欢的一种。它利用DDR颗粒的多Bank特性,当一个Bank在预充电时,调度器可以切换到另一个Bank去操作。

个人经验:我曾经在一个项目中,单纯用FCFS调度,DDR带宽利用率只有30%。后来改成Bank交错调度,直接飙到85%。差距就这么大。

调度器的内部结构,我画了个图,你一看就明白:

命令调度器内部结构 请求队列 (来自前端接口) 仲裁器 (选择下一个命令) 时序检查 (tRCD/tWTR等) 命令输出到PHY 状态反馈(Bank状态、时序计数器)

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交错调度效率最高
  • 数据通路要注意位宽对齐和缓冲深度
  • 地址映射选交错方式,对卷积加速器更友好

可可精品资料库