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/eda_base.py
CHANGED
|
@@ -26,7 +26,8 @@ from opencos import eda_config
|
|
|
26
26
|
|
|
27
27
|
from opencos.util import Colors, safe_emoji
|
|
28
28
|
from opencos.utils.str_helpers import sprint_time, strip_outer_quotes, string_or_space, \
|
|
29
|
-
indent_wrap_long_text, pretty_list_columns_manual, get_terminal_columns
|
|
29
|
+
indent_wrap_long_text, pretty_list_columns_manual, get_terminal_columns, \
|
|
30
|
+
PARAMETER_NAME_RSTR
|
|
30
31
|
from opencos.utils.subprocess_helpers import subprocess_run_background
|
|
31
32
|
from opencos.utils import status_constants
|
|
32
33
|
|
|
@@ -192,6 +193,8 @@ class Tool:
|
|
|
192
193
|
self.args_help = {}
|
|
193
194
|
if getattr(self, 'defines', None) is None:
|
|
194
195
|
self.defines = {}
|
|
196
|
+
if getattr(self, 'config', None) is None:
|
|
197
|
+
self.config = config
|
|
195
198
|
self.args.update({
|
|
196
199
|
'tool': self._TOOL, # Set for all derived classes.
|
|
197
200
|
})
|
|
@@ -211,7 +214,7 @@ class Tool:
|
|
|
211
214
|
|
|
212
215
|
def set_exe(self, config: dict) -> None:
|
|
213
216
|
'''Sets self._EXE based on config'''
|
|
214
|
-
if self._TOOL and self._TOOL in config.get('
|
|
217
|
+
if self._TOOL and self._TOOL in config.get('auto_tools_found', {}):
|
|
215
218
|
# config['auto_tools_found'] has the first exe full path:
|
|
216
219
|
exe = config.get('auto_tools_found', {}).get(self._TOOL)
|
|
217
220
|
if exe and exe != self._EXE:
|
|
@@ -231,7 +234,11 @@ class Tool:
|
|
|
231
234
|
return str(self._TOOL) + ':' + str(self._VERSION)
|
|
232
235
|
|
|
233
236
|
def get_versions(self) -> str:
|
|
234
|
-
'''Sets and returns self._VERSION
|
|
237
|
+
'''Sets and returns self._VERSION. Note that this is overriden by nearly all
|
|
238
|
+
|
|
239
|
+
child Tool classes, and should limit info/warning messaging since it is part
|
|
240
|
+
of opencos.eda.init_config(..)
|
|
241
|
+
'''
|
|
235
242
|
return self._VERSION
|
|
236
243
|
|
|
237
244
|
def report_tool_warn_error_counts(self) -> None:
|
|
@@ -247,7 +254,7 @@ class Tool:
|
|
|
247
254
|
info_color = Colors.yellow
|
|
248
255
|
if self.tool_error_count:
|
|
249
256
|
info_color = Colors.yellow
|
|
250
|
-
error_color = Colors.
|
|
257
|
+
error_color = Colors.bred
|
|
251
258
|
start = safe_emoji('🔶 ')
|
|
252
259
|
util.info(
|
|
253
260
|
f"{start}Tool - {Colors.bold}{tool_name}{Colors.normal}{info_color}, total counts:",
|
|
@@ -257,8 +264,20 @@ class Tool:
|
|
|
257
264
|
)
|
|
258
265
|
|
|
259
266
|
def set_tool_defines(self) -> None:
|
|
260
|
-
'''Derived classes may override, sets any additional defines based on tool.
|
|
261
|
-
|
|
267
|
+
'''Derived classes may override, sets any additional defines based on tool.
|
|
268
|
+
|
|
269
|
+
If overriding, you likely want to call:
|
|
270
|
+
super().set_tool_defines()
|
|
271
|
+
to pick up anything from self.config['tools'] (from the eda_config YAML)
|
|
272
|
+
'''
|
|
273
|
+
if not self._TOOL:
|
|
274
|
+
return
|
|
275
|
+
|
|
276
|
+
_tool_config = self.config.get('tools', {}).get(self._TOOL, {})
|
|
277
|
+
self.defines.update(
|
|
278
|
+
_tool_config.get('defines', {})
|
|
279
|
+
)
|
|
280
|
+
|
|
262
281
|
|
|
263
282
|
|
|
264
283
|
class Command: # pylint: disable=too-many-public-methods,too-many-instance-attributes
|
|
@@ -878,23 +897,20 @@ class Command: # pylint: disable=too-many-public-methods,too-many-instance-attri
|
|
|
878
897
|
|
|
879
898
|
return unparsed
|
|
880
899
|
|
|
881
|
-
def
|
|
882
|
-
self,
|
|
900
|
+
def get_sub_command_from_config(
|
|
901
|
+
self, error_if_no_command: bool = True,
|
|
883
902
|
) -> str:
|
|
884
|
-
'''
|
|
903
|
+
'''If this is a command with a sub-command (multi, sweep), then opencos.eda would
|
|
885
904
|
|
|
886
|
-
|
|
905
|
+
have already parsed in an place it in config['sub_command']. Get this value and return
|
|
906
|
+
it.
|
|
887
907
|
'''
|
|
888
|
-
ret = ''
|
|
889
|
-
for value in tokens:
|
|
890
|
-
if value in self.config['command_handler'].keys():
|
|
891
|
-
ret = value
|
|
892
|
-
tokens.remove(value)
|
|
893
|
-
break
|
|
908
|
+
ret = self.config.get('sub_command', '')
|
|
894
909
|
|
|
895
910
|
if not ret and error_if_no_command:
|
|
911
|
+
util.debug(f'get_sub_command_from_config: see: {self.config=}')
|
|
896
912
|
self.error(f"Looking for a valid eda {self.command_name} COMMAND",
|
|
897
|
-
|
|
913
|
+
"but didn't find one in config",
|
|
898
914
|
error_code=status_constants.EDA_COMMAND_OR_ARGS_ERROR)
|
|
899
915
|
return ret
|
|
900
916
|
|
|
@@ -1500,7 +1516,14 @@ class CommandDesign(Command): # pylint: disable=too-many-instance-attributes
|
|
|
1500
1516
|
def process_parameter_arg(
|
|
1501
1517
|
self, text: str, pwd: str = os.getcwd()
|
|
1502
1518
|
) -> None:
|
|
1503
|
-
'''Retuns None, parses -G<Name>=<Value> adds to internal self.parameters.
|
|
1519
|
+
'''Retuns None, parses -G<Name>=<Value> adds to internal self.parameters.
|
|
1520
|
+
|
|
1521
|
+
Note that not all tools will allow hierarchy path information when setting
|
|
1522
|
+
parameters. The Queta/Modelsim family can handle -G/Path/To/Name=Value,
|
|
1523
|
+
but other tools may crash.
|
|
1524
|
+
|
|
1525
|
+
Also note - we do not currently accept [0] in the /Path/To/generate[1]/Name=Value
|
|
1526
|
+
'''
|
|
1504
1527
|
|
|
1505
1528
|
# Deal with raw CLI/bash/powershell argparser, strip all outer quotes.
|
|
1506
1529
|
text = strip_outer_quotes(text)
|
|
@@ -1513,14 +1536,14 @@ class CommandDesign(Command): # pylint: disable=too-many-instance-attributes
|
|
|
1513
1536
|
return
|
|
1514
1537
|
|
|
1515
1538
|
text = text[2:] # strip leading -G
|
|
1516
|
-
m = re.match(
|
|
1539
|
+
m = re.match(rf'^({PARAMETER_NAME_RSTR})$', text)
|
|
1517
1540
|
if m:
|
|
1518
1541
|
k = m.group(1)
|
|
1519
1542
|
util.warning(f"Parameter {k} has no value and will not be applied")
|
|
1520
1543
|
return
|
|
1521
|
-
m = re.match(
|
|
1544
|
+
m = re.match(rf'^({PARAMETER_NAME_RSTR})\=(\S+)$', text)
|
|
1522
1545
|
if not m:
|
|
1523
|
-
m = re.match(
|
|
1546
|
+
m = re.match(rf'^({PARAMETER_NAME_RSTR})\=(\"[^\"]*\")$', text)
|
|
1524
1547
|
if m:
|
|
1525
1548
|
k = m.group(1)
|
|
1526
1549
|
v = m.group(2)
|
|
@@ -1535,7 +1558,11 @@ class CommandDesign(Command): # pylint: disable=too-many-instance-attributes
|
|
|
1535
1558
|
v = int(v)
|
|
1536
1559
|
except ValueError:
|
|
1537
1560
|
pass
|
|
1561
|
+
util.debug('set parameter Name={k} Value={v}')
|
|
1538
1562
|
self.set_parameter(k, v)
|
|
1563
|
+
else:
|
|
1564
|
+
util.warning(f'Unable to set parameter from token (-G removed): {text}')
|
|
1565
|
+
|
|
1539
1566
|
|
|
1540
1567
|
|
|
1541
1568
|
def process_plusarg( # pylint: disable=too-many-branches
|
|
@@ -1923,19 +1950,24 @@ class CommandDesign(Command): # pylint: disable=too-many-instance-attributes
|
|
|
1923
1950
|
m = re.match(r"^\'?\+\w+", token)
|
|
1924
1951
|
if m:
|
|
1925
1952
|
# Copy and strip all outer ' or " on the plusarg:
|
|
1953
|
+
util.debug(f'plusarg found at {token=}')
|
|
1926
1954
|
plusarg = strip_outer_quotes(token)
|
|
1927
1955
|
self.process_plusarg(plusarg, pwd=pwd)
|
|
1928
1956
|
remove_list.append(token)
|
|
1929
1957
|
continue
|
|
1930
1958
|
|
|
1931
1959
|
# Parameters in -G<word>=<something>
|
|
1932
|
-
|
|
1960
|
+
# Parameters in -Gpath.to.<word>=<something>
|
|
1961
|
+
# Parameters in -G/path/to/<word>=<something>
|
|
1962
|
+
m = re.match(rf"^\'?\-G{PARAMETER_NAME_RSTR}\=.+", token)
|
|
1933
1963
|
if m:
|
|
1934
1964
|
# Copy and strip all outer ' or " on the text:
|
|
1965
|
+
util.debug(f'found parameter at {token=}')
|
|
1935
1966
|
param = strip_outer_quotes(token)
|
|
1936
1967
|
self.process_parameter_arg(param, pwd=pwd)
|
|
1937
1968
|
remove_list.append(token)
|
|
1938
1969
|
|
|
1970
|
+
|
|
1939
1971
|
for x in remove_list:
|
|
1940
1972
|
unparsed.remove(x)
|
|
1941
1973
|
|
opencos/eda_config.py
CHANGED
|
@@ -22,6 +22,10 @@ class Defaults:
|
|
|
22
22
|
'''Defaults is a global namespace for constants and supported features.
|
|
23
23
|
|
|
24
24
|
Defaults.config_yml is set depending on search order for default eda_config[_defaults].yml
|
|
25
|
+
|
|
26
|
+
Note that opencos.eda and other packages are free to set and add additonal keys to the
|
|
27
|
+
"config", but the "supported_config_keys" are only what's allowed in the initial
|
|
28
|
+
--config-yml=YAML_FILE
|
|
25
29
|
'''
|
|
26
30
|
|
|
27
31
|
environ_override_config_yml = ''
|
|
@@ -29,6 +33,7 @@ class Defaults:
|
|
|
29
33
|
opencos_config_yml = 'eda_config_defaults.yml'
|
|
30
34
|
config_yml = ''
|
|
31
35
|
config_yml_set_from = ''
|
|
36
|
+
non_default_config_yml_arg_used = ''
|
|
32
37
|
|
|
33
38
|
supported_config_keys = set([
|
|
34
39
|
'DEFAULT_HANDLERS', 'DEFAULT_HANDLERS_HELP',
|
|
@@ -39,6 +44,7 @@ class Defaults:
|
|
|
39
44
|
'deps_subprocess_shell',
|
|
40
45
|
'bare_plusarg_supported',
|
|
41
46
|
'deps_expandvars_enable',
|
|
47
|
+
'show_tool_versions',
|
|
42
48
|
'dep_sub',
|
|
43
49
|
'vars',
|
|
44
50
|
'file_extensions',
|
|
@@ -48,13 +54,12 @@ class Defaults:
|
|
|
48
54
|
'tools',
|
|
49
55
|
'auto_tools_order',
|
|
50
56
|
])
|
|
51
|
-
|
|
57
|
+
supported_config_tool_keys = set([
|
|
52
58
|
'exe', 'handlers',
|
|
53
59
|
'requires_env', 'requires_py', 'requires_cmd', 'requires_in_exe_path',
|
|
54
|
-
'requires_vsim_helper',
|
|
60
|
+
'requires_vsim_helper',
|
|
61
|
+
'requires_vscode_extension',
|
|
55
62
|
'disable-tools-multi', 'disable-auto',
|
|
56
|
-
])
|
|
57
|
-
supported_config_tool_keys = set([
|
|
58
63
|
'defines',
|
|
59
64
|
'log-bad-strings',
|
|
60
65
|
'log-must-strings',
|
|
@@ -158,14 +163,6 @@ def check_config(config:dict, filename='') -> None:
|
|
|
158
163
|
util.error(f'eda_config.get_config({filename=}): has unsupported {key=}' \
|
|
159
164
|
+ f' {Defaults.supported_config_keys=}')
|
|
160
165
|
|
|
161
|
-
for row in config.get('auto_tools_order', []):
|
|
162
|
-
for tool, table in row.items():
|
|
163
|
-
for key in table:
|
|
164
|
-
if key not in Defaults.supported_config_auto_tools_order_keys:
|
|
165
|
-
util.error(f'eda_config.get_config({filename=}): has unsupported {key=}' \
|
|
166
|
-
+ f' in auto_tools_order, {tool=},' \
|
|
167
|
-
+ f' {Defaults.supported_config_auto_tools_order_keys=}')
|
|
168
|
-
|
|
169
166
|
for tool,table in config.get('tools', {}).items():
|
|
170
167
|
for key in table:
|
|
171
168
|
if key not in Defaults.supported_config_tool_keys:
|
|
@@ -318,11 +315,35 @@ def get_eda_config(args:list, quiet=False) -> (dict, list):
|
|
|
318
315
|
config = None
|
|
319
316
|
|
|
320
317
|
if parsed.config_yml != Defaults.config_yml:
|
|
318
|
+
Defaults.non_default_config_yml_arg_used = parsed.config_yml
|
|
321
319
|
config = get_config_merged_with_defaults(config)
|
|
322
320
|
|
|
323
321
|
return config, unparsed
|
|
324
322
|
|
|
325
323
|
|
|
324
|
+
def get_config_yml_args_for_flist() -> list:
|
|
325
|
+
'''Returns list of args, or empty list. Used by CommandFList when we want to get the args
|
|
326
|
+
|
|
327
|
+
to reproduce a target to be run again in `eda`'''
|
|
328
|
+
ret = []
|
|
329
|
+
|
|
330
|
+
if Defaults.non_default_config_yml_arg_used:
|
|
331
|
+
ret.append(f'--config-yml={Defaults.non_default_config_yml_arg_used}')
|
|
332
|
+
|
|
333
|
+
if Defaults.config_yml_set_from:
|
|
334
|
+
# a default config-yml was used, but it wasn't from eda, was from
|
|
335
|
+
# ENV or HOME dir. This is not included in the flist, so warn about it.
|
|
336
|
+
# Since we don't support > 1 config-yml args, need to warn about this.
|
|
337
|
+
util.warning('Note: for command "flist", picked up',
|
|
338
|
+
f'--config-yml={Defaults.config_yml},',
|
|
339
|
+
f'from: {Defaults.config_yml_set_from}')
|
|
340
|
+
|
|
341
|
+
elif Defaults.config_yml_set_from:
|
|
342
|
+
ret.append(f'--config-yml={Defaults.config_yml}')
|
|
343
|
+
|
|
344
|
+
return ret
|
|
345
|
+
|
|
346
|
+
|
|
326
347
|
def write_eda_config_and_args(
|
|
327
348
|
dirpath : str, filename: str = EDA_OUTPUT_CONFIG_FNAME,
|
|
328
349
|
command_obj_ref: object = None
|
|
@@ -338,16 +359,22 @@ def write_eda_config_and_args(
|
|
|
338
359
|
# Use deep copy b/c otherwise these are references to opencos.eda.
|
|
339
360
|
data[x] = copy.deepcopy(getattr(command_obj_ref, x, ''))
|
|
340
361
|
|
|
341
|
-
# copy util.args
|
|
342
|
-
data['util'] = {
|
|
343
|
-
|
|
344
|
-
|
|
362
|
+
# copy util.args, and other util globals:
|
|
363
|
+
data['util'] = {}
|
|
364
|
+
for x in ['INITIAL_CWD', 'args', 'dot_f_files_expanded', 'env_files_loaded', 'max_error_code']:
|
|
365
|
+
data['util'][x] = getattr(util, x, '')
|
|
366
|
+
|
|
367
|
+
# copy some information about which eda_config YAML was used:
|
|
368
|
+
for member in ['config_yml', 'config_yml_set_from', 'non_default_config_yml_arg_used']:
|
|
369
|
+
data[member] = getattr(Defaults, member, '')
|
|
345
370
|
|
|
346
371
|
# fix some burried class references in command_obj_ref.config,
|
|
347
372
|
# otherwise we won't be able to safe load this yaml, so cast as str repr.
|
|
348
|
-
|
|
373
|
+
config = getattr(command_obj_ref, 'config', {})
|
|
374
|
+
for k, v in config.items():
|
|
349
375
|
if k == 'command_handler':
|
|
350
|
-
data['config'][
|
|
376
|
+
data['config']['command_handler'] = str(v)
|
|
377
|
+
|
|
351
378
|
|
|
352
379
|
yaml_safe_writer(data=data, filepath=fullpath)
|
|
353
380
|
|
|
@@ -393,10 +420,10 @@ def tool_try_add_to_path( # pylint: disable=too-many-branches
|
|
|
393
420
|
|
|
394
421
|
name, path_arg = name_path_parts[0:2]
|
|
395
422
|
|
|
396
|
-
if name not in config['
|
|
423
|
+
if name not in config['tools']:
|
|
397
424
|
return name
|
|
398
425
|
|
|
399
|
-
config_exe = config['
|
|
426
|
+
config_exe = config['tools'][name].get('exe', str())
|
|
400
427
|
if isinstance(config_exe, list):
|
|
401
428
|
orig_exe = config_exe[0]
|
|
402
429
|
else:
|
|
@@ -433,17 +460,17 @@ def tool_try_add_to_path( # pylint: disable=too-many-branches
|
|
|
433
460
|
|
|
434
461
|
if update_config:
|
|
435
462
|
if isinstance(config_exe, list):
|
|
436
|
-
config['
|
|
463
|
+
config['tools'][name]['exe'][0] = user_exe
|
|
437
464
|
for index,value in enumerate(config_exe[1:]):
|
|
438
465
|
# update all entries, if we can, if the value is also in 'path'
|
|
439
466
|
# from our set --tool=Name=path/exe
|
|
440
467
|
new_value = os.path.join(path, os.path.split(value)[1])
|
|
441
468
|
if os.path.exists(new_value) and safe_shutil_which(new_value) and \
|
|
442
469
|
os.access(new_value, os.X_OK):
|
|
443
|
-
config['
|
|
470
|
+
config['tools'][name]['exe'][index] = new_value
|
|
444
471
|
else:
|
|
445
|
-
config['
|
|
446
|
-
util.debug(f'For {tool=},
|
|
472
|
+
config['tools'][name]['exe'] = user_exe
|
|
473
|
+
util.debug(f'For {tool=}, tools config entry updated')
|
|
447
474
|
|
|
448
475
|
util.debug(f'For {tool=}, final {user_exe=}')
|
|
449
476
|
|