From 2c4073c4c169f9d301cd9a47342c40fe527cefe4 Mon Sep 17 00:00:00 2001 From: fengkehan Date: Thu, 23 Jul 2026 10:18:13 +0800 Subject: [PATCH 1/2] fpga_diff: add Hejian UVHS flow and XiangShan support Add the reusable Hejian UVHS FPGA DiffTest build/runtime flow, preserve NutShell integration, and support the board-validated single-FPGA XiangShan configuration at 25 MHz. --- fpga_diff/.gitignore | 21 + fpga_diff/Makefile | 50 +- fpga_diff/README.md | 24 + fpga_diff/src/rtl/common/core_def_xdma.sv | 629 +++++++++- fpga_diff/src/rtl/common/fpga_top_debug.sv | 106 +- .../src/rtl/common/uvhs_axi64_to_axi256.sv | 211 ++++ .../src/rtl/common/uvhs_axilite_cdc_bridge.sv | 308 +++++ .../src/rtl/common/uvhs_blackbox_stubs.v | 314 +++++ fpga_diff/src/rtl/kmh/SimTop_wrapper.sv | 36 +- fpga_diff/src/tcl/common/AXI_bridge.tcl | 15 +- fpga_diff/tools/generate_reports.tcl | 3 - .../tools/report_pcie_route_evidence.tcl | 99 ++ fpga_diff/tools/report_post_route_cdc.tcl | 212 ++++ fpga_diff/user_script/.gitignore | 3 + fpga_diff/user_script/hw_run_download.tcl | 85 ++ fpga_diff/uvhs/Makefile | 367 ++++++ fpga_diff/uvhs/README.md | 76 ++ fpga_diff/uvhs/assemble_uvhs.tcl | 157 +++ fpga_diff/uvhs/assign_pin_nutshell_f2.tcl | 118 ++ fpga_diff/uvhs/async_clocks.tcl | 203 ++++ fpga_diff/uvhs/backend_run.tcl | 1046 +++++++++++++++++ fpga_diff/uvhs/check_flow_tools.sh | 92 ++ fpga_diff/uvhs/check_modules.sh | 56 + fpga_diff/uvhs/export_vivado_ip.tcl | 363 ++++++ fpga_diff/uvhs/filelist.awk | 5 + fpga_diff/uvhs/flow.md | 205 ++++ fpga_diff/uvhs/frontend_run.tcl | 273 +++++ fpga_diff/uvhs/hejian_pcie_x4_env.sh | 310 +++++ fpga_diff/uvhs/make_compat/csh | 14 + fpga_diff/uvhs/make_compat/make | 2 + fpga_diff/uvhs/make_compat/sh | 2 + fpga_diff/uvhs/partition.tcl | 3 + fpga_diff/uvhs/patch_uvsyn_shell.sh | 29 + fpga_diff/uvhs/setenv.local.example.sh | 28 + fpga_diff/uvhs/setenv.sh | 111 ++ fpga_diff/uvhs/status.sh | 53 + fpga_diff/uvhs/timing_common.tcl | 25 + fpga_diff/uvhs/tools/README.md | 9 + .../uvhs/tools/build/patch_nutshell_cdc.sh | 48 + fpga_diff/uvhs/uv_shell_exec_compat.sh | 20 + fpga_diff/uvhs/uvhs_preflight_status.sh | 82 ++ fpga_diff/uvhs/uvhs_tagged_runtime.sh | 306 +++++ 42 files changed, 6007 insertions(+), 112 deletions(-) create mode 100644 fpga_diff/src/rtl/common/uvhs_axi64_to_axi256.sv create mode 100644 fpga_diff/src/rtl/common/uvhs_axilite_cdc_bridge.sv create mode 100644 fpga_diff/src/rtl/common/uvhs_blackbox_stubs.v create mode 100644 fpga_diff/tools/report_pcie_route_evidence.tcl create mode 100644 fpga_diff/tools/report_post_route_cdc.tcl create mode 100644 fpga_diff/user_script/.gitignore create mode 100644 fpga_diff/user_script/hw_run_download.tcl create mode 100644 fpga_diff/uvhs/Makefile create mode 100644 fpga_diff/uvhs/README.md create mode 100644 fpga_diff/uvhs/assemble_uvhs.tcl create mode 100644 fpga_diff/uvhs/assign_pin_nutshell_f2.tcl create mode 100644 fpga_diff/uvhs/async_clocks.tcl create mode 100644 fpga_diff/uvhs/backend_run.tcl create mode 100755 fpga_diff/uvhs/check_flow_tools.sh create mode 100755 fpga_diff/uvhs/check_modules.sh create mode 100644 fpga_diff/uvhs/export_vivado_ip.tcl create mode 100644 fpga_diff/uvhs/filelist.awk create mode 100644 fpga_diff/uvhs/flow.md create mode 100644 fpga_diff/uvhs/frontend_run.tcl create mode 100755 fpga_diff/uvhs/hejian_pcie_x4_env.sh create mode 100755 fpga_diff/uvhs/make_compat/csh create mode 100755 fpga_diff/uvhs/make_compat/make create mode 100755 fpga_diff/uvhs/make_compat/sh create mode 100644 fpga_diff/uvhs/partition.tcl create mode 100755 fpga_diff/uvhs/patch_uvsyn_shell.sh create mode 100644 fpga_diff/uvhs/setenv.local.example.sh create mode 100644 fpga_diff/uvhs/setenv.sh create mode 100755 fpga_diff/uvhs/status.sh create mode 100644 fpga_diff/uvhs/timing_common.tcl create mode 100644 fpga_diff/uvhs/tools/README.md create mode 100755 fpga_diff/uvhs/tools/build/patch_nutshell_cdc.sh create mode 100755 fpga_diff/uvhs/uv_shell_exec_compat.sh create mode 100755 fpga_diff/uvhs/uvhs_preflight_status.sh create mode 100755 fpga_diff/uvhs/uvhs_tagged_runtime.sh diff --git a/fpga_diff/.gitignore b/fpga_diff/.gitignore index daea8b10..700bd102 100644 --- a/fpga_diff/.gitignore +++ b/fpga_diff/.gitignore @@ -4,6 +4,27 @@ !*.vh !*.xdc !*.tcl +.tmp_*.tcl +tools/write_debug_probes.tcl +uvhs/assign_pin_native_nutshell_f2.tcl +uvhs/probe.tcl +uvhs/setenv.local.sh fpga_*/ +logs/ src/tcl/cpu_files.tcl +src/tcl/chi_files.tcl + +# UVHS generated working trees and local configuration +uvhs_*/ +runtime_uvhs_*/ +uvhs/*.local.sh + +# FPGA build outputs +*.bit +*.ltx +*.dcp +*.rtdb +*.rpt +*.rpx +hw.dat/ diff --git a/fpga_diff/Makefile b/fpga_diff/Makefile index 9f3d2ee4..a06392e2 100755 --- a/fpga_diff/Makefile +++ b/fpga_diff/Makefile @@ -5,14 +5,45 @@ RTL_INCLUDE ?= FPGA_BIT_HOME ?= WORKLOAD ?= AXI_WIDTH ?= 64 +PLATFORM ?= U2.2 # Host-trigger ILA capture is opt-in. Keep the trigger mark_debug net in RTL, # but avoid instantiating the ILA core by default to reduce routing pressure. ENABLE_ILA ?= 0 ILA_DEPTH ?= 16384 XDMA_LINK_WIDTH ?= X4 +XDMA_ENABLE_PF0_BAR1 ?= 1 +# Expected values for the checked Hejian XDMA EP x4 DCP used by the UVHS flow: +# BAR0 512 KiB, BAR1 64 KiB, endpoint 10ee:9048. They are carried into the +# Hejian scripts and host probes; do not treat them as a request to patch the +# Vivado xdma_ep Tcl source. +XDMA_AXILITE_MASTER_SCALE ?= Kilobytes +XDMA_AXILITE_MASTER_SIZE ?= 512 +XDMA_EXPECTED_VENDOR ?= 0x10ee +XDMA_EXPECTED_DEVICE ?= 0x9048 +XDMA_EXPECTED_BAR0_SIZE ?= 0x80000 +XDMA_EXPECTED_BAR1_SIZE ?= 0x10000 +UVHS_BASE_ASSEMBLE_FILE ?= +UVHS_BASE_ASSEMBLE_SHA256 ?= 0e44f3ced8e5628a6f9b60a36d15194f03f6eddefcf29d0221a8abf140ecb18b +UVHS_XDMA_EP_DCP ?= +UVHS_XDMA_EP_DCP_SHA256 ?= e40002b4ccab73d4d7a1398630f59428add0a64b9f33d284d3789595bb791b32 +UVHS_XDMA_EP_EXPECTED_VIVADO ?= 2024.2 +UVHS_ALLOW_DCP_VIVADO_MISMATCH ?= 0 +UVHS_DESIGN_NAME ?= VU19P_X4 +UVHS_TARGET_BOARD ?= B0 +UVHS_TARGET_PACK ?= $(UVHS_TARGET_BOARD) +UVHS_TARGET_FPGA ?= F2 +UVHS_TARGET_FPGA_LOWER ?= $(shell printf '%s.%s' '$(UVHS_TARGET_PACK)' '$(UVHS_TARGET_FPGA)' | tr '[:upper:]' '[:lower:]') +UVHS_FPGA_COUNT ?= 1 +UVHS_CPU_CLK_PERIOD_NS ?= 40 +UVHS_CPU_DEBUG_CLK ?= 1 +UVHS_PNR_STRATEGY ?= Strategy_uv_high_fanout_explore +UVHS_COMPILE_STRATEGY_NUM ?= 1 +UVHS_COMPILE_STRATEGY0 ?= uv_high_fanout_explore +UVHS_COMPILE_STRATEGY1 ?= +UVHS_COMPILE_STRATEGY2 ?= VIVADO_JOBS ?= DDR_RANK_WIDTH ?= 2 -export ENABLE_ILA ILA_DEPTH XDMA_LINK_WIDTH VIVADO_JOBS DDR_RANK_WIDTH +export PLATFORM ENABLE_ILA ILA_DEPTH XDMA_LINK_WIDTH XDMA_ENABLE_PF0_BAR1 XDMA_AXILITE_MASTER_SCALE XDMA_AXILITE_MASTER_SIZE XDMA_EXPECTED_VENDOR XDMA_EXPECTED_DEVICE XDMA_EXPECTED_BAR0_SIZE XDMA_EXPECTED_BAR1_SIZE UVHS_BASE_ASSEMBLE_FILE UVHS_BASE_ASSEMBLE_SHA256 UVHS_XDMA_EP_DCP UVHS_XDMA_EP_DCP_SHA256 UVHS_XDMA_EP_EXPECTED_VIVADO UVHS_ALLOW_DCP_VIVADO_MISMATCH UVHS_DESIGN_NAME UVHS_TARGET_BOARD UVHS_TARGET_PACK UVHS_TARGET_FPGA UVHS_TARGET_FPGA_LOWER UVHS_FPGA_COUNT UVHS_CPU_CLK_PERIOD_NS UVHS_CPU_DEBUG_CLK UVHS_PNR_STRATEGY UVHS_COMPILE_STRATEGY_NUM UVHS_COMPILE_STRATEGY0 UVHS_COMPILE_STRATEGY1 UVHS_COMPILE_STRATEGY2 VIVADO_JOBS DDR_RANK_WIDTH # Supported CPU parameters: # kmh # nutshell @@ -23,7 +54,19 @@ PRJ_NAME = fpga_$(CPU)$(if $(strip $(SUFFIX)),-$(strip $(SUFFIX)),) PRJ_DIR ?= $(ENV_SCRIPTS_HOME)/$(PRJ_NAME) PRJ ?= $(PRJ_DIR)/$(PRJ_NAME).xpr -.PHONY: bitstream vivado dump_ila write_jtag_flash update_core_flist +UVHS_SUBMAKE = $(MAKE) -f $(CURDIR)/uvhs/Makefile ROOT_DIR="$(CURDIR)" + +UVHS_FORWARD_TARGETS := \ + uvhs_prepare uvhs_export_vivado_ip \ + uvhs_show_uvw_axi4_to_ddr4 uvhs_filelist uvhs_check_modules \ + uvhs_tools_check uvhs_frontend uvhs_backend uvhs_all \ + uvhs_clean uvhs_env uvhs_preflight uvhs_hejian_pcie_x4_env \ + uvhs_hejian_pcie_x4_preflight uvhs_hejian_pcie_x4_nutshell_all \ + uvhs_hejian_pcie_x4_xiangshan_all \ + uvhs_status uvhs_check_timing_clean uvhs_export_ltx \ + uvhs_package_bitstream + +.PHONY: bitstream vivado dump_ila write_jtag_flash update_core_flist $(UVHS_FORWARD_TARGETS) # Get Vivado version VIVADO_VERSION := $(shell vivado -version 2>/dev/null | head -1 | grep -o '[0-9]\{4\}\.[0-9]' || echo "unknown") @@ -43,6 +86,9 @@ bitstream: update_core_flist: @./tools/update_core_flist.sh "$(CORE_DIR)" -- $(RTL_INCLUDE) +$(UVHS_FORWARD_TARGETS): + $(UVHS_SUBMAKE) $@ + # Launch Vivado with FPGA project configuration vivado: check_vivado_version vivado -mode batch -source src/tcl/common/xs_uart.tcl -tclargs --cpu $(CPU) --project_name $(PRJ_NAME) --vivado_version $(VIVADO_VERSION) diff --git a/fpga_diff/README.md b/fpga_diff/README.md index ec464c50..97ce1f03 100755 --- a/fpga_diff/README.md +++ b/fpga_diff/README.md @@ -48,4 +48,28 @@ FPGA_DDR_LOAD_CMD="bash -lc ' \ ./fpga-host --diff -i .bin ``` +HJ/UVHS FPGA Flow +================= +The Hejian NutShell source-flow index is [uvhs/README.md](uvhs/README.md); +the canonical build, signoff, runtime, FPGA-host, and cleanup runbook is +[uvhs/flow.md](uvhs/flow.md). + +A typical build starts with: + +```shell +make uvhs_tools_check +make uvhs_hejian_pcie_x4_preflight CORE_DIR=/path/to/NutShell +make uvhs_hejian_pcie_x4_nutshell_all \ + CORE_DIR=/path/to/NutShell SUFFIX= +make uvhs_package_bitstream CPU=nutshell \ + UVHS_WORK_DIR=/path/to/fpga_diff_uvhs_nutshell- +``` + +Copy `uvhs/setenv.local.example.sh` to the ignored `setenv.local.sh` for +machine-specific tools and licenses. The caller supplies `CORE_DIR`, verified +UVHS DDR/XDMA IP assets, and a unique build suffix. Program the resulting +`hw.dat` through the vendor runtime using `user_script/hw_run_download.tcl`. +The normal `fpga-host` path loads workloads through H2C. Use +`uvhs_tagged_runtime.sh` so runtime sessions and cleanup remain exact-tag +scoped; keep board-specific debug helpers local. diff --git a/fpga_diff/src/rtl/common/core_def_xdma.sv b/fpga_diff/src/rtl/common/core_def_xdma.sv index 8956db12..b6f11dda 100755 --- a/fpga_diff/src/rtl/common/core_def_xdma.sv +++ b/fpga_diff/src/rtl/common/core_def_xdma.sv @@ -54,6 +54,7 @@ module core_def ( output [1:0] uhs1_drv_sth , output uhs1_swvolt_en , output sd_led_control , +`ifndef UVHS_UVW_AXI4_TO_DDR4 output [`CONFIG_RANK_WIDTH-1:0] DDR_CK_T , output [`CONFIG_RANK_WIDTH-1:0] DDR_CK_C , output [`CONFIG_RANK_WIDTH-1:0] DDR_CKE , @@ -68,6 +69,7 @@ module core_def ( inout [63:0] DDR_DQ , inout [7:0] DDR_DQS_T , inout [7:0] DDR_DQS_C , +`endif //==JTAG @@ -102,8 +104,6 @@ module core_def ( ); // Unbind useless output port {{{ -assign cpu_rd_qspi_valid = 0; -assign cpu_wr_ddr_valid = 0; assign uart1_sout = 0; assign uart2_sout = 0; assign sd_card_clk_out = 0; @@ -737,6 +737,67 @@ wire [1:0] gmac_m_arlock ; wire [3:0] gmac_m_arcache ; wire [2:0] gmac_m_arprot ; wire gmac_m_rready ; +`elsif UVHS_SOC_ADAPT +wire [39:0] gmac_m_awaddr ; +wire [39:0] gmac_m_araddr ; +wire gmac_m_awready ; +wire gmac_m_wready ; +wire [0:0] gmac_m_bid ; +wire [1:0] gmac_m_bresp ; +wire gmac_m_bvalid ; +wire gmac_m_arready ; +wire [0:0] gmac_m_rid ; +wire [1:0] gmac_m_rresp ; +wire [31:0] gmac_m_rdata ; +wire gmac_m_rvalid ; +wire gmac_m_rlast ; +wire [7:0] gmac_m_awlen ; +wire [0:0] gmac_m_awid ; +wire [1:0] gmac_m_awburst ; +wire gmac_m_awvalid ; +wire [2:0] gmac_m_awsize ; +wire gmac_m_awlock ; +wire [3:0] gmac_m_awcache ; +wire [2:0] gmac_m_awprot ; +wire [31:0] gmac_m_wdata ; +wire [3:0] gmac_m_wstrb ; +wire gmac_m_wlast ; +wire gmac_m_wvalid ; +wire gmac_m_bready ; +wire [7:0] gmac_m_arlen ; +wire [0:0] gmac_m_arid ; +wire [1:0] gmac_m_arburst ; +wire gmac_m_arvalid ; +wire [2:0] gmac_m_arsize ; +wire gmac_m_arlock ; +wire [3:0] gmac_m_arcache ; +wire [2:0] gmac_m_arprot ; +wire gmac_m_rready ; + +assign gmac_m_awaddr = 40'b0; +assign gmac_m_awlen = 8'b0; +assign gmac_m_awid = 1'b0; +assign gmac_m_awburst = 2'b0; +assign gmac_m_awvalid = 1'b0; +assign gmac_m_awsize = 3'b0; +assign gmac_m_awlock = 1'b0; +assign gmac_m_awcache = 4'b0; +assign gmac_m_awprot = 3'b0; +assign gmac_m_wdata = 32'b0; +assign gmac_m_wstrb = 4'b0; +assign gmac_m_wlast = 1'b0; +assign gmac_m_wvalid = 1'b0; +assign gmac_m_bready = 1'b1; +assign gmac_m_araddr = 40'b0; +assign gmac_m_arlen = 8'b0; +assign gmac_m_arid = 1'b0; +assign gmac_m_arburst = 2'b0; +assign gmac_m_arvalid = 1'b0; +assign gmac_m_arsize = 3'b0; +assign gmac_m_arlock = 1'b0; +assign gmac_m_arcache = 4'b0; +assign gmac_m_arprot = 3'b0; +assign gmac_m_rready = 1'b1; `endif `ifdef XS_UART wire uart2_penable ; @@ -898,16 +959,16 @@ wire [58:0] cpu_int ; // 11-26 add sd 2 int wire [104:0] cpu_pll_config ; assign cpu_int = { - 0 , - 0 , - 0 , + 1'b0, + 1'b0, + 1'b0, sd_wakeup_int , sd_int , pcie1_int , //`ifdef XS_XDMA - 0 , - 0 , - 0 , + 1'b0, + 1'b0, + 1'b0, //`endif dp_de_int , dp_se_int , @@ -1124,6 +1185,7 @@ wire [7:0] br2cfg_wstrb; wire [0:0] br2cfg_wvalid; wire [31:0] XDMA_AXI_LITE_awaddr; + wire [2:0] XDMA_AXI_LITE_awprot; wire XDMA_AXI_LITE_awvalid; wire XDMA_AXI_LITE_awready; wire [31:0] XDMA_AXI_LITE_wdata; @@ -1134,6 +1196,7 @@ wire [0:0] br2cfg_wvalid; wire XDMA_AXI_LITE_bvalid; wire XDMA_AXI_LITE_bready; wire [31:0] XDMA_AXI_LITE_araddr; + wire [2:0] XDMA_AXI_LITE_arprot; wire XDMA_AXI_LITE_arvalid; wire XDMA_AXI_LITE_arready; wire [31:0] XDMA_AXI_LITE_rdata; @@ -1141,6 +1204,24 @@ wire [0:0] br2cfg_wvalid; wire XDMA_AXI_LITE_rvalid; wire XDMA_AXI_LITE_rready; + wire [31:0] difftest_cfg_axilite_awaddr; + wire difftest_cfg_axilite_awvalid; + wire difftest_cfg_axilite_awready; + wire [31:0] difftest_cfg_axilite_wdata; + wire [3:0] difftest_cfg_axilite_wstrb; + wire difftest_cfg_axilite_wvalid; + wire difftest_cfg_axilite_wready; + wire [1:0] difftest_cfg_axilite_bresp; + wire difftest_cfg_axilite_bvalid; + wire difftest_cfg_axilite_bready; + wire [31:0] difftest_cfg_axilite_araddr; + wire difftest_cfg_axilite_arvalid; + wire difftest_cfg_axilite_arready; + wire [31:0] difftest_cfg_axilite_rdata; + wire [1:0] difftest_cfg_axilite_rresp; + wire difftest_cfg_axilite_rvalid; + wire difftest_cfg_axilite_rready; + wire difftest_to_host_axis_tready_io; wire difftest_to_host_axis_tvalid_io; wire difftest_to_host_axis_tready; @@ -1155,30 +1236,41 @@ wire [0:0] br2cfg_wvalid; wire difftest_from_host_axis_tlast; wire difftest_clock_enable; wire inter_soc_clk; + wire inter_soc_sync_rstn; wire inter_rtc_clk; wire io_host_reset; wire io_host_diff_enable; - (*mark_debug = "true"*) wire io_host_ila_trigger; + (*mark_debug = "true"*) wire io_host_ila_trigger; wire clock_enable; wire sys_rstn_io; wire cpu_rstn_io; wire difftest_startup_ready_pcie; wire difftest_startup_done_pcie; wire cpu_rstn_pcie; + reg cpu_rstn_pcie_src; wire io_host_diff_enable_pcie; wire xdma_link_up_pcie; reg [19:0] difftest_startup_wait_pcie; reg difftest_stream_enable_pcie; - + wire difftest_c2h_rstn; + wire difftest_clock_gate_enable = difftest_clock_enable; wire difftest_pcie_clock; + wire pcie_ep_lnk_up_raw; + (* ASYNC_REG = "TRUE" *) reg [1:0] pcie_lnk_sync; assign sys_rstn_io = sys_rstn & ~io_host_reset; assign cpu_rstn_io = cpu_rstn & ~io_host_reset; + assign difftest_c2h_rstn = cpu_rstn_pcie & difftest_stream_enable_pcie; + assign pcie_ep_lnk_up = pcie_lnk_sync[1]; + + always @(posedge sys_clk_i) begin + if (!sys_rstn) cpu_rstn_pcie_src <= 1'b0; + else cpu_rstn_pcie_src <= cpu_rstn_io; + end - reg [1:0] pcie_lnk_sync; always @(posedge sys_clk_i) begin if (!sys_rstn) pcie_lnk_sync <= 2'b00; - else pcie_lnk_sync <= {pcie_lnk_sync[0], pcie_ep_lnk_up}; + else pcie_lnk_sync <= {pcie_lnk_sync[0], pcie_ep_lnk_up_raw}; end wire xdma_link_up = pcie_lnk_sync[1]; @@ -1188,7 +1280,7 @@ wire [0:0] br2cfg_wvalid; .INIT(1'b0) ) difftest_cpu_rstn_pcie_sync ( .clk (difftest_pcie_clock), - .async_in (cpu_rstn_io), + .async_in (cpu_rstn_pcie_src), .sync_out (cpu_rstn_pcie) ); @@ -1229,22 +1321,121 @@ wire [0:0] br2cfg_wvalid; assign difftest_to_host_axis_tready = difftest_to_host_axis_tready_io & difftest_stream_enable_pcie; assign difftest_to_host_axis_tvalid_io = difftest_to_host_axis_tvalid & difftest_stream_enable_pcie; + wire difftest_axil_cdc_s_resetn = xdma_link_up_pcie; + wire difftest_axil_cdc_m_resetn = sys_rstn & xdma_link_up; + + uvhs_axilite_cdc_bridge #( + .ADDR_WIDTH (32), + .DATA_WIDTH (32) + ) difftest_cfg_axilite_cdc ( + .s_clk (difftest_pcie_clock), + .s_resetn (difftest_axil_cdc_s_resetn), + .s_awaddr (XDMA_AXI_LITE_awaddr), + .s_awprot (XDMA_AXI_LITE_awprot), + .s_awvalid (XDMA_AXI_LITE_awvalid), + .s_awready (XDMA_AXI_LITE_awready), + .s_wdata (XDMA_AXI_LITE_wdata), + .s_wstrb (XDMA_AXI_LITE_wstrb), + .s_wvalid (XDMA_AXI_LITE_wvalid), + .s_wready (XDMA_AXI_LITE_wready), + .s_bresp (XDMA_AXI_LITE_bresp), + .s_bvalid (XDMA_AXI_LITE_bvalid), + .s_bready (XDMA_AXI_LITE_bready), + .s_araddr (XDMA_AXI_LITE_araddr), + .s_arprot (XDMA_AXI_LITE_arprot), + .s_arvalid (XDMA_AXI_LITE_arvalid), + .s_arready (XDMA_AXI_LITE_arready), + .s_rdata (XDMA_AXI_LITE_rdata), + .s_rresp (XDMA_AXI_LITE_rresp), + .s_rvalid (XDMA_AXI_LITE_rvalid), + .s_rready (XDMA_AXI_LITE_rready), + + .m_clk (sys_clk_i), + .m_resetn (difftest_axil_cdc_m_resetn), + .m_awaddr (difftest_cfg_axilite_awaddr), + .m_awprot (), + .m_awvalid (difftest_cfg_axilite_awvalid), + .m_awready (difftest_cfg_axilite_awready), + .m_wdata (difftest_cfg_axilite_wdata), + .m_wstrb (difftest_cfg_axilite_wstrb), + .m_wvalid (difftest_cfg_axilite_wvalid), + .m_wready (difftest_cfg_axilite_wready), + .m_bresp (difftest_cfg_axilite_bresp), + .m_bvalid (difftest_cfg_axilite_bvalid), + .m_bready (difftest_cfg_axilite_bready), + .m_araddr (difftest_cfg_axilite_araddr), + .m_arprot (), + .m_arvalid (difftest_cfg_axilite_arvalid), + .m_arready (difftest_cfg_axilite_arready), + .m_rdata (difftest_cfg_axilite_rdata), + .m_rresp (difftest_cfg_axilite_rresp), + .m_rvalid (difftest_cfg_axilite_rvalid), + .m_rready (difftest_cfg_axilite_rready) + ); + + reg cpu_ddr_read_seen; + reg difftest_axis_valid_seen; + reg difftest_axis_valid_seen_pcie; + (* ASYNC_REG = "TRUE" *) reg [1:0] difftest_axis_valid_sync; + wire cpu_ddr_read_fire = cpu2ddr_m2s_arvalid & cpu2ddr_s2m_arready; + + always @(posedge difftest_pcie_clock) begin + if (!difftest_c2h_rstn) + difftest_axis_valid_seen_pcie <= 1'b0; + else + difftest_axis_valid_seen_pcie <= difftest_axis_valid_seen_pcie | difftest_to_host_axis_tvalid_io; + end + + always @(posedge sys_clk_i) begin + if (!cpu_rstn_io) begin + cpu_ddr_read_seen <= 1'b0; + difftest_axis_valid_seen <= 1'b0; + difftest_axis_valid_sync <= 2'b00; + end else begin + cpu_ddr_read_seen <= cpu_ddr_read_seen | cpu_ddr_read_fire; + difftest_axis_valid_sync <= {difftest_axis_valid_sync[0], difftest_axis_valid_seen_pcie}; + difftest_axis_valid_seen <= difftest_axis_valid_seen | difftest_axis_valid_sync[1]; + end + end + + assign cpu_rd_qspi_valid = cpu_ddr_read_seen; + assign cpu_wr_ddr_valid = difftest_axis_valid_seen; + + wire [`CONFIG_DIFFTEST_HOST_AXIS_WIDTH-1:0] xdma_s00_axis_tdata; + wire [`CONFIG_DIFFTEST_HOST_AXIS_BYTES-1:0] xdma_s00_axis_tkeep; + wire xdma_s00_axis_tlast; + wire xdma_s00_axis_tvalid; + wire xdma_m00_axis_tready; + wire xdma_cpu_clk; + wire xdma_cpu_rstn; + + assign xdma_s00_axis_tdata = difftest_to_host_axis_tdata; + assign xdma_s00_axis_tkeep = difftest_to_host_axis_tkeep; + assign xdma_s00_axis_tlast = difftest_to_host_axis_tlast; + assign xdma_s00_axis_tvalid = difftest_to_host_axis_tvalid_io; + assign xdma_m00_axis_tready = difftest_from_host_axis_tready; + // Match the verified UVHS XDMA x4 endpoint bring-up: keep the XDMA user + // side in the clock domain exported by the XDMA DCP and do not let SoC reset + // hold the endpoint user logic while the host probes BARs. + assign xdma_cpu_clk = difftest_pcie_clock; + assign xdma_cpu_rstn = 1'b1; + xdma_ep xdma_ep_i( - .cpu_clk (sys_clk_i), - .cpu_rstn (sys_rstn), - .S00_AXIS_0_tdata (difftest_to_host_axis_tdata), - .S00_AXIS_0_tkeep (difftest_to_host_axis_tkeep), - .S00_AXIS_0_tlast (difftest_to_host_axis_tlast), + .cpu_clk (xdma_cpu_clk), + .cpu_rstn (xdma_cpu_rstn), + .S00_AXIS_0_tdata (xdma_s00_axis_tdata), + .S00_AXIS_0_tkeep (xdma_s00_axis_tkeep), + .S00_AXIS_0_tlast (xdma_s00_axis_tlast), .S00_AXIS_0_tready (difftest_to_host_axis_tready_io), - .S00_AXIS_0_tvalid (difftest_to_host_axis_tvalid_io), + .S00_AXIS_0_tvalid (xdma_s00_axis_tvalid), .M00_AXIS_0_tdata (difftest_from_host_axis_tdata), .M00_AXIS_0_tkeep (difftest_from_host_axis_tkeep), .M00_AXIS_0_tlast (difftest_from_host_axis_tlast), - .M00_AXIS_0_tready (difftest_from_host_axis_tready), + .M00_AXIS_0_tready (xdma_m00_axis_tready), .M00_AXIS_0_tvalid (difftest_from_host_axis_tvalid), .XDMA_AXI_LITE_awaddr (XDMA_AXI_LITE_awaddr), - .XDMA_AXI_LITE_awprot (3'b000), + .XDMA_AXI_LITE_awprot (XDMA_AXI_LITE_awprot), .XDMA_AXI_LITE_awvalid(XDMA_AXI_LITE_awvalid), .XDMA_AXI_LITE_awready(XDMA_AXI_LITE_awready), .XDMA_AXI_LITE_wdata (XDMA_AXI_LITE_wdata), @@ -1255,7 +1446,7 @@ wire [0:0] br2cfg_wvalid; .XDMA_AXI_LITE_bvalid (XDMA_AXI_LITE_bvalid), .XDMA_AXI_LITE_bready (XDMA_AXI_LITE_bready), .XDMA_AXI_LITE_araddr (XDMA_AXI_LITE_araddr), - .XDMA_AXI_LITE_arprot (3'b000), + .XDMA_AXI_LITE_arprot (XDMA_AXI_LITE_arprot), .XDMA_AXI_LITE_arvalid(XDMA_AXI_LITE_arvalid), .XDMA_AXI_LITE_arready(XDMA_AXI_LITE_arready), .XDMA_AXI_LITE_rdata (XDMA_AXI_LITE_rdata), @@ -1270,22 +1461,79 @@ wire [0:0] br2cfg_wvalid; .pci_exp_txp(pci_ep_txp), .pcie_ep_gt_ref_clk_n(pcie_ep_gt_ref_clk_n), .pcie_ep_gt_ref_clk_p(pcie_ep_gt_ref_clk_p), - .pcie_ep_lnk_up(pcie_ep_lnk_up), + .pcie_ep_lnk_up(pcie_ep_lnk_up_raw), .pcie_ep_perstn(pcie_ep_perstn) ); DifftestClockGate SOC_CLK_CTRL( .CK (sys_clk_i), - .E ((difftest_clock_enable & xdma_link_up ) || ~io_host_diff_enable || ~sys_rstn_io || ~cpu_rstn_io), + .E ((difftest_clock_gate_enable & xdma_link_up ) || ~io_host_diff_enable || ~sys_rstn_io || ~cpu_rstn_io), .Q (inter_soc_clk) ); + wire difftest_clock_gate_enable_tmclk; + wire xdma_link_up_tmclk; + wire io_host_reset_tmclk; + wire io_host_diff_enable_tmclk; + wire sys_rstn_tmclk; + wire cpu_rstn_tmclk; + + RST_SYNC #(.INIT(1'b0)) rtc_clock_gate_enable_sync ( + .clk (tmclk), + .async_in (difftest_clock_gate_enable), + .sync_out (difftest_clock_gate_enable_tmclk) + ); + + RST_SYNC #(.INIT(1'b0)) rtc_xdma_link_up_sync ( + .clk (tmclk), + .async_in (xdma_link_up), + .sync_out (xdma_link_up_tmclk) + ); + + RST_SYNC #(.INIT(1'b1)) rtc_host_reset_sync ( + .clk (tmclk), + .async_in (io_host_reset), + .sync_out (io_host_reset_tmclk) + ); + + RST_SYNC #(.INIT(1'b0)) rtc_host_diff_enable_sync ( + .clk (tmclk), + .async_in (io_host_diff_enable), + .sync_out (io_host_diff_enable_tmclk) + ); + + RST_SYNC #(.INIT(1'b0)) rtc_sys_rstn_sync ( + .clk (tmclk), + .async_in (sys_rstn), + .sync_out (sys_rstn_tmclk) + ); + + RST_SYNC #(.INIT(1'b0)) rtc_cpu_rstn_sync ( + .clk (tmclk), + .async_in (cpu_rstn), + .sync_out (cpu_rstn_tmclk) + ); + + wire sys_rstn_io_tmclk = sys_rstn_tmclk & ~io_host_reset_tmclk; + wire cpu_rstn_io_tmclk = cpu_rstn_tmclk & ~io_host_reset_tmclk; + DifftestClockGate RTC_CLK_CTRL( .CK (tmclk), - .E ((difftest_clock_enable & xdma_link_up) || ~io_host_diff_enable || ~sys_rstn_io || ~cpu_rstn_io ), + .E ((difftest_clock_gate_enable_tmclk & xdma_link_up_tmclk) + || ~io_host_diff_enable_tmclk || ~sys_rstn_io_tmclk || ~cpu_rstn_io_tmclk), .Q (inter_rtc_clk) ); + RST_SYNC #( + .SYNC_STAGES(3), + .PIPELINE_STAGES(1), + .INIT(1'b0) + ) inter_soc_rstn_sync ( + .clk (inter_soc_clk), + .async_in (axi_bclk_sync_rstn), + .sync_out (inter_soc_sync_rstn) + ); + xilnx_crg xilnx_crg( .sys_clk (sys_clk_i ), .dev_clk (dev_clk_i ), @@ -1335,6 +1583,246 @@ mode_ctrl U_MODE_CTRL( .scan_mode ( ) ); +`ifdef UVHS_UVW_AXI4_TO_DDR4 +wire [13:0] uvhs_ddr_awid; +wire [33:0] uvhs_ddr_awaddr; +wire [7:0] uvhs_ddr_awlen; +wire [2:0] uvhs_ddr_awsize; +wire [1:0] uvhs_ddr_awburst; +wire [0:0] uvhs_ddr_awlock; +wire [3:0] uvhs_ddr_awcache; +wire [2:0] uvhs_ddr_awprot; +wire [3:0] uvhs_ddr_awqos; +wire [3:0] uvhs_ddr_awregion; +wire uvhs_ddr_awvalid; +wire uvhs_ddr_awready; +wire [255:0] uvhs_ddr_wdata; +wire [31:0] uvhs_ddr_wstrb; +wire uvhs_ddr_wlast; +wire uvhs_ddr_wvalid; +wire uvhs_ddr_wready; +wire [13:0] uvhs_ddr_bid; +wire [1:0] uvhs_ddr_bresp; +wire uvhs_ddr_bvalid; +wire uvhs_ddr_bready; +wire [13:0] uvhs_ddr_arid; +wire [33:0] uvhs_ddr_araddr; +wire [7:0] uvhs_ddr_arlen; +wire [2:0] uvhs_ddr_arsize; +wire [1:0] uvhs_ddr_arburst; +wire [0:0] uvhs_ddr_arlock; +wire [3:0] uvhs_ddr_arcache; +wire [2:0] uvhs_ddr_arprot; +wire [3:0] uvhs_ddr_arqos; +wire [3:0] uvhs_ddr_arregion; +wire uvhs_ddr_arvalid; +wire uvhs_ddr_arready; +wire [255:0] uvhs_ddr_rdata; +wire [13:0] uvhs_ddr_rid; +wire [1:0] uvhs_ddr_rresp; +wire uvhs_ddr_rlast; +wire uvhs_ddr_rvalid; +wire uvhs_ddr_rready; +wire uvhs_ddr_user_clk; +wire uvhs_ddr_user_rst; +wire [255:0] uvhs_ddr_sysbus_i; +wire [255:0] uvhs_ddr_sysbus_o; + +assign uvhs_ddr_sysbus_i = 256'b0; +assign init_calib_complete = rstn_sw4 & ~uvhs_ddr_user_rst; + +`ifdef CONFIG_USE_XSCORE_AXI +`ifdef CPU_NUTSHELL +uvhs_axi64_to_axi256 u_uvhs_axi64_to_axi256 ( + .clk (inter_soc_clk), + .rstn (rstn_sw4), + .s_axi_awid (cpu2ddr_m2s_awid_mix[13:0]), + .s_axi_awaddr (cpu2ddr_m2s_awaddr_mix), + .s_axi_awlen (cpu2ddr_m2s_awlen), + .s_axi_awsize (cpu2ddr_m2s_awsize), + .s_axi_awburst (cpu2ddr_m2s_awburst), + .s_axi_awlock (cpu2ddr_m2s_awlock), + .s_axi_awcache (cpu2ddr_m2s_awcache), + .s_axi_awprot (cpu2ddr_m2s_awprot), + .s_axi_awqos (cpu2ddr_m2s_awqos), + .s_axi_awvalid (cpu2ddr_m2s_awvalid), + .s_axi_awready (cpu2ddr_s2m_awready), + .s_axi_wdata (cpu2ddr_m2s_wdata[63:0]), + .s_axi_wstrb (cpu2ddr_m2s_wstrb[7:0]), + .s_axi_wlast (cpu2ddr_m2s_wlast), + .s_axi_wvalid (cpu2ddr_m2s_wvalid), + .s_axi_wready (cpu2ddr_s2m_wready), + .s_axi_bid (cpu2ddr_s2m_bid[13:0]), + .s_axi_bresp (cpu2ddr_s2m_bresp), + .s_axi_bvalid (cpu2ddr_s2m_bvalid), + .s_axi_bready (cpu2ddr_m2s_bready), + .s_axi_arid (cpu2ddr_m2s_arid_mix[13:0]), + .s_axi_araddr (cpu2ddr_m2s_araddr_mix), + .s_axi_arlen (cpu2ddr_m2s_arlen), + .s_axi_arsize (cpu2ddr_m2s_arsize), + .s_axi_arburst (cpu2ddr_m2s_arburst), + .s_axi_arlock (cpu2ddr_m2s_arlock), + .s_axi_arcache (cpu2ddr_m2s_arcache), + .s_axi_arprot (cpu2ddr_m2s_arprot), + .s_axi_arqos (cpu2ddr_m2s_arqos), + .s_axi_arvalid (cpu2ddr_m2s_arvalid), + .s_axi_arready (cpu2ddr_s2m_arready), + .s_axi_rid (cpu2ddr_s2m_rid[13:0]), + .s_axi_rdata (cpu2ddr_s2m_rdata[63:0]), + .s_axi_rresp (cpu2ddr_s2m_rresp), + .s_axi_rlast (cpu2ddr_s2m_rlast), + .s_axi_rvalid (cpu2ddr_s2m_rvalid), + .s_axi_rready (cpu2ddr_m2s_rready), + .m_axi_awid (uvhs_ddr_awid), + .m_axi_awaddr (uvhs_ddr_awaddr), + .m_axi_awlen (uvhs_ddr_awlen), + .m_axi_awsize (uvhs_ddr_awsize), + .m_axi_awburst (uvhs_ddr_awburst), + .m_axi_awlock (uvhs_ddr_awlock), + .m_axi_awcache (uvhs_ddr_awcache), + .m_axi_awprot (uvhs_ddr_awprot), + .m_axi_awqos (uvhs_ddr_awqos), + .m_axi_awregion (uvhs_ddr_awregion), + .m_axi_awvalid (uvhs_ddr_awvalid), + .m_axi_awready (uvhs_ddr_awready), + .m_axi_wdata (uvhs_ddr_wdata), + .m_axi_wstrb (uvhs_ddr_wstrb), + .m_axi_wlast (uvhs_ddr_wlast), + .m_axi_wvalid (uvhs_ddr_wvalid), + .m_axi_wready (uvhs_ddr_wready), + .m_axi_bid (uvhs_ddr_bid), + .m_axi_bresp (uvhs_ddr_bresp), + .m_axi_bvalid (uvhs_ddr_bvalid), + .m_axi_bready (uvhs_ddr_bready), + .m_axi_arid (uvhs_ddr_arid), + .m_axi_araddr (uvhs_ddr_araddr), + .m_axi_arlen (uvhs_ddr_arlen), + .m_axi_arsize (uvhs_ddr_arsize), + .m_axi_arburst (uvhs_ddr_arburst), + .m_axi_arlock (uvhs_ddr_arlock), + .m_axi_arcache (uvhs_ddr_arcache), + .m_axi_arprot (uvhs_ddr_arprot), + .m_axi_arqos (uvhs_ddr_arqos), + .m_axi_arregion (uvhs_ddr_arregion), + .m_axi_arvalid (uvhs_ddr_arvalid), + .m_axi_arready (uvhs_ddr_arready), + .m_axi_rid (uvhs_ddr_rid), + .m_axi_rdata (uvhs_ddr_rdata), + .m_axi_rresp (uvhs_ddr_rresp), + .m_axi_rlast (uvhs_ddr_rlast), + .m_axi_rvalid (uvhs_ddr_rvalid), + .m_axi_rready (uvhs_ddr_rready) +); +assign cpu2ddr_s2m_bid[17:14] = 4'b0; +assign cpu2ddr_s2m_rid[17:14] = 4'b0; +`else +assign uvhs_ddr_awid = cpu2ddr_m2s_awid_mix[13:0]; +assign uvhs_ddr_awaddr = cpu2ddr_m2s_awaddr_mix[33:0]; +assign uvhs_ddr_awlen = cpu2ddr_m2s_awlen; +assign uvhs_ddr_awsize = cpu2ddr_m2s_awsize; +assign uvhs_ddr_awburst = cpu2ddr_m2s_awburst; +assign uvhs_ddr_awlock = cpu2ddr_m2s_awlock; +assign uvhs_ddr_awcache = cpu2ddr_m2s_awcache; +assign uvhs_ddr_awprot = cpu2ddr_m2s_awprot; +assign uvhs_ddr_awqos = cpu2ddr_m2s_awqos; +assign uvhs_ddr_awregion = cpu2ddr_m2s_awregion; +assign uvhs_ddr_awvalid = cpu2ddr_m2s_awvalid; +assign cpu2ddr_s2m_awready = uvhs_ddr_awready; +assign uvhs_ddr_wdata = cpu2ddr_m2s_wdata; +assign uvhs_ddr_wstrb = cpu2ddr_m2s_wstrb; +assign uvhs_ddr_wlast = cpu2ddr_m2s_wlast; +assign uvhs_ddr_wvalid = cpu2ddr_m2s_wvalid; +assign cpu2ddr_s2m_wready = uvhs_ddr_wready; +assign cpu2ddr_s2m_bid = {4'b0, uvhs_ddr_bid}; +assign cpu2ddr_s2m_bresp = uvhs_ddr_bresp; +assign cpu2ddr_s2m_bvalid = uvhs_ddr_bvalid; +assign uvhs_ddr_bready = cpu2ddr_m2s_bready; +assign uvhs_ddr_arid = cpu2ddr_m2s_arid_mix[13:0]; +assign uvhs_ddr_araddr = cpu2ddr_m2s_araddr_mix[33:0]; +assign uvhs_ddr_arlen = cpu2ddr_m2s_arlen; +assign uvhs_ddr_arsize = cpu2ddr_m2s_arsize; +assign uvhs_ddr_arburst = cpu2ddr_m2s_arburst; +assign uvhs_ddr_arlock = cpu2ddr_m2s_arlock; +assign uvhs_ddr_arcache = cpu2ddr_m2s_arcache; +assign uvhs_ddr_arprot = cpu2ddr_m2s_arprot; +assign uvhs_ddr_arqos = cpu2ddr_m2s_arqos; +assign uvhs_ddr_arregion = cpu2ddr_m2s_arregion; +assign uvhs_ddr_arvalid = cpu2ddr_m2s_arvalid; +assign cpu2ddr_s2m_arready = uvhs_ddr_arready; +assign cpu2ddr_s2m_rid = {4'b0, uvhs_ddr_rid}; +assign cpu2ddr_s2m_rdata = uvhs_ddr_rdata; +assign cpu2ddr_s2m_rresp = uvhs_ddr_rresp; +assign cpu2ddr_s2m_rlast = uvhs_ddr_rlast; +assign cpu2ddr_s2m_rvalid = uvhs_ddr_rvalid; +assign uvhs_ddr_rready = cpu2ddr_m2s_rready; +`endif +`endif + +uvw_axi4_to_ddr4 U_UVHS_UVW_AXI4_TO_DDR4 ( + .ddr4ip_dut_axi_aclk (inter_soc_clk), + .ddr4ip_dut_axi_aresetn (rstn_sw4), + .ddr4ip_dut_axi_awaddr (uvhs_ddr_awaddr), + .ddr4ip_dut_axi_awburst (uvhs_ddr_awburst), + .ddr4ip_dut_axi_awcache (uvhs_ddr_awcache), + .ddr4ip_dut_axi_awid (uvhs_ddr_awid), + .ddr4ip_dut_axi_awlen (uvhs_ddr_awlen), + .ddr4ip_dut_axi_awlock (uvhs_ddr_awlock), + .ddr4ip_dut_axi_awprot (uvhs_ddr_awprot), + .ddr4ip_dut_axi_awqos (uvhs_ddr_awqos), + .ddr4ip_dut_axi_awready (uvhs_ddr_awready), + .ddr4ip_dut_axi_awregion (uvhs_ddr_awregion), + .ddr4ip_dut_axi_awsize (uvhs_ddr_awsize), + .ddr4ip_dut_axi_awvalid (uvhs_ddr_awvalid), + .ddr4ip_dut_axi_wdata (uvhs_ddr_wdata), + .ddr4ip_dut_axi_wlast (uvhs_ddr_wlast), + .ddr4ip_dut_axi_wready (uvhs_ddr_wready), + .ddr4ip_dut_axi_wstrb (uvhs_ddr_wstrb), + .ddr4ip_dut_axi_wvalid (uvhs_ddr_wvalid), + .ddr4ip_dut_axi_bid (uvhs_ddr_bid), + .ddr4ip_dut_axi_bready (uvhs_ddr_bready), + .ddr4ip_dut_axi_bresp (uvhs_ddr_bresp), + .ddr4ip_dut_axi_bvalid (uvhs_ddr_bvalid), + .ddr4ip_dut_axi_araddr (uvhs_ddr_araddr), + .ddr4ip_dut_axi_arburst (uvhs_ddr_arburst), + .ddr4ip_dut_axi_arcache (uvhs_ddr_arcache), + .ddr4ip_dut_axi_arid (uvhs_ddr_arid), + .ddr4ip_dut_axi_arlen (uvhs_ddr_arlen), + .ddr4ip_dut_axi_arlock (uvhs_ddr_arlock), + .ddr4ip_dut_axi_arprot (uvhs_ddr_arprot), + .ddr4ip_dut_axi_arqos (uvhs_ddr_arqos), + .ddr4ip_dut_axi_arready (uvhs_ddr_arready), + .ddr4ip_dut_axi_arregion (uvhs_ddr_arregion), + .ddr4ip_dut_axi_arsize (uvhs_ddr_arsize), + .ddr4ip_dut_axi_arvalid (uvhs_ddr_arvalid), + .ddr4ip_dut_axi_rdata (uvhs_ddr_rdata), + .ddr4ip_dut_axi_rid (uvhs_ddr_rid), + .ddr4ip_dut_axi_rlast (uvhs_ddr_rlast), + .ddr4ip_dut_axi_rready (uvhs_ddr_rready), + .ddr4ip_dut_axi_rresp (uvhs_ddr_rresp), + .ddr4ip_dut_axi_rvalid (uvhs_ddr_rvalid), + .ddr4ip_dut_axi_aclk_en (1'b1), + .ddr4ip_ddr4_user_clk (uvhs_ddr_user_clk), + .ddr4ip_ddr4_user_rst (uvhs_ddr_user_rst), + .sysbus_ghbd_i (uvhs_ddr_sysbus_i), + .sysbus_ghbd_o (uvhs_ddr_sysbus_o), + .FP_CLK_200M_P (), + .FP_CLK_200M_N (), + .DDR4_DIMM_ACT_N (), + .DDR4_DIMM_A (), + .DDR4_DIMM_BA (), + .DDR4_DIMM_BG (), + .DDR4_DIMM_CK_N (), + .DDR4_DIMM_CK_P (), + .DDR4_DIMM_CKE (), + .DDR4_DIMM_CS_N (), + .DDR4_DIMM_ODT (), + .DDR4_DIMM_RST_B (), + .DDR4_DIMM_DM (), + .DDR4_DIMM_DQ (), + .DDR4_DIMM_DQS_N (), + .DDR4_DIMM_DQS_P () +); +`else jtag_ddr_subsys_wrapper U_JTAG_DDR_SUBSYS( .DDR4_act_n (DDR_ACT_N), .DDR4_adr (DDR_A), @@ -1434,6 +1922,7 @@ jtag_ddr_subsys_wrapper U_JTAG_DDR_SUBSYS( .soc_rstn (rstn_sw4), .calib_complete (init_calib_complete) ); +`endif SimTop_wrapper U_CPU_TOP( .difftest_pcie_clock (difftest_pcie_clock), @@ -1453,23 +1942,24 @@ SimTop_wrapper U_CPU_TOP( .difftest_hostCtrl_reset (io_host_reset), .difftest_hostCtrl_diffEnable (io_host_diff_enable), .difftest_hostCtrl_ilaTrigger (io_host_ila_trigger), - .difftest_cfg_axilite_awaddr (XDMA_AXI_LITE_awaddr), - .difftest_cfg_axilite_awvalid (XDMA_AXI_LITE_awvalid), - .difftest_cfg_axilite_awready (XDMA_AXI_LITE_awready), - .difftest_cfg_axilite_wdata (XDMA_AXI_LITE_wdata), - .difftest_cfg_axilite_wstrb (XDMA_AXI_LITE_wstrb), - .difftest_cfg_axilite_wvalid (XDMA_AXI_LITE_wvalid), - .difftest_cfg_axilite_wready (XDMA_AXI_LITE_wready), - .difftest_cfg_axilite_bresp (XDMA_AXI_LITE_bresp), - .difftest_cfg_axilite_bvalid (XDMA_AXI_LITE_bvalid), - .difftest_cfg_axilite_bready (XDMA_AXI_LITE_bready), - .difftest_cfg_axilite_araddr (XDMA_AXI_LITE_araddr), - .difftest_cfg_axilite_arvalid (XDMA_AXI_LITE_arvalid), - .difftest_cfg_axilite_arready (XDMA_AXI_LITE_arready), - .difftest_cfg_axilite_rdata (XDMA_AXI_LITE_rdata), - .difftest_cfg_axilite_rresp (XDMA_AXI_LITE_rresp), - .difftest_cfg_axilite_rvalid (XDMA_AXI_LITE_rvalid), - .difftest_cfg_axilite_rready (XDMA_AXI_LITE_rready), + .difftest_hostCtrl_enableSquash (), + .difftest_cfg_axilite_awaddr (difftest_cfg_axilite_awaddr), + .difftest_cfg_axilite_awvalid (difftest_cfg_axilite_awvalid), + .difftest_cfg_axilite_awready (difftest_cfg_axilite_awready), + .difftest_cfg_axilite_wdata (difftest_cfg_axilite_wdata), + .difftest_cfg_axilite_wstrb (difftest_cfg_axilite_wstrb), + .difftest_cfg_axilite_wvalid (difftest_cfg_axilite_wvalid), + .difftest_cfg_axilite_wready (difftest_cfg_axilite_wready), + .difftest_cfg_axilite_bresp (difftest_cfg_axilite_bresp), + .difftest_cfg_axilite_bvalid (difftest_cfg_axilite_bvalid), + .difftest_cfg_axilite_bready (difftest_cfg_axilite_bready), + .difftest_cfg_axilite_araddr (difftest_cfg_axilite_araddr), + .difftest_cfg_axilite_arvalid (difftest_cfg_axilite_arvalid), + .difftest_cfg_axilite_arready (difftest_cfg_axilite_arready), + .difftest_cfg_axilite_rdata (difftest_cfg_axilite_rdata), + .difftest_cfg_axilite_rresp (difftest_cfg_axilite_rresp), + .difftest_cfg_axilite_rvalid (difftest_cfg_axilite_rvalid), + .difftest_cfg_axilite_rready (difftest_cfg_axilite_rready), .inter_soc_clk (inter_soc_clk), .sys_rstn_i (cpu_rstn_io ), .tmclk (inter_rtc_clk), @@ -1510,7 +2000,7 @@ SimTop_wrapper U_CPU_TOP( .io_systemjtag_jtag_TDO_driven (io_systemjtag_jtag_TDO_driven), // .io_systemjtag_reset (io_systemjtag_reset), .io_systemjtag_reset (~sys_rstn_io), - .io_sram_config (5'b0 ), + .io_sram_config (16'b0), .dma_core_awready (data_cpu_bridge_s2m_awready), .dma_core_awvalid (data_cpu_bridge_m2s_awvalid), @@ -1606,7 +2096,7 @@ SimTop_wrapper U_CPU_TOP( .mem_core_wlast (cpu2ddr_m2s_wlast), .mem_core_bready (cpu2ddr_m2s_bready), .mem_core_bvalid (cpu2ddr_s2m_bvalid), - .mem_core_bid (cpu2ddr_s2m_bid), + .mem_core_bid (cpu2ddr_s2m_bid[13:0]), .mem_core_bresp (cpu2ddr_s2m_bresp), .mem_core_arready (cpu2ddr_s2m_arready), .mem_core_arvalid (cpu2ddr_m2s_arvalid), @@ -1621,7 +2111,7 @@ SimTop_wrapper U_CPU_TOP( .mem_core_arqos (cpu2ddr_m2s_arqos), .mem_core_rready (cpu2ddr_m2s_rready), .mem_core_rvalid (cpu2ddr_s2m_rvalid), - .mem_core_rid (cpu2ddr_s2m_rid), + .mem_core_rid (cpu2ddr_s2m_rid[13:0]), .mem_core_rdata (cpu2ddr_s2m_rdata), .mem_core_rresp (cpu2ddr_s2m_rresp), .mem_core_rlast (cpu2ddr_s2m_rlast), @@ -1649,6 +2139,7 @@ assign hpm_dig_result = 0; AXI_bridge CFG_AXI_bridge_i (.SYS_INTER_CLK (inter_soc_clk), + .SYS_INTER_ARESETN (inter_soc_sync_rstn), .ACLK (sys_clk_i), .ARESETN (axi_bclk_sync_rstn), @@ -1661,6 +2152,9 @@ AXI_bridge CFG_AXI_bridge_i .S00_AXI_arprot (cpu2cfg_m2s_arprot), .S00_AXI_arqos (cpu2cfg_m2s_arqos), .S00_AXI_arready (cpu2cfg_s2m_arready), +`ifdef UVHS_SOC_ADAPT + .S00_AXI_arregion (4'b0), +`endif .S00_AXI_arsize (cpu2cfg_m2s_arsize), .S00_AXI_arvalid (cpu2cfg_m2s_arvalid), .S00_AXI_awaddr (cpu2cfg_m2s_awaddr), @@ -1672,6 +2166,9 @@ AXI_bridge CFG_AXI_bridge_i .S00_AXI_awprot (cpu2cfg_m2s_awprot), .S00_AXI_awqos (cpu2cfg_m2s_awqos), .S00_AXI_awready (cpu2cfg_s2m_awready), +`ifdef UVHS_SOC_ADAPT + .S00_AXI_awregion (4'b0), +`endif .S00_AXI_awsize (cpu2cfg_m2s_awsize), .S00_AXI_awvalid (cpu2cfg_m2s_awvalid), .S00_AXI_bid (cpu2cfg_s2m_bid), @@ -1698,22 +2195,40 @@ AXI_bridge CFG_AXI_bridge_i .SYS_CFG_APB_pslverr (syscfg_pslverr), .SYS_CFG_APB_pwdata (syscfg_pwdata), .SYS_CFG_APB_pwrite (syscfg_pwrite), +`ifdef UVHS_SOC_ADAPT + .DMAC_CFG_AHB_hrdata (32'b0), + .DMAC_CFG_AHB_hready (1'b1), + .DMAC_CFG_AHB_hresp (1'b0), + .IOMMU_CFG_APB_prdata (32'b0), + .IOMMU_CFG_APB_pready (1'b1), + .IOMMU_CFG_APB_pslverr (1'b0), - .UART_0_baudoutn (), - .UART_0_ctsn (1'b1), - .UART_0_dcdn (1'b0), - .UART_0_ddis (), - .UART_0_dsrn (1'b0), - .UART_0_dtrn (), - .UART_0_out1n (), - .UART_0_out2n (), - .UART_0_ri (1'b1), - .UART_0_rtsn (), +`endif .UART_0_rxd (uart0_sin), - .UART_0_rxrdyn (), .UART_0_txd (uart0_sout), - .UART_0_txrdyn (), .uart0_intc (uart0_int), +`ifdef UVHS_SOC_ADAPT + .UARTLITE_AXI_arready (1'b1), + .UARTLITE_AXI_awready (1'b1), + .UARTLITE_AXI_bid (2'b0), + .UARTLITE_AXI_bresp (2'b0), + .UARTLITE_AXI_bvalid (1'b0), + .UARTLITE_AXI_rdata (64'b0), + .UARTLITE_AXI_rid (2'b0), + .UARTLITE_AXI_rlast (1'b0), + .UARTLITE_AXI_rresp (2'b0), + .UARTLITE_AXI_rvalid (1'b0), + .UARTLITE_AXI_wready (1'b1), + .pcie_cfg_arready (1'b1), + .pcie_cfg_awready (1'b1), + .pcie_cfg_bresp (2'b0), + .pcie_cfg_bvalid (1'b0), + .pcie_cfg_rdata (32'b0), + .pcie_cfg_rlast (1'b0), + .pcie_cfg_rresp (2'b0), + .pcie_cfg_rvalid (1'b0), + .pcie_cfg_wready (1'b1), +`endif .rom_axi_araddr (rom_axi_araddr), .rom_axi_arburst (), diff --git a/fpga_diff/src/rtl/common/fpga_top_debug.sv b/fpga_diff/src/rtl/common/fpga_top_debug.sv index 9cf9bc9c..fbd6c679 100755 --- a/fpga_diff/src/rtl/common/fpga_top_debug.sv +++ b/fpga_diff/src/rtl/common/fpga_top_debug.sv @@ -15,9 +15,9 @@ module fpga_top_debug input clk8_n, input clk7_p, // ddr 80MHz input clk7_n, - input clk6_p, // system 200MHz + input clk6_p, // alternate system 200MHz input clk6_n, - input clk5_p, // debug 25MHz + input clk5_p, // CPU/debug 25MHz input clk5_n, input rstn_sw6, input rstn_sw5, @@ -34,14 +34,7 @@ module fpga_top_debug output uart2_sout, input uart2_sin, `endif - //PCIE - input refclk_p, // pcie 100MHz - input refclk_n, - output PERST_N, - - input refclk2_p, // pcie 100MHz - input refclk2_n, - output PERST2_N, + //PCIE `ifdef XS_XDMA_EP input [`XDMA_PCIE_LANES-1:0] pci_ep_rxn, input [`XDMA_PCIE_LANES-1:0] pci_ep_rxp, @@ -53,6 +46,7 @@ module fpga_top_debug input pcie_ep_perstn, `endif //DDR +`ifndef UVHS_UVW_AXI4_TO_DDR4 output [`CONFIG_RANK_WIDTH-1:0] DDR0_CK_T, output [`CONFIG_RANK_WIDTH-1:0] DDR0_CK_C, output [`CONFIG_RANK_WIDTH-1:0] DDR0_CKE, @@ -67,6 +61,7 @@ module fpga_top_debug inout [63:0] DDR0_DQ, inout [7:0] DDR0_DQS_T, inout [7:0] DDR0_DQS_C, +`endif `ifdef XS_GMAC input RGMII_RXCLK, @@ -108,14 +103,17 @@ wire vio_sw4; wire sys_rstn; wire cpu_setn_buf; wire sys_clk_i, dev_clk_i; +wire cpu_rstn_to_core; +wire rstn_sw4_to_core; +wire rstn_sw5_ctrl; +wire rstn_sw6_ctrl; +wire rstn_sw4_ctrl; wire pcie_rstn; (*mark_debug = "true"*) wire cpu_setn_rflag; (*mark_debug = "true"*) reg cpu_rstn; IBUF sys_rstn_ibuf (.O(sys_rstn), .I(rstn_sw6)); IBUF cpu_rstn_ibuf (.O(cpu_setn_buf), .I(rstn_sw5)); -OBUF pcie_rstn_obuf (.O(PERST_N), .I(vio_sw6)); -OBUF pcie2_rstn_obuf (.O(PERST2_N), .I(vio_sw6)); button_debounce u_cpu_rstn( .clk (sys_clk_i), @@ -133,13 +131,35 @@ always@(posedge sys_clk_i) begin cpu_rstn <= cpu_rstn; end +`ifdef UVHS_UVW_AXI4_TO_DDR4 +assign rstn_sw6_ctrl = sys_rstn; +assign rstn_sw5_ctrl = cpu_setn_buf; +assign rstn_sw4_ctrl = rstn_sw4; +assign cpu_rstn_to_core = rstn_sw5_ctrl; +assign rstn_sw4_to_core = rstn_sw4_ctrl; +`else +assign rstn_sw6_ctrl = sys_rstn && vio_sw6; +assign rstn_sw5_ctrl = cpu_rstn; +assign rstn_sw4_ctrl = rstn_sw4 && vio_sw4; +assign cpu_rstn_to_core = rstn_sw5_ctrl; +assign rstn_sw4_to_core = rstn_sw4_ctrl; +`endif + // Suppose PHY_RESET_B active high. // Although 88E1116R pin10 resetn is active low. // But this pin is on connector, which might not have current by default? -assign PHY_RESET_B = cpu_rstn; +assign PHY_RESET_B = cpu_rstn_to_core; -wire tmclk; wire tmclk_buf; +wire cqetmclk_buf; +wire dbg_clk_buf; + +`ifdef UVHS_NO_XILINX_CLK_PRIMS +assign tmclk_buf = clk8_p; +assign cqetmclk_buf = clk6_p; +assign dbg_clk_buf = clk5_p; +`else +wire tmclk; IBUFGDS ibufgds_tmclk_1MHz ( .I (clk8_p), @@ -153,7 +173,6 @@ BUFG bufg_tmclk ); wire cqetmclk; -wire cqetmclk_buf; IBUFGDS ibufgds_tmclk_200MHz ( .I (clk6_p), @@ -168,7 +187,6 @@ BUFG bufg_cqetmclk ); wire dbg_clk; -wire dbg_clk_buf; IBUFGDS ibufgds_dbgclk_25MHz ( .I (clk5_p), @@ -181,29 +199,20 @@ BUFG bufg_dbgclk .I (dbg_clk), .O (dbg_clk_buf) ); +`endif -wire pcie_sysclk_gt; -wire pcie_sysclk; - - -IBUFDS_GTE4 refclk_ibuf ( - .O(pcie_sysclk_gt), - .ODIV2(pcie_sysclk), - .I(refclk_p), - .CEB(1'b0), - .IB(refclk_n) -); - -IBUFDS_GTE4 refclk2_ibuf ( - .O(pcie2_sysclk_gt), - .ODIV2(pcie2_sysclk), - .I(refclk2_p), - .CEB(1'b0), - .IB(refclk2_n) -); - +`ifdef UVHS_UVW_AXI4_TO_DDR4 +`ifdef UVHS_CPU_DEBUG_CLK +assign sys_clk_i = dbg_clk_buf; +assign dev_clk_i = dbg_clk_buf; +`else +assign sys_clk_i = cqetmclk_buf; +assign dev_clk_i = cqetmclk_buf; +`endif +`else assign sys_clk_i = dbg_clk_buf; assign dev_clk_i = dbg_clk_buf; +`endif vio_0 u_vio( .clk (dbg_clk_buf), @@ -332,9 +341,9 @@ core_def core_def .cpu_wr_ddr_valid (led0), .sys_clk_i (sys_clk_i), .dev_clk_i (dev_clk_i), - .sys_rstn (sys_rstn && vio_sw6), - .cpu_rstn (cpu_rstn), - .rstn_sw4 (rstn_sw4 && vio_sw4), + .sys_rstn (rstn_sw6_ctrl), + .cpu_rstn (cpu_rstn_to_core), + .rstn_sw4 (rstn_sw4_to_core), .dft_lgc_rst_n (1'b1), .dft_se (1'b0), .chip_mode_i (2'b00), // normal mode @@ -394,7 +403,14 @@ core_def core_def .sd_card_wp_in (sdmmc_card_write_prot), .sd_cmd_in (sdmmc_sd_cmd_in), .sd_dat_in (sdmmc_sd_dat_in), - .sd_led_control ( ), + .sd_led_control ( ) +`ifdef XS_GMAC + , +`else +`ifndef UVHS_UVW_AXI4_TO_DDR4 + , +`endif +`endif `ifdef XS_GMAC // gmac @@ -407,10 +423,14 @@ core_def core_def .io_gmac_rx_clk ( io_gmac_rx_clk ), .io_gmac_rxd_vld ( io_gmac_rxd_vld ), .io_gmac_rxd ( io_gmac_rxd ), - .io_gmac_txd ( io_gmac_txd ), + .io_gmac_txd ( io_gmac_txd ) +`ifndef UVHS_UVW_AXI4_TO_DDR4 + , +`endif `endif // ddr +`ifndef UVHS_UVW_AXI4_TO_DDR4 .DDR_CK_T (DDR0_CK_T ), .DDR_CK_C (DDR0_CK_C ), .DDR_CKE (DDR0_CKE ), @@ -424,7 +444,8 @@ core_def core_def .DDR_DM_N (DDR0_DM ), .DDR_DQ (DDR0_DQ ), .DDR_DQS_T (DDR0_DQS_T ), - .DDR_DQS_C (DDR0_DQS_C )/*, + .DDR_DQS_C (DDR0_DQS_C ) + /* .DDR_ZQ (DDR0_ZQ), .gmac_gmii_mdc_o(gmac_gmii_mdc_o), .gmac_gmii_mdo_o(gmac_gmii_mdo_o), @@ -448,6 +469,7 @@ core_def core_def .jtag_tdo(jtag_tdo), */ +`endif ); diff --git a/fpga_diff/src/rtl/common/uvhs_axi64_to_axi256.sv b/fpga_diff/src/rtl/common/uvhs_axi64_to_axi256.sv new file mode 100644 index 00000000..23eaac51 --- /dev/null +++ b/fpga_diff/src/rtl/common/uvhs_axi64_to_axi256.sv @@ -0,0 +1,211 @@ +`timescale 1ns/1ps + +module uvhs_axi64_to_axi256 ( + input wire clk, + input wire rstn, + + input wire [13:0] s_axi_awid, + input wire [39:0] s_axi_awaddr, + input wire [7:0] s_axi_awlen, + input wire [2:0] s_axi_awsize, + input wire [1:0] s_axi_awburst, + input wire s_axi_awlock, + input wire [3:0] s_axi_awcache, + input wire [2:0] s_axi_awprot, + input wire [3:0] s_axi_awqos, + input wire s_axi_awvalid, + output wire s_axi_awready, + input wire [63:0] s_axi_wdata, + input wire [7:0] s_axi_wstrb, + input wire s_axi_wlast, + input wire s_axi_wvalid, + output wire s_axi_wready, + output wire [13:0] s_axi_bid, + output wire [1:0] s_axi_bresp, + output wire s_axi_bvalid, + input wire s_axi_bready, + + input wire [13:0] s_axi_arid, + input wire [39:0] s_axi_araddr, + input wire [7:0] s_axi_arlen, + input wire [2:0] s_axi_arsize, + input wire [1:0] s_axi_arburst, + input wire s_axi_arlock, + input wire [3:0] s_axi_arcache, + input wire [2:0] s_axi_arprot, + input wire [3:0] s_axi_arqos, + input wire s_axi_arvalid, + output wire s_axi_arready, + output wire [13:0] s_axi_rid, + output wire [63:0] s_axi_rdata, + output wire [1:0] s_axi_rresp, + output wire s_axi_rlast, + output wire s_axi_rvalid, + input wire s_axi_rready, + + output wire [13:0] m_axi_awid, + output wire [33:0] m_axi_awaddr, + output wire [7:0] m_axi_awlen, + output wire [2:0] m_axi_awsize, + output wire [1:0] m_axi_awburst, + output wire [0:0] m_axi_awlock, + output wire [3:0] m_axi_awcache, + output wire [2:0] m_axi_awprot, + output wire [3:0] m_axi_awqos, + output wire [3:0] m_axi_awregion, + output wire m_axi_awvalid, + input wire m_axi_awready, + output wire [255:0] m_axi_wdata, + output wire [31:0] m_axi_wstrb, + output wire m_axi_wlast, + output wire m_axi_wvalid, + input wire m_axi_wready, + input wire [13:0] m_axi_bid, + input wire [1:0] m_axi_bresp, + input wire m_axi_bvalid, + output wire m_axi_bready, + + output wire [13:0] m_axi_arid, + output wire [33:0] m_axi_araddr, + output wire [7:0] m_axi_arlen, + output wire [2:0] m_axi_arsize, + output wire [1:0] m_axi_arburst, + output wire [0:0] m_axi_arlock, + output wire [3:0] m_axi_arcache, + output wire [2:0] m_axi_arprot, + output wire [3:0] m_axi_arqos, + output wire [3:0] m_axi_arregion, + output wire m_axi_arvalid, + input wire m_axi_arready, + input wire [13:0] m_axi_rid, + input wire [255:0] m_axi_rdata, + input wire [1:0] m_axi_rresp, + input wire m_axi_rlast, + input wire m_axi_rvalid, + output wire m_axi_rready +); + + reg write_active; + reg write_resp_pending; + reg [1:0] write_lane; + reg [2:0] write_size; + reg [1:0] write_burst; + + reg read_active; + reg [1:0] read_lane; + reg [2:0] read_size; + reg [1:0] read_burst; + + wire [4:0] write_shift = {write_lane, 3'b000}; + wire [4:0] read_shift = {read_lane, 3'b000}; + wire [255:0] shifted_rdata = m_axi_rdata >> (read_shift * 8); + + assign m_axi_awid = s_axi_awid; + assign m_axi_awaddr = s_axi_awaddr[33:0]; + assign m_axi_awlen = s_axi_awlen; + assign m_axi_awsize = s_axi_awsize; + assign m_axi_awburst = s_axi_awburst; + assign m_axi_awlock = s_axi_awlock; + assign m_axi_awcache = s_axi_awcache; + assign m_axi_awprot = s_axi_awprot; + assign m_axi_awqos = s_axi_awqos; + assign m_axi_awregion = 4'b0; + assign m_axi_awvalid = s_axi_awvalid & ~write_active & ~write_resp_pending; + assign s_axi_awready = m_axi_awready & ~write_active & ~write_resp_pending; + + assign m_axi_wdata = {192'b0, s_axi_wdata} << (write_shift * 8); + assign m_axi_wstrb = {24'b0, s_axi_wstrb} << write_shift; + assign m_axi_wlast = s_axi_wlast; + assign m_axi_wvalid = s_axi_wvalid & write_active; + assign s_axi_wready = m_axi_wready & write_active; + + assign s_axi_bid = m_axi_bid; + assign s_axi_bresp = m_axi_bresp; + assign s_axi_bvalid = m_axi_bvalid; + assign m_axi_bready = s_axi_bready; + + assign m_axi_arid = s_axi_arid; + assign m_axi_araddr = s_axi_araddr[33:0]; + assign m_axi_arlen = s_axi_arlen; + assign m_axi_arsize = s_axi_arsize; + assign m_axi_arburst = s_axi_arburst; + assign m_axi_arlock = s_axi_arlock; + assign m_axi_arcache = s_axi_arcache; + assign m_axi_arprot = s_axi_arprot; + assign m_axi_arqos = s_axi_arqos; + assign m_axi_arregion = 4'b0; + assign m_axi_arvalid = s_axi_arvalid & ~read_active; + assign s_axi_arready = m_axi_arready & ~read_active; + + assign s_axi_rid = m_axi_rid; + assign s_axi_rdata = shifted_rdata[63:0]; + assign s_axi_rresp = m_axi_rresp; + assign s_axi_rlast = m_axi_rlast; + assign s_axi_rvalid = m_axi_rvalid & read_active; + assign m_axi_rready = s_axi_rready & read_active; + + function automatic [1:0] next_lane(input [1:0] lane, input [2:0] size, input [1:0] burst); + begin + // Both INCR and WRAP advance the transfer address on every beat. The + // NutShell cache-line port uses WRAP bursts, so keeping its lane fixed + // repeats lane zero after the first 64-bit beat on the 256-bit bus. + if ((burst == 2'b01 || burst == 2'b10) && size >= 3'd3) begin + next_lane = lane + (2'b01 << (size - 3'd3)); + end else begin + next_lane = lane; + end + end + endfunction + + always @(posedge clk) begin + if (!rstn) begin + write_active <= 1'b0; + write_resp_pending <= 1'b0; + write_lane <= 2'b0; + write_size <= 3'b0; + write_burst <= 2'b0; + end else begin + if (s_axi_awvalid && s_axi_awready) begin + write_active <= 1'b1; + write_resp_pending <= 1'b0; + write_lane <= s_axi_awaddr[4:3]; + write_size <= s_axi_awsize; + write_burst <= s_axi_awburst; + end else if (s_axi_wvalid && s_axi_wready) begin + if (s_axi_wlast) begin + write_active <= 1'b0; + write_resp_pending <= 1'b1; + end else begin + write_lane <= next_lane(write_lane, write_size, write_burst); + end + end + + if (s_axi_bvalid && s_axi_bready) begin + write_resp_pending <= 1'b0; + end + end + end + + always @(posedge clk) begin + if (!rstn) begin + read_active <= 1'b0; + read_lane <= 2'b0; + read_size <= 3'b0; + read_burst <= 2'b0; + end else begin + if (s_axi_arvalid && s_axi_arready) begin + read_active <= 1'b1; + read_lane <= s_axi_araddr[4:3]; + read_size <= s_axi_arsize; + read_burst <= s_axi_arburst; + end else if (s_axi_rvalid && s_axi_rready) begin + if (s_axi_rlast) begin + read_active <= 1'b0; + end else begin + read_lane <= next_lane(read_lane, read_size, read_burst); + end + end + end + end + +endmodule diff --git a/fpga_diff/src/rtl/common/uvhs_axilite_cdc_bridge.sv b/fpga_diff/src/rtl/common/uvhs_axilite_cdc_bridge.sv new file mode 100644 index 00000000..3556c472 --- /dev/null +++ b/fpga_diff/src/rtl/common/uvhs_axilite_cdc_bridge.sv @@ -0,0 +1,308 @@ +`timescale 1ns/1ps + +// Single-outstanding AXI4-Lite clock-domain bridge. +// +// Request/response payloads are held stable until the opposite domain returns +// a toggle acknowledgement. Control toggles use three synchronizer stages; +// payload buses use two stages and therefore settle before the third control +// stage is observed. AW and W may arrive independently, as required by AXI4- +// Lite. No timing exception is required for this owned-RTL CDC. +module uvhs_axilite_cdc_bridge #( + parameter integer ADDR_WIDTH = 32, + parameter integer DATA_WIDTH = 32 +) ( + input wire s_clk, + input wire s_resetn, + input wire [ADDR_WIDTH-1:0] s_awaddr, + input wire [2:0] s_awprot, + input wire s_awvalid, + output wire s_awready, + input wire [DATA_WIDTH-1:0] s_wdata, + input wire [DATA_WIDTH/8-1:0] s_wstrb, + input wire s_wvalid, + output wire s_wready, + output wire [1:0] s_bresp, + output wire s_bvalid, + input wire s_bready, + input wire [ADDR_WIDTH-1:0] s_araddr, + input wire [2:0] s_arprot, + input wire s_arvalid, + output wire s_arready, + output wire [DATA_WIDTH-1:0] s_rdata, + output wire [1:0] s_rresp, + output wire s_rvalid, + input wire s_rready, + + input wire m_clk, + input wire m_resetn, + output wire [ADDR_WIDTH-1:0] m_awaddr, + output wire [2:0] m_awprot, + output wire m_awvalid, + input wire m_awready, + output wire [DATA_WIDTH-1:0] m_wdata, + output wire [DATA_WIDTH/8-1:0] m_wstrb, + output wire m_wvalid, + input wire m_wready, + input wire [1:0] m_bresp, + input wire m_bvalid, + output wire m_bready, + output wire [ADDR_WIDTH-1:0] m_araddr, + output wire [2:0] m_arprot, + output wire m_arvalid, + input wire m_arready, + input wire [DATA_WIDTH-1:0] m_rdata, + input wire [1:0] m_rresp, + input wire m_rvalid, + output wire m_rready +); + + localparam integer STRB_WIDTH = DATA_WIDTH / 8; + localparam integer WRITE_PAYLOAD_WIDTH = ADDR_WIDTH + 3 + DATA_WIDTH + STRB_WIDTH; + localparam integer READ_PAYLOAD_WIDTH = ADDR_WIDTH + 3; + + reg [ADDR_WIDTH-1:0] s_awaddr_q; + reg [2:0] s_awprot_q; + reg s_aw_pending; + reg [DATA_WIDTH-1:0] s_wdata_q; + reg [STRB_WIDTH-1:0] s_wstrb_q; + reg s_w_pending; + reg s_write_busy; + reg s_write_req_toggle; + reg [1:0] s_bresp_q; + reg s_bvalid_q; + + reg [ADDR_WIDTH-1:0] s_araddr_q; + reg [2:0] s_arprot_q; + reg s_read_busy; + reg s_read_req_toggle; + reg [DATA_WIDTH-1:0] s_rdata_q; + reg [1:0] s_rresp_q; + reg s_rvalid_q; + + wire s_aw_fire = s_awvalid && s_awready; + wire s_w_fire = s_wvalid && s_wready; + wire s_write_payload_ready = (s_aw_pending || s_aw_fire) && + (s_w_pending || s_w_fire); + + assign s_awready = s_resetn && !s_write_busy && !s_aw_pending; + assign s_wready = s_resetn && !s_write_busy && !s_w_pending; + assign s_bresp = s_bresp_q; + assign s_bvalid = s_bvalid_q; + assign s_arready = s_resetn && !s_read_busy; + assign s_rdata = s_rdata_q; + assign s_rresp = s_rresp_q; + assign s_rvalid = s_rvalid_q; + + wire [WRITE_PAYLOAD_WIDTH-1:0] s_write_payload = { + s_awaddr_q, s_awprot_q, s_wdata_q, s_wstrb_q + }; + wire [READ_PAYLOAD_WIDTH-1:0] s_read_payload = {s_araddr_q, s_arprot_q}; + + reg [1:0] m_write_resp_payload; + reg m_write_resp_toggle; + reg [DATA_WIDTH+1:0] m_read_resp_payload; + reg m_read_resp_toggle; + + (* ASYNC_REG = "TRUE", SHREG_EXTRACT = "NO" *) reg [2:0] s_write_resp_sync; + (* ASYNC_REG = "TRUE", SHREG_EXTRACT = "NO" *) reg [1:0] s_write_resp_data_sync1; + (* ASYNC_REG = "TRUE", SHREG_EXTRACT = "NO" *) reg [1:0] s_write_resp_data_sync2; + reg s_write_resp_seen; + + (* ASYNC_REG = "TRUE", SHREG_EXTRACT = "NO" *) reg [2:0] s_read_resp_sync; + (* ASYNC_REG = "TRUE", SHREG_EXTRACT = "NO" *) reg [DATA_WIDTH+1:0] s_read_resp_data_sync1; + (* ASYNC_REG = "TRUE", SHREG_EXTRACT = "NO" *) reg [DATA_WIDTH+1:0] s_read_resp_data_sync2; + reg s_read_resp_seen; + + always @(posedge s_clk) begin + if (!s_resetn) begin + s_awaddr_q <= {ADDR_WIDTH{1'b0}}; + s_awprot_q <= 3'b0; + s_aw_pending <= 1'b0; + s_wdata_q <= {DATA_WIDTH{1'b0}}; + s_wstrb_q <= {STRB_WIDTH{1'b0}}; + s_w_pending <= 1'b0; + s_write_busy <= 1'b0; + s_write_req_toggle <= 1'b0; + s_bresp_q <= 2'b0; + s_bvalid_q <= 1'b0; + s_araddr_q <= {ADDR_WIDTH{1'b0}}; + s_arprot_q <= 3'b0; + s_read_busy <= 1'b0; + s_read_req_toggle <= 1'b0; + s_rdata_q <= {DATA_WIDTH{1'b0}}; + s_rresp_q <= 2'b0; + s_rvalid_q <= 1'b0; + s_write_resp_sync <= 3'b0; + s_write_resp_data_sync1 <= 2'b0; + s_write_resp_data_sync2 <= 2'b0; + s_write_resp_seen <= 1'b0; + s_read_resp_sync <= 3'b0; + s_read_resp_data_sync1 <= {(DATA_WIDTH+2){1'b0}}; + s_read_resp_data_sync2 <= {(DATA_WIDTH+2){1'b0}}; + s_read_resp_seen <= 1'b0; + end else begin + s_write_resp_sync <= {s_write_resp_sync[1:0], m_write_resp_toggle}; + s_write_resp_data_sync1 <= m_write_resp_payload; + s_write_resp_data_sync2 <= s_write_resp_data_sync1; + s_read_resp_sync <= {s_read_resp_sync[1:0], m_read_resp_toggle}; + s_read_resp_data_sync1 <= m_read_resp_payload; + s_read_resp_data_sync2 <= s_read_resp_data_sync1; + + if (s_aw_fire) begin + s_awaddr_q <= s_awaddr; + s_awprot_q <= s_awprot; + s_aw_pending <= 1'b1; + end + if (s_w_fire) begin + s_wdata_q <= s_wdata; + s_wstrb_q <= s_wstrb; + s_w_pending <= 1'b1; + end + if (!s_write_busy && s_write_payload_ready) begin + s_aw_pending <= 1'b0; + s_w_pending <= 1'b0; + s_write_busy <= 1'b1; + s_write_req_toggle <= ~s_write_req_toggle; + end + if (s_write_busy && !s_bvalid_q && + s_write_resp_sync[2] != s_write_resp_seen) begin + s_write_resp_seen <= s_write_resp_sync[2]; + s_bresp_q <= s_write_resp_data_sync2; + s_bvalid_q <= 1'b1; + end + if (s_bvalid_q && s_bready) begin + s_bvalid_q <= 1'b0; + s_write_busy <= 1'b0; + end + + if (s_arvalid && s_arready) begin + s_araddr_q <= s_araddr; + s_arprot_q <= s_arprot; + s_read_busy <= 1'b1; + s_read_req_toggle <= ~s_read_req_toggle; + end + if (s_read_busy && !s_rvalid_q && + s_read_resp_sync[2] != s_read_resp_seen) begin + s_read_resp_seen <= s_read_resp_sync[2]; + s_rdata_q <= s_read_resp_data_sync2[DATA_WIDTH+1:2]; + s_rresp_q <= s_read_resp_data_sync2[1:0]; + s_rvalid_q <= 1'b1; + end + if (s_rvalid_q && s_rready) begin + s_rvalid_q <= 1'b0; + s_read_busy <= 1'b0; + end + end + end + + (* ASYNC_REG = "TRUE", SHREG_EXTRACT = "NO" *) reg [2:0] m_write_req_sync; + (* ASYNC_REG = "TRUE", SHREG_EXTRACT = "NO" *) reg [WRITE_PAYLOAD_WIDTH-1:0] m_write_payload_sync1; + (* ASYNC_REG = "TRUE", SHREG_EXTRACT = "NO" *) reg [WRITE_PAYLOAD_WIDTH-1:0] m_write_payload_sync2; + reg m_write_req_seen; + reg [ADDR_WIDTH-1:0] m_awaddr_q; + reg [2:0] m_awprot_q; + reg m_awvalid_q; + reg [DATA_WIDTH-1:0] m_wdata_q; + reg [STRB_WIDTH-1:0] m_wstrb_q; + reg m_wvalid_q; + reg m_write_active; + reg m_write_wait_b; + + (* ASYNC_REG = "TRUE", SHREG_EXTRACT = "NO" *) reg [2:0] m_read_req_sync; + (* ASYNC_REG = "TRUE", SHREG_EXTRACT = "NO" *) reg [READ_PAYLOAD_WIDTH-1:0] m_read_payload_sync1; + (* ASYNC_REG = "TRUE", SHREG_EXTRACT = "NO" *) reg [READ_PAYLOAD_WIDTH-1:0] m_read_payload_sync2; + reg m_read_req_seen; + reg [ADDR_WIDTH-1:0] m_araddr_q; + reg [2:0] m_arprot_q; + reg m_arvalid_q; + reg m_read_wait_r; + + assign m_awaddr = m_awaddr_q; + assign m_awprot = m_awprot_q; + assign m_awvalid = m_awvalid_q; + assign m_wdata = m_wdata_q; + assign m_wstrb = m_wstrb_q; + assign m_wvalid = m_wvalid_q; + assign m_bready = m_write_wait_b; + assign m_araddr = m_araddr_q; + assign m_arprot = m_arprot_q; + assign m_arvalid = m_arvalid_q; + assign m_rready = m_read_wait_r; + + always @(posedge m_clk) begin + if (!m_resetn) begin + m_write_req_sync <= 3'b0; + m_write_payload_sync1 <= {WRITE_PAYLOAD_WIDTH{1'b0}}; + m_write_payload_sync2 <= {WRITE_PAYLOAD_WIDTH{1'b0}}; + m_write_req_seen <= 1'b0; + m_awaddr_q <= {ADDR_WIDTH{1'b0}}; + m_awprot_q <= 3'b0; + m_awvalid_q <= 1'b0; + m_wdata_q <= {DATA_WIDTH{1'b0}}; + m_wstrb_q <= {STRB_WIDTH{1'b0}}; + m_wvalid_q <= 1'b0; + m_write_active <= 1'b0; + m_write_wait_b <= 1'b0; + m_write_resp_payload <= 2'b0; + m_write_resp_toggle <= 1'b0; + m_read_req_sync <= 3'b0; + m_read_payload_sync1 <= {READ_PAYLOAD_WIDTH{1'b0}}; + m_read_payload_sync2 <= {READ_PAYLOAD_WIDTH{1'b0}}; + m_read_req_seen <= 1'b0; + m_araddr_q <= {ADDR_WIDTH{1'b0}}; + m_arprot_q <= 3'b0; + m_arvalid_q <= 1'b0; + m_read_wait_r <= 1'b0; + m_read_resp_payload <= {(DATA_WIDTH+2){1'b0}}; + m_read_resp_toggle <= 1'b0; + end else begin + m_write_req_sync <= {m_write_req_sync[1:0], s_write_req_toggle}; + m_write_payload_sync1 <= s_write_payload; + m_write_payload_sync2 <= m_write_payload_sync1; + m_read_req_sync <= {m_read_req_sync[1:0], s_read_req_toggle}; + m_read_payload_sync1 <= s_read_payload; + m_read_payload_sync2 <= m_read_payload_sync1; + + if (!m_write_active && !m_write_wait_b && + m_write_req_sync[2] != m_write_req_seen) begin + m_write_req_seen <= m_write_req_sync[2]; + {m_awaddr_q, m_awprot_q, m_wdata_q, m_wstrb_q} <= m_write_payload_sync2; + m_awvalid_q <= 1'b1; + m_wvalid_q <= 1'b1; + m_write_active <= 1'b1; + end + if (m_write_active) begin + if (m_awvalid_q && m_awready) + m_awvalid_q <= 1'b0; + if (m_wvalid_q && m_wready) + m_wvalid_q <= 1'b0; + if ((!m_awvalid_q || m_awready) && (!m_wvalid_q || m_wready)) begin + m_write_active <= 1'b0; + m_write_wait_b <= 1'b1; + end + end + if (m_write_wait_b && m_bvalid) begin + m_write_resp_payload <= m_bresp; + m_write_resp_toggle <= ~m_write_resp_toggle; + m_write_wait_b <= 1'b0; + end + + if (!m_arvalid_q && !m_read_wait_r && + m_read_req_sync[2] != m_read_req_seen) begin + m_read_req_seen <= m_read_req_sync[2]; + {m_araddr_q, m_arprot_q} <= m_read_payload_sync2; + m_arvalid_q <= 1'b1; + end + if (m_arvalid_q && m_arready) begin + m_arvalid_q <= 1'b0; + m_read_wait_r <= 1'b1; + end + if (m_read_wait_r && m_rvalid) begin + m_read_resp_payload <= {m_rdata, m_rresp}; + m_read_resp_toggle <= ~m_read_resp_toggle; + m_read_wait_r <= 1'b0; + end + end + end + +endmodule diff --git a/fpga_diff/src/rtl/common/uvhs_blackbox_stubs.v b/fpga_diff/src/rtl/common/uvhs_blackbox_stubs.v new file mode 100644 index 00000000..f5a27058 --- /dev/null +++ b/fpga_diff/src/rtl/common/uvhs_blackbox_stubs.v @@ -0,0 +1,314 @@ +`ifndef UVHS_BLACKBOX_STUBS_V +`define UVHS_BLACKBOX_STUBS_V + +module IBUF(output O, input I); + assign O = I; +endmodule + +module OBUF(output O, input I); + assign O = I; +endmodule + +module BUFG(output O, input I); + assign O = I; +endmodule + +module IBUFGDS(output O, input I, input IB); + assign O = I; +endmodule + +module IBUFDS_GTE4(output O, output ODIV2, input I, input IB, input CEB); + assign O = I; + assign ODIV2 = I; +endmodule + +module DifftestClockGate ( + input CK, + input E, + output Q +); +`ifdef SYNTHESIS + BUFGCE u_bufgce ( + .I(CK), + .CE(E), + .O(Q) + ); +`else + reg en; + always @(*) begin + if (!CK) en = E; + end + assign Q = CK & en; +`endif +endmodule + +(* black_box, syn_black_box *) +module vio_0 ( + clk, probe_out0, probe_out1, probe_out2 +); +input clk; +output probe_out0, probe_out1, probe_out2; +endmodule + +(* black_box, syn_black_box *) +module blk_mem_gen_0 ( + rsta_busy, rstb_busy, s_aclk, s_aresetn, s_axi_araddr, s_axi_arlen, + s_axi_arready, s_axi_arvalid, s_axi_awaddr, s_axi_awlen, s_axi_awready, + s_axi_awvalid, s_axi_bready, s_axi_bresp, s_axi_bvalid, s_axi_rdata, + s_axi_rlast, s_axi_rready, s_axi_rresp, s_axi_rvalid, s_axi_wdata, + s_axi_wlast, s_axi_wready, s_axi_wstrb, s_axi_wvalid +); +output rsta_busy, rstb_busy; +input s_aclk, s_aresetn; +input [31:0] s_axi_araddr, s_axi_awaddr, s_axi_wdata; +input [7:0] s_axi_arlen, s_axi_awlen; +input [3:0] s_axi_wstrb; +input s_axi_arvalid, s_axi_awvalid, s_axi_bready, s_axi_rready; +input s_axi_wlast, s_axi_wvalid; +output s_axi_arready, s_axi_awready, s_axi_wready; +output [1:0] s_axi_bresp, s_axi_rresp; +output [31:0] s_axi_rdata; +output s_axi_bvalid, s_axi_rlast, s_axi_rvalid; +endmodule + +(* black_box, syn_black_box *) +module xdma_ep ( + M00_AXIS_0_tdata, M00_AXIS_0_tkeep, M00_AXIS_0_tlast, M00_AXIS_0_tready, + M00_AXIS_0_tvalid, S00_AXIS_0_tdata, S00_AXIS_0_tkeep, S00_AXIS_0_tlast, + S00_AXIS_0_tready, S00_AXIS_0_tvalid, TO_DIFFTEST_PCIE_CLK, + XDMA_AXI_LITE_araddr, XDMA_AXI_LITE_arprot, XDMA_AXI_LITE_arready, + XDMA_AXI_LITE_arvalid, XDMA_AXI_LITE_awaddr, XDMA_AXI_LITE_awprot, + XDMA_AXI_LITE_awready, XDMA_AXI_LITE_awvalid, XDMA_AXI_LITE_bready, + XDMA_AXI_LITE_bresp, XDMA_AXI_LITE_bvalid, XDMA_AXI_LITE_rdata, + XDMA_AXI_LITE_rready, XDMA_AXI_LITE_rresp, XDMA_AXI_LITE_rvalid, + XDMA_AXI_LITE_wdata, XDMA_AXI_LITE_wready, XDMA_AXI_LITE_wstrb, + XDMA_AXI_LITE_wvalid, cpu_clk, cpu_rstn, pci_exp_rxn, pci_exp_rxp, + pci_exp_txn, pci_exp_txp, pcie_ep_gt_ref_clk_n, pcie_ep_gt_ref_clk_p, + pcie_ep_lnk_up, pcie_ep_perstn +); +output [255:0] M00_AXIS_0_tdata; +output [31:0] M00_AXIS_0_tkeep; +output M00_AXIS_0_tlast; +input M00_AXIS_0_tready; +output M00_AXIS_0_tvalid; +input [255:0] S00_AXIS_0_tdata; +input [31:0] S00_AXIS_0_tkeep; +input S00_AXIS_0_tlast, S00_AXIS_0_tvalid; +output S00_AXIS_0_tready, TO_DIFFTEST_PCIE_CLK; +output [31:0] XDMA_AXI_LITE_araddr, XDMA_AXI_LITE_awaddr, XDMA_AXI_LITE_wdata; +output [2:0] XDMA_AXI_LITE_arprot, XDMA_AXI_LITE_awprot; +input XDMA_AXI_LITE_arready, XDMA_AXI_LITE_awready; +output XDMA_AXI_LITE_arvalid, XDMA_AXI_LITE_awvalid, XDMA_AXI_LITE_bready; +input [1:0] XDMA_AXI_LITE_bresp, XDMA_AXI_LITE_rresp; +input XDMA_AXI_LITE_bvalid; +input [31:0] XDMA_AXI_LITE_rdata; +output XDMA_AXI_LITE_rready; +input XDMA_AXI_LITE_rvalid; +input XDMA_AXI_LITE_wready; +output [3:0] XDMA_AXI_LITE_wstrb; +output XDMA_AXI_LITE_wvalid; +input cpu_clk, cpu_rstn; +input [3:0] pci_exp_rxn, pci_exp_rxp; +output [3:0] pci_exp_txn, pci_exp_txp; +input pcie_ep_gt_ref_clk_n, pcie_ep_gt_ref_clk_p, pcie_ep_perstn; +output pcie_ep_lnk_up; +endmodule + +module xdma_ep_util_ds_buf_0_0 ( + IBUF_DS_P, IBUF_DS_N, IBUF_OUT, IBUF_DS_ODIV2 +); +input [0:0] IBUF_DS_P, IBUF_DS_N; +output [0:0] IBUF_OUT, IBUF_DS_ODIV2; + +wire ibuf_out; +wire ibuf_div2; + +IBUFDS_GTE4 u_ibufds_gte4 ( + .I(IBUF_DS_P[0]), + .IB(IBUF_DS_N[0]), + .CEB(1'b0), + .O(ibuf_out), + .ODIV2(ibuf_div2) +); + +assign IBUF_OUT[0] = ibuf_out; +assign IBUF_DS_ODIV2[0] = ibuf_div2; +endmodule + +(* black_box, syn_black_box *) +module AXI_bridge ( + ACLK, ARESETN, S00_AXI_araddr, S00_AXI_arburst, S00_AXI_arcache, + S00_AXI_arid, S00_AXI_arlen, S00_AXI_arlock, S00_AXI_arprot, + S00_AXI_arqos, S00_AXI_arready, S00_AXI_arregion, S00_AXI_arsize, S00_AXI_arvalid, + S00_AXI_awaddr, S00_AXI_awburst, S00_AXI_awcache, S00_AXI_awid, + S00_AXI_awlen, S00_AXI_awlock, S00_AXI_awprot, S00_AXI_awqos, + S00_AXI_awready, S00_AXI_awregion, S00_AXI_awsize, S00_AXI_awvalid, S00_AXI_bid, + S00_AXI_bready, S00_AXI_bresp, S00_AXI_bvalid, S00_AXI_rdata, + S00_AXI_rid, S00_AXI_rlast, S00_AXI_rready, S00_AXI_rresp, + S00_AXI_rvalid, S00_AXI_wdata, S00_AXI_wlast, S00_AXI_wready, + S00_AXI_wstrb, S00_AXI_wvalid, SYS_CFG_APB_paddr, SYS_CFG_APB_penable, + SYS_CFG_APB_prdata, SYS_CFG_APB_pready, SYS_CFG_APB_psel, + SYS_CFG_APB_pslverr, SYS_CFG_APB_pwdata, SYS_CFG_APB_pwrite, + DMAC_CFG_AHB_hrdata, DMAC_CFG_AHB_hready, DMAC_CFG_AHB_hresp, + IOMMU_CFG_APB_prdata, IOMMU_CFG_APB_pready, IOMMU_CFG_APB_pslverr, + SYS_INTER_CLK, SYS_INTER_ARESETN, UART_0_rxd, UART_0_txd, + UARTLITE_AXI_arready, UARTLITE_AXI_awready, UARTLITE_AXI_bid, + UARTLITE_AXI_bresp, UARTLITE_AXI_bvalid, UARTLITE_AXI_rdata, + UARTLITE_AXI_rid, UARTLITE_AXI_rlast, UARTLITE_AXI_rresp, + UARTLITE_AXI_rvalid, UARTLITE_AXI_wready, + pcie_cfg_arready, pcie_cfg_awready, pcie_cfg_bresp, pcie_cfg_bvalid, + pcie_cfg_rdata, pcie_cfg_rlast, pcie_cfg_rresp, pcie_cfg_rvalid, + pcie_cfg_wready, + rom_axi_araddr, rom_axi_arburst, rom_axi_arcache, rom_axi_arlen, + rom_axi_arlock, rom_axi_arprot, rom_axi_arqos, rom_axi_arready, + rom_axi_arregion, rom_axi_arsize, rom_axi_arvalid, rom_axi_awaddr, + rom_axi_awburst, rom_axi_awcache, rom_axi_awlen, rom_axi_awlock, + rom_axi_awprot, rom_axi_awqos, rom_axi_awready, rom_axi_awregion, + rom_axi_awsize, rom_axi_awvalid, rom_axi_bready, rom_axi_bresp, + rom_axi_bvalid, rom_axi_rdata, rom_axi_rlast, rom_axi_rready, + rom_axi_rresp, rom_axi_rvalid, rom_axi_wdata, rom_axi_wlast, + rom_axi_wready, rom_axi_wstrb, rom_axi_wvalid, uart0_intc +); +input ACLK, ARESETN, SYS_INTER_CLK, SYS_INTER_ARESETN; +input [30:0] S00_AXI_araddr, S00_AXI_awaddr; +input [1:0] S00_AXI_arburst, S00_AXI_awburst, S00_AXI_arid, S00_AXI_awid; +input [3:0] S00_AXI_arcache, S00_AXI_arqos, S00_AXI_awcache, S00_AXI_awqos; +input [3:0] S00_AXI_arregion, S00_AXI_awregion; +input [7:0] S00_AXI_arlen, S00_AXI_awlen, S00_AXI_wstrb; +input S00_AXI_arlock, S00_AXI_awlock; +input [2:0] S00_AXI_arprot, S00_AXI_arsize, S00_AXI_awprot, S00_AXI_awsize; +input S00_AXI_arvalid, S00_AXI_awvalid, S00_AXI_bready; +input S00_AXI_rready, S00_AXI_wlast, S00_AXI_wvalid; +input [63:0] S00_AXI_wdata; +output S00_AXI_arready, S00_AXI_awready, S00_AXI_bvalid; +output S00_AXI_rlast, S00_AXI_rvalid, S00_AXI_wready; +output [1:0] S00_AXI_bid, S00_AXI_bresp, S00_AXI_rid, S00_AXI_rresp; +output [63:0] S00_AXI_rdata; +output [30:0] SYS_CFG_APB_paddr; +output SYS_CFG_APB_penable, SYS_CFG_APB_pwrite; +output [31:0] SYS_CFG_APB_pwdata; +output SYS_CFG_APB_psel; +input [31:0] SYS_CFG_APB_prdata; +input SYS_CFG_APB_pready, SYS_CFG_APB_pslverr; +input [31:0] DMAC_CFG_AHB_hrdata; +input DMAC_CFG_AHB_hready, DMAC_CFG_AHB_hresp; +input [31:0] IOMMU_CFG_APB_prdata; +input IOMMU_CFG_APB_pready, IOMMU_CFG_APB_pslverr; +output UART_0_txd, uart0_intc; +input UART_0_rxd; +input UARTLITE_AXI_arready, UARTLITE_AXI_awready, UARTLITE_AXI_bvalid; +input UARTLITE_AXI_rlast, UARTLITE_AXI_rvalid, UARTLITE_AXI_wready; +input [1:0] UARTLITE_AXI_bid, UARTLITE_AXI_bresp; +input [1:0] UARTLITE_AXI_rid, UARTLITE_AXI_rresp; +input [63:0] UARTLITE_AXI_rdata; +input pcie_cfg_arready, pcie_cfg_awready, pcie_cfg_bvalid; +input pcie_cfg_rlast, pcie_cfg_rvalid, pcie_cfg_wready; +input [1:0] pcie_cfg_bresp, pcie_cfg_rresp; +input [31:0] pcie_cfg_rdata; +output [31:0] rom_axi_araddr, rom_axi_awaddr, rom_axi_wdata; +output [1:0] rom_axi_arburst, rom_axi_awburst; +output [3:0] rom_axi_arcache, rom_axi_arqos, rom_axi_arregion; +output [3:0] rom_axi_awcache, rom_axi_awqos, rom_axi_awregion; +output [7:0] rom_axi_arlen, rom_axi_awlen; +output rom_axi_arlock, rom_axi_awlock; +output [2:0] rom_axi_arprot, rom_axi_arsize, rom_axi_awprot, rom_axi_awsize; +output rom_axi_arvalid, rom_axi_awvalid, rom_axi_bready; +output rom_axi_rready, rom_axi_wlast, rom_axi_wvalid; +output [3:0] rom_axi_wstrb; +input rom_axi_arready, rom_axi_awready, rom_axi_bvalid; +input rom_axi_rlast, rom_axi_rvalid, rom_axi_wready; +input [1:0] rom_axi_bresp, rom_axi_rresp; +input [31:0] rom_axi_rdata; +endmodule + +(* black_box, syn_black_box *) +module data_bridge ( + ACLK, ARESETN, M00_AXI_araddr, M00_AXI_arburst, M00_AXI_arcache, + M00_AXI_arid, M00_AXI_arlen, M00_AXI_arlock, M00_AXI_arprot, + M00_AXI_arqos, M00_AXI_arready, M00_AXI_arregion, M00_AXI_arsize, + M00_AXI_arvalid, M00_AXI_awaddr, M00_AXI_awburst, M00_AXI_awcache, + M00_AXI_awid, M00_AXI_awlen, M00_AXI_awlock, M00_AXI_awprot, + M00_AXI_awqos, M00_AXI_awready, M00_AXI_awregion, M00_AXI_awsize, + M00_AXI_awvalid, M00_AXI_bid, M00_AXI_bready, M00_AXI_bresp, + M00_AXI_bvalid, M00_AXI_rdata, M00_AXI_rid, M00_AXI_rlast, + M00_AXI_rready, M00_AXI_rresp, M00_AXI_rvalid, M00_AXI_wdata, + M00_AXI_wlast, M00_AXI_wready, M00_AXI_wstrb, M00_AXI_wvalid, + S00_AXI_araddr, S00_AXI_arburst, S00_AXI_arcache, S00_AXI_arid, + S00_AXI_arlen, S00_AXI_arlock, S00_AXI_arprot, S00_AXI_arqos, + S00_AXI_arready, S00_AXI_arregion, S00_AXI_arsize, S00_AXI_arvalid, + S00_AXI_awaddr, S00_AXI_awburst, S00_AXI_awcache, S00_AXI_awid, + S00_AXI_awlen, S00_AXI_awlock, S00_AXI_awprot, S00_AXI_awqos, + S00_AXI_awready, S00_AXI_awregion, S00_AXI_awsize, S00_AXI_awvalid, + S00_AXI_bid, S00_AXI_bready, S00_AXI_bresp, S00_AXI_bvalid, + S00_AXI_rdata, S00_AXI_rid, S00_AXI_rlast, S00_AXI_rready, + S00_AXI_rresp, S00_AXI_rvalid, S00_AXI_wdata, S00_AXI_wlast, + S00_AXI_wready, S00_AXI_wstrb, S00_AXI_wvalid, S01_AXI_araddr, + S01_AXI_arburst, S01_AXI_arcache, S01_AXI_arid, S01_AXI_arlen, + S01_AXI_arlock, S01_AXI_arprot, S01_AXI_arqos, S01_AXI_arready, + S01_AXI_arregion, S01_AXI_arsize, S01_AXI_arvalid, S01_AXI_awaddr, + S01_AXI_awburst, S01_AXI_awcache, S01_AXI_awid, S01_AXI_awlen, + S01_AXI_awlock, S01_AXI_awprot, S01_AXI_awqos, S01_AXI_awready, + S01_AXI_awregion, S01_AXI_awsize, S01_AXI_awvalid, S01_AXI_bid, + S01_AXI_bready, S01_AXI_bresp, S01_AXI_bvalid, S01_AXI_rdata, + S01_AXI_rid, S01_AXI_rlast, S01_AXI_rready, S01_AXI_rresp, + S01_AXI_rvalid, S01_AXI_wdata, S01_AXI_wlast, S01_AXI_wready, + S01_AXI_wstrb, S01_AXI_wvalid +); +input ACLK, ARESETN; +output [39:0] M00_AXI_araddr, M00_AXI_awaddr; +output [0:0] M00_AXI_arid, M00_AXI_awid; +output [7:0] M00_AXI_arlen, M00_AXI_awlen; +output [1:0] M00_AXI_arburst, M00_AXI_awburst; +output [3:0] M00_AXI_arcache, M00_AXI_arqos, M00_AXI_arregion; +output [3:0] M00_AXI_awcache, M00_AXI_awqos, M00_AXI_awregion; +output [2:0] M00_AXI_arprot, M00_AXI_arsize, M00_AXI_awprot, M00_AXI_awsize; +output M00_AXI_arlock, M00_AXI_arvalid, M00_AXI_awlock, M00_AXI_awvalid; +input M00_AXI_arready, M00_AXI_awready, M00_AXI_wready; +input [0:0] M00_AXI_bid, M00_AXI_rid; +input [1:0] M00_AXI_bresp, M00_AXI_rresp; +input [255:0] M00_AXI_rdata; +input M00_AXI_bvalid, M00_AXI_rlast, M00_AXI_rvalid; +output M00_AXI_bready, M00_AXI_rready, M00_AXI_wlast, M00_AXI_wvalid; +output [255:0] M00_AXI_wdata; +output [31:0] M00_AXI_wstrb; +input [39:0] S00_AXI_araddr, S00_AXI_awaddr; +input [0:0] S00_AXI_arid, S00_AXI_awid; +input [7:0] S00_AXI_arlen, S00_AXI_awlen; +input [1:0] S00_AXI_arburst, S00_AXI_awburst; +input [3:0] S00_AXI_arcache, S00_AXI_arqos, S00_AXI_arregion; +input [3:0] S00_AXI_awcache, S00_AXI_awqos, S00_AXI_awregion; +input [2:0] S00_AXI_arprot, S00_AXI_arsize, S00_AXI_awprot, S00_AXI_awsize; +input S00_AXI_arlock, S00_AXI_arvalid, S00_AXI_awlock, S00_AXI_awvalid; +input S00_AXI_bready, S00_AXI_rready, S00_AXI_wlast, S00_AXI_wvalid; +input [31:0] S00_AXI_wdata; +input [3:0] S00_AXI_wstrb; +output S00_AXI_arready, S00_AXI_awready, S00_AXI_wready; +output [0:0] S00_AXI_bid, S00_AXI_rid; +output [1:0] S00_AXI_bresp, S00_AXI_rresp; +output [31:0] S00_AXI_rdata; +output S00_AXI_bvalid, S00_AXI_rlast, S00_AXI_rvalid; +input [39:0] S01_AXI_araddr, S01_AXI_awaddr; +input [0:0] S01_AXI_arid, S01_AXI_awid; +input [7:0] S01_AXI_arlen, S01_AXI_awlen; +input [1:0] S01_AXI_arburst, S01_AXI_awburst; +input [3:0] S01_AXI_arcache, S01_AXI_arqos, S01_AXI_arregion; +input [3:0] S01_AXI_awcache, S01_AXI_awqos, S01_AXI_awregion; +input [2:0] S01_AXI_arprot, S01_AXI_arsize, S01_AXI_awprot, S01_AXI_awsize; +input S01_AXI_arlock, S01_AXI_arvalid, S01_AXI_awlock, S01_AXI_awvalid; +input S01_AXI_bready, S01_AXI_rready, S01_AXI_wlast, S01_AXI_wvalid; +input [31:0] S01_AXI_wdata; +input [3:0] S01_AXI_wstrb; +output S01_AXI_arready, S01_AXI_awready, S01_AXI_wready; +output [0:0] S01_AXI_bid, S01_AXI_rid; +output [1:0] S01_AXI_bresp, S01_AXI_rresp; +output [31:0] S01_AXI_rdata; +output S01_AXI_bvalid, S01_AXI_rlast, S01_AXI_rvalid; +endmodule + +`ifndef UVHS_EXTERNAL_UVW_AXI4_TO_DDR4 +(* black_box, syn_black_box *) +module uvw_axi4_to_ddr4; +endmodule +`endif + +`endif diff --git a/fpga_diff/src/rtl/kmh/SimTop_wrapper.sv b/fpga_diff/src/rtl/kmh/SimTop_wrapper.sv index 8e7c5f93..f048b7a1 100755 --- a/fpga_diff/src/rtl/kmh/SimTop_wrapper.sv +++ b/fpga_diff/src/rtl/kmh/SimTop_wrapper.sv @@ -201,6 +201,7 @@ module SimTop_wrapper( output difftest_hostCtrl_reset, output difftest_hostCtrl_diffEnable, output difftest_hostCtrl_ilaTrigger, + output difftest_hostCtrl_enableSquash, input [31:0] difftest_cfg_axilite_awaddr, input difftest_cfg_axilite_awvalid, output difftest_cfg_axilite_awready, @@ -250,6 +251,11 @@ wire [149:0] trace_iaddr; wire [11:0] trace_itype; wire [20:0] trace_iretire; wire [2:0] trace_ilastsize; +wire [47:0] difftest_mem_awaddr_int; +wire [47:0] difftest_mem_araddr_int; + +assign mem_core_awaddr = difftest_mem_awaddr_int[35:0]; +assign mem_core_araddr = difftest_mem_araddr_int[35:0]; `ifndef CONFIG_SIMTOP_HAS_DMA assign dma_core_awready = 1'b0; @@ -275,7 +281,7 @@ SimTop u_XSTop( .peripheral_awready (peri_awready ), .peripheral_awvalid (peri_awvalid ), .peripheral_awid (peri_awid ), - .peripheral_awaddr ({16'd0, peri_awaddr}), + .peripheral_awaddr (peri_awaddr ), .peripheral_awlen (peri_awlen ), .peripheral_awsize (peri_awsize ), .peripheral_awburst (peri_awburst ), @@ -295,7 +301,7 @@ SimTop u_XSTop( .peripheral_arready (peri_arready ), .peripheral_arvalid (peri_arvalid ), .peripheral_arid (peri_arid ), - .peripheral_araddr ({16'd0, peri_araddr}), + .peripheral_araddr (peri_araddr ), .peripheral_arlen (peri_arlen ), .peripheral_arsize (peri_arsize ), .peripheral_arburst (peri_arburst ), @@ -313,7 +319,7 @@ SimTop u_XSTop( .dma_awready (dma_core_awready ), .dma_awvalid (dma_core_awvalid ), .dma_awid (dma_core_awid ), - .dma_awaddr (dma_core_awaddr[35:0] ), + .dma_awaddr ({12'b0, dma_core_awaddr}), .dma_awlen (dma_core_awlen ), .dma_awsize (dma_core_awsize ), .dma_awburst (dma_core_awburst ), @@ -333,7 +339,7 @@ SimTop u_XSTop( .dma_arready (dma_core_arready ), .dma_arvalid (dma_core_arvalid ), .dma_arid (dma_core_arid ), - .dma_araddr (dma_core_araddr[35:0] ), + .dma_araddr ({12'b0, dma_core_araddr}), .dma_arlen (dma_core_arlen ), .dma_arsize (dma_core_arsize ), .dma_arburst (dma_core_arburst ), @@ -348,9 +354,18 @@ SimTop u_XSTop( .dma_rresp (dma_core_rresp ), .dma_rlast (dma_core_rlast ), `endif +`ifdef UVHS_SOC_ADAPT + // The UVHS image does not expose XiangShan's SystemJTAG path. Hold the + // unused TAP in reset so asynchronous board pins do not fan out into the + // core after partitioning. + .io_systemjtag_jtag_TCK (1'b0), + .io_systemjtag_jtag_TMS (1'b1), + .io_systemjtag_jtag_TDI (1'b0), +`else .io_systemjtag_jtag_TCK (io_systemjtag_jtag_TCK), .io_systemjtag_jtag_TMS (io_systemjtag_jtag_TMS), .io_systemjtag_jtag_TDI (io_systemjtag_jtag_TDI), +`endif .io_systemjtag_jtag_TDO_data (io_systemjtag_jtag_TDO_data), .io_systemjtag_jtag_TDO_driven (io_systemjtag_jtag_TDO_driven), .io_systemjtag_reset (io_systemjtag_reset), @@ -369,8 +384,14 @@ SimTop u_XSTop( .io_pll0_ctrl_4 (io_pll0_ctrl_4), .io_pll0_ctrl_5 (io_pll0_ctrl_5), .io_extIntrs (io_extIntrs ), +`ifdef UVHS_SOC_ADAPT + // Keep SYSCNT in the SoC domain for FPGA DiffTest. This avoids the + // FPGA-only TimeAsync CDC without adding a false-path waiver. + .io_rtc_clock (inter_soc_clk), +`else .io_rtc_clock (tmclk), - .io_riscv_rst_vec_0 (38'h10000000), +`endif + .io_riscv_rst_vec_0 (48'h0000_1000_0000), //difftest @@ -391,6 +412,7 @@ SimTop u_XSTop( .difftest_hostCtrl_reset (difftest_hostCtrl_reset), .difftest_hostCtrl_diffEnable (difftest_hostCtrl_diffEnable), .difftest_hostCtrl_ilaTrigger (difftest_hostCtrl_ilaTrigger), + .difftest_hostCtrl_enableSquash (difftest_hostCtrl_enableSquash), .difftest_cfg_axilite_awready (difftest_cfg_axilite_awready), .difftest_cfg_axilite_awvalid (difftest_cfg_axilite_awvalid), .difftest_cfg_axilite_awaddr (difftest_cfg_axilite_awaddr), @@ -413,7 +435,7 @@ SimTop u_XSTop( .difftest_mem_awvalid (mem_core_awvalid ), .difftest_mem_awid (mem_core_awid ), .difftest_mem_awuser (), - .difftest_mem_awaddr ({12'd0, mem_core_awaddr}), + .difftest_mem_awaddr (difftest_mem_awaddr_int), .difftest_mem_awlen (mem_core_awlen ), .difftest_mem_awsize (mem_core_awsize ), .difftest_mem_awburst (mem_core_awburst ), @@ -435,7 +457,7 @@ SimTop u_XSTop( .difftest_mem_arvalid (mem_core_arvalid ), .difftest_mem_arid (mem_core_arid ), .difftest_mem_aruser (), - .difftest_mem_araddr ({12'd0, mem_core_araddr}), + .difftest_mem_araddr (difftest_mem_araddr_int), .difftest_mem_arlen (mem_core_arlen ), .difftest_mem_arsize (mem_core_arsize ), .difftest_mem_arburst (mem_core_arburst ), diff --git a/fpga_diff/src/tcl/common/AXI_bridge.tcl b/fpga_diff/src/tcl/common/AXI_bridge.tcl index c79b8654..c77a84a5 100644 --- a/fpga_diff/src/tcl/common/AXI_bridge.tcl +++ b/fpga_diff/src/tcl/common/AXI_bridge.tcl @@ -249,12 +249,21 @@ proc create_root_design { parentCell } { set ACLK [ create_bd_port -dir I -type clk -freq_hz 25000000 ACLK ] set_property -dict [ list \ CONFIG.ASSOCIATED_BUSIF {rom_axi} \ + CONFIG.ASSOCIATED_RESET {ARESETN} \ ] $ACLK set ARESETN [ create_bd_port -dir I -type rst ARESETN ] + set_property -dict [ list \ + CONFIG.POLARITY {ACTIVE_LOW} \ + ] $ARESETN set SYS_INTER_CLK [ create_bd_port -dir I -type clk -freq_hz 25000000 SYS_INTER_CLK ] set_property -dict [ list \ CONFIG.ASSOCIATED_BUSIF {S00_AXI} \ + CONFIG.ASSOCIATED_RESET {SYS_INTER_ARESETN} \ ] $SYS_INTER_CLK + set SYS_INTER_ARESETN [ create_bd_port -dir I -type rst SYS_INTER_ARESETN ] + set_property -dict [ list \ + CONFIG.POLARITY {ACTIVE_LOW} \ + ] $SYS_INTER_ARESETN set uart0_intc [ create_bd_port -dir O -type intr uart0_intc ] # Create instance: axi_apb_bridge_0, and set properties @@ -293,6 +302,9 @@ proc create_root_design { parentCell } { # Create instance: axi_uart16550_0, and set properties set axi_uart16550_0 [ create_bd_cell -type ip -vlnv xilinx.com:ip:axi_uart16550:2.0 axi_uart16550_0 ] + set_property -dict [ list \ + CONFIG.C_USE_MODEM_PORTS {0} \ + ] $axi_uart16550_0 # Create instance: jtag_axi_flash, and set properties set jtag_axi_flash [ create_bd_cell -type ip -vlnv xilinx.com:ip:jtag_axi:1.2 jtag_axi_flash ] @@ -326,8 +338,9 @@ proc create_root_design { parentCell } { # Create port connections connect_bd_net -net ACLK_0_1 [get_bd_ports ACLK] [get_bd_pins axi_apb_bridge_0/s_axi_aclk] [get_bd_pins axi_interconnect_0/ACLK] [get_bd_pins axi_interconnect_0/M00_ACLK] [get_bd_pins axi_interconnect_0/M01_ACLK] [get_bd_pins axi_interconnect_0/M02_ACLK] [get_bd_pins axi_interconnect_0/M03_ACLK] [get_bd_pins axi_interconnect_0/S01_ACLK] [get_bd_pins axi_uart16550_0/s_axi_aclk] [get_bd_pins extllc_bram_ctrl/s_axi_aclk] [get_bd_pins jtag_axi_flash/aclk] - connect_bd_net -net ARESETN_0_1 [get_bd_ports ARESETN] [get_bd_pins axi_apb_bridge_0/s_axi_aresetn] [get_bd_pins axi_interconnect_0/ARESETN] [get_bd_pins axi_interconnect_0/M00_ARESETN] [get_bd_pins axi_interconnect_0/M01_ARESETN] [get_bd_pins axi_interconnect_0/M02_ARESETN] [get_bd_pins axi_interconnect_0/M03_ARESETN] [get_bd_pins axi_interconnect_0/S00_ARESETN] [get_bd_pins axi_interconnect_0/S01_ARESETN] [get_bd_pins axi_uart16550_0/s_axi_aresetn] [get_bd_pins extllc_bram_ctrl/s_axi_aresetn] [get_bd_pins jtag_axi_flash/aresetn] + connect_bd_net -net ARESETN_0_1 [get_bd_ports ARESETN] [get_bd_pins axi_apb_bridge_0/s_axi_aresetn] [get_bd_pins axi_interconnect_0/ARESETN] [get_bd_pins axi_interconnect_0/M00_ARESETN] [get_bd_pins axi_interconnect_0/M01_ARESETN] [get_bd_pins axi_interconnect_0/M02_ARESETN] [get_bd_pins axi_interconnect_0/M03_ARESETN] [get_bd_pins axi_interconnect_0/S01_ARESETN] [get_bd_pins axi_uart16550_0/s_axi_aresetn] [get_bd_pins extllc_bram_ctrl/s_axi_aresetn] [get_bd_pins jtag_axi_flash/aresetn] connect_bd_net -net SYS_INTER_CLK_1 [get_bd_ports SYS_INTER_CLK] [get_bd_pins axi_interconnect_0/S00_ACLK] + connect_bd_net -net SYS_INTER_ARESETN_1 [get_bd_ports SYS_INTER_ARESETN] [get_bd_pins axi_interconnect_0/S00_ARESETN] connect_bd_net -net axi_uart16550_0_ip2intc_irpt [get_bd_ports uart0_intc] [get_bd_pins axi_uart16550_0/ip2intc_irpt] connect_bd_net -net xlconstant_0_dout [get_bd_pins axi_uart16550_0/freeze] [get_bd_pins xlconstant_0/dout] diff --git a/fpga_diff/tools/generate_reports.tcl b/fpga_diff/tools/generate_reports.tcl index 7319e93a..f4df5f68 100644 --- a/fpga_diff/tools/generate_reports.tcl +++ b/fpga_diff/tools/generate_reports.tcl @@ -2,9 +2,6 @@ # Vivado Tcl Script for Reporting (No GUI) # ---------------------------------------- -# Suppress WARNING messages -catch { set_msg_config -severity {WARNING} -suppress } - # 1) Read required arguments: OUT_FILE and PROJECT(.xpr) if {![info exists ::argv] || [llength $::argv] < 2} { puts "ERROR: Missing arguments." diff --git a/fpga_diff/tools/report_pcie_route_evidence.tcl b/fpga_diff/tools/report_pcie_route_evidence.tcl new file mode 100644 index 00000000..aad5298d --- /dev/null +++ b/fpga_diff/tools/report_pcie_route_evidence.tcl @@ -0,0 +1,99 @@ +if {$argc != 2} { + puts stderr "usage: vivado -mode batch -source report_pcie_route_evidence.tcl -tclargs " + exit 64 +} + +set dcp [file normalize [lindex $argv 0]] +set out [file normalize [lindex $argv 1]] +open_checkpoint $dcp + +set fh [open $out w] + +proc prop_or_dash {obj prop} { + if {[catch {set value [get_property $prop $obj]}]} { + return "-" + } + if {$value eq ""} { + return "-" + } + return $value +} + +proc emit_objects {fh label objects props} { + puts $fh "## $label count=[llength $objects]" + foreach obj [lsort $objects] { + set fields [list "name=$obj"] + foreach prop $props { + lappend fields "${prop}=[prop_or_dash $obj $prop]" + } + puts $fh [join $fields " | "] + } +} + +puts $fh "## checkpoint" +puts $fh "dcp=$dcp" +puts $fh "design=[current_design]" +puts $fh "part=[get_property PART [current_design]]" + +set pcie_ports {} +foreach pattern { + *pcie* *PCIE* *pci_ep* *PCI_EP* *diff_clock* *reset_rtl* +} { + foreach port [get_ports -quiet $pattern] { + if {[lsearch -exact $pcie_ports $port] < 0} { + lappend pcie_ports $port + } + } +} +emit_objects $fh ports $pcie_ports { + DIRECTION PACKAGE_PIN LOC IOSTANDARD DIFF_TERM + IO_BUFFER_TYPE CLOCK_BUFFER_TYPE PULLTYPE +} + +puts $fh "## port connectivity" +foreach port [lsort $pcie_ports] { + set nets [get_nets -quiet -segments -of_objects $port] + set pins [get_pins -quiet -leaf -of_objects $nets] + puts $fh "port=$port" + puts $fh " nets=[join [lsort $nets] { }]" + puts $fh " pins=[join [lsort $pins] { }]" +} + +set pcie_cells {} +foreach pattern { + *pcie_4_c_e4_inst* *PCIE4CE4* *pcie4c_ip_i* +} { + foreach cell [get_cells -hierarchical -quiet $pattern] { + if {[lsearch -exact $pcie_cells $cell] < 0} { + lappend pcie_cells $cell + } + } +} +emit_objects $fh pcie_cells $pcie_cells {REF_NAME PRIMITIVE_TYPE LOC BEL IS_LOC_FIXED} + +set gt_cells {} +foreach cell [get_cells -hierarchical -quiet -filter { + REF_NAME =~ IBUFDS_GTE* || + REF_NAME =~ GTYE4_CHANNEL* || + REF_NAME =~ GTYE4_COMMON* || + PRIMITIVE_TYPE =~ *GTYE4* || + PRIMITIVE_TYPE =~ *IBUFDS_GTE* +}] { + lappend gt_cells $cell +} +emit_objects $fh gt_and_refclk_cells $gt_cells {REF_NAME PRIMITIVE_TYPE LOC BEL IS_LOC_FIXED} + +puts $fh "## GT/refclk cell connectivity" +foreach cell [lsort $gt_cells] { + puts $fh "cell=$cell" + foreach pin [lsort [get_pins -quiet -of_objects $cell]] { + set nets [get_nets -quiet -segments -of_objects $pin] + if {[llength $nets]} { + puts $fh " pin=$pin | DIR=[prop_or_dash $pin DIRECTION] | nets=[join $nets { }]" + } + } +} + +close $fh +close_design +puts "INFO: wrote PCIe routed evidence to $out" diff --git a/fpga_diff/tools/report_post_route_cdc.tcl b/fpga_diff/tools/report_post_route_cdc.tcl new file mode 100644 index 00000000..61ee7b7d --- /dev/null +++ b/fpga_diff/tools/report_post_route_cdc.tcl @@ -0,0 +1,212 @@ +# Post-route CDC diagnostics for an existing Vivado DCP. +# +# Usage: +# vivado -mode batch -source report_post_route_cdc.tcl \ +# -tclargs +# +# The script is intentionally read-only with respect to the checkpoint. It +# writes report files and small Tcl-generated summaries under . + +if {![info exists ::argv] || [llength $::argv] < 2} { + puts "ERROR: usage: report_post_route_cdc.tcl " + exit 1 +} + +set dcp_file [file normalize [lindex $::argv 0]] +set out_dir [file normalize [lindex $::argv 1]] + +if {![file exists $dcp_file]} { + puts "ERROR: routed DCP not found: $dcp_file" + exit 1 +} + +file mkdir $out_dir + +proc write_text_file {path text} { + set fh [open $path w] + puts -nonewline $fh $text + close $fh +} + +proc append_line {fh line} { + puts $fh $line +} + +proc try_report {description args} { + puts "INFO: $description" + if {[catch {uplevel #0 $args} err]} { + puts "WARNING: $description failed: $err" + return 0 + } + return 1 +} + +proc remove_if_exists {path} { + if {[file exists $path]} { + file delete -force $path + } + return $path +} + +proc safe_get_property {prop object} { + if {[catch {set value [get_property $prop $object]}] || $value eq ""} { + return "" + } + return $value +} + +proc clock_or_empty {clock_name} { + return [get_clocks -quiet $clock_name] +} + +proc count_objects {objects} { + if {$objects eq ""} { + return 0 + } + return [llength $objects] +} + +proc safe_current_design {} { + if {[catch {current_design} design] || $design eq ""} { + return "unknown" + } + return $design +} + +proc safe_design_part {} { + set design [safe_current_design] + if {$design ne "unknown"} { + foreach prop {PART DEVICE} { + if {![catch {set value [get_property $prop $design]}] && $value ne ""} { + return $value + } + } + } + return "unknown" +} + +proc write_clock_pair_summary {fh from_clock to_clock} { + set from [clock_or_empty $from_clock] + set to [clock_or_empty $to_clock] + append_line $fh "" + append_line $fh "### $from_clock -> $to_clock" + append_line $fh "from_clock_count=[count_objects $from] to_clock_count=[count_objects $to]" + if {![llength $from] || ![llength $to]} { + append_line $fh "missing_clock=1" + return + } + + set paths [get_timing_paths -quiet -from $from -to $to -max_paths 20] + append_line $fh "timing_path_count=[count_objects $paths]" + foreach path $paths { + set startpoint [get_property STARTPOINT_PIN $path] + set endpoint [get_property ENDPOINT_PIN $path] + set slack [get_property SLACK $path] + append_line $fh "path slack=$slack start=$startpoint end=$endpoint" + } + + set from_cells [get_cells -quiet -hierarchical -filter "NAME =~ *${from_clock}*"] + set to_cells [get_cells -quiet -hierarchical -filter "NAME =~ *${to_clock}*"] + append_line $fh "name_match_from_cells=[count_objects $from_cells]" + foreach cell [lrange $from_cells 0 19] { + append_line $fh " from_cell=$cell ref=[get_property REF_NAME $cell]" + } + append_line $fh "name_match_to_cells=[count_objects $to_cells]" + foreach cell [lrange $to_cells 0 19] { + append_line $fh " to_cell=$cell ref=[get_property REF_NAME $cell]" + } +} + +open_checkpoint $dcp_file + +set summary_file [file join $out_dir cdc_diagnostics_summary.txt] +set fh [open $summary_file w] +append_line $fh "dcp=$dcp_file" +append_line $fh "part=[safe_design_part]" +append_line $fh "design=[safe_current_design]" +append_line $fh "vivado=[version -short]" +append_line $fh "" +append_line $fh "## Clocks" +foreach clk [lsort [get_clocks -quiet *]] { + set period [safe_get_property PERIOD $clk] + set waveform [safe_get_property WAVEFORM $clk] + set master [safe_get_property MASTER_CLOCK $clk] + append_line $fh "clock=$clk period=$period waveform={$waveform} master=$master" +} + +append_line $fh "" +append_line $fh "## Native wrapper markers" +foreach pattern { + *core_def_native* + *native* + *native_rstn_sync* + *uvhs_axi64_to_axi256* + *uvhs_debug* + *simtop_cfg_axilite* +} { + set cells [get_cells -quiet -hierarchical -filter "NAME =~ $pattern"] + append_line $fh "pattern=$pattern count=[count_objects $cells]" + foreach cell [lrange $cells 0 49] { + append_line $fh " cell=$cell ref=[get_property REF_NAME $cell]" + } +} + +append_line $fh "" +append_line $fh "## CDC critical clock-pair probes" +set critical_pairs { + {mmcm_clkout0 CPU_CLK_IN} + {s_clk_out0 FP_CLK_200M_P} + {s_clk_out1 FP_CLK_200M_P} + {uvw_sbus_GTYE4_CHANNEL_RXOUTCLK0 clk_out4_uvw_ctrl_pll} + {uvw_sbus_GTYE4_CHANNEL_TXOUTCLK0 clk_out4_uvw_ctrl_pll} + {uvw_sbus_sysclk0 clk_out4_uvw_ctrl_pll} + {uvw_sbus_sysclk1 clk_out4_uvw_ctrl_pll} + {uvw_sbus_clk_i s_clk_out0} + {uvw_sbus_clk_i s_clk_out1} + {uvw_sbus_sysclk0 uvw_sbus_GTYE4_CHANNEL_TXOUTCLK0} + {uvw_sbus_sysclk1 uvw_sbus_GTYE4_CHANNEL_TXOUTCLK0} + {clk_out4_uvw_ctrl_pll uvw_sbus_sysclk0} + {s_clk_out0 uvw_sbus_sysclk0} + {s_clk_out1 uvw_sbus_sysclk0} + {uvw_sbus_GTYE4_CHANNEL_TXOUTCLK0 uvw_sbus_sysclk0} + {difftest_pcie_clock_bufg_n CPU_CLK_IN} + {CPU_CLK_IN difftest_pcie_clock_bufg_n} + {difftest_pcie_clock_bufg_n pipe_clk} + {pipe_clk difftest_pcie_clock_bufg_n} + {difftest_pcie_clock_bufg_n xdma_ep_xdma_0_0_pcie4c_ip_gt_top_i_n_30} + {xdma_ep_xdma_0_0_pcie4c_ip_gt_top_i_n_30 difftest_pcie_clock_bufg_n} + {pcie_ep_refclk difftest_pcie_clock_bufg_n} + {difftest_pcie_clock_bufg_n pcie_ep_refclk} + {pcie_ep_refclk pipe_clk} + {pipe_clk pcie_ep_refclk} + {pcie_ep_refclk xdma_ep_xdma_0_0_pcie4c_ip_gt_top_i_n_30} + {xdma_ep_xdma_0_0_pcie4c_ip_gt_top_i_n_30 pcie_ep_refclk} + {pipe_clk xdma_ep_xdma_0_0_pcie4c_ip_gt_top_i_n_30} + {xdma_ep_xdma_0_0_pcie4c_ip_gt_top_i_n_30 pipe_clk} +} +foreach pair $critical_pairs { + write_clock_pair_summary $fh [lindex $pair 0] [lindex $pair 1] +} +close $fh + +try_report "write CDC summary" \ + report_cdc -file [remove_if_exists [file join $out_dir report_cdc_summary.rpt]] +if {![try_report "write CDC details" \ + report_cdc -details -file [remove_if_exists [file join $out_dir report_cdc_details.rpt]]]} { + try_report "write CDC verbose fallback" \ + report_cdc -verbose -file [remove_if_exists [file join $out_dir report_cdc_verbose.rpt]] +} +try_report "write CDC details verbose" \ + report_cdc -details -verbose -file [remove_if_exists [file join $out_dir report_cdc_details_verbose.rpt]] +try_report "write clock interaction" \ + report_clock_interaction -file [remove_if_exists [file join $out_dir report_clock_interaction.rpt]] +try_report "write check timing" \ + check_timing -file [remove_if_exists [file join $out_dir check_timing.rpt]] -verbose +try_report "write bus skew" \ + report_bus_skew -file [remove_if_exists [file join $out_dir report_bus_skew.rpt]] +try_report "write route status" \ + report_route_status -file [remove_if_exists [file join $out_dir route_status.rpt]] + +puts "INFO: wrote CDC diagnostics to $out_dir" +close_design +exit diff --git a/fpga_diff/user_script/.gitignore b/fpga_diff/user_script/.gitignore new file mode 100644 index 00000000..6ab2da6e --- /dev/null +++ b/fpga_diff/user_script/.gitignore @@ -0,0 +1,3 @@ +* +!.gitignore +!hw_run_download.tcl diff --git a/fpga_diff/user_script/hw_run_download.tcl b/fpga_diff/user_script/hw_run_download.tcl new file mode 100644 index 00000000..866a2592 --- /dev/null +++ b/fpga_diff/user_script/hw_run_download.tcl @@ -0,0 +1,85 @@ +# Minimal Hejian runtime bringup flow. +# Keep this script free of RTDB/readback/debug dependencies so a release DB is +# enough for programming and reset validation on the programming host. + +query -user +query -fpgas -all +query -version + +# uv_shell runtime invocation does not guarantee the script directory as the +# current working directory. Prefer an explicit override, otherwise assume the +# staged hw.dat sits next to user_script/ in the runtime directory. +if {[info exists ::env(UVHS_DB_PATH)] && $::env(UVHS_DB_PATH) ne ""} { + set uvhs_db_path $::env(UVHS_DB_PATH) +} else { + set uvhs_script_dir [file dirname [file normalize [info script]]] + set uvhs_db_path [file normalize [file join $uvhs_script_dir .. hw.dat]] +} +puts "INFO: loading runtime database $uvhs_db_path" +load_db -db $uvhs_db_path + +config -connector +query -connector -type fmc +query -voltage + +# Apply the compiler-assigned frequencies to the U2 ClockHub, then record both +# the requested defaults and the actual device state in the download log. +query -clock -default +config -clock -default +config -clock -commit +query -clock + +# Hold the top-level resets low across download, then release them in the +# same order used by the checked-in runtime scripts for this design. The host +# keeps CPU execution stopped later through HOST_IO_RESET while DDR is written. +reset -name rstn_sw6 -value 0 +reset -name rstn_sw5 -value 0 +reset -name rstn_sw4 -value 0 +query -reset + +download + +query -ipinfo +initialize +after 1000 + +reset -name rstn_sw6 -value 0 +reset -name rstn_sw5 -value 0 +reset -name rstn_sw4 -value 0 +after 1000 + +reset -name rstn_sw6 -value 1 +reset -name rstn_sw4 -value 1 +after 1000 + +reset -name rstn_sw5 -value 1 +after 1000 + +query -reset +query -fpgas -all + +proc uvhs_poll_command_file {} { + if {![info exists ::env(UVHS_COMMAND_FILE)] || $::env(UVHS_COMMAND_FILE) eq ""} { + after 500 uvhs_poll_command_file + return + } + + set uvhs_command_file $::env(UVHS_COMMAND_FILE) + if {[file exists $uvhs_command_file]} { + set uvhs_running_file "${uvhs_command_file}.running" + if {[catch { + file rename -force $uvhs_command_file $uvhs_running_file + source $uvhs_running_file + } uvhs_command_error]} { + puts stderr "ERROR: UVHS command file failed: $uvhs_command_error" + } + catch {file delete -force $uvhs_running_file} + } + after 500 uvhs_poll_command_file +} + +# Hejian runtime must stay attached after download; terminate the uv_shell +# process manually when the board is no longer needed. +set ::uvhs_keepalive 0 +uvhs_poll_command_file +vwait ::uvhs_keepalive diff --git a/fpga_diff/uvhs/Makefile b/fpga_diff/uvhs/Makefile new file mode 100644 index 00000000..be6bdf43 --- /dev/null +++ b/fpga_diff/uvhs/Makefile @@ -0,0 +1,367 @@ +ROOT_DIR ?= $(abspath $(dir $(lastword $(MAKEFILE_LIST)))/..) +ENV_SCRIPTS_HOME ?= $(ROOT_DIR) + +CORE_DIR ?= +CHI_DIR ?= +CPU ?= +SUFFIX ?= +VIVADO ?= vivado +PLATFORM ?= U2.2 +XDMA_LINK_WIDTH ?= X4 +XDMA_ENABLE_PF0_BAR1 ?= 1 +# Expected values for the checked Hejian XDMA EP x4 DCP used by this UVHS +# flow. They guide script preflight; xdma_ep itself comes from +# UVHS_XDMA_EP_DCP, not from patching the Vivado xdma_ep Tcl source. +XDMA_AXILITE_MASTER_SCALE ?= Kilobytes +XDMA_AXILITE_MASTER_SIZE ?= 512 +XDMA_EXPECTED_VENDOR ?= 0x10ee +XDMA_EXPECTED_DEVICE ?= 0x9048 +XDMA_EXPECTED_BAR0_SIZE ?= 0x80000 +XDMA_EXPECTED_BAR1_SIZE ?= 0x10000 +UVHS_BASE_ASSEMBLE_FILE ?= +UVHS_BASE_ASSEMBLE_SHA256 ?= 0e44f3ced8e5628a6f9b60a36d15194f03f6eddefcf29d0221a8abf140ecb18b +UVHS_XDMA_EP_DCP ?= +UVHS_XDMA_EP_DCP_SHA256 ?= e40002b4ccab73d4d7a1398630f59428add0a64b9f33d284d3789595bb791b32 +UVHS_XDMA_EP_EXPECTED_VIVADO ?= 2024.2 +UVHS_ALLOW_DCP_VIVADO_MISMATCH ?= 0 +UVHS_DESIGN_NAME ?= VU19P_X4 +export XDMA_LINK_WIDTH XDMA_ENABLE_PF0_BAR1 XDMA_AXILITE_MASTER_SCALE XDMA_AXILITE_MASTER_SIZE XDMA_EXPECTED_VENDOR XDMA_EXPECTED_DEVICE XDMA_EXPECTED_BAR0_SIZE XDMA_EXPECTED_BAR1_SIZE UVHS_BASE_ASSEMBLE_FILE UVHS_BASE_ASSEMBLE_SHA256 UVHS_XDMA_EP_DCP UVHS_XDMA_EP_DCP_SHA256 UVHS_XDMA_EP_EXPECTED_VIVADO UVHS_ALLOW_DCP_VIVADO_MISMATCH UVHS_DESIGN_NAME + +UVHS_TARBALL_CANDIDATES ?= $(abspath $(ROOT_DIR)/../uvhs.tar.gz) $(abspath $(ROOT_DIR)/../../../env-scripts/uvhs.tar.gz) +UVHS_TARBALL ?= $(firstword $(wildcard $(UVHS_TARBALL_CANDIDATES))) +UVHS_TEMPLATE ?= uvhs_1core_cpu0121_soc1229_iommu_1rc_19p +UVHS_WORK_DIR ?= $(ENV_SCRIPTS_HOME)/fpga_diff_uvhs_$(CPU)$(if $(strip $(SUFFIX)),-$(strip $(SUFFIX)),) +UVHS_TOP ?= fpga_top_debug +UVHS_FILELIST ?= $(UVHS_WORK_DIR)/rtl/filelist.f +UVHS_ROOT ?= $(shell bash -lc 'source "$(ROOT_DIR)/uvhs/setenv.sh" >/dev/null 2>&1 && printf "%s" "$$UV_ROOT"') +UVHS_UVW_AXI4_TO_DDR4_SRC_CANDIDATES ?= $(ENV_SCRIPTS_HOME)/uvhs_nutshell-uvhs-ddr-uvw-ip-bind $(ENV_SCRIPTS_HOME)/uvhs_nutshell-uvhs-ddr-uvw-ip +UVHS_UVW_AXI4_TO_DDR4_SRC ?= +UVHS_UVW_AXI4_TO_DDR4_FILES ?= rtl/soc/uvw_axi4_to_ddr4.dcp rtl/soc/uvw_axi4_to_ddr4_Stub.v script/uvw_axi4_to_ddr4_pblock.tcl script/custom_parts_ddr4_KSM26SES8_2666.csv +UVHS_UVW_AXI4_TO_DDR4_REQUIRED_FILES ?= rtl/soc/uvw_axi4_to_ddr4.dcp rtl/soc/uvw_axi4_to_ddr4_Stub.v script/uvw_axi4_to_ddr4_pblock.tcl +UVHS_UVW_AXI4_TO_DDR4_EXPECTED_MD5 ?= bbd428f1fa2ede1628e11b7ab14d1072 +UVHS_UVW_AXI4_TO_DDR4_ALLOW_TEMPLATE_FALLBACK ?= 0 +UVHS_EXPORT_IP ?= 1 +UVHS_EXPORT_IP_FORCE ?= 1 +UVHS_EXPORT_IP_JOBS ?= 8 +UVHS_EXPORT_IP_VIVADO_VERSION ?= +UVHS_EXPORT_IP_ONLY ?= +UVHS_EFFECTIVE_SKIP_VIVADO_EXPORT = $(if $(strip $(UVHS_XDMA_EP_DCP)),xdma_ep,) +UVHS_PREBUILT_DCP_DIR ?= +UVHS_AXI_BRIDGE_DCP ?= +UVHS_AXI_BRIDGE_DCP_SHA256 ?= +UVHS_REQUIRED_DCP_MODULES ?= blk_mem_gen_0 AXI_bridge data_bridge vio_0 xdma_ep uvw_axi4_to_ddr4 +UVHS_UVW_AXI4_TO_DDR4_RTL_INST ?= $(UVHS_TOP).core_def.U_UVHS_UVW_AXI4_TO_DDR4 +UVHS_FRONTEND_TCL ?= $(ROOT_DIR)/uvhs/frontend_run.tcl +UVHS_SKIP_BOARD_CONSTRAINTS ?= 0 +UVHS_PIN_OVERRIDE_FILE ?= +UVHS_PROBE_FILE ?= +UVHS_CPU_CLK_PERIOD_NS ?= 40 +UVHS_ROUTE_ENABLE_HOLD_EXPN_BAILOUT ?= +UVHS_CPU_DEBUG_CLK ?= 1 +UVHS_ASSIGN_PIN_FILE ?= $(if $(filter nutshell,$(CPU)),$(ROOT_DIR)/uvhs/assign_pin_nutshell_f2.tcl,) +UVHS_TIMING_FILE ?= +UVHS_PARTITION_FILE ?= +UVHS_ASSEMBLE_FILE ?= +UVHS_STOP_AFTER_PARTITION ?= 0 +UVHS_STOP_AFTER_GENSCRIPT ?= 0 +UVHS_USE_LSF ?= 0 +UVHS_FRONTEND_THREADS ?= 4 +UVHS_FRONTEND_PROCESSES ?= 16 +UVHS_FPGA_THREADS ?= 4 +UVHS_FPGA_PROCESSES ?= 8 +UVHS_TARGET_BOARD ?= B0 +UVHS_TARGET_PACK ?= $(UVHS_TARGET_BOARD) +UVHS_TARGET_FPGA ?= F2 +UVHS_TARGET_FPGA_LOWER ?= $(shell printf '%s.%s' '$(UVHS_TARGET_PACK)' '$(UVHS_TARGET_FPGA)' | tr '[:upper:]' '[:lower:]') +UVHS_FPGA_COUNT ?= 1 +UVHS_KEEP_FPGAS ?= $(UVHS_TARGET_FPGA_LOWER) +UVHS_UART0_CONNECTOR ?= b0.F2_APC16 +UVHS_UART0_TX_INDEX ?= 117 +UVHS_UART0_RX_INDEX ?= 57 +UVHS_TIME_GROUP_IO_LOGIC ?= default +UVHS_STRATEGY_NUM_RETRY ?= +UVHS_LUT_FILL_RATE ?= +UVHS_LUT6_FILL_RATE ?= +UVHS_REQUIRED_MODULES ?= $(if $(filter nanhu,$(CPU)),XlnFpgaTop,$(if $(filter kmh nutshell,$(CPU)),SimTop,)) +# Match the enumerable Hejian XDMA EP x4 example, whose successful bitstream +# landed under Strategy_uv_high_fanout_explore. +UVHS_PNR_STRATEGY ?= Strategy_uv_high_fanout_explore +UVHS_COMPILE_STRATEGY_NUM ?= 1 +UVHS_COMPILE_STRATEGY0 ?= uv_high_fanout_explore +UVHS_COMPILE_STRATEGY1 ?= +UVHS_COMPILE_STRATEGY2 ?= +export UVHS_PNR_STRATEGY UVHS_COMPILE_STRATEGY_NUM UVHS_COMPILE_STRATEGY0 UVHS_COMPILE_STRATEGY1 UVHS_COMPILE_STRATEGY2 +UVHS_HEJIAN_FLOW_ENV = XDMA_LINK_WIDTH="$(XDMA_LINK_WIDTH)" XDMA_ENABLE_PF0_BAR1="$(XDMA_ENABLE_PF0_BAR1)" XDMA_AXILITE_MASTER_SCALE="$(XDMA_AXILITE_MASTER_SCALE)" XDMA_AXILITE_MASTER_SIZE="$(XDMA_AXILITE_MASTER_SIZE)" XDMA_EXPECTED_VENDOR="$(XDMA_EXPECTED_VENDOR)" XDMA_EXPECTED_DEVICE="$(XDMA_EXPECTED_DEVICE)" XDMA_EXPECTED_BAR0_SIZE="$(XDMA_EXPECTED_BAR0_SIZE)" XDMA_EXPECTED_BAR1_SIZE="$(XDMA_EXPECTED_BAR1_SIZE)" UVHS_BASE_ASSEMBLE_FILE="$(UVHS_BASE_ASSEMBLE_FILE)" UVHS_BASE_ASSEMBLE_SHA256="$(UVHS_BASE_ASSEMBLE_SHA256)" UVHS_XDMA_EP_EXPECTED_VIVADO="$(UVHS_XDMA_EP_EXPECTED_VIVADO)" UVHS_ALLOW_DCP_VIVADO_MISMATCH="$(UVHS_ALLOW_DCP_VIVADO_MISMATCH)" UVHS_DESIGN_NAME="$(UVHS_DESIGN_NAME)" PLATFORM="$(PLATFORM)" UVHS_TARGET_BOARD="$(UVHS_TARGET_BOARD)" UVHS_TARGET_PACK="$(UVHS_TARGET_PACK)" UVHS_TARGET_FPGA="$(UVHS_TARGET_FPGA)" UVHS_TARGET_FPGA_LOWER="$(UVHS_TARGET_FPGA_LOWER)" UVHS_FPGA_COUNT="$(UVHS_FPGA_COUNT)" UVHS_KEEP_FPGAS="$(UVHS_KEEP_FPGAS)" UVHS_UART0_CONNECTOR="$(UVHS_UART0_CONNECTOR)" UVHS_UART0_TX_INDEX="$(UVHS_UART0_TX_INDEX)" UVHS_UART0_RX_INDEX="$(UVHS_UART0_RX_INDEX)" UVHS_TIME_GROUP_IO_LOGIC="$(UVHS_TIME_GROUP_IO_LOGIC)" UVHS_CPU_CLK_PERIOD_NS="$(UVHS_CPU_CLK_PERIOD_NS)" UVHS_CPU_DEBUG_CLK="$(UVHS_CPU_DEBUG_CLK)" UVHS_PNR_STRATEGY="$(UVHS_PNR_STRATEGY)" UVHS_COMPILE_STRATEGY_NUM="$(UVHS_COMPILE_STRATEGY_NUM)" UVHS_COMPILE_STRATEGY0="$(UVHS_COMPILE_STRATEGY0)" UVHS_COMPILE_STRATEGY1="$(UVHS_COMPILE_STRATEGY1)" UVHS_COMPILE_STRATEGY2="$(UVHS_COMPILE_STRATEGY2)" +UVHS_PNR_DIR ?= $(UVHS_WORK_DIR)/hw.dat/Compile/PnR/$(UVHS_TARGET_PACK)/$(UVHS_TARGET_FPGA)/vivado/Rundir/$(UVHS_PNR_STRATEGY) +UVHS_BITSTREAM_DIR ?= $(UVHS_PNR_DIR)/bitstream +UVHS_LTX ?= $(UVHS_BITSTREAM_DIR)/fpga_top_debug.ltx +UVHS_BIT_HOME ?= $(UVHS_WORK_DIR)/ready-to-program +UVHS_RESOLVED_BITSTREAM_DIR = $$(summary="$(UVHS_BITSTREAM_DIR)/after_xtalk_fix_timing_summary.txt"; if [ -f "$$summary" ]; then dirname "$$summary"; else pnr_root="$(UVHS_WORK_DIR)/hw.dat/Compile/PnR/$(UVHS_TARGET_PACK)/$(UVHS_TARGET_FPGA)/vivado/Rundir"; find "$$pnr_root" -name ".Task_strategy_*.timingclean" -printf "%h\n" 2>/dev/null | sort -u | while read -r dir; do test -f "$$dir/bitstream/after_xtalk_fix_timing_summary.txt" && printf "%s\n" "$$dir/bitstream"; done | head -1; fi) + +.PHONY: uvhs_prepare uvhs_filelist uvhs_check_modules uvhs_tools_check \ + uvhs_export_vivado_ip \ + uvhs_sync_uvw_axi4_to_ddr4 uvhs_show_uvw_axi4_to_ddr4 \ + uvhs_frontend uvhs_backend uvhs_all \ + uvhs_clean uvhs_env uvhs_preflight uvhs_hejian_pcie_x4_env \ + uvhs_hejian_pcie_x4_preflight uvhs_hejian_pcie_x4_nutshell_all \ + uvhs_hejian_pcie_x4_xiangshan_all \ + uvhs_status uvhs_check_timing_clean uvhs_export_ltx uvhs_package_bitstream + +uvhs_tools_check: + bash "$(ROOT_DIR)/uvhs/check_flow_tools.sh" + +uvhs_prepare: + test -f "$(UVHS_TARBALL)" + rm -rf "$(UVHS_WORK_DIR)/script" + rm -rf "$(UVHS_WORK_DIR)/uvhs" + mkdir -p "$(UVHS_WORK_DIR)" + tar -xzf "$(UVHS_TARBALL)" -C "$(UVHS_WORK_DIR)" --strip-components=1 \ + "$(UVHS_TEMPLATE)/script" \ + "$(UVHS_TEMPLATE)/Makefile" \ + "$(UVHS_TEMPLATE)/rerun" + mkdir -p "$(UVHS_WORK_DIR)/rtl" "$(UVHS_WORK_DIR)/rtl/soc" "$(UVHS_WORK_DIR)/rtl/device/pcie" + if ls "$(ROOT_DIR)"/uvhs/assign_pin_*.tcl >/dev/null 2>&1; then \ + mkdir -p "$(UVHS_WORK_DIR)/uvhs"; \ + cp "$(ROOT_DIR)"/uvhs/assign_pin_*.tcl "$(UVHS_WORK_DIR)/uvhs/"; \ + fi + if [ -f "$(ROOT_DIR)/uvhs/partition.tcl" ]; then \ + cp "$(ROOT_DIR)/uvhs/partition.tcl" "$(UVHS_WORK_DIR)/script/partition.tcl"; \ + fi + if [ -f "$(ROOT_DIR)/uvhs/probe.tcl" ]; then \ + cp "$(ROOT_DIR)/uvhs/probe.tcl" "$(UVHS_WORK_DIR)/script/probe.tcl"; \ + else \ + : > "$(UVHS_WORK_DIR)/script/probe.tcl"; \ + fi + for dcp in \ + rtl/soc/AXI_bridge.dcp \ + rtl/soc/blk_mem_gen_0.dcp \ + rtl/soc/uvw_axi4_to_ddr4.dcp \ + rtl/soc/uvw_axi4_to_ddr4_Stub.v; do \ + if [ ! -f "$(UVHS_WORK_DIR)/$$dcp" ]; then \ + tar -xzf "$(UVHS_TARBALL)" -C "$(UVHS_WORK_DIR)" --strip-components=1 "$(UVHS_TEMPLATE)/$$dcp"; \ + fi; \ + done + if [ -n "$(strip $(UVHS_PREBUILT_DCP_DIR))" ]; then \ + test -d "$(UVHS_PREBUILT_DCP_DIR)"; \ + for dcp in \ + rtl/soc/AXI_bridge.dcp \ + rtl/soc/blk_mem_gen_0.dcp \ + rtl/soc/data_bridge.dcp \ + rtl/soc/vio_0.dcp \ + rtl/device/pcie/xdma_ep.dcp; do \ + if [ -f "$(UVHS_PREBUILT_DCP_DIR)/$$dcp" ]; then \ + mkdir -p "$$(dirname "$(UVHS_WORK_DIR)/$$dcp")"; \ + cp -f "$(UVHS_PREBUILT_DCP_DIR)/$$dcp" "$(UVHS_WORK_DIR)/$$dcp"; \ + echo "INFO: synced prebuilt DCP $$dcp from $(UVHS_PREBUILT_DCP_DIR)"; \ + fi; \ + done; \ + fi + if [ -n "$(strip $(UVHS_AXI_BRIDGE_DCP))" ]; then \ + test -s "$(UVHS_AXI_BRIDGE_DCP)"; \ + if [ -n "$(strip $(UVHS_AXI_BRIDGE_DCP_SHA256))" ]; then \ + actual="$$(sha256sum "$(UVHS_AXI_BRIDGE_DCP)" | awk '{print $$1}')"; \ + if [ "$$actual" != "$(strip $(UVHS_AXI_BRIDGE_DCP_SHA256))" ]; then \ + echo "ERROR: UVHS_AXI_BRIDGE_DCP sha256 mismatch: $$actual != $(strip $(UVHS_AXI_BRIDGE_DCP_SHA256))" >&2; \ + exit 1; \ + fi; \ + fi; \ + cp -f "$(UVHS_AXI_BRIDGE_DCP)" "$(UVHS_WORK_DIR)/rtl/soc/AXI_bridge.dcp"; \ + echo "INFO: synced explicit AXI_bridge DCP from $(UVHS_AXI_BRIDGE_DCP)"; \ + fi + if [ -n "$(strip $(UVHS_XDMA_EP_DCP))" ]; then \ + test -s "$(UVHS_XDMA_EP_DCP)"; \ + if [ -n "$(strip $(UVHS_XDMA_EP_DCP_SHA256))" ]; then \ + actual="$$(sha256sum "$(UVHS_XDMA_EP_DCP)" | awk '{print $$1}')"; \ + if [ "$$actual" != "$(strip $(UVHS_XDMA_EP_DCP_SHA256))" ]; then \ + echo "ERROR: UVHS_XDMA_EP_DCP sha256 mismatch: $$actual != $(strip $(UVHS_XDMA_EP_DCP_SHA256))" >&2; \ + exit 1; \ + fi; \ + fi; \ + mkdir -p "$(UVHS_WORK_DIR)/rtl/device/pcie"; \ + cp -f "$(UVHS_XDMA_EP_DCP)" "$(UVHS_WORK_DIR)/rtl/device/pcie/xdma_ep.dcp"; \ + echo "INFO: synced XDMA EP DCP from $(UVHS_XDMA_EP_DCP)"; \ + fi + if [ -n "$(strip $(UVHS_BASE_ASSEMBLE_FILE))" ]; then \ + test -s "$(UVHS_BASE_ASSEMBLE_FILE)"; \ + if [ -n "$(strip $(UVHS_BASE_ASSEMBLE_SHA256))" ]; then \ + actual="$$(sha256sum "$(UVHS_BASE_ASSEMBLE_FILE)" | awk '{print $$1}')"; \ + if [ "$$actual" != "$(strip $(UVHS_BASE_ASSEMBLE_SHA256))" ]; then \ + echo "ERROR: UVHS_BASE_ASSEMBLE_FILE sha256 mismatch: $$actual != $(strip $(UVHS_BASE_ASSEMBLE_SHA256))" >&2; \ + exit 1; \ + fi; \ + fi; \ + echo "INFO: use Hejian base assembly from $(UVHS_BASE_ASSEMBLE_FILE)"; \ + fi + +uvhs_export_vivado_ip: uvhs_prepare +ifeq ($(strip $(UVHS_EXPORT_IP)),1) + bash -lc 'source "$(ROOT_DIR)/uvhs/setenv.sh" && if [ ! -x "$$UV_XILINX_VIVADO/bin/vivado" ]; then echo "ERROR: vivado not found at $$UV_XILINX_VIVADO/bin/vivado"; exit 1; fi; vivado_version="$(UVHS_EXPORT_IP_VIVADO_VERSION)"; if [ -z "$$vivado_version" ]; then vivado_version="$$("$$UV_XILINX_VIVADO/bin/vivado" -version | sed -n "s/^Vivado v\\([^ ]*\\).*/\\1/p" | head -n 1)"; fi; $(UVHS_HEJIAN_FLOW_ENV) "$$UV_XILINX_VIVADO/bin/vivado" -mode batch -source "$(ROOT_DIR)/uvhs/export_vivado_ip.tcl" -tclargs --origin_dir "$(ROOT_DIR)" --out_dir "$(UVHS_WORK_DIR)" --vivado_version "$$vivado_version" --cpu "$(CPU)" --core_dir "$(CORE_DIR)" --jobs "$(UVHS_EXPORT_IP_JOBS)" $(if $(strip $(UVHS_EXPORT_IP_ONLY)),--only "$(UVHS_EXPORT_IP_ONLY)",) $(foreach ip,$(UVHS_EFFECTIVE_SKIP_VIVADO_EXPORT),--skip "$(ip)") $(if $(filter 1,$(UVHS_EXPORT_IP_FORCE)),--force,)' +else + @echo "Skip Vivado IP export because UVHS_EXPORT_IP=$(UVHS_EXPORT_IP)" +endif + +uvhs_sync_uvw_axi4_to_ddr4: uvhs_prepare + bash -lc 'src="$(UVHS_UVW_AXI4_TO_DDR4_SRC)"; candidates="$(UVHS_UVW_AXI4_TO_DDR4_SRC_CANDIDATES)"; work="$(UVHS_WORK_DIR)"; allow_template="$(UVHS_UVW_AXI4_TO_DDR4_ALLOW_TEMPLATE_FALLBACK)"; expected_md5="$(UVHS_UVW_AXI4_TO_DDR4_EXPECTED_MD5)"; if [ -z "$$src" ]; then for cand in $$candidates; do if [ -d "$$cand" ]; then src="$$cand"; break; fi; done; fi; mkdir -p "$$work/rtl/soc" "$$work/script"; if [ -z "$$src" ] && [ "$$allow_template" != "1" ]; then echo "ERROR: UVHS DDR IP source is required for uvhs_frontend; set UVHS_UVW_AXI4_TO_DDR4_SRC" >&2; exit 1; fi; if [ -n "$$src" ]; then test -d "$$src"; echo "INFO: use UVW AXI4-to-DDR4 IP source $$src"; case "$$src" in *blackbox_test*) echo "ERROR: refusing blackbox_test UVW AXI4-to-DDR4 source: $$src" >&2; exit 1;; esac; for rel in $(UVHS_UVW_AXI4_TO_DDR4_FILES); do base="$${rel##*/}"; dst="$$work/$$rel"; found=""; for cand in "$$src/$$rel" "$$src/$$base"; do if [ -f "$$cand" ]; then found="$$cand"; break; fi; done; if [ -z "$$found" ]; then found="$$(find "$$src" -type f -name "$$base" ! -path "*blackbox_test*" -size +0c -print -quit)"; fi; if [ -n "$$found" ]; then case "$$found" in *blackbox_test*) echo "ERROR: refusing blackbox_test UVW AXI4-to-DDR4 asset: $$found" >&2; exit 1;; esac; mkdir -p "$$(dirname "$$dst")"; cp -f "$$found" "$$dst"; echo "INFO: synced UVW AXI4-to-DDR4 IP asset $$rel from $$found"; elif [ -f "$$dst" ] && [ "$$allow_template" = "1" ]; then echo "INFO: keep existing UVW AXI4-to-DDR4 IP asset $$rel"; else echo "INFO: optional UVW AXI4-to-DDR4 IP asset not found: $$rel"; fi; done; else echo "INFO: no UVW AXI4-to-DDR4 IP override source configured; template fallback enabled"; fi; for rel in $(UVHS_UVW_AXI4_TO_DDR4_REQUIRED_FILES); do if [ ! -s "$$work/$$rel" ]; then echo "ERROR: missing required UVW AXI4-to-DDR4 IP asset $$work/$$rel"; exit 1; fi; done; if [ -n "$$expected_md5" ]; then actual_md5="$$(md5sum "$$work/rtl/soc/uvw_axi4_to_ddr4.dcp" | awk "{print \$$1}")"; if [ "$$actual_md5" != "$$expected_md5" ]; then echo "ERROR: UVW AXI4-to-DDR4 DCP md5 mismatch: $$actual_md5 != $$expected_md5" >&2; echo "ERROR: override UVHS_UVW_AXI4_TO_DDR4_EXPECTED_MD5 only when intentionally testing a different verified DDR IP" >&2; exit 1; fi; echo "INFO: verified UVW AXI4-to-DDR4 DCP md5 $$actual_md5"; fi' + +uvhs_show_uvw_axi4_to_ddr4: + bash -lc 'src="$(UVHS_UVW_AXI4_TO_DDR4_SRC)"; candidates="$(UVHS_UVW_AXI4_TO_DDR4_SRC_CANDIDATES)"; expected_md5="$(UVHS_UVW_AXI4_TO_DDR4_EXPECTED_MD5)"; if [ -z "$$src" ]; then for cand in $$candidates; do if [ -d "$$cand" ]; then src="$$cand"; break; fi; done; fi; if [ -z "$$src" ]; then echo "ERROR: no visible UVW AXI4-to-DDR4 IP source; set UVHS_UVW_AXI4_TO_DDR4_SRC or fetch it first" >&2; exit 1; fi; echo "UVHS_UVW_AXI4_TO_DDR4_SRC=$$src"; for rel in $(UVHS_UVW_AXI4_TO_DDR4_REQUIRED_FILES); do if [ ! -s "$$src/$$rel" ]; then found="$$(find "$$src" -type f -name "$${rel##*/}" ! -path "*blackbox_test*" -size +0c -print -quit)"; else found="$$src/$$rel"; fi; if [ -z "$$found" ]; then echo "ERROR: missing $$rel under $$src" >&2; exit 1; fi; echo "$$rel -> $$found"; done; actual_md5="$$(md5sum "$$src/rtl/soc/uvw_axi4_to_ddr4.dcp" | awk "{print \$$1}")"; echo "uvw_axi4_to_ddr4.dcp md5=$$actual_md5"; if [ -n "$$expected_md5" ] && [ "$$actual_md5" != "$$expected_md5" ]; then echo "ERROR: md5 mismatch: $$actual_md5 != $$expected_md5" >&2; exit 1; fi' + +uvhs_filelist: + if [ "$(strip $(CPU))" = "nutshell" ] && [ -n "$(strip $(CORE_DIR))" ]; then \ + bash "$(ROOT_DIR)/uvhs/tools/build/patch_nutshell_cdc.sh" "$(CORE_DIR)"; \ + fi + mkdir -p "$(dir $(UVHS_FILELIST))" + { \ + printf '+define+SYNTHESIS\n'; \ + printf '+define+XIANGSHAN_FPGA\n'; \ + printf '+define+UVHS_SOC_ADAPT\n'; \ + printf '+define+UVHS_NO_XILINX_CLK_PRIMS\n'; \ + printf '+define+DDR4_16G_X8\n'; \ + printf '+define+DQ64\n'; \ + printf '+define+DDR4_2400\n'; \ + printf '+define+DQ=64\n'; \ + printf '+define+MICRON_DDR\n'; \ + printf '+define+DDR4_16Gbx8\n'; \ + printf '+define+DDR4\n'; \ + printf '+define+SRAM_SYN\n'; \ + printf '+define+DATA_VERSION=0\n'; \ + if [ "$(strip $(CPU))" = "nutshell" ]; then \ + printf '+define+CPU_NUTSHELL\n'; \ + fi; \ + printf '+define+UVHS_EXTERNAL_UVW_AXI4_TO_DDR4\n'; \ + printf '+define+UVHS_UVW_AXI4_TO_DDR4\n'; \ + if [ "$(strip $(UVHS_CPU_DEBUG_CLK))" = "1" ]; then \ + printf '+define+UVHS_CPU_DEBUG_CLK\n'; \ + fi; \ + if [ -n "$(strip $(CHI_DIR))" ]; then \ + printf '+define+MSI_MODE\n'; \ + printf '+define+CONFIG_USE_XSCORE_CHI\n'; \ + else \ + printf '+define+CONFIG_USE_XSCORE_AXI\n'; \ + fi; \ + printf '+incdir+%s/src/rtl/common\n' "$(ROOT_DIR)"; \ + if [ -n "$(strip $(CORE_DIR))" ] && [ -d "$(CORE_DIR)" ]; then \ + printf '+incdir+%s\n' "$(CORE_DIR)"; \ + test ! -d "$(CORE_DIR)/rtl" || printf '+incdir+%s/rtl\n' "$(CORE_DIR)"; \ + test ! -d "$(CORE_DIR)/build/generated-src" || printf '+incdir+%s/build/generated-src\n' "$(CORE_DIR)"; \ + test ! -d "$(CORE_DIR)/generated-src" || printf '+incdir+%s/generated-src\n' "$(CORE_DIR)"; \ + fi; \ + find "$(ROOT_DIR)/src/rtl/common" -type f \( -name "*.v" -o -name "*.sv" -o -name "*.vh" -o -name "*.svh" \) \ + ! -name "u0_xdma.v" ! -name "jtag_ddr_subsys_wrapper.v" -printf '%p\n' | sort; \ + test -z "$(strip $(CPU))" || test ! -d "$(ROOT_DIR)/src/rtl/$(CPU)" || find "$(ROOT_DIR)/src/rtl/$(CPU)" -type f \( -name "*.v" -o -name "*.sv" -o -name "*.vh" -o -name "*.svh" \) -printf '%p\n' | sort; \ + if [ -n "$(strip $(CORE_DIR))" ] && [ -d "$(CORE_DIR)" ]; then \ + if [ -d "$(CORE_DIR)/build/rtl" ]; then \ + test ! -d "$(CORE_DIR)/build/generated-src" || find "$(CORE_DIR)/build/generated-src" -type f \( -name "*.vh" -o -name "*.svh" \) -printf '%p\n' | sort; \ + find "$(CORE_DIR)/build/rtl" -type f \( -name "*.v" -o -name "*.sv" -o -name "*.vh" -o -name "*.svh" \) -printf '%p\n' | sort; \ + else \ + find "$(CORE_DIR)" \ + -path "$(CORE_DIR)/rtl/verification" -prune -o \ + -path "$(CORE_DIR)/20*" -prune -o \ + -path "$(CORE_DIR)/out" -prune -o \ + -type f \( -name "*.v" -o -name "*.sv" -o -name "*.vh" -o -name "*.svh" \) -printf '%p\n' | sort; \ + fi; \ + fi; \ + if [ -n "$(strip $(CHI_DIR))" ] && [ -d "$(CHI_DIR)" ]; then \ + find "$(CHI_DIR)" -type f \( -name "*.v" -o -name "*.sv" -o -name "*.vh" -o -name "*.svh" \) -printf '%p\n' | sort; \ + fi; \ + } | awk -f "$(ROOT_DIR)/uvhs/filelist.awk" > "$(UVHS_FILELIST)" + +uvhs_check_modules: uvhs_filelist + bash "$(ROOT_DIR)/uvhs/check_modules.sh" "$(UVHS_FILELIST)" "$(UVHS_REQUIRED_MODULES)" + +uvhs_frontend: UVHS_WORK_DIR = $(ENV_SCRIPTS_HOME)/fpga_diff_uvhs_$(CPU)$(if $(strip $(SUFFIX)),-$(strip $(SUFFIX)),) +uvhs_frontend: UVHS_ASSEMBLE_FILE = $(ROOT_DIR)/uvhs/assemble_uvhs.tcl +uvhs_frontend: uvhs_export_vivado_ip uvhs_sync_uvw_axi4_to_ddr4 uvhs_filelist uvhs_check_modules + bash -lc 'set -o pipefail; source "$(ROOT_DIR)/uvhs/setenv.sh" && cd "$(UVHS_WORK_DIR)" && $(UVHS_HEJIAN_FLOW_ENV) UVHS_TOP="$(UVHS_TOP)" UVHS_FILELIST="./rtl/filelist.f" UVHS_REQUIRED_DCP_MODULES="$(UVHS_REQUIRED_DCP_MODULES)" UVHS_CPU_CLK_PERIOD_NS="$(UVHS_CPU_CLK_PERIOD_NS)" UVHS_SKIP_BOARD_CONSTRAINTS="$(UVHS_SKIP_BOARD_CONSTRAINTS)" UVHS_PROBE_FILE="$(UVHS_PROBE_FILE)" UVHS_ASSIGN_PIN_FILE="$(UVHS_ASSIGN_PIN_FILE)" UVHS_TIMING_FILE="$(UVHS_TIMING_FILE)" UVHS_PARTITION_FILE="$(UVHS_PARTITION_FILE)" UVHS_ASSEMBLE_FILE="$(UVHS_ASSEMBLE_FILE)" UVHS_USE_LSF="$(UVHS_USE_LSF)" UVHS_FRONTEND_THREADS="$(UVHS_FRONTEND_THREADS)" UVHS_FRONTEND_PROCESSES="$(UVHS_FRONTEND_PROCESSES)" UVHS_FPGA_THREADS="$(UVHS_FPGA_THREADS)" UVHS_FPGA_PROCESSES="$(UVHS_FPGA_PROCESSES)" bash "$$UV_ROOT/bin/uv_shell" -bypass_vivado_version_check -s "$(UVHS_FRONTEND_TCL)" |& tee frontend_run.log' + +uvhs_backend: UVHS_WORK_DIR = $(ENV_SCRIPTS_HOME)/fpga_diff_uvhs_$(CPU)$(if $(strip $(SUFFIX)),-$(strip $(SUFFIX)),) +uvhs_backend: UVHS_ASSEMBLE_FILE = $(ROOT_DIR)/uvhs/assemble_uvhs.tcl +uvhs_backend: + bash -lc 'set -o pipefail; source "$(ROOT_DIR)/uvhs/setenv.sh" && cd "$(UVHS_WORK_DIR)" && $(UVHS_HEJIAN_FLOW_ENV) UVHS_TOP="$(UVHS_TOP)" UVHS_CPU_CLK_PERIOD_NS="$(UVHS_CPU_CLK_PERIOD_NS)" UVHS_ASSIGN_PIN_FILE="$(UVHS_ASSIGN_PIN_FILE)" UVHS_PIN_OVERRIDE_FILE="$(UVHS_PIN_OVERRIDE_FILE)" UVHS_ASSEMBLE_FILE="$(UVHS_ASSEMBLE_FILE)" UVHS_STOP_AFTER_PARTITION="$(UVHS_STOP_AFTER_PARTITION)" UVHS_STOP_AFTER_GENSCRIPT="$(UVHS_STOP_AFTER_GENSCRIPT)" UVHS_ROUTE_ENABLE_HOLD_EXPN_BAILOUT="$(UVHS_ROUTE_ENABLE_HOLD_EXPN_BAILOUT)" UVHS_USE_LSF="$(UVHS_USE_LSF)" UVHS_FPGA_THREADS="$(UVHS_FPGA_THREADS)" UVHS_FPGA_PROCESSES="$(UVHS_FPGA_PROCESSES)" UVHS_STRATEGY_NUM_RETRY="$(UVHS_STRATEGY_NUM_RETRY)" UVHS_LUT_FILL_RATE="$(UVHS_LUT_FILL_RATE)" UVHS_LUT6_FILL_RATE="$(UVHS_LUT6_FILL_RATE)" bash "$$UV_ROOT/bin/uv_shell" -bypass_vivado_version_check -s "$(ROOT_DIR)/uvhs/backend_run.tcl" |& tee backend_run.log' + +uvhs_all: UVHS_WORK_DIR = $(ENV_SCRIPTS_HOME)/fpga_diff_uvhs_$(CPU)$(if $(strip $(SUFFIX)),-$(strip $(SUFFIX)),) +uvhs_all: uvhs_frontend uvhs_backend + +uvhs_clean: + rm -rf "$(UVHS_WORK_DIR)" + +uvhs_env: + @echo "source $(ROOT_DIR)/uvhs/setenv.sh" + +uvhs_hejian_pcie_x4_env: + @bash -lc 'source "$(ROOT_DIR)/uvhs/setenv.sh" >/dev/null 2>&1 || true; ROOT_DIR="$(ROOT_DIR)" bash "$(ROOT_DIR)/uvhs/hejian_pcie_x4_env.sh" print' + +uvhs_hejian_pcie_x4_preflight: UVHS_CPU_CLK_PERIOD_NS = 40 +uvhs_hejian_pcie_x4_preflight: UVHS_CPU_DEBUG_CLK = 1 +uvhs_hejian_pcie_x4_preflight: + @bash -lc 'source "$(ROOT_DIR)/uvhs/setenv.sh" >/dev/null 2>&1 || true; $(UVHS_HEJIAN_FLOW_ENV) ROOT_DIR="$(ROOT_DIR)" bash "$(ROOT_DIR)/uvhs/hejian_pcie_x4_env.sh" check' + +uvhs_hejian_pcie_x4_nutshell_all: CPU = nutshell +uvhs_hejian_pcie_x4_nutshell_all: XDMA_LINK_WIDTH = X4 +uvhs_hejian_pcie_x4_nutshell_all: XDMA_ENABLE_PF0_BAR1 = 1 +uvhs_hejian_pcie_x4_nutshell_all: XDMA_AXILITE_MASTER_SCALE = Kilobytes +uvhs_hejian_pcie_x4_nutshell_all: XDMA_AXILITE_MASTER_SIZE = 512 +uvhs_hejian_pcie_x4_nutshell_all: XDMA_EXPECTED_VENDOR = 0x10ee +uvhs_hejian_pcie_x4_nutshell_all: XDMA_EXPECTED_DEVICE = 0x9048 +uvhs_hejian_pcie_x4_nutshell_all: XDMA_EXPECTED_BAR0_SIZE = 0x80000 +uvhs_hejian_pcie_x4_nutshell_all: XDMA_EXPECTED_BAR1_SIZE = 0x10000 +uvhs_hejian_pcie_x4_nutshell_all: UVHS_DESIGN_NAME = VU19P_X4 +uvhs_hejian_pcie_x4_nutshell_all: PLATFORM = U2.2 +uvhs_hejian_pcie_x4_nutshell_all: UVHS_TARGET_BOARD = B0 +uvhs_hejian_pcie_x4_nutshell_all: UVHS_TARGET_PACK = B0 +uvhs_hejian_pcie_x4_nutshell_all: UVHS_TARGET_FPGA = F2 +uvhs_hejian_pcie_x4_nutshell_all: UVHS_TARGET_FPGA_LOWER = b0.f2 +uvhs_hejian_pcie_x4_nutshell_all: UVHS_FPGA_COUNT = 1 +uvhs_hejian_pcie_x4_nutshell_all: UVHS_CPU_CLK_PERIOD_NS = 40 +uvhs_hejian_pcie_x4_nutshell_all: UVHS_CPU_DEBUG_CLK = 1 +uvhs_hejian_pcie_x4_nutshell_all: UVHS_PNR_STRATEGY = Strategy_uv_high_fanout_explore +uvhs_hejian_pcie_x4_nutshell_all: UVHS_COMPILE_STRATEGY_NUM = 1 +uvhs_hejian_pcie_x4_nutshell_all: UVHS_COMPILE_STRATEGY0 = uv_high_fanout_explore +uvhs_hejian_pcie_x4_nutshell_all: UVHS_ASSEMBLE_FILE = $(ROOT_DIR)/uvhs/assemble_uvhs.tcl +uvhs_hejian_pcie_x4_nutshell_all: UVHS_ASSIGN_PIN_FILE = $(ROOT_DIR)/uvhs/assign_pin_nutshell_f2.tcl +uvhs_hejian_pcie_x4_nutshell_all: uvhs_hejian_pcie_x4_preflight uvhs_all + +uvhs_hejian_pcie_x4_xiangshan_all: CPU = kmh +uvhs_hejian_pcie_x4_xiangshan_all: XDMA_LINK_WIDTH = X4 +uvhs_hejian_pcie_x4_xiangshan_all: XDMA_ENABLE_PF0_BAR1 = 1 +uvhs_hejian_pcie_x4_xiangshan_all: XDMA_AXILITE_MASTER_SCALE = Kilobytes +uvhs_hejian_pcie_x4_xiangshan_all: XDMA_AXILITE_MASTER_SIZE = 512 +uvhs_hejian_pcie_x4_xiangshan_all: XDMA_EXPECTED_VENDOR = 0x10ee +uvhs_hejian_pcie_x4_xiangshan_all: XDMA_EXPECTED_DEVICE = 0x9048 +uvhs_hejian_pcie_x4_xiangshan_all: XDMA_EXPECTED_BAR0_SIZE = 0x80000 +uvhs_hejian_pcie_x4_xiangshan_all: XDMA_EXPECTED_BAR1_SIZE = 0x10000 +uvhs_hejian_pcie_x4_xiangshan_all: UVHS_DESIGN_NAME = VU19P_X4 +uvhs_hejian_pcie_x4_xiangshan_all: PLATFORM = U2.2 +uvhs_hejian_pcie_x4_xiangshan_all: UVHS_TARGET_BOARD = B0 +uvhs_hejian_pcie_x4_xiangshan_all: UVHS_TARGET_PACK = B0 +uvhs_hejian_pcie_x4_xiangshan_all: UVHS_TARGET_FPGA = F2 +uvhs_hejian_pcie_x4_xiangshan_all: UVHS_TARGET_FPGA_LOWER = b0.f2 +uvhs_hejian_pcie_x4_xiangshan_all: UVHS_FPGA_COUNT = 1 +uvhs_hejian_pcie_x4_xiangshan_all: UVHS_KEEP_FPGAS = b0.f2 +uvhs_hejian_pcie_x4_xiangshan_all: UVHS_CPU_CLK_PERIOD_NS = 40 +uvhs_hejian_pcie_x4_xiangshan_all: UVHS_CPU_DEBUG_CLK = 1 +uvhs_hejian_pcie_x4_xiangshan_all: UVHS_PNR_STRATEGY = Strategy_uv_high_fanout_explore +uvhs_hejian_pcie_x4_xiangshan_all: UVHS_COMPILE_STRATEGY_NUM = 1 +uvhs_hejian_pcie_x4_xiangshan_all: UVHS_COMPILE_STRATEGY0 = uv_high_fanout_explore +uvhs_hejian_pcie_x4_xiangshan_all: UVHS_LUT_FILL_RATE = 80 +uvhs_hejian_pcie_x4_xiangshan_all: UVHS_LUT6_FILL_RATE = 30 +uvhs_hejian_pcie_x4_xiangshan_all: UVHS_ROUTE_ENABLE_HOLD_EXPN_BAILOUT = 0 +uvhs_hejian_pcie_x4_xiangshan_all: UVHS_ASSEMBLE_FILE = $(ROOT_DIR)/uvhs/assemble_uvhs.tcl +uvhs_hejian_pcie_x4_xiangshan_all: UVHS_ASSIGN_PIN_FILE = $(ROOT_DIR)/uvhs/assign_pin_nutshell_f2.tcl +uvhs_hejian_pcie_x4_xiangshan_all: uvhs_hejian_pcie_x4_preflight uvhs_all + +uvhs_preflight: + test -f "$(UVHS_TARBALL)" + bash -lc 'source "$(ROOT_DIR)/uvhs/setenv.sh"; command -v uv_shell; command -v csh; test -x "$$UV_XILINX_VIVADO/bin/vivado"; "$$UV_XILINX_VIVADO/bin/vivado" -version | head -1' + +uvhs_status: + bash "$(ROOT_DIR)/uvhs/status.sh" "$(UVHS_WORK_DIR)" + +uvhs_check_timing_clean: + bash -lc 'set -euo pipefail; summary="$(UVHS_BITSTREAM_DIR)/after_xtalk_fix_timing_summary.txt"; if [ ! -f "$$summary" ]; then pnr_root="$(UVHS_WORK_DIR)/hw.dat/Compile/PnR/$(UVHS_TARGET_PACK)/$(UVHS_TARGET_FPGA)/vivado/Rundir"; done_dirs="$$(find "$$pnr_root" -name ".Task_strategy_*.timingclean" -printf "%h\n" 2>/dev/null | sort -u || true)"; if [ -n "$$done_dirs" ]; then summary="$$(printf "%s\n" $$done_dirs | while read -r dir; do test -f "$$dir/bitstream/after_xtalk_fix_timing_summary.txt" && printf "%s\n" "$$dir/bitstream/after_xtalk_fix_timing_summary.txt"; done | head -1)"; fi; fi; if [ ! -f "$$summary" ]; then echo "ERROR: UVHS timing summary not found: $$summary" >&2; summaries="$$(find "$(UVHS_WORK_DIR)/hw.dat/Compile/PnR/$(UVHS_TARGET_PACK)/$(UVHS_TARGET_FPGA)/vivado/Rundir" -path "*/bitstream/after_xtalk_fix_timing_summary.txt" -print 2>/dev/null | sort || true)"; if [ -n "$$summaries" ]; then echo "Available timing summaries:" >&2; printf " %s\n" $$summaries >&2; fi; exit 1; fi; if grep -q "Timing constraints are not met" "$$summary"; then echo "ERROR: UVHS timing is not clean: $$summary" >&2; sed -n "/^[[:space:]]*WNS(ns)/,+5p" "$$summary" >&2; exit 1; fi; awk '\''/^[[:space:]]*-?[0-9]+[.][0-9]+[[:space:]]+-?[0-9]+[.][0-9]+/ { found=1; if ($$1 < 0 || $$2 < 0) exit 1 } END { exit found ? 0 : 1 }'\'' "$$summary" || { echo "ERROR: UVHS WNS/TNS gate failed: $$summary" >&2; sed -n "/^[[:space:]]*WNS(ns)/,+5p" "$$summary" >&2; exit 1; }; echo "INFO: UVHS timing clean: $$summary"' + +uvhs_export_ltx: uvhs_check_timing_clean + bash -lc 'set -euo pipefail; script="$(ROOT_DIR)/tools/write_debug_probes.tcl"; if [ ! -f "$$script" ]; then echo "INFO: skip optional LTX export; local debug-probe script is absent"; exit 0; fi; bitstream_dir="$(UVHS_RESOLVED_BITSTREAM_DIR)"; test -n "$$bitstream_dir"; test -f "$$bitstream_dir/pnr_routed.dcp"; ltx="$${bitstream_dir}/fpga_top_debug.ltx"; if [ -f "$$ltx" ]; then echo "INFO: UVHS LTX already exists: $$ltx"; exit 0; fi; source "$(ROOT_DIR)/uvhs/setenv.sh"; "$$UV_XILINX_VIVADO/bin/vivado" -mode batch -source "$$script" -tclargs "$$bitstream_dir/pnr_routed.dcp" "$$ltx"' + +uvhs_package_bitstream: uvhs_export_ltx + bash -lc 'set -euo pipefail; bitstream_dir="$(UVHS_RESOLVED_BITSTREAM_DIR)"; test -n "$$bitstream_dir"; test -f "$$bitstream_dir/pnr.bit"; mkdir -p "$(UVHS_BIT_HOME)"; ln -sf "$$bitstream_dir/pnr.bit" "$(UVHS_BIT_HOME)/fpga_top_debug.bit"; if [ -f "$$bitstream_dir/fpga_top_debug.ltx" ]; then ln -sf "$$bitstream_dir/fpga_top_debug.ltx" "$(UVHS_BIT_HOME)/fpga_top_debug.ltx"; fi; echo "FPGA_BIT_HOME=$(UVHS_BIT_HOME)"; echo "FPGA_BITSTREAM_DIR=$$bitstream_dir"' diff --git a/fpga_diff/uvhs/README.md b/fpga_diff/uvhs/README.md new file mode 100644 index 00000000..dc341d24 --- /dev/null +++ b/fpga_diff/uvhs/README.md @@ -0,0 +1,76 @@ +# UVHS FPGA-Diff Flow + +This directory is the source flow for the Hejian UVHS FPGA-Diff target. It is +separate from the legacy standalone Vivado targets in `fpga_diff/`. + +## Entry Points + +- `../Makefile`: public `uvhs_*` forwarding targets. +- `Makefile`: UVHS frontend, backend, packaging, and preflight implementation. +- `setenv.sh`: repository-generic tool and license setup. +- `setenv.local.example.sh`: template for machine-local settings. Copy it to + `setenv.local.sh`; the local file is ignored by Git. +- `flow.md`: detailed build, signoff, runtime, and troubleshooting runbook. + +## Build Stages + +Run the stages explicitly so frontend, backend, packaging, and board runtime +failures remain distinguishable: + +```sh +make uvhs_tools_check +make uvhs_frontend CPU=nutshell CORE_DIR=/path/to/generated/core SUFFIX= +make uvhs_backend CPU=nutshell SUFFIX= +make uvhs_package_bitstream CPU=nutshell \ + UVHS_WORK_DIR=/path/to/fpga_diff_uvhs_nutshell- +``` + +`uvhs_all` is available for a full build. `UVHS_USE_LSF=0` is the local-run +override when no scheduler is available. Keep `UVHS_WORK_DIR` outside the +source tree when possible; generated `fpga_diff_uvhs_*` directories are +ignored. + +## Runtime And Workload + +The minimal runtime stage contains `hw.dat` and the download script under +`../user_script/`: + +- `hw_run_download.tcl`: load the database, download the bitstream, initialize + the board, and release the software resets. + +For repeated experiments use `uvhs_tagged_runtime.sh`. Every run must have a +unique `UVHS_RUN_TAG`, stage directory, command file, work directory, tmux +session, and log path. Cleanup is exact-tag only: + +```sh +UVHS_RUN_TAG= \ +UVHS_STAGE_DIR=/path/to/runtime-stage \ + bash uvhs_tagged_runtime.sh cleanup +``` + +Do not add DDR writes or manual reset toggles to `hw_run_download.tcl`. The +normal `fpga-host` flow writes its workload through H2C. + +## Supporting Tools + +Tracked supporting helpers live under `tools/build/` and contain only +generated-RTL fixes applied while constructing the file list. Board-specific +probe, BAR, XDMA, strace, and backdoor-DDR debug scripts are kept as local +tools and intentionally excluded from Git. + +The Makefile accepts `UVHS_PROBE_FILE=/path/to/local/probe.tcl`; without a +local probe file it generates an intentionally empty probe script. + +## Useful Checks + +```sh +make uvhs_preflight +make uvhs_check_modules CPU=nutshell CORE_DIR=/path/to/generated/core +make uvhs_status CPU=nutshell \ + UVHS_WORK_DIR=/path/to/fpga_diff_uvhs_nutshell- +``` + +The generated database, bitstream, reports, logs, local debug helpers, and +temporary runtime files are not tracked. The source of truth is the Makefile, +frontend/backend Tcl, RTL wrapper, constraints, and download runtime listed +above. diff --git a/fpga_diff/uvhs/assemble_uvhs.tcl b/fpga_diff/uvhs/assemble_uvhs.tcl new file mode 100644 index 00000000..cc8ce0e5 --- /dev/null +++ b/fpga_diff/uvhs/assemble_uvhs.tcl @@ -0,0 +1,157 @@ +################################################################################ +# UVHS-only board assembly overlay for fpga_diff. +################################################################################ + +proc uvhs_env_or_default {name default} { + if {[info exists ::env($name)] && $::env($name) ne ""} { + return $::env($name) + } + return $default +} + +set uvhs_base_assemble [uvhs_env_or_default UVHS_BASE_ASSEMBLE_FILE ./script/1B_4F_HGC_assemble.tcl] +set uvhs_target_fpga [string tolower [uvhs_env_or_default UVHS_TARGET_FPGA_LOWER b0.f2]] +set uvhs_known_fpgas {b0.f0 b0.f1 b0.f2 b0.f3} +set uvhs_keep_fpgas {} +foreach uvhs_fpga [split [uvhs_env_or_default UVHS_KEEP_FPGAS $uvhs_target_fpga]] { + set uvhs_fpga [string tolower [string trim $uvhs_fpga]] + if {$uvhs_fpga ne "" && [lsearch -exact $uvhs_keep_fpgas $uvhs_fpga] < 0} { + lappend uvhs_keep_fpgas $uvhs_fpga + } +} +if {[lsearch -exact $uvhs_keep_fpgas $uvhs_target_fpga] < 0} { + lappend uvhs_keep_fpgas $uvhs_target_fpga +} +foreach uvhs_fpga $uvhs_keep_fpgas { + if {[lsearch -exact $uvhs_known_fpgas $uvhs_fpga] < 0} { + error "UVHS_KEEP_FPGAS contains unknown FPGA '$uvhs_fpga'" + } +} +# The deployed U2 exposes the B0 PLL clock bank directly and reports no +# external ClockHub at runtime. Keep the overlay opt-in for other systems. +set uvhs_enable_clock_hub [expr {[uvhs_env_or_default UVHS_ENABLE_CLOCK_HUB 0] eq "1"}] + +if {![file exists $uvhs_base_assemble]} { + error "missing base assembly file: $uvhs_base_assemble" +} + +set uvhs_fh [open $uvhs_base_assemble r] +set uvhs_base_data [read $uvhs_fh] +close $uvhs_fh + +set uvhs_remove_dc_instances {} +foreach uvhs_line [split $uvhs_base_data "\n"] { + set uvhs_trimmed_line [string trim $uvhs_line] + set uvhs_lower_line [string tolower $uvhs_trimmed_line] + if {[regexp {^config_hw[ \t]+-connect_daughter_card[ \t]+\{([^ \t]+)[ \t]+([^ \t]+)\.FMC\}} $uvhs_trimmed_line -> uvhs_connector uvhs_instance]} { + set uvhs_lower_connector [string tolower $uvhs_connector] + set uvhs_connector_fpga "" + foreach uvhs_fpga $uvhs_known_fpgas { + if {[string first $uvhs_fpga $uvhs_lower_connector] == 0} { + set uvhs_connector_fpga $uvhs_fpga + break + } + } + if {$uvhs_connector_fpga ne "" && [lsearch -exact $uvhs_keep_fpgas $uvhs_connector_fpga] < 0} { + lappend uvhs_remove_dc_instances $uvhs_instance + } + } +} + +set uvhs_filtered_lines [list] +foreach uvhs_line [split $uvhs_base_data "\n"] { + set uvhs_trimmed_line [string trim $uvhs_line] + if {[regexp {^config_hw[ \t]+-create_(daughter_card|peripheral)[ \t]+[^ \t]+[ \t]+-instance[ \t]+([^ \t]+)} $uvhs_trimmed_line -> uvhs_create_type uvhs_instance] && + [lsearch -exact $uvhs_remove_dc_instances $uvhs_instance] >= 0} { + puts "INFO: UVHS selected-FPGA overlay: remove unused daughter-card instance: $uvhs_trimmed_line" + continue + } + if {[regexp {^config_hw[ \t]+-unplug_fpga[ \t]+([^ \t]+)} $uvhs_trimmed_line -> uvhs_unplug_fpga]} { + set uvhs_unplug_fpga [string tolower $uvhs_unplug_fpga] + if {[lsearch -exact $uvhs_keep_fpgas $uvhs_unplug_fpga] >= 0} { + puts "INFO: UVHS selected-FPGA overlay: ignore base unplug for kept FPGA $uvhs_unplug_fpga" + continue + } + } + if {[regexp {^config_hw[ \t]+-connect_daughter_card[ \t]+\{([^ \t]+)[ \t]+([^ \t]+)\.FMC\}} $uvhs_trimmed_line -> uvhs_connector uvhs_instance]} { + set uvhs_lower_connector [string tolower $uvhs_connector] + set uvhs_connector_fpga "" + foreach uvhs_fpga $uvhs_known_fpgas { + if {[string first $uvhs_fpga $uvhs_lower_connector] == 0} { + set uvhs_connector_fpga $uvhs_fpga + break + } + } + if {$uvhs_connector_fpga ne "" && [lsearch -exact $uvhs_keep_fpgas $uvhs_connector_fpga] < 0} { + puts "INFO: UVHS selected-FPGA overlay: remove unused daughter-card link: $uvhs_trimmed_line" + continue + } + } + if {[string match "config_hw -connect_fpga *" $uvhs_trimmed_line]} { + set uvhs_lower_line [string tolower $uvhs_trimmed_line] + set uvhs_touches_kept 0 + set uvhs_touches_unused 0 + foreach uvhs_fpga $uvhs_known_fpgas { + if {[string first $uvhs_fpga $uvhs_lower_line] >= 0} { + if {[lsearch -exact $uvhs_keep_fpgas $uvhs_fpga] >= 0} { + set uvhs_touches_kept 1 + } else { + set uvhs_touches_unused 1 + } + } + } + if {$uvhs_touches_unused || !$uvhs_touches_kept} { + puts "INFO: UVHS selected-FPGA overlay: remove unused FPGA link: $uvhs_trimmed_line" + continue + } + } + lappend uvhs_filtered_lines $uvhs_line +} +set uvhs_base_data [join $uvhs_filtered_lines "\n"] + +set uvhs_unplug_overlay "" +foreach uvhs_fpga $uvhs_known_fpgas { + if {[lsearch -exact $uvhs_keep_fpgas $uvhs_fpga] >= 0} { + append uvhs_unplug_overlay "#config_hw -unplug_fpga $uvhs_fpga\n" + } else { + append uvhs_unplug_overlay "puts \"INFO: UVHS selected-FPGA overlay: unplug $uvhs_fpga, keep $uvhs_keep_fpgas\"\n" + append uvhs_unplug_overlay "config_hw -unplug_fpga $uvhs_fpga\n" + } +} +set uvhs_create_board_replacements [regsub -line {^(config_hw[ \t]+-create_board_instance[ \t]+1[ \t]*)$} \ + $uvhs_base_data "\\1\n$uvhs_unplug_overlay" uvhs_base_data] +if {$uvhs_create_board_replacements != 1} { + puts "WARN: UVHS single-FPGA overlay: failed to insert unplug commands after create_board_instance; prepend overlay" + set uvhs_base_data "$uvhs_unplug_overlay\n$uvhs_base_data" +} + +set uvhs_late_overlay "" +if {$uvhs_enable_clock_hub && ![regexp -line {^config_hw[ \t]+-create_clock_hub(?:[ \t]|$)} $uvhs_base_data]} { + # U2 global clocks are driven through the external ClockHub. The stock + # 1B/4F assembly has no ClockHub connection, leaving single-FPGA DUT clocks + # assigned to unplugged inter-FPGA connectors with no physical source. + append uvhs_late_overlay { +puts "INFO: UVHS single-board overlay: create and connect U2 ClockHub" +config_hw -create_clock_hub +config_hw -connect_board_clk {b0.GLBOUT0 hub0_0.IN00} -cable NV_CLK_1000 +config_hw -connect_board_clk {b0.GLBOUT1 hub0_0.IN04} -cable NV_CLK_1000 +config_hw -connect_board_clk {b0.GLBOUT2 hub0_0.IN08} -cable NV_CLK_1000 +config_hw -connect_board_clk {b0.GLBOUT3 hub0_0.IN12} -cable NV_CLK_1000 +config_hw -connect_board_clk {hub0_0.OUT00_0 b0.GLBIN0} -cable NV_CLK_1000 +config_hw -connect_board_clk {hub0_0.OUT00_1 b0.GLBIN1} -cable NV_CLK_1000 +config_hw -connect_board_clk {hub0_0.OUT00_2 b0.GLBIN2} -cable NV_CLK_1000 +config_hw -connect_board_clk {hub0_0.OUT00_3 b0.GLBIN3} -cable NV_CLK_1000 +} +} +set uvhs_replacements [regsub -line {^config_hw[ \t]+-assemble[ \t]*$} $uvhs_base_data "$uvhs_late_overlay\nconfig_hw -assemble" uvhs_patched_data] +if {$uvhs_replacements != 1} { + error "failed to patch assembly file before config_hw -assemble: $uvhs_base_assemble" +} + +set uvhs_overlay_file [file join [pwd] script .uvhs_assemble_single_fpga.tcl] +set uvhs_fh [open $uvhs_overlay_file w] +puts -nonewline $uvhs_fh $uvhs_patched_data +close $uvhs_fh + +puts "INFO: source UVHS assembly overlay $uvhs_overlay_file" +source $uvhs_overlay_file diff --git a/fpga_diff/uvhs/assign_pin_nutshell_f2.tcl b/fpga_diff/uvhs/assign_pin_nutshell_f2.tcl new file mode 100644 index 00000000..2b35089a --- /dev/null +++ b/fpga_diff/uvhs/assign_pin_nutshell_f2.tcl @@ -0,0 +1,118 @@ +################################################################################ +# Pin assignment for NutShell fpga_diff on a single U2.2 VU19P FPGA (b0.f2). +# +# This file is intended for UVHS flow bring-up on the current lab machine. The +# reference flow keeps only b0.f2 plugged in, so all user-visible top ports must +# bind to b0.F2_* connectors instead of the original mixed F0/F1/F2 template. +################################################################################ + +proc assign_indexed_ports {prefix count connector indices} { + for {set i 0} {$i < $count} {incr i} { + set port [format {%s[%d]} $prefix $i] + assign_pin -port $port -connector $connector -index [lindex $indices $i] + } +} + +proc pin_name {top port} { + if {$top eq "" || $top eq "none"} { + return $port + } + return "${top}.${port}" +} + +set top [env_or_default UVHS_ASSIGN_PIN_TOP [env_or_default UVHS_TOP fpga_top_debug]] +set uvhs_skip_pcie_pins [expr {[env_or_default UVHS_SKIP_PCIE_PINS 0] eq "1"}] +set xdma_link_width [string toupper [string trim [env_or_default XDMA_LINK_WIDTH X4]]] +if {$xdma_link_width ni {X4 X8}} { + error "XDMA_LINK_WIDTH must be one of X4/X8, got '$xdma_link_width'" +} +set xdma_lane_count [expr {$xdma_link_width eq "X8" ? 8 : 4}] + +set apc16_indices { + 101 102 104 105 114 115 117 57 + 81 82 84 85 94 95 97 98 + 61 62 64 65 74 75 77 78 + 118 58 54 55 44 45 41 42 +} + +proc apc16_pin {port slot} { + global apc16_indices + global top + assign_pin -port [pin_name $top $port] -connector b0.F2_APC16 -index [lindex $apc16_indices $slot] +} + +# Low-speed debug/control pins on the unused f2 APC16 connector. +# rstn_sw* are exported as UVHS global resets and must not also be assign_pin'd. +apc16_pin led0 3 +apc16_pin led2 4 +apc16_pin led3 5 + +# UART0 defaults to the F2 APC16 sideband connector. A two-FPGA build can +# route it through the F1 UV_FMCH_FLASH USB-UART with FMC indices 311/270. +set uvhs_uart0_connector [env_or_default UVHS_UART0_CONNECTOR b0.F2_APC16] +set uvhs_uart0_tx_index [env_or_default UVHS_UART0_TX_INDEX [lindex $apc16_indices 6]] +set uvhs_uart0_rx_index [env_or_default UVHS_UART0_RX_INDEX [lindex $apc16_indices 7]] +puts "INFO: UART0 pins: TX ${uvhs_uart0_connector}\[$uvhs_uart0_tx_index\], RX ${uvhs_uart0_connector}\[$uvhs_uart0_rx_index\]" +assign_pin -port [pin_name $top uart0_sout] -connector $uvhs_uart0_connector -index $uvhs_uart0_tx_index +assign_pin -port [pin_name $top uart0_sin] -connector $uvhs_uart0_connector -index $uvhs_uart0_rx_index +apc16_pin uart1_sout 8 +apc16_pin uart1_sin 9 +apc16_pin uart2_sout 10 +apc16_pin uart2_sin 11 + +apc16_pin JTAG_TCK 12 +apc16_pin JTAG_TMS 13 +apc16_pin JTAG_TDI 14 +apc16_pin JTAG_TDO 15 +apc16_pin JTAG_TRSTn 16 + +apc16_pin SD_CLK 17 +apc16_pin SD_CMD 18 +apc16_pin SD_DATA0 19 +apc16_pin SD_DATA1 20 +apc16_pin SD_DATA2 21 +apc16_pin SD_DATA3 22 +apc16_pin SD_DECT 23 + +# U2 ClockHub global clocks on F2. Keep the three unrelated frequencies in +# separate ClockHub groups: gclk0 (0..4), gclk5 (5..9), gclk10 (10..14). +# Package pins are the F2 GlobalClk pins declared by the U2.2 board file. +assign_pin -port [pin_name $top clk6_p] -fpga b0.f2 -pin CA39 +assign_pin -port [pin_name $top clk6_n] -fpga b0.f2 -pin CA40 +assign_pin -port [pin_name $top clk8_p] -fpga b0.f2 -pin G36 +assign_pin -port [pin_name $top clk8_n] -fpga b0.f2 -pin F36 +assign_pin -port [pin_name $top clk5_p] -fpga b0.f2 -pin AW17 +assign_pin -port [pin_name $top clk5_n] -fpga b0.f2 -pin AY17 +if {$uvhs_skip_pcie_pins} { + puts "INFO: skip PCIe/XDMA pin assignment because UVHS_SKIP_PCIE_PINS=1" +} else { + assign_pin -port [pin_name $top pcie_ep_lnk_up] -connector b0.F2_APC16 -index 58 + + # XDMA endpoint signals. X4 uses the HGC7 lane group from the Hejian + # official XDMA EP example; bind HGC6 only when X8 is explicitly selected. + puts "INFO: assign XDMA PCIe pins for $xdma_link_width" + assign_pin -port [pin_name $top pcie_ep_gt_ref_clk_p] -connector b0.F2_HGC7 -index 29 + assign_pin -port [pin_name $top pcie_ep_gt_ref_clk_n] -connector b0.F2_HGC7 -index 30 + assign_pin -port [pin_name $top pcie_ep_perstn] -connector b0.F2_APC16 -index 118 + + set xdma_rx_connectors {b0.F2_HGC7 b0.F2_HGC7 b0.F2_HGC7 b0.F2_HGC7 b0.F2_HGC6 b0.F2_HGC6 b0.F2_HGC6 b0.F2_HGC6} + set xdma_tx_connectors {b0.F2_HGC7 b0.F2_HGC7 b0.F2_HGC7 b0.F2_HGC7 b0.F2_HGC6 b0.F2_HGC6 b0.F2_HGC6 b0.F2_HGC6} + set xdma_rxp_indices {16 13 4 1 16 13 4 1} + set xdma_rxn_indices {17 14 5 2 17 14 5 2} + set xdma_txp_indices {35 32 23 20 35 32 23 20} + set xdma_txn_indices {36 33 24 21 36 33 24 21} + for {set i 0} {$i < $xdma_lane_count} {incr i} { + assign_pin -port [pin_name $top [format {pci_ep_rxp[%d]} $i]] -connector [lindex $xdma_rx_connectors $i] -index [lindex $xdma_rxp_indices $i] + assign_pin -port [pin_name $top [format {pci_ep_rxn[%d]} $i]] -connector [lindex $xdma_rx_connectors $i] -index [lindex $xdma_rxn_indices $i] + assign_pin -port [pin_name $top [format {pci_ep_txp[%d]} $i]] -connector [lindex $xdma_tx_connectors $i] -index [lindex $xdma_txp_indices $i] + assign_pin -port [pin_name $top [format {pci_ep_txn[%d]} $i]] -connector [lindex $xdma_tx_connectors $i] -index [lindex $xdma_txn_indices $i] + } +} + +# The UVHS uvw_axi4_to_ddr4 IP declares: +# (* UV_HW_IP = "... toFPGA:" *) +# In that mode the UVHS daughter-card/IP binding owns the PDDR4DME DDR pins and +# emits the required *_pad_net_* constraints. Hand-assigning the naked +# FP_CLK_200M_* or DDR4_DIMM_* top ports here double-constrains the same pads +# and causes Vivado "pad already occupied" plus UCIO failures. +puts "INFO: UVHS uvw_axi4_to_ddr4 owns the PDDR4DME DDR pin assignment" diff --git a/fpga_diff/uvhs/async_clocks.tcl b/fpga_diff/uvhs/async_clocks.tcl new file mode 100644 index 00000000..4be4bc0e --- /dev/null +++ b/fpga_diff/uvhs/async_clocks.tcl @@ -0,0 +1,203 @@ +################################################################################ +# UVHS asynchronous clock constraints. +# This file is intentionally separate from src/constr/common/async_clocks.xdc, +# which is used by the Vivado flow. Keep XDMA-internal clock relationships +# visible unless the crossing is implemented by a CDC IP. +################################################################################ + +proc fpga_diff_collect_clocks {patterns} { + set result {} + foreach pattern $patterns { + foreach clock [get_clocks -quiet $pattern] { + if {[lsearch -exact $result $clock] < 0} { + lappend result $clock + } + } + } + return $result +} + +proc fpga_diff_set_async_pair {name from_clocks to_clocks} { + if {![llength $from_clocks] || ![llength $to_clocks]} { + puts "INFO: fpga_diff UVHS async skip $name, missing clocks" + return + } + + puts "INFO: fpga_diff UVHS async false paths $name: $from_clocks <-> $to_clocks" + set_false_path -from $from_clocks -to $to_clocks + set_false_path -from $to_clocks -to $from_clocks +} + +proc fpga_diff_set_async_clock_groups {name from_clocks to_clocks} { + if {![llength $from_clocks] || ![llength $to_clocks]} { + puts "INFO: fpga_diff UVHS async clock-group skip $name, missing clocks" + return + } + + puts "INFO: fpga_diff UVHS async clock groups $name: $from_clocks <-> $to_clocks" + set_clock_groups -asynchronous -group $from_clocks -group $to_clocks +} + +proc fpga_diff_set_false_path_to_pins {name from_clocks to_pins} { + if {![llength $from_clocks] || ![llength $to_pins]} { + puts "INFO: fpga_diff UVHS async pin-path skip $name, missing clocks or pins: clocks=[llength $from_clocks] pins=[llength $to_pins]" + return + } + + puts "INFO: fpga_diff UVHS async false path $name: $from_clocks -> $to_pins" + set_false_path -from $from_clocks -to $to_pins +} + +proc fpga_diff_collect_pins_by_name {patterns} { + set result {} + foreach pattern $patterns { + foreach pin [get_pins -quiet -hierarchical -filter "NAME =~ $pattern"] { + if {[lsearch -exact $result $pin] < 0} { + lappend result $pin + } + } + } + return $result +} + +set fpga_diff_tm_clocks [fpga_diff_collect_clocks {TMCLK RTC_GATED_CLK}] +# SOC_GATED_CLK is generated by DifftestClockGate from CPU_CLK_IN/sys_clk_i. +# Keep it in the CPU timing group so CPU <-> gated-SoC paths remain timed +# instead of being hidden behind async false paths. +set fpga_diff_cpu_clocks [fpga_diff_collect_clocks {CPU_CLK_IN SOC_GATED_CLK}] +set fpga_diff_debug_clocks [fpga_diff_collect_clocks {DEBUG_CLK_IN}] +set fpga_diff_ddr_clocks [fpga_diff_collect_clocks { + DDR_UI_CLK + ddr_ref_clk + clk7_p_pad_net* + *c0_ddr4_ui_clk* + *ddr4_ui_clk* + mmcm_clkout* + pll_clk* +}] +set fpga_diff_jtag_clocks [fpga_diff_collect_clocks {jtag_vclk *INTERNAL_TCK*}] +set fpga_diff_pcie_ref_clocks [fpga_diff_collect_clocks {pcie_ep_refclk}] +set fpga_diff_pcie_gt_clocks [fpga_diff_collect_clocks { + qpll*outrefclk* + qpll*outclk* + GTYE4_CHANNEL_TXOUTCLK* + */GTYE4_CHANNEL_PRIM_INST/TXOUTCLK + *bufg_gt_txoutclkmon_inst/O + xdma_ep_xdma_0_0_pcie4c_ip_gt_top_i_n_* +}] +set fpga_diff_xdma_pipe_clocks [fpga_diff_collect_clocks { + pipe_clk + */xdma_ep_i/xdma_0/inst/pcie4c_ip_i/inst/*/diablo_gt.diablo_gt_phy_wrapper/phy_clk_i/bufg_gt_intclk/O +}] +set fpga_diff_xdma_axi_clocks [fpga_diff_collect_clocks { + M00_AXIS_ACLK* + XDMA_AXI_ACLK + difftest_pcie_clock_bufg_n + core_def_xdma_ep_i_TO_DIFFTEST_PCIE_CLK_infer + *TO_DIFFTEST_PCIE_CLK*infer* +}] +set fpga_diff_difftest_pcie_blackbox_clocks [fpga_diff_collect_clocks { + DIFFTEST_PCIE_CLK +}] +set fpga_diff_difftest_pcie_wiz_clocks [fpga_diff_collect_clocks { + clk_out*_xdma_ep_clk_wiz* +}] +set fpga_diff_difftest_pcie_clocks [concat \ + $fpga_diff_difftest_pcie_blackbox_clocks \ + $fpga_diff_difftest_pcie_wiz_clocks] +set fpga_diff_uvw_sbus_readback_source_clocks [fpga_diff_collect_clocks { + uvw_sbus_sysclk0 + s_clk_out1 +}] +set fpga_diff_uvw_sbus_readback_pins [fpga_diff_collect_pins_by_name { + *U_UVHS_UVW_AXI4_TO_DDR4/u_uvw_axi4_to_ddr4/u_sbus_bridge*/u_sbus3_ip_intf/u_uvw_ur_alp_if/u_uvw_sbus_alp_if_regs/reg_rd_data_reg*/D + *u_sbus_bridge*/u_sbus3_ip_intf/u_uvw_ur_alp_if/u_uvw_sbus_alp_if_regs/reg_rd_data_reg*/D +}] + +set fpga_diff_external_async_domains [list \ + [list tm $fpga_diff_tm_clocks] \ + [list cpu $fpga_diff_cpu_clocks] \ + [list debug $fpga_diff_debug_clocks] \ + [list ddr $fpga_diff_ddr_clocks] \ + [list jtag $fpga_diff_jtag_clocks] \ + [list pcie_ref $fpga_diff_pcie_ref_clocks]] + +set fpga_diff_domain_count [llength $fpga_diff_external_async_domains] +for {set fpga_diff_i 0} {$fpga_diff_i < $fpga_diff_domain_count} {incr fpga_diff_i} { + set fpga_diff_from [lindex $fpga_diff_external_async_domains $fpga_diff_i] + for {set fpga_diff_j [expr {$fpga_diff_i + 1}]} {$fpga_diff_j < $fpga_diff_domain_count} {incr fpga_diff_j} { + set fpga_diff_to [lindex $fpga_diff_external_async_domains $fpga_diff_j] + fpga_diff_set_async_pair \ + "[lindex $fpga_diff_from 0]_to_[lindex $fpga_diff_to 0]" \ + [lindex $fpga_diff_from 1] \ + [lindex $fpga_diff_to 1] + } +} + +# XDMA AXI clock follows XDMA_LINK_WIDTH (X4=125 MHz, X8=250 MHz). +# The UVHS black-box boundary exposes xdma_0/axi_aclk as TO_DIFFTEST_PCIE_CLK, +# so classify that inferred clock as XDMA AXI instead of a separate 100 MHz +# DiffTest PCIe domain. +set fpga_diff_xdma_axis_clocks $fpga_diff_xdma_axi_clocks +set fpga_diff_xdma_external_domains [concat \ + $fpga_diff_tm_clocks \ + $fpga_diff_cpu_clocks \ + $fpga_diff_debug_clocks \ + $fpga_diff_ddr_clocks \ + $fpga_diff_jtag_clocks \ + $fpga_diff_pcie_ref_clocks] + +fpga_diff_set_async_pair xdma_difftest_to_external \ + $fpga_diff_xdma_axis_clocks \ + $fpga_diff_xdma_external_domains +fpga_diff_set_async_pair xdma_axi_to_external \ + $fpga_diff_xdma_axis_clocks \ + $fpga_diff_xdma_external_domains +fpga_diff_set_async_pair xdma_axis_clock_converter \ + $fpga_diff_difftest_pcie_clocks \ + $fpga_diff_xdma_axis_clocks +fpga_diff_set_async_clock_groups xdma_axis_clock_converter \ + $fpga_diff_difftest_pcie_clocks \ + $fpga_diff_xdma_axis_clocks +fpga_diff_set_async_pair xdma_difftest_to_gt_pipe \ + $fpga_diff_xdma_axis_clocks \ + [concat $fpga_diff_pcie_gt_clocks $fpga_diff_xdma_pipe_clocks] +fpga_diff_set_async_clock_groups xdma_difftest_to_gt_pipe \ + $fpga_diff_xdma_axis_clocks \ + [concat $fpga_diff_pcie_gt_clocks $fpga_diff_xdma_pipe_clocks] +fpga_diff_set_async_pair xdma_axi_to_gt_pipe \ + $fpga_diff_xdma_axis_clocks \ + [concat $fpga_diff_pcie_gt_clocks $fpga_diff_xdma_pipe_clocks] +fpga_diff_set_async_clock_groups xdma_axi_to_gt_pipe \ + $fpga_diff_xdma_axis_clocks \ + [concat $fpga_diff_pcie_gt_clocks $fpga_diff_xdma_pipe_clocks] + +# UVHS may expose the same XDMA clk_wiz 100 MHz user clock twice: once as the +# real Vivado clk_wiz output and once as the xdma_ep black-box boundary output. +# Treating those as separate timed clocks creates large false hold violations +# through the XPM async FIFO write RAM. Keep the real CDC boundary constrained +# above, but suppress timing between these aliases. +fpga_diff_set_async_pair xdma_difftest_clock_alias \ + $fpga_diff_difftest_pcie_blackbox_clocks \ + $fpga_diff_difftest_pcie_wiz_clocks + +fpga_diff_set_false_path_to_pins uvw_sbus_bridge_alp_readback \ + $fpga_diff_uvw_sbus_readback_source_clocks \ + $fpga_diff_uvw_sbus_readback_pins + +unset -nocomplain fpga_diff_tm_clocks fpga_diff_cpu_clocks fpga_diff_debug_clocks +unset -nocomplain fpga_diff_ddr_clocks fpga_diff_jtag_clocks fpga_diff_pcie_ref_clocks +unset -nocomplain fpga_diff_pcie_gt_clocks fpga_diff_xdma_pipe_clocks +unset -nocomplain fpga_diff_xdma_axi_clocks fpga_diff_difftest_pcie_clocks +unset -nocomplain fpga_diff_difftest_pcie_blackbox_clocks fpga_diff_difftest_pcie_wiz_clocks +unset -nocomplain fpga_diff_uvw_sbus_readback_source_clocks +unset -nocomplain fpga_diff_uvw_sbus_readback_pins +unset -nocomplain fpga_diff_external_async_domains fpga_diff_domain_count +unset -nocomplain fpga_diff_i fpga_diff_j fpga_diff_from fpga_diff_to +unset -nocomplain fpga_diff_xdma_axis_clocks +unset -nocomplain fpga_diff_xdma_external_domains +rename fpga_diff_collect_clocks {} +rename fpga_diff_collect_pins_by_name {} +rename fpga_diff_set_async_pair {} +rename fpga_diff_set_async_clock_groups {} +rename fpga_diff_set_false_path_to_pins {} diff --git a/fpga_diff/uvhs/backend_run.tcl b/fpga_diff/uvhs/backend_run.tcl new file mode 100644 index 00000000..046a53ff --- /dev/null +++ b/fpga_diff/uvhs/backend_run.tcl @@ -0,0 +1,1046 @@ +set ::fpga_diff_uvhs_dir [file dirname [info script]] + +proc env_or_default {name default} { + if {[info exists ::env($name)] && $::env($name) ne ""} { + return $::env($name) + } + return $default +} + +proc source_if_exists {file} { + if {[file exists $file]} { + puts "INFO: source $file" + if {[catch {uplevel #0 [list source $file]} err]} { + puts "WARNING: source $file failed: $err" + } + } else { + puts "INFO: skip missing $file" + } +} + +proc run_or_warn {description command} { + puts "INFO: $description" + if {[catch {uplevel #0 $command} err]} { + puts "WARNING: $description failed: $err" + } +} + +proc uvhs_config_probe_group_resource {} { + set probe_group_resource [env_or_default UVHS_PROBE_GROUP_RESOURCE 35] + if {$probe_group_resource eq "none" || $probe_group_resource eq "0"} { + puts "INFO: skip Probe-Group resource override" + return + } + + run_or_warn "restore Probe-Group system resource to $probe_group_resource" \ + [list config_hw -set_resource $probe_group_resource -type Probe-Group] +} + +proc uvhs_config_fill_rates {} { + set lut_fill_rate [env_or_default UVHS_LUT_FILL_RATE none] + set lut6_fill_rate [env_or_default UVHS_LUT6_FILL_RATE none] + set args {} + + foreach {option value} [list -lut $lut_fill_rate -lut6 $lut6_fill_rate] { + if {$value eq "none" || $value eq ""} { + continue + } + if {![string is double -strict $value] || $value <= 0 || $value > 100} { + error "$option fill rate must be in (0, 100], got '$value'" + } + lappend args $option $value + } + + if {![llength $args]} { + puts "INFO: keep platform default LUT fill rates" + return + } + + puts "INFO: set UVHS fill rates: $args" + set_fill_rate {*}$args +} + +proc uvhs_cpu_clk_period_ns {} { + return [env_or_default UVHS_CPU_CLK_PERIOD_NS 40] +} + +proc uvhs_cpu_debug_clk {} { + return [expr {[env_or_default UVHS_CPU_DEBUG_CLK 1] eq "1"}] +} + +proc uvhs_xdma_axi_clk_period_ns {} { + return 8.000 +} + +proc uvhs_async_clock_patch_script {} { + set async_file [file normalize [file join $::fpga_diff_uvhs_dir async_clocks.tcl]] + return [format { +puts "INFO: UVHS patch: source async clock groups after Vivado clocks are available" +if {[file exists {%s}]} { + source {%s} +} else { + puts "WARNING: UVHS patch: missing async clock file %s" +} +} $async_file $async_file $async_file] +} + +proc uvhs_vivado_stage_hook_files {stage} { + switch -- $stage { + pre_opt { set rel_path user_pre_opt.tcl } + pre_place { set rel_path user_pre_place.tcl } + default { + error "unsupported Vivado stage hook '$stage'" + } + } + + set files {} + foreach pnr_dir [glob -nocomplain hw.dat/Compile/PnR/*/*] { + set file [file join $pnr_dir $rel_path] + if {[file exists $file]} { + lappend files $file + } + } + return $files +} + +proc uvhs_xdma_gt_refclk_patch_script {stage} { + return [string map [list __UVHS_STAGE__ $stage] { +puts "INFO: UVHS patch: keep XDMA GT refclk direct to IBUFDS_GTE4 before __UVHS_STAGE__" +set uvhs_xdma_refclk_ports {} +foreach uvhs_refclk_pattern { + pcie_ep_gt_ref_clk_p_pad_net_* + pcie_ep_gt_ref_clk_n_pad_net_* + pcie_ep_gt_ref_clk_p + pcie_ep_gt_ref_clk_n +} { + foreach uvhs_port [get_ports -quiet $uvhs_refclk_pattern] { + if {[lsearch -exact $uvhs_xdma_refclk_ports $uvhs_port] < 0} { + lappend uvhs_xdma_refclk_ports $uvhs_port + } + } +} +if {[llength $uvhs_xdma_refclk_ports]} { + catch {set_property IO_BUFFER_TYPE NONE $uvhs_xdma_refclk_ports} uvhs_iobuf_msg + catch {set_property CLOCK_BUFFER_TYPE NONE $uvhs_xdma_refclk_ports} uvhs_clkbuf_msg + puts "INFO: UVHS patch: XDMA GT refclk ports before __UVHS_STAGE__: $uvhs_xdma_refclk_ports" +} else { + puts "WARNING: UVHS patch: XDMA GT refclk ports not found before __UVHS_STAGE__" +} + +proc ::uvhs_xdma_gt_refclk_net_segments {uvhs_net} { + set uvhs_segments {} + if {$uvhs_net ne ""} { + lappend uvhs_segments $uvhs_net + } + if {[catch {set uvhs_more_segments [get_nets -quiet -segments $uvhs_net]} uvhs_segment_msg]} { + puts "WARNING: UVHS patch: get net segments for $uvhs_net failed: $uvhs_segment_msg" + } else { + foreach uvhs_segment $uvhs_more_segments { + if {[lsearch -exact $uvhs_segments $uvhs_segment] < 0} { + lappend uvhs_segments $uvhs_segment + } + } + } + return $uvhs_segments +} + +proc ::uvhs_xdma_gt_refclk_input_pins {} { + set uvhs_pins {} + foreach uvhs_pattern { + */core_def/xdma_ep_i/*IBUFDS_GTE4*/I + */core_def/xdma_ep_i/*IBUFDS_GTE4_I/I + */core_def/xdma_ep_i/util_ds_buf_0/*/I + } { + foreach uvhs_pin [get_pins -hier -quiet $uvhs_pattern] { + if {[lsearch -exact $uvhs_pins $uvhs_pin] < 0} { + lappend uvhs_pins $uvhs_pin + } + } + } + foreach uvhs_pin [get_pins -hier -quiet -filter {REF_PIN_NAME == I && NAME =~ */core_def/xdma_ep_i/*IBUFDS_GTE4*}] { + if {[lsearch -exact $uvhs_pins $uvhs_pin] < 0} { + lappend uvhs_pins $uvhs_pin + } + } + return $uvhs_pins +} + +proc ::uvhs_xdma_gt_refclk_pin_is_driven_by_bufg {uvhs_pin uvhs_bufg} { + set uvhs_pin_net [lindex [get_nets -quiet -of_objects $uvhs_pin] 0] + if {$uvhs_pin_net eq ""} { + return 0 + } + foreach uvhs_segment [::uvhs_xdma_gt_refclk_net_segments $uvhs_pin_net] { + foreach uvhs_driver [get_pins -quiet -of_objects $uvhs_segment -filter {DIRECTION == OUT}] { + set uvhs_driver_cell [lindex [get_cells -quiet -of_objects $uvhs_driver] 0] + if {$uvhs_driver_cell eq $uvhs_bufg} { + return 1 + } + } + } + return 0 +} + +# DLP may insert a BUFGCE after opt_design, but IBUFDS_GTE4 must remain driven +# directly by the PCIe reference-clock port. Repair only that generated path. +set uvhs_xdma_gt_input_pins [::uvhs_xdma_gt_refclk_input_pins] +if {[llength $uvhs_xdma_gt_input_pins]} { + puts "INFO: UVHS patch: XDMA IBUFDS_GTE4 input pins before __UVHS_STAGE__: $uvhs_xdma_gt_input_pins" +} else { + puts "WARNING: UVHS patch: no XDMA IBUFDS_GTE4 input pins found before __UVHS_STAGE__" +} +set uvhs_xdma_refclk_bufgs [get_cells -hier -quiet -filter {REF_NAME =~ BUFG* && NAME =~ *pcie_ep_gt_ref_clk*bufg*}] +if {[llength $uvhs_xdma_refclk_bufgs]} { + puts "INFO: UVHS patch: found XDMA GT refclk BUFG cells before __UVHS_STAGE__: $uvhs_xdma_refclk_bufgs" +} +foreach uvhs_bufg $uvhs_xdma_refclk_bufgs { + set uvhs_in_pins [get_pins -quiet ${uvhs_bufg}/I] + set uvhs_out_pins [get_pins -quiet ${uvhs_bufg}/O] + if {![llength $uvhs_in_pins] || ![llength $uvhs_out_pins]} { + puts "WARNING: UVHS patch: skip $uvhs_bufg, missing I/O pins" + continue + } + set uvhs_in_net [lindex [get_nets -quiet -of_objects [lindex $uvhs_in_pins 0]] 0] + set uvhs_out_net [lindex [get_nets -quiet -of_objects [lindex $uvhs_out_pins 0]] 0] + if {$uvhs_in_net eq "" || $uvhs_out_net eq ""} { + puts "WARNING: UVHS patch: skip $uvhs_bufg, missing I/O nets" + continue + } + + set uvhs_gt_loads {} + foreach uvhs_segment [::uvhs_xdma_gt_refclk_net_segments $uvhs_out_net] { + foreach uvhs_load [get_pins -quiet -of_objects $uvhs_segment -filter {DIRECTION == IN}] { + if {[string match *core_def/xdma_ep_i*IBUFDS_GTE4* $uvhs_load] || \ + [string match *core_def/xdma_ep_i/util_ds_buf_0* $uvhs_load]} { + if {[lsearch -exact $uvhs_gt_loads $uvhs_load] < 0} { + lappend uvhs_gt_loads $uvhs_load + } + } + } + } + foreach uvhs_gt_pin $uvhs_xdma_gt_input_pins { + if {[::uvhs_xdma_gt_refclk_pin_is_driven_by_bufg $uvhs_gt_pin $uvhs_bufg] && \ + [lsearch -exact $uvhs_gt_loads $uvhs_gt_pin] < 0} { + lappend uvhs_gt_loads $uvhs_gt_pin + } + } + if {![llength $uvhs_gt_loads]} { + puts "WARNING: UVHS patch: skip $uvhs_bufg, no XDMA IBUFDS_GTE4 loads on $uvhs_out_net" + foreach uvhs_segment [::uvhs_xdma_gt_refclk_net_segments $uvhs_out_net] { + puts "INFO: UVHS patch: pins on $uvhs_segment: [get_pins -quiet -of_objects $uvhs_segment]" + } + continue + } + + foreach uvhs_load $uvhs_gt_loads { + set uvhs_load_net [lindex [get_nets -quiet -of_objects $uvhs_load] 0] + if {$uvhs_load_net eq ""} { + puts "WARNING: UVHS patch: skip $uvhs_load, no connected net" + continue + } + if {[catch {disconnect_net -net $uvhs_load_net -objects $uvhs_load} uvhs_disconnect_msg]} { + puts "WARNING: UVHS patch: disconnect $uvhs_load_net -> $uvhs_load failed: $uvhs_disconnect_msg" + continue + } + if {[catch {connect_net -hier -net $uvhs_in_net -objects $uvhs_load} uvhs_connect_msg]} { + puts "WARNING: UVHS patch: connect $uvhs_in_net -> $uvhs_load failed: $uvhs_connect_msg" + } else { + puts "INFO: UVHS patch: rewired $uvhs_load from $uvhs_load_net to $uvhs_in_net" + } + } + + catch {disconnect_net -net $uvhs_in_net -objects [lindex $uvhs_in_pins 0]} uvhs_msg + catch {disconnect_net -net $uvhs_out_net -objects [lindex $uvhs_out_pins 0]} uvhs_msg + if {[info commands remove_cell] ne ""} { + catch {remove_cell $uvhs_bufg} uvhs_remove_msg + } elseif {[info commands delete_cells] ne ""} { + catch {delete_cells $uvhs_bufg} uvhs_remove_msg + } + puts "INFO: UVHS patch: bypassed XDMA GT refclk BUFG $uvhs_bufg" +} +}] +} + +proc uvhs_write_xdc_patch_script {} { + set async_file [file normalize [file join $::fpga_diff_uvhs_dir async_clocks.tcl]] + set script [format { +proc ::uvhs_create_ddr_ui_clock_patch {} { + if {[llength [get_clocks -quiet DDR_UI_CLK]] > 0} { + return + } + set uvhs_pins {} + foreach uvhs_pattern { + part_2/core_def/U_UVHS_UVW_AXI4_TO_DDR4/ddr4ip_ddr4_user_clk + */U_UVHS_UVW_AXI4_TO_DDR4/ddr4ip_ddr4_user_clk + } { + foreach uvhs_pin [get_pins -quiet $uvhs_pattern] { + if {[lsearch -exact $uvhs_pins $uvhs_pin] < 0} { + lappend uvhs_pins $uvhs_pin + } + } + foreach uvhs_pin [get_pins -hierarchical -quiet -filter "NAME =~ $uvhs_pattern"] { + if {[lsearch -exact $uvhs_pins $uvhs_pin] < 0} { + lappend uvhs_pins $uvhs_pin + } + } + } + if {[llength $uvhs_pins]} { + create_clock -period 5.000 -name DDR_UI_CLK -waveform {0.000 2.500} -add [lindex $uvhs_pins 0] + puts "INFO: UVHS patch: create DDR_UI_CLK on UVHS DDR user clock [lindex $uvhs_pins 0]" + } +} + +proc ::uvhs_patch_xdma_preopt_xdc {uvhs_xdc_file} { + if {![file exists $uvhs_xdc_file]} { + return + } + + set uvhs_fh [open $uvhs_xdc_file r] + set uvhs_data [read $uvhs_fh] + close $uvhs_fh + set uvhs_patched $uvhs_data + + if {[string match *pnr_timing_constraints_preopt.xdc $uvhs_xdc_file] || \ + [string match *pnr_timing_constraints.xdc $uvhs_xdc_file]} { + # Partitioning can serialize a replicated SoC clock gate as an unrelated + # primary clock. Preserve its original CPU_CLK_IN relationship. + set uvhs_lines {} + foreach uvhs_line [split $uvhs_patched "\n"] { + if {[string first {-name SOC_GATED_CLK} $uvhs_line] >= 0 && \ + [regexp {^create_clock[^\n]*\[get_pins[[:space:]]+([^]]+)/O\]} \ + $uvhs_line -> uvhs_soc_bufg]} { + set uvhs_line [format {create_generated_clock -name SOC_GATED_CLK -source [get_pins %%s/I] -divide_by 1 -add -master_clock [get_clocks CPU_CLK_IN] [get_pins %%s/O]} \ + $uvhs_soc_bufg $uvhs_soc_bufg] + puts "INFO: UVHS patch: restore replicated SOC_GATED_CLK as CPU_CLK_IN generated clock on $uvhs_soc_bufg" + } + lappend uvhs_lines $uvhs_line + } + set uvhs_patched [join $uvhs_lines "\n"] + + regsub -all {create_generated_clock[^\n]*-name[^\n]*core_def_xdma_ep_i_TO_DIFFTEST_PCIE_CLK_infer[^\n]*TO_DIFFTEST_PCIE_CLK[^\n]*} \ + $uvhs_patched {create_clock -period __UVHS_XDMA_AXI_CLK_PERIOD_NS__ -name XDMA_AXI_ACLK -waveform [list 0 [expr __UVHS_XDMA_AXI_CLK_PERIOD_NS__ / 2.0]] -add [get_pins part_2/core_def/xdma_ep_i/TO_DIFFTEST_PCIE_CLK]} uvhs_patched + regsub -all {core_def_xdma_ep_i_TO_DIFFTEST_PCIE_CLK_infer} $uvhs_patched {XDMA_AXI_ACLK} uvhs_patched + if {[string first {U_UVHS_UVW_AXI4_TO_DDR4} $uvhs_patched] >= 0 && \ + [string first {UVHS patch: create DDR_UI_CLK on UVHS DDR user clock} $uvhs_patched] < 0} { + append uvhs_patched "\n::uvhs_create_ddr_ui_clock_patch\n" + } + if {[string first {set ::uvhs_async_clocks_loaded 1} $uvhs_patched] < 0} { + append uvhs_patched "\nset ::uvhs_async_clocks_loaded 1\n" + append uvhs_patched "if {\[file exists {%s}\]} {\n source {%s}\n} else {\n puts \"WARNING: UVHS patch: missing async clock file %s\"\n}\n" + } + } + + if {$uvhs_patched ne $uvhs_data} { + set uvhs_fh [open $uvhs_xdc_file w] + puts -nonewline $uvhs_fh $uvhs_patched + close $uvhs_fh + puts "INFO: UVHS patch: patched generated preopt XDC $uvhs_xdc_file" + } +} + +if {[info commands ::uvhs_vivado_write_xdc] eq "" && [info commands ::write_xdc] ne ""} { + rename ::write_xdc ::uvhs_vivado_write_xdc + proc ::write_xdc {args} { + set uvhs_rc [catch {uplevel 1 [linsert $args 0 ::uvhs_vivado_write_xdc]} uvhs_result uvhs_options] + foreach uvhs_arg $args { + if {[string match *pnr_timing_constraints_preopt.xdc $uvhs_arg] || \ + [string match *pnr_timing_constraints.xdc $uvhs_arg]} { + ::uvhs_patch_xdma_preopt_xdc $uvhs_arg + } + } + return -options $uvhs_options $uvhs_result + } + puts "INFO: UVHS patch: wrapped write_xdc to patch generated XDMA preopt constraints" +} +} $async_file $async_file $async_file] + return [string map [list __UVHS_XDMA_AXI_CLK_PERIOD_NS__ [uvhs_xdma_axi_clk_period_ns]] $script] +} + +proc create_clock_if_port_exists {name period port} { + if {[llength [get_clocks -quiet $name]] > 0} { + puts "INFO: skip create_clock $name, already exists" + return + } + set ports [get_ports -quiet $port] + if {[llength $ports] > 0} { + run_or_warn "create_clock $name on $port" [list create_clock -name $name -per $period $ports] + } else { + puts "INFO: skip create_clock $name, missing port $port" + } +} + +proc create_blackbox_output_clock_if_pin_exists {name period pin} { + set pins [get_pins -quiet $pin] + if {[llength $pins] == 0} { + set pins [get_pins -hierarchical -quiet -filter "NAME =~ */$pin || NAME == $pin"] + } + if {[llength $pins] == 0} { + puts "INFO: skip create_clock $name, missing blackbox output pin $pin" + return + } + + set clocks [get_clocks -quiet -of_objects $pins] + if {[llength $clocks] > 0} { + puts "INFO: skip create_clock $name, blackbox output pin $pin already has clocks: $clocks" + return + } + + run_or_warn "create_clock $name on blackbox output $pin" \ + [list create_clock -name $name -per $period $pins] +} + +proc create_ddr_ui_clock_if_exists {} { + set pin_patterns { + core_def/U_UVHS_UVW_AXI4_TO_DDR4/ddr4ip_ddr4_user_clk + */U_UVHS_UVW_AXI4_TO_DDR4/ddr4ip_ddr4_user_clk + } + set pins {} + foreach pin_pattern $pin_patterns { + foreach pin [get_pins -quiet $pin_pattern] { + if {[lsearch -exact $pins $pin] < 0} { + lappend pins $pin + } + } + foreach pin [get_pins -hierarchical -quiet -filter "NAME =~ $pin_pattern"] { + if {[lsearch -exact $pins $pin] < 0} { + lappend pins $pin + } + } + } + if {![llength $pins]} { + puts "INFO: skip create_clock DDR_UI_CLK, missing UVHS DDR user clock pin" + return + } + + set pin [lindex $pins 0] + set clocks [get_clocks -quiet -of_objects $pin] + if {[llength $clocks]} { + puts "INFO: DDR UI pin $pin already has clocks: $clocks" + return + } + + run_or_warn "create_clock DDR_UI_CLK on $pin" \ + [list create_clock -name DDR_UI_CLK -per 5.000 $pin] +} + +proc create_generated_clock_if_bufgce_exists {name master_clock cell_patterns} { + if {[llength [get_clocks -quiet $name]] > 0} { + puts "INFO: skip create_generated_clock $name, already exists" + return + } + if {[llength [get_clocks -quiet $master_clock]] == 0} { + puts "WARNING: skip create_generated_clock $name, missing master clock $master_clock" + return + } + set cells {} + foreach cell_pattern $cell_patterns { + set matches [get_cells -hierarchical -quiet -filter "NAME =~ $cell_pattern"] + foreach match $matches { + if {[lsearch -exact $cells $match] < 0} { + lappend cells $match + } + } + } + if {[llength $cells] == 0} { + set bufgce_cells [get_cells -hierarchical -quiet -filter "REF_NAME =~ BUFGCE"] + puts "WARNING: skip create_generated_clock $name, missing BUFGCE cell patterns $cell_patterns; visible BUFGCE cells: $bufgce_cells" + return + } + if {[llength $cells] > 1} { + puts "WARNING: create_generated_clock $name matched multiple cells, using [lindex $cells 0]: $cells" + } + set cell [lindex $cells 0] + set in_pins [get_pins -quiet $cell/I] + set out_pins [get_pins -quiet $cell/O] + if {[llength $in_pins] == 0 || [llength $out_pins] == 0} { + puts "WARNING: skip create_generated_clock $name, missing I/O pins on $cell" + return + } + run_or_warn "create_generated_clock $name on $cell/O" \ + [list create_generated_clock -add -name $name -master_clock $master_clock -source $in_pins -divide_by 1 $out_pins] +} + +proc patch_vivado_wrappers {} { + set patterns { + hw.dat/Compile/PnR/*/*/vivado/Script/uv_vivado_wrapper.sh + hw.dat/Compile/PnR/*/*/vivado/Rundir/*/uv_vivado_wrapper.sh + } + foreach pattern $patterns { + foreach wrapper [glob -nocomplain $pattern] { + if {![file exists $wrapper]} { + continue + } + set fh [open $wrapper r] + set data [read $fh] + close $fh + if {[string match "#!/bin/sh*" $data]} { + regsub {^#!/bin/sh} $data {#!/bin/bash} data + set fh [open $wrapper w] + puts -nonewline $fh $data + close $fh + file attributes $wrapper -permissions u=rwx,g=rx,o=rx + puts "INFO: patched Vivado wrapper shell to bash: $wrapper" + } + } + } +} + +proc patch_vivado_pre_opt {} { + set write_xdc_patch_script [uvhs_write_xdc_patch_script] + set xdma_cdc_script { +puts "INFO: UVHS patch: source XDMA CDC attributes before opt_design" +set uvhs_xdma_cdc_script [file join $FILE_DIR xdma_cdc_attributes.tcl] +if {[file exists $uvhs_xdma_cdc_script]} { + source $uvhs_xdma_cdc_script +} else { + puts "WARNING: UVHS patch: missing XDMA CDC attribute script $uvhs_xdma_cdc_script" +} +} + set async_script [uvhs_async_clock_patch_script] + foreach pre_opt [uvhs_vivado_stage_hook_files pre_opt] { + set fh [open $pre_opt r] + set data [read $fh] + close $fh + if {[string first "UVHS patch: wrapped write_xdc to patch generated XDMA preopt constraints" $data] < 0} { + set fh [open $pre_opt a] + puts $fh "" + puts $fh $write_xdc_patch_script + close $fh + append data "\n" $write_xdc_patch_script + puts "INFO: patched Vivado pre-opt write_xdc wrapper: $pre_opt" + } + if {[string first "UVHS patch: source XDMA CDC attributes before opt_design" $data] < 0} { + set fh [open $pre_opt a] + puts $fh "" + puts $fh $xdma_cdc_script + close $fh + append data "\n" $xdma_cdc_script + puts "INFO: patched Vivado pre-opt XDMA CDC attributes: $pre_opt" + } + if {[string first "UVHS patch: source async clock groups" $data] < 0} { + set fh [open $pre_opt a] + puts $fh "" + puts $fh $async_script + close $fh + puts "INFO: patched Vivado pre-opt async clocks: $pre_opt" + } + } +} + +proc patch_vivado_pre_place {} { + set refclk_patch_script [uvhs_xdma_gt_refclk_patch_script "place"] + set script { +set uvhs_xdma_pcie_cells [get_cells -hier -quiet -filter {NAME =~ */core_def/xdma_ep_i/xdma_0/inst/pcie4c_ip_i/inst/*pcie_4_c_e4_inst}] +if {[llength $uvhs_xdma_pcie_cells]} { + set_property LOC PCIE4CE4_X0Y6 $uvhs_xdma_pcie_cells + puts "INFO: UVHS patch: set XDMA PCIe hard block LOC=PCIE4CE4_X0Y6" +} else { + puts "WARNING: UVHS patch: XDMA PCIe hard block cell not found for LOC override" +} +puts "INFO: UVHS patch: rewire Vivado debug hub clock if it is attached to an undriven net" +if {[llength [get_debug_cores -quiet dbg_hub]]} { + set uvhs_dbg_clk [get_nets -quiet clk5_p_pad_net_7_bufg_n] + if {![llength $uvhs_dbg_clk]} { + set uvhs_dbg_clk [get_nets -quiet clk5_p_pad_net_6] + } + if {[llength $uvhs_dbg_clk]} { + catch {disconnect_debug_port dbg_hub/clk} + connect_debug_port dbg_hub/clk [lindex $uvhs_dbg_clk 0] + puts "INFO: UVHS patch: dbg_hub/clk -> [lindex $uvhs_dbg_clk 0]" + } else { + puts "WARNING: UVHS patch: no clk5 debug clock net found for dbg_hub" + } +} +puts "INFO: UVHS patch: fix known bitstream DRCs" +set uvhs_ila_rams [get_cells -hier -quiet -filter {NAME =~ *system_ila_0*trace_block_memory*ram/DEVICE_8SERIES*ram}] +if {[llength $uvhs_ila_rams]} { + set_property CLOCK_DOMAINS INDEPENDENT $uvhs_ila_rams + puts "INFO: UVHS patch: set CLOCK_DOMAINS INDEPENDENT on [llength $uvhs_ila_rams] system ILA RAM cells" +} +set uvhs_ddr_ck_ports {} +foreach uvhs_pat {DDR0_CK_C_pad_net_* DDR0_CK_T_pad_net_*} { + set uvhs_ddr_ck_ports [concat $uvhs_ddr_ck_ports [get_ports -quiet $uvhs_pat]] +} +if {[llength $uvhs_ddr_ck_ports]} { + set_property IOSTANDARD DIFF_SSTL12_DCI $uvhs_ddr_ck_ports + puts "INFO: UVHS patch: set differential IOSTANDARD on generated DDR CK pad ports: $uvhs_ddr_ck_ports" +} +} + append script "\n" $refclk_patch_script + append script [uvhs_async_clock_patch_script] + foreach pre_place [uvhs_vivado_stage_hook_files pre_place] { + set fh [open $pre_place r] + set data [read $fh] + close $fh + if {[string first "UVHS patch: set XDMA PCIe hard block" $data] < 0} { + set fh [open $pre_place a] + puts $fh "" + puts $fh $script + close $fh + puts "INFO: patched Vivado pre-place constraints: $pre_place" + } + } +} + +proc patch_vivado_before_route {} { + set script "" + set hold_expn_bailout [env_or_default UVHS_ROUTE_ENABLE_HOLD_EXPN_BAILOUT ""] + if {$hold_expn_bailout ne ""} { + if {$hold_expn_bailout ni {0 1}} { + error "UVHS_ROUTE_ENABLE_HOLD_EXPN_BAILOUT must be empty, 0, or 1; got '$hold_expn_bailout'" + } + append script "\nputs \"INFO: UVHS patch: set route.enableHoldExpnBailout=$hold_expn_bailout before route\"\n" + append script "set_param route.enableHoldExpnBailout $hold_expn_bailout\n" + } + append script [uvhs_async_clock_patch_script] + foreach pnr_dir [glob -nocomplain hw.dat/Compile/PnR/*/*] { + set before_route [file join $pnr_dir before_route.tcl] + set data "" + if {[file exists $before_route]} { + set fh [open $before_route r] + set data [read $fh] + close $fh + } + if {[string first "UVHS patch: source async clock groups" $data] < 0} { + set fh [open $before_route a] + puts $fh "" + puts $fh $script + close $fh + puts "INFO: patched Vivado before-route settings: $before_route" + } + } +} + +proc patch_timing_constraint_files {} { + set xdma_axi_clk_period [uvhs_xdma_axi_clk_period_ns] + set xdma_axi_clk_half_period [format %.3f [expr {$xdma_axi_clk_period / 2.0}]] + set xdma_blackbox_clock_replacement [format { +create_clock -period %.3f -name XDMA_AXI_ACLK -waveform {0.000 %.3f} -add [get_pins part_2/core_def/xdma_ep_i/TO_DIFFTEST_PCIE_CLK]} \ + $xdma_axi_clk_period $xdma_axi_clk_half_period] + set difftest_clock_token_pattern {(^|[^[:alnum:]_])DIFFTEST_PCIE_CLK([^[:alnum:]_]|$)} + set async_xdc_block [format { + +set ::uvhs_async_clocks_loaded 1 +if {[file exists {%s}]} { + source {%s} +} else { + puts "WARNING: UVHS patch: missing async clock file %s" +} +} [file normalize [file join $::fpga_diff_uvhs_dir async_clocks.tcl]] \ + [file normalize [file join $::fpga_diff_uvhs_dir async_clocks.tcl]] \ + [file normalize [file join $::fpga_diff_uvhs_dir async_clocks.tcl]]] + + foreach file [glob -nocomplain hw.dat/Compile/PnR/*/*/timing_constraints.tcl] { + set fh [open $file r] + set data [read $fh] + close $fh + set patched $data + + set xdma_axi_primary_clock [format { +puts "WARNING: \[APS CSTR-1\] primary clock is defined on blackbox boundary which may cause inaccurate skew analysis." +create_clock -add -name XDMA_AXI_ACLK -period [expr $uvCstrRatio * %.3f + $uvCstrMargin] -waveform [list 0 [expr ($uvCstrRatio + $uvCstrMargin / %.3f) * %.3f]] [get_pins { part_2/core_def/xdma_ep_i/TO_DIFFTEST_PCIE_CLK }] +variable XDMA_AXI_ACLK_name [get_property NAME [get_clocks XDMA_AXI_ACLK]] +} $xdma_axi_clk_period $xdma_axi_clk_period $xdma_axi_clk_half_period] + set difftest_generated_block {set DIFFTEST_PCIE_CLK_master [get_clocks -of_objects [get_pins { part_2/core_def/xdma_ep_i/TO_DIFFTEST_PCIE_CLK }]] +set DIFFTEST_PCIE_CLK_source "" +if {$DIFFTEST_PCIE_CLK_master!=""} { + set DIFFTEST_PCIE_CLK_source [get_property SOURCE_PINS $DIFFTEST_PCIE_CLK_master] +} +if {[llength $DIFFTEST_PCIE_CLK_master] == 1 && [llength $DIFFTEST_PCIE_CLK_source] == 1} { + set DIFFTEST_PCIE_CLK_source [get_pins $DIFFTEST_PCIE_CLK_source] + if {$DIFFTEST_PCIE_CLK_source==""} { + set DIFFTEST_PCIE_CLK_source [get_ports [get_property SOURCE_PINS $DIFFTEST_PCIE_CLK_master]] + } + set driver [get_nets -top_net_of_hierarchical_group -segments -of_objects $DIFFTEST_PCIE_CLK_source] + if {[get_nets -top_net_of_hierarchical_group -segments -of_objects [get_pins { part_2/core_def/xdma_ep_i/TO_DIFFTEST_PCIE_CLK }]]==$driver} { + variable DIFFTEST_PCIE_CLK_name [get_property NAME $DIFFTEST_PCIE_CLK_master] + dict set uvRenameClocks DIFFTEST_PCIE_CLK [get_pins { part_2/core_def/xdma_ep_i/TO_DIFFTEST_PCIE_CLK }] + } else { + create_generated_clock -add -name DIFFTEST_PCIE_CLK -divide_by 1 -source $DIFFTEST_PCIE_CLK_source -master_clock $DIFFTEST_PCIE_CLK_master [get_pins { part_2/core_def/xdma_ep_i/TO_DIFFTEST_PCIE_CLK }] + variable DIFFTEST_PCIE_CLK_name [get_property NAME [get_clocks DIFFTEST_PCIE_CLK]] + } +} else { + puts "WARNING: \[APS CSTR-1\] primary clock is defined on blackbox boundary which may cause inaccurate skew analysis." + create_clock -add -name DIFFTEST_PCIE_CLK -period [expr $uvCstrRatio * 10.00 + $uvCstrMargin] [get_pins { part_2/core_def/xdma_ep_i/TO_DIFFTEST_PCIE_CLK }] +variable DIFFTEST_PCIE_CLK_name [get_property NAME [get_clocks DIFFTEST_PCIE_CLK]] +}} + if {[string first $difftest_generated_block $patched] >= 0} { + regsub [string map {\\ \\\\ \[ \\[ \] \\] \$ \\$ \( \\( \) \\) \{ \\{ \} \\} \. \\. \* \\* \+ \\+ \? \\? \^ \\^} $difftest_generated_block] \ + $patched $xdma_axi_primary_clock patched + } + regsub -all {create_clock[^\n]*-name[^\n]*DIFFTEST_PCIE_CLK[^\n]*TO_DIFFTEST_PCIE_CLK[^\n]*} \ + $patched $xdma_axi_primary_clock patched + regsub -all {create_generated_clock[^\n]*-name[^\n]*core_def_xdma_ep_i_TO_DIFFTEST_PCIE_CLK_infer[^\n]*TO_DIFFTEST_PCIE_CLK[^\n]*} \ + $patched $xdma_axi_primary_clock patched + regsub -all {core_def_xdma_ep_i_TO_DIFFTEST_PCIE_CLK_infer_name} $patched {XDMA_AXI_ACLK_name} patched + regsub -all {core_def_xdma_ep_i_TO_DIFFTEST_PCIE_CLK_infer} $patched {XDMA_AXI_ACLK} patched + regsub -all {DIFFTEST_PCIE_CLK_name} $patched {XDMA_AXI_ACLK_name} patched + regsub -all $difftest_clock_token_pattern $patched {\1XDMA_AXI_ACLK\2} patched + if {$patched ne $data} { + set fh [open $file w] + puts -nonewline $fh $patched + close $fh + puts "INFO: patched UVHS DIFFTEST PCIe / DDR UI clock timing constraints: $file" + } + } + + foreach file [concat \ + [glob -nocomplain hw.dat/Compile/PnR/*/*/vivado/Rundir/*/bitstream/pnr_timing_constraints_preopt.xdc] \ + [glob -nocomplain hw.dat/Compile/PnR/*/*/vivado/Rundir/*/bitstream/pnr_timing_constraints.xdc]] { + set fh [open $file r] + set data [read $fh] + close $fh + set patched $data + + regsub -all {create_clock[^\n]*-name[^\n]*DIFFTEST_PCIE_CLK[^\n]*TO_DIFFTEST_PCIE_CLK[^\n]*} \ + $patched $xdma_blackbox_clock_replacement patched + regsub -all {create_generated_clock[^\n]*-name[^\n]*core_def_xdma_ep_i_TO_DIFFTEST_PCIE_CLK_infer[^\n]*TO_DIFFTEST_PCIE_CLK[^\n]*} \ + $patched $xdma_blackbox_clock_replacement patched + regsub -all {core_def_xdma_ep_i_TO_DIFFTEST_PCIE_CLK_infer} $patched {XDMA_AXI_ACLK} patched + regsub -all $difftest_clock_token_pattern $patched {\1XDMA_AXI_ACLK\2} patched + if {[string first {set ::uvhs_async_clocks_loaded 1} $patched] < 0} { + append patched $async_xdc_block + } + if {$patched ne $data} { + set fh [open $file w] + puts -nonewline $fh $patched + close $fh + puts "INFO: patched Vivado DIFFTEST PCIe / DDR UI clock timing constraints: $file" + } + } + +} + +proc patch_xdma_gt_refclk_pad_constraints {} { + foreach file [concat \ + [glob -nocomplain hw.dat/Compile/PnR/*/*/pin_assign.xdc] \ + [glob -nocomplain hw.dat/Compile/PnR/*/*/dlp.xdc]] { + set fh [open $file r] + set data [read $fh] + close $fh + if {[string first {set ::uvhs_xdma_gt_refclk_pad_constraints 1} $data] >= 0} { + continue + } + + set p_ports [regexp -inline -all {pcie_ep_gt_ref_clk_p_pad_net_[0-9]+} $data] + set n_ports [regexp -inline -all {pcie_ep_gt_ref_clk_n_pad_net_[0-9]+} $data] + set refclk_ports {} + foreach port [concat $p_ports $n_ports] { + if {[lsearch -exact $refclk_ports $port] < 0} { + lappend refclk_ports $port + } + } + if {![llength $refclk_ports]} { + continue + } + + set block "\nset ::uvhs_xdma_gt_refclk_pad_constraints 1\n" + foreach port $refclk_ports { + append block "set_property IO_BUFFER_TYPE NONE \[get_ports -quiet {$port}\]\n" + append block "set_property CLOCK_BUFFER_TYPE NONE \[get_ports -quiet {$port}\]\n" + } + set patched $data + if {[regexp {set_property PACKAGE_PIN [^\n]+pcie_ep_gt_ref_clk_[pn]_pad_net_[0-9]+[^\n]*} $patched first_refclk_line]} { + set idx [string first $first_refclk_line $patched] + set patched [string range $patched 0 [expr {$idx - 1}]] + append patched $block $first_refclk_line + append patched [string range $data [expr {$idx + [string length $first_refclk_line]}] end] + } else { + append patched $block + } + set fh [open $file w] + puts -nonewline $fh $patched + close $fh + puts "INFO: patched XDMA GT refclk pad constraints: $file" + } +} + +proc patch_xdma_cdc_attribute_constraints {} { + foreach pnr_dir [glob -nocomplain hw.dat/Compile/PnR/*/*] { + set script [file join $pnr_dir xdma_cdc_attributes.tcl] + set fh [open $script w] + puts $fh { +# These patterns cover synchronizers owned by the fixed XDMA IP. They do not +# suppress or false-path CDC issues in fpga_diff-owned RTL. +proc ::uvhs_xdma_mark_cdc_cells {uvhs_label uvhs_patterns} { + set uvhs_cells {} + foreach uvhs_pattern $uvhs_patterns { + foreach uvhs_cell [get_cells -hier -quiet -filter "IS_SEQUENTIAL && NAME =~ $uvhs_pattern"] { + if {[lsearch -exact $uvhs_cells $uvhs_cell] < 0} { + lappend uvhs_cells $uvhs_cell + } + } + } + if {[llength $uvhs_cells]} { + set_property ASYNC_REG TRUE $uvhs_cells + } + puts "INFO: UVHS patch: XDMA CDC category $uvhs_label ASYNC_REG count=[llength $uvhs_cells]" + return $uvhs_cells +} + +set uvhs_xdma_cdc_cells {} +foreach {uvhs_label uvhs_patterns} { + auto_cc_clock_conv_lite { + */core_def/xdma_ep_i/axi_interconnect_0/*/auto_cc/inst/gen_clock_conv.gen_async_lite_conv*/clock_conv_lite_*/*_reg* + */core_def/xdma_ep_i/axi_interconnect_0/*/auto_cc/inst/gen_clock_conv.gen_async_lite_conv*/clock_conv_lite_*/handshake/*_reg* + */core_def/xdma_ep_i/axi_interconnect_0/*/auto_cc/*clock_conv_lite*/*_reg* + */core_def/xdma_ep_i/axi_interconnect_0/*/auto_cc/*handshake*/*_reg* + */core_def/xdma_ep_i/axi_interconnect_0/*/auto_cc/*cdc*/*_reg* + */core_def/xdma_ep_i/axi_interconnect_0/*/auto_cc/*sync*/*_reg* + } + auto_cc_xpm_cdc { + */core_def/xdma_ep_i/axi_interconnect_0/*/auto_cc/inst/*xpm_cdc*/*_reg* + */core_def/xdma_ep_i/axi_interconnect_0/*/auto_cc/inst/*xpm_cdc*/inst/*_reg* + */core_def/xdma_ep_i/axi_interconnect_0/*/auto_cc/*xpm_cdc*/*_reg* + */core_def/xdma_ep_i/axi_interconnect_0/*/auto_cc/*xpm_cdc*/inst/*_reg* + } + xdma_reset_and_link { + */core_def/xdma_ep_i/xdma_0/inst/*/*cdc*/*_reg* + */core_def/xdma_ep_i/xdma_0/inst/*/*cdc*/inst/*_reg* + */core_def/xdma_ep_i/xdma_0/inst/*/xpm_cdc*/*_reg* + */core_def/xdma_ep_i/xdma_0/inst/*/xpm_cdc*/inst/*_reg* + */core_def/xdma_ep_i/xdma_0/inst/*/arststages_ff_reg* + */core_def/xdma_ep_i/xdma_0/inst/*/*user_rst*_reg* + */core_def/xdma_ep_i/xdma_0/inst/udma_wrapper/dma_top/user_rst*_reg* + */core_def/xdma_ep_i/xdma_0/inst/udma_wrapper/dma_top/*user_rst*_reg* + */core_def/xdma_ep_i/xdma_0/inst/pcie4c_ip_i/inst/*/*cdc*/*_reg* + */core_def/xdma_ep_i/xdma_0/inst/pcie4c_ip_i/inst/*/*cdc*/inst/*_reg* + */core_def/xdma_ep_i/xdma_0/inst/pcie4c_ip_i/inst/*/*sync*/*_reg* + */core_def/xdma_ep_i/xdma_0/inst/pcie4c_ip_i/inst/*/*sync*/inst/*_reg* + */core_def/xdma_ep_i/xdma_0/inst/pcie4c_ip_i/inst/*/*rst*sync*reg* + */core_def/xdma_ep_i/xdma_0/inst/pcie4c_ip_i/inst/*/*reset*sync*reg* + } +} { + foreach uvhs_cell [::uvhs_xdma_mark_cdc_cells $uvhs_label $uvhs_patterns] { + if {[lsearch -exact $uvhs_xdma_cdc_cells $uvhs_cell] < 0} { + lappend uvhs_xdma_cdc_cells $uvhs_cell + } + } +} + +if {[llength $uvhs_xdma_cdc_cells]} { + set_property ASYNC_REG TRUE $uvhs_xdma_cdc_cells + puts "INFO: UVHS patch: marked XDMA CDC cells ASYNC_REG count=[llength $uvhs_xdma_cdc_cells]" +} else { + puts "INFO: UVHS patch: no XDMA CDC cells matched for ASYNC_REG annotation" +} +} + close $fh + puts "INFO: wrote XDMA CDC attribute script: $script" + + foreach hook_name {before_link.tcl pre_link.tcl} { + set hook [file join $pnr_dir $hook_name] + if {![file exists $hook]} { + continue + } + set fh [open $hook r] + set data [read $fh] + close $fh + set patched $data + regsub -all {\nadd_file \$FILE_DIR/xdma_cdc_attributes\.xdc} $patched "" patched + regsub -all {\nsource \$FILE_DIR/xdma_cdc_attributes\.tcl} $patched "" patched + if {$patched ne $data} { + set fh [open $hook w] + puts -nonewline $fh $patched + close $fh + puts "INFO: removed pre-link XDMA CDC attribute hook: $hook" + } + } + } +} + +proc patch_vivado_makefiles {} { + if {[info exists ::env(UV_SHELL)] && $::env(UV_SHELL) ne ""} { + set uv_shell $::env(UV_SHELL) + } elseif {[info exists ::env(UV_ROOT)] && $::env(UV_ROOT) ne ""} { + set uv_shell [file join $::env(UV_ROOT) bin uv_shell] + } else { + set uv_shell uv_shell + } + set replacement "bash $uv_shell" + foreach makefile [glob -nocomplain hw.dat/Compile/PnR/*/*/vivado/Rundir/*/Makefile] { + set fh [open $makefile r] + set data [read $fh] + close $fh + set patched $data + regsub -all {(^|[[:space:]&;])uv_shell([[:space:]])} \ + $patched "\\1$replacement\\2" patched + regsub -all {(^|[[:space:]&;])(/[^[:space:]]+/uv_shell)([[:space:]])} \ + $patched "\\1$replacement\\3" patched + regsub -all {(^|[[:space:]&;(@])(bash[[:space:]]+)+(/[^[:space:]]+/uv_shell)} \ + $patched "\\1$replacement" patched + regsub -all {(^|[[:space:]])@uv_shell([[:space:]])} \ + $patched "\\1@$replacement\\2" patched + if {$patched ne $data} { + set fh [open $makefile w] + puts -nonewline $fh $patched + close $fh + puts "INFO: patched UVHS Makefile uv_shell invocation: $makefile" + } + } +} + +set_working_space hw.dat + +set fpga_threads [env_or_default UVHS_FPGA_THREADS 8] +set fpga_processes [env_or_default UVHS_FPGA_PROCESSES 16] +if {[env_or_default UVHS_USE_LSF 1]} { + set_parallel_option -max_threads $fpga_threads -max_processes $fpga_processes -submit_command {bsub -R "rusage[mem=80000]"} -terminate_command bkill -label fpga +} else { + set_parallel_option -max_threads $fpga_threads -max_processes $fpga_processes -label fpga +} + +set_option time.auto_clock_config true +set_option clock.transform_clock.multi_iteration true +set_option clock.glitch.force_transform true +set_option clock.async_control.force_accept true +set_option time.enable_sign_off true +set_option time.incremental_sign_off true +set uvhs_time_group_io_logic [env_or_default UVHS_TIME_GROUP_IO_LOGIC default] +if {$uvhs_time_group_io_logic ne "default"} { + if {$uvhs_time_group_io_logic ni {0 1 false true}} { + error "UVHS_TIME_GROUP_IO_LOGIC must be one of default/0/1/false/true, got '$uvhs_time_group_io_logic'" + } + puts "INFO: set time.group_io_logic=$uvhs_time_group_io_logic" + set_option time.group_io_logic $uvhs_time_group_io_logic +} + +set design_name [env_or_default UVHS_DESIGN_NAME VU19P_X4] +set platform [env_or_default PLATFORM U2.2] +create_system_design -name $design_name -platform $platform +source_if_exists [env_or_default UVHS_ASSEMBLE_FILE ./script/1B_4F_HGC_assemble.tcl] +set assign_pin_file [env_or_default UVHS_ASSIGN_PIN_FILE ./script/assign_pin.tcl] +if {[file exists $assign_pin_file]} { + set ::env(UVHS_ASSIGN_PIN_TOP) none + source_if_exists $assign_pin_file + unset -nocomplain ::env(UVHS_ASSIGN_PIN_TOP) +} else { + error "missing pin assignment file: $assign_pin_file" +} +source_if_exists [env_or_default UVHS_PIN_OVERRIDE_FILE none] + +create_design -name test +read_netlist +link_design +report_resource -depth 4 + +instrument_design +sanitize_design +check_design +init_runtime_data +trigger_probe -check +sweep_design + +create_clock_if_port_exists TMCLK 1000 clk8_p +create_clock_if_port_exists ddr_ref_clk 12.5 clk7_p +if {[uvhs_cpu_debug_clk]} { + create_clock_if_port_exists CPU_CLK_IN [uvhs_cpu_clk_period_ns] clk5_p +} else { + create_clock_if_port_exists CPU_CLK_IN [uvhs_cpu_clk_period_ns] clk6_p + create_clock_if_port_exists DEBUG_CLK_IN 40 clk5_p +} +create_clock_if_port_exists jtag_vclk 83.333 JTAG_TCK +create_clock_if_port_exists pcie_ep_refclk 10 pcie_ep_gt_ref_clk_p +create_generated_clock_if_bufgce_exists SOC_GATED_CLK CPU_CLK_IN { + *SOC_CLK_CTRL*bufgce* + *inter_soc_clk* +} +create_generated_clock_if_bufgce_exists RTC_GATED_CLK TMCLK { + *RTC_CLK_CTRL*bufgce* + *inter_rtc_clk* +} +create_blackbox_output_clock_if_pin_exists XDMA_AXI_ACLK [uvhs_xdma_axi_clk_period_ns] core_def/xdma_ep_i/TO_DIFFTEST_PCIE_CLK +create_ddr_ui_clock_if_exists +run_or_warn "infer_clock" {infer_clock} +run_or_warn "report_clock -inferred" {report_clock -inferred} +source_if_exists [file normalize [file join [file dirname [info script]] async_clocks.tcl]] +run_or_warn "transform_clock" {transform_clock} +source_if_exists [file normalize [file join [file dirname [info script]] async_clocks.tcl]] +uvhs_config_fill_rates +uvhs_config_probe_group_resource +trigger_probe -group +sweep_design -remap +report_clock + +check_design +report_resource -depth 4 +report_system_resource +list_partition_constraints -all +partition_design -tdc -tdss true +report_resource -depth 4 + +if {[env_or_default UVHS_STOP_AFTER_PARTITION 0]} { + puts "INFO: UVHS_STOP_AFTER_PARTITION is set, stop before localization/route/FPGA compile" + commit_runtime_data + exit +} + +instrument_design +localize_design -replicate_cell -clock -self_check +sweep_design -keep_feedthrough +localize_design -data +route_design +check_timing -verbose +report_system_performance -show_clock_relation -verbose +report_path -normalize -exception -tdr -net -rtl -max_path 100 -sort_by fmax + +insert_tdm +reopt_design -verbose +bind_system +save_runtime_data + +set_option compile.resourceUsageLimit [env_or_default UVHS_RESOURCE_USAGE_LIMIT 100] +set strategy_num_retry [env_or_default UVHS_STRATEGY_NUM_RETRY none] +if {$strategy_num_retry eq "none" || $strategy_num_retry eq "0"} { + puts "INFO: skip compile.strategyNumRetry" +} else { + run_or_warn "set_option compile.strategyNumRetry" \ + [list set_option compile.strategyNumRetry $strategy_num_retry] +} + +set compile_strategy_num [env_or_default UVHS_COMPILE_STRATEGY_NUM 1] +if {![string is integer -strict $compile_strategy_num] || $compile_strategy_num < 1} { + error "UVHS_COMPILE_STRATEGY_NUM must be a positive integer, got '$compile_strategy_num'" +} +set_option compile.strategyNum $compile_strategy_num +set default_compile_strategies { + uv_high_fanout_explore + uv_placer_balance_slrs + uv_placer_extra_timing_opt +} +for {set i 0} {$i < $compile_strategy_num} {incr i} { + set default_strategy "" + if {$i < [llength $default_compile_strategies]} { + set default_strategy [lindex $default_compile_strategies $i] + } + set strategy [env_or_default [format {UVHS_COMPILE_STRATEGY%d} $i] $default_strategy] + if {$strategy eq "" || $strategy eq "none"} { + error "missing compile strategy $i; set UVHS_COMPILE_STRATEGY$i" + } + puts "INFO: UVHS compile.strategy$i=$strategy" + set_option [format {compile.strategy%d} $i] $strategy +} +set_option compile.stage.preOpt ./script/pre_opt.tcl +set_option compile.stage.prePlace ./script/pre_place.tcl + +compile_fpga -parallel_option fpga -genScriptOnly -explore +# The Hejian flow emits the final Vivado hooks only after genScriptOnly. +# Patch those generated files before starting the implementation run. +patch_timing_constraint_files +patch_xdma_gt_refclk_pad_constraints +patch_xdma_cdc_attribute_constraints +patch_vivado_wrappers +patch_vivado_makefiles +patch_vivado_pre_opt +patch_vivado_pre_place +patch_vivado_before_route +if {[env_or_default UVHS_STOP_AFTER_GENSCRIPT 0]} { + puts "INFO: UVHS_STOP_AFTER_GENSCRIPT is set, stop after generated Vivado scripts are patched" + exit +} +compile_fpga -parallel_option fpga -runOnly -explore + +report_path -max_path 100 +report_system_performance +commit_runtime_data +exit diff --git a/fpga_diff/uvhs/check_flow_tools.sh b/fpga_diff/uvhs/check_flow_tools.sh new file mode 100755 index 00000000..6af2d30e --- /dev/null +++ b/fpga_diff/uvhs/check_flow_tools.sh @@ -0,0 +1,92 @@ +#!/usr/bin/env bash +set -euo pipefail + +script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +root_dir="$(cd "$script_dir/.." && pwd)" +failures=0 + +fail() { + echo "FAIL: $*" >&2 + failures=$((failures + 1)) +} + +require_file() { + [ -f "$root_dir/$1" ] || fail "missing required flow file: $1" +} + +require_text() { + local path="$1" + local text="$2" + grep -Fq "$text" "$root_dir/$path" || fail "$path does not contain required text: $text" +} + +for path in \ + Makefile README.md \ + uvhs/Makefile uvhs/flow.md uvhs/setenv.sh uvhs/setenv.local.example.sh \ + uvhs/hejian_pcie_x4_env.sh uvhs/frontend_run.tcl uvhs/backend_run.tcl \ + uvhs/assemble_uvhs.tcl uvhs/assign_pin_nutshell_f2.tcl uvhs/async_clocks.tcl \ + uvhs/uvhs_preflight_status.sh uvhs/uvhs_tagged_runtime.sh \ + uvhs/tools/build/patch_nutshell_cdc.sh \ + user_script/hw_run_download.tcl \ + tools/report_post_route_cdc.tcl tools/report_pcie_route_evidence.tcl \ + src/rtl/common/core_def_xdma.sv src/rtl/common/uvhs_axi64_to_axi256.sv \ + src/rtl/common/uvhs_axilite_cdc_bridge.sv src/rtl/common/uvhs_blackbox_stubs.v \ + src/tcl/common/AXI_bridge.tcl; do + require_file "$path" +done + +echo "## Shell syntax" +while IFS= read -r -d '' path; do + bash -n "$root_dir/$path" || fail "shell syntax: $path" +done < <(git -C "$root_dir" ls-files -z 'uvhs/**/*.sh' | sort -z) + +echo "## Python syntax" +while IFS= read -r -d '' path; do + if ! python3 - "$root_dir/$path" <<'PY' +import pathlib +import sys + +path = pathlib.Path(sys.argv[1]) +compile(path.read_bytes(), str(path), "exec") +PY + then + fail "python syntax: $path" + fi +done < <(git -C "$root_dir" ls-files -z 'uvhs/**/*.py' | sort -z) + +echo "## Tcl completeness" +if command -v tclsh >/dev/null 2>&1; then + while IFS= read -r -d '' path; do + if ! tclsh /dev/stdin "$root_dir/$path" <<'TCL' +set path [lindex $argv 0] +set fh [open $path r] +set data [read $fh] +close $fh +if {![info complete $data]} { + puts stderr "incomplete Tcl input: $path" + exit 1 +} +TCL + then + fail "Tcl completeness: $path" + fi + done < <(git -C "$root_dir" ls-files -z \ + 'uvhs/**/*.tcl' 'tools/**/*.tcl' 'user_script/**/*.tcl' | sort -z) +else + fail "tclsh is required for Tcl completeness checks" +fi + +echo "## Safety defaults" +require_text uvhs/uvhs_tagged_runtime.sh 'UVHS_RUN_TAG:?set a unique UVHS_RUN_TAG' +if git -C "$root_dir" grep -En '/home/(user01|data/test/(fengkehan|codex))' -- uvhs; then + fail "personal or historical runtime path remains in UVHS tools" +fi +if git -C "$root_dir" grep -En '/sys/bus/pci/(devices/.*/remove|devices/.*/reset|drivers/.*/unbind)' -- uvhs; then + fail "forbidden PCIe remove/reset/unbind operation found" +fi + +if [ "$failures" -ne 0 ]; then + echo "FAIL: $failures UVHS flow tool check(s) failed" >&2 + exit 1 +fi +echo "OK: UVHS flow tools are complete and statically valid" diff --git a/fpga_diff/uvhs/check_modules.sh b/fpga_diff/uvhs/check_modules.sh new file mode 100755 index 00000000..869cbcb9 --- /dev/null +++ b/fpga_diff/uvhs/check_modules.sh @@ -0,0 +1,56 @@ +#!/usr/bin/env bash +set -euo pipefail + +filelist=${1:-} +required_modules=${2:-} + +if [[ -z "$filelist" || ! -f "$filelist" ]]; then + echo "Verilog filelist not found: $filelist" >&2 + exit 2 +fi + +if [[ -z "$required_modules" ]]; then + echo "No required modules configured; skip module check." + exit 0 +fi + +tmp_sources=$(mktemp) +trap 'rm -f "$tmp_sources"' EXIT + +awk 'NF && $0 !~ /^\+/ { print }' "$filelist" > "$tmp_sources" + +missing=0 +for module_name in $required_modules; do + found=0 + module_source= + while IFS= read -r source_file; do + [[ -f "$source_file" ]] || continue + if grep -Eq "^[[:space:]]*module[[:space:]]+$module_name([[:space:]#(]|$)" "$source_file"; then + found=1 + module_source=$source_file + break + fi + done < "$tmp_sources" + + if [[ "$found" == 1 ]]; then + echo "Found required module: $module_name" + if [[ "$module_name" == SimTop ]]; then + for port in \ + difftest_to_host_axis_tdata difftest_from_host_axis_tdata \ + difftest_cfg_axilite_awaddr difftest_cfg_axilite_rready; do + if ! grep -Fq "$port" "$module_source"; then + echo "Missing required SimTop FPGA DiffTest port $port in $module_source" >&2 + missing=1 + fi + done + fi + else + echo "Missing required module: $module_name" >&2 + missing=1 + fi +done + +if [[ "$missing" != 0 ]]; then + echo "Required module check failed for $filelist" >&2 + exit 1 +fi diff --git a/fpga_diff/uvhs/export_vivado_ip.tcl b/fpga_diff/uvhs/export_vivado_ip.tcl new file mode 100644 index 00000000..b005158c --- /dev/null +++ b/fpga_diff/uvhs/export_vivado_ip.tcl @@ -0,0 +1,363 @@ +################################################################################ +# Export Vivado IP/BD checkpoints for UVHS blackbox import. +################################################################################ + +set origin_dir "." +set out_dir "./uvhs_ip" +set vivado_version "" +set force 0 +set jobs 8 +set cpu "kmh" +set core_dir "" +set only "" +set skip_exports [list] + +proc print_help {} { + puts "Usage:" + puts " vivado -mode batch -source uvhs/export_vivado_ip.tcl -tclargs \\" + puts " --origin_dir --out_dir \[--vivado_version \] \[--cpu \] \[--core_dir \] \[--only \] \[--skip \] \[--force\] \[--jobs N\]" + exit 0 +} + +for {set i 0} {$i < $::argc} {incr i} { + set option [lindex $::argv $i] + switch -- $option { + "--origin_dir" { incr i; set origin_dir [lindex $::argv $i] } + "--out_dir" { incr i; set out_dir [lindex $::argv $i] } + "--vivado_version" { incr i; set vivado_version [lindex $::argv $i] } + "--cpu" { incr i; set cpu [lindex $::argv $i] } + "--core_dir" { incr i; set core_dir [lindex $::argv $i] } + "--only" { incr i; set only [lindex $::argv $i] } + "--skip" { incr i; lappend skip_exports [lindex $::argv $i] } + "--force" { set force 1 } + "--jobs" { incr i; set jobs [lindex $::argv $i] } + "--help" { print_help } + default { + puts "ERROR: unknown option $option" + print_help + } + } +} + +set origin_dir [file normalize $origin_dir] +set out_dir [file normalize $out_dir] +if {$core_dir ne ""} { + set core_dir [file normalize $core_dir] +} +set tcl_dir [file join $origin_dir src tcl common] +set export_project_dir [file join $out_dir vivado_ip_export] +set export_project [file join $export_project_dir vivado_ip_export.xpr] + +if {$vivado_version eq ""} { + set vivado_version [version -short] +} +set ::vivado_version $vivado_version +set ::cpu $cpu +set ::core_dir $core_dir + +if {$force && [file exists $export_project_dir]} { + file delete -force $export_project_dir +} + +file mkdir $out_dir +file mkdir [file join $out_dir rtl soc] +file mkdir [file join $out_dir rtl device pcie] +file mkdir $export_project_dir + +if {[file exists $export_project]} { + open_project $export_project +} else { + create_project vivado_ip_export $export_project_dir -part xcvu19p-fsva3824-2-e + set_property target_language Verilog [current_project] + set_property simulator_language Mixed [current_project] +} + +proc add_core_generated_headers {core_dir} { + if {$core_dir eq "" || ![file isdirectory $core_dir]} { + return + } + + set header_candidates [list \ + [file join $core_dir build generated-src DifftestMacros.svh] \ + [file join $core_dir generated-src DifftestMacros.svh] \ + [file join $core_dir build rtl DifftestMacros.svh] \ + [file join $core_dir rtl DifftestMacros.svh] \ + ] + foreach hdr $header_candidates { + if {![file exists $hdr]} { + continue + } + set hdr [file normalize $hdr] + if {[llength [get_files -quiet $hdr]] == 0} { + add_files -norecurse $hdr + } + set hdr_file [get_files -quiet $hdr] + if {[llength $hdr_file] > 0} { + set_property file_type {Verilog Header} $hdr_file + } + puts "INFO: added core generated header $hdr" + return + } + puts "WARNING: DifftestMacros.svh not found under core_dir $core_dir" +} + +add_core_generated_headers $core_dir + +proc source_ip_tcl {script} { + if {![file exists $script]} { + puts "WARNING: missing IP script $script" + return 0 + } + puts "INFO: source $script" + set rc [catch {uplevel #0 [list source $script]} err opts] + if {$rc != 0} { + puts "ERROR: failed to source $script" + puts $err + return -options $opts $err + } + return 1 +} + +proc run_and_copy_dcp {run_name out_file jobs force} { + if {[file exists $out_file] && !$force} { + puts "INFO: reuse existing $out_file" + return + } + + set run [get_runs -quiet $run_name] + if {[llength $run] == 0} { + error "run $run_name not found" + } + + reset_run $run + clear_auto_incremental_imports $run_name + launch_runs $run -jobs $jobs + wait_on_run $run + + set status [get_property STATUS $run] + puts "INFO: $run_name status: $status" + + set run_dir [get_property DIRECTORY $run] + set dcps [glob -nocomplain [file join $run_dir *.dcp]] + if {[llength $dcps] == 0} { + if {![string match "*Complete*" $status]} { + error "$run_name did not complete: $status" + } + error "no DCP generated in $run_dir" + } + + file mkdir [file dirname $out_file] + file copy -force [lindex $dcps 0] $out_file + puts "INFO: exported $out_file" +} + +proc run_and_write_opened_dcp {run_name out_file jobs force} { + if {[file exists $out_file] && !$force} { + puts "INFO: reuse existing $out_file" + return + } + + set run [get_runs -quiet $run_name] + if {[llength $run] == 0} { + error "run $run_name not found" + } + + reset_run $run + clear_auto_incremental_imports $run_name + launch_runs $run -jobs $jobs + wait_on_run $run + + set status [get_property STATUS $run] + puts "INFO: $run_name status: $status" + if {![string match "*Complete*" $status]} { + error "$run_name did not complete: $status" + } + + catch {close_design} + open_run $run_name + file mkdir [file dirname $out_file] + write_checkpoint -force $out_file + puts "INFO: exported stitched checkpoint $out_file" +} + +proc clear_auto_incremental_imports {run_name} { + global export_project_dir + + set run [get_runs -quiet $run_name] + if {[llength $run] > 0} { + set props [list_property $run] + foreach {prop value} { + AUTO_INCREMENTAL_CHECKPOINT 0 + INCREMENTAL_CHECKPOINT {} + STEPS.SYNTH_DESIGN.ARGS.INCREMENTAL_MODE off + } { + if {[lsearch -exact $props $prop] >= 0} { + set rc [catch {set_property $prop $value $run} err] + if {$rc == 0} { + puts "INFO: cleared auto-incremental property $prop on run $run_name" + } else { + puts "WARNING: failed to clear auto-incremental property $prop on run $run_name: $err" + } + } + } + } + + foreach imports_dir [glob -nocomplain [file join $export_project_dir *.srcs utils_1 imports $run_name]] { + file delete -force $imports_dir + puts "INFO: removed stale auto-incremental checkpoint imports for $run_name: $imports_dir" + } +} + +proc verify_checkpoint {label dcp} { + open_checkpoint $dcp + set cell_count [llength [get_cells -hier -quiet]] + if {$cell_count == 0} { + close_design + error "$label synthesized to an empty design" + } + set blackbox_cells [get_cells -hier -quiet -filter {IS_BLACKBOX == 1}] + set blackbox_names [list] + if {[llength $blackbox_cells] > 0} { + set blackbox_names [get_property NAME $blackbox_cells] + } + close_design + set unexpected_blackboxes [list] + foreach blackbox $blackbox_names { + if {$blackbox eq "dbg_hub"} { + puts "INFO: $label keeps Vivado debug hub blackbox $blackbox" + } else { + lappend unexpected_blackboxes $blackbox + } + } + if {[llength $unexpected_blackboxes] > 0} { + error "$label still has blackbox cells after checkpoint export: $unexpected_blackboxes" + } +} + +proc launch_run_list {run_list jobs} { + if {[llength $run_list] == 0} { + return + } + + foreach run $run_list { + reset_run $run + } + launch_runs $run_list -jobs $jobs + foreach run $run_list { + wait_on_run $run + } + + foreach run $run_list { + set status [get_property STATUS $run] + puts "INFO: $run status: $status" + if {![string match "*Complete*" $status] && ![string match "*cached IP results*" $status]} { + error "IP run $run did not complete: $status" + } + } +} + +proc create_and_launch_bd_ip_runs {bd jobs} { + catch {create_ip_run $bd} + set bd_name [file rootname [file tail $bd]] + set run_list [get_runs -quiet ${bd_name}_*_synth_1] + launch_run_list $run_list $jobs + return $run_list +} + +proc export_xci_ip {name script out_file jobs force} { + if {[llength [get_ips -quiet $name]] == 0} { + source_ip_tcl $script + } + + set ip [get_ips -quiet $name] + if {[llength $ip] == 0} { + error "IP $name was not created by $script" + } + + set ip_file [get_property IP_FILE $ip] + if {$ip_file ne ""} { + set ip_file_obj [get_files -quiet $ip_file] + if {[llength $ip_file_obj] > 0} { + set_property GENERATE_SYNTH_CHECKPOINT true $ip_file_obj + } + } + generate_target all $ip + catch {create_ip_run $ip} + run_and_copy_dcp ${name}_synth_1 $out_file $jobs $force + verify_checkpoint "IP $name" $out_file +} + +proc export_bd_ip {name script out_file jobs force} { + if {[llength [get_files -quiet ${name}.bd]] == 0} { + source_ip_tcl $script + } + + set bd [get_files -quiet ${name}.bd] + if {[llength $bd] == 0} { + error "BD $name was not created by $script" + } + + open_bd_design $bd + current_bd_design $name + validate_bd_design + save_bd_design + generate_target all $bd + catch {make_wrapper -files $bd -top} + + if {[file exists $out_file] && !$force} { + puts "INFO: reuse existing $out_file" + return + } + + # Build child IP OOC runs first, then synthesize the BD top as one + # checkpoint. This keeps the exported artifact as a single DCP while + # letting Vivado handle encrypted or packaged IP internals through their + # generated runs instead of compiling raw shared sources. + create_and_launch_bd_ip_runs $bd $jobs + set_property top $name [current_fileset] + update_compile_order -fileset sources_1 + run_and_write_opened_dcp synth_1 $out_file $jobs $force + verify_checkpoint "BD $name" $out_file +} + +set exports [list \ + [list xci blk_mem_gen_0 [file join $tcl_dir blk_mem_gen_0.tcl] [file join $out_dir rtl soc blk_mem_gen_0.dcp]] \ + [list bd AXI_bridge [file join $tcl_dir AXI_bridge.tcl] [file join $out_dir rtl soc AXI_bridge.dcp]] \ + [list bd data_bridge [file join $tcl_dir data_bridge.tcl] [file join $out_dir rtl soc data_bridge.dcp]] \ + [list xci vio_0 [file join $tcl_dir vio_0.tcl] [file join $out_dir rtl soc vio_0.dcp]] \ + [list bd xdma_ep [file join $tcl_dir xdma_ep.tcl] [file join $out_dir rtl device pcie xdma_ep.dcp]] \ +] + +set failed_exports [list] +foreach item $exports { + lassign $item kind name script out_file + if {$only ne "" && $name ne $only} { + puts "INFO: skip $name because --only $only is set" + continue + } + if {[lsearch -exact $skip_exports $name] >= 0} { + puts "INFO: skip $name because --skip requested" + continue + } + puts "INFO: exporting $kind $name" + set rc [catch { + if {$kind eq "xci"} { + export_xci_ip $name $script $out_file $jobs $force + } else { + export_bd_ip $name $script $out_file $jobs $force + } + } err opts] + if {$rc != 0} { + catch {close_design} + lappend failed_exports $name + puts "ERROR: failed to export $name" + puts "ERROR: $err" + } +} + +if {[llength $failed_exports] > 0} { + error "Vivado IP/BD DCP export failed for: $failed_exports" +} + +close_project +puts "INFO: Vivado IP export finished." diff --git a/fpga_diff/uvhs/filelist.awk b/fpga_diff/uvhs/filelist.awk new file mode 100644 index 00000000..6d7c9e2e --- /dev/null +++ b/fpga_diff/uvhs/filelist.awk @@ -0,0 +1,5 @@ +/^[[:space:]]*$/ { next } + +{ + print $0 +} diff --git a/fpga_diff/uvhs/flow.md b/fpga_diff/uvhs/flow.md new file mode 100644 index 00000000..b8b22326 --- /dev/null +++ b/fpga_diff/uvhs/flow.md @@ -0,0 +1,205 @@ +# Hejian UVHS NutShell FPGA DiffTest Flow + +This is the self-contained runbook for the `env-scripts/fpga_diff` Hejian +UVHS flow. The target is the NutShell SoC with the MinJie XDMA driver and +`fpga-host`. + +## Validated Configuration + +The checked Hejian preset uses: + +- target `B0/F2`, platform `U2.2`, design `VU19P_X4` +- XDMA Gen3 x4, vendor/device `10ee:9048` +- BAR0 `0x80000` (512 KiB), BAR1 `0x10000` (64 KiB) +- CPU/SoC clock 25 MHz from `clk5_p` (`UVHS_CPU_DEBUG_CLK=1`, + `UVHS_CPU_CLK_PERIOD_NS=40`), matching the Vivado flow +- XDMA AXI clock 125 MHz and DDR UI clock 200 MHz +- PnR strategy `Strategy_uv_high_fanout_explore` +- BAR1 signatures: offset `0x2000` high16 `0x1fc2`, offset `0x3000` + high16 `0x1fc3` + +This configuration has completed FPGA DiffTest with `HIT GOOD TRAP`. + +## Prerequisites + +The caller supplies all machine-specific paths. Do not commit licenses, +passwords, host keys, build databases, DCPs, bitstreams, workloads, NEMU shared +objects, or kernel modules. + +Required inputs are: + +- a NutShell generated RTL tree passed as `CORE_DIR` +- the Hejian template archive visible through `UVHS_TARBALL` +- Hejian UVHS and matching Vivado installations configured by + `uvhs/setenv.local.sh` +- the checked x4 XDMA endpoint DCP and base assembly project +- the UVHS `uvw_axi4_to_ddr4` DCP/stub/constraints +- on the FPGA host, a locally compiled MinJie mainline `xdma-chr.ko` whose + vermagic matches the running kernel +- a NutShell workload binary and matching NEMU shared object for DiffTest + +Required executables by machine are: + +- build host: GNU `bash`, `make`, `awk`, `sed`, `grep`, `find`, `sort`, `tar`, + `sha256sum`, `realpath`, Python 3, `tclsh`, `csh`, Hejian `uv_shell`, and the + Vivado release matching the selected DCPs (2024.2 for the checked XDMA DCP) +- runtime host: Hejian `uv_shell -rt_shell`, `tmux`, and the board runtime + dependencies installed with UVHS +- FPGA host: `ssh`, `lspci`, `setpci`, `modinfo`, `lsmod`, `dmesg`, `fuser`, + `strace` with `-yy`, GNU `timeout`, Python 3, a C/C++ build toolchain, and the + running-kernel headers used to build `xdma-chr.ko` + +Prepare the local environment: + +```bash +cd fpga_diff +cp uvhs/setenv.local.example.sh uvhs/setenv.local.sh +# Set only local tool, license, hardware-server, and DCP paths in this ignored file. +source uvhs/setenv.sh +make uvhs_tools_check +``` + +`make uvhs_tools_check` is a static check. It does not invoke UVHS/Vivado, +program a board, rescan PCIe, write BARs, load a driver, or reboot a host. + +## Tool Inventory + +### Build and configuration + +| File | Purpose | +| --- | --- | +| `Makefile` | Public forwarding targets for the UVHS sub-make. | +| `uvhs/Makefile` | Work-tree generation, IP export, frontend/backend, signoff, and packaging. | +| `uvhs/setenv.sh` | Repository-generic environment loader. | +| `uvhs/setenv.local.example.sh` | Template for ignored machine-local settings. | +| `uvhs/hejian_pcie_x4_env.sh` | Print/check the validated x4/BAR/clock preset. | +| `uvhs/filelist.awk`, `uvhs/check_modules.sh` | Generate and validate the UVHS RTL file list. | +| `uvhs/export_vivado_ip.tcl` | Export Vivado IP DCPs used by the UVHS assembly. | +| `uvhs/frontend_run.tcl` | Build the UVHS frontend database. | +| `uvhs/backend_run.tcl` | Partition, implement, route, and generate the bitstream. | +| `uvhs/assemble_uvhs.tcl`, `uvhs/partition.tcl` | Apply the selected B0/F2 hardware assembly and partition policy. | +| `uvhs/assign_pin_nutshell_f2.tcl`, `uvhs/async_clocks.tcl`, `uvhs/timing_common.tcl` | NutShell pin and clock constraints. | +| `uvhs/patch_uvsyn_shell.sh`, `uvhs/uv_shell_exec_compat.sh`, `uvhs/make_compat/` | Versioned UVHS compatibility helpers. | +| `uvhs/tools/build/patch_nutshell_cdc.sh` | Apply the required idempotent CDC fix to generated NutShell RTL. | + +### SoC RTL and IP adaptation + +| File | Purpose | +| --- | --- | +| `src/rtl/common/core_def_xdma.sv` | Final NutShell SoC, DDR, XDMA, DiffTest, UART, reset, and clock integration. | +| `src/rtl/common/uvhs_axi64_to_axi256.sv` | CPU-side 64-bit to UVHS DDR 256-bit AXI adaptation. | +| `src/rtl/common/uvhs_axilite_cdc_bridge.sv` | Owned AXI-Lite CDC bridge for the XDMA control path. | +| `src/rtl/common/uvhs_blackbox_stubs.v` | UVHS/Vivado IP interface declarations. | +| `src/tcl/common/AXI_bridge.tcl` | XDMA/DiffTest AXI bridge generation. | + +### Reports, timing, CDC, and probes + +| File | Purpose | +| --- | --- | +| `tools/report_post_route_cdc.tcl` | Routed CDC report and owned-vs-IP classification evidence. | +| `tools/report_pcie_route_evidence.tcl` | Routed XDMA link, BAR, clock, and connectivity evidence. | +| `uvhs/status.sh`, `uvhs/uvhs_preflight_status.sh` | Read-only build/runtime status summaries. | + +### Runtime programming and workload + +| File | Purpose | +| --- | --- | +| `user_script/hw_run_download.tcl` | Load DB, program, update clocks/link, initialize, release resets, and keep the runtime attached. | +| `uvhs/uvhs_tagged_runtime.sh` | Exact-tag tmux start, readiness evidence, status, and cleanup. | + +The final `fpga-host` path writes the workload to DDR through H2C. Local probe, +BAR, XDMA, strace, recovery, and backdoor-DDR scripts are intentionally not +tracked in this repository. + +## Build + +Use a new suffix for every build: + +```bash +cd fpga_diff +make uvhs_tools_check +make uvhs_hejian_pcie_x4_preflight CORE_DIR=/path/to/NutShell +make uvhs_hejian_pcie_x4_nutshell_all \ + CORE_DIR=/path/to/NutShell \ + SUFFIX= +``` + +The work directory is: + +```text +fpga_diff_uvhs_nutshell- +``` + +The expected routed directory is: + +```text +hw.dat/Compile/PnR/B0/F2/vivado/Rundir/Strategy_uv_high_fanout_explore +``` + +Package a clean routed build: + +```bash +make uvhs_status CPU=nutshell SUFFIX= +make uvhs_check_timing_clean CPU=nutshell SUFFIX= +make uvhs_package_bitstream CPU=nutshell SUFFIX= +``` + +## Signoff Gate + +Before programming, retain the frontend, backend, PnR, bit-generation, timing, +CDC, and warning reports. Required gates are: + +- frontend `ERROR=0`, backend `ERROR=0`, PnR `PASS=1` +- bit generation failure count `0` +- WNS/TNS/WHS/THS have no failing values +- owned RTL, `U_CPU_TOP`, `SimTop`, DiffTest blocks, host blocks, and XDMA + adapter have zero Critical CDC +- XDMA/Aurora/UVHS IP-internal CDC is classified separately + +Do not hide an owned CDC issue with a false path. Stop before board programming +if owned Critical CDC is nonzero. Keep the generated warning log: warnings are +part of signoff evidence and must not be disabled globally. + +## Runtime Programming + +Stage the build database and helpers on the runtime host under a path containing +the exact run tag. Then run: + +```bash +export UVHS_RUN_TAG= +export UVHS_STAGE_DIR=/path/containing/ +export UVHS_ALLOW_DOWNLOAD=1 +export UVHS_ALLOW_DOWNLOAD_TAG="$UVHS_RUN_TAG" + +bash "$UVHS_STAGE_DIR/uvhs_preflight_status.sh" +bash "$UVHS_STAGE_DIR/uvhs_tagged_runtime.sh" start +bash "$UVHS_STAGE_DIR/uvhs_tagged_runtime.sh" status +``` + +Readiness requires logged evidence for `load_db`, download success, link up, +initialize, DDR initialize, and released `rstn_sw4/5/6`. The runtime shell must +remain attached while the FPGA host runs. Never reboot the runtime host, and +never kill a PID that is not proven to contain the exact current tag. + +## FPGA-host and DiffTest + +On the FPGA host, verify the expected Gen3 x4 `10ee:9048` endpoint, BAR sizes +and signatures, then load a matching locally built MinJie `xdma-chr.ko` and +confirm `/dev/xdma*`. Compile `fpga-host`, NEMU, and the NutShell workload on +that host. The normal flow sets `UVHS_DISABLE_DOWNLOAD=1` and lets `fpga-host` +write the workload through H2C. Board-specific automation and evidence capture +remain local rather than being shipped as part of the source flow. + +## Cleanup + +At the end of every run, stop only artifacts carrying the exact run tag: + +```bash +UVHS_RUN_TAG= \ +UVHS_STAGE_DIR=/path/containing/ \ +bash /path/containing//uvhs_tagged_runtime.sh cleanup +``` + +Retain build warnings, download/init logs, UART logs, host logs, strace summary, +C2H progress, `/dev/xdma*` occupancy, and dmesg evidence. Cleanup must show no +remaining tmux session or runtime process for the current tag. diff --git a/fpga_diff/uvhs/frontend_run.tcl b/fpga_diff/uvhs/frontend_run.tcl new file mode 100644 index 00000000..ec06e853 --- /dev/null +++ b/fpga_diff/uvhs/frontend_run.tcl @@ -0,0 +1,273 @@ +################################################################################ +# UVHS frontend flow for fpga_diff. +################################################################################ + +proc env_or_default {name default} { + if {[info exists ::env($name)] && $::env($name) ne ""} { + return $::env($name) + } + return $default +} + +proc split_words {value} { + set out [list] + foreach item $value { + if {$item ne ""} { + lappend out $item + } + } + return $out +} + +set required_dcp_modules [split_words [env_or_default UVHS_REQUIRED_DCP_MODULES {blk_mem_gen_0 AXI_bridge data_bridge vio_0 xdma_ep uvw_axi4_to_ddr4}]] + +proc dcp_module_is_required {module} { + return [expr {[lsearch -exact $::required_dcp_modules $module] >= 0}] +} + +proc source_if_exists {file} { + if {$file eq "none"} { + puts "INFO: skip $file" + return + } + if {[file exists $file]} { + puts "INFO: source $file" + if {[catch {uplevel #0 [list source $file]} err]} { + puts "WARNING: source $file failed: $err" + } + } else { + puts "INFO: skip missing $file" + } +} + +proc set_blackbox_if_exists {module dcp args} { + if {![dcp_module_is_required $module]} { + puts "INFO: skip blackbox $module because it is not listed in UVHS_REQUIRED_DCP_MODULES" + return + } + if {[file exists $dcp]} { + puts "INFO: set_blackbox $module $dcp" + set command [list set_blackbox -module $module -source_file $dcp] + if {[llength $args] > 0} { + set command [concat $command $args] + } + uplevel #0 $command + } else { + error "required blackbox DCP missing for $module: $dcp" + } +} + +proc set_ip_if_exists {module dcp stub args} { + if {![dcp_module_is_required $module]} { + puts "INFO: skip set_ip $module because it is not listed in UVHS_REQUIRED_DCP_MODULES" + return + } + if {![file exists $dcp]} { + error "required IP DCP missing for $module: $dcp" + } + if {![file exists $stub]} { + error "required IP stub missing for $module: $stub" + } + + puts "INFO: set_ip $module $dcp" + set command [list set_ip -module $module -source_file $dcp] + if {[llength $args] > 0} { + set command [concat $command $args] + } + uplevel #0 $command + set read_stub [env_or_default [string toupper UVHS_${module}_READ_STUB] 1] + if {$read_stub eq "0"} { + puts "INFO: skip read_verilog $stub because UVHS_[string toupper ${module}]_READ_STUB=0" + } else { + puts "INFO: read_verilog $stub" + uplevel #0 [list read_verilog $stub] + } +} + +proc require_file_for_uvhs_ip {description file} { + if {![file exists $file] || [file size $file] == 0} { + error "required UVHS DDR IP $description missing or empty: $file" + } + puts "INFO: found UVHS DDR IP $description: $file" +} + +proc read_verilog_if_exists {description file} { + if {![file exists $file] || [file size $file] == 0} { + error "required Verilog $description missing or empty: $file" + } + puts "INFO: read_verilog $file" + uplevel #0 [list read_verilog $file] +} + +proc run_or_warn {description command} { + puts "INFO: $description" + if {[catch {uplevel #0 $command} err]} { + puts "WARNING: $description failed: $err" + } +} + +proc append_line_if_missing {file line} { + if {$file eq "" || $line eq ""} { + return + } + set existing "" + if {[file exists $file]} { + set fh [open $file r] + set existing [read $fh] + close $fh + foreach entry [split $existing "\n"] { + if {[string trim $entry] eq $line} { + puts "INFO: keep existing option line in $file: $line" + return + } + } + } + set fh [open $file a] + if {$existing ne "" && ![string match *\n $existing]} { + puts $fh "" + } + puts $fh $line + close $fh + puts "INFO: appended option line to $file: $line" +} + +proc remove_line_if_present {file line} { + if {$file eq "" || $line eq "" || ![file exists $file]} { + return + } + set fh [open $file r] + set existing [read $fh] + close $fh + + set changed 0 + set kept [list] + foreach entry [split $existing "\n"] { + if {[string trim $entry] eq $line} { + set changed 1 + } else { + lappend kept $entry + } + } + if {!$changed} { + return + } + + set fh [open $file w] + puts -nonewline $fh [join $kept "\n"] + close $fh + puts "INFO: removed stale option line from $file: $line" +} + +proc patch_option_add_rtl_inst {working_space inst_path} { + if {$inst_path eq "" || $inst_path eq "none"} { + puts "INFO: skip option.add_rtl_inst patch" + return + } + set option_dir [file join $working_space DB Options] + file mkdir $option_dir + set add_inst_line "add_rtl_inst -inst_name $inst_path" + append_line_if_missing [file join $option_dir option.add_rtl_inst.txt] $add_inst_line + append_line_if_missing [file join $option_dir option.txt] $add_inst_line +} + +proc start_uvsyn_shell_patch_watcher {} { + set module_makefile [file join [pwd] hw.dat Synthesis Uvsyn Script module.makefile] + set patch_script [file normalize [file join [file dirname [info script]] patch_uvsyn_shell.sh]] + if {![file exists $patch_script]} { + puts "WARNING: missing uvsyn shell patch helper: $patch_script" + return + } + if {[catch {exec bash $patch_script $module_makefile &} err]} { + puts "WARNING: start uvsyn shell patch watcher failed: $err" + } else { + puts "INFO: started uvsyn shell patch watcher for $module_makefile" + } +} + +create_working_space hw.dat +set_option syn.computeFeCheckSum true + +set frontend_threads [env_or_default UVHS_FRONTEND_THREADS 16] +set frontend_processes [env_or_default UVHS_FRONTEND_PROCESSES 64] +set fpga_threads [env_or_default UVHS_FPGA_THREADS 8] +set fpga_processes [env_or_default UVHS_FPGA_PROCESSES 32] + +if {[env_or_default UVHS_USE_LSF 1]} { + set_parallel_option -max_threads $frontend_threads -max_processes $frontend_processes -submit_command bsub -terminate_command bkill -label frontend + set_parallel_option -max_threads $fpga_threads -max_processes $fpga_processes -submit_command {bsub -R "rusage[mem=80000]"} -terminate_command bkill -label fpga +} else { + set_parallel_option -max_threads $frontend_threads -max_processes $frontend_processes -label frontend + set_parallel_option -max_threads $fpga_threads -max_processes $fpga_processes -label fpga +} + +set_option global.log.label MEMORY +set_option syn.checkMultiDriver false +set_option syn.multipleDriverConflict WOR +set_option time.auto_clock_config true +set_option clock.transform_clock.multi_iteration true +set_option clock.glitch.force_transform true +set_option clock.async_control.force_accept true +set_option time.enable_sign_off true +set_option time.incremental_sign_off true +set_option signal.uhd.sampling_clock.allow_local_clock true +set_option syn.logicFillingRateThreshold 0.001 + +set design_name [env_or_default UVHS_DESIGN_NAME VU19P_X4] +set platform [env_or_default PLATFORM U2.2] +set design_top [env_or_default UVHS_TOP fpga_top_debug] +set ddr_inst_path ${design_top}.core_def.U_UVHS_UVW_AXI4_TO_DDR4 +set reset_ports [split_words [env_or_default UVHS_RESET_PORTS {rstn_sw6 rstn_sw5 rstn_sw4}]] +create_system_design -name $design_name -platform $platform + +set assemble_file [env_or_default UVHS_ASSEMBLE_FILE ./script/1B_4F_HGC_assemble.tcl] +if {$assemble_file eq "none"} { + puts "INFO: skip board assembly" +} else { + source_if_exists $assemble_file +} +set skip_board_constraints [env_or_default UVHS_SKIP_BOARD_CONSTRAINTS 0] + +if {$skip_board_constraints} { + puts "INFO: skip board probe/pin/timing/partition constraints" +} else { + source_if_exists [env_or_default UVHS_PROBE_FILE ./script/probe.tcl] + source_if_exists [env_or_default UVHS_ASSIGN_PIN_FILE ./script/assign_pin.tcl] + set timing_file [env_or_default UVHS_TIMING_FILE none] + if {$timing_file eq "none"} { + puts "INFO: skip timing constraints" + } else { + run_or_warn "set_constraint_files" [list set_constraint_files $timing_file] + } + set partition_file [env_or_default UVHS_PARTITION_FILE ./script/partition.tcl] + if {$partition_file eq "none"} { + puts "INFO: skip partition constraints" + } else { + run_or_warn "set_partition_constraint_file" [list set_partition_constraint_file $partition_file] + } + foreach reset_port $reset_ports { + set reset_net ${design_top}.${reset_port} + run_or_warn "create_reset $reset_net" [list create_reset -port $reset_net -active 0] + } +} + +set_blackbox_if_exists blk_mem_gen_0 ./rtl/soc/blk_mem_gen_0.dcp +set_blackbox_if_exists AXI_bridge ./rtl/soc/AXI_bridge.dcp +set_blackbox_if_exists data_bridge ./rtl/soc/data_bridge.dcp +set_blackbox_if_exists vio_0 ./rtl/soc/vio_0.dcp +set_blackbox_if_exists xdma_ep ./rtl/device/pcie/xdma_ep.dcp +require_file_for_uvhs_ip DCP ./rtl/soc/uvw_axi4_to_ddr4.dcp +require_file_for_uvhs_ip stub ./rtl/soc/uvw_axi4_to_ddr4_Stub.v +require_file_for_uvhs_ip pblock ./script/uvw_axi4_to_ddr4_pblock.tcl +set_ip_if_exists uvw_axi4_to_ddr4 ./rtl/soc/uvw_axi4_to_ddr4.dcp ./rtl/soc/uvw_axi4_to_ddr4_Stub.v -clock_enable_pairs {ddr4ip_dut_axi_aclk ddr4ip_dut_axi_aclk_en 1} -script_file {prePlace ./script/uvw_axi4_to_ddr4_pblock.tcl} + +set filelist [env_or_default UVHS_FILELIST ./rtl/filelist.f] +puts "INFO: read_verilog -f $filelist" +read_verilog -f $filelist -mfcu + +puts "INFO: elaborate_design $design_top" +elaborate_design $design_top +start_uvsyn_shell_patch_watcher +synthesize_design -parallel_option frontend +save_working_space +patch_option_add_rtl_inst hw.dat $ddr_inst_path +exit diff --git a/fpga_diff/uvhs/hejian_pcie_x4_env.sh b/fpga_diff/uvhs/hejian_pcie_x4_env.sh new file mode 100755 index 00000000..99845fa5 --- /dev/null +++ b/fpga_diff/uvhs/hejian_pcie_x4_env.sh @@ -0,0 +1,310 @@ +#!/usr/bin/env bash +# Print, check, or source the Hejian UVHS PCIe fpga_diff x4 preset. +# +# The defaults mirror the validated Hejian XDMA EP x4 configuration: +# F2/HGC7 x4 lanes, endpoint device ID 10ee:9048, BAR0 512 KiB, BAR1 64 KiB. +# They are fpga_diff/UVHS flow-level defaults. This preset intentionally uses +# the checked official XDMA EP x4 DCP instead of patching the Vivado IP Tcl +# sources. +# Existing environment values are kept so accidental overrides remain visible +# to the preflight checks instead of being silently replaced. + +_uvhs_hejian_pcie_script_dir="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P)" +_uvhs_hejian_pcie_root_dir="$(cd -- "$_uvhs_hejian_pcie_script_dir/.." && pwd -P)" + +export XDMA_LINK_WIDTH="${XDMA_LINK_WIDTH:-X4}" +export XDMA_ENABLE_PF0_BAR1="${XDMA_ENABLE_PF0_BAR1:-1}" +export XDMA_AXILITE_MASTER_SCALE="${XDMA_AXILITE_MASTER_SCALE:-Kilobytes}" +export XDMA_AXILITE_MASTER_SIZE="${XDMA_AXILITE_MASTER_SIZE:-512}" +export XDMA_EXPECTED_VENDOR="${XDMA_EXPECTED_VENDOR:-0x10ee}" +export XDMA_EXPECTED_DEVICE="${XDMA_EXPECTED_DEVICE:-0x9048}" +export XDMA_EXPECTED_BAR0_SIZE="${XDMA_EXPECTED_BAR0_SIZE:-0x80000}" +export XDMA_EXPECTED_BAR1_SIZE="${XDMA_EXPECTED_BAR1_SIZE:-0x10000}" +export UVHS_XDMA_EP_DCP="${UVHS_XDMA_EP_DCP:-}" +export UVHS_XDMA_EP_DCP_SHA256="${UVHS_XDMA_EP_DCP_SHA256:-e40002b4ccab73d4d7a1398630f59428add0a64b9f33d284d3789595bb791b32}" +export UVHS_XDMA_EP_EXPECTED_VIVADO="${UVHS_XDMA_EP_EXPECTED_VIVADO:-2024.2}" +export UVHS_BASE_ASSEMBLE_FILE="${UVHS_BASE_ASSEMBLE_FILE:-}" +export UVHS_BASE_ASSEMBLE_SHA256="${UVHS_BASE_ASSEMBLE_SHA256:-0e44f3ced8e5628a6f9b60a36d15194f03f6eddefcf29d0221a8abf140ecb18b}" +export UVHS_ALLOW_DCP_VIVADO_MISMATCH="${UVHS_ALLOW_DCP_VIVADO_MISMATCH:-0}" + +export UVHS_DESIGN_NAME="${UVHS_DESIGN_NAME:-VU19P_X4}" +export PLATFORM="${PLATFORM:-U2.2}" +export UVHS_TARGET_BOARD="${UVHS_TARGET_BOARD:-B0}" +export UVHS_TARGET_PACK="${UVHS_TARGET_PACK:-$UVHS_TARGET_BOARD}" +export UVHS_TARGET_FPGA="${UVHS_TARGET_FPGA:-F2}" +export UVHS_TARGET_FPGA_LOWER="${UVHS_TARGET_FPGA_LOWER:-$(printf '%s.%s' "$UVHS_TARGET_PACK" "$UVHS_TARGET_FPGA" | tr '[:upper:]' '[:lower:]')}" +export UVHS_FPGA_COUNT="${UVHS_FPGA_COUNT:-1}" +export UVHS_CPU_CLK_PERIOD_NS="${UVHS_CPU_CLK_PERIOD_NS:-40}" +export UVHS_CPU_DEBUG_CLK="${UVHS_CPU_DEBUG_CLK:-1}" +export UVHS_PNR_STRATEGY="${UVHS_PNR_STRATEGY:-Strategy_uv_high_fanout_explore}" +export UVHS_COMPILE_STRATEGY_NUM="${UVHS_COMPILE_STRATEGY_NUM:-1}" +export UVHS_COMPILE_STRATEGY0="${UVHS_COMPILE_STRATEGY0:-uv_high_fanout_explore}" +export UVHS_FRONTEND_THREADS="${UVHS_FRONTEND_THREADS:-4}" +export UVHS_FRONTEND_PROCESSES="${UVHS_FRONTEND_PROCESSES:-16}" +export UVHS_FPGA_THREADS="${UVHS_FPGA_THREADS:-4}" +export UVHS_FPGA_PROCESSES="${UVHS_FPGA_PROCESSES:-8}" + +export UVHS_ASSEMBLE_FILE="${UVHS_ASSEMBLE_FILE:-$_uvhs_hejian_pcie_root_dir/uvhs/assemble_uvhs.tcl}" +export UVHS_ASSIGN_PIN_FILE="${UVHS_ASSIGN_PIN_FILE:-$_uvhs_hejian_pcie_root_dir/uvhs/assign_pin_nutshell_f2.tcl}" + +export XDMA_BDF="${XDMA_BDF:-0000:01:00.0}" + +uvhs_hejian_pcie_print_env() { + for name in \ + XDMA_LINK_WIDTH \ + XDMA_ENABLE_PF0_BAR1 \ + XDMA_AXILITE_MASTER_SCALE \ + XDMA_AXILITE_MASTER_SIZE \ + XDMA_EXPECTED_VENDOR \ + XDMA_EXPECTED_DEVICE \ + XDMA_EXPECTED_BAR0_SIZE \ + XDMA_EXPECTED_BAR1_SIZE \ + UVHS_XDMA_EP_DCP \ + UVHS_XDMA_EP_DCP_SHA256 \ + UVHS_XDMA_EP_EXPECTED_VIVADO \ + UVHS_BASE_ASSEMBLE_FILE \ + UVHS_BASE_ASSEMBLE_SHA256 \ + UVHS_ALLOW_DCP_VIVADO_MISMATCH \ + UVHS_DESIGN_NAME \ + PLATFORM \ + UVHS_TARGET_PACK \ + UVHS_TARGET_FPGA \ + UVHS_TARGET_FPGA_LOWER \ + UVHS_FPGA_COUNT \ + UVHS_CPU_CLK_PERIOD_NS \ + UVHS_CPU_DEBUG_CLK \ + UVHS_PNR_STRATEGY \ + UVHS_COMPILE_STRATEGY_NUM \ + UVHS_COMPILE_STRATEGY0 \ + UVHS_FRONTEND_THREADS \ + UVHS_FRONTEND_PROCESSES \ + UVHS_FPGA_THREADS \ + UVHS_FPGA_PROCESSES \ + UVHS_ASSEMBLE_FILE \ + UVHS_ASSIGN_PIN_FILE \ + XDMA_BDF; do + printf '%s=%q\n' "$name" "${!name}" + done +} + +uvhs_hejian_pcie_check_env() { + local failures=0 + + check_equal() { + local name=$1 + local expected=$2 + local actual=${!name} + if [[ "$actual" == "$expected" ]]; then + printf 'OK: %s=%s\n' "$name" "$actual" + else + printf 'FAIL: %s=%s, expected %s\n' "$name" "$actual" "$expected" >&2 + failures=$((failures + 1)) + fi + } + + check_file() { + local label=$1 + local path=$2 + if [[ -f "$path" ]]; then + printf 'OK: %s: %s\n' "$label" "$path" + else + printf 'FAIL: missing %s: %s\n' "$label" "$path" >&2 + failures=$((failures + 1)) + fi + } + + check_grep() { + local label=$1 + local pattern=$2 + local path=$3 + if [[ -f "$path" ]] && grep -Eq "$pattern" "$path"; then + printf 'OK: %s\n' "$label" + else + printf 'FAIL: %s\n' "$label" >&2 + failures=$((failures + 1)) + fi + } + + dcp_vivado_version() { + local dcp=$1 + command -v unzip >/dev/null 2>&1 || return 0 + unzip -p "$dcp" dcp.xml 2>/dev/null \ + | sed -n 's/.*/dev/null 2>&1; then + vivado_bin=$(command -v vivado) + fi + [[ -n "$vivado_bin" && -x "$vivado_bin" ]] || return 0 + "$vivado_bin" -version 2>/dev/null \ + | sed -n 's/^Vivado v\([^ ]*\).*/\1/p' \ + | head -n 1 + } + + check_equal XDMA_LINK_WIDTH X4 + check_equal XDMA_ENABLE_PF0_BAR1 1 + check_equal XDMA_AXILITE_MASTER_SCALE Kilobytes + check_equal XDMA_AXILITE_MASTER_SIZE 512 + check_equal XDMA_EXPECTED_VENDOR 0x10ee + check_equal XDMA_EXPECTED_DEVICE 0x9048 + check_equal XDMA_EXPECTED_BAR0_SIZE 0x80000 + check_equal XDMA_EXPECTED_BAR1_SIZE 0x10000 + if [[ -n "$UVHS_XDMA_EP_DCP" ]]; then + check_file "verified Hejian XDMA EP DCP" "$UVHS_XDMA_EP_DCP" + if [[ -s "$UVHS_XDMA_EP_DCP" && -n "$UVHS_XDMA_EP_DCP_SHA256" ]]; then + actual_sha256=$(sha256sum "$UVHS_XDMA_EP_DCP" | awk '{print $1}') + if [[ "$actual_sha256" == "$UVHS_XDMA_EP_DCP_SHA256" ]]; then + printf 'OK: UVHS_XDMA_EP_DCP sha256=%s\n' "$actual_sha256" + else + printf 'FAIL: UVHS_XDMA_EP_DCP sha256=%s, expected %s\n' "$actual_sha256" "$UVHS_XDMA_EP_DCP_SHA256" >&2 + failures=$((failures + 1)) + fi + fi + dcp_vivado=$(dcp_vivado_version "$UVHS_XDMA_EP_DCP") + if [[ -n "$dcp_vivado" ]]; then + printf 'OK: UVHS_XDMA_EP_DCP Vivado version=%s\n' "$dcp_vivado" + if [[ "$dcp_vivado" != "$UVHS_XDMA_EP_EXPECTED_VIVADO" ]]; then + printf 'FAIL: XDMA DCP Vivado version=%s, expected %s\n' "$dcp_vivado" "$UVHS_XDMA_EP_EXPECTED_VIVADO" >&2 + failures=$((failures + 1)) + fi + uvhs_vivado=$(active_vivado_version) + if [[ -n "$uvhs_vivado" ]]; then + if [[ "$uvhs_vivado" == "$dcp_vivado" ]]; then + printf 'OK: active Vivado version matches XDMA DCP: %s\n' "$uvhs_vivado" + elif [[ "$UVHS_ALLOW_DCP_VIVADO_MISMATCH" == "1" ]]; then + printf 'WARN: active Vivado version=%s, XDMA DCP version=%s; mismatch allowed by UVHS_ALLOW_DCP_VIVADO_MISMATCH=1\n' "$uvhs_vivado" "$dcp_vivado" + else + printf 'FAIL: active Vivado version=%s cannot consume XDMA DCP version=%s; regenerate xdma_ep.dcp with Vivado %s or run UVHS with Vivado %s\n' "$uvhs_vivado" "$dcp_vivado" "$uvhs_vivado" "$dcp_vivado" >&2 + failures=$((failures + 1)) + fi + else + printf 'INFO: active Vivado version unavailable; skip XDMA DCP/Vivado compatibility check\n' + fi + else + printf 'INFO: unable to read Vivado version from UVHS_XDMA_EP_DCP; skip compatibility check\n' + fi + else + printf 'FAIL: UVHS_XDMA_EP_DCP is unset; point to the verified XDMA x4 xdma_ep.dcp\n' >&2 + failures=$((failures + 1)) + fi + if [[ -n "$UVHS_BASE_ASSEMBLE_FILE" ]]; then + check_file "verified Hejian x4 base assemble" "$UVHS_BASE_ASSEMBLE_FILE" + if [[ -s "$UVHS_BASE_ASSEMBLE_FILE" && -n "$UVHS_BASE_ASSEMBLE_SHA256" ]]; then + actual_sha256=$(sha256sum "$UVHS_BASE_ASSEMBLE_FILE" | awk '{print $1}') + if [[ "$actual_sha256" == "$UVHS_BASE_ASSEMBLE_SHA256" ]]; then + printf 'OK: UVHS_BASE_ASSEMBLE_FILE sha256=%s\n' "$actual_sha256" + else + printf 'FAIL: UVHS_BASE_ASSEMBLE_FILE sha256=%s, expected %s\n' "$actual_sha256" "$UVHS_BASE_ASSEMBLE_SHA256" >&2 + failures=$((failures + 1)) + fi + fi + else + printf 'FAIL: UVHS_BASE_ASSEMBLE_FILE is unset; point to the verified x4 1B_4F_HGC_assemble.tcl\n' >&2 + failures=$((failures + 1)) + fi + check_equal UVHS_DESIGN_NAME VU19P_X4 + check_equal PLATFORM U2.2 + check_equal UVHS_TARGET_PACK B0 + check_equal UVHS_TARGET_FPGA F2 + check_equal UVHS_TARGET_FPGA_LOWER b0.f2 + check_equal UVHS_FPGA_COUNT 1 + check_equal UVHS_CPU_CLK_PERIOD_NS 40 + check_equal UVHS_CPU_DEBUG_CLK 1 + check_equal UVHS_PNR_STRATEGY Strategy_uv_high_fanout_explore + check_equal UVHS_COMPILE_STRATEGY_NUM 1 + check_equal UVHS_COMPILE_STRATEGY0 uv_high_fanout_explore + check_equal UVHS_FRONTEND_THREADS 4 + check_equal UVHS_FRONTEND_PROCESSES 16 + check_equal UVHS_FPGA_THREADS 4 + check_equal UVHS_FPGA_PROCESSES 8 + check_equal XDMA_BDF 0000:01:00.0 + + check_file "UVHS assembly overlay" "$UVHS_ASSEMBLE_FILE" + check_file "NutShell F2 pin file" "$UVHS_ASSIGN_PIN_FILE" + check_file "public Vivado xdma_ep Tcl source" "$_uvhs_hejian_pcie_root_dir/src/tcl/common/xdma_ep.tcl" + if command -v git >/dev/null 2>&1 \ + && git -C "$_uvhs_hejian_pcie_root_dir" rev-parse --is-inside-work-tree >/dev/null 2>&1; then + if git -C "$_uvhs_hejian_pcie_root_dir" diff --quiet -- src/tcl/common/xdma_ep.tcl \ + && git -C "$_uvhs_hejian_pcie_root_dir" diff --cached --quiet -- src/tcl/common/xdma_ep.tcl; then + printf 'OK: public Vivado xdma_ep.tcl has no local diff\n' + else + printf 'FAIL: public Vivado xdma_ep.tcl has local diff; keep Hejian x4 alignment in fpga_diff/uvhs scripts and UVHS_XDMA_EP_DCP\n' >&2 + failures=$((failures + 1)) + fi + else + printf 'INFO: skip git diff check for public Vivado xdma_ep.tcl\n' + fi + check_grep "pin file gates lanes by XDMA_LINK_WIDTH" 'XDMA_LINK_WIDTH' "$UVHS_ASSIGN_PIN_FILE" + check_grep "pin file uses official HGC7 x4 lane group" 'b0[.]F2_HGC7' "$UVHS_ASSIGN_PIN_FILE" + check_grep "fpga_diff UVHS Makefile defaults BAR1 on" 'XDMA_ENABLE_PF0_BAR1[[:space:]]+[?]?=[[:space:]]+1' "$_uvhs_hejian_pcie_root_dir/uvhs/Makefile" + check_grep "fpga_diff UVHS Makefile defaults BAR0 512 KiB" 'XDMA_AXILITE_MASTER_SIZE[[:space:]]+[?]?=[[:space:]]+512' "$_uvhs_hejian_pcie_root_dir/uvhs/Makefile" + check_grep "fpga_diff UVHS Makefile has explicit Hejian x4 UVHS environment" 'UVHS_HEJIAN_FLOW_ENV.*XDMA_LINK_WIDTH=.*UVHS_DESIGN_NAME=.*PLATFORM=' "$_uvhs_hejian_pcie_root_dir/uvhs/Makefile" + check_grep "fpga_diff UVHS frontend passes Hejian x4 environment to uv_shell" 'UVHS_HEJIAN_FLOW_ENV.*UVHS_FILELIST=.*uv_shell.*UVHS_FRONTEND_TCL' "$_uvhs_hejian_pcie_root_dir/uvhs/Makefile" + check_grep "fpga_diff UVHS backend passes Hejian x4 environment to uv_shell" 'UVHS_HEJIAN_FLOW_ENV.*UVHS_TOP=.*uv_shell.*backend_run[.]tcl' "$_uvhs_hejian_pcie_root_dir/uvhs/Makefile" + check_grep "Hejian x4 target pins official U2.2 platform" '^uvhs_hejian_pcie_x4_nutshell_all:[[:space:]]+PLATFORM[[:space:]]*=[[:space:]]*U2[.]2' "$_uvhs_hejian_pcie_root_dir/uvhs/Makefile" + check_grep "fpga_diff UVHS Makefile skips xdma_ep export when DCP is selected" 'UVHS_EFFECTIVE_SKIP_VIVADO_EXPORT.*UVHS_XDMA_EP_DCP' "$_uvhs_hejian_pcie_root_dir/uvhs/Makefile" + check_grep "fpga_diff UVHS Makefile passes base assemble into UVHS scripts" 'UVHS_HEJIAN_FLOW_ENV.*UVHS_BASE_ASSEMBLE_FILE=' "$_uvhs_hejian_pcie_root_dir/uvhs/Makefile" + check_grep "fpga_diff UVHS Makefile defaults official PnR strategy" 'UVHS_PNR_STRATEGY[[:space:]]+[?]?=[[:space:]]+Strategy_uv_high_fanout_explore' "$_uvhs_hejian_pcie_root_dir/uvhs/Makefile" + check_grep "fpga_diff UVHS Makefile has Hejian x4 NutShell build target" '^uvhs_hejian_pcie_x4_nutshell_all:' "$_uvhs_hejian_pcie_root_dir/uvhs/Makefile" + check_grep "fpga_diff UVHS Makefile has Hejian x4 XiangShan build target" '^uvhs_hejian_pcie_x4_xiangshan_all:' "$_uvhs_hejian_pcie_root_dir/uvhs/Makefile" + check_grep "fpga_diff UVHS x4 target uses 25 MHz NutShell CPU clock" '^uvhs_hejian_pcie_x4_nutshell_all:[[:space:]]+UVHS_CPU_CLK_PERIOD_NS[[:space:]]*=[[:space:]]*40' "$_uvhs_hejian_pcie_root_dir/uvhs/Makefile" + check_grep "fpga_diff UVHS x4 target selects the 25 MHz clock input" '^uvhs_hejian_pcie_x4_nutshell_all:[[:space:]]+UVHS_CPU_DEBUG_CLK[[:space:]]*=[[:space:]]*1' "$_uvhs_hejian_pcie_root_dir/uvhs/Makefile" + check_grep "fpga_diff UVHS XiangShan target selects kmh" '^uvhs_hejian_pcie_x4_xiangshan_all:[[:space:]]+CPU[[:space:]]*=[[:space:]]*kmh' "$_uvhs_hejian_pcie_root_dir/uvhs/Makefile" + check_grep "fpga_diff UVHS XiangShan target uses 25 MHz CPU clock" '^uvhs_hejian_pcie_x4_xiangshan_all:[[:space:]]+UVHS_CPU_CLK_PERIOD_NS[[:space:]]*=[[:space:]]*40' "$_uvhs_hejian_pcie_root_dir/uvhs/Makefile" + check_grep "fpga_diff backend defaults official compile strategy" 'uv_high_fanout_explore' "$_uvhs_hejian_pcie_root_dir/uvhs/backend_run.tcl" + + if [[ "$failures" -ne 0 ]]; then + printf 'FAIL: %d Hejian PCIe x4 preset check(s) failed\n' "$failures" >&2 + return 1 + fi + printf 'OK: Hejian PCIe x4 preset checks passed\n' +} + +_uvhs_hejian_pcie_executed=0 +_uvhs_hejian_pcie_status=0 +if [[ "${BASH_SOURCE[0]}" == "$0" ]]; then + _uvhs_hejian_pcie_executed=1 + case "${1:-print}" in + print|env) + uvhs_hejian_pcie_print_env + ;; + check|preflight) + uvhs_hejian_pcie_check_env || _uvhs_hejian_pcie_status=$? + ;; + -h|--help|help) + cat <<'USAGE' +Usage: + uvhs/hejian_pcie_x4_env.sh [print|check] + +Preferred fpga_diff build entry: + make -C env-scripts/fpga_diff uvhs_hejian_pcie_x4_nutshell_all CORE_DIR=/path/to/NutShell + make -C env-scripts/fpga_diff uvhs_hejian_pcie_x4_xiangshan_all CORE_DIR=/path/to/XiangShan-release/build + +Source it only when interactive shell overrides are useful: + source env-scripts/fpga_diff/uvhs/hejian_pcie_x4_env.sh + +Execute it to print or validate the current effective environment: + env-scripts/fpga_diff/uvhs/hejian_pcie_x4_env.sh print + env-scripts/fpga_diff/uvhs/hejian_pcie_x4_env.sh check +USAGE + ;; + *) + printf 'ERROR: unknown mode: %s\n' "$1" >&2 + _uvhs_hejian_pcie_status=2 + ;; + esac +fi + +unset -f uvhs_hejian_pcie_print_env uvhs_hejian_pcie_check_env dcp_vivado_version active_vivado_version 2>/dev/null || true +unset _uvhs_hejian_pcie_script_dir _uvhs_hejian_pcie_root_dir _uvhs_hejian_pcie_project_root +if [[ "$_uvhs_hejian_pcie_executed" == "1" ]]; then + exit "$_uvhs_hejian_pcie_status" +fi +unset _uvhs_hejian_pcie_executed _uvhs_hejian_pcie_status diff --git a/fpga_diff/uvhs/make_compat/csh b/fpga_diff/uvhs/make_compat/csh new file mode 100755 index 00000000..109fcd86 --- /dev/null +++ b/fpga_diff/uvhs/make_compat/csh @@ -0,0 +1,14 @@ +#!/usr/bin/env bash +set -euo pipefail + +if [[ $# -ge 2 && ( "$1" == "-fc" || "$1" == "-c" ) ]]; then + command_string=$2 + shift 2 + if [[ "$command_string" == "limit" ]]; then + ulimit -a + exit 0 + fi + exec /bin/bash -lc "$command_string" +fi + +exec /bin/bash "$@" diff --git a/fpga_diff/uvhs/make_compat/make b/fpga_diff/uvhs/make_compat/make new file mode 100755 index 00000000..2e930fbd --- /dev/null +++ b/fpga_diff/uvhs/make_compat/make @@ -0,0 +1,2 @@ +#!/usr/bin/env bash +exec /usr/bin/make SHELL=/bin/bash "$@" diff --git a/fpga_diff/uvhs/make_compat/sh b/fpga_diff/uvhs/make_compat/sh new file mode 100755 index 00000000..0382388b --- /dev/null +++ b/fpga_diff/uvhs/make_compat/sh @@ -0,0 +1,2 @@ +#!/usr/bin/env bash +exec /bin/bash "$@" diff --git a/fpga_diff/uvhs/partition.tcl b/fpga_diff/uvhs/partition.tcl new file mode 100644 index 00000000..7cfeff1b --- /dev/null +++ b/fpga_diff/uvhs/partition.tcl @@ -0,0 +1,3 @@ +# Keep the platform default fill rates. On UVHS U2.2/VU19P_X4, calling +# set_fill_rate with only LUT/BRAM resets Probe-Group capacity to 0, which +# rejects any trigger/probe insertion during partition. diff --git a/fpga_diff/uvhs/patch_uvsyn_shell.sh b/fpga_diff/uvhs/patch_uvsyn_shell.sh new file mode 100755 index 00000000..b91a7960 --- /dev/null +++ b/fpga_diff/uvhs/patch_uvsyn_shell.sh @@ -0,0 +1,29 @@ +#!/usr/bin/env bash +set -euo pipefail + +module_makefile=${1:?usage: $0 /path/to/module.makefile} + +for _ in $(seq 1 12000); do + if [[ -f "$module_makefile" ]]; then + if grep -Eq 'bash[[:space:]]+([^[:space:]]*/)?uv_shell[[:space:]]' "$module_makefile"; then + printf 'INFO: UVHS module worker already invokes uv_shell through bash: %s\n' "$module_makefile" + exit 0 + fi + sed -E -i \ + -e 's/until[[:space:]]+sh[[:space:]]+-c/until bash -c/' \ + -e 's#(^|[[:space:]])(/[^[:space:]]*/bin/uv_shell)([[:space:]])#\1bash \2\3#g' \ + -e 's#(^|[[:space:]])(uv_shell)([[:space:]])#\1bash \2\3#g' \ + "$module_makefile" + if grep -Eq 'bash[[:space:]]+([^[:space:]]*/)?uv_shell[[:space:]]' "$module_makefile"; then + printf 'INFO: patched UVHS module worker uv_shell invocation: %s\n' "$module_makefile" + : >"${module_makefile}.uvhs_bash_patch.ok" + exit 0 + fi + echo "ERROR: module.makefile appeared but uv_shell invocation was not patched: $module_makefile" >&2 + exit 1 + fi + sleep 0.05 +done + +echo "ERROR: timed out waiting for UVHS module makefile: $module_makefile" >&2 +exit 1 diff --git a/fpga_diff/uvhs/setenv.local.example.sh b/fpga_diff/uvhs/setenv.local.example.sh new file mode 100644 index 00000000..00e6c4a4 --- /dev/null +++ b/fpga_diff/uvhs/setenv.local.example.sh @@ -0,0 +1,28 @@ +#!/usr/bin/env bash + +# Copy this file to uvhs/setenv.local.sh and fill in local values. +# Do not commit uvhs/setenv.local.sh. + +export UV_ROOT= +export UV_XILINX_VIVADO= +export XILINX_HLS= +export UV_LICENSE= +export UVHS_UV_SHELL="$UV_ROOT/bin/uv_shell" + +# Optional overrides: +# export UVS_HOME= +# export UVD_HOME= +# export UVEC_HOME= +# export HW_SERVER_URL= +# export HW_TARGET= +# export UVHS_UVW_AXI4_TO_DDR4_SRC=/path/to/verified/uvw_axi4_to_ddr4/source +# export UVHS_UVW_AXI4_TO_DDR4_EXPECTED_MD5=bbd428f1fa2ede1628e11b7ab14d1072 +# export UVHS_XDMA_EP_DCP=/path/to/verified/xdma_ep.dcp +# export UVHS_BASE_ASSEMBLE_FILE=/path/to/verified/1B_4F_HGC_assemble.tcl + +# Optional Hejian PCIe x4 preset. +# The preferred fpga_diff build entry is: +# make -C env-scripts/fpga_diff uvhs_hejian_pcie_x4_nutshell_all CPU=nutshell +# Source the script only for interactive environment inspection/overrides. +# It only sets fpga_diff/UVHS flow environment; it does not patch Vivado Tcl. +# source /path/to/env-scripts/fpga_diff/uvhs/hejian_pcie_x4_env.sh diff --git a/fpga_diff/uvhs/setenv.sh b/fpga_diff/uvhs/setenv.sh new file mode 100644 index 00000000..0454d389 --- /dev/null +++ b/fpga_diff/uvhs/setenv.sh @@ -0,0 +1,111 @@ +#!/usr/bin/env bash + +# Generic UVHS environment entry point. +# +# Put machine-specific paths, licenses, and host settings in +# uvhs/setenv.local.sh +# or point UVHS_LOCAL_ENV at another local file before sourcing this script. + +if [ -n "${BASH_SOURCE:-}" ]; then + _uvhs_source="${BASH_SOURCE[0]}" +elif [ -n "${ZSH_VERSION:-}" ]; then + _uvhs_source="${(%):-%x}" +else + _uvhs_source="$0" +fi + +_uvhs_script_dir="$(cd "$(dirname "$_uvhs_source")" && pwd)" +_uvhs_repo_dir="$(cd "$_uvhs_script_dir/.." && pwd)" +_uvhs_local_env="${UVHS_LOCAL_ENV:-$_uvhs_script_dir/setenv.local.sh}" + +if [ -f "$_uvhs_local_env" ]; then + # shellcheck source=/dev/null + source "$_uvhs_local_env" +fi + +if [ -z "${UVHS_UVW_AXI4_TO_DDR4_SRC:-}" ]; then + for _uvhs_ddr_ip_src in \ + "$_uvhs_repo_dir/uvhs_nutshell-uvhs-ddr-uvw-ip-bind" \ + "$_uvhs_repo_dir/uvhs_nutshell-uvhs-ddr-uvw-ip"; do + if [ -s "$_uvhs_ddr_ip_src/rtl/soc/uvw_axi4_to_ddr4.dcp" ]; then + export UVHS_UVW_AXI4_TO_DDR4_SRC="$_uvhs_ddr_ip_src" + break + fi + done +fi + +export UVHS_UVW_AXI4_TO_DDR4_EXPECTED_MD5="${UVHS_UVW_AXI4_TO_DDR4_EXPECTED_MD5:-bbd428f1fa2ede1628e11b7ab14d1072}" + +export PS1="${PS1:-\[\033[01;33m\]\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\$ }" + +_uvhs_prepend_path() { + [ -n "$1" ] || return 0 + case ":$PATH:" in + *":$1:"*) ;; + *) export PATH="$1:$PATH" ;; + esac +} + +_uvhs_prepend_ld_path() { + [ -n "$1" ] || return 0 + case ":${LD_LIBRARY_PATH:-}:" in + *":$1:"*) ;; + *) export LD_LIBRARY_PATH="$1:${LD_LIBRARY_PATH:-}" ;; + esac +} + +if [ -n "${UV_ROOT:-}" ]; then + _uvhs_prepend_path "$UV_ROOT/bin" + _uvhs_prepend_path "$UV_ROOT/lib/venv3.8/bin" + _uvhs_prepend_path "$UV_ROOT/lib/gcc10.3/bin" + + export UVS_HOME="${UVS_HOME:-$UV_ROOT/uvd/uvs}" + export UVD_HOME="${UVD_HOME:-$UV_ROOT/uvd}" + _uvhs_prepend_path "$UVS_HOME/bin" + _uvhs_prepend_path "$UVD_HOME/bin" + + export UVEC_FLOW="${UVEC_FLOW:-1}" + export UVEC_HOME="${UVEC_HOME:-$UV_ROOT/uvec}" + export UV_ECIR_HOME="${UV_ECIR_HOME:-$UVEC_HOME}" + _uvhs_prepend_path "$UVEC_HOME/bin" +fi + +if [ -n "${UV_XILINX_VIVADO:-}" ]; then + export VIVADO_HOME="$UV_XILINX_VIVADO" + export XILINX_VIVADO="$UV_XILINX_VIVADO" +fi + +if [ -n "${VIVADO_HOME:-}" ]; then + _uvhs_prepend_path "$VIVADO_HOME/bin" +fi + +if [ -n "${XILINX_HLS:-}" ]; then + _uvhs_prepend_path "$XILINX_HLS/bin" +fi + +_uvhs_prepend_path "${FIRTOOL_TOOLS:-}" +_uvhs_prepend_path "${RISCV_TOOLS:-}" +_uvhs_prepend_path "${MC_OBJTOOLS:-}" + +if [ -d "$_uvhs_script_dir/make_compat" ]; then + _uvhs_prepend_path "$_uvhs_script_dir/make_compat" +fi + +if [ -d "$_uvhs_script_dir/libffi_compat" ]; then + _uvhs_prepend_ld_path "$_uvhs_script_dir/libffi_compat" +fi + +_uvhs_prepend_ld_path "${UVHS_LIBFFI_COMPAT_DIR:-}" + +alias uvec='${UVEC_HOME:?UVEC_HOME is not set}/bin/uvec -emu' +alias gdiff='vimdiff' +alias g='vim' +alias ll='ls -alhF --color=auto' +alias ls='ls -aF' + +cd () +{ + builtin cd "$@" && ls -alhF --color=auto +} + +unset _uvhs_source _uvhs_script_dir _uvhs_repo_dir _uvhs_local_env _uvhs_ddr_ip_src diff --git a/fpga_diff/uvhs/status.sh b/fpga_diff/uvhs/status.sh new file mode 100755 index 00000000..f87bb243 --- /dev/null +++ b/fpga_diff/uvhs/status.sh @@ -0,0 +1,53 @@ +#!/usr/bin/env bash +set -euo pipefail + +work_dir=${1:-} +if [[ -z "$work_dir" ]]; then + echo "Usage: $0 " >&2 + exit 2 +fi + +if [[ ! -d "$work_dir" ]]; then + echo "UVHS work dir not found: $work_dir" >&2 + exit 1 +fi + +echo "UVHS work dir: $work_dir" + +echo +echo "DCPs:" +if ! find "$work_dir/rtl" -maxdepth 3 -type f -name '*.dcp' -printf ' %P %k KB\n' 2>/dev/null | sort; then + echo " none" +fi + +summarize_log() { + local name=$1 + local log=$2 + + echo + echo "$name:" + if [[ ! -f "$log" ]]; then + echo " missing: $log" + return + fi + + grep -E 'Design synthesized successfully|Uvsyn Synthesis Modules Result Summary|PnR PASS fpga number|FPGA: .*PASS|Total FATAL|Total ERROR|Total WARN|WNS:' "$log" \ + | sed 's/^/ /' || true + + local fatal_count error_count + fatal_count=$(grep -Ei 'fatal' "$log" | grep -Evc 'Total FATAL:[[:space:]]+0' || true) + error_count=$(grep -E 'ERROR|Error' "$log" | grep -Evc 'Total ERROR:[[:space:]]+0|0 error\(s\)|No errors|maxerror|Total 0 error|Failed\(0\)|[[:space:]]0[[:space:]]+\[(ERROR|CRITICAL)\]' || true) + echo " non-summary fatal lines: $fatal_count" + echo " non-summary error lines: $error_count" +} + +summarize_log "Frontend" "$work_dir/frontend_run.log" +summarize_log "Backend" "$work_dir/backend_run.log" + +echo +echo "Recent serious lines:" +grep -REn 'FATAL|Fatal|ERROR|Error|failed|Failed|Timing +FAIL|Bitstream +FAIL' \ + "$work_dir/frontend_run.log" "$work_dir/backend_run.log" 2>/dev/null \ + | grep -Ev 'Total FATAL:[[:space:]]+0|Total ERROR:[[:space:]]+0|0 error\(s\)|No errors|maxerror|Total 0 error|Failed\(0\)|dmesg: read kernel buffer failed|Timing +Failed FPGAs:[[:space:]]*$|Bitstream Failed FPGAs:[[:space:]]*$|Timing +FAIL fpga number:[[:space:]]*0|Bitstream FAIL fpga number:[[:space:]]*0|[[:space:]]0[[:space:]]+\[(ERROR|CRITICAL)\]' \ + | tail -n 40 \ + | sed 's/^/ /' || true diff --git a/fpga_diff/uvhs/timing_common.tcl b/fpga_diff/uvhs/timing_common.tcl new file mode 100644 index 00000000..b046af6b --- /dev/null +++ b/fpga_diff/uvhs/timing_common.tcl @@ -0,0 +1,25 @@ +################################################################################ +# Shared UVHS timing constraints for fpga_diff. +# Keep this direct so UVHS sees the RTL ports without extra guards. +################################################################################ + +create_clock -name TMCLK -period 1000 [get_ports -rtl clk8_p] +create_clock -name ddr_ref_clk -period 12.5 [get_ports -rtl clk7_p] +set fpga_diff_cpu_clk_period_ns 40 +if {[info exists ::env(UVHS_CPU_CLK_PERIOD_NS)] && $::env(UVHS_CPU_CLK_PERIOD_NS) ne ""} { + set fpga_diff_cpu_clk_period_ns $::env(UVHS_CPU_CLK_PERIOD_NS) +} +set fpga_diff_cpu_debug_clk 1 +if {[info exists ::env(UVHS_CPU_DEBUG_CLK)] && $::env(UVHS_CPU_DEBUG_CLK) ne ""} { + set fpga_diff_cpu_debug_clk [expr {$::env(UVHS_CPU_DEBUG_CLK) eq "1"}] +} +if {$fpga_diff_cpu_debug_clk} { + create_clock -name CPU_CLK_IN -period $fpga_diff_cpu_clk_period_ns [get_ports -rtl clk5_p] +} else { + create_clock -name CPU_CLK_IN -period $fpga_diff_cpu_clk_period_ns [get_ports -rtl clk6_p] + create_clock -name DEBUG_CLK_IN -period 40 [get_ports -rtl clk5_p] +} +create_clock -name jtag_vclk -period 83.333 [get_ports -rtl JTAG_TCK] +create_clock -name pcie_ep_refclk -period 10 [get_ports -rtl pcie_ep_gt_ref_clk_p] + +source [file normalize [file join [file dirname [info script]] async_clocks.tcl]] diff --git a/fpga_diff/uvhs/tools/README.md b/fpga_diff/uvhs/tools/README.md new file mode 100644 index 00000000..cb524b5f --- /dev/null +++ b/fpga_diff/uvhs/tools/README.md @@ -0,0 +1,9 @@ +# UVHS Flow Tools + +These helpers support the main UVHS flow. Build and tagged-runtime entry points +remain in the parent `uvhs/` directory. + +- `build/`: build-time generated-RTL fixes required by the UVHS integration. + +Public Makefile targets in `fpga_diff/Makefile` remain the stable build and +preflight entry points. Local board-debug helpers are intentionally not tracked. diff --git a/fpga_diff/uvhs/tools/build/patch_nutshell_cdc.sh b/fpga_diff/uvhs/tools/build/patch_nutshell_cdc.sh new file mode 100755 index 00000000..765f61b8 --- /dev/null +++ b/fpga_diff/uvhs/tools/build/patch_nutshell_cdc.sh @@ -0,0 +1,48 @@ +#!/usr/bin/env bash +set -euo pipefail + +core_dir=${1:-} +if [[ -z "$core_dir" || ! -d "$core_dir" ]]; then + exit 0 +fi + +rtl_dir="$core_dir/build/rtl" +if [[ ! -d "$rtl_dir" ]]; then + rtl_dir="$core_dir/rtl" +fi +if [[ ! -d "$rtl_dir" ]]; then + exit 0 +fi + +patched=0 +while IFS= read -r -d '' file; do + before=$(sha256sum "$file" | awk '{print $1}') + perl -0pi -e ' + s/(?:\(\*\s*ASYNC_REG="TRUE"\s*\*\)\s*)+reg\s+(\[[^\]]+\]\s+(?:wrPtrGraySync|rdPtrGraySync)(?:_REG)?\s*;)/(* ASYNC_REG="TRUE" *) reg $1/g; + s/(^|\n)([ \t]*)(reg\s+\[[^\]]+\]\s+(?:wrPtrGraySync|rdPtrGraySync)(?:_REG)?\s*;)/$1$2(* ASYNC_REG="TRUE" *) $3/g; + ' "$file" + after=$(sha256sum "$file" | awk '{print $1}') + if [[ "$before" != "$after" ]]; then + echo "INFO: patched AsyncClockFIFO CDC synchronizer attributes: $file" + patched=1 + fi +done < <(find "$rtl_dir" -maxdepth 1 -type f \( -name 'AsyncClockFIFO.sv' -o -name 'AsyncClockFIFO_*.sv' \) -print0) + +while IFS= read -r -d '' file; do + before=$(sha256sum "$file" | awk '{print $1}') + perl -0pi -e ' + s/(?:\(\*\s*ASYNC_REG="TRUE"\s*\*\)\s*)+reg(\s+)enableSync_REG\s*;/(* ASYNC_REG="TRUE" *) reg$1enableSync_REG;/g; + s/(?:\(\*\s*ASYNC_REG="TRUE"\s*\*\)\s*)+reg(\s+)enableSync\s*;/(* ASYNC_REG="TRUE" *) reg$1enableSync;/g; + s/(^|\n)([ \t]*)(reg(\s+)enableSync_REG\s*;)/$1$2(* ASYNC_REG="TRUE" *) $3/g; + s/(^|\n)([ \t]*)(reg(\s+)enableSync\s*;)/$1$2(* ASYNC_REG="TRUE" *) $3/g; + ' "$file" + after=$(sha256sum "$file" | awk '{print $1}') + if [[ "$before" != "$after" ]]; then + echo "INFO: patched H2CAXIs2Mem enable synchronizer attributes: $file" + patched=1 + fi +done < <(find "$rtl_dir" -maxdepth 1 -type f \( -name 'H2CAXIs2Mem.sv' -o -name 'H2CAXIs2Mem_*.sv' \) -print0) + +if [[ "$patched" == 0 ]]; then + echo "INFO: NutShell CDC synchronizer attributes already patched or no matching RTL found" +fi diff --git a/fpga_diff/uvhs/uv_shell_exec_compat.sh b/fpga_diff/uvhs/uv_shell_exec_compat.sh new file mode 100755 index 00000000..b2161484 --- /dev/null +++ b/fpga_diff/uvhs/uv_shell_exec_compat.sh @@ -0,0 +1,20 @@ +#!/usr/bin/env bash +set -euo pipefail + +: "${UV_ROOT:?UV_ROOT is required}" +: "${UVHS_PCRE_COMPAT_DIR:?UVHS_PCRE_COMPAT_DIR is required}" + +pcre="$UVHS_PCRE_COMPAT_DIR/libpcre.so.1" +test -s "$pcre" + +compat_path="$UVHS_PCRE_COMPAT_DIR" +if [ -n "${UVHS_LIBFFI_COMPAT_DIR:-}" ]; then + test -s "$UVHS_LIBFFI_COMPAT_DIR/libffi.so.6" + compat_path="$compat_path:$UVHS_LIBFFI_COMPAT_DIR" +fi + +# uv_common intentionally clears LD_LIBRARY_PATH before launching the selected +# executable. Restore only the caller-selected compatibility directory here, +# then execute the UVHS binary from the active UV_ROOT. +export LD_LIBRARY_PATH="$compat_path${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}" +exec "$UV_ROOT/bin/uv_shell_exec" "$@" diff --git a/fpga_diff/uvhs/uvhs_preflight_status.sh b/fpga_diff/uvhs/uvhs_preflight_status.sh new file mode 100755 index 00000000..b237fa9d --- /dev/null +++ b/fpga_diff/uvhs/uvhs_preflight_status.sh @@ -0,0 +1,82 @@ +#!/usr/bin/env bash +set -euo pipefail + +STAGE="${UVHS_STAGE_DIR:?set UVHS_STAGE_DIR}" +TAG="${UVHS_RUN_TAG:-$(basename "$STAGE")}" +RUNDIR="${UVHS_PNR_RUNDIR:-$STAGE/hw.dat/Compile/PnR/B0/F2/vivado/Rundir/Strategy_uv_high_fanout_explore}" +COMMAND_FILE="${UVHS_COMMAND_FILE:-$STAGE/commands/${TAG}.command.tcl}" +SESSION="${UVHS_TMUX_SESSION:-uvhs_${TAG}}" + +ts() { + date '+%Y-%m-%dT%H:%M:%S%z' +} + +show_path() { + local label="$1" + local path="$2" + if [ -e "$path" ]; then + stat -c "$label=%n size=%s mtime=%y" "$path" + else + echo "MISSING $label=$path" + fi +} + +echo "## uvhs_preflight_status $(ts)" +echo "tag=$TAG" +echo "stage=$STAGE" +echo "rundir=$RUNDIR" +echo "session=$SESSION" +echo "command_file=$COMMAND_FILE" + +echo "## required paths" +show_path stage "$STAGE" +show_path hw_dat "$STAGE/hw.dat" +show_path pnr_bit "$RUNDIR/bitstream/pnr.bit" +show_path pnr_bin "$RUNDIR/bitstream/pnr.bin" +show_path pnr_routed_dcp "$RUNDIR/bitstream/pnr_routed.dcp" +show_path cdc_regular "$RUNDIR/bitstream/timing/pnr_cdc.rpt" +show_path cdc_details "$RUNDIR/bitstream/timing/pnr_cdc_details_codex.rpt" +show_path timing_summary "$RUNDIR/bitstream/timing/pnr_timing_summary_codex.rpt" +show_path cdc_classification "$RUNDIR/bitstream/timing/cdc_classification_codex.txt" +show_path runtime_script "$STAGE/uvhs_tagged_runtime.sh" +show_path download_script "$STAGE/user_script/hw_run_download.tcl" +show_path workload_hello "$STAGE/ready-to-run/hello-xs.txt" + +echo "## helper hashes" +sha256sum \ + "$STAGE/uvhs_tagged_runtime.sh" \ + "$STAGE/user_script/hw_run_download.tcl" \ + 2>/dev/null || true + +echo "## cdc/timing gates" +if [ -f "$RUNDIR/bitstream/timing/cdc_classification_codex.txt" ]; then + awk ' + /severity_counts:/ || /owned_critical_count:/ || /difftest_cfg_rows:/ {print} + /critical_category_counts:/ {print; show=1; next} + show && /^[^[:space:]]/ {show=0} + show && /^[[:space:]]/ {print} + ' "$RUNDIR/bitstream/timing/cdc_classification_codex.txt" || true +fi +if [ -f "$RUNDIR/bitstream/timing/pnr_timing_summary_codex.rpt" ]; then + awk ' + /Design Timing Summary/ {flag=1} + flag && /WNS\(ns\)/ {print; header=1; next} + header && /^[[:space:]]*-+/ {next} + header && /[0-9]/ {print; exit} + ' "$RUNDIR/bitstream/timing/pnr_timing_summary_codex.rpt" || true + grep -m1 'All user specified timing constraints are met' "$RUNDIR/bitstream/timing/pnr_timing_summary_codex.rpt" || true +fi + +echo "## command-file residue" +ls -l "$COMMAND_FILE" "$COMMAND_FILE.running" 2>/dev/null || true + +echo "## tmux exact tag matches" +tmux ls 2>/dev/null | grep -F "$TAG" || true + +echo "## ps exact tag matches" +ps -eo pid,ppid,user,etime,stat,wchan:24,cmd \ + | awk -v tag="$TAG" -v self="$$" -v parent="$PPID" \ + '$1 != self && $1 != parent && index($0, tag) && $0 !~ /awk -v tag=/ {print}' || true + +echo "## preflight note" +echo "No download/programming is performed by this script." diff --git a/fpga_diff/uvhs/uvhs_tagged_runtime.sh b/fpga_diff/uvhs/uvhs_tagged_runtime.sh new file mode 100755 index 00000000..e48684da --- /dev/null +++ b/fpga_diff/uvhs/uvhs_tagged_runtime.sh @@ -0,0 +1,306 @@ +#!/usr/bin/env bash +set -euo pipefail + +TAG="${UVHS_RUN_TAG:?set a unique UVHS_RUN_TAG}" +STAGE="${UVHS_STAGE_DIR:?set UVHS_STAGE_DIR}" +UV_SHELL="${UVHS_UV_SHELL:-${UV_ROOT:+$UV_ROOT/bin/uv_shell}}" +SESSION="${UVHS_TMUX_SESSION:-uvhs_${TAG}}" +DB_PATH="${UVHS_DB_PATH:-$STAGE/hw.dat}" +DOWNLOAD_SCRIPT="${UVHS_DOWNLOAD_SCRIPT:-$STAGE/user_script/hw_run_download.tcl}" +DDR_RTL="${UVHS_DDR_RTL:-fpga_top_debug.core_def.U_UVHS_UVW_AXI4_TO_DDR4}" +COMMAND_FILE="${UVHS_COMMAND_FILE:-$STAGE/commands/${TAG}.command.tcl}" +WORKDIR="${UVHS_DOWNLOAD_WORKDIR:-$STAGE/workdir/${TAG}}" +LOG="${UVHS_DOWNLOAD_LOG:-$STAGE/logs/${TAG}.download.log}" +ALLOW_DOWNLOAD="${UVHS_ALLOW_DOWNLOAD:-0}" +ALLOW_DOWNLOAD_TAG="${UVHS_ALLOW_DOWNLOAD_TAG:-}" +WAIT_AFTER_START="${UVHS_WAIT_READY_AFTER_START:-1}" +READY_WAIT_SEC="${UVHS_READY_WAIT_SEC:-600}" +READY_POLL_SEC="${UVHS_READY_POLL_SEC:-2}" + +ts() { + date '+%Y-%m-%dT%H:%M:%S%z' +} + +if [ -z "$UV_SHELL" ]; then + echo "ERROR: set UVHS_UV_SHELL or UV_ROOT before starting the runtime" >&2 + exit 1 +fi + +die() { + echo "ERROR: $*" >&2 + exit 1 +} + +require_tagged_value() { + local label="$1" + local value="$2" + case "$value" in + *"$TAG"*) ;; + *) die "$label must contain the exact current run tag '$TAG': $value" ;; + esac +} + +validate_run_identity() { + case "$TAG" in + ""|*[!A-Za-z0-9_.-]*) + die "UVHS_RUN_TAG must be non-empty and contain only A-Z, a-z, 0-9, '.', '_' or '-': $TAG" + ;; + esac + if [ "${#TAG}" -gt 120 ]; then + die "UVHS_RUN_TAG is longer than 120 characters" + fi + require_tagged_value UVHS_STAGE_DIR "$STAGE" + require_tagged_value UVHS_TMUX_SESSION "$SESSION" + require_tagged_value UVHS_COMMAND_FILE "$COMMAND_FILE" + require_tagged_value UVHS_DOWNLOAD_WORKDIR "$WORKDIR" + require_tagged_value UVHS_DOWNLOAD_LOG "$LOG" + case "$WAIT_AFTER_START" in + 0|1) ;; + *) die "UVHS_WAIT_READY_AFTER_START must be 0 or 1" ;; + esac + local item name value + for item in "UVHS_READY_WAIT_SEC:$READY_WAIT_SEC" "UVHS_READY_POLL_SEC:$READY_POLL_SEC"; do + name="${item%%:*}" + value="${item#*:}" + case "$value" in + ''|*[!0-9]*|0*) die "$name must be a positive integer" ;; + esac + done +} + +require_fresh_start_artifacts() { + local run_script existing + run_script="$STAGE/commands/${TAG}.start-download.sh" + for existing in \ + "$COMMAND_FILE" \ + "$COMMAND_FILE.running" \ + "$WORKDIR" \ + "$LOG" \ + "$run_script"; do + if [ -e "$existing" ]; then + die "refusing reused UVHS_RUN_TAG; artifact already exists: $existing" + fi + done +} + +has_exact_session() { + tmux list-sessions -F '#{session_name}' 2>/dev/null | grep -Fxq "$SESSION" +} + +show_exact_matches() { + echo "## tmux exact tag matches" + tmux ls 2>/dev/null | grep -F "$TAG" || true + echo "## ps exact tag matches" + ps -eo pid,ppid,user,etime,stat,wchan:24,cmd \ + | awk -v tag="$TAG" -v self="$$" -v parent="$PPID" \ + '$1 != self && $1 != parent && index($0, tag) && $0 !~ /awk -v tag=/ {print}' || true +} + +require_stage() { + for p in "$STAGE" "$DB_PATH" "$DOWNLOAD_SCRIPT"; do + if [ ! -e "$p" ]; then + echo "ERROR: missing required path: $p" >&2 + exit 2 + fi + done + if [ ! -x "$UV_SHELL" ]; then + echo "ERROR: uv_shell not executable: $UV_SHELL" >&2 + exit 3 + fi +} + +last_log_match() { + local pattern="$1" + grep -Ein "$pattern" "$LOG" 2>/dev/null | tail -n 1 || true +} + +emit_log_evidence() { + local label="$1" + local match="$2" + local line text_value + line="${match%%:*}" + text_value="${match#*:}" + echo "${label}_evidence=$LOG:$line:$text_value" +} + +wait_ready_runtime() { + local deadline error_match load_match download_match link_match init_match + local ddr_match reset4_match reset5_match reset6_match + deadline=$(( $(date +%s) + READY_WAIT_SEC )) + echo "## uvhs_tagged_runtime wait-ready $(ts)" + echo "tag=$TAG" + echo "session=$SESSION" + echo "log=$LOG" + echo "ready_wait_sec=$READY_WAIT_SEC" + echo "ready_poll_sec=$READY_POLL_SEC" + + while [ "$(date +%s)" -lt "$deadline" ]; do + if [ -f "$LOG" ]; then + error_match="$(last_log_match '(^|[[:space:]])ERROR:|[[]RTM-[0-9]+[]].*ERROR|download.*fail|initializ(e|ation).*fail|Load_DB.*fail')" + if [ -n "$error_match" ]; then + echo "runtime_ready=0" + emit_log_evidence runtime_error "$error_match" + return 10 + fi + + load_match="$(last_log_match 'INFO: loading runtime database|load_db.*success')" + download_match="$(last_log_match 'Done: download success|download.*success')" + link_match="$(last_log_match 'linked up|(^|[[:space:]])link up')" + init_match="$(last_log_match 'Done: initialization complete success|systembus initialize success|ps initialize success')" + ddr_match="$(last_log_match 'DDR4 initialize success')" + reset4_match="$(last_log_match 'rstn_sw4[^0-9]*1')" + reset5_match="$(last_log_match 'rstn_sw5[^0-9]*1')" + reset6_match="$(last_log_match 'rstn_sw6[^0-9]*1')" + + if [ -n "$load_match" ] && [ -n "$download_match" ] && [ -n "$link_match" ] && \ + [ -n "$init_match" ] && [ -n "$ddr_match" ] && \ + [ -n "$reset4_match" ] && [ -n "$reset5_match" ] && [ -n "$reset6_match" ]; then + echo "runtime_ready=1" + emit_log_evidence load_db "$load_match" + emit_log_evidence download "$download_match" + emit_log_evidence link "$link_match" + emit_log_evidence initialize "$init_match" + emit_log_evidence ddr_initialize "$ddr_match" + emit_log_evidence reset_sw4 "$reset4_match" + emit_log_evidence reset_sw5 "$reset5_match" + emit_log_evidence reset_sw6 "$reset6_match" + return 0 + fi + fi + + if ! has_exact_session; then + echo "runtime_ready=0" + echo "runtime_ready_reason=session_exited" + return 11 + fi + sleep "$READY_POLL_SEC" + done + + echo "runtime_ready=0" + echo "runtime_ready_reason=timeout" + [ -f "$LOG" ] && tail -120 "$LOG" || true + return 12 +} + +start_runtime() { + require_stage + echo "## uvhs_tagged_runtime start $(ts)" + echo "tag=$TAG" + echo "stage=$STAGE" + echo "session=$SESSION" + echo "db_path=$DB_PATH" + echo "download_script=$DOWNLOAD_SCRIPT" + echo "command_file=$COMMAND_FILE" + echo "workdir=$WORKDIR" + echo "log=$LOG" + echo "allow_download=$ALLOW_DOWNLOAD" + echo "allow_download_tag=$ALLOW_DOWNLOAD_TAG" + echo "wait_ready_after_start=$WAIT_AFTER_START" + show_exact_matches + + if has_exact_session; then + echo "ERROR: tmux session already exists: $SESSION" >&2 + exit 4 + fi + if [ "$ALLOW_DOWNLOAD" != 1 ] || [ "$ALLOW_DOWNLOAD_TAG" != "$TAG" ]; then + echo "DRY_RUN: set UVHS_ALLOW_DOWNLOAD=1 and UVHS_ALLOW_DOWNLOAD_TAG=$TAG to start tmux/uv_shell download" + return 0 + fi + + require_fresh_start_artifacts + + mkdir -p "$(dirname "$COMMAND_FILE")" "$WORKDIR" "$(dirname "$LOG")" + + local run_script + run_script="$STAGE/commands/${TAG}.start-download.sh" + cat > "$run_script" <>\"$LOG\" 2>&1'" + echo "started_session=$SESSION" + echo "started_log=$LOG" + if [ "$WAIT_AFTER_START" = 1 ]; then + wait_ready_runtime + fi +} + +status_runtime() { + echo "## uvhs_tagged_runtime status $(ts)" + echo "tag=$TAG" + echo "session=$SESSION" + echo "log=$LOG" + show_exact_matches + if [ -f "$LOG" ]; then + echo "## log tail" + tail -120 "$LOG" || true + fi +} + +cleanup_runtime() { + echo "## uvhs_tagged_runtime cleanup $(ts)" + echo "tag=$TAG" + echo "session=$SESSION" + echo "command_file=$COMMAND_FILE" + show_exact_matches + + if has_exact_session; then + local wait_left + wait_left="${UVHS_COMMAND_IDLE_WAIT_SEC:-10}" + case "$wait_left" in + ''|*[!0-9]*|0[0-9]*) die "UVHS_COMMAND_IDLE_WAIT_SEC must be a non-negative integer" ;; + esac + while { [ -e "$COMMAND_FILE" ] || [ -e "$COMMAND_FILE.running" ]; } && [ "$wait_left" -gt 0 ]; do + echo "waiting_for_existing_command_file seconds_left=$wait_left" + sleep 1 + wait_left=$((wait_left - 1)) + done + if [ -e "$COMMAND_FILE" ] || [ -e "$COMMAND_FILE.running" ]; then + die "refusing to overwrite active/stale command file during cleanup: $COMMAND_FILE" + fi + mkdir -p "$(dirname "$COMMAND_FILE")" + cat > "$COMMAND_FILE.tmp" <<'EOF' +set ::uvhs_keepalive 1 +EOF + mv -n "$COMMAND_FILE.tmp" "$COMMAND_FILE" + if [ -e "$COMMAND_FILE.tmp" ]; then + rm -f "$COMMAND_FILE.tmp" + die "command file appeared during cleanup; refusing overwrite: $COMMAND_FILE" + fi + sleep "${UVHS_CLEANUP_GRACE_SEC:-3}" + fi + + if has_exact_session; then + echo "## exact matches immediately before forced session cleanup" + show_exact_matches + # has_exact_session above already proves a full-name match. Older tmux + # releases on the runtime host do not accept the newer "=name" syntax. + tmux kill-session -t "$SESSION" + echo "killed_session=$SESSION" + else + echo "no_session=$SESSION" + fi + + echo "## post-cleanup exact tag matches" + show_exact_matches +} + +validate_run_identity + +case "${1:-status}" in + start) start_runtime ;; + wait-ready) wait_ready_runtime ;; + status) status_runtime ;; + cleanup) cleanup_runtime ;; + *) + echo "usage: $0 {start|wait-ready|status|cleanup}" >&2 + exit 64 + ;; +esac From 0b1f9b4ec3751798b7b030d0919049ac55bb1a6f Mon Sep 17 00:00:00 2001 From: fengkehan Date: Tue, 4 Aug 2026 08:59:02 +0800 Subject: [PATCH 2/2] fpga_diff: add validated UVHS UHD capture flow --- fpga_diff/Makefile | 1 + fpga_diff/user_script/.gitignore | 2 + fpga_diff/user_script/hw_capture_uhd.tcl | 61 ++++++++++++++ fpga_diff/user_script/hw_run_download.tcl | 21 ++++- fpga_diff/user_script/uhd_c2h.ini | 9 ++ fpga_diff/uvhs/Makefile | 77 ++++++++++------- fpga_diff/uvhs/README.md | 53 ++++++++++-- fpga_diff/uvhs/async_clocks.tcl | 10 +++ fpga_diff/uvhs/backend_run.tcl | 86 ++++++++++++++++++- fpga_diff/uvhs/check_flow_tools.sh | 38 ++++++++- fpga_diff/uvhs/flow.md | 64 ++++++++++++++- fpga_diff/uvhs/frontend_run.tcl | 15 +++- fpga_diff/uvhs/partition_capture_ddr_f3.tcl | 11 +++ fpga_diff/uvhs/probe_template.tcl | 91 +++++++++++++++++++++ fpga_diff/uvhs/uvhs_queue_uhd_capture.sh | 64 +++++++++++++++ fpga_diff/uvhs/uvhs_tagged_runtime.sh | 7 +- 16 files changed, 564 insertions(+), 46 deletions(-) create mode 100644 fpga_diff/user_script/hw_capture_uhd.tcl create mode 100644 fpga_diff/user_script/uhd_c2h.ini create mode 100644 fpga_diff/uvhs/partition_capture_ddr_f3.tcl create mode 100644 fpga_diff/uvhs/probe_template.tcl create mode 100755 fpga_diff/uvhs/uvhs_queue_uhd_capture.sh diff --git a/fpga_diff/Makefile b/fpga_diff/Makefile index a06392e2..c9e6f975 100755 --- a/fpga_diff/Makefile +++ b/fpga_diff/Makefile @@ -62,6 +62,7 @@ UVHS_FORWARD_TARGETS := \ uvhs_tools_check uvhs_frontend uvhs_backend uvhs_all \ uvhs_clean uvhs_env uvhs_preflight uvhs_hejian_pcie_x4_env \ uvhs_hejian_pcie_x4_preflight uvhs_hejian_pcie_x4_nutshell_all \ + uvhs_hejian_pcie_x4_nutshell_probe_all \ uvhs_hejian_pcie_x4_xiangshan_all \ uvhs_status uvhs_check_timing_clean uvhs_export_ltx \ uvhs_package_bitstream diff --git a/fpga_diff/user_script/.gitignore b/fpga_diff/user_script/.gitignore index 6ab2da6e..df919aba 100644 --- a/fpga_diff/user_script/.gitignore +++ b/fpga_diff/user_script/.gitignore @@ -1,3 +1,5 @@ * !.gitignore !hw_run_download.tcl +!hw_capture_uhd.tcl +!uhd_c2h.ini diff --git a/fpga_diff/user_script/hw_capture_uhd.tcl b/fpga_diff/user_script/hw_capture_uhd.tcl new file mode 100644 index 00000000..cbb1a9c9 --- /dev/null +++ b/fpga_diff/user_script/hw_capture_uhd.tcl @@ -0,0 +1,61 @@ +# Capture command fragment for an already attached hw_run_download.tcl runtime. +# This consumes the single probe/trigger definition in uvhs/probe_template.tcl; +# it does not declare another instrumentation or DDR path. + +proc uvhs_uhd_env {name default} { + if {[info exists ::env($name)] && $::env($name) ne ""} { + return $::env($name) + } + return $default +} + +set uvhs_uhd_script_dir [file dirname [file normalize [info script]]] +set uvhs_uhd_workdir [uvhs_uhd_env UVHS_RUNTIME_WORKDIR [pwd]] +set uvhs_uhd_ini [file normalize [uvhs_uhd_env UVHS_UHD_TRIGGER_INI \ + [file join $uvhs_uhd_script_dir uhd_c2h.ini]]] +set uvhs_uhd_clock [uvhs_uhd_env UVHS_UHD_GATED_CLOCK \ + b0/f2/part_2/core_def/xdma_ep_i/TO_DIFFTEST_PCIE_CLK] +set uvhs_uhd_frequency [uvhs_uhd_env UVHS_UHD_GATED_CLOCK_HZ 125000000] +set uvhs_uhd_position [uvhs_uhd_env UVHS_UHD_TRIGGER_POSITION 50] +set uvhs_uhd_depth [uvhs_uhd_env UVHS_UHD_CAPTURE_DEPTH 20000] +set uvhs_uhd_wait [uvhs_uhd_env UVHS_UHD_TRIGGER_WAIT_SEC 420] +set uvhs_uhd_out [uvhs_uhd_env UVHS_UHD_OUTPUT uvhs_c2h_capture] +set uvhs_uhd_bindir [file normalize [file join $uvhs_uhd_workdir UHD $uvhs_uhd_out]] + +if {![file isfile $uvhs_uhd_ini]} { + error "UVHS UHD trigger condition file not found: $uvhs_uhd_ini" +} +if {![regexp {^[A-Za-z0-9][A-Za-z0-9_.-]*$} $uvhs_uhd_out] || + $uvhs_uhd_out in {. ..}} { + error "UVHS_UHD_OUTPUT must be a relative run name using A-Z, a-z, 0-9, '.', '_' or '-': '$uvhs_uhd_out'" +} +foreach {name value} [list \ + UVHS_UHD_GATED_CLOCK_HZ $uvhs_uhd_frequency \ + UVHS_UHD_TRIGGER_POSITION $uvhs_uhd_position \ + UVHS_UHD_CAPTURE_DEPTH $uvhs_uhd_depth \ + UVHS_UHD_TRIGGER_WAIT_SEC $uvhs_uhd_wait] { + if {![string is integer -strict $value] || $value <= 0} { + error "$name must be a positive integer, got '$value'" + } +} + +puts "UVHS_UHD_CAPTURE_BEGIN [clock format [clock seconds] -format {%Y-%m-%dT%H:%M:%S%z}]" +puts "UVHS_UHD_TRIGGER_INI $uvhs_uhd_ini" +puts "UVHS_UHD_OUTPUT $uvhs_uhd_bindir" +query -capture +query -trigger +trigger -ini_check $uvhs_uhd_ini +trigger -set -gatedclk $uvhs_uhd_clock \ + -frequency $uvhs_uhd_frequency -polarity H +trigger -set -condition $uvhs_uhd_ini -position $uvhs_uhd_position +capture -enable +trigger -enable +puts "UVHS_UHD_CAPTURE_ARMED [clock format [clock seconds] -format {%Y-%m-%dT%H:%M:%S%z}]" +trigger -status -wait 1 -timeout $uvhs_uhd_wait +puts "UVHS_UHD_CAPTURE_TRIGGERED [clock format [clock seconds] -format {%Y-%m-%dT%H:%M:%S%z}]" +upload_uhd -depth $uvhs_uhd_depth -position $uvhs_uhd_position \ + -out $uvhs_uhd_out -force_overwrite -timeout 300 +puts "UVHS_UHD_CAPTURE_UPLOADED $uvhs_uhd_bindir" +wavegen -bindir $uvhs_uhd_bindir +puts "UVHS_UHD_WAVEFORM [file join $uvhs_uhd_bindir UvData.usdb]" +puts "UVHS_UHD_CAPTURE_END [clock format [clock seconds] -format {%Y-%m-%dT%H:%M:%S%z}]" diff --git a/fpga_diff/user_script/hw_run_download.tcl b/fpga_diff/user_script/hw_run_download.tcl index 866a2592..23703ce3 100644 --- a/fpga_diff/user_script/hw_run_download.tcl +++ b/fpga_diff/user_script/hw_run_download.tcl @@ -23,9 +23,24 @@ query -connector -type fmc query -voltage # Apply the compiler-assigned frequencies to the U2 ClockHub, then record both -# the requested defaults and the actual device state in the download log. -query -clock -default -config -clock -default +# the requested defaults and the actual device state in the download log. A +# design may intentionally leave an unused ClockHub output dangling; older +# runtimes reject the complete default table in that case. Allow a caller to +# provide the one used clock explicitly, but never guess its index/frequency. +if {[catch {query -clock -default} uvhs_clock_default_error]} { + if {![info exists ::env(UVHS_CLOCK_INDEX)] || + ![info exists ::env(UVHS_CLOCK_FREQUENCY_HZ)] || + $::env(UVHS_CLOCK_INDEX) eq "" || + $::env(UVHS_CLOCK_FREQUENCY_HZ) eq ""} { + error "query -clock -default failed and explicit UVHS_CLOCK_INDEX/UVHS_CLOCK_FREQUENCY_HZ are not set: $uvhs_clock_default_error" + } + puts "WARNING: default ClockHub table unavailable: $uvhs_clock_default_error" + puts "INFO: configuring ClockHub index $::env(UVHS_CLOCK_INDEX) to $::env(UVHS_CLOCK_FREQUENCY_HZ) Hz" + config -clock -index $::env(UVHS_CLOCK_INDEX) \ + -frequency $::env(UVHS_CLOCK_FREQUENCY_HZ) +} else { + config -clock -default +} config -clock -commit query -clock diff --git a/fpga_diff/user_script/uhd_c2h.ini b/fpga_diff/user_script/uhd_c2h.ini new file mode 100644 index 00000000..1064f55f --- /dev/null +++ b/fpga_diff/user_script/uhd_c2h.ini @@ -0,0 +1,9 @@ +# Standard runtime condition for the 15-signal fpga_diff UVHS template. +# Group and signal names must match uvhs/probe_template.tcl. +[uvhs_c2h] +LOGIC = OR +fpga_top_debug.core_def.difftest_to_host_axis_tvalid_io = 1 + +[UHD_FINAL_CONDITIONS_LOGIC] +LOGIC = OR +uvhs_c2h diff --git a/fpga_diff/uvhs/Makefile b/fpga_diff/uvhs/Makefile index be6bdf43..0cfc2c73 100644 --- a/fpga_diff/uvhs/Makefile +++ b/fpga_diff/uvhs/Makefile @@ -54,7 +54,10 @@ UVHS_UVW_AXI4_TO_DDR4_RTL_INST ?= $(UVHS_TOP).core_def.U_UVHS_UVW_AXI4_TO_DDR4 UVHS_FRONTEND_TCL ?= $(ROOT_DIR)/uvhs/frontend_run.tcl UVHS_SKIP_BOARD_CONSTRAINTS ?= 0 UVHS_PIN_OVERRIDE_FILE ?= -UVHS_PROBE_FILE ?= +UVHS_PROBE_FILE ?= $(ROOT_DIR)/uvhs/probe_template.tcl +UVHS_ENABLE_PROBE_NET ?= 0 +UVHS_ENABLE_TRIGGER_NET ?= 1 +UVHS_PROBE_TOP ?= $(UVHS_TOP) UVHS_CPU_CLK_PERIOD_NS ?= 40 UVHS_ROUTE_ENABLE_HOLD_EXPN_BAILOUT ?= UVHS_CPU_DEBUG_CLK ?= 1 @@ -79,6 +82,7 @@ UVHS_UART0_CONNECTOR ?= b0.F2_APC16 UVHS_UART0_TX_INDEX ?= 117 UVHS_UART0_RX_INDEX ?= 57 UVHS_TIME_GROUP_IO_LOGIC ?= default +UVHS_LOCALIZE_CLOCKS ?= UVHS_STRATEGY_NUM_RETRY ?= UVHS_LUT_FILL_RATE ?= UVHS_LUT6_FILL_RATE ?= @@ -91,7 +95,7 @@ UVHS_COMPILE_STRATEGY0 ?= uv_high_fanout_explore UVHS_COMPILE_STRATEGY1 ?= UVHS_COMPILE_STRATEGY2 ?= export UVHS_PNR_STRATEGY UVHS_COMPILE_STRATEGY_NUM UVHS_COMPILE_STRATEGY0 UVHS_COMPILE_STRATEGY1 UVHS_COMPILE_STRATEGY2 -UVHS_HEJIAN_FLOW_ENV = XDMA_LINK_WIDTH="$(XDMA_LINK_WIDTH)" XDMA_ENABLE_PF0_BAR1="$(XDMA_ENABLE_PF0_BAR1)" XDMA_AXILITE_MASTER_SCALE="$(XDMA_AXILITE_MASTER_SCALE)" XDMA_AXILITE_MASTER_SIZE="$(XDMA_AXILITE_MASTER_SIZE)" XDMA_EXPECTED_VENDOR="$(XDMA_EXPECTED_VENDOR)" XDMA_EXPECTED_DEVICE="$(XDMA_EXPECTED_DEVICE)" XDMA_EXPECTED_BAR0_SIZE="$(XDMA_EXPECTED_BAR0_SIZE)" XDMA_EXPECTED_BAR1_SIZE="$(XDMA_EXPECTED_BAR1_SIZE)" UVHS_BASE_ASSEMBLE_FILE="$(UVHS_BASE_ASSEMBLE_FILE)" UVHS_BASE_ASSEMBLE_SHA256="$(UVHS_BASE_ASSEMBLE_SHA256)" UVHS_XDMA_EP_EXPECTED_VIVADO="$(UVHS_XDMA_EP_EXPECTED_VIVADO)" UVHS_ALLOW_DCP_VIVADO_MISMATCH="$(UVHS_ALLOW_DCP_VIVADO_MISMATCH)" UVHS_DESIGN_NAME="$(UVHS_DESIGN_NAME)" PLATFORM="$(PLATFORM)" UVHS_TARGET_BOARD="$(UVHS_TARGET_BOARD)" UVHS_TARGET_PACK="$(UVHS_TARGET_PACK)" UVHS_TARGET_FPGA="$(UVHS_TARGET_FPGA)" UVHS_TARGET_FPGA_LOWER="$(UVHS_TARGET_FPGA_LOWER)" UVHS_FPGA_COUNT="$(UVHS_FPGA_COUNT)" UVHS_KEEP_FPGAS="$(UVHS_KEEP_FPGAS)" UVHS_UART0_CONNECTOR="$(UVHS_UART0_CONNECTOR)" UVHS_UART0_TX_INDEX="$(UVHS_UART0_TX_INDEX)" UVHS_UART0_RX_INDEX="$(UVHS_UART0_RX_INDEX)" UVHS_TIME_GROUP_IO_LOGIC="$(UVHS_TIME_GROUP_IO_LOGIC)" UVHS_CPU_CLK_PERIOD_NS="$(UVHS_CPU_CLK_PERIOD_NS)" UVHS_CPU_DEBUG_CLK="$(UVHS_CPU_DEBUG_CLK)" UVHS_PNR_STRATEGY="$(UVHS_PNR_STRATEGY)" UVHS_COMPILE_STRATEGY_NUM="$(UVHS_COMPILE_STRATEGY_NUM)" UVHS_COMPILE_STRATEGY0="$(UVHS_COMPILE_STRATEGY0)" UVHS_COMPILE_STRATEGY1="$(UVHS_COMPILE_STRATEGY1)" UVHS_COMPILE_STRATEGY2="$(UVHS_COMPILE_STRATEGY2)" +UVHS_HEJIAN_FLOW_ENV = XDMA_LINK_WIDTH="$(XDMA_LINK_WIDTH)" XDMA_ENABLE_PF0_BAR1="$(XDMA_ENABLE_PF0_BAR1)" XDMA_AXILITE_MASTER_SCALE="$(XDMA_AXILITE_MASTER_SCALE)" XDMA_AXILITE_MASTER_SIZE="$(XDMA_AXILITE_MASTER_SIZE)" XDMA_EXPECTED_VENDOR="$(XDMA_EXPECTED_VENDOR)" XDMA_EXPECTED_DEVICE="$(XDMA_EXPECTED_DEVICE)" XDMA_EXPECTED_BAR0_SIZE="$(XDMA_EXPECTED_BAR0_SIZE)" XDMA_EXPECTED_BAR1_SIZE="$(XDMA_EXPECTED_BAR1_SIZE)" UVHS_BASE_ASSEMBLE_FILE="$(UVHS_BASE_ASSEMBLE_FILE)" UVHS_BASE_ASSEMBLE_SHA256="$(UVHS_BASE_ASSEMBLE_SHA256)" UVHS_XDMA_EP_EXPECTED_VIVADO="$(UVHS_XDMA_EP_EXPECTED_VIVADO)" UVHS_ALLOW_DCP_VIVADO_MISMATCH="$(UVHS_ALLOW_DCP_VIVADO_MISMATCH)" UVHS_DESIGN_NAME="$(UVHS_DESIGN_NAME)" PLATFORM="$(PLATFORM)" UVHS_TARGET_BOARD="$(UVHS_TARGET_BOARD)" UVHS_TARGET_PACK="$(UVHS_TARGET_PACK)" UVHS_TARGET_FPGA="$(UVHS_TARGET_FPGA)" UVHS_TARGET_FPGA_LOWER="$(UVHS_TARGET_FPGA_LOWER)" UVHS_FPGA_COUNT="$(UVHS_FPGA_COUNT)" UVHS_KEEP_FPGAS="$(UVHS_KEEP_FPGAS)" UVHS_UART0_CONNECTOR="$(UVHS_UART0_CONNECTOR)" UVHS_UART0_TX_INDEX="$(UVHS_UART0_TX_INDEX)" UVHS_UART0_RX_INDEX="$(UVHS_UART0_RX_INDEX)" UVHS_TIME_GROUP_IO_LOGIC="$(UVHS_TIME_GROUP_IO_LOGIC)" UVHS_LOCALIZE_CLOCKS="$(UVHS_LOCALIZE_CLOCKS)" UVHS_CPU_CLK_PERIOD_NS="$(UVHS_CPU_CLK_PERIOD_NS)" UVHS_CPU_DEBUG_CLK="$(UVHS_CPU_DEBUG_CLK)" UVHS_PNR_STRATEGY="$(UVHS_PNR_STRATEGY)" UVHS_COMPILE_STRATEGY_NUM="$(UVHS_COMPILE_STRATEGY_NUM)" UVHS_COMPILE_STRATEGY0="$(UVHS_COMPILE_STRATEGY0)" UVHS_COMPILE_STRATEGY1="$(UVHS_COMPILE_STRATEGY1)" UVHS_COMPILE_STRATEGY2="$(UVHS_COMPILE_STRATEGY2)" UVHS_PNR_DIR ?= $(UVHS_WORK_DIR)/hw.dat/Compile/PnR/$(UVHS_TARGET_PACK)/$(UVHS_TARGET_FPGA)/vivado/Rundir/$(UVHS_PNR_STRATEGY) UVHS_BITSTREAM_DIR ?= $(UVHS_PNR_DIR)/bitstream UVHS_LTX ?= $(UVHS_BITSTREAM_DIR)/fpga_top_debug.ltx @@ -104,6 +108,7 @@ UVHS_RESOLVED_BITSTREAM_DIR = $$(summary="$(UVHS_BITSTREAM_DIR)/after_xtalk_fix_ uvhs_frontend uvhs_backend uvhs_all \ uvhs_clean uvhs_env uvhs_preflight uvhs_hejian_pcie_x4_env \ uvhs_hejian_pcie_x4_preflight uvhs_hejian_pcie_x4_nutshell_all \ + uvhs_hejian_pcie_x4_nutshell_probe_all \ uvhs_hejian_pcie_x4_xiangshan_all \ uvhs_status uvhs_check_timing_clean uvhs_export_ltx uvhs_package_bitstream @@ -127,11 +132,7 @@ uvhs_prepare: if [ -f "$(ROOT_DIR)/uvhs/partition.tcl" ]; then \ cp "$(ROOT_DIR)/uvhs/partition.tcl" "$(UVHS_WORK_DIR)/script/partition.tcl"; \ fi - if [ -f "$(ROOT_DIR)/uvhs/probe.tcl" ]; then \ - cp "$(ROOT_DIR)/uvhs/probe.tcl" "$(UVHS_WORK_DIR)/script/probe.tcl"; \ - else \ - : > "$(UVHS_WORK_DIR)/script/probe.tcl"; \ - fi + : > "$(UVHS_WORK_DIR)/script/probe.tcl" for dcp in \ rtl/soc/AXI_bridge.dcp \ rtl/soc/blk_mem_gen_0.dcp \ @@ -272,7 +273,7 @@ uvhs_check_modules: uvhs_filelist uvhs_frontend: UVHS_WORK_DIR = $(ENV_SCRIPTS_HOME)/fpga_diff_uvhs_$(CPU)$(if $(strip $(SUFFIX)),-$(strip $(SUFFIX)),) uvhs_frontend: UVHS_ASSEMBLE_FILE = $(ROOT_DIR)/uvhs/assemble_uvhs.tcl uvhs_frontend: uvhs_export_vivado_ip uvhs_sync_uvw_axi4_to_ddr4 uvhs_filelist uvhs_check_modules - bash -lc 'set -o pipefail; source "$(ROOT_DIR)/uvhs/setenv.sh" && cd "$(UVHS_WORK_DIR)" && $(UVHS_HEJIAN_FLOW_ENV) UVHS_TOP="$(UVHS_TOP)" UVHS_FILELIST="./rtl/filelist.f" UVHS_REQUIRED_DCP_MODULES="$(UVHS_REQUIRED_DCP_MODULES)" UVHS_CPU_CLK_PERIOD_NS="$(UVHS_CPU_CLK_PERIOD_NS)" UVHS_SKIP_BOARD_CONSTRAINTS="$(UVHS_SKIP_BOARD_CONSTRAINTS)" UVHS_PROBE_FILE="$(UVHS_PROBE_FILE)" UVHS_ASSIGN_PIN_FILE="$(UVHS_ASSIGN_PIN_FILE)" UVHS_TIMING_FILE="$(UVHS_TIMING_FILE)" UVHS_PARTITION_FILE="$(UVHS_PARTITION_FILE)" UVHS_ASSEMBLE_FILE="$(UVHS_ASSEMBLE_FILE)" UVHS_USE_LSF="$(UVHS_USE_LSF)" UVHS_FRONTEND_THREADS="$(UVHS_FRONTEND_THREADS)" UVHS_FRONTEND_PROCESSES="$(UVHS_FRONTEND_PROCESSES)" UVHS_FPGA_THREADS="$(UVHS_FPGA_THREADS)" UVHS_FPGA_PROCESSES="$(UVHS_FPGA_PROCESSES)" bash "$$UV_ROOT/bin/uv_shell" -bypass_vivado_version_check -s "$(UVHS_FRONTEND_TCL)" |& tee frontend_run.log' + bash -lc 'set -o pipefail; source "$(ROOT_DIR)/uvhs/setenv.sh" && cd "$(UVHS_WORK_DIR)" && $(UVHS_HEJIAN_FLOW_ENV) UVHS_TOP="$(UVHS_TOP)" UVHS_FILELIST="./rtl/filelist.f" UVHS_REQUIRED_DCP_MODULES="$(UVHS_REQUIRED_DCP_MODULES)" UVHS_CPU_CLK_PERIOD_NS="$(UVHS_CPU_CLK_PERIOD_NS)" UVHS_SKIP_BOARD_CONSTRAINTS="$(UVHS_SKIP_BOARD_CONSTRAINTS)" UVHS_PROBE_FILE="$(UVHS_PROBE_FILE)" UVHS_ENABLE_PROBE_NET="$(UVHS_ENABLE_PROBE_NET)" UVHS_ENABLE_TRIGGER_NET="$(UVHS_ENABLE_TRIGGER_NET)" UVHS_PROBE_TOP="$(UVHS_PROBE_TOP)" UVHS_ASSIGN_PIN_FILE="$(UVHS_ASSIGN_PIN_FILE)" UVHS_TIMING_FILE="$(UVHS_TIMING_FILE)" UVHS_PARTITION_FILE="$(UVHS_PARTITION_FILE)" UVHS_ASSEMBLE_FILE="$(UVHS_ASSEMBLE_FILE)" UVHS_USE_LSF="$(UVHS_USE_LSF)" UVHS_FRONTEND_THREADS="$(UVHS_FRONTEND_THREADS)" UVHS_FRONTEND_PROCESSES="$(UVHS_FRONTEND_PROCESSES)" UVHS_FPGA_THREADS="$(UVHS_FPGA_THREADS)" UVHS_FPGA_PROCESSES="$(UVHS_FPGA_PROCESSES)" bash "$$UV_ROOT/bin/uv_shell" -bypass_vivado_version_check -s "$(UVHS_FRONTEND_TCL)" |& tee frontend_run.log' uvhs_backend: UVHS_WORK_DIR = $(ENV_SCRIPTS_HOME)/fpga_diff_uvhs_$(CPU)$(if $(strip $(SUFFIX)),-$(strip $(SUFFIX)),) uvhs_backend: UVHS_ASSEMBLE_FILE = $(ROOT_DIR)/uvhs/assemble_uvhs.tcl @@ -296,31 +297,47 @@ uvhs_hejian_pcie_x4_preflight: UVHS_CPU_DEBUG_CLK = 1 uvhs_hejian_pcie_x4_preflight: @bash -lc 'source "$(ROOT_DIR)/uvhs/setenv.sh" >/dev/null 2>&1 || true; $(UVHS_HEJIAN_FLOW_ENV) ROOT_DIR="$(ROOT_DIR)" bash "$(ROOT_DIR)/uvhs/hejian_pcie_x4_env.sh" check' -uvhs_hejian_pcie_x4_nutshell_all: CPU = nutshell -uvhs_hejian_pcie_x4_nutshell_all: XDMA_LINK_WIDTH = X4 -uvhs_hejian_pcie_x4_nutshell_all: XDMA_ENABLE_PF0_BAR1 = 1 -uvhs_hejian_pcie_x4_nutshell_all: XDMA_AXILITE_MASTER_SCALE = Kilobytes -uvhs_hejian_pcie_x4_nutshell_all: XDMA_AXILITE_MASTER_SIZE = 512 -uvhs_hejian_pcie_x4_nutshell_all: XDMA_EXPECTED_VENDOR = 0x10ee -uvhs_hejian_pcie_x4_nutshell_all: XDMA_EXPECTED_DEVICE = 0x9048 -uvhs_hejian_pcie_x4_nutshell_all: XDMA_EXPECTED_BAR0_SIZE = 0x80000 -uvhs_hejian_pcie_x4_nutshell_all: XDMA_EXPECTED_BAR1_SIZE = 0x10000 -uvhs_hejian_pcie_x4_nutshell_all: UVHS_DESIGN_NAME = VU19P_X4 -uvhs_hejian_pcie_x4_nutshell_all: PLATFORM = U2.2 -uvhs_hejian_pcie_x4_nutshell_all: UVHS_TARGET_BOARD = B0 -uvhs_hejian_pcie_x4_nutshell_all: UVHS_TARGET_PACK = B0 -uvhs_hejian_pcie_x4_nutshell_all: UVHS_TARGET_FPGA = F2 -uvhs_hejian_pcie_x4_nutshell_all: UVHS_TARGET_FPGA_LOWER = b0.f2 +UVHS_NUTSHELL_X4_TARGETS := \ + uvhs_hejian_pcie_x4_nutshell_all \ + uvhs_hejian_pcie_x4_nutshell_probe_all +$(UVHS_NUTSHELL_X4_TARGETS): CPU = nutshell +$(UVHS_NUTSHELL_X4_TARGETS): XDMA_LINK_WIDTH = X4 +$(UVHS_NUTSHELL_X4_TARGETS): XDMA_ENABLE_PF0_BAR1 = 1 +$(UVHS_NUTSHELL_X4_TARGETS): XDMA_AXILITE_MASTER_SCALE = Kilobytes +$(UVHS_NUTSHELL_X4_TARGETS): XDMA_AXILITE_MASTER_SIZE = 512 +$(UVHS_NUTSHELL_X4_TARGETS): XDMA_EXPECTED_VENDOR = 0x10ee +$(UVHS_NUTSHELL_X4_TARGETS): XDMA_EXPECTED_DEVICE = 0x9048 +$(UVHS_NUTSHELL_X4_TARGETS): XDMA_EXPECTED_BAR0_SIZE = 0x80000 +$(UVHS_NUTSHELL_X4_TARGETS): XDMA_EXPECTED_BAR1_SIZE = 0x10000 +$(UVHS_NUTSHELL_X4_TARGETS): UVHS_DESIGN_NAME = VU19P_X4 +$(UVHS_NUTSHELL_X4_TARGETS): PLATFORM = U2.2 +$(UVHS_NUTSHELL_X4_TARGETS): UVHS_TARGET_BOARD = B0 +$(UVHS_NUTSHELL_X4_TARGETS): UVHS_TARGET_PACK = B0 +$(UVHS_NUTSHELL_X4_TARGETS): UVHS_TARGET_FPGA = F2 +$(UVHS_NUTSHELL_X4_TARGETS): UVHS_TARGET_FPGA_LOWER = b0.f2 +$(UVHS_NUTSHELL_X4_TARGETS): UVHS_CPU_CLK_PERIOD_NS = 40 +$(UVHS_NUTSHELL_X4_TARGETS): UVHS_CPU_DEBUG_CLK = 1 +$(UVHS_NUTSHELL_X4_TARGETS): UVHS_PNR_STRATEGY = Strategy_uv_high_fanout_explore +$(UVHS_NUTSHELL_X4_TARGETS): UVHS_COMPILE_STRATEGY_NUM = 1 +$(UVHS_NUTSHELL_X4_TARGETS): UVHS_COMPILE_STRATEGY0 = uv_high_fanout_explore +$(UVHS_NUTSHELL_X4_TARGETS): UVHS_ASSEMBLE_FILE = $(ROOT_DIR)/uvhs/assemble_uvhs.tcl +$(UVHS_NUTSHELL_X4_TARGETS): UVHS_ASSIGN_PIN_FILE = $(ROOT_DIR)/uvhs/assign_pin_nutshell_f2.tcl + uvhs_hejian_pcie_x4_nutshell_all: UVHS_FPGA_COUNT = 1 -uvhs_hejian_pcie_x4_nutshell_all: UVHS_CPU_CLK_PERIOD_NS = 40 -uvhs_hejian_pcie_x4_nutshell_all: UVHS_CPU_DEBUG_CLK = 1 -uvhs_hejian_pcie_x4_nutshell_all: UVHS_PNR_STRATEGY = Strategy_uv_high_fanout_explore -uvhs_hejian_pcie_x4_nutshell_all: UVHS_COMPILE_STRATEGY_NUM = 1 -uvhs_hejian_pcie_x4_nutshell_all: UVHS_COMPILE_STRATEGY0 = uv_high_fanout_explore -uvhs_hejian_pcie_x4_nutshell_all: UVHS_ASSEMBLE_FILE = $(ROOT_DIR)/uvhs/assemble_uvhs.tcl -uvhs_hejian_pcie_x4_nutshell_all: UVHS_ASSIGN_PIN_FILE = $(ROOT_DIR)/uvhs/assign_pin_nutshell_f2.tcl +uvhs_hejian_pcie_x4_nutshell_all: UVHS_KEEP_FPGAS = b0.f2 uvhs_hejian_pcie_x4_nutshell_all: uvhs_hejian_pcie_x4_preflight uvhs_all +# Validated UHD waveform topology: the probe/capture logic and its physical +# capture DDR remain on F2, while the complete functional DDR IP moves to F3. +uvhs_hejian_pcie_x4_nutshell_probe_all: UVHS_ENABLE_PROBE_NET = 1 +uvhs_hejian_pcie_x4_nutshell_probe_all: UVHS_ENABLE_TRIGGER_NET = 1 +uvhs_hejian_pcie_x4_nutshell_probe_all: UVHS_PARTITION_FILE = $(ROOT_DIR)/uvhs/partition_capture_ddr_f3.tcl +uvhs_hejian_pcie_x4_nutshell_probe_all: UVHS_FPGA_COUNT = 2 +uvhs_hejian_pcie_x4_nutshell_probe_all: UVHS_KEEP_FPGAS = b0.f2 b0.f3 +uvhs_hejian_pcie_x4_nutshell_probe_all: UVHS_TIME_GROUP_IO_LOGIC = 0 +uvhs_hejian_pcie_x4_nutshell_probe_all: UVHS_LOCALIZE_CLOCKS = SOC_GATED_CLK +uvhs_hejian_pcie_x4_nutshell_probe_all: uvhs_hejian_pcie_x4_preflight uvhs_all + uvhs_hejian_pcie_x4_xiangshan_all: CPU = kmh uvhs_hejian_pcie_x4_xiangshan_all: XDMA_LINK_WIDTH = X4 uvhs_hejian_pcie_x4_xiangshan_all: XDMA_ENABLE_PF0_BAR1 = 1 diff --git a/fpga_diff/uvhs/README.md b/fpga_diff/uvhs/README.md index dc341d24..dd308f7d 100644 --- a/fpga_diff/uvhs/README.md +++ b/fpga_diff/uvhs/README.md @@ -51,15 +51,56 @@ UVHS_STAGE_DIR=/path/to/runtime-stage \ Do not add DDR writes or manual reset toggles to `hw_run_download.tcl`. The normal `fpga-host` flow writes its workload through H2C. +For a bitstream built with the standard probe template, queue the one supported +runtime capture flow before starting `fpga-host`: + +```sh +UVHS_RUN_TAG= UVHS_STAGE_DIR=/path/containing/ \ + bash uvhs/uvhs_queue_uhd_capture.sh +``` + +This arms the `difftest_to_host_axis_tvalid_io` condition, uploads the existing +15-signal stations, and runs `wavegen`. It writes `UvData.usdb` under the same +tagged runtime work directory. The helper does not program the board, write the +workload, or declare another probe/capture implementation. + ## Supporting Tools -Tracked supporting helpers live under `tools/build/` and contain only -generated-RTL fixes applied while constructing the file list. Board-specific -probe, BAR, XDMA, strace, and backdoor-DDR debug scripts are kept as local -tools and intentionally excluded from Git. +Tracked supporting helpers live under `tools/build/` and contain generated-RTL +fixes applied while constructing the file list. `probe_template.tcl` is the +standard CPU-independent Hejian `probe_net`/`trigger_net` template. It covers +reset, host control, XDMA link, DiffTest startup, and C2H handshakes. More +focused CPU hierarchy, BAR, XDMA, strace, and backdoor-DDR debug scripts remain +local tools. + +The standard template is deliberately opt-in. It does not change CPU/SoC RTL +or the functional AXI-to-DDR connection. On a board verified to have +PDDR4DME cards at both F2/FMC3 and F3/FMC3, build the validated two-FPGA +waveform topology with: + +```sh +make uvhs_hejian_pcie_x4_nutshell_probe_all \ + CORE_DIR=/path/to/NutShell SUFFIX= +``` -The Makefile accepts `UVHS_PROBE_FILE=/path/to/local/probe.tcl`; without a -local probe file it generates an intentionally empty probe script. +This keeps the F2/FMC3 capture DDR with the single F2 probe implementation and +moves only the complete functional AXI-to-DDR IP to F3/FMC3. The target keeps +F2 and F3, selects `partition_capture_ddr_f3.tcl`, and localizes the gated SoC +clock used by the CPU side of the cross-FPGA memory path. + +The template creates the `uvhs_control` and `uvhs_c2h` trigger groups in their +respective clock domains. Set `UVHS_ENABLE_TRIGGER_NET=0` for probes without +trigger groups. These groups expose trigger-capable signals; runtime capture +still selects the comparison condition and arms the trigger. `UVHS_PROBE_TOP` +defaults to `UVHS_TOP`. A specialized local template can explicitly override +the default with `UVHS_PROBE_FILE=/path/to/local/probe.tcl`. + +There is one supported probe/trigger definition: the standard template selected +by `UVHS_ENABLE_PROBE_NET`. Do not add a second probe flow. UHD uses a capture +DDR controller in addition to the probe/trigger instrumentation; it does not +replace that instrumentation or reduce its LUT cost. Board programming must +stop unless the generated binding report and physical inventory prove that UHD +capture and functional AXI-to-DDR each have usable memory hardware. ## Useful Checks diff --git a/fpga_diff/uvhs/async_clocks.tcl b/fpga_diff/uvhs/async_clocks.tcl index 4be4bc0e..99e6db38 100644 --- a/fpga_diff/uvhs/async_clocks.tcl +++ b/fpga_diff/uvhs/async_clocks.tcl @@ -172,6 +172,16 @@ fpga_diff_set_async_clock_groups xdma_axi_to_gt_pipe \ $fpga_diff_xdma_axis_clocks \ [concat $fpga_diff_pcie_gt_clocks $fpga_diff_xdma_pipe_clocks] +# The XDMA PCIe4C core creates an internal GT clock that is unrelated to the +# endpoint reference clock. Match the proven UVHS baseline constraint in the +# one reported direction; do not hide any crossing in fpga_diff-owned RTL. +if {[llength $fpga_diff_xdma_pipe_clocks] && [llength $fpga_diff_pcie_ref_clocks]} { + puts "INFO: UVHS patch: false path XDMA GT internal clock -> pcie_ep_refclk" + set_false_path -from $fpga_diff_xdma_pipe_clocks -to $fpga_diff_pcie_ref_clocks +} else { + puts "INFO: UVHS patch: skip XDMA GT internal clock -> pcie_ep_refclk, missing clocks" +} + # UVHS may expose the same XDMA clk_wiz 100 MHz user clock twice: once as the # real Vivado clk_wiz output and once as the xdma_ep black-box boundary output. # Treating those as separate timed clocks creates large false hold violations diff --git a/fpga_diff/uvhs/backend_run.tcl b/fpga_diff/uvhs/backend_run.tcl index 046a53ff..1fd94f2d 100644 --- a/fpga_diff/uvhs/backend_run.tcl +++ b/fpga_diff/uvhs/backend_run.tcl @@ -485,6 +485,69 @@ proc patch_vivado_wrappers {} { } } +proc uvhs_guard_vendor_ip_false_paths {pre_opt} { + set fh [open $pre_opt r] + set data [read $fh] + close $fh + + # The platform template can emit an IP-internal PCIe reset exception for + # every FPGA even when that partition contains no PCIe instance. Vivado + # treats set_false_path with an empty object set as an error. Guard only + # these vendor-IP targets; do not weaken fpga_diff-owned RTL constraints. + set uvhs_lines {} + set uvhs_guarded_ip_false_paths 0 + foreach uvhs_line [split $data "\n"] { + if {[regexp {^[[:space:]]*set_false_path[[:space:]]+-to[[:space:]]+\[get_pins[[:space:]]+([^]]*u_pcie_wrapper[^]]*)\][[:space:]]*$} \ + $uvhs_line -> uvhs_ip_pin_pattern]} { + lappend uvhs_lines [format {set uvhs_ip_false_path_pins [get_pins -quiet {%s}]} $uvhs_ip_pin_pattern] + lappend uvhs_lines {if {[llength $uvhs_ip_false_path_pins]} { + set_false_path -to $uvhs_ip_false_path_pins + puts "INFO: UVHS patch: applied vendor PCIe IP reset false path to $uvhs_ip_false_path_pins" +} else { + puts "INFO: UVHS patch: skip absent vendor PCIe IP reset false-path target" +}} + incr uvhs_guarded_ip_false_paths + } else { + lappend uvhs_lines $uvhs_line + } + } + if {$uvhs_guarded_ip_false_paths} { + set fh [open $pre_opt w] + puts -nonewline $fh [join $uvhs_lines "\n"] + close $fh + puts "INFO: guarded $uvhs_guarded_ip_false_paths vendor PCIe IP false path(s): $pre_opt" + } +} + +proc uvhs_guard_vendor_pcie_pblock {pre_place} { + set fh [open $pre_place r] + set data [read $fh] + close $fh + + set uvhs_get_cells {[get_cells [list part_3/xs_core_def/u_pcie_wrapper/inst/u_xilinx_pcie_phy_x4]]} + if {[string first $uvhs_get_cells $data] < 0 || + [string first {set uvhs_vendor_pcie_cells [get_cells -quiet} $data] >= 0} { + return + } + set uvhs_patched $data + set uvhs_lines {} + set uvhs_vendor_if_line [format {if {$current_fpga == "b0.f3"} %s} "\{"] + foreach uvhs_line [split $uvhs_patched "\n"] { + if {[string trim $uvhs_line] eq $uvhs_vendor_if_line} { + lappend uvhs_lines {set uvhs_vendor_pcie_cells [get_cells -quiet {part_3/xs_core_def/u_pcie_wrapper/inst/u_xilinx_pcie_phy_x4}]} + lappend uvhs_lines [format {if {$current_fpga == "b0.f3" && [llength $uvhs_vendor_pcie_cells]} %s} "\{"] + } else { + lappend uvhs_lines [string map \ + [list $uvhs_get_cells {$uvhs_vendor_pcie_cells}] $uvhs_line] + } + } + set uvhs_patched [join $uvhs_lines "\n"] + set fh [open $pre_place w] + puts -nonewline $fh $uvhs_patched + close $fh + puts "INFO: guarded absent vendor PCIe IP pblock target: $pre_place" +} + proc patch_vivado_pre_opt {} { set write_xdc_patch_script [uvhs_write_xdc_patch_script] set xdma_cdc_script { @@ -494,9 +557,13 @@ if {[file exists $uvhs_xdma_cdc_script]} { source $uvhs_xdma_cdc_script } else { puts "WARNING: UVHS patch: missing XDMA CDC attribute script $uvhs_xdma_cdc_script" -} + } } set async_script [uvhs_async_clock_patch_script] + foreach platform_pre_opt [glob -nocomplain \ + hw.dat/Compile/PnR/*/*/user_stage_tcl/preOpt_pre_opt.tcl] { + uvhs_guard_vendor_ip_false_paths $platform_pre_opt + } foreach pre_opt [uvhs_vivado_stage_hook_files pre_opt] { set fh [open $pre_opt r] set data [read $fh] @@ -568,6 +635,10 @@ if {[llength $uvhs_ddr_ck_ports]} { } append script "\n" $refclk_patch_script append script [uvhs_async_clock_patch_script] + foreach platform_pre_place [glob -nocomplain \ + hw.dat/Compile/PnR/*/*/user_stage_tcl/prePlace_pre_place.tcl] { + uvhs_guard_vendor_pcie_pblock $platform_pre_place + } foreach pre_place [uvhs_vivado_stage_hook_files pre_place] { set fh [open $pre_place r] set data [read $fh] @@ -951,6 +1022,19 @@ create_generated_clock_if_bufgce_exists RTC_GATED_CLK TMCLK { } create_blackbox_output_clock_if_pin_exists XDMA_AXI_ACLK [uvhs_xdma_axi_clk_period_ns] core_def/xdma_ep_i/TO_DIFFTEST_PCIE_CLK create_ddr_ui_clock_if_exists +foreach uvhs_localize_clock [split [env_or_default UVHS_LOCALIZE_CLOCKS ""]] { + set uvhs_localize_clock [string trim $uvhs_localize_clock] + if {$uvhs_localize_clock eq ""} { + continue + } + set uvhs_localize_clock_obj [get_clocks -quiet $uvhs_localize_clock] + if {[llength $uvhs_localize_clock_obj] != 1} { + error "UVHS_LOCALIZE_CLOCKS entry must resolve to exactly one clock, got [llength $uvhs_localize_clock_obj]: '$uvhs_localize_clock'" + } + puts "INFO: localize cross-FPGA clock network: $uvhs_localize_clock_obj" + config_clock -localize $uvhs_localize_clock_obj +} +config_clock -check run_or_warn "infer_clock" {infer_clock} run_or_warn "report_clock -inferred" {report_clock -inferred} source_if_exists [file normalize [file join [file dirname [info script]] async_clocks.tcl]] diff --git a/fpga_diff/uvhs/check_flow_tools.sh b/fpga_diff/uvhs/check_flow_tools.sh index 6af2d30e..1b62e46c 100755 --- a/fpga_diff/uvhs/check_flow_tools.sh +++ b/fpga_diff/uvhs/check_flow_tools.sh @@ -25,9 +25,12 @@ for path in \ uvhs/Makefile uvhs/flow.md uvhs/setenv.sh uvhs/setenv.local.example.sh \ uvhs/hejian_pcie_x4_env.sh uvhs/frontend_run.tcl uvhs/backend_run.tcl \ uvhs/assemble_uvhs.tcl uvhs/assign_pin_nutshell_f2.tcl uvhs/async_clocks.tcl \ + uvhs/probe_template.tcl uvhs/partition_capture_ddr_f3.tcl \ uvhs/uvhs_preflight_status.sh uvhs/uvhs_tagged_runtime.sh \ + uvhs/uvhs_queue_uhd_capture.sh \ uvhs/tools/build/patch_nutshell_cdc.sh \ - user_script/hw_run_download.tcl \ + user_script/hw_run_download.tcl user_script/hw_capture_uhd.tcl \ + user_script/uhd_c2h.ini \ tools/report_post_route_cdc.tcl tools/report_pcie_route_evidence.tcl \ src/rtl/common/core_def_xdma.sv src/rtl/common/uvhs_axi64_to_axi256.sv \ src/rtl/common/uvhs_axilite_cdc_bridge.sv src/rtl/common/uvhs_blackbox_stubs.v \ @@ -78,6 +81,39 @@ fi echo "## Safety defaults" require_text uvhs/uvhs_tagged_runtime.sh 'UVHS_RUN_TAG:?set a unique UVHS_RUN_TAG' +require_text uvhs/uvhs_queue_uhd_capture.sh 'UVHS_RUN_TAG:?set the exact active UVHS_RUN_TAG' +require_text uvhs/probe_template.tcl 'trigger_net -add -group $group -clock $clock -signal $signals' +require_text uvhs/partition_capture_ddr_f3.tcl 'get_cells core_def/U_UVHS_UVW_AXI4_TO_DDR4' +require_text user_script/uhd_c2h.ini 'difftest_to_host_axis_tvalid_io = 1' +require_text uvhs/Makefile 'UVHS_PROBE_FILE ?= $(ROOT_DIR)/uvhs/probe_template.tcl' +require_text uvhs/Makefile 'UVHS_ENABLE_PROBE_NET ?= 0' +require_text uvhs/Makefile 'uvhs_hejian_pcie_x4_nutshell_probe_all: UVHS_FPGA_COUNT = 2' +require_text uvhs/Makefile 'uvhs_hejian_pcie_x4_nutshell_probe_all: UVHS_KEEP_FPGAS = b0.f2 b0.f3' +require_text uvhs/Makefile 'uvhs_hejian_pcie_x4_nutshell_probe_all: UVHS_PARTITION_FILE = $(ROOT_DIR)/uvhs/partition_capture_ddr_f3.tcl' +require_text uvhs/Makefile 'uvhs_hejian_pcie_x4_nutshell_probe_all: UVHS_LOCALIZE_CLOCKS = SOC_GATED_CLK' +require_text uvhs/frontend_run.tcl 'enabled UVHS probe file is missing or empty' + +if git -C "$root_dir" grep -En 'UVHS_UHD_CAPTURE_DDR|pddr4dme_f2_uhd_inst' -- \ + uvhs/Makefile uvhs/assemble_uvhs.tcl; then + fail "unverified UHD capture daughter-card overlay remains" +fi + +if command -v tclsh >/dev/null 2>&1; then + if ! UVHS_ENABLE_PROBE_NET=1 UVHS_ENABLE_TRIGGER_NET=1 \ + tclsh /dev/stdin "$root_dir/uvhs/probe_template.tcl" <<'TCL' +set probe_calls {} +set trigger_calls {} +proc probe_net {args} { lappend ::probe_calls $args } +proc trigger_net {args} { lappend ::trigger_calls $args } +source [lindex $argv 0] +if {[llength $probe_calls] != 15 || [llength $trigger_calls] != 2} { + error "standard probe template expected 15 probes and 2 trigger groups; got [llength $probe_calls] and [llength $trigger_calls]" +} +TCL + then + fail "standard probe/trigger template behavior" + fi +fi if git -C "$root_dir" grep -En '/home/(user01|data/test/(fengkehan|codex))' -- uvhs; then fail "personal or historical runtime path remains in UVHS tools" fi diff --git a/fpga_diff/uvhs/flow.md b/fpga_diff/uvhs/flow.md index b8b22326..8bd476bc 100644 --- a/fpga_diff/uvhs/flow.md +++ b/fpga_diff/uvhs/flow.md @@ -78,6 +78,7 @@ program a board, rescan PCIe, write BARs, load a driver, or reboot a host. | `uvhs/frontend_run.tcl` | Build the UVHS frontend database. | | `uvhs/backend_run.tcl` | Partition, implement, route, and generate the bitstream. | | `uvhs/assemble_uvhs.tcl`, `uvhs/partition.tcl` | Apply the selected B0/F2 hardware assembly and partition policy. | +| `uvhs/partition_capture_ddr_f3.tcl` | Move the complete functional DDR IP to F3 for the validated two-FPGA UHD build. | | `uvhs/assign_pin_nutshell_f2.tcl`, `uvhs/async_clocks.tcl`, `uvhs/timing_common.tcl` | NutShell pin and clock constraints. | | `uvhs/patch_uvsyn_shell.sh`, `uvhs/uv_shell_exec_compat.sh`, `uvhs/make_compat/` | Versioned UVHS compatibility helpers. | | `uvhs/tools/build/patch_nutshell_cdc.sh` | Apply the required idempotent CDC fix to generated NutShell RTL. | @@ -99,17 +100,20 @@ program a board, rescan PCIe, write BARs, load a driver, or reboot a host. | `tools/report_post_route_cdc.tcl` | Routed CDC report and owned-vs-IP classification evidence. | | `tools/report_pcie_route_evidence.tcl` | Routed XDMA link, BAR, clock, and connectivity evidence. | | `uvhs/status.sh`, `uvhs/uvhs_preflight_status.sh` | Read-only build/runtime status summaries. | +| `uvhs/probe_template.tcl` | The single standard 15-signal, two-trigger-group UHD instrumentation definition. | ### Runtime programming and workload | File | Purpose | | --- | --- | | `user_script/hw_run_download.tcl` | Load DB, program, update clocks/link, initialize, release resets, and keep the runtime attached. | +| `user_script/hw_capture_uhd.tcl`, `user_script/uhd_c2h.ini` | Arm the C2H condition, upload UHD capture data, and generate `UvData.usdb`. | | `uvhs/uvhs_tagged_runtime.sh` | Exact-tag tmux start, readiness evidence, status, and cleanup. | +| `uvhs/uvhs_queue_uhd_capture.sh` | Queue the standard capture fragment into the exact tagged runtime. | -The final `fpga-host` path writes the workload to DDR through H2C. Local probe, -BAR, XDMA, strace, recovery, and backdoor-DDR scripts are intentionally not -tracked in this repository. +The final `fpga-host` path writes the workload to DDR through H2C. Specialized +CPU probes, BAR, XDMA, strace, recovery, and backdoor-DDR scripts remain local; +only the CPU-independent standard UHD path above is tracked. ## Build @@ -160,6 +164,60 @@ Do not hide an owned CDC issue with a false path. Stop before board programming if owned Critical CDC is nonzero. Keep the generated warning log: warnings are part of signoff evidence and must not be disabled globally. +## Probe And Trigger Template + +`probe_template.tcl` is the standard CPU-independent Hejian UHD template. It +adds two clock-domain-specific groups: + +- `uvhs_control` samples reset, host control, and XDMA link state on `sys_clk_i`. +- `uvhs_c2h` samples DiffTest startup and C2H AXI-Stream handshakes on + `difftest_pcie_clock`. + +The template is sourced by default but does nothing unless +`UVHS_ENABLE_PROBE_NET=1`. `UVHS_ENABLE_TRIGGER_NET=1` declares the same signals +as runtime-configurable trigger groups; it does not select a comparison value or +arm capture during the build. It does not change CPU/SoC RTL or the functional +AXI-to-DDR RTL connection. After verifying physical PDDR4DME cards at F2/FMC3 +and F3/FMC3, use the validated two-FPGA target: + +```bash +make uvhs_hejian_pcie_x4_nutshell_probe_all \ + CORE_DIR=/path/to/NutShell SUFFIX= +``` + +This target keeps F2/F3, places the complete functional AXI-to-DDR IP on F3, +leaves the single UHD probe/capture implementation and its capture DDR on F2, +and localizes `SOC_GATED_CLK` for the cross-FPGA memory path. + +Set `UVHS_ENABLE_TRIGGER_NET=0` to retain the probes without trigger groups. A +CPU-specific local template can replace the standard one with +`UVHS_PROBE_FILE=/path/to/local/probe.tcl`. + +The probe/trigger template is the only signal-instrumentation flow. The vendor +UHD capture controller still requires capture storage; it is not an alternative +to `probe_net` and does not save LUT. A waveform build must verify that the +backend binding report and physical board inventory provide usable memory for +both UHD capture and the functional `uvw_axi4_to_ddr4` IP. Do not synthesize a +daughter-card declaration for an unverified physical connector. + +After the tagged runtime is ready, queue the standard C2H condition capture and +then start the normal H2C `fpga-host` flow: + +```bash +export UVHS_RUN_TAG= +export UVHS_STAGE_DIR=/path/containing/ +bash "$UVHS_STAGE_DIR/uvhs_queue_uhd_capture.sh" +# Wait for UVHS_UHD_CAPTURE_ARMED in the runtime log, then start fpga-host. +``` + +The queued fragment uses `uhd_c2h.ini`, waits for +`difftest_to_host_axis_tvalid_io=1`, runs `upload_uhd`, and converts the same +capture with `wavegen` to `UHD//UvData.usdb`. This runtime layer consumes +the 15 probes and two trigger groups already built by `probe_template.tcl`; it +does not add a second instrumentation or capture-DDR path. The UVHS 2025.06.P4 +runtime may reject `query -capture -sgfile` with `RTM-103`, so the validated +flow uses plain `query -capture` and the generated `VisualUsdb_Cap.wcf`. + ## Runtime Programming Stage the build database and helpers on the runtime host under a path containing diff --git a/fpga_diff/uvhs/frontend_run.tcl b/fpga_diff/uvhs/frontend_run.tcl index ec06e853..aa420d2c 100644 --- a/fpga_diff/uvhs/frontend_run.tcl +++ b/fpga_diff/uvhs/frontend_run.tcl @@ -230,7 +230,20 @@ set skip_board_constraints [env_or_default UVHS_SKIP_BOARD_CONSTRAINTS 0] if {$skip_board_constraints} { puts "INFO: skip board probe/pin/timing/partition constraints" } else { - source_if_exists [env_or_default UVHS_PROBE_FILE ./script/probe.tcl] + set probe_file [env_or_default UVHS_PROBE_FILE ./script/probe.tcl] + set enable_probe_net [env_or_default UVHS_ENABLE_PROBE_NET 0] + if {$enable_probe_net ni {0 1}} { + error "UVHS_ENABLE_PROBE_NET must be 0 or 1, got '$enable_probe_net'" + } + if {$enable_probe_net} { + if {![file exists $probe_file] || [file size $probe_file] == 0} { + error "enabled UVHS probe file is missing or empty: $probe_file" + } + puts "INFO: source required UVHS probe file $probe_file" + source $probe_file + } else { + source_if_exists $probe_file + } source_if_exists [env_or_default UVHS_ASSIGN_PIN_FILE ./script/assign_pin.tcl] set timing_file [env_or_default UVHS_TIMING_FILE none] if {$timing_file eq "none"} { diff --git a/fpga_diff/uvhs/partition_capture_ddr_f3.tcl b/fpga_diff/uvhs/partition_capture_ddr_f3.tcl new file mode 100644 index 00000000..dbe693ed --- /dev/null +++ b/fpga_diff/uvhs/partition_capture_ddr_f3.tcl @@ -0,0 +1,11 @@ +# Dedicated two-FPGA partition for the standard UVHS waveform build. +# +# F2 owns the CPU, XDMA, UART, and the single probe/trigger instrumentation +# path. Hejian UHD therefore binds its capture backend to the physical +# PDDR4DME on F2/FMC3. Move only the complete business AXI-to-DDR black box +# to F3, where it can bind the independent physical PDDR4DME on F3/FMC3. +# The partition boundary is outside the SoC DDR implementation and does not +# alter the functional AXI interface or RTL connectivity. +create_fpga -name b0.f3 \ + -cells [get_cells core_def/U_UVHS_UVW_AXI4_TO_DDR4] \ + -update diff --git a/fpga_diff/uvhs/probe_template.tcl b/fpga_diff/uvhs/probe_template.tcl new file mode 100644 index 00000000..c21db40f --- /dev/null +++ b/fpga_diff/uvhs/probe_template.tcl @@ -0,0 +1,91 @@ +# Standard CPU-independent probe and trigger template for fpga_diff UVHS builds. +# Enabling probe_net instantiates the Hejian UHD capture path, so use this only +# for a dedicated waveform bitstream with the required capture DDR available. +# trigger_net declares runtime-configurable trigger signals; it does not hardcode +# a trigger condition or arm capture during the build. + +proc uvhs_probe_env_bool {name default} { + if {[info exists ::env($name)] && $::env($name) ne ""} { + set value $::env($name) + } else { + set value $default + } + if {$value ni {0 1}} { + error "$name must be 0 or 1, got '$value'" + } + return $value +} + +proc uvhs_add_probe_group {clock group signals} { + set added 0 + foreach signal $signals { + if {[catch {probe_net -clock $clock -add [list $signal]} err]} { + puts "WARNING: UVHS probe template: skip probe $signal: $err" + } else { + incr added + } + } + puts "INFO: UVHS probe template: group=$group probes=$added clock=$clock" + if {$added != [llength $signals]} { + error "UVHS probe template: group $group added $added of [llength $signals] required probes" + } + return $added +} + +proc uvhs_add_trigger_group {clock group signals} { + if {[catch { + trigger_net -add -group $group -clock $clock -signal $signals + } err]} { + error "UVHS probe template: failed to add trigger group $group: $err" + } + puts "INFO: UVHS probe template: trigger group=$group signals=[llength $signals] clock=$clock" + return 1 +} + +if {![uvhs_probe_env_bool UVHS_ENABLE_PROBE_NET 0]} { + puts "INFO: UVHS probe template disabled; set UVHS_ENABLE_PROBE_NET=1 for a waveform build" + return +} + +set uvhs_probe_top fpga_top_debug +if {[info exists ::env(UVHS_PROBE_TOP)] && $::env(UVHS_PROBE_TOP) ne ""} { + set uvhs_probe_top $::env(UVHS_PROBE_TOP) +} +set uvhs_probe_core ${uvhs_probe_top}.core_def +set uvhs_sys_clock ${uvhs_probe_core}.sys_clk_i +set uvhs_pcie_clock ${uvhs_probe_core}.difftest_pcie_clock + +set uvhs_control_signals [list \ + ${uvhs_probe_core}.sys_rstn_io \ + ${uvhs_probe_core}.cpu_rstn_io \ + ${uvhs_probe_core}.io_host_reset \ + ${uvhs_probe_core}.io_host_diff_enable \ + ${uvhs_probe_core}.xdma_link_up] + +set uvhs_c2h_signals [list \ + ${uvhs_probe_core}.cpu_rstn_pcie \ + ${uvhs_probe_core}.io_host_diff_enable_pcie \ + ${uvhs_probe_core}.xdma_link_up_pcie \ + ${uvhs_probe_core}.difftest_startup_ready_pcie \ + ${uvhs_probe_core}.difftest_startup_done_pcie \ + ${uvhs_probe_core}.difftest_stream_enable_pcie \ + ${uvhs_probe_core}.difftest_c2h_rstn \ + ${uvhs_probe_core}.difftest_to_host_axis_tready_io \ + ${uvhs_probe_core}.difftest_to_host_axis_tvalid_io \ + ${uvhs_probe_core}.difftest_to_host_axis_tlast] + +set uvhs_probe_count 0 +incr uvhs_probe_count [uvhs_add_probe_group \ + $uvhs_sys_clock uvhs_control $uvhs_control_signals] +incr uvhs_probe_count [uvhs_add_probe_group \ + $uvhs_pcie_clock uvhs_c2h $uvhs_c2h_signals] +if {$uvhs_probe_count == 0} { + error "UVHS probe template did not add any probes; check UVHS_PROBE_TOP and RTL hierarchy" +} + +if {[uvhs_probe_env_bool UVHS_ENABLE_TRIGGER_NET 1]} { + uvhs_add_trigger_group $uvhs_sys_clock uvhs_control $uvhs_control_signals + uvhs_add_trigger_group $uvhs_pcie_clock uvhs_c2h $uvhs_c2h_signals +} else { + puts "INFO: UVHS probe template: trigger groups disabled by UVHS_ENABLE_TRIGGER_NET=0" +} diff --git a/fpga_diff/uvhs/uvhs_queue_uhd_capture.sh b/fpga_diff/uvhs/uvhs_queue_uhd_capture.sh new file mode 100755 index 00000000..ca6d821c --- /dev/null +++ b/fpga_diff/uvhs/uvhs_queue_uhd_capture.sh @@ -0,0 +1,64 @@ +#!/usr/bin/env bash +set -euo pipefail + +TAG="${UVHS_RUN_TAG:?set the exact active UVHS_RUN_TAG}" +STAGE="${UVHS_STAGE_DIR:?set the exact active UVHS_STAGE_DIR}" +COMMAND_FILE="${UVHS_COMMAND_FILE:-$STAGE/commands/${TAG}.command.tcl}" +CAPTURE_SCRIPT="${UVHS_UHD_CAPTURE_SCRIPT:-$STAGE/user_script/hw_capture_uhd.tcl}" +TRIGGER_INI="${UVHS_UHD_TRIGGER_INI:-$STAGE/user_script/uhd_c2h.ini}" +WORKDIR="${UVHS_RUNTIME_WORKDIR:-$STAGE/workdir/$TAG}" +OUTPUT="${UVHS_UHD_OUTPUT:-uvhs_c2h_capture}" +DEPTH="${UVHS_UHD_CAPTURE_DEPTH:-20000}" +POSITION="${UVHS_UHD_TRIGGER_POSITION:-50}" +WAIT_SEC="${UVHS_UHD_TRIGGER_WAIT_SEC:-420}" + +die() { + echo "ERROR: $*" >&2 + exit 1 +} + +case "$TAG" in + ''|*[!A-Za-z0-9_.-]*) die "invalid UVHS_RUN_TAG: $TAG" ;; +esac +for item in "UVHS_STAGE_DIR:$STAGE" "UVHS_COMMAND_FILE:$COMMAND_FILE" "UVHS_RUNTIME_WORKDIR:$WORKDIR"; do + name="${item%%:*}" + value="${item#*:}" + case "$value" in + *"$TAG"*) ;; + *) die "$name must contain the exact tag '$TAG': $value" ;; + esac +done +case "$OUTPUT" in + ''|.|..|*[!A-Za-z0-9_.-]*) die "invalid UVHS_UHD_OUTPUT: $OUTPUT" ;; +esac +for item in "UVHS_UHD_CAPTURE_DEPTH:$DEPTH" "UVHS_UHD_TRIGGER_POSITION:$POSITION" "UVHS_UHD_TRIGGER_WAIT_SEC:$WAIT_SEC"; do + name="${item%%:*}" + value="${item#*:}" + case "$value" in + ''|*[!0-9]*|0*) die "$name must be a positive integer" ;; + esac +done +test -f "$CAPTURE_SCRIPT" || die "capture script not found: $CAPTURE_SCRIPT" +test -f "$TRIGGER_INI" || die "trigger condition not found: $TRIGGER_INI" +if [ -e "$COMMAND_FILE" ] || [ -e "$COMMAND_FILE.running" ]; then + die "runtime command is already queued or running: $COMMAND_FILE" +fi + +mkdir -p "$(dirname "$COMMAND_FILE")" +tmp="${COMMAND_FILE}.tmp.$$" +trap 'rm -f "$tmp"' EXIT +printf 'set ::env(UVHS_RUNTIME_WORKDIR) {%s}\n' "$WORKDIR" >"$tmp" +printf 'set ::env(UVHS_UHD_TRIGGER_INI) {%s}\n' "$TRIGGER_INI" >>"$tmp" +printf 'set ::env(UVHS_UHD_OUTPUT) {%s}\n' "$OUTPUT" >>"$tmp" +printf 'set ::env(UVHS_UHD_CAPTURE_DEPTH) {%s}\n' "$DEPTH" >>"$tmp" +printf 'set ::env(UVHS_UHD_TRIGGER_POSITION) {%s}\n' "$POSITION" >>"$tmp" +printf 'set ::env(UVHS_UHD_TRIGGER_WAIT_SEC) {%s}\n' "$WAIT_SEC" >>"$tmp" +printf 'source {%s}\n' "$CAPTURE_SCRIPT" >>"$tmp" +mv -n "$tmp" "$COMMAND_FILE" +if [ -e "$tmp" ]; then + die "command file appeared concurrently; capture was not queued" +fi +trap - EXIT +echo "queued_tag=$TAG" +echo "queued_command=$COMMAND_FILE" +echo "capture_output=$WORKDIR/UHD/$OUTPUT" diff --git a/fpga_diff/uvhs/uvhs_tagged_runtime.sh b/fpga_diff/uvhs/uvhs_tagged_runtime.sh index e48684da..a8d4f400 100755 --- a/fpga_diff/uvhs/uvhs_tagged_runtime.sh +++ b/fpga_diff/uvhs/uvhs_tagged_runtime.sh @@ -136,7 +136,8 @@ wait_ready_runtime() { while [ "$(date +%s)" -lt "$deadline" ]; do if [ -f "$LOG" ]; then - error_match="$(last_log_match '(^|[[:space:]])ERROR:|[[]RTM-[0-9]+[]].*ERROR|download.*fail|initializ(e|ation).*fail|Load_DB.*fail')" + error_match="$(last_log_match '(^|[[:space:]])ERROR:|[[]RTM-[0-9]+[]].*ERROR|download.*fail|initializ(e|ation).*fail|Load_DB.*fail' \ + | grep -v 'RTM-103.*Query get invalid parameter, clock has no sign-off frequency set' || true)" if [ -n "$error_match" ]; then echo "runtime_ready=0" emit_log_evidence runtime_error "$error_match" @@ -219,6 +220,10 @@ set -euo pipefail export UVHS_DB_PATH=$(printf '%q' "$DB_PATH") export UVHS_COMMAND_FILE=$(printf '%q' "$COMMAND_FILE") export UVHS_DDR_RTL=$(printf '%q' "$DDR_RTL") +export UVHS_STAGE_DIR=$(printf '%q' "$STAGE") +export UVHS_RUNTIME_WORKDIR=$(printf '%q' "$WORKDIR") +$(if [ -n "${UVHS_CLOCK_INDEX:-}" ]; then printf 'export UVHS_CLOCK_INDEX=%q' "$UVHS_CLOCK_INDEX"; fi) +$(if [ -n "${UVHS_CLOCK_FREQUENCY_HZ:-}" ]; then printf 'export UVHS_CLOCK_FREQUENCY_HZ=%q' "$UVHS_CLOCK_FREQUENCY_HZ"; fi) cd $(printf '%q' "$STAGE") exec $(printf '%q' "$UV_SHELL") -rt_shell -workdir $(printf '%q' "$WORKDIR") -script $(printf '%q' "$DOWNLOAD_SCRIPT") EOF