第三章:FPGA PCIe Root Port 与 Endpoint 有什么区别?NVMe SSD 为什么需要 Host
2026/8/29 4:50:52 网站建设 项目流程

本篇位置:FPGA NVMe Host 实战连载 · 🟩 第一幕|共同底座 · 第 03 篇

第 02 篇已经把 M.2 SSD 的供电、参考时钟、复位和 Lane 检查了一遍。假设现在user_lnk_up=1,LTSSM 已经进入 L0,说明 FPGA 与 SSD 的 PCIe 物理链路已经可以传输数据。

但这时 SSD 还没有真正“被识别”。如果 FPGA 不主动发起配置访问,SSD 不会自己把 Vendor ID、BAR 或 NVMe 寄存器送到 FPGA 面前。

原因在于两端的 PCIe 角色不同:三星 980 这样的 NVMe SSD 是Endpoint;FPGA 直连 SSD 时,需要站在Root Port和 Host 一侧,负责从配置空间访问开始,把后面的枚举、BAR、NVMe 初始化和数据通路逐步建立起来。

本文沿着下面这条控制路径展开:

FPGA Root Port → PCIe Configuration Request → NVMe SSD Endpoint → Completion → FPGA

先把这几个角色分清,后面的 Vendor ID、BAR、CAP、CC、CSTS、队列和 DMA 才不会混在一起。

PC 和 FPGA 直连 SSD,谁在做 Host

在 PC 里,CPU、芯片组、固件和操作系统共同承担 Host 一侧工作。SSD 插入 M.2 插槽后,系统会完成配置访问、资源分配和驱动初始化,用户平时几乎看不到这些步骤。

FPGA 直连 SSD 时,PC 的那套软件环境不在链路上。SSD 面前的上游设备就是 FPGA,因此配置访问和 NVMe Host 所需的逻辑,要由 FPGA 工程实现。

图 1:PC 和 FPGA 直连 SSD 的共同点是:SSD 都处于 PCIe Endpoint 一侧;差别在于由 PC 或 FPGA 承担 Host 工作。

这里有一个词需要分开说。

  • Root Complex可以理解为 PCIe 树最上游的 Host 一侧整体。它把处理器、内存和 PCIe 域接在一起,并为下游设备提供访问路径和资源管理。
  • Root Port是 Root Complex 面向下游设备的端口。它直接与 Endpoint 建链,并能够向下游发起 PCIe 访问。
  • Endpoint是被上游发现和配置的设备。本文的三星 980 1TB 对 PCIe 来说是 Endpoint;它内部包含 NVMe 控制器和 NAND Flash。

在一个 FPGA 直连单块 SSD 的工程里,没有必要照搬完整 PC 的软件体系。但从 SSD 的角度看,FPGA 仍要完成这个 Root Port 需要完成的工作:先发起配置访问,再建立可访问的 NVMe 控制器路径。

Root Port 不是 Endpoint 的另一种叫法

很多 FPGA PCIe 示例工程默认采用 Endpoint 模式。它们的目标通常是把 FPGA 做成一张 PCIe 扩展卡,让 PC 枚举 FPGA,并访问 FPGA 暴露出来的 BAR。这个方向没有问题,只是和“FPGA 直连 SSD”正好相反。

场景上游 Root Port / HostEndpoint谁先发起配置访问
PC 插 FPGA 加速卡PCFPGAPC
FPGA 直连 NVMe SSDFPGANVMe SSDFPGA

因此,不能只把 PCIe 差分线接好,再拿一个 Endpoint 示例工程去等待 SSD 工作。即使链路训练成功,两个只按 Endpoint 方式工作的设计之间,也没有一方承担枚举、资源分配和下游配置请求的责任。

这里要说得严谨一些:这不等于“两个 Endpoint 的电气链路一定无法建立”。问题在于,即便底层链路能够进入 L0,SSD 也不会因此自动变成可供 FPGA 使用的 NVMe 设备。它仍在等待上游 Host 的后续动作。

AMD 的 7 系列 PCIe IP 本身支持 Endpoint 和 Root Port 两种配置。本文使用的 XC7Z100 平台将 PCIe IP 配置为 Root Port;但 IP 角色配置只是起点,后续的配置请求、Completion 处理和 NVMe Host 逻辑仍要由工程逻辑完成。

