opencos-eda 0.2.55__tar.gz → 0.2.57__tar.gz

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 (102) hide show
  1. {opencos_eda-0.2.55/opencos_eda.egg-info → opencos_eda-0.2.57}/PKG-INFO +1 -1
  2. {opencos_eda-0.2.55 → opencos_eda-0.2.57}/opencos/commands/flist.py +10 -0
  3. {opencos_eda-0.2.55 → opencos_eda-0.2.57}/opencos/commands/lec.py +4 -0
  4. {opencos_eda-0.2.55 → opencos_eda-0.2.57}/opencos/commands/multi.py +4 -0
  5. {opencos_eda-0.2.55 → opencos_eda-0.2.57}/opencos/commands/sim.py +31 -0
  6. {opencos_eda-0.2.55 → opencos_eda-0.2.57}/opencos/deps/defaults.py +2 -0
  7. {opencos_eda-0.2.55 → opencos_eda-0.2.57}/opencos/deps/deps_processor.py +39 -1
  8. {opencos_eda-0.2.55 → opencos_eda-0.2.57}/opencos/deps_schema.py +22 -0
  9. {opencos_eda-0.2.55 → opencos_eda-0.2.57}/opencos/eda.py +13 -1
  10. {opencos_eda-0.2.55 → opencos_eda-0.2.57}/opencos/eda_base.py +108 -10
  11. {opencos_eda-0.2.55 → opencos_eda-0.2.57}/opencos/eda_config.py +37 -2
  12. {opencos_eda-0.2.55 → opencos_eda-0.2.57}/opencos/eda_config_defaults.yml +1 -0
  13. {opencos_eda-0.2.55 → opencos_eda-0.2.57}/opencos/export_helper.py +20 -6
  14. {opencos_eda-0.2.55 → opencos_eda-0.2.57}/opencos/hw/oc_cli.py +1 -1
  15. {opencos_eda-0.2.55 → opencos_eda-0.2.57}/opencos/names.py +4 -1
  16. {opencos_eda-0.2.55 → opencos_eda-0.2.57}/opencos/tests/helpers.py +9 -0
  17. {opencos_eda-0.2.55 → opencos_eda-0.2.57}/opencos/tests/test_eda_synth.py +12 -0
  18. {opencos_eda-0.2.55 → opencos_eda-0.2.57}/opencos/tools/cocotb.py +25 -0
  19. {opencos_eda-0.2.55 → opencos_eda-0.2.57}/opencos/tools/invio_helpers.py +25 -4
  20. {opencos_eda-0.2.55 → opencos_eda-0.2.57}/opencos/tools/iverilog.py +5 -0
  21. {opencos_eda-0.2.55 → opencos_eda-0.2.57}/opencos/tools/modelsim_ase.py +52 -6
  22. {opencos_eda-0.2.55 → opencos_eda-0.2.57}/opencos/tools/quartus.py +32 -6
  23. {opencos_eda-0.2.55 → opencos_eda-0.2.57}/opencos/tools/riviera.py +17 -3
  24. {opencos_eda-0.2.55 → opencos_eda-0.2.57}/opencos/tools/slang.py +5 -0
  25. {opencos_eda-0.2.55 → opencos_eda-0.2.57}/opencos/tools/slang_yosys.py +13 -1
  26. {opencos_eda-0.2.55 → opencos_eda-0.2.57}/opencos/tools/surelog.py +5 -0
  27. {opencos_eda-0.2.55 → opencos_eda-0.2.57}/opencos/tools/verilator.py +5 -0
  28. {opencos_eda-0.2.55 → opencos_eda-0.2.57}/opencos/tools/vivado.py +15 -2
  29. {opencos_eda-0.2.55 → opencos_eda-0.2.57}/opencos/tools/yosys.py +50 -20
  30. {opencos_eda-0.2.55 → opencos_eda-0.2.57}/opencos/utils/vsim_helper.py +1 -2
  31. {opencos_eda-0.2.55 → opencos_eda-0.2.57/opencos_eda.egg-info}/PKG-INFO +1 -1
  32. {opencos_eda-0.2.55 → opencos_eda-0.2.57}/pyproject.toml +1 -1
  33. {opencos_eda-0.2.55 → opencos_eda-0.2.57}/LICENSE +0 -0
  34. {opencos_eda-0.2.55 → opencos_eda-0.2.57}/LICENSE.spdx +0 -0
  35. {opencos_eda-0.2.55 → opencos_eda-0.2.57}/README.md +0 -0
  36. {opencos_eda-0.2.55 → opencos_eda-0.2.57}/opencos/__init__.py +0 -0
  37. {opencos_eda-0.2.55 → opencos_eda-0.2.57}/opencos/_version.py +0 -0
  38. {opencos_eda-0.2.55 → opencos_eda-0.2.57}/opencos/_waves_pkg.sv +0 -0
  39. {opencos_eda-0.2.55 → opencos_eda-0.2.57}/opencos/commands/__init__.py +0 -0
  40. {opencos_eda-0.2.55 → opencos_eda-0.2.57}/opencos/commands/build.py +0 -0
  41. {opencos_eda-0.2.55 → opencos_eda-0.2.57}/opencos/commands/deps_help.py +0 -0
  42. {opencos_eda-0.2.55 → opencos_eda-0.2.57}/opencos/commands/elab.py +0 -0
  43. {opencos_eda-0.2.55 → opencos_eda-0.2.57}/opencos/commands/export.py +0 -0
  44. {opencos_eda-0.2.55 → opencos_eda-0.2.57}/opencos/commands/lint.py +0 -0
  45. {opencos_eda-0.2.55 → opencos_eda-0.2.57}/opencos/commands/open.py +0 -0
  46. {opencos_eda-0.2.55 → opencos_eda-0.2.57}/opencos/commands/proj.py +0 -0
  47. {opencos_eda-0.2.55 → opencos_eda-0.2.57}/opencos/commands/shell.py +0 -0
  48. {opencos_eda-0.2.55 → opencos_eda-0.2.57}/opencos/commands/sweep.py +0 -0
  49. {opencos_eda-0.2.55 → opencos_eda-0.2.57}/opencos/commands/synth.py +0 -0
  50. {opencos_eda-0.2.55 → opencos_eda-0.2.57}/opencos/commands/targets.py +0 -0
  51. {opencos_eda-0.2.55 → opencos_eda-0.2.57}/opencos/commands/upload.py +0 -0
  52. {opencos_eda-0.2.55 → opencos_eda-0.2.57}/opencos/commands/waves.py +0 -0
  53. {opencos_eda-0.2.55 → opencos_eda-0.2.57}/opencos/deps/__init__.py +0 -0
  54. {opencos_eda-0.2.55 → opencos_eda-0.2.57}/opencos/deps/deps_commands.py +0 -0
  55. {opencos_eda-0.2.55 → opencos_eda-0.2.57}/opencos/deps/deps_file.py +0 -0
  56. {opencos_eda-0.2.55 → opencos_eda-0.2.57}/opencos/eda_config_max_verilator_waivers.yml +0 -0
  57. {opencos_eda-0.2.55 → opencos_eda-0.2.57}/opencos/eda_config_reduced.yml +0 -0
  58. {opencos_eda-0.2.55 → opencos_eda-0.2.57}/opencos/eda_deps_bash_completion.bash +0 -0
  59. {opencos_eda-0.2.55 → opencos_eda-0.2.57}/opencos/eda_deps_sanitize.py +0 -0
  60. {opencos_eda-0.2.55 → opencos_eda-0.2.57}/opencos/eda_extract_targets.py +0 -0
  61. {opencos_eda-0.2.55 → opencos_eda-0.2.57}/opencos/eda_tool_helper.py +0 -0
  62. {opencos_eda-0.2.55 → opencos_eda-0.2.57}/opencos/export_json_convert.py +0 -0
  63. {opencos_eda-0.2.55 → opencos_eda-0.2.57}/opencos/files.py +0 -0
  64. {opencos_eda-0.2.55 → opencos_eda-0.2.57}/opencos/hw/__init__.py +0 -0
  65. {opencos_eda-0.2.55 → opencos_eda-0.2.57}/opencos/hw/pcie.py +0 -0
  66. {opencos_eda-0.2.55 → opencos_eda-0.2.57}/opencos/peakrdl_cleanup.py +0 -0
  67. {opencos_eda-0.2.55 → opencos_eda-0.2.57}/opencos/seed.py +0 -0
  68. {opencos_eda-0.2.55 → opencos_eda-0.2.57}/opencos/tests/__init__.py +0 -0
  69. {opencos_eda-0.2.55 → opencos_eda-0.2.57}/opencos/tests/custom_config.yml +0 -0
  70. {opencos_eda-0.2.55 → opencos_eda-0.2.57}/opencos/tests/deps_files/command_order/DEPS.yml +0 -0
  71. {opencos_eda-0.2.55 → opencos_eda-0.2.57}/opencos/tests/deps_files/error_msgs/DEPS.yml +0 -0
  72. {opencos_eda-0.2.55 → opencos_eda-0.2.57}/opencos/tests/deps_files/iverilog_test/DEPS.yml +0 -0
  73. {opencos_eda-0.2.55 → opencos_eda-0.2.57}/opencos/tests/deps_files/no_deps_here/DEPS.yml +0 -0
  74. {opencos_eda-0.2.55 → opencos_eda-0.2.57}/opencos/tests/deps_files/non_sv_reqs/DEPS.yml +0 -0
  75. {opencos_eda-0.2.55 → opencos_eda-0.2.57}/opencos/tests/deps_files/tags_with_tools/DEPS.yml +0 -0
  76. {opencos_eda-0.2.55 → opencos_eda-0.2.57}/opencos/tests/deps_files/test_err_fatal/DEPS.yml +0 -0
  77. {opencos_eda-0.2.55 → opencos_eda-0.2.57}/opencos/tests/test_build.py +0 -0
  78. {opencos_eda-0.2.55 → opencos_eda-0.2.57}/opencos/tests/test_deps_helpers.py +0 -0
  79. {opencos_eda-0.2.55 → opencos_eda-0.2.57}/opencos/tests/test_deps_schema.py +0 -0
  80. {opencos_eda-0.2.55 → opencos_eda-0.2.57}/opencos/tests/test_eda.py +0 -0
  81. {opencos_eda-0.2.55 → opencos_eda-0.2.57}/opencos/tests/test_eda_elab.py +0 -0
  82. {opencos_eda-0.2.55 → opencos_eda-0.2.57}/opencos/tests/test_oc_cli.py +0 -0
  83. {opencos_eda-0.2.55 → opencos_eda-0.2.57}/opencos/tests/test_tools.py +0 -0
  84. {opencos_eda-0.2.55 → opencos_eda-0.2.57}/opencos/tools/__init__.py +0 -0
  85. {opencos_eda-0.2.55 → opencos_eda-0.2.57}/opencos/tools/invio.py +0 -0
  86. {opencos_eda-0.2.55 → opencos_eda-0.2.57}/opencos/tools/invio_yosys.py +0 -0
  87. {opencos_eda-0.2.55 → opencos_eda-0.2.57}/opencos/tools/questa.py +0 -0
  88. {opencos_eda-0.2.55 → opencos_eda-0.2.57}/opencos/tools/questa_fse.py +0 -0
  89. {opencos_eda-0.2.55 → opencos_eda-0.2.57}/opencos/tools/tabbycad_yosys.py +0 -0
  90. {opencos_eda-0.2.55 → opencos_eda-0.2.57}/opencos/util.py +0 -0
  91. {opencos_eda-0.2.55 → opencos_eda-0.2.57}/opencos/utils/__init__.py +0 -0
  92. {opencos_eda-0.2.55 → opencos_eda-0.2.57}/opencos/utils/markup_helpers.py +0 -0
  93. {opencos_eda-0.2.55 → opencos_eda-0.2.57}/opencos/utils/status_constants.py +0 -0
  94. {opencos_eda-0.2.55 → opencos_eda-0.2.57}/opencos/utils/str_helpers.py +0 -0
  95. {opencos_eda-0.2.55 → opencos_eda-0.2.57}/opencos/utils/subprocess_helpers.py +0 -0
  96. {opencos_eda-0.2.55 → opencos_eda-0.2.57}/opencos/utils/vscode_helper.py +0 -0
  97. {opencos_eda-0.2.55 → opencos_eda-0.2.57}/opencos_eda.egg-info/SOURCES.txt +0 -0
  98. {opencos_eda-0.2.55 → opencos_eda-0.2.57}/opencos_eda.egg-info/dependency_links.txt +0 -0
  99. {opencos_eda-0.2.55 → opencos_eda-0.2.57}/opencos_eda.egg-info/entry_points.txt +0 -0
  100. {opencos_eda-0.2.55 → opencos_eda-0.2.57}/opencos_eda.egg-info/requires.txt +0 -0
  101. {opencos_eda-0.2.55 → opencos_eda-0.2.57}/opencos_eda.egg-info/top_level.txt +0 -0
  102. {opencos_eda-0.2.55 → opencos_eda-0.2.57}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: opencos-eda
