opencos-eda 0.2.54__tar.gz → 0.2.56__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.54/opencos_eda.egg-info → opencos_eda-0.2.56}/PKG-INFO +2 -2
  2. {opencos_eda-0.2.54 → opencos_eda-0.2.56}/opencos/commands/__init__.py +2 -0
  3. {opencos_eda-0.2.54 → opencos_eda-0.2.56}/opencos/commands/flist.py +10 -0
  4. opencos_eda-0.2.56/opencos/commands/lint.py +51 -0
  5. {opencos_eda-0.2.54 → opencos_eda-0.2.56}/opencos/commands/sim.py +31 -0
  6. {opencos_eda-0.2.54 → opencos_eda-0.2.56}/opencos/deps/defaults.py +2 -0
  7. {opencos_eda-0.2.54 → opencos_eda-0.2.56}/opencos/deps/deps_file.py +3 -1
  8. {opencos_eda-0.2.54 → opencos_eda-0.2.56}/opencos/deps/deps_processor.py +39 -1
  9. {opencos_eda-0.2.54 → opencos_eda-0.2.56}/opencos/deps_schema.py +22 -0
  10. {opencos_eda-0.2.54 → opencos_eda-0.2.56}/opencos/eda.py +16 -5
  11. {opencos_eda-0.2.54 → opencos_eda-0.2.56}/opencos/eda_base.py +108 -10
  12. {opencos_eda-0.2.54 → opencos_eda-0.2.56}/opencos/eda_config.py +2 -1
  13. {opencos_eda-0.2.54 → opencos_eda-0.2.56}/opencos/eda_config_defaults.yml +13 -1
  14. {opencos_eda-0.2.54 → opencos_eda-0.2.56}/opencos/export_helper.py +15 -5
  15. {opencos_eda-0.2.54 → opencos_eda-0.2.56}/opencos/hw/oc_cli.py +1 -1
  16. {opencos_eda-0.2.54 → opencos_eda-0.2.56}/opencos/names.py +4 -1
  17. {opencos_eda-0.2.54 → opencos_eda-0.2.56}/opencos/tests/helpers.py +5 -0
  18. {opencos_eda-0.2.54 → opencos_eda-0.2.56}/opencos/tests/test_eda_elab.py +26 -5
  19. {opencos_eda-0.2.54 → opencos_eda-0.2.56}/opencos/tests/test_eda_synth.py +12 -0
  20. {opencos_eda-0.2.54 → opencos_eda-0.2.56}/opencos/tools/invio.py +8 -1
  21. {opencos_eda-0.2.54 → opencos_eda-0.2.56}/opencos/tools/invio_helpers.py +47 -8
  22. {opencos_eda-0.2.54 → opencos_eda-0.2.56}/opencos/tools/invio_yosys.py +3 -2
  23. {opencos_eda-0.2.54 → opencos_eda-0.2.56}/opencos/tools/iverilog.py +18 -0
  24. {opencos_eda-0.2.54 → opencos_eda-0.2.56}/opencos/tools/modelsim_ase.py +19 -1
  25. {opencos_eda-0.2.54 → opencos_eda-0.2.56}/opencos/tools/quartus.py +93 -12
  26. {opencos_eda-0.2.54 → opencos_eda-0.2.56}/opencos/tools/questa.py +14 -0
  27. {opencos_eda-0.2.54 → opencos_eda-0.2.56}/opencos/tools/questa_fse.py +13 -0
  28. {opencos_eda-0.2.54 → opencos_eda-0.2.56}/opencos/tools/riviera.py +30 -3
  29. {opencos_eda-0.2.54 → opencos_eda-0.2.56}/opencos/tools/slang.py +17 -1
  30. {opencos_eda-0.2.54 → opencos_eda-0.2.56}/opencos/tools/slang_yosys.py +9 -0
  31. {opencos_eda-0.2.54 → opencos_eda-0.2.56}/opencos/tools/surelog.py +18 -0
  32. {opencos_eda-0.2.54 → opencos_eda-0.2.56}/opencos/tools/verilator.py +19 -0
  33. {opencos_eda-0.2.54 → opencos_eda-0.2.56}/opencos/tools/vivado.py +26 -2
  34. {opencos_eda-0.2.54 → opencos_eda-0.2.56}/opencos/tools/yosys.py +15 -0
  35. {opencos_eda-0.2.54 → opencos_eda-0.2.56}/opencos/util.py +7 -1
  36. {opencos_eda-0.2.54 → opencos_eda-0.2.56}/opencos/utils/str_helpers.py +8 -4
  37. {opencos_eda-0.2.54 → opencos_eda-0.2.56/opencos_eda.egg-info}/PKG-INFO +2 -2
  38. {opencos_eda-0.2.54 → opencos_eda-0.2.56}/opencos_eda.egg-info/SOURCES.txt +1 -0
  39. {opencos_eda-0.2.54 → opencos_eda-0.2.56}/opencos_eda.egg-info/requires.txt +1 -1
  40. {opencos_eda-0.2.54 → opencos_eda-0.2.56}/pyproject.toml +2 -2
  41. {opencos_eda-0.2.54 → opencos_eda-0.2.56}/LICENSE +0 -0
  42. {opencos_eda-0.2.54 → opencos_eda-0.2.56}/LICENSE.spdx +0 -0
  43. {opencos_eda-0.2.54 → opencos_eda-0.2.56}/README.md +0 -0
  44. {opencos_eda-0.2.54 → opencos_eda-0.2.56}/opencos/__init__.py +0 -0
  45. {opencos_eda-0.2.54 → opencos_eda-0.2.56}/opencos/_version.py +0 -0
  46. {opencos_eda-0.2.54 → opencos_eda-0.2.56}/opencos/_waves_pkg.sv +0 -0
  47. {opencos_eda-0.2.54 → opencos_eda-0.2.56}/opencos/commands/build.py +0 -0
  48. {opencos_eda-0.2.54 → opencos_eda-0.2.56}/opencos/commands/deps_help.py +0 -0
  49. {opencos_eda-0.2.54 → opencos_eda-0.2.56}/opencos/commands/elab.py +0 -0
  50. {opencos_eda-0.2.54 → opencos_eda-0.2.56}/opencos/commands/export.py +0 -0
  51. {opencos_eda-0.2.54 → opencos_eda-0.2.56}/opencos/commands/lec.py +0 -0
  52. {opencos_eda-0.2.54 → opencos_eda-0.2.56}/opencos/commands/multi.py +0 -0
  53. {opencos_eda-0.2.54 → opencos_eda-0.2.56}/opencos/commands/open.py +0 -0
  54. {opencos_eda-0.2.54 → opencos_eda-0.2.56}/opencos/commands/proj.py +0 -0
  55. {opencos_eda-0.2.54 → opencos_eda-0.2.56}/opencos/commands/shell.py +0 -0
  56. {opencos_eda-0.2.54 → opencos_eda-0.2.56}/opencos/commands/sweep.py +0 -0
  57. {opencos_eda-0.2.54 → opencos_eda-0.2.56}/opencos/commands/synth.py +0 -0
  58. {opencos_eda-0.2.54 → opencos_eda-0.2.56}/opencos/commands/targets.py +0 -0
  59. {opencos_eda-0.2.54 → opencos_eda-0.2.56}/opencos/commands/upload.py +0 -0
  60. {opencos_eda-0.2.54 → opencos_eda-0.2.56}/opencos/commands/waves.py +0 -0
  61. {opencos_eda-0.2.54 → opencos_eda-0.2.56}/opencos/deps/__init__.py +0 -0
  62. {opencos_eda-0.2.54 → opencos_eda-0.2.56}/opencos/deps/deps_commands.py +0 -0
  63. {opencos_eda-0.2.54 → opencos_eda-0.2.56}/opencos/eda_config_max_verilator_waivers.yml +0 -0
  64. {opencos_eda-0.2.54 → opencos_eda-0.2.56}/opencos/eda_config_reduced.yml +0 -0
  65. {opencos_eda-0.2.54 → opencos_eda-0.2.56}/opencos/eda_deps_bash_completion.bash +0 -0
  66. {opencos_eda-0.2.54 → opencos_eda-0.2.56}/opencos/eda_deps_sanitize.py +0 -0
  67. {opencos_eda-0.2.54 → opencos_eda-0.2.56}/opencos/eda_extract_targets.py +0 -0
  68. {opencos_eda-0.2.54 → opencos_eda-0.2.56}/opencos/eda_tool_helper.py +0 -0
  69. {opencos_eda-0.2.54 → opencos_eda-0.2.56}/opencos/export_json_convert.py +0 -0
  70. {opencos_eda-0.2.54 → opencos_eda-0.2.56}/opencos/files.py +0 -0
  71. {opencos_eda-0.2.54 → opencos_eda-0.2.56}/opencos/hw/__init__.py +0 -0
  72. {opencos_eda-0.2.54 → opencos_eda-0.2.56}/opencos/hw/pcie.py +0 -0
  73. {opencos_eda-0.2.54 → opencos_eda-0.2.56}/opencos/peakrdl_cleanup.py +0 -0
  74. {opencos_eda-0.2.54 → opencos_eda-0.2.56}/opencos/seed.py +0 -0
  75. {opencos_eda-0.2.54 → opencos_eda-0.2.56}/opencos/tests/__init__.py +0 -0
  76. {opencos_eda-0.2.54 → opencos_eda-0.2.56}/opencos/tests/custom_config.yml +0 -0
  77. {opencos_eda-0.2.54 → opencos_eda-0.2.56}/opencos/tests/deps_files/command_order/DEPS.yml +0 -0
  78. {opencos_eda-0.2.54 → opencos_eda-0.2.56}/opencos/tests/deps_files/error_msgs/DEPS.yml +0 -0
  79. {opencos_eda-0.2.54 → opencos_eda-0.2.56}/opencos/tests/deps_files/iverilog_test/DEPS.yml +0 -0
  80. {opencos_eda-0.2.54 → opencos_eda-0.2.56}/opencos/tests/deps_files/no_deps_here/DEPS.yml +0 -0
  81. {opencos_eda-0.2.54 → opencos_eda-0.2.56}/opencos/tests/deps_files/non_sv_reqs/DEPS.yml +0 -0
  82. {opencos_eda-0.2.54 → opencos_eda-0.2.56}/opencos/tests/deps_files/tags_with_tools/DEPS.yml +0 -0
  83. {opencos_eda-0.2.54 → opencos_eda-0.2.56}/opencos/tests/deps_files/test_err_fatal/DEPS.yml +0 -0
  84. {opencos_eda-0.2.54 → opencos_eda-0.2.56}/opencos/tests/test_build.py +0 -0
  85. {opencos_eda-0.2.54 → opencos_eda-0.2.56}/opencos/tests/test_deps_helpers.py +0 -0
  86. {opencos_eda-0.2.54 → opencos_eda-0.2.56}/opencos/tests/test_deps_schema.py +0 -0
  87. {opencos_eda-0.2.54 → opencos_eda-0.2.56}/opencos/tests/test_eda.py +0 -0
  88. {opencos_eda-0.2.54 → opencos_eda-0.2.56}/opencos/tests/test_oc_cli.py +0 -0
  89. {opencos_eda-0.2.54 → opencos_eda-0.2.56}/opencos/tests/test_tools.py +0 -0
  90. {opencos_eda-0.2.54 → opencos_eda-0.2.56}/opencos/tools/__init__.py +0 -0
  91. {opencos_eda-0.2.54 → opencos_eda-0.2.56}/opencos/tools/cocotb.py +0 -0
  92. {opencos_eda-0.2.54 → opencos_eda-0.2.56}/opencos/tools/tabbycad_yosys.py +0 -0
  93. {opencos_eda-0.2.54 → opencos_eda-0.2.56}/opencos/utils/__init__.py +0 -0
  94. {opencos_eda-0.2.54 → opencos_eda-0.2.56}/opencos/utils/markup_helpers.py +0 -0
  95. {opencos_eda-0.2.54 → opencos_eda-0.2.56}/opencos/utils/status_constants.py +0 -0
  96. {opencos_eda-0.2.54 → opencos_eda-0.2.56}/opencos/utils/subprocess_helpers.py +0 -0
  97. {opencos_eda-0.2.54 → opencos_eda-0.2.56}/opencos/utils/vscode_helper.py +0 -0
  98. {opencos_eda-0.2.54 → opencos_eda-0.2.56}/opencos/utils/vsim_helper.py +0 -0
  99. {opencos_eda-0.2.54 → opencos_eda-0.2.56}/opencos_eda.egg-info/dependency_links.txt +0 -0
  100. {opencos_eda-0.2.54 → opencos_eda-0.2.56}/opencos_eda.egg-info/entry_points.txt +0 -0
  101. {opencos_eda-0.2.54 → opencos_eda-0.2.56}/opencos_eda.egg-info/top_level.txt +0 -0
  102. {opencos_eda-0.2.54 → opencos_eda-0.2.56}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: opencos-eda
