From ff8dc7804960ff7db9c1c2087882f46d10c0c9c4 Mon Sep 17 00:00:00 2001 From: Andrei Solodovnikov Date: Wed, 18 Jun 2025 14:50:15 +0300 Subject: [PATCH 1/5] Add windows support This change adds ability to compile test software, synthesize and simulate on windows platform through WSL2. --- cheshire.mk | 19 ++++++++++-- target/sim/vsim/start.cheshire_soc.tcl | 11 ++++++- target/xilinx/scripts/impl_ip.tcl | 1 - target/xilinx/scripts/impl_sys.tcl | 12 +++++++- target/xilinx/xilinx.mk | 40 +++++++++++++++++++++++--- 5 files changed, 74 insertions(+), 9 deletions(-) diff --git a/cheshire.mk b/cheshire.mk index f838911b7..cf0a8e6fc 100644 --- a/cheshire.mk +++ b/cheshire.mk @@ -10,7 +10,13 @@ BENDER ?= bender VLOGAN ?= vlogan # Caution: Questasim requires this to point to the *actual* compiler install path -CXX_PATH := $(shell which $(CXX)) +# If Make has been started from WSL, search Windows compiler, +# take first occurrence and replace '\' in path with '/'. +ifeq ($(strip $(WSL_DISTRO_NAME)),) + CXX_PATH := $(shell which $(CXX)) +else + CXX_PATH := $(shell cmd.exe /c "where $(CXX)" | tr -d '\r' | head -n 1 | sed 's|\\|/|g') +endif VLOG_ARGS ?= -suppress 2583 -suppress 13314 -timescale 1ns/1ps VLOGAN_ARGS ?= -kdb -nc -assert svaext +v2k -timescale=1ns/1ps @@ -146,10 +152,19 @@ CHS_BOOTROM_ALL += $(CHS_ROOT)/hw/bootrom/cheshire_bootrom.sv $(CHS_ROOT)/hw/boo ############## # Simulation # ############## +ifneq ($(strip $(WSL_DISTRO_NAME)),) + CHS_ROOT_OS_SPECIFIC := $(shell wslpath -m $(realpath $(CHS_ROOT))) +else + CHS_ROOT_OS_SPECIFIC := $(realpath $(CHS_ROOT)) +endif $(CHS_ROOT)/target/sim/vsim/compile.cheshire_soc.tcl: $(CHS_ROOT)/Bender.yml $(BENDER) script vsim -t sim -t test $(CHS_BENDER_RTL_FLAGS) --vlog-arg="$(VLOG_ARGS)" > $@ - echo 'vlog "$(realpath $(CHS_ROOT))/target/sim/src/elfloader.cpp" -ccflags "-std=c++11" -cpppath "$(CXX_PATH)"' >> $@ +ifneq ($(strip $(WSL_DISTRO_NAME)),) + # If run from WSL, replace pattern like "/c/..." with "c:/..." + sed -i -E 's|(set ROOT ")/([a-zA-Z])/(.*)"|\1\2:/\3"|g' $@ +endif + echo 'vlog "$(CHS_ROOT_OS_SPECIFIC)/target/sim/src/elfloader.cpp" -ccflags "-std=c++11" -cpppath "$(CXX_PATH)"' >> $@ $(CHS_ROOT)/target/sim/vcs/compile.cheshire_soc.sh: $(CHS_ROOT)/Bender.yml $(BENDER) script vcs -t sim -t test $(CHS_BENDER_RTL_FLAGS) --vlog-arg="$(VLOGAN_ARGS)" --vlogan-bin="$(VLOGAN)" > $@ diff --git a/target/sim/vsim/start.cheshire_soc.tcl b/target/sim/vsim/start.cheshire_soc.tcl index e6a005d4a..38c2ed8d1 100644 --- a/target/sim/vsim/start.cheshire_soc.tcl +++ b/target/sim/vsim/start.cheshire_soc.tcl @@ -18,7 +18,16 @@ if { ![info exists CXX_PATH] } { set CXX $::env(CXX) } } - set CXX_PATH [exec which $CXX] + + # Cross-platform search g++, where '\' replaced with '/' + if {[string equal $tcl_platform(platform) "windows"]} { + set raw_path [exec cmd.exe /c "where $CXX"] + set lines [split $raw_path "\n"] + set first_line [string trim [lindex $lines 0]] + set CXX_PATH [string map {\\ /} $first_line] + } else { + set CXX_PATH [exec which $CXX] + } } # Set voptargs only if not already set to make overridable. diff --git a/target/xilinx/scripts/impl_ip.tcl b/target/xilinx/scripts/impl_ip.tcl index be4aa9a41..1828b4bf3 100644 --- a/target/xilinx/scripts/impl_ip.tcl +++ b/target/xilinx/scripts/impl_ip.tcl @@ -179,4 +179,3 @@ wait_on_run ${proj}_synth_1 # Symlink proj for easy access and build tracking, ensuring its update file delete -force ${project_root}/out.xci -file link -symbolic ${project_root}/out.xci $xci diff --git a/target/xilinx/scripts/impl_sys.tcl b/target/xilinx/scripts/impl_sys.tcl index 5dc521dad..0888ac84f 100644 --- a/target/xilinx/scripts/impl_sys.tcl +++ b/target/xilinx/scripts/impl_sys.tcl @@ -13,7 +13,17 @@ source ${xilinx_root}/scripts/common.tcl init_impl $xilinx_root $argc $argv # Addtional args provide IPs -read_ip [exec realpath {*}[lrange $argv 2 end]] +# Despite info provided by help, Vivado can't read_ip with multiple args +# on Windows. Because of that this script read_ip one by one. +set ip_paths {} +foreach arg [lrange $argv 2 end] { + lappend ip_paths [file normalize $arg] +} + +foreach ip $ip_paths { + puts "Reading IP: $ip" + read_ip $ip +} # Load constraints import_files -fileset constrs_1 -norecurse ${xilinx_root}/constraints/${proj}.xdc diff --git a/target/xilinx/xilinx.mk b/target/xilinx/xilinx.mk index 6942a821b..81f45499b 100644 --- a/target/xilinx/xilinx.mk +++ b/target/xilinx/xilinx.mk @@ -23,13 +23,26 @@ CHS_XILINX_DIR ?= $(CHS_ROOT)/target/xilinx $(CHS_XILINX_DIR)/build/%/: mkdir -p $@ +BOARD_AND_IP = $(subst ., ,$*) +IP = $(word 2,$(BOARD_AND_IP)) +IP_SUFFIX = .srcs/sources_1/ip/$(IP) +IP_XCI_PATH = $(IP)$(IP_SUFFIX)/$(IP).xci + # We split the stem into a board and an IP and resolve dependencies accordingly $(CHS_XILINX_DIR)/build/%/out.xci: \ $(CHS_XILINX_DIR)/scripts/impl_ip.tcl \ $$(wildcard $(CHS_XILINX_DIR)/src/ips/$$*.prj) \ | $(CHS_XILINX_DIR)/build/%/ @rm -f $(CHS_XILINX_DIR)/build/$(*)*.log $(CHS_XILINX_DIR)/build/$(*)*.jou - cd $| && $(VIVADO) -mode batch -log ../$*.log -jou ../$*.jou -source $< -tclargs $(subst ., ,$*) + cd $| && { \ + IMPL_TCL="$<"; \ + if [ -n "$$WSL_DISTRO_NAME" ]; then \ + IMPL_TCL=$$(wslpath -m $$IMPL_TCL); \ + fi; \ + $(VIVADO) -mode batch -log ../$*.log -jou ../$*.jou \ + -source $$IMPL_TCL -tclargs $(BOARD_AND_IP); \ + ln -s $(@D)/$(IP_XCI_PATH) $(@D)/out.xci; \ + } ############## # Bitstreams # @@ -42,7 +55,13 @@ CHS_XILINX_IPS_vcu128 := clkwiz vio ddr4 $(CHS_XILINX_DIR)/scripts/add_sources.%.tcl: $(CHS_ROOT)/Bender.yml $(BENDER) script vivado -t fpga -t $* $(CHS_BENDER_RTL_FLAGS) > $@ +ifneq ($(strip $(WSL_DISTRO_NAME)),) + # If run from WSL, replace pattern like "/c/..." with "c:/..." + sed -i -E 's|(set ROOT ")/([a-zA-Z])/(.*)"|\1\2:/\3"|g' $@ +endif +# wslpath can't work with multiple path at once, so we need to process them in +# a loop. define chs_xilinx_bit_rule $$(CHS_XILINX_DIR)/out/%.$(1).bit: \ $$(CHS_XILINX_DIR)/scripts/impl_sys.tcl \ @@ -51,9 +70,22 @@ $$(CHS_XILINX_DIR)/out/%.$(1).bit: \ $$(CHS_HW_ALL) \ | $$(CHS_XILINX_DIR)/build/$(1).%/ @rm -f $$(CHS_XILINX_DIR)/build/$$*.$(1)*.log $$(CHS_XILINX_DIR)/build/$$*.$(1)*.jou - cd $$| && $$(VIVADO) -mode batch -log ../$$*.$(1).log -jou ../$$*.$(1).jou -source $$< \ - -tclargs $(1) $$* $$(CHS_XILINX_IPS_$(1):%=$$(CHS_XILINX_DIR)/build/$(1).%/out.xci) - + cd $$| && { \ + IMPL_TCL=$$<; \ + if [ -n "$$$$WSL_DISTRO_NAME" ]; then \ + IMPL_TCL=$$$$(wslpath -m $$$$IMPL_TCL); \ + XCI_LIST="$(CHS_XILINX_IPS_$(1):%=$(CHS_XILINX_DIR)/build/$(1).%/out.xci)";\ + XCI_LIST_PROCESSED=""; \ + for f in $$$$XCI_LIST; do \ + XCI_LIST_PROCESSED="$$$$XCI_LIST_PROCESSED $$$$(wslpath -m $$$$f)"; \ + done; \ + else \ + XCI_LIST_PROCESSED="$$$$XCI_LIST"; \ + fi; \ + echo $$$$XCI_LIST_PROCESSED; \ + $$(VIVADO) -mode batch -log ../$$*.$(1).log -jou ../$$*.$(1).jou -source \ + $$$$IMPL_TCL -tclargs $(1) $$* $$$$XCI_LIST_PROCESSED; \ + } CHS_PHONY += chs-xilinx-$(1) chs-xilinx-$(1): $$(CHS_XILINX_DIR)/out/cheshire.$(1).bit endef From c35517d04c51e9fcf8dc74db2a18975b34bf46a9 Mon Sep 17 00:00:00 2001 From: Andrei Solodovnikov Date: Thu, 19 Jun 2025 15:01:06 +0300 Subject: [PATCH 2/5] Use absolute path of cmd.exe This makes script to be independent of import Windows PATH variable. --- cheshire.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cheshire.mk b/cheshire.mk index cf0a8e6fc..15e42dcd8 100644 --- a/cheshire.mk +++ b/cheshire.mk @@ -15,7 +15,7 @@ VLOGAN ?= vlogan ifeq ($(strip $(WSL_DISTRO_NAME)),) CXX_PATH := $(shell which $(CXX)) else - CXX_PATH := $(shell cmd.exe /c "where $(CXX)" | tr -d '\r' | head -n 1 | sed 's|\\|/|g') + CXX_PATH := $(shell /c/Windows/System32/cmd.exe /c "where $(CXX)" | tr -d '\r' | head -n 1 | sed 's|\\|/|g') endif VLOG_ARGS ?= -suppress 2583 -suppress 13314 -timescale 1ns/1ps From 8638ccc2626cbb4d4d41b7a989bbee7c65fd602f Mon Sep 17 00:00:00 2001 From: Andrei Solodovnikov Date: Sat, 28 Feb 2026 18:45:32 +0300 Subject: [PATCH 3/5] target/xilinx: Refactor WSL path handling using PLATFORM_PATH macro Add PLATFORM_PATH macro to convert paths with wslpath when running under WSL, and leave them unchanged otherwise. This removes inline WSL checks, eliminates shell loops for XCI path conversion, and simplifies Vivado invocation rules. --- target/xilinx/xilinx.mk | 37 ++++++++++++++----------------------- 1 file changed, 14 insertions(+), 23 deletions(-) diff --git a/target/xilinx/xilinx.mk b/target/xilinx/xilinx.mk index 81f45499b..20a6e418d 100644 --- a/target/xilinx/xilinx.mk +++ b/target/xilinx/xilinx.mk @@ -28,6 +28,16 @@ IP = $(word 2,$(BOARD_AND_IP)) IP_SUFFIX = .srcs/sources_1/ip/$(IP) IP_XCI_PATH = $(IP)$(IP_SUFFIX)/$(IP).xci +ifneq ($(strip $(WSL_DISTRO_NAME)),) +define PLATFORM_PATH +$(shell wslpath -m $(1)) +endef +else +define PLATFORM_PATH +$(1) +endef +endif + # We split the stem into a board and an IP and resolve dependencies accordingly $(CHS_XILINX_DIR)/build/%/out.xci: \ $(CHS_XILINX_DIR)/scripts/impl_ip.tcl \ @@ -35,12 +45,8 @@ $(CHS_XILINX_DIR)/build/%/out.xci: \ | $(CHS_XILINX_DIR)/build/%/ @rm -f $(CHS_XILINX_DIR)/build/$(*)*.log $(CHS_XILINX_DIR)/build/$(*)*.jou cd $| && { \ - IMPL_TCL="$<"; \ - if [ -n "$$WSL_DISTRO_NAME" ]; then \ - IMPL_TCL=$$(wslpath -m $$IMPL_TCL); \ - fi; \ $(VIVADO) -mode batch -log ../$*.log -jou ../$*.jou \ - -source $$IMPL_TCL -tclargs $(BOARD_AND_IP); \ + -source $(call PLATFORM_PATH,$<) -tclargs $(BOARD_AND_IP); \ ln -s $(@D)/$(IP_XCI_PATH) $(@D)/out.xci; \ } @@ -60,8 +66,6 @@ ifneq ($(strip $(WSL_DISTRO_NAME)),) sed -i -E 's|(set ROOT ")/([a-zA-Z])/(.*)"|\1\2:/\3"|g' $@ endif -# wslpath can't work with multiple path at once, so we need to process them in -# a loop. define chs_xilinx_bit_rule $$(CHS_XILINX_DIR)/out/%.$(1).bit: \ $$(CHS_XILINX_DIR)/scripts/impl_sys.tcl \ @@ -70,22 +74,9 @@ $$(CHS_XILINX_DIR)/out/%.$(1).bit: \ $$(CHS_HW_ALL) \ | $$(CHS_XILINX_DIR)/build/$(1).%/ @rm -f $$(CHS_XILINX_DIR)/build/$$*.$(1)*.log $$(CHS_XILINX_DIR)/build/$$*.$(1)*.jou - cd $$| && { \ - IMPL_TCL=$$<; \ - if [ -n "$$$$WSL_DISTRO_NAME" ]; then \ - IMPL_TCL=$$$$(wslpath -m $$$$IMPL_TCL); \ - XCI_LIST="$(CHS_XILINX_IPS_$(1):%=$(CHS_XILINX_DIR)/build/$(1).%/out.xci)";\ - XCI_LIST_PROCESSED=""; \ - for f in $$$$XCI_LIST; do \ - XCI_LIST_PROCESSED="$$$$XCI_LIST_PROCESSED $$$$(wslpath -m $$$$f)"; \ - done; \ - else \ - XCI_LIST_PROCESSED="$$$$XCI_LIST"; \ - fi; \ - echo $$$$XCI_LIST_PROCESSED; \ - $$(VIVADO) -mode batch -log ../$$*.$(1).log -jou ../$$*.$(1).jou -source \ - $$$$IMPL_TCL -tclargs $(1) $$* $$$$XCI_LIST_PROCESSED; \ - } + cd $$| && $$(VIVADO) -mode batch -log ../$$*.$(1).log -jou ../$$*.$(1).jou -source $$(call PLATFORM_PATH,$$<) \ + -tclargs $(1) $$* $$(foreach ip,$$(CHS_XILINX_IPS_$(1)),$$(call PLATFORM_PATH,$$(CHS_XILINX_DIR)/build/$(1).$$(ip)/out.xci)) + CHS_PHONY += chs-xilinx-$(1) chs-xilinx-$(1): $$(CHS_XILINX_DIR)/out/cheshire.$(1).bit endef From 006ad27109411edc56c5ab9b3250647b3599af7c Mon Sep 17 00:00:00 2001 From: Andrei Solodovnikov Date: Sat, 28 Feb 2026 18:46:22 +0300 Subject: [PATCH 4/5] target/xilinx: Use Tcl realpath implementation for IP paths Introduce realpath_tcl helper that resolves absolute paths and symbolic links in a cross-platform way. --- target/xilinx/scripts/impl_sys.tcl | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/target/xilinx/scripts/impl_sys.tcl b/target/xilinx/scripts/impl_sys.tcl index 0888ac84f..3d6699107 100644 --- a/target/xilinx/scripts/impl_sys.tcl +++ b/target/xilinx/scripts/impl_sys.tcl @@ -12,18 +12,26 @@ set xilinx_root [file dirname [file dirname [file normalize [info script]]]] source ${xilinx_root}/scripts/common.tcl init_impl $xilinx_root $argc $argv +# Cross-platform Tcl implementation of the realpath utility. +# Unlike a simple call to `normalize`, this function not only converts +# a path to an absolute path, but also resolves symbolic links. +proc realpath_tcl {path} { + set p [file normalize $path] + + while {[file type $p] eq "link"} { + set p [file normalize [file readlink $p]] + } + + return $p +} + # Addtional args provide IPs -# Despite info provided by help, Vivado can't read_ip with multiple args -# on Windows. Because of that this script read_ip one by one. set ip_paths {} foreach arg [lrange $argv 2 end] { - lappend ip_paths [file normalize $arg] -} - -foreach ip $ip_paths { - puts "Reading IP: $ip" - read_ip $ip + lappend ip_paths [realpath_tcl $arg] } +puts [list read_ip $ip_paths] +read_ip $ip_paths # Load constraints import_files -fileset constrs_1 -norecurse ${xilinx_root}/constraints/${proj}.xdc From a2f75ac5769a78e7ca513d387cb0c877ab02e90a Mon Sep 17 00:00:00 2001 From: Andrei Solodovnikov Date: Tue, 17 Mar 2026 12:19:25 +0300 Subject: [PATCH 5/5] target(xilinx): Simplify out.xci target - Move symlink creation into a separate recipe command - Change Vivado call to a one-line version for easier review --- target/xilinx/xilinx.mk | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/target/xilinx/xilinx.mk b/target/xilinx/xilinx.mk index 20a6e418d..91b07f603 100644 --- a/target/xilinx/xilinx.mk +++ b/target/xilinx/xilinx.mk @@ -44,11 +44,8 @@ $(CHS_XILINX_DIR)/build/%/out.xci: \ $$(wildcard $(CHS_XILINX_DIR)/src/ips/$$*.prj) \ | $(CHS_XILINX_DIR)/build/%/ @rm -f $(CHS_XILINX_DIR)/build/$(*)*.log $(CHS_XILINX_DIR)/build/$(*)*.jou - cd $| && { \ - $(VIVADO) -mode batch -log ../$*.log -jou ../$*.jou \ - -source $(call PLATFORM_PATH,$<) -tclargs $(BOARD_AND_IP); \ - ln -s $(@D)/$(IP_XCI_PATH) $(@D)/out.xci; \ - } + cd $| && $(VIVADO) -mode batch -log ../$*.log -jou ../$*.jou -source $(call PLATFORM_PATH,$<) -tclargs $(BOARD_AND_IP) + ln -s $(@D)/$(IP_XCI_PATH) $(@D)/out.xci ############## # Bitstreams #