opencos-eda 0.2.44__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/lec.py +103 -0
- opencos/commands/shell.py +202 -0
- opencos/commands/sim.py +1 -1
- opencos/eda.py +2 -0
- opencos/eda_base.py +5 -3
- opencos/eda_config_defaults.yml +16 -5
- opencos/eda_extract_targets.py +13 -3
- opencos/tests/helpers.py +2 -1
- opencos/tests/test_deps_schema.py +3 -1
- opencos/tests/test_eda.py +19 -9
- opencos/tools/invio_yosys.py +7 -7
- opencos/tools/slang_yosys.py +29 -38
- opencos/tools/tabbycad_yosys.py +1 -1
- opencos/tools/vivado.py +8 -7
- opencos/tools/yosys.py +433 -12
- opencos/util.py +14 -6
- {opencos_eda-0.2.44.dist-info → opencos_eda-0.2.45.dist-info}/METADATA +1 -1
- {opencos_eda-0.2.44.dist-info → opencos_eda-0.2.45.dist-info}/RECORD +24 -22
- {opencos_eda-0.2.44.dist-info → opencos_eda-0.2.45.dist-info}/WHEEL +0 -0
- {opencos_eda-0.2.44.dist-info → opencos_eda-0.2.45.dist-info}/entry_points.txt +0 -0
- {opencos_eda-0.2.44.dist-info → opencos_eda-0.2.45.dist-info}/licenses/LICENSE +0 -0
- {opencos_eda-0.2.44.dist-info → opencos_eda-0.2.45.dist-info}/licenses/LICENSE.spdx +0 -0
- {opencos_eda-0.2.44.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/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/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,
|
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()
|
|
@@ -740,9 +742,9 @@ class CommandDesign(Command):
|
|
|
740
742
|
util.info(f'run_dep_shell_commands {iter=}: {d=}')
|
|
741
743
|
clist = util.ShellCommandList(d['exec_list'])
|
|
742
744
|
# 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
745
|
if sys.platform.startswith('win'):
|
|
745
|
-
|
|
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)
|
|
746
748
|
else:
|
|
747
749
|
self.exec(self.args['work-dir'], clist, tee_fpath=clist.tee_fpath,
|
|
748
750
|
shell=self.config.get('deps_subprocess_shell', False))
|
|
@@ -1160,7 +1162,7 @@ class CommandDesign(Command):
|
|
|
1160
1162
|
else:
|
|
1161
1163
|
target_name = os.path.join(".", token) # prepend ./so that we always have a <path>/<file>
|
|
1162
1164
|
|
|
1163
|
-
util.debug(f'Calling self.resolve_target on {target_name=}')
|
|
1165
|
+
util.debug(f'Calling self.resolve_target on {target_name=} ({token=})')
|
|
1164
1166
|
if self.resolve_target(target_name, caller_info=caller_info):
|
|
1165
1167
|
if not self.args['top']:
|
|
1166
1168
|
# last cmd line arg was a target that we'll likely use for inferred top.
|
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
|
|
|
@@ -337,6 +339,15 @@ auto_tools_order:
|
|
|
337
339
|
flist: opencos.tools.vivado.CommandFListVivado
|
|
338
340
|
build: opencos.tools.vivado.CommandBuildVivado
|
|
339
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
|
+
|
|
340
351
|
tabbycad_yosys:
|
|
341
352
|
exe: yosys
|
|
342
353
|
requires_env:
|
|
@@ -354,13 +365,13 @@ auto_tools_order:
|
|
|
354
365
|
elab: opencos.tools.invio_yosys.CommandElabInvioYosys
|
|
355
366
|
synth: opencos.tools.invio_yosys.CommandSynthInvioYosys
|
|
356
367
|
|
|
357
|
-
|
|
368
|
+
yosys:
|
|
358
369
|
exe: yosys
|
|
359
370
|
requires_cmd:
|
|
360
|
-
- yosys
|
|
371
|
+
- yosys
|
|
361
372
|
handlers:
|
|
362
|
-
|
|
363
|
-
|
|
373
|
+
synth: opencos.tools.slang_yosys.CommonSynthYosys
|
|
374
|
+
lec: opencos.tools.slang_yosys.CommandLecYosys
|
|
364
375
|
|
|
365
376
|
questa:
|
|
366
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/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):
|
|
@@ -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
|
|
opencos/tests/test_eda.py
CHANGED
|
@@ -692,14 +692,22 @@ class TestDepsReqs:
|
|
|
692
692
|
assert rc != 0
|
|
693
693
|
|
|
694
694
|
|
|
695
|
-
@pytest.mark.
|
|
696
|
-
def test_deps_command_order():
|
|
695
|
+
@pytest.mark.parametrize("command", ['sim', 'shell'])
|
|
696
|
+
def test_deps_command_order(command):
|
|
697
697
|
'''Test for various "commands" within a DEPS target. This test checks that command
|
|
698
698
|
order is preserved in the top-to-bottom deps order, meaning that eda.py has to collect
|
|
699
699
|
all commands deps order, and then execute them in that order.'''
|
|
700
700
|
|
|
701
701
|
chdir_remove_work_dir('deps_files/command_order')
|
|
702
|
-
|
|
702
|
+
if command == 'sim' and not can_run_eda_sim():
|
|
703
|
+
pytest.skip(f'sim skipped, {can_run_eda_sim()=}')
|
|
704
|
+
return # skip/pass
|
|
705
|
+
|
|
706
|
+
if command == 'shell':
|
|
707
|
+
cmd_list = 'shell target_test'.split()
|
|
708
|
+
else:
|
|
709
|
+
cmd_list = 'sim --stop-before-compile target_test'.split()
|
|
710
|
+
|
|
703
711
|
with open('eda.log', 'w', encoding='utf-8') as f:
|
|
704
712
|
with redirect_stdout(f):
|
|
705
713
|
with redirect_stderr(f):
|
|
@@ -730,14 +738,16 @@ def test_deps_command_order():
|
|
|
730
738
|
# Added check, we redirected to create eda.log earlier to confirm the targets worked,
|
|
731
739
|
# but as a general eda.py check, all shell commands should create their own
|
|
732
740
|
# {target}__shell_0.log file:
|
|
733
|
-
work_dir = os.path.join(
|
|
741
|
+
work_dir = os.path.join(
|
|
742
|
+
THISPATH, 'deps_files', 'command_order', 'eda.work', f'target_test.{command}'
|
|
743
|
+
)
|
|
734
744
|
with open(os.path.join(work_dir, 'target_echo_hi__shell_0.log'), encoding='utf-8') as f:
|
|
735
745
|
text = ''.join(f.readlines()).strip()
|
|
736
|
-
assert text
|
|
746
|
+
assert text in ['hi', '"hi"', '\\"hi\\"']
|
|
737
747
|
# Added check, one of the targets uses a custom 'tee' file name, instead of the default log.
|
|
738
748
|
with open(os.path.join(work_dir, 'custom_tee_echo_bye.log'), encoding='utf-8') as f:
|
|
739
749
|
text = ''.join(f.readlines()).strip()
|
|
740
|
-
assert text
|
|
750
|
+
assert text in ['bye', '"bye"', '\\"bye\\"']
|
|
741
751
|
|
|
742
752
|
|
|
743
753
|
@pytest.mark.skipif('verilator' not in tools_loaded, reason="requires verilator")
|
|
@@ -896,9 +906,9 @@ class TestDepsTags(Helpers):
|
|
|
896
906
|
# b/c that should only apply in 'verilator' for this target.)
|
|
897
907
|
exec_lines = self.get_log_lines_with('exec: ', logfile=logfile)
|
|
898
908
|
assert len(exec_lines) == 3
|
|
899
|
-
assert 'xvlog
|
|
900
|
-
assert 'xelab
|
|
901
|
-
assert 'xsim
|
|
909
|
+
assert 'xvlog' in exec_lines[0]
|
|
910
|
+
assert 'xelab' in exec_lines[1]
|
|
911
|
+
assert 'xsim' in exec_lines[2]
|
|
902
912
|
assert not self.is_in_log('--lint-only', logfile=logfile)
|
|
903
913
|
|
|
904
914
|
|
opencos/tools/invio_yosys.py
CHANGED
|
@@ -51,7 +51,7 @@ class CommandSynthInvioYosys(CommonSynthYosys, ToolInvioYosys):
|
|
|
51
51
|
'invio-blackbox': 'List of modules that invio will blackbox prior to yosys',
|
|
52
52
|
})
|
|
53
53
|
|
|
54
|
-
def write_and_run_yosys_f_files(self
|
|
54
|
+
def write_and_run_yosys_f_files(self) -> None:
|
|
55
55
|
|
|
56
56
|
# Use helper module for Invio/Verific to save out Verilog-2001 from our
|
|
57
57
|
# Verilog + SystemVerilog + VHDL file lists.
|
|
@@ -104,7 +104,7 @@ class CommandSynthInvioYosys(CommonSynthYosys, ToolInvioYosys):
|
|
|
104
104
|
)
|
|
105
105
|
|
|
106
106
|
# Optinally create and run a sta.f:
|
|
107
|
-
|
|
107
|
+
sta_command_lists = self.create_sta_f() # [] or [util.ShellCommandList]
|
|
108
108
|
|
|
109
109
|
# We create a run_yosys.sh wrapping these scripts, but we do not run this one.
|
|
110
110
|
util.write_shell_command_file(
|
|
@@ -122,8 +122,7 @@ class CommandSynthInvioYosys(CommonSynthYosys, ToolInvioYosys):
|
|
|
122
122
|
# Gives us bash commands with tee and pipstatus:
|
|
123
123
|
invio_command_list,
|
|
124
124
|
synth_command_list,
|
|
125
|
-
|
|
126
|
-
],
|
|
125
|
+
] + sta_command_lists,
|
|
127
126
|
)
|
|
128
127
|
|
|
129
128
|
# Do not run this if args['stop-before-compile'] is True
|
|
@@ -135,9 +134,10 @@ class CommandSynthInvioYosys(CommonSynthYosys, ToolInvioYosys):
|
|
|
135
134
|
self.exec( work_dir=work_dir, command_list=synth_command_list,
|
|
136
135
|
tee_fpath=synth_command_list.tee_fpath )
|
|
137
136
|
|
|
138
|
-
|
|
139
|
-
self.
|
|
140
|
-
|
|
137
|
+
for x in sta_command_lists:
|
|
138
|
+
if self.args['sta'] and x:
|
|
139
|
+
self.exec(work_dir=self.full_work_dir, command_list=x,
|
|
140
|
+
tee_fpath=x.tee_fpath)
|
|
141
141
|
|
|
142
142
|
if self.status == 0:
|
|
143
143
|
util.info(f'yosys: wrote verilog to {self.yosys_v_path}')
|