opencos-eda 0.3.11__py3-none-any.whl → 0.3.13__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- opencos/commands/flist.py +222 -39
- opencos/commands/multi.py +5 -3
- opencos/commands/sim.py +121 -7
- opencos/commands/sweep.py +1 -1
- opencos/commands/upload.py +4 -4
- opencos/commands/waves.py +44 -0
- opencos/deps/deps_file.py +4 -1
- opencos/deps_schema.py +2 -2
- opencos/eda.py +284 -175
- opencos/eda_base.py +54 -22
- opencos/eda_config.py +51 -24
- opencos/eda_config_defaults.yml +326 -195
- opencos/eda_config_reduced.yml +19 -39
- opencos/eda_tool_helper.py +193 -24
- opencos/tools/cocotb.py +6 -29
- opencos/tools/invio.py +0 -6
- opencos/tools/iverilog.py +16 -16
- opencos/tools/modelsim_ase.py +1 -13
- opencos/tools/quartus.py +81 -24
- opencos/tools/questa.py +0 -14
- opencos/tools/questa_common.py +95 -30
- opencos/tools/questa_fe.py +0 -14
- opencos/tools/questa_fse.py +0 -14
- opencos/tools/riviera.py +56 -25
- opencos/tools/slang.py +15 -12
- opencos/tools/slang_yosys.py +0 -6
- opencos/tools/surelog.py +14 -11
- opencos/tools/tabbycad_yosys.py +1 -7
- opencos/tools/verilator.py +18 -14
- opencos/tools/vivado.py +57 -22
- opencos/tools/yosys.py +2 -4
- opencos/util.py +14 -5
- opencos/utils/str_helpers.py +42 -1
- {opencos_eda-0.3.11.dist-info → opencos_eda-0.3.13.dist-info}/METADATA +1 -2
- {opencos_eda-0.3.11.dist-info → opencos_eda-0.3.13.dist-info}/RECORD +40 -41
- opencos/eda_config_max_verilator_waivers.yml +0 -39
- {opencos_eda-0.3.11.dist-info → opencos_eda-0.3.13.dist-info}/WHEEL +0 -0
- {opencos_eda-0.3.11.dist-info → opencos_eda-0.3.13.dist-info}/entry_points.txt +0 -0
- {opencos_eda-0.3.11.dist-info → opencos_eda-0.3.13.dist-info}/licenses/LICENSE +0 -0
- {opencos_eda-0.3.11.dist-info → opencos_eda-0.3.13.dist-info}/licenses/LICENSE.spdx +0 -0
- {opencos_eda-0.3.11.dist-info → opencos_eda-0.3.13.dist-info}/top_level.txt +0 -0
opencos/commands/flist.py
CHANGED
|
@@ -6,11 +6,13 @@ Intended to be overriden by Tool based classes (such as CommandFListVivado, etc)
|
|
|
6
6
|
# pylint: disable=too-many-statements
|
|
7
7
|
|
|
8
8
|
import os
|
|
9
|
+
import shlex
|
|
9
10
|
|
|
10
|
-
from opencos import util
|
|
11
|
-
from opencos.eda_base import CommandDesign
|
|
11
|
+
from opencos import util, eda_config
|
|
12
|
+
from opencos.eda_base import CommandDesign, Tool
|
|
12
13
|
from opencos.utils.str_helpers import strip_all_quotes
|
|
13
14
|
from opencos.commands.sim import parameters_dict_get_command_list
|
|
15
|
+
from opencos.utils.str_helpers import strip_outer_quotes
|
|
14
16
|
|
|
15
17
|
class CommandFList(CommandDesign):
|
|
16
18
|
'''Base class command handler for: eda flist ...'''
|
|
@@ -19,29 +21,41 @@ class CommandFList(CommandDesign):
|
|
|
19
21
|
|
|
20
22
|
def __init__(self, config: dict):
|
|
21
23
|
CommandDesign.__init__(self, config=config)
|
|
22
|
-
|
|
23
|
-
|
|
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 = {
|
|
24
30
|
'out' : "flist.out",
|
|
25
31
|
'emit-define' : True,
|
|
26
32
|
'emit-parameter' : True,
|
|
27
33
|
'emit-incdir' : True,
|
|
34
|
+
'emit-plusargs' : True,
|
|
28
35
|
'emit-v' : True,
|
|
29
36
|
'emit-sv' : True,
|
|
30
37
|
'emit-vhd' : True,
|
|
31
38
|
'emit-cpp' : True,
|
|
32
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.
|
|
33
41
|
'prefix-define' : "+define+",
|
|
34
42
|
'prefix-parameter' : "-G",
|
|
35
43
|
'prefix-incdir' : "+incdir+",
|
|
44
|
+
'prefix-plusargs' : "+",
|
|
36
45
|
'prefix-v' : "",
|
|
37
46
|
'prefix-sv' : "",
|
|
38
47
|
'prefix-vhd' : "",
|
|
39
48
|
'prefix-cpp' : "",
|
|
40
49
|
'prefix-non-sources' : "", # as comments anyway.
|
|
41
|
-
#
|
|
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
|
|
42
56
|
'bracket-quote-define': False,
|
|
43
57
|
'single-quote-define': False,
|
|
44
|
-
'quote-define' :
|
|
58
|
+
'quote-define' : self.flist_has_tool, # Tool, this is NOT for eda.
|
|
45
59
|
'equal-define' : True,
|
|
46
60
|
'escape-define-value': False,
|
|
47
61
|
'quote-define-value' : False,
|
|
@@ -55,7 +69,14 @@ class CommandFList(CommandDesign):
|
|
|
55
69
|
|
|
56
70
|
# ex: eda flist --print-to-stdout --emit-rel-path --quiet <target>
|
|
57
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.
|
|
58
77
|
})
|
|
78
|
+
self.args.update(self.flist_args)
|
|
79
|
+
|
|
59
80
|
self.args_help.update({
|
|
60
81
|
'print-to-stdout': "do not save file, print to stdout",
|
|
61
82
|
})
|
|
@@ -75,14 +96,15 @@ class CommandFList(CommandDesign):
|
|
|
75
96
|
def get_flist_dict(self) -> dict:
|
|
76
97
|
'''Returns dict of some internal class member vars, ignores args
|
|
77
98
|
|
|
78
|
-
Useful for an
|
|
99
|
+
Useful for an external caller to get details about this CommandDesign child
|
|
79
100
|
object without generating a .f file, or having to know specifics about the
|
|
80
101
|
class
|
|
81
102
|
'''
|
|
82
103
|
self.command_safe_set_tool_defines() # (Command.command_safe_set_tool_defines)
|
|
83
104
|
|
|
84
105
|
ret = {}
|
|
85
|
-
for key in ['files_sv', 'files_v', 'files_vhd', 'defines', 'incdirs'
|
|
106
|
+
for key in ['files_sv', 'files_v', 'files_vhd', 'defines', 'incdirs',
|
|
107
|
+
'parameters', 'unprocessed-plusargs']:
|
|
86
108
|
# These keys must exist, all are lists, defines is a dict
|
|
87
109
|
x = getattr(self, key, None)
|
|
88
110
|
if isinstance(x, (dict, list)):
|
|
@@ -91,6 +113,134 @@ class CommandFList(CommandDesign):
|
|
|
91
113
|
ret[key] = x
|
|
92
114
|
return ret
|
|
93
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
|
+
|
|
94
244
|
def do_it(self) -> None:
|
|
95
245
|
'''do_it() is the main entry point for creating the flist(),
|
|
96
246
|
|
|
@@ -105,6 +255,30 @@ class CommandFList(CommandDesign):
|
|
|
105
255
|
self.write_eda_config_and_args()
|
|
106
256
|
return
|
|
107
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
|
+
|
|
108
282
|
# check if we're overwriting the output flist file.
|
|
109
283
|
if self.args['print-to-stdout']:
|
|
110
284
|
pass
|
|
@@ -153,41 +327,33 @@ class CommandFList(CommandDesign):
|
|
|
153
327
|
f = os.path.relpath(f)
|
|
154
328
|
print('## ' + prefix + pq1 + f + pq2, file=fo)
|
|
155
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
|
+
|
|
156
349
|
if self.args['emit-define']:
|
|
157
|
-
|
|
158
|
-
for d, value in self.defines.items():
|
|
159
|
-
if value is None:
|
|
160
|
-
newline = prefix + d
|
|
161
|
-
else:
|
|
162
|
-
if self.args['bracket-quote-define']:
|
|
163
|
-
qd1 = "{"
|
|
164
|
-
qd2 = "}"
|
|
165
|
-
elif self.args['single-quote-define']:
|
|
166
|
-
qd1 = "'"
|
|
167
|
-
qd2 = "'"
|
|
168
|
-
elif self.args['quote-define']:
|
|
169
|
-
qd1 = '"'
|
|
170
|
-
qd2 = '"' # rename this one when things calm down
|
|
171
|
-
else:
|
|
172
|
-
qd1 = ''
|
|
173
|
-
qd2 = ''
|
|
174
|
-
if self.args['equal-define']:
|
|
175
|
-
ed1 = '='
|
|
176
|
-
else:
|
|
177
|
-
ed1 = ' '
|
|
178
|
-
if self.args['escape-define-value']:
|
|
179
|
-
value = value.replace('\\', '\\\\').replace('"', '\\"')
|
|
180
|
-
if self.args['quote-define-value']:
|
|
181
|
-
value =f'"{value}"'
|
|
182
|
-
newline = prefix + qd1 + f"{d}{ed1}{value}" + qd2
|
|
350
|
+
for newline in defines_lines:
|
|
183
351
|
print(newline, file=fo)
|
|
184
352
|
|
|
185
353
|
if self.args['emit-parameter']:
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
):
|
|
190
|
-
print(item, file=fo)
|
|
354
|
+
for newline in parameter_lines:
|
|
355
|
+
print(newline, file=fo)
|
|
356
|
+
|
|
191
357
|
|
|
192
358
|
if self.args['emit-incdir']:
|
|
193
359
|
prefix = strip_all_quotes(self.args['prefix-incdir'])
|
|
@@ -195,12 +361,24 @@ class CommandFList(CommandDesign):
|
|
|
195
361
|
if self.args['emit-rel-path']:
|
|
196
362
|
i = os.path.relpath(i)
|
|
197
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
|
+
|
|
198
375
|
if self.args['emit-v']:
|
|
199
376
|
prefix = strip_all_quotes(self.args['prefix-v'])
|
|
200
377
|
for f in self.files_v:
|
|
201
378
|
if self.args['emit-rel-path']:
|
|
202
379
|
f = os.path.relpath(f)
|
|
203
380
|
print(prefix + pq1 + f + pq2, file=fo)
|
|
381
|
+
|
|
204
382
|
if self.args['emit-sv']:
|
|
205
383
|
prefix = strip_all_quotes(self.args['prefix-sv'])
|
|
206
384
|
for f in self.files_sv:
|
|
@@ -220,6 +398,11 @@ class CommandFList(CommandDesign):
|
|
|
220
398
|
f = os.path.relpath(f)
|
|
221
399
|
print(prefix + pq1 + f + pq2, file=fo)
|
|
222
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
|
+
|
|
223
406
|
if self.args['print-to-stdout']:
|
|
224
407
|
print() # don't need to close fo (None)
|
|
225
408
|
else:
|
opencos/commands/multi.py
CHANGED
|
@@ -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.
|
|
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' {
|
|
361
|
+
print(f' {x}')
|
|
360
362
|
else:
|
|
361
363
|
util.debug("Multi: converting list of targets into list of jobs")
|
|
362
364
|
self.jobs = []
|
opencos/commands/sim.py
CHANGED
|
@@ -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
|
|
21
|
+
from opencos.utils.str_helpers import strip_outer_quotes
|
|
21
22
|
|
|
22
|
-
def parameters_dict_get_command_list(
|
|
23
|
-
|
|
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
|
-
|
|
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(
|
|
137
|
-
|
|
138
|
-
|
|
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'
|
opencos/commands/sweep.py
CHANGED
|
@@ -72,7 +72,7 @@ class CommandSweep(CommandDesign, CommandParallel):
|
|
|
72
72
|
|
|
73
73
|
self.check_args()
|
|
74
74
|
|
|
75
|
-
self.single_command = self.
|
|
75
|
+
self.single_command = self.get_sub_command_from_config()
|
|
76
76
|
|
|
77
77
|
self._append_sweep_args(arg_tokens=arg_tokens)
|
|
78
78
|
|
opencos/commands/upload.py
CHANGED
|
@@ -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
|
-
|
|
208
|
-
if
|
|
209
|
-
cls_str =
|
|
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 --
|
|
220
|
+
debug(f'config -- tools for tool: {self.config["tools"].get(tool, "")}')
|
|
221
221
|
return self
|
opencos/commands/waves.py
CHANGED
|
@@ -15,10 +15,12 @@ handler).
|
|
|
15
15
|
# pylint: disable=R0801
|
|
16
16
|
|
|
17
17
|
import os
|
|
18
|
+
import subprocess
|
|
18
19
|
|
|
19
20
|
from opencos import util
|
|
20
21
|
from opencos.eda_base import CommandDesign
|
|
21
22
|
from opencos.files import safe_shutil_which
|
|
23
|
+
from opencos.utils import vscode_helper
|
|
22
24
|
|
|
23
25
|
|
|
24
26
|
class CommandWaves(CommandDesign):
|
|
@@ -51,6 +53,48 @@ class CommandWaves(CommandDesign):
|
|
|
51
53
|
+ ' to stdout',
|
|
52
54
|
})
|
|
53
55
|
|
|
56
|
+
def get_versions_of_tool(self, tool: str) -> str:
|
|
57
|
+
'''Similar to Tool.get_versions(), returns the version of 'tool' for tools like:
|
|
58
|
+
|
|
59
|
+
- vaporview
|
|
60
|
+
- gtkwave
|
|
61
|
+
|
|
62
|
+
This is called by eda_tool_helper.get_handler_tool_version(tool, cmd, config)
|
|
63
|
+
'''
|
|
64
|
+
|
|
65
|
+
entry = self.config.get('tools', {}).get(tool, {})
|
|
66
|
+
|
|
67
|
+
if entry and 'requires_vscode_extension' in entry:
|
|
68
|
+
# vaporview, surfer
|
|
69
|
+
vscode_ext_name = entry.get('requires_vscode_extension', [''])[0]
|
|
70
|
+
vscode_helper.init()
|
|
71
|
+
ver = vscode_helper.EXTENSIONS.get(vscode_ext_name)
|
|
72
|
+
return ver
|
|
73
|
+
|
|
74
|
+
if entry and tool == 'gtkwave':
|
|
75
|
+
# gtkwave --version is fast.
|
|
76
|
+
proc = None
|
|
77
|
+
try:
|
|
78
|
+
proc = subprocess.run(
|
|
79
|
+
[safe_shutil_which('gtkwave'), '--version'],
|
|
80
|
+
capture_output=True, check=False
|
|
81
|
+
)
|
|
82
|
+
except Exception as e:
|
|
83
|
+
util.debug(f'gtkwave --version: exception {e}')
|
|
84
|
+
|
|
85
|
+
if not proc or not proc.stdout:
|
|
86
|
+
return ''
|
|
87
|
+
|
|
88
|
+
for line in proc.stdout.decode('utf-8', errors='replace').split('\n'):
|
|
89
|
+
if line.lower().startswith('gtkwave analyzer v'):
|
|
90
|
+
parts = line.split(' ')
|
|
91
|
+
return parts[2][1:] # trim the leading 'v' in 'v1.2.3'
|
|
92
|
+
return ''
|
|
93
|
+
|
|
94
|
+
return ''
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
|
|
54
98
|
|
|
55
99
|
def get_wave_files_in_dirs(self, wave_dirs: list, quiet: bool = False) -> list:
|
|
56
100
|
'''Returns list of all wave files give wave_dirs (list)'''
|
opencos/deps/deps_file.py
CHANGED
|
@@ -124,6 +124,7 @@ def get_all_targets( # pylint: disable=dangerous-default-value,too-many-locals,t
|
|
|
124
124
|
targets = list(targets)
|
|
125
125
|
if not targets and error_on_empty_return:
|
|
126
126
|
error(f'get_all_targets: {base_path=} {filter_using_multi=} returned no targets')
|
|
127
|
+
targets.sort()
|
|
127
128
|
return targets
|
|
128
129
|
|
|
129
130
|
targets = set()
|
|
@@ -144,7 +145,9 @@ def get_all_targets( # pylint: disable=dangerous-default-value,too-many-locals,t
|
|
|
144
145
|
|
|
145
146
|
if not targets and error_on_empty_return:
|
|
146
147
|
error(f'get_all_targets: {base_path=} {dirs=} {filter_str=} returned no targets')
|
|
147
|
-
|
|
148
|
+
targets = list(targets)
|
|
149
|
+
targets.sort()
|
|
150
|
+
return targets
|
|
148
151
|
|
|
149
152
|
|
|
150
153
|
def deps_target_get_deps_list(
|