2. SMMU核心架构:TBU、TCU与地址翻译流程

好,咱们直接进入正题。SMMU 内部到底长什么样?很多人看了几百页的 ARM 架构手册,还是搞不清 TBU 和 TCU 到底怎么分工。我刚开始接触 SMMU 时也犯过这毛病——以为它就是个黑盒子,IO 设备发个地址进去,出来就是物理地址。其实内部拆开看,结构相当清晰。

2.1 SMMU 的两大核心模块:TBU 与 TCU

ARM 把 SMMU 拆成了两个逻辑部分:TBU(Translation Buffer Unit) 和 TCU(Translation Control Unit)。说白了,一个管数据缓存,一个管控制逻辑。

模块 全称 核心职责 硬件位置
TBU Translation Buffer Unit TLB 缓存、页表 walk 请求发起 靠近 IO 设备端(分散部署)
TCU Translation Control Unit 页表基址管理、配置寄存器、一致性协议 集中式控制单元

我在一个项目中遇到过这样的场景:芯片里挂了 8 个 PCIe EP 设备,每个设备都配了一个 TBU。结果调试时发现某个 TBU 的 TLB 老是 miss,性能上不去。查了半天,原来是 TCU 里配的页表基址寄存器写错了——TBU 本身没错,是控制层给了错误指令。所以,理解这两者的分工,对定位问题至关重要。

2.2 TBU 微架构:TLB 与页表 Walk 引擎

TBU 内部其实是个小型的“翻译引擎”。它主要包含三部分:

  • TLB 阵列:缓存最近用过的页表项。我习惯把它理解成“快表”,命中就直接出结果。
  • 页表 Walk 引擎:TLB miss 时,自动发起多级页表查询。这个引擎会按页表层级一级一级往下读内存。
  • 请求队列:缓存等待翻译的 IO 请求。当 TLB miss 且 walk 引擎忙时,请求先排队。

关键点:TBU 不存储页表本身,它只负责“查”和“缓存”。页表数据存在系统内存里,TCU 告诉 TBU 页表基址在哪。

你想想看,如果每个 TBU 都自己维护一份完整的页表,那内存开销得多大?所以 ARM 的设计思路是:TBU 只管跑腿,TCU 管方向。

2.3 TCU 微架构:配置与一致性管家

TCU 是 SMMU 的“大脑”。它负责:

  • Stream Table 管理:每个 IO 设备(Stream)对应一个配置条目,里面存着页表基址、转换粒度、权限等信息。
  • 全局配置寄存器:比如 SMMU_CR0、SMMU_CR1,控制整个 SMMU 的使能、安全模式、异常处理策略。
  • TLB 一致性协议:当软件修改了页表,TCU 负责向所有 TBU 广播 TLB 无效化命令。

个人经验:调试 TLB 一致性问题时,我建议先检查 TCU 的 SMMU_TLBIVAL 寄存器是否写对了。曾经有个项目,软件工程师只 invalidate 了本地的 TBU,忘了广播给其他 TBU,结果数据错乱查了两天。

2.4 地址翻译流程概览

好,现在我们把 TBU 和 TCU 串起来,看看一次完整的地址翻译是怎么走的。我画了一张流程图,方便你对照理解。

SMMU 地址翻译流程 SMMU 地址翻译流程 IO 设备发起 DMA 请求 TBU 接收请求 TLB 命中? 直接返回物理地址 TBU 发起页表 Walk TCU 提供 Stream 配置 从内存读取页表项 更新 TLB + 返回物理地址 IO 设备继续 DMA 操作 TLB 命中时直接返回 未命中才走 Walk 流程

流程其实不复杂,我拆成几步讲:

  1. IO 设备发起 DMA:设备发一个 IOVA(IO Virtual Address)给 TBU。
  2. TBU 查 TLB:如果命中,直接返回物理地址,走快速路径。
  3. TLB Miss 触发 Walk:没命中?TBU 的 walk 引擎开始工作。它先问 TCU 要这个 Stream 的页表基址和配置。
  4. 多级页表查询:Walk 引擎按页表层级(比如 4 级页表)逐级读取内存。每一级都从内存中取出下一级的基址。
  5. 更新 TLB 并返回:找到最终物理地址后,写入 TLB 缓存,同时把结果返回给 IO 设备。

注意:页表 Walk 是串行的。如果页表层级深(比如 4 级),一次 miss 就要读 4 次内存。我在调试一个 NVMe 控制器时,发现随机读性能差,就是因为 TLB 命中率太低,每次都要走 4 级 walk。后来通过调整页表大小(用 2MB 大页),性能直接翻倍。

2.5 TBU 与 TCU 的交互协议

TBU 和 TCU 之间怎么通信?ARM 定义了一套内部接口协议,我简单列一下关键信号:

信号方向 信号名 含义
TBU → TCU REQ_STREAMID 请求查询某个 Stream 的配置
TCU → TBU CFG_TTBR 返回页表基址(Translation Table Base Register)
TCU → TBU CFG_TCR 返回页表控制寄存器(粒度、层级等)
TCU → TBU TLBI_CMD 广播 TLB 无效化命令

嗯,这里要注意:TCU 返回的配置是缓存在 TBU 内部的。也就是说,TBU 不会每次翻译都去问 TCU——它会把 Stream 配置缓存下来。只有 Stream 配置被修改时,TCU 才会发无效化命令。

2.6 微架构设计的权衡

实际芯片设计时,TBU 和 TCU 的部署方式有很多变种。我见过三种常见方案:

  • 全集中式:一个 TCU 带一个 TBU,所有设备共享。适合设备少、性能要求不高的场景。
  • 全分布式:每个设备配独立的 TBU,TCU 集中管理配置。适合高性能场景,但芯片面积大。
  • 混合式:几个设备共享一个 TBU,TCU 做全局协调。这是大多数 SoC 的选择。

我的建议:调试时先搞清楚你的芯片用的是哪种拓扑。我曾经在混合式架构上吃过亏——两个设备共享一个 TBU,结果一个设备改了页表,另一个设备的 TLB 没被 invalidate,数据全乱了。后来在 TCU 侧加了 Stream 隔离才解决。

2.7 小结

这一章我们把 SMMU 的核心架构拆开了。TBU 管缓存和 walk,TCU 管配置和一致性。地址翻译的路径其实很直观:TLB 命中走快道,miss 走慢道。理解了这个流程,后面讲页表管理和 TLB 结构时,你就能知道每个环节的瓶颈在哪。