opencos-eda 0.3.11__tar.gz → 0.3.13__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.3.11/opencos_eda.egg-info → opencos_eda-0.3.13}/PKG-INFO +1 -2
  2. opencos_eda-0.3.13/opencos/commands/flist.py +413 -0
  3. {opencos_eda-0.3.11 → opencos_eda-0.3.13}/opencos/commands/multi.py +5 -3
  4. {opencos_eda-0.3.11 → opencos_eda-0.3.13}/opencos/commands/sim.py +121 -7
  5. {opencos_eda-0.3.11 → opencos_eda-0.3.13}/opencos/commands/sweep.py +1 -1
  6. {opencos_eda-0.3.11 → opencos_eda-0.3.13}/opencos/commands/upload.py +4 -4
  7. {opencos_eda-0.3.11 → opencos_eda-0.3.13}/opencos/commands/waves.py +44 -0
  8. {opencos_eda-0.3.11 → opencos_eda-0.3.13}/opencos/deps/deps_file.py +4 -1
  9. {opencos_eda-0.3.11 → opencos_eda-0.3.13}/opencos/deps_schema.py +2 -2
  10. {opencos_eda-0.3.11 → opencos_eda-0.3.13}/opencos/eda.py +284 -175
  11. {opencos_eda-0.3.11 → opencos_eda-0.3.13}/opencos/eda_base.py +54 -22
  12. {opencos_eda-0.3.11 → opencos_eda-0.3.13}/opencos/eda_config.py +51 -24
  13. {opencos_eda-0.3.11 → opencos_eda-0.3.13}/opencos/eda_config_defaults.yml +326 -195
  14. opencos_eda-0.3.13/opencos/eda_config_reduced.yml +45 -0
  15. opencos_eda-0.3.13/opencos/eda_tool_helper.py +258 -0
  16. {opencos_eda-0.3.11 → opencos_eda-0.3.13}/opencos/tools/cocotb.py +6 -29
  17. {opencos_eda-0.3.11 → opencos_eda-0.3.13}/opencos/tools/invio.py +0 -6
  18. {opencos_eda-0.3.11 → opencos_eda-0.3.13}/opencos/tools/iverilog.py +16 -16
  19. {opencos_eda-0.3.11 → opencos_eda-0.3.13}/opencos/tools/modelsim_ase.py +1 -13
  20. {opencos_eda-0.3.11 → opencos_eda-0.3.13}/opencos/tools/quartus.py +81 -24
  21. {opencos_eda-0.3.11 → opencos_eda-0.3.13}/opencos/tools/questa.py +0 -14
  22. {opencos_eda-0.3.11 → opencos_eda-0.3.13}/opencos/tools/questa_common.py +95 -30
  23. {opencos_eda-0.3.11 → opencos_eda-0.3.13}/opencos/tools/questa_fe.py +0 -14
  24. {opencos_eda-0.3.11 → opencos_eda-0.3.13}/opencos/tools/questa_fse.py +0 -14
  25. {opencos_eda-0.3.11 → opencos_eda-0.3.13}/opencos/tools/riviera.py +56 -25
  26. {opencos_eda-0.3.11 → opencos_eda-0.3.13}/opencos/tools/slang.py +15 -12
  27. {opencos_eda-0.3.11 → opencos_eda-0.3.13}/opencos/tools/slang_yosys.py +0 -6
  28. {opencos_eda-0.3.11 → opencos_eda-0.3.13}/opencos/tools/surelog.py +14 -11
  29. {opencos_eda-0.3.11 → opencos_eda-0.3.13}/opencos/tools/tabbycad_yosys.py +1 -7
  30. {opencos_eda-0.3.11 → opencos_eda-0.3.13}/opencos/tools/verilator.py +18 -14
  31. {opencos_eda-0.3.11 → opencos_eda-0.3.13}/opencos/tools/vivado.py +57 -22
  32. {opencos_eda-0.3.11 → opencos_eda-0.3.13}/opencos/tools/yosys.py +2 -4
  33. {opencos_eda-0.3.11 → opencos_eda-0.3.13}/opencos/util.py +14 -5
  34. {opencos_eda-0.3.11 → opencos_eda-0.3.13}/opencos/utils/str_helpers.py +42 -1
  35. {opencos_eda-0.3.11 → opencos_eda-0.3.13/opencos_eda.egg-info}/PKG-INFO +1 -2
  36. {opencos_eda-0.3.11 → opencos_eda-0.3.13}/opencos_eda.egg-info/SOURCES.txt +0 -1
  37. {opencos_eda-0.3.11 → opencos_eda-0.3.13}/opencos_eda.egg-info/requires.txt +0 -1
  38. {opencos_eda-0.3.11 → opencos_eda-0.3.13}/pyproject.toml +1 -2
  39. opencos_eda-0.3.11/opencos/commands/flist.py +0 -230
  40. opencos_eda-0.3.11/opencos/eda_config_max_verilator_waivers.yml +0 -39
  41. opencos_eda-0.3.11/opencos/eda_config_reduced.yml +0 -65
  42. opencos_eda-0.3.11/opencos/eda_tool_helper.py +0 -89
  43. {opencos_eda-0.3.11 → opencos_eda-0.3.13}/LICENSE +0 -0
  44. {opencos_eda-0.3.11 → opencos_eda-0.3.13}/LICENSE.spdx +0 -0
  45. {opencos_eda-0.3.11 → opencos_eda-0.3.13}/README.md +0 -0
  46. {opencos_eda-0.3.11 → opencos_eda-0.3.13}/docs/Architecture.md +0 -0
  47. {opencos_eda-0.3.11 → opencos_eda-0.3.13}/docs/ConnectingApps.md +0 -0
  48. {opencos_eda-0.3.11 → opencos_eda-0.3.13}/docs/DEPS.md +0 -0
  49. {opencos_eda-0.3.11 → opencos_eda-0.3.13}/docs/Debug.md +0 -0
  50. {opencos_eda-0.3.11 → opencos_eda-0.3.13}/docs/DirectoryStructure.md +0 -0
  51. {opencos_eda-0.3.11 → opencos_eda-0.3.13}/docs/Installation.md +0 -0
  52. {opencos_eda-0.3.11 → opencos_eda-0.3.13}/docs/OcVivadoTcl.md +0 -0
  53. {opencos_eda-0.3.11 → opencos_eda-0.3.13}/docs/OpenQuestions.md +0 -0
  54. {opencos_eda-0.3.11 → opencos_eda-0.3.13}/docs/README.md +0 -0
  55. {opencos_eda-0.3.11 → opencos_eda-0.3.13}/docs/RtlCodingStyle.md +0 -0
  56. {opencos_eda-0.3.11 → opencos_eda-0.3.13}/docs/eda.md +0 -0
  57. {opencos_eda-0.3.11 → opencos_eda-0.3.13}/docs/oc_cli.md +0 -0
  58. {opencos_eda-0.3.11 → opencos_eda-0.3.13}/opencos/__init__.py +0 -0
  59. {opencos_eda-0.3.11 → opencos_eda-0.3.13}/opencos/_version.py +0 -0
  60. {opencos_eda-0.3.11 → opencos_eda-0.3.13}/opencos/_waves_pkg.sv +0 -0
  61. {opencos_eda-0.3.11 → opencos_eda-0.3.13}/opencos/commands/__init__.py +0 -0
  62. {opencos_eda-0.3.11 → opencos_eda-0.3.13}/opencos/commands/build.py +0 -0
  63. {opencos_eda-0.3.11 → opencos_eda-0.3.13}/opencos/commands/deps_help.py +0 -0
  64. {opencos_eda-0.3.11 → opencos_eda-0.3.13}/opencos/commands/elab.py +0 -0
  65. {opencos_eda-0.3.11 → opencos_eda-0.3.13}/opencos/commands/export.py +0 -0
  66. {opencos_eda-0.3.11 → opencos_eda-0.3.13}/opencos/commands/lec.py +0 -0
  67. {opencos_eda-0.3.11 → opencos_eda-0.3.13}/opencos/commands/lint.py +0 -0
  68. {opencos_eda-0.3.11 → opencos_eda-0.3.13}/opencos/commands/open.py +0 -0
  69. {opencos_eda-0.3.11 → opencos_eda-0.3.13}/opencos/commands/proj.py +0 -0
  70. {opencos_eda-0.3.11 → opencos_eda-0.3.13}/opencos/commands/shell.py +0 -0
  71. {opencos_eda-0.3.11 → opencos_eda-0.3.13}/opencos/commands/synth.py +0 -0
  72. {opencos_eda-0.3.11 → opencos_eda-0.3.13}/opencos/commands/targets.py +0 -0
  73. {opencos_eda-0.3.11 → opencos_eda-0.3.13}/opencos/deps/__init__.py +0 -0
  74. {opencos_eda-0.3.11 → opencos_eda-0.3.13}/opencos/deps/defaults.py +0 -0
  75. {opencos_eda-0.3.11 → opencos_eda-0.3.13}/opencos/deps/deps_commands.py +0 -0
  76. {opencos_eda-0.3.11 → opencos_eda-0.3.13}/opencos/deps/deps_processor.py +0 -0
  77. {opencos_eda-0.3.11 → opencos_eda-0.3.13}/opencos/eda_deps_bash_completion.bash +0 -0
  78. {opencos_eda-0.3.11 → opencos_eda-0.3.13}/opencos/eda_deps_sanitize.py +0 -0
  79. {opencos_eda-0.3.11 → opencos_eda-0.3.13}/opencos/eda_extract_targets.py +0 -0
  80. {opencos_eda-0.3.11 → opencos_eda-0.3.13}/opencos/export_helper.py +0 -0
  81. {opencos_eda-0.3.11 → opencos_eda-0.3.13}/opencos/export_json_convert.py +0 -0
  82. {opencos_eda-0.3.11 → opencos_eda-0.3.13}/opencos/files.py +0 -0
  83. {opencos_eda-0.3.11 → opencos_eda-0.3.13}/opencos/hw/__init__.py +0 -0
  84. {opencos_eda-0.3.11 → opencos_eda-0.3.13}/opencos/hw/oc_cli.py +0 -0
  85. {opencos_eda-0.3.11 → opencos_eda-0.3.13}/opencos/hw/pcie.py +0 -0
  86. {opencos_eda-0.3.11 → opencos_eda-0.3.13}/opencos/names.py +0 -0
  87. {opencos_eda-0.3.11 → opencos_eda-0.3.13}/opencos/peakrdl_cleanup.py +0 -0
  88. {opencos_eda-0.3.11 → opencos_eda-0.3.13}/opencos/seed.py +0 -0
  89. {opencos_eda-0.3.11 → opencos_eda-0.3.13}/opencos/tools/__init__.py +0 -0
  90. {opencos_eda-0.3.11 → opencos_eda-0.3.13}/opencos/tools/invio_helpers.py +0 -0
  91. {opencos_eda-0.3.11 → opencos_eda-0.3.13}/opencos/tools/invio_yosys.py +0 -0
  92. {opencos_eda-0.3.11 → opencos_eda-0.3.13}/opencos/utils/__init__.py +0 -0
  93. {opencos_eda-0.3.11 → opencos_eda-0.3.13}/opencos/utils/dict_helpers.py +0 -0
  94. {opencos_eda-0.3.11 → opencos_eda-0.3.13}/opencos/utils/markup_helpers.py +0 -0
  95. {opencos_eda-0.3.11 → opencos_eda-0.3.13}/opencos/utils/status_constants.py +0 -0
  96. {opencos_eda-0.3.11 → opencos_eda-0.3.13}/opencos/utils/subprocess_helpers.py +0 -0
  97. {opencos_eda-0.3.11 → opencos_eda-0.3.13}/opencos/utils/vscode_helper.py +0 -0
  98. {opencos_eda-0.3.11 → opencos_eda-0.3.13}/opencos/utils/vsim_helper.py +0 -0
  99. {opencos_eda-0.3.11 → opencos_eda-0.3.13}/opencos_eda.egg-info/dependency_links.txt +0 -0
  100. {opencos_eda-0.3.11 → opencos_eda-0.3.13}/opencos_eda.egg-info/entry_points.txt +0 -0
  101. {opencos_eda-0.3.11 → opencos_eda-0.3.13}/opencos_eda.egg-info/top_level.txt +0 -0
  102. {opencos_eda-0.3.11 → opencos_eda-0.3.13}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: opencos-eda
