Fpga部分 2 PC Ie Ip集成 Xilinx Phy

PCIe SerDes 全流程实战

FPGA 部分——PCIe IP 集成 Xilinx PHY

1 SystemVerilog 集成(快速验证,非最佳实践)

  1. 原有 PCIe IP 与 PHY 已在 pcie_iip_subsystem.sv 中完成集成,需要在该文件中修改与 PHY 相关的代码。
  2. 新增 rst_sync 模块,用于将复位信号同步到 PHY 时钟域。
  3. 新增 ltssm_en_gen 模块,在 PHY 复位完成后启动 LTSSM 链路训练。
  4. 时钟路径采用 IBUFDS_GTE4BUFG_GT 进行处理。

2 VCS 仿真

  1. 在用于数字设计的虚拟机中安装 Vivado 2023.1。
  2. 在 Vivado 中执行 compile_simlib -simulator vcs 以生成 VCS 所需的仿真库。
  3. 将生成的仿真库路径添加到 pcie_rtl.fMakefile 中。

3 Vivado 综合 / 布局布线 / 上板调试

  1. 配置 XDC 约束:包括时钟频率、复位、SerDes、时钟引脚以及电压等。
  2. 在综合与布局布线阶段,通过 Schematic 核对网表是否与 SystemVerilog 描述保持一致;注意 Vivado 可能对代码进行裁剪与优化。
  3. 关注 Warning 与时序报告。本项目采用 125 MHz PHY 时钟,但工具识别为 250 MHz,部分时序告警可能为误报,可暂时忽略。
  4. 若上板运行出现异常,可借助 Vivado ILA 进行定位与排查。

