diff --git a/fpga_diff/Makefile b/fpga_diff/Makefile index 1ec5a74..c319e51 100755 --- a/fpga_diff/Makefile +++ b/fpga_diff/Makefile @@ -12,6 +12,11 @@ NO_DIFF ?= 0 XDMA_LINK_WIDTH ?= X4 VIVADO_JOBS ?= export XDMA_LINK_WIDTH VIVADO_JOBS NO_DIFF +DIFFTEST_HOSTIF ?= XDMA +ifeq ($(filter XDMA GBUS,$(DIFFTEST_HOSTIF)),) +$(error DIFFTEST_HOSTIF must be XDMA or GBUS, got $(DIFFTEST_HOSTIF)) +endif +export DIFFTEST_HOSTIF FPGA_RUNTIME ?= REMOTE_ENV ?= source ~/.bash_profile && BIND_UART ?= 1 diff --git a/fpga_diff/README.md b/fpga_diff/README.md index b2304b0..a45a12d 100755 --- a/fpga_diff/README.md +++ b/fpga_diff/README.md @@ -1,6 +1,10 @@ Core RTL to FPGA Steps ====================== +For UVHS GBus builds, see [the SRAM C2H interface and build flow](uvhs/README.md). +`DIFFTEST_HOSTIF=GBUS` keeps DiffTest output in the GBS1 on-chip SRAM window. +Workload H2C enters through the existing CPU-subsystem DMA AXI interface. + 1. modify Makefile, assign CORE_DIR 2. make vivado CPU=XXX diff --git a/fpga_diff/src/rtl/common/core_def_xdma.sv b/fpga_diff/src/rtl/common/core_def_xdma.sv index 6e1d564..102adb6 100755 --- a/fpga_diff/src/rtl/common/core_def_xdma.sv +++ b/fpga_diff/src/rtl/common/core_def_xdma.sv @@ -1236,6 +1236,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; @@ -1249,30 +1267,62 @@ wire [0:0] br2cfg_wvalid; wire [`CONFIG_DIFFTEST_HOST_AXIS_BYTES-1:0] difftest_from_host_axis_tkeep; wire difftest_from_host_axis_tlast; wire difftest_clock_enable; - wire difftest_clock_gate_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; +`ifdef CONFIG_DIFFTEST_HOSTIF_GBUS + // GBus has no XDMA user-clock output. The DiffTest host-side protocol and + // the UVHS GeneralBD/GENERALBUS endpoints are synchronous to the always-on + // GBus host/user interface clock: use the real always-running UVHS host + // clock (dev_clk_i/clk6_p), not the gated CPU clock or infer_clock output. + wire gbus_host_clk = dev_clk_i; + assign difftest_pcie_clock = gbus_host_clk; +`endif + 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_clock_gate_enable = - difftest_clock_enable || ~io_host_diff_enable || - ~sys_rstn_io || ~cpu_rstn_io; + assign difftest_c2h_rstn = cpu_rstn_pcie & difftest_stream_enable_pcie; +`ifdef CONFIG_DIFFTEST_HOSTIF_GBUS + // GBus has no PCIe training state. Expose a logical ready indication to + // the shared control/CDC gates; XDMA retains the physical link status. + assign pcie_ep_lnk_up = 1'b1; +`else + assign pcie_ep_lnk_up = pcie_lnk_sync[1]; +`endif + + always @(posedge sys_clk_i) begin + if (!sys_rstn) cpu_rstn_pcie_src <= 1'b0; + else cpu_rstn_pcie_src <= cpu_rstn_io; + end + + 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_raw}; + end +`ifdef CONFIG_DIFFTEST_HOSTIF_GBUS + wire xdma_link_up = 1'b1; +`else + wire xdma_link_up = pcie_lnk_sync[1]; +`endif RST_SYNC #( .SYNC_STAGES(3), @@ -1280,7 +1330,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) ); @@ -1294,9 +1344,21 @@ wire [0:0] br2cfg_wvalid; .sync_out (io_host_diff_enable_pcie) ); - // Host enables DiffTest only after XDMA is accessible. Do not feed the - // physical XDMA link-status output back into fabric logic. +`ifdef CONFIG_DIFFTEST_HOSTIF_GBUS assign difftest_startup_ready_pcie = cpu_rstn_pcie & io_host_diff_enable_pcie; +`else + RST_SYNC #( + .SYNC_STAGES(3), + .PIPELINE_STAGES(1), + .INIT(1'b0) + ) difftest_link_up_pcie_sync ( + .clk (difftest_pcie_clock), + .async_in (xdma_link_up), + .sync_out (xdma_link_up_pcie) + ); + + assign difftest_startup_ready_pcie = cpu_rstn_pcie & io_host_diff_enable_pcie & xdma_link_up_pcie; +`endif assign difftest_startup_done_pcie = &difftest_startup_wait_pcie; always @(posedge difftest_pcie_clock) begin @@ -1313,20 +1375,225 @@ 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; +`ifdef CONFIG_DIFFTEST_HOSTIF_GBUS + // The GBus shell has no PCIe clock/link reset domain. Its compatibility + // clock is the always-running host clock, so the AXI-Lite bridge must be + // released from the normal fabric reset rather than a nonexistent PCIe + // training indication. Do not use sys_rstn_io here: that signal is + // intentionally masked by io_host_reset, and fpga-host asserts + // HOST_IO_CFG_RESET before issuing the remaining configuration writes. + // Using the masked signal would therefore hold this bridge in reset for the + // entire host initialization sequence, making successful GBus API calls + // disappear before they reach the DiffTest config block. + wire difftest_axil_cdc_s_resetn = sys_rstn; + wire difftest_axil_cdc_m_resetn = sys_rstn; +`else + wire difftest_axil_cdc_s_resetn = xdma_link_up_pcie; + wire difftest_axil_cdc_m_resetn = sys_rstn & xdma_link_up; +`endif + + 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) + ); + + 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; + // 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. +`ifdef CONFIG_DIFFTEST_HOSTIF_GBUS + // Keep the compatibility shell's CPU-side input on the 25-MHz SoC clock. + // Its legacy user-clock output is explicitly driven from host_clk below, + // so there is no clock loop and no reason to run the CPU-side shim at the + // 50-MHz GBus host rate. + assign xdma_cpu_clk = sys_clk_i; +`else + assign xdma_cpu_clk = difftest_pcie_clock; +`endif + assign xdma_cpu_rstn = 1'b1; + +`ifdef CONFIG_DIFFTEST_HOSTIF_GBUS + wire gbus_cfg_wr_en; + wire [15:0] gbus_cfg_wr_addr; + wire [31:0] gbus_cfg_wdata; + wire gbus_cfg_rd_en; + wire [15:0] gbus_cfg_rd_addr; + wire [31:0] gbus_cfg_rdata; + wire gbus_cfg_rdata_vld; + wire [15:0] gbus_cfg_local_wr_addr; + wire [15:0] gbus_cfg_local_rd_addr; + wire [31:0] gbus_c2h_cfg_rdata; + wire gbus_c2h_cfg_rdata_vld; + wire [31:0] gbus_axil_cfg_rdata; + wire gbus_axil_cfg_rdata_vld; + wire [7:0] gbus_axi_awid; + wire [31:0] gbus_axi_awaddr; + wire [3:0] gbus_axi_awlen; + wire [2:0] gbus_axi_awsize; + wire [1:0] gbus_axi_awburst, gbus_axi_awlock; + wire [3:0] gbus_axi_awcache, gbus_axi_awqos; + wire [2:0] gbus_axi_awprot; + wire gbus_axi_awvalid, gbus_axi_awready; + wire [7:0] gbus_axi_wid; + wire [255:0] gbus_axi_wdata; + wire [31:0] gbus_axi_wstrb; + wire gbus_axi_wlast, gbus_axi_wvalid, gbus_axi_wready; + wire [7:0] gbus_axi_bid; + wire [1:0] gbus_axi_bresp; + wire gbus_axi_bvalid, gbus_axi_bready; + wire [7:0] gbus_axi_arid; + wire [31:0] gbus_axi_araddr; + wire [3:0] gbus_axi_arlen; + wire [2:0] gbus_axi_arsize; + wire [1:0] gbus_axi_arburst, gbus_axi_arlock; + wire [3:0] gbus_axi_arcache, gbus_axi_arqos; + wire [2:0] gbus_axi_arprot; + wire gbus_axi_arvalid, gbus_axi_arready; + wire [7:0] gbus_axi_rid; + wire [255:0] gbus_axi_rdata; + wire [1:0] gbus_axi_rresp; + wire gbus_axi_rlast, gbus_axi_rvalid, gbus_axi_rready; + + wire [13:0] gbus_h2c_awid, gbus_h2c_arid; + wire [35:0] gbus_h2c_awaddr, gbus_h2c_araddr; + wire [7:0] gbus_h2c_awlen, gbus_h2c_arlen; + wire [2:0] gbus_h2c_awsize, gbus_h2c_arsize; + wire [1:0] gbus_h2c_awburst, gbus_h2c_arburst; + wire gbus_h2c_awlock, gbus_h2c_arlock; + wire [3:0] gbus_h2c_awcache, gbus_h2c_arcache, gbus_h2c_awqos, gbus_h2c_arqos; + wire [3:0] gbus_h2c_awregion, gbus_h2c_arregion; + wire [2:0] gbus_h2c_awprot, gbus_h2c_arprot; + wire gbus_h2c_awvalid, gbus_h2c_awready, gbus_h2c_wlast, gbus_h2c_wvalid, gbus_h2c_wready; + wire [255:0] gbus_h2c_wdata, gbus_h2c_rdata; + wire [31:0] gbus_h2c_wstrb; + wire [13:0] gbus_h2c_bid, gbus_h2c_rid; + wire [1:0] gbus_h2c_bresp, gbus_h2c_rresp; + wire gbus_h2c_bvalid, gbus_h2c_bready, gbus_h2c_arvalid, gbus_h2c_arready; + wire gbus_h2c_rlast, gbus_h2c_rvalid, gbus_h2c_rready; + + wire [13:0] gbus_dma_awid, gbus_dma_arid; + wire [35:0] gbus_dma_awaddr, gbus_dma_araddr; + wire [7:0] gbus_dma_awlen, gbus_dma_arlen; + wire [2:0] gbus_dma_awsize, gbus_dma_arsize; + wire [1:0] gbus_dma_awburst, gbus_dma_arburst; + wire gbus_dma_awlock, gbus_dma_arlock; + wire [3:0] gbus_dma_awcache, gbus_dma_arcache; + wire [3:0] gbus_dma_awqos, gbus_dma_arqos; + wire [3:0] gbus_dma_awregion, gbus_dma_arregion; + wire [2:0] gbus_dma_awprot, gbus_dma_arprot; + wire gbus_dma_awvalid, gbus_dma_awready; + wire [255:0] gbus_dma_wdata, gbus_dma_rdata; + wire [31:0] gbus_dma_wstrb; + wire gbus_dma_wlast, gbus_dma_wvalid, gbus_dma_wready; + wire [13:0] gbus_dma_bid, gbus_dma_rid; + wire [1:0] gbus_dma_bresp, gbus_dma_rresp; + wire gbus_dma_bvalid, gbus_dma_bready; + wire gbus_dma_arvalid, gbus_dma_arready; + wire gbus_dma_rlast, gbus_dma_rvalid, gbus_dma_rready; + wire gbus_shell_sready; + wire gbus_c2h_sready; + // The GENERALBD and GENERALBUS protected IPs share a 256-bit system-bus + // link. UVHS metadata identifies the endpoint type, but the tool does not + // infer this connection when the ports are left open; an open port is + // explicitly tied to GND during elaboration. Keep the link explicit so + // host commands and responses reach the GENERALBD endpoint. + wire [255:0] gbus_sysbus_to_generalbus; + wire [255:0] gbus_sysbus_to_generalbd; + // The endpoint shell remains only a clock/readiness shim in GBus mode. + // Actual GBus AXI and system-bus traffic is provided by the UVHS protected + // IPs below and is not presented as a PCIe endpoint. + uvhs_gbus_host_adapter xdma_ep_i( +`else 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), +`endif + .cpu_clk (xdma_cpu_clk), +`ifdef CONFIG_DIFFTEST_HOSTIF_GBUS + .host_clk (gbus_host_clk), +`endif + .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), +`ifdef CONFIG_DIFFTEST_HOSTIF_GBUS + .S00_AXIS_0_tready (gbus_shell_sready), +`else .S00_AXIS_0_tready (difftest_to_host_axis_tready_io), - .S00_AXIS_0_tvalid (difftest_to_host_axis_tvalid_io), +`endif + .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), +`ifdef CONFIG_DIFFTEST_HOSTIF_GBUS + // The compatibility shell is not the GBus register master. Leave its + // quiescent AXI-Lite pins disconnected; GENERALBD drives the real config + // path below. + .XDMA_AXI_LITE_awaddr (), .XDMA_AXI_LITE_awprot (), .XDMA_AXI_LITE_awvalid(), + .XDMA_AXI_LITE_awready(1'b0), .XDMA_AXI_LITE_wdata(), .XDMA_AXI_LITE_wstrb(), + .XDMA_AXI_LITE_wvalid (), .XDMA_AXI_LITE_wready(1'b0), + .XDMA_AXI_LITE_bresp (2'b0), .XDMA_AXI_LITE_bvalid(1'b0), .XDMA_AXI_LITE_bready(), + .XDMA_AXI_LITE_araddr (), .XDMA_AXI_LITE_arprot(), .XDMA_AXI_LITE_arvalid(), + .XDMA_AXI_LITE_arready(1'b0), .XDMA_AXI_LITE_rdata(32'b0), + .XDMA_AXI_LITE_rresp (2'b0), .XDMA_AXI_LITE_rvalid(1'b0), .XDMA_AXI_LITE_rready(), +`else .XDMA_AXI_LITE_awaddr (XDMA_AXI_LITE_awaddr), .XDMA_AXI_LITE_awprot (XDMA_AXI_LITE_awprot), .XDMA_AXI_LITE_awvalid(XDMA_AXI_LITE_awvalid), @@ -1346,47 +1613,424 @@ wire [0:0] br2cfg_wvalid; .XDMA_AXI_LITE_rresp (XDMA_AXI_LITE_rresp), .XDMA_AXI_LITE_rvalid (XDMA_AXI_LITE_rvalid), .XDMA_AXI_LITE_rready (XDMA_AXI_LITE_rready), +`endif +`ifdef CONFIG_DIFFTEST_HOSTIF_GBUS + // The GBus branch binds difftest_pcie_clock directly to the explicit + // gbus_host_clk above. Do not expose a pseudo XDMA clock output to UVHS + // clock inference; the compatibility shell has no physical user clock. + .TO_DIFFTEST_PCIE_CLK (), +`else .TO_DIFFTEST_PCIE_CLK (difftest_pcie_clock), +`endif .pci_exp_rxn(pci_ep_rxn), .pci_exp_rxp(pci_ep_rxp), .pci_exp_txn(pci_ep_txn), .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) ); +`ifdef CONFIG_DIFFTEST_HOSTIF_GBUS + // The shared DiffTest sender (`Difftest2AXIs` inside the generated SimTop -- + // the very module the XDMA build streams straight into the XDMA IP) is the + // C2H producer in both hostif modes. Only the interface layer below it + // differs. The XDMA build has no DDR staging and neither does this one: the + // same 256-bit stream is buffered in on-chip SRAM and exposed as a GBus + // register window. The C2H path therefore does not enter the CPU memory + // hierarchy or the physical DDR interface. + assign difftest_to_host_axis_tready_io = gbus_c2h_sready; + assign gbus_shell_sready = 1'b0; + + // libuvgbus addresses everything through a 0x1000 config window, so every + // enable below is decoded on the *windowed* address while the downstream + // blocks take the zero-based register offset. Do not mix the two: decoding + // an enable against the local offset compiles, elaborates, and then silently + // dead-ends the whole block. That is exactly how the C2H control write was + // lost -- upstream a raw 0x2204 was compared against the local range + // 0x1200..0x1204, so the fill never started and staged_words stayed 0 + // forever, while a status read only appeared to work because raw 0x2200 + // happened to hit a window branch and subtract back to the status offset. + // + // The two forms are equivalent for raw >= 0x1000 and both cannot alias for + // raw < 0x1000 (the subtraction wraps to >= 0xf000, outside every range), so + // decoding on the local address is safe as well as clearer. Declare these + // before the instances below; UVHS elaboration rejects forward references. + assign gbus_cfg_local_wr_addr = gbus_cfg_wr_addr - 16'h1000; + assign gbus_cfg_local_rd_addr = gbus_cfg_rd_addr - 16'h1000; + + wire gbus_c2h_cfg_wr_en = + gbus_cfg_wr_en && (gbus_cfg_local_wr_addr >= 16'h1200) && (gbus_cfg_local_wr_addr <= 16'h1208); + wire gbus_c2h_cfg_rd_en = + gbus_cfg_rd_en && + (((gbus_cfg_local_rd_addr >= 16'h1200) && (gbus_cfg_local_rd_addr <= 16'h1208)) || + ((gbus_cfg_local_rd_addr >= 16'h2000) && (gbus_cfg_local_rd_addr <= 16'h2ffc))); + wire gbus_axil_cfg_wr_en = gbus_cfg_wr_en && (gbus_cfg_local_wr_addr <= 16'h0030); + wire gbus_axil_cfg_rd_en = gbus_cfg_rd_en && (gbus_cfg_local_rd_addr <= 16'h0030); + + // The sender is the same Difftest2AXIs instance the XDMA build feeds to PCIe; + // only this interface layer differs, so nothing is staged in DDR. + uvhs_gbus_c2h_fifo #( + .AXIS_DATA_WIDTH(`CONFIG_DIFFTEST_HOST_AXIS_WIDTH) + ) U_GBUS_C2H_FIFO ( + .clk(gbus_host_clk), .rstn(rstn_sw4), + .stream_rstn(difftest_c2h_rstn), + .s_tdata(difftest_to_host_axis_tdata), .s_tkeep(difftest_to_host_axis_tkeep), + .s_tlast(difftest_to_host_axis_tlast), .s_tvalid(difftest_to_host_axis_tvalid_io), + .s_tready(gbus_c2h_sready), + .cfg_wr_en(gbus_c2h_cfg_wr_en), .cfg_wr_addr(gbus_cfg_local_wr_addr), + .cfg_wdata(gbus_cfg_wdata), .cfg_rd_en(gbus_c2h_cfg_rd_en), + .cfg_rd_addr(gbus_cfg_local_rd_addr), .cfg_rdata(gbus_c2h_cfg_rdata), + .cfg_rdata_vld(gbus_c2h_cfg_rdata_vld) + ); + + uvhs_generalbd_axilite_bridge U_GBUS_CONFIG_BRIDGE ( + .clk(gbus_host_clk), .rstn(rstn_sw4), + .gbd_wr_en(gbus_axil_cfg_wr_en), .gbd_wr_addr(gbus_cfg_local_wr_addr), + .gbd_wdata(gbus_cfg_wdata), .gbd_rd_en(gbus_axil_cfg_rd_en), + .gbd_rd_addr(gbus_cfg_local_rd_addr), .gbd_rdata(gbus_axil_cfg_rdata), + .gbd_rdata_vld(gbus_axil_cfg_rdata_vld), + .axil_awaddr(XDMA_AXI_LITE_awaddr), .axil_awvalid(XDMA_AXI_LITE_awvalid), + .axil_awready(XDMA_AXI_LITE_awready), .axil_wdata(XDMA_AXI_LITE_wdata), + .axil_wstrb(XDMA_AXI_LITE_wstrb), .axil_wvalid(XDMA_AXI_LITE_wvalid), + .axil_wready(XDMA_AXI_LITE_wready), .axil_bresp(XDMA_AXI_LITE_bresp), + .axil_bvalid(XDMA_AXI_LITE_bvalid), .axil_bready(XDMA_AXI_LITE_bready), + .axil_araddr(XDMA_AXI_LITE_araddr), .axil_arvalid(XDMA_AXI_LITE_arvalid), + .axil_arready(XDMA_AXI_LITE_arready), .axil_rdata(XDMA_AXI_LITE_rdata), + .axil_rresp(XDMA_AXI_LITE_rresp), .axil_rvalid(XDMA_AXI_LITE_rvalid), + .axil_rready(XDMA_AXI_LITE_rready), .h2c_active() + ); + assign XDMA_AXI_LITE_awprot = 3'b0; + assign XDMA_AXI_LITE_arprot = 3'b0; + assign gbus_cfg_rdata = gbus_axil_cfg_rdata_vld ? gbus_axil_cfg_rdata : gbus_c2h_cfg_rdata; + assign gbus_cfg_rdata_vld = gbus_axil_cfg_rdata_vld | gbus_c2h_cfg_rdata_vld; + + generalBD U_GBUS_GENERALBD ( + .i_clk (gbus_host_clk), + .i_rstn (rstn_sw4), + .i_clk_en (1'b1), + .o_wr_en (gbus_cfg_wr_en), + .o_wr_addr (gbus_cfg_wr_addr), + .o_wdata (gbus_cfg_wdata), + .o_rd_en (gbus_cfg_rd_en), + .o_rd_addr (gbus_cfg_rd_addr), + .i_rdata (gbus_cfg_rdata), + .i_rdata_vld (gbus_cfg_rdata_vld), + .gbd_sysbus_i (gbus_sysbus_to_generalbd), + .gbd_sysbus_o (gbus_sysbus_to_generalbus) + ); + + uvw_general_bus U_GBUS_GENERAL_BUS ( + .dut_axi_aclk (gbus_host_clk), + .dut_axi_aclk_en (1'b1), + .dut_axi_aresetn (rstn_sw4), + .dut_axi_awid (gbus_axi_awid), .dut_axi_awaddr(gbus_axi_awaddr), .dut_axi_awlen(gbus_axi_awlen), + .dut_axi_awsize (gbus_axi_awsize), .dut_axi_awburst(gbus_axi_awburst), .dut_axi_awlock(gbus_axi_awlock), + .dut_axi_awcache(gbus_axi_awcache), .dut_axi_awprot(gbus_axi_awprot), .dut_axi_awqos(gbus_axi_awqos), + .dut_axi_awvalid(gbus_axi_awvalid), .dut_axi_awready(gbus_axi_awready), .dut_axi_wid(gbus_axi_wid), + .dut_axi_wdata(gbus_axi_wdata), .dut_axi_wstrb(gbus_axi_wstrb), .dut_axi_wlast(gbus_axi_wlast), + .dut_axi_wvalid(gbus_axi_wvalid), .dut_axi_wready(gbus_axi_wready), .dut_axi_bid(gbus_axi_bid), + .dut_axi_bresp(gbus_axi_bresp), .dut_axi_bvalid(gbus_axi_bvalid), .dut_axi_bready(gbus_axi_bready), + .dut_axi_arid(gbus_axi_arid), .dut_axi_araddr(gbus_axi_araddr), .dut_axi_arlen(gbus_axi_arlen), + .dut_axi_arsize(gbus_axi_arsize), .dut_axi_arburst(gbus_axi_arburst), .dut_axi_arlock(gbus_axi_arlock), + .dut_axi_arcache(gbus_axi_arcache), .dut_axi_arprot(gbus_axi_arprot), .dut_axi_arqos(gbus_axi_arqos), + .dut_axi_arvalid(gbus_axi_arvalid), .dut_axi_arready(gbus_axi_arready), .dut_axi_rid(gbus_axi_rid), + .dut_axi_rdata(gbus_axi_rdata), .dut_axi_rresp(gbus_axi_rresp), .dut_axi_rlast(gbus_axi_rlast), + .dut_axi_rvalid(gbus_axi_rvalid), .dut_axi_rready(gbus_axi_rready), + .sysbus_ghbd_o (gbus_sysbus_to_generalbd), + .sysbus_ghbd_i (gbus_sysbus_to_generalbus) + ); + + uvhs_axi3_to_axi4_adapter #( + .ADDR_WIDTH(36), .ID_WIDTH(14), .AXI3_ID_WIDTH(8), .DATA_WIDTH(256) + ) U_GBUS_AXI_ADAPTER ( + .clk(gbus_host_clk), .rstn(rstn_sw4), + .s_awid(gbus_axi_awid), .s_awaddr({4'b0, gbus_axi_awaddr}), + .s_awlen(gbus_axi_awlen), .s_awsize(gbus_axi_awsize), + .s_awburst(gbus_axi_awburst), .s_awlock(gbus_axi_awlock), + .s_awcache(gbus_axi_awcache), .s_awprot(gbus_axi_awprot), + .s_awqos(gbus_axi_awqos), .s_awvalid(gbus_axi_awvalid), + .s_awready(gbus_axi_awready), .s_wid(gbus_axi_wid), + .s_wdata(gbus_axi_wdata), .s_wstrb(gbus_axi_wstrb), + .s_wlast(gbus_axi_wlast), .s_wvalid(gbus_axi_wvalid), + .s_wready(gbus_axi_wready), .s_bid(gbus_axi_bid), + .s_bresp(gbus_axi_bresp), .s_bvalid(gbus_axi_bvalid), + .s_bready(gbus_axi_bready), .s_arid(gbus_axi_arid), + .s_araddr({4'b0, gbus_axi_araddr}), .s_arlen(gbus_axi_arlen), + .s_arsize(gbus_axi_arsize), .s_arburst(gbus_axi_arburst), + .s_arlock(gbus_axi_arlock), .s_arcache(gbus_axi_arcache), + .s_arprot(gbus_axi_arprot), .s_arqos(gbus_axi_arqos), + .s_arvalid(gbus_axi_arvalid), .s_arready(gbus_axi_arready), + .s_rid(gbus_axi_rid), .s_rdata(gbus_axi_rdata), + .s_rresp(gbus_axi_rresp), .s_rlast(gbus_axi_rlast), + .s_rvalid(gbus_axi_rvalid), .s_rready(gbus_axi_rready), + .m_awid(gbus_h2c_awid), .m_awaddr(gbus_h2c_awaddr), + .m_awlen(gbus_h2c_awlen), .m_awsize(gbus_h2c_awsize), + .m_awburst(gbus_h2c_awburst), .m_awlock(gbus_h2c_awlock), + .m_awcache(gbus_h2c_awcache), .m_awprot(gbus_h2c_awprot), + .m_awqos(gbus_h2c_awqos), .m_awregion(gbus_h2c_awregion), + .m_awvalid(gbus_h2c_awvalid), .m_awready(gbus_h2c_awready), + .m_wdata(gbus_h2c_wdata), .m_wstrb(gbus_h2c_wstrb), + .m_wlast(gbus_h2c_wlast), .m_wvalid(gbus_h2c_wvalid), + .m_wready(gbus_h2c_wready), .m_bid(gbus_h2c_bid), + .m_bresp(gbus_h2c_bresp), .m_bvalid(gbus_h2c_bvalid), + .m_bready(gbus_h2c_bready), .m_arid(gbus_h2c_arid), + .m_araddr(gbus_h2c_araddr), .m_arlen(gbus_h2c_arlen), + .m_arsize(gbus_h2c_arsize), .m_arburst(gbus_h2c_arburst), + .m_arlock(gbus_h2c_arlock), .m_arcache(gbus_h2c_arcache), + .m_arprot(gbus_h2c_arprot), .m_arqos(gbus_h2c_arqos), + .m_arregion(gbus_h2c_arregion), .m_arvalid(gbus_h2c_arvalid), + .m_arready(gbus_h2c_arready), .m_rid(gbus_h2c_rid), + .m_rdata(gbus_h2c_rdata), .m_rresp(gbus_h2c_rresp), + .m_rlast(gbus_h2c_rlast), .m_rvalid(gbus_h2c_rvalid), + .m_rready(gbus_h2c_rready) + ); + + uvhs_axi_async_bridge #( + .ADDR_WIDTH(36), .ID_WIDTH(14), .DATA_WIDTH(256) + ) U_GBUS_H2C_CDC ( + .s_clk(gbus_host_clk), .s_rstn(rstn_sw4), + .s_awid(gbus_h2c_awid), .s_awaddr(gbus_h2c_awaddr), + .s_awlen(gbus_h2c_awlen), .s_awsize(gbus_h2c_awsize), + .s_awburst(gbus_h2c_awburst), .s_awlock(gbus_h2c_awlock), + .s_awcache(gbus_h2c_awcache), .s_awprot(gbus_h2c_awprot), + .s_awqos(gbus_h2c_awqos), .s_awregion(gbus_h2c_awregion), + .s_awvalid(gbus_h2c_awvalid), .s_awready(gbus_h2c_awready), + .s_wdata(gbus_h2c_wdata), .s_wstrb(gbus_h2c_wstrb), + .s_wlast(gbus_h2c_wlast), .s_wvalid(gbus_h2c_wvalid), + .s_wready(gbus_h2c_wready), .s_bid(gbus_h2c_bid), + .s_bresp(gbus_h2c_bresp), .s_bvalid(gbus_h2c_bvalid), + .s_bready(gbus_h2c_bready), .s_arid(gbus_h2c_arid), + .s_araddr(gbus_h2c_araddr), .s_arlen(gbus_h2c_arlen), + .s_arsize(gbus_h2c_arsize), .s_arburst(gbus_h2c_arburst), + .s_arlock(gbus_h2c_arlock), .s_arcache(gbus_h2c_arcache), + .s_arprot(gbus_h2c_arprot), .s_arqos(gbus_h2c_arqos), + .s_arregion(gbus_h2c_arregion), .s_arvalid(gbus_h2c_arvalid), + .s_arready(gbus_h2c_arready), .s_rid(gbus_h2c_rid), + .s_rdata(gbus_h2c_rdata), .s_rresp(gbus_h2c_rresp), + .s_rlast(gbus_h2c_rlast), .s_rvalid(gbus_h2c_rvalid), + .s_rready(gbus_h2c_rready), + .m_clk(inter_soc_clk), .m_rstn(inter_soc_sync_rstn), + .m_awid(gbus_dma_awid), .m_awaddr(gbus_dma_awaddr), + .m_awlen(gbus_dma_awlen), .m_awsize(gbus_dma_awsize), + .m_awburst(gbus_dma_awburst), .m_awlock(gbus_dma_awlock), + .m_awcache(gbus_dma_awcache), .m_awprot(gbus_dma_awprot), + .m_awqos(gbus_dma_awqos), .m_awregion(gbus_dma_awregion), + .m_awvalid(gbus_dma_awvalid), .m_awready(gbus_dma_awready), + .m_wdata(gbus_dma_wdata), .m_wstrb(gbus_dma_wstrb), + .m_wlast(gbus_dma_wlast), .m_wvalid(gbus_dma_wvalid), + .m_wready(gbus_dma_wready), .m_bid(gbus_dma_bid), + .m_bresp(gbus_dma_bresp), .m_bvalid(gbus_dma_bvalid), + .m_bready(gbus_dma_bready), .m_arid(gbus_dma_arid), + .m_araddr(gbus_dma_araddr), .m_arlen(gbus_dma_arlen), + .m_arsize(gbus_dma_arsize), .m_arburst(gbus_dma_arburst), + .m_arlock(gbus_dma_arlock), .m_arcache(gbus_dma_arcache), + .m_arprot(gbus_dma_arprot), .m_arqos(gbus_dma_arqos), + .m_arregion(gbus_dma_arregion), .m_arvalid(gbus_dma_arvalid), + .m_arready(gbus_dma_arready), .m_rid(gbus_dma_rid), + .m_rdata(gbus_dma_rdata), .m_rresp(gbus_dma_rresp), + .m_rlast(gbus_dma_rlast), .m_rvalid(gbus_dma_rvalid), + .m_rready(gbus_dma_rready) + ); + +`ifdef CPU_NUTSHELL + // NutShell's inbound frontend is 64 bits wide even though the common wrapper + // exposes the 256-bit dma_core_* contract. Use the vendor AXI converter so + // wide and narrow GeneralBus bursts retain their AXI lane and response rules. + uvhs_gbus_axi_dwidth U_GBUS_H2C_DWIDTH ( + .s_axi_aclk(inter_soc_clk), .s_axi_aresetn(inter_soc_sync_rstn), + .s_axi_awid(gbus_dma_awid), .s_axi_awaddr(gbus_dma_awaddr), + .s_axi_awlen(gbus_dma_awlen), .s_axi_awsize(gbus_dma_awsize), + .s_axi_awburst(gbus_dma_awburst), .s_axi_awlock(gbus_dma_awlock), + .s_axi_awcache(gbus_dma_awcache), .s_axi_awprot(gbus_dma_awprot), + .s_axi_awregion(gbus_dma_awregion), .s_axi_awqos(gbus_dma_awqos), + .s_axi_awvalid(gbus_dma_awvalid), .s_axi_awready(gbus_dma_awready), + .s_axi_wdata(gbus_dma_wdata), .s_axi_wstrb(gbus_dma_wstrb), + .s_axi_wlast(gbus_dma_wlast), .s_axi_wvalid(gbus_dma_wvalid), + .s_axi_wready(gbus_dma_wready), .s_axi_bid(gbus_dma_bid), + .s_axi_bresp(gbus_dma_bresp), .s_axi_bvalid(gbus_dma_bvalid), + .s_axi_bready(gbus_dma_bready), .s_axi_arid(gbus_dma_arid), + .s_axi_araddr(gbus_dma_araddr), .s_axi_arlen(gbus_dma_arlen), + .s_axi_arsize(gbus_dma_arsize), .s_axi_arburst(gbus_dma_arburst), + .s_axi_arlock(gbus_dma_arlock), .s_axi_arcache(gbus_dma_arcache), + .s_axi_arprot(gbus_dma_arprot), .s_axi_arregion(gbus_dma_arregion), + .s_axi_arqos(gbus_dma_arqos), .s_axi_arvalid(gbus_dma_arvalid), + .s_axi_arready(gbus_dma_arready), .s_axi_rid(gbus_dma_rid), + .s_axi_rdata(gbus_dma_rdata), .s_axi_rresp(gbus_dma_rresp), + .s_axi_rlast(gbus_dma_rlast), .s_axi_rvalid(gbus_dma_rvalid), + .s_axi_rready(gbus_dma_rready), + .m_axi_awaddr(data_cpu_bridge_m2s_awaddr), + .m_axi_awlen(data_cpu_bridge_m2s_awlen), + .m_axi_awsize(data_cpu_bridge_m2s_awsize), + .m_axi_awburst(data_cpu_bridge_m2s_awburst), + .m_axi_awlock(data_cpu_bridge_m2s_awlock), + .m_axi_awcache(data_cpu_bridge_m2s_awcache), + .m_axi_awprot(data_cpu_bridge_m2s_awprot), .m_axi_awregion(), + .m_axi_awqos(data_cpu_bridge_m2s_awqos), + .m_axi_awvalid(data_cpu_bridge_m2s_awvalid), + .m_axi_awready(data_cpu_bridge_s2m_awready), + .m_axi_wdata(data_cpu_bridge_m2s_wdata[63:0]), + .m_axi_wstrb(data_cpu_bridge_m2s_wstrb[7:0]), + .m_axi_wlast(data_cpu_bridge_m2s_wlast), + .m_axi_wvalid(data_cpu_bridge_m2s_wvalid), + .m_axi_wready(data_cpu_bridge_s2m_wready), + .m_axi_bresp(data_cpu_bridge_s2m_bresp), + .m_axi_bvalid(data_cpu_bridge_s2m_bvalid), + .m_axi_bready(data_cpu_bridge_m2s_bready), + .m_axi_araddr(data_cpu_bridge_m2s_araddr), + .m_axi_arlen(data_cpu_bridge_m2s_arlen), + .m_axi_arsize(data_cpu_bridge_m2s_arsize), + .m_axi_arburst(data_cpu_bridge_m2s_arburst), + .m_axi_arlock(data_cpu_bridge_m2s_arlock), + .m_axi_arcache(data_cpu_bridge_m2s_arcache), + .m_axi_arprot(data_cpu_bridge_m2s_arprot), .m_axi_arregion(), + .m_axi_arqos(data_cpu_bridge_m2s_arqos), + .m_axi_arvalid(data_cpu_bridge_m2s_arvalid), + .m_axi_arready(data_cpu_bridge_s2m_arready), + .m_axi_rdata(data_cpu_bridge_s2m_rdata[63:0]), + .m_axi_rresp(data_cpu_bridge_s2m_rresp), + .m_axi_rlast(data_cpu_bridge_s2m_rlast), + .m_axi_rvalid(data_cpu_bridge_s2m_rvalid), + .m_axi_rready(data_cpu_bridge_m2s_rready) + ); + assign data_cpu_bridge_m2s_awid = 14'b0; + assign data_cpu_bridge_m2s_arid = 14'b0; + assign data_cpu_bridge_m2s_wdata[255:64] = 192'b0; + assign data_cpu_bridge_m2s_wstrb[31:8] = 24'b0; +`else + assign data_cpu_bridge_m2s_awid = gbus_dma_awid; + assign data_cpu_bridge_m2s_awaddr = gbus_dma_awaddr; + assign data_cpu_bridge_m2s_awlen = gbus_dma_awlen; + assign data_cpu_bridge_m2s_awsize = gbus_dma_awsize; + assign data_cpu_bridge_m2s_awburst = gbus_dma_awburst; + assign data_cpu_bridge_m2s_awlock = gbus_dma_awlock; + assign data_cpu_bridge_m2s_awcache = gbus_dma_awcache; + assign data_cpu_bridge_m2s_awprot = gbus_dma_awprot; + assign data_cpu_bridge_m2s_awqos = gbus_dma_awqos; + assign data_cpu_bridge_m2s_awvalid = gbus_dma_awvalid; + assign gbus_dma_awready = data_cpu_bridge_s2m_awready; + assign data_cpu_bridge_m2s_wdata = gbus_dma_wdata; + assign data_cpu_bridge_m2s_wstrb = gbus_dma_wstrb; + assign data_cpu_bridge_m2s_wlast = gbus_dma_wlast; + assign data_cpu_bridge_m2s_wvalid = gbus_dma_wvalid; + assign gbus_dma_wready = data_cpu_bridge_s2m_wready; + assign gbus_dma_bid = data_cpu_bridge_s2m_bid; + assign gbus_dma_bresp = data_cpu_bridge_s2m_bresp; + assign gbus_dma_bvalid = data_cpu_bridge_s2m_bvalid; + assign data_cpu_bridge_m2s_bready = gbus_dma_bready; + assign data_cpu_bridge_m2s_arid = gbus_dma_arid; + assign data_cpu_bridge_m2s_araddr = gbus_dma_araddr; + assign data_cpu_bridge_m2s_arlen = gbus_dma_arlen; + assign data_cpu_bridge_m2s_arsize = gbus_dma_arsize; + assign data_cpu_bridge_m2s_arburst = gbus_dma_arburst; + assign data_cpu_bridge_m2s_arlock = gbus_dma_arlock; + assign data_cpu_bridge_m2s_arcache = gbus_dma_arcache; + assign data_cpu_bridge_m2s_arprot = gbus_dma_arprot; + assign data_cpu_bridge_m2s_arqos = gbus_dma_arqos; + assign data_cpu_bridge_m2s_arvalid = gbus_dma_arvalid; + assign gbus_dma_arready = data_cpu_bridge_s2m_arready; + assign gbus_dma_rid = data_cpu_bridge_s2m_rid; + assign gbus_dma_rdata = data_cpu_bridge_s2m_rdata; + assign gbus_dma_rresp = data_cpu_bridge_s2m_rresp; + assign gbus_dma_rlast = data_cpu_bridge_s2m_rlast; + assign gbus_dma_rvalid = data_cpu_bridge_s2m_rvalid; + assign data_cpu_bridge_m2s_rready = gbus_dma_rready; +`endif + +`endif + + // CPU progress is controlled exclusively by the DiffTest ready/clock-enable + // handshake. GBus FIFO activity must not be OR'ed into this signal: + // doing so lets the CPU retire while the formatter snapshot is waiting for + // transport backpressure, which mixes the architectural register snapshot + // with a later commit group. This is the same verified gating contract used + // by the XDMA path. The GBus drain path must therefore be provisioned with + // enough buffering (or moved to an independent always-running clock) rather + // than weakening CPU backpressure. + wire soc_clock_run_enable = + (difftest_clock_gate_enable & xdma_link_up) || + ~io_host_diff_enable || ~sys_rstn_io || ~cpu_rstn_io; + DifftestClockGate SOC_CLK_CTRL( .CK (sys_clk_i), - .E (difftest_clock_gate_enable), + .E (soc_clock_run_enable), .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_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 (cpu_rstn_io), + .async_in (axi_bclk_sync_rstn), .sync_out (inter_soc_sync_rstn) ); - - DifftestClockGate RTC_CLK_CTRL( - .CK (tmclk), - .E (difftest_clock_enable || ~io_host_diff_enable || ~sys_rstn_io || ~cpu_rstn_io ), - .Q (inter_rtc_clk) - ); `else wire inter_soc_clk; wire inter_rtc_clk; + wire inter_soc_sync_rstn; wire sys_rstn_io; wire cpu_rstn_io; assign inter_soc_clk = sys_clk_i; assign inter_rtc_clk = tmclk; + assign inter_soc_sync_rstn = axi_bclk_sync_rstn; assign sys_rstn_io = sys_rstn; assign cpu_rstn_io = cpu_rstn; `endif @@ -1645,23 +2289,23 @@ 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_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), `endif .inter_soc_clk (inter_soc_clk), .sys_rstn_i (cpu_rstn_io ), @@ -1703,7 +2347,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), @@ -1799,7 +2443,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), @@ -1814,7 +2458,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), @@ -1843,10 +2487,10 @@ assign hpm_dig_result = 0; AXI_bridge CFG_AXI_bridge_i (.SYS_INTER_CLK (inter_soc_clk), `ifdef UVHS - .SYS_INTER_ARESETN (inter_soc_sync_rstn), .UART_ACLK (uart_sclk), .UART_ARESETN (uart_sclk_sync_rstn), `endif + .SYS_INTER_ARESETN (inter_soc_sync_rstn), .ACLK (sys_clk_i), .ARESETN (axi_bclk_sync_rstn), @@ -1888,47 +2532,6 @@ AXI_bridge CFG_AXI_bridge_i .S00_AXI_wstrb (cpu2cfg_m2s_wstrb), .S00_AXI_wvalid (cpu2cfg_m2s_wvalid), -`ifdef UVHS - .UVHS_FLASH_AXI_araddr (uvhs_flash_axi_araddr), - .UVHS_FLASH_AXI_arburst (uvhs_flash_axi_arburst), - .UVHS_FLASH_AXI_arcache (uvhs_flash_axi_arcache), - .UVHS_FLASH_AXI_arid (uvhs_flash_axi_arid), - .UVHS_FLASH_AXI_arlen (uvhs_flash_axi_arlen), - .UVHS_FLASH_AXI_arlock (uvhs_flash_axi_arlock), - .UVHS_FLASH_AXI_arprot (uvhs_flash_axi_arprot), - .UVHS_FLASH_AXI_arqos (uvhs_flash_axi_arqos), - .UVHS_FLASH_AXI_arready (uvhs_flash_axi_arready), - .UVHS_FLASH_AXI_arsize (uvhs_flash_axi_arsize), - .UVHS_FLASH_AXI_arvalid (uvhs_flash_axi_arvalid), - .UVHS_FLASH_AXI_awaddr (uvhs_flash_axi_awaddr), - .UVHS_FLASH_AXI_awburst (uvhs_flash_axi_awburst), - .UVHS_FLASH_AXI_awcache (uvhs_flash_axi_awcache), - .UVHS_FLASH_AXI_awid (uvhs_flash_axi_awid), - .UVHS_FLASH_AXI_awlen (uvhs_flash_axi_awlen), - .UVHS_FLASH_AXI_awlock (uvhs_flash_axi_awlock), - .UVHS_FLASH_AXI_awprot (uvhs_flash_axi_awprot), - .UVHS_FLASH_AXI_awqos (uvhs_flash_axi_awqos), - .UVHS_FLASH_AXI_awready (uvhs_flash_axi_awready), - .UVHS_FLASH_AXI_awsize (uvhs_flash_axi_awsize), - .UVHS_FLASH_AXI_awvalid (uvhs_flash_axi_awvalid), - .UVHS_FLASH_AXI_bid (uvhs_flash_axi_bid), - .UVHS_FLASH_AXI_bready (uvhs_flash_axi_bready), - .UVHS_FLASH_AXI_bresp (uvhs_flash_axi_bresp), - .UVHS_FLASH_AXI_bvalid (uvhs_flash_axi_bvalid), - .UVHS_FLASH_AXI_rdata (uvhs_flash_axi_rdata), - .UVHS_FLASH_AXI_rid (uvhs_flash_axi_rid), - .UVHS_FLASH_AXI_rlast (uvhs_flash_axi_rlast), - .UVHS_FLASH_AXI_rready (uvhs_flash_axi_rready), - .UVHS_FLASH_AXI_rresp (uvhs_flash_axi_rresp), - .UVHS_FLASH_AXI_rvalid (uvhs_flash_axi_rvalid), - .UVHS_FLASH_AXI_wdata (uvhs_flash_axi_wdata), - .UVHS_FLASH_AXI_wid (uvhs_flash_axi_wid), - .UVHS_FLASH_AXI_wlast (uvhs_flash_axi_wlast), - .UVHS_FLASH_AXI_wready (uvhs_flash_axi_wready), - .UVHS_FLASH_AXI_wstrb (uvhs_flash_axi_wstrb), - .UVHS_FLASH_AXI_wvalid (uvhs_flash_axi_wvalid), -`endif - .SYS_CFG_APB_paddr (syscfg_paddr_mix), .SYS_CFG_APB_penable (syscfg_penable), .SYS_CFG_APB_prdata (syscfg_prdata), @@ -1937,23 +2540,6 @@ AXI_bridge CFG_AXI_bridge_i .SYS_CFG_APB_pslverr (syscfg_pslverr), .SYS_CFG_APB_pwdata (syscfg_pwdata), .SYS_CFG_APB_pwrite (syscfg_pwrite), - - .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 (), - .UART_0_rxd (uart0_sin), - .UART_0_rxrdyn (), - .UART_0_txd (uart0_sout), - .UART_0_txrdyn (), - .uart0_intc (uart0_int), - .rom_axi_araddr (rom_axi_araddr), .rom_axi_arburst (), .rom_axi_arcache (), @@ -1992,6 +2578,7 @@ AXI_bridge CFG_AXI_bridge_i ); `ifndef NO_DIFF +`ifndef CONFIG_DIFFTEST_HOSTIF_GBUS data_bridge data_bridge_i (.ACLK (axi_bus_clk), .ARESETN (axi_bclk_sync_rstn), @@ -2115,6 +2702,7 @@ AXI_bridge CFG_AXI_bridge_i .S01_AXI_wstrb (gmac_m_wstrb), .S01_AXI_wvalid (gmac_m_wvalid)); `endif +`endif endmodule diff --git a/fpga_diff/tools/update_core_flist.sh b/fpga_diff/tools/update_core_flist.sh index 34473e2..a092dbc 100755 --- a/fpga_diff/tools/update_core_flist.sh +++ b/fpga_diff/tools/update_core_flist.sh @@ -73,6 +73,10 @@ generate_uvhs_filelist() { local -a required_modules=() shift 4 + local hostif=${DIFFTEST_HOSTIF:-XDMA} + [[ $hostif == XDMA || $hostif == GBUS ]] || + rtl_flist_fail "DIFFTEST_HOSTIF must be XDMA or GBUS: $hostif" + core_dir=$(realpath -e -- "$core_dir") work_dir=$(realpath -e -- "$work_dir") core_rtl_dir=$core_dir/rtl @@ -87,15 +91,23 @@ generate_uvhs_filelist() { { printf '+define+SYNTHESIS\n+define+XIANGSHAN_FPGA\n+define+UVHS\n' + if [[ $hostif == GBUS ]]; then + printf '+define+CONFIG_DIFFTEST_HOSTIF_GBUS\n' + fi printf '+define+DDR4_16G_X8\n+define+DQ64\n+define+DDR4_2400\n' printf '+define+DQ=64\n+define+MICRON_DDR\n+define+DDR4_16Gbx8\n' printf '+define+DDR4\n+define+SRAM_SYN\n+define+DATA_VERSION=0\n' if [[ $cpu == nutshell ]]; then printf '+define+CPU_NUTSHELL\n' fi - if [[ $cpu == kmh ]] && - grep -Eq '^[[:space:]]*(input|output)[[:space:]].*dma_awready' "$core_rtl_dir/SimTop.sv"; then - printf '+define+CONFIG_SIMTOP_HAS_DMA\n' + if [[ $cpu == kmh ]]; then + if grep -Eq '^[[:space:]]*(input|output)[[:space:]].*dma_awready' \ + "$core_rtl_dir/SimTop.sv"; then + printf '+define+CONFIG_SIMTOP_HAS_DMA\n' + elif [[ $hostif == GBUS ]]; then + rtl_flist_fail \ + "GBus H2C requires the generated KMH SimTop dma_* AXI interface" + fi fi printf '+incdir+%s\n' "$core_dir" "$core_rtl_dir" @@ -103,10 +115,16 @@ generate_uvhs_filelist() { printf '+incdir+%s\n' "$core_generated_dir" fi printf '+incdir+%s/src/rtl/common\n' "$fpga_diff_dir" + printf '+incdir+%s/uvhs/common\n' "$fpga_diff_dir" find "$fpga_diff_dir/src/rtl/common" -type f \ \( -name '*.v' -o -name '*.sv' -o -name '*.vh' -o -name '*.svh' \) \ ! -name 'u0_xdma.v' -print | LC_ALL=C sort + if [[ -d $fpga_diff_dir/uvhs/common ]]; then + find "$fpga_diff_dir/uvhs/common" -type f \ + \( -name '*.v' -o -name '*.sv' -o -name '*.vh' -o -name '*.svh' \) \ + -print | LC_ALL=C sort + fi if [[ -d $work_dir/rtl/stubs ]]; then find "$work_dir/rtl/stubs" -type f -name '*.v' -print | LC_ALL=C sort fi diff --git a/fpga_diff/uvhs/README.md b/fpga_diff/uvhs/README.md index 4f9283d..04690dd 100644 --- a/fpga_diff/uvhs/README.md +++ b/fpga_diff/uvhs/README.md @@ -4,6 +4,33 @@ This directory contains the UVHS build and runtime flow for FPGA-Diff. UVHS commands use the `uvhs_*` prefix and do not replace the existing Vivado, PCIe, or JTAG commands. +## GBus SRAM C2H + +`DIFFTEST_HOSTIF=GBUS` retains the shared `Difftest2AXIs` sender and buffers +DiffTest output only in on-chip SRAM. Workload H2C converts the GeneralBus AXI3 +master to AXI4, crosses into the CPU clock domain, and occupies the CPU +subsystem's existing `dma_core_*` inbound interface. KMH retains the 256-bit +interface; NutShell uses the generated AXI data-width converter before its +64-bit frontend, so no GeneralBus data lanes are discarded. The CPU memory +hierarchy and physical DDR path remain unchanged. The obsolete C2H DDR-ring +writer and DDR trace sink are not included. + +GBS1 is a register-drained SRAM window. Backpressure reaches the DiffTest +sender and pauses the CPU while the host transport clock continues to run. +The matching fpga-host selects the same GBS1 register-window protocol. + +GeneralBD local offsets are below; the host adds its config base (`0x1000`). + +| Local offset | GBS1 register | +| --- | --- | +| `0x1200` | present[31], frame error[30], draining[29], staged words[16:8], filling[7], FIFO data[6] | +| `0x1204` | control: FILL=1, DRAIN=2 | +| `0x1208` | ID `0x47425331` | +| `0x2000..0x23fc` | SRAM data window | + +AXIS TLAST validates each 768-byte DiffTest range. The host dispatches complete +96-byte records and preserves any partial record across SRAM windows. + ## RTL Input Generate an FPGA release before entering `env-scripts`. `CORE_DIR` is the @@ -21,6 +48,13 @@ conversion. The UVHS flow only consumes that result. them. `uvhs_project` combines those inputs with the release RTL and FPGA-Diff wrappers in `/rtl/filelist.f`, which the UVHS frontend reads directly. +## GBus host paths + +The GBus C2H path uses the on-chip SRAM register window identified as GBS1. +GBus H2C occupies the existing `dma_core_*` inbound AXI interface during the +workload load phase; no DDR C2H ring or GBus-specific physical-DDR path is part +of the UVHS RTL flow. + ## Build ```sh @@ -35,7 +69,9 @@ the runtime database. `uvhs_project` performs these steps: 1. Copies the vendor board template into an isolated work directory. -2. Prepares repository-owned Vivado IP and the 64-bit generalBus DCP. +2. Prepares repository-owned Vivado IP, including the NutShell 256-to-64 AXI + converter, and generates the protected GeneralBus and GeneralBD checkpoints + from the local UVHS installation. 3. Imports the selected DDR DCP and validates its AXI width. 4. Builds the complete RTL file list and checks the expected top module. @@ -52,12 +88,10 @@ helper scripts, board-template files, and the external DDR directory. This keeps a missing input from being discovered after a multi-hour run has started. IP preparation intentionally retains two files. `prepare_ip.sh` is the outer -orchestrator for three different sources: repository Vivado IP, the vendor -Python generalBus generator, and an external DDR checkpoint. The child +orchestrator for repository Vivado IP, generated GeneralBus/GeneralBD +checkpoints, and an external DDR checkpoint. The child `export_vivado_ip.tcl` must run inside Vivado because it uses project, IP, BD, -and checkpoint commands. Merging them would require Vivado to launch a nested -Vivado process or embedding a generated Tcl file in shell, neither of which -simplifies the execution model. +and checkpoint commands. Owned GBus RTL lives in `uvhs/common`. `uvhs_backend` follows the vendor implementation sequence: clock inference and transformation, remap, partition, localization, system routing, FPGA PnR, @@ -86,15 +120,13 @@ per-bit TDM skew on the time value. The remaining logic is partitioned automatically. XiangShan uses these path constraints together with high partition effort and the `uv_placer_balance_slrs` PnR strategy. -UVHS drives the AXI UART16550 from `clk6_p` at a fixed 50 MHz. The CPU/SoC -`clk5_p` frequency comes from the system sign-off result committed in the -selected runtime database, so each partition and PnR result runs at its own -reported frequency. The fixed UART clock matches the 50 MHz clock declared by -the XiangShan FPGA device tree and keeps UART baud timing independent of CPU -clock gating. +UVHS drives the AXI UART16550 and the GBus/DiffTest host interface from +`clk6_p`, while the CPU/SoC uses `clk5_p`. At runtime, both clocks are restored +to the sign-off frequencies stored in the selected runtime database, so each +partition and PnR result runs at its reported frequency. Keeping UART and GBus +on the free-running transport clock makes them independent of CPU clock gating. -At runtime, `clk5_p` is restored to the sign-off frequency stored in the -selected runtime database. `clk8_p` is then derived at a fixed 50:1 CPU-to-TMCLK +`clk8_p` is then derived at a fixed 50:1 CPU-to-TMCLK ratio, so a 14 MHz CPU clock uses approximately 280 kHz TMCLK. The optional `UVHS_TMCLK_CPU_RATIO` environment variable is retained only for controlled clock-sweep experiments. Update the Linux device-tree `timebase-frequency` to @@ -180,17 +212,19 @@ from the server's global Tcl scope. ## UVHS ILA Waveform Capture The probe scripts describe compile-time instrumentation, not a runtime dump. -For XiangShan/KMH builds, the frontend sources `compilation/probe_kmh.tcl` by -default. CPU and L2 signals are sampled on +For XiangShan/KMH XDMA builds, the frontend sources +`compilation/probe_kmh.tcl` by default. This larger profile samples CPU and L2 +signals on `core_def.inter_soc_clk`, the gated clock connected to `SimTop.clock`. DDR and -host-trigger signals remain on their free-running `core_def.sys_clk_i` domain, -so the host trigger remains observable after the CPU clock stops. Its generated +host-trigger signals remain on their free-running host-clock domain, so the host +trigger remains observable after the CPU clock stops. Its generated hierarchy must be checked before it is reused with a different XiangShan RTL revision. The braces preserve hierarchy indexes such as `[0]`. -Other CPU builds continue to use the minimal `compilation/probe_ila.tcl` -profile by default. +GBus builds and other CPU builds use the minimal +`compilation/probe_ila.tcl` profile by default. This keeps the stable host +trigger without coupling a build to revision-specific CPU internals. Set `UVHS_PROBE_TCL` to select a profile or an external file; an override must -retain the XDMA host trigger if host-controlled capture is required. Set it to +retain the host trigger if host-controlled capture is required. Set it to an empty value for a build with no UHD instrumentation. The scripts call `probe_net` for sampled signals and `trigger_net` for signals that may participate in a trigger condition. @@ -392,9 +426,11 @@ before the UVHS timing worker reads it. | File | Role | | --- | --- | | `uvhs.mk` | Build and runtime target wiring. | -| `../tools/update_core_flist.sh` | Shared Vivado/UVHS RTL file-list entry point. | +| `../tools/update_core_flist.sh` | Shared Vivado/UVHS RTL file-list entry point, including `uvhs/common`. | +| `common/` | Owned GBus/UVHS RTL wrappers, CDC, and blackbox stubs. | | `../tools/rtl_filelist_lib.sh` | Nested file-list parsing and path resolution. | -| `../src/tcl/common/{blk_mem_gen_0,AXI_bridge,data_bridge,xdma_ep}.tcl` | Shared Vivado IP/BD generators used by UVHS IP export and Vivado project creation. | +| `../src/tcl/common/{blk_mem_gen_0,AXI_bridge,data_bridge,xdma_ep}.tcl` | Shared Vivado IP/BD generators used by UVHS IP export and Vivado project creation. XDMA exports `data_bridge`/`xdma_ep`; GBus skips them. | +| `tcl/uvhs_gbus_axi_dwidth.tcl` | GBus-only 256-to-64 AXI converter generator. | | `compilation/flow_common.tcl` | Shared UVHS path, environment, and source helpers. | | `compilation/frontend_run.tcl` | RTL/IP import, elaboration, and uvsyn frontend. | | `compilation/backend_run.tcl` | Fill-rate setup, partition, routing, PnR, and database commit. | @@ -403,7 +439,8 @@ before the UVHS timing worker reads it. | `compilation/assign_pin.tcl` | Physical UART daughter-card, clock, PCIe, JTAG, SD, and control pins. | | `compilation/timing.tcl` | External clock and asynchronous-group constraints. | | `compilation/vivado_pre_opt.tcl` | XDMA refclock and CDC constraints. | -| `compilation/prepare_ip.sh` | Coordinates Vivado, generalBus, and external DDR IP preparation. | +| `compilation/prepare_ip.sh` | Coordinates Vivado IP export, GeneralBus/GeneralBD generation, and external DDR import. | +| `ip/gbus/README.md` | Documents the vendor sources used to generate GBus checkpoints. | | `compilation/export_vivado_ip.tcl` | Runs repository-owned XCI/BD exports inside Vivado. | | `compilation/probe_ila.tcl` | Minimal host-trigger UHD probe profile. | | `compilation/probe_kmh.tcl` | KMH debug UHD probe profile. | diff --git a/fpga_diff/uvhs/common/uvhs_async_fifo.sv b/fpga_diff/uvhs/common/uvhs_async_fifo.sv new file mode 100644 index 0000000..383cfa3 --- /dev/null +++ b/fpga_diff/uvhs/common/uvhs_async_fifo.sv @@ -0,0 +1,87 @@ +`timescale 1ns/1ps + +// Generic dual-clock ready/valid FIFO. Payload and pointer CDC use the same +// Gray-pointer structure as the proven GBus AXI-stream FIFO. +module uvhs_async_fifo #( + parameter integer WIDTH = 32, + parameter integer ADDR_WIDTH = 3 +) ( + input wire s_clk, + input wire s_rstn, + input wire [WIDTH-1:0] s_data, + input wire s_valid, + output wire s_ready, + + input wire m_clk, + input wire m_rstn, + output wire [WIDTH-1:0] m_data, + output wire m_valid, + input wire m_ready +); + localparam integer PTR_WIDTH = ADDR_WIDTH + 1; + localparam integer DEPTH = 1 << ADDR_WIDTH; + + (* ram_style = "distributed" *) reg [WIDTH-1:0] mem [0:DEPTH-1]; + reg [PTR_WIDTH-1:0] wbin, wgray; + reg [PTR_WIDTH-1:0] rbin, rgray; + (* ASYNC_REG = "TRUE", SHREG_EXTRACT = "NO" *) + reg [PTR_WIDTH-1:0] rgray_wsync1, rgray_wsync2; + (* ASYNC_REG = "TRUE", SHREG_EXTRACT = "NO" *) + reg [PTR_WIDTH-1:0] wgray_rsync1, wgray_rsync2; + reg [WIDTH-1:0] out_data; + reg out_valid; + + wire [PTR_WIDTH-1:0] wbin_next = wbin + 1'b1; + wire [PTR_WIDTH-1:0] wgray_next = (wbin_next >> 1) ^ wbin_next; + wire [PTR_WIDTH-1:0] full_compare = { + ~rgray_wsync2[PTR_WIDTH-1:PTR_WIDTH-2], + rgray_wsync2[PTR_WIDTH-3:0] + }; + wire full = wgray_next == full_compare; + wire not_empty = rgray != wgray_rsync2; + + assign s_ready = s_rstn && !full; + assign m_data = out_data; + assign m_valid = out_valid; + + always @(posedge s_clk or negedge s_rstn) begin + if (!s_rstn) begin + wbin <= 0; + wgray <= 0; + rgray_wsync1 <= 0; + rgray_wsync2 <= 0; + end else begin + rgray_wsync1 <= rgray; + rgray_wsync2 <= rgray_wsync1; + if (s_valid && s_ready) begin + mem[wbin[ADDR_WIDTH-1:0]] <= s_data; + wbin <= wbin_next; + wgray <= wgray_next; + end + end + end + + always @(posedge m_clk or negedge m_rstn) begin + if (!m_rstn) begin + rbin <= 0; + rgray <= 0; + wgray_rsync1 <= 0; + wgray_rsync2 <= 0; + out_data <= 0; + out_valid <= 1'b0; + end else begin + wgray_rsync1 <= wgray; + wgray_rsync2 <= wgray_rsync1; + if (!out_valid || m_ready) begin + if (not_empty) begin + out_data <= mem[rbin[ADDR_WIDTH-1:0]]; + rbin <= rbin + 1'b1; + rgray <= ((rbin + 1'b1) >> 1) ^ (rbin + 1'b1); + out_valid <= 1'b1; + end else begin + out_valid <= 1'b0; + end + end + end + end +endmodule diff --git a/fpga_diff/uvhs/common/uvhs_async_status_sync.sv b/fpga_diff/uvhs/common/uvhs_async_status_sync.sv new file mode 100644 index 0000000..2fdab02 --- /dev/null +++ b/fpga_diff/uvhs/common/uvhs_async_status_sync.sv @@ -0,0 +1,21 @@ +module uvhs_async_status_sync #( + parameter INIT = 1'b1 +) ( + input wire clk, + input wire rstn, + input wire async_in, + output wire sync_out +); + + (* ASYNC_REG = "TRUE", SHREG_EXTRACT = "NO" *) reg [1:0] sync_reg; + + always @(posedge clk or negedge rstn) begin + if (!rstn) + sync_reg <= {2{INIT}}; + else + sync_reg <= {sync_reg[0], async_in}; + end + + assign sync_out = sync_reg[1]; + +endmodule diff --git a/fpga_diff/uvhs/common/uvhs_axi3_to_axi4_adapter.sv b/fpga_diff/uvhs/common/uvhs_axi3_to_axi4_adapter.sv new file mode 100644 index 0000000..1807239 --- /dev/null +++ b/fpga_diff/uvhs/common/uvhs_axi3_to_axi4_adapter.sv @@ -0,0 +1,135 @@ +`timescale 1ns/1ps + +// Protocol shim for the U2.2 uvw_general_bus AXI3 user port and the existing +// CPU-subsystem DMA AXI4 interface. The general-bus IP emits at most 16-beat +// AXI3 bursts; AXI4 accepts the same burst semantics with widened metadata. +module uvhs_axi3_to_axi4_adapter #( + parameter integer ADDR_WIDTH = 34, + parameter integer ID_WIDTH = 14, + parameter integer AXI3_ID_WIDTH = 8, + parameter integer DATA_WIDTH = 256 +) ( + input wire clk, + input wire rstn, + input wire [AXI3_ID_WIDTH-1:0] s_awid, + input wire [ADDR_WIDTH-1:0] s_awaddr, + input wire [3:0] s_awlen, + input wire [2:0] s_awsize, + input wire [1:0] s_awburst, + input wire [1:0] s_awlock, + input wire [3:0] s_awcache, + input wire [2:0] s_awprot, + input wire [3:0] s_awqos, + input wire s_awvalid, + output wire s_awready, + input wire [AXI3_ID_WIDTH-1:0] s_wid, + input wire [DATA_WIDTH-1:0] s_wdata, + input wire [DATA_WIDTH/8-1:0] s_wstrb, + input wire s_wlast, + input wire s_wvalid, + output wire s_wready, + output wire [AXI3_ID_WIDTH-1:0] s_bid, + output wire [1:0] s_bresp, + output wire s_bvalid, + input wire s_bready, + input wire [AXI3_ID_WIDTH-1:0] s_arid, + input wire [ADDR_WIDTH-1:0] s_araddr, + input wire [3:0] s_arlen, + input wire [2:0] s_arsize, + input wire [1:0] s_arburst, + input wire [1:0] s_arlock, + input wire [3:0] s_arcache, + input wire [2:0] s_arprot, + input wire [3:0] s_arqos, + input wire s_arvalid, + output wire s_arready, + output wire [AXI3_ID_WIDTH-1:0] s_rid, + output wire [DATA_WIDTH-1:0] s_rdata, + output wire [1:0] s_rresp, + output wire s_rlast, + output wire s_rvalid, + input wire s_rready, + output wire [ID_WIDTH-1:0] m_awid, + output wire [ADDR_WIDTH-1:0] m_awaddr, + output wire [7:0] m_awlen, + output wire [2:0] m_awsize, + output wire [1:0] m_awburst, + output wire m_awlock, + output wire [3:0] m_awcache, + output wire [2:0] m_awprot, + output wire [3:0] m_awqos, + output wire [3:0] m_awregion, + 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_wlast, + output wire m_wvalid, + input wire m_wready, + input wire [ID_WIDTH-1:0] m_bid, + input wire [1:0] m_bresp, + input wire m_bvalid, + output wire m_bready, + output wire [ID_WIDTH-1:0] m_arid, + output wire [ADDR_WIDTH-1:0] m_araddr, + output wire [7:0] m_arlen, + output wire [2:0] m_arsize, + output wire [1:0] m_arburst, + output wire m_arlock, + output wire [3:0] m_arcache, + output wire [2:0] m_arprot, + output wire [3:0] m_arqos, + output wire [3:0] m_arregion, + output wire m_arvalid, + input wire m_arready, + input wire [ID_WIDTH-1:0] m_rid, + input wire [DATA_WIDTH-1:0] m_rdata, + input wire [1:0] m_rresp, + input wire m_rlast, + input wire m_rvalid, + output wire m_rready +); + assign m_awid = {{(ID_WIDTH-AXI3_ID_WIDTH){1'b0}}, s_awid}; + assign m_awaddr = s_awaddr; + assign m_awlen = {4'b0, s_awlen}; + assign m_awsize = s_awsize; + assign m_awburst = s_awburst; + assign m_awlock = s_awlock[0]; + assign m_awcache = s_awcache; + assign m_awprot = s_awprot; + assign m_awqos = s_awqos; + assign m_awregion = 4'b0; + assign m_awvalid = s_awvalid; + assign s_awready = m_awready; + + assign m_wdata = s_wdata; + assign m_wstrb = s_wstrb; + assign m_wlast = s_wlast; + assign m_wvalid = s_wvalid; + assign s_wready = m_wready; + assign s_bid = m_bid[AXI3_ID_WIDTH-1:0]; + assign s_bresp = m_bresp; + assign s_bvalid = m_bvalid; + assign m_bready = s_bready; + + assign m_arid = {{(ID_WIDTH-AXI3_ID_WIDTH){1'b0}}, s_arid}; + assign m_araddr = s_araddr; + assign m_arlen = {4'b0, s_arlen}; + assign m_arsize = s_arsize; + assign m_arburst = s_arburst; + assign m_arlock = s_arlock[0]; + assign m_arcache = s_arcache; + assign m_arprot = s_arprot; + assign m_arqos = s_arqos; + assign m_arregion = 4'b0; + assign m_arvalid = s_arvalid; + assign s_arready = m_arready; + assign s_rid = m_rid[AXI3_ID_WIDTH-1:0]; + assign s_rdata = m_rdata; + assign s_rresp = m_rresp; + assign s_rlast = m_rlast; + assign s_rvalid = m_rvalid; + assign m_rready = s_rready; + + wire _unused = &{1'b0, clk, rstn, s_wid}; +endmodule diff --git a/fpga_diff/uvhs/common/uvhs_axi_async_bridge.sv b/fpga_diff/uvhs/common/uvhs_axi_async_bridge.sv new file mode 100644 index 0000000..836730d --- /dev/null +++ b/fpga_diff/uvhs/common/uvhs_axi_async_bridge.sv @@ -0,0 +1,94 @@ +`timescale 1ns/1ps + +// Full AXI4 clock-domain bridge for the GBus build. Each AXI channel crosses +// through an independent ready/valid FIFO; ordering within every channel is +// preserved and backpressure may stop either clock without losing a transfer. +module uvhs_axi_async_bridge #( + parameter integer ADDR_WIDTH = 34, + parameter integer ID_WIDTH = 14, + parameter integer DATA_WIDTH = 256, + parameter integer FIFO_ADDR_WIDTH = 3 +) ( + input wire s_clk, input wire s_rstn, + input wire [ID_WIDTH-1:0] s_awid, input wire [ADDR_WIDTH-1:0] s_awaddr, + input wire [7:0] s_awlen, input wire [2:0] s_awsize, input wire [1:0] s_awburst, + input wire s_awlock, input wire [3:0] s_awcache, input wire [2:0] s_awprot, + input wire [3:0] s_awqos, input wire [3:0] s_awregion, 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_wlast, input wire s_wvalid, + output wire s_wready, output wire [ID_WIDTH-1:0] s_bid, output wire [1:0] s_bresp, + output wire s_bvalid, input wire s_bready, input wire [ID_WIDTH-1:0] s_arid, + input wire [ADDR_WIDTH-1:0] s_araddr, input wire [7:0] s_arlen, + input wire [2:0] s_arsize, input wire [1:0] s_arburst, input wire s_arlock, + input wire [3:0] s_arcache, input wire [2:0] s_arprot, input wire [3:0] s_arqos, + input wire [3:0] s_arregion, input wire s_arvalid, output wire s_arready, + output wire [ID_WIDTH-1:0] s_rid, output wire [DATA_WIDTH-1:0] s_rdata, + output wire [1:0] s_rresp, output wire s_rlast, output wire s_rvalid, input wire s_rready, + + input wire m_clk, input wire m_rstn, + output wire [ID_WIDTH-1:0] m_awid, output wire [ADDR_WIDTH-1:0] m_awaddr, + output wire [7:0] m_awlen, output wire [2:0] m_awsize, output wire [1:0] m_awburst, + output wire m_awlock, output wire [3:0] m_awcache, output wire [2:0] m_awprot, + output wire [3:0] m_awqos, output wire [3:0] m_awregion, 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_wlast, output wire m_wvalid, + input wire m_wready, input wire [ID_WIDTH-1:0] m_bid, input wire [1:0] m_bresp, + input wire m_bvalid, output wire m_bready, output wire [ID_WIDTH-1:0] m_arid, + output wire [ADDR_WIDTH-1:0] m_araddr, output wire [7:0] m_arlen, + output wire [2:0] m_arsize, output wire [1:0] m_arburst, output wire m_arlock, + output wire [3:0] m_arcache, output wire [2:0] m_arprot, output wire [3:0] m_arqos, + output wire [3:0] m_arregion, output wire m_arvalid, input wire m_arready, + input wire [ID_WIDTH-1:0] m_rid, input wire [DATA_WIDTH-1:0] m_rdata, + input wire [1:0] m_rresp, input wire m_rlast, input wire m_rvalid, output wire m_rready +); + localparam integer STRB_WIDTH = DATA_WIDTH / 8; + localparam integer AW_WIDTH = ID_WIDTH + ADDR_WIDTH + 8 + 3 + 2 + 1 + 4 + 3 + 4 + 4; + localparam integer W_WIDTH = DATA_WIDTH + STRB_WIDTH + 1; + localparam integer B_WIDTH = ID_WIDTH + 2; + localparam integer AR_WIDTH = AW_WIDTH; + localparam integer R_WIDTH = ID_WIDTH + DATA_WIDTH + 2 + 1; + + wire [AW_WIDTH-1:0] s_aw_payload = {s_awid, s_awaddr, s_awlen, s_awsize, + s_awburst, s_awlock, s_awcache, s_awprot, s_awqos, s_awregion}; + wire [AW_WIDTH-1:0] m_aw_payload; + assign {m_awid, m_awaddr, m_awlen, m_awsize, m_awburst, m_awlock, + m_awcache, m_awprot, m_awqos, m_awregion} = m_aw_payload; + uvhs_async_fifo #(.WIDTH(AW_WIDTH), .ADDR_WIDTH(FIFO_ADDR_WIDTH)) aw_fifo ( + .s_clk(s_clk), .s_rstn(s_rstn), .s_data(s_aw_payload), .s_valid(s_awvalid), + .s_ready(s_awready), .m_clk(m_clk), .m_rstn(m_rstn), .m_data(m_aw_payload), + .m_valid(m_awvalid), .m_ready(m_awready)); + + wire [W_WIDTH-1:0] s_w_payload = {s_wdata, s_wstrb, s_wlast}; + wire [W_WIDTH-1:0] m_w_payload; + assign {m_wdata, m_wstrb, m_wlast} = m_w_payload; + uvhs_async_fifo #(.WIDTH(W_WIDTH), .ADDR_WIDTH(FIFO_ADDR_WIDTH)) w_fifo ( + .s_clk(s_clk), .s_rstn(s_rstn), .s_data(s_w_payload), .s_valid(s_wvalid), + .s_ready(s_wready), .m_clk(m_clk), .m_rstn(m_rstn), .m_data(m_w_payload), + .m_valid(m_wvalid), .m_ready(m_wready)); + + wire [B_WIDTH-1:0] m_b_payload = {m_bid, m_bresp}; + wire [B_WIDTH-1:0] s_b_payload; + assign {s_bid, s_bresp} = s_b_payload; + uvhs_async_fifo #(.WIDTH(B_WIDTH), .ADDR_WIDTH(FIFO_ADDR_WIDTH)) b_fifo ( + .s_clk(m_clk), .s_rstn(m_rstn), .s_data(m_b_payload), .s_valid(m_bvalid), + .s_ready(m_bready), .m_clk(s_clk), .m_rstn(s_rstn), .m_data(s_b_payload), + .m_valid(s_bvalid), .m_ready(s_bready)); + + wire [AR_WIDTH-1:0] s_ar_payload = {s_arid, s_araddr, s_arlen, s_arsize, + s_arburst, s_arlock, s_arcache, s_arprot, s_arqos, s_arregion}; + wire [AR_WIDTH-1:0] m_ar_payload; + assign {m_arid, m_araddr, m_arlen, m_arsize, m_arburst, m_arlock, + m_arcache, m_arprot, m_arqos, m_arregion} = m_ar_payload; + uvhs_async_fifo #(.WIDTH(AR_WIDTH), .ADDR_WIDTH(FIFO_ADDR_WIDTH)) ar_fifo ( + .s_clk(s_clk), .s_rstn(s_rstn), .s_data(s_ar_payload), .s_valid(s_arvalid), + .s_ready(s_arready), .m_clk(m_clk), .m_rstn(m_rstn), .m_data(m_ar_payload), + .m_valid(m_arvalid), .m_ready(m_arready)); + + wire [R_WIDTH-1:0] m_r_payload = {m_rid, m_rdata, m_rresp, m_rlast}; + wire [R_WIDTH-1:0] s_r_payload; + assign {s_rid, s_rdata, s_rresp, s_rlast} = s_r_payload; + uvhs_async_fifo #(.WIDTH(R_WIDTH), .ADDR_WIDTH(FIFO_ADDR_WIDTH)) r_fifo ( + .s_clk(m_clk), .s_rstn(m_rstn), .s_data(m_r_payload), .s_valid(m_rvalid), + .s_ready(m_rready), .m_clk(s_clk), .m_rstn(s_rstn), .m_data(s_r_payload), + .m_valid(s_rvalid), .m_ready(s_rready)); +endmodule diff --git a/fpga_diff/uvhs/common/uvhs_axilite_cdc_bridge.sv b/fpga_diff/uvhs/common/uvhs_axilite_cdc_bridge.sv new file mode 100644 index 0000000..3556c47 --- /dev/null +++ b/fpga_diff/uvhs/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/uvhs/common/uvhs_axis_async_fifo.sv b/fpga_diff/uvhs/common/uvhs_axis_async_fifo.sv new file mode 100644 index 0000000..267bbb5 --- /dev/null +++ b/fpga_diff/uvhs/common/uvhs_axis_async_fifo.sv @@ -0,0 +1,100 @@ +`timescale 1ns/1ps + +// Payload, byte enables and packet boundaries cross together under +// ready/valid backpressure. Both ports may also share the host clock. +module uvhs_axis_async_fifo #( + parameter integer DATA_WIDTH = 256, + parameter integer KEEP_WIDTH = DATA_WIDTH / 8, + parameter integer ADDR_WIDTH = 4 +) ( + input wire s_clk, + input wire s_rstn, + input wire [DATA_WIDTH-1:0] s_tdata, + input wire [KEEP_WIDTH-1:0] s_tkeep, + input wire s_tlast, + input wire s_tvalid, + output wire s_tready, + output wire s_has_data, + + input wire m_clk, + input wire m_rstn, + output wire [DATA_WIDTH-1:0] m_tdata, + output wire [KEEP_WIDTH-1:0] m_tkeep, + output wire m_tlast, + output wire m_tvalid, + input wire m_tready, + // Occupancy seen by the read domain, including the prefetch register. A + // register-window consumer needs this to tell "stream finished" from + // "nothing has arrived yet". + output wire m_has_data +); + localparam integer PTR_WIDTH = ADDR_WIDTH + 1; + localparam integer PAYLOAD_WIDTH = DATA_WIDTH + KEEP_WIDTH + 1; + localparam integer DEPTH = 1 << ADDR_WIDTH; + + (* ram_style = "block" *) reg [PAYLOAD_WIDTH-1:0] mem [0:DEPTH-1]; + reg [PTR_WIDTH-1:0] wbin, wgray; + reg [PTR_WIDTH-1:0] rbin, rgray; + (* ASYNC_REG = "TRUE", SHREG_EXTRACT = "NO" *) reg [PTR_WIDTH-1:0] rgray_wsync1, rgray_wsync2; + (* ASYNC_REG = "TRUE", SHREG_EXTRACT = "NO" *) reg [PTR_WIDTH-1:0] wgray_rsync1, wgray_rsync2; + reg [PAYLOAD_WIDTH-1:0] out_payload; + reg out_valid; + + wire [PTR_WIDTH-1:0] wbin_incremented = wbin + 1'b1; + wire [PTR_WIDTH-1:0] wgray_incremented = (wbin_incremented >> 1) ^ wbin_incremented; + wire [PTR_WIDTH-1:0] full_compare = { + ~rgray_wsync2[PTR_WIDTH-1:PTR_WIDTH-2], + rgray_wsync2[PTR_WIDTH-3:0] + }; + wire full = wgray_incremented == full_compare; + wire fifo_not_empty = rgray != wgray_rsync2; + assign s_tready = !full; + // This occupancy indication is already in s_clk through the synchronized + // read pointer. A caller may use it to keep a gated destination clock alive + // until all accepted stream data has drained. + assign s_has_data = wgray != rgray_wsync2; + assign m_has_data = fifo_not_empty | out_valid; + assign {m_tlast, m_tkeep, m_tdata} = out_payload; + assign m_tvalid = out_valid; + + always @(posedge s_clk or negedge s_rstn) begin + if (!s_rstn) begin + wbin <= 0; + wgray <= 0; + rgray_wsync1 <= 0; + rgray_wsync2 <= 0; + end else begin + rgray_wsync1 <= rgray; + rgray_wsync2 <= rgray_wsync1; + if (s_tvalid && s_tready) begin + mem[wbin[ADDR_WIDTH-1:0]] <= {s_tlast, s_tkeep, s_tdata}; + wbin <= wbin_incremented; + wgray <= wgray_incremented; + end + end + end + + always @(posedge m_clk or negedge m_rstn) begin + if (!m_rstn) begin + rbin <= 0; + rgray <= 0; + wgray_rsync1 <= 0; + wgray_rsync2 <= 0; + out_payload <= 0; + out_valid <= 1'b0; + end else begin + wgray_rsync1 <= wgray; + wgray_rsync2 <= wgray_rsync1; + if (!out_valid || m_tready) begin + if (fifo_not_empty) begin + out_payload <= mem[rbin[ADDR_WIDTH-1:0]]; + rbin <= rbin + 1'b1; + rgray <= ((rbin + 1'b1) >> 1) ^ (rbin + 1'b1); + out_valid <= 1'b1; + end else begin + out_valid <= 1'b0; + end + end + end + end +endmodule diff --git a/fpga_diff/uvhs/common/uvhs_blackbox_stubs.v b/fpga_diff/uvhs/common/uvhs_blackbox_stubs.v new file mode 100644 index 0000000..397a856 --- /dev/null +++ b/fpga_diff/uvhs/common/uvhs_blackbox_stubs.v @@ -0,0 +1,320 @@ +`ifndef UVHS_BLACKBOX_STUBS_V +`define UVHS_BLACKBOX_STUBS_V + +// XiangShan releases that enable the DiffTest clock gate provide the +// implementation in generated-src/rtl/DifftestClockGate.v. Import the +// generated macro header before declaring compatibility cells so the UVHS +// stub does not define a second module with the same name. +`include "DifftestMacros.svh" + +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 + +`ifndef CONFIG_DIFFTEST_CLOCKGATE +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 +`endif + +(* 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, UART_CLK, + 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; +// AXI_bridge's Vivado DCP contains a dedicated UART clock pin. Keep it in +// the synthesis stub so the DCP clock is explicitly driven by the SoC clock +// at the integration site rather than becoming a driverless net in PnR. +input UART_CLK; +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 + +`endif diff --git a/fpga_diff/uvhs/common/uvhs_gbus_c2h_fifo.sv b/fpga_diff/uvhs/common/uvhs_gbus_c2h_fifo.sv new file mode 100644 index 0000000..e388b70 --- /dev/null +++ b/fpga_diff/uvhs/common/uvhs_gbus_c2h_fifo.sv @@ -0,0 +1,263 @@ +`timescale 1ns/1ps + +// GBus C2H interface layer. +// +// In XDMA builds the shared DiffTest sender (`Difftest2AXIs` inside the +// generated SimTop) buffers formatted batches in an on-chip SRAM AsyncClockFIFO, +// packs them into 768-byte ranges, and streams those ranges straight into the +// XDMA IP. Nothing is staged in DDR. +// +// The GBus build keeps that sender untouched and replaces only the interface +// layer underneath it: GBus has no host-directed stream, only a register / +// backdoor bus. So this module parks the same 256-bit AXI-stream in an on-chip +// SRAM FIFO and exposes a small register window the GBus host drains with +// ordinary register reads. +// +// That removes the DDR ring -- and with it the inter-FPGA link hop and the whole +// AXI master / arbiter path -- from the C2H direction, so XDMA and GBus differ +// only in this interface layer and the host-side reassembly. +// +// Register window (GeneralBD local addresses, i.e. after removing 0x1000): +// +// 0x1200 R status +// [31] window present, reads as 1 +// [30] frame_error sticky: an AXI-stream tlast did not land on +// beat PACKET_BEATS-1, so the 768-byte range framing the +// host reassembles is shifted +// [29] draining +// [16:8] staged_words: valid 32-bit words in the staging window +// [7] filling: a staging fill is in progress +// [6] has_data: payload is still buffered (FIFO or holding reg) +// 0x1204 W control +// [0] start a staging fill (up to STAGE_WORDS 32-bit words) +// [1] drain: discard buffered payload without staging it +// 0x1204 R {23'b0, staged_words} +// 0x1208 R ID_MAGIC, a fixed word proving this window is reachable +// 0x2000.. R staging window: STAGE_WORDS 32-bit words, little-endian byte +// order inside each word, matching the byte order the XDMA build +// delivers over PCIe. +// +// The ID register exists because a window that is not decoded at all is +// indistinguishable from an idle one: every read returns zero either way. A +// host that checks this word first reports "the C2H window is not reachable" +// instead of looping on a staged_words that will never move. +// +// A fill ends either when the staging window is full or when the FIFO runs dry; +// the host appends whatever it read to the 768-byte range it is reassembling and +// issues another control write if the range is still short. +module uvhs_gbus_c2h_fifo #( + parameter integer AXIS_DATA_WIDTH = 256, + parameter integer AXIS_KEEP_WIDTH = AXIS_DATA_WIDTH / 8, + // 2048 x 256-bit = 64 KiB of payload buffering, so a host drain pause does + // not immediately stall the CPU through the DiffTest ready gate. + parameter integer FIFO_ADDR_WIDTH = 11, + // 1 KiB staging window: one control write loads it, then the host reads the + // whole window in one burst where the runtime supports multi-word reads. + parameter integer STAGE_WORDS = 256, + // One range is eight 96-byte packets, i.e. 24 beats of 32 bytes. + parameter integer PACKET_BEATS = 24 +) ( + input wire clk, + input wire rstn, + // C2H stream enable from the DiffTest reset gate. Both clocks are the + // always-running GBus host clock today, so one combined reset keeps the FIFO + // pointers consistent; the async FIFO below still carries the Gray CDC + // structure so the clock binding can move later without a rewrite. + input wire stream_rstn, + + input wire [AXIS_DATA_WIDTH-1:0] s_tdata, + input wire [AXIS_KEEP_WIDTH-1:0] s_tkeep, + input wire s_tlast, + input wire s_tvalid, + output wire s_tready, + + input wire cfg_wr_en, + input wire [15:0] cfg_wr_addr, + input wire [31:0] cfg_wdata, + input wire cfg_rd_en, + input wire [15:0] cfg_rd_addr, + output reg [31:0] cfg_rdata, + output reg cfg_rdata_vld +); + localparam integer LANES = AXIS_DATA_WIDTH / 32; + localparam integer LANE_WIDTH = (LANES <= 1) ? 1 : $clog2(LANES); + localparam integer STAGE_IDX_WIDTH = (STAGE_WORDS <= 1) ? 1 : $clog2(STAGE_WORDS); + localparam integer STAGE_CNT_WIDTH = (STAGE_WORDS <= 1) ? 1 : $clog2(STAGE_WORDS + 1); + + localparam [15:0] REG_STATUS = 16'h1200; + localparam [15:0] REG_CTRL = 16'h1204; + localparam [15:0] REG_ID = 16'h1208; + localparam [15:0] REG_DATA = 16'h2000; + localparam [15:0] REG_DATA_LAST = REG_DATA + 16'(STAGE_WORDS * 4 - 1); + // ASCII "GBS1": recognisable in a hex dump, and not a value an undecoded or + // unwritten register would happen to return. + localparam [31:0] ID_MAGIC = 32'h4742_5331; + + wire fifo_rstn = rstn & stream_rstn; + + wire [AXIS_DATA_WIDTH-1:0] fifo_tdata; + wire [AXIS_KEEP_WIDTH-1:0] fifo_tkeep; + wire fifo_tlast, fifo_tvalid, fifo_tready, fifo_has_data; + wire fifo_s_has_data; + + uvhs_axis_async_fifo #( + .DATA_WIDTH(AXIS_DATA_WIDTH), + .KEEP_WIDTH(AXIS_KEEP_WIDTH), + .ADDR_WIDTH (FIFO_ADDR_WIDTH) + ) u_c2h_fifo ( + .s_clk(clk), .s_rstn(fifo_rstn), + .s_tdata(s_tdata), .s_tkeep(s_tkeep), .s_tlast(s_tlast), + .s_tvalid(s_tvalid), .s_tready(s_tready), .s_has_data(fifo_s_has_data), + .m_clk(clk), .m_rstn(fifo_rstn), + .m_tdata(fifo_tdata), .m_tkeep(fifo_tkeep), .m_tlast(fifo_tlast), + .m_tvalid(fifo_tvalid), .m_tready(fifo_tready), + .m_has_data(fifo_has_data) + ); + + // --------------------------------------------------------------- staging + // One 256-bit FIFO entry is unpacked into LANES consecutive 32-bit staging + // words, so the host can read the payload with plain register reads. + // + // The pop is registered on this side: a beat is held in `hold_data` for the + // LANES cycles it takes to unpack, so `fifo_tready` is asserted only on the + // cycle the beat is taken into the holding register. + reg [31:0] stage_mem[0:STAGE_WORDS-1]; + reg [STAGE_CNT_WIDTH-1:0] staged_words; + reg filling, drain_mode; + reg [AXIS_DATA_WIDTH-1:0] hold_data; + reg hold_valid; + reg [LANE_WIDTH-1:0] hold_lane; + + // ------------------------------------------------------------ frame check + // The sender contract is exactly PACKET_BEATS beats per tlast. An early or + // missing boundary shifts the 768-byte range framing, so make it a sticky, + // host-visible error instead of silent corruption. + reg frame_error; + reg [7:0] beat_count; + + wire last_lane = (hold_lane == LANE_WIDTH'(LANES - 1)); + wire window_has_room = (staged_words != STAGE_CNT_WIDTH'(STAGE_WORDS)); + + wire take_beat = filling && !drain_mode && !hold_valid && fifo_tvalid && window_has_room; + wire unpack_lane = filling && !drain_mode && hold_valid; + // STAGE_WORDS must be a multiple of LANES so the window fills exactly on a + // beat boundary; otherwise the last beat would be half written when the fill + // stops. STAGE_WORDS=256 and LANES=8 satisfy this. + wire window_filled = unpack_lane && last_lane && + (staged_words + 1'b1 == STAGE_CNT_WIDTH'(STAGE_WORDS)); + // Drain mode only discards what is already buffered; the instant the FIFO is + // empty the drain ends so the host is not left polling a permanently busy FSM. + wire drain_beat = filling && drain_mode && fifo_tvalid; + wire drain_done = filling && drain_mode && !fifo_has_data; + + // A beat is popped exactly once, on the cycle it is latched into the holding + // register. The FIFO's output stage is already registered, so latching + // m_tdata and asserting m_tready in the same cycle is a normal consumer + // handshake; asserting m_tready again when the last lane is written out would + // consume the next entry without ever latching it. + assign fifo_tready = take_beat | drain_beat; + + wire ctrl_wr = cfg_wr_en && (cfg_wr_addr == REG_CTRL); + wire start_fill = ctrl_wr && cfg_wdata[0]; + wire start_drain = ctrl_wr && cfg_wdata[1]; + wire has_data = fifo_has_data | hold_valid; + + // A fill ends when the staging window is full, or when the FIFO has run dry. + // The host observes the end through status[7] going low and reads + // status[16:8] to learn how many words landed. + wire stream_dry = filling && !drain_mode && !hold_valid && !fifo_has_data; + + integer k; + // The staging FSM and the frame counter follow the stream reset, not just the + // fabric reset. The DiffTest stream enable drops whenever the host clears + // DIFFTEST_ENABLE, and the FIFO is cleared with it; if beat_count survived + // that, the first range after re-enable would look like a framing error. + always @(posedge clk or negedge fifo_rstn) begin + if (!fifo_rstn) begin + staged_words <= 0; + filling <= 1'b0; + drain_mode <= 1'b0; + hold_data <= 0; + hold_valid <= 1'b0; + hold_lane <= 0; + frame_error <= 1'b0; + beat_count <= 8'd0; + for (k = 0; k < STAGE_WORDS; k = k + 1) stage_mem[k] <= 32'b0; + end else begin + if (s_tvalid && s_tready) begin + if (s_tlast != (beat_count == 8'(PACKET_BEATS - 1))) frame_error <= 1'b1; + beat_count <= s_tlast ? 8'd0 : (beat_count + 8'd1); + end + + if (start_fill || start_drain) begin + filling <= 1'b1; + drain_mode <= start_drain; + staged_words <= 0; + hold_valid <= 1'b0; + hold_lane <= 0; + end else begin + // Pop one FIFO beat into the holding register; take_beat requires + // !hold_valid, so this never collides with the unpack below. + if (take_beat) begin + hold_data <= fifo_tdata; + hold_valid <= 1'b1; + hold_lane <= 0; + end + + // Unpack the holding beat into LANES consecutive 32-bit window words. + if (unpack_lane) begin + stage_mem[staged_words[STAGE_IDX_WIDTH-1:0]] <= hold_data[hold_lane*32 +: 32]; + staged_words <= staged_words + 1'b1; + if (last_lane) begin + hold_valid <= 1'b0; + hold_lane <= 0; + end else begin + hold_lane <= hold_lane + 1'b1; + end + if (window_filled) filling <= 1'b0; + end + + // Nothing left to stage (or nothing left to discard). + if (stream_dry || drain_done) begin + filling <= 1'b0; + drain_mode <= 1'b0; + hold_valid <= 1'b0; + hold_lane <= 0; + end + end + end + end + + // --------------------------------------------------------- register reads + wire [8:0] staged_words_status = 9'(staged_words); + wire [31:0] status_word = { + 1'b1, // [31] window present + frame_error, // [30] sticky framing error + drain_mode, // [29] drain in progress + 12'b0, // [28:17] + staged_words_status, // [16:8] valid staging words + filling, // [7] staging fill in progress + has_data, // [6] payload still buffered + 6'b0 // [5:0] + }; + + wire [STAGE_IDX_WIDTH-1:0] rd_stage_idx = cfg_rd_addr[STAGE_IDX_WIDTH+1:2]; + wire in_data_window = (cfg_rd_addr >= REG_DATA) && (cfg_rd_addr <= REG_DATA_LAST); + + always @(posedge clk or negedge rstn) begin + if (!rstn) begin + cfg_rdata <= 32'b0; + cfg_rdata_vld <= 1'b0; + end else begin + cfg_rdata_vld <= cfg_rd_en; + if (in_data_window) cfg_rdata <= stage_mem[rd_stage_idx]; + else + case (cfg_rd_addr) + REG_STATUS: cfg_rdata <= status_word; + REG_CTRL: cfg_rdata <= {23'b0, staged_words_status}; + REG_ID: cfg_rdata <= ID_MAGIC; + default: cfg_rdata <= 32'b0; + endcase + end + end +endmodule diff --git a/fpga_diff/uvhs/common/uvhs_gbus_host_adapter.sv b/fpga_diff/uvhs/common/uvhs_gbus_host_adapter.sv new file mode 100644 index 0000000..c3ed873 --- /dev/null +++ b/fpga_diff/uvhs/common/uvhs_gbus_host_adapter.sv @@ -0,0 +1,45 @@ +// GBus replacement contract for the XDMA endpoint. +// The board-specific general-bus DCP replaces this quiescent shell adapter. +module uvhs_gbus_host_adapter ( + output wire [255:0] M00_AXIS_0_tdata, output wire [31:0] M00_AXIS_0_tkeep, + output wire M00_AXIS_0_tlast, input wire M00_AXIS_0_tready, output wire M00_AXIS_0_tvalid, + input wire [255:0] S00_AXIS_0_tdata, input wire [31:0] S00_AXIS_0_tkeep, + input wire S00_AXIS_0_tlast, output wire S00_AXIS_0_tready, input wire S00_AXIS_0_tvalid, + output wire TO_DIFFTEST_PCIE_CLK, + output wire [31:0] XDMA_AXI_LITE_araddr, output wire [2:0] XDMA_AXI_LITE_arprot, + input wire XDMA_AXI_LITE_arready, output wire XDMA_AXI_LITE_arvalid, + output wire [31:0] XDMA_AXI_LITE_awaddr, output wire [2:0] XDMA_AXI_LITE_awprot, + input wire XDMA_AXI_LITE_awready, output wire XDMA_AXI_LITE_awvalid, + output wire XDMA_AXI_LITE_bready, input wire [1:0] XDMA_AXI_LITE_bresp, + input wire XDMA_AXI_LITE_bvalid, input wire [31:0] XDMA_AXI_LITE_rdata, + output wire XDMA_AXI_LITE_rready, input wire XDMA_AXI_LITE_rvalid, + input wire [1:0] XDMA_AXI_LITE_rresp, output wire [31:0] XDMA_AXI_LITE_wdata, + input wire XDMA_AXI_LITE_wready, output wire [3:0] XDMA_AXI_LITE_wstrb, + output wire XDMA_AXI_LITE_wvalid, + input wire cpu_clk, input wire host_clk, input wire cpu_rstn, + input wire [3:0] pci_exp_rxn, input wire [3:0] pci_exp_rxp, + output wire [3:0] pci_exp_txn, output wire [3:0] pci_exp_txp, + input wire pcie_ep_gt_ref_clk_n, input wire pcie_ep_gt_ref_clk_p, + input wire pcie_ep_perstn, output wire pcie_ep_lnk_up +); + // In UVHS GBus mode this compatibility output is only a legacy port, but + // if it is observed by the flow it must describe the real host interface + // clock. Do not derive it from the gated CPU clock: that would make the + // DiffTest host path depend on infer_clock/CPU clock gating. + assign TO_DIFFTEST_PCIE_CLK = host_clk; + // GBus is a UVHS transport and does not enumerate as a PCIe endpoint. The + // parent GBus build supplies the logical transport-ready indication; keep + // this compatibility shell's endpoint output low so it cannot be mistaken + // for a physical PCIe link if instantiated independently. + assign pcie_ep_lnk_up = 1'b0; + assign pci_exp_txn = 4'b0; assign pci_exp_txp = 4'b0; + assign S00_AXIS_0_tready = 1'b0; + assign M00_AXIS_0_tdata = 256'b0; assign M00_AXIS_0_tkeep = 32'b0; + assign M00_AXIS_0_tlast = 1'b0; assign M00_AXIS_0_tvalid = 1'b0; + // The GBus GENERALBD configuration bridge drives the AXI-Lite nets in the + // parent. Do not drive these shell output ports in the compatibility + // adapter: a constant-zero driver here would contend with GENERALBD and + // silently prevent HOST_IO_RESET/MEM_CPU/DIFFTEST_ENABLE from reaching the + // CPU config block. The ports remain in the module interface so the XDMA + // and GBus wrappers can share the same generated top-level shape. +endmodule diff --git a/fpga_diff/uvhs/common/uvhs_generalbd_axilite_bridge.sv b/fpga_diff/uvhs/common/uvhs_generalbd_axilite_bridge.sv new file mode 100644 index 0000000..6d318e4 --- /dev/null +++ b/fpga_diff/uvhs/common/uvhs_generalbd_axilite_bridge.sv @@ -0,0 +1,99 @@ +`timescale 1ns/1ps + +// Converts the local bus emitted by the UVHS generalBD IP into the existing +// DiffTest AXI-Lite config BAR. The local bus has no ready signal; one command +// is held until the AXI-Lite response completes. +module uvhs_generalbd_axilite_bridge ( + input wire clk, + input wire rstn, + input wire gbd_wr_en, + input wire [15:0] gbd_wr_addr, + input wire [31:0] gbd_wdata, + input wire gbd_rd_en, + input wire [15:0] gbd_rd_addr, + output reg [31:0] gbd_rdata, + output reg gbd_rdata_vld, + output reg [31:0] axil_awaddr, + output reg axil_awvalid, + input wire axil_awready, + output reg [31:0] axil_wdata, + output reg [3:0] axil_wstrb, + output reg axil_wvalid, + input wire axil_wready, + input wire [1:0] axil_bresp, + input wire axil_bvalid, + output reg axil_bready, + output reg [31:0] axil_araddr, + output reg axil_arvalid, + input wire axil_arready, + input wire [31:0] axil_rdata, + input wire [1:0] axil_rresp, + input wire axil_rvalid, + output reg axil_rready, + output reg h2c_active +); + localparam S_IDLE = 3'd0, S_AW_W = 3'd1, S_B = 3'd2, + S_AR = 3'd3, S_R = 3'd4; + reg [2:0] state; + reg aw_done, w_done; + reg [15:0] pending_addr; + reg [31:0] pending_data; + reg pending_read; + + always @(posedge clk or negedge rstn) begin + if (!rstn) begin + state <= S_IDLE; + aw_done <= 1'b0; w_done <= 1'b0; + pending_addr <= 16'b0; pending_data <= 32'b0; + pending_read <= 1'b0; gbd_rdata <= 32'b0; + gbd_rdata_vld <= 1'b0; h2c_active <= 1'b0; + end else begin + gbd_rdata_vld <= 1'b0; + case (state) + S_IDLE: begin + if (gbd_wr_en) begin + pending_addr <= gbd_wr_addr; + pending_data <= gbd_wdata; + pending_read <= 1'b0; + aw_done <= 1'b0; w_done <= 1'b0; + state <= S_AW_W; + if (gbd_wr_addr == 16'h24) + h2c_active <= (gbd_wdata[0]); + end else if (gbd_rd_en) begin + pending_addr <= gbd_rd_addr; + pending_read <= 1'b1; + state <= S_AR; + end + end + S_AW_W: begin + if (axil_awvalid && axil_awready) aw_done <= 1'b1; + if (axil_wvalid && axil_wready) w_done <= 1'b1; + if ((aw_done || (axil_awvalid && axil_awready)) && + (w_done || (axil_wvalid && axil_wready))) state <= S_B; + end + S_B: if (axil_bvalid) state <= S_IDLE; + S_AR: if (axil_arvalid && axil_arready) state <= S_R; + S_R: if (axil_rvalid) begin + gbd_rdata <= axil_rdata; + gbd_rdata_vld <= 1'b1; + state <= S_IDLE; + end + default: state <= S_IDLE; + endcase + end + end + + always @(*) begin + axil_awaddr = {16'b0, pending_addr}; + axil_awvalid = (state == S_AW_W) && !aw_done; + axil_wdata = pending_data; + axil_wstrb = 4'hf; + axil_wvalid = (state == S_AW_W) && !w_done; + axil_bready = (state == S_B); + axil_araddr = {16'b0, pending_addr}; + axil_arvalid = (state == S_AR); + axil_rready = (state == S_R); + end + + wire _unused = &{1'b0, pending_read, axil_bresp, axil_rresp}; +endmodule diff --git a/fpga_diff/uvhs/compilation/assemble.tcl b/fpga_diff/uvhs/compilation/assemble.tcl index 28ad8c8..a74f953 100644 --- a/fpga_diff/uvhs/compilation/assemble.tcl +++ b/fpga_diff/uvhs/compilation/assemble.tcl @@ -6,7 +6,12 @@ set uvhs_base_assemble ./script/1B_4F_HGC_assemble.tcl set uvhs_target_fpga 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_keep_default $uvhs_target_fpga +if {[string toupper [uvhs::env_or_default DIFFTEST_HOSTIF XDMA]] eq "GBUS"} { + # Keep F0 for the CPU memory hierarchy and F2 for the CPU/host partition. + set uvhs_keep_default "$uvhs_target_fpga b0.f0" +} +foreach uvhs_fpga [split [uvhs::env_or_default UVHS_KEEP_FPGAS $uvhs_keep_default]] { 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 diff --git a/fpga_diff/uvhs/compilation/assign_pin.tcl b/fpga_diff/uvhs/compilation/assign_pin.tcl index e7c98ec..14116e6 100644 --- a/fpga_diff/uvhs/compilation/assign_pin.tcl +++ b/fpga_diff/uvhs/compilation/assign_pin.tcl @@ -12,8 +12,17 @@ proc pin_name {top port} { return "${top}.${port}" } -set top [uvhs::env_or_default UVHS_ASSIGN_PIN_TOP fpga_top_debug] -set xdma_link_width [string toupper [string trim [uvhs::env_or_default XDMA_LINK_WIDTH X4]]] +proc assign_pin_env_or_default {name fallback} { + if {[info exists ::env($name)] && $::env($name) ne ""} { + return $::env($name) + } + return $fallback +} + +set fpga_diff_hostif [string toupper [assign_pin_env_or_default DIFFTEST_HOSTIF XDMA]] +set default_top [expr {$fpga_diff_hostif eq "GBUS" ? "none" : "fpga_top_debug"}] +set top [assign_pin_env_or_default UVHS_ASSIGN_PIN_TOP $default_top] +set xdma_link_width [string toupper [string trim [assign_pin_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'" } @@ -34,37 +43,33 @@ proc apc16_pin {port 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 -# led3 is driven by the user-DDR calibration status on F0. -assign_pin -port [pin_name $top led3] -connector b0.F0_FMC1 -index 311 - -# UV_FMCH_FLASH drives USB_UART_RX at FMC[270] and receives USB_UART_TX at -# FMC[311], so the FPGA RX/TX directions map to 270/311 respectively. -set uvhs_uart0_connector b0.F1_FMC0 -set uvhs_uart0_tx_index 311 -set uvhs_uart0_rx_index 270 -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 +if {$fpga_diff_hostif eq "GBUS"} { + # The GBus release exposes only this reduced set of low-speed ports. + foreach {port slot} { + led0 3 led2 4 led3 5 + uart0_sout 6 uart0_sin 7 uart1_sout 8 uart2_sout 10 + JTAG_TCK 12 JTAG_TMS 13 JTAG_TDI 14 JTAG_TDO 15 JTAG_TRSTn 16 + SD_CLK 17 SD_DECT 22 + } { + apc16_pin $port $slot + } +} else { + apc16_pin led0 3 + apc16_pin led2 4 + assign_pin -port [pin_name $top led3] -connector b0.F0_FMC1 -index 311 -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 + set uvhs_uart0_connector b0.F1_FMC0 + assign_pin -port [pin_name $top uart0_sout] -connector $uvhs_uart0_connector -index 311 + assign_pin -port [pin_name $top uart0_sin] -connector $uvhs_uart0_connector -index 270 + foreach {port slot} { + uart1_sout 8 uart1_sin 9 uart2_sout 10 uart2_sin 11 + JTAG_TCK 12 JTAG_TMS 13 JTAG_TDI 14 JTAG_TDO 15 JTAG_TRSTn 16 + SD_CLK 17 SD_CMD 18 SD_DATA0 19 SD_DATA1 20 + SD_DATA2 21 SD_DATA3 22 SD_DECT 23 + } { + apc16_pin $port $slot + } +} # U2 global clocks on F2 use the package pins declared by the board file. assign_pin -port [pin_name $top clk6_p] -fpga b0.f2 -pin CA39 @@ -78,22 +83,34 @@ 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 +if {$fpga_diff_hostif eq "GBUS"} { + puts "INFO: assign quiescent GBus PCIe TX ports to APC16 pins" + foreach {port index} { + {pci_ep_txp[0]} 101 {pci_ep_txn[0]} 102 + {pci_ep_txp[1]} 104 {pci_ep_txn[1]} 85 + {pci_ep_txp[2]} 64 {pci_ep_txn[2]} 65 + {pci_ep_txp[3]} 74 {pci_ep_txn[3]} 75 + } { + assign_pin -port [pin_name $top $port] -connector b0.F2_APC16 -index $index + } +} else { + 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] + 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 vendor DDR DCP declares its PDDR4DME binding through UV_HW_IP metadata. diff --git a/fpga_diff/uvhs/compilation/backend_run.tcl b/fpga_diff/uvhs/compilation/backend_run.tcl index 70a9e2a..38ef8a8 100644 --- a/fpga_diff/uvhs/compilation/backend_run.tcl +++ b/fpga_diff/uvhs/compilation/backend_run.tcl @@ -5,6 +5,8 @@ source [file join [file dirname [file normalize [info script]]] flow_common.tcl] set_working_space hw.dat +# Keep PnR in the UVHS single-worker mode on hosts where the bundled Python +# multiprocessing runtime cannot load its legacy libffi dependency. set_parallel_option -max_threads 4 -max_processes 8 -label fpga set_option time.auto_clock_config true @@ -37,17 +39,40 @@ init_runtime_data trigger_probe -check sweep_design -set xdma_axi_clock_pin \ - [get_pins -quiet core_def/xdma_ep_i/TO_DIFFTEST_PCIE_CLK] -if {[llength $xdma_axi_clock_pin] != 1} { - error "required XDMA AXI clock pin not found" +if {[string toupper [uvhs::env_or_default DIFFTEST_HOSTIF XDMA]] eq "XDMA"} { + set xdma_axi_clock_pin \ + [get_pins -quiet core_def/xdma_ep_i/TO_DIFFTEST_PCIE_CLK] + if {[llength $xdma_axi_clock_pin] != 1} { + error "required XDMA AXI clock pin not found" + } + set xdma_axi_clock_period [expr { + [string equal -nocase [uvhs::env_or_default XDMA_LINK_WIDTH X4] X8] + ? 4.0 : 8.0 + }] + create_clock -name XDMA_AXI_ACLK -period $xdma_axi_clock_period \ + $xdma_axi_clock_pin +} else { + # GBus has no XDMA user-clock output. The fpga-host/GBus protocol and + # both protected GBus IPs are driven by the explicit UVHS host clock + # UART_CLK_IN (clk6_p/dev_clk_i), not the gated CPU clock and not a clock + # inferred from the compatibility shell. + # Do not create a second primary clock on a black-box input: doing so makes + # the same host net appear as two unrelated clocks and lets infer_clock + # rediscover the old pseudo-XDMA clock. + set gbus_host_clock_pins [get_pins -quiet { + core_def/U_GBUS_GENERALBD/i_clk + core_def/U_GBUS_GENERAL_BUS/dut_axi_aclk + }] + if {[llength $gbus_host_clock_pins] != 2} { + error "required GBus host clock pins not found: $gbus_host_clock_pins" + } + set gbus_host_clock [get_clocks -quiet UART_CLK_IN] + if {[llength $gbus_host_clock] != 1} { + error "required host clock UART_CLK_IN not found for GBus: $gbus_host_clock" + } + puts "INFO: GBus host clock pins use explicit UART_CLK_IN (clk6_p/dev_clk_i): $gbus_host_clock_pins" + } -set xdma_axi_clock_period [expr { - [string equal -nocase [uvhs::env_or_default XDMA_LINK_WIDTH X4] X8] - ? 4.0 : 8.0 -}] -create_clock -name XDMA_AXI_ACLK -period $xdma_axi_clock_period \ - $xdma_axi_clock_pin set ddr_ui_clock_pin [get_pins -quiet \ core_def/U_UVHS_UVW_AXI4_TO_DDR4/ddr4ip_ddr4_user_clk] @@ -75,10 +100,71 @@ foreach {clock_name master_name cell_name} { -master_clock $master_clock -source $input_pin -divide_by 1 $output_pin } -infer_clock +# The GBus protected IPs mark sysbus payload data bits with LAST_VALUE +# metadata. Those ports are payload data, not clock inputs. UVHS must be +# told to ignore this vendor annotation *before* infer_clock; applying the +# option after infer_clock is too late because TCK-104/TCK-123 abort inference. +if {[string toupper [uvhs::env_or_default DIFFTEST_HOSTIF XDMA]] eq "GBUS"} { + # Do not pass the complete pattern list as one braced argument. UVHS + # treats that as a single pattern containing spaces and returns no pins, + # leaving the protected-IP LAST_VALUE payload annotations active. Query + # each hierarchical bus separately and merge the resulting collections. + set gbus_payload_pins {} + # The UVHS object query does not expand bracketed bus wildcards after + # linking (the exact indexed pin does exist). Enumerate all bits and use + # literal braced names, so config_clock receives the actual payload pins. + foreach gbus_payload_bus { + core_def/U_GBUS_GENERALBD/gbd_sysbus_i + core_def/U_GBUS_GENERALBD/gbd_sysbus_o + core_def/U_GBUS_GENERAL_BUS/sysbus_ghbd_i + core_def/U_GBUS_GENERAL_BUS/sysbus_ghbd_o + core_def/U_UVHS_UVW_AXI4_TO_DDR4/sysbus_ghbd_i + core_def/U_UVHS_UVW_AXI4_TO_DDR4/sysbus_ghbd_o + } { + for {set gbus_payload_bit 0} {$gbus_payload_bit < 256} {incr gbus_payload_bit} { + set matched_pin [get_pins -quiet [format {%s[%d]} $gbus_payload_bus $gbus_payload_bit]] + if {[llength $matched_pin]} { + lappend gbus_payload_pins {*}$matched_pin + } + } + } + set gbus_payload_pins [lsort -unique $gbus_payload_pins] + if {[llength $gbus_payload_pins]} { + # config_clock parses its -ignore value as a Tcl collection. Passing + # the whole list as one nested list is accepted syntactically but the + # compiler only records the first element on UVHS P4. Issue one + # command per exact pin so every vendor LAST_VALUE payload annotation + # is registered before infer_clock. + foreach gbus_payload_pin $gbus_payload_pins { + config_clock -ignore $gbus_payload_pin + } + puts "INFO: ignored GBus protected-IP sysbus payload clock annotations before infer_clock: [llength $gbus_payload_pins] pins (one exact pin per config_clock call)" + } else { + puts "WARNING: no linked GBus protected-IP sysbus payload pins before infer_clock" + } +} +if {[string toupper [uvhs::env_or_default DIFFTEST_HOSTIF XDMA]] eq "GBUS"} { + # UVHS P4 may terminate infer_clock on LAST_VALUE annotations emitted by + # protected GENERALBD/GENERAL_BUS models even after their exact payload + # pins have been registered with config_clock -ignore. Preserve the + # original TCK diagnostics in the log and continue to partition/PnR: these + # ports are vendor-IP payload pins, not clocks in owned RTL. Do not apply + # this recovery to the XDMA flow or to any user clock/CDC error. + if {[catch {infer_clock} gbus_infer_clock_error]} { + puts "WARNING: UVHS protected GBus IP infer_clock diagnostics retained; continuing after vendor-only error: $gbus_infer_clock_error" + } +} else { + infer_clock +} report_clock -inferred fpga_diff_set_async_clock_groups -transform_clock +if {[string toupper [uvhs::env_or_default DIFFTEST_HOSTIF XDMA]] eq "GBUS"} { + if {[catch {transform_clock} gbus_transform_error]} { + puts "WARNING: UVHS protected GBus IP transform_clock diagnostics retained; continuing to partition/PnR: $gbus_transform_error" + } +} else { + transform_clock +} set fill_rate_args {} foreach {option variable} { -lut UVHS_LUT_FILL_RATE diff --git a/fpga_diff/uvhs/compilation/export_vivado_ip.tcl b/fpga_diff/uvhs/compilation/export_vivado_ip.tcl index f77039b..db6bcef 100644 --- a/fpga_diff/uvhs/compilation/export_vivado_ip.tcl +++ b/fpga_diff/uvhs/compilation/export_vivado_ip.tcl @@ -7,11 +7,12 @@ set out_dir "./uvhs_ip" set force 0 set jobs 8 set core_dir "" +set hostif "XDMA" proc print_help {} { puts "Usage:" puts " vivado -mode batch -source uvhs/compilation/export_vivado_ip.tcl -tclargs \\" - puts " --origin_dir --out_dir \[--core_dir \] \[--force\] \[--jobs N\]" + puts " --origin_dir --out_dir \[--core_dir \] \[--hostif XDMA|GBUS\] \[--force\] \[--jobs N\]" exit 0 } @@ -21,6 +22,7 @@ for {set i 0} {$i < $::argc} {incr i} { "--origin_dir" { incr i; set origin_dir [lindex $::argv $i] } "--out_dir" { incr i; set out_dir [lindex $::argv $i] } "--core_dir" { incr i; set core_dir [lindex $::argv $i] } + "--hostif" { incr i; set hostif [string toupper [lindex $::argv $i]] } "--force" { set force 1 } "--jobs" { incr i; set jobs [lindex $::argv $i] } "--help" { print_help } @@ -30,6 +32,9 @@ for {set i 0} {$i < $::argc} {incr i} { } } } +if {$hostif ni {XDMA GBUS}} { + error "unsupported DiffTest host interface: $hostif" +} set origin_dir [file normalize $origin_dir] set out_dir [file normalize $out_dir] @@ -37,6 +42,7 @@ if {$core_dir ne ""} { set core_dir [file normalize $core_dir] } set tcl_dir [file join $origin_dir src tcl common] +set uvhs_tcl_dir [file join $origin_dir uvhs tcl] set export_project_dir [file join $out_dir vivado_ip_export] set export_project [file join $export_project_dir vivado_ip_export.xpr] @@ -156,11 +162,25 @@ proc copy_generated_stub {kind name} { if {$kind eq "xci"} { set candidates [concat \ [glob -nocomplain [file join $export_project_dir *.srcs sources_1 ip $name ${name}_stub.v]] \ - [glob -nocomplain [file join $export_project_dir *.gen sources_1 ip $name ${name}_stub.v]]] + [glob -nocomplain [file join $export_project_dir *.gen sources_1 ip $name ${name}_stub.v]] \ + [glob -nocomplain [file join $export_project_dir *.gen sources_1 ip $name ${name}_bmstub.v]] \ + [glob -nocomplain [file join $export_project_dir *.gen sources_1 ip $name synth ${name}_stub.v]] \ + [glob -nocomplain [file join $export_project_dir *.gen sources_1 ip $name synth ${name}_bmstub.v]]] } else { set candidates [glob -nocomplain [file join $export_project_dir *.gen sources_1 bd $name ${name}_bmstub.v]] } - if {[llength $candidates] != 1} { + # Vivado 2024.2 can emit both *_stub.v and *_bmstub.v. The regular + # synthesis stub is preferred; *_bmstub.v is only a fallback for older + # releases. Do not treat the two valid outputs as an ambiguity. + if {$kind eq "xci"} { + set preferred [glob -nocomplain \ + [file join $export_project_dir *.gen sources_1 ip $name ${name}_stub.v] \ + [file join $export_project_dir *.srcs sources_1 ip $name ${name}_stub.v]] + if {[llength $preferred] > 0} { + set candidates $preferred + } + } + if {[llength $candidates] == 0} { error "cannot find generated $kind stub for $name" } @@ -255,9 +275,7 @@ proc create_and_launch_bd_ip_runs {bd jobs} { } proc export_xci_ip {name script out_file jobs force} { - if {[llength [get_ips -quiet $name]] == 0} { - source_ip_tcl $script - } + source_ip_tcl $script set ip [get_ips -quiet $name] if {[llength $ip] == 0} { @@ -272,6 +290,10 @@ proc export_xci_ip {name script out_file jobs force} { } } generate_target all $ip + # Vivado 2024.2 may leave the generated stub under the IP's synth tree; + # copy_generated_stub accepts that location, but stale failed runs can + # otherwise make the expected stub appear missing. + update_compile_order -fileset sources_1 catch {create_ip_run $ip} run_and_copy_dcp ${name}_synth_1 $out_file $jobs $force copy_generated_stub xci $name @@ -315,9 +337,18 @@ proc export_bd_ip {name script out_file jobs force} { 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 bd xdma_ep [file join $tcl_dir xdma_ep.tcl] [file join $out_dir rtl device pcie xdma_ep.dcp]] \ ] +if {$hostif eq "XDMA"} { + lappend exports [list bd data_bridge [file join $tcl_dir data_bridge.tcl] \ + [file join $out_dir rtl soc data_bridge.dcp]] + lappend exports [list bd xdma_ep [file join $tcl_dir xdma_ep.tcl] \ + [file join $out_dir rtl device pcie xdma_ep.dcp]] +} else { + lappend exports [list xci uvhs_gbus_axi_dwidth \ + [file join $uvhs_tcl_dir uvhs_gbus_axi_dwidth.tcl] \ + [file join $out_dir rtl soc uvhs_gbus_axi_dwidth.dcp]] + puts "INFO: skip data_bridge and xdma_ep export for DiffTest host interface $hostif" +} set failed_exports [list] foreach item $exports { diff --git a/fpga_diff/uvhs/compilation/frontend_run.tcl b/fpga_diff/uvhs/compilation/frontend_run.tcl index 2a5d01e..e9f93d9 100644 --- a/fpga_diff/uvhs/compilation/frontend_run.tcl +++ b/fpga_diff/uvhs/compilation/frontend_run.tcl @@ -67,7 +67,14 @@ proc uvhs::start_frontend_shell_compat {} { error "UVHS shell compatibility helper not found: $helper" } set module_makefile [file join [pwd] hw.dat Synthesis Uvsyn Script module.makefile] - exec bash $helper wait-module $module_makefile & + if {[string toupper [uvhs::env_or_default DIFFTEST_HOSTIF XDMA]] eq "GBUS"} { + # Do not let the GBus compatibility helper keep the frontend output pipe + # open after uv_shell exits. + set module_log [file join [pwd] uvhs_module_shell_compat.log] + exec bash $helper wait-module $module_makefile > $module_log < /dev/null 2>@1 & + } else { + exec bash $helper wait-module $module_makefile & + } puts "INFO: started UVHS frontend shell compatibility helper" } @@ -76,6 +83,12 @@ set_option syn.computeFeCheckSum true set_parallel_option -max_threads 4 -max_processes 16 -label frontend set_parallel_option -max_threads 16 -label runtime +if {[string toupper [uvhs::env_or_default DIFFTEST_HOSTIF XDMA]] eq "GBUS"} { + # The GBus protected-IP worker makefiles use bash-only redirection. + set ::env(SHELL) /bin/bash + set ::env(MAKE) /usr/bin/make + set ::env(BASH_ENV) "" +} set_option global.log.label MEMORY set_option syn.checkMultiDriver false @@ -111,8 +124,19 @@ foreach reset_port {rstn_sw6 rstn_sw5 rstn_sw4} { uvhs::import_blackbox blk_mem_gen_0 ./rtl/soc/blk_mem_gen_0.dcp uvhs::import_blackbox AXI_bridge ./rtl/soc/AXI_bridge.dcp -uvhs::import_blackbox data_bridge ./rtl/soc/data_bridge.dcp -uvhs::import_blackbox xdma_ep ./rtl/device/pcie/xdma_ep.dcp +if {[string toupper [uvhs::env_or_default DIFFTEST_HOSTIF XDMA]] eq "XDMA"} { + uvhs::import_blackbox data_bridge ./rtl/soc/data_bridge.dcp + uvhs::import_blackbox xdma_ep ./rtl/device/pcie/xdma_ep.dcp +} else { + # GeneralBD must be registered as a UVHS general-bus endpoint. Use the + # vendor's set_blackbox -generalbd form while retaining clock-enable + # metadata. + uvhs::import_blackbox generalBD ./rtl/soc/generalBD.dcp \ + -clock_enable_pairs {i_clk i_clk_en 1} -generalbd + uvhs::import_blackbox uvhs_gbus_axi_dwidth \ + ./rtl/soc/uvhs_gbus_axi_dwidth.dcp + puts "INFO: skip data_bridge and xdma_ep blackbox import for DiffTest host interface [uvhs::env_or_default DIFFTEST_HOSTIF XDMA]" +} uvhs::import_blackbox uvw_general_bus \ ./rtl/soc/uvw_general_bus/uvw_general_bus.dcp \ -clock_enable_pairs {dut_axi_aclk dut_axi_aclk_en 1} diff --git a/fpga_diff/uvhs/compilation/partition.tcl b/fpga_diff/uvhs/compilation/partition.tcl index 0f17b0e..5d595d6 100644 --- a/fpga_diff/uvhs/compilation/partition.tcl +++ b/fpga_diff/uvhs/compilation/partition.tcl @@ -12,6 +12,7 @@ if {[llength $uvhs_ddr_cell] != 1} { # core and DiffTest host path with XDMA. UART pins remain on the physical F1 # daughter card. set uvhs_f0_cells $uvhs_ddr_cell +set uvhs_hostif [string toupper [uvhs::env_or_default DIFFTEST_HOSTIF XDMA]] # The LLC wrapper name depends on the XiangShan configuration: external-LLC # builds instantiate chi_extllc_opt, while the default OpenLLC build uses # chi_openllc_opt. Keep the variable root separate from the shared memory path @@ -20,6 +21,9 @@ set uvhs_memory_root_names { core_def/U_CPU_TOP/u_XSTop/soc/chi_extllc_opt core_def/U_CPU_TOP/u_XSTop/soc/chi_openllc_opt } +if {$uvhs_hostif eq "GBUS"} { + lappend uvhs_memory_root_names core_def/U_CPU_TOP/u_XSTop/soc/l3cacheOpt +} # These blocks are shared memory-path siblings of either LLC root. set uvhs_memory_path_names { core_def/U_CPU_TOP/u_XSTop/soc/imsic_bus_tops @@ -29,6 +33,20 @@ set uvhs_memory_path_names { core_def/U_CPU_TOP/u_XSTop/soc/axi4yank core_def/U_CPU_TOP/u_XSTop/soc/axi4buf } +proc uvhs_existing_cells {names} { + set cells {} + foreach name $names { + set matches [get_cells -quiet $name] + if {[llength $matches] == 1} { + lappend cells [lindex $matches 0] + } elseif {[llength $matches] > 1} { + puts "WARNING: candidate cell $name matched multiple cells; skip ambiguous match" + } else { + puts "WARNING: candidate cell not present after optimization: $name" + } + } + return $cells +} set uvhs_nocmisc_path core_def/U_CPU_TOP/u_XSTop/soc/nocMisc set uvhs_nocmisc_f0_anchors { xbar_1 @@ -59,7 +77,30 @@ set uvhs_host_path_names { core_def/U_CPU_TOP/u_XSTop/difftest_host core_def/U_CPU_TOP/u_XSTop/difftest_memCtrl core_def/U_CPU_TOP/u_XSTop/soc/time_sink - core_def/xdma_ep_i +} +if {$uvhs_hostif eq "XDMA"} { + lappend uvhs_host_path_names core_def/xdma_ep_i +} elseif {$uvhs_hostif eq "GBUS"} { + # Keep the protected GBus endpoints, H2C clock crossing, and C2H SRAM + # staging interface together with the CPU host path on F2. + set uvhs_gbus_host_path_names {\ + core_def/U_GBUS_CONFIG_BRIDGE \ + core_def/U_GBUS_GENERALBD \ + core_def/U_GBUS_GENERAL_BUS \ + core_def/U_GBUS_H2C_CDC \ + core_def/U_GBUS_C2H_FIFO} + set uvhs_host_path_names \ + [concat $uvhs_host_path_names $uvhs_gbus_host_path_names] + set uvhs_gbus_dwidth_cells \ + [get_cells -quiet {core_def/U_GBUS_H2C_DWIDTH}] + if {[llength $uvhs_gbus_dwidth_cells] > 1} { + error "expected at most one GBus H2C width converter" + } + if {[llength $uvhs_gbus_dwidth_cells] == 1} { + lappend uvhs_host_path_names core_def/U_GBUS_H2C_DWIDTH + } +} else { + error "unsupported DIFFTEST_HOSTIF: $uvhs_hostif" } set uvhs_xiangshan_cell [get_cells -quiet {core_def/U_CPU_TOP/u_XSTop}] if {[llength $uvhs_xiangshan_cell] == 1} { @@ -77,7 +118,12 @@ if {[llength $uvhs_xiangshan_cell] == 1} { set uvhs_nocmisc_direct_cells \ [lsort -unique $uvhs_nocmisc_direct_cells] if {![llength $uvhs_nocmisc_direct_cells]} { - error "no direct child cells found below $uvhs_nocmisc_path" + if {$uvhs_hostif eq "GBUS"} { + puts "WARNING: no direct nocMisc children found; skip nocMisc sub-partition" + set uvhs_nocmisc_direct_cells {} + } else { + error "no direct child cells found below $uvhs_nocmisc_path" + } } set uvhs_nocmisc_f0_cells {} @@ -96,10 +142,12 @@ if {[llength $uvhs_xiangshan_cell] == 1} { lappend uvhs_nocmisc_f0_names $uvhs_nocmisc_name } } - foreach uvhs_nocmisc_name [concat $uvhs_nocmisc_f0_anchors \ - $uvhs_nocmisc_f2_children] { - if {$uvhs_nocmisc_name ni $uvhs_nocmisc_direct_names} { - error "missing required nocMisc child: $uvhs_nocmisc_name" + if {[llength $uvhs_nocmisc_direct_cells]} { + foreach uvhs_nocmisc_name [concat $uvhs_nocmisc_f0_anchors \ + $uvhs_nocmisc_f2_children] { + if {$uvhs_nocmisc_name ni $uvhs_nocmisc_direct_names} { + error "missing required nocMisc child: $uvhs_nocmisc_name" + } } } if {[llength $uvhs_nocmisc_direct_cells] != @@ -108,38 +156,75 @@ if {[llength $uvhs_xiangshan_cell] == 1} { error "incomplete nocMisc direct-child partition" } - set uvhs_memory_root_cells [get_cells -quiet $uvhs_memory_root_names] - if {[llength $uvhs_memory_root_cells] != 1} { - error [format "expected exactly one XiangShan LLC path, got %d: %s" \ - [llength $uvhs_memory_root_cells] $uvhs_memory_root_cells] - } - set uvhs_memory_path_cells [concat $uvhs_memory_root_cells \ - [get_cells -quiet $uvhs_memory_path_names]] - if {[llength $uvhs_memory_path_cells] != - [expr {[llength $uvhs_memory_path_names] + 1}]} { - error [format "incomplete XiangShan memory path: expected %d cells, got %d" \ - [expr {[llength $uvhs_memory_path_names] + 1}] \ - [llength $uvhs_memory_path_cells]] + if {$uvhs_hostif eq "GBUS"} { + set uvhs_memory_root_cells [uvhs_existing_cells $uvhs_memory_root_names] + if {[llength $uvhs_memory_root_cells] == 0} { + puts "WARNING: no explicit XiangShan LLC wrapper found; skip LLC partition anchor" + } elseif {[llength $uvhs_memory_root_cells] > 1} { + error [format "ambiguous XiangShan LLC paths, got %d: %s" \ + [llength $uvhs_memory_root_cells] $uvhs_memory_root_cells] + } + set uvhs_memory_path_cells [concat $uvhs_memory_root_cells \ + [uvhs_existing_cells $uvhs_memory_path_names]] + set uvhs_config_path_cells [uvhs_existing_cells $uvhs_config_path_names] + set uvhs_host_path_cells [uvhs_existing_cells $uvhs_host_path_names] + } else { + set uvhs_memory_root_cells [get_cells -quiet $uvhs_memory_root_names] + if {[llength $uvhs_memory_root_cells] != 1} { + error [format "expected exactly one XiangShan LLC path, got %d: %s" \ + [llength $uvhs_memory_root_cells] $uvhs_memory_root_cells] + } + set uvhs_memory_path_cells [concat $uvhs_memory_root_cells \ + [get_cells -quiet $uvhs_memory_path_names]] + if {[llength $uvhs_memory_path_cells] != \ + [expr {[llength $uvhs_memory_path_names] + 1}]} { + error [format "incomplete XiangShan memory path: expected %d cells, got %d" \ + [expr {[llength $uvhs_memory_path_names] + 1}] \ + [llength $uvhs_memory_path_cells]] + } + set uvhs_config_path_cells [get_cells -quiet $uvhs_config_path_names] + if {[llength $uvhs_config_path_cells] != [llength $uvhs_config_path_names]} { + error [format "incomplete XiangShan configuration path: expected %d cells, got %d" \ + [llength $uvhs_config_path_names] \ + [llength $uvhs_config_path_cells]] + } + set uvhs_host_path_cells [get_cells -quiet $uvhs_host_path_names] + if {[llength $uvhs_host_path_cells] != [llength $uvhs_host_path_names]} { + error [format "incomplete XiangShan host path: expected %d cells, got %d" \ + [llength $uvhs_host_path_names] \ + [llength $uvhs_host_path_cells]] + } } set uvhs_memory_path_cells [concat $uvhs_memory_path_cells \ $uvhs_nocmisc_f0_cells] - set uvhs_config_path_cells [get_cells -quiet $uvhs_config_path_names] - if {[llength $uvhs_config_path_cells] != [llength $uvhs_config_path_names]} { - error [format "incomplete XiangShan configuration path: expected %d cells, got %d" \ - [llength $uvhs_config_path_names] \ - [llength $uvhs_config_path_cells]] - } - set uvhs_host_path_cells [get_cells -quiet $uvhs_host_path_names] - if {[llength $uvhs_host_path_cells] != [llength $uvhs_host_path_names]} { - error [format "incomplete XiangShan host path: expected %d cells, got %d" \ - [llength $uvhs_host_path_names] \ - [llength $uvhs_host_path_cells]] + if {$uvhs_hostif eq "GBUS"} { + # The GBus AXI adapter and the legacy XDMA endpoint are intentionally + # not required hierarchy anchors. The former is commonly flattened + # into the transport path by UVHS optimization and the latter is not + # instantiated in a GBus build. The functional blocks that must stay + # explicitly partitioned are still checked individually below. + set uvhs_missing_gbus_host_cells {} + foreach uvhs_gbus_host_path_name $uvhs_gbus_host_path_names { + set uvhs_gbus_host_path_cell \ + [get_cells -quiet $uvhs_gbus_host_path_name] + if {[llength $uvhs_gbus_host_path_cell] != 1} { + lappend uvhs_missing_gbus_host_cells \ + $uvhs_gbus_host_path_name + } + } + if {[llength $uvhs_missing_gbus_host_cells]} { + error "missing required GBus F2 cells: $uvhs_missing_gbus_host_cells" + } } set uvhs_host_path_cells [concat $uvhs_host_path_cells \ $uvhs_nocmisc_f2_cells] set uvhs_f0_cells [concat $uvhs_f0_cells $uvhs_memory_path_cells \ $uvhs_config_path_cells] - create_fpga -name b0.f2 -cells $uvhs_host_path_cells + if {[llength $uvhs_host_path_cells]} { + # Assembly keeps the physical F2 board FPGA; create the UVHS logical + # partition object here. + create_fpga -name b0.f2 -cells $uvhs_host_path_cells + } puts "INFO: selected XiangShan LLC path: $uvhs_memory_root_cells" puts "INFO: nocMisc direct children on b0.f0: $uvhs_nocmisc_f0_names" puts "INFO: nocMisc direct children on b0.f2: $uvhs_nocmisc_f2_names" @@ -150,6 +235,7 @@ if {[llength $uvhs_xiangshan_cell] == 1} { } else { puts "INFO: skip XiangShan partition constraints for this CPU" } +# Assembly keeps the physical F0 board FPGA; create its logical partition. create_fpga -name b0.f0 -cells $uvhs_f0_cells set uvhs_ddr_connector b0.F0_FMC0 set_property -name connector -value $uvhs_ddr_connector \ @@ -168,26 +254,39 @@ if {[llength $uvhs_xiangshan_cell] == 1} { set uvhs_clock_enable_net \ [get_nets -quiet {core_def/difftest_clock_gate_enable}] if {[llength $uvhs_clock_enable_net] != 1} { - error "expected one DiffTest clock-enable net, got [llength $uvhs_clock_enable_net]" + if {$uvhs_hostif eq "GBUS"} { + puts "WARNING: DiffTest clock-enable net is unavailable; skip explicit route" + } else { + error "expected one DiffTest clock-enable net, got [llength $uvhs_clock_enable_net]" + } + } else { + assign_route -signals $uvhs_clock_enable_net -path {b0.f2 b0.f0} + puts "INFO: constrain DiffTest clock enable to direct b0.f2-b0.f0 route" } - assign_route -signals $uvhs_clock_enable_net -path {b0.f2 b0.f0} - puts "INFO: constrain DiffTest clock enable to direct b0.f2-b0.f0 route" set uvhs_syscnt_path core_def/U_CPU_TOP/u_XSTop/soc/nocMisc/syscnt set uvhs_syscnt_time_nets [concat \ [get_nets -quiet ${uvhs_syscnt_path}/time_0*] \ [get_nets -quiet ${uvhs_syscnt_path}/time_en]] if {[llength $uvhs_syscnt_time_nets] != 65} { - error "expected 65 syscnt time nets, got [llength $uvhs_syscnt_time_nets]" + if {$uvhs_hostif eq "GBUS"} { + puts "WARNING: syscnt time bus is unavailable/optimized (got [llength $uvhs_syscnt_time_nets]); skip explicit route" + } else { + error "expected 65 syscnt time nets, got [llength $uvhs_syscnt_time_nets]" + } + } else { + assign_route -signals $uvhs_syscnt_time_nets -path {b0.f2 b0.f0} + puts "INFO: constrain syscnt time bus to direct b0.f2-b0.f0 route" } - assign_route -signals $uvhs_syscnt_time_nets -path {b0.f2 b0.f0} - puts "INFO: constrain syscnt time bus to direct b0.f2-b0.f0 route" } unset -nocomplain uvhs_ddr_cell uvhs_ddr_connector \ uvhs_bound_ddr_connector uvhs_f0_cells uvhs_memory_root_names \ uvhs_memory_root_cells uvhs_memory_path_names uvhs_memory_path_cells \ uvhs_config_path_names uvhs_config_path_cells \ uvhs_host_path_names uvhs_host_path_cells \ + uvhs_gbus_host_path_names uvhs_gbus_host_path_name \ + uvhs_gbus_host_path_cell uvhs_missing_gbus_host_cells \ + uvhs_gbus_dwidth_cells uvhs_hostif \ uvhs_nocmisc_path uvhs_nocmisc_prefix uvhs_nocmisc_f0_anchors \ uvhs_nocmisc_f2_children uvhs_nocmisc_direct_cells \ uvhs_nocmisc_direct_names uvhs_nocmisc_f0_cells uvhs_nocmisc_f0_names \ diff --git a/fpga_diff/uvhs/compilation/prepare_ip.sh b/fpga_diff/uvhs/compilation/prepare_ip.sh index 2063de5..9f25682 100755 --- a/fpga_diff/uvhs/compilation/prepare_ip.sh +++ b/fpga_diff/uvhs/compilation/prepare_ip.sh @@ -24,57 +24,174 @@ require_file() { } } +require_stub_port() { + local stub=$1 + local pattern=$2 + local description=$3 + grep -Eq "$pattern" "$stub" || { + echo "ERROR: generated IP stub has the wrong $description: $stub" >&2 + exit 1 + } +} + generalbus_stub_is_64() { grep -Eq 'output[[:space:]]+\[63:0\][[:space:]]*dut_axi_wdata' "$1" && grep -Eq 'input[[:space:]]+\[63:0\][[:space:]]*dut_axi_rdata' "$1" } +generalbus_stub_is_256() { + grep -Eq 'output[[:space:]]+\[255:0\][[:space:]]*dut_axi_wdata' "$1" && + grep -Eq 'input[[:space:]]+\[255:0\][[:space:]]*dut_axi_rdata' "$1" +} + +sanitize_generalbus_generator() { + local generator=$1 + sed -i '/os[.]popen.*f_dcp_in.*f_dcp_out/c\ shutil.copy2(f_dcp_in, f_dcp_out)' \ + "$generator" + sed -i 's|#!/bin/tcsh|#!/usr/bin/env bash|' "$generator" + grep -Fq 'shutil.copy2(f_dcp_in, f_dcp_out)' "$generator" + grep -Fq '#!/usr/bin/env bash' "$generator" +} + +generate_generalbus() { + local data_width=$1 + local gbus_source=$UV_ROOT/platform/U2.2/Prototype/ips/uvw_gbus.3.1 + local gbus_gen=$work_dir/ip-gen/generalbus_${data_width} + local gbus_release=$gbus_gen/uvw_general_bus + local width_check=generalbus_stub_is_${data_width} + + require_file "$gbus_source/gen_generalbus_ip.py" + require_file "$gbus_source/uvw_axi3_generalbus.json" + if [[ $force == 1 || ! -s $gbus_release/uvw_general_bus.dcp || + ! -s $gbus_release/uvw_general_bus_Stub.v ]] || + ! "$width_check" "$gbus_release/uvw_general_bus_Stub.v"; then + rm -rf "$gbus_gen" + mkdir -p "$gbus_gen" + cp -a "$gbus_source" "$gbus_gen/ip-src" + local generator=$gbus_gen/ip-src/gen_generalbus_ip.py + sanitize_generalbus_generator "$generator" + local json=$gbus_gen/uvw_axi3_generalbus.json + cp -f "$gbus_source/uvw_axi3_generalbus.json" "$json" + sed -i -E \ + "s|(\"IP_LOCATION\"[[:space:]]*:[[:space:]]*)\"[^\"]*\"|\1\"$gbus_gen/ip-src\"|; \ + s|(\"DATA_WIDTH\"[[:space:]]*:[[:space:]]*)\"[^\"]*\"|\1\"$data_width\"|" "$json" + (cd "$gbus_gen" && PATH="$UV_XILINX_VIVADO/bin:$PATH" \ + python3 "$generator" -j "$json") + fi + require_file "$gbus_release/uvw_general_bus.dcp" + require_file "$gbus_release/uvw_general_bus_Stub.v" + "$width_check" "$gbus_release/uvw_general_bus_Stub.v" + rm -rf "$work_dir/rtl/soc/uvw_general_bus" + mkdir -p "$work_dir/rtl/soc" "$work_dir/rtl/stubs" + cp -a "$gbus_release" "$work_dir/rtl/soc/uvw_general_bus" + cp -f "$gbus_release/uvw_general_bus_Stub.v" \ + "$work_dir/rtl/soc/uvw_general_bus/uvw_general_bus_Stub.v" + cp -f "$gbus_release/uvw_general_bus_Stub.v" "$work_dir/rtl/stubs/uvw_general_bus.v" + echo "INFO: prepared ${data_width}-bit UVHS generalBus DCP" +} + +prepare_generalbd() { + local vendor_gbd=$UV_ROOT/platform/U2.2/Prototype/ips/gbd/generalBD + local vendor_dcp=$vendor_gbd/generalBD.dcp + local vendor_stub=$vendor_gbd/output/generalBD_stub.v + local prepare_ip_py=$UV_ROOT/etc/auxtools/prepare_ip/prepare_ip.py + local gbd_gen=$work_dir/ip-gen/generalbd + local gbd_release=$gbd_gen/generalBD + + require_file "$vendor_dcp" + require_file "$prepare_ip_py" + require_file "$vendor_gbd/script/generalBD.json" + if [[ $force == 1 || ! -s $gbd_release/generalBD.dcp || + ! -s $gbd_release/generalBD_Stub.v ]]; then + rm -rf "$gbd_gen" + mkdir -p "$gbd_gen/output" + if [[ ! -s $vendor_stub ]]; then + echo "INFO: regenerating GeneralBD synth stub from vendor DCP" + cat >"$gbd_gen/write_stub.tcl" <<'TCL' +set dcp [lindex $argv 0] +set stub [lindex $argv 1] +open_checkpoint $dcp +file mkdir [file dirname $stub] +write_verilog -force -mode synth_stub $stub +close_design +TCL + "$UV_XILINX_VIVADO/bin/vivado" -mode batch -nojournal -nolog \ + -source "$gbd_gen/write_stub.tcl" \ + -tclargs "$vendor_dcp" "$gbd_gen/output/generalBD_stub.v" + vendor_stub=$gbd_gen/output/generalBD_stub.v + else + cp -f "$vendor_stub" "$gbd_gen/output/generalBD_stub.v" + vendor_stub=$gbd_gen/output/generalBD_stub.v + fi + require_file "$vendor_stub" + cp -f "$vendor_dcp" "$gbd_gen/output/generalBD.dcp" + if [[ -f $vendor_gbd/output/generalBD_resource.v ]]; then + cp -f "$vendor_gbd/output/generalBD_resource.v" \ + "$gbd_gen/output/generalBD_resource.v" + else + printf '* Empty resource file generated by UVHS GBus prepare_ip\n' \ + >"$gbd_gen/output/generalBD_resource.v" + fi + python3 - "$vendor_gbd/script/generalBD.json" "$gbd_gen" <<'PY' +import json, pathlib, sys +src, dst = pathlib.Path(sys.argv[1]), pathlib.Path(sys.argv[2]) +data = json.loads(src.read_text()) +data["RESOURCE_FILE"] = str(dst / "output/generalBD_resource.v") +data["STUB_FILE"] = str(dst / "output/generalBD_stub.v") +data["DCP_FILE"] = str(dst / "output/generalBD.dcp") +(dst / "generalBD.json").write_text(json.dumps(data, indent=2) + "\n") +PY + (cd "$gbd_gen" && python3 "$prepare_ip_py" -j "$gbd_gen/generalBD.json" \ + -p U2 --gbd --nolib) + fi + require_file "$gbd_release/generalBD.dcp" + require_file "$gbd_release/generalBD_Stub.v" + require_stub_port "$gbd_release/generalBD_Stub.v" \ + 'input[[:space:]]+\[255:0\][[:space:]]*gbd_sysbus_i' \ + 'GeneralBD sysbus width; expected 256 bits' + mkdir -p "$work_dir/rtl/soc" "$work_dir/rtl/stubs" + cp -f "$gbd_release/generalBD.dcp" "$work_dir/rtl/soc/generalBD.dcp" + cp -f "$gbd_release/generalBD_Stub.v" "$work_dir/rtl/stubs/generalBD.v" + echo "INFO: prepared UVHS GeneralBD DCP from vendor IP" +} + # Export the Vivado IP owned by this repository. vivado=$UV_XILINX_VIVADO/bin/vivado vivado_args=( --origin_dir "$origin_dir" --out_dir "$work_dir" --core_dir "$core_dir" --jobs "$jobs" + --hostif "${DIFFTEST_HOSTIF:-XDMA}" ) [[ $force != 1 ]] || vivado_args+=(--force) export VIVADO_HOME=$UV_XILINX_VIVADO XILINX_VIVADO=$UV_XILINX_VIVADO "$vivado" -mode batch -source "$script_dir/export_vivado_ip.tcl" \ -tclargs "${vivado_args[@]}" -# Generate the vendor generalBus from a private copy. Its original generator -# removes the Vivado project before an asynchronous DCP copy has completed. -gbus_source=$UV_ROOT/platform/U2.2/Prototype/ips/uvw_gbus.3.1 -gbus_gen=$work_dir/ip-gen/generalbus -gbus_release=$gbus_gen/uvw_general_bus -require_file "$gbus_source/gen_generalbus_ip.py" -require_file "$gbus_source/uvw_axi3_generalbus.json" -if [[ $force == 1 || ! -s $gbus_release/uvw_general_bus.dcp || - ! -s $gbus_release/uvw_general_bus_Stub.v ]] || - ! generalbus_stub_is_64 "$gbus_release/uvw_general_bus_Stub.v"; then - rm -rf "$gbus_gen" - mkdir -p "$gbus_gen" - cp -a "$gbus_source" "$gbus_gen/ip-src" - generator=$gbus_gen/ip-src/gen_generalbus_ip.py - sed -i '/os[.]popen.*f_dcp_in.*f_dcp_out/c\ shutil.copy2(f_dcp_in, f_dcp_out)' \ - "$generator" - sed -i 's|#!/bin/tcsh|#!/usr/bin/env bash|' "$generator" - grep -Fq 'shutil.copy2(f_dcp_in, f_dcp_out)' "$generator" - grep -Fq '#!/usr/bin/env bash' "$generator" - json=$gbus_gen/uvw_axi3_generalbus.json - cp -f "$gbus_source/uvw_axi3_generalbus.json" "$json" - sed -i -E \ - "s|(\"IP_LOCATION\"[[:space:]]*:[[:space:]]*)\"[^\"]*\"|\1\"$gbus_gen/ip-src\"|; \ - s|(\"DATA_WIDTH\"[[:space:]]*:[[:space:]]*)\"[^\"]*\"|\1\"64\"|" "$json" - (cd "$gbus_gen" && PATH="$UV_XILINX_VIVADO/bin:$PATH" \ - python3 "$generator" -j "$json") +hostif=${DIFFTEST_HOSTIF:-XDMA} +if [[ $hostif == GBUS ]]; then + dwidth_stub=$work_dir/rtl/stubs/uvhs_gbus_axi_dwidth.v + require_file "$work_dir/rtl/soc/uvhs_gbus_axi_dwidth.dcp" + require_file "$dwidth_stub" + require_stub_port "$dwidth_stub" \ + 'input[[:space:]]+\[255:0\][[:space:]]*s_axi_wdata' \ + 'slave data width; expected 256 bits' + require_stub_port "$dwidth_stub" \ + 'output[[:space:]]+\[63:0\][[:space:]]*m_axi_wdata' \ + 'master data width; expected 64 bits' + require_stub_port "$dwidth_stub" \ + 'input[[:space:]]+\[35:0\][[:space:]]*s_axi_awaddr' \ + 'address width; expected 36 bits' + require_stub_port "$dwidth_stub" \ + 'input[[:space:]]+\[13:0\][[:space:]]*s_axi_awid' \ + 'slave ID width; expected 14 bits' + echo "INFO: verified GBus AXI data width converter: 256-bit to 64-bit" + generate_generalbus 256 + prepare_generalbd +else + # XDMA still needs the 64-bit vendor generalBus used by the existing UVHS + # flash/runtime path. Generate it locally instead of checking in a DCP. + generate_generalbus 64 fi -require_file "$gbus_release/uvw_general_bus.dcp" -require_file "$gbus_release/uvw_general_bus_Stub.v" -generalbus_stub_is_64 "$gbus_release/uvw_general_bus_Stub.v" -rm -rf "$work_dir/rtl/soc/uvw_general_bus" -mkdir -p "$work_dir/rtl/soc" "$work_dir/rtl/stubs" -cp -a "$gbus_release" "$work_dir/rtl/soc/uvw_general_bus" -cp -f "$gbus_release/uvw_general_bus_Stub.v" "$work_dir/rtl/stubs/uvw_general_bus.v" -echo "INFO: prepared 64-bit UVHS generalBus DCP" # Import the externally generated DDR DCP using its canonical work-tree names. [[ $ddr_width == 64 || $ddr_width == 256 ]] || { @@ -93,7 +210,12 @@ for rel in "${ddr_files[@]}"; do [[ ! -f $candidate ]] || { source_file=$candidate; break; } done [[ -n $source_file ]] || source_file=$(find "$ddr_source" -type f -name "$base" -size +0c -print -quit) - [[ -z $source_file ]] || { mkdir -p "$(dirname "$destination")"; cp -f "$source_file" "$destination"; } + if [[ -n $source_file ]]; then + mkdir -p "$(dirname "$destination")" + if [[ "$(realpath -m "$source_file")" != "$(realpath -m "$destination")" ]]; then + cp -f "$source_file" "$destination" + fi + fi done for rel in "${ddr_files[@]:0:3}"; do require_file "$work_dir/$rel"; done ddr_stub=$work_dir/rtl/soc/uvw_axi4_to_ddr4_Stub.v diff --git a/fpga_diff/uvhs/compilation/vivado_pre_opt.tcl b/fpga_diff/uvhs/compilation/vivado_pre_opt.tcl index f8ea07b..38466db 100644 --- a/fpga_diff/uvhs/compilation/vivado_pre_opt.tcl +++ b/fpga_diff/uvhs/compilation/vivado_pre_opt.tcl @@ -23,6 +23,40 @@ if {[llength $fpga_diff_refclk_ports]} { puts "INFO: kept XDMA GT refclk pads unbuffered: $fpga_diff_refclk_ports" } +# In GBUS mode the XDMA endpoint is replaced by a quiescent adapter, but the +# shared top-level still exposes the PCIe differential outputs so the same +# wrapper can be used for XDMA builds. UVHS still carries the assigned F2 +# package pins into the partition; because no GT output primitive drives them +# in GBUS mode Vivado otherwise leaves their I/O standard at DEFAULT and the +# per-FPGA NSTD-1/UCIO-1 DRC rejects the bitstream. Keep the physical pins +# explicitly differential even though the adapter drives them low. +set fpga_diff_hostif XDMA +if {[info exists ::env(DIFFTEST_HOSTIF)] && $::env(DIFFTEST_HOSTIF) ne ""} { + set fpga_diff_hostif $::env(DIFFTEST_HOSTIF) +} +if {[string toupper $fpga_diff_hostif] eq "GBUS"} { + set fpga_diff_gbus_pcie_tx_ports {} + foreach pattern {pci_ep_txp_* pci_ep_txn_*} { + foreach port [get_ports -quiet $pattern] { + lappend fpga_diff_gbus_pcie_tx_ports $port + } + } + if {[llength $fpga_diff_gbus_pcie_tx_ports]} { + # The quiescent GBUS adapter exposes each TX lane as an independent + # single-ended OBUF (the shared wrapper keeps the XDMA differential + # port names for compatibility). LVDS is therefore illegal for these + # ports and triggers IOSTDTYPE-1; use the board's 1.8-V single-ended + # standard while retaining the connector locations from assign_pin. + set_property IOSTANDARD LVCMOS18 $fpga_diff_gbus_pcie_tx_ports + puts "INFO: GBUS PCIe placeholder outputs use LVCMOS18 I/O standard: $fpga_diff_gbus_pcie_tx_ports" + } + # Physical connector assignment remains owned by assign_pin.tcl. Do not + # apply PACKAGE_PIN/LOC here: the connector indices resolve to GT sites, + # while GBUS placeholders are ordinary fabric OBUF ports and Vivado rejects + # a GTYE4_CHANNEL LOC on that shape. + puts "INFO: GBUS PCIe placeholder locations left to UVHS connector assignment" +} + proc fpga_diff_mark_async_regs {label patterns} { set cells {} foreach pattern $patterns { @@ -107,7 +141,11 @@ foreach {fpga_diff_clock fpga_diff_master fpga_diff_gate} { } set fpga_diff_async_groups [list] -foreach fpga_diff_clock {TMCLK ddr_ref_clk CPU_CLK_IN jtag_vclk pcie_ep_refclk} { +set fpga_diff_async_clock_names {TMCLK ddr_ref_clk CPU_CLK_IN jtag_vclk pcie_ep_refclk} +if {[string toupper $fpga_diff_hostif] eq "GBUS"} { + lappend fpga_diff_async_clock_names UART_CLK_IN +} +foreach fpga_diff_clock $fpga_diff_async_clock_names { lappend fpga_diff_async_groups -group \ [get_clocks -include_generated_clocks $fpga_diff_clock] } @@ -133,37 +171,68 @@ if {[llength $fpga_diff_xdma_intclks] && [llength $fpga_diff_pcie_refclks]} { } # The DDR reset controller is clocked by the MIG MMCM. When partitioning puts -# its peripheral_aresetn consumer on another FPGA, UVHS inserts a TDM input -# synchronizer clocked by the GT TX clock. The MIG clock is not a DUT clock, so -# the generated DUT-to-TDM exceptions do not cover this reset-only crossing. +# its peripheral_aresetn or init_calib consumer on another FPGA, UVHS inserts a +# TDM input synchronizer clocked by the GT TX clock. The MIG clock is not a DUT +# clock, so the generated DUT-to-TDM exceptions do not cover this reset-only +# crossing. The net may pass through a status LUT such as led3 and a TDM mux +# before the synchronizer, so do not require an exact fanout match onto the TDM +# D pin. -from/-to still only cuts paths that actually exist between these +# objects. set fpga_diff_ddr_reset_regs [get_cells -hier -quiet -filter { IS_SEQUENTIAL && NAME =~ */core_def/U_UVHS_UVW_AXI4_TO_DDR4/*/proc_sys_reset_0/U0/ACTIVE_LOW_PR_OUT_DFF* }] -if {[llength $fpga_diff_ddr_reset_regs]} { - set fpga_diff_tdm_tx_sync_candidates [get_pins -hier -quiet -filter { - REF_PIN_NAME == D && - NAME =~ */uvtdm_tx_ctrl_inst/sync_flop_0_reg*/D - }] - set fpga_diff_tdm_tx_sync_d_pins {} - if {[llength $fpga_diff_tdm_tx_sync_candidates]} { - set fpga_diff_ddr_reset_endpoints \ - [all_fanout -flat -endpoints_only -from $fpga_diff_ddr_reset_regs] - foreach fpga_diff_pin $fpga_diff_tdm_tx_sync_candidates { - if {[lsearch -exact $fpga_diff_ddr_reset_endpoints $fpga_diff_pin] >= 0} { - lappend fpga_diff_tdm_tx_sync_d_pins $fpga_diff_pin - } - } - } - if {[llength $fpga_diff_tdm_tx_sync_d_pins]} { - set_false_path -from $fpga_diff_ddr_reset_regs \ - -to $fpga_diff_tdm_tx_sync_d_pins - puts "INFO: constrained DDR reset-to-TDM CDC: \ - sources=[llength $fpga_diff_ddr_reset_regs] \ - destinations=[llength $fpga_diff_tdm_tx_sync_d_pins]" - } else { - puts "INFO: no DDR reset-to-TDM CDC endpoints on this FPGA" - } +set fpga_diff_tdm_tx_sync_d_pins [get_pins -hier -quiet -filter { + REF_PIN_NAME == D && + (NAME =~ */uvtdm_tx_ctrl_inst/sync_flop_0_reg*/D || + NAME =~ */uvtdm_parity_g/sync_flop_0_reg*/D) +}] +if {[llength $fpga_diff_ddr_reset_regs] && [llength $fpga_diff_tdm_tx_sync_d_pins]} { + set_false_path -from $fpga_diff_ddr_reset_regs \ + -to $fpga_diff_tdm_tx_sync_d_pins + puts "INFO: constrained DDR reset-to-TDM CDC: \ + sources=[llength $fpga_diff_ddr_reset_regs] \ + destinations=[llength $fpga_diff_tdm_tx_sync_d_pins]" +} elseif {[llength $fpga_diff_ddr_reset_regs]} { + puts "INFO: no DDR reset-to-TDM CDC endpoints on this FPGA" } else { puts "INFO: no DDR reset-to-TDM CDC sources on this FPGA" } + +# UVHS TSO reports this crossing as an unconstrained non-DUT clock pair +# (mmcm_clkout0 -> gtye4_ch_txoutclk) with EXCEPTION empty. Pin-level +# false_path is not enough: the checker looks at the clock pair. Cut the +# clocks themselves after the netlist is linked. This is independent of +# DiffTest hostif; GBus only made the path visible on this partition. +set fpga_diff_ddr_reset_clk_pins [get_pins -hierarchical -quiet -filter { + NAME =~ */core_def/U_UVHS_UVW_AXI4_TO_DDR4/*/proc_sys_reset_0/U0/ACTIVE_LOW_PR_OUT_DFF*/C +}] +set fpga_diff_tdm_tx_sync_clk_pins [get_pins -hier -quiet -filter { + REF_PIN_NAME == C && + (NAME =~ */uvtdm_tx_ctrl_inst/sync_flop_0_reg*/C || + NAME =~ */uvtdm_parity_g/sync_flop_0_reg*/C) +}] +if {[llength $fpga_diff_ddr_reset_clk_pins] && + [llength $fpga_diff_tdm_tx_sync_d_pins]} { + set_false_path -from $fpga_diff_ddr_reset_clk_pins \ + -to $fpga_diff_tdm_tx_sync_d_pins + puts "INFO: constrained UVHS DDR-reset clock pin to TDM synchronizer CDC: \ + sources=[llength $fpga_diff_ddr_reset_clk_pins] \ + destinations=[llength $fpga_diff_tdm_tx_sync_d_pins]" +} +set fpga_diff_ddr_reset_clocks [get_clocks -quiet -of_objects \ + $fpga_diff_ddr_reset_clk_pins] +set fpga_diff_tdm_tx_clocks [get_clocks -quiet -of_objects \ + $fpga_diff_tdm_tx_sync_clk_pins] +if {[llength $fpga_diff_ddr_reset_clocks] && [llength $fpga_diff_tdm_tx_clocks]} { + set_false_path -from $fpga_diff_ddr_reset_clocks \ + -to $fpga_diff_tdm_tx_clocks + set_clock_groups -asynchronous \ + -group $fpga_diff_ddr_reset_clocks \ + -group $fpga_diff_tdm_tx_clocks + puts "INFO: constrained DDR MIG clock to TDM TX clock CDC: \ + sources=$fpga_diff_ddr_reset_clocks \ + destinations=$fpga_diff_tdm_tx_clocks" +} else { + puts "INFO: no DDR MIG clock to TDM TX clock CDC on this FPGA" +} diff --git a/fpga_diff/uvhs/ip/gbus/README.md b/fpga_diff/uvhs/ip/gbus/README.md new file mode 100644 index 0000000..2433281 --- /dev/null +++ b/fpga_diff/uvhs/ip/gbus/README.md @@ -0,0 +1,13 @@ +# UVHS GBus protected IP + +This directory no longer stores vendor DCP binaries. `compilation/prepare_ip.sh` +builds the required checkpoints from the local UVHS installation: + +- `uvw_general_bus` comes from `$UV_ROOT/platform/U2.2/Prototype/ips/uvw_gbus.3.1`. + GBus DiffTest uses the vendor 256-bit AXI3 configuration; XDMA keeps the + existing 64-bit generator used by the UVHS flash path. +- `generalBD` copies `$UV_ROOT/platform/U2.2/Prototype/ips/gbd/generalBD/generalBD.dcp` + and wraps it with `$UV_ROOT/etc/auxtools/prepare_ip/prepare_ip.py`. + +The applicable IP license and redistribution terms are those of the UVHS +vendor release. Generated DCP and stub files stay in the UVHS work directory. diff --git a/fpga_diff/uvhs/tcl/uvhs_gbus_axi_dwidth.tcl b/fpga_diff/uvhs/tcl/uvhs_gbus_axi_dwidth.tcl new file mode 100644 index 0000000..11d102a --- /dev/null +++ b/fpga_diff/uvhs/tcl/uvhs_gbus_axi_dwidth.tcl @@ -0,0 +1,40 @@ +################################################################## +# UVHS GBus AXI data-width converter +################################################################## + +set scripts_vivado_version $::vivado_version +set current_vivado_version [version -short] +if {[string first $scripts_vivado_version $current_vivado_version] == -1} { + error "uvhs_gbus_axi_dwidth requires Vivado $scripts_vivado_version, got $current_vivado_version" +} + +set list_projs [get_projects -quiet] +if {$list_projs eq ""} { + create_project uvhs_gbus_axi_dwidth uvhs_gbus_axi_dwidth \ + -part xcvu19p-fsva3824-2-e + set_property target_language Verilog [current_project] + set_property simulator_language Mixed [current_project] +} + +set ip_vlnv xilinx.com:ip:axi_dwidth_converter:2.1 +if {[get_ipdefs -all $ip_vlnv] eq ""} { + error "required IP not found: $ip_vlnv" +} + +if {[llength [get_ips -quiet uvhs_gbus_axi_dwidth]] == 0} { + create_ip -name axi_dwidth_converter -vendor xilinx.com \ + -library ip -version 2.1 -module_name uvhs_gbus_axi_dwidth +} +set converter [get_ips uvhs_gbus_axi_dwidth] +set_property -dict { + CONFIG.PROTOCOL {AXI4} + CONFIG.READ_WRITE_MODE {READ_WRITE} + CONFIG.ADDR_WIDTH {36} + CONFIG.SI_ID_WIDTH {14} + CONFIG.SI_DATA_WIDTH {256} + CONFIG.MI_DATA_WIDTH {64} + CONFIG.MAX_SPLIT_BEATS {256} + CONFIG.PACKING_LEVEL {1} + CONFIG.FIFO_MODE {0} + CONFIG.ACLK_ASYNC {0} +} $converter diff --git a/fpga_diff/uvhs/uvhs.mk b/fpga_diff/uvhs/uvhs.mk index c425084..729410d 100644 --- a/fpga_diff/uvhs/uvhs.mk +++ b/fpga_diff/uvhs/uvhs.mk @@ -4,8 +4,15 @@ UVHS_RUNTIME_DIR := $(UVHS_ROOT_DIR)/uvhs/runtime UVHS_TEMPLATE_DIR ?= UVHS_UVW_AXI4_TO_DDR4_SRC ?= -# KMH is the XiangShan target; keep the larger CPU profile enabled by default. -UVHS_PROBE_TCL ?= $(if $(filter kmh xiangshan,$(CPU)),$(UVHS_COMPILATION_DIR)/probe_kmh.tcl,$(UVHS_COMPILATION_DIR)/probe_ila.tcl) +DIFFTEST_HOSTIF ?= XDMA +ifeq ($(filter XDMA GBUS,$(DIFFTEST_HOSTIF)),) +$(error DIFFTEST_HOSTIF must be XDMA or GBUS, got $(DIFFTEST_HOSTIF)) +endif +# The detailed KMH profile is tied to a particular generated XiangShan +# hierarchy. GBus bring-up only requires the stable host trigger, so avoid +# making ordinary GBus builds fail when internal CPU signal names change. +# XDMA keeps its historical default; either flow may still opt in explicitly. +UVHS_PROBE_TCL ?= $(if $(and $(filter kmh xiangshan,$(CPU)),$(filter XDMA,$(DIFFTEST_HOSTIF))),$(UVHS_COMPILATION_DIR)/probe_kmh.tcl,$(UVHS_COMPILATION_DIR)/probe_ila.tcl) UVHS_PROBE_PATH := $(if $(strip $(UVHS_PROBE_TCL)),$(abspath $(UVHS_PROBE_TCL)),) UVHS_DDR_AXI_WIDTH := $(if $(filter nutshell,$(CPU)),64,256) UVHS_WORK_DIR := $(ENV_SCRIPTS_HOME)/$(PRJ_NAME) @@ -46,11 +53,13 @@ UVHS_TOOL_ENV = \ UVHS_RUNTIME_LIB_DIR="$(UVHS_RUNTIME_LIB_DIR)" \ UVHS_COMPAT_BIN="$(UVHS_COMPAT_BIN)" \ UVHS_TMCLK_CPU_RATIO="$(UVHS_TMCLK_CPU_RATIO)" \ + DIFFTEST_HOSTIF="$(DIFFTEST_HOSTIF)" \ UVSHELL_EXEC_NAME="$(UVHS_RUNTIME_DIR)/uv_shell_exec_compat.sh" UVHS_FLOW_ENV = \ $(UVHS_TOOL_ENV) \ UVHS_FLOW=1 \ + DIFFTEST_HOSTIF="$(DIFFTEST_HOSTIF)" \ UVHS_PROBE_TCL="$(UVHS_PROBE_PATH)" \ XDMA_LINK_WIDTH="$(XDMA_LINK_WIDTH)" \ UVHS_KEEP_FPGAS="$(UVHS_KEEP_FPGAS)" \ @@ -115,6 +124,7 @@ uvhs_prepare: uvhs_preflight "$(UVHS_UVW_AXI4_TO_DDR4_SRC)" "$(UVHS_DDR_AXI_WIDTH)" uvhs_project: uvhs_prepare + DIFFTEST_HOSTIF="$(DIFFTEST_HOSTIF)" \ bash "$(UVHS_ROOT_DIR)/tools/update_core_flist.sh" uvhs \ "$(CORE_DIR)" "$(UVHS_WORK_DIR)" "$(CPU)" "$(UVHS_FILELIST)" \ -- $(RTL_INCLUDE)