opencos-eda 0.2.43__py3-none-any.whl → 0.2.45__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/__init__.py +4 -0
- opencos/commands/export.py +0 -1
- opencos/commands/flist.py +4 -1
- opencos/commands/lec.py +103 -0
- opencos/commands/shell.py +202 -0
- opencos/commands/sim.py +1 -1
- opencos/deps_helpers.py +2 -0
- opencos/eda.py +6 -3
- opencos/eda_base.py +12 -4
- opencos/eda_config.py +1 -1
- opencos/eda_config_defaults.yml +22 -7
- opencos/eda_extract_targets.py +13 -3
- opencos/export_helper.py +6 -4
- opencos/files.py +1 -0
- opencos/tests/helpers.py +3 -2
- opencos/tests/test_deps_schema.py +3 -1
- opencos/tests/test_eda.py +19 -9
- opencos/tests/test_eda_synth.py +63 -2
- opencos/tools/invio_yosys.py +7 -7
- opencos/tools/iverilog.py +1 -1
- opencos/tools/riviera.py +1 -1
- opencos/tools/slang.py +1 -1
- opencos/tools/slang_yosys.py +29 -38
- opencos/tools/surelog.py +1 -1
- opencos/tools/tabbycad_yosys.py +1 -1
- opencos/tools/verilator.py +1 -1
- opencos/tools/vivado.py +30 -11
- opencos/tools/yosys.py +465 -25
- opencos/util.py +19 -12
- {opencos_eda-0.2.43.dist-info → opencos_eda-0.2.45.dist-info}/METADATA +1 -1
- {opencos_eda-0.2.43.dist-info → opencos_eda-0.2.45.dist-info}/RECORD +36 -34
- {opencos_eda-0.2.43.dist-info → opencos_eda-0.2.45.dist-info}/WHEEL +0 -0
- {opencos_eda-0.2.43.dist-info → opencos_eda-0.2.45.dist-info}/entry_points.txt +0 -0
- {opencos_eda-0.2.43.dist-info → opencos_eda-0.2.45.dist-info}/licenses/LICENSE +0 -0
- {opencos_eda-0.2.43.dist-info → opencos_eda-0.2.45.dist-info}/licenses/LICENSE.spdx +0 -0
- {opencos_eda-0.2.43.dist-info → opencos_eda-0.2.45.dist-info}/top_level.txt +0 -0
opencos/commands/__init__.py
CHANGED
|
@@ -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
|
]
|
opencos/commands/export.py
CHANGED
|
@@ -37,7 +37,6 @@ class CommandExport(CommandDesign):
|
|
|
37
37
|
self, tokens: list, process_all: bool = True, pwd: str = os.getcwd()
|
|
38
38
|
) -> list:
|
|
39
39
|
|
|
40
|
-
self.defines['OC_EXPORT'] = None
|
|
41
40
|
unparsed = CommandDesign.process_tokens(
|
|
42
41
|
self, tokens=tokens, process_all=process_all, pwd=pwd
|
|
43
42
|
)
|
opencos/commands/flist.py
CHANGED
|
@@ -47,11 +47,14 @@ class CommandFList(CommandDesign):
|
|
|
47
47
|
'no-quote-path' : False,
|
|
48
48
|
'build-script' : "", # we don't want this to error either
|
|
49
49
|
|
|
50
|
-
'print-to-stdout': False,
|
|
50
|
+
'print-to-stdout': False,
|
|
51
51
|
|
|
52
52
|
# ex: eda flist --print-to-stdout --emit-rel-path --quiet <target>
|
|
53
53
|
'emit-rel-path' : False,
|
|
54
54
|
})
|
|
55
|
+
self.args_help.update({
|
|
56
|
+
'print-to-stdout': "do not save file, print to stdout",
|
|
57
|
+
})
|
|
55
58
|
|
|
56
59
|
def process_tokens(
|
|
57
60
|
self, tokens: list , process_all: bool = True, pwd: str = os.getcwd()
|
opencos/commands/lec.py
ADDED
|
@@ -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
|
opencos/commands/sim.py
CHANGED
|
@@ -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
|
|
opencos/deps_helpers.py
CHANGED
opencos/eda.py
CHANGED
|
@@ -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,
|
|
@@ -320,9 +322,10 @@ def process_tokens(tokens: list, original_args: list, config: dict, interactive=
|
|
|
320
322
|
for value in unparsed:
|
|
321
323
|
if value in config['DEFAULT_HANDLERS'].keys():
|
|
322
324
|
command = value
|
|
323
|
-
if value in config['
|
|
325
|
+
if not parsed.tool and value in config['command_tool_is_optional']:
|
|
326
|
+
# only do this if --tool was not set.
|
|
324
327
|
run_auto_tool_setup = False
|
|
325
|
-
unparsed.remove(value)
|
|
328
|
+
unparsed.remove(value) # remove command (flist, export, targets, etc)
|
|
326
329
|
break
|
|
327
330
|
|
|
328
331
|
if not interactive:
|
|
@@ -361,7 +364,7 @@ def process_tokens(tokens: list, original_args: list, config: dict, interactive=
|
|
|
361
364
|
util.debug(f'{type(sco)=}')
|
|
362
365
|
if not parsed.tool and \
|
|
363
366
|
command not in config.get('command_determines_tool', []) and \
|
|
364
|
-
command not in config.get('
|
|
367
|
+
command not in config.get('command_tool_is_optional', []):
|
|
365
368
|
use_tool = which_tool(command, config)
|
|
366
369
|
util.info(f"--tool not specified, using default for {command=}: {use_tool}")
|
|
367
370
|
|
opencos/eda_base.py
CHANGED
|
@@ -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()
|
|
@@ -686,6 +688,7 @@ class CommandDesign(Command):
|
|
|
686
688
|
self.files_sv = []
|
|
687
689
|
self.files_vhd = []
|
|
688
690
|
self.files_cpp = []
|
|
691
|
+
self.files_sdc = []
|
|
689
692
|
self.files_non_source = []
|
|
690
693
|
self.files_caller_info = {}
|
|
691
694
|
self.dep_shell_commands = [] # each list entry is a {}
|
|
@@ -739,9 +742,9 @@ class CommandDesign(Command):
|
|
|
739
742
|
util.info(f'run_dep_shell_commands {iter=}: {d=}')
|
|
740
743
|
clist = util.ShellCommandList(d['exec_list'])
|
|
741
744
|
# NOTE(drew): shell=True subprocess call, can disable with self.config
|
|
742
|
-
# However, in Windows, we seem to have to run these with shell=False
|
|
743
745
|
if sys.platform.startswith('win'):
|
|
744
|
-
|
|
746
|
+
# for Windows, we run shell=True otherwise most built-in cmd.exe calls won't work.
|
|
747
|
+
self.exec(self.args['work-dir'], clist, tee_fpath=clist.tee_fpath, shell=True)
|
|
745
748
|
else:
|
|
746
749
|
self.exec(self.args['work-dir'], clist, tee_fpath=clist.tee_fpath,
|
|
747
750
|
shell=self.config.get('deps_subprocess_shell', False))
|
|
@@ -760,7 +763,8 @@ class CommandDesign(Command):
|
|
|
760
763
|
self.files.pop(key)
|
|
761
764
|
self.files[new_key] = True
|
|
762
765
|
|
|
763
|
-
my_file_lists_list = [self.files_v, self.files_sv, self.files_vhd, self.files_cpp
|
|
766
|
+
my_file_lists_list = [self.files_v, self.files_sv, self.files_vhd, self.files_cpp,
|
|
767
|
+
self.files_sdc]
|
|
764
768
|
for my_file_list in my_file_lists_list:
|
|
765
769
|
for iter,value in enumerate(my_file_list):
|
|
766
770
|
if value and type(value) is str and value.startswith(self._work_dir_add_srcs_path_string):
|
|
@@ -1029,6 +1033,7 @@ class CommandDesign(Command):
|
|
|
1029
1033
|
sv_file_ext_list = known_file_ext_dict.get('systemverilog', [])
|
|
1030
1034
|
vhdl_file_ext_list = known_file_ext_dict.get('vhdl', [])
|
|
1031
1035
|
cpp_file_ext_list = known_file_ext_dict.get('cpp', [])
|
|
1036
|
+
sdc_file_ext_list = known_file_ext_dict.get('synth_constraints', [])
|
|
1032
1037
|
|
|
1033
1038
|
if forced_extension:
|
|
1034
1039
|
# If forced_extension='systemverilog', then use the first known extension for
|
|
@@ -1057,6 +1062,9 @@ class CommandDesign(Command):
|
|
|
1057
1062
|
elif file_ext in cpp_file_ext_list:
|
|
1058
1063
|
self.files_cpp.append(file_abspath)
|
|
1059
1064
|
util.debug("Added C++ file %s as %s" % (filename, file_abspath))
|
|
1065
|
+
elif file_ext in sdc_file_ext_list:
|
|
1066
|
+
self.files_sdc.append(file_abspath)
|
|
1067
|
+
util.debug("Added SDC file %s as %s" % (filename, file_abspath))
|
|
1060
1068
|
else:
|
|
1061
1069
|
# unknown file extension. In these cases we link the file to the working directory
|
|
1062
1070
|
# so it is available (for example, a .mem file that is expected to exist with relative path)
|
|
@@ -1154,7 +1162,7 @@ class CommandDesign(Command):
|
|
|
1154
1162
|
else:
|
|
1155
1163
|
target_name = os.path.join(".", token) # prepend ./so that we always have a <path>/<file>
|
|
1156
1164
|
|
|
1157
|
-
util.debug(f'Calling self.resolve_target on {target_name=}')
|
|
1165
|
+
util.debug(f'Calling self.resolve_target on {target_name=} ({token=})')
|
|
1158
1166
|
if self.resolve_target(target_name, caller_info=caller_info):
|
|
1159
1167
|
if not self.args['top']:
|
|
1160
1168
|
# last cmd line arg was a target that we'll likely use for inferred top.
|
opencos/eda_config.py
CHANGED
opencos/eda_config_defaults.yml
CHANGED
|
@@ -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,9 @@ file_extensions:
|
|
|
60
62
|
- .vhdl
|
|
61
63
|
cpp:
|
|
62
64
|
- .cpp
|
|
65
|
+
synth_constraints:
|
|
66
|
+
- .sdc
|
|
67
|
+
- .xdc
|
|
63
68
|
|
|
64
69
|
inferred_top:
|
|
65
70
|
# file extensions that we can infer "top" module from, if --top omitted.
|
|
@@ -72,8 +77,9 @@ command_determines_tool:
|
|
|
72
77
|
# eda commands that will self-determine the tool to use
|
|
73
78
|
- waves
|
|
74
79
|
|
|
75
|
-
|
|
76
|
-
# eda commands that
|
|
80
|
+
command_tool_is_optional:
|
|
81
|
+
# eda commands that may not need to use a tool at all, will skip auto_tools_order if --tool=None (default)
|
|
82
|
+
- flist
|
|
77
83
|
- export
|
|
78
84
|
- targets
|
|
79
85
|
|
|
@@ -333,6 +339,15 @@ auto_tools_order:
|
|
|
333
339
|
flist: opencos.tools.vivado.CommandFListVivado
|
|
334
340
|
build: opencos.tools.vivado.CommandBuildVivado
|
|
335
341
|
|
|
342
|
+
slang_yosys:
|
|
343
|
+
exe: yosys
|
|
344
|
+
requires_cmd:
|
|
345
|
+
- yosys -m slang
|
|
346
|
+
handlers:
|
|
347
|
+
elab: opencos.tools.slang_yosys.CommandElabSlangYosys
|
|
348
|
+
synth: opencos.tools.slang_yosys.CommandSynthSlangYosys
|
|
349
|
+
lec: opencos.tools.slang_yosys.CommandLecSlangYosys
|
|
350
|
+
|
|
336
351
|
tabbycad_yosys:
|
|
337
352
|
exe: yosys
|
|
338
353
|
requires_env:
|
|
@@ -350,13 +365,13 @@ auto_tools_order:
|
|
|
350
365
|
elab: opencos.tools.invio_yosys.CommandElabInvioYosys
|
|
351
366
|
synth: opencos.tools.invio_yosys.CommandSynthInvioYosys
|
|
352
367
|
|
|
353
|
-
|
|
368
|
+
yosys:
|
|
354
369
|
exe: yosys
|
|
355
370
|
requires_cmd:
|
|
356
|
-
- yosys
|
|
371
|
+
- yosys
|
|
357
372
|
handlers:
|
|
358
|
-
|
|
359
|
-
|
|
373
|
+
synth: opencos.tools.slang_yosys.CommonSynthYosys
|
|
374
|
+
lec: opencos.tools.slang_yosys.CommandLecYosys
|
|
360
375
|
|
|
361
376
|
questa:
|
|
362
377
|
exe: qrun
|
opencos/eda_extract_targets.py
CHANGED
|
@@ -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
|
|
opencos/export_helper.py
CHANGED
|
@@ -352,23 +352,25 @@ class ExportHelper:
|
|
|
352
352
|
}
|
|
353
353
|
}
|
|
354
354
|
|
|
355
|
-
|
|
355
|
+
|
|
356
|
+
if deps_file_args:
|
|
356
357
|
data[self.target]['args'] = deps_file_args.copy()
|
|
357
358
|
|
|
358
359
|
if self.args.get('top', None):
|
|
359
360
|
data[self.target]['top'] = self.args['top']
|
|
360
361
|
|
|
361
|
-
if
|
|
362
|
+
if self.cmd_design_obj.defines:
|
|
362
363
|
data[self.target]['defines'] = self.cmd_design_obj.defines.copy()
|
|
363
364
|
for define in _remove_DEPS_yml_defines:
|
|
364
365
|
# Remove defines keys for OC_ROOT and OC_SEED. Change OC_SEED to _ORIG_OC_SEED
|
|
365
366
|
if define in data[self.target]['defines']:
|
|
366
367
|
data[self.target]['defines'].pop(define)
|
|
367
368
|
|
|
368
|
-
|
|
369
|
+
reqs_fullpath_list = self.included_files + self.cmd_design_obj.files_non_source
|
|
370
|
+
if reqs_fullpath_list:
|
|
369
371
|
# Need to strip path information from non-source files:
|
|
370
372
|
data[self.target]['reqs'] = list()
|
|
371
|
-
for fullpath in
|
|
373
|
+
for fullpath in reqs_fullpath_list:
|
|
372
374
|
filename = os.path.split(fullpath)[1]
|
|
373
375
|
data[self.target]['reqs'].append(filename)
|
|
374
376
|
|
opencos/files.py
CHANGED
opencos/tests/helpers.py
CHANGED
|
@@ -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):
|
|
@@ -128,7 +129,7 @@ class Helpers:
|
|
|
128
129
|
'''Changes directory to self.DEFAULT_DIR and removes eda.work, eda.export paths'''
|
|
129
130
|
chdir_remove_work_dir('', self.DEFAULT_DIR)
|
|
130
131
|
|
|
131
|
-
def log_it(self, command_str:str, logfile=None, use_eda_wrap=True):
|
|
132
|
+
def log_it(self, command_str:str, logfile=None, use_eda_wrap=True) -> int:
|
|
132
133
|
'''Replacement for calling eda.main or eda_wrap, when you want an internal logfile
|
|
133
134
|
|
|
134
135
|
Usage:
|
|
@@ -16,7 +16,9 @@ def test_all_deps():
|
|
|
16
16
|
for root, _, files in os.walk(os.getcwd()):
|
|
17
17
|
for fname in files:
|
|
18
18
|
if fname.startswith('DEPS') and \
|
|
19
|
-
any(fname.endswith(x) for x in [
|
|
19
|
+
any(fname.endswith(x) for x in [
|
|
20
|
+
'.yml', '.yaml', '.json', '.toml', 'DEPS'
|
|
21
|
+
]):
|
|
20
22
|
|
|
21
23
|
all_deps_files.append(os.path.join(root, fname))
|
|
22
24
|
|