fpga-latency 指标分析与触发机制

1. fpga-latency 指标分析

run_latency.shrun_ramplayer_analyzer.sh 输出的 fpga-latency 的含义与完整变量逻辑链条。

1. 定义

fpga-latency纯 FPGA 内部的一段延迟,由主机侧分析器用同一帧 metadata 中的两个 FPGA 时间戳相减得到:

复制代码
fpga-latency = metadata_s/ns  −  timestamp_s/ns
             = cur_ptp@hdr_trigger − sof_ptp@i_sof
             = 帧尾 write-with-imm 发出时刻 − 帧首数据进入打包器时刻

两个时间戳来自 FPGA 内同一个自由运行的 PTP 计数器(同一时钟域),主机只计算差值,因此无需与主机对时,测量是精确的。

计算位置:tools/roce_ramplayer_analyzer/roce_ramplayer_analyzer.cpp:778-788

cpp 复制代码
const double ts_ns = m.timestamp_s * 1e9 + m.timestamp_ns;   // 起点: sof_ptp
const double meta_ns = m.metadata_s * 1e9 + m.metadata_ns;   // 终点: cur_ptp
if (meta_ns >= ts_ns) {
    st->add_fpga_latency(meta_ns - ts_ns);
}

2. 两个端点的确切含义

端点 metadata 字段 FPGA 变量 采集时刻
起点 timestamp_s/ns sof_ptp 一帧的第一个数据 beat 进入 RoCE 打包器 (dp_pkt,i_sof 脉冲)时锁存的 PTP 时间
终点 metadata_s/ns cur_ptp FPGA 发完该帧全部数据包、开始发出帧尾 metadata / RDMA write-with-immediate 包 那一刻(hdr_trigger)锁存的当前 PTP 时间

3. 物理意义

衡量一帧数据在 FPGA 内部的驻留/处理时间,即"帧首进入发包流水线"到"整帧全部推上网线"之间的时间,包含:

  • 帧打包成 MTU 大小 RoCE 包的时间
  • HIF 调度/仲裁/排队时间
  • 以太网 MAC 串行化发送时间(如 4096B 帧在 10GbE 上仅串行化约 3.3 µs)

不包含

  • 网线/交换机传输延迟(FPGA MAC → 主机 NIC)
  • 主机侧 NIC DMA、驱动、completion 轮询、用户态处理时间
  • 控制面(UDP/APB)延迟 ------ 由 rtt 指标测量

与脚本中其他指标的分工:rtt ≈ 链路往返;fpga-latency = FPGA 内部驻留;host-interval 对比 fpga-interval = 网络/主机引入的抖动。

4. 完整变量逻辑链条

① FPGA 侧:两个时间戳的诞生(i_pclk 包时钟域)