3
- Version: 0.2.54
3
+ Version: 0.2.56
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
@@ -15,5 +15,5 @@ Requires-Dist: schema>=0.7.7
15
15
  Requires-Dist: toml>=0.10.2
16
16
  Requires-Dist: yamllint>=1.35.1
17
17
  Requires-Dist: PySerial>=3.5
18
- Requires-Dist: cocotb>=2.0.0b1
18
+ Requires-Dist: cocotb>=2.0
19
19
  Dynamic: license-file
@@ -10,6 +10,7 @@ from .elab import CommandElab
10
10
  from .export import CommandExport
11
11
  from .flist import CommandFList
12
12
  from .multi import CommandMulti, CommandToolsMulti
13
+ from .lint import CommandLint
13
14
  from .open import CommandOpen
14
15
  from .proj import CommandProj
15
16
  from .sim import CommandSim
@@ -28,6 +29,7 @@ __all__ = [
28
29
  'CommandExport',
29
30
  'CommandFList',
30
31
  'CommandMulti',
32
+ 'CommandLint',
31
33
  'CommandOpen',
32
34
  'CommandProj',
33
35
  'CommandSim',
@@ -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:
@@ -0,0 +1,51 @@
1
+ '''opencos.commands.lint - Base class command handler for: eda lint ...
2
+
3
+ Intended to be overriden by Tool based classes (such as CommandLintVivado, etc)
4
+
5
+ Note that many 'lint' command handlers that also can perform simulations, such
6
+ as VerilatorLint, will instead inherit from VerilatorSim and simply perform a
7
+ shortened sim as the lint, instead of inheriting CommandLint.
8
+
9
+ Tools that don't support a 'sim' command will generally use CommandLint, such
10
+ as CommandLintSlang.'''
11
+
12
+ from opencos.commands.sim import CommandSim
13
+
14
+ class CommandLint(CommandSim):
15
+ '''Base class command handler for: eda lint ...'''
16
+
17
+ command_name = 'lint'
18
+
19
+ def __init__(self, config: dict):
20
+ CommandSim.__init__(self, config=config)
21
+ # add args specific to this simulator
22
+ self.args['stop-after-compile'] = True
23
+ self.args['lint'] = True
24
+ self.args['verilate-args'] = []
25
+
26
+
27
+
28
+ def compile(self) -> None:
29
+ raise NotImplementedError
30
+
31
+ def elaborate(self) -> None:
32
+ raise NotImplementedError
33
+
34
+ def get_compile_command_lists(self, **kwargs) -> list:
35
+ ''' Returns a list of lists (list of command lists).'''
36
+ raise NotImplementedError
37
+
38
+ def get_elaborate_command_lists(self, **kwargs) -> list:
39
+ ''' Returns a list of lists (list of command lists).'''
40
+ raise NotImplementedError
41
+
42
+ # CommandSim methods that elab does not use:
43
+
44
+ def simulate(self):
45
+ pass
46
+
47
+ def get_simulate_command_lists(self, **kwargs) -> list:
48
+ return []
49
+
50
+ def get_post_simulate_command_lists(self, **kwargs) -> list:
51
+ return []
@@ -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',
@@ -306,7 +306,9 @@ class DepsFile:
306
306
  return False
307
307
 
308
308
  if not is_valid_target_name(target_node):
309
- util.warning(f"In file {self.rel_deps_file}, {target_node} {VALID_TARGET_INFO_STR}")
309
+ util.warning(
310
+ f"In file {self.rel_deps_file}, {target_node} {VALID_TARGET_INFO_STR}"
311
+ )
310
312
 
311
313
  if not caller_info:
312
314
  # If we don't have caller_info, likely came from command line (or DEPS JSON data):
@@ -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],
@@ -23,7 +23,7 @@ from opencos import util, eda_config, eda_base
23
23
  from opencos.eda_base import Tool, which_tool, get_eda_exec
