labview-fpga-hdl-tools 2.0.0.dev0__tar.gz → 2.0.0.dev1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (28) hide show
  1. {labview_fpga_hdl_tools-2.0.0.dev0 → labview_fpga_hdl_tools-2.0.0.dev1}/PKG-INFO +1 -1
  2. {labview_fpga_hdl_tools-2.0.0.dev0 → labview_fpga_hdl_tools-2.0.0.dev1}/labview_fpga_hdl_tools/create_vivado_project.py +61 -27
  3. {labview_fpga_hdl_tools-2.0.0.dev0 → labview_fpga_hdl_tools-2.0.0.dev1}/labview_fpga_hdl_tools/gen_labview_target_plugin.py +106 -22
  4. {labview_fpga_hdl_tools-2.0.0.dev0 → labview_fpga_hdl_tools-2.0.0.dev1}/labview_fpga_hdl_tools/process_constraints.py +54 -0
  5. {labview_fpga_hdl_tools-2.0.0.dev0 → labview_fpga_hdl_tools-2.0.0.dev1}/pyproject.toml +1 -1
  6. {labview_fpga_hdl_tools-2.0.0.dev0 → labview_fpga_hdl_tools-2.0.0.dev1}/LICENSE +0 -0
  7. {labview_fpga_hdl_tools-2.0.0.dev0 → labview_fpga_hdl_tools-2.0.0.dev1}/README.md +0 -0
  8. {labview_fpga_hdl_tools-2.0.0.dev0 → labview_fpga_hdl_tools-2.0.0.dev1}/labview_fpga_hdl_tools/.gitignore +0 -0
  9. {labview_fpga_hdl_tools-2.0.0.dev0 → labview_fpga_hdl_tools-2.0.0.dev1}/labview_fpga_hdl_tools/__init__.py +0 -0
  10. {labview_fpga_hdl_tools-2.0.0.dev0 → labview_fpga_hdl_tools-2.0.0.dev1}/labview_fpga_hdl_tools/__main__.py +0 -0
  11. {labview_fpga_hdl_tools-2.0.0.dev0 → labview_fpga_hdl_tools-2.0.0.dev1}/labview_fpga_hdl_tools/check_syntax.py +0 -0
  12. {labview_fpga_hdl_tools-2.0.0.dev0 → labview_fpga_hdl_tools-2.0.0.dev1}/labview_fpga_hdl_tools/command_config.py +0 -0
  13. {labview_fpga_hdl_tools-2.0.0.dev0 → labview_fpga_hdl_tools-2.0.0.dev1}/labview_fpga_hdl_tools/command_hooks.py +0 -0
  14. {labview_fpga_hdl_tools-2.0.0.dev0 → labview_fpga_hdl_tools-2.0.0.dev1}/labview_fpga_hdl_tools/common.py +0 -0
  15. {labview_fpga_hdl_tools-2.0.0.dev0 → labview_fpga_hdl_tools-2.0.0.dev1}/labview_fpga_hdl_tools/compile_modelsim_lib.py +0 -0
  16. {labview_fpga_hdl_tools-2.0.0.dev0 → labview_fpga_hdl_tools-2.0.0.dev1}/labview_fpga_hdl_tools/compile_project.py +0 -0
  17. {labview_fpga_hdl_tools-2.0.0.dev0 → labview_fpga_hdl_tools-2.0.0.dev1}/labview_fpga_hdl_tools/create_lvbitx.py +0 -0
  18. {labview_fpga_hdl_tools-2.0.0.dev0 → labview_fpga_hdl_tools-2.0.0.dev1}/labview_fpga_hdl_tools/create_modelsim_project.py +0 -0
  19. {labview_fpga_hdl_tools-2.0.0.dev0 → labview_fpga_hdl_tools-2.0.0.dev1}/labview_fpga_hdl_tools/generate_vhdl.py +0 -0
  20. {labview_fpga_hdl_tools-2.0.0.dev0 → labview_fpga_hdl_tools-2.0.0.dev1}/labview_fpga_hdl_tools/get_window_netlist.py +0 -0
  21. {labview_fpga_hdl_tools-2.0.0.dev0 → labview_fpga_hdl_tools-2.0.0.dev1}/labview_fpga_hdl_tools/install_dependencies.py +0 -0
  22. {labview_fpga_hdl_tools-2.0.0.dev0 → labview_fpga_hdl_tools-2.0.0.dev1}/labview_fpga_hdl_tools/install_labview_target_plugin.py +0 -0
  23. {labview_fpga_hdl_tools-2.0.0.dev0 → labview_fpga_hdl_tools-2.0.0.dev1}/labview_fpga_hdl_tools/launch_modelsim.py +0 -0
  24. {labview_fpga_hdl_tools-2.0.0.dev0 → labview_fpga_hdl_tools-2.0.0.dev1}/labview_fpga_hdl_tools/launch_vivado.py +0 -0
  25. {labview_fpga_hdl_tools-2.0.0.dev0 → labview_fpga_hdl_tools-2.0.0.dev1}/labview_fpga_hdl_tools/migrate_clip.py +0 -0
  26. {labview_fpga_hdl_tools-2.0.0.dev0 → labview_fpga_hdl_tools-2.0.0.dev1}/labview_fpga_hdl_tools/nihdlsettings_default.py +0 -0
  27. {labview_fpga_hdl_tools-2.0.0.dev0 → labview_fpga_hdl_tools-2.0.0.dev1}/labview_fpga_hdl_tools/reporting.py +0 -0
  28. {labview_fpga_hdl_tools-2.0.0.dev0 → labview_fpga_hdl_tools-2.0.0.dev1}/labview_fpga_hdl_tools/sim_modelsim.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: labview-fpga-hdl-tools