复制代码
PTP 计数器 (ptp_clk 域, 自由运行, 无需与主机对时)
  ptp_sec[47:0], ptp_nsec[31:0]
      │  CDC 同步 (fpga/pynq/rfsoc-pynq/rtl/top/FPGA_top.sv:262 u_ptp_hif_cdc)
      ▼
  i_cur_ptp[79:0] = {sec[47:0], nsec[31:0]}          ─┐
      │                                               │
      ├─── 起点 ──────────────────────────────┐       │
      │  dp_pkt_top.sv:208  i_sof[vp] 脉冲    │       │
      │  (帧首数据 beat 进入打包器)            │       │
      ▼                                       │       │
  dp_pkt_ts.sv:56  FIFO 锁存 i_ptp[63:0]     │       │
      = {sec[31:0], nsec[31:0]}               │       │
      │  按 VP 存入 Timestamp RAM (line 177)  │       │
      │  帧尾时读出 (line 198)                │       │
      ▼                                       │       │
  sof_ptp[79:0] = {16'h0, sec[31:0], nsec}    │       │
                                              │       │
      ├─── 终点 ──────────────────────────────┤       │
      │  dp_pkt_top.sv:771                    │       │
      ▼                                       │       │
  cur_ptp_sync = i_cur_ptp ◄──────────────────┘       │
      │  dp_pkt_top.sv:1321-1322                      │
      │  hdr_trigger=1 时锁存                         │
      │  (整帧数据包发完, 开始发 metadata/write-imm)   │
      ▼                                               │
  cur_ptp[79:0] = {sec[47:0], nsec[31:0]} ◄───────────┘

② FPGA 侧:组包发出(fpga/nv_hsb_ip/dp_pkt/dp_pkt_top.sv)

复制代码
wr_imm_data[383:0] = { md_flags,            ──→ flags
                       8'h0, psn,           ──→ psn
                       frame_crc,           ──→ crc
                       16'h0, sof_ptp,      ──→ timestamp_s/ns  (起点, 96bit)
                       roce_val_addr,       ──→ bytes_written
                       16'h0, frame_num,    ──→ frame_number
                       16'h0, cur_ptp }     ──→ metadata_s/ns   (终点, 96bit)
                          (line 1303, 共 384bit = 48B)
      │ line 1310:  pkt_commit 时 wr_imm_meta_r <= wr_imm_data[383:80]
      │ line 1314:  wr_imm_data_hdr = {wr_imm_meta_r, cur_ptp}
      │             ※ cur_ptp 在最后时刻才拼入, 最贴近真实发出点
      ▼ line 1338:  gen_imm_byte_align 字节反转 → 大端
wr_imm_pkt_be  ──以太网──►  作为该帧最后一个包:
                   RDMA Write-with-Immediate 的 payload
                   (imm = {psn[19:0], buf_ptr[11:0]}, line 1302)

③ 主机侧:接收解析(tools/roce_ramplayer_analyzer/roce_ramplayer_analyzer.cpp)

复制代码
NIC RDMA 写入 ring buffer 一页:
  page   = imm & 0xFFF                     (line 735)
  frame  = buffer + page * page_size       (line 744)
  meta   = frame + metadata_offset         (metadata_offset = aligned_frame, line 489)
      ▼
parse_metadata(meta)  (line 133, BeReader 大端, 与 FPGA 组包顺序一一对应;
                       布局同 src/hololink/core/hololink.cpp:3139 deserialize_metadata)
  → m.timestamp_s / m.timestamp_ns   ← sof_ptp  (起点)
  → m.metadata_s  / m.metadata_ns    ← cur_ptp  (终点)

④ 主机侧:计算与输出

复制代码
ts_ns   = m.timestamp_s*1e9 + m.timestamp_ns   ┐ line 778
meta_ns = m.metadata_s*1e9  + m.metadata_ns    ┘ line 785
      ▼
if (meta_ns >= ts_ns)
    add_fpga_latency(meta_ns - ts_ns)          line 786-788
      ▼ 累加进
fpga_lat_sum / fpga_lat_min / fpga_lat_max / fpga_lat_count   (Stats, line 257)
      ▼ Stats::print()  line 316
"fpga-latency mean %.2f us min %.2f max %.2f us"   (ns÷1e3 → µs)
      ▼ 写入 /tmp/latency.log (run_latency.sh) 或 /tmp/final.log (run_ramplayer_analyzer.sh)
grep "fpga-latency" | tail -4   ──► 终端输出

5. 保证测量正确性的两个关键设计

  1. 同时钟域 :起点和终点都来自同一个自由运行 PTP 计数器,主机只算差值,所以分析器可以安全地设置 ptp_enable=0(跳过 PTP 对时配置,见 roce_ramplayer_analyzer.cpp:444)。
  2. 终点采得晚cur_ptp 不随其他字段在 pkt_commit 时一起寄存,而是在组包最后一刻(wr_imm_data_hdr = {wr_imm_meta_r, cur_ptp},dp_pkt_top.sv:1314)才拼入,确保它真实反映 metadata 包的发出时刻,而非提前锁存的旧值。

6. 关键文件索引

文件 作用
fpga/nv_hsb_ip/dp_pkt/dp_pkt_top.sv:1303 metadata 48B 组包(sof_ptp / cur_ptp 拼接点)
fpga/nv_hsb_ip/dp_pkt/dp_pkt_top.sv:1316-1325 cur_ptphdr_trigger 时锁存(终点)
fpga/nv_hsb_ip/dp_pkt/dp_pkt_ts.sv SOF 时刻 PTP 锁存 FIFO + 按 VP 的 Timestamp RAM(起点)
src/hololink/core/hololink.cpp:3139 deserialize_metadata,主机侧 metadata 布局定义
tools/roce_ramplayer_analyzer/roce_ramplayer_analyzer.cpp:778-788 fpga-latency 计算
tools/roce_ramplayer_analyzer/roce_ramplayer_analyzer.cpp:316 fpga-latency 打印
run_latency.sh / run_ramplayer_analyzer.sh 采集流程与结果 grep

2. fpga-latency 两个 PTP 时间戳的触发机制

fpga-latency 的两个端点(sof_ptp / cur_ptp)由 FPGA 硬件走两条完全不同的触发路径自动注入数据包,主机端程序只负责收包解析,不参与触发。

1. 起点 sof_ptp:SOF 脉冲 → 按 VP 缓存 → 帧尾读出

复制代码
① SOF 检测(sensor 时钟域 sclk)
   fpga/nv_hsb_ip/packetizer/packetizer_top.sv:219
   sif_sof = i_axis_tvalid && !pkt_active && o_axis_tready
   ── 一帧第一个数据 beat 握手成功、且当前无活跃包 → 产生 1 拍脉冲

② 跨时钟域同步(sclk → pclk)
   packetizer_top.sv:201  pulse_sync u_sof_sync
   sif_sof ──► sif_sof_sync ──► o_sof[vp] (line 224)

③ 传入打包器
   fpga/nv_hsb_ip/top/HOLOLINK_top.sv:1467/1514
   pkt_raw_sof → sif_sof → dp_pkt_top.i_sof

④ SOF 时刻锁存 PTP(关键!)
   fpga/nv_hsb_ip/dp_pkt/dp_pkt_ts.sv:52-56   reg_fifo
   wr  = |i_req (= i_sof)
   din = {i_req, i_ptp[63:0]}   ← 这一拍把"当前 PTP"和 VP 号一起推进 FIFO

⑤ 仲裁后按 VP 写入 Timestamp RAM
   dp_pkt_ts.sv:118 priority_arb → ram_wr (line 200)
   每个 VP 有 4 槽循环缓冲(wr_ptr/rd_ptr),
   允许多帧在飞,SOF 时间戳按序排队

⑥ 帧尾读出
   fpga/nv_hsb_ip/dp_pkt/dp_pkt_top.sv:513
   ram_rd = (roce_state == ROCE_IDLE) && tlast_aligned
   ── 该 VP 的帧数据全部发完 → 取出它排队中的 SOF 时间戳
   o_ptp = {16'h0, ram_rd_data} = sof_ptp  ──► 拼入 wr_imm_data (line 1303)

为什么要缓存:SOF 时刻帧才刚刚开始发送,metadata 包要等整帧发完才生成,所以 SOF 时间戳必须先存起来,等帧尾再取出。

2. 终点 cur_ptp:FSM 帧尾提交 → hdr_trigger → 最后一刻拼包

复制代码
① 帧尾识别
   dp_pkt_top.sv:833/893  sif_eof <= sif_eof | sif_wtlast  (传感器侧 tlast)
   line 846               pkt_is_imm <= sif_eof[...]
   ── 下一包 = 本帧最后一包 = write-with-immediate(hdr_type=2'b10, opcode=0x2B)

② FSM 逐包发送:ROCE_IDLE→LOAD_STS→GRANT→CALC→STORE→HDR_WAIT
   数据包全部提交后,最后一个 imm 包在 CALC 态等待 commit

③ 提交触发(关键!)
   line 953  pkt_commit = commit_pending_r && !hif_busy && (pkt_is_imm || ...)
   line 877  hdr_trigger <= md_disable ? !pkt_is_imm : '1
   ── imm 包被实际提交发往 MAC 的那一拍,hdr_trigger 拉高

④ 锁存当前 PTP(关键!)
   line 1321-1322
   if (hdr_trigger)  cur_ptp <= cur_ptp_sync;   ← 此刻的实时 PTP

⑤ 最后一刻才拼进数据包
   line 1310  wr_imm_meta_r <= wr_imm_data[383:80]  (pkt_commit 时先寄存前 304 bit,
                                                     其中已含 ①⑥ 读出的 sof_ptp)
   line 1314  wr_imm_data_hdr = {wr_imm_meta_r, cur_ptp}  ← cur_ptp 组合逻辑即时拼接
   line 1338  字节反转 → wr_imm_pkt_be(大端)
   ── 所以 cur_ptp 采的是"包头真正流出"那一拍的值,而不是提前寄存

⑥ 包头发出
   dp_pkt_hdr (line 1395): i_metadata(wr_imm_pkt_be) + i_trigger(hdr_trigger)
   → RoCE header + 4B imm + 128B metadata(payload, 前48B有效) 串成 AXI 流
   → hdr_mux (line 1464) → MAC → 以太网

3. 主机端"看到"的接力

复制代码
FPGA 发出 RDMA Write-with-Immediate (dma_len=128, dp_pkt_top.sv:863)
  ├─ payload = 128B metadata(含两个时间戳),RDMA 地址紧接帧数据之后
  │   → NIC 硬件 DMA 直接写进主机页内 frame + metadata_offset,CPU 零拷贝
  └─ imm = {psn[19:0], buf_ptr[11:0]}
       ↓
ibv_poll_cq → IBV_WC_RECV_RDMA_WITH_IMM
  page = imm & 0xFFF                       (roce_ramplayer_analyzer.cpp:735)
  parse_metadata(frame + metadata_offset)  大端解析 (analyzer:133/745)
  → timestamp_s/ns(起点)与 metadata_s/ns(终点)同时到手
  → fpga-latency = metadata_ts − frame_ts  (analyzer:778-788)

4. 设计要点小结

起点 sof_ptp 终点 cur_ptp
触发信号 i_sof(帧首 beat 握手) hdr_trigger(imm 包 commit)
锁存方式 FIFO + 按 VP 的循环 RAM(需跨整帧保存) 单寄存器,随用随采
拼包时机 pkt_commit 时随 wr_imm_meta_r 寄存 发包头那一刻组合逻辑拼入(最新值)
精度保障 SOF 与发包并发进行,不丢帧 采得晚,贴近真实发出点

两条路径一"早"一"晚":起点必须先存后取 (SOF 时 metadata 还不存在),终点必须随采随用(保证是发出时刻的真实时间)。二者共用同一个自由运行 PTP 计数器,差值即精确的 FPGA 内部驻留时间。

相关推荐
Eloudy1 天前
基于 PTP 的 FPGA→RoCE→CPU 单向延迟测量实验方案
gpu·fpga
博览鸿蒙1 天前
新手怎么挑一块靠谱的FPGA开发板?
fpga开发·fpga·fpga开发板
FPGA小迷弟2 天前
【无标题】
fpga开发·dsp开发·fpga·sdr·无线电
Eloudy2 天前
RAM Player 实验:hololink-write 开流 + RoCEv2 接收分析
fpga
FPGA小迷弟3 天前
FPGA 应用技术能力的完整闭环(5-8)
fpga开发·dsp开发·fpga·sdr·无线电
Eloudy3 天前
vitis 工程 ping FPGA 丢包(~50% 随机)问题分析
fpga
学习FPGA的电气小兴兴3 天前
基于FPGA的CORDIC向量模式实现arctan运算,Verilog HDL实现
嵌入式硬件·fpga开发·数字信号处理·verilog·fpga·cordic·arctan
FPGA小迷弟4 天前
FPGA 应用技术能力的完整闭环(1-4)
fpga开发·dsp开发·fpga·sdr·无线电
NamoAmitabha1284 天前
vivado时序报红/插入流水线寄存器/降低扇出
fpga·vivado