Fpga部分 2 PC Ie Ip集成 Xilinx Phy
PCIe SerDes 全流程实战
FPGA 部分——PCIe IP 集成 Xilinx PHY
1 SystemVerilog 集成(快速验证,非最佳实践)
- 原有 PCIe IP 与 PHY 已在
pcie_iip_subsystem.sv
中完成集成,需要在该文件中修改与 PHY 相关的代码。 - 新增
rst_sync
模块,用于将复位信号同步到 PHY 时钟域。 - 新增
ltssm_en_gen
模块,在 PHY 复位完成后启动 LTSSM 链路训练。 - 时钟路径采用
IBUFDS_GTE4
与BUFG_GT
进行处理。
2 VCS 仿真
- 在用于数字设计的虚拟机中安装 Vivado 2023.1。
- 在 Vivado 中执行
compile_simlib -simulator vcs
以生成 VCS 所需的仿真库。 - 将生成的仿真库路径添加到
pcie_rtl.f
和Makefile
中。
3 Vivado 综合 / 布局布线 / 上板调试
- 配置 XDC 约束:包括时钟频率、复位、SerDes、时钟引脚以及电压等。
- 在综合与布局布线阶段,通过 Schematic 核对网表是否与 SystemVerilog 描述保持一致;注意 Vivado 可能对代码进行裁剪与优化。
- 关注 Warning 与时序报告。本项目采用 125 MHz PHY 时钟,但工具识别为 250 MHz,部分时序告警可能为误报,可暂时忽略。
- 若上板运行出现异常,可借助 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截图