opencos-eda 0.3.6__py3-none-any.whl → 0.3.8__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 CHANGED
@@ -48,7 +48,7 @@ class CommandFList(CommandDesign):
48
48
  'bracket-quote-path' : False,
49
49
  'single-quote-path' : False,
50
50
  'double-quote-path' : False,
51
- 'no-quote-path' : False,
51
+ 'quote-path' : True,
52
52
  'build-script' : "", # we don't want this to error either
53
53
 
54
54
  'print-to-stdout': False,
@@ -122,7 +122,7 @@ class CommandFList(CommandDesign):
122
122
 
123
123
  pq1 = ""
124
124
  pq2 = "" # pq = path quote
125
- if self.args['no-quote-path']:
125
+ if not self.args['quote-path']:
126
126
  pass # if we decide to make one of the below default, this will override
127
127
  elif self.args['bracket-quote-path']:
128
128
  pq1 = "{"
opencos/commands/multi.py CHANGED
@@ -28,7 +28,7 @@ class CommandMulti(CommandParallel):
28
28
  'fake': False,
29
29
  'parallel': 1,
30
30
  'single-timeout': None,
31
- 'fail-if-no-targets': False,
31
+ 'fail-if-no-targets': True,
32
32
  'export-jsonl': False,
33
33
  'print-targets': False,
34
34
  }
@@ -325,8 +325,9 @@ class CommandMulti(CommandParallel):
325
325
  current_targets = len(self.targets)
326
326
  util.info(f"Multi: Expanded {target_globs} to {len(self.targets)} {command} targets")
327
327
 
328
- if parsed.fail_if_no_targets and len(self.targets) == 0:
329
- self.error(f'Multi: --fail-if-no-targets set, and {self.targets=}')
328
+ if self.args['fail-if-no-targets'] and not self.targets:
329
+ self.error(f'Multi: --fail-if-no-targets set, and {self.targets=}. Disable with',
330
+ '--no-fail-if-no-targets, or see: eda multi --help')
330
331
  if not all_multi_tools:
331
332
  possible_tools = self.all_handler_commands.get(command, [])
332
333
  self.error(f'Multi: no tools to run for {command=}, available tools: {possible_tools}')
opencos/commands/sim.py CHANGED
@@ -44,7 +44,7 @@ def parameters_dict_get_command_list(params: dict, arg_prefix: str = '-G') -> li
44
44
  return ret_list
45
45
 
46
46
 
47
- class CommandSim(CommandDesign):
47
+ class CommandSim(CommandDesign): # pylint: disable=too-many-public-methods
48
48
  '''Base class command handler for: eda sim ...'''
49
49
 
50
50
  CHECK_REQUIRES = [Tool] # Used by check_command_handler_cls()
@@ -59,11 +59,11 @@ class CommandSim(CommandDesign):
59
59
  self.args.update({
60
60
  "pre-sim-tcl": [],
61
61
  'compile-args': [],
62
+ 'compile-waivers': [],
62
63
  'elab-args': [],
63
64
  'sim-args': [],
64
65
  'sim-plusargs': [], # lists are handled by 'set_arg(k,v)' so they append.
65
66
  'sim-library': [],
66
- 'compile-waivers': [],
67
67
  'sim-waivers': [],
68
68
  'coverage': False,
69
69
  'waves': False,
@@ -72,24 +72,39 @@ class CommandSim(CommandDesign):
72
72
  'optimize': False,
73
73
  'log-bad-strings': ['ERROR: ', 'FATAL: ', 'Error: ', 'Fatal: '],
74
74
  'log-must-strings': [],
75
+ 'log-warning-strings': ['WARNING: ', 'Warning: '],
76
+ 'uvm': False,
77
+ 'uvm-version': '1.2',
75
78
  # verilate-args: list of args you can only pass to Verilator,
76
79
  # not used by other simulators, so these can go in DEPS files for custom things
77
80
  # like -CFLAGS -O0, etc.
78
81
  'verilate-args': [],
79
82
  })