3
- Version: 0.3.11
3
+ Version: 0.3.13
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
@@ -20,7 +20,6 @@ Requires-Dist: supports_color>=0.2.0
20
20
  Provides-Extra: dev
21
21
  Requires-Dist: pylint>=3.0.0; extra == "dev"
22
22
  Requires-Dist: pytest>=8.3.5; extra == "dev"
23
- Requires-Dist: twine>=6.1.0; extra == "dev"
24
23
  Provides-Extra: cocotb
25
24
  Requires-Dist: cocotb>=2.0; extra == "cocotb"
26
25
  Requires-Dist: pytest>=8.3.5; extra == "cocotb"
@@ -0,0 +1,413 @@
1
+ '''opencos.commands.flist - Base class command handler for: eda flist ...
2
+
3
+ Intended to be overriden by Tool based classes (such as CommandFListVivado, etc).'''
4
+
5
+ # pylint: disable=too-many-branches
6
+ # pylint: disable=too-many-statements
7
+
8
+ import os
9
+ import shlex
10
+
11
+ from opencos import util, eda_config
12
+ from opencos.eda_base import CommandDesign, Tool
13
+ from opencos.utils.str_helpers import strip_all_quotes
14
+ from opencos.commands.sim import parameters_dict_get_command_list
15
+ from opencos.utils.str_helpers import strip_outer_quotes
16
+
17
+ class CommandFList(CommandDesign):
18
+ '''Base class command handler for: eda flist ...'''
19
+
20
+ command_name = 'flist'
21
+
22
+ def __init__(self, config: dict):
23
+ CommandDesign.__init__(self, config=config)
24
+
25
+ # If there's no tool attached, then we'll assume this flist is being created
26
+ # to run in `eda`, not some vendor tool.
27
+ self.flist_has_tool = isinstance(self, Tool)
28
+
29
+ self.flist_args = {
30
+ 'out' : "flist.out",
31
+ 'emit-define' : True,
32
+ 'emit-parameter' : True,
33
+ 'emit-incdir' : True,
34
+ 'emit-plusargs' : True,
35
+ 'emit-v' : True,
36
+ 'emit-sv' : True,
37
+ 'emit-vhd' : True,
38
+ 'emit-cpp' : True,
39
+ 'emit-non-sources' : True, # as comments, from DEPS 'reqs'
40
+ 'emit-eda-args' : not self.flist_has_tool, # no Tool means flist for eda.
41
+ 'prefix-define' : "+define+",
42
+ 'prefix-parameter' : "-G",
43
+ 'prefix-incdir' : "+incdir+",
44
+ 'prefix-plusargs' : "+",
45
+ 'prefix-v' : "",
46
+ 'prefix-sv' : "",
47
+ 'prefix-vhd' : "",
48
+ 'prefix-cpp' : "",
49
+ 'prefix-non-sources' : "", # as comments anyway.
50
+ # NOTE - the defaults are for creating an flist that is suitable for 'eda', which for
51
+ # defines means: optionally single-quote the entire thing, double-quote string values
52
+ # only.
53
+ # Tool classes should override if they want to set safe-mode-defines=True.
54
+ # Tool classes may also avoid these args entirely in their derived CommandFList class
55
+ 'safe-mode-defines' : not self.flist_has_tool, # no Tool
56
+ 'bracket-quote-define': False,
57
+ 'single-quote-define': False,
58
+ 'quote-define' : self.flist_has_tool, # Tool, this is NOT for eda.
59
+ 'equal-define' : True,
60
+ 'escape-define-value': False,
61
+ 'quote-define-value' : False,
62
+ 'bracket-quote-path' : False,
63
+ 'single-quote-path' : False,
64
+ 'double-quote-path' : False,
65
+ 'quote-path' : True,
66
+ 'build-script' : "", # we don't want this to error either
67
+
68
+ 'print-to-stdout': False,
69
+
70
+ # ex: eda flist --print-to-stdout --emit-rel-path --quiet <target>
71
+ 'emit-rel-path' : False,
72
+ }
73
+
74
+
75
+ self.args.update({
76
+ 'eda-dir' : 'eda.flist', # user can specify eda-dir if files are generated.
77
+ })
78
+ self.args.update(self.flist_args)
79
+
80
+ self.args_help.update({
81
+ 'print-to-stdout': "do not save file, print to stdout",
82
+ })
83
+
84
+ def process_tokens(
85
+ self, tokens: list , process_all: bool = True, pwd: str = os.getcwd()
86
+ ) -> list:
87
+ unparsed = CommandDesign.process_tokens(
88
+ self, tokens=tokens, process_all=process_all, pwd=pwd
89
+ )
90
+ if self.stop_process_tokens_before_do_it():
91
+ return unparsed
92
+
93
+ self.do_it()
94
+ return unparsed
95
+
96
+ def get_flist_dict(self) -> dict:
97
+ '''Returns dict of some internal class member vars, ignores args
98
+
99
+ Useful for an external caller to get details about this CommandDesign child
100
+ object without generating a .f file, or having to know specifics about the
101
+ class
102
+ '''
103
+ self.command_safe_set_tool_defines() # (Command.command_safe_set_tool_defines)
104
+
105
+ ret = {}
106
+ for key in ['files_sv', 'files_v', 'files_vhd', 'defines', 'incdirs',
107
+ 'parameters', 'unprocessed-plusargs']:
108
+ # These keys must exist, all are lists, defines is a dict
109
+ x = getattr(self, key, None)
110
+ if isinstance(x, (dict, list)):
111
+ ret[key] = x.copy()
112
+ else:
113
+ ret[key] = x
114
+ return ret
115
+
116
+ def get_flist_defines_list(self) -> list:
117
+ '''Returns formatted list of str for known defines'''
118
+
119
+ ret = []
120
+ prefix = strip_all_quotes(self.args['prefix-define'])
121
+ for d, value in self.defines.items():
122
+
123
+ if value is None:
124
+ ret.append(prefix + d)
125
+ continue
126
+
127
+ # else, value exists:
128
+ safe_mode_guard_str_value = bool(
129
+ self.args['safe-mode-defines'] and isinstance(value, str) and ' ' in value
130
+ )
131
+
132
+ if self.args['bracket-quote-define']:
133
+ qd1 = "{"
134
+ qd2 = "}"
135
+ elif self.args['single-quote-define']:
136
+ qd1 = "'"
137
+ qd2 = "'"
138
+ elif self.args['quote-define']:
139
+ qd1 = '"'
140
+ qd2 = '"'
141
+ else:
142
+ qd1 = ''
143
+ qd2 = ''
144
+
145
+ if self.args['equal-define']:
146
+ ed1 = '='
147
+ else:
148
+ ed1 = ' '
149
+
150
+ if self.args['escape-define-value']:
151
+ value = value.replace('\\', '\\\\').replace('"', '\\"')
152
+ if self.args['quote-define-value']:
153
+ value = '"' + value + '"'
154
+ if safe_mode_guard_str_value:
155
+ value = strip_outer_quotes(value.strip('\n'))
156
+ value = '"' + value + '"'
157
+
158
+ if self.args['quote-define'] and value.startswith('"') and value.endswith('"'):
159
+ # If you wanted your define to look like:
160
+ # +define+"NAME=VALUE", but VALUE also has double quotes wrapping it,
161
+ # it's unlikely to work so we'll optimistically so escape the " wrapping value.
162
+ # If you have additional " in the middle of the value, good luck.
163
+ value = '\\"' + value[1:-1] + '\\"'
164
+
165
+ newline = prefix + qd1 + f"{d}{ed1}{value}" + qd2
166
+
167
+ if safe_mode_guard_str_value:
168
+ # wrap the entire thing with single-quotes, so it survives as a single
169
+ # token in an eda dot-f file:
170
+ newline = shlex.quote(newline)
171
+
172
+ ret.append(newline)
173
+
174
+ return ret
175
+
176
+ def get_flist_plusargs_list(self) -> list:
177
+ '''Returns formatted list of str for unprocessed plusargs
178
+
179
+ Tool based classes can override if they also want to query their own
180
+ processed plusargs, such as CommandSim.args[sim-plusargs']
181
+ '''
182
+ ret = []
183
+ for x in self.args.get('unprocessed-plusargs', []) + self.args.get('sim-plusargs', []):
184
+ if self.args['prefix-plusargs']:
185
+ if x.startswith('+'):
186
+ x = x[1:] # strip leading +
187
+ x = self.args['prefix-plusargs'] + x
188
+ ret.append(x)
189
+ return ret
190
+
191
+ def get_flist_parameter_list(self) -> list:
192
+ '''Returns formatted list of str for parameters'''
193
+ prefix = strip_all_quotes(self.args['prefix-parameter'])
194
+ return parameters_dict_get_command_list(
195
+ params=self.parameters, arg_prefix=prefix, for_flist=True
196
+ )
197
+
198
+ def get_flist_eda_args_list(self) -> list:
199
+ '''Returns list of eda args for an eda-capable flist
200
+
201
+ - This will NOT add any util based args (--color | --no-color, --debug, etc)
202
+ - This will NOT add any -f/--input-file args (those are already resolved)
203
+
204
+ - This WILL add --env-file args
205
+ - This WILL add --config-yml args that were not default value
206
+
207
+ Not intended to be overriden by Tool based command classes.
208
+ '''
209
+ ret = []
210
+
211
+ # --env-file(s), if used:
212
+ for env_file in util.env_files_loaded:
213
+ ret.append(f'--env-file={env_file}')
214
+
215
+ # --config-yml, if non-default:
216
+ ret.extend(eda_config.get_config_yml_args_for_flist())
217
+
218
+ # EDA args, but not the flist specific args, and only those that were modified.
219
+ for arg, _ in self.modified_args.items():
220
+
221
+ if arg in self.flist_args:
222
+ # do not emit flist command args
223
+ continue
224
+
225
+ value = self.args[arg]
226
+ if isinstance(value, bool):
227
+ if value:
228
+ ret.append(f'--{arg}')
229
+ else:
230
+ ret.append(f'--no-{arg}')
231
+ else:
232
+ ret.append(f'--{arg}={value}')
233
+ return ret
234
+
235
+ def get_additional_flist_args_list(self) -> list:
236
+ '''Derived classes may override, to output additional args in the flist'''
237
+ return []
238
+
239
+ def get_additional_flist_files_list(self) -> list:
240
+ '''Derived classes may override, to output additional files in the flist'''
241
+ return []
242
+
243
+
244
+ def do_it(self) -> None:
245
+ '''do_it() is the main entry point for creating the flist(),
246
+
247
+ Usually it is called from self.process_tokens()'''
248
+
249
+ # add defines for this job
250
+ self.command_safe_set_tool_defines() # (Command.command_safe_set_tool_defines)
251
+
252
+ if not self.args['top']:
253
+ util.warning(f'CommandFList: {self.command_name=} not run due to lack of',
254
+ f'{self.args["top"]=} value')
255
+ self.write_eda_config_and_args()
256
+ return
257
+
258
+ if self.config['tool']:
259
+ tool_string = f' (with --tool={self.config["tool"]})'
260
+ else:
261
+ tool_string = ''
262
+
263
+ # if config['tool'] is set, but self.flist_has_tool is False, we're likely using
264
+ # this default handler CommandFList and the Tool class hasn't defined what they
265
+ # do. In this case, simply warn that this will emit a non-tool specific default flist
266
+ # intended for use by `eda`:
267
+ if self.config['tool'] and not self.flist_has_tool:
268
+ util.warning(f'For command="flist"{tool_string}, there is no tool',
269
+ 'specific handler for producing an flist. The default eda flist will',
270
+ 'be emitted')
271
+ # If this happens, you'll likely want the Tool based defines (that were never set
272
+ # by Tool.set_tool_defines(self) b/c we have no Tool class.
273
+ # TODO(drew): This is only a best-effort, we could create a derived Tool object and
274
+ # instead call obj.set_tool_defines(), and update self.defines instead?
275
+ _tool_config = self.config.get('tools', {}).get(self.config['tool'], {})
276
+ self.defines.update(
277
+ _tool_config.get('defines', {})
278
+ )
279
+
280
+
281
+
282
+ # check if we're overwriting the output flist file.
283
+ if self.args['print-to-stdout']:
284
+ pass
285
+ elif os.path.exists(self.args['out']):
286
+ if self.args['force']:
287
+ util.info(f"Removing existing {self.args['out']}")
288
+ os.remove(self.args['out'])
289
+ else:
290
+ self.error(f"Not overwriting {self.args['out']} unless you specify --force")
291
+
292
+ # Note - we create a work_dir in case any DEPS commands created files that need to be
293
+ # added to our sources.
294
+ self.create_work_dir()
295
+ self.run_dep_commands()
296
+
297
+ pq1 = ""
298
+ pq2 = "" # pq = path quote
299
+ if not self.args['quote-path']:
300
+ pass # if we decide to make one of the below default, this will override
301
+ elif self.args['bracket-quote-path']:
302
+ pq1 = "{"
303
+ pq2 = "}"
304
+ elif self.args['single-quote-path']:
305
+ pq1 = "'"
306
+ pq2 = "'"
307
+ elif self.args['double-quote-path']:
308
+ pq1 = '"'
309
+ pq2 = '"'
310
+
311
+ if self.args['print-to-stdout']:
312
+ fo = None
313
+ print()
314
+ else:
315
+ util.debug(f"Opening {self.args['out']} for writing")
316
+ fo = open( # pylint: disable=consider-using-with
317
+ self.args['out'], 'w', encoding='utf-8'
318
+ )
319
+ print(f"## {self.args=}", file=fo)
320
+
321
+ if self.args['emit-non-sources']:
322
+ if self.files_non_source:
323
+ print('## reqs (non-source files that are dependencies):', file=fo)
324
+ prefix = strip_all_quotes(self.args['prefix-non-sources'])
325
+ for f in self.files_non_source:
326
+ if self.args['emit-rel-path']:
327
+ f = os.path.relpath(f)
328
+ print('## ' + prefix + pq1 + f + pq2, file=fo)
329
+
330
+ if self.args['emit-eda-args']:
331
+ for newline in self.get_flist_eda_args_list():
332
+ print(newline, file=fo)
333
+
334
+ defines_lines = self.get_flist_defines_list()
335
+ if not self.args['emit-define'] and defines_lines:
336
+ util.warning(f'Command "flist"{tool_string}, has defines present but they were not',
337
+ f'included in the output flist: {defines_lines}')
338
+
339
+ parameter_lines = self.get_flist_parameter_list()
340
+ if not self.args['emit-parameter'] and parameter_lines:
341
+ util.warning(f'Command "flist"{tool_string}, has parameters present but they were not',
342
+ f'included in the output flist: {parameter_lines}')
343
+
344
+ plusarg_lines = self.get_flist_plusargs_list()
345
+ if not self.args['emit-plusargs'] and plusarg_lines:
346
+ util.warning(f'Command "flist"{tool_string}, has plusargs present but they were not',
347
+ f'included in the output flist: {plusarg_lines}')
348
+
349
+ if self.args['emit-define']:
350
+ for newline in defines_lines:
351
+ print(newline, file=fo)
352
+
353
+ if self.args['emit-parameter']:
354
+ for newline in parameter_lines:
355
+ print(newline, file=fo)
356
+
357
+
358
+ if self.args['emit-incdir']:
359
+ prefix = strip_all_quotes(self.args['prefix-incdir'])
360
+ for i in self.incdirs:
361
+ if self.args['emit-rel-path']:
362
+ i = os.path.relpath(i)
363
+ print(prefix + pq1 + i + pq2, file=fo)
364
+
365
+ if self.args['emit-plusargs']:
366
+ for newline in plusarg_lines:
367
+ print(newline, file=fo)
368
+
369
+
370
+ # Hook for derived classes to optionally print additional custom args, prior to
371
+ # any files:
372
+ for newline in self.get_additional_flist_args_list():
373
+ print(newline, file=fo)
374
+
375
+ if self.args['emit-v']:
376
+ prefix = strip_all_quotes(self.args['prefix-v'])
377
+ for f in self.files_v:
378
+ if self.args['emit-rel-path']:
379
+ f = os.path.relpath(f)
380
+ print(prefix + pq1 + f + pq2, file=fo)
381
+
382
+ if self.args['emit-sv']:
383
+ prefix = strip_all_quotes(self.args['prefix-sv'])
384
+ for f in self.files_sv:
385
+ if self.args['emit-rel-path']:
386
+ f = os.path.relpath(f)
387
+ print(prefix + pq1 + f + pq2, file=fo)
388
+ if self.args['emit-vhd']:
389
+ prefix = strip_all_quotes(self.args['prefix-vhd'])
390
+ for f in self.files_vhd:
391
+ if self.args['emit-rel-path']:
392
+ f = os.path.relpath(f)
393
+ print(prefix + pq1 + f + pq2, file=fo)
394
+ if self.args['emit-cpp']:
395
+ prefix = strip_all_quotes(self.args['prefix-cpp'])
396
+ for f in self.files_cpp:
397
+ if self.args['emit-rel-path']:
398
+ f = os.path.relpath(f)
399
+ print(prefix + pq1 + f + pq2, file=fo)
400
+
401
+ # Hook for derived classes to optionally print additional flist items after
402
+ # any files:
403
+ for newline in self.get_additional_flist_files_list():
404
+ print(newline, file=fo)
405
+
406
+ if self.args['print-to-stdout']:
407
+ print() # don't need to close fo (None)
408
+ else:
409
+ fo.close()
410
+ util.info(f"Created file: {self.args['out']}")
411
+
412
+ self.write_eda_config_and_args()
413
+ self.run_post_tool_dep_commands()
@@ -281,7 +281,7 @@ class CommandMulti(CommandParallel):
281
281
  if parsed.parallel < 1 or parsed.parallel > 256:
