55# SPDX-License-Identifier: MIT
66#
77import os # For file and directory operations
8+ import re
89import subprocess # For executing external programs
910
1011from . import common # For shared utilities across tools
1112
1213
14+ def _get_sw_attr (obj , names ):
15+ """Return the first non-empty string attribute from *names*, or empty string."""
16+ for name in names :
17+ value = getattr (obj , name , None )
18+ if value is not None :
19+ text = str (value ).strip ()
20+ if text :
21+ return text
22+ return ""
23+
24+
25+ def _find_createbitfile_exe ():
26+ """Auto-discover createBitfile.exe from the latest installed LabVIEW using nisyscfg.
27+
28+ Queries NI System Configuration for installed LabVIEW versions, then
29+ checks the standard installation directories for createBitfile.exe,
30+ preferring the latest version.
31+
32+ Returns:
33+ str or None: Absolute path to createBitfile.exe, or None if not found.
34+ """
35+ try :
36+ import nisyscfg
37+ except ImportError :
38+ print ("Warning: nisyscfg package not available, cannot auto-discover LabVIEW" )
39+ return None
40+
41+ labview_years = set ()
42+ try :
43+ with nisyscfg .Session () as session :
44+ for sw in session .get_installed_software_components ():
45+ title = _get_sw_attr (sw , ["title" , "display_name" , "name" , "product_name" , "id" ])
46+ if re .match (r"^(NI\s+)?LabVIEW\s+\d{4}" , title , re .IGNORECASE ):
47+ year_match = re .search (r"(\d{4})" , title )
48+ if year_match :
49+ labview_years .add (int (year_match .group (1 )))
50+ except Exception as exc :
51+ print (f"Warning: Failed to query NI System Configuration: { exc } " )
52+ return None
53+
54+ if not labview_years :
55+ return None
56+
57+ # Try each LabVIEW version from latest to oldest
58+ program_files = os .environ .get ("ProgramFiles" , r"C:\Program Files" )
59+ for year in sorted (labview_years , reverse = True ):
60+ candidate = os .path .join (
61+ program_files ,
62+ "National Instruments" ,
63+ f"LabVIEW { year } " ,
64+ "vi.lib" ,
65+ "rvi" ,
66+ "CDR" ,
67+ "createBitfile.exe" ,
68+ )
69+ if os .path .isfile (candidate ):
70+ print (f"Found createBitfile.exe from LabVIEW { year } " )
71+ return candidate
72+
73+ return None
74+
75+
1376def _create_lv_bitfile (config = None ):
1477 """Create the LabVIEW FPGA .lvbitx file by executing the createBitfile.exe tool."""
1578 if os .name != "nt" :
@@ -21,7 +84,22 @@ def _create_lv_bitfile(config=None):
2184 path_parts = [part .lower () for part in os .path .normpath (vivado_impl_folder ).split (os .sep )]
2285 if "impl_1" not in path_parts :
2386 print (
24- "WARNING: This function must be run from within the implementation folder of a Vivado project.\n (e.g. C:\\ dev\\ github\\ flexrio\\ targets\\ pxie-7903\\ VivadoProject\\ MySasquatchProj.runs\\ impl_1)"
87+ "\n "
88+ "************************************************************\n "
89+ "*** WARNING ***\n "
90+ "************************************************************\n "
91+ "* This function must be run from within the implementation\n "
92+ "* folder of a Vivado project.\n "
93+ "*\n "
94+ "* Expected CWD example:\n "
95+ "* C:\\ dev\\ flexrio\\ targets\\ pxie-7903\\ VivadoProject\\ MyProj.runs\\ impl_1\n "
96+ "*\n "
97+ "* When called from that folder, use --config to\n "
98+ "* point back to the target's nihdlsettings.py:\n "
99+ "*\n "
100+ "* nihdl create-lvbitx --config=../../../nihdlsettings.py\n "
101+ "*\n "
102+ "************************************************************\n "
25103 )
26104
27105 # This script is run by a TCL script in Vivado after the bitstream is generated and the
@@ -33,19 +111,6 @@ def _create_lv_bitfile(config=None):
33111 if config is None :
34112 config = common .FileConfiguration ()
35113
36- # Check if LV path is set
37- if config .lv_path is None :
38- print ("Error: LabVIEW path not set in configuration" )
39- return 1
40-
41- # Construct path to createBitfile.exe
42- createbitfile_exe = os .path .join (config .lv_path , "vi.lib" , "rvi" , "CDR" , "createBitfile.exe" )
43-
44- # Check if the executable exists
45- if not os .path .exists (createbitfile_exe ):
46- print (f"Error: createBitfile.exe not found at { createbitfile_exe } " )
47- return 1
48-
49114 # Determine path to CodeGenerationResults.lvtxt based on UseGeneratedLVWindowFiles setting
50115 if config .use_gen_lv_window_files :
51116 # Check if window folder is set
@@ -62,8 +127,14 @@ def _create_lv_bitfile(config=None):
62127 code_gen_results_path = os .path .join (window_folder , "CodeGenerationResults.lvtxt" )
63128 else :
64129 print ("Using default LV window files" )
65- # Use the path from configuration
66130 code_gen_results_path = config .code_generation_results_stub
131+ if code_gen_results_path is None :
132+ print ("Error: code_generation_results_stub not set in configuration" )
133+ return 1
134+
135+ if config .top_level_entity is None :
136+ print ("Error: top_level_entity not set in configuration" )
137+ return 1
67138
68139 print (f"LabVIEW code generation results path: { code_gen_results_path } " )
69140
@@ -73,6 +144,21 @@ def _create_lv_bitfile(config=None):
73144 lvbitx_output_path = os .path .abspath (f"objects/bitfiles/{ config .top_level_entity } .lvbitx" )
74145 print (f"Output .lvbitx path: { lvbitx_output_path } " )
75146
147+ # In skip_vivado mode, create a mock file without needing createBitfile.exe
148+ if config .skip_vivado :
149+ print ("SKIP VIVADO: Validation successful, skipping createBitfile.exe launch" )
150+ os .makedirs (os .path .dirname (lvbitx_output_path ), exist_ok = True )
151+ with open (lvbitx_output_path , "w" ) as f :
152+ f .write ("# Mock LVBITX file created for testing\n " )
153+ print (f"Created mock LVBITX file at: { lvbitx_output_path } " )
154+ return 0
155+
156+ # Auto-discover createBitfile.exe from the latest installed LabVIEW
157+ createbitfile_exe = _find_createbitfile_exe ()
158+ if createbitfile_exe is None :
159+ print ("Error: Could not find createBitfile.exe. Is LabVIEW installed?" )
160+ return 1
161+
76162 # Create the directory for the new file if it doesn't exist
77163 os .makedirs (os .path .dirname (lvbitx_output_path ), exist_ok = True )
78164
@@ -86,17 +172,6 @@ def _create_lv_bitfile(config=None):
86172
87173 print (f"Executing: { ' ' .join (cmd )} " )
88174
89- # In skip_vivado mode, stop here after validation
90- if config .skip_vivado :
91- print ("SKIP VIVADO: Validation successful, skipping createBitfile.exe launch" )
92-
93- # Create a mock LVBITX file for testing
94- os .makedirs (os .path .dirname (lvbitx_output_path ), exist_ok = True )
95- with open (lvbitx_output_path , "w" ) as f :
96- f .write ("# Mock LVBITX file created for testing\n " )
97- print (f"Created mock LVBITX file at: { lvbitx_output_path } " )
98- return 0
99-
100175 # Execute the command
101176 result = subprocess .run (cmd , capture_output = True , text = True , check = False )
102177
0 commit comments