3
- Version: 2.0.0.dev0
3
+ Version: 2.0.0.dev1
4
4
  Summary: LabVIEW FPGA HDL Tools
5
5
  License: MIT
6
6
  Author: Salvador Santolucito
@@ -25,6 +25,27 @@ from enum import Enum
25
25
  from . import common, generate_vhdl, process_constraints
26
26
  from .reporting import reporter
27
27
 
28
+ # Windows MAX_PATH. When the generated TCL adds a source file, Vivado resolves the
29
+ # file's add_files path (which is relative to the TCL folder) against the Vivado
30
+ # project folder -- its working directory -- WITHOUT collapsing the leading ".."
31
+ # segments. On Windows that raw, non-normalized string is bounded by MAX_PATH.
32
+ # Empirically a resolved length of 260 fails with "[Vivado 12-172] File or
33
+ # Directory ... does not exist" even though the file exists; 259 is the longest
34
+ # that works.
35
+ VIVADO_MAX_PATH_LEN = 260
36
+
37
+ # Copy a source file into objects/gatheredfiles once its Vivado-resolved path would
38
+ # reach this length. This sits just below VIVADO_MAX_PATH_LEN so a normal install
39
+ # never trips the hard failure, while leaving as much room as possible so that
40
+ # dependency files shipped at their real paths are added in place rather than
41
+ # copied. The dependency path shortening in the flexrio build (build.py
42
+ # DEPS_MAX_REL_PATH_LEN) is sized to keep normal installs under this limit.
43
+ VIVADO_SAFE_MAX_PATH_LEN = 255
44
+
45
+ # Non-Windows platforms are not affected by MAX_PATH; use a generous limit that
46
+ # effectively only guards against pathological path lengths.
47
+ POSIX_MAX_PATH_LEN = 4096
48
+
28
49
 
29
50
  def _has_spaces(file_path):