282
282
  self.error("Arg 'parallel' must be between 1 and 256")
283
283
 
284
- command = self.get_command_from_unparsed_args(tokens=unparsed)
284
+ command = self.get_sub_command_from_config()
285
285
 
286
286
  # Need to know the tool for this command, either it was set correctly via --tool and/or
287
287
  # the command (class) will tell us.
@@ -354,9 +354,11 @@ class CommandMulti(CommandParallel):
354
354
 
355
355
  if self.args['print-targets']:
356
356
  util.info('Multi print-targets (will not run jobs): -->')
357
- for t in self.targets:
357
+ print_targets = [t[0] for t in self.targets]
358
+ print_targets.sort()
359
+ for x in print_targets:
358
360
  # t = tuple of (target:str, tool:str), we just want the target.
359
- print(f' {t[0]}')
361
+ print(f' {x}')
360
362
  else:
361
363
  util.debug("Multi: converting list of targets into list of jobs")
362
364
  self.jobs = []
@@ -12,15 +12,32 @@ Note that CommandSim is also a base class for opencos.commands.elab.CommandElab.
12
12
  # pylint: disable=too-many-arguments,too-many-positional-arguments
13
13
 
14
14
  import os
15
+ import shlex
15
16
 
16
17
  from opencos import util, export_helper
