opencos-eda 0.2.44__tar.gz → 0.2.46__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.2.44/opencos_eda.egg-info → opencos_eda-0.2.46}/PKG-INFO +1 -1
- {opencos_eda-0.2.44 → opencos_eda-0.2.46}/opencos/commands/__init__.py +4 -0
- opencos_eda-0.2.46/opencos/commands/lec.py +103 -0
- opencos_eda-0.2.46/opencos/commands/shell.py +202 -0
- {opencos_eda-0.2.44 → opencos_eda-0.2.46}/opencos/commands/sim.py +1 -1
- {opencos_eda-0.2.44 → opencos_eda-0.2.46}/opencos/deps_helpers.py +15 -12
- {opencos_eda-0.2.44 → opencos_eda-0.2.46}/opencos/deps_schema.py +3 -0
- {opencos_eda-0.2.44 → opencos_eda-0.2.46}/opencos/eda.py +2 -0
- {opencos_eda-0.2.44 → opencos_eda-0.2.46}/opencos/eda_base.py +32 -15
- {opencos_eda-0.2.44 → opencos_eda-0.2.46}/opencos/eda_config_defaults.yml +17 -5
- {opencos_eda-0.2.44 → opencos_eda-0.2.46}/opencos/eda_extract_targets.py +13 -3
- {opencos_eda-0.2.44 → opencos_eda-0.2.46}/opencos/tests/helpers.py +2 -1
- {opencos_eda-0.2.44 → opencos_eda-0.2.46}/opencos/tests/test_deps_schema.py +3 -1
- {opencos_eda-0.2.44 → opencos_eda-0.2.46}/opencos/tests/test_eda.py +19 -9
- {opencos_eda-0.2.44 → opencos_eda-0.2.46}/opencos/tools/invio_yosys.py +7 -7
- {opencos_eda-0.2.44 → opencos_eda-0.2.46}/opencos/tools/slang_yosys.py +29 -38
- {opencos_eda-0.2.44 → opencos_eda-0.2.46}/opencos/tools/tabbycad_yosys.py +1 -1
- {opencos_eda-0.2.44 → opencos_eda-0.2.46}/opencos/tools/vivado.py +8 -7
- opencos_eda-0.2.46/opencos/tools/yosys.py +679 -0
- {opencos_eda-0.2.44 → opencos_eda-0.2.46}/opencos/util.py +14 -6
- {opencos_eda-0.2.44 → opencos_eda-0.2.46/opencos_eda.egg-info}/PKG-INFO +1 -1
- {opencos_eda-0.2.44 → opencos_eda-0.2.46}/opencos_eda.egg-info/SOURCES.txt +2 -0
- {opencos_eda-0.2.44 → opencos_eda-0.2.46}/pyproject.toml +1 -1
- opencos_eda-0.2.44/opencos/tools/yosys.py +0 -258
- {opencos_eda-0.2.44 → opencos_eda-0.2.46}/LICENSE +0 -0
- {opencos_eda-0.2.44 → opencos_eda-0.2.46}/LICENSE.spdx +0 -0
- {opencos_eda-0.2.44 → opencos_eda-0.2.46}/README.md +0 -0
- {opencos_eda-0.2.44 → opencos_eda-0.2.46}/opencos/__init__.py +0 -0
- {opencos_eda-0.2.44 → opencos_eda-0.2.46}/opencos/_version.py +0 -0
- {opencos_eda-0.2.44 → opencos_eda-0.2.46}/opencos/_waves_pkg.sv +0 -0
- {opencos_eda-0.2.44 → opencos_eda-0.2.46}/opencos/commands/build.py +0 -0
- {opencos_eda-0.2.44 → opencos_eda-0.2.46}/opencos/commands/elab.py +0 -0
- {opencos_eda-0.2.44 → opencos_eda-0.2.46}/opencos/commands/export.py +0 -0
- {opencos_eda-0.2.44 → opencos_eda-0.2.46}/opencos/commands/flist.py +0 -0
- {opencos_eda-0.2.44 → opencos_eda-0.2.46}/opencos/commands/multi.py +0 -0
- {opencos_eda-0.2.44 → opencos_eda-0.2.46}/opencos/commands/open.py +0 -0
- {opencos_eda-0.2.44 → opencos_eda-0.2.46}/opencos/commands/proj.py +0 -0
- {opencos_eda-0.2.44 → opencos_eda-0.2.46}/opencos/commands/sweep.py +0 -0
- {opencos_eda-0.2.44 → opencos_eda-0.2.46}/opencos/commands/synth.py +0 -0
- {opencos_eda-0.2.44 → opencos_eda-0.2.46}/opencos/commands/targets.py +0 -0
- {opencos_eda-0.2.44 → opencos_eda-0.2.46}/opencos/commands/upload.py +0 -0
- {opencos_eda-0.2.44 → opencos_eda-0.2.46}/opencos/commands/waves.py +0 -0
- {opencos_eda-0.2.44 → opencos_eda-0.2.46}/opencos/eda_config.py +0 -0
- {opencos_eda-0.2.44 → opencos_eda-0.2.46}/opencos/eda_config_max_verilator_waivers.yml +0 -0
- {opencos_eda-0.2.44 → opencos_eda-0.2.46}/opencos/eda_config_reduced.yml +0 -0
- {opencos_eda-0.2.44 → opencos_eda-0.2.46}/opencos/eda_deps_bash_completion.bash +0 -0
- {opencos_eda-0.2.44 → opencos_eda-0.2.46}/opencos/eda_tool_helper.py +0 -0
- {opencos_eda-0.2.44 → opencos_eda-0.2.46}/opencos/export_helper.py +0 -0
- {opencos_eda-0.2.44 → opencos_eda-0.2.46}/opencos/export_json_convert.py +0 -0
- {opencos_eda-0.2.44 → opencos_eda-0.2.46}/opencos/files.py +0 -0
- {opencos_eda-0.2.44 → opencos_eda-0.2.46}/opencos/names.py +0 -0
- {opencos_eda-0.2.44 → opencos_eda-0.2.46}/opencos/oc_cli.py +0 -0
- {opencos_eda-0.2.44 → opencos_eda-0.2.46}/opencos/pcie.py +0 -0
- {opencos_eda-0.2.44 → opencos_eda-0.2.46}/opencos/peakrdl_cleanup.py +0 -0
- {opencos_eda-0.2.44 → opencos_eda-0.2.46}/opencos/seed.py +0 -0
- {opencos_eda-0.2.44 → opencos_eda-0.2.46}/opencos/tests/__init__.py +0 -0
- {opencos_eda-0.2.44 → opencos_eda-0.2.46}/opencos/tests/custom_config.yml +0 -0
- {opencos_eda-0.2.44 → opencos_eda-0.2.46}/opencos/tests/deps_files/command_order/DEPS.yml +0 -0
- {opencos_eda-0.2.44 → opencos_eda-0.2.46}/opencos/tests/deps_files/error_msgs/DEPS.yml +0 -0
- {opencos_eda-0.2.44 → opencos_eda-0.2.46}/opencos/tests/deps_files/iverilog_test/DEPS.yml +0 -0
- {opencos_eda-0.2.44 → opencos_eda-0.2.46}/opencos/tests/deps_files/no_deps_here/DEPS.yml +0 -0
- {opencos_eda-0.2.44 → opencos_eda-0.2.46}/opencos/tests/deps_files/non_sv_reqs/DEPS.yml +0 -0
- {opencos_eda-0.2.44 → opencos_eda-0.2.46}/opencos/tests/deps_files/tags_with_tools/DEPS.yml +0 -0
- {opencos_eda-0.2.44 → opencos_eda-0.2.46}/opencos/tests/deps_files/test_err_fatal/DEPS.yml +0 -0
- {opencos_eda-0.2.44 → opencos_eda-0.2.46}/opencos/tests/test_build.py +0 -0
- {opencos_eda-0.2.44 → opencos_eda-0.2.46}/opencos/tests/test_deps_helpers.py +0 -0
- {opencos_eda-0.2.44 → opencos_eda-0.2.46}/opencos/tests/test_eda_elab.py +0 -0
- {opencos_eda-0.2.44 → opencos_eda-0.2.46}/opencos/tests/test_eda_synth.py +0 -0
- {opencos_eda-0.2.44 → opencos_eda-0.2.46}/opencos/tests/test_oc_cli.py +0 -0
- {opencos_eda-0.2.44 → opencos_eda-0.2.46}/opencos/tests/test_tools.py +0 -0
- {opencos_eda-0.2.44 → opencos_eda-0.2.46}/opencos/tools/__init__.py +0 -0
- {opencos_eda-0.2.44 → opencos_eda-0.2.46}/opencos/tools/invio.py +0 -0
- {opencos_eda-0.2.44 → opencos_eda-0.2.46}/opencos/tools/invio_helpers.py +0 -0
- {opencos_eda-0.2.44 → opencos_eda-0.2.46}/opencos/tools/iverilog.py +0 -0
- {opencos_eda-0.2.44 → opencos_eda-0.2.46}/opencos/tools/modelsim_ase.py +0 -0
- {opencos_eda-0.2.44 → opencos_eda-0.2.46}/opencos/tools/questa.py +0 -0
- {opencos_eda-0.2.44 → opencos_eda-0.2.46}/opencos/tools/riviera.py +0 -0
- {opencos_eda-0.2.44 → opencos_eda-0.2.46}/opencos/tools/slang.py +0 -0
- {opencos_eda-0.2.44 → opencos_eda-0.2.46}/opencos/tools/surelog.py +0 -0
- {opencos_eda-0.2.44 → opencos_eda-0.2.46}/opencos/tools/verilator.py +0 -0
- {opencos_eda-0.2.44 → opencos_eda-0.2.46}/opencos_eda.egg-info/dependency_links.txt +0 -0
- {opencos_eda-0.2.44 → opencos_eda-0.2.46}/opencos_eda.egg-info/entry_points.txt +0 -0
- {opencos_eda-0.2.44 → opencos_eda-0.2.46}/opencos_eda.egg-info/requires.txt +0 -0
- {opencos_eda-0.2.44 → opencos_eda-0.2.46}/opencos_eda.egg-info/top_level.txt +0 -0
- {opencos_eda-0.2.44 → opencos_eda-0.2.46}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: opencos-eda
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.46
|
|
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,7 +17,9 @@ from .sweep import CommandSweep
|
|
|
17
17
|
from .synth import CommandSynth
|
|
18
18
|
from .upload import CommandUpload
|
|
19
19
|
from .waves import CommandWaves
|
|
20
|
+
from .shell import CommandShell
|
|
20
21
|
from .targets import CommandTargets
|
|
22
|
+
from .lec import CommandLec
|
|
21
23
|
|
|
22
24
|
__all__ = [
|
|
23
25
|
'CommandBuild',
|
|
@@ -33,5 +35,7 @@ __all__ = [
|
|
|
33
35
|
'CommandToolsMulti',
|
|
34
36
|
'CommandUpload',
|
|
35
37
|
'CommandWaves',
|
|
38
|
+
'CommandShell',
|
|
36
39
|
'CommandTargets',
|
|
40
|
+
'CommandLec',
|
|
37
41
|
]
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
'''opencos.commands.lec - Base class command handler for: eda lec ...
|
|
2
|
+
|
|
3
|
+
Intended to be overriden by Tool based classes (such as CommandLecYosys, etc)
|
|
4
|
+
'''
|
|
5
|
+
|
|
6
|
+
# Note - similar code waiver, tricky to eliminate it with inheritance when
|
|
7
|
+
# calling reusable methods.
|
|
8
|
+
# pylint: disable=R0801
|
|
9
|
+
|
|
10
|
+
import os
|
|
11
|
+
|
|
12
|
+
from opencos import eda_extract_targets
|
|
13
|
+
from opencos.eda_base import CommandDesign, Tool
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class CommandLec(CommandDesign):
|
|
17
|
+
'''Base class command handler for: eda lec ...'''
|
|
18
|
+
|
|
19
|
+
CHECK_REQUIRES = [Tool]
|
|
20
|
+
error_on_no_files_or_targets = False
|
|
21
|
+
error_on_missing_top = False # we'll override it.
|
|
22
|
+
|
|
23
|
+
command_name = 'lec'
|
|
24
|
+
|
|
25
|
+
def __init__(self, config: dict):
|
|
26
|
+
CommandDesign.__init__(self, config=config)
|
|
27
|
+
self.args.update({
|
|
28
|
+
'designs': [],
|
|
29
|
+
'synth': True,
|
|
30
|
+
})
|
|
31
|
+
self.args_help.update({
|
|
32
|
+
'designs': (
|
|
33
|
+
'Set the two LEC comparison designs: --designs=<target1> --designs=<target2>,'
|
|
34
|
+
' use this arg twice'
|
|
35
|
+
),
|
|
36
|
+
'synth': 'run synthesis on the two designs prior to running LEC',
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
self.synth_design_verilog_fpaths = ['', '']
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def do_it(self) -> None:
|
|
43
|
+
'''Common do_it() method that child classes can use prior to customization'''
|
|
44
|
+
|
|
45
|
+
# set_tool_defines() is from class Tool. Since that is not inherited yet, but
|
|
46
|
+
# should be by any handlers, check on the existence of set_tool_defines, and
|
|
47
|
+
# error if not present.
|
|
48
|
+
if not all(isinstance(self, x) for x in self.CHECK_REQUIRES):
|
|
49
|
+
self.error('CommandLec.do_it() requires a Tool to be in parent classes, but none is.',
|
|
50
|
+
f'{self.CHECK_REQUIRES=}')
|
|
51
|
+
return
|
|
52
|
+
|
|
53
|
+
# add defines for this job from Tool class if present
|
|
54
|
+
self.command_safe_set_tool_defines() # (Command.command_safe_set_tool_defines)
|
|
55
|
+
|
|
56
|
+
# dump our config to work-dir for debug
|
|
57
|
+
self.write_eda_config_and_args()
|
|
58
|
+
|
|
59
|
+
# Note - we do not support --export in LEC.
|
|
60
|
+
# Derived classes can do the rest, can call CommandLec.do_it(self) as a first step.
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def process_tokens(self, tokens: list, process_all: bool = True,
|
|
64
|
+
pwd: str = os.getcwd()) -> list:
|
|
65
|
+
unparsed = CommandDesign.process_tokens(
|
|
66
|
+
self, tokens=tokens, process_all=process_all, pwd=pwd
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
if self.status_any_error():
|
|
70
|
+
return unparsed
|
|
71
|
+
|
|
72
|
+
# add defines for this job type
|
|
73
|
+
if not self.args['top']:
|
|
74
|
+
self.args['top'] = 'eda.lec'
|
|
75
|
+
|
|
76
|
+
# we require there to be two --designs set.
|
|
77
|
+
if not self.args['designs'] and len(self.args['designs']) != 2:
|
|
78
|
+
self.error('Requires two designs via --designs=<target1> --designs=<target2>',
|
|
79
|
+
f'designs={self.args["designs"]}')
|
|
80
|
+
return []
|
|
81
|
+
|
|
82
|
+
# Before we do anything else, make sure the two designs actually exist.
|
|
83
|
+
for design in self.args['designs']:
|
|
84
|
+
# maybe it's a file?
|
|
85
|
+
if os.path.isfile(design):
|
|
86
|
+
pass
|
|
87
|
+
elif design in eda_extract_targets.get_targets(partial_paths=[design], base_path=pwd):
|
|
88
|
+
pass
|
|
89
|
+
else:
|
|
90
|
+
self.error(f'--designs={design}, value is not a file or target')
|
|
91
|
+
|
|
92
|
+
# create our work dir
|
|
93
|
+
self.create_work_dir()
|
|
94
|
+
self.run_dep_commands()
|
|
95
|
+
self.do_it()
|
|
96
|
+
return unparsed
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def get_synth_result_fpath(self, target: str) -> str:
|
|
100
|
+
'''Derived classes must define. Given a synth target for one of the two
|
|
101
|
+
|
|
102
|
+
designs to compare, return the location of the synthesis file'''
|
|
103
|
+
raise NotImplementedError
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
'''opencos.commands.shell - Base class command handler for: eda shell ...
|
|
2
|
+
|
|
3
|
+
Not intended to be overriden by Tool based classes.'''
|
|
4
|
+
|
|
5
|
+
# Note - tricky to eliminate it with inheritance when calling reusable methods.
|
|
6
|
+
# pylint: disable=R0801
|
|
7
|
+
|
|
8
|
+
# TODO(drew): clean up CommandShell.check_logs_for_errors and CommandShell.run_commands_check_logs
|
|
9
|
+
# These also share a lot with CommandSim.* methods, so consider refactoring to share code,
|
|
10
|
+
# for example, CommandShell.do_export could move to CommandDesign, and derived classes
|
|
11
|
+
# set the allow-listed args to pass to export.
|
|
12
|
+
# pylint: disable=too-many-arguments,too-many-positional-arguments
|
|
13
|
+
|
|
14
|
+
import os
|
|
15
|
+
|
|
16
|
+
from opencos import util, export_helper
|
|
17
|
+
from opencos.eda_base import CommandDesign
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class CommandShell(CommandDesign):
|
|
21
|
+
'''Base class command handler for: eda sim ...'''
|
|
22
|
+
|
|
23
|
+
command_name = 'shell'
|
|
24
|
+
|
|
25
|
+
def __init__(self, config: dict):
|
|
26
|
+
CommandDesign.__init__(self, config=config)
|
|
27
|
+
self.args.update({
|
|
28
|
+
'pass-pattern': "",
|
|
29
|
+
'log-bad-strings': [],
|
|
30
|
+
'log-must-strings': [],
|
|
31
|
+
})
|
|
32
|
+
self.args_help.update({
|
|
33
|
+
'log-bad-strings': (
|
|
34
|
+
'strings that if present in the log will cause an `eda shell` error'
|
|
35
|
+
),
|
|
36
|
+
'log-must-strings': (
|
|
37
|
+
'strings that are required by the log to not-fail the `eda shell` call'
|
|
38
|
+
),
|
|
39
|
+
'pass-pattern': (
|
|
40
|
+
'Additional string required to pass the `eda shell` call, appends to'
|
|
41
|
+
' log-must-strings'
|
|
42
|
+
),
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
def process_tokens(self, tokens: list, process_all: bool = True,
|
|
46
|
+
pwd: str = os.getcwd()) -> list:
|
|
47
|
+
unparsed = CommandDesign.process_tokens(
|
|
48
|
+
self, tokens=tokens, process_all=process_all, pwd=pwd
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
if self.status_any_error():
|
|
52
|
+
return unparsed
|
|
53
|
+
|
|
54
|
+
if self.args['top']:
|
|
55
|
+
# create our work dir
|
|
56
|
+
self.create_work_dir()
|
|
57
|
+
self.run_dep_commands()
|
|
58
|
+
self.do_it()
|
|
59
|
+
return unparsed
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def run_commands_check_logs( # pylint: disable=dangerous-default-value
|
|
63
|
+
self, commands: list , check_logs: bool = True, log_filename=None,
|
|
64
|
+
bad_strings: list = [],
|
|
65
|
+
must_strings: list = [],
|
|
66
|
+
use_bad_strings: bool = True, use_must_strings: bool = True
|
|
67
|
+
) -> None:
|
|
68
|
+
'''Returns None, runs all commands (each element is a list) and checks logs
|
|
69
|
+
|
|
70
|
+
for bad-strings and must-strings (args or class member vars)
|
|
71
|
+
'''
|
|
72
|
+
|
|
73
|
+
for obj in commands:
|
|
74
|
+
|
|
75
|
+
assert isinstance(obj, list), \
|
|
76
|
+
(f'{self.target=} command {obj=} is not a list or util.ShellCommandList,'
|
|
77
|
+
' not going to run it.')
|
|
78
|
+
|
|
79
|
+
clist = list(obj).copy()
|
|
80
|
+
tee_fpath = getattr(obj, 'tee_fpath', None)
|
|
81
|
+
|
|
82
|
+
util.debug(f'run_commands_check_logs: {clist=}, {tee_fpath=}')
|
|
83
|
+
|
|
84
|
+
log_fname = None
|
|
85
|
+
if tee_fpath:
|
|
86
|
+
log_fname = tee_fpath
|
|
87
|
+
if log_filename:
|
|
88
|
+
log_fname = log_filename
|
|
89
|
+
|
|
90
|
+
self.exec(work_dir=self.args['work-dir'], command_list=clist, tee_fpath=tee_fpath)
|
|
91
|
+
|
|
92
|
+
if check_logs and log_fname:
|
|
93
|
+
self.check_logs_for_errors(
|
|
94
|
+
filename=log_fname, bad_strings=bad_strings, must_strings=must_strings,
|
|
95
|
+
use_bad_strings=use_bad_strings, use_must_strings=use_must_strings
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def do_export(self) -> None:
|
|
100
|
+
'''CommandShell helper for handling args --export*
|
|
101
|
+
|
|
102
|
+
We allow commands such as: eda shell --export <target>
|
|
103
|
+
'''
|
|
104
|
+
|
|
105
|
+
out_dir = os.path.join(self.args['work-dir'], 'export')
|
|
106
|
+
|
|
107
|
+
target = self.target
|
|
108
|
+
if not target:
|
|
109
|
+
target = 'test'
|
|
110
|
+
|
|
111
|
+
export_obj = export_helper.ExportHelper(
|
|
112
|
+
cmd_design_obj=self,
|
|
113
|
+
eda_command=self.command_name,
|
|
114
|
+
out_dir=out_dir,
|
|
115
|
+
# Note this may not be the correct target for debug infomation,
|
|
116
|
+
# so we'll only have the first one.
|
|
117
|
+
target=target
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
# Set things in the exported: DEPS.yml
|
|
121
|
+
tool = self.args.get('tool', None)
|
|
122
|
+
# Certain args are allow-listed here
|
|
123
|
+
deps_file_args = []
|
|
124
|
+
for a in self.get_command_line_args():
|
|
125
|
+
if any(a.startswith(x) for x in [
|
|
126
|
+
'--log-must',
|
|
127
|
+
'--log-bad',
|
|
128
|
+
'--pass-pattern']):
|
|
129
|
+
deps_file_args.append(a)
|
|
130
|
+
|
|
131
|
+
export_obj.run(
|
|
132
|
+
deps_file_args=deps_file_args,
|
|
133
|
+
export_json_eda_config={
|
|
134
|
+
'tool': tool,
|
|
135
|
+
}
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
if self.args['export-run']:
|
|
139
|
+
|
|
140
|
+
# remove the '--export' named args, we don't want those.
|
|
141
|
+
args_no_export = self.get_command_line_args(remove_args_startswith=['export'])
|
|
142
|
+
|
|
143
|
+
command_list = ['eda', self.command_name] + args_no_export + [target]
|
|
144
|
+
|
|
145
|
+
util.info(f'export-run: from {export_obj.out_dir=}: {command_list=}')
|
|
146
|
+
self.exec(
|
|
147
|
+
work_dir=export_obj.out_dir,
|
|
148
|
+
command_list=command_list,
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def do_it(self) -> None:
|
|
153
|
+
self.write_eda_config_and_args()
|
|
154
|
+
|
|
155
|
+
if self.is_export_enabled():
|
|
156
|
+
# If we're exporting the target, we do NOT run the test here
|
|
157
|
+
# (do_export() may run the test in a separate process and
|
|
158
|
+
# from the out_dir if --export-run was set)
|
|
159
|
+
self.do_export()
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def check_logs_for_errors( # pylint: disable=dangerous-default-value
|
|
163
|
+
self, filename: str,
|
|
164
|
+
bad_strings: list = [], must_strings: list = [],
|
|
165
|
+
use_bad_strings: bool = True, use_must_strings: bool = True
|
|
166
|
+
) -> None:
|
|
167
|
+
'''Returns None, checks logs using args bad_strings, must_strings,
|
|
168
|
+
|
|
169
|
+
and internals self.args["log-[bad|must]-strings"] (lists).
|
|
170
|
+
'''
|
|
171
|
+
|
|
172
|
+
_bad_strings = bad_strings
|
|
173
|
+
_must_strings = must_strings
|
|
174
|
+
# append, if not they would 'replace' the args values:
|
|
175
|
+
if use_bad_strings:
|
|
176
|
+
_bad_strings = bad_strings + self.args.get('log-bad-strings', [])
|
|
177
|
+
if use_must_strings:
|
|
178
|
+
_must_strings = must_strings + self.args.get('log-must-strings', [])
|
|
179
|
+
|
|
180
|
+
if self.args['pass-pattern'] != "":
|
|
181
|
+
_must_strings.append(self.args['pass-pattern'])
|
|
182
|
+
|
|
183
|
+
if len(_bad_strings) > 0 or len(_must_strings) > 0:
|
|
184
|
+
hit_bad_string = False
|
|
185
|
+
hit_must_string_dict = dict.fromkeys(_must_strings)
|
|
186
|
+
fname = os.path.join(self.args['work-dir'], filename)
|
|
187
|
+
with open(fname, 'r', encoding='utf-8') as f:
|
|
188
|
+
for lineno, line in enumerate(f):
|
|
189
|
+
if any(must_str in line for must_str in _must_strings):
|
|
190
|
+
for k, _ in hit_must_string_dict.items():
|
|
191
|
+
if k in line:
|
|
192
|
+
hit_must_string_dict[k] = True
|
|
193
|
+
if any(bad_str in line for bad_str in _bad_strings):
|
|
194
|
+
hit_bad_string = True
|
|
195
|
+
self.error(f"log {fname}:{lineno} contains one of {_bad_strings=}")
|
|
196
|
+
|
|
197
|
+
if hit_bad_string:
|
|
198
|
+
self.status += 1
|
|
199
|
+
if any(x is None for x in hit_must_string_dict.values()):
|
|
200
|
+
self.error(f"Didn't get all passing patterns in log {fname}: {_must_strings=}",
|
|
201
|
+
f" {hit_must_string_dict=}")
|
|
202
|
+
self.status += 1
|
|
@@ -306,7 +306,7 @@ class CommandSim(CommandDesign):
|
|
|
306
306
|
if hit_bad_string:
|
|
307
307
|
self.status += 1
|
|
308
308
|
if any(x is None for x in hit_must_string_dict.values()):
|
|
309
|
-
self.error(f"Didn't get all passing
|
|
309
|
+
self.error(f"Didn't get all passing patterns in log {fname}: {_must_strings=}",
|
|
310
310
|
f" {hit_must_string_dict=}")
|
|
311
311
|
self.status += 1
|
|
312
312
|
|
|
@@ -68,6 +68,7 @@ class Defaults:
|
|
|
68
68
|
'shell',
|
|
69
69
|
'work-dir-add-srcs', 'work-dir-add-sources',
|
|
70
70
|
'peakrdl',
|
|
71
|
+
'run-from-work-dir',
|
|
71
72
|
'var-subst-args',
|
|
72
73
|
'var-subst-os-env',
|
|
73
74
|
'tee',
|
|
@@ -387,20 +388,20 @@ def path_substitutions_relative_to_work_dir(exec_list : list, info_str : str, ta
|
|
|
387
388
|
|
|
388
389
|
# Look for path substitutions, b/c we later "work" in self.args['work-dir'], but
|
|
389
390
|
# files should be relative to our target_path.
|
|
390
|
-
for
|
|
391
|
+
for i,word in enumerate(exec_list):
|
|
391
392
|
m = re.search(r'(\.+\/+[^"\;\:\|\<\>\*]*)$', word)
|
|
392
393
|
if m:
|
|
393
394
|
# ./, ../, file=./../whatever It might be a filepath.
|
|
394
395
|
# [^"\;\:\|\<\>\*] is looking for non-path like characters, so we dont' have a trailing
|
|
395
396
|
# " : ; < > |
|
|
396
|
-
# try and see if this file exists. Note that files in the self.args['work-dir'] don't
|
|
397
|
+
# try and see if this file or dir exists. Note that files in the self.args['work-dir'] don't
|
|
397
398
|
# need this, and we can't assume dir levels in the work-dir.
|
|
398
399
|
try:
|
|
399
400
|
try_path = os.path.abspath(os.path.join(os.path.abspath(target_path), m.group(1)))
|
|
400
|
-
if os.path.isfile(try_path):
|
|
401
|
+
if os.path.isfile(try_path) or os.path.isdir(try_path):
|
|
401
402
|
# make the substitution
|
|
402
|
-
exec_list[
|
|
403
|
-
debug(f'path substitution {info_str=} {target_path=}: replaced - {word=} is now ={exec_list[
|
|
403
|
+
exec_list[i] = word.replace(m.group(1), try_path)
|
|
404
|
+
debug(f'path substitution {info_str=} {target_path=}: replaced - {word=} is now ={exec_list[i]}')
|
|
404
405
|
except:
|
|
405
406
|
pass
|
|
406
407
|
|
|
@@ -1064,6 +1065,7 @@ def parse_deps_shell_str(line : str, target_path : str, target_node : str, enabl
|
|
|
1064
1065
|
|
|
1065
1066
|
d = {'target_path': os.path.abspath(target_path),
|
|
1066
1067
|
'target_node': target_node,
|
|
1068
|
+
'run_from_work_dir': True,
|
|
1067
1069
|
'exec_list': exec_list,
|
|
1068
1070
|
}
|
|
1069
1071
|
return d
|
|
@@ -1137,12 +1139,12 @@ def parse_deps_peakrdl(line : str, target_path : str, target_node : str, enable
|
|
|
1137
1139
|
|
|
1138
1140
|
sv_files = list()
|
|
1139
1141
|
top = ''
|
|
1140
|
-
for
|
|
1142
|
+
for i,str_value in enumerate(args_list):
|
|
1141
1143
|
if '--top=' in str_value:
|
|
1142
1144
|
_, top = str_value.split('=')
|
|
1143
1145
|
elif '--top' in str_value:
|
|
1144
|
-
if
|
|
1145
|
-
top = args_list[
|
|
1146
|
+
if i + 1 < len(args_list):
|
|
1147
|
+
top = args_list[i + 1]
|
|
1146
1148
|
|
|
1147
1149
|
for str_item in args_list:
|
|
1148
1150
|
if str_item[-4:] == '.rdl':
|
|
@@ -1235,14 +1237,14 @@ def deps_commands_handler(config: dict, eda_args: dict,
|
|
|
1235
1237
|
var_subst_dict = eda_args
|
|
1236
1238
|
|
|
1237
1239
|
tee_fpath = command.get('tee', None)
|
|
1240
|
+
run_from_work_dir = command.get('run-from-work-dir', True) # for shell, default True
|
|
1238
1241
|
|
|
1239
1242
|
for key,item in command.items():
|
|
1240
1243
|
|
|
1241
1244
|
# skip the var-subst-* keys, since these types are bools
|
|
1242
|
-
if key.startswith('var-subst')
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
if key.startswith('tee'):
|
|
1245
|
+
if key.startswith('var-subst') or \
|
|
1246
|
+
key.startswith('tee') or \
|
|
1247
|
+
key.startswith('run-from-work-dir'):
|
|
1246
1248
|
continue
|
|
1247
1249
|
|
|
1248
1250
|
# Optional variable substituion in commands
|
|
@@ -1262,6 +1264,7 @@ def deps_commands_handler(config: dict, eda_args: dict,
|
|
|
1262
1264
|
# list item in ret_dict['exec_list'], and make it a util.ShellCommandList.
|
|
1263
1265
|
if tee_fpath:
|
|
1264
1266
|
ret_dict['exec_list'] = ShellCommandList(ret_dict['exec_list'], tee_fpath=tee_fpath)
|
|
1267
|
+
ret_dict['run_from_work_dir'] = run_from_work_dir
|
|
1265
1268
|
assert ret_dict, f'shell command failed in {dep=} {target_node=} in {deps_file=}'
|
|
1266
1269
|
shell_commands_list.append(ret_dict) # process this later, append to our to-be-returned tuple
|
|
1267
1270
|
|
|
@@ -69,6 +69,8 @@ my_target_name:
|
|
|
69
69
|
- shell: <---- string for shell command to be run
|
|
70
70
|
var-subst-args: <---- bool, perform var substitution using args
|
|
71
71
|
var-subst-os-env: <---- bool, perform var substitution using os.environ
|
|
72
|
+
run-from-work-dir: <---- bool, default True, if False runs from target dir
|
|
73
|
+
instead of work-dir.
|
|
72
74
|
tee: <---- string, filename to write logs to
|
|
73
75
|
- work-dir-add-sources: <---- work-dir-add-sources, optional list (or string)
|
|
74
76
|
- some_file_gen_from_sh.sv <---- string filename that we created with sh command
|
|
@@ -150,6 +152,7 @@ DEPS_COMMANDS_LIST = [
|
|
|
150
152
|
Optional('shell'): str,
|
|
151
153
|
Optional('var-subst-args'): bool,
|
|
152
154
|
Optional('var-subst-os-env'): bool,
|
|
155
|
+
Optional('run-from-work-dir'): bool,
|
|
153
156
|
Optional('tee'): Or(str, type(None)),
|
|
154
157
|
},
|
|
155
158
|
{
|
|
@@ -87,6 +87,8 @@ Where <command> is one of:
|
|
|
87
87
|
upload - Uploads a finished design into hardware
|
|
88
88
|
open - Opens a project
|
|
89
89
|
export - Export files related to a target, tool independent
|
|
90
|
+
shell - Runs only commands for DEPS target (like sim or elab, but stops prior to tool)
|
|
91
|
+
targets - list all possible targets given glob path.
|
|
90
92
|
help - This help (without args), or i.e. "eda help sim" for specific help
|
|
91
93
|
|
|
92
94
|
And <files|targets, ...> is one or more source file or DEPS markup file target,
|
|
@@ -302,6 +302,8 @@ class Command:
|
|
|
302
302
|
|
|
303
303
|
def exec(self, work_dir, command_list, background=False, stop_on_error=True,
|
|
304
304
|
quiet=False, tee_fpath=None, shell=False):
|
|
305
|
+
if not tee_fpath and getattr(command_list, 'tee_fpath', None):
|
|
306
|
+
tee_fpath = getattr(command_list, 'tee_fpath', '')
|
|
305
307
|
if not quiet:
|
|
306
308
|
util.info(f"exec: {' '.join(command_list)} (in {work_dir}, {tee_fpath=})")
|
|
307
309
|
original_cwd = util.getcwd()
|
|
@@ -714,8 +716,9 @@ class CommandDesign(Command):
|
|
|
714
716
|
|
|
715
717
|
log_fnames_count = {} # count per target_node.
|
|
716
718
|
|
|
717
|
-
for
|
|
719
|
+
for i, d in enumerate(self.dep_shell_commands):
|
|
718
720
|
clist = util.ShellCommandList(d['exec_list'])
|
|
721
|
+
run_from_work_dir = d['run_from_work_dir'] # default True
|
|
719
722
|
log = clist.tee_fpath
|
|
720
723
|
target_node = d["target_node"]
|
|
721
724
|
if clist.tee_fpath is None:
|
|
@@ -727,24 +730,38 @@ class CommandDesign(Command):
|
|
|
727
730
|
all_cmds_lists += [
|
|
728
731
|
[], # blank line
|
|
729
732
|
# comment, where it came from, log to {node}__shell_{lognum}.log (or tee name from DEPS.yml)
|
|
730
|
-
[f'# command {
|
|
731
|
-
# actual command (list or util.ShellCommandList)
|
|
732
|
-
clist,
|
|
733
|
+
[f'# command {i}: target: {d["target_path"]} : {target_node} --> {log}'],
|
|
733
734
|
]
|
|
735
|
+
if not run_from_work_dir:
|
|
736
|
+
all_cmds_lists.append([f'cd {d["target_path"]}'])
|
|
737
|
+
|
|
738
|
+
# actual command (list or util.ShellCommandList)
|
|
739
|
+
all_cmds_lists.append(clist)
|
|
740
|
+
|
|
741
|
+
if not run_from_work_dir:
|
|
742
|
+
all_cmds_lists.append([f'cd {os.path.abspath(self.args["work-dir"])}'])
|
|
743
|
+
|
|
734
744
|
d['exec_list'] = clist # update to tee_fpath is set.
|
|
735
745
|
|
|
736
746
|
util.write_shell_command_file(dirpath=self.args['work-dir'], filename='pre_compile_dep_shell_commands.sh',
|
|
737
747
|
command_lists=all_cmds_lists)
|
|
738
748
|
|
|
739
|
-
for
|
|
740
|
-
util.info(f'run_dep_shell_commands {
|
|
749
|
+
for i,d in enumerate(self.dep_shell_commands):
|
|
750
|
+
util.info(f'run_dep_shell_commands {i=}: {d=}')
|
|
741
751
|
clist = util.ShellCommandList(d['exec_list'])
|
|
752
|
+
tee_fpath=clist.tee_fpath
|
|
753
|
+
if d['run_from_work_dir']:
|
|
754
|
+
run_from_dir = self.args['work-dir']
|
|
755
|
+
else:
|
|
756
|
+
# Run from the target's directory (not the `eda` caller $PWD)
|
|
757
|
+
run_from_dir = d["target_path"]
|
|
758
|
+
tee_fpath = os.path.abspath(os.path.join(self.args['work-dir'], tee_fpath))
|
|
742
759
|
# NOTE(drew): shell=True subprocess call, can disable with self.config
|
|
743
|
-
# However, in Windows, we seem to have to run these with shell=False
|
|
744
760
|
if sys.platform.startswith('win'):
|
|
745
|
-
|
|
761
|
+
# for Windows, we run shell=True otherwise most built-in cmd.exe calls won't work.
|
|
762
|
+
self.exec(run_from_dir, clist, tee_fpath=tee_fpath, shell=True)
|
|
746
763
|
else:
|
|
747
|
-
self.exec(
|
|
764
|
+
self.exec(run_from_dir, clist, tee_fpath=tee_fpath,
|
|
748
765
|
shell=self.config.get('deps_subprocess_shell', False))
|
|
749
766
|
|
|
750
767
|
def update_file_lists_for_work_dir(self):
|
|
@@ -764,10 +781,10 @@ class CommandDesign(Command):
|
|
|
764
781
|
my_file_lists_list = [self.files_v, self.files_sv, self.files_vhd, self.files_cpp,
|
|
765
782
|
self.files_sdc]
|
|
766
783
|
for my_file_list in my_file_lists_list:
|
|
767
|
-
for
|
|
784
|
+
for i,value in enumerate(my_file_list):
|
|
768
785
|
if value and type(value) is str and value.startswith(self._work_dir_add_srcs_path_string):
|
|
769
786
|
new_value = os.path.join(work_dir_abspath, value[_work_dir_add_srcs_path_string_len :])
|
|
770
|
-
my_file_list[
|
|
787
|
+
my_file_list[i] = new_value
|
|
771
788
|
util.debug(f"file lists: replaced {value} with {new_value}")
|
|
772
789
|
|
|
773
790
|
def update_non_source_files_in_work_dir(self):
|
|
@@ -1160,7 +1177,7 @@ class CommandDesign(Command):
|
|
|
1160
1177
|
else:
|
|
1161
1178
|
target_name = os.path.join(".", token) # prepend ./so that we always have a <path>/<file>
|
|
1162
1179
|
|
|
1163
|
-
util.debug(f'Calling self.resolve_target on {target_name=}')
|
|
1180
|
+
util.debug(f'Calling self.resolve_target on {target_name=} ({token=})')
|
|
1164
1181
|
if self.resolve_target(target_name, caller_info=caller_info):
|
|
1165
1182
|
if not self.args['top']:
|
|
1166
1183
|
# last cmd line arg was a target that we'll likely use for inferred top.
|
|
@@ -1573,10 +1590,10 @@ class CommandParallel(Command):
|
|
|
1573
1590
|
else:
|
|
1574
1591
|
util.info(f"Parallel: <No jobs found>")
|
|
1575
1592
|
# Make sure all jobs have a set status:
|
|
1576
|
-
for
|
|
1593
|
+
for i,rc in enumerate(self.jobs_status):
|
|
1577
1594
|
if rc is None or type(rc) != int:
|
|
1578
|
-
self.error(f'job {
|
|
1579
|
-
jobs_status[
|
|
1595
|
+
self.error(f'job {i=} {rc=} did not return a proper return code')
|
|
1596
|
+
jobs_status[i] = 1
|
|
1580
1597
|
|
|
1581
1598
|
# if self.status > 0, then keep it non-zero, else set it if we still have running jobs.
|
|
1582
1599
|
if self.status == 0:
|
|
@@ -9,19 +9,21 @@ DEFAULT_HANDLERS:
|
|
|
9
9
|
sim : opencos.commands.CommandSim
|
|
10
10
|
elab : opencos.commands.CommandElab
|
|
11
11
|
synth : opencos.commands.CommandSynth
|
|
12
|
-
flist : opencos.commands.CommandFList
|
|
13
12
|
proj : opencos.commands.CommandProj
|
|
14
13
|
build : opencos.commands.CommandBuild
|
|
15
14
|
upload : opencos.commands.CommandUpload
|
|
16
15
|
open : opencos.commands.CommandOpen
|
|
16
|
+
lec : opencos.commands.CommandLec
|
|
17
17
|
# These commands don't necessarily require a tool
|
|
18
18
|
multi : opencos.commands.CommandMulti
|
|
19
19
|
tools-multi : opencos.commands.CommandToolsMulti
|
|
20
20
|
sweep : opencos.commands.CommandSweep
|
|
21
|
+
flist : opencos.commands.CommandFList
|
|
21
22
|
# These commands (waves, export, targets) do not require a tool, or
|
|
22
23
|
# will self determine the tool:
|
|
23
24
|
waves : opencos.commands.CommandWaves
|
|
24
25
|
export : opencos.commands.CommandExport
|
|
26
|
+
shell : opencos.commands.CommandShell
|
|
25
27
|
targets : opencos.commands.CommandTargets
|
|
26
28
|
|
|
27
29
|
|
|
@@ -60,6 +62,7 @@ file_extensions:
|
|
|
60
62
|
- .vhdl
|
|
61
63
|
cpp:
|
|
62
64
|
- .cpp
|
|
65
|
+
- .cc
|
|
63
66
|
synth_constraints:
|
|
64
67
|
- .sdc
|
|
65
68
|
- .xdc
|
|
@@ -337,6 +340,15 @@ auto_tools_order:
|
|
|
337
340
|
flist: opencos.tools.vivado.CommandFListVivado
|
|
338
341
|
build: opencos.tools.vivado.CommandBuildVivado
|
|
339
342
|
|
|
343
|
+
slang_yosys:
|
|
344
|
+
exe: yosys
|
|
345
|
+
requires_cmd:
|
|
346
|
+
- yosys -m slang
|
|
347
|
+
handlers:
|
|
348
|
+
elab: opencos.tools.slang_yosys.CommandElabSlangYosys
|
|
349
|
+
synth: opencos.tools.slang_yosys.CommandSynthSlangYosys
|
|
350
|
+
lec: opencos.tools.slang_yosys.CommandLecSlangYosys
|
|
351
|
+
|
|
340
352
|
tabbycad_yosys:
|
|
341
353
|
exe: yosys
|
|
342
354
|
requires_env:
|
|
@@ -354,13 +366,13 @@ auto_tools_order:
|
|
|
354
366
|
elab: opencos.tools.invio_yosys.CommandElabInvioYosys
|
|
355
367
|
synth: opencos.tools.invio_yosys.CommandSynthInvioYosys
|
|
356
368
|
|
|
357
|
-
|
|
369
|
+
yosys:
|
|
358
370
|
exe: yosys
|
|
359
371
|
requires_cmd:
|
|
360
|
-
- yosys
|
|
372
|
+
- yosys
|
|
361
373
|
handlers:
|
|
362
|
-
|
|
363
|
-
|
|
374
|
+
synth: opencos.tools.slang_yosys.CommonSynthYosys
|
|
375
|
+
lec: opencos.tools.slang_yosys.CommandLecYosys
|
|
364
376
|
|
|
365
377
|
questa:
|
|
366
378
|
exe: qrun
|
|
@@ -86,8 +86,9 @@ def get_path_and_pattern(partial_path: str = '', base_path=str(Path('.'))) -> (s
|
|
|
86
86
|
|
|
87
87
|
|
|
88
88
|
|
|
89
|
-
|
|
90
|
-
|
|
89
|
+
|
|
90
|
+
def get_targets(partial_paths: list, base_path=str(Path('.'))) -> list:
|
|
91
|
+
'''Returns a list of DEPS keys into pretty columns, using arg
|
|
91
92
|
|
|
92
93
|
partial_path as a string filter for target completions.
|
|
93
94
|
'''
|
|
@@ -113,7 +114,16 @@ def run(partial_paths: list, base_path=str(Path('.'))) -> None:
|
|
|
113
114
|
for key in keys:
|
|
114
115
|
targets_set.add(key)
|
|
115
116
|
|
|
116
|
-
|
|
117
|
+
return list(targets_set)
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def run(partial_paths: list, base_path=str(Path('.'))) -> None:
|
|
121
|
+
'''Returns None, prints DEPS keys into pretty columns, using arg
|
|
122
|
+
|
|
123
|
+
partial_path as a string filter for target completions.
|
|
124
|
+
'''
|
|
125
|
+
|
|
126
|
+
data = get_targets(partial_paths=partial_paths, base_path=base_path)
|
|
117
127
|
data.sort()
|
|
118
128
|
print_columns_manual(data=data, num_columns=4, auto_columns=True)
|
|
119
129
|
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
import json
|
|
6
6
|
import os
|
|
7
7
|
import shutil
|
|
8
|
+
from pathlib import Path
|
|
8
9
|
|
|
9
10
|
from contextlib import redirect_stdout, redirect_stderr
|
|
10
11
|
|
|
@@ -26,7 +27,7 @@ def can_run_eda_command(*commands, config: dict) -> bool:
|
|
|
26
27
|
|
|
27
28
|
def chdir_remove_work_dir(startpath, relpath):
|
|
28
29
|
'''Changes dir to startpath/relpath, removes the work directories (eda.work, eda.export*)'''
|
|
29
|
-
os.chdir(os.path.join(startpath, relpath))
|
|
30
|
+
os.chdir(os.path.join(str(Path(startpath)), str(Path(relpath))))
|
|
30
31
|
for outdir in ['eda.export', 'eda.work']:
|
|
31
32
|
fullp = os.path.join(os.getcwd(), outdir)
|
|
32
33
|
if fullp and ('eda.' in fullp) and os.path.isdir(fullp):
|