labview-fpga-hdl-tools 2.0.0.dev3__tar.gz → 2.0.0.dev5__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 (29) hide show
  1. {labview_fpga_hdl_tools-2.0.0.dev3 → labview_fpga_hdl_tools-2.0.0.dev5}/PKG-INFO +29 -14
  2. {labview_fpga_hdl_tools-2.0.0.dev3 → labview_fpga_hdl_tools-2.0.0.dev5}/README.md +26 -11
  3. {labview_fpga_hdl_tools-2.0.0.dev3 → labview_fpga_hdl_tools-2.0.0.dev5}/labview_fpga_hdl_tools/__main__.py +3 -2
  4. {labview_fpga_hdl_tools-2.0.0.dev3 → labview_fpga_hdl_tools-2.0.0.dev5}/labview_fpga_hdl_tools/check_syntax.py +4 -3
  5. {labview_fpga_hdl_tools-2.0.0.dev3 → labview_fpga_hdl_tools-2.0.0.dev5}/labview_fpga_hdl_tools/common.py +2 -3
  6. {labview_fpga_hdl_tools-2.0.0.dev3 → labview_fpga_hdl_tools-2.0.0.dev5}/labview_fpga_hdl_tools/compile_modelsim_lib.py +5 -4
  7. {labview_fpga_hdl_tools-2.0.0.dev3 → labview_fpga_hdl_tools-2.0.0.dev5}/labview_fpga_hdl_tools/compile_project.py +4 -3
  8. {labview_fpga_hdl_tools-2.0.0.dev3 → labview_fpga_hdl_tools-2.0.0.dev5}/labview_fpga_hdl_tools/create_lvbitx.py +3 -3
  9. {labview_fpga_hdl_tools-2.0.0.dev3 → labview_fpga_hdl_tools-2.0.0.dev5}/labview_fpga_hdl_tools/create_modelsim_project.py +5 -4
  10. {labview_fpga_hdl_tools-2.0.0.dev3 → labview_fpga_hdl_tools-2.0.0.dev5}/labview_fpga_hdl_tools/create_vivado_project.py +4 -3
  11. {labview_fpga_hdl_tools-2.0.0.dev3 → labview_fpga_hdl_tools-2.0.0.dev5}/labview_fpga_hdl_tools/gen_labview_target_plugin.py +10 -7
  12. {labview_fpga_hdl_tools-2.0.0.dev3 → labview_fpga_hdl_tools-2.0.0.dev5}/labview_fpga_hdl_tools/generate_vhdl.py +5 -4
  13. {labview_fpga_hdl_tools-2.0.0.dev3 → labview_fpga_hdl_tools-2.0.0.dev5}/labview_fpga_hdl_tools/get_window_netlist.py +4 -3
  14. {labview_fpga_hdl_tools-2.0.0.dev3 → labview_fpga_hdl_tools-2.0.0.dev5}/labview_fpga_hdl_tools/install_dependencies.py +1 -1
  15. {labview_fpga_hdl_tools-2.0.0.dev3 → labview_fpga_hdl_tools-2.0.0.dev5}/labview_fpga_hdl_tools/install_labview_target_plugin.py +52 -13
  16. {labview_fpga_hdl_tools-2.0.0.dev3 → labview_fpga_hdl_tools-2.0.0.dev5}/labview_fpga_hdl_tools/launch_modelsim.py +5 -4
  17. {labview_fpga_hdl_tools-2.0.0.dev3 → labview_fpga_hdl_tools-2.0.0.dev5}/labview_fpga_hdl_tools/launch_vivado.py +4 -3
  18. {labview_fpga_hdl_tools-2.0.0.dev3 → labview_fpga_hdl_tools-2.0.0.dev5}/labview_fpga_hdl_tools/migrate_clip.py +8 -5
  19. {labview_fpga_hdl_tools-2.0.0.dev3 → labview_fpga_hdl_tools-2.0.0.dev5}/labview_fpga_hdl_tools/process_constraints.py +3 -3
  20. {labview_fpga_hdl_tools-2.0.0.dev3 → labview_fpga_hdl_tools-2.0.0.dev5}/labview_fpga_hdl_tools/sim_modelsim.py +5 -4
  21. labview_fpga_hdl_tools-2.0.0.dev5/pyproject.toml +75 -0
  22. labview_fpga_hdl_tools-2.0.0.dev3/pyproject.toml +0 -50
  23. {labview_fpga_hdl_tools-2.0.0.dev3 → labview_fpga_hdl_tools-2.0.0.dev5}/LICENSE +0 -0
  24. {labview_fpga_hdl_tools-2.0.0.dev3 → labview_fpga_hdl_tools-2.0.0.dev5}/labview_fpga_hdl_tools/.gitignore +0 -0
  25. {labview_fpga_hdl_tools-2.0.0.dev3 → labview_fpga_hdl_tools-2.0.0.dev5}/labview_fpga_hdl_tools/__init__.py +0 -0
  26. {labview_fpga_hdl_tools-2.0.0.dev3 → labview_fpga_hdl_tools-2.0.0.dev5}/labview_fpga_hdl_tools/command_config.py +0 -0
  27. {labview_fpga_hdl_tools-2.0.0.dev3 → labview_fpga_hdl_tools-2.0.0.dev5}/labview_fpga_hdl_tools/command_hooks.py +0 -0
  28. {labview_fpga_hdl_tools-2.0.0.dev3 → labview_fpga_hdl_tools-2.0.0.dev5}/labview_fpga_hdl_tools/nihdlsettings_default.py +0 -0
  29. {labview_fpga_hdl_tools-2.0.0.dev3 → labview_fpga_hdl_tools-2.0.0.dev5}/labview_fpga_hdl_tools/reporting.py +0 -0
@@ -1,19 +1,19 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: labview-fpga-hdl-tools
3
- Version: 2.0.0.dev3
3
+ Version: 2.0.0.dev5
4
4
  Summary: LabVIEW FPGA HDL Tools
5
5
  License: MIT
6
6
  Author: Salvador Santolucito
7
7
  Author-email: salvador.santolucito@gmail.com
8
- Requires-Python: >=3.9,<4.0
8
+ Requires-Python: >=3.10,<3.15
9
9
  Classifier: License :: OSI Approved :: MIT License
