opencos-eda 0.3.6__tar.gz → 0.3.7__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.
- {opencos_eda-0.3.6/opencos_eda.egg-info → opencos_eda-0.3.7}/PKG-INFO +6 -4
- {opencos_eda-0.3.6 → opencos_eda-0.3.7}/opencos/commands/flist.py +2 -2
- {opencos_eda-0.3.6 → opencos_eda-0.3.7}/opencos/commands/multi.py +4 -3
- {opencos_eda-0.3.6 → opencos_eda-0.3.7}/opencos/commands/sim.py +12 -3
- {opencos_eda-0.3.6 → opencos_eda-0.3.7}/opencos/eda.py +22 -20
- {opencos_eda-0.3.6 → opencos_eda-0.3.7}/opencos/eda_base.py +150 -64
- {opencos_eda-0.3.6 → opencos_eda-0.3.7}/opencos/tests/deps_files/command_order/DEPS.yml +13 -0
- {opencos_eda-0.3.6 → opencos_eda-0.3.7}/opencos/tests/helpers.py +9 -5
- {opencos_eda-0.3.6 → opencos_eda-0.3.7}/opencos/tests/test_eda.py +1 -1
- {opencos_eda-0.3.6 → opencos_eda-0.3.7}/opencos/tests/test_tools.py +9 -3
- {opencos_eda-0.3.6 → opencos_eda-0.3.7}/opencos/tools/verilator.py +1 -2
- {opencos_eda-0.3.6 → opencos_eda-0.3.7}/opencos/tools/yosys.py +3 -3
- {opencos_eda-0.3.6 → opencos_eda-0.3.7}/opencos/util.py +27 -5
- {opencos_eda-0.3.6 → opencos_eda-0.3.7}/opencos/utils/subprocess_helpers.py +23 -6
- {opencos_eda-0.3.6 → opencos_eda-0.3.7/opencos_eda.egg-info}/PKG-INFO +6 -4
- {opencos_eda-0.3.6 → opencos_eda-0.3.7}/opencos_eda.egg-info/requires.txt +3 -0
- {opencos_eda-0.3.6 → opencos_eda-0.3.7}/pyproject.toml +9 -5
- {opencos_eda-0.3.6 → opencos_eda-0.3.7}/LICENSE +0 -0
- {opencos_eda-0.3.6 → opencos_eda-0.3.7}/LICENSE.spdx +0 -0
- {opencos_eda-0.3.6 → opencos_eda-0.3.7}/README.md +0 -0
- {opencos_eda-0.3.6 → opencos_eda-0.3.7}/opencos/__init__.py +0 -0
- {opencos_eda-0.3.6 → opencos_eda-0.3.7}/opencos/_version.py +0 -0
- {opencos_eda-0.3.6 → opencos_eda-0.3.7}/opencos/_waves_pkg.sv +0 -0
- {opencos_eda-0.3.6 → opencos_eda-0.3.7}/opencos/commands/__init__.py +0 -0
- {opencos_eda-0.3.6 → opencos_eda-0.3.7}/opencos/commands/build.py +0 -0
- {opencos_eda-0.3.6 → opencos_eda-0.3.7}/opencos/commands/deps_help.py +0 -0
- {opencos_eda-0.3.6 → opencos_eda-0.3.7}/opencos/commands/elab.py +0 -0
- {opencos_eda-0.3.6 → opencos_eda-0.3.7}/opencos/commands/export.py +0 -0
- {opencos_eda-0.3.6 → opencos_eda-0.3.7}/opencos/commands/lec.py +0 -0
- {opencos_eda-0.3.6 → opencos_eda-0.3.7}/opencos/commands/lint.py +0 -0
- {opencos_eda-0.3.6 → opencos_eda-0.3.7}/opencos/commands/open.py +0 -0
- {opencos_eda-0.3.6 → opencos_eda-0.3.7}/opencos/commands/proj.py +0 -0
- {opencos_eda-0.3.6 → opencos_eda-0.3.7}/opencos/commands/shell.py +0 -0
- {opencos_eda-0.3.6 → opencos_eda-0.3.7}/opencos/commands/sweep.py +0 -0
- {opencos_eda-0.3.6 → opencos_eda-0.3.7}/opencos/commands/synth.py +0 -0
- {opencos_eda-0.3.6 → opencos_eda-0.3.7}/opencos/commands/targets.py +0 -0
- {opencos_eda-0.3.6 → opencos_eda-0.3.7}/opencos/commands/upload.py +0 -0
- {opencos_eda-0.3.6 → opencos_eda-0.3.7}/opencos/commands/waves.py +0 -0
- {opencos_eda-0.3.6 → opencos_eda-0.3.7}/opencos/deps/__init__.py +0 -0
- {opencos_eda-0.3.6 → opencos_eda-0.3.7}/opencos/deps/defaults.py +0 -0
- {opencos_eda-0.3.6 → opencos_eda-0.3.7}/opencos/deps/deps_commands.py +0 -0
- {opencos_eda-0.3.6 → opencos_eda-0.3.7}/opencos/deps/deps_file.py +0 -0
- {opencos_eda-0.3.6 → opencos_eda-0.3.7}/opencos/deps/deps_processor.py +0 -0
- {opencos_eda-0.3.6 → opencos_eda-0.3.7}/opencos/deps_schema.py +0 -0
- {opencos_eda-0.3.6 → opencos_eda-0.3.7}/opencos/eda_config.py +0 -0
- {opencos_eda-0.3.6 → opencos_eda-0.3.7}/opencos/eda_config_defaults.yml +0 -0
- {opencos_eda-0.3.6 → opencos_eda-0.3.7}/opencos/eda_config_max_verilator_waivers.yml +0 -0
- {opencos_eda-0.3.6 → opencos_eda-0.3.7}/opencos/eda_config_reduced.yml +0 -0
- {opencos_eda-0.3.6 → opencos_eda-0.3.7}/opencos/eda_deps_bash_completion.bash +0 -0
- {opencos_eda-0.3.6 → opencos_eda-0.3.7}/opencos/eda_deps_sanitize.py +0 -0
- {opencos_eda-0.3.6 → opencos_eda-0.3.7}/opencos/eda_extract_targets.py +0 -0
- {opencos_eda-0.3.6 → opencos_eda-0.3.7}/opencos/eda_tool_helper.py +0 -0
- {opencos_eda-0.3.6 → opencos_eda-0.3.7}/opencos/export_helper.py +0 -0
- {opencos_eda-0.3.6 → opencos_eda-0.3.7}/opencos/export_json_convert.py +0 -0
- {opencos_eda-0.3.6 → opencos_eda-0.3.7}/opencos/files.py +0 -0
- {opencos_eda-0.3.6 → opencos_eda-0.3.7}/opencos/hw/__init__.py +0 -0
- {opencos_eda-0.3.6 → opencos_eda-0.3.7}/opencos/hw/oc_cli.py +0 -0
- {opencos_eda-0.3.6 → opencos_eda-0.3.7}/opencos/hw/pcie.py +0 -0
- {opencos_eda-0.3.6 → opencos_eda-0.3.7}/opencos/names.py +0 -0
- {opencos_eda-0.3.6 → opencos_eda-0.3.7}/opencos/peakrdl_cleanup.py +0 -0
- {opencos_eda-0.3.6 → opencos_eda-0.3.7}/opencos/seed.py +0 -0
- {opencos_eda-0.3.6 → opencos_eda-0.3.7}/opencos/tests/__init__.py +0 -0
- {opencos_eda-0.3.6 → opencos_eda-0.3.7}/opencos/tests/custom_config.yml +0 -0
- {opencos_eda-0.3.6 → opencos_eda-0.3.7}/opencos/tests/deps_files/error_msgs/DEPS.yml +0 -0
- {opencos_eda-0.3.6 → opencos_eda-0.3.7}/opencos/tests/deps_files/iverilog_test/DEPS.yml +0 -0
- {opencos_eda-0.3.6 → opencos_eda-0.3.7}/opencos/tests/deps_files/no_deps_here/DEPS.yml +0 -0
- {opencos_eda-0.3.6 → opencos_eda-0.3.7}/opencos/tests/deps_files/non_sv_reqs/DEPS.yml +0 -0
- {opencos_eda-0.3.6 → opencos_eda-0.3.7}/opencos/tests/deps_files/tags_with_tools/DEPS.yml +0 -0
- {opencos_eda-0.3.6 → opencos_eda-0.3.7}/opencos/tests/deps_files/test_err_fatal/DEPS.yml +0 -0
- {opencos_eda-0.3.6 → opencos_eda-0.3.7}/opencos/tests/test_build.py +0 -0
- {opencos_eda-0.3.6 → opencos_eda-0.3.7}/opencos/tests/test_deps_helpers.py +0 -0
- {opencos_eda-0.3.6 → opencos_eda-0.3.7}/opencos/tests/test_deps_schema.py +0 -0
- {opencos_eda-0.3.6 → opencos_eda-0.3.7}/opencos/tests/test_eda_elab.py +0 -0
- {opencos_eda-0.3.6 → opencos_eda-0.3.7}/opencos/tests/test_eda_synth.py +0 -0
- {opencos_eda-0.3.6 → opencos_eda-0.3.7}/opencos/tests/test_oc_cli.py +0 -0
- {opencos_eda-0.3.6 → opencos_eda-0.3.7}/opencos/tools/__init__.py +0 -0
- {opencos_eda-0.3.6 → opencos_eda-0.3.7}/opencos/tools/cocotb.py +0 -0
- {opencos_eda-0.3.6 → opencos_eda-0.3.7}/opencos/tools/invio.py +0 -0
- {opencos_eda-0.3.6 → opencos_eda-0.3.7}/opencos/tools/invio_helpers.py +0 -0
- {opencos_eda-0.3.6 → opencos_eda-0.3.7}/opencos/tools/invio_yosys.py +0 -0
- {opencos_eda-0.3.6 → opencos_eda-0.3.7}/opencos/tools/iverilog.py +0 -0
- {opencos_eda-0.3.6 → opencos_eda-0.3.7}/opencos/tools/modelsim_ase.py +0 -0
- {opencos_eda-0.3.6 → opencos_eda-0.3.7}/opencos/tools/quartus.py +0 -0
- {opencos_eda-0.3.6 → opencos_eda-0.3.7}/opencos/tools/questa.py +0 -0
- {opencos_eda-0.3.6 → opencos_eda-0.3.7}/opencos/tools/questa_fse.py +0 -0
- {opencos_eda-0.3.6 → opencos_eda-0.3.7}/opencos/tools/riviera.py +0 -0
- {opencos_eda-0.3.6 → opencos_eda-0.3.7}/opencos/tools/slang.py +0 -0
- {opencos_eda-0.3.6 → opencos_eda-0.3.7}/opencos/tools/slang_yosys.py +0 -0
- {opencos_eda-0.3.6 → opencos_eda-0.3.7}/opencos/tools/surelog.py +0 -0
- {opencos_eda-0.3.6 → opencos_eda-0.3.7}/opencos/tools/tabbycad_yosys.py +0 -0
- {opencos_eda-0.3.6 → opencos_eda-0.3.7}/opencos/tools/vivado.py +0 -0
- {opencos_eda-0.3.6 → opencos_eda-0.3.7}/opencos/utils/__init__.py +0 -0
- {opencos_eda-0.3.6 → opencos_eda-0.3.7}/opencos/utils/markup_helpers.py +0 -0
- {opencos_eda-0.3.6 → opencos_eda-0.3.7}/opencos/utils/status_constants.py +0 -0
- {opencos_eda-0.3.6 → opencos_eda-0.3.7}/opencos/utils/str_helpers.py +0 -0
- {opencos_eda-0.3.6 → opencos_eda-0.3.7}/opencos/utils/vscode_helper.py +0 -0
- {opencos_eda-0.3.6 → opencos_eda-0.3.7}/opencos/utils/vsim_helper.py +0 -0
- {opencos_eda-0.3.6 → opencos_eda-0.3.7}/opencos_eda.egg-info/SOURCES.txt +0 -0
- {opencos_eda-0.3.6 → opencos_eda-0.3.7}/opencos_eda.egg-info/dependency_links.txt +0 -0
- {opencos_eda-0.3.6 → opencos_eda-0.3.7}/opencos_eda.egg-info/entry_points.txt +0 -0
- {opencos_eda-0.3.6 → opencos_eda-0.3.7}/opencos_eda.egg-info/top_level.txt +0 -0
- {opencos_eda-0.3.6 → opencos_eda-0.3.7}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: opencos-eda
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.7
|
|
4
4
|
Summary: A simple Python package for wrapping RTL simuliatons and synthesis
|
|
5
5
|
Author-email: Simon Sabato <simon@cognichip.ai>, Drew Ranck <drew@cognichip.ai>
|
|
6
6
|
Project-URL: Homepage, https://github.com/cognichip/opencos
|
|
@@ -17,11 +17,13 @@ Requires-Dist: toml>=0.10.2
|
|
|
17
17
|
Requires-Dist: yamllint>=1.35.1
|
|
18
18
|
Requires-Dist: PySerial>=3.5
|
|
19
19
|
Requires-Dist: supports_color>=0.2.0
|
|
20
|
-
Requires-Dist: cocotb>=2.0
|
|
21
|
-
Requires-Dist: pytest>=8.3.5
|
|
22
|
-
Requires-Dist: coverage>=7.6.1
|
|
23
20
|
Provides-Extra: dev
|
|
24
21
|
Requires-Dist: pylint>=3.0.0; extra == "dev"
|
|
22
|
+
Requires-Dist: pytest>=8.3.5; extra == "dev"
|
|
23
|
+
Provides-Extra: cocotb
|
|
24
|
+
Requires-Dist: cocotb>=2.0; extra == "cocotb"
|
|
25
|
+
Requires-Dist: pytest>=8.3.5; extra == "cocotb"
|
|
26
|
+
Requires-Dist: coverage>=7.6.1; extra == "cocotb"
|
|
25
27
|
Provides-Extra: docs
|
|
26
28
|
Requires-Dist: mkdocs; extra == "docs"
|
|
27
29
|
Requires-Dist: mkdocs-material; extra == "docs"
|
|
@@ -48,7 +48,7 @@ class CommandFList(CommandDesign):
|
|
|
48
48
|
'bracket-quote-path' : False,
|
|
49
49
|
'single-quote-path' : False,
|
|
50
50
|
'double-quote-path' : False,
|
|
51
|
-
'
|
|
51
|
+
'quote-path' : True,
|
|
52
52
|
'build-script' : "", # we don't want this to error either
|
|
53
53
|
|
|
54
54
|
'print-to-stdout': False,
|
|
@@ -122,7 +122,7 @@ class CommandFList(CommandDesign):
|
|
|
122
122
|
|
|
123
123
|
pq1 = ""
|
|
124
124
|
pq2 = "" # pq = path quote
|
|
125
|
-
if self.args['
|
|
125
|
+
if not self.args['quote-path']:
|
|
126
126
|
pass # if we decide to make one of the below default, this will override
|
|
127
127
|
elif self.args['bracket-quote-path']:
|
|
128
128
|
pq1 = "{"
|
|
@@ -28,7 +28,7 @@ class CommandMulti(CommandParallel):
|
|
|
28
28
|
'fake': False,
|
|
29
29
|
'parallel': 1,
|
|
30
30
|
'single-timeout': None,
|
|
31
|
-
'fail-if-no-targets':
|
|
31
|
+
'fail-if-no-targets': True,
|
|
32
32
|
'export-jsonl': False,
|
|
33
33
|
'print-targets': False,
|
|
34
34
|
}
|
|
@@ -325,8 +325,9 @@ class CommandMulti(CommandParallel):
|
|
|
325
325
|
current_targets = len(self.targets)
|
|
326
326
|
util.info(f"Multi: Expanded {target_globs} to {len(self.targets)} {command} targets")
|
|
327
327
|
|
|
328
|
-
if
|
|
329
|
-
self.error(f'Multi: --fail-if-no-targets set, and {self.targets=}'
|
|
328
|
+
if self.args['fail-if-no-targets'] and not self.targets:
|
|
329
|
+
self.error(f'Multi: --fail-if-no-targets set, and {self.targets=}. Disable with',
|
|
330
|
+
'--no-fail-if-no-targets, or see: eda multi --help')
|
|
330
331
|
if not all_multi_tools:
|
|
331
332
|
possible_tools = self.all_handler_commands.get(command, [])
|
|
332
333
|
self.error(f'Multi: no tools to run for {command=}, available tools: {possible_tools}')
|
|
@@ -59,11 +59,11 @@ class CommandSim(CommandDesign):
|
|
|
59
59
|
self.args.update({
|
|
60
60
|
"pre-sim-tcl": [],
|
|
61
61
|
'compile-args': [],
|
|
62
|
+
'compile-waivers': [],
|
|
62
63
|
'elab-args': [],
|
|
63
64
|
'sim-args': [],
|
|
64
65
|
'sim-plusargs': [], # lists are handled by 'set_arg(k,v)' so they append.
|
|
65
66
|
'sim-library': [],
|
|
66
|
-
'compile-waivers': [],
|
|
67
67
|
'sim-waivers': [],
|
|
68
68
|
'coverage': False,
|
|
69
69
|
'waves': False,
|
|
@@ -78,7 +78,13 @@ class CommandSim(CommandDesign):
|
|
|
78
78
|
'verilate-args': [],
|
|
79
79
|
})
|
|
80
80
|
self.args_help.update({
|
|
81
|
+
'pre-sim-tcl': (
|
|
82
|
+
'Tool dependent, TCL file to run prior to simulation (if tool supports TCL)'
|
|
83
|
+
),
|
|
81
84
|
'compile-args': 'args added to sim/elab "compile" step',
|
|
85
|
+
'compile-waivers': (
|
|
86
|
+
'simulation tool depenent waivers by name, applied at compilation step'
|
|
87
|
+
),
|
|
82
88
|
'coverage': 'attempt to run coverage steps on the compile/elab/simulation',
|
|
83
89
|
'elab-args': 'args added to sim/elab "elaboration" step, if required by tool',
|
|
84
90
|
'log-bad-strings': 'strings that if present in the log will fail the simulation',
|
|
@@ -90,6 +96,10 @@ class CommandSim(CommandDesign):
|
|
|
90
96
|
'sim-args': 'args added to final "simulation" step',
|
|
91
97
|
'sim-plusargs': ('"simulation" step run-time args passed to tool, these can also'
|
|
92
98
|
' be set using --sim-plusargs=name[=value], or simply +name[=value]'),
|
|
99
|
+
'sim-library': 'named libraries added to simulation, tool dependent',
|
|
100
|
+
'sim-waivers': (
|
|
101
|
+
'simulation tool depenent waivers by name, applied at "simulation" step'
|
|
102
|
+
),
|
|
93
103
|
'stop-before-compile': ('Create work-dir sh scripts for compile/elab/simulate, but do'
|
|
94
104
|
' not run them.'),
|
|
95
105
|
'stop-after-compile': 'Create work-dir sh scripts, but only run the compile step.',
|
|
@@ -97,10 +107,9 @@ class CommandSim(CommandDesign):
|
|
|
97
107
|
' simulation step.'),
|
|
98
108
|
'top': 'Name of topmost Verilog/SystemVerilog module, or VHDL entity',
|
|
99
109
|
'verilate-args': ('args added to "compile" step in Verilator simulation'
|
|
100
|
-
' (for --tool=verilator)'),
|
|
110
|
+
' (for --tool=verilator) these are identical to --compile-args'),
|
|
101
111
|
'waves': 'Include waveforms, if possible for tool',
|
|
102
112
|
'waves-start': 'Starting time of waveform capture, if possible for tool',
|
|
103
|
-
'work-dir': 'Optional override for working directory, defaults to ./eda.work/<top>.sim',
|
|
104
113
|
'test-mode': ('stops the command early without executing, if --gui is present will'
|
|
105
114
|
' instead test without spawning gui')
|
|
106
115
|
|
|
@@ -20,8 +20,8 @@ from pathlib import Path
|
|
|
20
20
|
|
|
21
21
|
import opencos
|
|
22
22
|
from opencos import util, eda_config, eda_base
|
|
23
|
-
from opencos.util import safe_emoji
|
|
24
|
-
from opencos.eda_base import Tool, which_tool, get_eda_exec
|
|
23
|
+
from opencos.util import safe_emoji, Colors
|
|
24
|
+
from opencos.eda_base import Tool, which_tool, get_eda_exec, print_eda_usage_line
|
|
25
25
|
from opencos.utils import vsim_helper, vscode_helper
|
|
26
26
|
from opencos.utils.subprocess_helpers import subprocess_run_background
|
|
27
27
|
from opencos.utils import status_constants, str_helpers, subprocess_helpers
|
|
@@ -76,7 +76,7 @@ def get_all_commands_help_str(config: dict) -> str:
|
|
|
76
76
|
all_commands_help.append(f' {key:<{max_command_str_len}} - {value.strip()}')
|
|
77
77
|
if all_commands_help:
|
|
78
78
|
all_commands_help = [
|
|
79
|
-
'Where
|
|
79
|
+
f'Where {Colors.byellow}COMMAND{Colors.normal} is one of:',
|
|
80
80
|
'',
|
|
81
81
|
] + all_commands_help
|
|
82
82
|
return '\n'.join(all_commands_help)
|
|
@@ -95,22 +95,24 @@ def usage(tokens: list, config: dict, command: str = "", tool: str = "") -> int:
|
|
|
95
95
|
'''
|
|
96
96
|
|
|
97
97
|
if command == "":
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
eda [<options>] <command> [options] <files|targets, ...>
|
|
102
|
-
"""
|
|
103
|
-
)
|
|
98
|
+
util.info('Help:', color=Colors.cyan)
|
|
99
|
+
print()
|
|
100
|
+
print_eda_usage_line()
|
|
104
101
|
print(get_all_commands_help_str(config=config))
|
|
105
|
-
print(
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
102
|
+
print()
|
|
103
|
+
print(str_helpers.indent_wrap_long_text(
|
|
104
|
+
(
|
|
105
|
+
f'{safe_emoji("❕ ")}where {Colors.byellow}FILES|TARGETS{Colors.normal}'
|
|
106
|
+
' is one or more source file or DEPS markup file target,'
|
|
107
|
+
' such as .v, .sv, .vhd[l], .cpp files, or a target key in a'
|
|
108
|
+
f' {Colors.byellow}DEPS.[yml|yaml|toml|json]{Colors.normal}. Note that you can'
|
|
109
|
+
f' prefix source files with {Colors.bcyan}sv@{Colors.normal},'
|
|
110
|
+
f' {Colors.bcyan}v@{Colors.normal}, {Colors.bcyan}vhdl@{Colors.normal},'
|
|
111
|
+
f' or {Colors.bcyan}cpp@{Colors.normal}'
|
|
112
|
+
' to force use that file as systemverilog, verilog, vhdl, or C++, respectively.'
|
|
113
|
+
), width=str_helpers.get_terminal_columns(), indent=4
|
|
114
|
+
))
|
|
115
|
+
print()
|
|
114
116
|
eda_base.print_base_help()
|
|
115
117
|
return 0
|
|
116
118
|
|
|
@@ -527,7 +529,7 @@ def main(*args):
|
|
|
527
529
|
sys.exit(0)
|
|
528
530
|
|
|
529
531
|
if not util.args['quiet']:
|
|
530
|
-
util.info(f'eda: version {opencos.__version__}')
|
|
532
|
+
util.info(f'eda: version {opencos.__version__}', color=Colors.bcyan)
|
|
531
533
|
# And show the command that was run (all args):
|
|
532
534
|
util.info(f"main: eda {' '.join(args)}; (run from {os.getcwd()})")
|
|
533
535
|
|
|
@@ -537,7 +539,7 @@ def main(*args):
|
|
|
537
539
|
# Note - we used to call: config = init_config(config=config)
|
|
538
540
|
# However, we now defer calling init_config(..) until eda.process_tokens(..)
|
|
539
541
|
|
|
540
|
-
util.info("*** OpenCOS EDA ***")
|
|
542
|
+
util.info("*** OpenCOS EDA ***", color=Colors.bcyan)
|
|
541
543
|
|
|
542
544
|
if len(args) == 0 or (len(args) == 1 and '--debug' in args):
|
|
543
545
|
# special snowflake case if someone called with a singular arg --debug
|
|
@@ -26,7 +26,7 @@ from opencos import eda_config
|
|
|
26
26
|
|
|
27
27
|
from opencos.util import Colors, safe_emoji
|
|
28
28
|
from opencos.utils.str_helpers import sprint_time, strip_outer_quotes, string_or_space, \
|
|
29
|
-
indent_wrap_long_text, pretty_list_columns_manual
|
|
29
|
+
indent_wrap_long_text, pretty_list_columns_manual, get_terminal_columns
|
|
30
30
|
from opencos.utils.subprocess_helpers import subprocess_run_background
|
|
31
31
|
from opencos.utils import status_constants
|
|
32
32
|
|
|
@@ -42,6 +42,24 @@ def print_base_help() -> None:
|
|
|
42
42
|
print(get_argparser_short_help())
|
|
43
43
|
|
|
44
44
|
|
|
45
|
+
def print_eda_usage_line(no_targets: bool = False, command_name='COMMAND') -> None:
|
|
46
|
+
'''Prints line for eda [options] COMMAND [options] FILES|TARGETS,...'''
|
|
47
|
+
print(f'{safe_emoji("🔦 ")}Usage:')
|
|
48
|
+
if no_targets:
|
|
49
|
+
print(
|
|
50
|
+
(f' {Colors.bold}{Colors.yellow}eda {Colors.cyan}[options]'
|
|
51
|
+
f' {Colors.yellow}{command_name} {Colors.cyan}[options]{Colors.normal}')
|
|
52
|
+
)
|
|
53
|
+
else:
|
|
54
|
+
print(
|
|
55
|
+
(f' {Colors.bold}{Colors.yellow}eda {Colors.cyan}[options]'
|
|
56
|
+
f' {Colors.yellow}{command_name} {Colors.cyan}[options]'
|
|
57
|
+
f' {Colors.yellow}FILES|TARGETS,...{Colors.normal}')
|
|
58
|
+
)
|
|
59
|
+
print()
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
|
|
45
63
|
def get_argparser() -> argparse.ArgumentParser:
|
|
46
64
|
'''Returns the ArgumentParser for general eda CLI'''
|
|
47
65
|
parser = argparse.ArgumentParser(
|
|
@@ -82,7 +100,7 @@ def get_argparsers_args_list() -> list:
|
|
|
82
100
|
|
|
83
101
|
|
|
84
102
|
def get_eda_exec(command: str = '') -> str:
|
|
85
|
-
'''Returns the full path of `eda` executable to be used for a given eda
|
|
103
|
+
'''Returns the full path of `eda` executable to be used for a given eda COMMAND'''
|
|
86
104
|
# NOTE(drew): This is kind of flaky. 'eda multi' reinvokes 'eda'. But the executable for 'eda'
|
|
87
105
|
# is one of:
|
|
88
106
|
# 1. pip3 install opencos-eda
|
|
@@ -133,6 +151,17 @@ def which_tool(command: str, config: dict) -> str:
|
|
|
133
151
|
return tool
|
|
134
152
|
|
|
135
153
|
|
|
154
|
+
def get_class_tool_name(command_obj: object) -> str:
|
|
155
|
+
'''Attempts to return command_obj._TOOL via command_obj.get_tool_name()'''
|
|
156
|
+
if f := getattr(command_obj, 'get_tool_name', None):
|
|
157
|
+
if callable(f):
|
|
158
|
+
ret = f()
|
|
159
|
+
if ret and isinstance(ret, str):
|
|
160
|
+
return ret
|
|
161
|
+
return ''
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
|
|
136
165
|
class Tool:
|
|
137
166
|
'''opencos.eda_base.Tool is a base class used by opencos.tools.<name>.
|
|
138
167
|
|
|
@@ -182,6 +211,10 @@ class Tool:
|
|
|
182
211
|
util.info(f'Override for {self._TOOL} using exe {exe}')
|
|
183
212
|
self._EXE = exe
|
|
184
213
|
|
|
214
|
+
def get_tool_name(self) -> str:
|
|
215
|
+
'''Returns _TOOL'''
|
|
216
|
+
return str(self._TOOL)
|
|
217
|
+
|
|
185
218
|
def get_full_tool_and_versions(self) -> str:
|
|
186
219
|
'''Returns tool:version, such as: verilator:5.033'''
|
|
187
220
|
if not self._VERSION:
|
|
@@ -198,7 +231,7 @@ class Tool:
|
|
|
198
231
|
|
|
199
232
|
|
|
200
233
|
class Command: # pylint: disable=too-many-public-methods
|
|
201
|
-
'''Base class for all: eda
|
|
234
|
+
'''Base class for all: eda COMMAND
|
|
202
235
|
|
|
203
236
|
The Command class should be used when you don't require files, otherwise consider
|
|
204
237
|
CommandDesign.
|
|
@@ -235,21 +268,54 @@ class Command: # pylint: disable=too-many-public-methods
|
|
|
235
268
|
'error-unknown-args': True,
|
|
236
269
|
})
|
|
237
270
|
self.args_help.update({
|
|
238
|
-
'
|
|
239
|
-
|
|
240
|
-
|
|
271
|
+
'keep': (
|
|
272
|
+
'Determined by eda <tool>, generally prevents jobs from overwriting artifacts'
|
|
273
|
+
),
|
|
274
|
+
'force': 'Determined by eda <tool>, force override',
|
|
275
|
+
'fake': (
|
|
276
|
+
'Determined by eda <tool>, generally is a dry-run that does not execute tool'
|
|
277
|
+
),
|
|
278
|
+
'stop-before-compile': (
|
|
279
|
+
'stop this run before any compile steps (if possible for tool) and'
|
|
280
|
+
' save .sh scripts in eda-dir/'
|
|
281
|
+
),
|
|
282
|
+
'stop-after-compile': (
|
|
283
|
+
'stop this run after any compile steps (if possible for tool) and'
|
|
284
|
+
' save .sh scripts in eda-dir/'
|
|
285
|
+
),
|
|
286
|
+
'stop-after-elaborate': (
|
|
287
|
+
'stop this run after any elaborate steps (if possible for tool) and'
|
|
288
|
+
' save .sh scripts in eda-dir/'
|
|
289
|
+
),
|
|
290
|
+
'lint': 'tool dependent, run with linting options',
|
|
291
|
+
"job-name" : 'Optional, used to create a sub directory under base work-dir (eda-dir)',
|
|
292
|
+
"sub-work-dir" : (
|
|
293
|
+
'Optional, similar to job-name, can be used to name the directory created under'
|
|
294
|
+
' eda-dir'
|
|
295
|
+
),
|
|
296
|
+
'eda-dir': 'Optional, relative base directory where eda logs are saved',
|
|
241
297
|
'export': 'export results for these targets in eda-dir',
|
|
242
298
|
'export-run': 'export, and run, results for these targets in eda-dir',
|
|
243
299
|
'export-json': 'export, and save a JSON file per target',
|
|
244
|
-
'work-dir': ('Optional override for working directory, often
|
|
245
|
-
' ./eda.work
|
|
300
|
+
'work-dir': ('Optional override for working directory, if unset will often use '
|
|
301
|
+
' ./eda.work/[TOP|TARGET].COMMAND'),
|
|
246
302
|
"work-dir-use-target-dir": ('Set the work-dir to be the same as the in-place location'
|
|
247
303
|
' where the target (DEPS) exists'),
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
304
|
+
"suffix": (
|
|
305
|
+
"Optional, determined by eda COMMAND, used by 'multi' jobs information logging"
|
|
306
|
+
),
|
|
307
|
+
"design": (
|
|
308
|
+
"Optional, used to override both the work-dir, and if unset the top DEPS target"
|
|
309
|
+
),
|
|
310
|
+
'enable-tags': (
|
|
311
|
+
'DEPS markup tag names to be force enabled for this'
|
|
312
|
+
' command (mulitple appends to list).'
|
|
313
|
+
),
|
|
314
|
+
'disable-tags': (
|
|
315
|
+
'DEPS markup tag names to be disabled (even if they match the criteria) for this'
|
|
316
|
+
' command (mulitple appends to list). --disable-tags has higher precedence than'
|
|
317
|
+
' --enable-tags.'
|
|
318
|
+
),
|
|
253
319
|
'test-mode': ('command and tool dependent, usually stops the command early without'
|
|
254
320
|
' executing.'),
|
|
255
321
|
'error-unknown-args': (
|
|
@@ -356,7 +422,7 @@ class Command: # pylint: disable=too-many-public-methods
|
|
|
356
422
|
) -> str:
|
|
357
423
|
'''Creates the working directory and populates self.args['work-dir']
|
|
358
424
|
|
|
359
|
-
Generally uses ./ self.args['eda-dir'] /
|
|
425
|
+
Generally uses ./ self.args['eda-dir'] / TARGET-NAME.COMMAND /
|
|
360
426
|
however, self.args['job-name'] or ['sub-work-dir'] can override that.
|
|
361
427
|
|
|
362
428
|
Additionally, the work-dir is attempted to be deleted if it already exists
|
|
@@ -619,20 +685,33 @@ class Command: # pylint: disable=too-many-public-methods
|
|
|
619
685
|
# is []. If the parsed Namespace has values set to None or [], we do not update. This
|
|
620
686
|
# means that as deps are processed that have args set, they cannot override the top
|
|
621
687
|
# level args that were already set, nor be overriden by defaults.
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
688
|
+
try:
|
|
689
|
+
if isinstance(value, bool):
|
|
690
|
+
# For bool, support --key and --no-key with
|
|
691
|
+
# action=argparse.BooleanOptionalAction. Note, this means you cannot use
|
|
692
|
+
# --some-bool=True, or --some-bool=False, has to be --some-bool or
|
|
693
|
+
# --no-some-bool.
|
|
694
|
+
# Also we cannot have self.args.keys() that start with 'no-', which is
|
|
695
|
+
# why this entire thing is wrapped with try/except.
|
|
696
|
+
parser.add_argument(
|
|
697
|
+
*arguments, default=None, **bool_action_kwargs, **help_kwargs)
|
|
698
|
+
elif isinstance(value, (list, set)):
|
|
699
|
+
parser.add_argument(*arguments, default=value, action='append', **help_kwargs)
|
|
700
|
+
elif isinstance(value, (int, str)):
|
|
701
|
+
parser.add_argument(*arguments, default=value, type=type(value), **help_kwargs)
|
|
702
|
+
elif value is None:
|
|
703
|
+
parser.add_argument(*arguments, default=None, **help_kwargs)
|
|
704
|
+
else:
|
|
705
|
+
assert False, f'{key=} {value=} how do we do argparse for this type of value?'
|
|
706
|
+
except Exception as e:
|
|
707
|
+
if isinstance(value, bool):
|
|
708
|
+
self.error(f'Could not add argument: {key=} {value=} {type(value)=}',
|
|
709
|
+
f'{arguments=} {bool_action_kwargs=} {help_kwargs=} {e=}')
|
|
710
|
+
else:
|
|
711
|
+
self.error(f'Could not add argument: {key=} {value=} {type(value)=}',
|
|
712
|
+
f'{arguments=} {help_kwargs=} {e=}')
|
|
713
|
+
|
|
714
|
+
|
|
636
715
|
|
|
637
716
|
return parser
|
|
638
717
|
|
|
@@ -730,7 +809,7 @@ class Command: # pylint: disable=too-many-public-methods
|
|
|
730
809
|
def get_command_from_unparsed_args(
|
|
731
810
|
self, tokens: list, error_if_no_command: bool = True
|
|
732
811
|
) -> str:
|
|
733
|
-
'''Given a list of unparsed args, try to fish out the eda
|
|
812
|
+
'''Given a list of unparsed args, try to fish out the eda COMMAND value.
|
|
734
813
|
|
|
735
814
|
This will remove the value from the tokens list.
|
|
736
815
|
'''
|
|
@@ -742,7 +821,7 @@ class Command: # pylint: disable=too-many-public-methods
|
|
|
742
821
|
break
|
|
743
822
|
|
|
744
823
|
if not ret and error_if_no_command:
|
|
745
|
-
self.error(f"Looking for a valid eda {self.command_name}
|
|
824
|
+
self.error(f"Looking for a valid eda {self.command_name} COMMAND",
|
|
746
825
|
f"but didn't find one in {tokens=}",
|
|
747
826
|
error_code=status_constants.EDA_COMMAND_OR_ARGS_ERROR)
|
|
748
827
|
return ret
|
|
@@ -808,19 +887,16 @@ class Command: # pylint: disable=too-many-public-methods
|
|
|
808
887
|
if self.args_help has additional help information.
|
|
809
888
|
'''
|
|
810
889
|
|
|
811
|
-
# Indent long lines (>100) to indent=
|
|
812
|
-
def indent_me(text:str):
|
|
813
|
-
return indent_wrap_long_text(
|
|
890
|
+
# Indent long lines (>100) to indent=24 (this is where usual argparse help leaves off)
|
|
891
|
+
def indent_me(text: str, initial_indent: int = 23):
|
|
892
|
+
return indent_wrap_long_text(
|
|
893
|
+
' ' * initial_indent + text, width=get_terminal_columns(), indent=24
|
|
894
|
+
)
|
|
814
895
|
|
|
815
|
-
util.info('Help:')
|
|
896
|
+
util.info('Help:', color=Colors.cyan)
|
|
816
897
|
# using bare 'print' here, since help was requested, avoids --color and --quiet
|
|
817
898
|
print()
|
|
818
|
-
|
|
819
|
-
if no_targets:
|
|
820
|
-
print(f' eda [options] {self.command_name} [options]')
|
|
821
|
-
else:
|
|
822
|
-
print(f' eda [options] {self.command_name} [options] [files|targets, ...]')
|
|
823
|
-
print()
|
|
899
|
+
print_eda_usage_line(no_targets=no_targets, command_name=self.command_name)
|
|
824
900
|
|
|
825
901
|
print_base_help()
|
|
826
902
|
lines = []
|
|
@@ -829,10 +905,18 @@ class Command: # pylint: disable=too-many-public-methods
|
|
|
829
905
|
return
|
|
830
906
|
|
|
831
907
|
if self.command_name:
|
|
832
|
-
|
|
833
|
-
|
|
908
|
+
line = (
|
|
909
|
+
f"{safe_emoji('🔧 ')}{Colors.cyan}Generic help for"
|
|
910
|
+
f" command={Colors.byellow}{self.command_name}{Colors.cyan}"
|
|
911
|
+
)
|
|
912
|
+
if tool := get_class_tool_name(self):
|
|
913
|
+
line += f" tool={Colors.byellow}{tool}"
|
|
914
|
+
line += f" {Colors.normal}(using '{self.__class__.__name__}')"
|
|
915
|
+
lines.append(line)
|
|
834
916
|
else:
|
|
835
|
-
lines.append(
|
|
917
|
+
lines.append(
|
|
918
|
+
f"{safe_emoji('🔧 ')}{Colors.cyan}Generic help:{Colors.normal}"
|
|
919
|
+
)
|
|
836
920
|
|
|
837
921
|
# Attempt to run argparser on args, but don't error if it fails.
|
|
838
922
|
unparsed = []
|
|
@@ -842,44 +926,46 @@ class Command: # pylint: disable=too-many-public-methods
|
|
|
842
926
|
except Exception:
|
|
843
927
|
pass
|
|
844
928
|
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
vstr = "'" + v + "'"
|
|
859
|
-
lines.append(indent_me(f" --{k:20} : string : {vstr:12}{khelp}"))
|
|
860
|
-
else:
|
|
861
|
-
lines.append(indent_me(f" --{k:20} : <unknown> : {vstr:12}{khelp}"))
|
|
929
|
+
short_help_lines = util.get_argparser_short_help(
|
|
930
|
+
parser=self.get_argparser(support_underscores=False)
|
|
931
|
+
).split('\n')
|
|
932
|
+
short_help_lines.pop(0) # Strip first line w/ argparser prog name
|
|
933
|
+
lines.extend(short_help_lines)
|
|
934
|
+
|
|
935
|
+
# For these custom args, -GParameter=Value, +define+Name[=value] +incdir+path
|
|
936
|
+
# make colors to look like python >= 3.14, if that is our version.
|
|
937
|
+
color = Colors()
|
|
938
|
+
if sys.version_info.major < 3 or \
|
|
939
|
+
(sys.version_info.major == 3 and sys.version_info.minor < 14):
|
|
940
|
+
color.disable() # strip our color object if < 3.14
|
|
941
|
+
|
|
862
942
|
|
|
863
943
|
lines.append('')
|
|
944
|
+
lines.append(
|
|
945
|
+
f" {color.cyan}-G{color.byellow}<parameterName>{color.normal}=" \
|
|
946
|
+
+ f"{color.yellow}<value>{color.normal}"
|
|
947
|
+
)
|
|
864
948
|
lines.append(indent_me((
|
|
865
|
-
" -G<parameterName>=<value> "
|
|
866
949
|
" Add parameter to top level, support bit/int/string types only."
|
|
867
950
|
" Example: -GDEPTH=8 (DEPTH treated as SV int/integer)."
|
|
868
951
|
" -GENABLE=1 (ENABLED treated as SV bit/int/integer)."
|
|
869
952
|
" -GName=eda (Name treated as SV string \"eda\")."
|
|
870
953
|
)))
|
|
954
|
+
|
|
955
|
+
lines.append(f" {color.cyan}+define+{color.byellow}<defineName>{color.normal}")
|
|
871
956
|
lines.append(indent_me((
|
|
872
|
-
" +define+<defineName> "
|
|
873
957
|
" Add define w/out value to tool ahead of SV sources"
|
|
874
958
|
" Example: +define+SIM_SPEEDUP"
|
|
875
959
|
)))
|
|
960
|
+
lines.append(
|
|
961
|
+
f" {color.cyan}+define+{color.byellow}<defineName>{color.normal}=" \
|
|
962
|
+
+ f"{color.yellow}<value>{color.normal}")
|
|
876
963
|
lines.append(indent_me((
|
|
877
|
-
" +define+<defineName>=<value> "
|
|
878
964
|
" Add define w/ value to tool ahead of SV sources"
|
|
879
965
|
" Example: +define+TECH_LIB=2 +define+FULL_NAME=\"E D A\""
|
|
880
966
|
)))
|
|
967
|
+
lines.append(f" {color.cyan}+incdir+{color.byellow}PATH{color.normal}")
|
|
881
968
|
lines.append(indent_me((
|
|
882
|
-
" +incdir+<path> "
|
|
883
969
|
" Add path (absolute or relative) for include directories"
|
|
884
970
|
" for SystemVerilog `include \"<some-file>.svh\""
|
|
885
971
|
" Example: +incdir+../lib"
|
|
@@ -29,3 +29,16 @@ target_test_with_post_tool_commands:
|
|
|
29
29
|
run-after-tool: true
|
|
30
30
|
- target_echo_hi_bye
|
|
31
31
|
top: foo
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
test_run_from_work_dir:
|
|
35
|
+
deps:
|
|
36
|
+
- commands:
|
|
37
|
+
- shell: echo "pwd=$PWD"
|
|
38
|
+
run-from-work-dir: true
|
|
39
|
+
|
|
40
|
+
test_run_from_work_dir_false:
|
|
41
|
+
deps:
|
|
42
|
+
- commands:
|
|
43
|
+
- shell: echo "pwd=$PWD"
|
|
44
|
+
run-from-work-dir: false
|
|
@@ -10,8 +10,8 @@ from pathlib import Path
|
|
|
10
10
|
from contextlib import redirect_stdout, redirect_stderr
|
|
11
11
|
|
|
12
12
|
from opencos import eda, eda_tool_helper, deps_schema
|
|
13
|
-
from opencos.utils.markup_helpers import yaml_safe_load
|
|
14
13
|
from opencos.utils import status_constants
|
|
14
|
+
from opencos.utils.markup_helpers import yaml_safe_load
|
|
15
15
|
from opencos.utils.subprocess_helpers import subprocess_run_background
|
|
16
16
|
|
|
17
17
|
# Figure out what tools the system has available, without calling eda.main(..)
|
|
@@ -238,7 +238,7 @@ class Helpers:
|
|
|
238
238
|
|
|
239
239
|
def log_it(
|
|
240
240
|
self, command_str: str, logfile=None, use_eda_wrap: bool = True,
|
|
241
|
-
run_in_subprocess: bool = False,
|
|
241
|
+
run_in_subprocess: bool = False, include_default_log: bool = False,
|
|
242
242
|
preserve_env: bool = False
|
|
243
243
|
) -> int:
|
|
244
244
|
'''Replacement for calling eda.main or eda_wrap, when you want an internal logfile
|
|
@@ -257,13 +257,17 @@ class Helpers:
|
|
|
257
257
|
logfile = self._resolve_logfile(logfile)
|
|
258
258
|
rc = 50
|
|
259
259
|
|
|
260
|
+
eda_log_arg = '--no-default-log'
|
|
261
|
+
if include_default_log:
|
|
262
|
+
eda_log_arg = ''
|
|
263
|
+
|
|
260
264
|
# TODO(drew): There are some issues with log_it redirecting stdout from vivado
|
|
261
265
|
# and modelsim_ase. So this may not work for all tools, you may have to directly
|
|
262
266
|
# look at eda.work/{target}.sim/sim.log or xsim.log.
|
|
263
267
|
print(f'{os.getcwd()=}')
|
|
264
268
|
print(f'{command_str=}')
|
|
265
269
|
if run_in_subprocess or self.RUN_IN_SUBPROCESS:
|
|
266
|
-
command_list = ['eda',
|
|
270
|
+
command_list = ['eda', eda_log_arg] + command_str.split()
|
|
267
271
|
_, _, rc = subprocess_run_background(
|
|
268
272
|
work_dir=self.DEFAULT_DIR,
|
|
269
273
|
command_list=command_list,
|
|
@@ -275,9 +279,9 @@ class Helpers:
|
|
|
275
279
|
with open(logfile, 'w', encoding='utf-8') as f:
|
|
276
280
|
with redirect_stdout(f), redirect_stderr(f):
|
|
277
281
|
if use_eda_wrap or self.USE_EDA_WRAP:
|
|
278
|
-
rc = eda_wrap(
|
|
282
|
+
rc = eda_wrap(eda_log_arg, *(command_str.split()))
|
|
279
283
|
else:
|
|
280
|
-
rc = eda.main(
|
|
284
|
+
rc = eda.main(eda_log_arg, *(command_str.split()))
|
|
281
285
|
print(f'Wrote: {os.path.abspath(logfile)=}')
|
|
282
286
|
|
|
283
287
|
if self.PRESERVE_ENV or preserve_env:
|
|
@@ -648,7 +648,7 @@ class TestsRequiresIVerilog(Helpers):
|
|
|
648
648
|
print(f'{rc=}')
|
|
649
649
|
assert rc == 0
|
|
650
650
|
assert self.is_in_log('Detected iverilog')
|
|
651
|
-
assert self.is_in_log("Generic help for command=
|
|
651
|
+
assert self.is_in_log("Generic help for command=sim tool=iverilog")
|
|
652
652
|
|
|
653
653
|
def test_iverilog_sim(self):
|
|
654
654
|
'''Test for command sim'''
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
# pylint: disable=R0801 # (similar lines in 2+ files)
|
|
4
4
|
|
|
5
5
|
import os
|
|
6
|
+
import shutil
|
|
6
7
|
import sys
|
|
7
8
|
import pytest
|
|
8
9
|
|
|
@@ -32,14 +33,19 @@ def test_tools_loaded():
|
|
|
32
33
|
assert config
|
|
33
34
|
assert len(config.keys()) > 0
|
|
34
35
|
|
|
36
|
+
|
|
35
37
|
# It's possible we're running in some container or install that has no tools, for example,
|
|
36
38
|
# Windows.
|
|
37
39
|
if sys.platform.startswith('win') and \
|
|
38
40
|
not helpers.can_run_eda_command('elab', 'sim', cfg=config):
|
|
39
|
-
# Windows,
|
|
41
|
+
# Windows, no handlers for elab or sim:
|
|
40
42
|
pass
|
|
41
43
|
else:
|
|
42
|
-
|
|
44
|
+
basic_tools_available_via_path = any(shutil.which(x) for x in (
|
|
45
|
+
'verilator', 'iverilog', 'xsim', 'vsim', 'slang', 'yosys'
|
|
46
|
+
))
|
|
47
|
+
if basic_tools_available_via_path:
|
|
48
|
+
assert len(tools_loaded) > 0
|
|
43
49
|
|
|
44
50
|
def version_checker(
|
|
45
51
|
obj: eda_base.Tool, chk_str: str
|
|
@@ -49,7 +55,7 @@ def test_tools_loaded():
|
|
|
49
55
|
assert chk_str in full_ver, f'{chk_str=} not in {full_ver=}'
|
|
50
56
|
ver_num = full_ver.rsplit(':', maxsplit=1)[-1]
|
|
51
57
|
if 'b' in ver_num:
|
|
52
|
-
ver_num = ver_num.split('b')[0]
|
|
58
|
+
ver_num = ver_num.split('b')[0]
|
|
53
59
|
if '.' in ver_num:
|
|
54
60
|
major_ver = ver_num.split('.')[0]
|
|
55
61
|
assert major_ver.isdigit(), (
|
|
@@ -76,7 +76,6 @@ class VerilatorSim(CommandSim, ToolVerilator):
|
|
|
76
76
|
ToolVerilator.__init__(self, config=self.config)
|
|
77
77
|
self.args.update({
|
|
78
78
|
'gui': False,
|
|
79
|
-
'tcl-file': None,
|
|
80
79
|
'dump-vcd': False,
|
|
81
80
|
'waves-fst': True,
|
|
82
81
|
'waves-vcd': False,
|
|
@@ -95,7 +94,7 @@ class VerilatorSim(CommandSim, ToolVerilator):
|
|
|
95
94
|
' plusarg and apply $dumpfile("dump.fst").'
|
|
96
95
|
),
|
|
97
96
|
'waves-fst': (
|
|
98
|
-
'
|
|
97
|
+
'If using --waves, apply simulation runtime arg +trace.'
|
|
99
98
|
' Note that if you do not have SV code using $dumpfile, eda will add'
|
|
100
99
|
' _waves_pkg.sv to handle this for you with +trace runtime plusarg.'
|
|
101
100
|
),
|
|
@@ -70,9 +70,9 @@ class ToolYosys(Tool):
|
|
|
70
70
|
[self.sta_exe, '-version'], capture_output=True, check=False
|
|
71
71
|
)
|
|
72
72
|
util.debug(f'{self.yosys_exe} {sta_version_ret=}')
|
|
73
|
-
sta_ver = sta_version_ret.stdout.decode('utf-8', errors='replace').split()
|
|
74
|
-
if sta_ver:
|
|
75
|
-
self.sta_version = sta_ver
|
|
73
|
+
sta_ver = sta_version_ret.stdout.decode('utf-8', errors='replace').split()
|
|
74
|
+
if sta_ver and isinstance(sta_ver, list):
|
|
75
|
+
self.sta_version = sta_ver[0]
|
|
76
76
|
|
|
77
77
|
version_ret = subprocess.run(
|
|
78
78
|
[self.yosys_exe, '--version'], capture_output=True, check=False
|
|
@@ -54,7 +54,11 @@ class Colors:
|
|
|
54
54
|
red = "\x1B[31m"
|
|
55
55
|
green = "\x1B[32m"
|
|
56
56
|
yellow = "\x1B[33m" # This looks orange, but it's techincally yellow
|
|
57
|
+
cyan = "\x1b[36m"
|
|
57
58
|
foreground = "\x1B[39m"
|
|
59
|
+
bold = "\x1B[1m"
|
|
60
|
+
byellow = "\x1B[1m\x1B[33m"
|
|
61
|
+
bcyan = "\x1B[1m\x1b[36m"
|
|
58
62
|
normal = "\x1B[0m"
|
|
59
63
|
|
|
60
64
|
@staticmethod
|
|
@@ -67,6 +71,14 @@ class Colors:
|
|
|
67
71
|
return color + text + "\x1B[0m" # (normal)
|
|
68
72
|
return text
|
|
69
73
|
|
|
74
|
+
def disable(self) -> None:
|
|
75
|
+
'''Clears all color str in class Colors, to prevent print() methods that use
|
|
76
|
+
util.Colors from printing color'''
|
|
77
|
+
for x in dir(self):
|
|
78
|
+
if not callable(getattr(self, x, None)) and isinstance(x, str) and \
|
|
79
|
+
not x.startswith('_'):
|
|
80
|
+
setattr(self, x, '')
|
|
81
|
+
|
|
70
82
|
|
|
71
83
|
def safe_emoji(emoji: str, default: str = '') -> str:
|
|
72
84
|
'''Returns emoji character if args['emoji'] is True'''
|
|
@@ -421,7 +433,7 @@ def get_argparser_short_help(parser: object = None) -> str:
|
|
|
421
433
|
break
|
|
422
434
|
|
|
423
435
|
# skip the line that says 'options:', replace with the progname:
|
|
424
|
-
return f'{parser.prog}
|
|
436
|
+
return f'{Colors.cyan}{parser.prog}:{Colors.normal}\n' + '\n'.join(full_lines[lineno + 1:])
|
|
425
437
|
|
|
426
438
|
|
|
427
439
|
def process_token(arg: list) -> bool:
|
|
@@ -533,13 +545,14 @@ def process_tokens( # pylint: disable=too-many-branches
|
|
|
533
545
|
global debug_level # pylint: disable=global-statement
|
|
534
546
|
debug_level = 0
|
|
535
547
|
|
|
536
|
-
# Deal with --debug, --debug-level, --env-file, -f/--input-file(s)
|
|
537
|
-
# for now put dot-f file contents in front of of tokens, do this first
|
|
538
|
-
# separate custom argparser:
|
|
548
|
+
# Deal with --version, --debug, --debug-level, --env-file, -f/--input-file(s)
|
|
549
|
+
# tokens first, for now put dot-f file contents in front of of tokens, do this first
|
|
550
|
+
# in a separate custom argparser:
|
|
539
551
|
bool_action_kwargs = get_argparse_bool_action_kwargs()
|
|
540
552
|
parser = argparse.ArgumentParser(
|
|
541
553
|
prog='opencos -f/--input-file', add_help=False, allow_abbrev=False
|
|
542
554
|
)
|
|
555
|
+
parser.add_argument('--version', default=False, action='store_true')
|
|
543
556
|
parser.add_argument('--debug', **bool_action_kwargs,
|
|
544
557
|
help='Display additional debug messaging level 1 or higher')
|
|
545
558
|
parser.add_argument('--debug-level', type=int, default=0,
|
|
@@ -560,6 +573,10 @@ def process_tokens( # pylint: disable=too-many-branches
|
|
|
560
573
|
except argparse.ArgumentError:
|
|
561
574
|
error(f'util -f/--input-file, problem attempting to parse_known_args for: {tokens}')
|
|
562
575
|
|
|
576
|
+
if parsed.version:
|
|
577
|
+
# Stop processing, return nothing, caller can print version and exit.
|
|
578
|
+
return parsed, []
|
|
579
|
+
|
|
563
580
|
process_debug_args(parsed=parsed)
|
|
564
581
|
debug(f'util.process_tokens: {parsed=} {unparsed=} from: {tokens}')
|
|
565
582
|
|
|
@@ -599,6 +616,9 @@ def process_tokens( # pylint: disable=too-many-branches
|
|
|
599
616
|
warning(f'python error, nested -f/--input-file(s) {parsed.input_file} should',
|
|
600
617
|
'have already been resolved')
|
|
601
618
|
|
|
619
|
+
if not parsed.color:
|
|
620
|
+
Colors.disable(Colors) # strip strings in Colors class
|
|
621
|
+
|
|
602
622
|
# clear existing artifacts dicts (mostly for pytests repeatedly calling eda.main),
|
|
603
623
|
# set artifacts.enabled based on args['artifacts-json']
|
|
604
624
|
artifacts.reset(enable=parsed.artifacts_json)
|
|
@@ -836,7 +856,9 @@ def error(
|
|
|
836
856
|
global max_error_code # pylint: disable=global-statement
|
|
837
857
|
|
|
838
858
|
if start is None:
|
|
839
|
-
start = "ERROR:
|
|
859
|
+
start = f"{Colors.bold}ERROR:{Colors.normal}{Colors.red} "
|
|
860
|
+
if progname_in_message:
|
|
861
|
+
start += f"[{progname}] "
|
|
840
862
|
start += safe_emoji("❌ ")
|
|
841
863
|
args['errors'] += 1
|
|
842
864
|
max_error_code = max(max_error_code, error_code)
|
|
@@ -6,7 +6,9 @@ import subprocess
|
|
|
6
6
|
import sys
|
|
7
7
|
|
|
8
8
|
import psutil
|
|
9
|
+
from opencos import util
|
|
9
10
|
from opencos.util import debug, error, info, warning, progname, global_log
|
|
11
|
+
from opencos.utils.str_helpers import strip_ansi_color
|
|
10
12
|
|
|
11
13
|
IS_WINDOWS = sys.platform.startswith('win')
|
|
12
14
|
|
|
@@ -68,7 +70,7 @@ def subprocess_run_background( # pylint: disable=too-many-branches
|
|
|
68
70
|
|
|
69
71
|
proc_kwargs = {'shell': shell,
|
|
70
72
|
'stdout': subprocess.PIPE,
|
|
71
|
-
'stderr': subprocess.STDOUT
|
|
73
|
+
'stderr': subprocess.STDOUT
|
|
72
74
|
}
|
|
73
75
|
if work_dir:
|
|
74
76
|
proc_kwargs['cwd'] = work_dir
|
|
@@ -101,14 +103,29 @@ def subprocess_run_background( # pylint: disable=too-many-branches
|
|
|
101
103
|
error(f'Unable to open file "{tee_fpath}" for writing, {e}')
|
|
102
104
|
|
|
103
105
|
for line in iter(proc.stdout.readline, b''):
|
|
104
|
-
line = line.
|
|
106
|
+
line = line.decode("utf-8", errors="replace") # leave \n intact
|
|
107
|
+
|
|
108
|
+
# Since we don't control what the subprocess command did, if it
|
|
109
|
+
# thinks we support color, but user ran with --no-color, we need to strip ANSI colors:
|
|
110
|
+
if not util.args['color']:
|
|
111
|
+
line = strip_ansi_color(line)
|
|
112
|
+
|
|
113
|
+
# Print the line with color, if --color:
|
|
105
114
|
if not background:
|
|
106
|
-
print(line)
|
|
115
|
+
print(line, end='')
|
|
116
|
+
|
|
117
|
+
# for all logs, and the returned stdout str, if we haven't stripped color yet,
|
|
118
|
+
# we need to now, before writing to tee_fpath_f, or to global_log:
|
|
119
|
+
if util.args['color']:
|
|
120
|
+
line = strip_ansi_color(line)
|
|
121
|
+
line = line.replace('\r', '') # remove CR
|
|
122
|
+
|
|
107
123
|
if tee_fpath_f:
|
|
108
|
-
tee_fpath_f.write(line
|
|
124
|
+
tee_fpath_f.write(line)
|
|
109
125
|
if global_log.file:
|
|
110
|
-
|
|
111
|
-
|
|
126
|
+
# directly write to file handle, avoid util.UtilLogger.write(line, end='')
|
|
127
|
+
global_log.file.write(line)
|
|
128
|
+
stdout += line
|
|
112
129
|
|
|
113
130
|
proc.communicate()
|
|
114
131
|
remove_completed_parent_pid(proc.pid)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: opencos-eda
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.7
|
|
4
4
|
Summary: A simple Python package for wrapping RTL simuliatons and synthesis
|
|
5
5
|
Author-email: Simon Sabato <simon@cognichip.ai>, Drew Ranck <drew@cognichip.ai>
|
|
6
6
|
Project-URL: Homepage, https://github.com/cognichip/opencos
|
|
@@ -17,11 +17,13 @@ Requires-Dist: toml>=0.10.2
|
|
|
17
17
|
Requires-Dist: yamllint>=1.35.1
|
|
18
18
|
Requires-Dist: PySerial>=3.5
|
|
19
19
|
Requires-Dist: supports_color>=0.2.0
|
|
20
|
-
Requires-Dist: cocotb>=2.0
|
|
21
|
-
Requires-Dist: pytest>=8.3.5
|
|
22
|
-
Requires-Dist: coverage>=7.6.1
|
|
23
20
|
Provides-Extra: dev
|
|
24
21
|
Requires-Dist: pylint>=3.0.0; extra == "dev"
|
|
22
|
+
Requires-Dist: pytest>=8.3.5; extra == "dev"
|
|
23
|
+
Provides-Extra: cocotb
|
|
24
|
+
Requires-Dist: cocotb>=2.0; extra == "cocotb"
|
|
25
|
+
Requires-Dist: pytest>=8.3.5; extra == "cocotb"
|
|
26
|
+
Requires-Dist: coverage>=7.6.1; extra == "cocotb"
|
|
25
27
|
Provides-Extra: docs
|
|
26
28
|
Requires-Dist: mkdocs; extra == "docs"
|
|
27
29
|
Requires-Dist: mkdocs-material; extra == "docs"
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
[project]
|
|
4
4
|
name = "opencos-eda"
|
|
5
|
-
version = "0.3.
|
|
5
|
+
version = "0.3.7"
|
|
6
6
|
dependencies = [
|
|
7
7
|
# opencos/eda.py dependencies
|
|
8
8
|
"mergedeep >= 1.3.4",
|
|
@@ -16,10 +16,6 @@ dependencies = [
|
|
|
16
16
|
# opencos/oc_cli.py dependencies
|
|
17
17
|
"PySerial >= 3.5",
|
|
18
18
|
"supports_color >= 0.2.0",
|
|
19
|
-
# cocotb, pytest + coverage no longer optional.
|
|
20
|
-
"cocotb >= 2.0",
|
|
21
|
-
"pytest >= 8.3.5",
|
|
22
|
-
"coverage >= 7.6.1", # For python3.10, we could go >= 7.11.3
|
|
23
19
|
]
|
|
24
20
|
requires-python = ">= 3.8"
|
|
25
21
|
authors = [
|
|
@@ -31,6 +27,14 @@ description = "A simple Python package for wrapping RTL simuliatons and synthesi
|
|
|
31
27
|
[project.optional-dependencies]
|
|
32
28
|
dev = [
|
|
33
29
|
"pylint >= 3.0.0",
|
|
30
|
+
"pytest >= 8.3.5",
|
|
31
|
+
]
|
|
32
|
+
cocotb = [
|
|
33
|
+
# cocotb is optional. You may want to set env COCOTB_IGNORE_PYTHON_REQUIRES
|
|
34
|
+
# cocotb set a maximum python version
|
|
35
|
+
"cocotb >= 2.0",
|
|
36
|
+
"pytest >= 8.3.5",
|
|
37
|
+
"coverage >= 7.6.1", # For python3.10, we could go >= 7.11.3
|
|
34
38
|
]
|
|
35
39
|
docs = [
|
|
36
40
|
"mkdocs",
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|