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.
Files changed (38) 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 +6 -2
  6. opencos/deps/deps_file.py +30 -9
  7. opencos/deps/deps_processor.py +99 -65
  8. opencos/deps_schema.py +8 -0
  9. opencos/docs/DEPS.md +6 -0
  10. opencos/eda.py +30 -5
  11. opencos/eda_base.py +21 -9
  12. opencos/eda_config.py +2 -1
  13. opencos/eda_config_defaults.yml +9 -1
  14. opencos/eda_tool_helper.py +84 -9
  15. opencos/files.py +41 -0
  16. opencos/tools/cocotb.py +1 -1
  17. opencos/tools/invio.py +1 -1
  18. opencos/tools/invio_yosys.py +1 -1
  19. opencos/tools/iverilog.py +1 -1
  20. opencos/tools/quartus.py +1 -1
  21. opencos/tools/questa_common.py +6 -3
  22. opencos/tools/riviera.py +5 -3
  23. opencos/tools/slang.py +1 -1
  24. opencos/tools/slang_yosys.py +36 -8
  25. opencos/tools/surelog.py +1 -1
  26. opencos/tools/verilator.py +209 -20
  27. opencos/tools/vivado.py +1 -1
  28. opencos/tools/yosys.py +155 -30
  29. opencos/util.py +5 -1
  30. opencos/utils/docker_checks.py +224 -0
  31. opencos/utils/subprocess_helpers.py +3 -1
  32. {opencos_eda-0.3.15.dist-info → opencos_eda-0.3.17.dist-info}/METADATA +1 -1
  33. {opencos_eda-0.3.15.dist-info → opencos_eda-0.3.17.dist-info}/RECORD +38 -37
  34. {opencos_eda-0.3.15.dist-info → opencos_eda-0.3.17.dist-info}/WHEEL +0 -0
  35. {opencos_eda-0.3.15.dist-info → opencos_eda-0.3.17.dist-info}/entry_points.txt +0 -0
  36. {opencos_eda-0.3.15.dist-info → opencos_eda-0.3.17.dist-info}/licenses/LICENSE +0 -0
  37. {opencos_eda-0.3.15.dist-info → opencos_eda-0.3.17.dist-info}/licenses/LICENSE.spdx +0 -0
  38. {opencos_eda-0.3.15.dist-info → opencos_eda-0.3.17.dist-info}/top_level.txt +0 -0