80
83
  self.args_help.update({
84
+ 'pre-sim-tcl': (
85
+ 'Tool dependent, TCL file to run prior to simulation (if tool supports TCL)'
86
+ ),
81
87
  'compile-args': 'args added to sim/elab "compile" step',
88
+ 'compile-waivers': (
89
+ 'simulation tool depenent waivers by name, applied at compilation step'
90
+ ),
82
91
  'coverage': 'attempt to run coverage steps on the compile/elab/simulation',
83
92
  'elab-args': 'args added to sim/elab "elaboration" step, if required by tool',
84
93
  'log-bad-strings': 'strings that if present in the log will fail the simulation',
85
94
  'log-must-strings': ('strings that are required by the log to not-fail the simulation.'
86
95
  ' Some tools use these at only certain phases'
87
96
  ' (compile/elab/sim).'),
97
+ 'log-warning-strings': ('strings that if present will be counted in the total tool'
98
+ ' warnings at end of simulation'),
88
99
  'pass-pattern': ('Additional string required to pass a simulation, appends to'
89
100
  ' log-must-strings'),
90
101
  'sim-args': 'args added to final "simulation" step',
91
102
  'sim-plusargs': ('"simulation" step run-time args passed to tool, these can also'
92
103
  ' be set using --sim-plusargs=name[=value], or simply +name[=value]'),
104
+ 'sim-library': 'named libraries added to simulation, tool dependent',
105
+ 'sim-waivers': (
106
+ 'simulation tool depenent waivers by name, applied at "simulation" step'
107
+ ),
93
108
  'stop-before-compile': ('Create work-dir sh scripts for compile/elab/simulate, but do'
94
109
  ' not run them.'),
95
110
  'stop-after-compile': 'Create work-dir sh scripts, but only run the compile step.',
@@ -97,17 +112,26 @@ class CommandSim(CommandDesign):
97
112
  ' simulation step.'),
98
113
  'top': 'Name of topmost Verilog/SystemVerilog module, or VHDL entity',
99
114
  'verilate-args': ('args added to "compile" step in Verilator simulation'
100
- ' (for --tool=verilator)'),
115
+ ' (for --tool=verilator) these are identical to --compile-args'),
101
116
  'waves': 'Include waveforms, if possible for tool',
102
117
  'waves-start': 'Starting time of waveform capture, if possible for tool',
103
- 'work-dir': 'Optional override for working directory, defaults to ./eda.work/<top>.sim',
104
118
  'test-mode': ('stops the command early without executing, if --gui is present will'
105
- ' instead test without spawning gui')
119
+ ' instead test without spawning gui'),
120
+ 'uvm': (
121
+ 'Attempts to support UVM (aka 1.2 if --uvm-version=1.2) for the'
122
+ ' the simulation tool. May add libraries for uvm, tool dependent.'
123
+ ),
124
+ 'uvm-version': (
125
+ 'Used if --uvm is set, for example --uvm-version=1.2'
126
+ ),
127
+
128
+ })
106
129
 
130
+ self.args_kwargs.update({
131
+ 'uvm-version': { 'choices': ['1.2'] },
107
132
  })
108
133
 
109
134
 
110
- self.args['verilate-args'] = []
111
135
 
112
136
  def process_parameters_get_list(self, arg_prefix: str = '-G') -> list:
113
137
  '''Returns list (suitable command list for shell or for tool) from self.parameters'''
@@ -173,14 +197,14 @@ class CommandSim(CommandDesign):
173
197
 
174
198
  # Collect (overwrite CommandSim) the bad and must strings, if present,
175
199
  # from our config.tools.verilator:
176
- for tool_config_key in ['log-bad-strings', 'log-must-strings']:
200
+ for tool_config_key in ('log-bad-strings', 'log-must-strings', 'log-warning-strings'):
177
201
  if len(self.tool_config.get(tool_config_key, [])) > 0:
178
202
  self.args[tool_config_key] = self.tool_config.get(tool_config_key, [])
179
203
 
180
204
 
181
205
  # Methods that derived classes may override:
182
206
 
