3. Kria DDR接口架构解析
Kria SOM 的 DDR 存储体系并不是“一颗 DDR 芯片接一个控制器”这么简单。以 Kria K26 为例,PS 侧有专用 DDR 控制器直连 PS DDR4 颗粒,PL 侧则通过 NoC(Network on Chip)与 AXI 接口访问同一片或独立的 DDR 存储资源。理解这套架构的关键,是分清“谁发起访问、经过哪条路径、落到哪个地址、由谁仲裁”。
一、PS 端 DDR 控制器:固定通道与固定地址
PS 端 DDR 控制器(DDRC)位于 Zynq UltraScale+ MPSoC 的 PS 内部,直接连接 PS DDR4 颗粒,通常为 64 位或 32 位数据宽度。它的地址空间在系统启动阶段由 FSBL 或 PMU 固件根据 DDR 容量完成初始化,之后对软件呈现为一段连续的物理地址区间。Kria K26 常见配置为 4GB PS DDR,其物理地址起点为 0x0000_0000,结束于 0xFFFF_FFFF。这段空间被划分为多个用途区域:低地址区用于 FSBL、PMU 固件和 ATF,中段用于 U-Boot、Linux 内核与设备树,高段用于根文件系统、CMA 连续内存和保留区。
PS DDRC 的访问主体包括:Cortex-A53 应用核、Cortex-R5 实时核、PMU、DMA 控制器以及 PL 通过 AXI HP/NoC 发起的访问。PS 内部通过一致性互连(CCI)和 SMMU 对访问进行路由与保护。需要特别注意的是,PS DDRC 的带宽是有限的,当 PL 侧高吞吐数据流与 CPU 同时访问时,会通过 QoS 机制竞争,表现为延迟抖动而非直接报错。
二、PL 端 DDR 接口:NoC 与 AXI 两条路径
PL 侧访问 DDR 有两条典型路径。第一条是经 NoC 的 AXI 接口:Kria K26 提供多个 AXI Slave 端口(如 S_AXI_HP0~HP3、S_AXI_HPC0/1),PL 逻辑通过这些端口发起读写,NoC 负责地址译码、QoS 调度和跨时钟域转换,最终路由到 PS DDRC 或 PL DDR。第二条是 PL 直接连接独立的 DDR4 颗粒,由 PL 内例化的 DDR4 IP(如 Xilinx DDR4 SDRAM IP)管理,这条路径不经过 PS DDRC,地址空间独立。
两条路径的选择依据是数据归属:如果数据需要被 CPU 直接处理、参与 Linux 内存管理,应走 PS DDR;如果是纯 PL 内部的高带宽流式处理(如视频帧缓存、雷达采样缓存),且 CPU 只做控制不做逐字节处理,走 PL DDR 或 NoC 到 PS DDR 的保留区更合适。NoC 路径的优势是带宽高、延迟可预测,但需要正确配置 AXI 端口的位宽、突发长度和 QoS 等级。
三、AXI 互联与地址译码
PL 发出的 AXI 事务首先进入 NoC 的 AXI Slave 端口,NoC 根据事务地址的高位进行译码。以 4GB PS DDR 为例,若 PL 要访问 PS DDR 的 0x0000_0000~0x7FFF_FFFF 区间,NoC 会将该事务路由到 PS DDRC 的对应端口。若地址落在 PL DDR 区间(例如 0x8_0000_0000 起始),则路由到 PL DDR 控制器。地址译码表在 Vivado 的 Address Editor 中配置,必须与软件侧设备树中的 reg 属性一致,否则会出现“写进去读不出来”或总线挂死。
AXI 互联还涉及数据宽度转换。PL 逻辑常用 128 位或 256 位 AXI 数据宽度,而 PS DDRC 的本地接口可能是 64 位或 128 位。NoC 会自动完成位宽转换和突发拆分,但前提是 AXI 事务的突发长度不超过端口支持的最大值。例如,一个 256 位宽、突发长度 16 的写事务,总数据量为 512 字节,NoC 会将其拆分为多个 64 位事务发往 DDRC。如果突发长度设置过大,可能导致 NoC 缓冲区溢出,表现为 AXI 响应超时。
四、内存映射与地址空间分配
Kria 的地址空间分配遵循“PS 固定、PL 可配、保留区隔离”的原则。PS DDR 的低 2GB 通常映射为 Linux 可管理内存,高 2GB 中划出 CMA 区域供 DMA 使用,再高段保留给 PL 通过 NoC 访问的共享缓冲区。PL DDR 的地址空间由 Vivado 地址编辑器分配,常见做法是将其放在 0x8_0000_0000 以上,避免与 PS DDR 重叠。
在设备树中,PS DDR 由 memory 节点描述,PL DDR 或 NoC 共享区则由 reserved-memory 节点描述。例如,为 PL 视频帧缓存保留 256MB,可写为:
reserved-memory {
#address-cells = <2>;
#size-cells = <2>;
ranges;
pl_fb: framebuffer@80000000 {
reg = <0x0 0x80000000 0x0 0x10000000>;
no-map;
};
};
其中 no-map 表示该区域不进入 Linux 线性映射,避免 CPU 缓存与 PL DMA 产生一致性冲突。如果忘记加 no-map,CPU 可能缓存该区域,导致 PL 写入的数据被 CPU 读成旧值。
五、典型访问路径与判断依据
下面这张图给出了 Kria DDR 接口的架构关系:PS 侧 DDRC 直连 PS DDR4,PL 侧通过 NoC AXI 端口访问 PS DDR 保留区,或通过独立 DDR4 IP 访问 PL DDR。图中同时标出了地址译码的分界点和典型地址区间,便于对照 Vivado 地址编辑器与设备树配置。
从图中可以读出三条判断规则。第一,PS DDR 的地址是固定的,软件启动后不可更改,PL 若要通过 NoC 访问 PS DDR,必须使用设备树中 reserved-memory 声明的保留区地址,不能随意指定。第二,PL DDR 的地址由 Vivado 地址编辑器分配,通常放在 0x8_0000_0000 以上,与 PS DDR 不重叠;如果重叠,NoC 译码会优先路由到 PS DDRC,导致 PL DDR 访问失败。第三,NoC 的 QoS 配置会影响带宽分配,当 PL 高优先级流与 CPU 访问竞争时,可通过调整 AXI 端口的 QoS 值来保证实时性。
devmem 命令从 CPU 侧读写 PL 保留区地址,验证 NoC 路径是否连通。例如 devmem 0x80000000 32 读取一个 32 位字,若返回全 0 或全 F,需检查地址译码和设备树保留区是否匹配。实际工程中,最常见的错误是“PL 写数据后 CPU 读不到”。排查顺序应为:先确认 Vivado 地址编辑器中 PL 主端口的目标地址是否指向 PS DDR 保留区;再确认设备树 reserved-memory 的 reg 是否与之一致;最后确认该区域是否加了 no-map,以及 CPU 是否使用了正确的物理地址进行映射。三步都正确后,NoC 路径通常可以稳定工作。