4 以下是PCIE PHY实例化代码

    // [新增] Clocking and Reset Logic for FPGA from the working version
    //----------------------------------------------------------------
    // Clock assignments using pclk from Xilinx PHY
    // pclk (125MHz) is the main clock for the core and AXI interface
    assign core_clk = pclk;
    assign core_clk_ug = pclk;
    assign muxd_aux_clk = pclk;
    assign muxd_aux_clk_g = pclk;
    assign radm_clk_g = pclk;
    
    // Reset for PHY is driven directly by perst_n
    assign phy_rst_n = perst_n;
    // Tie-off this perst_n to the core, as we will generate a new controlled reset
    assign clkrst_perst_n = perst_n;

    // This is the CRITICAL reset generation logic
    //-------------------------------------------------
    wire phystatus_rst; // This signal comes from the PHY instance below
    
    // 1. Synchronize the PHY status reset signal to the pclk domain
    wire phystatus_rst_sync;
    rst_sync #(.STAGES(2)) u_sync_phystatus (
       .clk         (pclk),
       .rst_n_async (~phystatus_rst), // PHY uses active-high reset for this status flag
       .rst_n_sync  (phystatus_rst_sync)
    );

    // 2. Generate the controlled reset for the PCIe Core and AXI interfaces
    localparam integer CTRL_RST_CYC = 50;
    reg        ctrl_rst_n   = 1'b0;
    reg [6:0]  ctrl_cnt     = '0;

    always @(posedge pclk) begin
       if (!phystatus_rst_sync) begin // Wait for PHY to be ready
          ctrl_rst_n <= 1'b0;         // Keep core/AXI in reset
          ctrl_cnt   <= '0;
       end
       else if (ctrl_cnt < CTRL_RST_CYC-1) begin // PHY is ready, start counting
          ctrl_cnt   <= ctrl_cnt + 1'b1;
          ctrl_rst_n <= 1'b0;         // Keep core/AXI in reset during countdown
       end
       else begin // Countdown finished
          ctrl_rst_n <= 1'b1;         // Release the reset
       end
    end

    // 3. Drive all core and AXI resets from this controlled signal
    assign core_rst_n        = ctrl_rst_n;
    assign pwr_rst_n         = ctrl_rst_n;
    assign sticky_rst_n      = ctrl_rst_n;
    assign non_sticky_rst_n  = ctrl_rst_n;
    assign mstr_aresetn      = ctrl_rst_n; // [关键] AXI Master 复位由主控制器复位驱动
    assign slv_aresetn       = ctrl_rst_n; // [关键] AXI Slave 复位也一样
    
    // Tie off unused clock/reset related signals
    assign aux_clk_active = 1'b0;
    assign wake_ref_rst_n = perst_n;
    assign en_muxd_aux_clk_g = 1'b1;
    assign en_radm_clk_g = 1'b1;

    // [新增] Xilinx PHY and Clocking Primitives Instantiation
    //----------------------------------------------------------------
    wire refclk_gt;
    wire refclk_div2;
    wire refclk_bufg;
    
    IBUFDS_GTE4 #(
        .REFCLK_EN_TX_PATH(1'b0)
    ) u_ibufds_gt (
        .I     (refclk_p),
        .IB    (refclk_n),
        .CEB   (1'b0),
        .O     (refclk_gt),
        .ODIV2 (refclk_div2)
    );

    BUFG_GT u_bufg_refclk (
        .I       (refclk_div2),
        .CE      (1'b1),
        .CEMASK  (1'b0),
        .CLR     (1'b0),
        .CLRMASK (1'b0),
        .DIV     (3'd0),
        .O       (refclk_bufg)
    );

    wire [63:0] phy_rxdata;
    wire [1:0]  phy_rxdatak;
    wire [0:0]  phy_rxdata_valid;
    wire [1:0]  phy_rxstart_block;
    wire [1:0]  phy_rxsync_header;
    wire [0:0]  phy_rxvalid;
    wire [0:0]  phy_phystatus;
    wire [0:0]  phy_rxelecidle;
    wire [2:0]  phy_rxstatus;
    wire gt_gtpowergood;

     pcie_phy_0 u_phy (
        .phy_refclk       (refclk_bufg),
        .phy_gtrefclk     (refclk_gt),
        .phy_rst_n        (phy_rst_n),
        .phy_txdata       ({48'b0, mac_phy_txdata[15:0]}),
        .phy_txdatak      ({6'b0, mac_phy_txdatak[1:0]}),
        .phy_txdata_valid (mac_phy_txdatavalid[0]),
        .phy_txstart_block(1'b0),
        .phy_txsync_header(2'b00),
        .phy_rxp          (rxp[0]),
        .phy_rxn          (rxn[0]),
        .phy_txdetectrx   (mac_phy_txdetectrx_loopback[0]),
        .phy_txelecidle   (mac_phy_txelecidle[0]),
        .phy_txcompliance (mac_phy_txcompliance[0]),
        .phy_rxpolarity   (mac_phy_rxpolarity[0]),
        .phy_powerdown    (mac_phy_powerdown[1:0]),
        .phy_rate         (2'b00),
        .phy_txmargin     (3'b000),
        .phy_txswing      (1'b0),
        .phy_txdeemph     (1'b0),
        .phy_pclk         (pclk), // <-- 125MHz pclk output
        .phy_txp          (txp[0]),
        .phy_txn          (txn[0]),
        .phy_rxdata       (phy_rxdata),
        .phy_rxdatak      (phy_rxdatak),
        .phy_rxdata_valid (phy_rxdata_valid),
        .phy_rxvalid      (phy_rxvalid),
        .phy_phystatus    (phy_phystatus),
        .phy_phystatus_rst(phystatus_rst), // <-- This is the key status signal
        .phy_rxelecidle   (phy_rxelecidle),
        .phy_rxstatus     (phy_rxstatus),
        .gt_gtpowergood   (gt_gtpowergood)
    );

    // [新增] PIPE Interface assignments
    assign phy_mac_rxdata        = {{(NL*PHY_NB*PIPE_DATA_WD-16){1'b0}}, phy_rxdata[15:0]};
    assign phy_mac_rxdatak       = phy_rxdatak[1:0];
    assign phy_mac_rxvalid       = phy_rxvalid;
    assign phy_mac_rxstatus      = phy_rxstatus;
    assign phy_mac_phystatus     = phy_phystatus;
    assign phy_mac_rxelecidle    = phy_rxelecidle;
    assign phy_mac_rxdatavalid   = phy_rxdata_valid;
    assign phy_mac_rxstandbystatus = 1'b0;

    // [新增] LTSSM enable generator
    ltssm_en_gen #(
      .DELAY_CYCLES(12_500)   
    ) u_ltssm_enable (
      .pclk            (pclk),
      .perst_n         (ctrl_rst_n),
      .app_ltssm_enable(app_ltssm_enable)
    );

5 Vivado截图

本地图片