nv_hsb_ip 中的 RoCEv2 包结构与链路类型分析
2026/9/24 18:09:16 网站建设 项目流程

基于fpga/nv_hsb_ip实际 RTL 代码分析:

  • dp_pkt/dp_pkt_top.sv(数据面包头构造)
  • roce/roce_ack.sv(RC 响应端,可选)
  • roce/roce_icrc.sv(ICRC 计算)
  • rx_parser/rx_parser_pkg.sv/rx_parser/rx_parser.sv(头部解析结构体)

一、RoCEv2 包结构

1. 整体帧格式(传感器 → 主机数据面,dp_pkt_top.svhdr_roce构造)

代码位置:dp_pkt/dp_pkt_top.svL1148-1163,ROCE_HDR_WIDTH = 224 + 336 = 560 bit = 70 Byte

0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 ┌───────────────────────────────────────────────────────────────┐ ─┐ │ Destination MAC (hif_mac_addr, 6B) │ │ ├──────────────────────────────┬────────────────────────────────┤ │ │ Source MAC (前4B) │ Source MAC (后2B) │ │ Ethernet ├──────────────────────────────┴────────────────────────────────┤ │ 14B │ EtherType = 0x0800 (IPv4) │ Ver=4/IHL=5 │ DSCP/ECN│ │ ├───────────────────────────────────────┴───────────┬───────────┤ ─┘ │ IP Total Len = dma_len+60(+64带Imm) │ ID=0 │Flg=DF/Frag│ ─┐ ├───────────┬───────────┬───────────────────────────┴───────────┤ │ │ TTL=0x40 │ Proto=0x11│ IP Header Checksum (RTL实时计算) │ │ IPv4 ├───────────────────────────────┬───────────────────────────────┤ │ 20B │ Source IP Address │ Destination IP Address │ │ ├───────────────────────────────┼───────────────────────────────┤ ─┘ │ UDP Src Port (寄存器可配) │ UDP Dst Port = 0x12B7 (4791) │ ─┐ UDP ├───────────────────────────────┼───────────────────────────────┤ │ 8B │ UDP Length = dma_len+40(+44) │ UDP Checksum = 0 (不用) │ ─┘ ├───────┬───────┬───────────────┴───────┬───────────────────────┤ ─┐ │Opcode │SE/M/P/│ P_Key = 0xFFFF │ Rsvd │ Dest QP(24b) │ │ │0x2A/2B│ TVer │ │ │ (高16b) │ │ BTH ├───────┴───┬───┴───────────┬───────────┴───────┬───────────────┤ │ 12B │ DestQP低8b│ AckReq=0/Rsvd │ PSN (24b, 每QP递增计数器) │ │ ├───────────┴───────────────┴───────────────────┴───────────────┤ ─┘ │ RETH: Virtual Address (64b, GPU显存地址) │ ─┐ ├───────────────────────────────────────┬───────────────────────┤ │ RETH │ RETH: R_Key (32b) │ DMA Length = {16'h0, │ │ 16B │ │ dma_len(16b)} │ │ ├───────────────────────────────────────┴───────────────────────┤ ─┘ │ [ImmDt 4B] 仅 opcode=0x2B: {PSN[23:0], 6'h0, buf_ptr[1:0]} │ ─┐ 可选 ├───────────────────────────────────────────────────────────────┤ │ │ [Metadata 48B] 带Imm包: md_flags/PSN/frame_crc/SOF_PTP/ │ │ (记入 │ buf_addr/frame_num/当前PTP时间戳 │ │ dma_len) ├───────────────────────────────────────────────────────────────┤ │ │ │ │ │ Sensor Payload (图像/传感器数据, ≤MTU) │ │ │ │ │ ├───────────────────────────────────────────────────────────────┤ ─┘ │ ICRC (4B, Invariant CRC-32, roce_icrc.sv 计算) │ └───────────────────────────────────────────────────────────────┘ (最后由 MAC 再追加以太网 FCS 4B)

2. 关键字段说明

