ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

FPGA光口收发(双光口收发验证)

FPGA光口收发(双光口收发验证) Kintex-7 双 SFP 光口 Aurora 8B/10B 独立全双工通信从 IP 配置到双 ILA 抓波形功能简介:.本文使用 Vivado 2019.2 和 XC7K325TFFG676-2实现两个彼此独立的 Aurora 8B/10B 全双工链路。光口 0 连续发送 0、2、4、6……并检查收到的 1、3、5、7……光口 1 连续发送1、3、5、7……并检查收到的 1、3、5、7…… 工程已经完成上板测试两个链路均能正常建立并连续收发。一、为什么写这篇文章刚接触 FPGA 高速串行通信时下面几个概念很容易混淆1. 开发板有两个 SFP 光口是配置一个两 Lane 的 Aurora IP还是配置两个独立 IP2. SFP 模块标称“10G”Aurora 的 Line Rate 是否也必须设成 10 Gb/s3. Lane Width 设置为 2究竟表示两条 GTX Lane还是 2 字节用户数据4. Aurora 例程里有 GT Wizard自己的设计是否也必须直接使用 GT Wizard5. 两个光口能否共用一个 ILA6. 156.25 MHz 参考时钟和 3.125 Gb/s 线速之间是什么关系7. 为什么照着网上代码例化 IP会报 “named port connection init_clk does not exist”本文围绕一个实际运行成功的工程把这些问题一次讲清楚并给出完整顶层代码、完整 XDC 约束和 ILA 调试方法。二、最终实现目标器件和软件| 项目 | 配置 || FPGA | Xilinx Kintex-7 XC7K325TFFG676-2 || 开发工具 | Vivado 2019.2 || 协议 IP | Aurora 8B/10B || Aurora IP 数量 | 2 个名称分别为 aurora_0、aurora_1 || 每个 IP 的物理 Lane 数 | 1 || Lane Width | 2 Byte即 16 位 AXI4-Stream 数据 || Line Rate | 3.125 Gb/s || GT 参考时钟 | 156.25 MHz 差分时钟 || 初始化时钟 | 50 MHz 单端时钟 || 接口模式 | Streaming || 数据流模式 | Duplex || 流控 | None || 调试 IP | 2 个 ILA每个 Aurora 时钟域一个 || ILA 深度 | 2048 |数据规则| 光口 | 发送序列 | 应接收序列 ||---|---|---|| SFP0 / aurora_0 | 0、2、4、6…… | 1、3、5、7…… || SFP1 / aurora_1 | 1、3、5、7…… | 0、2、4、6…… |工程属于板内双光口互连测试连接关系是FPGA 内部 光纤┌────────────────────────────┐ ┌────────────────────┐│ Aurora_0 ── TX ── SFP0_TX │ ───────────────► │ SFP1_RX ── Aurora_1││ Aurora_0 ◄─ RX ── SFP0_RX │ ◄─────────────── │ SFP1_TX ── Aurora_1│└────────────────────────────┘ └────────────────────┘普通 LC 双工跳线内部有两根光纤分别承担两个方向。若手上只有一根单芯光纤则不能用普通双纤 SFP 模块同时完成全双工收发单纤双向需要成对的 BiDi 模块。三、先理解几个关键概念3.1 为什么使用两个 Aurora IP本文要求两个光口是两个独立链路因此正确结构是- aurora_0 只控制 SFP0 对应的 GTX Channel- aurora_1 只控制 SFP1 对应的 GTX Channel- 两个 IP 分别产生自己的 channel_up、lane_up、user_clk 和 AXI4-Stream 接口。如果把一个 Aurora IP 的 Aurora Lanes 设置为 2含义是把两条 GTX Lane 绑定成一个更宽的 Aurora 逻辑通道。它不是“两个独立光口”- 两条 Lane 共同组成一条链路- 通道初始化和状态有关联- 不能自然地把一个 Lane 当奇数端口另一个 Lane 当偶数端口- 任意一条 Lane 异常都可能影响整个逻辑通道。所以 两个独立光口 两个 Aurora IP每个 IP 的 Aurora Lanes 都设置为 1。3.2 Lane Width2 并不表示两条光纤Aurora 8B/10B 配置界面里的 Lane Width 表示每条物理 Lane 每个用户时钟周期传输多少字节- Lane Width2 Byte- AXI4-Stream 数据宽度为 16 bit- 它与 SFP 光口数量没有直接关系。物理 Lane 数由 Aurora Lanes 决定。本工程中每个 IP 都是Aurora Lanes 1Lane Width 2 Byte用户数据宽度 1 × 2 × 8 16 bit3.3 为什么 3.125 Gb/s 配 156.25 MHzAurora 8B/10B 每 8 位有效数据编码成 10 位线路数据编码效率是8 / 10 80%因此单方向理论有效载荷带宽为3.125 Gb/s × 0.8 2.5 Gb/s本工程用户接口宽度是 16 bit用户时钟约为2.5 Gb/s ÷ 16 bit 156.25 MHz所以 3.125 Gb/s、16 bit AXI 数据和约 156.25 MHz user_clk 是相互匹配的。请注意156.25 MHz 的 GT 参考时钟并不等于“只能运行 10G”。GT 内部 CPLL/QPLL 会根据 Line Rate、参考时钟和分频参数产生高速串行时钟。本工程在 3.125 Gb/s 下使用每个 GTX Channel 自己的 CPLL。3.4 10G 光模块不代表必须工作在 10 Gb/s“10G SFP”通常描述模块的目标速率等级不代表任何 10G 模块都能稳定工作在 3.125 Gb/s。实际使用前应确认模块是否支持多速率或是否能在 3.125 Gb/s 下正常工作。常见情况- 多速率模块可能支持 1.25G、2.5G、3.125G 等速率- 带固定速率 CDR 的模块可能只在标称速率附近锁定- 模块不支持当前速率时FPGA 逻辑完全正确channel_up 也可能始终不起。本文所用模块已经在 3.125 Gb/s 下实际建立链路。3.5 Aurora 与 GT Wizard 的关系GT Wizard 更接近 GTX/GTH 收发器底层负责 PLL、编码宽度、均衡、复位等基础配置。Aurora 8B/10B 是建立在 GT 之上的链路协议 IP还提供- 8B/10B 协议处理- Lane 初始化- Channel 初始化- 错误检测- AXI4-Stream 用户接口- channel_up、lane_up 等状态信号。Aurora IP 内部本来就会使用 GT 相关逻辑。我们不需要在顶层再用一个独立 GT Wizard 代替 Aurora。四、创建 Vivado 工程4.1 新建工程在 Vivado 2019.2 中选择File → Project → New创建 RTL Project并选择器件xc7k325tffg676-2如果器件选错即使 RTL 一样Aurora 可选择的速率、GT 位置和最终引脚都会不同。4.2 添加 Aurora 8B/10B IP打开 IP Catalog搜索Aurora 8B/10B创建第一个 IP名称设为aurora_0关键参数如下| 参数 | 设置 ||---|---|| Dataflow Mode | Duplex || Interface | Streaming || Aurora Lanes | 1 || Lane Width | 2 Bytes || Line Rate | 3.125 Gb/s || GT Refclk Frequency | 156.250 MHz || INIT CLK | 50.0 MHz || DRP CLK | 50.0 MHz || Flow Control | None || CRC | 不启用 || Scrambler | 不启用 || Byte Swap | 不启用 |完成后选择 Generate Output Products。再创建第二个完全相同的 IP名称设为aurora_1不要让两个 IP 同名也不要把第一个 IP 简单复制后仍保留原模块名否则综合时会发生模块冲突。4.3 必须查看 Instantiation Template不同 Vivado 版本、器件、IP 配置和 Shared Logic 选项生成的端口可能不同。不要凭印象手写端口。右键 Aurora IP选择Open IP Example Design或View Instantiation TemplateVivado 生成的 .veo 文件才是当前 IP 的真实端口定义。本工程实际端口名是- init_clk_in而不是 init_clk- gt_refclk1 是经过 IBUFDS_GTE2 后的单端 GT 参考时钟- AXI 发送接口为 s_axi_tx_tdata、s_axi_tx_tvalid、s_axi_tx_tready- 接收接口为 m_axi_rx_tdata、m_axi_rx_tvalid- 此配置没有 RX tready也没有使用 tlast、tkeep。曾经遇到的错误[Synth 8-448] named port connection init_clk does not exist根本原因不是开发板不支持 Aurora而是顶层例化的端口名称与新生成的 Aurora 模块不一致。把端口名改成模板里的 init_clk_in 后才能继续。五、时钟结构本工程共有三类时钟。5.1 156.25 MHz GT 参考时钟两个 GTX Channel 位于同一个 Quad共用同一组 156.25 MHz 差分参考时钟。顶层只例化一次 IBUFDS_GTE2verilogIBUFDS_GTE2 gt_refclk_ibufds_i ( .I (gt_refclk_p), .IB (gt_refclk_n), .CEB (1b0), .O (gt_refclk), .ODIV2 () );缓冲后的 gt_refclk 同时送给 aurora_0 和 aurora_1 的 gt_refclk1。不要为同一组物理参考时钟引脚例化两个 IBUFDS_GTE2否则可能产生时钟资源冲突。5.2 50 MHz 初始化时钟板载 G22 引脚提供 50 MHz 单端时钟经 BUFG 后连接- init_clk_in- drpclk_in- GT 复位同步逻辑。verilogBUFG init_clk_bufg_i ( .I (init_clk_50m), .O (init_clk) );5.3 两个独立 user_clk每个 Aurora IP 都输出自己的 tx_out_clk并分别经过一个 BUFGverilogBUFG ip0_user_clk_bufg_i ( .I (ip0_tx_out_clk), .O (ip0_user_clk) ); BUFG ip1_user_clk_bufg_i ( .I (ip1_tx_out_clk), .O (ip1_user_clk) );虽然两个 user_clk 的频率都约为 156.25 MHz但它们来自两个独立 GTX/CPLL彼此没有固定相位关系。因此两个 Aurora 用户逻辑必须按照两个独立时钟域处理。六、复位设计开发板按键 rst_n 是低有效而 Aurora 的 reset 和 gt_reset 是高有效verilogassign reset_req ~rst_n;高速接口复位不能只做一个简单的组合取反。比较稳妥的方式是- 异步拉高复位- 在目标时钟域内同步释放- 每个时钟域使用自己的同步寄存器链。本工程使用 4 级移位寄存器verilog(* ASYNC_REG TRUE *) reg [3:0] ip0_gt_reset_pipe 4hf; (* ASYNC_REG TRUE *) reg [3:0] ip0_sys_reset_pipe 4hf;GT reset 在 init_clk 域释放系统 reset 在对应的 user_clk 域释放。第二个 Aurora 核使用另一套独立寄存器链不能直接跨时钟域共用同步后的复位。七、AXI4-Stream 数据发送与检查7.1 AXI 发送握手AXI4-Stream 的一次有效传输必须同时满足tvalid 1 且 tready 1因此计数器只能在握手成功时加 2verilogif (ip0_tx_tvalid ip0_tx_tready) ip0_tx_count ip0_tx_count 2;如果只看 tvalid 就增加计数器当 Aurora 暂时拉低 tready 时发送端会跳过数据接收端就会报错。7.2 偶数发送端aurora_0 从 0 开始每次成功发送后加 20000、0002、0004、0006……同时它期待收到 aurora_1 发来的奇数0001、0003、0005、0007……7.3 奇数发送端aurora_1 从 1 开始每次成功发送后加 2同时期待收到偶数。7.4 错误锁存收到有效数据时将 RX 数据与 expected 比较verilogif (ip0_rx_tdata ! ip0_expected) ip0_data_error 1b1;data_error 是粘滞错误标志- 正常情况下始终为 0- 任意一次比较失败后变为 1- 只有重新复位才清零。这种方式比输出一个单周期错误脉冲更方便观察 LED 和 ILA。八、完整顶层代码下面代码对应本次实际运行通过的工程。注意数据总线使用 [0:15]这是当前 Aurora 模板生成的方向建议保持与自己 IP 的 .veo 模板一致。verilogtimescale 1ns/1ps // Two independent Aurora 8B/10B duplex links. // IP configuration: 1 lane/core, 2-byte lane width, 3.125 Gb/s, // 156.25 MHz GT reference clock, AXI4-Stream, no flow control. // // With a duplex LC fibre crossed between the two SFP cages: // SFP0/IP0 transmits 0,2,4,... and checks 1,3,5,... // SFP1/IP1 transmits 1,3,5,... and checks 0,2,4,... module aurora_duplex_top ( input wire rst_n, input wire init_clk_50m, input wire gt_refclk_p, input wire gt_refclk_n, input wire sfp0_rx_p, input wire sfp0_rx_n, output wire sfp0_tx_p, output wire sfp0_tx_n, output wire sfp0_tx_disable, input wire sfp1_rx_p, input wire sfp1_rx_n, output wire sfp1_tx_p, output wire sfp1_tx_n, output wire sfp1_tx_disable, output wire [3:0] led ); localparam [0:15] TWO 16h0002; wire reset_req; wire init_clk; wire gt_refclk; assign reset_req ~rst_n; assign sfp0_tx_disable 1b0; assign sfp1_tx_disable 1b0; // G22 is the boards stable 50 MHz single-ended clock. It is used by // the Aurora GT reset state machines and by the unused DRP interfaces. BUFG init_clk_bufg_i ( .I (init_clk_50m), .O (init_clk) ); // Both GTX channels are in the same quad and use the same 156.25 MHz // differential reference clock. Buffer it only once. IBUFDS_GTE2 gt_refclk_ibufds_i ( .I (gt_refclk_p), .IB (gt_refclk_n), .CEB (1b0), .O (gt_refclk), .ODIV2 () ); // --------------------------------------------------------------------- // Aurora core 0 clocks, reset and stream signals // --------------------------------------------------------------------- wire ip0_tx_out_clk; wire ip0_user_clk; wire ip0_tx_lock; wire ip0_pll_not_locked; wire ip0_channel_up; wire ip0_lane_up; wire ip0_hard_err; wire ip0_soft_err; wire ip0_tx_tready; wire ip0_rx_tvalid; wire [0:15] ip0_rx_tdata; wire ip0_sys_reset_out; BUFG ip0_user_clk_bufg_i ( .I (ip0_tx_out_clk), .O (ip0_user_clk) ); assign ip0_pll_not_locked ~ip0_tx_lock; // Assert asynchronously and release synchronously in each required // clock domain. Initial values also hold reset during FPGA startup. (* ASYNC_REG TRUE *) reg [3:0] ip0_gt_reset_pipe 4hf; (* ASYNC_REG TRUE *) reg [3:0] ip0_sys_reset_pipe 4hf; always (posedge init_clk or posedge reset_req) begin if (reset_req) ip0_gt_reset_pipe 4hf; else ip0_gt_reset_pipe {ip0_gt_reset_pipe[2:0], 1b0}; end always (posedge ip0_user_clk or posedge reset_req) begin if (reset_req) ip0_sys_reset_pipe 4hf; else ip0_sys_reset_pipe {ip0_sys_reset_pipe[2:0], 1b0}; end wire ip0_gt_reset; wire ip0_sys_reset; assign ip0_gt_reset |ip0_gt_reset_pipe; assign ip0_sys_reset |ip0_sys_reset_pipe; reg [0:15] ip0_tx_count 16h0000; reg [0:15] ip0_expected 16h0001; reg ip0_data_error 1b0; wire [0:15] ip0_tx_tdata; wire ip0_tx_tvalid; assign ip0_tx_tdata ip0_tx_count; assign ip0_tx_tvalid ip0_channel_up; always (posedge ip0_user_clk) begin if (ip0_sys_reset) begin ip0_tx_count 16h0000; ip0_expected 16h0001; ip0_data_error 1b0; end else begin if (ip0_tx_tvalid ip0_tx_tready) ip0_tx_count ip0_tx_count TWO; if (!ip0_channel_up) ip0_expected 16h0001; else if (ip0_rx_tvalid) begin if (ip0_rx_tdata ! ip0_expected) ip0_data_error 1b1; ip0_expected ip0_expected TWO; end end end aurora_0 aurora_0_i ( .s_axi_tx_tdata (ip0_tx_tdata), .s_axi_tx_tvalid (ip0_tx_tvalid), .s_axi_tx_tready (ip0_tx_tready), .m_axi_rx_tdata (ip0_rx_tdata), .m_axi_rx_tvalid (ip0_rx_tvalid), .rxp (sfp0_rx_p), .rxn (sfp0_rx_n), .txp (sfp0_tx_p), .txn (sfp0_tx_n), .gt_refclk1 (gt_refclk), .hard_err (ip0_hard_err), .soft_err (ip0_soft_err), .lane_up (ip0_lane_up), .channel_up (ip0_channel_up), .user_clk (ip0_user_clk), .sync_clk (ip0_user_clk), .gt_reset (ip0_gt_reset), .reset (ip0_sys_reset), .sys_reset_out (ip0_sys_reset_out), .power_down (1b0), .loopback (3b000), .tx_lock (ip0_tx_lock), .init_clk_in (init_clk), .tx_resetdone_out (), .rx_resetdone_out (), .link_reset_out (), .drpclk_in (init_clk), .drpaddr_in (9d0), .drpen_in (1b0), .drpdi_in (16d0), .drprdy_out (), .drpdo_out (), .drpwe_in (1b0), // 3.125 Gb/s uses each channels CPLL; QPLL ports are unused. .gt0_qplllock_in (1b1), .gt0_qpllrefclklost_in (1b0), .gt0_qpllreset_out (), .gt_qpllclk_quad1_in (1b0), .gt_qpllrefclk_quad1_in (1b0), .tx_out_clk (ip0_tx_out_clk), .pll_not_locked (ip0_pll_not_locked) ); // --------------------------------------------------------------------- // Aurora core 1 clocks, reset and stream signals // --------------------------------------------------------------------- wire ip1_tx_out_clk; wire ip1_user_clk; wire ip1_tx_lock; wire ip1_pll_not_locked; wire ip1_channel_up; wire ip1_lane_up; wire ip1_hard_err; wire ip1_soft_err; wire ip1_tx_tready; wire ip1_rx_tvalid; wire [0:15] ip1_rx_tdata; wire ip1_sys_reset_out; BUFG ip1_user_clk_bufg_i ( .I (ip1_tx_out_clk), .O (ip1_user_clk) ); assign ip1_pll_not_locked ~ip1_tx_lock; (* ASYNC_REG TRUE *) reg [3:0] ip1_gt_reset_pipe 4hf; (* ASYNC_REG TRUE *) reg [3:0] ip1_sys_reset_pipe 4hf; always (posedge init_clk or posedge reset_req) begin if (reset_req) ip1_gt_reset_pipe 4hf; else ip1_gt_reset_pipe {ip1_gt_reset_pipe[2:0], 1b0}; end always (posedge ip1_user_clk or posedge reset_req) begin if (reset_req) ip1_sys_reset_pipe 4hf; else ip1_sys_reset_pipe {ip1_sys_reset_pipe[2:0], 1b0}; end wire ip1_gt_reset; wire ip1_sys_reset; assign ip1_gt_reset |ip1_gt_reset_pipe; assign ip1_sys_reset |ip1_sys_reset_pipe; reg [0:15] ip1_tx_count 16h0001; reg [0:15] ip1_expected 16h0000; reg ip1_data_error 1b0; wire [0:15] ip1_tx_tdata; wire ip1_tx_tvalid; assign ip1_tx_tdata ip1_tx_count; assign ip1_tx_tvalid ip1_channel_up; always (posedge ip1_user_clk) begin if (ip1_sys_reset) begin ip1_tx_count 16h0001; ip1_expected 16h0000; ip1_data_error 1b0; end else begin if (ip1_tx_tvalid ip1_tx_tready) ip1_tx_count ip1_tx_count TWO; if (!ip1_channel_up) ip1_expected 16h0000; else if (ip1_rx_tvalid) begin if (ip1_rx_tdata ! ip1_expected) ip1_data_error 1b1; ip1_expected ip1_expected TWO; end end end aurora_1 aurora_1_i ( .s_axi_tx_tdata (ip1_tx_tdata), .s_axi_tx_tvalid (ip1_tx_tvalid), .s_axi_tx_tready (ip1_tx_tready), .m_axi_rx_tdata (ip1_rx_tdata), .m_axi_rx_tvalid (ip1_rx_tvalid), .rxp (sfp1_rx_p), .rxn (sfp1_rx_n), .txp (sfp1_tx_p), .txn (sfp1_tx_n), .gt_refclk1 (gt_refclk), .hard_err (ip1_hard_err), .soft_err (ip1_soft_err), .lane_up (ip1_lane_up), .channel_up (ip1_channel_up), .user_clk (ip1_user_clk), .sync_clk (ip1_user_clk), .gt_reset (ip1_gt_reset), .reset (ip1_sys_reset), .sys_reset_out (ip1_sys_reset_out), .power_down (1b0), .loopback (3b000), .tx_lock (ip1_tx_lock), .init_clk_in (init_clk), .tx_resetdone_out (), .rx_resetdone_out (), .link_reset_out (), .drpclk_in (init_clk), .drpaddr_in (9d0), .drpen_in (1b0), .drpdi_in (16d0), .drprdy_out (), .drpdo_out (), .drpwe_in (1b0), .gt0_qplllock_in (1b1), .gt0_qpllrefclklost_in (1b0), .gt0_qpllreset_out (), .gt_qpllclk_quad1_in (1b0), .gt_qpllrefclk_quad1_in (1b0), .tx_out_clk (ip1_tx_out_clk), .pll_not_locked (ip1_pll_not_locked) ); // --------------------------------------------------------------------- // Hardware debug. Each ILA must use the clock of the Aurora signals it // samples; the two GTX user clocks are not phase-aligned to each other. // Probe map for both ILAs: // 0 TX data, 1 TX valid, 2 TX ready, 3 RX data, 4 RX valid, // 5 channel up, 6 lane up, 7 expected RX data, // 8 sticky data error, 9 hard error, 10 soft error. // --------------------------------------------------------------------- ila_aurora0 ila_aurora0_i ( .clk (ip0_user_clk), .probe0 (ip0_tx_tdata), .probe1 (ip0_tx_tvalid), .probe2 (ip0_tx_tready), .probe3 (ip0_rx_tdata), .probe4 (ip0_rx_tvalid), .probe5 (ip0_channel_up), .probe6 (ip0_lane_up), .probe7 (ip0_expected), .probe8 (ip0_data_error), .probe9 (ip0_hard_err), .probe10 (ip0_soft_err) ); ila_aurora1 ila_aurora1_i ( .clk (ip1_user_clk), .probe0 (ip1_tx_tdata), .probe1 (ip1_tx_tvalid), .probe2 (ip1_tx_tready), .probe3 (ip1_rx_tdata), .probe4 (ip1_rx_tvalid), .probe5 (ip1_channel_up), .probe6 (ip1_lane_up), .probe7 (ip1_expected), .probe8 (ip1_data_error), .probe9 (ip1_hard_err), .probe10 (ip1_soft_err) ); // LED0/1: channel up. LED2/3: sticky data error or Aurora error. assign led[0] ip0_channel_up; assign led[1] ip1_channel_up; assign led[2] ip0_data_error | ip0_hard_err | ip0_soft_err; assign led[3] ip1_data_error | ip1_hard_err | ip1_soft_err; endmodule九、完整 XDC 引脚约束9.1 本工程的引脚分配| 信号 | FPGA 引脚/资源 | 说明 ||---|---|---|| gt_refclk_p/n | D6 / D5 | 156.25 MHz GTX 差分参考时钟 || init_clk_50m | G22 | 50 MHz 初始化时钟 || rst_n | D26 | 低有效复位 || SFP0 RX P | E4 | aurora_0 接收 || SFP0 TX P | D2 | aurora_0 发送 || SFP0 GTX | GTXE2_CHANNEL_X0Y5 | 独立 GTX Channel || SFP0 TX_DISABLE | H23 | 低电平使能模块发送 || SFP1 RX P | C4 | aurora_1 接收 || SFP1 TX P | B2 | aurora_1 发送 || SFP1 GTX | GTXE2_CHANNEL_X0Y6 | 独立 GTX Channel || SFP1 TX_DISABLE | H24 | 低电平使能模块发送 || LED[3:0] | C24、D23、A24、A23 | 链路与错误状态 |GTX 差分 P/N 引脚是专用配对资源。本文约束 P 侧和 GTXE2_CHANNEL 的 LOCN 侧由选定的差分对和 GTX Channel 确定。9.2 XDC 全文## xc7k325tffg676-2 / Kintex-7 BaseC ## Aurora 8B/10B, two independent one-lane cores, 3.125 Gb/s ## Board clocks set_property PACKAGE_PIN D6 [get_ports gt_refclk_p] set_property PACKAGE_PIN D5 [get_ports gt_refclk_n] create_clock -name gt_refclk_156p25 -period 6.400 [get_ports gt_refclk_p] set_property PACKAGE_PIN G22 [get_ports init_clk_50m] set_property IOSTANDARD LVCMOS33 [get_ports init_clk_50m] create_clock -name init_clk_50m -period 20.000 [get_ports init_clk_50m] set_property PACKAGE_PIN D26 [get_ports rst_n] set_property IOSTANDARD LVCMOS33 [get_ports rst_n] ## SFP electrical pins. GTX differential N pins are fixed by the selected ## GTX channel; constrain the P-side package pins and the RX P-side here. set_property PACKAGE_PIN E4 [get_ports sfp0_rx_p] set_property PACKAGE_PIN D2 [get_ports sfp0_tx_p] set_property PACKAGE_PIN C4 [get_ports sfp1_rx_p] set_property PACKAGE_PIN B2 [get_ports sfp1_tx_p] set_property IOSTANDARD LVCMOS33 [get_ports {sfp0_tx_disable sfp1_tx_disable}] set_property PACKAGE_PIN H23 [get_ports sfp0_tx_disable] set_property PACKAGE_PIN H24 [get_ports sfp1_tx_disable] ## Force each independent Aurora core onto the SFPs GTX channel. The ## REF_NAME filter keeps this valid across Vivado-generated hierarchy names. set_property LOC GTXE2_CHANNEL_X0Y5 \ [get_cells -hier -filter {REF_NAME GTXE2_CHANNEL NAME ~ *aurora_0*}] set_property LOC GTXE2_CHANNEL_X0Y6 \ [get_cells -hier -filter {REF_NAME GTXE2_CHANNEL NAME ~ *aurora_1*}] ## Status LEDs (optional but useful while bringing up the links) set_property PACKAGE_PIN A23 [get_ports {led[0]}] set_property PACKAGE_PIN A24 [get_ports {led[1]}] set_property PACKAGE_PIN D23 [get_ports {led[2]}] set_property PACKAGE_PIN C24 [get_ports {led[3]}] set_property IOSTANDARD LVCMOS33 [get_ports {led[0] led[1] led[2] led[3]}]这份引脚表只适用于本文所用开发板。即使 FPGA 型号相同不同 PCB 的 SFP、时钟和 LED 走线也可能不同移植时必须以自己的原理图为准。Vivado 可能提示 CFGBVS 和 CONFIG_VOLTAGE 未设置。它主要与配置 Bank 电压检查有关。应查清开发板配置 Bank 的真实供电后再设置不能直接照抄其他板卡的数值。十、添加两个 ILA10.1 为什么不建议只用一个 ILA标准 ILA 只有一个采样时钟。一个 ILA 内所有 probe 都在 clk 上采样。本工程中- aurora_0 的数据属于 ip0_user_clk- aurora_1 的数据属于 ip1_user_clk- 两个时钟频率相同但相位不固定。如果用 ip0_user_clk 的一个 ILA 直接观察 ip1_rx_tdata 或 ip1_tx_tdata会形成没有同步处理的跨时钟域采样看到的数据可能撕裂或出现并不存在的跳变。因此最可靠的做法是ila_aurora0.clk ip0_user_clkila_aurora1.clk ip1_user_clk只有在下列情况下才适合只用一个 ILA- 只观察其中一个 Aurora- 或先把另一个时钟域的少量状态信号通过同步器同步过来- 不直接跨域采集高速多位数据总线。10.2 ILA 配置创建两个 ILA IPila_aurora0ila_aurora1两个 ILA 的配置相同- Sample Data Depth2048- Number of Probes11- Advanced Trigger可以先关闭- Storage Qualification关闭- Trigger In/Out关闭。探针表| Probe | 宽度 | 信号含义 ||---|---:|---|| probe0 | 16 | TX 数据 || probe1 | 1 | TX valid || probe2 | 1 | TX ready || probe3 | 16 | RX 数据 || probe4 | 1 | RX valid || probe5 | 1 | channel_up || probe6 | 1 | lane_up || probe7 | 16 | RX 期望值 || probe8 | 1 | 粘滞数据错误 || probe9 | 1 | hard_err || probe10 | 1 | soft_err |两个 ILA 的例化已经包含在完整顶层代码中。十一、综合、实现和生成 Bitstream依次执行textRun SynthesisRun ImplementationGenerate Bitstream本次实际实现结果| 项目 | 结果 ||---|---:|| 综合错误 | 0 || 实现错误 | 0 || 未布通网络 | 0 || WNS | 2.238 ns || WHS | 0.052 ns || Slice LUT | 3430 / 2038001.68% || Slice Register | 5659 / 4076001.39% || Block RAM Tile | 7 / 4451.57% || GTXE2_CHANNEL | 2 / 8 || BUFGCTRL | 4 / 32 |最终报告显示textAll user specified timing constraints are met.加入 ILA 后资源会比纯 Aurora 设计多尤其会占用 BRAM。调试结束准备发布正式版本时可以删除 ILA 或减小采样深度。有时 ILA Out-of-Context 综合会提示默认 10 ns 时钟与实际 6.4 ns 不同。最终是否安全应以整个顶层完成布局布线后的 Timing Summary 为准而不是只看 ILA 单独综合时的提示。十二、连接光纤并抓取波形12.1 上板连接1. 开发板断电时插入两个匹配的 SFP/SFP 光模块2. 用 LC 双工光纤连接两个光模块3. 确认 TX0 到 RX1、TX1 到 RX04. 接好 JTAG 下载线5. 开发板上电6. 不要直视光模块或光纤端面。顶层中verilogassign sfp0_tx_disable 1b0; assign sfp1_tx_disable 1b0;SFP 的 TX_DISABLE 通常高电平关闭发送低电平使能发送因此这里固定输出 0。12.2 下载程序打开textOpen Hardware Manager → Open Target → Auto Connect选择 xc7k325t 设备下载生成的 .bit。如果 Vivado 没有自动关联探针文件再手动指定同一次实现生成的 .ltx。.bit 和 .ltx 必须来自同一次实现。如果混用旧文件常见报错是 ILA UUID 不匹配或找不到 Debug Core。12.3 建议的 ILA 触发方式第一次调试建议立即触发先确认时钟和 Debug Core 正常。链路能够建立后可以设置- probe5channel_up上升沿触发- 或 probe8data_error等于 1 时触发- Trigger Position 放在采样窗口中间以同时观察错误前后的数据。12.4 正常波形应满足什么条件aurora_0 的 ILA- channel_up1- lane_up1- TX valid1- 大多数周期 TX ready1- TX 数据为 0000、0002、0004、0006……- RX valid 有效时RX 数据为 0001、0003、0005、0007……- RX 数据与 expected 相等- data_error0- hard_err0- soft_err0。aurora_1 的 ILA- TX 数据为奇数递增- RX 数据为偶数递增- 其余链路和错误状态与 aurora_0 相同。由于 AXI4-Stream 只在 valid 和 ready 同时为 1 时完成发送检查数据连续性时必须以握手周期为准不能只按每一个 user_clk 周期判断。十三、LED 状态说明本文定义| LED | 含义 ||---|---|| LED0 | aurora_0 channel_up || LED1 | aurora_1 channel_up || LED2 | aurora_0 数据错误、hard_err 或 soft_err || LED3 | aurora_1 数据错误、hard_err 或 soft_err |理想状态textLED0 亮LED1 亮LED2 灭LED3 灭如果开发板 LED 是低电平点亮需要在顶层对 LED 输出取反。这个取决于板级电路必须看原理图。十四、常见故障排查14.1 channel_up 和 lane_up 始终为 0按照下面顺序检查1. 156.25 MHz 差分参考时钟是否真实存在2. XDC 的 D6/D5 是否与原理图一致3. GTX Channel 是否约束到 X0Y5 和 X0Y64. SFP TX_DISABLE 是否为低5. 光模块是否支持 3.125 Gb/s6. 光纤 TX/RX 是否交叉7. 两个 Aurora IP 的 Line Rate 和编码配置是否一致8. reset、gt_reset 是否已经释放9. tx_lock 是否为 110. 是否把 aurora_0 和 aurora_1 错误地放到了同一个 GTX Channel。14.2 只有一个光口能建立链路重点检查- 两个 IP 是否分别约束到 X0Y5、X0Y6- 是否误把两个核连接到同一组 RX/TX 顶层引脚- 两个 TX_DISABLE 是否都为低- 第二个 IP 是否真正 Generate Output Products- aurora_1 的实例端口是否仍错误连接到 aurora_0 的信号- 第二个光模块或光纤方向是否异常。14.3 channel_up 正常但 data_error 变成 1可能原因- 发送计数器没有按 valid ready 握手更新- 链路重新初始化后 expected 没有复位- 两端初始奇偶值写反- 数据位方向与 IP 模板不一致- 用一个 ILA 跨时钟域观察两个多位总线产生了假象- 接收数据前还有其他测试数据或初始化数据。本文在 channel_up 下降时把 expected 恢复到初始值避免链路重连后继续使用旧期望值。14.4 报 init_clk 端口不存在不要继续猜端口名。打开当前 IP 的 .veo 或 stub 文件逐个核对。本工程使用verilog.init_clk_in(init_clk)不是verilog.init_clk(init_clk)14.5 综合时找不到 aurora_0 或 ila_aurora0检查 Sources 窗口中是否真实存在对应的 .xci而不是只写了一个同名 Verilog 实例。正确层次中应能看到textIP├─ aurora_0├─ aurora_1├─ ila_aurora0└─ ila_aurora1右键 IP 执行 Generate Output Products再重新综合。14.6 Hardware Manager 找不到设备这与 Aurora 逻辑无关应检查- 开发板是否上电- JTAG 下载线是否连接- 板上启动/JTAG 模式跳帽- Xilinx Cable Driver- hw_server 是否被异常进程占用- Windows 设备管理器是否识别下载器。14.7 ILA 找不到或时钟停止ILA 本身也需要稳定采样时钟。本工程 ILA 使用 Aurora user_clk。如果 GT 没有正确产生 tx_out_clkILA 可能无法正常工作。这时可以先- 用 LED 查看基础状态- 检查参考时钟和复位- 临时添加一个由稳定 init_clk 驱动的小 ILA只观察已经同步到 init_clk 域的单比特状态。不要用 init_clk ILA 直接抓取未同步的 16 位 Aurora 数据总线。十五、为什么本工程不用 10 Gb/s本文的目的首先是学习- 两个 Aurora IP 独立控制- GTX 时钟与复位- AXI4-Stream 握手- 双向奇偶数数据校验- ILA 在线调试。3.125 Gb/s 已经足够验证完整链路且 8B/10B 后单方向理论有效数据率达到 2.5 Gb/s。更高线速不是只修改一个数字就结束还要重新确认- 器件速度等级- CPLL/QPLL 选择- Aurora IP 允许的参考时钟组合- PCB 高速链路质量- SFP 模块速率范围- 光模块 CDR- 发送预加重与接收均衡- 实现后的时序和 GT 状态。先在较低速率把结构、协议和调试方法完全跑通再提高速率是更适合初学者的学习路线。十六、进一步扩展完成本文实验后可以继续做以下练习1. 把固定计数器改成 PRBS 数据发生器和检查器2. 增加错误计数器而不仅是一个粘滞错误位3. 统计有效吞吐率和 tready 拉低次数4. 增加 AXI Stream FIFO连接用户业务模块5. 测试光纤拔插后的自动重连6. 增加 VIO在线控制复位、回环和发送使能7. 在确认模块和 PCB 支持后逐步提高 Line Rate8. 将两个 SFP 分别连接到两块 FPGA 板验证真正的板间通信。七、总结本实验最重要的结论有四点1. 两个独立光口应该使用两个独立 Aurora IP每个 IP 配置 1 Lane2. Lane Width2 表示 2 字节、16 位用户数据不表示两个 GTX Lane3. 3.125 Gb/s 经过 8B/10B 后有效载荷为 2.5 Gb/s对应 16 bit × 156.25 MHz4. 两个 Aurora user_clk 属于不同相位的时钟域应分别使用两个 ILA 抓取。最终工程实现了- SFP0 发送偶数、接收并校验奇数- SFP1 发送奇数、接收并校验偶数- 两个 Aurora 链路独立全双工工作- LED 显示链路与错误- 双 ILA 分别观察两个用户时钟域- 综合、布局布线和时序检查全部通过。希望这篇文章能帮助刚接触 Kintex-7、GTX、SFP 和 Aurora 的同学少走一些弯路。
返回列表