3. DDR控制器架构:控制器内部模块划分、命令调度器、数据通路、地址映射
好,咱们进入正题。DDR控制器,说白了就是CPU和DDR颗粒之间的“翻译官”加“调度员”。你想想看,CPU发出的读写请求,那叫一个随意,时间、地址、数据长度都没个准。但DDR颗粒呢?它是个“死脑筋”,必须按严格的时序来,什么行激活、列读写、预充电,一步都不能乱。
所以,控制器的核心任务就两个:把CPU的请求翻译成DDR能懂的时序,把DDR返回的数据整理好送回给CPU。我这些年调过的DDR控制器,从Xilinx的MIG到Altera的UniPHY,再到自己撸的RTL代码,架构上其实都大同小异。咱们今天就把它拆开看看。
3.1 控制器内部模块划分
一个典型的DDR控制器,内部大概可以分成这么几块:
- 前端接口(Front-End):负责和CPU/用户逻辑打交道。接收读写请求,返回数据和状态。说白了,这是控制器的“门面”。
- 命令调度器(Command Scheduler):这是控制器的“大脑”。它决定下一个时钟周期该发什么命令给DDR颗粒。是读?是写?还是刷新?
- 数据通路(Data Path):负责数据的读写、对齐、缓冲。这是控制器的“手脚”。
- 地址映射(Address Mapping):把CPU发来的逻辑地址,翻译成DDR颗粒的物理地址(行、列、Bank、Rank)。
- 物理层接口(PHY Interface):直接和DDR颗粒的管脚打交道,处理DQS/DQ信号的延迟校准、ODT控制等。这部分通常和具体的FPGA器件绑定。
嗯,这里要注意,不同厂家的IP核,模块划分的粒度可能不一样。但万变不离其宗,你只要抓住“命令、数据、地址”这三条主线,就能看懂任何一款控制器。
3.2 命令调度器:控制器的“大脑”
命令调度器,我个人觉得这是整个控制器里最考验设计功力的地方。为什么?因为它要处理一堆互相冲突的需求。
举个例子,CPU刚发了一个读请求,紧接着又发了一个写请求。调度器得判断:是先读后写?还是先写后读?如果先读,那读回来的数据会不会和写的数据冲突?如果先写,那读请求的延迟会不会太大?
我遇到过最头疼的情况,是在一个彩超系统里。前端处理单元需要连续读取多行数据,同时后端又要写入新的图像帧。读写请求几乎同时到达,调度器如果处理不好,要么丢数据,要么带宽利用率掉到50%以下。
常见的调度策略有这么几种:
- 严格优先级:读优先或写优先。简单粗暴,但容易造成另一方的“饿死”。
- 轮询(Round-Robin):轮流服务。公平,但延迟不可控。
- 带权重的轮询:给读请求更高的权重。适合读多写少的场景。
- 基于Bank状态的调度:这是最常用的。调度器会维护一个“Bank状态表”,记录每个Bank当前是空闲、激活还是预充电状态。只有处于空闲状态的Bank才能接受新的行激活命令。
我个人习惯用最后一种。说白了,就是“看菜下饭”。哪个Bank准备好了,我就先服务哪个Bank的请求。这样可以最大化利用DDR的并行性。
避坑指南:我曾经在一个项目里,为了追求极致的带宽利用率,把调度器的队列深度设得很大。结果呢?延迟变得非常大,导致前端处理单元的数据缓存频繁溢出。后来我学乖了,调度器的队列深度不是越大越好,要根据系统的延迟容忍度来定。
3.3 数据通路:数据的“高速公路”
数据通路,负责把数据从CPU搬到DDR,再从DDR搬回CPU。听起来简单,但细节很多。
首先,数据宽度要匹配。CPU的数据总线可能是64位或128位,但DDR颗粒的数据总线可能是16位或32位。控制器内部需要做位宽转换。
其次,时钟域要同步。CPU通常工作在几百兆赫兹,而DDR的时钟频率可能高达1.6GHz甚至更高。数据通路里需要用到异步FIFO来跨时钟域。
最后,读写数据要分开。读数据和写数据不能共用同一条总线,否则会打架。所以数据通路里通常有独立的读数据FIFO和写数据FIFO。
我建议你在设计数据通路时,重点关注数据对齐。DDR的读写操作,数据必须和DQS信号对齐。如果对齐不好,采回来的数据全是错的。嗯,这个坑我踩过,调了整整两天才发现是数据对齐的问题。
3.4 地址映射:逻辑到物理的“翻译”
地址映射,就是把CPU发来的连续逻辑地址,映射到DDR颗粒的物理地址空间上。DDR的物理地址是分层的:Rank -> Bank -> Row -> Column。
不同的映射方式,对性能影响很大。举个例子:
| 映射方式 | 逻辑地址位 | 物理地址 | 特点 |
|---|---|---|---|
| 顺序映射 | 低位 -> Column,高位 -> Row | 连续访问同一行 | 适合顺序读写,但频繁换行时效率低 |
| 交叉映射 | 低位 -> Bank,高位 -> Row | 连续访问不同Bank | 适合随机访问,利用Bank并行性 |
| Bank交错映射 | 中间位 -> Bank | 地址在Bank间交错 | 兼顾顺序和随机访问 |
在彩超系统里,我推荐使用Bank交错映射。为什么?因为彩超的数据访问模式很特殊:它既有大块的连续数据(比如一行扫描线),又有小块的随机数据(比如某个ROI区域的像素)。Bank交错映射可以很好地平衡这两种访问模式。
我曾经在一个项目里,默认用了顺序映射。结果做B模式成像时,带宽利用率只有60%。后来改成Bank交错映射,利用率直接飙到85%以上。你想想看,同样的硬件,只是改了一下地址映射方式,性能就提升了这么多。
小技巧:在FPGA里实现地址映射,最简单的方法就是用“位重组”。比如,把逻辑地址的[5:3]位映射到Bank地址,[2:0]位映射到Column地址的低位。这样只需要几根连线,不需要额外的逻辑资源。
3.5 总结一下
DDR控制器的架构,说白了就是“一个大脑,两条通路,一张地图”。
- 大脑是命令调度器,决定什么时候做什么事。
- 两条通路是数据通路和命令通路,一个管数据,一个管控制。
- 一张地图是地址映射,告诉数据该去哪。
理解了这些,你再看任何一款DDR控制器的数据手册,都不会觉得头大了。下一节,咱们聊聊DDR的时序参数,那才是真正考验耐心的地方。