opencos-eda 0.3.6__py3-none-any.whl → 0.3.8__py3-none-any.whl
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/commands/flist.py +2 -2
- opencos/commands/multi.py +4 -3
- opencos/commands/sim.py +73 -14
- opencos/eda.py +22 -20
- opencos/eda_base.py +227 -82
- opencos/eda_config.py +1 -0
- opencos/eda_config_defaults.yml +28 -3
- opencos/tests/deps_files/command_order/DEPS.yml +13 -0
- opencos/tests/helpers.py +9 -5
- opencos/tests/test_eda.py +1 -1
- opencos/tests/test_tools.py +62 -33
- opencos/tools/iverilog.py +2 -2
- opencos/tools/riviera.py +33 -2
- opencos/tools/slang.py +24 -0
- opencos/tools/surelog.py +22 -0
- opencos/tools/verilator.py +12 -7
- opencos/tools/vivado.py +4 -0
- opencos/tools/yosys.py +3 -3
- opencos/util.py +29 -6
- opencos/utils/str_helpers.py +4 -4
- opencos/utils/subprocess_helpers.py +23 -6
- {opencos_eda-0.3.6.dist-info → opencos_eda-0.3.8.dist-info}/METADATA +6 -4
- {opencos_eda-0.3.6.dist-info → opencos_eda-0.3.8.dist-info}/RECORD +28 -28
- {opencos_eda-0.3.6.dist-info → opencos_eda-0.3.8.dist-info}/WHEEL +0 -0
- {opencos_eda-0.3.6.dist-info → opencos_eda-0.3.8.dist-info}/entry_points.txt +0 -0
- {opencos_eda-0.3.6.dist-info → opencos_eda-0.3.8.dist-info}/licenses/LICENSE +0 -0
- {opencos_eda-0.3.6.dist-info → opencos_eda-0.3.8.dist-info}/licenses/LICENSE.spdx +0 -0
- {opencos_eda-0.3.6.dist-info → opencos_eda-0.3.8.dist-info}/top_level.txt +0 -0
opencos/commands/flist.py
CHANGED
|
@@ -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 = "{"
|
opencos/commands/multi.py
CHANGED
|
@@ -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}')
|
opencos/commands/sim.py
CHANGED
|
@@ -44,7 +44,7 @@ def parameters_dict_get_command_list(params: dict, arg_prefix: str = '-G') -> li
|
|
|
44
44
|
return ret_list
|
|
45
45
|
|
|
46
46
|
|
|
47
|
-
class CommandSim(CommandDesign):
|
|
47
|
+
class CommandSim(CommandDesign): # pylint: disable=too-many-public-methods
|
|
48
48
|
'''Base class command handler for: eda sim ...'''
|
|
49
49
|
|
|
50
50
|
CHECK_REQUIRES = [Tool] # Used by check_command_handler_cls()
|
|
@@ -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,
|
|
@@ -72,24 +72,39 @@ class CommandSim(CommandDesign):
|
|
|
72
72
|
'optimize': False,
|
|
73
73
|
'log-bad-strings': ['ERROR: ', 'FATAL: ', 'Error: ', 'Fatal: '],
|
|
74
74
|
'log-must-strings': [],
|
|
75
|
+
'log-warning-strings': ['WARNING: ', 'Warning: '],
|
|
76
|
+
'uvm': False,
|
|
77
|
+
'uvm-version': '1.2',
|
|
75
78
|
# verilate-args: list of args you can only pass to Verilator,
|
|
76
79
|
# not used by other simulators, so these can go in DEPS files for custom things
|
|
77
80
|
# like -CFLAGS -O0, etc.
|
|
78
81
|
'verilate-args': [],
|
|
79
82
|
})
|
|
80
83
|
self.args_help.update({
|
|
84
|
+
'pre-sim-tcl': (
|
|
85
|
+
'Tool dependent, TCL file to run prior to simulation (if tool supports TCL)'
|
|
86
|
+
),
|
|
81
87
|
'compile-args': 'args added to sim/elab "compile" step',
|
|
88
|
+
'compile-waivers': (
|
|
89
|
+
'simulation tool depenent waivers by name, applied at compilation step'
|
|
90
|
+
),
|
|
82
91
|
'coverage': 'attempt to run coverage steps on the compile/elab/simulation',
|
|
83
92
|
'elab-args': 'args added to sim/elab "elaboration" step, if required by tool',
|
|
84
93
|
'log-bad-strings': 'strings that if present in the log will fail the simulation',
|
|
85
94
|
'log-must-strings': ('strings that are required by the log to not-fail the simulation.'
|
|
86
95
|
' Some tools use these at only certain phases'
|
|
87
96
|
' (compile/elab/sim).'),
|
|
97
|
+
'log-warning-strings': ('strings that if present will be counted in the total tool'
|
|
98
|
+
' warnings at end of simulation'),
|
|
88
99
|
'pass-pattern': ('Additional string required to pass a simulation, appends to'
|
|
89
100
|
' log-must-strings'),
|
|
90
101
|
'sim-args': 'args added to final "simulation" step',
|
|
91
102
|
'sim-plusargs': ('"simulation" step run-time args passed to tool, these can also'
|
|
92
103
|
' be set using --sim-plusargs=name[=value], or simply +name[=value]'),
|
|
104
|
+
'sim-library': 'named libraries added to simulation, tool dependent',
|
|
105
|
+
'sim-waivers': (
|
|
106
|
+
'simulation tool depenent waivers by name, applied at "simulation" step'
|
|
107
|
+
),
|
|
93
108
|
'stop-before-compile': ('Create work-dir sh scripts for compile/elab/simulate, but do'
|
|
94
109
|
' not run them.'),
|
|
95
110
|
'stop-after-compile': 'Create work-dir sh scripts, but only run the compile step.',
|
|
@@ -97,17 +112,26 @@ class CommandSim(CommandDesign):
|
|
|
97
112
|
' simulation step.'),
|
|
98
113
|
'top': 'Name of topmost Verilog/SystemVerilog module, or VHDL entity',
|
|
99
114
|
'verilate-args': ('args added to "compile" step in Verilator simulation'
|
|
100
|
-
' (for --tool=verilator)'),
|
|
115
|
+
' (for --tool=verilator) these are identical to --compile-args'),
|
|
101
116
|
'waves': 'Include waveforms, if possible for tool',
|
|
102
117
|
'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
118
|
'test-mode': ('stops the command early without executing, if --gui is present will'
|
|
105
|
-
' instead test without spawning gui')
|
|
119
|
+
' instead test without spawning gui'),
|
|
120
|
+
'uvm': (
|
|
121
|
+
'Attempts to support UVM (aka 1.2 if --uvm-version=1.2) for the'
|
|
122
|
+
' the simulation tool. May add libraries for uvm, tool dependent.'
|
|
123
|
+
),
|
|
124
|
+
'uvm-version': (
|
|
125
|
+
'Used if --uvm is set, for example --uvm-version=1.2'
|
|
126
|
+
),
|
|
127
|
+
|
|
128
|
+
})
|
|
106
129
|
|
|
130
|
+
self.args_kwargs.update({
|
|
131
|
+
'uvm-version': { 'choices': ['1.2'] },
|
|
107
132
|
})
|
|
108
133
|
|
|
109
134
|
|
|
110
|
-
self.args['verilate-args'] = []
|
|
111
135
|
|
|
112
136
|
def process_parameters_get_list(self, arg_prefix: str = '-G') -> list:
|
|
113
137
|
'''Returns list (suitable command list for shell or for tool) from self.parameters'''
|
|
@@ -173,14 +197,14 @@ class CommandSim(CommandDesign):
|
|
|
173
197
|
|
|
174
198
|
# Collect (overwrite CommandSim) the bad and must strings, if present,
|
|
175
199
|
# from our config.tools.verilator:
|
|
176
|
-
for tool_config_key in
|
|
200
|
+
for tool_config_key in ('log-bad-strings', 'log-must-strings', 'log-warning-strings'):
|
|
177
201
|
if len(self.tool_config.get(tool_config_key, [])) > 0:
|
|
178
202
|
self.args[tool_config_key] = self.tool_config.get(tool_config_key, [])
|
|
179
203
|
|
|
180
204
|
|
|
181
205
|
# Methods that derived classes may override:
|
|
182
206
|
|
|
183
|
-
def run_commands_check_logs( # pylint: disable=dangerous-default-value
|
|
207
|
+
def run_commands_check_logs( # pylint: disable=dangerous-default-value,too-many-locals
|
|
184
208
|
self, commands: list , check_logs: bool = True, log_filename=None,
|
|
185
209
|
bad_strings: list = [],
|
|
186
210
|
must_strings: list = [],
|
|
@@ -188,7 +212,10 @@ class CommandSim(CommandDesign):
|
|
|
188
212
|
) -> None:
|
|
189
213
|
'''Returns None, runs all commands (each element is a list) and checks logs
|
|
190
214
|
|
|
191
|
-
for bad-strings and must-strings (args or class member vars)
|
|
215
|
+
for bad-strings and must-strings (args or class member vars).
|
|
216
|
+
|
|
217
|
+
Will add any bad_strings or tool log-warning-strings to self, so tool
|
|
218
|
+
related warning/error counts can be reported later.
|
|
192
219
|
'''
|
|
193
220
|
|
|
194
221
|
for obj in commands:
|
|
@@ -203,7 +230,7 @@ class CommandSim(CommandDesign):
|
|
|
203
230
|
if not work_dir:
|
|
204
231
|
work_dir = self.args['work-dir']
|
|
205
232
|
|
|
206
|
-
util.debug(f'run_commands_check_logs: {clist=}, {tee_fpath=}')
|
|
233
|
+
util.debug(f'run_commands_check_logs: {clist=}, {tee_fpath=}, {log_filename=}')
|
|
207
234
|
|
|
208
235
|
log_fname = None
|
|
209
236
|
if tee_fpath:
|
|
@@ -211,11 +238,18 @@ class CommandSim(CommandDesign):
|
|
|
211
238
|
if log_filename:
|
|
212
239
|
log_fname = log_filename
|
|
213
240
|
|
|
214
|
-
|
|
215
|
-
_, stdout,
|
|
216
|
-
work_dir=work_dir, command_list=clist, tee_fpath=tee_fpath
|
|
241
|
+
# track the retcode because we run stop_on_error=False (to count warnings/errors)
|
|
242
|
+
_, stdout, retcode = self.exec(
|
|
243
|
+
work_dir=work_dir, command_list=clist, tee_fpath=tee_fpath,
|
|
244
|
+
stop_on_error=False,
|
|
217
245
|
)
|
|
218
246
|
|
|
247
|
+
if check_logs and not log_fname and bad_strings:
|
|
248
|
+
util.warning(
|
|
249
|
+
f'{self.get_info_job_name()}: check_logs=True but no log file set',
|
|
250
|
+
f'(run --debug for more info), for command: {" ".join(clist)}'
|
|
251
|
+
)
|
|
252
|
+
|
|
219
253
|
if check_logs and log_fname:
|
|
220
254
|
# Note this call will check on stdout if not GUI, not opening the log_fname,
|
|
221
255
|
# but if this is GUI we normally lose stdout and have to open the log.
|
|
@@ -225,6 +259,7 @@ class CommandSim(CommandDesign):
|
|
|
225
259
|
file_contents_str = stdout
|
|
226
260
|
|
|
227
261
|
self.check_logs_for_errors(
|
|
262
|
+
sim_retcode=retcode,
|
|
228
263
|
filename=os.path.join(work_dir, log_fname),
|
|
229
264
|
file_contents_str=file_contents_str,
|
|
230
265
|
bad_strings=bad_strings, must_strings=must_strings,
|
|
@@ -235,6 +270,10 @@ class CommandSim(CommandDesign):
|
|
|
235
270
|
name=os.path.join(work_dir, log_fname),
|
|
236
271
|
typ='text', description='Simulator stdout/stderr log file'
|
|
237
272
|
)
|
|
273
|
+
if retcode:
|
|
274
|
+
self.error(
|
|
275
|
+
f"{self.get_info_job_name()}: exec returned with error code: {retcode}"
|
|
276
|
+
)
|
|
238
277
|
|
|
239
278
|
def do_export(self) -> None:
|
|
240
279
|
'''CommandSim helper for handling args --export*
|
|
@@ -328,9 +367,13 @@ class CommandSim(CommandDesign):
|
|
|
328
367
|
'''
|
|
329
368
|
return
|
|
330
369
|
|
|
370
|
+
|
|
331
371
|
def check_logs_for_errors( # pylint: disable=dangerous-default-value,too-many-locals,too-many-branches
|
|
332
|
-
self,
|
|
372
|
+
self,
|
|
373
|
+
sim_retcode: int = 0,
|
|
374
|
+
filename: str = '', file_contents_str: str = '',
|
|
333
375
|
bad_strings: list = [], must_strings: list = [],
|
|
376
|
+
warning_strings: list = [],
|
|
334
377
|
use_bad_strings: bool = True, use_must_strings: bool = True
|
|
335
378
|
) -> None:
|
|
336
379
|
'''Returns None, checks logs using args bad_strings, must_strings,
|
|
@@ -349,6 +392,8 @@ class CommandSim(CommandDesign):
|
|
|
349
392
|
if use_must_strings:
|
|
350
393
|
_must_strings = must_strings + self.args.get('log-must-strings', [])
|
|
351
394
|
|
|
395
|
+
_warning_strings = warning_strings + self.args.get('log-warning-strings', [])
|
|
396
|
+
|
|
352
397
|
if self.args['pass-pattern'] != "":
|
|
353
398
|
_must_strings.append(self.args['pass-pattern'])
|
|
354
399
|
|
|
@@ -380,6 +425,20 @@ class CommandSim(CommandDesign):
|
|
|
380
425
|
self.error(f'sim.check_logs_for_errors: {log_fpath=} does not exist, and no',
|
|
381
426
|
'file_contents_str exists to check')
|
|
382
427
|
|
|
428
|
+
self.update_tool_warn_err_counts_from_log_lines(
|
|
429
|
+
log_lines=lines, bad_strings=_bad_strings, warning_strings=_warning_strings
|
|
430
|
+
)
|
|
431
|
+
|
|
432
|
+
if isinstance(self, Tool):
|
|
433
|
+
self.report_tool_warn_error_counts()
|
|
434
|
+
|
|
435
|
+
if sim_retcode > 0:
|
|
436
|
+
# We have to update artifacts first, have the caller set the error.
|
|
437
|
+
# Skip the checking for bad strings or must strings, because we've already
|
|
438
|
+
# failed, but we did the important counting of Error and Warning lines above,
|
|
439
|
+
# and reported it.
|
|
440
|
+
return
|
|
441
|
+
|
|
383
442
|
if lines:
|
|
384
443
|
for lineno, line in enumerate(lines):
|
|
385
444
|
if any(must_str in line for must_str in _must_strings):
|
opencos/eda.py
CHANGED
|
@@ -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
|