183
- def run_commands_check_logs( # pylint: disable=dangerous-default-value
207
+ def run_commands_check_logs( # pylint: disable=dangerous-default-value,too-many-locals
184
208
  self, commands: list , check_logs: bool = True, log_filename=None,
185
209
  bad_strings: list = [],
186
210
  must_strings: list = [],
@@ -188,7 +212,10 @@ class CommandSim(CommandDesign):
188
212
  ) -> None:
189
213
  '''Returns None, runs all commands (each element is a list) and checks logs
190
214
 
191
- for bad-strings and must-strings (args or class member vars)
215
+ for bad-strings and must-strings (args or class member vars).
216
+
217
+ Will add any bad_strings or tool log-warning-strings to self, so tool
218
+ related warning/error counts can be reported later.
192
219
  '''
193
220
 
194
221
  for obj in commands:
@@ -203,7 +230,7 @@ class CommandSim(CommandDesign):
203
230
  if not work_dir:
204
231
  work_dir = self.args['work-dir']
205
232
 
206
- util.debug(f'run_commands_check_logs: {clist=}, {tee_fpath=}')
233
+ util.debug(f'run_commands_check_logs: {clist=}, {tee_fpath=}, {log_filename=}')
207
234
 
208
235
  log_fname = None
209
236
  if tee_fpath:
@@ -211,11 +238,18 @@ class CommandSim(CommandDesign):
211
238
  if log_filename:
212
239
  log_fname = log_filename
213
240
 
214
-
215
- _, stdout, _ = self.exec(
216
- work_dir=work_dir, command_list=clist, tee_fpath=tee_fpath
241
+ # track the retcode because we run stop_on_error=False (to count warnings/errors)
242
+ _, stdout, retcode = self.exec(
243
+ work_dir=work_dir, command_list=clist, tee_fpath=tee_fpath,
244
+ stop_on_error=False,
217
245
  )
218
246
 
247
+ if check_logs and not log_fname and bad_strings:
248
+ util.warning(
249
+ f'{self.get_info_job_name()}: check_logs=True but no log file set',
250
+ f'(run --debug for more info), for command: {" ".join(clist)}'
251
+ )
252
+
219
253
  if check_logs and log_fname:
220
254
  # Note this call will check on stdout if not GUI, not opening the log_fname,
221
255
  # but if this is GUI we normally lose stdout and have to open the log.
@@ -225,6 +259,7 @@ class CommandSim(CommandDesign):
225
259
  file_contents_str = stdout
226
260
 
227
261
  self.check_logs_for_errors(
262
+ sim_retcode=retcode,
228
263
  filename=os.path.join(work_dir, log_fname),
229
264
  file_contents_str=file_contents_str,
230
265
  bad_strings=bad_strings, must_strings=must_strings,
@@ -235,6 +270,10 @@ class CommandSim(CommandDesign):
235
270
  name=os.path.join(work_dir, log_fname),
236
271
  typ='text', description='Simulator stdout/stderr log file'
237
272
  )
273
+ if retcode:
274
+ self.error(
275
+ f"{self.get_info_job_name()}: exec returned with error code: {retcode}"
276
+ )
238
277
 
239
278
  def do_export(self) -> None:
240
279
  '''CommandSim helper for handling args --export*
@@ -328,9 +367,13 @@ class CommandSim(CommandDesign):
328
367
  '''
329
368
  return
330
369
 
370
+
331
371
  def check_logs_for_errors( # pylint: disable=dangerous-default-value,too-many-locals,too-many-branches
332
- self, filename: str = '', file_contents_str: str = '',
372
+ self,
373
+ sim_retcode: int = 0,
374
+ filename: str = '', file_contents_str: str = '',
333
375
  bad_strings: list = [], must_strings: list = [],
376
+ warning_strings: list = [],
334
377
  use_bad_strings: bool = True, use_must_strings: bool = True
335
378
  ) -> None:
336
379
  '''Returns None, checks logs using args bad_strings, must_strings,
@@ -349,6 +392,8 @@ class CommandSim(CommandDesign):
349
392
  if use_must_strings:
350
393
  _must_strings = must_strings + self.args.get('log-must-strings', [])
351
394
 
395
+ _warning_strings = warning_strings + self.args.get('log-warning-strings', [])
396
+
352
397
  if self.args['pass-pattern'] != "":
353
398
  _must_strings.append(self.args['pass-pattern'])
354
399
 
@@ -380,6 +425,20 @@ class CommandSim(CommandDesign):
380
425
  self.error(f'sim.check_logs_for_errors: {log_fpath=} does not exist, and no',
381
426
  'file_contents_str exists to check')
382
427
 
428
+ self.update_tool_warn_err_counts_from_log_lines(
429
+ log_lines=lines, bad_strings=_bad_strings, warning_strings=_warning_strings
430
+ )
431
+
432
+ if isinstance(self, Tool):
433
+ self.report_tool_warn_error_counts()
434
+
435
+ if sim_retcode > 0:
436
+ # We have to update artifacts first, have the caller set the error.
437
+ # Skip the checking for bad strings or must strings, because we've already
438
+ # failed, but we did the important counting of Error and Warning lines above,
439
+ # and reported it.
440
+ return
441
+
383
442
  if lines:
384
443
  for lineno, line in enumerate(lines):
385
444
  if any(must_str in line for must_str in _must_strings):
opencos/eda.py CHANGED
@@ -20,8 +20,8 @@ from pathlib import Path
20
20
 
21
21
  import opencos
22
22
  from opencos import util, eda_config, eda_base
23
- from opencos.util import safe_emoji
24
- from opencos.eda_base import Tool, which_tool, get_eda_exec
23
+ from opencos.util import safe_emoji, Colors
24
+ from opencos.eda_base import Tool, which_tool, get_eda_exec, print_eda_usage_line
25
25
  from opencos.utils import vsim_helper, vscode_helper
26
26
  from opencos.utils.subprocess_helpers import subprocess_run_background
27
27
  from opencos.utils import status_constants, str_helpers, subprocess_helpers
@@ -76,7 +76,7 @@ def get_all_commands_help_str(config: dict) -> str:
76
76
  all_commands_help.append(f' {key:<{max_command_str_len}} - {value.strip()}')
77
77
  if all_commands_help:
78
78
  all_commands_help = [
79
- 'Where <command> is one of:',
79
+ f'Where {Colors.byellow}COMMAND{Colors.normal} is one of:',
80
80
  '',
81
81
  ] + all_commands_help
82
82
  return '\n'.join(all_commands_help)
@@ -95,22 +95,24 @@ def usage(tokens: list, config: dict, command: str = "", tool: str = "") -> int:
95
95
  '''
96
96
 
97
97
  if command == "":
98
- print(
99
- f"""
100
- {safe_emoji("🔦 ")}Usage:
101
- eda [<options>] <command> [options] <files|targets, ...>
102
- """
103
- )
98
+ util.info('Help:', color=Colors.cyan)
99
+ print()
100
+ print_eda_usage_line()
104
101
  print(get_all_commands_help_str(config=config))
105
- print(
106
- f"""
107
- {safe_emoji("❕ ")}where <files|targets, ...> is one or more source file or DEPS markup file target,
108
- such as .v, .sv, .vhd[l], .cpp files, or a target key in a DEPS.[yml|yaml|toml|json].
109
- Note that you can prefix source files with `sv@`, `v@`, `vhdl@` or `cpp@` to
110
- force use that file as systemverilog, verilog, vhdl, or C++, respectively.
111
-
112
- """
113
- )
102
+ print()
103
+ print(str_helpers.indent_wrap_long_text(
104
+ (
105
+ f'{safe_emoji("❕ ")}where {Colors.byellow}FILES|TARGETS{Colors.normal}'
106
+ ' is one or more source file or DEPS markup file target,'
107
+ ' such as .v, .sv, .vhd[l], .cpp files, or a target key in a'
108
+ f' {Colors.byellow}DEPS.[yml|yaml|toml|json]{Colors.normal}. Note that you can'
109
+ f' prefix source files with {Colors.bcyan}sv@{Colors.normal},'
110
+ f' {Colors.bcyan}v@{Colors.normal}, {Colors.bcyan}vhdl@{Colors.normal},'
111
+ f' or {Colors.bcyan}cpp@{Colors.normal}'
112
+ ' to force use that file as systemverilog, verilog, vhdl, or C++, respectively.'
113
+ ), width=str_helpers.get_terminal_columns(), indent=4
114
+ ))
115
+ print()
114
116
  eda_base.print_base_help()
115
117
  return 0
116
118
 
@@ -527,7 +529,7 @@ def main(*args):
527
529
  sys.exit(0)
528
530
 
529
531
  if not util.args['quiet']:
530
- util.info(f'eda: version {opencos.__version__}')
532
+ util.info(f'eda: version {opencos.__version__}', color=Colors.bcyan)
531
533
  # And show the command that was run (all args):
532
534
  util.info(f"main: eda {' '.join(args)}; (run from {os.getcwd()})")
533
535
 
@@ -537,7 +539,7 @@ def main(*args):
537
539
  # Note - we used to call: config = init_config(config=config)
538
540
  # However, we now defer calling init_config(..) until eda.process_tokens(..)
539
541
 
540
- util.info("*** OpenCOS EDA ***")
542
+ util.info("*** OpenCOS EDA ***", color=Colors.bcyan)
541
543
 
542
544
  if len(args) == 0 or (len(args) == 1 and '--debug' in args):
543
545
  # special snowflake case if someone called with a singular arg --debug