3
- Version: 0.2.55
3
+ Version: 0.2.57
4
4
  Summary: A simple Python package for wrapping RTL simuliatons and synthesis
5
5
  Author-email: Simon Sabato <simon@cognichip.ai>, Drew Ranck <drew@cognichip.ai>
6
6
  Project-URL: Homepage, https://github.com/cognichip/opencos
@@ -10,6 +10,7 @@ import os
10
10
  from opencos import util
11
11
  from opencos.eda_base import CommandDesign
12
12
  from opencos.utils.str_helpers import strip_all_quotes
13
+ from opencos.commands.sim import parameters_dict_get_command_list
13
14
 
14
15
  class CommandFList(CommandDesign):
15
16
  '''Base class command handler for: eda flist ...'''
@@ -22,6 +23,7 @@ class CommandFList(CommandDesign):
22
23
  'eda-dir' : 'eda.flist', # user can specify eda-dir if files are generated.
23
24
  'out' : "flist.out",
24
25
  'emit-define' : True,
26
+ 'emit-parameter' : True,
25
27
  'emit-incdir' : True,
26
28
  'emit-v' : True,
27
29
  'emit-sv' : True,
@@ -29,6 +31,7 @@ class CommandFList(CommandDesign):
29
31
  'emit-cpp' : True,