17
18
  from opencos.eda_base import CommandDesign, Tool
18
19
  from opencos.utils import status_constants
19
20
 
20
- from opencos.utils.str_helpers import sanitize_defines_for_sh, strip_outer_quotes
21
+ from opencos.utils.str_helpers import strip_outer_quotes
21
22
 
22
- def parameters_dict_get_command_list(params: dict, arg_prefix: str = '-G') -> list:
23
- '''Given dict of parameters, returns a command list'''
23
+ def parameters_dict_get_command_list(
24
+ params: dict, arg_prefix: str = '-G',
25
+ hier_delimiter: str = '.',
26
+ top_hier_str: str = '',
27
+ for_flist: bool = False
28
+ ) -> list:
29
+ '''Given dict of parameters, returns a command list.
30
+
31
+ Some simulators will set ALL parameters with Name=Value at all levels of hierarchy,
32
+ so to avoid that and only set top level parameters, but still allow package or
33
+ hierarchy parameters to be set, you can use:
34
+
35
+ hier_delimiter='/'
36
+ top_hier_str=f'/{TOP_MODULE_NAME}/'
37
+
38
+ Which will, if a parameter Name does not contain the heir delimiter, the Name
39
+ will be prepended with top_hier_str.
40
+ '''
24
41
 
25
42
  ret_list = []