30
51
  """Checks if the given file path contains spaces.
@@ -211,17 +232,25 @@ def _find_and_log_duplicates(file_list):
211
232
  return unique_file_list
212
233
 
213
234
 
214
- def _copy_long_path_files(file_list):
215
- """Copies files to the "objects/gatheredfiles" folder.
235
+ def _copy_long_path_files(file_list, vivado_project_dir, tcl_base_dir):
236
+ """Copies files that would exceed Vivado's path limit into "objects/gatheredfiles".
237
+
238
+ Vivado does not use a source file's absolute path to decide whether it can open
239
+ it. The generated TCL adds each file with a path that is relative to the TCL
240
+ folder, and Vivado resolves that relative path against the Vivado project folder
241
+ (its working directory) WITHOUT collapsing the leading ".." segments. On Windows
242
+ that raw, non-normalized string is bounded by the 260-char MAX_PATH limit, so it
243
+ -- not the shorter normalized absolute path -- is what determines whether Vivado
244
+ reports "[Vivado 12-172] File or Directory ... does not exist".
216
245
 
217
- The function handles:
218
- - Creating the target directory if needed
219
- - Handling Windows long paths for deep directory structures
220
- - Setting proper file permissions
221
- - Error reporting for failed copy operations
246
+ This function reproduces that exact resolution to decide which files must be
247
+ copied to a short local path before being added to the project.
222
248
 
223
249
  Args:
224
250
  file_list (list): Original list of file paths
251
+ vivado_project_dir (str): Absolute path of the Vivado project folder, which
252
+ Vivado uses as its working directory when the generated TCL runs
253
+ tcl_base_dir (str): Folder that the add_files paths are made relative to
225
254
 
226
255
  Returns:
227
256
  list: Updated file list with long path files moved to local paths
@@ -237,29 +266,28 @@ def _copy_long_path_files(file_list):
237
266
  # Store original file path before modification
238
267
  original_file = file
239
268
  absolute_file_path = os.path.abspath(original_file)
240
- try:
241
- relative_file_path = os.path.relpath(absolute_file_path, os.getcwd())
242
- except ValueError:
243
- relative_file_path = original_file
244
269
 
245
- # Handle long paths on Windows
270
+ # Reproduce the path length Vivado will actually see. On Windows this is the
271
+ # non-normalized "<project folder>\<relative add_files path>" string that is
272
+ # subject to MAX_PATH; elsewhere the generous absolute-path limit applies.
273
+ if os.name == "nt":
274
+ tcl_relative_path = os.path.relpath(absolute_file_path, tcl_base_dir)
275
+ vivado_path_len = len(vivado_project_dir) + len(os.sep) + len(tcl_relative_path)
276
+ path_len_limit = VIVADO_SAFE_MAX_PATH_LEN
277
+ else:
278
+ vivado_path_len = len(absolute_file_path)
279
+ path_len_limit = POSIX_MAX_PATH_LEN
280
+
281
+ # Handle long source paths on Windows so the copy itself can read them.
246
282
  if os.name == "nt":
247
283
  file = f"\\\\?\\{absolute_file_path}"
248
284
  target_folder_long = f"\\\\?\\{os.path.abspath(target_folder)}"
249
285
  else:
250
286
  target_folder_long = target_folder
251
287
 
252
- # Vivado has a problem with adding long file paths to the project,
253
- # so we check if the file path is too long and if so we copy it
254
- # to a local folder and add it from there instead. We check both
255
- # the absolute path and the relative path because Vivado might be
256
- # using either one when processing the TCL script, and we want to
257
- # ensure that we catch all cases where the path length could be
258
- # an issue.
259
- max_path = 200 if os.name == "nt" else 4096
260
-
261
- # Check if the absolute or relative path is longer than max_path characters.
262
- if len(absolute_file_path) > max_path or len(relative_file_path) > max_path:
288
+ # Copy the file to a short local path when Vivado's resolved path would be
289
+ # too long. Files at short paths are added from their original location.
290
+ if vivado_path_len > path_len_limit:
263
291
  target_path = os.path.join(target_folder_long, os.path.basename(file))
264
292
  if os.path.exists(target_path):
265
293
  os.chmod(target_path, 0o777) # Make the file writable
@@ -267,7 +295,10 @@ def _copy_long_path_files(file_list):
267
295
  shutil.copy2(file, target_path)
268
296
  new_file_list.append(target_path)
269
297
  reporter.warn(f"WARNING: Long path file {original_file}")
270
- reporter.detail(f" was copied into the objects/gatheredfiles folder.")
298
+ reporter.detail(
299
+ f" (Vivado path length {vivado_path_len} exceeds the safe "
300
+ f"limit of {path_len_limit}) was copied into the objects/gatheredfiles folder."
301
+ )
271
302
  reporter.detail(
272
303
  f" You must run 'nihdl create-project --update' to pull in any changes to the source file."
273
304
  )
@@ -592,9 +623,12 @@ def _create_project(mode: ProjectMode, config):
592
623
 
593
624
  # Copy long path files to the gatheredfiles folder
594
625
  # Returns the file list with the files from old long path locations having
595
- # new locations in gatheredfiles
596
- file_list = _copy_long_path_files(file_list)
597
- vhdl2008_file_list = _copy_long_path_files(vhdl2008_file_list)
626
+ # new locations in gatheredfiles. The Vivado project folder and TCL folder are
627
+ # needed to reproduce the exact path length Vivado resolves at add_files time.
628
+ tcl_base_dir = os.path.join(current_dir, "TCL")
629
+ vivado_project_dir = os.path.join(current_dir, config.vivado_project_folder)
630
+ file_list = _copy_long_path_files(file_list, vivado_project_dir, tcl_base_dir)
631
+ vhdl2008_file_list = _copy_long_path_files(vhdl2008_file_list, vivado_project_dir, tcl_base_dir)
598
632
 
599
633
  # Override default LV generated files with extracted window files
600
634
  if config.lv_window_netlist_folder:
@@ -38,6 +38,10 @@ DOCUMENT_ROOT_PREFIX = (
38
38
 
39
39
  # Data type prototypes mapping - used to map LabVIEW data types to their FPGA representations
40
40
  # The {direction} placeholder is replaced with Input/OutputWithoutReadback based on signal direction
41
+ #
42
+ # LV FPGA supported prototypes are documented here -
43
+ # src/lvfpga/source/LabVIEW/resource/RVI/FPGATargetXML/niLvFpgaTargetNecessities.xml
44
+ #
41
45
  DATA_TYPE_PROTOTYPES = {
42
46
  "FXP": DOCUMENT_ROOT_PREFIX
43
47
  + "FXPDigital{direction}{output_readback}{zero_sync_regs}", # Fixed-point numeric type
@@ -61,6 +65,49 @@ DATA_TYPE_PROTOTYPES = {
61
65
  + "i64Digital{direction}{output_readback}{zero_sync_regs}", # Signed 64-bit integer
62
66
  }
63
67
 
68
+ # The BoardIO prototype name is assembled as:
69
+ # <type>Digital<direction><output_readback><zero_sync_regs>
70
+ # Not every permutation of the CSV Direction / OutputReadback / ZeroSyncRegs
71
+ # columns corresponds to a prototype that LabVIEW FPGA publishes. These sets
72
+ # capture the supported suffixes (everything after "<type>Digital") so that
73
+ # unsupported combinations are reported as validation errors instead of being
74
+ # written out as unusable prototypes.
75
+ #
76
+ # Every supported data type allows these combinations:
77
+ _BASE_PROTOTYPE_SUFFIXES = frozenset(
78
+ {
79
+ "Input",
80
+ "InputZeroDefaultSyncRegisters",
81
+ "OutputWithReadback",
82
+ "OutputWithReadbackZeroDefaultSyncRegisters",
83
+ "OutputWithoutReadback",
84
+ }
85
+ )
86
+
87
+ # Only Boolean and FXP additionally support a zero-default-sync-register form of
88
+ # the without-readback output. The integer port types (U8-U64, I8-I64) do not.
89
+ _TYPES_SUPPORTING_WITHOUT_READBACK_ZERO_SYNC = frozenset({"Boolean", "FXP"})
90
+
91
+
92
+ def _get_supported_prototype_suffixes(data_type_name):
93
+ """Return the set of supported prototype suffixes for a CSV DataType.
94
+
95
+ The suffix is the portion of the prototype name after ``<type>Digital``
96
+ (i.e. ``<direction><output_readback><zero_sync_regs>``). This encodes which
97
+ Direction / OutputReadback / ZeroSyncRegs permutations map to a real
98
+ LabVIEW FPGA prototype for the given data type.
99
+
100
+ Args:
101
+ data_type_name (str): DataType base name (e.g. ``Boolean``, ``U8``, ``FXP``).
102
+
103
+ Returns:
104
+ set[str]: Supported prototype-name suffixes for the data type.
105
+ """
106
+ suffixes = set(_BASE_PROTOTYPE_SUFFIXES)
107
+ if data_type_name in _TYPES_SUPPORTING_WITHOUT_READBACK_ZERO_SYNC:
108
+ suffixes.add("OutputWithoutReadbackZeroDefaultSyncRegisters")
109
+ return suffixes
110
+
64
111
 
65
112
  def _parse_register_offset(offset_text):
66
113
  """Parse a register offset string as an integer.