字段值/来源说明
EtherType16'h0800IPv4
IP Protocol8'h11(17)UDP
UDP Dst Port0x12B7=4791RoCEv2 标准端口(roce_ack.svL493 注释确认)
UDP Checksum16'h0RoCEv2 允许置 0
Opcode8'h2A/8'h2B见第二节
SE/MigReq/PadCnt/TVer全 0无请求应答事件、无迁移、无填充
P_Key16'hFFFF默认分区
Dest QProce_dest_qp,寄存器配置每路 sensor 一个 QP
AckReq0不要求对端回 ACK
PSN每 QP 24bit 计数器(dp_pkt_top.svL408/418),普通包每包 +1同时被 host 软件用于丢包检测
RETH VA{roce_buf_fixed_msb, vaddr<<7}GPU 显存目标地址,环形 buffer 翻页
RETH R_Key寄存器配置host 注册内存时下发
DMA Length{16'h0, dma_len}含 metadata + payload
ICRCroce_icrc.sv:按 RoCE 规范把 TOS/TTL/IP校验和/UDP校验和/BTH F/B 位掩码为全 1 后做 CRC-32Invariant CRC

带 Immediate 的包hdr_type=2'b10,帧尾/元数据上报用)在 RETH 后依次插入:

  • ImmDt 4B:{psn[19:0], buf_ptr[11:0]}BUFFER_4K_REG模式)——把"数据写到哪个 buffer"随包通知 host
  • Metadata 48B(384bit,dp_pkt_top.svL1303):
wr_imm_data = {md_flags, 8'h0, psn, frame_crc, 16'h0, sof_ptp, roce_val_addr, 16'h0, frame_num, 16'h0, cur_ptp};

即帧 CRC、SOF/当前 PTP 时间戳、帧号、buffer 地址等。

BTH 结构体定义rx_parser/rx_parser_pkg.svL67-75):