26
43
  if ' ' in arg_prefix:
@@ -34,13 +51,29 @@ def parameters_dict_get_command_list(params: dict, arg_prefix: str = '-G') -> li
34
51
  if not isinstance(v, (int, str)):
35
52
  util.warning(f'parameter {k} has value: {v}, parameters must be int/string types')
36
53
 
54
+ if top_hier_str and hier_delimiter not in k:
55
+ k = f'{top_hier_str}{k}'
56
+
37
57
  ret_list.extend(arg_list_prefix)
38
58
  if isinstance(v, int):
39
59
  ret_list.append(f'{arg_str_prefix}{k}={v}')
40
60
  else: # string
61
+ # Note if for_flist=True, then we have to take extra care on what gets
62
+ # double-quotes " and \".
41
63
  v = strip_outer_quotes(v.strip('\n'))
42
64
  v = '"' + v + '"'
43
- ret_list.append(f'{arg_str_prefix}{k}={sanitize_defines_for_sh(v)}')
65
+
66
+ if for_flist:
67
+ # do not wrap with " unless there are spaces, if there are spaces
68
+ # wrap the entire thing with single quotes (bash)
69
+ if ' ' in v:
70
+ ret_list.append(shlex.quote(f'{arg_str_prefix}{k}={v}'))
71
+ else:
72
+ ret_list.append(f'{arg_str_prefix}{k}={v}')
73
+ else:
74
+ # This is for a tool, so simply guard single quotes:
75
+ v = v.replace("'", "\\" + "'")
76
+ ret_list.append(f'{arg_str_prefix}{k}={v}')
44
77
  return ret_list
