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.
Files changed (35) hide show
  1. opencos/commands/flist.py +143 -88
  2. opencos/commands/shell.py +1 -1
  3. opencos/commands/sim.py +21 -8
  4. opencos/commands/waves.py +3 -1
  5. opencos/deps/defaults.py +2 -2
  6. opencos/deps/deps_file.py +20 -7
  7. opencos/deps/deps_processor.py +3 -3
  8. opencos/eda.py +29 -5
  9. opencos/eda_base.py +21 -9
  10. opencos/eda_config.py +2 -1
  11. opencos/eda_config_defaults.yml +9 -1
  12. opencos/eda_tool_helper.py +84 -9
  13. opencos/files.py +41 -0
  14. opencos/tools/cocotb.py +1 -1
  15. opencos/tools/invio.py +1 -1
  16. opencos/tools/invio_yosys.py +1 -1
  17. opencos/tools/iverilog.py +1 -1
  18. opencos/tools/quartus.py +1 -1
  19. opencos/tools/questa_common.py +6 -3
  20. opencos/tools/riviera.py +1 -1
  21. opencos/tools/slang.py +1 -1
  22. opencos/tools/slang_yosys.py +36 -8
  23. opencos/tools/surelog.py +1 -1
  24. opencos/tools/verilator.py +209 -20
  25. opencos/tools/vivado.py +1 -1
  26. opencos/tools/yosys.py +155 -30
  27. opencos/utils/docker_checks.py +224 -0
  28. opencos/utils/subprocess_helpers.py +3 -1
  29. {opencos_eda-0.3.16.dist-info → opencos_eda-0.3.17.dist-info}/METADATA +1 -1
  30. {opencos_eda-0.3.16.dist-info → opencos_eda-0.3.17.dist-info}/RECORD +35 -34
  31. {opencos_eda-0.3.16.dist-info → opencos_eda-0.3.17.dist-info}/WHEEL +0 -0
  32. {opencos_eda-0.3.16.dist-info → opencos_eda-0.3.17.dist-info}/entry_points.txt +0 -0
  33. {opencos_eda-0.3.16.dist-info → opencos_eda-0.3.17.dist-info}/licenses/LICENSE +0 -0
  34. {opencos_eda-0.3.16.dist-info → opencos_eda-0.3.17.dist-info}/licenses/LICENSE.spdx +0 -0
  35. {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(self) -> str:
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
- self.targets_dict = {} # key = targets that we've already processed in DEPS files
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] = None
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'
@@ -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 sim" for specific 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
@@ -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(config=None, tools_loaded=None) -> dict:
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
- if config is None or tools_loaded is None:
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
- tools_rows, return_as_2d_list=True, header_row_centered=False)):
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 tool (index 0)
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
@@ -25,7 +25,7 @@ class ToolCocotb(Tool):
25
25
  cocotb_version = ''
26
26
  python_exe = ''
27
27
 
28
- def get_versions(self) -> str:
28
+ def get_versions(self, **kwargs) -> str:
29
29
  if self._VERSION:
30
30
  return self._VERSION
31
31
 
opencos/tools/invio.py CHANGED
@@ -17,7 +17,7 @@ class ToolInvio(Tool):
17
17
  _TOOL = 'invio'
18
18
  _URL = 'https://www.verific.com/products/invio/'
19
19
 
20
- def get_versions(self) -> str:
20
+ def get_versions(self, **kwargs) -> str:
21
21
  if self._VERSION:
22
22
  return self._VERSION
23
23
 
@@ -19,7 +19,7 @@ class ToolInvioYosys(ToolYosys):
19
19
  _URL = 'https://www.verific.com/products/invio/'
20
20
  _EXE = 'yosys'
21
21
 
22
- def get_versions(self) -> str:
22
+ def get_versions(self, **kwargs) -> str:
23
23
  if self._VERSION:
24
24
  return self._VERSION
25
25
 
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
@@ -91,7 +91,7 @@ class ToolQuartus(Tool):
91
91
  util.debug("Could not determine Quartus version from: quartus_sh --version")
92
92
 
93
93
 
94
- def get_versions(self) -> str:
94
+ def get_versions(self, **kwargs) -> str:
95
95
  if self._VERSION:
96
96
  return self._VERSION
97
97
 
@@ -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(1))
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)
opencos/tools/slang.py CHANGED
@@ -27,7 +27,7 @@ class ToolSlang(Tool):
27
27
  slang_tidy_exe = ''
28
28
  slang_hier_exe = ''
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)
@@ -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
- 'read_slang',
103
- '--ignore-unknown-modules',
104
- '--best-effort-hierarchy',
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
+ })
opencos/tools/surelog.py CHANGED
@@ -22,7 +22,7 @@ class ToolSurelog(Tool):
22
22
 
23
23
  surelog_exe = ''
24
24
 
25
- def get_versions(self) -> str:
25
+ def get_versions(self, **kwargs) -> str:
26
26
  if self._VERSION:
27
27
  return self._VERSION
28
28
  path = safe_shutil_which(self._EXE)