@@ -286,10 +333,14 @@ def _generate_xml_from_csv(csv_path, boardio_output_path, clock_output_path):
286
333
  "true": "ZeroDefaultSyncRegisters",
287
334
  "false": "",
288
335
  }.get(zero_sync_regs.lower())
289
- if io_zero_sync_regs is None and zero_sync_regs:
290
- error = f"Row {row_count}: Invalid ZeroSyncRegs '{zero_sync_regs}' for signal '{lv_name}'. Must be 'TRUE' or 'FALSE'."
291
- validation_errors.append(error)
292
- io_zero_sync_regs = "INVALID_SYNC_REGS"
336
+ if io_zero_sync_regs is None:
337
+ if zero_sync_regs:
338
+ error = f"Row {row_count}: Invalid ZeroSyncRegs '{zero_sync_regs}' for signal '{lv_name}'. Must be 'TRUE' or 'FALSE'."
339
+ validation_errors.append(error)
340
+ io_zero_sync_regs = "INVALID_SYNC_REGS"
341
+ else:
342
+ # An empty cell is treated as FALSE (no suffix appended).
343
+ io_zero_sync_regs = ""
293
344
 
294
345
  # Validate output readback setting for outputs
295
346
  if io_direction == "Output":
@@ -308,25 +359,54 @@ def _generate_xml_from_csv(csv_path, boardio_output_path, clock_output_path):
308
359
  data_type_name = data_type.split("(")[0] if "(" in data_type else data_type
