第十三章:FPGA 第一次读取 NVMe 寄存器:从 BAR 地址到 CAP 和 VS
本篇位置FPGA NVMe Host 实战连载 · 第二阶段 · 第 13 篇第 12 章已经为三星 980 1TB 建立了 BAR 地址窗口基地址为0x80000000大小为 16 KiB配置回读和 Command 使能符合预期。现在要验证这个窗口是否真正可用。最直接的办法是发出 PCIe Memory Read读取 NVMe 控制器的只读寄存器 CAP 和 VS。前者描述控制器能力后者报告设备支持的 NVMe 规范版本。本章控制路径为PL Memory Read → PCIe Root Port → SSD BAR/MMIO → Completion with Data → PL 状态与 Tag 校验 → CAP/VS 解析项目已有这一过程的实板结果。本章使用 XC7Z100、PCIe Gen2 x4、直连三星 980 1TB 在 2026-07-31 保存的 ILA 导出记录结合工程中的请求组织方式解释这些结果。它是控制器寄存器访问不涉及 SSD 用户数据区域的读取或写入。1. BAR 配置成功后还要验证一次 Memory 事务BAR 回读一致说明配置空间中的地址字段已经设置成功。它不能单独证明 Memory Read 的包头、地址、发送握手和返回解析都正确。因此这一阶段先选择只读属性CAP 位于 Offset00hVS 位于 Offset08h。读取它们不会启用控制器也不会提交 NVMe 命令适合检查从地址计算到返回数据的完整路径。本次还读取 CC 和 CSTS作为当时控制器状态的记录。它们用于观察不在本章写入修改。图 1NVM Express《NVM Express Base Specification Revision 2.4》§3.1.4、Figure 33PDF p.78文档页码 52。表中定义 CAP、VS、CC、CSTS 等属性的 Offset 和宽度。根据本项目的 MMIO 基地址实际访问位置如下属性相对 Offset属性宽度当前 Memory 地址CAP00h64 位80000000hVS08h32 位80000008hCC14h32 位80000014hCSTS1Ch32 位8000001Ch注意使用屏蔽属性后的基地址0x80000000。BAR0 回读值0x80000004包含只读属性位不能直接当作窗口起点否则 CAP 会错读到高 32 位的位置其他寄存器也会整体偏移。2. CAP 是 64 位能不能分两次读取项目现有寄存器读取引擎一次处理一个 DWORD因此它把 CAP 分成对齐的两笔 32 位 Memory Read。这个做法有 PCIe 传输绑定的明确依据。NVMe Base Specification 给出属性宽度和通用访问要求NVMe over PCIe Transport Specification 则进一步规定 PCIe 寄存器的访问方法。其中 §3.1.2 明确要求 Host 使用寄存器的原生宽度或者对齐的 32 位访问。图 2NVM Express《NVMe over PCIe Transport Specification Revision 1.4》§3.1.2PDF p.9。原文允许 native width 或 aligned 32-bit accesses并要求有序访问、禁止 locked access 和跨越多个寄存器的访问。所以对本章的 CAP 可以读取base 00hCAP 的低 32 位。base 04hCAP 的高 32 位。两个地址都按 4 字节对齐且各自访问只落在 CAP 内部。不能把这条规则扩展成任意字节、任意宽度或跨属性读取都可接受。项目先读低半部分收到成功返回后再读高半部分分别保存两笔结果最后组合CAP (高 32 位 32) | 低 32 位CAP 是控制器能力属性适合在当前稳定初始化条件下按这种方式读取。对于会随设备运行变化的其他 64 位寄存器分两笔访问是否能够获得一致结果还要看具体寄存器规则两笔 32 位访问本身不保证 64 位原子快照。如果引擎支持原生 64 位读取也可以按规范组织一笔 2-DWORD MRd。但返回接收器、Length、Byte Enable、Payload 拼接和末拍有效字节都必须相应支持不能只把 Length 从 1 改成 2。3. 从 VS 入手看清一笔 Memory ReadVS 只有 32 位适合用来解释最小请求。它的请求地址计算为address 00000000_80000000h 0008h 00000000_80000008h这里的08h是 NVMe MMIO 属性偏移。它和配置空间 Offset08h不是同一个位置前者访问 VS后者访问 Class/Revision DWORD。必须先看请求属于 Memory 还是 Configuration 事务再解释偏移的含义。项目采用 64 位请求地址因此使用 4-DW Memory Read Header字段本笔 VS 读取的含义Fmt001b4-DW Header无 PayloadType00000bMemory RequestByte 020h以上 Fmt 与 Type 的拼接Length1 DW请求读取 4 字节First DW BE1111b本 DWORD 的四个字节都有效Last DW BE0000b单 DWORD 请求不使用尾 DWORD BERequester ID请求方身份由当前 Root Port 的配置身份取得Tag本次请求的关联编号等待期间保持不变Address00000000_80000008h图 3PCI-SIG《PCI Express Base Specification Revision 7.0》Figure 2-36PDF p.209。Non-Flit Mode 的 4-DW Memory Request Header 在 DW2/DW3 中放入高、低地址。请求并没有写入 PayloadLength1 描述的是要读取多少数据。对于本项目的请求格式DW2 是地址高 32 位00000000hDW3 的地址部分对应80000008h低两位按当前普通、无 TPH 请求的设置为零。4-DW Header 不表示 FPGA 或 AXI 接口必须是 128 位宽。地址宽度决定包头格式接口宽度决定分几拍传输它们是两个不同维度。4. 请求与返回在64位AXI接口上怎样排列4-DW MRd 没有 PayloadHeader 总计 16 字节。64 位 AXI4-Stream 每拍传输 8 字节因此发送需要两个完整拍。发送拍tdata[63:32]tdata[31:0]tkeeptlast1DW1DW0FFh02DW3DW2FFh1每拍都只在tvalid tready同时成立时交付。IP 拉低 tready 时应保持当前数据、tkeep 和 tlast等待握手后再推进。接收方向的成功 1-DWORD CplD 则由 3-DW Completion Header 和 1-DW Payload 组成同样占两个完整拍。第二拍的低 32 位为 DW2高 32 位为 Payload。图 4项目请求与接收路径示意。按当前 VS 地址、64 位 PG054 接口和最终读取值解释 MRd、CplD 和数据解析它不是板上逐拍收发波形截图。第二个发送拍完成只能说明 Root Port IP 已接收完整请求。SSD 是否处理、返回是否有效仍要由 RX 接收和校验确认。5. CplD 的校验不能停在4Ah接收首拍 Byte 0 为4Ah表示 Fmt010b、Type01010b即 CplD。但任何一个 CplD 都不一定属于本次 VS 读取。图 5PCI-SIG《PCI Express Base Specification Revision 7.0》Figure 2-79PDF p.243。Completion Header 中包含 Status、Byte Count、Requester ID、Tag 和 Lower Address。对成功读取一个完整 DWORD 的 VS 请求至少应核对检查项本例预期判断目的包边界从首拍识别到正确末拍结束避免把无关包的数据拍当作 Header类型CplD确认返回有 PayloadCompletion StatusSC000b对端报告成功Requester ID、Tag与已保存请求一致确认是当前这笔读取Length1 DW当前 Payload 长度与预期相符Byte Count4本次完整 DWORD 读取的字节数Lower Address08h首个返回字节地址低 7 位与本例相符末拍tkeepFFh、tlast1DW2 与 Payload 都完整到达Lower Address 不要照搬 Vendor ID Configuration Read 中的取值。对本章的普通 Memory Read它反映首个返回字节地址的低 7 位。VS 地址为0x80000008四个字节全使能所以该字段应为08hCAP 低、高 DWORD 对应00h和04h。如果返回 UR、CA 等非成功状态不能把 Payload 或接收寄存器残留值交给上层作为有效结果。数据被标记 Poisoned 或接口报告错误时也需要按系统错误策略处理。现有小读取引擎的边界当前工程已经检查返回类型、Completion Status、Requester ID、Tag以及第二拍 tlast/tkeep。它没有完整检查上表全部字段也没有通用的无关包跳过、分段 Completion 和多请求表管理。因此已有板测证明的是当前受控小寄存器读取路径。完善接收器时应补齐 Header 与实际包长校验、包边界跟踪和错误处理不能把一个“只接两拍”的实现直接扩展为通用 DMA 接收引擎。6. VS 的00010400h怎样从Payload恢复出来AMD PG054 对 64 位接口的字节排列有明确要求首个 TLP 字节位于首拍tdata[31:24]。不能把接收总线上的 32 位半区直接解释为已经整理好的寄存器数值。图 6AMD《PG054 v3.37 Series FPGAs Integrated Block for PCI Express》Figure 3PDF p.46。Header 和 Payload 的接口位置需要按官方 Byte Order 核对。VS 的常规寄存器值为00010400h时按 PCIe 数据字节地址顺序四个 Payload 字节为00 04 01 00。在本项目的 CplD 第二拍高 32 位中它们依次出现在[63:56]、[55:48]、[47:40]、[39:32]。因此交付给上层的 32 位值按下面顺序组合read_data {rx[39:32], rx[47:40], rx[55:48], rx[63:56]}这是当前 PG054 接口的字节重组不是让整个 TLP Header 都按同一公式翻转。Header 字段应按图 3、图 5 的定义和接口位置解释。实际使用结果时还要同时检查 done 和 success。当前引擎可能在一条失败返回中更新数据寄存器单独看到 read_data 变化不能说明读取成功。7. CAP两笔读取怎样组织与拼接项目现有读取序列如下。这里只列地址和关联关系便于对照仿真、ILA 或日志。顺序读取目标请求地址当前项目Tag1CAP 低 32 位80000000h30h2CAP 高 32 位80000004h31h3VS80000008h32h4CC80000014h33h5CSTS8000001Ch34h这些 Tag 是项目选择的请求编号不是规范规定的寄存器编号。它们必须和当前未完成请求管理一致尤其要避免超时后立即复用 Tag使迟到返回混入新事务。现有序列一次只发起一笔读取上一笔成功后才进入下一笔。任何一步失败就记录错误步骤和返回状态不再继续读取后续属性。实测完整 CAP 为CAP 00000030_3C033FFFh对应低半部分3C033FFFh、高半部分00000030h组合顺序是高在前、低在后。原始最终结果记录保存的是完整 CAP这两个半值由该值分解并与工程的锁存关系核对不能把它们当作已经保存了两笔 CplD 的逐拍证据。本章的每笔读取只有 1 DWORD因此返回可以按最小 CplD 处理。如果以后将 CAP 合并为 2-DWORD 读取成功返回总长会变成 12 字节 Header 加 8 字节 Payload在 64 位接口上需要三拍它不再符合当前“两拍结束”的接收假设。8. VS为什么是NVMe 1.4.0而不是我们引用的2.4项目读取到VS 00010400hVS 的主、次、第三版本号分别位于 bits 31:16、15:8、7:0。按字段拆解为1、4、0即设备报告 NVMe 1.4.0。图 7NVM Express《NVM Express Base Specification Revision 2.4》§3.1.4.2、Figure 37PDF p.84文档页码 58。VS 是只读属性MJR、MNR、TER 分别表示三个版本字段。文章引用较新的官方文档作为定义依据不会让设备的版本变成 2.4。实际控制器能力必须以 VS、CAP 和后续 Identify 信息为准。较新规范中增加的属性或功能不应在旧版本设备上默认存在。VS 表示 NVMe 规范版本不是 SSD 固件版本。型号、序列号和固件版本需要在管理队列建立后通过 Identify Controller 管理命令获取SSD 会将这些信息返回到 Host 指定的数据缓冲区。后续管理命令章节会展开这个过程。9. 实板记录中CAP、VS、CC和CSTS是什么已有 ILA CSV 保存了设备身份、BAR 回值、窗口大小、CAP、VS/CC、CSTS 和读取状态。核对后全部 4096 个采样中的七组结果字段保持一致。图 82026-07-31 三星 980 1TB 的实板 ILA CSV 汇总。CAP、VS、CC、CSTS 等值来自保存的采样图由数据生成展示最终结果不代替 TX/CplD 逐拍波形。几项结果要连起来理解CAP 不再是配置空间里的设备身份而是 NVMe 控制器能力。VS00010400h设备报告 NVMe 1.4.0。CC00000000hEN0控制器尚未启用。CSTS00000000hRDY0与当前未启用状态一致。CSTS.RDY0 不等于本次PCIe读取失败。本次 Memory Read 已经通过匹配的返回取得 CSTS只是读到的控制器状态为尚未 Ready。链路、寄存器访问和 NVMe 控制器工作状态必须分别记录。同样PCIe Completion with Data 和 NVMe Completion Queue Entry 是不同层的返回。前者结束一笔 PCIe 读取事务后者报告 NVMe 命令执行结果只有进入队列机制后才会涉及。10. CAP先看哪些字段下一章再逐项展开本章先确认值读得可靠再用几个字段检查解析方向是否合理。字段本次CAP解析值当前能说明什么MQES3FFFh采用零基编码I/O 单队列最大项数为 16384不是要求分配这么大的队列CQR1I/O SQ/CQ 要求物理连续Admin Queue 的连续性要求另按其规则处理TO3Ch对本设备的该能力字段单位为 500 ms对应 30 秒的 Ready 转换超时依据DSTRD0Doorbell 间距为4 0即 4 字节MPSMIN/MPSMAX0/0支持的控制器内存页大小范围为 4 KiB这些都是设备能力信息不是本次 PCIe 读取等待了多久。TO 描述控制器状态转换等待条件当前 MRd 的 Completion 超时由请求引擎另行处理两者不能用同一个“超时”字段混写。MQES 也不是 Admin Queue 大小的直接上限更不是 SSD 可以存储多少个用户数据块。CAP 各字段的适用范围、零基编码、单位和版本差异下一章会结合官方位定义详细展开。11. 仿真和ILA应怎样安排检查先验证地址和数据解析仿真中可以使用已知模式作为返回数据先确认它被正确重组然后分别注入 Tag 不匹配、UR/CA、无返回和错误末拍观察结果是否被拒绝或记录失败。对 CAP 分次读取要分别验证低、高半部分都成功后才形成有效完整值。高半部分失败时即使低半部分已经更新也不能把半个 CAP 交给初始化逻辑。如果需要可反压接收再验证 tready 拉低时的状态和缓存行为。当前项目 RX 一直允许接收不能把这一结果当作已验证任意反压场景。上板时按事务观察检查点应观察什么启动条件Link Up、BAR 配置成功、基地址有效才启动 MMIO 读取MRd 首拍Fmt/Type、Length、Requester ID、TagMRd 第二拍地址高低位、tkeepFF、tlast1发送握手两拍均完成 valid/ready等待时字段保持CplD 首拍返回类型、SC 和 Byte CountCplD 第二拍Requester ID、Tag、Lower Address、Payload、tkeep/tlast属性结果CAP 两半、VS、CC、CSTS以及有效/成功状态错误路径失败属性、超时与对端错误分别记录如果只想记录最终属性稳定结果触发即可。若要定位首次读取为什么超时应在 MRd 发出或错误事件触发时保存前后采样而不是只看最后一屏全零寄存器。12. 常见异常从哪里查Vendor ID正常VS却没有返回。检查 MSE 和 BAR 地址再看是否确实生成 MRd。配置空间可访问不代表 Memory 路由和接收处理也正确。读取地址用了80000004h。检查是否把 BAR0 属性位带进了 base。当前合法 MMIO 基地址是80000000hCAP 高 DWORD 才位于base04h。VS读成00040100h。先查 Payload 字节重组。该模式很可能是把接口上的字节排列直接当成寄存器值但仍需结合状态、Tag 和实际包判断不能仅凭数值猜原因。CAP低32位正确高32位为零。先查第二笔地址和 Tag 是否正确、高半部分是否真的成功不能直接认定控制器能力的高字段全不支持。CplD到了却仍然超时。查 Requester ID/Tag、包起止边界和接收状态机。无关包或迟到返回也可能出现在 RX 上“收到一个 CplD”与“收到当前请求的 CplD”不同。CC和CSTS为零。对当前未启用控制器它们可以是有效读数。需要看读事务的 success而不是把零值一律当成访问失败。CAP很合理但后续初始化失败。需要进一步按实际 VS 和 CAP 检查队列、页大小、初始化顺序和 Ready 超时不能用寄存器读取成功替代初始化验证。13. 下一步从能力读取进入参数选择本章把“已配置 BAR”推进到“已通过 Memory Read 读取 NVMe 控制器属性”。这一步在 PL 内可以完成仍不需要为读取少量寄存器引入 PS 或外部 DDR。两条后续数据路线都可以复用这项控制能力SSD 与 PS 内存交互时由软件与 PL 分工管理初始化和校验用户数据流与 PL DDR、SSD 交互时则需要在硬件控制面安排相应参数和状态处理。两条路线都应尊重同一块 SSD 返回的能力限制。第 08 章配套工程下载仍为FPGA 读取 NVMe SSD Vendor ID 实战工程。其内容和验证目标以第 08 章为准本章使用的是项目进一步的寄存器读取记录。下一章重点解读 CAP队列最大深度、连续内存要求、Doorbell 间距、控制器页大小和超时条件怎样影响 Host 参数哪些值可以配置哪些值必须服从控制器能力。参考资料NVM ExpressNVM Express Base Specification Revision 2.4§3.1.4、Figure 33PDF p.78 起控制器属性 Offset 和宽度Figure 36PDF p.8184CAP§3.1.4.2、Figure 37PDF p.84VS 字段。NVM ExpressNVMe over PCIe Transport Specification Revision 1.4§3.1.2PDF p.9寄存器映射、原生宽度/对齐 32 位访问、有序访问及禁止 locked access 的要求。PCI-SIGPCI Express Base Specification Revision 7.0Table 2-2/2-3PDF p.158159、§2.2.5PDF p.194 起、Figure 2-36PDF p.209MRd 编码、Byte Enable 和 64 位 Memory HeaderFigure 2-79PDF p.243、§2.3.1.1PDF p.263 起Completion 字段和返回规则。AMDPG054 v3.37 Series FPGAs Integrated Block for PCI ExpressTLP Format on the AXI4-Stream Interface、Figure 3PDF p.4546及 Basic TLP Transmit/Receive Operation64 位接口字节顺序和握手依据。