2828import iio
2929import adijif
3030import adi
31+ from test .dts_utils import download_and_cache_toolchain , compile_dts_to_dtb , verify_dts_match , TOOLCHAIN_2025_R1_ARCH_ARM64
3132
3233TFTP_BOOT_FOLDER = "/var/lib/tftpboot/"
3334
34- # ARM GNU Toolchain configuration for cross-compiling ARM64 device trees
35- # Vivado/Vitis 2023.2
36- TOOLCHAIN_2023_R2_URL_ARM64 = "https://developer.arm.com/-/media/Files/downloads/gnu/12.2.rel1/binrel/arm-gnu-toolchain-12.2.rel1-x86_64-aarch64-none-elf.tar.xz"
37- TOOLCHAIN_2023_R2_VERSION = "12.2.rel1"
38- TOOLCHAIN_2023_R2_ARCH_ARM64 = "aarch64-none-elf"
39-
40- # ARM GNU Toolchain configuration for cross-compiling ARM32 device trees
41- # Vivado/Vitis 2023.2
42- TOOLCHAIN_2023_R2_URL_ARM32 = "https://developer.arm.com/-/media/Files/downloads/gnu/12.2.rel1/binrel/arm-gnu-toolchain-12.2.rel1-x86_64-arm-none-eabi.tar.xz"
43- TOOLCHAIN_2023_R2_ARCH_ARM32 = "arm-none-eabi"
44-
45- # ARM GNU Toolchain configuration for cross-compiling ARM64 device trees
46- # Vivado/Vitis 2025.1
47- TOOLCHAIN_2025_R1_URL_ARM64 = "https://developer.arm.com/-/media/Files/downloads/gnu/13.3.rel1/binrel/arm-gnu-toolchain-13.3.rel1-x86_64-aarch64-none-elf.tar.xz"
48- TOOLCHAIN_2025_R1_VERSION = "13.3.rel1"
49- TOOLCHAIN_2025_R1_ARCH_ARM64 = "aarch64-none-elf"
50-
51-
52- def download_and_cache_toolchain (arch : str = "arm64" , version : str = "2023.2" , cache_dir : Path = None ) -> Path :
53- """Download and cache ARM GNU toolchain for cross-compilation.
54-
55- Downloads the ARM GNU toolchain for arm or arm64 from ARM's official site
56- and extracts it to a cache directory. If already cached, skips download.
57-
58- Args:
59- arch: Target architecture ('arm' or 'arm64')
60- version: Target version ('2023.2' or '2025.1')
61- cache_dir: Directory to cache toolchain. Defaults to ~/.cache/pyadi-dt/
62-
63- Returns:
64- Path to toolchain bin directory containing cross-compiler
65-
66- Raises:
67- RuntimeError: If download or extraction fails or arch is invalid
68- """
69- # Select toolchain based on architecture
70- if version == "2023.2" :
71- if arch == "arm64" :
72- toolchain_url = TOOLCHAIN_2023_R2_URL_ARM64
73- toolchain_version = TOOLCHAIN_2023_R2_VERSION
74- toolchain_arch = TOOLCHAIN_2023_R2_ARCH_ARM64
75- elif arch == "arm" :
76- toolchain_url = TOOLCHAIN_2023_R2_URL_ARM32
77- toolchain_version = TOOLCHAIN_2023_R2_VERSION
78- toolchain_arch = TOOLCHAIN_2023_R2_ARCH_ARM32
79- else :
80- raise ValueError (f"Unsupported architecture: { arch } . Must be 'arm' or 'arm64'" )
81- elif version == "2025.1" :
82- if arch == "arm64" :
83- toolchain_url = TOOLCHAIN_2025_R1_URL_ARM64
84- toolchain_version = TOOLCHAIN_2025_R1_VERSION
85- toolchain_arch = TOOLCHAIN_2025_R1_ARCH_ARM64
86- else :
87- raise ValueError (f"Unsupported architecture: { arch } . Must be 'arm64'" )
88- else :
89- raise ValueError (f"Unsupported version: { version } . Must be '2023.2' or '2025.1'" )
90-
91- if toolchain_url == "" :
92- raise ValueError (f"Toolchain URL not found for version: { version } " )
93-
94- # Determine cache directory
95- if cache_dir is None :
96- cache_dir = Path .home () / ".cache" / "pyadi-dt" / "toolchains"
97- cache_dir = Path (cache_dir )
98- cache_dir .mkdir (parents = True , exist_ok = True )
99-
100- # Expected toolchain directory after extraction
101- toolchain_name = f"arm-gnu-toolchain-{ toolchain_version } -x86_64-{ toolchain_arch } "
102- toolchain_dir = cache_dir / toolchain_name
103- toolchain_bin = toolchain_dir / "bin"
104-
105- # Check if already cached
106- if toolchain_bin .exists ():
107- print (f" Using cached toolchain: { toolchain_dir } " )
108- return toolchain_bin
109-
110- # Download toolchain
111- print (f" Downloading ARM GNU toolchain { toolchain_version } ({ arch } )..." )
112- tarball_path = cache_dir / f"{ toolchain_name } .tar.xz"
113-
114- try :
115- with urllib .request .urlopen (toolchain_url ) as response :
116- total_size = int (response .headers .get ('content-length' , 0 ))
117- downloaded = 0
118- chunk_size = 1024 * 1024 # 1MB chunks
119-
120- with open (tarball_path , 'wb' ) as f :
121- while True :
122- chunk = response .read (chunk_size )
123- if not chunk :
124- break
125- f .write (chunk )
126- downloaded += len (chunk )
127- if total_size > 0 :
128- percent = (downloaded / total_size ) * 100
129- print (f" Progress: { percent :.1f} % ({ downloaded // (1024 * 1024 )} MB / { total_size // (1024 * 1024 )} MB)" , end = '\r ' )
130-
131- print (f"\n Download complete: { tarball_path } " )
132-
133- # Extract toolchain
134- print (f" Extracting toolchain..." )
135- with tarfile .open (tarball_path , 'r:xz' ) as tar :
136- tar .extractall (cache_dir )
137-
138- # Verify extraction
139- if not toolchain_bin .exists ():
140- raise RuntimeError (f"Toolchain extraction failed: { toolchain_bin } not found" )
141-
142- # Clean up tarball to save space
143- tarball_path .unlink ()
144- print (f" Toolchain ready: { toolchain_bin } " )
145-
146- return toolchain_bin
147-
148- except Exception as e :
149- # Clean up on failure
150- if tarball_path .exists ():
151- tarball_path .unlink ()
152- raise RuntimeError (f"Failed to download/extract toolchain: { e } " )
153-
15435
15536def get_ad9084_config () -> dict :
15637 """Get AD9084 configuration.
15738
15839 Returns:
15940 Complete configuration dict for ad9084_fmc board
16041 """
161- vcxo = 122880000
42+ vcxo = 125000000
16243 sys = adijif .system ("ad9084" , "hmc7044" , "xilinx" , vcxo )
16344 sys .fpga .setup_by_dev_kit_name ("vpk180" )
16445
@@ -184,107 +65,7 @@ def get_ad9084_config() -> dict:
18465 return cfg
18566
18667
187- def compile_dts_to_dtb (dts_path : Path , dtb_path : Path , kernel_path : str , arch : str = "arm64" , version : str = "2023.2" , cross_compile : str = None ) -> None :
188- """Compile DTS to DTB using kernel build system with cross-compiler.
189- """
190- # Validate architecture
191- if arch not in ["arm" , "arm64" ]:
192- raise ValueError (f"Unsupported architecture: { arch } . Must be 'arm' or 'arm64'" )
193-
194- # Download and cache cross-compiler if not provided
195- if cross_compile is None :
196- print (f" Setting up { arch .upper ()} cross-compiler..." )
197- toolchain_bin = download_and_cache_toolchain (arch = arch , version = version )
198-
199- if version == "2023.2" :
200- if arch == "arm64" :
201- cross_compile = f"{ toolchain_bin } /{ TOOLCHAIN_2023_R2_ARCH_ARM64 } -"
202- else : # arch == "arm"
203- cross_compile = f"{ toolchain_bin } /{ TOOLCHAIN_2023_R2_ARCH_ARM32 } -"
204- elif version == "2025.1" :
205- if arch == "arm64" :
206- cross_compile = f"{ toolchain_bin } /{ TOOLCHAIN_2025_R1_ARCH_ARM64 } -"
207- else : # arch == "arm"
208- # Assuming 2025.1 arm32 follows same pattern or is not supported yet?
209- # The file didn't define TOOLCHAIN_2025_R1_ARCH_ARM32, so we might fail here if used.
210- # Lines 81-87 in download_and_cache_toolchain suggest 2025.1 only supports arm64 for now.
211- raise ValueError ("ARM32 not supported for version 2025.1" )
212- else :
213- raise ValueError (f"Unsupported version: { version } . Must be '2023.2' or '2025.1'" )
214-
215- print (f" ✓ Cross-compiler ready: { cross_compile } " )
216-
217- # Set up environment for kernel compilation
218- env = os .environ .copy ()
219- env ['ARCH' ] = arch
220- env ['CROSS_COMPILE' ] = cross_compile
221-
222- # Determine platform-specific paths
223- dts_filename = dts_path .name
224- if arch == "arm64" :
225- kernel_dts_dir = Path (kernel_path ) / "arch" / arch / "boot" / "dts" / "xilinx"
226- else :
227- kernel_dts_dir = Path (kernel_path ) / "arch" / arch / "boot" / "dts"
228- kernel_dts_path = kernel_dts_dir / dts_filename
229-
230- # DTB will be compiled to same location with .dtb extension
231- kernel_dtb_path = kernel_dts_path .with_suffix ('.dtb' )
232-
233- # Step 1: Copy DTS file into kernel tree
234- shutil .copy2 (dts_path , kernel_dts_path )
235-
236- # Step 2: Ensure kernel is configured
237- print (" Configuring kernel..." )
238-
239- if arch == "arm64" :
240- defconfig = "adi_zynqmp_defconfig"
241- else :
242- defconfig = "zynq_xcomm_adv7511_defconfig"
243-
244- config_cmd = ["make" , defconfig ]
245- print (f" Running: { ' ' .join (config_cmd )} " )
246- config_result = subprocess .run (
247- config_cmd ,
248- cwd = kernel_path ,
249- capture_output = True ,
250- text = True ,
251- env = env
252- )
253- if config_result .returncode != 0 :
254- raise RuntimeError (f"Kernel configuration failed: { config_result .stderr } " )
255- print (" Kernel configured." )
256-
257- # Step 3: Compile DTB using kernel make system
258- print (" Compiling DTB..." )
259- if arch == "arm64" :
260- make_target = f"xilinx/{ dts_filename .replace ('.dts' , '.dtb' )} "
261- else :
262- make_target = f"{ dts_filename .replace ('.dts' , '.dtb' )} "
263- make_cmd = ["make" , make_target ]
264- print (f" Running: { ' ' .join (make_cmd )} " )
265-
266- make_result = subprocess .run (
267- make_cmd ,
268- cwd = kernel_path ,
269- capture_output = True ,
270- text = True ,
271- env = env
272- )
273-
274- if make_result .returncode != 0 :
275- raise RuntimeError (
276- f"DTB compilation failed:\n "
277- f"Command: { ' ' .join (make_cmd )} \n "
278- f"ARCH={ env ['ARCH' ]} CROSS_COMPILE={ env ['CROSS_COMPILE' ]} \n "
279- f"Error: { make_result .stderr } "
280- )
28168
282- # Step 4: Verify DTB was created
283- if not kernel_dtb_path .exists ():
284- raise RuntimeError (f"DTB file not created at { kernel_dtb_path } " )
285-
286- # Step 5: Copy compiled DTB to desired output location
287- shutil .copy2 (kernel_dtb_path , dtb_path )
28869
28970
29071class ad9084_fmc_no_plugin (ad9084_fmc ):
@@ -395,7 +176,7 @@ def test_vpk180_rev10_ad9084(
395176
396177 # 3a. Compile Generated
397178 gen_dtb = dtb_output_dir / "generated.dtb"
398- compile_dts_to_dtb (Path (generated_dts ), gen_dtb , kernel_path , arch = "arm64" , version = "2025.1" )
179+ compile_dts_to_dtb (Path (generated_dts ), gen_dtb , kernel_path , arch = "arm64" , version = "2025.1" , platform = "vpk180" )
399180
400181 # 3b. Compile Reference
401182 ref_dts_name = "versal-vpk180-reva-ad9084.dts"
@@ -438,65 +219,7 @@ def test_vpk180_rev10_ad9084(
438219 print (f" Reference DTB Size: { ref_dtb .stat ().st_size } " )
439220
440221 print (f"[3/4] Verifying content match..." )
441-
442- dtc_path = Path (kernel_path ) / "scripts/dtc/dtc"
443- if not dtc_path .exists ():
444- dtc_path = "dtc"
445-
446- def dtb_to_dts (dtb , output ):
447- cmd = [str (dtc_path ), "-I" , "dtb" , "-O" , "dts" , "-o" , str (output ), "-s" , str (dtb )]
448- subprocess .run (cmd , check = True , capture_output = True )
449-
450- gen_dts_flat = dtb_output_dir / "generated_flat.dts"
451- ref_dts_flat = dtb_output_dir / "reference_flat.dts"
452-
453- try :
454- dtb_to_dts (gen_dtb , gen_dts_flat )
455- dtb_to_dts (ref_dtb , ref_dts_flat )
456-
457- # Read and Compare text
458- with open (gen_dts_flat ) as f : gen_text = f .readlines ()
459- with open (ref_dts_flat ) as f : ref_text = f .readlines ()
460-
461- print (" Comparing flattened DTS content..." )
462-
463- mismatches = []
464-
465- ref_set = set ([l .strip () for l in ref_text ])
466-
467- for line in gen_text :
468- l = line .strip ()
469- if not l : continue
470- # Skip phandle noise if it varies
471- if "phandle =" in l : continue
472- if "linux,phandle" in l : continue
473-
474- if l not in ref_set :
475- mismatches .append (f"Excess in GEN: { l } " )
476-
477- # Also check if REF has lines missing in GEN (Critical!)
478- gen_set = set ([l .strip () for l in gen_text ])
479- for line in ref_text :
480- l = line .strip ()
481- if not l : continue
482- if "phandle =" in l : continue
483- if "linux,phandle" in l : continue
484-
485- if l not in gen_set :
486- mismatches .append (f"Missing in GEN: { l } " )
487-
488- if mismatches :
489- # Dump first few
490- print (" Mismatch details:" )
491- for m in mismatches [:50 ]:
492- print (f" { m } " )
493- raise Exception (f"Found { len (mismatches )} mismatches in generated DTS content" )
494-
495- print (" ✓ DTS Content Matches Reference" )
496-
497- except Exception as e :
498- print (f" Warning: DTC comparison failed: { e } " )
499- raise e
222+ verify_dts_match (gen_dtb , ref_dtb , kernel_path , dtb_output_dir )
500223
501224 # Step 4: Deploy (Run)
502225 print (f"[3/4] Deploying to board..." )
0 commit comments