45
78
 
46
79
 
@@ -79,6 +112,8 @@ class CommandSim(CommandDesign): # pylint: disable=too-many-public-methods
79
112
  # not used by other simulators, so these can go in DEPS files for custom things
80
113
  # like -CFLAGS -O0, etc.
81
114
  'verilate-args': [],
115
+ 'ext-defines-sv-fname': '',
116
+ 'license-queue': False,
82
117
  })
83
118
  self.args_help.update({
84
119
  'pre-sim-tcl': (
@@ -124,6 +159,15 @@ class CommandSim(CommandDesign): # pylint: disable=too-many-public-methods
124
159
  'uvm-version': (
125
160
  'Used if --uvm is set, for example --uvm-version=1.2'
126
161
  ),
162
+ 'ext-defines-sv-fname': (
163
+ 'If set to empty str, --ext-defines-sv-fname="", applies defines to tool via args.'
164
+ ' If set to non-emtpy str as a .sv file name, such as'
165
+ ' --ext-defines-sv-fname=_ext_defines.sv, saves defines to a file name in the'
166
+ ' work-dif, and adds this file to the SV/Verilog file list ahead of other files.'
167
+ ),
168
+ 'license-queue': (
169
+ 'Set to enable env vars LICENSE_QUEUE=1 which has effects on certain simulators'
170
+ ),
127
171
 
128
172
  })
129
173
 
@@ -133,9 +177,24 @@ class CommandSim(CommandDesign): # pylint: disable=too-many-public-methods
133
177
 
134
178
 
135
179
 
136
- def process_parameters_get_list(self, arg_prefix: str = '-G') -> list:
137
- '''Returns list (suitable command list for shell or for tool) from self.parameters'''
138
- return parameters_dict_get_command_list(params=self.parameters, arg_prefix=arg_prefix)
180
+ def process_parameters_get_list(
181
+ self, arg_prefix: str = '-G', **kwargs
182
+ ) -> list:
183
+ '''Returns list (suitable command list for shell or for tool) from self.parameters
184
+
185
+ Some simulators will set ALL parameters with Name=Value at all levels of hierarchy,
186
+ so to avoid that and only set top level parameters, but still allow package or
187
+ hierarchy parameters to be set, you can use args (in kwargs):
188
+
189
+ hier_delimiter='/'
190
+ top_hier_str=f'/{TOP_MODULE_NAME}/'
191
+
192
+ Which will, if a parameter Name does not contain the heir delimiter, the Name
193
+ will be prepended with top_hier_str.
194
+ '''
195
+ return parameters_dict_get_command_list(
196
+ params=self.parameters, arg_prefix=arg_prefix, **kwargs
197
+ )
139
198
 
140
199
  def process_plusarg(self, plusarg: str, pwd: str = os.getcwd()) -> None:
141
200
  '''Override for CommandDesign.process_plusarg(..)'''
@@ -159,6 +218,8 @@ class CommandSim(CommandDesign): # pylint: disable=too-many-public-methods
159
218
  if self.stop_process_tokens_before_do_it():
160
219
  return unparsed
161
220
 
221
+ self.handle_arg_license_queue()
222
+
162
223
  # add defines for this job type
163
224
  if self.args['lint'] or self.args['stop-after-elaborate']:
164
225
  self.args['lint'] = True
@@ -572,3 +633,56 @@ class CommandSim(CommandDesign): # pylint: disable=too-many-public-methods
572
633
  search_paths=self.args['work-dir'], file_extension='vcd',
573
634
  typ='waveform', description='Simulation Waveform VCD (Value Change Dump) file'
574
635
  )