30
32
  'emit-non-sources' : True, # as comments, from DEPS 'reqs'
31
33
  'prefix-define' : "+define+",
34
+ 'prefix-parameter' : "-G",
32
35
  'prefix-incdir' : "+incdir+",
33
36
  'prefix-v' : "",
34
37
  'prefix-sv' : "",
@@ -179,6 +182,13 @@ class CommandFList(CommandDesign):
179
182
  newline = prefix + qd1 + f"{d}{ed1}{value}" + qd2
180
183
  print(newline, file=fo)
181
184
 
185
+ if self.args['emit-parameter']:
186
+ prefix = strip_all_quotes(self.args['prefix-parameter'])
187
+ for item in parameters_dict_get_command_list(
188
+ params=self.parameters, arg_prefix=prefix
189
+ ):
190
+ print(item, file=fo)
191
+
182
192
  if self.args['emit-incdir']:
183
193
  prefix = strip_all_quotes(self.args['prefix-incdir'])
184
194
  for i in self.incdirs:
@@ -27,6 +27,7 @@ class CommandLec(CommandDesign):
27
27
  self.args.update({
28
28
  'designs': [],
29
29
  'synth': True,
30
+ 'flatten-all': True,
30
31
  })
31
32
  self.args_help.update({
32
33
  'designs': (
@@ -34,6 +35,9 @@ class CommandLec(CommandDesign):
34
35
  ' use this arg twice'
35
36
  ),
36
37
  'synth': 'run synthesis on the two designs prior to running LEC',
38
+ 'flatten-all': (
39
+ 'arg passed to "synth" if run with --synth, to disable use --no-flatten-all'
40
+ ),
37
41
  })
38
42
 
39
43
  self.synth_design_verilog_fpaths = ['', '']
@@ -327,6 +327,10 @@ class CommandMulti(CommandParallel):
327
327
 
328
328
  if parsed.fail_if_no_targets and len(self.targets) == 0:
329
329
  self.error(f'Multi: --fail-if-no-targets set, and {self.targets=}')
330
+ if not all_multi_tools:
331
+ possible_tools = self.all_handler_commands.get(command, [])
332
+ self.error(f'Multi: no tools to run for {command=}, available tools: {possible_tools}')
333
+
330
334
  util.info("Multi: About to run: ", end="")
331
335
 
332
336
  def get_pretty_targets_tuple_as_list(l:list):
@@ -17,6 +17,33 @@ from opencos import util, export_helper
17
17
  from opencos.eda_base import CommandDesign, Tool
18
18
  from opencos.utils import status_constants
19
19
 
