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.
Files changed (41) hide show
  1. opencos/commands/flist.py +222 -39
  2. opencos/commands/multi.py +5 -3
  3. opencos/commands/sim.py +121 -7
  4. opencos/commands/sweep.py +1 -1
  5. opencos/commands/upload.py +4 -4
  6. opencos/commands/waves.py +44 -0
  7. opencos/deps/deps_file.py +4 -1
  8. opencos/deps_schema.py +2 -2
  9. opencos/eda.py +284 -175
  10. opencos/eda_base.py +54 -22
  11. opencos/eda_config.py +51 -24
  12. opencos/eda_config_defaults.yml +326 -195
  13. opencos/eda_config_reduced.yml +19 -39
  14. opencos/eda_tool_helper.py +193 -24
  15. opencos/tools/cocotb.py +6 -29
  16. opencos/tools/invio.py +0 -6
  17. opencos/tools/iverilog.py +16 -16
  18. opencos/tools/modelsim_ase.py +1 -13
  19. opencos/tools/quartus.py +81 -24
  20. opencos/tools/questa.py +0 -14
  21. opencos/tools/questa_common.py +95 -30
  22. opencos/tools/questa_fe.py +0 -14
  23. opencos/tools/questa_fse.py +0 -14
  24. opencos/tools/riviera.py +56 -25
  25. opencos/tools/slang.py +15 -12
  26. opencos/tools/slang_yosys.py +0 -6
  27. opencos/tools/surelog.py +14 -11
  28. opencos/tools/tabbycad_yosys.py +1 -7
  29. opencos/tools/verilator.py +18 -14
  30. opencos/tools/vivado.py +57 -22
  31. opencos/tools/yosys.py +2 -4
  32. opencos/util.py +14 -5
  33. opencos/utils/str_helpers.py +42 -1
  34. {opencos_eda-0.3.11.dist-info → opencos_eda-0.3.13.dist-info}/METADATA +1 -2
  35. {opencos_eda-0.3.11.dist-info → opencos_eda-0.3.13.dist-info}/RECORD +40 -41
  36. opencos/eda_config_max_verilator_waivers.yml +0 -39
  37. {opencos_eda-0.3.11.dist-info → opencos_eda-0.3.13.dist-info}/WHEEL +0 -0
  38. {opencos_eda-0.3.11.dist-info → opencos_eda-0.3.13.dist-info}/entry_points.txt +0 -0
  39. {opencos_eda-0.3.11.dist-info → opencos_eda-0.3.13.dist-info}/licenses/LICENSE +0 -0
  40. {opencos_eda-0.3.11.dist-info → opencos_eda-0.3.13.dist-info}/licenses/LICENSE.spdx +0 -0
  41. {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('auto_tools_order', [{}])[0]:
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.byellow
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
- return
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 get_command_from_unparsed_args(
882
- self, tokens: list, error_if_no_command: bool = True
900
+ def get_sub_command_from_config(
901
+ self, error_if_no_command: bool = True,
883
902
  ) -> str:
884
- '''Given a list of unparsed args, try to fish out the eda COMMAND value.
903
+ '''If this is a command with a sub-command (multi, sweep), then opencos.eda would
885
904
 
886
- This will remove the value from the tokens list.
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
- f"but didn't find one in {tokens=}",
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(r'^(\w+)$', text)
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(r'^(\w+)\=(\S+)$', text)
1544
+ m = re.match(rf'^({PARAMETER_NAME_RSTR})\=(\S+)$', text)
1522
1545
  if not m:
1523
- m = re.match(r'^(\w+)\=(\"[^\"]*\")$', text)
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
- m = re.match(r"^\'?\-G\w+\=.+", token)
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
- supported_config_auto_tools_order_keys = set([
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', 'requires_vscode_extension',
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
- 'args': util.args
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
- for k, v in getattr(command_obj_ref, 'config', {}).items():
373
+ config = getattr(command_obj_ref, 'config', {})
374
+ for k, v in config.items():
349
375
  if k == 'command_handler':
350
- data['config'][k] = str(v)
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['auto_tools_order'][0]:
423
+ if name not in config['tools']:
397
424
  return name
398
425
 
399
- config_exe = config['auto_tools_order'][0][name].get('exe', str())
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['auto_tools_order'][0][name]['exe'][0] = user_exe
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['auto_tools_order'][0][name]['exe'][index] = new_value
470
+ config['tools'][name]['exe'][index] = new_value
444
471
  else:
445
- config['auto_tools_order'][0][name]['exe'] = user_exe
446
- util.debug(f'For {tool=}, auto_tools_order config updated')
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