636
+
637
+
638
+ def create_ext_defines_sv(self) -> None:
639
+ '''Creates _ext_defines.sv in the work-dir, adds to front of self.files_sv'''
640
+
641
+ if not self.defines:
642
+ return
643
+
644
+ if not self.args['ext-defines-sv-fname']:
645
+ return
646
+
647
+ fpath = os.path.join(self.args['work-dir'], self.args['ext-defines-sv-fname'])
648
+ fpath_abs = os.path.abspath(fpath)
649
+ if fpath_abs in self.files:
650
+ util.warning(f'{fpath} already exists, will overwrite. Consider changing the value',
651
+ f'of arg: --ext-defines-sv-fname={self.args["ext-defines-sv-fname"]}')
652
+ if not fpath.endswith('.sv') and not fpath.endswith('.v'):
653
+ util.error(f'--ext-defines-sv-fname={self.args["ext-defines-sv-fname"]} must end with',
654
+ '.v or .sv')
655
+ return
656
+
657
+ ifdef_guard = os.path.split(self.args['ext-defines-sv-fname'])[1].upper()
658
+ ifdef_guard = ifdef_guard.replace('.', '__').replace('/', '__')
659
+ with open(fpath, 'w', encoding='utf-8') as f:
660
+ f.write(f'`ifndef {ifdef_guard}\n')
661
+ f.write(f'`define {ifdef_guard}\n')
662
+ f.write('\n')
663
+ for key, value in self.defines.items():
664
+ if value is None:
665
+ f.write(f'`define {key}\n')
666
+ else:
667
+ # if the value v is a double-quoted string, such as v='"hi"' that needs
668
+ # to be preserved.
669
+ f.write(f'`define {key} {value}\n')
670
+
671
+ f.write('\n')
672
+ f.write(f'`endif // {ifdef_guard}\n')
673
+
674
+ self.files_sv.insert(0, fpath_abs)
675
+ self.files[fpath_abs] = True
676
+ self.files_caller_info[fpath_abs] = (
677
+ 'opencos.commands.sim::CommandSim::create_ext_defines_sv'
678
+ )
679
+ util.info(f'command "sim": {fpath} created for defines: {list(self.defines.keys())}')
680
+
681
+
682
+ def handle_arg_license_queue(self) -> None:
683
+ '''Handles self.args['license-queue'] (bool) to set env vars'''
684
+ if not self.args['license-queue']:
685
+ return
686
+
687
+ if 'LICENSE_QUEUE' not in os.environ:
688
+ os.environ['LICENSE_QUEUE'] = '1'
@@ -72,7 +72,7 @@ class CommandSweep(CommandDesign, CommandParallel):
72
72
 