20
+ from opencos.utils.str_helpers import sanitize_defines_for_sh, strip_outer_quotes
21
+
22
+ def parameters_dict_get_command_list(params: dict, arg_prefix: str = '-G') -> list:
23
+ '''Given dict of parameters, returns a command list'''
24
+
25
+ ret_list = []
26
+ if ' ' in arg_prefix:
27
+ arg_list_prefix = arg_prefix.split()
28
+ arg_str_prefix = ''
29
+ else:
30
+ arg_list_prefix = []
31
+ arg_str_prefix = arg_prefix
32
+
33
+ for k,v in params.items():
34
+ if not isinstance(v, (int, str)):
35
+ util.warning(f'parameter {k} has value: {v}, parameters must be int/string types')
36
+
37
+ ret_list.extend(arg_list_prefix)
38
+ if isinstance(v, int):
39
+ ret_list.append(f'{arg_str_prefix}{k}={v}')
40
+ else: # string
41
+ v = strip_outer_quotes(v.strip('\n'))
42
+ v = '"' + v + '"'
43
+ ret_list.append(f'{arg_str_prefix}{k}={sanitize_defines_for_sh(v)}')
44
+ return ret_list
45
+
46
+
20
47
  class CommandSim(CommandDesign):
21
48
  '''Base class command handler for: eda sim ...'''
22
49
 
@@ -80,6 +107,10 @@ class CommandSim(CommandDesign):
80
107
 
81
108
  self.args['verilate-args'] = []
82
109
 
110
+ def process_parameters_get_list(self, arg_prefix: str = '-G') -> list:
111
+ '''Returns list (suitable command list for shell or for tool) from self.parameters'''
112
+ return parameters_dict_get_command_list(params=self.parameters, arg_prefix=arg_prefix)
113
+
83
114
  def process_plusarg(self, plusarg: str, pwd: str = os.getcwd()) -> None:
84
115
  '''Override for CommandDesign.process_plusarg(..)'''
85
116
  maybe_plusarg = CommandDesign.process_plusarg(self, plusarg, pwd)