309
360
 
310
361
  if data_type_name in DATA_TYPE_PROTOTYPES:
311
- prototype = DATA_TYPE_PROTOTYPES[data_type_name].format(
312
- direction=io_direction,
313
- zero_sync_regs=io_zero_sync_regs,
314
- output_readback=io_output_readback,
362
+ # The prototype name is assembled from the direction,
363
+ # readback, and sync-register tokens. Not every
364
+ # permutation corresponds to a prototype that LabVIEW
365
+ # FPGA publishes, so reject unsupported combinations
366
+ # instead of emitting an unusable prototype.
367
+ prototype_suffix = f"{io_direction}{io_output_readback}{io_zero_sync_regs}"
368
+ supported_suffixes = _get_supported_prototype_suffixes(data_type_name)
369
+ tokens_valid = (
370
+ io_direction in ("Input", "Output")
371
+ and io_output_readback in ("", "WithReadback", "WithoutReadback")
372
+ and io_zero_sync_regs in ("", "ZeroDefaultSyncRegisters")
315
373
  )
316
- io_resource.set("prototype", prototype)
317
-
318
- # Handle FXP attributes
319
- if data_type_name == "FXP" and "(" in data_type:
320
- try:
321
- parts = data_type.split("(")[1].split(")")[0].split(",")
322
- io_resource.set("wordLength", parts[0])
323
- io_resource.set("integerWordLength", parts[1])
324
- io_resource.set(
325
- "unsigned",
326
- "true" if "Unsigned" in data_type else "false",
327
- )
328
- except Exception as e:
329
- reporter.error(f"Error parsing FXP parameters for {lv_name}: {e}")
374
+
375
+ if tokens_valid and prototype_suffix not in supported_suffixes:
376
+ error = (
377
+ f"Row {row_count}: DataType='{data_type}' with "
378
+ f"Direction='{direction}', OutputReadback='{output_readback}', "
379
+ f"and ZeroSyncRegs='{zero_sync_regs}' for signal '{lv_name}' "
380
+ f"does not map to a supported LabVIEW FPGA prototype. The integer "
381
+ f"port types (U8-U64, I8-I64) have no WithoutReadback output with "
382
+ f"ZeroSyncRegs=TRUE; set OutputReadback=TRUE or ZeroSyncRegs=FALSE. "
383
+ f"See the DataType compatibility table in "
384
+ f"docs/LVTargetCustomIO-Reference.md for all valid combinations."
385
+ )
386
+ validation_errors.append(error)
387
+ io_resource.set("prototype", "UNSUPPORTED_PROTOTYPE_COMBINATION")
388
+ else:
389
+ prototype = DATA_TYPE_PROTOTYPES[data_type_name].format(
390
+ direction=io_direction,
391
+ zero_sync_regs=io_zero_sync_regs,
392
+ output_readback=io_output_readback,
393
+ )
394
+ io_resource.set("prototype", prototype)
395
+
396
+ # Handle FXP attributes
397
+ if data_type_name == "FXP" and "(" in data_type:
398
+ try:
399
+ parts = data_type.split("(")[1].split(")")[0].split(",")
400
+ io_resource.set("wordLength", parts[0])
401
+ io_resource.set("integerWordLength", parts[1])
402
+ io_resource.set(
403
+ "unsigned",
404
+ "true" if "Unsigned" in data_type else "false",
405
+ )
406
+ except Exception as e:
407
+ reporter.error(
408
+ f"Error parsing FXP parameters for {lv_name}: {e}"
409
+ )
330
410
  else:
331
411
  # Add validation error for invalid signal type
332
412
  error = f"Row {row_count}: Invalid signal type '{data_type}' for signal '{lv_name}'. Valid types: {', '.join(DATA_TYPE_PROTOTYPES.keys())}"
@@ -755,6 +835,10 @@ def gen_lv_target_support(config=None):
755
835
  fpga_files_folder,
756
836
  process_constraints.build_custom_constraints_content(config.custom_constraints),
757
837
  )