73
73
  self.check_args()
74
74
 
75
- self.single_command = self.get_command_from_unparsed_args(tokens=unparsed)
75
+ self.single_command = self.get_sub_command_from_config()
76
76
 
77
77
  self._append_sweep_args(arg_tokens=arg_tokens)
78
78
 
@@ -204,9 +204,9 @@ class CommandUpload(Command):
204
204
  tool = tools_found.pop() # only item in set
205
205
  # Do we have a handler for this in our config?
206
206
  if tool in self.config.get('tools_loaded', []):
207
- auto_tools_order = self.config.get('auto_tools_order', [{}])[0]
208
- if tool in auto_tools_order:
209
- cls_str = auto_tools_order[tool].get('handlers', {}).get(self.command_name, None)
207
+ tool_cfg = self.config.get('tools', {}).get(tool, {})
208
+ if tool_cfg:
209
+ cls_str = tool_cfg.get('handlers', {}).get(self.command_name, None)
210
210
  if cls_str:
211
211
  cls = import_class_from_string(cls_str)
212
212
  if issubclass(cls, Command):
@@ -217,5 +217,5 @@ class CommandUpload(Command):
217
217
 
218
218
  warning(f'No handler found for tool={tool} to process bitfiles: {self.bitfiles}')
219
219
  debug(f'config -- tools_loaded: {self.config["tools_loaded"]}')
220
- debug(f'config -- auto_tools_order for tool: {self.config["auto_tools_order"][0][tool]}')
220
+ debug(f'config -- tools for tool: {self.config["tools"].get(tool, "")}')
221
221
  return self