@@ -4,8 +4,11 @@ a DEPS markup files targets (applying deps, reqs, commands, tags, incdirs, defin
4
4
  CommandDesign ref object
5
5
  '''
6
6
 
7
+ # pylint: disable=too-many-lines
8
+
7
9
  import argparse
8
10
  import copy
11
+ import glob
9
12
  import os
10
13
 
11
14
  from opencos import files
@@ -418,7 +421,8 @@ class DepsProcessor: # pylint: disable=too-many-instance-attributes
418
421
  self.process_commands()
419
422
  elif key == 'reqs':
420
423
  self.process_reqs()
421
- elif key == 'deps':
424
+ elif key == 'deps' or \
425
+ (key == 'files' and 'deps' not in self.deps_entry):
422
426
  remaining_deps_list += self.process_deps_return_discovered_deps()
423
427
 
424
428
  if self.command_design_ref.tool_changed_respawn:
@@ -528,10 +532,10 @@ class DepsProcessor: # pylint: disable=too-many-instance-attributes
528
532
  with_args_matched_list = []
529
533
  for k,v in with_args.items():
530
534
  with_args_matched_list.append(False)
531
- if not apply_tag_items_tools:
535
+ if not all((apply_tag_items_tools, apply_tag_items_commands)):
532
536
  # If we didn't previously match with-tools (if with-tools was present),
533
- # then we may not match the args, b/c those are tool dependend in the
534
- # Command handling class.
537
+ # or match with-commands, then we may not match the args, b/c those are
538
+ # command + tool dependent in the Command handling class.
535
539
  pass
536
540
  elif k not in self.command_design_ref.args:
537
541
  warning(f'{tagname=} in {self.caller_info}:',
@@ -613,7 +617,8 @@ class DepsProcessor: # pylint: disable=too-many-instance-attributes
613
617
  deps_file=self.deps_file)
614
618
  self.apply_reqs(reqs_list)
615
619
 
616
- elif key == 'deps':
620
+ elif key == 'deps' or \
621
+ (key == 'files' and 'deps' not in tags_dict_to_apply):
617
622
 
618
623
  # apply deps (includes commands, stray +define+ +incdir+)
619
624
  # treat the same way we treat self.process_deps_return_discovered_deps
@@ -861,6 +866,7 @@ class DepsProcessor: # pylint: disable=too-many-instance-attributes
861
866
  '''Returns list of deps targets to continue processing,
862
867
 
863
868
  -- iterates through 'deps' for this target (self.deps_entry['deps'])
869
+ -- note this will also append 'files' if that table key + list/str exists.
864
870
  -- applies to self.command_design_ref
865
871
  '''
866
872
 
@@ -872,7 +878,9 @@ class DepsProcessor: # pylint: disable=too-many-instance-attributes
872
878
  )
873
879
  return self.get_remaining_and_apply_deps(deps)
874
880
 
875
- def get_remaining_and_apply_deps(self, deps:list) -> list:
881
+ def get_remaining_and_apply_deps(
882
+ self, deps: list
883
+ ) -> list:
876
884
  '''Given a list of deps, process what is supported in a "deps:" table in DEPS
877
885
  markup file.'''
878
886
 
@@ -880,66 +888,98 @@ class DepsProcessor: # pylint: disable=too-many-instance-attributes
880
888
 
881
889
  # Process deps (list)
882
890
  for dep in deps:
891
+ deps_targets_to_resolve.extend(
892
+ self._get_remaining_and_apply_single_dep(dep)
893
+ )
894
+ return deps_targets_to_resolve
895
+
883
896
 
884
- typ = type(dep)
885
- if typ not in SUPPORTED_DEP_KEYS_BY_TYPE:
886
- self.error(f'{self.target_node=} {dep=} in {self.deps_file=}:' \
887
- + f'has unsupported {type(dep)=} {SUPPORTED_DEP_KEYS_BY_TYPE=}')
888
-
889
- for supported_values in SUPPORTED_DEP_KEYS_BY_TYPE.values():
890
- if '*' in supported_values:
891
- continue
892
- if typ in [dict,list] and any(k not in supported_values for k in dep):
893
- self.error(
894
- f'{self.target_node=} {dep=} in {self.deps_file=}: has dict-key or',
895
- f'list-item not in {SUPPORTED_DEP_KEYS_BY_TYPE[typ]=}'
896
- )
897
-
898
- # In-line commands in the deps list, in case the results need to be in strict file
899
- # order for other deps
900
- if isinstance(dep, dict) and 'commands' in dep:
901
-
902
- commands = dep['commands']
903
- debug(f"Got commands {dep=} for in {self.caller_info}, {commands=}")
904
-
905
- assert isinstance(commands, list), \
906
- f'dep commands must be a list: {dep=} in {self.caller_info}'
907
-
908
- # For this, we need to get the returned commands (to keep strict order w/ other
909
- # deps)
910
- command_tuple = self.get_commands( commands=commands, dep=dep )
911
- # TODO(drew): it might be cleaner to return a dict instead of list, b/c those
912
- # are also ordered and we can pass type information, something like:
913
- deps_targets_to_resolve.append(command_tuple)
914
-
915
-
916
- elif isinstance(dep, str) and \
917
- any(dep.startswith(x) for x in ['+define+', '+incdir+']) and \
918
- self.is_command_design:
919
- # Note: we still support +define+ and +incdir in the deps list.
920
- # check for compile-time Verilog style plusarg, which are supported under targets
921
- # These are not run-time Verilog style plusargs comsumable from within the .sv:
922
- debug(f"Got plusarg (define, incdir) {dep=} for {self.caller_info}")
923
- self.command_design_ref.process_plusarg(plusarg=dep, pwd=self.target_path)
897
+ def _get_remaining_and_apply_single_dep( # pylint: disable=too-many-branches
898
+ self, dep: str
899
+ ) -> list:
900
+ '''Given a single dep, process is and return targets/files that need resolving'''
924
901
 
902
+ deps_targets_to_resolve = []
903
+ typ = type(dep)
904
+ if typ not in SUPPORTED_DEP_KEYS_BY_TYPE:
905
+ self.error(f'{self.target_node=} {dep=} in {self.deps_file=}:' \
906
+ + f'has unsupported {type(dep)=} {SUPPORTED_DEP_KEYS_BY_TYPE=}')
907
+
908
+ for supported_values in SUPPORTED_DEP_KEYS_BY_TYPE.values():
909
+ if '*' in supported_values:
910
+ continue
911
+ if typ in [dict,list] and any(k not in supported_values for k in dep):
912
+ self.error(
913
+ f'{self.target_node=} {dep=} in {self.deps_file=}: has dict-key or',
914
+ f'list-item not in {SUPPORTED_DEP_KEYS_BY_TYPE[typ]=}'
915
+ )
916
+
917
+ # In-line commands in the deps list, in case the results need to be in strict file
918
+ # order for other deps
919
+ if isinstance(dep, dict) and 'commands' in dep:
920
+
921
+ commands = dep['commands']
922
+ debug(f"Got commands {dep=} for in {self.caller_info}, {commands=}")
923
+
924
+ assert isinstance(commands, list), \
925
+ f'dep commands must be a list: {dep=} in {self.caller_info}'
926
+
927
+ # For this, we need to get the returned commands (to keep strict order w/ other
928
+ # deps)
929
+ command_tuple = self.get_commands( commands=commands, dep=dep )
930
+ # TODO(drew): it might be cleaner to return a dict instead of list, b/c those
931
+ # are also ordered and we can pass type information, something like:
932
+ deps_targets_to_resolve.append(command_tuple)
933
+
934
+
935
+ elif isinstance(dep, str) and \
936
+ any(dep.startswith(x) for x in ['+define+', '+incdir+']) and \
937
+ self.is_command_design:
938
+ # Note: we still support +define+ and +incdir in the deps list.
939
+ # check for compile-time Verilog style plusarg, which are supported under targets
940
+ # These are not run-time Verilog style plusargs comsumable from within the .sv:
941
+ debug(f"Got plusarg (define, incdir) {dep=} for {self.caller_info}")
942
+ self.command_design_ref.process_plusarg(plusarg=dep, pwd=self.target_path)
943
+
944
+ else:
945
+ # If we made it this far, dep better be a str type.
946
+ assert isinstance(dep, str), f'{dep=} {type(dep)=} must be str'
947
+ dep_path = self.correct_a_deps_target(target=dep, deps_dir=self.target_path)
948
+ debug(f"Got dep {dep_path=} for in {self.caller_info}")
949
+
950
+ if self.is_command_design and \
951
+ dep_path in self.command_design_ref.targets_dict or \
952
+ dep_path in deps_targets_to_resolve:
953
+ debug(f" - already processed ({dep_path}), skipping")
925
954
  else:
926
- # If we made it this far, dep better be a str type.
927
- assert isinstance(dep, str), f'{dep=} {type(dep)=} must be str'
928
- dep_path = self.correct_a_deps_target(target=dep, deps_dir=self.target_path)
929
- debug(f"Got dep {dep_path=} for in {self.caller_info}")
930
-
931
- if self.is_command_design and \
932
- dep_path in self.command_design_ref.targets_dict or \
933
- dep_path in deps_targets_to_resolve:
934
- debug(" - already processed, skipping")
935
- else:
955
+ # This is where we support files/deps file wildcards via glob syntax.
956
+ # If glob found none, we fall back to a single-file or target
957
+ glob_added_from_dep_path = False
958
+ if any(x in dep_path for x in ('*', '?', '[')):
959
+ try:
960
+ glob_list = glob.glob(dep_path)
961
+ for fpath in glob_list:
962
+ file_exists, _, _ = files.get_source_file(fpath)
963
+ if file_exists:
964
+ debug(f" - raw file ({fpath}), from glob ({dep_path}) adding",
965
+ "to return list...")
966
+ deps_targets_to_resolve.append(fpath) # append
967
+ glob_added_from_dep_path = True
968
+ if glob_list and not glob_added_from_dep_path:
969
+ self.error(f'No files were expanded from glob: {dep_path} in',
970
+ f'{self.caller_info}')
971
+ except Exception as e:
972
+ self.error(f'Unable to add files via glob {dep_path}, in',
973
+ f'{self.caller_info}exception {e}')
974
+
975
+ if not glob_added_from_dep_path:
936
976
  file_exists, _, _ = files.get_source_file(dep_path)
937
977
  if file_exists:
938
- debug(" - raw file, adding to return list...")
978
+ debug(f" - raw file ({dep_path}), adding to return list...")
939
979
  deps_targets_to_resolve.append(dep_path) # append, keeping file order.
940
980
  else:
941
- debug(" - a target (not a file) needing to be resolved, adding to return",
942
- "list...")
981
+ debug(f" - a target non-file ({dep_path}) needs to be resolved,",
982
+ "adding to return list...")
943
983
  deps_targets_to_resolve.append(dep_path) # append, keeping file order.
944
984
 
945
985
  # We return the list of deps or files that still need to be resolved
@@ -947,12 +987,6 @@ class DepsProcessor: # pylint: disable=too-many-instance-attributes
947
987
  # items in this list are either:
948
988
  # -- string (dep or file)
949
989
  # -- tuple (unprocessed commands, in form: (shell_commands_list, work_dir_add_srcs_list))
950
- # TODO(drew): it might be cleaner to return a dict instead of list, b/c those are also
951
- # ordered and we can pass type information, something like:
952
- # { dep1: 'file',
953
- # dep2: 'target',
954
- # dep3: 'command_tuple',
955
- # }
956
990
  return deps_targets_to_resolve
957
991
 
958
992
 
opencos/deps_schema.py CHANGED
@@ -264,6 +264,9 @@ TARGET_TAGS_TABLE = {
264
264
  TARGET_CONTENTS = Or(
265
265
  ARRAY_OR_SPACE_SEPARATED_STRING,
266
266
  {
267
+ # description, info: str
268
+ Optional('description'): Or(str, type(None)),
269
+ Optional('info'): Or(str, type(None)),
267
270
  # args: array
268
271
  Optional('args'): ARRAY_OR_SPACE_SEPARATED_STRING,
269
272
  # commands: array
@@ -285,7 +288,9 @@ TARGET_CONTENTS = Or(
285
288
  # top: string
286
289
  Optional('top'): str,
287
290
  # deps: array
291
+ # AND/OR files: array
288
292
  Optional('deps'): TARGET_DEPS_CONTENTS,
293
+ Optional('files'): TARGET_DEPS_CONTENTS,
289
294
  # reqs: array
290
295
  Optional('reqs'): ARRAY_OR_SPACE_SEPARATED_STRING,
291
296
  # multi: table
@@ -334,6 +339,8 @@ FILE_SIMPLIFIED = Schema(
334
339
  Optional('METADATA'): dict,
335
340
  Optional(str): Or( # User named target contents
336
341
  {
342
+ Optional('description'): str,
343
+ Optional('info'): str,
337
344
  Optional('args'): [str],
338
345
  Optional('defines'): {
339
346
  Optional(str): Or(type(None), int, str),
@@ -347,6 +354,7 @@ FILE_SIMPLIFIED = Schema(
347
354
  Optional('incdirs'): [str],
348
355
  Optional('top'): str,
349
356
  Optional('deps'): [str],
357
+ Optional('files'): [str],
350
358
  Optional('reqs'): [str],
351
359
  }
352
360
  )
opencos/docs/DEPS.md CHANGED
@@ -9,6 +9,9 @@ METADATA: # <table> unstructured data, any UPPERCASE first level key is not cons
9
9
 
10
10
  target-spec:
11
11
 
12
+ description: # <str> optional description
13
+ info: # <str> optional description
14
+
12
15
  args: # <array or | separated str>
13
16
  - --waves
14
17
  - --sim_plusargs="+info=500"
@@ -56,6 +59,8 @@ target-spec:
56
59
  # to compile order list.
57
60
  - peakrdl: # <string> ## peakrdl command to generate CSRs
58
61
 
62
+ files: # <Identical to "deps", will append to any deps.>
63
+
59
64
  reqs: # <array or | space separated string>
60
65
  - some_file.mem # <string> aka, a non-source file required for this target.
61
66
  # This file is checked for existence prior to invoking the tool involved, for example,
@@ -100,6 +105,7 @@ target-spec:
100
105
  args: <array or | space separated string> # args to be applied if this target is used, with a matching
101
106
  # tool in 'with-tools'.
102
107
  deps: <array or | space separated string, applied with tag>
108
+ files: <identical to deps, will append to deps>
103
109
  defines: <table, applied with tag>
104
110
  plusargs: <table, applied with tag>
105
111
  parameters: <table, applied with tag>
opencos/eda.py CHANGED
@@ -20,10 +20,11 @@ from pathlib import Path
20
20
  import opencos
21
21
  from opencos import util, eda_config, eda_base
22
22
  from opencos.eda_base import Command, Tool, which_tool, print_eda_usage_line
23
- from opencos.eda_tool_helper import pretty_info_handler_tools
23
+ from opencos.eda_tool_helper import DOCKER_TOOL_NOTE, pretty_info_handler_tools, \
24
+ tool_loaded_but_flagged_as_docker_only, get_all_handler_commands
24
25
  from opencos.files import safe_shutil_which
25
26
  from opencos.util import safe_emoji, Colors
26
- from opencos.utils import vsim_helper, vscode_helper
27
+ from opencos.utils import vsim_helper, vscode_helper, docker_checks
27
28
  from opencos.utils import status_constants, str_helpers, subprocess_helpers
28
29
 
29
30
  # Configure util:
@@ -99,9 +100,17 @@ def init_config(
99
100
  def get_all_commands_help_str(config: dict) -> str:
100
101
  '''Returns a str of help based on what commands eda supports, from config'''
101
102
  all_commands_help = []
103
+ all_handler_commands = get_all_handler_commands(config=config)
102
104
  max_command_str_len = max(len(s) for s in config.get('DEFAULT_HANDLERS_HELP', {}).keys())
103
105
  for key, value in config.get('DEFAULT_HANDLERS_HELP', {}).items():
104
- all_commands_help.append(f' {key:<{max_command_str_len}} - {value.strip()}')
106
+ all_commands_help.append(
107
+ f' {Colors.bcyan}{key:<{max_command_str_len}}{Colors.normal} - {value.strip()}'
108
+ )
109
+ if key in all_handler_commands:
110
+ all_commands_help[-1] += (
111
+ f'\n {" ":<{max_command_str_len}} '
112
+ f'--tool: {Colors.cyan}{", ".join(all_handler_commands[key])}{Colors.normal}'
113
+ )
105
114
  if all_commands_help:
106
115
  all_commands_help = [
107
116
  f'Where {Colors.byellow}COMMAND{Colors.normal} is one of:',
@@ -277,13 +286,21 @@ def auto_tool_setup( # pylint: disable=too-many-locals,too-many-branches,too-man
277
286
  if not exe_path:
278
287
  exe_path = p # set on first required exe
279
288
  if not p:
280
- has_all_exe = False
289
+ # skip the fail (has_all_exe=False) if we can run via docker.
290
+ if tool_cfg.get('docker_support', None):
291
+ if not docker_checks.docker_ok():
292
+ has_all_exe = False
293
+ util.debug("... No, missing docker requirements")
294
+ else:
295
+ has_all_exe = False
281
296
  util.debug(f"... No, missing exe {exe}")
282
297
  for req in tool_cfg.get('requires_in_exe_path', []):
283
298
  if p and req and str(Path(req)) not in str(Path(p)):
284
299
  has_all_in_exe_path = False
285
300
  util.debug(f"... No, missing path requirement {req}")
286
301
 
302
+
303
+
287
304
  has_vsim_helper = True
288
305
  if tool_cfg.get('requires_vsim_helper', False):
289
306
  # This tool name must be in opencos.utils.vsim_helper.TOOL_PATH[name].
@@ -327,6 +344,9 @@ def auto_tool_setup( # pylint: disable=too-many-locals,too-many-branches,too-man
327
344
  p = exe_path
328
345
  else:
329
346
  p = safe_shutil_which(exe_list[0])
347
+ if not p and tool_cfg.get('docker_support', None) and docker_checks.docker_ok():
348
+ # Sets note that this tool is loaded, but only avail via docker:
349
+ p = DOCKER_TOOL_NOTE
330
350
  config['auto_tools_found'][name] = p # populate key-value pairs w/ first exe in list
331
351
  if name not in config['tools_loaded']:
332
352
  config['tools_loaded'].append(name)
@@ -357,7 +377,7 @@ def auto_tool_setup( # pylint: disable=too-many-locals,too-many-branches,too-man
357
377
  )
358
378
  return config
359
379
 
360
- # If --tool was not set, look at config['auto_tools_found'] should be populated based on
380
+ # If --tool was not set, look at config['auto_tools_order'] should be populated based on
361
381
  # config['tools'], and if we called this method (auto_tool_setup(...)) with tool= and command=
362
382
  # arg(s). Call set_command_handlers for each command/tool we need to:
363
383
  for _command, list_of_tools in config['auto_tools_order'].items():
@@ -365,6 +385,10 @@ def auto_tool_setup( # pylint: disable=too-many-locals,too-many-branches,too-man
365
385
  for _tool in list_of_tools:
366
386
  if _tool not in config['tools_loaded']:
367
387
  continue
388
+ if tool_loaded_but_flagged_as_docker_only(tool=_tool, config=config):
389
+ # do not let this tool be the auto-handler for a command (aka, it is the
390
+ # chosen tool if --tool=TOOL arg not present) if it can only run via docker.
391
+ continue
368
392
 
369
393
  set_command_handler(
370
394
  command=_command, tool=_tool, config=config, auto_setup=(not tool),
@@ -801,6 +825,7 @@ def main(*args):
801
825
  # And show python version:
802
826
  util.info(f'python: version {sys.version_info.major}.{sys.version_info.minor}.'
803
827
  f'{sys.version_info.micro}')
828
+ util.info(f'eda from: {__file__}')
804
829
 
805
830
  # Handle --config-yml= arg
806
831
  config, unparsed = eda_config.get_eda_config(unparsed)
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