24
24
  from opencos.utils import vsim_helper, vscode_helper
25
25
  from opencos.utils.subprocess_helpers import subprocess_run_background
26
- from opencos.utils import status_constants
26
+ from opencos.utils import status_constants, str_helpers
27
27
 
28
28
  # Configure util:
29
29
  util.progname = "EDA"
@@ -78,7 +78,7 @@ def get_all_commands_help_str(config: dict) -> str:
78
78
  return '\n'.join(all_commands_help)
79
79
 
80
80
 
81
- def usage(tokens: list, config: dict, command="") -> int:
81
+ def usage(tokens: list, config: dict, command: str = "", tool: str = "") -> int:
82
82
  '''Returns an int shell return code, given remaining args (tokens list) and eda command.
83
83
 
84
84
  config is the config dict. Used to check valid commands in config['command_handler']
@@ -112,6 +112,9 @@ And <files|targets, ...> is one or more source file or DEPS markup file target,
112
112
 
113
113
  if command in config['command_handler'].keys():
114
114
  sco = config['command_handler'][command](config=config) # sub command object
115
+ sco_tool = getattr(sco, '_TOOL', '')
116
+ if tool and tool != sco_tool:
117
+ util.warning(f'{tool=} does not support {command=}')
115
118
  sco.help(tokens=tokens)
116
119
  return util.exit(0)
117
120
 
@@ -282,9 +285,15 @@ def tool_setup(tool: str, config: dict, quiet: bool = False, auto_setup: bool =
282
285
  if tool not in config['auto_tools_order'][0]:
283
286
  tools = list(config.get('auto_tools_order', [{}])[0].keys())
284
287
  cfg_yaml_fname = config.get('config-yml', None)
288
+ util.warning(f'Unknown tool: {tool}')
289
+ if tools:
290
+ util.info('Known tools:')
291
+ pretty_tools = str_helpers.pretty_list_columns_manual(data=tools)
292
+ for row in pretty_tools:
293
+ if row:
294
+ util.info(row)
285
295
  util.error(f"Don't know how to run tool_setup({tool=}), is not in",
286
- f"config['auto_tools_order'] for {tools=}",
287
- f"from {cfg_yaml_fname}")
296
+ f"config['auto_tools_order'] from {cfg_yaml_fname}")
288
297
  return
289
298
 
290
299
  if tool not in config['auto_tools_found']:
@@ -394,7 +403,9 @@ def process_tokens( # pylint: disable=too-many-branches,too-many-statements,too-
394
403
  for arg in unparsed:
395
404
  if not arg.startswith('-'):
396
405
  command = arg
397
- return usage(tokens=unparsed, config=config, command=command)
406
+ if parsed.tool:
407
+ tool_setup(parsed.tool, config=config)
408
+ return usage(tokens=unparsed, config=config, command=command, tool=parsed.tool)
398
409
 
399
410
  if parsed.tool:
400
411
  tool_setup(parsed.tool, config=config)
@@ -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)
@@ -305,7 +305,8 @@ def write_eda_config_and_args(
305
305
  fullpath = os.path.join(dirpath, filename)
306
306
  data = {}
307
307
  for x in ['command_name', 'config', 'target', 'args', 'modified_args', 'defines',
308
- 'incdirs', 'files_v', 'files_sv', 'files_vhd']:
308
+ 'incdirs', 'files_v', 'files_sv', 'files_vhd', 'files_cpp', 'files_sdc',
309
+ 'files_non_source']:
309
310
  # Use deep copy b/c otherwise these are references to opencos.eda.
310
311
  data[x] = copy.deepcopy(getattr(command_obj_ref, x, ''))
311
312