opencos-eda 0.3.15__py3-none-any.whl → 0.3.17__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 +143 -88
- opencos/commands/shell.py +1 -1
- opencos/commands/sim.py +21 -8
- opencos/commands/waves.py +3 -1
- opencos/deps/defaults.py +6 -2
- opencos/deps/deps_file.py +30 -9
- opencos/deps/deps_processor.py +99 -65
- opencos/deps_schema.py +8 -0
- opencos/docs/DEPS.md +6 -0
- opencos/eda.py +30 -5
- opencos/eda_base.py +21 -9
- opencos/eda_config.py +2 -1
- opencos/eda_config_defaults.yml +9 -1
- opencos/eda_tool_helper.py +84 -9
- opencos/files.py +41 -0
- opencos/tools/cocotb.py +1 -1
- opencos/tools/invio.py +1 -1
- opencos/tools/invio_yosys.py +1 -1
- opencos/tools/iverilog.py +1 -1
- opencos/tools/quartus.py +1 -1
- opencos/tools/questa_common.py +6 -3
- opencos/tools/riviera.py +5 -3
- opencos/tools/slang.py +1 -1
- opencos/tools/slang_yosys.py +36 -8
- opencos/tools/surelog.py +1 -1
- opencos/tools/verilator.py +209 -20
- opencos/tools/vivado.py +1 -1
- opencos/tools/yosys.py +155 -30
- opencos/util.py +5 -1
- opencos/utils/docker_checks.py +224 -0
- opencos/utils/subprocess_helpers.py +3 -1
- {opencos_eda-0.3.15.dist-info → opencos_eda-0.3.17.dist-info}/METADATA +1 -1
- {opencos_eda-0.3.15.dist-info → opencos_eda-0.3.17.dist-info}/RECORD +38 -37
- {opencos_eda-0.3.15.dist-info → opencos_eda-0.3.17.dist-info}/WHEEL +0 -0
- {opencos_eda-0.3.15.dist-info → opencos_eda-0.3.17.dist-info}/entry_points.txt +0 -0
- {opencos_eda-0.3.15.dist-info → opencos_eda-0.3.17.dist-info}/licenses/LICENSE +0 -0
- {opencos_eda-0.3.15.dist-info → opencos_eda-0.3.17.dist-info}/licenses/LICENSE.spdx +0 -0
- {opencos_eda-0.3.15.dist-info → opencos_eda-0.3.17.dist-info}/top_level.txt +0 -0
opencos/eda_tool_helper.py
CHANGED
|
@@ -14,12 +14,25 @@ from importlib import import_module
|
|
|
14
14
|
|
|
15
15
|
from opencos import eda_config, util
|
|
16
16
|
from opencos.util import Colors
|
|
17
|
-
from opencos.utils import str_helpers
|
|
17
|
+
from opencos.utils import str_helpers, docker_checks
|
|
18
|
+
|
|
19
|
+
# Used by pytest, so we can skip tests if tools aren't present, also used by
|
|
20
|
+
# opencos.eda itself
|
|
21
|
+
|
|
22
|
+
DOCKER_TOOL_NOTE = ' [via docker] '
|
|
23
|
+
|
|
24
|
+
def tool_loaded_but_flagged_as_docker_only(tool: str, config: dict) -> bool:
|
|
25
|
+
'''Checks if a tool was flagged in config as only being able to run
|
|
26
|
+
|
|
27
|
+
via docker (and wasn't availble in PATH, shutil.which(..))
|
|
28
|
+
'''
|
|
29
|
+
exe = config.get('auto_tools_found', {}).get(tool, '')
|
|
30
|
+
return exe and exe == DOCKER_TOOL_NOTE
|
|
18
31
|
|
|
19
|
-
# Used by pytest, so we can skip tests if tools aren't present.
|
|
20
32
|
|
|
21
33
|
def get_config_and_tools_loaded( # pylint: disable=dangerous-default-value
|
|
22
|
-
quiet: bool = False, args: list = []
|
|
34
|
+
quiet: bool = False, args: list = [],
|
|
35
|
+
remove_tools_with_docker_note: bool = True
|
|
23
36
|
) -> (dict, set):
|
|
24
37
|
'''Returns config dict and list tools_loaded, given the found config.
|
|
25
38
|
|
|
@@ -36,14 +49,34 @@ def get_config_and_tools_loaded( # pylint: disable=dangerous-default-value
|
|
|
36
49
|
config = eda.init_config(config=config, quiet=quiet)
|
|
37
50
|
tools_loaded = config.get('tools_loaded', []).copy()
|
|
38
51
|
|
|
52
|
+
# Remove any tools from tools_loaded that had their exe flagged with
|
|
53
|
+
# str DOCKER_TOOL_NOTE. This is also for pytests so that heplers.can_run_eda_sim()
|
|
54
|
+
# returns False if it were to otherwise appear that we can run verilator
|
|
55
|
+
# (but only with --docker flag set, that majority of test do not)
|
|
56
|
+
if remove_tools_with_docker_note:
|
|
57
|
+
for _tool in list(tools_loaded):
|
|
58
|
+
if tool_loaded_but_flagged_as_docker_only(tool=_tool, config=config):
|
|
59
|
+
tools_loaded.remove(_tool)
|
|
60
|
+
|
|
39
61
|
return config, tools_loaded
|
|
40
62
|
|
|
41
63
|
|
|
42
|
-
def get_all_handler_commands(
|
|
64
|
+
def get_all_handler_commands( # pylint: disable=too-many-branches
|
|
65
|
+
config=None, tools_loaded=None, auto_tools_only: bool = False
|
|
66
|
+
) -> dict:
|
|
43
67
|
'''Given a config and tools_loaded (or if not supplied uses defaults) returns a dict
|
|
44
68
|
|
|
45
69
|
of { <command>: [list of tools that run that command, in auto-tool-order] }.
|
|
46
70
|
|
|
71
|
+
If you use auto_tools_only=True, then you will only get command: tools that exist in
|
|
72
|
+
config['auto_tools_order'] (and are loaded tools). This would be what you'd get if you
|
|
73
|
+
ran `eda [command]` and did not set the --tool=TOOL arg, for what can run that command.
|
|
74
|
+
|
|
75
|
+
If you use auto_tools_only=False (default), you will get the auto tool order +
|
|
76
|
+
tool handlers (loaded tools only) that can service the command. For example, eda waves,
|
|
77
|
+
eda upload, and others may not be in the auto tool list for an `eda [command]`, because
|
|
78
|
+
those command have to self determine their tool.
|
|
79
|
+
|
|
47
80
|
For example:
|
|
48
81
|
{ "sim": ["verilator", "vivado"],
|
|
49
82
|
"elab": ["slang", "verilator", ...], ...
|
|
@@ -51,9 +84,14 @@ def get_all_handler_commands(config=None, tools_loaded=None) -> dict:
|
|
|
51
84
|
'''
|
|
52
85
|
all_handler_commands = {}
|
|
53
86
|
|
|
54
|
-
|
|
87
|
+
|
|
88
|
+
if config and tools_loaded is None:
|
|
89
|
+
tools_loaded = config.get('tools_loaded', [])
|
|
90
|
+
elif config is None or tools_loaded is None:
|
|
91
|
+
# This will re-run eda.init_config(..), b/c you didn't bring a config.
|
|
55
92
|
config, tools_loaded = get_config_and_tools_loaded()
|
|
56
93
|
|
|
94
|
+
|
|
57
95
|
assert isinstance(config, dict)
|
|
58
96
|
assert isinstance(tools_loaded, list)
|
|
59
97
|
|
|
@@ -80,6 +118,19 @@ def get_all_handler_commands(config=None, tools_loaded=None) -> dict:
|
|
|
80
118
|
else:
|
|
81
119
|
all_handler_commands[command].append(tool)
|
|
82
120
|
|
|
121
|
+
if auto_tools_only:
|
|
122
|
+
# skip the next step and return what we've got.
|
|
123
|
+
return all_handler_commands
|
|
124
|
+
|
|
125
|
+
# Note that the above will miss commands (waves, upload) that are not in auto_tools_order
|
|
126
|
+
for tool in tools_loaded:
|
|
127
|
+
handlers = config.get('tools', {}).get(tool, {}).get('handlers', {})
|
|
128
|
+
for command, _ in handlers.items():
|
|
129
|
+
if command not in all_handler_commands:
|
|
130
|
+
all_handler_commands[command] = list([tool])
|
|
131
|
+
elif tool not in all_handler_commands.get(command, []):
|
|
132
|
+
all_handler_commands[command].append(tool)
|
|
133
|
+
|
|
83
134
|
return all_handler_commands
|
|
84
135
|
|
|
85
136
|
|
|
@@ -90,6 +141,9 @@ def get_handler_tool_version(tool: str, eda_command: str, config: dict) -> str:
|
|
|
90
141
|
if not entry:
|
|
91
142
|
return ''
|
|
92
143
|
|
|
144
|
+
# TODO(drew): maybe if there are no handlers, then do something to get version?
|
|
145
|
+
# like, offer tool_class?
|
|
146
|
+
|
|
93
147
|
handler_name = entry.get('handlers', {}).get(eda_command, '')
|
|
94
148
|
if not handler_name:
|
|
95
149
|
return ''
|
|
@@ -246,13 +300,34 @@ def pretty_info_handler_tools( # pylint: disable=dangerous-default-value
|
|
|
246
300
|
else:
|
|
247
301
|
tools_rows.append([_tool, path])
|
|
248
302
|
|
|
303
|
+
# Since Docker isn't technically a tool, and only lives in opencos.utils.docker_checks,
|
|
304
|
+
# we would like to still list it here (as "other") to at least show path/version information.
|
|
305
|
+
# This also makes it less surprising if an actual tool has their PATH set with ' [via docker] '
|
|
306
|
+
# (DOCKER_TOOL_NOTE str)
|
|
307
|
+
other_rows = [
|
|
308
|
+
['--Detected other--', '--Path--']
|
|
309
|
+
]
|
|
310
|
+
if show_versions:
|
|
311
|
+
other_rows[0].append('--Version--')
|
|
312
|
+
if docker_checks.DOCKER_EXE:
|
|
313
|
+
other_rows.append([
|
|
314
|
+
'docker',
|
|
315
|
+
docker_checks.DOCKER_EXE,
|
|
316
|
+
docker_checks.get_docker_version()
|
|
317
|
+
])
|
|
318
|
+
|
|
319
|
+
# Finally, print everything
|
|
320
|
+
pretty_info_tool_rows(tools_rows + other_rows)
|
|
321
|
+
|
|
322
|
+
|
|
323
|
+
def pretty_info_tool_rows(rows: list) -> None:
|
|
324
|
+
'''Pretty print a 2-d list, called by pretty_info_handler_tools'''
|
|
249
325
|
|
|
250
|
-
# Finally, print detected tools:
|
|
251
326
|
for rownum, row in enumerate(str_helpers.pretty_2dlist_columns(
|
|
252
|
-
|
|
253
|
-
if rownum == 0:
|
|
327
|
+
rows, return_as_2d_list=True, header_row_centered=False)):
|
|
328
|
+
if rownum == 0 or row[0].startswith('--'):
|
|
254
329
|
util.info(f'{Colors.bgreen}{"".join(row)}')
|
|
255
330
|
else:
|
|
256
|
-
# get the results in a padded 2D list so we can colorize the
|
|
331
|
+
# get the results in a padded 2D list so we can colorize the name (index 0)
|
|
257
332
|
util.info(f'{Colors.bgreen}{row[0]}{Colors.normal}{Colors.cyan}' \
|
|
258
333
|
+ ''.join(row[1:]))
|
opencos/files.py
CHANGED
|
@@ -13,6 +13,7 @@ as part of a verilog $readmemh, etc)
|
|
|
13
13
|
'''
|
|
14
14
|
|
|
15
15
|
import os
|
|
16
|
+
from pathlib import Path
|
|
16
17
|
import shutil
|
|
17
18
|
|
|
18
19
|
# Ways to force files not ending in .sv to be systemverilog (for tools
|
|
@@ -68,6 +69,46 @@ def safe_shutil_which(path: str) -> str:
|
|
|
68
69
|
return found
|
|
69
70
|
return ''
|
|
70
71
|
|
|
72
|
+
|
|
73
|
+
def get_source_files_paths(
|
|
74
|
+
cmd_des_obj, minimal_root_paths: bool = False
|
|
75
|
+
) -> list:
|
|
76
|
+
'''Given a CommandDesign object, returns list of Path objs for all source files
|
|
77
|
+
|
|
78
|
+
If minimal_root_paths=True, attempts to return the minimal set of root paths for
|
|
79
|
+
docker read-only volume mapping
|
|
80
|
+
|
|
81
|
+
will look at members for all source files that (any member list type in cmd_des_obj.files_)
|
|
82
|
+
'''
|
|
83
|
+
|
|
84
|
+
# Iterate over lists and add to our set, we'll include cmd_des_obj.incdirs (list)
|
|
85
|
+
# here as well.
|
|
86
|
+
file_dirs = set()
|
|
87
|
+
for name in [x for x in dir(cmd_des_obj) if x.startswith('files_')]:
|
|
88
|
+
member = getattr(cmd_des_obj, name, None)
|
|
89
|
+
if callable(member) or not isinstance(member, list):
|
|
90
|
+
continue
|
|
91
|
+
_ = [file_dirs.add(Path(x).resolve().parent) for x in member]
|
|
92
|
+
|
|
93
|
+
# also incdirs, a path not a file, so don't use .parent
|
|
94
|
+
_ = [file_dirs.add(Path(incdir).resolve()) for incdir in getattr(cmd_des_obj, 'incdirs', [])]
|
|
95
|
+
|
|
96
|
+
if not minimal_root_paths:
|
|
97
|
+
# return them all:
|
|
98
|
+
return list(file_dirs)
|
|
99
|
+
|
|
100
|
+
for x in list(file_dirs):
|
|
101
|
+
# - Using list(file_dirs) as iterator as cheap copy b/c we're altering set file_dirs
|
|
102
|
+
# - We don't want to add '/' as a root path, so make sure dirs have at least
|
|
103
|
+
# 2 parts if we're going to trim items from file_dirs.
|
|
104
|
+
# - Also this is a n^2 traversal, make sure we aren't matching ourselves (x != y)
|
|
105
|
+
# in check. Using childPath.is_relative_to(parentPath).
|
|
106
|
+
if any(x != y and x.is_relative_to(y) and len(y.parts) >= 2 for y in file_dirs):
|
|
107
|
+
file_dirs.remove(x)
|
|
108
|
+
|
|
109
|
+
return list(file_dirs)
|
|
110
|
+
|
|
111
|
+
|
|
71
112
|
# Note that in Windows, 'python' will return the venv or uv version, 'python3' will
|
|
72
113
|
# return the installed version (which may not be what you want), so we'll prefer
|
|
73
114
|
# 'python':
|
opencos/tools/cocotb.py
CHANGED
opencos/tools/invio.py
CHANGED
opencos/tools/invio_yosys.py
CHANGED
opencos/tools/iverilog.py
CHANGED
|
@@ -23,7 +23,7 @@ class ToolIverilog(Tool):
|
|
|
23
23
|
self.iverilog_exe = ''
|
|
24
24
|
super().__init__(config=config) # calls self.get_versions()
|
|
25
25
|
|
|
26
|
-
def get_versions(self) -> str:
|
|
26
|
+
def get_versions(self, **kwargs) -> str:
|
|
27
27
|
self.iverilog_exe = ''
|
|
28
28
|
if self._VERSION:
|
|
29
29
|
return self._VERSION
|
opencos/tools/quartus.py
CHANGED
opencos/tools/questa_common.py
CHANGED
|
@@ -53,14 +53,16 @@ class ToolQuesta(Tool):
|
|
|
53
53
|
if line.strip().startswith('Release Notes For'):
|
|
54
54
|
m = re.search(r'(\d+)\.(\d+)', line)
|
|
55
55
|
if m:
|
|
56
|
+
_maj, _min = int(m.group(1)), int(m.group(2))
|
|
57
|
+
self._VERSION = f'{_maj}.{_min}'
|
|
56
58
|
if self._TOOL.startswith('questa'):
|
|
57
59
|
# don't set these for ModelsimASE:
|
|
58
|
-
self.questa_major = int(m.group(
|
|
60
|
+
self.questa_major = int(m.group(2))
|
|
59
61
|
self.questa_minor = int(m.group(2))
|
|
60
|
-
self._VERSION = str(self.questa_major) + '.' + str(self.questa_minor)
|
|
61
62
|
util.debug(f'version {self._VERSION} ({release_notes_txt_filepath})')
|
|
62
63
|
break
|
|
63
64
|
|
|
65
|
+
|
|
64
66
|
def _try_set_version_from_path(self) -> None:
|
|
65
67
|
'''Attempts to use portions of exe path to get version info
|
|
66
68
|
|
|
@@ -75,7 +77,7 @@ class ToolQuesta(Tool):
|
|
|
75
77
|
util.warning("Questa path doesn't specificy version, expecting (d+.d+)")
|
|
76
78
|
|
|
77
79
|
|
|
78
|
-
def get_versions(self) -> str:
|
|
80
|
+
def get_versions(self, **kwargs) -> str:
|
|
79
81
|
if self._VERSION:
|
|
80
82
|
return self._VERSION
|
|
81
83
|
path = safe_shutil_which(self._EXE)
|
|
@@ -103,6 +105,7 @@ class ToolQuesta(Tool):
|
|
|
103
105
|
|
|
104
106
|
return self._VERSION
|
|
105
107
|
|
|
108
|
+
|
|
106
109
|
def set_tool_defines(self) -> None:
|
|
107
110
|
'''Override from class Tool, which handles picking up config['tools'][self._TOOL]
|
|
108
111
|
|
opencos/tools/riviera.py
CHANGED
|
@@ -27,7 +27,7 @@ class ToolRiviera(ToolQuesta):
|
|
|
27
27
|
use_vopt = False
|
|
28
28
|
uvm_versions = set()
|
|
29
29
|
|
|
30
|
-
def get_versions(self) -> str:
|
|
30
|
+
def get_versions(self, **kwargs) -> str:
|
|
31
31
|
if self._VERSION:
|
|
32
32
|
return self._VERSION
|
|
33
33
|
path = safe_shutil_which(self._EXE)
|
|
@@ -102,8 +102,10 @@ class CommandSimRiviera(CommonSimQuesta, ToolRiviera):
|
|
|
102
102
|
' tcl steps are (from tool config in --config-yml): '
|
|
103
103
|
) + '; '.join(self.tool_config.get('simulate-coverage-tcl', [])),
|
|
104
104
|
'uvm': (
|
|
105
|
-
'Attempts to support UVM.
|
|
106
|
-
'
|
|
105
|
+
'Attempts to support UVM. For Riviera, this adds "-uvmver NUMBER -dbg" to vlog.f.'
|
|
106
|
+
' You can choose your uvmver using eda arg --uvm-version=NUMBER.'
|
|
107
|
+
' Also adds +access +r to vopt/vsim. There is no -l or -L library modifications,'
|
|
108
|
+
' we rely on Riviera to handle this internally based on running: vlog -uvm'
|
|
107
109
|
),
|
|
108
110
|
'license-queue': (
|
|
109
111
|
'Set to enable env vars (if unset) LICENSE_QUEUE=1, ALDEC_LICENSE_QUEUE=1,'
|
opencos/tools/slang.py
CHANGED
opencos/tools/slang_yosys.py
CHANGED
|
@@ -8,10 +8,10 @@ Contains classes for ToolSlangYosys, CommandSynthSlangYosys
|
|
|
8
8
|
import os
|
|
9
9
|
|
|
10
10
|
from opencos import util
|
|
11
|
-
from opencos.tools.yosys import ToolYosys, CommonSynthYosys, CommandLecYosys
|
|
12
|
-
|
|
11
|
+
from opencos.tools.yosys import ToolYosys, CommonSynthYosys, CommandLecYosys, CommonFListYosys
|
|
13
12
|
from opencos.commands.sim import parameters_dict_get_command_list
|
|
14
13
|
|
|
14
|
+
|
|
15
15
|
class ToolSlangYosys(ToolYosys):
|
|
16
16
|
'''Uses slang.so in yosys plugins directory, called via yosys > plugin -i slang'''
|
|
17
17
|
_TOOL = 'slang_yosys'
|
|
@@ -30,6 +30,15 @@ class CommandSynthSlangYosys(CommonSynthYosys, ToolSlangYosys):
|
|
|
30
30
|
CommonSynthYosys.__init__(self, config)
|
|
31
31
|
ToolSlangYosys.__init__(self, config=self.config)
|
|
32
32
|
|
|
33
|
+
self.args.update({
|
|
34
|
+
'yosys-read-slang-args': []
|
|
35
|
+
})
|
|
36
|
+
self.args_help.update({
|
|
37
|
+
'yosys-read-slang-args': (
|
|
38
|
+
'list-style arg, these are applied to the "read_slang" command'
|
|
39
|
+
)
|
|
40
|
+
})
|
|
41
|
+
|
|
33
42
|
self.slang_out_dir = ''
|
|
34
43
|
self.slang_v_path = ''
|
|
35
44
|
|
|
@@ -98,11 +107,10 @@ class CommandSynthSlangYosys(CommonSynthYosys, ToolSlangYosys):
|
|
|
98
107
|
|
|
99
108
|
def _get_read_slang_cmd_str(self) -> str:
|
|
100
109
|
|
|
101
|
-
read_slang_cmd = [
|
|
102
|
-
'
|
|
103
|
-
'
|
|
104
|
-
|
|
105
|
-
]
|
|
110
|
+
read_slang_cmd = ['read_slang'] + \
|
|
111
|
+
self.args.get('yosys-read-slang-args', []) + \
|
|
112
|
+
self.config.get('tools', {}).get(self._TOOL, {}).get(
|
|
113
|
+
'tcl-command-args', {}).get('read_slang', [])
|
|
106
114
|
|
|
107
115
|
read_slang_cmd += self.get_yosys_read_verilog_defines_incdirs_files()
|
|
108
116
|
|
|
@@ -113,7 +121,6 @@ class CommandSynthSlangYosys(CommonSynthYosys, ToolSlangYosys):
|
|
|
113
121
|
)
|
|
114
122
|
|
|
115
123
|
# In case --top was not set:
|
|
116
|
-
# TODO(drew): Can we skip this if it was an inferred top???
|
|
117
124
|
if not any(x.startswith('--top') for x in read_slang_cmd):
|
|
118
125
|
read_slang_cmd.append(f'--top {self.args["top"]}')
|
|
119
126
|
|
|
@@ -249,3 +256,24 @@ class CommandLecSlangYosys(CommandLecYosys, ToolSlangYosys): # pylint: disable=t
|
|
|
249
256
|
def __init__(self, config: dict):
|
|
250
257
|
CommandLecYosys.__init__(self, config)
|
|
251
258
|
ToolSlangYosys.__init__(self, config=self.config)
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
class CommandFListSlangYosys(CommonFListYosys, ToolSlangYosys):
|
|
262
|
+
'''
|
|
263
|
+
Handler for: eda flist --tool=slang_yosys
|
|
264
|
+
|
|
265
|
+
This will create a eda.yosys_slang.f flist (or to std-out if --print-to-stdout),
|
|
266
|
+
suitable for args passed to a "read_slang" command in yosys, such as:
|
|
267
|
+
|
|
268
|
+
yosys -i slang
|
|
269
|
+
read_slang -f FILE_CREATED_BY_THIS_HANDLER [other args]
|
|
270
|
+
|
|
271
|
+
'''
|
|
272
|
+
def __init__(self, config: dict):
|
|
273
|
+
CommonFListYosys.__init__(self, config=config)
|
|
274
|
+
ToolSlangYosys.__init__(self, config=self.config)
|
|
275
|
+
|
|
276
|
+
self.args.update({
|
|
277
|
+
'force': True, # always overwrite the output
|
|
278
|
+
'out': 'eda_flist.yosys.read_slang.f',
|
|
279
|
+
})
|