opencos-eda 0.2.49__tar.gz → 0.2.50__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 (94) hide show
  1. {opencos_eda-0.2.49/opencos_eda.egg-info → opencos_eda-0.2.50}/PKG-INFO +1 -1
  2. {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/commands/multi.py +1 -1
  3. {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/commands/sim.py +5 -0
  4. {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/eda_base.py +15 -2
  5. {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/eda_config.py +5 -0
  6. {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/eda_config_defaults.yml +36 -4
  7. {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/tests/test_eda_elab.py +2 -1
  8. {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/tests/test_tools.py +1 -0
  9. {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/tools/modelsim_ase.py +22 -0
  10. {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/tools/questa.py +5 -3
  11. opencos_eda-0.2.50/opencos/tools/questa_fse.py +57 -0
  12. {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/tools/slang.py +8 -2
  13. {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/tools/verilator.py +25 -0
  14. {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/tools/vivado.py +33 -26
  15. {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/util.py +158 -6
  16. {opencos_eda-0.2.49 → opencos_eda-0.2.50/opencos_eda.egg-info}/PKG-INFO +1 -1
  17. {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos_eda.egg-info/SOURCES.txt +1 -0
  18. {opencos_eda-0.2.49 → opencos_eda-0.2.50}/pyproject.toml +1 -1
  19. {opencos_eda-0.2.49 → opencos_eda-0.2.50}/LICENSE +0 -0
  20. {opencos_eda-0.2.49 → opencos_eda-0.2.50}/LICENSE.spdx +0 -0
  21. {opencos_eda-0.2.49 → opencos_eda-0.2.50}/README.md +0 -0
  22. {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/__init__.py +0 -0
  23. {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/_version.py +0 -0
  24. {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/_waves_pkg.sv +0 -0
  25. {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/commands/__init__.py +0 -0
  26. {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/commands/build.py +0 -0
  27. {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/commands/elab.py +0 -0
  28. {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/commands/export.py +0 -0
  29. {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/commands/flist.py +0 -0
  30. {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/commands/lec.py +0 -0
  31. {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/commands/open.py +0 -0
  32. {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/commands/proj.py +0 -0
  33. {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/commands/shell.py +0 -0
  34. {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/commands/sweep.py +0 -0
  35. {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/commands/synth.py +0 -0
  36. {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/commands/targets.py +0 -0
  37. {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/commands/upload.py +0 -0
  38. {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/commands/waves.py +0 -0
  39. {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/deps/__init__.py +0 -0
  40. {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/deps/defaults.py +0 -0
  41. {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/deps/deps_commands.py +0 -0
  42. {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/deps/deps_file.py +0 -0
  43. {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/deps/deps_processor.py +0 -0
  44. {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/deps_schema.py +0 -0
  45. {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/eda.py +0 -0
  46. {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/eda_config_max_verilator_waivers.yml +0 -0
  47. {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/eda_config_reduced.yml +0 -0
  48. {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/eda_deps_bash_completion.bash +0 -0
  49. {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/eda_extract_targets.py +0 -0
  50. {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/eda_tool_helper.py +0 -0
  51. {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/export_helper.py +0 -0
  52. {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/export_json_convert.py +0 -0
  53. {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/files.py +0 -0
  54. {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/hw/__init__.py +0 -0
  55. {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/hw/oc_cli.py +0 -0
  56. {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/hw/pcie.py +0 -0
  57. {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/names.py +0 -0
  58. {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/peakrdl_cleanup.py +0 -0
  59. {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/seed.py +0 -0
  60. {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/tests/__init__.py +0 -0
  61. {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/tests/custom_config.yml +0 -0
  62. {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/tests/deps_files/command_order/DEPS.yml +0 -0
  63. {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/tests/deps_files/error_msgs/DEPS.yml +0 -0
  64. {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/tests/deps_files/iverilog_test/DEPS.yml +0 -0
  65. {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/tests/deps_files/no_deps_here/DEPS.yml +0 -0
  66. {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/tests/deps_files/non_sv_reqs/DEPS.yml +0 -0
  67. {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/tests/deps_files/tags_with_tools/DEPS.yml +0 -0
  68. {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/tests/deps_files/test_err_fatal/DEPS.yml +0 -0
  69. {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/tests/helpers.py +0 -0
  70. {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/tests/test_build.py +0 -0
  71. {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/tests/test_deps_helpers.py +0 -0
  72. {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/tests/test_deps_schema.py +0 -0
  73. {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/tests/test_eda.py +0 -0
  74. {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/tests/test_eda_synth.py +0 -0
  75. {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/tests/test_oc_cli.py +0 -0
  76. {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/tools/__init__.py +0 -0
  77. {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/tools/invio.py +0 -0
  78. {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/tools/invio_helpers.py +0 -0
  79. {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/tools/invio_yosys.py +0 -0
  80. {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/tools/iverilog.py +0 -0
  81. {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/tools/riviera.py +0 -0
  82. {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/tools/slang_yosys.py +0 -0
  83. {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/tools/surelog.py +0 -0
  84. {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/tools/tabbycad_yosys.py +0 -0
  85. {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/tools/yosys.py +0 -0
  86. {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/utils/__init__.py +0 -0
  87. {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/utils/markup_helpers.py +0 -0
  88. {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/utils/str_helpers.py +0 -0
  89. {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos/utils/subprocess_helpers.py +0 -0
  90. {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos_eda.egg-info/dependency_links.txt +0 -0
  91. {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos_eda.egg-info/entry_points.txt +0 -0
  92. {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos_eda.egg-info/requires.txt +0 -0
  93. {opencos_eda-0.2.49 → opencos_eda-0.2.50}/opencos_eda.egg-info/top_level.txt +0 -0
  94. {opencos_eda-0.2.49 → opencos_eda-0.2.50}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: opencos-eda
3
- Version: 0.2.49
3
+ Version: 0.2.50
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
@@ -590,7 +590,7 @@ class CommandToolsMulti(CommandMulti):
590
590
  def multi_which_tools(self, command):
591
591
  '''Overrides CommandMulti.multi_which_tool(command), return a list of all
592
592
  possible tools that can run this command'''
593
- if self.tools is None or len(self.tools) == 0:
593
+ if self.tools is None or not self.tools:
594
594
  # wasn't set via arg --tools, so use all if possible for this command.
595
595
  which_tools = self.all_handler_commands.get(command, [])
596
596
  else:
@@ -177,6 +177,11 @@ class CommandSim(CommandDesign):
177
177
  filename=log_fname, bad_strings=bad_strings, must_strings=must_strings,
178
178
  use_bad_strings=use_bad_strings, use_must_strings=use_must_strings
179
179
  )
180
+ if log_fname:
181
+ self.artifacts_add(
182
+ name=os.path.join(self.args['work-dir'], log_fname),
183
+ typ='text', description='Simulator stdout/stderr log file'
184
+ )
180
185
 
181
186
  def do_export(self) -> None:
182
187
  '''CommandSim helper for handling args --export*
@@ -257,10 +257,15 @@ class Command:
257
257
  if self.args['work-dir']:
258
258
  if not self.errors_log_f:
259
259
  try:
260
+ fullpath = os.path.join(self.args['work-dir'], 'eda.errors.log')
260
261
  self.errors_log_f = open( # pylint: disable=consider-using-with
261
- os.path.join(self.args['work-dir'], 'eda.errors.log'), 'w',
262
- encoding='utf-8'
262
+ fullpath, 'w', encoding='utf-8'
263
263
  )
264
+ util.artifacts.add(
265
+ name=fullpath,
266
+ typ='text', description='EDA reported errors'
267
+ )
268
+
264
269
  except FileNotFoundError:
265
270
  pass
266
271
  if self.errors_log_f:
@@ -374,8 +379,16 @@ class Command:
374
379
  if self.args['keep']:
375
380
  open(keep_file, 'w', encoding='utf-8').close() # pylint: disable=consider-using-with
376
381
  util.debug(f'create_work_dir: created {keep_file=}')
382
+
383
+ # Set the util.artifacts path with our work-dir:
384
+ util.artifacts.set_artifacts_json_dir(self.args['work-dir'])
385
+
377
386
  return self.args['work-dir']
378
387
 
388
+ def artifacts_add(self, name: str, typ: str, description: str) -> None:
389
+ '''Adds a file to util.artifacts, derived classes may override'''
390
+ util.artifacts.add(name=name, typ=typ, description=description)
391
+
379
392
 
380
393
  def exec(self, work_dir: str, command_list: list, background: bool = False,
381
394
  stop_on_error: bool = True, quiet: bool = False, tee_fpath: str = '',
@@ -308,6 +308,11 @@ def write_eda_config_and_args(
308
308
  # Use deep copy b/c otherwise these are references to opencos.eda.
309
309
  data[x] = copy.deepcopy(getattr(command_obj_ref, x, ''))
310
310
 
311
+ # copy util.args
312
+ data['util'] = {
313
+ 'args': util.args
314
+ }
315
+
311
316
  # fix some burried class references in command_obj_ref.config,
312
317
  # otherwise we won't be able to safe load this yaml, so cast as str repr.
313
318
  for k, v in getattr(command_obj_ref, 'config', {}).items():
@@ -230,17 +230,39 @@ tools:
230
230
  compile-args: |
231
231
  -sv -svinputport=net -lint
232
232
  compile-waivers:
233
- - 2275 # 2275 - Existing package 'foo_pkg' will be overwritten.
234
- - 2555 # 2555 - assignment to input port foo
233
+ - 2275 # 2275 - Existing package 'myname_pkg' will be overwritten.
234
+ - 2555 # 2555 - assignment to input port myname
235
235
  - 2583 # 2583 - [SVCHK] - Extra checking for conflicts with always_comb and
236
236
  # always_latch variables is done at vopt time.
237
237
  simulate-waivers:
238
- - 3009 # 3009: [TSCALE] - Module 'foo' does not have a timeunit/timeprecision
238
+ - 3009 # 3009: [TSCALE] - Module 'myname' does not have a timeunit/timeprecision
239
239
  # specification in effect, but other modules do.
240
240
  simulate-waves-args: |
241
241
  +acc
242
242
 
243
243
 
244
+ questa_fse:
245
+ defines:
246
+ OC_TOOL_QUESTA_FSE: 1
247
+ log-bad-strings:
248
+ - "Error:"
249
+ log-must-strings:
250
+ - " vsim "
251
+ - "Errors: 0"
252
+ compile-args: |
253
+ -sv -svinputport=net -lint
254
+ compile-waivers:
255
+ - 2275 # 2275 - Existing package 'myname_pkg' will be overwritten.
256
+ - 2555 # 2555 - assignment to input port myname
257
+ - 2583 # 2583 - [SVCHK] - Extra checking for conflicts with always_comb and
258
+ # always_latch variables is done at vopt time.
259
+ simulate-waivers:
260
+ - 3009 # 3009: [TSCALE] - Module 'myname' does not have a timeunit/timeprecision
261
+ # specification in effect, but other modules do.
262
+ simulate-waves-args: |
263
+ -voptargs=+acc=bcgnprst
264
+
265
+
244
266
  iverilog:
245
267
  log-bad-strings:
246
268
  - "Error:"
@@ -303,7 +325,7 @@ auto_tools_order:
303
325
  # TODO(drew): surelog is disabled from `eda tools-multi`. It is still
304
326
  # enabled for `eda [multi] elab --tool surelog`. It does not support
305
327
  # type comparisons:
306
- # if type(foo) == type(bar)
328
+ # if type(myname) == type(othername)
307
329
  # [ERR:UH0700] ... Unsupported expression
308
330
  # modelsim_ase also doesn't, but it won't fail elab, whereas surelog does.
309
331
  surelog:
@@ -404,6 +426,16 @@ auto_tools_order:
404
426
  elab: opencos.tools.modelsim_ase.CommandElabModelsimAse
405
427
  sim: opencos.tools.modelsim_ase.CommandSimModelsimAse
406
428
 
429
+ questa_fse: # free student edition, works similar to modelsim_ase
430
+ exe: vsim
431
+ requires_cmd:
432
+ - vsim -version
433
+ requires_in_exe_path:
434
+ - questa_fse
435
+ handlers:
436
+ elab: opencos.tools.questa_fse.CommandElabQuestaFse
437
+ sim: opencos.tools.questa_fse.CommandSimQuestaFse
438
+
407
439
  iverilog:
408
440
  exe: iverilog
409
441
  handlers:
@@ -22,7 +22,8 @@ list_of_elab_tools = [
22
22
  'slang',
23
23
  'verilator',
24
24
  'vivado',
25
- 'modelsim_ase',
25
+ 'modelsim_ase'
26
+ 'questa_fse',
26
27
  'invio',
27
28
  'surelog',
28
29
  'invio_yosys',
@@ -73,6 +73,7 @@ list_of_tools = [
73
73
  'verilator',
74
74
  'vivado',
75
75
  'modelsim_ase',
76
+ 'questa_fse',
76
77
  ]
77
78
 
78
79
  list_of_deps_targets = [
@@ -1,6 +1,8 @@
1
1
  ''' opencos.tools.modelsim_ase - Used by opencos.eda for sim/elab commands w/ --tool=modelsim_ase.
2
2
 
3
3
  Contains classes for ToolModelsimAse, CommandSimModelsimAse, CommandElabModelsimAse.
4
+
5
+ Note that this is for 32-bit Modelsim Student Edition. Consider using --tool=questa_fse instead.
4
6
  '''
5
7
 
6
8
  # pylint: disable=R0801 # (duplicate code in derived classes, such as if-condition return.)
@@ -151,6 +153,9 @@ class CommandSimModelsimAse(CommandSim, ToolModelsimAse):
151
153
  ]):
152
154
  vlog_dot_f_lines += ['-suppress', str(waiver)]
153
155
 
156
+ if self.args['gui'] or self.args['waves']:
157
+ vlog_dot_f_lines += self.tool_config.get('compile-waves-args', '').split()
158
+
154
159
  vlog_dot_f_fname = filename
155
160
  vlog_dot_f_fpath = os.path.join(self.args['work-dir'], vlog_dot_f_fname)
156
161
 
@@ -203,6 +208,10 @@ class CommandSimModelsimAse(CommandSim, ToolModelsimAse):
203
208
  voptargs_str = ""
204
209
  if self.args['gui'] or self.args['waves']:
205
210
  voptargs_str = self.tool_config.get('simulate-waves-args', '+acc')
211
+ util.artifacts.add_extension(
212
+ search_paths=self.args['work-dir'], file_extension='wlf',
213
+ typ='waveform', description='Modelsim/Questa Waveform WLF (Wave Log Format) file'
214
+ )
206
215
 
207
216
  # TODO(drew): support self.args['sim_libary', 'elab-args', sim-args'] (3 lists)
208
217
  # to add to vsim_one_liner.
@@ -331,6 +340,19 @@ class CommandSimModelsimAse(CommandSim, ToolModelsimAse):
331
340
  return ' '.join(vsim_suppress_list)
332
341
 
333
342
 
343
+ def artifacts_add(self, name: str, typ: str, description: str) -> None:
344
+ '''Override from Command.artifacts_add, so we can catch known file
345
+
346
+ names to make their typ/description better, such as CommandSim using
347
+ sim.log
348
+ '''
349
+ _, leafname = os.path.split(name)
350
+ if leafname == 'sim.log':
351
+ description = 'Modelsim/Questa Transcript log file'
352
+
353
+ super().artifacts_add(name=name, typ=typ, description=description)
354
+
355
+
334
356
  class CommandElabModelsimAse(CommandSimModelsimAse):
335
357
  '''CommandElabModelsimAse is a command handler for: eda elab --tool=modelsim_ase'''
336
358
 
@@ -40,9 +40,11 @@ class ToolQuesta(Tool):
40
40
  self.error(f"{self._EXE} not in path, need to setup",
41
41
  "(i.e. source /opt/intelFPGA_pro/23.4/settings64.sh")
42
42
  util.debug(f"{path=}")
43
- if self._EXE.endswith('qrun') and 'modelsim_ase' in path:
44
- util.warning(f"{self._EXE=} Questa path is for starter edition (modelsim_ase),",
45
- "consider using --tool modelsim_ase")
43
+ if self._EXE.endswith('qrun') and \
44
+ any(x in path for x in ('modelsim_ase', 'questa_fse')):
45
+ util.warning(f"{self._EXE=} Questa path is for starter edition",
46
+ "(modelsim_ase, questa_fse), consider using --tool=modelsim_ase",
47
+ "or --tool=questa_fse")
46
48
  else:
47
49
  self.sim_exe = path
48
50
  self.sim_exe_base_path, _ = os.path.split(path)
@@ -0,0 +1,57 @@
1
+ ''' opencos.tools.questa_fse - Used by opencos.eda for sim/elab commands w/ --tool=questa_fse.
2
+
3
+ Contains classes for CommandSimQuestaFse, CommandElabQuestaFse.
4
+ '''
5
+
6
+ # pylint: disable=R0801 # (duplicate code in derived classes, such as if-condition return.)
7
+ # pylint: disable=too-many-ancestors
8
+
9
+ import os
10
+
11
+ from opencos.tools.modelsim_ase import CommandSimModelsimAse
12
+
13
+
14
+ class CommandSimQuestaFse(CommandSimModelsimAse):
15
+ '''CommandSimQuestaFse is a command handler for: eda sim --tool=questa_fse
16
+
17
+ Note this inherits 99% from CommandSimModelSimAse for command handling
18
+ '''
19
+ _TOOL = 'questa_fse'
20
+ _EXE = 'vsim'
21
+
22
+ def __init__(self, config: dict):
23
+ # this will setup with self._TOOL = modelsim_ase, which is not ideal so
24
+ # we have to repait it later.
25
+ CommandSimModelsimAse.__init__(self, config=config)
26
+
27
+ # repairs: override self._TOOL, and run get_versions() again.
28
+ self._TOOL = 'questa_fse'
29
+
30
+ self.shell_command = os.path.join(self.sim_exe_base_path, 'vsim')
31
+ self.starter_edition = True
32
+ self.args.update({
33
+ 'tool': self._TOOL, # override
34
+ 'gui': False,
35
+ })
36
+
37
+
38
+ def set_tool_defines(self):
39
+ '''Override from questa.ToolQuesta'''
40
+ # Update any defines from config.tools.questa_fse:
41
+ self.defines.update(
42
+ self.tool_config.get(
43
+ 'defines',
44
+ # defaults, if not set:
45
+ {
46
+ 'OC_TOOL_QUESTA_FSE': 1
47
+ }
48
+ )
49
+ )
50
+
51
+
52
+ class CommandElabQuestaFse(CommandSimQuestaFse):
53
+ '''CommandElabQuestaFse is a command handler for: eda elab --tool=questa_fse'''
54
+
55
+ def __init__(self, config:dict):
56
+ super().__init__(config)
57
+ self.args['stop-after-elaborate'] = True
@@ -191,14 +191,20 @@ class CommandElabSlang(CommandElab, ToolSlang):
191
191
 
192
192
  return command_list
193
193
 
194
- def _get_slang_json_args(self, command_exe:str) -> list:
194
+ def _get_slang_json_args(self, command_exe: str) -> list:
195
195
  command_list = []
196
196
 
197
- if self.args.get('slang-json', False) and command_exe == 'slang':
197
+ _, command_exe_leaf = os.path.split(command_exe)
198
+ if self.args.get('slang-json', False) and command_exe_leaf == 'slang':
198
199
  for arg in self.all_json_args:
199
200
  if arg not in command_list:
200
201
  command_list.append(arg)
201
202
  if arg == '--ast-json': # needs filename
202
203
  command_list.append('slang.json')
204
+ util.artifacts.add(
205
+ name=os.path.join(self.args['work-dir'], 'slang.json'),
206
+ typ='json',
207
+ description='Abstract syntax tree from slang --ast-json'
208
+ )
203
209
 
204
210
  return command_list
@@ -346,6 +346,16 @@ class VerilatorSim(CommandSim, ToolVerilator):
346
346
  util.info(f'--waves arg present, no $dumpfile found, adding SV file: {file_to_add}')
347
347
  self.add_file(file_to_add)
348
348
 
349
+ # register .vcd or .fst artifacts:
350
+ util.artifacts.add_extension(
351
+ search_paths=self.args['work-dir'], file_extension='fst',
352
+ typ='waveform', description='Simulation Waveform FST (Fast Signal Trace) file'
353
+ )
354
+ util.artifacts.add_extension(
355
+ search_paths=self.args['work-dir'], file_extension='vcd',
356
+ typ='waveform', description='Simulation Waveform VCD (Value Change Dump) file'
357
+ )
358
+
349
359
 
350
360
  def _get_start_verilator_command_list(self, lint_only: bool = False) -> list:
351
361
 
@@ -447,6 +457,21 @@ class VerilatorSim(CommandSim, ToolVerilator):
447
457
  return verilate_args
448
458
 
449
459
 
460
+ def artifacts_add(self, name: str, typ: str, description: str) -> None:
461
+ '''Override from Command.artifacts_add, so we can catch known file
462
+
463
+ names to make their typ/description better, such as CommandSim using
464
+ sim.log or compile.log
465
+ '''
466
+ _, leafname = os.path.split(name)
467
+ if leafname == 'sim.log':
468
+ description = 'Verilated executable log from stdout/stderr'
469
+ elif leafname == 'compile.log':
470
+ description = 'Verilator compile step log from verilator call'
471
+
472
+ super().artifacts_add(name=name, typ=typ, description=description)
473
+
474
+
450
475
  class VerilatorElab(VerilatorSim):
451
476
  '''VerilatorElab is a command handler for: eda elab --tool=verilator'''
452
477
 
@@ -63,29 +63,14 @@ class ToolVivado(Tool):
63
63
  util.info("environment for XILINX_VIVADO is not set or doesn't match the vivado path:",
64
64
  f"XILINX_VIVADO={xilinx_vivado} EXE PATH={self.vivado_exe}")
65
65
 
66
- version = None
67
- # Note this is commented out b/c it's a bit slow, up to 1.0 second to
68
- # run their tool just to query the version information.
69
- # Do this if you need the extra minor version like 2024.2.1.
70
- #try:
71
- # # Get version from vivado -version, or xsim --version:
72
- # vivado_ret = subprocess.run(['vivado', '-version'], capture_output=True)
73
- # lines = vivado_ret.stdout.decode('utf-8', errors='replace').split('\n')
74
- # words = lines[0].split() # vivado v2024.2.1 (64-bit)
75
- # version = words[1][1:] # 2024.2.1
76
- # self._VERSION = version
77
- #except:
78
- # pass
79
-
80
- if not version:
81
- # Get version based on install path name:
82
- util.debug(f"vivado path = {self.vivado_exe}")
83
- m = re.search(r'(\d\d\d\d)\.(\d)', self.vivado_exe)
84
- if m:
85
- version = m.group(1) + '.' + m.group(2)
86
- self._VERSION = version
87
- else:
88
- self.error("Vivado path doesn't specificy version, expecting (dddd.d)")
66
+ # Get version based on install path name. Calling vivado -verison is too slow.
67
+ util.debug(f"vivado path = {self.vivado_exe}")
68
+ m = re.search(r'(\d\d\d\d)\.(\d)', self.vivado_exe)
69
+ if m:
70
+ version = m.group(1) + '.' + m.group(2)
71
+ self._VERSION = version
72
+ else:
73
+ self.error("Vivado path doesn't specificy version, expecting (dddd.d)")
89
74
 
90
75
  if version:
91
76
  numbers_list = version.split('.')
@@ -260,6 +245,14 @@ class CommandSimVivado(CommandSim, ToolVivado):
260
245
  def get_simulate_command_lists(self, **kwargs) -> list:
261
246
  # create TCL
262
247
  tcl_name = os.path.abspath(os.path.join(self.args['work-dir'], self.args['tcl-file']))
248
+
249
+ if self.args['waves']:
250
+ util.artifacts.add_extension(
251
+ search_paths=self.args['work-dir'], file_extension='wdb',
252
+ typ='waveform', description='Vivado XSim Waveform WDB (Wave DataBase) file'
253
+ )
254
+
255
+
263
256
  with open( tcl_name, 'w', encoding='utf-8' ) as fo:
264
257
  if self.args['waves']:
265
258
  if self.args['waves-start']:
@@ -345,6 +338,23 @@ class CommandSimVivado(CommandSim, ToolVivado):
345
338
  self.files_v.insert(0, glbl_v)
346
339
 
347
340
 
341
+ def artifacts_add(self, name: str, typ: str, description: str) -> None:
342
+ '''Override from Command.artifacts_add, so we can catch known file
343
+
344
+ names to make their typ/description better, such as CommandSim using
345
+ sim.log or compile.log
346
+ '''
347
+ _, leafname = os.path.split(name)
348
+ if leafname == 'xsim.log':
349
+ description = 'Vivado XSim simulation step (3/3) log from stdout/stderr'
350
+ elif leafname == 'xelab.log':
351
+ description = 'Vivado XSim elaboration step (2/3) log from stdout/stderr'
352
+ elif leafname == 'xvlog.log':
353
+ description = 'Vivado XSim compile step (1/3) log from stdout/stderr'
354
+
355
+ super().artifacts_add(name=name, typ=typ, description=description)
356
+
357
+
348
358
  class CommandElabVivado(CommandSimVivado):
349
359
  '''CommandElabVivado is a command handler for: eda elab --tool=vivado, uses xvlog, xelab'''
350
360
  def __init__(self, config: dict):
@@ -933,9 +943,6 @@ class CommandUploadVivado(CommandUpload, ToolVivado):
933
943
  util.info("Upload done")
934
944
 
935
945
 
936
-
937
-
938
-
939
946
  class CommandOpenVivado(CommandOpen, ToolVivado):
940
947
  '''CommandOpenVivado command handler class used by: eda open --tool vivado'''
941
948
  def __init__(self, config: dict):
@@ -1,16 +1,19 @@
1
1
  '''opencos.util -- support global logging, argparser for printing (colors)'''
2
2
 
3
- import sys
4
- import subprocess
3
+ import argparse
4
+ import atexit
5
5
  import datetime
6
+ import json
6
7
  import os
7
- import time
8
- import atexit
8
+ import re
9
9
  import shutil
10
+ import subprocess
11
+ import sys
12
+ import time
10
13
  import traceback
11
- import argparse
12
- import re
13
14
 
15
+ from enum import Enum
16
+ from pathlib import Path
14
17
  from importlib import import_module
15
18
 
16
19
  global_exit_allowed = False # pylint: disable=invalid-name
@@ -26,6 +29,7 @@ args = { # pylint: disable=invalid-name
26
29
  'fancy' : sys.stdout.isatty(),
27
30
  'warnings' : 0,
28
31
  'errors' : 0,
32
+ 'artifacts-json': True,
29
33
  }
30
34
 
31
35
  class Colors:
@@ -70,6 +74,138 @@ def yellow_text(text: str) -> str:
70
74
  return Colors.yellow + text + Colors.normal
71
75
  return text
72
76
 
77
+ class ArtifactTypes(Enum):
78
+ '''Types that are allow-listed for artifacts.add* methods. If you don't use one of
79
+ these, you'll get a warning'''
80
+ TEXT = 0
81
+ JSON = 1
82
+ YAML = 2
83
+ WAVEFORM = 3
84
+ DOTF = 4
85
+ TCL = 5
86
+ SHELL = 6
87
+
88
+ class Artifacts:
89
+ '''Class to hold file artifacts, for logs generated by EDA, or other artifcats created
90
+ by a specific tool.
91
+
92
+ If enabled, will write out an artifacts.json file upon util.exit()
93
+ '''
94
+
95
+ data = {}
96
+ unresolved_data = {}
97
+ enabled = True
98
+ artifacts_json_filepath = os.path.join('eda.work', 'artifacts.json')
99
+
100
+ def _get_type_str(self, typ: str) -> (bool, str):
101
+ '''Gets the name from ArtifactTypes, and returns tuple of (True, str)
102
+
103
+ if the ArtifactTypes exists, else (False, str.upper())'''
104
+ ret = getattr(ArtifactTypes, typ.upper(), '')
105
+ if ret:
106
+ return True, ret.name.lower()
107
+ return False, typ.lower()
108
+
109
+ def add(self, name: str, typ: str, description: str = '') -> None:
110
+ '''Adds file to artifacts'''
111
+
112
+ if not self.enabled:
113
+ return # artifacts dumping disabled.
114
+
115
+ _, shortname = os.path.split(name)
116
+ if shortname in self.data:
117
+ return # artifact already registered
118
+
119
+ type_exists, type_str = self._get_type_str(typ)
120
+ if not type_exists:
121
+ warning(f'Unknown artifacts {typ=} add for {name=} {description=}')
122
+
123
+ self.data[shortname] = {
124
+ 'name': name,
125
+ 'type': type_str,
126
+ 'size': 0, # will be filled in when JSON written upon exit.
127
+ 'description': description,
128
+ }
129
+
130
+ def add_extension(
131
+ self, search_paths: list, file_extension: str, typ: str, description: str = ''
132
+ ) -> None:
133
+ '''Adds a placeholder for file extensions to add to artifacts
134
+
135
+ For example, when saving a Simulation waves, we may know it's a .fst or .vcd file,
136
+ but we do not know what the testbench SV file has set in $dumpfile(filepath)
137
+ '''
138
+ type_exists, type_str = self._get_type_str(typ)
139
+ if not type_exists:
140
+ warning(f'Unknown artifacts {typ=} add_extension for {search_paths=}',
141
+ f'{file_extension=} {description=}')
142
+
143
+ if isinstance(search_paths, str):
144
+ sp = [search_paths]
145
+ else:
146
+ sp = search_paths
147
+ self.unresolved_data[file_extension] = {
148
+ 'search_paths': sp,
149
+ 'type': type_str,
150
+ 'description': description
151
+ }
152
+
153
+ def set_artifacts_json_dir(self, dirpath: str) -> None:
154
+ '''Sets the artifacts_json_filepath'''
155
+ _, right = os.path.split(self.artifacts_json_filepath)
156
+ self.artifacts_json_filepath = os.path.join(dirpath, right)
157
+
158
+ def reset(self, enable: bool = True) -> None:
159
+ '''Clears internal data (dict). Called by util.process_tokens()'''
160
+ self.data = {}
161
+ self.unresolved_data = {}
162
+ self.enabled = enable or args['artifacts-json']
163
+ self.artifacts_json_filepath = os.path.join('eda.work', 'artifacts.json')
164
+
165
+ def _resolve_unresolved_data(self, ext: str) -> None:
166
+ '''Find files for this extension that were registered using add_extension(...)
167
+
168
+ adds to self.data'''
169
+ entry = self.unresolved_data[ext]
170
+ if not entry:
171
+ return
172
+
173
+ for search_path in entry['search_paths']:
174
+ p = Path(search_path)
175
+ for posix_filename in p.glob(f'*.{ext}'):
176
+ self.add(
177
+ name=str(posix_filename), typ=entry['type'], description=entry['description']
178
+ )
179
+
180
+ def write_json(self) -> None:
181
+ '''Write out the artifacts.json file, called by util.exit()'''
182
+
183
+ if not self.enabled:
184
+ return # artifacts dumping disabled.
185
+
186
+ if self.unresolved_data:
187
+ for ext in self.unresolved_data:
188
+ self._resolve_unresolved_data(ext)
189
+
190
+ if not self.data:
191
+ return # do nothing if we have no artifacts
192
+
193
+ # Update all file sizes:
194
+ for key, entry in self.data.items():
195
+ if os.path.isfile(entry['name']):
196
+ entry['size'] = os.path.getsize(entry['name'])
197
+ else:
198
+ # file doesn't exist, remove it from artifacts.
199
+ warning(f'Removing {key} ({entry["name"]}) from artifacts (file does not exist)')
200
+ del self.data[key]
201
+
202
+ with open(self.artifacts_json_filepath, 'w', encoding='utf-8') as f:
203
+ json.dump(self.data, f, indent=4)
204
+ info(f'Wrote artifacts JSON: {self.artifacts_json_filepath}')
205
+
206
+
207
+ artifacts = Artifacts()
208
+
73
209
 
74
210
  class UtilLogger:
75
211
  '''Class for the util.global_log'''
@@ -115,6 +251,8 @@ class UtilLogger:
115
251
  debug(f"Opened logfile '{filename}' for writing")
116
252
  self.filepath = filename
117
253
  self.write_timestamp(f'start - {self.filepath}')
254
+ # add to global artifacts:
255
+ artifacts.add(name=filename, typ='text', description='EDA stdout logfile')
118
256
  except Exception as e:
119
257
  error(f"Error opening '{filename}' for writing, {e}")
120
258
  self.clear()
@@ -213,6 +351,8 @@ def get_argparser() -> argparse.ArgumentParser:
213
351
  parser.add_argument('--no-respawn', action='store_true',
214
352
  help=('Legacy mode (default respawn disabled) for respawning eda.py'
215
353
  ' using $OC_ROOT/bin'))
354
+ parser.add_argument('--artifacts-json', **bool_action_kwargs,
355
+ help='Store a work-dir/artifacts.json file of known tool output files')
216
356
  return parser
217
357
 
218
358
 
@@ -265,21 +405,29 @@ def process_tokens(tokens:list) -> (argparse.Namespace, list):
265
405
 
266
406
  debug(f'util.process_tokens: {parsed=} {unparsed=} from {tokens=}')
267
407
 
408
+ # clear existing artifacts dicts (mostly for pytests repeatedly calling eda.main),
409
+ # set artifacts.enabled based on args['artifacts-json']
410
+ artifacts.reset(enable=parsed.artifacts_json)
411
+
268
412
  if parsed.force_logfile:
269
413
  start_log(parsed.force_logfile, force=True)
270
414
  elif parsed.logfile:
271
415
  start_log(parsed.logfile, force=False)
272
416
  elif parsed.default_log and \
417
+ not any(x in unparsed for x in ('help', '-h', '--help')) and \
273
418
  (parsed.force_logfile is None and parsed.logfile is None):
274
419
  # Use a forced logfile in the eda.work/eda.log:
420
+ # avoid this if someone has --help arg not yet parsed.
275
421
  start_log(global_log.default_log_filepath, force=True)
276
422
 
277
423
 
278
424
  parsed_as_dict = vars(parsed)
279
425
  for key,value in parsed_as_dict.items():
426
+ key = key.replace('_', '-')
280
427
  if value is not None:
281
428
  args[key] = value # only update with non-None values to our global 'args' dict
282
429
 
430
+
283
431
  return parsed, unparsed
284
432
 
285
433
  # ********************
@@ -500,6 +648,10 @@ def exit( # pylint: disable=redefined-builtin
500
648
  error_code: int = 0, quiet: bool = False
501
649
  ) -> int:
502
650
  '''sys.exit(int) wrapper, returns the error_code if global_exit_allowed=False'''
651
+
652
+ # Save out artifacts file:
653
+ artifacts.write_json()
654
+
503
655
  if global_exit_allowed:
504
656
  if not quiet:
505
657
  info(f"Exiting with {args['warnings']} warnings, {args['errors']} errors")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: opencos-eda
3
- Version: 0.2.49
3
+ Version: 0.2.50
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
@@ -71,6 +71,7 @@ opencos/tools/invio_yosys.py
71
71
  opencos/tools/iverilog.py
72
72
  opencos/tools/modelsim_ase.py
73
73
  opencos/tools/questa.py
74
+ opencos/tools/questa_fse.py
74
75
  opencos/tools/riviera.py
75
76
  opencos/tools/slang.py
76
77
  opencos/tools/slang_yosys.py
@@ -2,7 +2,7 @@
2
2
 
3
3
  [project]
4
4
  name = "opencos-eda"
5
- version = "0.2.49"
5
+ version = "0.2.50"
6
6
  dependencies = [
7
7
  # opencos/eda.py dependencies
8
8
  "mergedeep >= 1.3.4",
File without changes
File without changes
File without changes
File without changes