10
10
  Classifier: Programming Language :: Python :: 3
11
- Classifier: Programming Language :: Python :: 3.9
12
11
  Classifier: Programming Language :: Python :: 3.10
13
12
  Classifier: Programming Language :: Python :: 3.11
14
13
  Classifier: Programming Language :: Python :: 3.12
15
14
  Classifier: Programming Language :: Python :: 3.13
16
15
  Requires-Dist: click (>=8.1.8,<8.2)
16
+ Requires-Dist: defusedxml (>=0.7.1)
17
17
  Requires-Dist: mako (>=1.3.10)
18
18
  Requires-Dist: packaging (>=21.0)
19
19
  Requires-Dist: tomli (>=1.1.0) ; python_version < "3.11"
@@ -39,6 +39,12 @@ run Vivado in the end — they differ in what compiles the bitfile:
39
39
  target (`gen-target` → `install-target`), then write a VI and let LabVIEW FPGA
40
40
  compile the bitfile (it runs Vivado under the hood).
41
41
 
42
+ > **Host-side note:** to communicate with the compiled `.lvbitx` from a LabVIEW FPGA
43
+ > host VI, open it with **Open Dynamic Bitfile Reference** (wire in the bitfile path and
44
+ > a matching FPGA Interface Dynamic Refnum) — the standard **Open FPGA VI Reference**
45
+ > node does **not** work with these custom targets. See
46
+ > [Vivado Compile Flow → Opening the bitfile from a LabVIEW FPGA host VI](docs/VivadoCompileFlow.md#opening-the-bitfile-from-a-labview-fpga-host-vi).
47
+
42
48
  In short: in the **Vivado flow** you drive Vivado; in the **LabVIEW FPGA flow**
43
49
  LabVIEW FPGA drives Vivado for you. See
44
50
  [Theory of Operation](docs/TheoryOfOperation.md) for the full story.
@@ -48,9 +54,13 @@ LabVIEW FPGA drives Vivado for you. See
48
54
  | Doc | What's in it |
49
55
  | --- | --- |
50
56
  | [Theory of Operation](docs/TheoryOfOperation.md) | The architecture, the supported workflows, and how the pieces fit together. Start here. |
57
+ | [Vivado Compile Flow](docs/VivadoCompileFlow.md) | End-to-end walkthrough: extend the HDL, generate a Vivado project, and compile a `.lvbitx` directly in Vivado. |
58
+ | [LabVIEW FPGA Target and Compile Flow](docs/LabVIEWFpgaTargetFlow.md) | End-to-end walkthrough: build and install a custom LabVIEW FPGA target, then compile in LabVIEW FPGA. |
59
+ | [ModelSim Simulation Flow](docs/ModelSimSimulationFlow.md) | End-to-end walkthrough: build the Xilinx sim libraries, create the ModelSim project, and run a testbench. |
51
60
  | [Command Reference](docs/CommandReference.md) | Every `nihdl` command, its options, the command flow, and per-command required settings. |
52
61
  | [Settings Reference](docs/SettingsReference.md) | The `nihdlsettings.py` model: hooks, context, `--set` overrides, and the full list of setters. |
53
62
  | [LVTargetCustomIO Reference](docs/LVTargetCustomIO-Reference.md) | The custom I/O CSV format used to define HDL ↔ LabVIEW FPGA signals. |
63
+ | [Generated VHDL](docs/GeneratedVHDL.md) | What VHDL the tools generate (window wrappers, `PkgNiHdlSettings`) and why — single-sourcing the facts shared by the HDL and the LabVIEW FPGA target. |
54
64
  | [Window Netlist and Constraints](docs/WindowNetlistAndConstraints.md) | How the LabVIEW Window netlist is produced/consumed and how XDC constraints are processed for each compile flow (including the `current_instance` scoping rules). |
55
65
 
56
66
  ## Prerequisites
@@ -66,8 +76,8 @@ You need the external tools your flow uses:
66
76
  - **ModelSim** — only for simulation (`gen-modelsim`, `sim-modelsim`,
67
77
  `launch-modelsim`, `compile-modelsim-lib`). `compile-modelsim-lib` also needs
68
78
  Vivado to compile the Xilinx simulation libraries.
69
- - **Git** and **Python** — the tools support Python 3.9+; CI tests on 3.9 and
70
- 3.11 (3.11 is also used for static analysis).
79
+ - **Git** and **Python** — the tools support Python 3.10–3.14; CI tests on
80
+ 3.10 through 3.14 (3.11 is also used for static analysis).
71
81
 
72
82
  ### Installing the tools
73
83
 
@@ -142,6 +152,8 @@ nihdl compile-vivado
142
152
 
143
153
  Open the project interactively at any point with `nihdl launch-vivado`.
144
154
 
155
+ > **Full walkthrough:** [Vivado Compile Flow](docs/VivadoCompileFlow.md).
156
+
145
157
  ### The LabVIEW FPGA compile flow (custom target)
146
158
 
147
159
  To expose your HDL to LabVIEW FPGA as a custom target, define your I/O in the
@@ -151,23 +163,26 @@ To expose your HDL to LabVIEW FPGA as a custom target, define your I/O in the
151
163
  # Generate target support files (BoardIO/Clock XML, Window VHDL, plugin content)
152
164
  nihdl gen-target
153
165
 
154
- # Install the generated plugin into your LabVIEW FPGA install
166
+ # Close ALL LabVIEW instances, then install the generated plugin into your LabVIEW FPGA install
155
167
  nihdl install-target
156
168
  ```
157
169
 
170
+ > **Close LabVIEW around `install-target`.** Close all LabVIEW instances before running it, and
171
+ > (re)start LabVIEW afterward — LabVIEW only scans for target plugins at startup.
172
+
173
+ > **Full walkthrough:** [LabVIEW FPGA Target and Compile Flow](docs/LabVIEWFpgaTargetFlow.md).
174
+
158
175
  ### Simulating with ModelSim
159
176
 
160
177
  ```bash
161
- # Create the ModelSim project and compile all VHDL (vcom -autoorder -2008)
162
- # When XilinxSimLibFolder is configured, this also compiles the Xilinx
163
- # simulation libraries (unisim, secureip, ...) on the first run, which can
164
- # take several minutes. Run it standalone with: nihdl compile-modelsim-lib
165
- nihdl gen-modelsim
166
-
167
- # Launch the GUI, or run headless with --batch
168
- nihdl launch-modelsim
178
+ # Create the ModelSim project and compile all VHDL, then run the testbench
179
+ nihdl gen-modelsim --overwrite
180
+ nihdl sim-modelsim # or: nihdl launch-modelsim (GUI; --batch for headless)
169
181
  ```
170
182
 
183
+ > **Full walkthrough:** [ModelSim Simulation Flow](docs/ModelSimSimulationFlow.md)
184
+ > — including the Xilinx simulation libraries and how the pass/fail verdict works.
185
+
171
186
  For the complete command list, options, and required settings, see the
172
187
  [Command Reference](docs/CommandReference.md).
173
188
 
@@ -18,6 +18,12 @@ run Vivado in the end — they differ in what compiles the bitfile:
18
18
  target (`gen-target` → `install-target`), then write a VI and let LabVIEW FPGA
19
19
  compile the bitfile (it runs Vivado under the hood).
20
20
 
21
+ > **Host-side note:** to communicate with the compiled `.lvbitx` from a LabVIEW FPGA
22
+ > host VI, open it with **Open Dynamic Bitfile Reference** (wire in the bitfile path and
23
+ > a matching FPGA Interface Dynamic Refnum) — the standard **Open FPGA VI Reference**
24
+ > node does **not** work with these custom targets. See
25
+ > [Vivado Compile Flow → Opening the bitfile from a LabVIEW FPGA host VI](docs/VivadoCompileFlow.md#opening-the-bitfile-from-a-labview-fpga-host-vi).
26
+
21
27
  In short: in the **Vivado flow** you drive Vivado; in the **LabVIEW FPGA flow**
22
28
  LabVIEW FPGA drives Vivado for you. See
23
29
  [Theory of Operation](docs/TheoryOfOperation.md) for the full story.
@@ -27,9 +33,13 @@ LabVIEW FPGA drives Vivado for you. See
27
33
  | Doc | What's in it |
28
34
  | --- | --- |
29
35
  | [Theory of Operation](docs/TheoryOfOperation.md) | The architecture, the supported workflows, and how the pieces fit together. Start here. |
36
+ | [Vivado Compile Flow](docs/VivadoCompileFlow.md) | End-to-end walkthrough: extend the HDL, generate a Vivado project, and compile a `.lvbitx` directly in Vivado. |
37
+ | [LabVIEW FPGA Target and Compile Flow](docs/LabVIEWFpgaTargetFlow.md) | End-to-end walkthrough: build and install a custom LabVIEW FPGA target, then compile in LabVIEW FPGA. |
38
+ | [ModelSim Simulation Flow](docs/ModelSimSimulationFlow.md) | End-to-end walkthrough: build the Xilinx sim libraries, create the ModelSim project, and run a testbench. |
30
39
  | [Command Reference](docs/CommandReference.md) | Every `nihdl` command, its options, the command flow, and per-command required settings. |
31
40
  | [Settings Reference](docs/SettingsReference.md) | The `nihdlsettings.py` model: hooks, context, `--set` overrides, and the full list of setters. |
32
41
  | [LVTargetCustomIO Reference](docs/LVTargetCustomIO-Reference.md) | The custom I/O CSV format used to define HDL ↔ LabVIEW FPGA signals. |
42
+ | [Generated VHDL](docs/GeneratedVHDL.md) | What VHDL the tools generate (window wrappers, `PkgNiHdlSettings`) and why — single-sourcing the facts shared by the HDL and the LabVIEW FPGA target. |
33
43
  | [Window Netlist and Constraints](docs/WindowNetlistAndConstraints.md) | How the LabVIEW Window netlist is produced/consumed and how XDC constraints are processed for each compile flow (including the `current_instance` scoping rules). |
34
44
 
35
45
  ## Prerequisites
@@ -45,8 +55,8 @@ You need the external tools your flow uses:
45
55
  - **ModelSim** — only for simulation (`gen-modelsim`, `sim-modelsim`,
46
56
  `launch-modelsim`, `compile-modelsim-lib`). `compile-modelsim-lib` also needs
47
57
  Vivado to compile the Xilinx simulation libraries.
48
- - **Git** and **Python** — the tools support Python 3.9+; CI tests on 3.9 and
49
- 3.11 (3.11 is also used for static analysis).
58
+ - **Git** and **Python** — the tools support Python 3.10–3.14; CI tests on
59
+ 3.10 through 3.14 (3.11 is also used for static analysis).
50
60
 
51
61
  ### Installing the tools
52
62
 
@@ -121,6 +131,8 @@ nihdl compile-vivado
121
131
 
122
132
  Open the project interactively at any point with `nihdl launch-vivado`.
123
133
 
134
+ > **Full walkthrough:** [Vivado Compile Flow](docs/VivadoCompileFlow.md).
135
+
124
136
  ### The LabVIEW FPGA compile flow (custom target)
125
137
 
126
138
  To expose your HDL to LabVIEW FPGA as a custom target, define your I/O in the
@@ -130,23 +142,26 @@ To expose your HDL to LabVIEW FPGA as a custom target, define your I/O in the
130
142
  # Generate target support files (BoardIO/Clock XML, Window VHDL, plugin content)
131
143
  nihdl gen-target
132
144
 
133
- # Install the generated plugin into your LabVIEW FPGA install
145
+ # Close ALL LabVIEW instances, then install the generated plugin into your LabVIEW FPGA install
134
146
  nihdl install-target
135
147
  ```
136
148
 
149
+ > **Close LabVIEW around `install-target`.** Close all LabVIEW instances before running it, and
150
+ > (re)start LabVIEW afterward — LabVIEW only scans for target plugins at startup.
151
+
152
+ > **Full walkthrough:** [LabVIEW FPGA Target and Compile Flow](docs/LabVIEWFpgaTargetFlow.md).
153
+
137
154
  ### Simulating with ModelSim
138
155
 
139
156
  ```bash
140
- # Create the ModelSim project and compile all VHDL (vcom -autoorder -2008)
141
- # When XilinxSimLibFolder is configured, this also compiles the Xilinx
142
- # simulation libraries (unisim, secureip, ...) on the first run, which can
143
- # take several minutes. Run it standalone with: nihdl compile-modelsim-lib
144
- nihdl gen-modelsim
145
-
146
- # Launch the GUI, or run headless with --batch
147
- nihdl launch-modelsim
157
+ # Create the ModelSim project and compile all VHDL, then run the testbench
158
+ nihdl gen-modelsim --overwrite
159
+ nihdl sim-modelsim # or: nihdl launch-modelsim (GUI; --batch for headless)
148
160
  ```
149
161
 
162
+ > **Full walkthrough:** [ModelSim Simulation Flow](docs/ModelSimSimulationFlow.md)
163
+ > — including the Xilinx simulation libraries and how the pass/fail verdict works.
164
+
150
165
  For the complete command list, options, and required settings, see the
151
166
  [Command Reference](docs/CommandReference.md).
152
167
 
@@ -1,5 +1,6 @@
1
1
  #!/usr/bin/env python3
2
2
  """LVFPGAHDLTools - Command-line interface for LabVIEW FPGA HDL Tools."""
3
+
3
4
  # Copyright (c) 2025 National Instruments Corporation
4
5
  #
5
6
  # SPDX-License-Identifier: MIT
@@ -11,7 +12,7 @@ import traceback
11
12
  import click
12
13
 
13
14
  # Import main functions from all the tool modules
14
- from . import (
15
+ from labview_fpga_hdl_tools import (
15
16
  __version__,
16
17
  check_syntax,
17
18
  command_hooks,
@@ -32,7 +33,7 @@ from . import (
32
33
  process_constraints,
33
34
  sim_modelsim,
34
35
  )
35
- from .reporting import reporter
36
+ from labview_fpga_hdl_tools.reporting import reporter
36
37
 
37
38
 
38
39
  class SectionGroup(click.Group):
@@ -8,8 +8,9 @@
8
8
  import os
9
9
  import subprocess
10
10
 
11
- from . import common
12
- from .reporting import reporter
11
+ from labview_fpga_hdl_tools import common
12
+ from labview_fpga_hdl_tools.command_config import CommandConfiguration
13
+ from labview_fpga_hdl_tools.reporting import reporter
13
14
 
14
15
 
15
16
  def _generate_check_syntax_tcl(config, output_path):
@@ -178,7 +179,7 @@ def check_syntax(config=None):
178
179
  int: 0 for success, 1 for error
179
180
  """
180
181
  if config is None:
181
- config = common.CommandConfiguration()
182
+ config = CommandConfiguration()
182
183
 
183
184
  try:
184
185
  _validate_ini(config)
@@ -14,8 +14,7 @@ from typing import Optional
14
14
 
15
15
  from mako.template import Template
16
16
 
17
- from .command_config import CommandConfiguration, resolve_path # noqa: F401
18
- from .reporting import reporter
17
+ from labview_fpga_hdl_tools.reporting import reporter
19
18
 
20
19
 
21
20
  def handle_long_path(path):
@@ -599,7 +598,7 @@ def render_mako_template(template_path, output_path, **kwargs):
599
598
  output_path (str): Path where the rendered output will be written
600
599
  **kwargs: Template variables to substitute
601
600
  """
602
- template = Template(filename=template_path, input_encoding="utf-8")
601
+ template = Template(filename=template_path, input_encoding="utf-8") # nosec B702
603
602
  rendered = str(template.render(**kwargs))
604
603
 
605
604
  # Mako preserves \r\n from template files on Windows. Normalize to \n
@@ -21,9 +21,10 @@ import os
21
21
  import subprocess
22
22
  import sys
23
23
 
24
- from . import common
25
- from .create_modelsim_project import _find_modelsim_executable
26
- from .reporting import reporter
24
+ from labview_fpga_hdl_tools import common
25
+ from labview_fpga_hdl_tools.command_config import CommandConfiguration
26
+ from labview_fpga_hdl_tools.create_modelsim_project import _find_modelsim_executable
27
+ from labview_fpga_hdl_tools.reporting import reporter
27
28
 
28
29
  # Libraries built when nihdlsettings.py does not specify an explicit scope.
29
30
  _DEFAULT_XILINX_SIM_LIBRARIES = ["unisim", "secureip"]
@@ -250,7 +251,7 @@ def compile_modelsim_lib(config=None, force=False):
250
251
  int: 0 for success, 1 for error.
251
252
  """
252
253
  if config is None:
253
- config = common.CommandConfiguration()
254
+ config = CommandConfiguration()
254
255
 
255
256
  out_folder = config.xilinx_sim_lib_folder
256
257
  if not out_folder:
@@ -8,8 +8,9 @@
8
8
  import os
9
9
  import subprocess
10
10
 
11
- from . import common
12
- from .reporting import reporter
11
+ from labview_fpga_hdl_tools import common
12
+ from labview_fpga_hdl_tools.command_config import CommandConfiguration
13
+ from labview_fpga_hdl_tools.reporting import reporter
13
14
 
14
15
 
15
16
  def _generate_compile_project_tcl(config, output_path):
@@ -187,7 +188,7 @@ def compile_project(config=None):
187
188
  int: 0 for success, 1 for error
188
189
  """
189
190
  if config is None:
190
- config = common.CommandConfiguration()
191
+ config = CommandConfiguration()
191
192
 
192
193
  try:
193
194
  _validate_ini(config)
@@ -7,8 +7,8 @@
7
7
  import os # For file and directory operations
8
8
  import subprocess # For executing external programs
9
9
 
10
- from . import common # For shared utilities across tools
11
- from .reporting import reporter
10
+ from labview_fpga_hdl_tools.command_config import CommandConfiguration
11
+ from labview_fpga_hdl_tools.reporting import reporter
12
12
 
13
13
  # createBitfile.exe lives at this path relative to a LabVIEW install root.
14
14
  _CREATEBITFILE_RELPATH = os.path.join("vi.lib", "rvi", "CDR", "createBitfile.exe")
@@ -114,7 +114,7 @@ def _create_lv_bitfile(config=None):
114
114
 
115
115
  # Load configuration
116
116
  if config is None:
117
- config = common.CommandConfiguration()
117
+ config = CommandConfiguration()
118
118
 
119
119
  # Determine path to CodeGenerationResults.lvtxt from TheWindow folder
120
120
  if not config.lv_window_netlist_folder:
@@ -11,8 +11,9 @@ import shutil
11
11
  import stat
12
12
  import subprocess
13
13
 
14
- from . import common, generate_vhdl
15
- from .reporting import reporter
14
+ from labview_fpga_hdl_tools import common, generate_vhdl
15
+ from labview_fpga_hdl_tools.command_config import CommandConfiguration
16
+ from labview_fpga_hdl_tools.reporting import reporter
16
17
 
17
18
 
18
19
  def _validate_ini(config):
@@ -426,7 +427,7 @@ def create_modelsim_project(overwrite=False, config=None):
426
427
  - .do files for GUI and batch simulation
427
428
  """
428
429
  if config is None:
429
- config = common.CommandConfiguration()
430
+ config = CommandConfiguration()
430
431
 
431
432
  try:
432
433
  _validate_ini(config)
@@ -499,7 +500,7 @@ def create_modelsim_project(overwrite=False, config=None):
499
500
  # This is idempotent: once compiled it is a cheap no-op (and does not
500
501
  # even require Vivado). Imported locally to avoid a circular import,
501
502
  # since compile_modelsim_lib imports helpers from this module.
502
- from . import compile_modelsim_lib
503
+ from labview_fpga_hdl_tools import compile_modelsim_lib
503
504
 
504
505
  if compile_modelsim_lib.compile_modelsim_lib(config=config) != 0:
505
506
  return 1
@@ -23,8 +23,9 @@ import subprocess
23
23
  from collections import defaultdict
24
24
  from enum import Enum
25
25
 
26
- from . import common, generate_vhdl, process_constraints
27
- from .reporting import reporter
26
+ from labview_fpga_hdl_tools import common, generate_vhdl, process_constraints
27
+ from labview_fpga_hdl_tools.command_config import CommandConfiguration
28
+ from labview_fpga_hdl_tools.reporting import reporter
28
29
 
29
30
  # Windows MAX_PATH. When the generated TCL adds a source file, Vivado resolves the
30
31
  # file's add_files path (which is relative to the TCL folder) against the Vivado
@@ -810,7 +811,7 @@ def create_project(overwrite=False, update=False, config=None):
810
811
  """
811
812
  # Load configuration with optional custom config path
812
813
  if config is None:
813
- config = common.CommandConfiguration()
814
+ config = CommandConfiguration()
814
815
 
815
816
  result = _run_create_project_steps(overwrite, update, config)
816
817
 
@@ -22,14 +22,17 @@ import re
22
22
  import shutil # For file copying operations
23
23
  import stat
24
24
  import xml.etree.ElementTree as ET # For XML generation and manipulation # noqa: N817
25
- from xml.dom.minidom import parseString # For pretty-formatted XML output
26
25
 
26
+ from defusedxml.minidom import parseString # For safe pretty-formatted XML output
27
27
  from mako.template import Template # For template-based file generation # type: ignore
28
28
 
29
- from . import common # For shared utilities across tools
30
- from . import generate_vhdl # For generated VHDL file generation
31
- from . import process_constraints # For shared constraint macro replacement
32
- from .reporting import reporter
29
+ from labview_fpga_hdl_tools import common # For shared utilities across tools
30
+ from labview_fpga_hdl_tools import generate_vhdl # For generated VHDL file generation
31
+ from labview_fpga_hdl_tools import (
32
+ process_constraints, # For shared constraint macro replacement
33
+ )
34
+ from labview_fpga_hdl_tools.command_config import CommandConfiguration
35
+ from labview_fpga_hdl_tools.reporting import reporter
33
36
 
34
37
  # Constants
35
38
  BOARDIO_WRAPPER_NAME = "BoardIO" # Top-level wrapper name in the BoardIO XML hierarchy
@@ -507,7 +510,7 @@ def _generate_target_xml(
507
510
  # Render template
508
511
  try:
509
512
  with open(template_path, "r", encoding="utf-8") as f:
510
- template = Template(f.read())
513
+ template = Template(f.read()) # nosec B702
511
514
 
512
515
  render_kwargs = {
513
516
  "include_board_io": include_board_io,
@@ -750,7 +753,7 @@ def gen_lv_target_support(config=None):
750
753
  """Generate target support files."""
751
754
  # Load configuration
752
755
  if config is None:
753
- config = common.CommandConfiguration()
756
+ config = CommandConfiguration()
754
757
  has_validation_errors = False
755
758
  validation_errors = []
756
759
  register_space_warnings = []
@@ -23,8 +23,9 @@ import os # For file and directory operations
23
23
 
24
24
  from mako.template import Template # For template-based file generation # type: ignore
25
25
 
26
- from . import common # For shared utilities across tools
27
- from .reporting import reporter
26
+ from labview_fpga_hdl_tools import common # For shared utilities across tools
27
+ from labview_fpga_hdl_tools.command_config import CommandConfiguration
28
+ from labview_fpga_hdl_tools.reporting import reporter
28
29
 
29
30
 
30
31
  def _ensure_text(value):
@@ -241,7 +242,7 @@ def _render_generated_vhdl(template_paths, output_folder, context):
241
242
  reporter.detail(f"Processing template: {template_path} -> {output_path}")
242
243
 
243
244
  with open(template_path, "r", encoding="utf-8") as f:
244
- template = Template(f.read())
245
+ template = Template(f.read()) # nosec B702
245
246
 
246
247
  output_text = template.render(**context)
247
248
 
@@ -303,7 +304,7 @@ def gen_generated_vhdl(config=None):
303
304
  """
304
305
  # Load configuration
305
306
  if config is None:
306
- config = common.CommandConfiguration()
307
+ config = CommandConfiguration()
307
308
 
308
309
  if not config.generated_vhdl_templates:
309
310
  # Nothing to generate; not an error.
@@ -10,8 +10,9 @@ import os
10
10
  import shutil
11
11
  import subprocess
12
12
 
13
- from . import common
14
- from .reporting import reporter
13
+ from labview_fpga_hdl_tools import common
14
+ from labview_fpga_hdl_tools.command_config import CommandConfiguration
15
+ from labview_fpga_hdl_tools.reporting import reporter
15
16
 
16
17
  # Generated flat wrapper file produced by the GetWindowNetlist TCL script.
17
18
  _WINDOW_FLAT_WRAPPER_FILE = "TheLvWindowFlatWrapper.v"
@@ -305,7 +306,7 @@ def get_window(config=None):
305
306
  """Main entry point for the script."""
306
307
  # Load configuration
307
308
  if config is None:
308
- config = common.CommandConfiguration()
309
+ config = CommandConfiguration()
309
310
 
310
311
  # Validate that all required settings are present
311
312
  try:
@@ -52,7 +52,7 @@ except ModuleNotFoundError:
52
52
  from packaging.specifiers import SpecifierSet
53
53
  from packaging.version import InvalidVersion, Version
54
54
 
55
- from .reporting import reporter
55
+ from labview_fpga_hdl_tools.reporting import reporter
56
56
 
57
57
  # Suffix for the per-dependency version marker files written next to each
58
58
  # cloned repo. Mirrors the discoverability of Python's "<pkg>-<ver>.dist-info"
@@ -8,8 +8,9 @@ import os # For file and directory operations
8
8
  import shutil # For file copying and directory removal
9
9
  import sys # For command-line arguments and error handling
10
10
 
11
- from . import common # For shared utilities across tools
12
- from .reporting import reporter
11
+ from labview_fpga_hdl_tools import common # For shared utilities across tools
12
+ from labview_fpga_hdl_tools.command_config import CommandConfiguration
13
+ from labview_fpga_hdl_tools.reporting import reporter
13
14
 
14
15
 
15
16
  def _is_admin():
@@ -26,8 +27,30 @@ def _is_admin():
26
27
 
27
28
 
28
29
  def _run_as_admin():
29
- """Re-launch the command with administrator privileges."""
30
+ """Re-launch the command with administrator privileges.
31
+
32
+ Security notes:
33
+ - Elevation spawns a *separate* short-lived process via UAC; the elevated
34
+ privileges live and die with that child process. The elevated instance
35
+ performs the install and then exits, so admin rights are never retained
36
+ by any process after installation completes.
37
+ - Re-launch through the current Python interpreter (``sys.executable``, an
38
+ absolute, trusted path) running the tool as a module (``-m
39
+ labview_fpga_hdl_tools``). We deliberately do NOT re-invoke ``sys.argv[0]``
40
+ (the console-script launcher, e.g. nihdl.exe): those launcher stubs are
41
+ resolved for the *current* user's environment (venv / pyenv shims) and do
42
+ not reliably re-bootstrap their interpreter under the elevated (admin)
43
+ account, so the elevated child would exit without doing any work. Passing
44
+ a bare program name (e.g. "nihdl") is likewise avoided because Windows
45
+ would resolve it against the working directory / PATH in the elevated
46
+ context, allowing an attacker-planted binary to run as admin
47
+ (CWE-426/CWE-427, untrusted search path).
48
+ - The elevated child is launched with its working directory pinned to the
49
+ current working directory so it discovers the same ``nihdlsettings.py``
50
+ the user invoked against (config is resolved relative to the cwd).
51
+ """
30
52
  import ctypes
53
+ import subprocess
31
54
  import sys
32
55
 
33
56
  # Skip on non-Windows platforms
@@ -35,19 +58,21 @@ def _run_as_admin():
35
58
  reporter.detail("Admin elevation only supported on Windows")
36
59
  return
37
60
 
38
- # When running via pip-installed entry point
39
- if sys.argv[0].endswith("nihdl") or sys.argv[0].endswith("nihdl.exe"):
40
- command = "nihdl"
41
- arguments = " ".join(sys.argv[1:])
42
- else:
43
- command = sys.executable
44
- arguments = f'"{sys.argv[0]}" {" ".join(sys.argv[1:])}'
61
+ # Re-launch via the real interpreter running the package as a module. This
62
+ # is both trusted (absolute interpreter path, module named explicitly) and
63
+ # robust across launchers (venv exe, pyenv shim, editable install).
64
+ command = os.path.abspath(sys.executable)
65
+ arguments = subprocess.list2cmdline(["-m", "labview_fpga_hdl_tools", *sys.argv[1:]])
66
+
67
+ # Pin the elevated child's working directory to the current directory so it
68
+ # finds the same nihdlsettings.py the user invoked against.
69
+ working_dir = os.getcwd()
45
70
 
46
71
  reporter.detail("Requesting administrator privileges...")
47
72
 
48
73
  # Execute with elevation
49
74
  result = ctypes.windll.shell32.ShellExecuteW( # type: ignore
50
- None, "runas", command, arguments, None, 1
75
+ None, "runas", command, arguments, working_dir, 1
51
76
  )
52
77
 
53
78
  # Check if the elevation was successful
@@ -55,7 +80,8 @@ def _run_as_admin():
55
80
  reporter.error(f"Error elevating privileges. Error code: {result}")
56
81
  sys.exit(1)
57
82
 
58
- # The original process should exit after launching the elevated one
83
+ # The original (non-elevated) process exits after launching the elevated
84
+ # one, so no admin privileges are held by a lingering process.
59
85
  reporter.detail("Elevated process launched. This process will now exit.")
60
86
  sys.exit(0)
61
87
 
@@ -121,7 +147,7 @@ def install_lv_target_support(config=None):
121
147
  """
122
148
  # Load configuration
123
149
  if config is None:
124
- config = common.CommandConfiguration()
150
+ config = CommandConfiguration()
125
151
 
126
152
  # Validate that all required settings are present
127
153
  try:
@@ -141,6 +167,14 @@ def install_lv_target_support(config=None):
141
167
 
142
168
  # If we need admin and don't have it, relaunch with elevated privileges
143
169
  if needs_admin and not _is_admin():
170
+ # Always-visible notice: the actual install runs in a separate elevated
171
+ # process (a UAC prompt will appear). Without this, the command appears
172
+ # to do nothing because the elevated child owns the success output.
173
+ reporter.success(
174
+ f"Installing '{config.lv_target_name}' to '{install_folder}' requires "
175
+ "administrator rights. Approve the Windows UAC prompt; the install "
176
+ "completes in a separate elevated window."
177
+ )
144
178
  _run_as_admin()
145
179
  return # Exit current instance as the elevated instance will continue
146
180
 
@@ -181,6 +215,11 @@ def install_lv_target_support(config=None):
181
215
  reporter.success(
182
216
  f"Successfully installed LabVIEW Target '{config.lv_target_name}' to {install_folder}"
183
217
  )
218
+ reporter.success(
219
+ "Reminder: close ALL open LabVIEW instances before installing, and (re)start "
220
+ "LabVIEW afterward. LabVIEW FPGA only scans for target plugins at startup, so a "
221
+ "running instance will not see this newly installed or updated target."
222
+ )
184
223
 
185
224
  except PermissionError:
186
225
  reporter.error("Error: Permission denied. Administrator privileges are required.")
@@ -9,9 +9,10 @@ import os
9
9
  import platform
10
10
  import subprocess
11
11
 
12
- from . import common
13
- from .create_modelsim_project import _get_vsim_executable
14
- from .reporting import reporter
12
+ from labview_fpga_hdl_tools import common
13
+ from labview_fpga_hdl_tools.command_config import CommandConfiguration
14
+ from labview_fpga_hdl_tools.create_modelsim_project import _get_vsim_executable
15
+ from labview_fpga_hdl_tools.reporting import reporter
15
16
 
16
17
 
17
18
  def _validate_ini(config):
@@ -38,7 +39,7 @@ def _validate_ini(config):
38
39
  def launch_modelsim(batch=False, config=None):
39
40
  """Launch ModelSim using settings from nihdlsettings.py."""
40
41
  if config is None:
41
- config = common.CommandConfiguration()
42
+ config = CommandConfiguration()
42
43
 
43
44
  try:
44
45
  _validate_ini(config)
@@ -9,8 +9,9 @@ import os
9
9
  import platform
10
10
  import subprocess
11
11
 
12
- from . import common
13
- from .reporting import reporter
12
+ from labview_fpga_hdl_tools import common
13
+ from labview_fpga_hdl_tools.command_config import CommandConfiguration
14
+ from labview_fpga_hdl_tools.reporting import reporter
14
15
 
15
16
 
16
17
  def _validate_ini(config):
@@ -50,7 +51,7 @@ def launch_vivado(config=None):
50
51
  """Launch Vivado using settings from nihdlsettings.py."""
51
52
  # Load configuration from nihdlsettings.py
52
53
  if config is None:
53
- config = common.CommandConfiguration()
54
+ config = CommandConfiguration()
54
55
 
55
56
  # Validate that all required settings are present and paths exist
56
57
  try:
@@ -21,8 +21,11 @@ import sys
21
21
  import traceback
22
22
  import xml.etree.ElementTree as ET # noqa: N817
23
23
 
24
- from . import common
25
- from .reporting import reporter
24
+ import defusedxml.ElementTree as DefusedET
25
+
26
+ from labview_fpga_hdl_tools import common
27
+ from labview_fpga_hdl_tools.command_config import CommandConfiguration
28
+ from labview_fpga_hdl_tools.reporting import reporter
26
29
 
27
30
  INVALID_LV_DATA_TYPE = "INVALID_LV_DATA_TYPE"
28
31
 
@@ -181,7 +184,7 @@ def _generate_board_io_csv_from_clip_xml(input_xml_path, clip_output_csv):
181
184
 
182
185
  # Parse XML
183
186
  try:
184
- tree = ET.parse(input_xml_path)
187
+ tree = DefusedET.parse(input_xml_path)
185
188
  root = tree.getroot()
186
189
  except ET.ParseError as e:
187
190
  sys.exit(f"Error parsing XML file: {e}")
@@ -369,7 +372,7 @@ def _generate_clip_to_window_signals(input_xml_path, output_vhdl_path):
369
372
 
370
373
  # Parse XML
371
374
  try:
372
- tree = ET.parse(input_xml_path)
375
+ tree = DefusedET.parse(input_xml_path)
373
376
  root = tree.getroot()
374
377
  except ET.ParseError as e:
375
378
  reporter.error(f"Error parsing XML file: {e}")
@@ -637,7 +640,7 @@ def migrate_clip(config=None):
637
640
  """Main program entry point."""
638
641
  # Load configuration
639
642
  if config is None:
640
- config = common.CommandConfiguration()
643
+ config = CommandConfiguration()
641
644
  validation_errors = []
642
645
 
643
646
  # Validate that all required settings are present
@@ -13,8 +13,8 @@ into user-provided template files, replacing macro tokens.
13
13
  import os
14
14
  import re
15
15
 
16
- from . import common
17
- from .reporting import reporter
16
+ from labview_fpga_hdl_tools.command_config import CommandConfiguration
17
+ from labview_fpga_hdl_tools.reporting import reporter
18
18
 
19
19
  # ---------------------------------------------------------------------------
20
20
  # TG/TNM constraint conversion helpers
@@ -435,6 +435,6 @@ def process_constraints(config=None):
435
435
  int: 0 on success.
436
436
  """
437
437
  if config is None:
438
- config = common.CommandConfiguration()
438
+ config = CommandConfiguration()
439
439
  process_constraints_template(config)
440
440
  return 0
@@ -10,9 +10,10 @@ import subprocess
10
10
  import sys
11
11
  import time
12
12
 
13
- from . import common
14
- from .create_modelsim_project import _get_vsim_executable
15
- from .reporting import reporter
13
+ from labview_fpga_hdl_tools import common
14
+ from labview_fpga_hdl_tools.command_config import CommandConfiguration
15
+ from labview_fpga_hdl_tools.create_modelsim_project import _get_vsim_executable
16
+ from labview_fpga_hdl_tools.reporting import reporter
16
17
 
17
18
 
18
19
  def _validate_ini(config):
@@ -50,7 +51,7 @@ def sim_modelsim(do_file=None, config=None):
50
51
  int: 0 on success, non-zero on failure.
51
52
  """
52
53
  if config is None:
53
- config = common.CommandConfiguration()
54
+ config = CommandConfiguration()
54
55
 
55
56
  try:
56
57
  _validate_ini(config)
@@ -0,0 +1,75 @@
1
+ [build-system]
2
+ requires = ["poetry-core>=1.8"]
3
+ build-backend = "poetry.core.masonry.api"
4
+
5
+ [tool.poetry]
6
+ name = "labview-fpga-hdl-tools"
7
+ version = "2.0.0.dev5"
8
+ description = "LabVIEW FPGA HDL Tools"
9
+ authors = [
10
+ "Salvador Santolucito <salvador.santolucito@gmail.com>"
11
+ ]
12
+ readme = "README.md"
13
+ license = "MIT"
14
+ include = [
15
+ { path = "labview_fpga_hdl_tools/host_interfaces/**", format = "sdist" }
16
+ ]
17
+
18
+ [tool.poetry.dependencies]
19
+ # Supports the Python versions in their maintenance window (3.10-3.14).
20
+ python = ">=3.10,<3.15"
21
+ mako = ">=1.3.10"
22
+ click = ">=8.1.8,<8.2"
23
+ packaging = ">=21.0"
24
+ # Hardens XML parsing against XXE / entity-expansion attacks (used instead of xml.etree/minidom).
25
+ defusedxml = ">=0.7.1"
26
+ tomli = {version = ">=1.1.0", python = "<3.11"}
27
+
28
+ [tool.poetry.group.test.dependencies]
29
+ # >=9.0.3 fixes insecure /tmp/pytest-of-{user} tmpdir handling (CVE); requires Python >=3.10.
30
+ pytest = ">=9.0.3"
31
+
32
+ [tool.poetry.group.lint.dependencies]
33
+ bandit = { version = ">=1.7", extras = ["toml"] }
34
+ # 0.5.x bundles flake8-tidy-imports (bans relative imports) and a pkg_resources-free
35
+ # flake8-import-order (>=0.19.2), which is required for setuptools >= 81.
36
+ ni-python-styleguide = ">=0.5.1"
37
+ # Pinned directly (transitive via ni-python-styleguide) so security updates can be applied.
38
+ # Capped < 26 because ni-python-styleguide 0.5.x requires black < 26.0; 25.12.0 needs Python >= 3.10.
39
+ black = ">=25.12.0,<26"
40
+ # flake8-black < 0.4.0 crashes (BLK900) on black >= 25.9.0 due to a decode_bytes API change.
41
+ flake8-black = ">=0.4.0"
42
+ # Transitive via bandit/pbr and ni-python-styleguide. Bumped to the latest to clear the
43
+ # setuptools sdist Unicode-normalization advisory (fixed in 83.0.0); 83 dropped pkg_resources,
44
+ # which is why ni-python-styleguide 0.5.x (pkg_resources-free import-order) is required.
45
+ setuptools = ">=83.0.0"
46
+ mypy = ">=1.0"
47
+ pyright = { version = ">=1.1.400", extras = ["nodejs"] }
48
+
49
+ [tool.poetry.scripts]
50
+ nihdl = "labview_fpga_hdl_tools.__main__:main"
51
+
52
+ [tool.mypy]
53
+ packages = ["labview_fpga_hdl_tools"]
54
+ python_version = "3.10"
55
+ warn_return_any = true
56
+ warn_unused_configs = true
57
+ disallow_untyped_defs = false
58
+ disallow_incomplete_defs = false
59
+
60
+ [[tool.mypy.overrides]]
61
+ module = "mako.*"
62
+ ignore_missing_imports = true
63
+
64
+ [[tool.mypy.overrides]]
65
+ module = "defusedxml.*"
66
+ ignore_missing_imports = true
67
+
68
+ [tool.bandit]
69
+ exclude_dirs = ["tests"]
70
+ # Reviewed low-severity informational tests, safe by construction:
71
+ # B404/B603/B607 - subprocess is invoked with argument lists (never shell=True), so there is
72
+ # no shell-injection surface; executable paths are resolved from trusted configuration.
73
+ # B405 - xml.etree is used only for XML generation / ParseError handling; all XML parsing
74
+ # uses defusedxml (see the B314/B318 remediations).
75
+ skips = ["B404", "B603", "B607", "B405"]
@@ -1,50 +0,0 @@
1
- [build-system]
2
- requires = ["poetry-core>=1.8"]
3
- build-backend = "poetry.core.masonry.api"
4
-
5
- [tool.poetry]
6
- name = "labview-fpga-hdl-tools"
7
- version = "2.0.0.dev3"
8
- description = "LabVIEW FPGA HDL Tools"
9
- authors = [
10
- "Salvador Santolucito <salvador.santolucito@gmail.com>"
11
- ]
12
- readme = "README.md"
13
- license = "MIT"
14
- include = [
15
- { path = "labview_fpga_hdl_tools/host_interfaces/**", format = "sdist" }
16
- ]
17
-
18
- [tool.poetry.dependencies]
19
- python = ">=3.9,<4.0"
20
- mako = ">=1.3.10"
21
- click = ">=8.1.8,<8.2"
22
- packaging = ">=21.0"
23
- tomli = {version = ">=1.1.0", python = "<3.11"}
24
-
25
- [tool.poetry.group.test.dependencies]
26
- pytest = ">=7.2"
27
-
28
- [tool.poetry.group.lint.dependencies]
29
- bandit = { version = ">=1.7", extras = ["toml"] }
30
- ni-python-styleguide = ">=0.4.1"
31
- mypy = ">=1.0"
32
- pyright = { version = ">=1.1.400", extras = ["nodejs"] }
33
-
34
- [tool.poetry.scripts]
35
- nihdl = "labview_fpga_hdl_tools.__main__:main"
36
-
37
- [tool.mypy]
38
- packages = ["labview_fpga_hdl_tools"]
39
- python_version = "3.9"
40
- warn_return_any = true
41
- warn_unused_configs = true
42
- disallow_untyped_defs = false
43
- disallow_incomplete_defs = false
44
-
45
- [[tool.mypy.overrides]]
46
- module = "mako.*"
47
- ignore_missing_imports = true
48
-
49
- [tool.bandit]
50
- exclude_dirs = ["tests"]