@@ -31,6 +31,7 @@ KNOWN_EDA_COMMANDS = set([
31
31
  SUPPORTED_TARGET_TABLE_KEYS = set([
32
32
  'args',
33
33
  'defines',
34
+ 'parameters',
34
35
  'incdirs',
35
36
  'top',
36
37
  'deps',
@@ -52,6 +53,7 @@ SUPPORTED_TAG_KEYS = set([
52
53
  'deps',
53
54
  'reqs',
54
55
  'defines',
56
+ 'parameters',
55
57
  'incdirs',
56
58
  'replace-config-tools',
57
59
  'additive-config-tools',
@@ -91,6 +91,22 @@ class DepsProcessor: # pylint: disable=too-many-instance-attributes
91
91
  v = v.replace('%SEED%', str(self.args.get('seed', 1)))
92
92
  self.command_design_ref.process_plusarg(f'+define+{k}={v}')
93
93
 
94
+
95
+ def apply_parameters(self, parameters_dict: dict):
96
+ '''Given parameters_dict, applies them to our self.command_design_ref obj'''
97
+ if not isinstance(parameters_dict, dict):
98
+ self.error(f"{parameters_dict=} is not type dict, can't apply defines,",
99
+ f"in {self.caller_info}")
100
+ for k,v in parameters_dict.items():
101
+ if v is None or v == '' or not isinstance(v, (int, str, bool)):
102
+ warning(f'parameter {k} has value: {v}, parameters must be bool/int/string types',
103
+ f'from {self.caller_info}')
104
+ else:
105
+ self.command_design_ref.set_parameter(
106
+ name=k, value=v, caller_info=self.caller_info
107
+ )
108
+
109
+
94
110
  def apply_incdirs(self, incdirs_list:list):
95
111
  '''Given incdirs_list, applies them to our self.command_design_ref obj'''
96
112
  if not isinstance(incdirs_list, (str, list)):
@@ -252,6 +268,8 @@ class DepsProcessor: # pylint: disable=too-many-instance-attributes
252
268
  remaining_deps_list += self.process_tags()
253
269
  elif key == 'defines':
254
270
  self.process_defines()
271
+ elif key == 'parameters':
272
+ self.process_parameters()
255
273
  elif key == 'incdirs':
256
274
  self.process_incdirs()
257
275
  elif key == 'top':
@@ -426,6 +444,9 @@ class DepsProcessor: # pylint: disable=too-many-instance-attributes
426
444
  # apply defines:
427
445
  self.apply_defines(value.get('defines', {}))
428
446
 
447
+ elif key == 'parameters':
448
+ self.apply_parameters(value.get('parameters', {}))
449
+
429
450
  elif key == 'incdirs':
430
451
  # apply incdirs:
431
452
  self.apply_incdirs(value.get('incdirs', []))
@@ -517,6 +538,22 @@ class DepsProcessor: # pylint: disable=too-many-instance-attributes
517
538
 
518
539
  self.apply_defines(entry_defines)
519
540
 
541
+ def process_parameters(self):
542
+ '''Returns None, applies parameters (dict, if any) from self.deps_entry to
543
+ self.command_design_ref.'''
544
+
545
+ # Parameters:
546
+ # apply command specific parameters, with higher priority than the a
547
+ # deps_entry['sim']['parameters'] entry,
548
+ # do this with dict1.update(dict2):
549
+ entry_parameters = {}
550
+ entry_parameters.update(self.deps_entry.get('parameters', {}))
551
+ entry_parameters.update(self.entry_eda_command.get('parameters', {}))
552
+ assert isinstance(entry_parameters, dict), \
553
+ f'{entry_parameters=} for in {self.caller_info} must be a dict'
554
+
555
+ self.apply_parameters(entry_parameters)
556
+
520
557
  def process_incdirs(self) -> None:
521
558
  '''Returns None, applies incdirs (dict, if any) from self.deps_entry to
522
559
  self.command_design_ref.'''
@@ -700,7 +737,8 @@ class DepsProcessor: # pylint: disable=too-many-instance-attributes
700
737
  deps_targets_to_resolve.append(command_tuple)
701
738
 
702
739
 
703
- elif isinstance(dep, str) and any(dep.startswith(x) for x in ['+define+', '+incdir']):
740
+ elif isinstance(dep, str) and \
741
+ any(dep.startswith(x) for x in ['+define+', '+incdir+']):
704
742
  # Note: we still support +define+ and +incdir in the deps list.
705
743
  # check for compile-time Verilog style plusarg, which are supported under targets
706
744
  # These are not run-time Verilog style plusargs comsumable from within the .sv:
@@ -11,6 +11,9 @@ my_target_name:
11
11
  defines: <---- defines, optional table
12
12
  USER_DEFINE_VALUE: 12
13
13
 
14
+ parameters: <---- parameters, optional table
15
+ USER_PARAM_VALUE: 12
16
+
14
17
  incdirs: <---- incdirs, optional array (or string)
15
18
  - ./
16
19
 
@@ -32,6 +35,9 @@ my_target_name:
32
35
  defines: <---- defines, optional table of (key: str Or null)
33
36
  USER_DEFINE_VALUE: 12
34
37
 
38
+ parameters: <---- parameters, optional table
39
+ USER_PARAM_VALUE: 12
40
+
35
41
  incdirs: <---- incdirs, optional array of strings
36
42
  - ./
37
43
 
@@ -55,6 +61,7 @@ METADATA: <---- (optional) unstructured table
55
61
 
56
62
  my_target_name:
57
63
  defines: <---- defines, optional table
64
+ parameters: <---- parameters, optional table
58
65
  incdirs: <---- incdirs, optional array (or string)
59
66
  top: tb <---- top, optional string
60
67
  deps: <---- TARGET_DEPS_CONTENTS schema
@@ -105,6 +112,8 @@ my_target_name:
105
112
  to the current target for this named eda command.
106
113
  defines: <---- optional table, defines to be applied to the current
107
114
  target for this named eda command.
115
+ parameters: <---- optional table, parameters to be applied to the current
116
+ target to for this named eda command.
108
117
  incdirs: <---- optional array (or string) incdirs to be applied to
109
118
  the current target for this named eda command.
110
119
  tags: <---- tags, optional table using TARGET_TAGS_TABLE schema.
@@ -204,6 +213,9 @@ TARGET_EDA_COMMAND_ENTRY_TABLE = {
204
213
  Optional('defines'): {
205
214
  Optional(str): Or(str, int, type(None)),
206
215
  },
216
+ Optional('parameters'): {
217
+ Optional(str): Or(str, int),
218
+ },
207
219
  Optional('incdirs'): ARRAY_OR_SPACE_SEPARATED_STRING,
208
220
  }
209
221
 
@@ -226,6 +238,9 @@ TARGET_TAGS_TABLE = {
226
238
  Optional('defines'): {
227
239
  Optional(str): Or(str, int, type(None)),
228
240
  },
241
+ Optional('parameters'): {
242
+ Optional(str): Or(str, int),
243
+ },
229
244
  Optional('incdirs'): ARRAY_OR_SPACE_SEPARATED_STRING,
230
245
  Optional('replace-config-tools'): dict,
231
246
  Optional('additive-config-tools'): dict,
@@ -244,6 +259,10 @@ TARGET_CONTENTS = Or(
244
259
  Optional('defines'): {
245
260
  Optional(str): Or(str, int, type(None)),
246
261
  },
262
+ # parameters: table of key-value
263
+ Optional('parameters'): {
264
+ Optional(str): Or(str, int),
265
+ },
247
266
  # incdirs: array
248
267
  Optional('incdirs'): ARRAY_OR_SPACE_SEPARATED_STRING,
249
268
  # top: string
@@ -302,6 +321,9 @@ FILE_SIMPLIFIED = Schema(
302
321
  Optional('defines'): {
303
322
  Optional(str): Or(type(None), str),
304
323
  },
324
+ Optional('parameters'): {
325
+ Optional(str): str,
326
+ },
305
327
  Optional('incdirs'): [str],
306
328
  Optional('top'): str,
307
329
  Optional('deps'): [str],
@@ -49,6 +49,9 @@ def init_config(
49
49
 
50
50
  # For key DEFAULT_HANDLERS, we'll update config['command_handler'] with
51
51
  # the actual class using importlib (via opencos.util)
52
+
53
+ eda_config.tool_try_add_to_path(tool)
54
+
52
55
  config['command_handler'] = {}
53
56
  for _cmd, str_class in config['DEFAULT_HANDLERS'].items():
54
57
  cls = util.import_class_from_string(str_class)
@@ -329,7 +332,7 @@ def tool_setup(tool: str, config: dict, quiet: bool = False, auto_setup: bool =
329
332
  config['tools_loaded'].add(tool)
330
333
 
331
334
 
332
- def process_tokens( # pylint: disable=too-many-branches,too-many-statements,too-many-locals
335
+ def process_tokens( # pylint: disable=too-many-branches,too-many-statements,too-many-locals,too-many-return-statements
333
336
  tokens: list, original_args: list, config: dict, is_interactive=False
334
337
  ) -> int:
335
338
  '''Returns bash/sh style return code int (0 pass, non-zero fail).
@@ -416,6 +419,15 @@ def process_tokens( # pylint: disable=too-many-branches,too-many-statements,too-
416
419
  return 2
417
420
 
418
421
  sco = config['command_handler'][command](config=config) # sub command object
422
+ if not parsed.tool:
423
+ # then what was the auto selected tool?
424
+ sco_tool = getattr(sco, '_TOOL', '')
425
+ if sco_tool in config['auto_tools_order'][0] and \
426
+ config['auto_tools_order'][0][sco_tool].get('disable-auto', False):
427
+ util.error(f'Cannot use tool={sco_tool} without arg --tool, it cannot be selected',
428
+ 'automatically')
429
+ return status_constants.EDA_COMMAND_OR_ARGS_ERROR
430
+
419
431
  util.debug(f'{command=}')
420
432
  util.debug(f'{sco.config=}')
421
433
  util.debug(f'{type(sco)=}')
@@ -831,7 +831,33 @@ class Command: # pylint: disable=too-many-public-methods
831
831
  lines.append(indent_me(f" --{k:20} : string : {vstr:12}{khelp}"))
832
832
  else:
833
833
  lines.append(indent_me(f" --{k:20} : <unknown> : {vstr:12}{khelp}"))
834
+
835
+ lines.append('')
836
+ lines.append(indent_me((
837
+ " -G<parameterName>=<value> "
838
+ " Add parameter to top level, support bit/int/string types only."
839
+ " Example: -GDEPTH=8 (DEPTH treated as SV int/integer)."
840
+ " -GENABLE=1 (ENABLED treated as SV bit/int/integer)."
841
+ " -GName=eda (Name treated as SV string \"eda\")."
842
+ )))
843
+ lines.append(indent_me((
844
+ " +define+<defineName> "
845
+ " Add define w/out value to tool ahead of SV sources"
846
+ " Example: +define+SIM_SPEEDUP"
847
+ )))
848
+ lines.append(indent_me((
849
+ " +define+<defineName>=<value> "
850
+ " Add define w/ value to tool ahead of SV sources"
851
+ " Example: +define+TECH_LIB=2 +define+FULL_NAME=\"E D A\""
852
+ )))
853
+ lines.append(indent_me((
854
+ " +incdir+<path> "
855
+ " Add path (absolute or relative) for include directories"
856
+ " for SystemVerilog `include \"<some-file>.svh\""
857
+ " Example: +incdir+../lib"
858
+ )))
834
859
  lines.append('')
860
+
835
861
  for line in lines:
836
862
  print(line)
837
863
 
@@ -922,6 +948,7 @@ class CommandDesign(Command): # pylint: disable=too-many-instance-attributes
922
948
  ),
923
949
  })
924
950
  self.defines = {}
951
+ self.parameters = {}
925
952
  self.incdirs = []
926
953
  self.files = {}
927
954
  self.files_v = []
@@ -1086,10 +1113,72 @@ class CommandDesign(Command): # pylint: disable=too-many-instance-attributes
1086
1113
  # TODO(drew): Use the helper method in util for this instead to peek in file contents?
1087
1114
  return os.path.splitext(os.path.basename(name))[0]
1088
1115
 
1116
+ def set_parameter(
1117
+ self, name: str, value, caller_info: str = '(CLI)',
1118
+ wrap_str_double_quotes: bool = True
1119
+ ) -> None:
1120
+ '''Safe wrapper for setting a parameter (can only set once, from CLI or DEPS)'''
1121
+
1122
+ if name in self.parameters:
1123
+ util.debug("Parameter not set because it is already modified",
1124
+ f"(via -G<Name>=<Value>): {name}={value}; orig value",
1125
+ f'{self.parameters[name]} from {caller_info}')
1126
+ else:
1127
+ if isinstance(value, bool):
1128
+ value = int(value)
1129
+ elif isinstance(value, str):
1130
+ value = strip_outer_quotes(value.strip('\n'))
1131
+ if wrap_str_double_quotes:
1132
+ value = '"' + value + '"'
1133
+ self.parameters[name] = value
1134
+ util.debug(f"Parameter (via -G<Name>=<Value>): {name}={value}",
1135
+ f'from {caller_info}')
1136
+
1137
+
1138
+ def process_parameter_arg(
1139
+ self, text: str, pwd: str = os.getcwd()
1140
+ ) -> None:
1141
+ '''Retuns None, parses -G<Name>=<Value> adds to internal self.parameters.'''
1142
+
1143
+ # Deal with raw CLI/bash/powershell argparser, strip all outer quotes.
1144
+ text = strip_outer_quotes(text)
1145
+ if not pwd:
1146
+ pwd = ''
1147
+
1148
+ if not text.startswith('-G'):
1149
+ self.error(f"Didn't understand -G parameter arg: '{text}'",
1150
+ error_code=status_constants.EDA_COMMAND_OR_ARGS_ERROR)
1151
+ return
1152
+
1153
+ text = text[2:] # strip leading -G
1154
+ m = re.match(r'^(\w+)$', text)
1155
+ if m:
1156
+ k = m.group(1)
1157
+ util.warning(f"Parameter {k} has no value and will not be applied")
1158
+ return
1159
+ m = re.match(r'^(\w+)\=(\S+)$', text)
1160
+ if not m:
1161
+ m = re.match(r'^(\w+)\=(\"[^\"]*\")$', text)
1162
+ if m:
1163
+ k = m.group(1)
1164
+ v = m.group(2)
1165
+ # since this is coming from a CLI string, we have to guess at types,
1166
+ # for int or bool (not str) and convert bool to int:
1167
+ if not isinstance(v, (int, str)):
1168
+ self.error(f"Didn't understand -G parameter arg: name={k} value={v}",
1169
+ f"value must be int/str type, from: '{text}'",
1170
+ error_code=status_constants.EDA_COMMAND_OR_ARGS_ERROR)
1171
+ return
1172
+ try:
1173
+ v = int(v)
1174
+ except ValueError:
1175
+ pass
1176
+ self.set_parameter(k, v)
1177
+
1089
1178
 
1090
1179
  def process_plusarg( # pylint: disable=too-many-branches
1091
1180
  self, plusarg: str, pwd: str = os.getcwd()
1092
- ) -> None:
1181
+ ) -> str:
1093
1182
  '''Retuns str, parses a +define+, +incdir+, +key=value str; adds to internal.
1094
1183
 
1095
1184
  Adds to self.defines, self.incdirs,
@@ -1112,7 +1201,7 @@ class CommandDesign(Command): # pylint: disable=too-many-instance-attributes
1112
1201
  k = m.group(1)
1113
1202
  self.defines[k] = None
1114
1203
  util.debug(f"Defined {k}")
1115
- return None
1204
+ return ''
1116
1205
  m = re.match(r'^(\w+)\=(\S+)$', plusarg)
1117
1206
  if not m:
1118
1207
  m = re.match(r'^(\w+)\=(\"[^\"]*\")$', plusarg)
@@ -1126,10 +1215,10 @@ class CommandDesign(Command): # pylint: disable=too-many-instance-attributes
1126
1215
  v = v.replace('%SEED%', str(self.args.get('seed', 1)))
1127
1216
  self.defines[k] = v
1128
1217
  util.debug(f"Defined {k}={v}")
1129
- return None
1218
+ return ''
1130
1219
  self.error(f"Didn't understand +define+: '{plusarg}'",
1131
1220
  error_code=status_constants.EDA_COMMAND_OR_ARGS_ERROR)
1132
- return None
1221
+ return ''
1133
1222
 
1134
1223
  if plusarg.startswith('+incdir+'):
1135
1224
  plusarg = plusarg[len('+incdir+'):]
@@ -1139,16 +1228,16 @@ class CommandDesign(Command): # pylint: disable=too-many-instance-attributes
1139
1228
  if incdir not in self.incdirs:
1140
1229
  self.incdirs.append(os.path.abspath(incdir))
1141
1230
  util.debug(f"Added include dir '{os.path.abspath(incdir)}'")
1142
- return None
1231
+ return ''
1143
1232
  self.error(f"Didn't understand +incdir+: '{plusarg}'",
1144
1233
  error_code=status_constants.EDA_COMMAND_OR_ARGS_ERROR)
1145
- return None
1234
+ return ''
1146
1235
 
1147
1236
  # remaining plusargs as stored in self.args['unprocessed-plusargs'] (list)
1148
1237
  if plusarg.startswith('+'):
1149
1238
  if not self.config.get('bare_plusarg_supported', False):
1150
1239
  self.error(f"bare plusarg(s) are not supported: {plusarg}'")
1151
- return None
1240
+ return ''
1152
1241
  if plusarg not in self.args['unprocessed-plusargs']:
1153
1242
  self.args['unprocessed-plusargs'].append(plusarg)
1154
1243
  # For anything added to unprocessed-plusarg, we have to return it, to let
@@ -1157,7 +1246,7 @@ class CommandDesign(Command): # pylint: disable=too-many-instance-attributes
1157
1246
 
1158
1247
  self.error(f"Didn't understand +plusarg: '{plusarg}'",
1159
1248
  error_code=status_constants.EDA_COMMAND_OR_ARGS_ERROR)
1160
- return None
1249
+ return ''
1161
1250
 
1162
1251
 
1163
1252
  def append_shell_commands(self, cmds : list) -> None:
@@ -1439,8 +1528,8 @@ class CommandDesign(Command): # pylint: disable=too-many-instance-attributes
1439
1528
  unparsed = Command.process_tokens(self, tokens, process_all=False, pwd=pwd)
1440
1529
  util.debug(f'CommandDesign - after Command.process_tokens(..) {unparsed=}')
1441
1530
 
1442
- # deal with +define+ or +incdir+, consume it and remove from unparsed
1443
- # walk the list, remove all items after we're done.
1531
+ # deal with +define+, +incdir+, +(plusargName)+, or -GParameterName=Value:
1532
+ # consume it and remove from unparsed, walk the list, remove all items after we're done.
1444
1533
  remove_list = []
1445
1534
  for token in unparsed:
1446
1535
  # Since this is a raw argparser, we may have args that come from shlex.quote(token),
@@ -1454,6 +1543,15 @@ class CommandDesign(Command): # pylint: disable=too-many-instance-attributes
1454
1543
  plusarg = strip_outer_quotes(token)
1455
1544
  self.process_plusarg(plusarg, pwd=pwd)
1456
1545
  remove_list.append(token)
1546
+ continue
1547
+
1548
+ # Parameters in -G<word>=<something>
1549
+ m = re.match(r"^\'?\-G\w+\=.+", token)
1550
+ if m:
1551
+ # Copy and strip all outer ' or " on the text:
1552
+ param = strip_outer_quotes(token)
1553
+ self.process_parameter_arg(param, pwd=pwd)
1554
+ remove_list.append(token)
1457
1555
 
1458
1556
  for x in remove_list:
1459
1557
  unparsed.remove(x)
@@ -47,7 +47,7 @@ class Defaults:
47
47
  'exe', 'handlers',
48
48
  'requires_env', 'requires_py', 'requires_cmd', 'requires_in_exe_path',
49
49
  'requires_vsim_helper', 'requires_vscode_extension',
50
- 'disable-tools-multi',
50
+ 'disable-tools-multi', 'disable-auto',
51
51
  ])
52
52
  supported_config_tool_keys = set([
53
53
  'defines',
@@ -158,6 +158,9 @@ def update_config_auto_tool_order_for_tool(tool: str, config: dict) -> str:
158
158
 
159
159
  user_exe = shutil.which(user_exe)
160
160
 
161
+ # try adding to $PATH if in form --tool=/path/to/exe
162
+ tool_try_add_to_path(tool)
163
+
161
164
  if tool not in config['auto_tools_order'][0]:
162
165
  return tool
163
166
  if not user_exe:
@@ -305,7 +308,8 @@ def write_eda_config_and_args(
305
308
  fullpath = os.path.join(dirpath, filename)
306
309
  data = {}
307
310
  for x in ['command_name', 'config', 'target', 'args', 'modified_args', 'defines',
308
- 'incdirs', 'files_v', 'files_sv', 'files_vhd']:
311
+ 'incdirs', 'files_v', 'files_sv', 'files_vhd', 'files_cpp', 'files_sdc',
312
+ 'files_non_source']:
309
313
  # Use deep copy b/c otherwise these are references to opencos.eda.
310
314
  data[x] = copy.deepcopy(getattr(command_obj_ref, x, ''))
311
315
 
@@ -321,3 +325,34 @@ def write_eda_config_and_args(
321
325
  data['config'][k] = str(v)
322
326
 
323
327
  yaml_safe_writer(data=data, filepath=fullpath)
328
+
329
+
330
+ def tool_try_add_to_path(tool: str) -> None:
331
+ '''Since we support --tool=<name>=/path/to/bin/exe, attempt to prepend $PATH
332
+
333
+ with this information for this tool (which will nicely affect all subprocesses,
334
+ but not wreck our original shell).'''
335
+
336
+ if not tool or '=' not in tool:
337
+ return
338
+
339
+ name, exe = tool.split('=')
340
+ if os.path.isdir(name):
341
+ # Someone passes us --tool=<name>=/path/to/bin/ (did not have exe)
342
+ path = name
343
+ else:
344
+ # Someone passes us --tool=<name>=/path/to/bin/exe, remove the exe.
345
+ path, _ = os.path.split(exe)
346
+ if not path:
347
+ return
348
+
349
+ path = os.path.abspath(path)
350
+ if os.path.isdir(path):
351
+ paths = os.environ['PATH'].split(':')
352
+ if path not in paths:
353
+ os.environ['PATH'] = path + ':' + os.environ['PATH']
354
+ util.info(f'--tool={tool} has path information, prepending PATH with: {path}')
355
+ else:
356
+ util.info(f'--tool={tool} has path information, but {path} already in $PATH')
357
+ if exe and os.path.isfile(exe):
358
+ util.info(f'--tool={tool} has path information, using exe {shutil.which(exe)}')
@@ -516,6 +516,7 @@ auto_tools_order:
516
516
  sim: opencos.tools.iverilog.CommandSimIverilog
517
517
 
518
518
  cocotb:
519
+ disable-auto: True # do not allow this to run `eda sim` with --tool not set
519
520
  exe: python
520
521
  requires_cmd:
521
522
  - python -c "import cocotb; print(cocotb.__version__)"
@@ -286,7 +286,11 @@ def get_list_sv_included_files(
286
286
  # If we have more than N levels of `include hunting, then rethink this.
287
287
  # For example, some codebases would do their file dependencies as `include
288
288
  # as part of their header guards, which could be ~100 levels of nesting.
289
- for fname,traversed in sv_included_files_dict.items():
289
+
290
+ # make a copy of keys so we don't alter during traversal of the dict:
291
+ fnames = list(sv_included_files_dict.keys())
292
+ for fname in fnames:
293
+ traversed = sv_included_files_dict[fname]
290
294
  if not traversed:
291
295
  included_files_list = find_sv_included_files_within_file(
292
296
  filename=fname,
@@ -387,6 +391,13 @@ class ExportHelper:
387
391
  paths.
388
392
  '''
389
393
 
394
+ # We'll copy files_sv and files_v later, along with discovered included files,
395
+ # need to copy any others:
396
+ remaing_files_to_cp = []
397
+ for x in self.cmd_design_obj.files.keys():
398
+ if x not in self.cmd_design_obj.files_v + self.cmd_design_obj.files_sv:
399
+ remaing_files_to_cp.append(x)
400
+
390
401
  # Also sets our list of included files.
391
402
  self.included_files = get_list_sv_included_files(
392
403
  all_src_files=self.cmd_design_obj.files_sv + self.cmd_design_obj.files_v,
@@ -398,7 +409,8 @@ class ExportHelper:
398
409
 
399
410
  info(f"export_helper: {self.target=} included files {self.included_files=}")
400
411
 
401
- for filename in self.cmd_design_obj.files_non_source:
412
+
413
+ for filename in remaing_files_to_cp:
402
414
  dst = os.path.join(self.out_dir, os.path.split(filename)[1])
403
415
  if not os.path.exists(dst):
404
416
  shutil.copy(src=filename, dst=dst)
@@ -415,11 +427,13 @@ class ExportHelper:
415
427
 
416
428
  info(f'export_helper: Creating DEPS.yml for {self.target=} in {self.out_dir=}')
417
429
 
418
- # Need to strip path information from our files_sv and files_v:
430
+ # Need to strip path information from our files_sv and files_v
431
+ # (and all source files: cpp, sdc, etc; but skip the non-source files):
419
432
  deps_files = []
420
- for fullpath in self.cmd_design_obj.files_sv + self.cmd_design_obj.files_v:
421
- filename = os.path.split(fullpath)[1]
422
- deps_files.append(filename)
433
+ for fullpath in self.cmd_design_obj.files.keys():
434
+ if fullpath not in self.cmd_design_obj.files_non_source:
435
+ filename = os.path.split(fullpath)[1]
436
+ deps_files.append(filename)
423
437
 
424
438
 
425
439
  data = {
@@ -2508,7 +2508,7 @@ name_tables = { 'OC_VENDOR' : {},
2508
2508
  }
2509
2509
 
2510
2510
  def parse_names():
2511
- name_tables.update(opencos_names.table) #update our global name_table from names.py
2511
+ name_tables.update(opencos_names.NAMES) #update our global name_table from names.py
2512
2512
 
2513
2513
  def lookup_table_string(table, index):
2514
2514
  if isinstance(table, str) and table in name_tables: