// SPDX-License-Identifier: CERN-OHL-S-2.0 /* Copyright (c) 2026 FPGA Ninja, LLC Authors: - Alex Forencich */ `resetall `timescale 1ns / 1ps `default_nettype none /* * Pyrite flashing support for UltraScale+ PCIe VPD and QSPI flash */ module pyrite_pcie_us_vpd_qspi # ( parameter logic [7:0] VPD_CAP_ID = 8'h03, parameter logic [7:0] VPD_CAP_OFFSET = 8'hB0, parameter logic [7:0] VPD_CAP_NEXT = 8'h00, // FW ID parameter FPGA_ID = 32'hDEADBEEF, parameter FW_ID = 32'h00000000, parameter FW_VER = 32'h000_01_000, parameter BOARD_ID = 32'h1234_0000, parameter BOARD_VER = 32'h001_00_000, parameter BUILD_DATE = 32'd602976000, parameter GIT_HASH = 32'h5f87c2e8, parameter RELEASE_INFO = 32'h00000000, // Flash parameter logic [3:0] FLASH_SEG_COUNT = 2, parameter logic [3:0] FLASH_SEG_DEFAULT = 1, parameter logic [3:0] FLASH_SEG_FALLBACK = 0, parameter logic [31:0] FLASH_SEG0_SIZE = 32'h00000000, parameter FLASH_DATA_W = 4, parameter logic FLASH_DUAL_QSPI = 1'b1 ) ( input wire logic clk, input wire logic rst, /* * PCIe */ input wire logic cfg_ext_read_received, input wire logic cfg_ext_write_received, input wire logic [9:0] cfg_ext_register_number, input wire logic [7:0] cfg_ext_function_number, input wire logic [31:0] cfg_ext_write_data, input wire logic [3:0] cfg_ext_write_byte_enable, output wire logic [31:0] cfg_ext_read_data, output wire logic cfg_ext_read_data_valid, /* * QSPI flash */ output wire logic fpga_boot, output wire logic qspi_clk, input wire logic [FLASH_DATA_W-1:0] qspi_0_dq_i, output wire logic [FLASH_DATA_W-1:0] qspi_0_dq_o, output wire logic [FLASH_DATA_W-1:0] qspi_0_dq_oe, output wire logic qspi_0_cs, input wire logic [FLASH_DATA_W-1:0] qspi_1_dq_i, output wire logic [FLASH_DATA_W-1:0] qspi_1_dq_o, output wire logic [FLASH_DATA_W-1:0] qspi_1_dq_oe, output wire logic qspi_1_cs ); taxi_apb_if #( .DATA_W(32), .ADDR_W(15) ) vpd_apb(); taxi_pcie_us_vpd #( .CAP_ID(VPD_CAP_ID), .CAP_OFFSET(VPD_CAP_OFFSET), .CAP_NEXT(VPD_CAP_NEXT) ) vpd_cap_inst ( .clk(clk), .rst(rst), /* * APB interface for VPD address space */ .m_apb(vpd_apb), /* * Interface to Ultrascale PCIe IP core */ .cfg_ext_read_received(cfg_ext_read_received), .cfg_ext_write_received(cfg_ext_write_received), .cfg_ext_register_number(cfg_ext_register_number), .cfg_ext_function_number(cfg_ext_function_number), .cfg_ext_write_data(cfg_ext_write_data), .cfg_ext_write_byte_enable(cfg_ext_write_byte_enable), .cfg_ext_read_data(cfg_ext_read_data), .cfg_ext_read_data_valid(cfg_ext_read_data_valid) ); taxi_apb_if #( .DATA_W(32), .ADDR_W(14) ) vpd_apb_int[2](); taxi_apb_interconnect_1s #( .M_CNT(2), .ADDR_W(15), .M_REGIONS(1), .M_BASE_ADDR('0), .M_ADDR_W({2{{1{{32'd14}}}}}), .M_SECURE({2{1'b0}}) ) vpd_apb_intercon ( .clk(clk), .rst(rst), /* * APB slave interface */ .s_apb(vpd_apb), /* * APB master interface */ .m_apb(vpd_apb_int) ); taxi_apb_ram #( .ADDR_W(11), .PIPELINE_OUTPUT(1) ) vpd_ram_inst ( .clk(clk), .rst(rst), /* * AXI4-Lite slave interface */ .s_apb(vpd_apb_int[0]) ); logic vpd_apb_pready_reg = 1'b0; logic [31:0] vpd_apb_prdata_reg = '0; logic fpga_boot_reg = 1'b0; logic qspi_clk_reg = 1'b0; logic qspi_0_cs_reg = 1'b1; logic [FLASH_DATA_W-1:0] qspi_0_dq_o_reg = '0; logic [FLASH_DATA_W-1:0] qspi_0_dq_oe_reg = '0; logic qspi_1_cs_reg = 1'b1; logic [FLASH_DATA_W-1:0] qspi_1_dq_o_reg = '0; logic [FLASH_DATA_W-1:0] qspi_1_dq_oe_reg = '0; assign vpd_apb_int[1].pready = vpd_apb_pready_reg; assign vpd_apb_int[1].prdata = vpd_apb_prdata_reg; assign vpd_apb_int[1].pslverr = 1'b0; assign vpd_apb_int[1].pruser = '0; assign vpd_apb_int[1].pbuser = '0; assign fpga_boot = fpga_boot_reg; assign qspi_clk = qspi_clk_reg; assign qspi_0_cs = qspi_0_cs_reg; assign qspi_0_dq_o = qspi_0_dq_o_reg; assign qspi_0_dq_oe = qspi_0_dq_oe_reg; assign qspi_1_cs = FLASH_DUAL_QSPI ? qspi_1_cs_reg : 1'b1; assign qspi_1_dq_o = FLASH_DUAL_QSPI ? qspi_1_dq_o_reg : '0; assign qspi_1_dq_oe = FLASH_DUAL_QSPI ? qspi_1_dq_oe_reg : '0; always_ff @(posedge clk) begin vpd_apb_pready_reg <= 1'b0; if (vpd_apb_int[1].penable && vpd_apb_int[1].psel && !vpd_apb_pready_reg) begin vpd_apb_pready_reg <= 1'b1; vpd_apb_prdata_reg <= '0; if (vpd_apb_int[1].pwrite) begin case (8'({vpd_apb_int[1].paddr >> 2, 2'b00})) // FW ID 8'h0C: begin // FW ID: FPGA JTAG ID fpga_boot_reg <= vpd_apb_int[1].pwdata == 32'hFEE1DEAD; end // QSPI flash 8'h4C: begin // SPI flash ctrl: format fpga_boot_reg <= vpd_apb_int[1].pwdata == 32'hFEE1DEAD; end 8'h50: begin // SPI flash ctrl: control 0 if (vpd_apb_int[1].pstrb[0]) begin qspi_0_dq_o_reg <= vpd_apb_int[1].pwdata[3:0]; end if (vpd_apb_int[1].pstrb[1]) begin qspi_0_dq_oe_reg <= vpd_apb_int[1].pwdata[11:8]; end if (vpd_apb_int[1].pstrb[2]) begin qspi_clk_reg <= vpd_apb_int[1].pwdata[16]; qspi_0_cs_reg <= vpd_apb_int[1].pwdata[17]; end end 8'h54: begin // SPI flash ctrl: control 1 if (FLASH_DUAL_QSPI) begin if (vpd_apb_int[1].pstrb[0]) begin qspi_1_dq_o_reg <= vpd_apb_int[1].pwdata[3:0]; end if (vpd_apb_int[1].pstrb[1]) begin qspi_1_dq_oe_reg <= vpd_apb_int[1].pwdata[11:8]; end if (vpd_apb_int[1].pstrb[2]) begin qspi_clk_reg <= vpd_apb_int[1].pwdata[16]; qspi_1_cs_reg <= vpd_apb_int[1].pwdata[17]; end end end default: begin end endcase end case (8'({vpd_apb_int[1].paddr >> 2, 2'b00})) // FW ID 8'h00: vpd_apb_prdata_reg <= 32'hffffffff; // FW ID: Type 8'h04: vpd_apb_prdata_reg <= 32'h000_01_000; // FW ID: Version 8'h08: vpd_apb_prdata_reg <= 32'h40; // FW ID: Next header 8'h0C: vpd_apb_prdata_reg <= FPGA_ID; // FW ID: FPGA JTAG ID 8'h10: vpd_apb_prdata_reg <= FW_ID; // FW ID: Firmware ID 8'h14: vpd_apb_prdata_reg <= FW_VER; // FW ID: Firmware version 8'h18: vpd_apb_prdata_reg <= BOARD_ID; // FW ID: Board ID 8'h1C: vpd_apb_prdata_reg <= BOARD_VER; // FW ID: Board version 8'h20: vpd_apb_prdata_reg <= BUILD_DATE; // FW ID: Build date 8'h24: vpd_apb_prdata_reg <= GIT_HASH; // FW ID: Git commit hash 8'h28: vpd_apb_prdata_reg <= RELEASE_INFO; // FW ID: Release info // QSPI flash 8'h40: vpd_apb_prdata_reg <= 32'h0000C120; // SPI flash ctrl: Type 8'h44: vpd_apb_prdata_reg <= 32'h000_01_000; // SPI flash ctrl: Version 8'h48: vpd_apb_prdata_reg <= 0; // SPI flash ctrl: Next header 8'h4C: begin // SPI flash ctrl: format vpd_apb_prdata_reg[3:0] <= FLASH_SEG_COUNT; // configuration vpd_apb_prdata_reg[7:4] <= FLASH_SEG_DEFAULT; // default segment vpd_apb_prdata_reg[11:8] <= FLASH_SEG_FALLBACK; // fallback segment vpd_apb_prdata_reg[31:12] <= 20'(FLASH_SEG0_SIZE >> 12); // first segment size end 8'h50: begin // SPI flash ctrl: control 0 vpd_apb_prdata_reg[3:0] <= qspi_0_dq_i; vpd_apb_prdata_reg[11:8] <= qspi_0_dq_oe; vpd_apb_prdata_reg[16] <= qspi_clk; vpd_apb_prdata_reg[17] <= qspi_0_cs; end 8'h54: begin // SPI flash ctrl: control 1 if (FLASH_DUAL_QSPI) begin vpd_apb_prdata_reg[3:0] <= qspi_1_dq_i; vpd_apb_prdata_reg[11:8] <= qspi_1_dq_oe; vpd_apb_prdata_reg[16] <= qspi_clk; vpd_apb_prdata_reg[17] <= qspi_1_cs; end end default: begin end endcase end if (rst) begin vpd_apb_pready_reg <= 1'b0; fpga_boot_reg <= 1'b0; qspi_clk_reg <= 1'b0; qspi_0_cs_reg <= 1'b1; qspi_0_dq_o_reg <= '0; qspi_0_dq_oe_reg <= '0; qspi_1_cs_reg <= 1'b1; qspi_1_dq_o_reg <= '0; qspi_1_dq_oe_reg <= '0; end end endmodule `resetall