838
+ process_constraints.wrap_from_to_constraints_macro_in_folder(
839
+ fpga_files_folder,
840
+ config.entity_path_to_window_wrapper,
841
+ )
758
842
 
759
843
  _copy_menu_files(config.lv_target_plugin_output_folder, config.lv_target_menus_folder)
760
844
 
@@ -176,6 +176,60 @@ def replace_custom_constraints_in_xdc_folder(folder, custom_constraints_content)
176
176
  reporter.detail(f"Replaced macro_GitHubCustomConstraints in {filename}")
177
177
 
178
178
 
179
+ _FROM_TO_CONSTRAINTS_MACRO_RE = re.compile(
180
+ r"^([ \t]*)(#LabVIEWFPGA_Macro[ \t]+macro_fromToConstraints)[ \t]*$",
181
+ re.IGNORECASE | re.MULTILINE,
182
+ )
183
+
184
+
185
+ def wrap_from_to_constraints_macro_in_folder(folder, entity_path_to_window_wrapper):
186
+ """Wrap the from-to-constraints macro with current_instance scoping in constraint files.
187
+
188
+ In the LabVIEW FPGA target plugin flow, LabVIEW FPGA replaces
189
+ ``#LabVIEWFPGA_Macro macro_fromToConstraints`` with the generated window From/To timing
190
+ constraints when a VI is compiled against the custom target. Those constraints
191
+ reference cells relative to the window wrapper instance, so the macro must be wrapped
192
+ with ``current_instance`` scoping (mirroring the FROM_TO handling in
193
+ ``process_constraints_template``). Without it, instance-relative constraints fail to
194
+ match cells inside the window when the window VHDL is encrypted.
195
+
196
+ Scans every ``.xdc`` and ``.xdc_template`` file in *folder* and wraps the
197
+ from-to-constraints macro line, leaving the macro token itself intact for LabVIEW FPGA
198
+ to replace later.
199
+
200
+ Args:
201
+ folder (str): Directory containing constraint files to process.
202
+ entity_path_to_window_wrapper (str | None): Hierarchical path to the window wrapper
203
+ instance. If falsy, no wrapping is performed.
204
+ """
205
+ if not os.path.isdir(folder) or not entity_path_to_window_wrapper:
206
+ return
207
+
208
+ def _wrap(match):
209
+ indent = match.group(1)
210
+ token = match.group(2)
211
+ return (
212
+ f"{indent}set TopInstance0 [current_instance .]\n"
213
+ f"{indent}current_instance {entity_path_to_window_wrapper}\n"
214
+ f"{indent}{token}\n"
215
+ f"{indent}current_instance -quiet\n"
216
+ f"{indent}current_instance $TopInstance0"
217
+ )
218
+
219
+ for filename in os.listdir(folder):
220
+ lowered = filename.lower()
221
+ if not (lowered.endswith(".xdc") or lowered.endswith(".xdc_template")):
222
+ continue
223
+ filepath = os.path.join(folder, filename)
224
+ with open(filepath, "r", encoding="utf-8") as f:
225
+ content = f.read()
226
+ new_content, count = _FROM_TO_CONSTRAINTS_MACRO_RE.subn(_wrap, content)
227
+ if count > 0:
228
+ with open(filepath, "w", encoding="utf-8") as f:
229
+ f.write(new_content)
230
+ reporter.detail(f"Wrapped macro_fromToConstraints with current_instance in {filename}")
231
+
232
+
179
233
  def process_constraints_template(config):
180
234
  """Process XDC constraint template files.
181
235
 
@@ -4,7 +4,7 @@ build-backend = "poetry.core.masonry.api"
4
4
 
5
5
  [tool.poetry]
6
6
  name = "labview-fpga-hdl-tools"
7
- version = "2.0.0.dev0"
7
+ version = "2.0.0.dev1"
8
8
  description = "LabVIEW FPGA HDL Tools"
9
9
  authors = [
10
10
  "Salvador Santolucito <salvador.santolucito@gmail.com>"