From edac52a8d988c6fda523c6050c971f0a8221fc12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Max=20K=C3=B6hler?= Date: Wed, 9 Sep 2026 11:15:06 +0200 Subject: [PATCH] fpga: tools: Upgrade to Vivado 2025.2 --- fpga/usrp3/tools/make/viv_hls_ip_builder.mak | 4 +- fpga/usrp3/tools/make/viv_preamble.mak | 2 +- fpga/usrp3/tools/scripts/setupenv_base.sh | 116 ++++++++++++++---- .../tools/scripts/viv_ip_retarget_subcores.py | 17 +++ fpga/usrp3/tools/scripts/viv_ip_xci_editor.py | 48 +++++++- fpga/usrp3/tools/scripts/viv_strategies.tcl | 2 +- 6 files changed, 157 insertions(+), 32 deletions(-) diff --git a/fpga/usrp3/tools/make/viv_hls_ip_builder.mak b/fpga/usrp3/tools/make/viv_hls_ip_builder.mak index a5d30ce77..6067bac22 100644 --- a/fpga/usrp3/tools/make/viv_hls_ip_builder.mak +++ b/fpga/usrp3/tools/make/viv_hls_ip_builder.mak @@ -20,7 +20,7 @@ BUILD_VIVADO_HLS_IP = \ echo "BUILDER: Building HLS IP $(1)"; \ echo "========================================================"; \ export HLS_IP_NAME=$(1); \ - export PART_NAME=$(subst /,,$(2)); \ + export PART_NAME=$(subst /,-,$(subst /-,-,$(2))); \ export HLS_IP_SRCS=$(call RESOLVE_PATHS,$(3)); \ export HLS_IP_INCLUDES=$(call RESOLVE_PATHS,$(6)); \ echo "BUILDER: Staging HLS IP in build directory..."; \ @@ -29,7 +29,7 @@ BUILD_VIVADO_HLS_IP = \ cd $(5); \ echo "BUILDER: Building HLS IP..."; \ export VIV_ERR=0; \ - vitis_hls -f $(call RESOLVE_PATH,$(TOOLS_DIR)/scripts/viv_generate_hls_ip.tcl) -l $(1).log || export VIV_ERR=$$?; \ + vitis-run --mode hls --tcl $(call RESOLVE_PATH,$(TOOLS_DIR)/scripts/viv_generate_hls_ip.tcl) || export VIV_ERR=$$?; \ $(TOOLS_DIR)/scripts/shared-ip-loc-manage.sh --path=$(5)/$(1) release; \ exit $$(($$VIV_ERR)) diff --git a/fpga/usrp3/tools/make/viv_preamble.mak b/fpga/usrp3/tools/make/viv_preamble.mak index 28c0e18fe..5d8c23cb8 100644 --- a/fpga/usrp3/tools/make/viv_preamble.mak +++ b/fpga/usrp3/tools/make/viv_preamble.mak @@ -81,7 +81,7 @@ endif @echo "BUILDER: Checking tools..." @echo -n "* "; bash --version | grep bash || (echo "ERROR: Bash not found in environment. Please install it"; exit 1;) @echo -n "* "; python3 --version || (echo "ERROR: Python not found in environment. Please install it"; exit 1;) - @echo -n "* "; vivado -version 2>&1 | grep Vivado || (echo "ERROR: Vivado not found in environment. Please run setupenv.sh"; exit 1;) + @echo -n "* "; vivado -version 2>&1 | grep -i Vivado || (echo "ERROR: Vivado not found in environment. Please run setupenv.sh"; exit 1;) # ------------------------------------------------------------------- # Intermediate build dirs diff --git a/fpga/usrp3/tools/scripts/setupenv_base.sh b/fpga/usrp3/tools/scripts/setupenv_base.sh index 917f4583d..0ff3bf506 100644 --- a/fpga/usrp3/tools/scripts/setupenv_base.sh +++ b/fpga/usrp3/tools/scripts/setupenv_base.sh @@ -10,22 +10,11 @@ export VIV_PLATFORM=$(uname -o) # Vivado specific if [[ $VIV_PLATFORM = "Cygwin" ]]; then - if [[ -d "/cygdrive/c/Xilinx/Vivado_Lab" ]]; then - VIVADO_BASE_PATH="/cygdrive/c/Xilinx/Vivado_Lab" - else - VIVADO_BASE_PATH="/cygdrive/c/Xilinx/Vivado" - fi + VIVADO_BASE_PATH="/cygdrive/c/Xilinx/Vivado" MODELSIM_BASE_PATH="/cygdrive/c/mentor/modelsim" else - if [[ -d "/tools/Xilinx/Vivado_Lab" ]]; then - VIVADO_BASE_PATH="/tools/Xilinx/Vivado_Lab" - elif [[ -d "/tools/Xilinx/Vivado" ]]; then - VIVADO_BASE_PATH="/tools/Xilinx/Vivado" - elif [[ -d "/opt/Xilinx/Vivado_Lab" ]]; then - VIVADO_BASE_PATH="/opt/Xilinx/Vivado_Lab" - else - VIVADO_BASE_PATH="/opt/Xilinx/Vivado" - fi + # Find the requested Vivado version below the standard Xilinx roots later. + VIVADO_BASE_PATH="" MODELSIM_BASE_PATH="" for MODELSIM_BASE in /opt /opt/local /tools /opt/share /opt/synced; do for MODELSIM_TOOL in \ @@ -55,6 +44,38 @@ function resolve_viv_path { fi } +# Locate a requested Vivado version below either an explicit path or the +# standard Xilinx installation roots. +function find_vivado_path { + local vivado_root + local vivado_candidate + local -a vivado_roots + + if [[ -n "$VIVADO_BASE_PATH" ]]; then + # An explicit path restricts discovery to that installation tree. + vivado_roots=("$VIVADO_BASE_PATH") + else + # Search the standard roots in priority order when none was provided. + vivado_roots=(/tools/Xilinx /opt/Xilinx) + fi + + for vivado_root in "${vivado_roots[@]}"; do + [[ -d "$vivado_root" ]] || continue + while IFS= read -r vivado_candidate; do + # Convert /bin/vivado to its installation directory. + vivado_candidate=$(dirname "$(dirname "$vivado_candidate")") + # Require the requested version as a complete path component. + [[ "/$vivado_candidate/" == *"/$VIVADO_VER/"* ]] || continue + export VIVADO_PATH="$vivado_candidate" + return 0 + # The supported layouts place bin/vivado within four levels of the root. + done < <(find "$vivado_root" -maxdepth 5 \( -type f -o -type l \) \ + \( -path '*/Vivado/*/bin/vivado' -o -path '*/Vivado/bin/vivado' \) \ + -print 2>/dev/null) + done + return 1 +} + #---------------------------------------------------------------------------- # Help message display function #---------------------------------------------------------------------------- @@ -190,8 +211,23 @@ if [[ ${VIVADO_VER^^} = "CMDLINE_ARG" ]]; then VIVADO_VER=$VIVADO_USER_VER fi fi -export VIVADO_PATH=$VIVADO_BASE_PATH/$VIVADO_VER -export VIVADO_HLS_PATH=$VIVADO_BASE_PATH/../Vitis_HLS/$VIVADO_VER +# Find a requested-version install. An explicit --vivado-path may name the +# install or either parent. +if ! find_vivado_path; then + # Preserve the historical fallback to report a useful missing-path error. + export VIVADO_PATH="$VIVADO_BASE_PATH/$VIVADO_VER/Vivado" +fi + +# Unified installs place Vitis beside Vivado. Older installs use a separate +# Vitis_HLS/ hierarchy beside Vivado/. +VIVADO_INSTALL_PARENT=$(dirname "$VIVADO_PATH") +if [[ -d "$VIVADO_INSTALL_PARENT/Vitis" ]]; then + export VIVADO_HLS_PATH="$VIVADO_INSTALL_PARENT/Vitis" +else + export VIVADO_HLS_PATH="$(dirname "$VIVADO_INSTALL_PARENT")/Vitis_HLS/$VIVADO_VER" +fi +# Both Vivado layouts have the Xilinx root one level above the install parent. +XILINX_ROOT=$(dirname "$VIVADO_INSTALL_PARENT") echo "Setting up a ${BITNESS}-bit FPGA build environment for the ${DISPLAY_NAME}..." @@ -201,7 +237,7 @@ echo "Setting up a ${BITNESS}-bit FPGA build environment for the ${DISPLAY_NAME} if [ -d "$VIVADO_PATH/bin" ]; then echo "- Vivado: Found ($VIVADO_PATH/bin)" else - echo "- Vivado: Version $VIVADO_VER not found in $VIVADO_BASE_PATH (ERROR.. Builds and simulations will not work)" + echo "- Vivado: Version $VIVADO_VER not found${VIVADO_BASE_PATH:+ in $VIVADO_BASE_PATH} (ERROR.. Builds and simulations will not work)" if [[ -z $VIVADO_USER_VER ]]; then echo " Use the --vivado-path option to override the search path" else @@ -211,13 +247,15 @@ else return 1 fi -$VIVADO_PATH/settings${BITNESS}.sh -$VIVADO_PATH/.settings${BITNESS}-Vivado.sh -if [[ -e $(readlink -f $VIVADO_HLS_PATH)/.settings${BITNESS}-Vitis_HLS.sh ]]; then - $(readlink -f $VIVADO_HLS_PATH)/.settings${BITNESS}-Vitis_HLS.sh +. "$VIVADO_PATH/settings${BITNESS}.sh" +. "$VIVADO_PATH/.settings${BITNESS}-Vivado.sh" +if [[ -e $(readlink -f "$VIVADO_HLS_PATH")/.settings${BITNESS}-Vitis_HLS.sh ]]; then + . "$(readlink -f "$VIVADO_HLS_PATH")/.settings${BITNESS}-Vitis_HLS.sh" +elif [[ -e $(readlink -f "$VIVADO_HLS_PATH")/.settings${BITNESS}-Vitis_for_HLS.sh ]]; then + . "$(readlink -f "$VIVADO_HLS_PATH")/.settings${BITNESS}-Vitis_for_HLS.sh" fi -if [[ -e $(readlink -f $VIVADO_BASE_PATH/..)/DocNav/.settings${BITNESS}-DocNav.sh ]]; then - $(readlink -f $VIVADO_BASE_PATH/..)/DocNav/.settings${BITNESS}-DocNav.sh +if [[ -e $(readlink -f "$XILINX_ROOT")/DocNav/.settings${BITNESS}-DocNav.sh ]]; then + . "$(readlink -f "$XILINX_ROOT")/DocNav/.settings${BITNESS}-DocNav.sh" fi if [[ -x `which tput 2>/dev/null` ]] ; then @@ -379,6 +417,17 @@ fi export PATH=$(echo ${PATH} | tr ':' '\n' | awk '$0 !~ "/Vivado/"' | paste -sd:) export PATH=${PATH}:$VIVADO_PATH:$VIVADO_PATH/bin:$VIVADO_HLS_PATH:$VIVADO_HLS_PATH/bin:$MODELSIM_PATH +# In 2025.x+, vitis_hls is no longer a standalone executable. Provide a wrapper +# function that invokes it through the Vitis loader. XILINX_VIVADO must be set +# so that vitis_hls can locate device libraries from the Vivado installation. +if [[ $VIVADO_VER == 2025* ]] && [[ ! -x "$(command -v vitis_hls 2>/dev/null)" ]]; then + export XILINX_VIVADO=$(readlink -f "$VIVADO_PATH") + vitis_hls() { + "$VIVADO_HLS_PATH/bin/loader" -exec vitis_hls "$@" + } + export -f vitis_hls +fi + for prod in "${!PRODUCT_ID_MAP[@]}"; do IFS='/' read -r -a prod_tokens <<< "${PRODUCT_ID_MAP[$prod]}" if [ ${#prod_tokens[@]} -eq 6 ]; then @@ -532,9 +581,20 @@ function viv_upgrade_tcl_bd { fi bd_ip_repo="${src_path%/top*}/lib/vivado_ipi" if [[ -f $src_path ]]; then - $3 -mode $viv_mode -source $(resolve_viv_path $VIV_IP_UTILS) -nolog -nojournal -tclargs modify_bdtcl $part_name $(resolve_viv_path $src_path) $(resolve_viv_path $bd_ip_repo) - echo "INFO: Vivado BD loaded, writing updated source TCL..." - $VIVADO_EXEC -mode $viv_mode -source $(resolve_viv_path $VIV_IP_UTILS) -nolog -nojournal -tclargs write_bdtcl $part_name $(resolve_viv_path $src_path) upgrade + log_path="${src_path%.tcl}.viv_upgrade.log" + echo "Vivado log: $log_path" + : > "$log_path" + echo "=== Loading block design with $3 ===" | tee -a "$log_path" + $3 -mode $viv_mode -source $(resolve_viv_path $VIV_IP_UTILS) -nolog -nojournal -tclargs modify_bdtcl $part_name $(resolve_viv_path $src_path) $(resolve_viv_path $bd_ip_repo) 2>&1 | tee -a "$log_path" + old_vivado_status=${PIPESTATUS[0]} + echo "INFO: Vivado BD loaded, writing updated source TCL..." | tee -a "$log_path" + echo "=== Writing upgraded block design with $VIVADO_EXEC ===" | tee -a "$log_path" + $VIVADO_EXEC -mode $viv_mode -source $(resolve_viv_path $VIV_IP_UTILS) -nolog -nojournal -tclargs write_bdtcl $part_name $(resolve_viv_path $src_path) upgrade 2>&1 | tee -a "$log_path" + new_vivado_status=${PIPESTATUS[0]} + if [[ $old_vivado_status -ne 0 ]]; then + return $old_vivado_status + fi + return $new_vivado_status else echo "ERROR: IP $src_path not found." return 1 @@ -577,7 +637,9 @@ function viv_upgrade_ip { if [[ -f $xci_path ]]; then echo "Upgrading $xci_path..." part_name=$(python3 $REPO_BASE_PATH/tools/scripts/viv_ip_xci_editor.py read_part $xci_path) - $VIVADO_EXEC -mode batch -source $(resolve_viv_path $VIV_IP_UTILS) -nolog -nojournal -tclargs upgrade $part_name $(resolve_viv_path $xci_path) | grep -v -E '(^$|^#|\*\*)' + log_path="${xci_path%.xci}.viv_upgrade.log" + echo "Vivado log: $log_path" + $VIVADO_EXEC -mode batch -source $(resolve_viv_path $VIV_IP_UTILS) -nolog -nojournal -tclargs upgrade $part_name $(resolve_viv_path $xci_path) 2>&1 | tee "$log_path" | grep -v -E '(^$|^#|\*\*)' test ${PIPESTATUS[0]} -eq 0 else echo "ERROR: IP $xci_path not found." diff --git a/fpga/usrp3/tools/scripts/viv_ip_retarget_subcores.py b/fpga/usrp3/tools/scripts/viv_ip_retarget_subcores.py index f8c4a9197..86ade861f 100644 --- a/fpga/usrp3/tools/scripts/viv_ip_retarget_subcores.py +++ b/fpga/usrp3/tools/scripts/viv_ip_retarget_subcores.py @@ -71,6 +71,13 @@ def get_empty_match_str(item): ) +def get_json_match_str(item): + """Produce the regex string to match a JSON XCI parameter value.""" + return ( + r'(\s*"' + item + r'"\s*:\s*\[\s*\{\s*"value"\s*:\s*")([^"]*)(\".*)' + ) + + def retarget_subcore(path, file, output_dir, target_tok): """Update target parameter for the specified sub-core.""" # Read XCI File @@ -115,6 +122,16 @@ def retarget_subcore(path, file, output_dir, target_tok): + replace_dict[m.group(2)] + "\n" ) + else: + # Try JSON format + m = re.search( + get_json_match_str( + "(" + "|".join(list(replace_dict.keys())) + ")" + ), + r_line, + ) + if m is not None: + w_line = m.group(1) + replace_dict[m.group(2)] + m.group(4) + "\n" out_file.write(w_line) diff --git a/fpga/usrp3/tools/scripts/viv_ip_xci_editor.py b/fpga/usrp3/tools/scripts/viv_ip_xci_editor.py index 2dd6786ab..1726d9927 100644 --- a/fpga/usrp3/tools/scripts/viv_ip_xci_editor.py +++ b/fpga/usrp3/tools/scripts/viv_ip_xci_editor.py @@ -69,6 +69,13 @@ def get_empty_match_str(item): return r'(.*\' +def get_json_match_str(item): + """Get the regex string to match a JSON XCI parameter value.""" + return ( + r'(\s*"' + item + r'"\s*:\s*\[\s*\{\s*"value"\s*:\s*")([^"]*)(\".*)' + ) + + def main(): """Main function.""" args = parse_args() @@ -98,6 +105,16 @@ def main(): ) if m is not None: xci_info[m.group(2)] = "" + else: + # Try JSON format + m = re.search( + get_json_match_str( + "(ARCHITECTURE|DEVICE|PACKAGE|SPEEDGRADE|TEMPERATURE_GRADE|SILICON_REVISION)" + ), + line, + ) + if m is not None: + xci_info[m.group(2)] = m.group(3) if args.action == "read_target": print( xci_info["ARCHITECTURE"] @@ -167,7 +184,7 @@ def main(): "C_XDEVICE": target_tok[1], } if len(target_tok) > 4: - replace_dict["TEMPERATURE_GRADE"] = target_tok[4] + replace_dict["TEMPERATURE_GRADE"] = target_tok[4].upper() if len(target_tok) > 5: replace_dict["SILICON_REVISION"] = target_tok[5] out_xci_filename = os.path.join( @@ -193,6 +210,14 @@ def main(): + replace_dict[m.group(2)] + "\n" ) + else: + # Try JSON format + m = re.search( + get_json_match_str("(" + "|".join(list(replace_dict.keys())) + ")"), + r_line, + ) + if m is not None: + w_line = m.group(1) + replace_dict[m.group(2)] + m.group(4) + "\n" out_file.write(w_line) elif args.action == "rename": # Write a new XCI file with a new name @@ -210,6 +235,8 @@ def main(): replace_dict = {"Component_Name": new_name} replace_top_level = ("spirit:instanceName", new_name) + # For JSON format, also match xci_name + json_replace_dict = {"Component_Name": new_name} out_xci_filename = os.path.join(os.path.abspath(args.output_dir), new_name + ".xci") with open(out_xci_filename, "w") as out_file: @@ -235,6 +262,25 @@ def main(): m = re.search(get_top_level_match_str(replace_top_level[0]), r_line) if m is not None: w_line = m.group(1) + replace_top_level[1] + m.group(3) + "\n" + else: + # Try JSON format + m = re.search( + get_json_match_str( + "(" + "|".join(list(json_replace_dict.keys())) + ")" + ), + r_line, + ) + if m is not None: + w_line = ( + m.group(1) + json_replace_dict[m.group(2)] + m.group(4) + "\n" + ) + else: + # Also handle xci_name in JSON + m = re.search( + r'(\s*"xci_name"\s*:\s*")([^"]*)(".*)', r_line + ) + if m is not None: + w_line = m.group(1) + new_name + m.group(3) + "\n" out_file.write(w_line) diff --git a/fpga/usrp3/tools/scripts/viv_strategies.tcl b/fpga/usrp3/tools/scripts/viv_strategies.tcl index 64f28b14b..3a3774151 100644 --- a/fpga/usrp3/tools/scripts/viv_strategies.tcl +++ b/fpga/usrp3/tools/scripts/viv_strategies.tcl @@ -63,7 +63,7 @@ proc ::vivado_strategies::get_impl_preset {preset} { dict set strategy "route_design.directive" "Explore" dict set strategy "route_design.more_options" "-tns_cleanup" dict set strategy "post_route_phys_opt_design.is_enabled" 1 - dict set strategy "post_route_phys_opt_design.directive" "Explore" + dict set strategy "post_route_phys_opt_design.directive" "ExploreWithHoldFix" } } return $strategy