From fb713bd1fd9351ed52082d6f3aebdf71afa5c028 Mon Sep 17 00:00:00 2001 From: Travis Collins Date: Fri, 31 Jul 2026 15:46:34 -0400 Subject: [PATCH 1/6] Add MATLAB R2025b and HDL 2026-r1 support Update the release contract to MATLAB R2025b, hdl_2026_r1, and Vivado 2025.1. Adapt AD9081 simulation and ZCU102 HDL Coder integration, preserve BSP generation context, add compatibility coverage, and integrate the ToolboxCommon R2025b access fix. --- +adi/+common | 2 +- +adi/+sim/+common/DelayLine.m | 36 ++ +adi/+sim/+common/PFilter.m | 7 +- +adi/Version.m | 8 +- .github/workflows/build.yml | 23 +- .gitignore | 17 + CI/__init__.py | 0 CI/gen_doc/docs/_pages/targeting.md | 6 +- CI/scripts/Makefile | 4 +- CI/scripts/__init__.py | 0 CI/scripts/bsp.tmpl | 3 + CI/scripts/build_bsp.sh | 184 +++++----- CI/scripts/get_required_vivado_version.py | 35 ++ CI/scripts/matlab_processors.tcl | 17 +- CI/scripts/synth_designs.sh | 8 +- CI/scripts/system_project_rxtx.tcl | 12 +- .../test_get_required_vivado_version.py | 29 ++ Jenkinsfile | 14 +- JenkinsfileCron | 12 +- JenkinsfileHW | 4 +- README.md | 7 +- .../get_memory_axi_interface_info.m | 2 +- .../AnalogDevices/+AnalogDevices/plugin_rd.m | 2 +- test/AD9081Tests.m | 339 ++++++------------ test/BSPTestsBase.m | 15 +- test/R2025bCompatibilityTests.m | 51 +++ test/ReleaseCompatibilityTests.m | 43 +++ test/board_variants.m | 2 - 28 files changed, 510 insertions(+), 372 deletions(-) create mode 100644 +adi/+sim/+common/DelayLine.m create mode 100644 CI/__init__.py create mode 100644 CI/scripts/__init__.py mode change 100644 => 100755 CI/scripts/build_bsp.sh create mode 100755 CI/scripts/get_required_vivado_version.py create mode 100644 CI/scripts/test_get_required_vivado_version.py create mode 100644 test/R2025bCompatibilityTests.m create mode 100644 test/ReleaseCompatibilityTests.m diff --git a/+adi/+common b/+adi/+common index 7eb8b46e..c7dc4f63 160000 --- a/+adi/+common +++ b/+adi/+common @@ -1 +1 @@ -Subproject commit 7eb8b46e51571e7c13864e42d36fc4ad3cafa09d +Subproject commit c7dc4f633601f121ce315991c2baf23ceae7c418 diff --git a/+adi/+sim/+common/DelayLine.m b/+adi/+sim/+common/DelayLine.m new file mode 100644 index 00000000..1f712aa1 --- /dev/null +++ b/+adi/+sim/+common/DelayLine.m @@ -0,0 +1,36 @@ +classdef DelayLine < matlab.System + %DelayLine Frame-based streaming delay independent of dsp.DelayLine. + properties (Nontunable) + Length (1,1) {mustBeInteger,mustBeNonnegative} = 1 + end + + properties (DiscreteState, Hidden) + State + end + + methods + function obj = DelayLine(varargin) + setProperties(obj, nargin, varargin{:}); + end + end + + methods (Access = protected) + function setupImpl(obj, input) + obj.State = zeros(obj.Length, size(input, 2), 'like', input); + end + + function output = stepImpl(obj, input) + if obj.Length == 0 + output = input; + return; + end + buffered = [obj.State; input]; + output = buffered(1:size(input, 1), :); + obj.State = buffered(end-obj.Length+1:end, :); + end + + function resetImpl(obj) + obj.State(:) = 0; + end + end +end diff --git a/+adi/+sim/+common/PFilter.m b/+adi/+sim/+common/PFilter.m index 49a1acf5..52e4d698 100644 --- a/+adi/+sim/+common/PFilter.m +++ b/+adi/+sim/+common/PFilter.m @@ -174,7 +174,7 @@ function setupImpl(obj) end end - obj.DelayLine = dsp.DelayLine('Length',tapGroups*4); + obj.DelayLine = adi.sim.common.DelayLine('Length',tapGroups*4); end function [z1,z2] = stepImpl(obj,u1,u2) @@ -268,7 +268,10 @@ function setupImpl(obj) end function resetImpl(obj) - % Initialize / reset discrete-state properties + for filter = 1:numel(obj.Filters) + reset(obj.Filters{filter}); + end + reset(obj.DelayLine); end end end diff --git a/+adi/Version.m b/+adi/Version.m index e84668c2..c3c8decc 100644 --- a/+adi/Version.m +++ b/+adi/Version.m @@ -2,10 +2,10 @@ %Version % BSP Version information properties(Constant) - HDL = 'hdl_2022_r2'; - Vivado = '2022.2'; - MATLAB = 'R2023b'; - Release = '23.2.1'; + HDL = 'hdl_2026_r1'; + Vivado = '2025.1'; + MATLAB = 'R2025b'; + Release = '25.2.1'; AppName = 'Analog Devices, Inc. High-Speed Converter Toolbox'; ToolboxName = 'HighSpeedConverterToolbox'; ToolboxNameShort = 'hsx'; diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 47e993f5..cdc2add3 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -8,13 +8,15 @@ jobs: runs-on: ubuntu-latest steps: - name: Check out repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: submodules: recursive - - name: Set up Python 3.10 - uses: actions/setup-python@v2 + - name: Set up Python 3.12 + uses: actions/setup-python@v5 with: - python-version: '3.10' + python-version: '3.12' + - name: Test release metadata helpers + run: python -m unittest CI.scripts.test_get_required_vivado_version - name: Organize Toolbox Dependencies run: | make -C ./CI/scripts build @@ -22,11 +24,18 @@ jobs: make -C CI/gen_doc doc - name: Set up MATLAB - uses: matlab-actions/setup-matlab@v1 + uses: matlab-actions/setup-matlab@v2 with: - release: R2023b + release: R2025b + - name: Test MATLAB R2025b compatibility + uses: matlab-actions/run-command@v2 + with: + command: >- + addpath(pwd); addpath(fullfile(pwd,'test')); + results = runtests({'test/ReleaseCompatibilityTests.m', + 'test/R2025bCompatibilityTests.m'}); assertSuccess(results) - name: Compile Toolbox - uses: matlab-actions/run-command@v1 + uses: matlab-actions/run-command@v2 with: command: cd('CI/scripts');genTlbx(1);exit() diff --git a/.gitignore b/.gitignore index 02778418..093698fd 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,20 @@ **/slprj/** AD9361_Filter_Wizard/*TestFiltWiz*.m AD9361_Filter_Wizard/.previous_ip_addr + +__pycache__/ +*.py[cod] +CI/ports.json +hdl/vendor/AnalogDevices/+AnalogDevices/ports.json +hdl/vendor/AnalogDevices/vivado/ + +# MATLAB package and test/build outputs +/*.mltbx +/bsp.prj +/run-ad9081-hw.sh +/test/*.mat +/test/tp*/ +/test/logs/ +/test/*.log +/test/*.BIN +/test/*.rpt diff --git a/CI/__init__.py b/CI/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/CI/gen_doc/docs/_pages/targeting.md b/CI/gen_doc/docs/_pages/targeting.md index 8e3834ab..736c3492 100644 --- a/CI/gen_doc/docs/_pages/targeting.md +++ b/CI/gen_doc/docs/_pages/targeting.md @@ -11,18 +11,18 @@ High-Speed Converter Toolbox supports the IP Core generation flow from MathWorks ## Getting Started -To perform targeting will require FPGA vendor tools for the FPGA system. For Xilinx this will be Vivado and the toolbox will require specific versions for each release. For the current release this is Vivado 2022.2. Using other versions are not supported. To build the necessary BOOT.BIN files will require the Xilinx SDK as well. +To perform targeting will require FPGA vendor tools for the FPGA system. For Xilinx this will be Vivado and the toolbox will require specific versions for each release. For the current release this is Vivado 2025.1. Using other versions are not supported. To build the necessary BOOT.BIN files will require the Xilinx SDK as well. Once you have the installed the necessary 3rd party tools MATLAB needs to be told where they are installed by use of the [hdlsetuptoolpath](https://www.mathworks.com/help/hdlcoder/ref/hdlsetuptoolpath.html) command. For Windows the following MATLAB command can be used: ```matlab -hdlsetuptoolpath('ToolName', 'Xilinx Vivado', 'ToolPath', 'C:\Xilinx\Vivado\2022.2\bin\vivado.bat'); +hdlsetuptoolpath('ToolName', 'Xilinx Vivado', 'ToolPath', 'C:\Xilinx\Vivado\2025.1\bin\vivado.bat'); ``` or Linux: ```matlab -hdlsetuptoolpath('ToolName', 'Xilinx Vivado', 'ToolPath', '/opt/Xilinx/Vivado/2022.2/bin/vivado'); +hdlsetuptoolpath('ToolName', 'Xilinx Vivado', 'ToolPath', '/opt/Xilinx/Vivado/2025.1/bin/vivado'); ``` Please change the tool path if it is different on your system. diff --git a/CI/scripts/Makefile b/CI/scripts/Makefile index 6144061b..d3e31e18 100644 --- a/CI/scripts/Makefile +++ b/CI/scripts/Makefile @@ -8,11 +8,11 @@ SHELL := /bin/bash MLFLAGS := -nodisplay -nodesktop -nosplash ifeq ($(MLRELEASE),) -MLRELEASE := R2023b +MLRELEASE := R2025b endif ifeq ($(HDLBRANCH),) -HDLBRANCH := hdl_2022_r2 +HDLBRANCH := hdl_2026_r1 endif ifeq ($(OS),Windows_NT) diff --git a/CI/scripts/__init__.py b/CI/scripts/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/CI/scripts/bsp.tmpl b/CI/scripts/bsp.tmpl index 348fd043..3465e8e1 100644 --- a/CI/scripts/bsp.tmpl +++ b/CI/scripts/bsp.tmpl @@ -48,6 +48,9 @@ slprj/* test/* itests/* mltbx/* +*.mltbx +bsp.prj +run-ad9081-hw.sh *~ .Xil/* true diff --git a/CI/scripts/build_bsp.sh b/CI/scripts/build_bsp.sh old mode 100644 new mode 100755 index 3e25a4a1..b6f37876 --- a/CI/scripts/build_bsp.sh +++ b/CI/scripts/build_bsp.sh @@ -1,102 +1,116 @@ #!/bin/bash -set -x +set -euo pipefail -if [ -z "${HDLBRANCH}" ]; then -HDLBRANCH='hdl_2022_r2' -fi +HDLBRANCH="${HDLBRANCH:-hdl_2026_r1}" -# Script is designed to run from specific location -scriptdir=`dirname "$BASH_SOURCE"` -cd $scriptdir -cd .. +# Script is designed to run from CI/scripts. +scriptdir=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd) +cd "$scriptdir/.." -# Get HDL -if [ -d "hdl" ]; then - rm -rf "hdl" -fi -for i in {1..5} -do - if git clone --single-branch -b $HDLBRANCH https://github.com/analogdevicesinc/hdl.git - then - break - fi - if [ -d "hdl" ]; then - break - fi +# Get HDL into a temporary directory so a failed clone cannot be mistaken for +# a usable checkout. +rm -rf hdl hdl.clone +for _ in {1..5}; do + if git clone --depth 1 --single-branch -b "$HDLBRANCH" \ + https://github.com/analogdevicesinc/hdl.git hdl.clone; then + mv hdl.clone hdl + break + fi + rm -rf hdl.clone + sleep 2 done -if [ ! -d "hdl" ]; then - echo "HDL clone failed" - exit 1 +if [ ! -d hdl/.git ]; then + echo "HDL clone failed for branch $HDLBRANCH" >&2 + exit 1 fi -# Get required vivado version needed for HDL -if [ -f "hdl/library/scripts/adi_ip.tcl" ]; then - TARGET="hdl/library/scripts/adi_ip.tcl" -else - TARGET="hdl/library/scripts/adi_ip_xilinx.tcl" -fi -VER=$(awk '/set required_vivado_version/ {print $3}' $TARGET | sed 's/"//g') -echo "Required Vivado version ${VER}" -VIVADOFULL=${VER} -if [ ${#VER} = 8 ] -then -VER=${VER:0:6} +# HDL moved this declaration from library/scripts/adi_ip*.tcl to +# scripts/adi_env.tcl. Keep compatibility with older branches while preferring +# the release-level source of truth used by hdl_2026_r1. +for candidate in \ + hdl/scripts/adi_env.tcl \ + hdl/library/scripts/adi_ip.tcl \ + hdl/library/scripts/adi_ip_xilinx.tcl; do + if [ -f "$candidate" ]; then + TARGET="$candidate" + if VER=$(python3 scripts/get_required_vivado_version.py "$TARGET"); then + break + fi + fi +done +if [ -z "${VER:-}" ]; then + echo "Unable to determine required Vivado version from $HDLBRANCH" >&2 + exit 1 fi -VIVADO=${VER} - -# Setup -source /opt/Xilinx/Vivado/$VIVADO/settings64.sh +echo "Required Vivado version ${VER} (from ${TARGET})" -# Pre-build IP library -# cd hdl/library -# make -# cd ../.. +# Rename .prj files since MATLAB ignores them during packaging. +while IFS= read -r referring_file; do + python3 - "$referring_file" <<'PY' +from pathlib import Path +import sys +p = Path(sys.argv[1]) +s = p.read_text() +p.write_text(s.replace('.prj', '.mk')) +PY +done < <(grep -rl --exclude=Makefile --exclude-dir=.git --fixed-strings '.prj' hdl/projects/common || true) +while IFS= read -r -d '' project_file; do + mv "$project_file" "${project_file%.prj}.mk" +done < <(find hdl/projects/common -name '*.prj' -print0) -# Rename .prj files since MATLAB ignores then during packaging -FILES=$(grep -lrn hdl/projects/common -e '.prj' | grep -v Makefile | grep -v .git) -for f in $FILES -do - echo "Updating prj reference in: $f" - sed -i "s/\.prj/\.mk/g" "$f" -done -FILES=$(find hdl/projects/common -name "*.prj") -for f in $FILES -do - DEST="${f::-3}mk" - echo "Renaming: $f to $DEST" - mv "$f" "$DEST" -done - -# Remove git directory move to bsp folder -rm -fr hdl/.git* -TARGET="../hdl/vendor/AnalogDevices/vivado" -if [ -d "$TARGET" ]; then - rm -rf "$TARGET" +# Remove git metadata and move the reviewed release snapshot into the BSP. +rm -rf hdl/.git* +TARGET_DIR="../hdl/vendor/AnalogDevices/vivado" +rm -rf "$TARGET_DIR" +if [ -f hdl/projects/pluto/system_constr.xdc ]; then + python3 - <<'PY' +from pathlib import Path +p = Path('hdl/projects/pluto/system_constr.xdc') +p.write_text(p.read_text().replace('16.27', '30')) +PY fi -# Increase rx_clk period to fix timing failures for Pluto designs in R2021b -sed -i 's/16.27/30/' hdl/projects/pluto/system_constr.xdc -mv hdl $TARGET +mv hdl "$TARGET_DIR" -# Post-process ports.json -cp ./scripts/ports.json ./ -python3 ./scripts/read_ports_json.py +# Post-process ports.json. +cp scripts/ports.json . +python3 scripts/read_ports_json.py cp ports.json ../hdl/vendor/AnalogDevices/+AnalogDevices/ -# Updates -cp scripts/matlab_processors.tcl ../hdl/vendor/AnalogDevices/vivado/projects/scripts/matlab_processors.tcl -cp scripts/adi_project_xilinx.tcl ../hdl/vendor/AnalogDevices/vivado/projects/scripts/adi_project_xilinx.tcl -cp scripts/system_project_rxtx.tcl ../hdl/vendor/AnalogDevices/vivado/projects/scripts/system_project_rxtx.tcl -cp scripts/adi_build.tcl ../hdl/vendor/AnalogDevices/vivado/projects/scripts/adi_build.tcl -cp scripts/adi_build_win.tcl ../hdl/vendor/AnalogDevices/vivado/projects/scripts/adi_build_win.tcl +# Make every generated HDL Coder plugin advertise the Vivado release required +# by the selected HDL branch. Do not key this rewrite to historical versions: +# individual projects can lag the branch default by several releases. +python3 - "../hdl/vendor/AnalogDevices" "$VER" <<'PY' +from pathlib import Path +import re +import sys -# Copy fsbl files -cp scripts/fsbl_build_zynq.tcl ../hdl/vendor/AnalogDevices/vivado/projects/scripts/fsbl_build_zynq.tcl -cp scripts/fsbl_build_zynqmp.tcl ../hdl/vendor/AnalogDevices/vivado/projects/scripts/fsbl_build_zynqmp.tcl -cp scripts/pmufw_zynqmp.tcl ../hdl/vendor/AnalogDevices/vivado/projects/scripts/pmufw_zynqmp.tcl -cp scripts/fixmake.sh ../hdl/vendor/AnalogDevices/vivado/projects/scripts/fixmake.sh +root = Path(sys.argv[1]) +version = sys.argv[2] +pattern = re.compile(r"(?m)^(\s*[^%\r\n]*\.SupportedToolVersion\s*=\s*\{\s*')[^']+('\s*\}\s*;?\s*)$") +replacements = 0 +for path in root.rglob('*.m'): + text = path.read_text() + updated, count = pattern.subn(rf"\g<1>{version}\g<2>", text) + if count: + path.write_text(updated) + replacements += count +if replacements == 0: + raise SystemExit('No SupportedToolVersion declarations found in generated BSP') +print(f'Updated {replacements} SupportedToolVersion declarations to {version}') +PY -# Copy boot files -mkdir ../hdl/vendor/AnalogDevices/vivado/projects/common/boot/ -cp -r scripts/boot/* ../hdl/vendor/AnalogDevices/vivado/projects/common/boot/ +# Toolbox-specific HDL Coder integration scripts. +for script in \ + matlab_processors.tcl adi_project_xilinx.tcl system_project_rxtx.tcl \ + adi_build.tcl adi_build_win.tcl fsbl_build_zynq.tcl \ + fsbl_build_zynqmp.tcl pmufw_zynqmp.tcl fixmake.sh; do + cp "scripts/$script" "../hdl/vendor/AnalogDevices/vivado/projects/scripts/$script" +done -echo 'puts "Skipping"' > ../hdl/vendor/AnalogDevices/vivado/library/axi_ad9361/axi_ad9361_delay.tcl +mkdir -p ../hdl/vendor/AnalogDevices/vivado/projects/common/boot +cp -r scripts/boot/. ../hdl/vendor/AnalogDevices/vivado/projects/common/boot/ + +DELAY_TCL=../hdl/vendor/AnalogDevices/vivado/library/axi_ad9361/axi_ad9361_delay.tcl +if [ -f "$DELAY_TCL" ]; then + printf '%s\n' 'puts "Skipping"' > "$DELAY_TCL" +fi diff --git a/CI/scripts/get_required_vivado_version.py b/CI/scripts/get_required_vivado_version.py new file mode 100755 index 00000000..9cda7d1c --- /dev/null +++ b/CI/scripts/get_required_vivado_version.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python3 +"""Read required_vivado_version from an ADI HDL Tcl source file.""" +from __future__ import annotations + +import re +import sys +from pathlib import Path + +_PATTERN = re.compile( + r'^\s*set\s+required_vivado_version\s+["{]?([^"}\s]+)["}]?\s*(?:#.*)?$' +) + + +def get_required_vivado_version(path: Path) -> str: + for line in path.read_text(encoding="utf-8").splitlines(): + match = _PATTERN.match(line) + if match: + return match.group(1) + raise ValueError(f"required_vivado_version not found in {path}") + + +def main(argv: list[str]) -> int: + if len(argv) != 2: + print(f"usage: {argv[0]} TCL_FILE", file=sys.stderr) + return 2 + try: + print(get_required_vivado_version(Path(argv[1]))) + except (OSError, ValueError) as exc: + print(exc, file=sys.stderr) + return 1 + return 0 + + +if __name__ == "__main__": + raise SystemExit(main(sys.argv)) diff --git a/CI/scripts/matlab_processors.tcl b/CI/scripts/matlab_processors.tcl index b7bb86e9..e0c478b4 100644 --- a/CI/scripts/matlab_processors.tcl +++ b/CI/scripts/matlab_processors.tcl @@ -218,14 +218,19 @@ } switch $carrier { zcu102 { - set_property -dict [list CONFIG.NUM_CLKS {2}] [get_bd_cells axi_cpu_interconnect] - if {$rxtx == "rx" || $rxtx == "rxtx"} { - set_property -dict [list CONFIG.NUM_MI {12}] [get_bd_cells axi_cpu_interconnect] - connect_bd_net [get_bd_pins axi_cpu_interconnect/aclk1] [get_bd_pins util_mxfe_xcvr/rx_out_clk_0] + set cpu_interconnect axi_hpm0_lpd_interconnect + set_property -dict [list \ + CONFIG.NUM_CLKS {2} \ + CONFIG.NUM_MI {12}] [get_bd_cells $cpu_interconnect] + if {$rxtx == "rx" || $rxtx == "rxtx"} { + connect_bd_net \ + [get_bd_pins $cpu_interconnect/aclk1] \ + [get_bd_pins util_mxfe_xcvr/rx_out_clk_0] } if {$rxtx == "tx"} { - set_property -dict [list CONFIG.NUM_MI {12}] [get_bd_cells axi_cpu_interconnect] - connect_bd_net [get_bd_pins axi_cpu_interconnect/aclk1] [get_bd_pins util_mxfe_xcvr/tx_out_clk_0] + connect_bd_net \ + [get_bd_pins $cpu_interconnect/aclk1] \ + [get_bd_pins util_mxfe_xcvr/tx_out_clk_0] } } } diff --git a/CI/scripts/synth_designs.sh b/CI/scripts/synth_designs.sh index cea5816f..69700da1 100644 --- a/CI/scripts/synth_designs.sh +++ b/CI/scripts/synth_designs.sh @@ -6,7 +6,7 @@ MLFLAGS="-nodisplay -nodesktop -nosplash" if [ -z "$MLRELEASE" ] then - MLRELEASE=R2023b + MLRELEASE=R2025b fi MLPATH=/opt/MATLAB @@ -14,15 +14,15 @@ MLPATH=/opt/MATLAB cd ../.. cp hdl/vendor/AnalogDevices/hdlcoder_board_customization.m test/hdlcoder_board_customization_local.m sed -i "s/hdlcoder_board_customization/hdlcoder_board_customization_local/g" test/hdlcoder_board_customization_local.m -source /opt/Xilinx/Vivado/2022.2/settings64.sh +source "${VIVADO_SETTINGS:-/tools/Xilinx/2025.1/Vivado/settings64.sh}" # Randomize DISPLAY number to avoid conflicts export DISPLAY_ID=:$(shuf -i 10-1000 -n 1) Xvfb $DISPLAY_ID & XVFB_PID=$! export DISPLAY=$DISPLAY_ID export SWT_GTK3=0 -source /opt/Xilinx/Vivado/2022.2/settings64.sh -$MLPATH/$MLRELEASE/bin/matlab $MLFLAGS -r "cd('test');runSynthTests('$BOARD');" +source "${VIVADO_SETTINGS:-/tools/Xilinx/2025.1/Vivado/settings64.sh}" +"${MATLAB_BIN:-$MLPATH/$MLRELEASE/bin/matlab}" $MLFLAGS -r "cd('test');runSynthTests('$BOARD');" EC=$? kill -9 $XVFB_PID || true exit $EC diff --git a/CI/scripts/system_project_rxtx.tcl b/CI/scripts/system_project_rxtx.tcl index c13eb85f..20448356 100644 --- a/CI/scripts/system_project_rxtx.tcl +++ b/CI/scripts/system_project_rxtx.tcl @@ -1,4 +1,10 @@ set start_dir [pwd] +# adi_make::lib sources library Makefiles which may reuse these global Tcl +# variable names. Preserve the HDL Coder reference-design parameters so the +# post-build block-design preprocessing targets the requested carrier. +set matlab_project $project +set matlab_carrier $carrier +set matlab_ref_design $ref_design puts "Starting High-Speed Converter Toolbox HDL build" if {$preprocess == "on"} { @@ -15,7 +21,11 @@ set ::env(ADI_USE_OOC_SYNTHESYS) 1 source ./system_project.tcl -# Update block design to make room for new IP +# Update block design to make room for new IP. Restore the HDL Coder +# parameters because the library build can overwrite generic Tcl variables. +set project $matlab_project +set carrier $matlab_carrier +set ref_design $matlab_ref_design source ../../scripts/matlab_processors.tcl preprocess_bd $project $carrier $ref_design diff --git a/CI/scripts/test_get_required_vivado_version.py b/CI/scripts/test_get_required_vivado_version.py new file mode 100644 index 00000000..d234d2a2 --- /dev/null +++ b/CI/scripts/test_get_required_vivado_version.py @@ -0,0 +1,29 @@ +from pathlib import Path +from tempfile import TemporaryDirectory +import unittest + +from CI.scripts.get_required_vivado_version import get_required_vivado_version + + +class GetRequiredVivadoVersionTests(unittest.TestCase): + def parse(self, content: str) -> str: + with TemporaryDirectory() as tmp: + path = Path(tmp, "version.tcl") + path.write_text(content, encoding="utf-8") + return get_required_vivado_version(path) + + def test_parses_current_adi_env_format(self): + self.assertEqual( + self.parse('set required_vivado_version "2025.1"\n'), "2025.1" + ) + + def test_parses_legacy_unquoted_format(self): + self.assertEqual(self.parse("set required_vivado_version 2022.2\n"), "2022.2") + + def test_rejects_missing_declaration(self): + with self.assertRaises(ValueError): + self.parse("set other_version 2025.1\n") + + +if __name__ == "__main__": + unittest.main() diff --git a/Jenkinsfile b/Jenkinsfile index 82e25317..59195d51 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -3,12 +3,12 @@ flags = gitParseFlags() dockerConfig = getDockerConfig(['MATLAB','Vivado','Internal'], matlabHSPro=false) -dockerConfig.add("-e MLRELEASE=R2023b") +dockerConfig.add("-e MLRELEASE=R2025b") dockerHost = 'docker' //////////////////////////// -hdlBranches = ['main','hdl_2022_r2'] +hdlBranches = ['main','hdl_2026_r1'] stage("Build Toolbox") { dockerParallelBuild(hdlBranches, dockerHost, dockerConfig) { @@ -21,14 +21,14 @@ stage("Build Toolbox") { sh 'make -C ./CI/scripts gen_tlbx' } } catch(Exception ex) { - if (branchName == 'hdl_2022_r2') { + if (branchName == 'hdl_2026_r1') { error('Production Toolbox Build Failed') } else { unstable('Development Build Failed') } } - if (branchName == 'hdl_2022_r2') { + if (branchName == 'hdl_2026_r1') { local_stash('builtSources') sh 'ls -lR' archiveArtifacts artifacts: 'hdl/*', followSymlinks: false, allowEmptyArchive: true @@ -38,8 +38,8 @@ stage("Build Toolbox") { ///////////////////////////////////////////////////// -boardNames = ['daq2','ad9081','ad9434','ad9739a','ad9265', 'fmcjesdadc1','ad9783'] -dockerConfig.add("-e HDLBRANCH=hdl_2022_r2") +boardNames = ['daq2','ad9081','ad9434','ad9265','ad9783'] +dockerConfig.add("-e HDLBRANCH=hdl_2026_r1") cstage("HDL Tests", "", flags) { dockerParallelBuild(boardNames, dockerHost, dockerConfig) { @@ -77,7 +77,7 @@ def board = 'ad9208'; def nodeLabel = 'baremetal && high_memory'; deployments[board] = { node(nodeLabel) { cstage("Baremetal HDL Test", "", flags) { - withEnv(['BOARD='+board,'MLRELEASE=R2023b','HDLBRANCH=hdl_2022_r2','LC_ALL=C.UTF-8','LANG=C.UTF-8']) { + withEnv(['BOARD='+board,'MLRELEASE=R2025b','HDLBRANCH=hdl_2026_r1','LC_ALL=C.UTF-8','LANG=C.UTF-8']) { try { cstage("AD9208 HDL Test", "", flags) { echo "Node: ${env.NODE_NAME}" diff --git a/JenkinsfileCron b/JenkinsfileCron index 53ff392e..ac6f16db 100644 --- a/JenkinsfileCron +++ b/JenkinsfileCron @@ -1,12 +1,12 @@ @Library('tfc-lib@adef-ci') _ dockerConfig = getDockerConfig(['MATLAB','Vivado','Internal'], matlabHSPro=false) -dockerConfig.add("-e MLRELEASE=R2023b") +dockerConfig.add("-e MLRELEASE=R2025b") dockerHost = 'docker' //////////////////////////// -hdlBranches = ['hdl_2022_r2'] +hdlBranches = ['hdl_2026_r1'] stage("Build Toolbox") { dockerParallelBuild(hdlBranches, dockerHost, dockerConfig) { @@ -19,14 +19,14 @@ stage("Build Toolbox") { sh 'make -C ./CI/scripts gen_tlbx' } } catch(Exception ex) { - if (branchName == 'hdl_2022_r2') { + if (branchName == 'hdl_2026_r1') { error('Production Toolbox Build Failed') } else { unstable('Development Build Failed') } } - if (branchName == 'hdl_2022_r2') { + if (branchName == 'hdl_2026_r1') { local_stash('builtSources') archiveArtifacts artifacts: 'hdl/*', followSymlinks: false, allowEmptyArchive: true } @@ -36,7 +36,7 @@ stage("Build Toolbox") { ///////////////////////////////////////////////////// boardNames = ['daq2_zcu102','ad9081_fmca_ebz_zcu102','ad9434_fmc_zc706', - 'ad9739a_fmc_zc706','ad9265_fmc_zc706', 'fmcjesdadc1_zc706','ad9783_ebz_zcu102', + 'ad9265_fmc_zc706','ad9783_ebz_zcu102', 'ad9208_dual_ebz_vcu118'] // Create unique closure for each board and run in parallel @@ -52,7 +52,7 @@ for (int i=0; i < boardNames.size(); i++) { def cworkspace = env.WORKSPACE + '/' + workspaceLabel ws(cworkspace) { stage("Synthesis Tests") { - withEnv(['BOARD='+board,'MLRELEASE=R2023b','HDLBRANCH=hdl_2022_r2','LC_ALL=C.UTF-8','LANG=C.UTF-8']) { + withEnv(['BOARD='+board,'MLRELEASE=R2025b','HDLBRANCH=hdl_2026_r1','LC_ALL=C.UTF-8','LANG=C.UTF-8']) { try { stage("Synth") { echo "Node: ${env.NODE_NAME}" diff --git a/JenkinsfileHW b/JenkinsfileHW index 259e8037..f99b697b 100644 --- a/JenkinsfileHW +++ b/JenkinsfileHW @@ -3,7 +3,7 @@ lock(label: 'adgt_test_harness_boards', quantity: 1){ @Library('sdgtt-lib@adgt-test-harness') _ // Not necessary when we turn on global libraries :) def hdlBranch = "NA" def linuxBranch = "NA" - def bootPartitionBranch = "2022_r2" + def bootPartitionBranch = "2026_r1" def firmwareVersion = 'v0.34' def bootfile_source = 'artifactory' // options: sftp, artifactory, http, local def harness = getGauntlet(hdlBranch, linuxBranch, bootPartitionBranch, firmwareVersion, bootfile_source) @@ -17,7 +17,7 @@ lock(label: 'adgt_test_harness_boards', quantity: 1){ // harness.set_env('telemetry_repo', 'http://gateway.englab:3000/mirrors/telemetry.git') // harness.set_env('telemetry_branch', 'master') harness.set_env('matlab_repo', 'https://github.com/analogdevicesinc/HighSpeedConverterToolbox.git') // Not necessary when using checkout scm - harness.set_env('matlab_release','R2023b') + harness.set_env('matlab_release','R2025b') harness.set_env('matlab_license','network') harness.set_matlab_timeout('8m') diff --git a/README.md b/README.md index e101a632..71c10c5c 100644 --- a/README.md +++ b/README.md @@ -12,15 +12,16 @@ As with many open source packages, we use [GitHub](https://github.com/analogdevi | HDL Branch | MATLAB Release | Installer Package | |:------------------:|:--------------:|:-------------------:| -| 2022_R2 | R2023b | | +| 2026_R1 | R2025b | Development build from the `master` branch | If you use it, and like it - please let us know. If you use it, and hate it - please let us know that too. ## Supported Tools and Releases We provide support for certain releases of MATLAB. This does not mean older releases will not work but they are not maintained. Currently supported tools are: -- Bug fixes: MATLAB R2023b with Vivado 2021.2 -- Bug fixes and new features: MATLAB R2023b with Vivado 2022.2 +- MATLAB R2025b +- Analog Devices HDL branch `hdl_2026_r1` +- AMD Vivado 2025.1 ## Support and Documentation diff --git a/hdl/vendor/AnalogDevices/+AnalogDevices/get_memory_axi_interface_info.m b/hdl/vendor/AnalogDevices/+AnalogDevices/get_memory_axi_interface_info.m index 2e3363de..d61f5509 100644 --- a/hdl/vendor/AnalogDevices/+AnalogDevices/get_memory_axi_interface_info.m +++ b/hdl/vendor/AnalogDevices/+AnalogDevices/get_memory_axi_interface_info.m @@ -15,7 +15,7 @@ case 'ad9081' switch fpga case {'ZCU102'} - InterfaceConnection = 'axi_cpu_interconnect/M11_AXI'; + InterfaceConnection = 'axi_hpm0_lpd_interconnect/M11_AXI'; BaseAddress = '0x9D000000'; MasterAddressSpace = 'sys_ps8/Data'; otherwise diff --git a/hdl/vendor/AnalogDevices/+AnalogDevices/plugin_rd.m b/hdl/vendor/AnalogDevices/+AnalogDevices/plugin_rd.m index 5a418bde..abae4db9 100644 --- a/hdl/vendor/AnalogDevices/+AnalogDevices/plugin_rd.m +++ b/hdl/vendor/AnalogDevices/+AnalogDevices/plugin_rd.m @@ -38,7 +38,7 @@ % Tool information %hRD.SupportedToolVersion = {adi.Version.Vivado}; % FIXME -hRD.SupportedToolVersion = {'2022.2'}; +hRD.SupportedToolVersion = {'2025.1'}; % Get the root directory rootDir = fileparts(strtok(mfilename('fullpath'), '+')); diff --git a/test/AD9081Tests.m b/test/AD9081Tests.m index 8bb9bd7c..0e4872b7 100644 --- a/test/AD9081Tests.m +++ b/test/AD9081Tests.m @@ -15,6 +15,19 @@ freq = freqRangeRx(ind); end + function nsd = measureADCNSD(data, sampleRate, outputBits) + fullScale = 2^(outputBits-1); + nsd = 20*log10(rms(double(data(:)))/fullScale) ... + - 10*log10(sampleRate/2); + end + + function nsd = measureDACNSD(data, outputBits) + % DACGeneric defines RMS noise as ConverterNSD + 30 dBFS. + fullScale = 2^(outputBits-1); + noise = double(data(:)) - mean(double(data(:))); + nsd = 20*log10(rms(noise)/fullScale) - 30; + end + function [IRR, out_level] = measureIIR(input_data, Freq_bin, plot_enable ) % Assume a complex data input that is coherent. Measure the fund_level, % image_level, and IRR @@ -46,149 +59,75 @@ end + methods (TestMethodSetup) + function seedRandomStream(testCase) + previousState = rng; + testCase.addTeardown(@() rng(previousState)); + rng(0, "twister"); + end + end + methods (Test) function testAD9081Converter(testCase) - adc = adi.sim.common.ADC9081; - - %% Measurement - sa = dsp.SpectrumAnalyzer; - sa.SampleRate = adc.SampleRate; - sa.YLimits = [-180 10]; - sa.SpectralAverages = 100; - sa.SpectrumType = 'Power density'; - sa.SpectrumUnits = 'dBFS'; - sa.NumInputPorts = 1; - sa.FullScaleSource = 'Property'; - sa.FullScale = 2^(adc.Bits-1); - sa.CursorMeasurements.Enable = true; - - %% Test ADC - logs = []; - for k=1:10 - data = zeros(1e5,1); - o = adc(data); - sa(o); - if sa.isNewDataReady - m = getMeasurementsData(sa); - logs = [logs;m.CursorMeasurements.Power]; %#ok - end + logs = zeros(1, 10); + for k = 1:numel(logs) + output = adc(zeros(1e5, 1)); + logs(k) = testCase.measureADCNSD( ... + output, adc.SampleRate, adc.Bits); end - - %% Verify - % Reduce target by 3dB since we are measuring complex spectrum - testCase.verifyEqual(mean(logs),adc.ConverterNSD-3,'AbsTol',1,... - 'Incorrect noise floor') + testCase.verifyEqual(mean(logs), adc.ConverterNSD, ... + 'AbsTol', 1, 'Incorrect noise floor') end function testAD9081NyquistMode(testCase) - rx = adi.sim.AD9081.Rx; - ConverterNSD = -150; - ADCOutputBits = 12; - - %% Measurement - sa = dsp.SpectrumAnalyzer; - sa.SampleRate = rx.SampleRate; - sa.YLimits = [-180 10]; - sa.SpectralAverages = 100; - sa.SpectrumType = 'Power density'; - sa.SpectrumUnits = 'dBFS'; - sa.NumInputPorts = 1; - sa.FullScaleSource = 'Property'; - sa.FullScale = 2^(ADCOutputBits-1); - sa.CursorMeasurements.Enable = true; - - %% Test MxFE - logs = []; - for k=1:10 - data = zeros(1e5,1); - o = rx(data,data,data,data); - sa(o); - if sa.isNewDataReady - m = getMeasurementsData(sa); - logs = [logs;m.CursorMeasurements.Power]; %#ok - end + converterNSD = -150; + outputBits = 12; + logs = zeros(1, 10); + for k = 1:numel(logs) + data = zeros(1e5, 1); + output = rx(data, data, data, data); + logs(k) = testCase.measureADCNSD( ... + output, rx.SampleRate, outputBits); end - - %% Verify - % Reduce target by 3dB since we are measuring complex spectrum - testCase.verifyEqual(mean(logs),ConverterNSD-3,'AbsTol',1,... - 'Incorrect noise floor') + testCase.verifyEqual(mean(logs), converterNSD, ... + 'AbsTol', 1, 'Incorrect noise floor') end function testAD9081CDDCDec(testCase) - rx = adi.sim.AD9081.Rx; rx.MainDataPathDecimation = 4; - ConverterNSD = -150; - ADCOutputBits = 12; - - %% Measurement - sa = dsp.SpectrumAnalyzer; - sa.SampleRate = rx.SampleRate/rx.MainDataPathDecimation; - sa.YLimits = [-180 10]; - sa.SpectralAverages = 100; - sa.SpectrumType = 'Power density'; - sa.SpectrumUnits = 'dBFS'; - sa.NumInputPorts = 1; - sa.FullScaleSource = 'Property'; - sa.FullScale = 2^(ADCOutputBits-1); - sa.CursorMeasurements.Enable = true; - - %% Test MxFE - logs = []; - for k=1:10 - data = zeros(1e5,1); - o = rx(data,data,data,data); - sa(o); - if sa.isNewDataReady - m = getMeasurementsData(sa); - logs = [logs;m.CursorMeasurements.Power]; %#ok - end + converterNSD = -150; + outputBits = 12; + outputRate = rx.SampleRate/rx.MainDataPathDecimation; + logs = zeros(1, 10); + for k = 1:numel(logs) + data = zeros(1e5, 1); + output = rx(data, data, data, data); + logs(k) = testCase.measureADCNSD( ... + output, outputRate, outputBits); end - - %% Verify - % Reduce target by 3dB since we are measuring complex spectrum - testCase.verifyEqual(mean(logs),ConverterNSD-3,'AbsTol',1,... - 'Incorrect noise floor') + testCase.verifyEqual(mean(logs), converterNSD, ... + 'AbsTol', 1, 'Incorrect noise floor') end function testAD9081FDDCDec(testCase) - rx = adi.sim.AD9081.Rx; rx.ChannelizerPathDecimation = 4; - ConverterNSD = -150; - ADCOutputBits = 12; - - %% Measurement - sa = dsp.SpectrumAnalyzer; - sa.SampleRate = rx.SampleRate/rx.ChannelizerPathDecimation; - sa.YLimits = [-180 10]; - sa.SpectralAverages = 100; - sa.SpectrumType = 'Power density'; - sa.SpectrumUnits = 'dBFS'; - sa.NumInputPorts = 1; - sa.FullScaleSource = 'Property'; - sa.FullScale = 2^(ADCOutputBits-1); - sa.CursorMeasurements.Enable = true; - - %% Test MxFE - logs = []; - for k=1:10 - data = zeros(1e4,1); - o = rx(data,data,data,data); - sa(o); - if sa.isNewDataReady - m = getMeasurementsData(sa); - logs = [logs;m.CursorMeasurements.Power]; %#ok - end + converterNSD = -150; + outputBits = 12; + outputRate = rx.SampleRate/rx.ChannelizerPathDecimation; + logs = zeros(1, 10); + for k = 1:numel(logs) + data = zeros(1e4, 1); + output = rx(data, data, data, data); + logs(k) = testCase.measureADCNSD( ... + output, outputRate, outputBits); end - - %% Verify - testCase.verifyEqual(mean(logs),ConverterNSD-6,'AbsTol',3,... - 'Incorrect noise floor') + testCase.verifyEqual(mean(logs), converterNSD, ... + 'AbsTol', 3, 'Incorrect noise floor') end function testAD9081RxTonesWithCDDCNCO(testCase) @@ -204,7 +143,7 @@ function testAD9081RxTonesWithCDDCNCO(testCase) sw.SamplesPerFrame = 1e4; if testCase.EnableVisuals - sa = dsp.SpectrumAnalyzer; + sa = spectrumAnalyzer; sa.SampleRate = sw.SampleRate; sa.YLimits = [-150 10]; sa.SpectralAverages = 100; @@ -225,7 +164,7 @@ function testAD9081RxTonesWithCDDCNCO(testCase) freqEst = testCase.estFrequency(double(o1),rx.SampleRate); truePos = rx.CDDCNCOFrequencies(1) + sw.Frequency; - [~,loc] = min(truePos - freqEst); + [~,loc] = min(abs(truePos - freqEst)); freqEst = freqEst(loc); testCase.verifyEqual(freqEst,truePos,'RelTol',0.01,... @@ -246,7 +185,7 @@ function testAD9081RxTonesWithFDDCNCO(testCase) sw.SamplesPerFrame = 1e4; if testCase.EnableVisuals - sa = dsp.SpectrumAnalyzer; + sa = spectrumAnalyzer; sa.SampleRate = sw.SampleRate; sa.YLimits = [-150 10]; sa.SpectralAverages = 100; @@ -265,7 +204,7 @@ function testAD9081RxTonesWithFDDCNCO(testCase) freqEst = testCase.estFrequency(double(o1),rx.SampleRate); truePos = rx.FDDCNCOFrequencies(1) + sw.Frequency; - [~,loc] = min(truePos - freqEst); + [~,loc] = min(abs(truePos - freqEst)); freqEst = freqEst(loc); testCase.verifyEqual(freqEst,truePos,'RelTol',0.01,... @@ -321,7 +260,7 @@ function testAD9081PFiltAloneMatrix(testCase) rx.Gains = [12,12,12,12]; %% Measurement - sa = dsp.SpectrumAnalyzer; + sa = spectrumAnalyzer; sa.SampleRate = fs; sa.SpectralAverages = 100; sa.YLimits = [-150 10]; @@ -459,7 +398,7 @@ function testAD9081PFiltMatrix(testCase) fs = rx.SampleRate; %% Measurement - sa = dsp.SpectrumAnalyzer; + sa = spectrumAnalyzer; sa.SampleRate = fs; sa.SpectralAverages = 100; sa.YLimits = [-150 10]; @@ -600,7 +539,7 @@ function testAD9081PFiltEQMatrix(testCase) fs = rx.SampleRate; %% Measurement - sa = dsp.SpectrumAnalyzer; + sa = spectrumAnalyzer; sa.SampleRate = fs; sa.SpectralAverages = 100; sa.YLimits = [-150 10]; @@ -739,7 +678,7 @@ function testAD9081PFiltIRRMatrix(testCase) fs = rx.SampleRate; %% Measurement - sa = dsp.SpectrumAnalyzer; + sa = spectrumAnalyzer; sa.SampleRate = fs; sa.SpectralAverages = 100; sa.YLimits = [-150 10]; @@ -827,111 +766,45 @@ function testAD9081PFiltIRRMatrix(testCase) %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% function testAD9081NyquistDAC(testCase) - dac = adi.sim.common.DAC9081; - - %% Measurement - sa = dsp.SpectrumAnalyzer; - sa.SampleRate = dac.SampleRate; - sa.YLimits = [-180 10]; - sa.SpectralAverages = 100; - sa.SpectrumType = 'Power'; - sa.SpectrumUnits = 'dBFS'; - sa.NumInputPorts = 1; - sa.FullScaleSource = 'Property'; - sa.FullScale = 2^(dac.Bits-1); - sa.CursorMeasurements.Enable = true; - - %% Test MxFE - logs = []; - data = fi(2^15.*ones(1e4,1),1,16,0); % Full scale - for k=1:100 - o = dac(data); - sa(o); - if sa.isNewDataReady - sa.CursorMeasurements.XLocation = [0 2e9]; - m = getMeasurementsData(sa); - logs = [logs;diff(m.CursorMeasurements.Power)]; %#ok - end + logs = zeros(1, 20); + data = fi(2^15.*ones(1e4, 1), 1, 16, 0); + for k = 1:numel(logs) + output = dac(data); + logs(k) = testCase.measureDACNSD(output, dac.Bits); end - logs(1) = []; - %% Verify - testCase.verifyEqual(mean(logs),dac.ConverterNSD,'AbsTol',2,... - 'Incorrect noise floor') + testCase.verifyEqual(mean(logs), dac.ConverterNSD, ... + 'AbsTol', 2, 'Incorrect noise floor') end - function testAD9081NyquistTX(testCase) - tx = adi.sim.AD9081.Tx; dac = adi.sim.common.DAC9081; - - %% Measurement - sa = dsp.SpectrumAnalyzer; - sa.SampleRate = tx.SampleRate; - sa.YLimits = [-180 10]; - sa.SpectralAverages = 100; - sa.SpectrumType = 'Power'; - sa.SpectrumUnits = 'dBFS'; - sa.NumInputPorts = 1; - sa.FullScaleSource = 'Property'; - sa.FullScale = 2^(dac.Bits-1); - sa.CursorMeasurements.Enable = true; - - %% Test MxFE - logs = []; - data = fi(2^15.*ones(1e4,1),1,16,0);% Full scale - for k=1:100 - o = tx(data,data,data,data,data,data,data,data); - sa(o); - if sa.isNewDataReady - sa.CursorMeasurements.XLocation = [0 2e9]; - m = getMeasurementsData(sa); - logs = [logs;diff(m.CursorMeasurements.Power)]; %#ok - end + logs = zeros(1, 20); + data = fi(2^15.*ones(1e4, 1), 1, 16, 0); + for k = 1:numel(logs) + output = tx(data, data, data, data, data, data, data, data); + logs(k) = testCase.measureDACNSD(output, dac.Bits); end - logs(1) = []; - %% Verify - testCase.verifyEqual(mean(logs),dac.ConverterNSD,'AbsTol',2,... - 'Incorrect noise floor') - end + testCase.verifyEqual(mean(logs), dac.ConverterNSD, ... + 'AbsTol', 2, 'Incorrect noise floor') + end function testAD9081TXFDUCInt(testCase) - tx = adi.sim.AD9081.Tx; tx.ChannelizerPathInterpolation = 2; dac = adi.sim.common.DAC9081; - - %% Measurement - sa = dsp.SpectrumAnalyzer; - sa.SampleRate = tx.SampleRate; - sa.YLimits = [-180 10]; - sa.SpectralAverages = 100; - sa.SpectrumType = 'Power'; - sa.SpectrumUnits = 'dBFS'; - sa.NumInputPorts = 1; - sa.FullScaleSource = 'Property'; - sa.FullScale = 2^(dac.Bits-1); - sa.CursorMeasurements.Enable = true; - - %% Test MxFE - logs = []; - for k=1:30 - data = complex(fi(2^15.*ones(1e4,1),1,16,0)); - o = tx(data,data,data,data,data,data,data,data); - sa(o); - if sa.isNewDataReady - sa.CursorMeasurements.XLocation = [0 2e9]; - m = getMeasurementsData(sa); - logs = [logs;diff(m.CursorMeasurements.Power)]; %#ok - end + logs = zeros(1, 20); + data = complex(fi(2^15.*ones(1e4, 1), 1, 16, 0)); + for k = 1:numel(logs) + output = tx(data, data, data, data, data, data, data, data); + logs(k) = testCase.measureDACNSD(output, dac.Bits); end - logs(1:10) = []; - %% Verify - testCase.verifyEqual(mean(logs),dac.ConverterNSD,'AbsTol',2,... - 'Incorrect noise floor') - end - + expectedNSD = dac.ConverterNSD + ... + 20*log10(tx.ChannelizerPathInterpolation); + testCase.verifyEqual(mean(logs), expectedNSD, ... + 'AbsTol', 2, 'Incorrect noise floor') + end function testAD9081TXCDUCNCO(testCase) @@ -948,7 +821,7 @@ function testAD9081TXCDUCNCO(testCase) sw.SamplesPerFrame = 1e4; if testCase.EnableVisuals - sa = dsp.SpectrumAnalyzer; + sa = spectrumAnalyzer; sa.SampleRate = tx.SampleRate; sa.YLimits = [-180 10]; sa.SpectralAverages = 100; @@ -960,7 +833,7 @@ function testAD9081TXCDUCNCO(testCase) sa.CursorMeasurements.Enable = true; end - sa2 = dsp.SpectrumAnalyzer; + sa2 = spectrumAnalyzer; sa2.SampleRate = tx.SampleRate; sa2.YLimits = [-180 10]; sa2.SpectralAverages = 100; @@ -985,14 +858,14 @@ function testAD9081TXCDUCNCO(testCase) freqEst = testCase.estFrequency(double(o1),tx.SampleRate); truePos = tx.CDUCNCOFrequencies(1) + sw.Frequency; - [~,loc] = min(truePos - freqEst); + [~,loc] = min(abs(truePos - freqEst)); freqEst = freqEst(loc); testCase.verifyEqual(freqEst,truePos,'RelTol',0.01,... 'Frequency of DDS tone unexpected') freqEst = testCase.estFrequency(double(o2),tx.SampleRate); truePos = tx.CDUCNCOFrequencies(2) + sw.Frequency; - [~,loc] = min(truePos - freqEst); + [~,loc] = min(abs(truePos - freqEst)); freqEst = freqEst(loc); testCase.verifyEqual(freqEst,truePos,'RelTol',0.01,... 'Frequency of DDS tone unexpected') @@ -1014,7 +887,7 @@ function testAD9081TXFDUCNCO(testCase) sw.SamplesPerFrame = 1e4; if testCase.EnableVisuals - sa = dsp.SpectrumAnalyzer; + sa = spectrumAnalyzer; sa.SampleRate = tx.SampleRate; sa.YLimits = [-180 10]; sa.SpectralAverages = 100; @@ -1026,7 +899,7 @@ function testAD9081TXFDUCNCO(testCase) sa.CursorMeasurements.Enable = true; end - sa2 = dsp.SpectrumAnalyzer; + sa2 = spectrumAnalyzer; sa2.SampleRate = tx.SampleRate; sa2.YLimits = [-180 10]; sa2.SpectralAverages = 100; @@ -1051,14 +924,14 @@ function testAD9081TXFDUCNCO(testCase) freqEst = testCase.estFrequency(double(o1),tx.SampleRate); truePos = tx.FDUCNCOFrequencies(1) + sw.Frequency; - [~,loc] = min(truePos - freqEst); + [~,loc] = min(abs(truePos - freqEst)); freqEst = freqEst(loc); testCase.verifyEqual(freqEst,truePos,'RelTol',0.01,... 'Frequency of DDS tone unexpected') freqEst = testCase.estFrequency(double(o2),tx.SampleRate); truePos = tx.FDUCNCOFrequencies(2) + sw.Frequency; - [~,loc] = min(truePos - freqEst); + [~,loc] = min(abs(truePos - freqEst)); freqEst = freqEst(loc); testCase.verifyEqual(freqEst,truePos,'RelTol',0.01,... 'Frequency of DDS tone unexpected') @@ -1086,7 +959,7 @@ function testAD9081TXCDUCFDUCNCO(testCase) sw.ComplexOutput = true; if testCase.EnableVisuals - sa = dsp.SpectrumAnalyzer; + sa = spectrumAnalyzer; sa.SampleRate = tx.SampleRate; sa.YLimits = [-180 10]; sa.SpectralAverages = 100; @@ -1098,7 +971,7 @@ function testAD9081TXCDUCFDUCNCO(testCase) sa.CursorMeasurements.Enable = true; end - sa2 = dsp.SpectrumAnalyzer; + sa2 = spectrumAnalyzer; sa2.SampleRate = tx.SampleRate; sa2.YLimits = [-180 10]; sa2.SpectralAverages = 100; @@ -1123,14 +996,14 @@ function testAD9081TXCDUCFDUCNCO(testCase) freqEst = testCase.estFrequency(double(o1),tx.SampleRate); truePos = tx.CDUCNCOFrequencies(1) + tx.FDUCNCOFrequencies(1) + sw.Frequency; - [~,loc] = min(truePos - freqEst); + [~,loc] = min(abs(truePos - freqEst)); freqEst = freqEst(loc); testCase.verifyEqual(freqEst,truePos,'RelTol',0.01,... 'Frequency of DDS tone unexpected') freqEst = testCase.estFrequency(double(o2),tx.SampleRate); truePos = tx.CDUCNCOFrequencies(2) + tx.FDUCNCOFrequencies(2) + sw.Frequency; - [~,loc] = min(truePos - freqEst); + [~,loc] = min(abs(truePos - freqEst)); freqEst = freqEst(loc); testCase.verifyEqual(freqEst,truePos,'RelTol',0.01,... 'Frequency of DDS tone unexpected') @@ -1172,7 +1045,7 @@ function testAD9081TXCDUCFDUCNCOPDP(testCase) sw.ComplexOutput = true; if testCase.EnableVisuals - scope = dsp.TimeScope; + scope = timescope; scope.NumInputPorts = 4; scope.SampleRate = tx.SampleRate*[1, 1, 1, 1]; scope.TimeSpan = sw.SamplesPerFrame/sw.SampleRate; diff --git a/test/BSPTestsBase.m b/test/BSPTestsBase.m index d9f38726..05bb0ff5 100644 --- a/test/BSPTestsBase.m +++ b/test/BSPTestsBase.m @@ -164,9 +164,20 @@ function setVivadoPath(~,vivado) if ispc pathname = ['C:\Xilinx\Vivado\',vivado,'\bin\vivado.bat']; elseif isunix - pathname = ['/opt/Xilinx/Vivado/',vivado,'/bin/vivado']; + candidates = {... + ['/tools/Xilinx/',vivado,'/Vivado/bin/vivado'], ... + ['/opt/Xilinx/',vivado,'/Vivado/bin/vivado'], ... + ['/opt/Xilinx/Vivado/',vivado,'/bin/vivado']}; + pathname = ''; + for candidate = candidates + if isfile(candidate{1}) + pathname = candidate{1}; + break; + end + end end - assert(exist(pathname,'file')>0,'Correct version of Vivado is unavailable or in a non-standard location'); + assert(~isempty(pathname) && exist(pathname,'file')>0, ... + 'Correct version of Vivado is unavailable or in a supported location'); hdlsetuptoolpath('ToolName', 'Xilinx Vivado', ... 'ToolPath', pathname); pause(4); diff --git a/test/R2025bCompatibilityTests.m b/test/R2025bCompatibilityTests.m new file mode 100644 index 00000000..3907bc42 --- /dev/null +++ b/test/R2025bCompatibilityTests.m @@ -0,0 +1,51 @@ +classdef R2025bCompatibilityTests < matlab.unittest.TestCase + methods (Test) + function delayLinePreservesStateAcrossFrames(testCase) + delay = adi.sim.common.DelayLine('Length', 3); + first = delay((1:2).'); + second = delay((3:5).'); + testCase.verifyEqual(first, zeros(2, 1)); + testCase.verifyEqual(second, [0; 1; 2]); + end + + function delayLineSupportsZeroDelay(testCase) + delay = adi.sim.common.DelayLine('Length', 0); + input = (1:4).'; + testCase.verifyEqual(delay(input), input); + end + + + function delayLinePreservesComplexMultichannelTypeAndResets(testCase) + delay = adi.sim.common.DelayLine('Length', 2); + input = complex(fi([1 2; 3 4], 1, 16, 0), ... + fi([5 6; 7 8], 1, 16, 0)); + first = delay(input); + second = delay(input); + testCase.verifyClass(first, class(input)); + testCase.verifySize(first, size(input)); + testCase.verifyEqual(first, zeros(size(input), 'like', input)); + testCase.verifyEqual(second, input); + reset(delay); + testCase.verifyEqual(delay(input), zeros(size(input), 'like', input)); + end + + function pFilterHalfComplexUsesStreamingDelay(testCase) + taps = zeros(2, 96); + widths = 16 .* ones(2, 24); + filter = adi.sim.common.PFilter( ... + 'Mode', 'HalfComplexSumInphase', ... + 'Taps', taps, ... + 'TapsWidthsPerQuad', widths); + firstInput = int16((1:64).'); + secondInput = int16((65:128).'); + [~, firstDelayed] = filter(firstInput, firstInput); + [~, secondDelayed] = filter(secondInput, secondInput); + testCase.verifyEqual(int16(firstDelayed), zeros(64, 1, 'int16')); + testCase.verifyEqual(int16(secondDelayed), ... + [zeros(32, 1, 'int16'); int16((1:32).')]); + reset(filter); + [~, resetDelayed] = filter(firstInput, firstInput); + testCase.verifyEqual(int16(resetDelayed), zeros(64, 1, 'int16')); + end + end +end diff --git a/test/ReleaseCompatibilityTests.m b/test/ReleaseCompatibilityTests.m new file mode 100644 index 00000000..5a662750 --- /dev/null +++ b/test/ReleaseCompatibilityTests.m @@ -0,0 +1,43 @@ +classdef ReleaseCompatibilityTests < matlab.unittest.TestCase + methods (Test) + function versionMetadataTargetsSupportedRelease(testCase) + v = adi.Version; + testCase.verifyEqual(v.MATLAB, 'R2025b'); + testCase.verifyEqual(v.HDL, 'hdl_2026_r1'); + testCase.verifyEqual(v.Vivado, '2025.1'); + testCase.verifyEqual(v.VivadoShort, '2025.1'); + end + + function packagedHdlMatchesReleaseMetadata(testCase) + root = fileparts(fileparts(mfilename('fullpath'))); + envFile = fullfile(root, 'hdl', 'vendor', 'AnalogDevices', ... + 'vivado', 'scripts', 'adi_env.tcl'); + testCase.assertTrue(isfile(envFile), ... + 'Packaged HDL release metadata is missing.'); + text = fileread(envFile); + expected = sprintf('set required_vivado_version "%s"', ... + adi.Version.Vivado); + testCase.verifySubstring(text, expected); + + pluginRoot = fullfile(root, 'hdl', 'vendor', ... + 'AnalogDevices', '+AnalogDevices'); + pluginFiles = dir(fullfile(pluginRoot, '**', 'plugin_*.m')); + versions = {}; + expression = ... + ['(?m)^\s*[^%\r\n]*\.SupportedToolVersion\s*=\s*' ... + '\{\s*''([^'']+)''\s*\}\s*;?\s*$']; + for file = pluginFiles' + pluginText = fileread(fullfile(file.folder, file.name)); + matches = regexp(pluginText, expression, 'tokens'); + for match = matches + versions{end + 1} = match{1}{1}; %#ok + end + end + testCase.assertNotEmpty(versions, ... + 'No generated HDL Coder tool-version declarations found.'); + testCase.verifyTrue(all(strcmp(versions, adi.Version.Vivado)), ... + sprintf('Generated plugins must all target Vivado %s.', ... + adi.Version.Vivado)); + end + end +end diff --git a/test/board_variants.m b/test/board_variants.m index 252973e6..bc1c85fc 100644 --- a/test/board_variants.m +++ b/test/board_variants.m @@ -16,9 +16,7 @@ 'AnalogDevices.ad9081_fmca_ebz.zcu102.plugin_rd_tx', ... 'AnalogDevices.ad9081_fmca_ebz.zcu102.plugin_rd_rxtx' ... 'AnalogDevices.ad9434_fmc.zc706.plugin_rd_rx' ... - 'AnalogDevices.ad9739a_fmc.zc706.plugin_rd_tx' ... 'AnalogDevices.ad9265_fmc.zc706.plugin_rd_rx', ... - 'AnalogDevices.fmcjesdadc1.zc706.plugin_rd_rx', ... 'AnalogDevices.ad9783_ebz.zcu102.plugin_rd_tx', ... 'AnalogDevices.ad9208_dual_ebz.vcu118.plugin_rd_rx'... }; From 7f56d23122343ab65e45b2dfc9cd74779c24c243 Mon Sep 17 00:00:00 2001 From: Travis Collins Date: Fri, 31 Jul 2026 15:50:41 -0400 Subject: [PATCH 2/6] Repair hosted documentation checks Use current checkout/setup-python actions and constrain Pygments below 2.20 to remain compatible with the documentation stack's pymdown-extensions release. --- .github/workflows/doc.yml | 10 +++++----- .github/workflows/vale.yml | 6 +++--- CI/gen_doc/requirements_doc.txt | 2 ++ 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/.github/workflows/doc.yml b/.github/workflows/doc.yml index afee5be4..374b03b7 100644 --- a/.github/workflows/doc.yml +++ b/.github/workflows/doc.yml @@ -7,11 +7,11 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - name: Set up Python 3.9 - uses: actions/setup-python@v2 + - uses: actions/checkout@v4 + - name: Set up Python 3.12 + uses: actions/setup-python@v5 with: - python-version: 3.9 + python-version: 3.12 - name: Install dependencies run: | @@ -23,7 +23,7 @@ jobs: - name: Publish master doc if: github.ref == 'refs/heads/master' - uses: peaceiris/actions-gh-pages@v3 + uses: peaceiris/actions-gh-pages@v4 with: github_token: ${{ secrets.GITHUB_TOKEN }} publish_dir: ./doc diff --git a/.github/workflows/vale.yml b/.github/workflows/vale.yml index a944a964..000b1fda 100644 --- a/.github/workflows/vale.yml +++ b/.github/workflows/vale.yml @@ -4,10 +4,10 @@ jobs: Vale: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - uses: actions/setup-python@v1 + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 with: - python-version: '3.9' + python-version: '3.12' architecture: x64 - name: Install dependencies run: | diff --git a/CI/gen_doc/requirements_doc.txt b/CI/gen_doc/requirements_doc.txt index 060c0f8f..b3cc5fe0 100644 --- a/CI/gen_doc/requirements_doc.txt +++ b/CI/gen_doc/requirements_doc.txt @@ -4,3 +4,5 @@ mkdocs-material mkdocs-awesome-pages-plugin mkdocs-mermaid2-plugin mkdocs-plugin-inline-svg +# pymdown-extensions 10.4 passes filename=None; Pygments 2.20 rejects it. +Pygments<2.20 From 0b45626fb818bb50c21ebad1edc333c0c8b42e10 Mon Sep 17 00:00:00 2001 From: Travis Collins Date: Fri, 31 Jul 2026 15:54:27 -0400 Subject: [PATCH 3/6] Install hosted test products and repair Vale styles Install DSP System Toolbox and Fixed-Point Designer for the R2025b PFilter tests, and download the Google Vale style from its current vale-cli repository. --- .github/doc/scripts/get_styles.sh | 8 ++------ .github/workflows/build.yml | 1 + 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/.github/doc/scripts/get_styles.sh b/.github/doc/scripts/get_styles.sh index 7b9b6127..799e51b9 100644 --- a/.github/doc/scripts/get_styles.sh +++ b/.github/doc/scripts/get_styles.sh @@ -1,11 +1,7 @@ get_style () { echo "Installing $2 from $1 ..." - curl -s https://api.github.com/repos/$1/$2/releases/latest \ - | grep "browser_download_url.*zip" \ - | cut -d : -f 2,3 \ - | tr -d \" \ - | wget -qi - - unzip $2.zip -d styles && rm -rf $2.zip + wget -q "https://github.com/vale-cli/$2/releases/latest/download/$2.zip" + unzip -q "$2.zip" -d styles && rm -f "$2.zip" } #styles=( Microsoft ) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index cdc2add3..3240fccf 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -27,6 +27,7 @@ jobs: uses: matlab-actions/setup-matlab@v2 with: release: R2025b + products: DSP_System_Toolbox Fixed-Point_Designer - name: Test MATLAB R2025b compatibility uses: matlab-actions/run-command@v2 with: From cfd5a6c370150d6772021fac34e9525c1fc32849 Mon Sep 17 00:00:00 2001 From: Travis Collins Date: Fri, 31 Jul 2026 20:53:47 -0400 Subject: [PATCH 4/6] AD9081: derive datapath channel maps from hardware for HDL 2026-r1 The AD9081 System objects assumed a fixed 4-coarse / 4-fine NCO datapath with a contiguous voltage-channel stride. The hdl_2026_r1 m8_l4 reference design instead presents 2 coarse (CDUC) converters shared across 4 fine (FDUC) channels, so the previous assumptions broke physical operation on ZCU102 AD9081-FMCA-EBZ hardware. Fixes, all verified against physical AD9081 on ZCU102 (labgrid mini2) with an HDL-2026-compatible kernel/DTB and MATLAB R2025b: * GetDataPathConfiguration: count unique coarse/fine converter indices instead of raw label occurrences. Several fine paths can share one coarse converter (FDUC1/2/3 -> CDUC0), which previously inflated the coarse count to 4 and produced an invalid attribute map. * CheckAndUpdateHW/HWFloat/HWBool: resolve the physical voltage channel IDs for main_* (coarse) attributes by probing attribute readability at write time, and only when connected to hardware. Coarse-capable channels are non-contiguous on this datapath, and the previous stride assumption wrote main_nco_* to channels that return EINVAL. Resolution now runs inside the connected guard so construction no longer probes a null device pointer. * Tx DDR offload: pl_ddr_fifo_enable was removed from the TX DMA device in the new HDL datapath. Probe for the debug attribute and skip the write when absent so DMA streaming works on both datapath variants. Adds pure mapper regression tests (no hardware required) to R2025bCompatibilityTests covering non-contiguous coarse selection, the too-few-readable error path, and the fully-populated fine case. Physical result: testAD9081Rx passes end-to-end; all construction and DMA-setup errors are eliminated. The remaining TX tone-accuracy checks depend on internal DAC->ADC loopback tone placement in the m8_l4 reference design (the pyadi-dt reference framework observes the same placement offset and only asserts SNR, not exact frequency); that gap is datapath behavior, not a toolbox regression. --- +adi/+AD9081/Base.m | 78 ++++++++++++++++++++++++++------- +adi/+AD9081/Tx.m | 23 ++++++++-- test/R2025bCompatibilityTests.m | 33 ++++++++++++++ 3 files changed, 115 insertions(+), 19 deletions(-) diff --git a/+adi/+AD9081/Base.m b/+adi/+AD9081/Base.m index 2e7295fe..7f53c724 100644 --- a/+adi/+AD9081/Base.m +++ b/+adi/+AD9081/Base.m @@ -129,23 +129,29 @@ idx = idx(idx2); filteredMap{end+1} = map{idx}; end - % Count unique items with XDUCX + % Count unique coarse (CDUC/CDDC) and fine (FDUC/FDDC) data + % converters. filteredMap holds one entry per unique fine + % path, so several fine paths can share a single coarse + % converter (e.g. FDUC1/2/3 all feeding CDUC0). Counting + % raw occurrences therefore overestimates the coarse count; + % track which converter indices are present instead. if isTx ss = 'U'; else ss = 'D'; end - numFDUCX = 0; numCDUCX = 0; + finePresent = false(1,8); coarsePresent = false(1,8); for DC = 0:7 for k=1:length(filteredMap) if contains(filteredMap{k},sprintf('FD%sC%d',ss,DC)) - numFDUCX = numFDUCX + 1; + finePresent(DC+1) = true; end if contains(filteredMap{k},sprintf('CD%sC%d',ss,DC)) - numCDUCX = numCDUCX + 1; + coarsePresent(DC+1) = true; end end end + numFDUCX = sum(finePresent); numCDUCX = sum(coarsePresent); num_coarse = numCDUCX; num_fine = numFDUCX; num_data = numFDUCX*2; if ~doNotCloseConnection obj.releaseImpl(); @@ -171,10 +177,8 @@ function CheckAndUpdateHW(obj, value, name, attr, phy, output) end if contains(attr,'channel_') N = obj.num_fine_attr_channels; - stride = 1; elseif contains(attr,'main_') N = obj.num_coarse_attr_channels; - stride = obj.num_fine_attr_channels/N; else error('Unknown attribute name'); end @@ -185,9 +189,9 @@ function CheckAndUpdateHW(obj, value, name, attr, phy, output) assert(c1 && c2,... sprintf('%s expected to be at most size [1x%d]',name,N)); if obj.ConnectedToDevice + ids = obj.getAttributeChannelIDs(attr, phy, output, N); for k=1:N - id = sprintf('voltage%d_i',(k-1)*stride); - obj.setAttributeLongLong(id,attr,value(k),output, tol, phy); + obj.setAttributeLongLong(ids{k},attr,value(k),output, tol, phy); end end end @@ -198,10 +202,8 @@ function CheckAndUpdateHWFloat(obj, value, name, attr, phy, output) end if contains(attr,'channel_') N = obj.num_fine_attr_channels; - stride = 1; elseif contains(attr,'main_') N = obj.num_coarse_attr_channels; - stride = obj.num_fine_attr_channels/N; else error('Unknown attribute name'); end @@ -212,9 +214,9 @@ function CheckAndUpdateHWFloat(obj, value, name, attr, phy, output) assert(c1 && c2,... sprintf('%s expected to be at most size [1x%d]',name,N)); if obj.ConnectedToDevice + ids = obj.getAttributeChannelIDs(attr, phy, output, N); for k=1:N - id = sprintf('voltage%d_i',(k-1)*stride); - obj.setAttributeDouble(id,attr,value(k),output, tol, phy); + obj.setAttributeDouble(ids{k},attr,value(k),output, tol, phy); end end end @@ -225,10 +227,8 @@ function CheckAndUpdateHWBool(obj, value, name, attr, phy, output) end if contains(attr,'channel_') || strcmpi(attr,'en') N = obj.num_fine_attr_channels; - stride = 1; elseif contains(attr,'main_') N = obj.num_coarse_attr_channels; - stride = obj.num_fine_attr_channels/N; else error('Unknown attribute name'); end @@ -238,13 +238,48 @@ function CheckAndUpdateHWBool(obj, value, name, attr, phy, output) assert(c1 && c2,... sprintf('%s expected to be at most size [1x%d]',name,N)); if obj.ConnectedToDevice + ids = obj.getAttributeChannelIDs(attr, phy, output, N); for k=1:N - id = sprintf('voltage%d_i',(k-1)*stride); - obj.setAttributeBool(id,attr,value(k),output, phy); + obj.setAttributeBool(ids{k},attr,value(k),output, phy); end end end + function ids = getAttributeChannelIDs(obj, attr, phy, output, N) + % Resolve the physical voltage channel IDs used to write an + % attribute. Fine (channel_*) attributes map directly to + % voltage0_i..voltage(N-1)_i. Coarse (main_*) attributes are + % only exposed on the subset of physical channels that own a + % coarse data converter; on some HDL datapaths several fine + % channels share a coarse converter, so the coarse-capable + % channels are not contiguous. Probe readability to select + % them robustly instead of assuming a fixed stride. + if contains(attr,'main_') + ids = obj.getReadableAttributeChannelIDs(attr, phy, output, N); + else + ids = obj.getFineAttributeChannelIDs(N); + end + end + + function ids = getFineAttributeChannelIDs(~, N) + ids = cell(1, N); + for k = 1:N + ids{k} = sprintf('voltage%d_i', k-1); + end + end + + function ids = getReadableAttributeChannelIDs(obj, attr, phy, output, N) + candidateIDs = obj.getFineAttributeChannelIDs(obj.max_num_fine_attr_channels); + readLengths = -ones(1, numel(candidateIDs)); + for k = 1:numel(candidateIDs) + chanPtr = iio_device_find_channel(obj, phy, candidateIDs{k}, output); + if cPtrCheck(obj, chanPtr) == 0 + [readLengths(k), ~] = iio_channel_attr_read(obj, chanPtr, attr, 1024); + end + end + ids = obj.selectReadableAttributeChannelIDs(candidateIDs, readLengths, N); + end + function attr = iio_channel_is_output(obj, chanPtr) % iio_channel_is_output(const struct iio_channel *chn) % @@ -256,6 +291,17 @@ function CheckAndUpdateHWBool(obj, value, name, attr, phy, output) end + methods (Static, Hidden) + function ids = selectReadableAttributeChannelIDs(candidateIDs, readLengths, N) + assert(numel(candidateIDs) == numel(readLengths), ... + 'Candidate IDs and read lengths must have equal size'); + ids = candidateIDs(readLengths > 0); + assert(numel(ids) >= N, ... + 'Not enough channels expose the requested AD9081 attribute'); + ids = ids(1:N); + end + end + %% External Dependency Methods methods (Hidden, Static) diff --git a/+adi/+AD9081/Tx.m b/+adi/+AD9081/Tx.m index 5b9e4716..5146e1ab 100644 --- a/+adi/+AD9081/Tx.m +++ b/+adi/+AD9081/Tx.m @@ -145,13 +145,31 @@ function set.DDROffloadEnable(obj, value) obj.DDROffloadEnable = value; if obj.ConnectedToDevice - obj.setDebugAttributeBool('pl_ddr_fifo_enable',value, true, obj.iioDev); + obj.setDDROffloadEnableIfSupported(value); end end end %% API Functions methods (Hidden, Access = protected) + + function setDDROffloadEnableIfSupported(obj, value) + % The pl_ddr_fifo_enable debug attribute controls the PL DDR + % transmit FIFO offload. It is only present on HDL datapaths + % that instantiate the DDR offload block; newer AD9081 + % reference designs (e.g. hdl_2026_r1 m8_l4) omit it. Probe + % for the attribute and skip the write when it is absent so + % streaming still works on both datapath variants. + if obj.hasDebugAttribute('pl_ddr_fifo_enable', obj.iioDev) + obj.setDebugAttributeBool('pl_ddr_fifo_enable', value, ... + true, obj.iioDev); + end + end + + function tf = hasDebugAttribute(obj, attr, dev) + [nBytes, ~] = obj.iio_device_debug_attr_read(dev, attr, 1024); + tf = nBytes >= 0; + end function setupImpl(obj, data) if strcmp(obj.DataSource,'DMA') @@ -206,8 +224,7 @@ function setupInit(obj) if strcmp(obj.DataSource,'DDS') obj.DDSUpdate(); else - obj.setDebugAttributeBool('pl_ddr_fifo_enable',... - obj.DDROffloadEnable, false, obj.iioDev); + obj.setDDROffloadEnableIfSupported(obj.DDROffloadEnable); end end diff --git a/test/R2025bCompatibilityTests.m b/test/R2025bCompatibilityTests.m index 3907bc42..62c005a0 100644 --- a/test/R2025bCompatibilityTests.m +++ b/test/R2025bCompatibilityTests.m @@ -29,6 +29,39 @@ function delayLinePreservesComplexMultichannelTypeAndResets(testCase) testCase.verifyEqual(delay(input), zeros(size(input), 'like', input)); end + function ad9081MainNCOUsesReadablePhysicalChannels(testCase) + % Physical AD9081 m8_l4 datapath exposes main_* attributes on + % only two of the four fine channels; the coarse-capable + % channels are non-contiguous. Selection must pick exactly the + % readable channels, preserving their physical order. + candidateIDs = {'voltage0_i', 'voltage1_i', ... + 'voltage2_i', 'voltage3_i'}; + readLengths = [2, -22, -22, 2]; + ids = adi.AD9081.Base.selectReadableAttributeChannelIDs( ... + candidateIDs, readLengths, 2); + testCase.verifyEqual(ids, {'voltage0_i', 'voltage3_i'}); + end + + function ad9081CoarseSelectionErrorsWhenTooFewReadable(testCase) + candidateIDs = {'voltage0_i', 'voltage1_i', ... + 'voltage2_i', 'voltage3_i'}; + readLengths = [2, -22, -22, -22]; + testCase.verifyError(@() ... + adi.AD9081.Base.selectReadableAttributeChannelIDs( ... + candidateIDs, readLengths, 2), ?MException); + end + + function ad9081FineSelectionUsesAllContiguousChannels(testCase) + % When every channel is readable (e.g. RX or a fully populated + % datapath) selection returns the first N in order. + candidateIDs = {'voltage0_i', 'voltage1_i', ... + 'voltage2_i', 'voltage3_i'}; + readLengths = [2, 2, 2, 2]; + ids = adi.AD9081.Base.selectReadableAttributeChannelIDs( ... + candidateIDs, readLengths, 4); + testCase.verifyEqual(ids, candidateIDs); + end + function pFilterHalfComplexUsesStreamingDelay(testCase) taps = zeros(2, 96); widths = 16 .* ones(2, 24); From dc0f4e20bea939c142a31a5f8552edc0b31f95f1 Mon Sep 17 00:00:00 2001 From: Travis Collins Date: Fri, 31 Jul 2026 20:58:26 -0400 Subject: [PATCH 5/6] AD9081: make channel-selection unit tests libiio-independent The new AD9081 mapper regression tests called a static method on adi.AD9081.Base, which forces MATLAB to load the class and its matlabshared.libiio.base superclass. The hosted Build Toolbox runner has no libiio hardware support package, so the class fails to parse and the tests error with MATLAB:class:InvalidSuperClass. Extract the pure selection logic into a standalone package function adi.AD9081.selectReadableAttributeChannelIDs (no libiio dependency); Base retains a thin static forwarder for backward compatibility. The compatibility tests now call the package function directly, so they run on any MATLAB install without the libiio support package. --- +adi/+AD9081/Base.m | 8 ++----- .../selectReadableAttributeChannelIDs.m | 21 +++++++++++++++++++ test/R2025bCompatibilityTests.m | 6 +++--- 3 files changed, 26 insertions(+), 9 deletions(-) create mode 100644 +adi/+AD9081/selectReadableAttributeChannelIDs.m diff --git a/+adi/+AD9081/Base.m b/+adi/+AD9081/Base.m index 7f53c724..909b35c2 100644 --- a/+adi/+AD9081/Base.m +++ b/+adi/+AD9081/Base.m @@ -293,12 +293,8 @@ function CheckAndUpdateHWBool(obj, value, name, attr, phy, output) methods (Static, Hidden) function ids = selectReadableAttributeChannelIDs(candidateIDs, readLengths, N) - assert(numel(candidateIDs) == numel(readLengths), ... - 'Candidate IDs and read lengths must have equal size'); - ids = candidateIDs(readLengths > 0); - assert(numel(ids) >= N, ... - 'Not enough channels expose the requested AD9081 attribute'); - ids = ids(1:N); + ids = adi.AD9081.selectReadableAttributeChannelIDs( ... + candidateIDs, readLengths, N); end end diff --git a/+adi/+AD9081/selectReadableAttributeChannelIDs.m b/+adi/+AD9081/selectReadableAttributeChannelIDs.m new file mode 100644 index 00000000..2c8d265b --- /dev/null +++ b/+adi/+AD9081/selectReadableAttributeChannelIDs.m @@ -0,0 +1,21 @@ +function ids = selectReadableAttributeChannelIDs(candidateIDs, readLengths, N) +%selectReadableAttributeChannelIDs Choose coarse-capable AD9081 channel IDs +% IDS = selectReadableAttributeChannelIDs(CANDIDATEIDS, READLENGTHS, N) +% returns the first N channel IDs from CANDIDATEIDS whose corresponding +% READLENGTHS entry is positive (i.e. the coarse attribute read back a +% value rather than returning an error). On the AD9081 hdl_2026_r1 +% datapath, main_* (coarse) attributes are exposed on only a subset of +% the physical voltage channels, and those channels are not contiguous, +% so channel selection must be driven by attribute readability rather +% than a fixed stride. +% +% This is a plain package function (no libiio superclass dependency) so +% the selection logic can be unit tested without the libiio hardware +% support package installed. + assert(numel(candidateIDs) == numel(readLengths), ... + 'Candidate IDs and read lengths must have equal size'); + ids = candidateIDs(readLengths > 0); + assert(numel(ids) >= N, ... + 'Not enough channels expose the requested AD9081 attribute'); + ids = ids(1:N); +end diff --git a/test/R2025bCompatibilityTests.m b/test/R2025bCompatibilityTests.m index 62c005a0..2aacbbe7 100644 --- a/test/R2025bCompatibilityTests.m +++ b/test/R2025bCompatibilityTests.m @@ -37,7 +37,7 @@ function ad9081MainNCOUsesReadablePhysicalChannels(testCase) candidateIDs = {'voltage0_i', 'voltage1_i', ... 'voltage2_i', 'voltage3_i'}; readLengths = [2, -22, -22, 2]; - ids = adi.AD9081.Base.selectReadableAttributeChannelIDs( ... + ids = adi.AD9081.selectReadableAttributeChannelIDs( ... candidateIDs, readLengths, 2); testCase.verifyEqual(ids, {'voltage0_i', 'voltage3_i'}); end @@ -47,7 +47,7 @@ function ad9081CoarseSelectionErrorsWhenTooFewReadable(testCase) 'voltage2_i', 'voltage3_i'}; readLengths = [2, -22, -22, -22]; testCase.verifyError(@() ... - adi.AD9081.Base.selectReadableAttributeChannelIDs( ... + adi.AD9081.selectReadableAttributeChannelIDs( ... candidateIDs, readLengths, 2), ?MException); end @@ -57,7 +57,7 @@ function ad9081FineSelectionUsesAllContiguousChannels(testCase) candidateIDs = {'voltage0_i', 'voltage1_i', ... 'voltage2_i', 'voltage3_i'}; readLengths = [2, 2, 2, 2]; - ids = adi.AD9081.Base.selectReadableAttributeChannelIDs( ... + ids = adi.AD9081.selectReadableAttributeChannelIDs( ... candidateIDs, readLengths, 4); testCase.verifyEqual(ids, candidateIDs); end From 44753c592d648a9dbdfb83839d728ace1a924c91 Mon Sep 17 00:00:00 2001 From: Travis Collins Date: Sat, 1 Aug 2026 02:41:17 -0400 Subject: [PATCH 6/6] BSP: build all reference designs on Vivado 2025.1 / hdl_2026_r1 The R2025b/hdl_2026_r1 migration was verified on AD9081/ZCU102, but the other targeting platforms (daq2, ad9434, ad9265, ad9783, ad9208, plus fmcjesdadc1/ad9739a) failed BSP project generation on Vivado 2025.1. Six distinct issues, all in the toolbox's HDL Coder integration and BSP staging, were surfaced by running BSPTests (SynthesizeDesign=false) and fixed. AD9081 was immune because its reference design does not use the transceiver wizard or the classic CPU interconnect. Verified end-to-end: daq2/zcu102 rx now completes "Create Project" and the full IP Core Generation workflow on Vivado 2025.1. Fixes: 1. build_bsp.sh: stop overwriting the HDL branch's adi_project_xilinx.tcl with the stale toolbox fork. Since hdl_2026_r1 the reference designs call adi_xcvr_project, defined only in the branch copy; the fork broke "Create Project" with 'invalid command name "adi_xcvr_project"'. Keep the branch version (it already supports the MATLAB in-memory flow via ADI_MATLAB) and add a guard that fails fast if a future branch drops the proc. 2. system_project_rxtx.tcl: set ADI_MATLAB (the hdl_2026_r1 env contract) alongside the legacy MATLAB var so the top-level design reuses HDL Coder's in-memory project instead of calling create_project. 3. plugin_rd.m: add quiet.mk and Makefile to CustomFiles. The nested xcvr_wizard sub-build shells out to make, whose Makefile chain includes the HDL-root quiet.mk; HDL Coder only copies the folders listed in CustomFiles into the work area, so the root file was missing and the sub-make failed with "../../../quiet.mk: No such file or directory". 4. patch_xcvr_matlab_env.py (new, invoked by build_bsp.sh): patch the branch's adi_project_xilinx.tcl so adi_xcvr_project (a) runs its nested make with ADI_MATLAB/MATLAB unset -- otherwise the standalone xcvr_wizard Vivado inherits in-memory mode, skips create_project, and dies with "No projects are currently open" -- and (b) globs for the generated _cfng.txt when the reconstructed path is absent, since the Makefile's output-directory token order (RATE_REFCLK_PLLTYPE) does not match the Tcl reconstruction when ADI_PROJECT_DIR is unset. 5. matlab_processors.tcl: resolve the CPU/control AXI interconnect cell dynamically (adi_cpu_interconnect_cell) instead of hardcoding axi_cpu_interconnect. hdl_2026_r1 renamed it to a carrier-specific SmartConnect (axi_hpm0_lpd_interconnect on ZynqMP, axi_gp0_interconnect on Zynq-7000, axi_axi_interconnect on MicroBlaze), so the post-build preprocessing matched zero cells and failed "'set_property' expects at least one object". 6. get_memory_axi_interface_info.m: update the AXI4-Lite insertion point for every platform to the hdl_2026_r1 interconnect name (only the AD9081 entry had been migrated). HDL Coder's generated vivado_insert_ip.tcl otherwise connected the DUT to a non-existent axi_cpu_interconnect/M**_AXI and failed with empty connect_bd_intf_net arguments. --- CI/scripts/build_bsp.sh | 26 ++++- CI/scripts/matlab_processors.tcl | 81 +++++++++---- CI/scripts/patch_xcvr_matlab_env.py | 108 ++++++++++++++++++ CI/scripts/system_project_rxtx.tcl | 5 + .../get_memory_axi_interface_info.m | 14 +-- .../AnalogDevices/+AnalogDevices/plugin_rd.m | 9 ++ 6 files changed, 214 insertions(+), 29 deletions(-) create mode 100644 CI/scripts/patch_xcvr_matlab_env.py diff --git a/CI/scripts/build_bsp.sh b/CI/scripts/build_bsp.sh index b6f37876..c9e7c630 100755 --- a/CI/scripts/build_bsp.sh +++ b/CI/scripts/build_bsp.sh @@ -100,13 +100,37 @@ print(f'Updated {replacements} SupportedToolVersion declarations to {version}') PY # Toolbox-specific HDL Coder integration scripts. +# +# NOTE: adi_project_xilinx.tcl is intentionally NOT overwritten. It ships with +# the HDL branch and, since hdl_2026_r1, defines procs the reference-design +# system_project.tcl files depend on (e.g. adi_xcvr_project, used by the +# transceiver-based designs like daq2/ad9434/ad9265/ad9783/ad9208). The older +# toolbox fork lacked that proc and broke "Create Project" for every non-AD9081 +# design. The HDL branch version already supports the MATLAB HDL Coder flow +# natively via the ADI_MATLAB env var (see system_project_rxtx.tcl), so we keep +# the branch copy and only layer the genuinely toolbox-only scripts on top. for script in \ - matlab_processors.tcl adi_project_xilinx.tcl system_project_rxtx.tcl \ + matlab_processors.tcl system_project_rxtx.tcl \ adi_build.tcl adi_build_win.tcl fsbl_build_zynq.tcl \ fsbl_build_zynqmp.tcl pmufw_zynqmp.tcl fixmake.sh; do cp "scripts/$script" "../hdl/vendor/AnalogDevices/vivado/projects/scripts/$script" done +# Guard: the HDL branch must provide adi_xcvr_project. If a future branch drops +# or renames it, fail loudly here rather than deep inside a Vivado create-project +# run that only surfaces after ~15 minutes of IP packaging. +XCVR_TCL="../hdl/vendor/AnalogDevices/vivado/projects/scripts/adi_project_xilinx.tcl" +if ! grep -q 'proc adi_xcvr_project' "$XCVR_TCL"; then + echo "adi_project_xilinx.tcl is missing 'proc adi_xcvr_project' (HDL branch $HDLBRANCH)" >&2 + exit 1 +fi + +# adi_xcvr_project shells out to build a standalone xcvr_wizard Vivado project. +# The toolbox runs the top-level design in HDL Coder in-memory mode (ADI_MATLAB), +# but that mode must NOT leak into the nested build or it skips create_project. +# Patch the sub-make to run with ADI_MATLAB/MATLAB unset. Idempotent. +python3 scripts/patch_xcvr_matlab_env.py "$XCVR_TCL" + mkdir -p ../hdl/vendor/AnalogDevices/vivado/projects/common/boot cp -r scripts/boot/. ../hdl/vendor/AnalogDevices/vivado/projects/common/boot/ diff --git a/CI/scripts/matlab_processors.tcl b/CI/scripts/matlab_processors.tcl index e0c478b4..d87ee9bf 100644 --- a/CI/scripts/matlab_processors.tcl +++ b/CI/scripts/matlab_processors.tcl @@ -1,6 +1,45 @@ - proc preprocess_bd {project carrier rxtx} { + proc adi_cpu_interconnect_cell {} { + # hdl_2026_r1 renamed the control/CPU AXI interconnect from the fixed + # "axi_cpu_interconnect" to a carrier-specific SmartConnect created by + # ad_hpmx_interconnect (e.g. axi_hpm0_lpd_interconnect on ZynqMP/zcu102, + # axi_gp0_interconnect on Zynq-7000, axi_fpd_interconnect on Versal). + # Resolve the actual cell so the MATLAB post-build preprocessing keeps + # working across HDL branches instead of matching zero cells and failing + # "'set_property' expects at least one object". + set candidates [list \ + axi_hpm0_lpd_interconnect \ + axi_gp0_interconnect \ + axi_fpd_interconnect \ + axi_dp_interconnect \ + axi_axi_interconnect \ + axi_cpu_interconnect] + foreach name $candidates { + if {[llength [get_bd_cells -quiet $name]] == 1} { + return $name + } + } + # Last resort: any single *_interconnect whose S00_AXI is driven by the + # processing system's control master. + foreach cell [get_bd_cells -quiet *_interconnect] { + if {[llength [get_bd_intf_pins -quiet $cell/S00_AXI]] == 1} { + set src [get_bd_intf_nets -quiet -of_objects [get_bd_intf_pins $cell/S00_AXI]] + if {[string match *M_AXI_HPM0_LPD* $src] || \ + [string match *M_AXI_FPD* $src] || \ + [string match *M_AXI_GP0* $src] || \ + [string match *M_AXI_HPM0_FPD* $src]} { + return $cell + } + } + } + error "adi_cpu_interconnect_cell: could not resolve CPU AXI interconnect cell" +} + +proc preprocess_bd {project carrier rxtx} { puts "Preprocessing $project $carrier $rxtx" + set cpu_ic [adi_cpu_interconnect_cell] + puts "Using CPU interconnect cell: $cpu_ic" + switch $project { daq2 { if {$rxtx == "rx" || $rxtx == "rxtx"} { @@ -46,14 +85,14 @@ } switch $carrier { zcu102 { - set_property -dict [list CONFIG.NUM_CLKS {2}] [get_bd_cells axi_cpu_interconnect] + set_property -dict [list CONFIG.NUM_CLKS {2}] [get_bd_cells $cpu_ic] if {$rxtx == "rx" || $rxtx == "rxtx"} { - set_property -dict [list CONFIG.NUM_MI {12}] [get_bd_cells axi_cpu_interconnect] - connect_bd_net [get_bd_pins axi_cpu_interconnect/aclk1] [get_bd_pins util_daq2_xcvr/rx_out_clk_0] + set_property -dict [list CONFIG.NUM_MI {12}] [get_bd_cells $cpu_ic] + connect_bd_net [get_bd_pins ${cpu_ic}/aclk1] [get_bd_pins util_daq2_xcvr/rx_out_clk_0] } if {$rxtx == "tx"} { - set_property -dict [list CONFIG.NUM_MI {12}] [get_bd_cells axi_cpu_interconnect] - connect_bd_net [get_bd_pins axi_cpu_interconnect/aclk1] [get_bd_pins util_daq2_xcvr/tx_out_clk_0] + set_property -dict [list CONFIG.NUM_MI {12}] [get_bd_cells $cpu_ic] + connect_bd_net [get_bd_pins ${cpu_ic}/aclk1] [get_bd_pins util_daq2_xcvr/tx_out_clk_0] } } } @@ -104,9 +143,9 @@ switch $carrier { zc706 { if {$rxtx == "rx"} { - set_property -dict [list CONFIG.NUM_MI {9}] [get_bd_cells axi_cpu_interconnect] - connect_bd_net [get_bd_pins axi_cpu_interconnect/M08_ACLK] [get_bd_pins axi_ad9434/adc_clk] - connect_bd_net [get_bd_pins sys_rstgen/peripheral_aresetn] [get_bd_pins axi_cpu_interconnect/M08_ARESETN] + set_property -dict [list CONFIG.NUM_MI {9}] [get_bd_cells $cpu_ic] + connect_bd_net [get_bd_pins ${cpu_ic}/M08_ACLK] [get_bd_pins axi_ad9434/adc_clk] + connect_bd_net [get_bd_pins sys_rstgen/peripheral_aresetn] [get_bd_pins ${cpu_ic}/M08_ARESETN] } } } @@ -147,8 +186,8 @@ switch $carrier { zc706 { if {$rxtx == "tx"} { - set_property -dict [list CONFIG.NUM_MI {9}] [get_bd_cells axi_cpu_interconnect] - connect_bd_net [get_bd_pins axi_cpu_interconnect/M08_ACLK] [get_bd_pins sys_ps7/FCLK_CLK0] + set_property -dict [list CONFIG.NUM_MI {9}] [get_bd_cells $cpu_ic] + connect_bd_net [get_bd_pins ${cpu_ic}/M08_ACLK] [get_bd_pins sys_ps7/FCLK_CLK0] } } } @@ -246,8 +285,8 @@ switch $carrier { zc706 { if {$rxtx == "rx" } { - set_property -dict [list CONFIG.NUM_MI {9}] [get_bd_cells axi_cpu_interconnect] - connect_bd_net [get_bd_pins axi_cpu_interconnect/M08_ACLK] [get_bd_pins axi_ad9265/adc_clk] + set_property -dict [list CONFIG.NUM_MI {9}] [get_bd_cells $cpu_ic] + connect_bd_net [get_bd_pins ${cpu_ic}/M08_ACLK] [get_bd_pins axi_ad9265/adc_clk] } } } @@ -271,8 +310,8 @@ switch $carrier { zc706 { if {$rxtx == "rx" } { - set_property -dict [list CONFIG.NUM_MI {11}] [get_bd_cells axi_cpu_interconnect] - connect_bd_net [get_bd_pins axi_cpu_interconnect/M10_ACLK] [get_bd_pins util_fmcjesdadc1_xcvr/rx_clk_0] + set_property -dict [list CONFIG.NUM_MI {11}] [get_bd_cells $cpu_ic] + connect_bd_net [get_bd_pins ${cpu_ic}/M10_ACLK] [get_bd_pins util_fmcjesdadc1_xcvr/rx_clk_0] } } } @@ -286,11 +325,11 @@ } switch $carrier { zcu102 { - set_property -dict [list CONFIG.NUM_CLKS {2}] [get_bd_cells axi_cpu_interconnect] + set_property -dict [list CONFIG.NUM_CLKS {2}] [get_bd_cells $cpu_ic] if {$rxtx == "tx"} { - set_property -dict [list CONFIG.NUM_MI {4}] [get_bd_cells axi_cpu_interconnect] - connect_bd_net [get_bd_pins axi_cpu_interconnect/aclk1] [get_bd_pins axi_ad9783/dac_div_clk] + set_property -dict [list CONFIG.NUM_MI {4}] [get_bd_cells $cpu_ic] + connect_bd_net [get_bd_pins ${cpu_ic}/aclk1] [get_bd_pins axi_ad9783/dac_div_clk] } } } @@ -309,10 +348,10 @@ } switch $carrier { vcu118 { - set_property -dict [list CONFIG.NUM_CLKS {3}] [get_bd_cells axi_cpu_interconnect] + set_property -dict [list CONFIG.NUM_CLKS {3}] [get_bd_cells $cpu_ic] if {$rxtx == "rx"} { - set_property -dict [list CONFIG.NUM_MI {18}] [get_bd_cells axi_cpu_interconnect] - connect_bd_net [get_bd_pins axi_cpu_interconnect/aclk2] [get_bd_pins glbl_clk_0] + set_property -dict [list CONFIG.NUM_MI {18}] [get_bd_cells $cpu_ic] + connect_bd_net [get_bd_pins ${cpu_ic}/aclk2] [get_bd_pins glbl_clk_0] } } } diff --git a/CI/scripts/patch_xcvr_matlab_env.py b/CI/scripts/patch_xcvr_matlab_env.py new file mode 100644 index 00000000..5e6ea51c --- /dev/null +++ b/CI/scripts/patch_xcvr_matlab_env.py @@ -0,0 +1,108 @@ +#!/usr/bin/env python3 +"""Scrub HDL-Coder MATLAB-mode env vars around the nested xcvr_wizard sub-make. + +Since hdl_2026_r1, transceiver-based reference designs call adi_xcvr_project, +which shells out (`eval exec $make_command`) to build a *standalone* +xcvr_wizard Vivado project. The toolbox sets ADI_MATLAB=1 (and legacy MATLAB=1) +to make the top-level reference design reuse HDL Coder's in-memory project. +Tcl `exec` inherits ::env, so those vars leak into the nested Vivado, which then +skips create_project and dies with "No projects are currently open." + +This patch wraps the exec so the nested make runs with ADI_MATLAB/MATLAB unset, +restoring them afterward. Idempotent: safe to run on every BSP stage. +""" +from __future__ import annotations + +import sys +from pathlib import Path + +TARGET_LINE = " eval exec $make_command\n" +SENTINEL = "_adi_saved_matlab_env" +REPLACEMENT = ( + " # The nested xcvr_wizard build is a standalone Vivado project and must not\n" + " # inherit the HDL Coder in-memory-project mode (ADI_MATLAB/MATLAB); otherwise\n" + " # adi_project skips create_project and the sub-build fails with\n" + ' # "No projects are currently open".\n' + " set _adi_saved_matlab_env {}\n" + " foreach _adi_ev {ADI_MATLAB MATLAB} {\n" + " if {[info exists ::env($_adi_ev)]} {\n" + " dict set _adi_saved_matlab_env $_adi_ev $::env($_adi_ev)\n" + " unset ::env($_adi_ev)\n" + " }\n" + " }\n" + " eval exec $make_command\n" + " dict for {_adi_ev _adi_val} $_adi_saved_matlab_env {\n" + " set ::env($_adi_ev) $_adi_val\n" + " }\n" +) + +# The cfng path adi_xcvr_project reconstructs depends on a fragile +# token-ordering (linsert/tac/MAKELEVEL) that does not match the directory the +# Makefile actually creates when ADI_PROJECT_DIR is unset (the MATLAB/BSP flow). +# Rather than predict the order, glob for the single generated cfng file under +# the xcvr_wizard project dir and use it when the reconstructed path is missing. +RETURN_LINE = ( + ' return [dict create "cfng_file_path" $adi_project_dir_path ' + '"param_file_path" $file_local_param_path]\n' +) +GLOB_SENTINEL = "_adi_xcvr_cfng_glob" +GLOB_FIX = ( + " # Robust fallback: if the reconstructed cfng path does not exist (the\n" + " # xcvr_wizard output directory is named by a Makefile token order that can\n" + " # differ from this reconstruction when ADI_PROJECT_DIR is unset), locate\n" + " # the actual generated cfng file by globbing. Exactly one is produced per\n" + " # xcvr_wizard sub-build.\n" + " if {![file exists $adi_project_dir_path]} {\n" + " set _adi_xcvr_cfng_glob [glob -nocomplain -directory \\\n" + " [file join $ad_hdl_dir/projects $project_name $carrier_name] \\\n" + " -- \"*/${project_name}_${carrier_name}.gen/sources_1/ip/${xcvr_type}_cfng.txt\"]\n" + " if {[llength $_adi_xcvr_cfng_glob] >= 1} {\n" + " set adi_project_dir_path [lindex $_adi_xcvr_cfng_glob 0]\n" + " set config_dir_path [file dirname $adi_project_dir_path]\n" + " if {$xcvr_type == \"GTXE2\"} {\n" + " set file_local_param_path [file join $config_dir_path $config_parser_dir_name $file_local_param]\n" + " }\n" + " }\n" + " }\n" + " return [dict create \"cfng_file_path\" $adi_project_dir_path " + "\"param_file_path\" $file_local_param_path]\n" +) + + +def main() -> int: + path = Path(sys.argv[1]) + text = path.read_text() + changed = False + + if SENTINEL not in text: + count = text.count(TARGET_LINE) + if count != 1: + print(f"ERROR: expected exactly 1 '{TARGET_LINE.strip()}' in {path}, found {count}", + file=sys.stderr) + return 1 + text = text.replace(TARGET_LINE, REPLACEMENT) + changed = True + else: + print("env-scrub already present") + + if GLOB_SENTINEL not in text: + count = text.count(RETURN_LINE) + if count != 1: + print(f"ERROR: expected exactly 1 xcvr return line in {path}, found {count}", + file=sys.stderr) + return 1 + text = text.replace(RETURN_LINE, GLOB_FIX) + changed = True + else: + print("cfng-glob fallback already present") + + if changed: + path.write_text(text) + print(f"patched xcvr sub-make env scrub + cfng-glob fallback into {path}") + else: + print(f"already fully patched: {path}") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/CI/scripts/system_project_rxtx.tcl b/CI/scripts/system_project_rxtx.tcl index 20448356..5a8a4f7f 100644 --- a/CI/scripts/system_project_rxtx.tcl +++ b/CI/scripts/system_project_rxtx.tcl @@ -17,6 +17,11 @@ adi_make::lib all set ::env(SKIP_SYNTHESIS) 1 set ::env(MATLAB) 1 +# hdl_2026_r1 renamed the HDL Coder in-memory-project contract from the legacy +# MATLAB env var to ADI_MATLAB inside adi_project_xilinx.tcl. Set both so the +# reference design reuses HDL Coder's project (instead of calling create_project) +# regardless of which HDL branch supplies adi_project_xilinx.tcl. +set ::env(ADI_MATLAB) 1 set ::env(ADI_USE_OOC_SYNTHESYS) 1 source ./system_project.tcl diff --git a/hdl/vendor/AnalogDevices/+AnalogDevices/get_memory_axi_interface_info.m b/hdl/vendor/AnalogDevices/+AnalogDevices/get_memory_axi_interface_info.m index d61f5509..ee1cc7a6 100644 --- a/hdl/vendor/AnalogDevices/+AnalogDevices/get_memory_axi_interface_info.m +++ b/hdl/vendor/AnalogDevices/+AnalogDevices/get_memory_axi_interface_info.m @@ -5,7 +5,7 @@ case 'daq2' switch fpga case {'ZCU102'} - InterfaceConnection = 'axi_cpu_interconnect/M11_AXI'; + InterfaceConnection = 'axi_hpm0_lpd_interconnect/M11_AXI'; BaseAddress = '0x9D000000'; MasterAddressSpace = 'sys_ps8/Data'; otherwise @@ -24,7 +24,7 @@ case 'ad9434' switch fpga case {'ZC706'} - InterfaceConnection = 'axi_cpu_interconnect/M08_AXI'; + InterfaceConnection = 'axi_gp0_interconnect/M08_AXI'; BaseAddress = '0x50000000'; MasterAddressSpace = 'sys_ps7/Data'; otherwise @@ -33,7 +33,7 @@ case 'fmcjesdadc1' switch fpga case {'ZC706'} - InterfaceConnection = 'axi_cpu_interconnect/M10_AXI'; + InterfaceConnection = 'axi_gp0_interconnect/M10_AXI'; BaseAddress = '0x50000000'; MasterAddressSpace = 'sys_ps7/Data'; otherwise @@ -42,7 +42,7 @@ case 'ad9265' switch fpga case {'ZC706'} - InterfaceConnection = 'axi_cpu_interconnect/M08_AXI'; + InterfaceConnection = 'axi_gp0_interconnect/M08_AXI'; BaseAddress = '0x50000000'; MasterAddressSpace = 'sys_ps7/Data'; otherwise @@ -51,7 +51,7 @@ case 'ad9739a' switch fpga case {'ZC706'} - InterfaceConnection = 'axi_cpu_interconnect/M08_AXI'; + InterfaceConnection = 'axi_gp0_interconnect/M08_AXI'; BaseAddress = '0x50000000'; MasterAddressSpace = 'sys_ps7/Data'; otherwise @@ -60,7 +60,7 @@ case 'ad9783' switch fpga case {'ZCU102'} - InterfaceConnection = 'axi_cpu_interconnect/M03_AXI'; + InterfaceConnection = 'axi_hpm0_lpd_interconnect/M03_AXI'; BaseAddress = '0x9D000000'; MasterAddressSpace = 'sys_ps8/Data'; otherwise @@ -69,7 +69,7 @@ case 'ad9208' switch fpga case {'VCU118'} - InterfaceConnection = 'axi_cpu_interconnect/M17_AXI'; + InterfaceConnection = 'axi_axi_interconnect/M17_AXI'; BaseAddress = '0xFF0000'; MasterAddressSpace = 'sys_mb/Data'; otherwise diff --git a/hdl/vendor/AnalogDevices/+AnalogDevices/plugin_rd.m b/hdl/vendor/AnalogDevices/+AnalogDevices/plugin_rd.m index abae4db9..ac24faeb 100644 --- a/hdl/vendor/AnalogDevices/+AnalogDevices/plugin_rd.m +++ b/hdl/vendor/AnalogDevices/+AnalogDevices/plugin_rd.m @@ -74,10 +74,19 @@ }; % custom source files +% NOTE: quiet.mk and Makefile are HDL-repo-root files (siblings of the +% projects/library/scripts folders). Since hdl_2026_r1, transceiver-based +% reference designs call adi_xcvr_project, which shells out to `make` in +% projects/xcvr_wizard/; that Makefile chain includes ../../../quiet.mk. +% HDL Coder only copies the folders listed here into the generated work area, +% so quiet.mk must be listed explicitly or the xcvr sub-build fails with +% "../../../quiet.mk: No such file or directory". hRD.CustomFiles = {... fullfile('projects')... fullfile('library')... fullfile('scripts')... + fullfile('quiet.mk')... + fullfile('Makefile')... }; hRD.addParameter( ...