typedef struct { logic [7:0] opcode; // 操作码 logic [7:0] s_m_pad_tver; // SE | MigReq | PadCnt | TVer logic [15:0] pkey; // P_Key logic [7:0] f_b_rsv6; // F/B/Rsvd6 logic [23:0] dest_qp; // Destination QP logic [7:0] a_rsv7; // AckReq | Rsvd7 logic [23:0] psn; // Packet Sequence Number } bth_hdr;

二、链路类型:不是 RC,数据面是 UC(Unreliable Connection)

判定依据

IB/RoCE opcode 编码规则:opcode[7:4] 区分传输类型,0x0=RC,0x2=UC

dp_pkt_top.svL1146:

assign opcode = (pkt_is_imm || is_data_wr_imm) ? 8'h2B : 8'h2A;

对照 IB 规范 opcode 表:

Opcode含义传输类型
0x0A / 0x0BRDMA Write Only / Write Only w/ ImmRC(未采用)
0x2A / 0x2BRDMA Write Only / Write Only w/ ImmUC(本设计采用)

代码自身定义印证(roce_ack.svL92-93):

localparam BTH_UC = 4'h2; // opcode[7:4] = 2 → UC localparam BTH_RC = 4'h0; // opcode[7:4] = 0 → RC

为什么是 UC 而不是 RC

  1. 单向高吞吐图像流:FPGA 只做 RDMA Write 把传感器数据直接写进 GPU 显存环形 buffer,不需要对端回 ACK;
  2. BTH 中AckReq=0、Solicited Event=0,FPGA 侧没有任何等待/处理 ACK 的逻辑;
  3. 可靠性由上层保证:PSN + ImmDt 中的 buf_ptr + Metadata(frame_crc、帧号、PTP 时间戳)随包带给 NVIDIA host 软件,由软件检测丢包/乱序,而不是 IB 硬件重传;
  4. 无需建立 QP 连接状态机(无 CM/handshake),FPGA 上电配好寄存器(dest_qp、rkey、buffer 地址)即可发包。

补充:RX 方向有可选的 RC 响应端

下行(host → sensor 的寄存器写)在定义了`ifdef SIF_TX_ROCE_RC_ENABLEHOLOLINK_top.svL377)时,FPGA 通过roce/roce_ack.sv扮演RC Responder

Host (RC Requester) FPGA (RC Responder) │ RDMA Write/Send (opcode[7:4]=0x0) │ │──────────────────────────────────▶│ roce_ack.sv: 每QP跟踪 ePSN (L307) │ │ 乱序→丢弃/NAK, 重复→丢弃 │ ACK: opcode=0x11 + AETH(4B) │ │◀──────────────────────────────────│ AETH = {syndrome, MSN} │ ETH+IP+UDP+BTH(12B)+AETH+ICRC │ syndrome: 0x00=ACK(credit disable) │ │ 0x20+=RNR NAK / 0x60+=NAK seq err

即:roce_ack.sv只对RC 入向包opcode[7:4]==BTH_RC,L234/358/367)生成 ACKNOWLEDGE 响应包(ETH+IPv4+UDP+BTH+AETH+ICRC,L482-500),支持 PSN 检查、RNR NAK、错误注入测试。


三、总结

方向模块包格式传输类型
Sensor→Host(图像数据上行)dp_pkt/dp_pkt_top.svETH+IPv4+UDP(4791)+BTH+RETH+[ImmDt+Metadata]+Payload+ICRCUC RDMA Write Only (0x2A/0x2B)
Host→Sensor(控制下行,可选)roce/roce_ack.sv应答包 ETH+IPv4+UDP+BTH(opcode 0x11)+AETH+ICRCRC(仅作 responder 回 ACK/NAK/RNR)

核心结论:nv_hsb_ip 数据面的 RoCEv2 是 UC(Unreliable Connection)类型的 RDMA Write(带可选 Immediate),不是 RC;RC 只作为可选编译项(SIF_TX_ROCE_RC_ENABLE)用于下行的 ACK 响应。

这符合 Holoscan Sensor Bridge 的设计哲学——传感器数据流用无连接、无重传的 UC 达到最低时延,可靠性检测上移到 host 软件。

2. LA 数据读取

是否需要 GUI Vivado?

结论先行:不需要

  1. pynq/rfsoc-pynq工程中的 “ILA” 是 NVIDIA 自研的s_apb_ila(APB 寄存器 + 片上捕获 RAM),不是 Xilinx ILA IP core,运行阶段用 Python 经以太网控制面读取即可,完全不需要 Vivado(GUI/batch 都不需要)
  2. 即便是一般项目中真正的 Xilinx ILA core,也不必须 GUI——可用免费的 Vivado Lab Edition +hw_server+ Tcl 脚本在纯命令行/批处理模式经 JTAG 抓取数据(见本文第四节)。

一、本工程里的 “ILA” 是自研 APB 模块

pynq/rfsoc-pynq/rtl/top/FPGA_top.sv中例化的两个 ILA 均为nv_hsb_ip/lib_apb/s_apb_ila.sv(APB 从机 + 片上 RAM,非 XilinxilaIP):

实例挂载数据宽度/深度捕获内容触发/写使能
u_apb_ilaapb_psel[2]256bit × 512PTP 时间戳、frame_cntsof/eof、标记0x123456789ABCDEF、计数器trigger=sof
u_apb_sif_ilaapb_psel[3]585bit × 512整拍 AXIS:tdata[511:0]+tvalid+tlast+tkeep 计数+PTP sec/nsecwr_en=sif_tx_axis_tvalid[0]

工作机制(s_apb_ila.svFSM:ILA_IDLE → ILA_CAPTURE → ILA_DONE):

  1. ctrl_reg[0].bit0 = 1(trigger_en)启动捕获;
  2. 等待i_trigger,随后每个i_wr_en采样一个数据写入 RAM(s_apb_ram_dyn);
  3. 采满DEPTH=512个样本后置fsm_done=1
  4. 通过 APB 读 RAM 窗口取出数据。

二、访问通道:以太网控制面,不经过 PS/JTAG

  • 该设计为PL-onlybuild/ip/bd_gen.tcl中只有 CMAC + clk_wiz,寄存器通路不含 Zynq PS;
  • ILA 挂在HOLOLINK_topUser Reg APB 接口o_apb_psel/paddr/...)上,其 APB 主机来自内部u_apb_intc(APB 互连),master 是ECB(以太网控制桥)——host 通过UDP 寄存器读写包(Holoscan Sensor Bridge 标准控制协议)访问;
  • 因此在 PYNQ(或任何能连通板卡的 PC)上用Python(NVIDIA hololink 库 / socket 发 UDP 控制包)即可 dump ILA 数据;PYNQ 框架本身只负责加载 bitstream。

三、s_apb_ila寄存器/存储器映射与读出流程

基址 = 该 psel 实例的 User Reg 地址(由 Hololink 枚举/寄存器映射决定),模块内部偏移:

偏移名称含义
0x00ctrl_reg[0]bit0 = trigger_en,bit1 = ila_rst(清 RAM)
0x04ctrl_reg[1]通用输出o_ctrl_reg
0x08stat_reg[0]bit0 = fsm_busy,bit1 = fsm_done,bit2 = i_trigger 状态
0x0Cstat_reg[1]当前 sample_addr
1 << W_OFSETRAM 窗口见下

RAM 窗口基址W_OFSET = clog2(DEPTH) + clog2(ceil(W_DATA/32)) + 2

  • ILA#1u_apb_ila,256bit):W_OFSET = 9+3+2 = 14→ RAM 在偏移0x4000,512 样本 × 8 个 32bit 字,样本ik字地址 =0x4000 + (i*8+k)*4
  • ILA#2u_apb_sif_ila,585bit):W_OFSET = 9+5+2 = 16→ RAM 在偏移0x10000,512 样本 × 19 个有效 32bit 字(步长 32 字)。

读出流程:

写 [base+0x00] = 0x1 # trigger_en = 1 轮询 [base+0x08] 直到 bit1=1 # 等待 fsm_done 读 RAM 窗口 512 个样本 写 [base+0x00] = 0x0 # 回 IDLE(或 bit1=1 清 RAM 重采) Python 解析/绘图

四、通用情况:真正的 Xilinx ILA,无 GUI 的 JTAG/Tcl 读法

即便设计中例化了 Xilinx ILA core,也不需要 Vivado GUI。可用Vivado Lab Edition(免费,无需 license)的硬件服务器 + Tcl 全流程命令行完成:

1. 启动 hw_server(板卡 JTAG 所连机器上)

hw_server&# 默认监听 3121;也可由 Tcl 内 connect_hw_server -url localhost:3121 自动拉起

2. Tcl 抓取脚本read_ila.tcl

# ---- 连接 ---- open_hw_manager connect_hw_server -url localhost:3121 open_hw_target ;# 或: open_hw_target -jtag_mode ... # 选择器件(按实际 JTAG 链上的器件名,如 xczu48dr_0 / xcku060_0) set dev [lindex [get_hw_devices] 0] current_hw_device $dev refresh_hw_device $dev # ---- 关联探针文件 (.ltx,由 write_debug_probes 生成;若已随 bitstream 关联可省略) ---- set_property PROBES.FILE {design.ltx} $dev set_property FULL_PROBES.FILE {design.ltx} $dev # 若需先下载: program_hw_devices $dev # ---- 配置 ILA ---- set ila [lindex [get_hw_ilas -of_objects $dev] 0] set_property CONTROL.TRIGGER_POSITION 256 $ila ;# 触发点在窗口中的位置 set_property CONTROL.WINDOW_COUNT 1 $ila set_property CONTROL.DATA_DEPTH 512 $ila # 触发条件示例:probe "sof" == 1 set_property TRIGGER_COMPARE_VALUE eq1'b1 [get_hw_probes sof -of_objects $ila] # ---- 布防并等待触发 ---- run_hw_ila $ila wait_on_hw_ila $ila ;# 阻塞直到触发完成;可加 -timeout # ---- 上传 & 导出 ---- set data [upload_hw_ila_data $ila] current_hw_ila_data $data write_hw_ila_data -csv_file ila_capture.csv $data # 也可直接读属性: report_property $data # ---- 收尾 ---- close_hw_target disconnect_hw_server close_hw_manager

3. 批处理运行

vivado-modebatch-sourceread_ila.tcl# 或用 Lab Edition:# vivado_lab -mode batch -source read_ila.tcl

要点:

  • GUI 从来不是必须的;GUI(Hardware Manager)只是交互最方便的方式;
  • 无 GUI 时必需的两样:JTAG 连接(本地或远程hw_server)+ 免费的 Vivado Lab Edition 运行时;
  • .ltx探针文件在实现阶段用write_debug_probes生成,离线分析(Python/pandas 读 CSV)不依赖任何 Vivado;
  • 本仓库的替代方案(s_apb_ila走以太网寄存器面)进一步连 JTAG 和 Lab Edition 都省掉了,更适合 PYNQ 这类无头(headless)远程调试场景。

五、总结

场景是否需要 GUI Vivado需要什么
本 pynq 工程(s_apb_ila否(连 Vivado 都不需要)Python + 以太网(Hololink 寄存器协议)
一般 Xilinx ILA,交互调试否(GUI 可选)Vivado Lab Edition + JTAG + Tcl 批处理
一般 Xilinx ILA,完全脚本化vivado_lab -mode batch -source xxx.tcl+ CSV 离线分析

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

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

立即咨询