2、DDR控制器架构:模块划分与通路设计

好,咱们进入第二章。这一章我聊聊DDR控制器的内部骨架——架构。很多初学者会直接去抠寄存器,结果越看越懵。为啥?因为你不清楚每个寄存器到底管哪个模块,数据是怎么流过去的。

我个人习惯是:先看框图,再读手册。结构理顺了,寄存器配置就是填空题。今天咱们就把这个框图画清楚。

2.1 典型DDR控制器框图

先给一个典型的DDR控制器内部结构,我手绘过无数遍,每次给新人培训都从这个图开始。

典型DDR控制器内部架构 典型DDR控制器内部架构 AHB / AXI 系统总线接口 地址映射 Bank / Row / Col 命令调度器 (仲裁/重排序) 时序约束检查 数据通路 写数据缓冲 / 读数据缓冲 ECC / CRC 校验 PHY 接口 DFI 协议 DLL / DQS DDR 颗粒 配置寄存器组 (CSR) — 模式寄存器、时序参数、控制位 通过 APB 从接口访问,驱动各模块工作模式 时序控制单元(TCU) tRCD / tCL / tCWL / tRFC / tRP / tFAW … 💡 关键说明: 1. 命令调度器根据时序约束将总线请求转换为DDR命令序列(ACT/RD/WR/PRE/REF)。 2. 数据通路在读/写方向独立,支持数据掩码和ECC。 3. 配置寄存器组通过独立的APB从接口写入,不影响主数据流。

这个图我刚入行时啃了很久。你看最左边是系统总线接口(AXI/AHB),经过地址映射、命令调度、数据通路,最后通过PHY连到DDR颗粒。下面横着一大块是配置寄存器组,所有模块都受它控制。

嗯,要注意——配置寄存器组是通过独立的APB从接口读写的,不会干扰主数据通路。这一点在设计验证时特别重要,我以前遇到过有人把APB和AXI地址搞混,结果配置写不进去,仿真跑了半天全是未知状态。

2.2 控制器内部模块划分

我把主要模块拆开聊聊,每个模块在项目里都见过坑。

模块功能常见寄存器配置域
总线接口单元(BIU)转换AXI/AHB协议,处理outstanding、burstAXI_*_CFG、OUTSTANDING_LIMIT
地址映射单元(AMU)将系统地址映射为rank/bank/row/colADDR_MAP_*、ROW_BIT、COL_BIT
命令调度器(CS)仲裁、重排序、生成ACT/RD/WR/PRE/REFSCHED_*、PRIORITY、BANK_MGMT
时序控制单元(TCU)管理tRCD/tCL/tRP/tRFC等时序参数DRAM_TIMING_*
数据通路(DP)写数据缓冲、读数据缓冲、CRC/ECCDATA_CTRL、ECC_EN、CRC_EN
PHY接口单元(PIU)DFI协议转换、DLL控制、DQS门控DFI_*、PHY_*、DLL_CTRL
配置寄存器组(CSR)APB从接口,存储所有配置整个寄存器地址空间

你看这张表,每个模块都对应一组寄存器域。配置的时候如果不清楚模块归属,很容易张冠李戴。我曾经在调时序时一直改TCU的寄存器,结果发现调度器的优先级配置没动,命令顺序乱套了。

2.3 数据通路与控制通路

这是核心概念。说白了,DDR控制器内部有两条“路”:一条干活(数据),一条发令(控制)。

控制通路

控制通路负责生成命令序列。来自总线的读/写请求先进入命令调度器,调度器根据仲裁策略(比如Bank轮询、优先级、QoS)选出下一个待处理请求,然后结合时序控制单元查表,输出ACT、READ、WRITE、PRECHARGE、REFRESH等命令。

我建议你把控制通路想象成“交通警察”。路口有多个方向的车(请求),警察根据红绿灯(时序参数)决定哪个方向先走,同时还要保证不撞车(Bank冲突检测)。

数据通路

数据通路相对单纯,就是搬数据。写方向:从总线接收写数据,经过写缓冲,加上ECC/CRC校验码(如果使能),然后通过PHY打到DDR颗粒。读方向:从颗粒读回数据,经过数据缓冲,校验,再返回给总线。

有意思的是,写数据通路和读数据通路是完全独立的。你想想看,如果读写共用一个缓冲,那DDR的读写交替效率会大打折扣。所以控制器内部一般都有独立的write buffer和read buffer,深度可配置。

实战经验:数据通路里的ECC生成和校验需要在正确的时序点上使能。我以前有个项目,ECC使能位写晚了两个时钟周期,结果读回来的数据校验全部报错——因为前几笔没加ECC,后面校验又强制使能,乱套了。记住:ECC的使能应该在初始化阶段完成,不要在正常工作后动态开关。

2.4 关键设计考量

  1. 命令重排序的代价:为了提高总线利用率,调度器会乱序执行请求。但乱序带来的是数据返回顺序问题,需要给每个请求打标签(transaction ID)。AXI总线本身支持ID,这一点还好。
  2. 时序参数表的设计:不同DDR速率等级时序差异很大。DDR4-2400和DDR4-3200的tRCD能差2~3个时钟。我建议把时序参数做成一张查找表,或者用寄存器组灵活配置,不要硬编码。
  3. 低功耗与自刷新:控制通路里还要处理自刷新(Self-Refresh)进入/退出、预充电省电(Precharge Power-Down)、时钟停止等。这些都需要在控制通路的状态机中妥善处理。

小技巧:调试时可以先旁路命令调度器的重排序逻辑,设置所有请求按FIFO顺序执行。这样能快速确认时序参数配置是否正确。我每次调新板子都这么干,等基本配置对了再开重排序。

2.5 控制通路与数据通路的交互

这两条路不是完全独立的。举个具体的例子:读命令从控制通路发出后,数据通路需要等待一个可变的延迟(read latency)才能从PHY拿到数据。这个延迟由timing参数和DLL相位决定。

控制通路会通知数据通路:“我发了个读命令,地址是X,你预计在N个周期后收到数据。”数据通路收到通知后,在对应时刻打开读缓冲窗口。如果窗口没对准,数据就丢了——这就是常说的“数据训练”问题。

⚠️ 曾经掉过的坑:我在一个DDR4项目里,控制通路输出的读命令和数据通路的窗口使能信号之间差了半个时钟周期(跨时钟域没处理干净),导致读数据偶尔少一个beat。后来在PHY侧加了延时链才修好。所以这两个通路之间的握手信号,一定要做async FIFO同步,不要偷懒用两级寄存器。

好了,这一章把DDR控制器的骨架搭起来了。后面我们聊寄存器的时候,你会不断回头对照这个框图:这个位是控制命令调度器的,那个域是调节时序的,这个使能是开ECC的——心里就有底了。