opencos-eda 0.3.16__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 +2 -2
- opencos/deps/deps_file.py +20 -7
- opencos/deps/deps_processor.py +3 -3
- opencos/eda.py +29 -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 +1 -1
- 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/utils/docker_checks.py +224 -0
- opencos/utils/subprocess_helpers.py +3 -1
- {opencos_eda-0.3.16.dist-info → opencos_eda-0.3.17.dist-info}/METADATA +1 -1
- {opencos_eda-0.3.16.dist-info → opencos_eda-0.3.17.dist-info}/RECORD +35 -34
- {opencos_eda-0.3.16.dist-info → opencos_eda-0.3.17.dist-info}/WHEEL +0 -0
- {opencos_eda-0.3.16.dist-info → opencos_eda-0.3.17.dist-info}/entry_points.txt +0 -0
- {opencos_eda-0.3.16.dist-info → opencos_eda-0.3.17.dist-info}/licenses/LICENSE +0 -0
- {opencos_eda-0.3.16.dist-info → opencos_eda-0.3.17.dist-info}/licenses/LICENSE.spdx +0 -0
- {opencos_eda-0.3.16.dist-info → opencos_eda-0.3.17.dist-info}/top_level.txt +0 -0
opencos/eda_base.py
CHANGED
|
@@ -203,7 +203,7 @@ class Tool:
|
|
|
203
203
|
})
|
|
204
204
|
# update self._EXE if config says to:
|
|
205
205
|
self.set_exe(config)
|
|
206
|
-
self.get_versions()
|
|
206
|
+
self.get_versions(quiet=True)
|
|
207
207
|
|
|
208
208
|
if getattr(self, 'set_tool_config_from_config', None):
|
|
209
209
|
# Hook for classes like CommandSim that have a Tool derived class attached:
|
|
@@ -233,7 +233,9 @@ class Tool:
|
|
|
233
233
|
self.get_versions()
|
|
234
234
|
return str(self._TOOL) + ':' + str(self._VERSION)
|
|
235
235
|
|
|
236
|
-
def get_versions(
|
|
236
|
+
def get_versions( # pylint: disable=unused-argument
|
|
237
|
+
self, **kwargs
|
|
238
|
+
) -> str:
|
|
237
239
|
'''Sets and returns self._VERSION. Note that this is overriden by nearly all
|
|
238
240
|
|
|
239
241
|
child Tool classes, and should limit info/warning messaging since it is part
|
|
@@ -1256,7 +1258,9 @@ class CommandDesign(Command): # pylint: disable=too-many-instance-attributes
|
|
|
1256
1258
|
# cached_deps: key = abspath of DEPS markup file, value is a dict with
|
|
1257
1259
|
# keys 'data' and 'line_numbers'
|
|
1258
1260
|
self.cached_deps = {}
|
|
1259
|
-
|
|
1261
|
+
|
|
1262
|
+
# targets_dict: key = str(Path(target)), value is DepsFile caller_info, or path
|
|
1263
|
+
self.targets_dict = {}
|
|
1260
1264
|
|
|
1261
1265
|
self.has_pre_compile_dep_shell_commands = False
|
|
1262
1266
|
self.has_post_tool_dep_shell_commands = False
|
|
@@ -1717,11 +1721,11 @@ class CommandDesign(Command): # pylint: disable=too-many-instance-attributes
|
|
|
1717
1721
|
|
|
1718
1722
|
self.target_path, self.target = os.path.split(target)
|
|
1719
1723
|
|
|
1720
|
-
if target in self.targets_dict:
|
|
1724
|
+
if str(Path(target)) in self.targets_dict:
|
|
1721
1725
|
# If we're encountered this target before, stop. We're not traversing again.
|
|
1722
1726
|
return True
|
|
1723
1727
|
|
|
1724
|
-
self.targets_dict[target] =
|
|
1728
|
+
self.targets_dict[str(Path(target))] = f'({self.target_path})'
|
|
1725
1729
|
file_exists, fpath, forced_extension = files.get_source_file(target)
|
|
1726
1730
|
if file_exists:
|
|
1727
1731
|
# If the target is a file (we're at the root here processing CLI arg tokens)
|
|
@@ -1748,6 +1752,7 @@ class CommandDesign(Command): # pylint: disable=too-many-instance-attributes
|
|
|
1748
1752
|
|
|
1749
1753
|
deps, data, deps_file = None, None, None
|
|
1750
1754
|
found_deps_file = False
|
|
1755
|
+
found_caller_info = ''
|
|
1751
1756
|
|
|
1752
1757
|
if self.config['deps_markup_supported']:
|
|
1753
1758
|
deps = DepsFile(
|
|
@@ -1763,6 +1768,7 @@ class CommandDesign(Command): # pylint: disable=too-many-instance-attributes
|
|
|
1763
1768
|
|
|
1764
1769
|
if found_deps_file and found_target:
|
|
1765
1770
|
|
|
1771
|
+
found_caller_info = deps.gen_caller_info(target_node)
|
|
1766
1772
|
entry = deps.get_entry(target_node=target_node)
|
|
1767
1773
|
|
|
1768
1774
|
# For convenience, use an external class for this DEPS.yml table/dict
|
|
@@ -1799,7 +1805,7 @@ class CommandDesign(Command): # pylint: disable=too-many-instance-attributes
|
|
|
1799
1805
|
caller_info=caller_info )
|
|
1800
1806
|
|
|
1801
1807
|
elif x and x not in self.targets_dict:
|
|
1802
|
-
self.targets_dict[x] = None # add it before processing.
|
|
1808
|
+
self.targets_dict[str(Path(x))] = None # add it before processing.
|
|
1803
1809
|
file_exists, fpath, forced_extension = files.get_source_file(x)
|
|
1804
1810
|
if file_exists:
|
|
1805
1811
|
self.add_file(filename=fpath, caller_info=caller_info,
|
|
@@ -1810,7 +1816,6 @@ class CommandDesign(Command): # pylint: disable=too-many-instance-attributes
|
|
|
1810
1816
|
x, no_recursion, caller_info=caller_info
|
|
1811
1817
|
)
|
|
1812
1818
|
|
|
1813
|
-
|
|
1814
1819
|
# Done with DEPS.yml if it existed.
|
|
1815
1820
|
|
|
1816
1821
|
if not found_target:
|
|
@@ -1825,6 +1830,7 @@ class CommandDesign(Command): # pylint: disable=too-many-instance-attributes
|
|
|
1825
1830
|
if os.path.exists(try_file):
|
|
1826
1831
|
self.add_file(try_file, caller_info=f'n/a::{target}::n/a')
|
|
1827
1832
|
found_target = True
|
|
1833
|
+
found_caller_info = os.path.split(try_file)[0]
|
|
1828
1834
|
break # move on to the next target
|
|
1829
1835
|
if not found_target and error_on_not_found: # if STILL not found_this_target...
|
|
1830
1836
|
# allow this if --no-error-unknown-args:
|
|
@@ -1835,6 +1841,8 @@ class CommandDesign(Command): # pylint: disable=too-many-instance-attributes
|
|
|
1835
1841
|
)
|
|
1836
1842
|
|
|
1837
1843
|
# if we've found any target since being called, it means we found the one we were called for
|
|
1844
|
+
if found_target:
|
|
1845
|
+
self.targets_dict[str(Path(target))] = f'({found_caller_info})'
|
|
1838
1846
|
return found_target
|
|
1839
1847
|
|
|
1840
1848
|
def add_file( # pylint: disable=too-many-locals,too-many-branches,too-many-statements
|
|
@@ -2107,10 +2115,14 @@ class CommandDesign(Command): # pylint: disable=too-many-instance-attributes
|
|
|
2107
2115
|
# (not from DEPS.yml) we need to override self.target if that was set. Otherwise
|
|
2108
2116
|
# it won't save to the correct work-dir:
|
|
2109
2117
|
self.target = self.args['top']
|
|
2118
|
+
self.targets_dict[str(Path(self.target_path) / self.target)] = (
|
|
2119
|
+
f'({os.path.split(top_path)[-1]})'
|
|
2120
|
+
)
|
|
2110
2121
|
self.top_details['inferred_from_last_added_source_file'] = top_path
|
|
2111
2122
|
|
|
2112
|
-
|
|
2113
|
-
util.info(f'{self.command_name}: top-most target name: {self.target}'
|
|
2123
|
+
caller_info_lookup = str(Path(self.target_path) / self.target)
|
|
2124
|
+
util.info(f'{self.command_name}: top-most target name: {self.target}',
|
|
2125
|
+
f'{self.targets_dict.get(caller_info_lookup, "")}')
|
|
2114
2126
|
|
|
2115
2127
|
if self.error_on_missing_top and not self.args.get('top', ''):
|
|
2116
2128
|
self.error("Did not get a --top or DEPS top, required to run command",
|
opencos/eda_config.py
CHANGED
|
@@ -55,7 +55,7 @@ class Defaults:
|
|
|
55
55
|
'auto_tools_order',
|
|
56
56
|
])
|
|
57
57
|
supported_config_tool_keys = set([
|
|
58
|
-
'exe', 'handlers',
|
|
58
|
+
'exe', 'docker_support', 'handlers',
|
|
59
59
|
'requires_env', 'requires_py', 'requires_cmd', 'requires_in_exe_path',
|
|
60
60
|
'requires_vsim_helper',
|
|
61
61
|
'requires_vscode_extension',
|
|
@@ -76,6 +76,7 @@ class Defaults:
|
|
|
76
76
|
'simulate-waivers',
|
|
77
77
|
'simulate-coverage-tcl',
|
|
78
78
|
'coverage-args',
|
|
79
|
+
'tcl-command-args',
|
|
79
80
|
])
|
|
80
81
|
|
|
81
82
|
EDA_OUTPUT_CONFIG_FNAME = 'eda_output_config.yml'
|
opencos/eda_config_defaults.yml
CHANGED
|
@@ -47,7 +47,7 @@ DEFAULT_HANDLERS_HELP:
|
|
|
47
47
|
targets: List all possible targets given glob path.
|
|
48
48
|
lec: Run equivalence on two designs.
|
|
49
49
|
deps-help: Provide help about DEPS markup files, or schema using --verbose or --help.
|
|
50
|
-
help: This help (without args), or i.e. "eda help
|
|
50
|
+
help: This help (without args), or i.e. "eda sim --help [--tool=TOOL]" for specific help.
|
|
51
51
|
|
|
52
52
|
|
|
53
53
|
|
|
@@ -270,6 +270,7 @@ tools:
|
|
|
270
270
|
|
|
271
271
|
verilator:
|
|
272
272
|
exe: verilator
|
|
273
|
+
docker_support: true
|
|
273
274
|
handlers:
|
|
274
275
|
lint: opencos.tools.verilator.VerilatorLint
|
|
275
276
|
elab: opencos.tools.verilator.VerilatorElab
|
|
@@ -682,9 +683,15 @@ tools:
|
|
|
682
683
|
requires_cmd:
|
|
683
684
|
- yosys -m slang
|
|
684
685
|
handlers:
|
|
686
|
+
flist: opencos.tools.slang_yosys.CommandFListSlangYosys
|
|
685
687
|
elab: opencos.tools.slang_yosys.CommandElabSlangYosys
|
|
686
688
|
synth: opencos.tools.slang_yosys.CommandSynthSlangYosys
|
|
687
689
|
lec: opencos.tools.slang_yosys.CommandLecSlangYosys
|
|
690
|
+
tcl-command-args:
|
|
691
|
+
read_slang:
|
|
692
|
+
- --ignore-unknown-modules
|
|
693
|
+
- --best-effort-hierarchy
|
|
694
|
+
- --ignore-timing
|
|
688
695
|
defines:
|
|
689
696
|
OC_TOOL_YOSYS: null
|
|
690
697
|
OC_TOOL_SLANG: null
|
|
@@ -717,5 +724,6 @@ tools:
|
|
|
717
724
|
requires_cmd:
|
|
718
725
|
- yosys
|
|
719
726
|
handlers:
|
|
727
|
+
flist: opencos.tools.slang_yosys.CommonFListYosys
|
|
720
728
|
synth: opencos.tools.slang_yosys.CommonSynthYosys
|
|
721
729
|
lec: opencos.tools.slang_yosys.CommandLecYosys
|
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
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
|
+
})
|