Root Port 自己也有配置空间

Root Port 和 Endpoint 都有配置空间,但使用的配置头类型不同。AMD PG054 中明确区分了两者:Endpoint 使用 Type 0 Header,Root Port 应用使用 Type 1 Header。

图 2:AMD《PG054 v3.3》Table 25,p.44。Root Port 使用的 Type 1 PCI Configuration Space Header,非本项目实测截图。

这张表不用在本篇逐字段背下来,但可以先看出 Root Port 和普通 Endpoint 的侧重点不同。Type 1 Header 中出现了 Primary Bus Number、Secondary Bus Number、Subordinate Bus Number,以及 Memory Base、Memory Limit 等桥接和下游资源相关字段。

对本项目来说,最重要的理解是:FPGA 不只是“链路另一端”。它需要作为上游端,知道怎样访问下游 SSD,并为后续的地址分配和控制器寄存器访问做好准备。先把最小的配置读取走通,才能对这些后续工作建立可靠的判断。

SSD 作为 Endpoint,会暴露什么给 Host

NVMe SSD 并不是一块把 NAND 地址直接接到 PCIe 总线上的存储器。它首先是一个 PCIe Endpoint,具有 PCI Header、PCIe Capability、MSI/MSI-X 和 AER 等 PCIe 结构;在此基础上,才通过 BAR 暴露 NVMe 控制器寄存器。

NVM Express 的 NVMe over PCIe Transport 规范列出了这些 PCIe 寄存器结构,并说明控制器属性映射在 MLBAR/MUBAR 指定的地址范围,也就是 PCI BAR0/BAR1 对应的内存窗口。

图 3:NVM Express《NVMe over PCIe Transport Specification v1.4》Figure 3,p.9。图中列出 NVMe PCIe 控制器的 PCI Header、Capability 与 AER 结构;下方文字说明控制器属性由 BAR0/BAR1 指定的内存窗口访问。

这也解释了为什么不能跳过 PCIe 枚举,直接读 CAP 或写 CC:在访问 NVMe 控制器寄存器前,Host 先要确认 Endpoint 存在,读取配置空间,并处理 BAR 等 PCIe 层的前置条件。

Link Up 后,第一件事是谁发起

链路进入 L0 后,SSD 不会先“报告自己是 NVMe SSD”。最小的下一步是由 FPGA Root Port 发起 Configuration Read。SSD 作为 Endpoint 返回 Completion with Data,FPGA 可以先从返回数据中确认 Vendor ID,判断下游设备是否已经被正确识别。

图 4:从 PCIe Link Up 到读取配置空间标识信息的最小控制路径。图为项目调试路径示意,不是完整 PCIe 枚举状态机。

这张图有两个方向,不能混淆。

  1. FPGA 先发出 Configuration Read,请求读取 SSD 的配置空间。
  2. SSD 返回 Completion with Data,带回这次读取的结果。

因此,user_lnk_up=1的意义是“现在可以开始发起这类访问了”,而不是“Vendor ID 已经读到”。读到有效的 Vendor ID,才是枚举真正有了第一个可验证结果。

这条路径在板上需要确认三件事:请求是否从 FPGA 发出、SSD 是否返回 Completion with Data、返回的配置空间字段能否被正确解析。后续上板实测章节会用这三个观察点给出完整结果;本篇先把 Root Port 与 Endpoint 的职责和通信方向理清。

SSD 后面也会主动发数据,为什么它仍不是 Host

这个问题很容易让人困惑。后面的 NVMe 读命令执行时,SSD 会通过 PCIe Memory Write 把读取结果 DMA 到 Host 提供的内存地址;写入 SSD 时,SSD 还会通过 Memory Read 取得 Host 准备好的数据。看上去 SSD 也会主动发 TLP。

但这不改变它是 Endpoint 的角色。因为这些动作发生在 Host 已经完成控制器初始化、建立队列,并通过 PRP 提供 Host Memory 地址之后。SSD 是按照 Host 提交的 NVMe 命令执行数据传输,不是主动承担配置、枚举和资源管理的一方。

可以把两件事分开看:

阶段谁先发起SSD 的角色
PCIe 枚举FPGA Root Port返回配置访问的 Completion
NVMe 控制器初始化FPGA NVMe Host响应寄存器访问,进入 Ready
NVMe 读取数据FPGA 提交 NVM Read依据 PRP,DMA 写入 Host Memory
NVMe 写入数据FPGA 提交 NVM Write依据 PRP,读取 Host Memory 中的数据

角色说的是 PCIe 拓扑和控制责任,不是“哪一侧永远不能发起任何数据包”。

从 Root Port IP 到可用系统,工程怎样分层

“PCIe IP 已配置为 Root Port”并不等于“FPGA 已经是完整 NVMe Host”。在这个工程里,至少还有下面几层工作。

图 5:本篇重点在第 2 层 PCIe Host。第 02 篇解决物理链路,后续章节再进入 NVMe Host 与两条数据通路。

  • 物理链路层:确认供电、REFCLK、PERST#、Lane 和 LTSSM,最终进入 L0。这是第 02 篇解决的问题。
  • PCIe Host 层:Root Port 发起配置访问、读取配置空间、完成枚举并处理 BAR。这是当前和接下来几篇的重点。
  • NVMe Host 层:读取 CAP、配置 CC、等待 CSTS,建立 Admin Queue 和 I/O Queue。
  • 数据通路层:让 SSD 能依据 PRP 访问 PS DDR 或 PL DDR,并完成 DMA、缓冲和校验。

这四层按顺序依赖。前一层没有证据,后面的调试就容易变成猜测。例如,如果配置空间还读不到,就不应先检查 CC.CSTS;如果队列还没有建立,也不能要求 SSD 把用户数据 DMA 到 DDR。

三个容易走偏的判断

1. “Link Up 了,说明 NVMe 驱动有问题”

不一定。Link Up 只说明 PCIe 物理链路可用。下一步应该观察 Configuration Request 有没有发出、Completion 有没有回来、返回数据是否合理。NVMe 初始化还在更后面。

2. “用 Endpoint 示例工程,SSD 应该会自动被识别”

不会。Endpoint 示例适合让 PC 枚举 FPGA;而 FPGA 直连 SSD 时,FPGA 需要改为 Root Port 和 Host 侧角色。两种场景的上游、下游方向不同。

3. “SSD 会 DMA,所以 SSD 也算 Host”

不算。SSD 的 DMA 是在 Host 提交 NVMe 命令并给出 PRP 地址后执行的。枚举、BAR、控制器初始化和队列建立仍由 FPGA Host 发起。

本篇自查清单

检查项需要看到的结果还不能据此断定什么
PCIe IP 角色Root Port 模式不代表已经完成枚举
物理链路LTSSM=L0,user_lnk_up=1不代表配置空间可读
配置访问FPGA 发起请求,收到 Completion不代表 BAR 已配置完成

小结

FPGA 直连 NVMe SSD 时,SSD 是 PCIe Endpoint,FPGA 需要位于 Root Port 和 Host 一侧。链路进入 L0 后,第一件需要做的事不是提交 NVMe 命令,而是由 FPGA 发起 Configuration Request,读取 SSD 配置空间并确认设备存在。具体的板上读取结果放在后续实测章节展示。

Root Port 模式解决的是“谁来面对下游 SSD”;从这里到稳定的 NVMe 读写系统,还会经历枚举、BAR、控制器初始化、队列和数据通路等层次。下一篇先看链路进入 L0 后真正在线上运行的内容:TLP、DLLP 和 Completion 分别是什么,Configuration Read、Memory Read 与 Memory Write 各自承担什么角色。随后几篇会从配置空间访问开始,把请求发送、返回接收和结果解析的工程过程逐步讲清楚。

参考资料

  1. AMD,PG054 v3.3:7 Series FPGAs Integrated Block for PCI Express,本文图 2 引用 Table 25,p.44;文中 Root Port / Endpoint 配置能力参见 p.5。
  2. NVM Express,NVMe over PCIe Transport Specification v1.4,本文图 3 引用 Figure 3,p.9;BAR0/BAR1 映射说明位于同页。
  3. NVM Express,Base Specification v2.4,用于 Host、Controller 和 PCIe Memory-Based Transport 术语核对。
  4. PCI-SIG Specifications,PCIe 术语与规范入口。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询