opencos-eda 0.2.53__tar.gz → 0.2.54__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 (101) hide show
  1. {opencos_eda-0.2.53/opencos_eda.egg-info → opencos_eda-0.2.54}/PKG-INFO +1 -1
  2. {opencos_eda-0.2.53 → opencos_eda-0.2.54}/opencos/commands/flist.py +1 -1
  3. {opencos_eda-0.2.53 → opencos_eda-0.2.54}/opencos/commands/sim.py +44 -0
  4. {opencos_eda-0.2.53 → opencos_eda-0.2.54}/opencos/eda_base.py +19 -14
  5. {opencos_eda-0.2.53 → opencos_eda-0.2.54}/opencos/tests/test_eda.py +1 -1
  6. {opencos_eda-0.2.53 → opencos_eda-0.2.54}/opencos/tools/cocotb.py +7 -15
  7. {opencos_eda-0.2.53 → opencos_eda-0.2.54}/opencos/tools/iverilog.py +4 -24
  8. {opencos_eda-0.2.53 → opencos_eda-0.2.54}/opencos/tools/modelsim_ase.py +3 -6
  9. {opencos_eda-0.2.53 → opencos_eda-0.2.54}/opencos/tools/quartus.py +124 -82
  10. {opencos_eda-0.2.53 → opencos_eda-0.2.54}/opencos/tools/questa.py +5 -16
  11. {opencos_eda-0.2.53 → opencos_eda-0.2.54}/opencos/tools/verilator.py +9 -15
  12. {opencos_eda-0.2.53 → opencos_eda-0.2.54}/opencos/tools/vivado.py +27 -20
  13. {opencos_eda-0.2.53 → opencos_eda-0.2.54}/opencos/util.py +70 -13
  14. {opencos_eda-0.2.53 → opencos_eda-0.2.54}/opencos/utils/status_constants.py +1 -0
  15. {opencos_eda-0.2.53 → opencos_eda-0.2.54/opencos_eda.egg-info}/PKG-INFO +1 -1
  16. {opencos_eda-0.2.53 → opencos_eda-0.2.54}/pyproject.toml +1 -1
  17. {opencos_eda-0.2.53 → opencos_eda-0.2.54}/LICENSE +0 -0
  18. {opencos_eda-0.2.53 → opencos_eda-0.2.54}/LICENSE.spdx +0 -0
  19. {opencos_eda-0.2.53 → opencos_eda-0.2.54}/README.md +0 -0
  20. {opencos_eda-0.2.53 → opencos_eda-0.2.54}/opencos/__init__.py +0 -0
  21. {opencos_eda-0.2.53 → opencos_eda-0.2.54}/opencos/_version.py +0 -0
  22. {opencos_eda-0.2.53 → opencos_eda-0.2.54}/opencos/_waves_pkg.sv +0 -0
  23. {opencos_eda-0.2.53 → opencos_eda-0.2.54}/opencos/commands/__init__.py +0 -0
  24. {opencos_eda-0.2.53 → opencos_eda-0.2.54}/opencos/commands/build.py +0 -0
  25. {opencos_eda-0.2.53 → opencos_eda-0.2.54}/opencos/commands/deps_help.py +0 -0
  26. {opencos_eda-0.2.53 → opencos_eda-0.2.54}/opencos/commands/elab.py +0 -0
  27. {opencos_eda-0.2.53 → opencos_eda-0.2.54}/opencos/commands/export.py +0 -0
  28. {opencos_eda-0.2.53 → opencos_eda-0.2.54}/opencos/commands/lec.py +0 -0
  29. {opencos_eda-0.2.53 → opencos_eda-0.2.54}/opencos/commands/multi.py +0 -0
  30. {opencos_eda-0.2.53 → opencos_eda-0.2.54}/opencos/commands/open.py +0 -0
  31. {opencos_eda-0.2.53 → opencos_eda-0.2.54}/opencos/commands/proj.py +0 -0
  32. {opencos_eda-0.2.53 → opencos_eda-0.2.54}/opencos/commands/shell.py +0 -0
  33. {opencos_eda-0.2.53 → opencos_eda-0.2.54}/opencos/commands/sweep.py +0 -0
  34. {opencos_eda-0.2.53 → opencos_eda-0.2.54}/opencos/commands/synth.py +0 -0
  35. {opencos_eda-0.2.53 → opencos_eda-0.2.54}/opencos/commands/targets.py +0 -0
  36. {opencos_eda-0.2.53 → opencos_eda-0.2.54}/opencos/commands/upload.py +0 -0
  37. {opencos_eda-0.2.53 → opencos_eda-0.2.54}/opencos/commands/waves.py +0 -0
  38. {opencos_eda-0.2.53 → opencos_eda-0.2.54}/opencos/deps/__init__.py +0 -0
  39. {opencos_eda-0.2.53 → opencos_eda-0.2.54}/opencos/deps/defaults.py +0 -0
  40. {opencos_eda-0.2.53 → opencos_eda-0.2.54}/opencos/deps/deps_commands.py +0 -0
  41. {opencos_eda-0.2.53 → opencos_eda-0.2.54}/opencos/deps/deps_file.py +0 -0
  42. {opencos_eda-0.2.53 → opencos_eda-0.2.54}/opencos/deps/deps_processor.py +0 -0
  43. {opencos_eda-0.2.53 → opencos_eda-0.2.54}/opencos/deps_schema.py +0 -0
  44. {opencos_eda-0.2.53 → opencos_eda-0.2.54}/opencos/eda.py +0 -0
  45. {opencos_eda-0.2.53 → opencos_eda-0.2.54}/opencos/eda_config.py +0 -0
  46. {opencos_eda-0.2.53 → opencos_eda-0.2.54}/opencos/eda_config_defaults.yml +0 -0
  47. {opencos_eda-0.2.53 → opencos_eda-0.2.54}/opencos/eda_config_max_verilator_waivers.yml +0 -0
  48. {opencos_eda-0.2.53 → opencos_eda-0.2.54}/opencos/eda_config_reduced.yml +0 -0
  49. {opencos_eda-0.2.53 → opencos_eda-0.2.54}/opencos/eda_deps_bash_completion.bash +0 -0
  50. {opencos_eda-0.2.53 → opencos_eda-0.2.54}/opencos/eda_deps_sanitize.py +0 -0
  51. {opencos_eda-0.2.53 → opencos_eda-0.2.54}/opencos/eda_extract_targets.py +0 -0
  52. {opencos_eda-0.2.53 → opencos_eda-0.2.54}/opencos/eda_tool_helper.py +0 -0
  53. {opencos_eda-0.2.53 → opencos_eda-0.2.54}/opencos/export_helper.py +0 -0
  54. {opencos_eda-0.2.53 → opencos_eda-0.2.54}/opencos/export_json_convert.py +0 -0
  55. {opencos_eda-0.2.53 → opencos_eda-0.2.54}/opencos/files.py +0 -0
  56. {opencos_eda-0.2.53 → opencos_eda-0.2.54}/opencos/hw/__init__.py +0 -0
  57. {opencos_eda-0.2.53 → opencos_eda-0.2.54}/opencos/hw/oc_cli.py +0 -0
  58. {opencos_eda-0.2.53 → opencos_eda-0.2.54}/opencos/hw/pcie.py +0 -0
  59. {opencos_eda-0.2.53 → opencos_eda-0.2.54}/opencos/names.py +0 -0
  60. {opencos_eda-0.2.53 → opencos_eda-0.2.54}/opencos/peakrdl_cleanup.py +0 -0
  61. {opencos_eda-0.2.53 → opencos_eda-0.2.54}/opencos/seed.py +0 -0
  62. {opencos_eda-0.2.53 → opencos_eda-0.2.54}/opencos/tests/__init__.py +0 -0
  63. {opencos_eda-0.2.53 → opencos_eda-0.2.54}/opencos/tests/custom_config.yml +0 -0
  64. {opencos_eda-0.2.53 → opencos_eda-0.2.54}/opencos/tests/deps_files/command_order/DEPS.yml +0 -0
  65. {opencos_eda-0.2.53 → opencos_eda-0.2.54}/opencos/tests/deps_files/error_msgs/DEPS.yml +0 -0
  66. {opencos_eda-0.2.53 → opencos_eda-0.2.54}/opencos/tests/deps_files/iverilog_test/DEPS.yml +0 -0
  67. {opencos_eda-0.2.53 → opencos_eda-0.2.54}/opencos/tests/deps_files/no_deps_here/DEPS.yml +0 -0
  68. {opencos_eda-0.2.53 → opencos_eda-0.2.54}/opencos/tests/deps_files/non_sv_reqs/DEPS.yml +0 -0
  69. {opencos_eda-0.2.53 → opencos_eda-0.2.54}/opencos/tests/deps_files/tags_with_tools/DEPS.yml +0 -0
  70. {opencos_eda-0.2.53 → opencos_eda-0.2.54}/opencos/tests/deps_files/test_err_fatal/DEPS.yml +0 -0
  71. {opencos_eda-0.2.53 → opencos_eda-0.2.54}/opencos/tests/helpers.py +0 -0
  72. {opencos_eda-0.2.53 → opencos_eda-0.2.54}/opencos/tests/test_build.py +0 -0
  73. {opencos_eda-0.2.53 → opencos_eda-0.2.54}/opencos/tests/test_deps_helpers.py +0 -0
  74. {opencos_eda-0.2.53 → opencos_eda-0.2.54}/opencos/tests/test_deps_schema.py +0 -0
  75. {opencos_eda-0.2.53 → opencos_eda-0.2.54}/opencos/tests/test_eda_elab.py +0 -0
  76. {opencos_eda-0.2.53 → opencos_eda-0.2.54}/opencos/tests/test_eda_synth.py +0 -0
  77. {opencos_eda-0.2.53 → opencos_eda-0.2.54}/opencos/tests/test_oc_cli.py +0 -0
  78. {opencos_eda-0.2.53 → opencos_eda-0.2.54}/opencos/tests/test_tools.py +0 -0
  79. {opencos_eda-0.2.53 → opencos_eda-0.2.54}/opencos/tools/__init__.py +0 -0
  80. {opencos_eda-0.2.53 → opencos_eda-0.2.54}/opencos/tools/invio.py +0 -0
  81. {opencos_eda-0.2.53 → opencos_eda-0.2.54}/opencos/tools/invio_helpers.py +0 -0
  82. {opencos_eda-0.2.53 → opencos_eda-0.2.54}/opencos/tools/invio_yosys.py +0 -0
  83. {opencos_eda-0.2.53 → opencos_eda-0.2.54}/opencos/tools/questa_fse.py +0 -0
  84. {opencos_eda-0.2.53 → opencos_eda-0.2.54}/opencos/tools/riviera.py +0 -0
  85. {opencos_eda-0.2.53 → opencos_eda-0.2.54}/opencos/tools/slang.py +0 -0
  86. {opencos_eda-0.2.53 → opencos_eda-0.2.54}/opencos/tools/slang_yosys.py +0 -0
  87. {opencos_eda-0.2.53 → opencos_eda-0.2.54}/opencos/tools/surelog.py +0 -0
  88. {opencos_eda-0.2.53 → opencos_eda-0.2.54}/opencos/tools/tabbycad_yosys.py +0 -0
  89. {opencos_eda-0.2.53 → opencos_eda-0.2.54}/opencos/tools/yosys.py +0 -0
  90. {opencos_eda-0.2.53 → opencos_eda-0.2.54}/opencos/utils/__init__.py +0 -0
  91. {opencos_eda-0.2.53 → opencos_eda-0.2.54}/opencos/utils/markup_helpers.py +0 -0
  92. {opencos_eda-0.2.53 → opencos_eda-0.2.54}/opencos/utils/str_helpers.py +0 -0
  93. {opencos_eda-0.2.53 → opencos_eda-0.2.54}/opencos/utils/subprocess_helpers.py +0 -0
  94. {opencos_eda-0.2.53 → opencos_eda-0.2.54}/opencos/utils/vscode_helper.py +0 -0
  95. {opencos_eda-0.2.53 → opencos_eda-0.2.54}/opencos/utils/vsim_helper.py +0 -0
  96. {opencos_eda-0.2.53 → opencos_eda-0.2.54}/opencos_eda.egg-info/SOURCES.txt +0 -0
  97. {opencos_eda-0.2.53 → opencos_eda-0.2.54}/opencos_eda.egg-info/dependency_links.txt +0 -0
  98. {opencos_eda-0.2.53 → opencos_eda-0.2.54}/opencos_eda.egg-info/entry_points.txt +0 -0
  99. {opencos_eda-0.2.53 → opencos_eda-0.2.54}/opencos_eda.egg-info/requires.txt +0 -0
  100. {opencos_eda-0.2.53 → opencos_eda-0.2.54}/opencos_eda.egg-info/top_level.txt +0 -0
  101. {opencos_eda-0.2.53 → opencos_eda-0.2.54}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: opencos-eda
3
- Version: 0.2.53
3
+ Version: 0.2.54
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
@@ -214,6 +214,6 @@ class CommandFList(CommandDesign):
214
214
  print() # don't need to close fo (None)
215
215
  else:
216
216
  fo.close()
217
- util.info(f"Created {self.args['out']}")
217
+ util.info(f"Created file: {self.args['out']}")
218
218
 
219
219
  self.write_eda_config_and_args()
@@ -355,6 +355,50 @@ class CommandSim(CommandDesign):
355
355
  error_code=status_constants.EDA_SIM_LOG_MISSING_MUST_STRING
356
356
  )
357
357
 
358
+ def write_sh_scripts_to_work_dir(
359
+ self, compile_lists: list, elaborate_lists: list, simulate_lists: list,
360
+ compile_line_breaks: bool = True,
361
+ elaborate_line_breaks: bool = False,
362
+ simulate_line_breaks: bool = False,
363
+ simulate_sh_fname: str = 'simulate.sh'
364
+ ) -> None:
365
+ '''Writes compile.sh, elaborate.sh, simulate.sh (if present), all.sh to work-dir
366
+
367
+ Will include the pre_compile_dep_shell_commands.sh if those are present.
368
+ compile_line_breaks defaults to True (one word per line w/ line breaks added)
369
+ '''
370
+
371
+ all_lists = [] # list - of - (command-list)
372
+ if self.has_dep_shell_commands:
373
+ all_lists = [
374
+ ['./pre_compile_dep_shell_commands.sh']
375
+ ]
376
+
377
+ if compile_lists:
378
+ util.write_shell_command_file(dirpath=self.args['work-dir'], filename='compile.sh',
379
+ command_lists=compile_lists,
380
+ line_breaks=compile_line_breaks)
381
+ all_lists.append(['./compile.sh'])
382
+
383
+ if elaborate_lists:
384
+ util.write_shell_command_file(dirpath=self.args['work-dir'], filename='elaborate.sh',
385
+ command_lists=elaborate_lists,
386
+ line_breaks=elaborate_line_breaks)
387
+ all_lists.append(['./elaborate.sh'])
388
+
389
+ if simulate_lists:
390
+ util.write_shell_command_file(dirpath=self.args['work-dir'], filename=simulate_sh_fname,
391
+ command_lists=simulate_lists,
392
+ line_breaks=simulate_line_breaks)
393
+ all_lists.append(['./' + simulate_sh_fname])
394
+
395
+ util.write_shell_command_file(dirpath=self.args['work-dir'], filename='all.sh',
396
+ command_lists=all_lists)
397
+
398
+ self.write_eda_config_and_args()
399
+
400
+
401
+
358
402
  # Methods that derived classes must override:
359
403
 
360
404
  def compile(self) -> None:
@@ -252,7 +252,7 @@ class Command: # pylint: disable=too-many-public-methods
252
252
  })
253
253
  self.modified_args = {}
254
254
  self.config = copy.deepcopy(config) # avoid external modifications.
255
- self.target = ""
255
+ self.target = "" # is set as the 'top' or final target short-name (no path info)
256
256
  self.target_path = ""
257
257
  self.status = 0
258
258
  self.errors_log_f = None
@@ -943,6 +943,8 @@ class CommandDesign(Command): # pylint: disable=too-many-instance-attributes
943
943
  self.targets_dict = {} # key = targets that we've already processed in DEPS files
944
944
  self.last_added_source_file_inferred_top = ''
945
945
 
946
+ self.has_dep_shell_commands = False
947
+
946
948
 
947
949
  def run_dep_commands(self) -> None:
948
950
  '''Run shell/peakrdl style commands from DEPS files
@@ -993,10 +995,12 @@ class CommandDesign(Command): # pylint: disable=too-many-instance-attributes
993
995
 
994
996
  d['exec_list'] = clist # update to tee_fpath is set.
995
997
 
996
- util.write_shell_command_file(
997
- dirpath=self.args['work-dir'], filename='pre_compile_dep_shell_commands.sh',
998
- command_lists=all_cmds_lists
999
- )
998
+ if all_cmds_lists:
999
+ util.write_shell_command_file(
1000
+ dirpath=self.args['work-dir'], filename='pre_compile_dep_shell_commands.sh',
1001
+ command_lists=all_cmds_lists
1002
+ )
1003
+ self.has_dep_shell_commands = True
1000
1004
 
1001
1005
  for i, d in enumerate(self.dep_shell_commands):
1002
1006
  util.info(f'run_dep_shell_commands {i=}: {d=}')
@@ -1556,11 +1560,12 @@ class CommandDesign(Command): # pylint: disable=too-many-instance-attributes
1556
1560
 
1557
1561
  # handle a missing self.args['top'] with last filepath or last target:
1558
1562
  if not self.args.get('top', ''):
1563
+ top_path = ''
1559
1564
  if not last_potential_top_isfile and last_potential_top_target[0]:
1560
1565
  # If we have a target name from DEPS, prefer to use that.
1561
- self.args['top'], self.args['top-path'] = last_potential_top_target
1566
+ self.args['top'], top_path = last_potential_top_target
1562
1567
  util.info("--top not specified, inferred from target:",
1563
- f"{self.args['top']} ({self.args['top-path']})")
1568
+ f"{self.args['top']} ({top_path})")
1564
1569
 
1565
1570
  else:
1566
1571
  best_top_fname = self.last_added_source_file_inferred_top
@@ -1570,26 +1575,29 @@ class CommandDesign(Command): # pylint: disable=too-many-instance-attributes
1570
1575
  if not self.args['top'] and last_potential_top_file[0]:
1571
1576
  # If we don't have a target name, and no top name yet, then go looking for the
1572
1577
  # module name in the final source file added.
1573
- self.args['top-path'] = last_potential_top_file[1] # from tuple: (top, fpath)
1578
+ top_path = last_potential_top_file[1] # from tuple: (top, fpath)
1574
1579
  self.args['top'] = util.get_inferred_top_module_name(
1575
1580
  module_guess=last_potential_top_file[0],
1576
1581
  module_fpath=last_potential_top_file[1]
1577
1582
  )
1578
1583
  if self.args['top']:
1579
1584
  util.info("--top not specified, inferred from final source file:",
1580
- f"{self.args['top']} ({self.args['top-path']})")
1585
+ f"{self.args['top']} ({top_path})")
1581
1586
  # If top wasn't set, and we're using the final command-line 'arg' filename
1582
1587
  # (not from DEPS.yml) we need to override self.target if that was set. Otherwise
1583
1588
  # it won't save to the correct work-dir:
1584
1589
  self.target = self.args['top']
1585
1590
 
1591
+
1592
+ util.info(f'{self.command_name}: top-most target name: {self.target}')
1593
+
1586
1594
  if self.error_on_missing_top and not self.args.get('top', ''):
1587
1595
  self.error("Did not get a --top or DEPS top, required to run command",
1588
1596
  f"'{self.command_name}' for tool={self.args.get('tool', None)}",
1589
1597
  error_code=status_constants.EDA_COMMAND_MISSING_TOP)
1590
1598
 
1591
1599
  if self.tool_changed_respawn:
1592
- util.warning(
1600
+ util.info(
1593
1601
  'CommandDesign: need to respawn due to tool change to',
1594
1602
  f'\'{self.tool_changed_respawn["tool"]}\' from',
1595
1603
  f'\'{self.tool_changed_respawn["orig_tool"]}\'',
@@ -1615,7 +1623,7 @@ class CommandDesign(Command): # pylint: disable=too-many-instance-attributes
1615
1623
  for k,v in self.args.items():
1616
1624
 
1617
1625
  # Some args cannot be extracted and work, so omit these:
1618
- if k in ['top-path'] + remove_args:
1626
+ if k in remove_args:
1619
1627
  continue
1620
1628
  if any(k.startswith(x) for x in remove_args_startswith):
1621
1629
  continue
@@ -1637,9 +1645,6 @@ class CommandDesign(Command): # pylint: disable=too-many-instance-attributes
1637
1645
  return ret
1638
1646
 
1639
1647
 
1640
- #_THREADS_START = 0
1641
- #_THREADS_DONE = 0
1642
-
1643
1648
  class ThreadStats:
1644
1649
  '''To avoid globals for two ints, keep a class holder so CommandParallel and
1645
1650
  CommandParallelWorker can share values'''
@@ -170,7 +170,7 @@ class TestsRequiresVerilator( # pylint: disable=too-many-public-methods
170
170
  assert res.stderr == b''
171
171
 
172
172
  res = subprocess.run(
173
- [ './simulate_only.sh' ], stdout=subprocess.PIPE, stderr=subprocess.PIPE,
173
+ [ './simulate.sh' ], stdout=subprocess.PIPE, stderr=subprocess.PIPE,
174
174
  check=True
175
175
  )
176
176
  rc = res.returncode
@@ -122,20 +122,12 @@ class CommandSimCocotb(CommandSim, ToolCocotb):
122
122
  util.safe_mkdirs(base=self.args['work-dir'], new_dirs=paths)
123
123
 
124
124
  # Write shell scripts
125
- util.write_shell_command_file(
126
- dirpath=self.args['work-dir'],
127
- filename='cocotb_test.sh',
128
- command_lists=self.cocotb_command_lists,
129
- line_breaks=True
130
- )
131
-
132
- util.write_shell_command_file(
133
- dirpath=self.args['work-dir'],
134
- filename='all.sh',
135
- command_lists=[
136
- ['./pre_compile_dep_shell_commands.sh'],
137
- ['./cocotb_test.sh'],
138
- ]
125
+ self.write_sh_scripts_to_work_dir(
126
+ compile_lists=[],
127
+ elaborate_lists=[],
128
+ simulate_lists=self.cocotb_command_lists,
129
+ simulate_line_breaks=True,
130
+ simulate_sh_fname='cocotb_test.sh'
139
131
  )
140
132
 
141
133
  def _find_cocotb_test_files(self):
@@ -317,7 +309,7 @@ def run_cocotb_test():
317
309
  runner = get_runner(simulator)
318
310
 
319
311
  build_args = []
320
-
312
+
321
313
  if simulator == "verilator":
322
314
  build_args.extend({list(self.args.get('verilate-args', []))!r})
323
315
 
@@ -84,32 +84,12 @@ class CommandSimIverilog(CommandSim, ToolIverilog):
84
84
 
85
85
  paths = ['logs']
86
86
  util.safe_mkdirs(base=self.args['work-dir'], new_dirs=paths)
87
-
88
- util.write_shell_command_file(
89
- dirpath=self.args['work-dir'],
90
- filename='compile_only.sh',
91
- command_lists=self.iverilog_command_lists,
92
- line_breaks=True
93
- )
94
-
95
- util.write_shell_command_file(
96
- dirpath=self.args['work-dir'],
97
- filename='simulate_only.sh',
98
- command_lists=self.iverilog_exec_command_lists
87
+ self.write_sh_scripts_to_work_dir(
88
+ compile_lists=self.iverilog_command_lists,
89
+ elaborate_lists=[],
90
+ simulate_lists=self.iverilog_exec_command_lists
99
91
  )
100
92
 
101
- util.write_shell_command_file(
102
- dirpath=self.args['work-dir'],
103
- filename='all.sh',
104
- command_lists = [
105
- ['./pre_compile_dep_shell_commands.sh'],
106
- ['./compile_only.sh'],
107
- ['./simulate_only.sh'],
108
- ]
109
- )
110
-
111
- self.write_eda_config_and_args()
112
-
113
93
  def compile(self):
114
94
  if self.args['stop-before-compile']:
115
95
  return
@@ -82,15 +82,12 @@ class CommandSimModelsimAse(CommandSim, ToolModelsimAse):
82
82
  command_lists=vsim_command_lists
83
83
  )
84
84
 
85
+ # Write simulate.sh and all.sh to work-dir:
85
86
  vsim_command_lists = self.get_simulate_command_lists()
86
- util.write_shell_command_file(
87
- dirpath=self.args['work-dir'],
88
- filename='all.sh',
89
- command_lists = [['./pre_compile_dep_shell_commands.sh']] + vsim_command_lists
87
+ self.write_sh_scripts_to_work_dir(
88
+ compile_lists=[], elaborate_lists=[], simulate_lists=vsim_command_lists
90
89
  )
91
90
 
92
- self.write_eda_config_and_args()
93
-
94
91
  def compile(self):
95
92
  if self.args['stop-before-compile']:
96
93
  # don't run anything, save everyting we've already run in _prep_compile()
@@ -30,12 +30,13 @@ class ToolQuartus(Tool):
30
30
  quartus_release = None
31
31
  quartus_base_path = ''
32
32
  quartus_exe = ''
33
+ quartus_gui_exe = ''
33
34
 
34
35
  def __init__(self, config: dict):
35
36
  super().__init__(config=config)
36
37
  self.args.update({
37
- 'part': 'EP4SGX230KF40C2',
38
- 'family': 'Stratix IV',
38
+ 'part': 'A3CY135BM16AE6S',
39
+ 'family': 'Agilex 3',
39
40
  })
40
41
  self.args_help.update({
41
42
  'part': 'Device used for commands: synth, build.',
@@ -53,6 +54,7 @@ class ToolQuartus(Tool):
53
54
  else:
54
55
  self.quartus_exe = path
55
56
  self.quartus_base_path, _ = os.path.split(path)
57
+ self.quartus_gui_exe = shutil.which('quartus') # vs quartus_sh
56
58
 
57
59
 
58
60
 
@@ -157,15 +159,15 @@ class CommandSynthQuartus(CommandSynth, ToolQuartus):
157
159
  # Add artifact tracking
158
160
  util.artifacts.add_extension(
159
161
  search_paths=self.args['work-dir'], file_extension='qpf',
160
- typ='project', description='Quartus Project File'
162
+ typ='tcl', description='Quartus Project File'
161
163
  )
162
164
  util.artifacts.add_extension(
163
165
  search_paths=self.args['work-dir'], file_extension='qsf',
164
- typ='project', description='Quartus Settings File'
166
+ typ='tcl', description='Quartus Settings File'
165
167
  )
166
168
  util.artifacts.add_extension(
167
169
  search_paths=self.args['work-dir'], file_extension='rpt',
168
- typ='report', description='Quartus Synthesis Report'
170
+ typ='text', description='Quartus Synthesis Report'
169
171
  )
170
172
 
171
173
  self.exec(self.args['work-dir'], command_list)
@@ -187,27 +189,9 @@ class CommandSynthQuartus(CommandSynth, ToolQuartus):
187
189
  f"set_global_assignment -name TOP_LEVEL_ENTITY {top}",
188
190
  ]
189
191
 
190
- # Create a symbolic link or copy the include file to resolve lib/ paths
191
- for incdir in self.incdirs:
192
- if 'lib' in incdir:
193
- lib_defines = os.path.join(incdir, 'oclib_defines.vh')
194
- if os.path.exists(lib_defines):
195
- # Copy the include file to the work directory as lib/oclib_defines.vh
196
- work_lib_dir = os.path.join(self.args['work-dir'], 'lib')
197
- os.makedirs(work_lib_dir, exist_ok=True)
198
- work_lib_defines = os.path.join(work_lib_dir, 'oclib_defines.vh')
199
-
200
- shutil.copy2(lib_defines, work_lib_defines)
201
-
202
- # Add the copied file as a source
203
- rel_path = os.path.relpath(
204
- work_lib_defines, self.args['work-dir']
205
- ).replace('\\', '/')
206
- tcl_lines.append(
207
- f"set_global_assignment -name VERILOG_FILE \"{rel_path}\""
208
- )
209
- break
210
192
  # Add source files (convert to relative paths and use forward slashes)
193
+ # Note that default of self.args['all-sv'] is False so we should have added
194
+ # all files to self.files_sv instead of files_v:
211
195
  for f in self.files_v:
212
196
  rel_path = os.path.relpath(f, self.args['work-dir']).replace('\\', '/')
213
197
  tcl_lines.append(f"set_global_assignment -name VERILOG_FILE \"{rel_path}\"")
@@ -219,25 +203,8 @@ class CommandSynthQuartus(CommandSynth, ToolQuartus):
219
203
  tcl_lines.append(f"set_global_assignment -name VHDL_FILE \"{rel_path}\"")
220
204
 
221
205
  # Add include directories - Quartus needs the base directory where "lib/" can be found
222
- project_root = None
223
206
  for incdir in self.incdirs:
224
207
  tcl_lines.append(f"set_global_assignment -name SEARCH_PATH \"{incdir}\"")
225
- # Find the project root (where lib/ directory exists)
226
- if 'lib' in incdir and os.path.exists(incdir):
227
- parent_dir = os.path.dirname(incdir)
228
- if os.path.exists(os.path.join(parent_dir, 'lib')):
229
- project_root = parent_dir
230
-
231
- # Set the project root as the main search path so "lib/oclib_defines.vh" resolves correctly
232
- if project_root:
233
- tcl_lines.append(
234
- f"set_global_assignment -name SEARCH_PATH \"{project_root}\""
235
- )
236
- # Add the lib directory as an include path
237
- lib_path = os.path.join(project_root, 'lib').replace('\\', '/')
238
- tcl_lines.append(
239
- f"set_global_assignment -name USER_LIBRARIES \"{lib_path}\""
240
- )
241
208
 
242
209
  # Add all include directories as user libraries for better include resolution
243
210
  for incdir in self.incdirs:
@@ -262,9 +229,8 @@ class CommandSynthQuartus(CommandSynth, ToolQuartus):
262
229
 
263
230
  tcl_lines += [
264
231
  "# Run synthesis",
265
- "execute_flow -analysis_and_elaboration",
266
- "execute_flow -compile",
267
- "project_close"
232
+ 'flng::run_flow_command -flow "compile" -end "dni_synthesis"',
233
+ 'flng::run_flow_command -flow "compile" -end "sta_early" -resume',
268
234
  ]
269
235
 
270
236
  with open(tcl_file, 'w', encoding='utf-8') as ftcl:
@@ -283,22 +249,24 @@ class CommandBuildQuartus(CommandBuild, ToolQuartus):
283
249
  'proj': False,
284
250
  'resynth': False,
285
251
  'reset': False,
252
+ 'add-tcl-files': [],
253
+ 'flow-tcl-files': [],
286
254
  })
287
255
 
288
- def do_it(self):
256
+ def do_it(self) -> None: # pylint: disable=too-many-branches,too-many-statements
289
257
  # add defines for this job
290
258
  self.set_tool_defines()
291
259
  self.write_eda_config_and_args()
292
260
 
293
261
  # create FLIST
294
- flist_file = os.path.join(self.args['work-dir'],'build.flist')
295
- util.debug(f"CommandBuildQuartus: {self.args['top-path']=}")
262
+ flist_file = os.path.abspath(os.path.join(self.args['work-dir'], 'build.flist'))
263
+ util.debug(f"CommandBuildQuartus: top={self.args['top']} target={self.target}",
264
+ f"design={self.args['design']}")
296
265
 
297
- eda_path = eda_base.get_eda_exec('flist')
298
266
  command_list = [
299
- eda_path, 'flist',
267
+ eda_base.get_eda_exec('flist'), 'flist',
268
+ '--no-default-log',
300
269
  '--tool=' + self.args['tool'],
301
- self.args['top-path'],
302
270
  '--force',
303
271
  '--out=' + flist_file,
304
272
  '--no-quote-define',
@@ -314,46 +282,85 @@ class CommandBuildQuartus(CommandBuild, ToolQuartus):
314
282
  '--prefix-vhd=' + shlex.quote("set_global_assignment -name VHDL_FILE "),
315
283
  '--emit-rel-path', # Use relative paths for better portability
316
284
  ]
285
+
286
+ # create an eda.flist_input.f that we'll pass to flist:
287
+ with open(os.path.join(self.args['work-dir'], 'eda.flist_input.f'),
288
+ 'w', encoding='utf-8') as f:
289
+ f.write('\n'.join(self.files_v + self.files_sv + self.files_vhd + ['']))
290
+ command_list.append('--input-file=eda.flist_input.f')
291
+
292
+
317
293
  for key,value in self.defines.items():
318
294
  if value is None:
319
295
  command_list += [ f"+define+{key}" ]
320
296
  else:
321
297
  command_list += [ shlex.quote(f"+define+{key}={value}") ]
322
298
 
323
- cwd = util.getcwd()
324
- util.debug(f"CommandBuildQuartus: {cwd=}")
325
-
326
299
  # Write out a .sh command for debug
327
300
  command_list = util.ShellCommandList(command_list, tee_fpath='run_eda_flist.log')
328
301
  util.write_shell_command_file(dirpath=self.args['work-dir'], filename='run_eda_flist.sh',
329
302
  command_lists=[command_list], line_breaks=True)
330
303
 
331
- self.exec(cwd, command_list, tee_fpath=command_list.tee_fpath)
304
+ self.exec(work_dir=self.args['work-dir'], command_list=command_list,
305
+ tee_fpath=command_list.tee_fpath)
332
306
 
333
307
  if self.args['job-name'] == "":
334
308
  self.args['job-name'] = self.args['design']
335
- project_dir = 'project.'+self.args['job-name']
309
+ project_dir = 'project.' + self.args['job-name']
336
310
 
337
311
  # Create a simple Quartus build TCL script
338
- build_tcl_file = os.path.join(self.args['work-dir'], 'build.tcl')
312
+ build_tcl_file = os.path.abspath(os.path.join(self.args['work-dir'], 'build.tcl'))
313
+ build_tcl_lines = [
314
+ '# Quartus Build Script',
315
+ '',
316
+ f'set Top {self.args["top"]}'
317
+ '',
318
+ 'load_package flow',
319
+ f'project_new {self.args["design"]} -overwrite',
320
+ f'set_global_assignment -name FAMILY \"{self.args["family"]}\"',
321
+ f'set_global_assignment -name DEVICE {self.args["part"]}',
322
+ 'set_global_assignment -name TOP_LEVEL_ENTITY "$Top"',
323
+ '',
324
+ '# Source the flist file',
325
+ 'source build.flist',
326
+ '',
327
+ ]
328
+
329
+ # If we have additinal TCL files via --add-tcl-files, then source those too:
330
+ if self.args['add-tcl-files']:
331
+ build_tcl_lines.append('')
332
+ build_tcl_lines.append('# Source TCL files from --add-tcl-files args')
333
+ for fname in self.args['add-tcl-files']:
334
+ fname_abs = os.path.abspath(fname)
335
+ if not os.path.isfile(fname_abs):
336
+ self.error(f'add-tcl-files: "{fname_abs}"; does not exist')
337
+ build_tcl_lines.append(f'source {fname_abs}')
338
+ build_tcl_lines.append('')
339
+
340
+ # If we don't have any args for --flow-tcl-files, then use a default flow:
341
+ if not self.args['flow-tcl-files']:
342
+ build_tcl_lines.extend([
343
+ '# Default flow for compile',
344
+ 'flng::run_flow_command -flow "compile"',
345
+ ''
346
+ ])
347
+ else:
348
+ build_tcl_lines.append('')
349
+ build_tcl_lines.append('# Flow TCL files from --flow-tcl-files args')
350
+ for fname in self.args['flow-tcl-files']:
351
+ fname_abs = os.path.abspath(fname)
352
+ if not os.path.isfile(fname_abs):
353
+ self.error(f'flow-tcl-files: "{fname_abs}"; does not exist')
354
+ build_tcl_lines.append(f'source {fname_abs}')
355
+ build_tcl_lines.append('')
356
+
339
357
  with open(build_tcl_file, 'w', encoding='utf-8') as ftcl:
340
- ftcl.write(f'''# Quartus Build Script
341
- load_package flow
342
- project_new {self.args['design']} -overwrite
343
- set_global_assignment -name FAMILY "{self.args['family']}"
344
- set_global_assignment -name DEVICE {self.args['part']}
345
- set_global_assignment -name TOP_LEVEL_ENTITY {self.args['top']}
346
-
347
- # Source the flist file
348
- source [lindex $argv 1]
349
-
350
- # Run synthesis and compile
351
- execute_flow -compile
352
- project_close
353
- ''')
354
-
355
- # launch Quartus build
356
- command_list = [self.quartus_exe, '-t', build_tcl_file, project_dir, flist_file]
358
+ ftcl.write('\n'.join(build_tcl_lines))
359
+
360
+ # launch Quartus build, from work-dir:
361
+ command_list_gui = [self.quartus_gui_exe, '-t', 'build.tcl', project_dir]
362
+ command_list = [self.quartus_exe, '-t', 'build.tcl', project_dir]
363
+ saved_qpf_filename = self.args["design"] + '.qpf'
357
364
  if not util.args['verbose']:
358
365
  command_list.append('-q')
359
366
 
@@ -361,28 +368,63 @@ project_close
361
368
  command_list = util.ShellCommandList(command_list, tee_fpath=None)
362
369
  util.write_shell_command_file(dirpath=self.args['work-dir'], filename='run_quartus.sh',
363
370
  command_lists=[command_list], line_breaks=True)
371
+ util.write_shell_command_file(dirpath=self.args['work-dir'], filename='run_quartus_gui.sh',
372
+ command_lists=[
373
+ command_list_gui,
374
+ ['quartus', saved_qpf_filename], # reopen when done.
375
+ ], line_breaks=True)
364
376
 
365
377
  # Add artifact tracking for build
378
+ artifacts_search_paths = [
379
+ self.args['work-dir'],
380
+ os.path.join(self.args['work-dir'], 'output_files'),
381
+ ]
382
+
366
383
  util.artifacts.add_extension(
367
- search_paths=self.args['work-dir'], file_extension='sof',
384
+ search_paths=artifacts_search_paths, file_extension='sof',
368
385
  typ='bitstream', description='Quartus SRAM Object File (bitstream)'
369
386
  )
370
387
  util.artifacts.add_extension(
371
- search_paths=self.args['work-dir'], file_extension='pof',
388
+ search_paths=artifacts_search_paths, file_extension='pof',
372
389
  typ='bitstream', description='Quartus Programmer Object File'
373
390
  )
374
391
  util.artifacts.add_extension(
375
- search_paths=self.args['work-dir'], file_extension='fit.rpt',
376
- typ='report', description='Quartus Fitter Report'
392
+ search_paths=artifacts_search_paths, file_extension='rpt',
393
+ typ='text', description='Quartus Timing, Fitter, or other report'
377
394
  )
378
395
  util.artifacts.add_extension(
379
- search_paths=self.args['work-dir'], file_extension='sta.rpt',
380
- typ='report', description='Quartus Timing Analyzer Report'
396
+ search_paths=artifacts_search_paths, file_extension='summary',
397
+ typ='text', description='Quartus Timing, Fitter, or other summary'
381
398
  )
382
399
 
383
- self.exec(cwd, command_list, tee_fpath=command_list.tee_fpath)
400
+ if self.args['stop-before-compile']:
401
+ util.info(f"--stop-before-compile set: scripts in : {self.args['work-dir']}")
402
+ return
403
+
404
+
405
+ if self.args['gui'] and self.quartus_gui_exe:
406
+ self.exec(
407
+ work_dir=self.args['work-dir'], command_list=command_list_gui
408
+ )
409
+ else:
410
+ self.exec(
411
+ work_dir=self.args['work-dir'], command_list=command_list,
412
+ tee_fpath=command_list.tee_fpath
413
+ )
414
+ if not os.path.isfile(os.path.join(self.args['work-dir'], saved_qpf_filename)):
415
+ self.error('Saved project file does not exist:',
416
+ os.path.join(self.args['work-dir'], saved_qpf_filename))
417
+
384
418
  util.info(f"Build done, results are in: {self.args['work-dir']}")
385
419
 
420
+ # Note: in GUI mode, if you ran: quaruts -t build.tcl, it will exit on completion,
421
+ # so we'll re-open the project.
422
+ if self.args['gui'] and self.quartus_gui_exe:
423
+ self.exec(
424
+ work_dir=self.args['work-dir'],
425
+ command_list=['quartus', saved_qpf_filename]
426
+ )
427
+
386
428
 
387
429
  class CommandFListQuartus(CommandFList, ToolQuartus):
388
430
  '''CommandFListQuartus is a command handler for: eda flist --tool=quartus'''
@@ -485,7 +527,7 @@ class CommandUploadQuartus(CommandUpload, ToolQuartus):
485
527
  self.args.update({
486
528
  'sof-file': "",
487
529
  'cable': "1",
488
- 'device': "1",
530
+ 'device': "1",
489
531
  'list-cables': False,
490
532
  'list-devices': False,
491
533
  'list-sof-files': False,
@@ -101,22 +101,11 @@ class CommandSimQuesta(CommandSim, ToolQuesta):
101
101
  self.vlog_commands = self.get_compile_command_lists()
102
102
  self.vopt_commands = self.get_elaborate_command_lists()
103
103
  self.vsim_commands = self.get_simulate_command_lists()
104
-
105
- util.write_shell_command_file(dirpath=self.args['work-dir'], filename='compile.sh',
106
- command_lists=self.vlog_commands)
107
- util.write_shell_command_file(dirpath=self.args['work-dir'], filename='elaborate.sh',
108
- command_lists=self.vopt_commands)
109
- util.write_shell_command_file(dirpath=self.args['work-dir'], filename='simulate.sh',
110
- command_lists=self.vsim_commands)
111
- util.write_shell_command_file(dirpath=self.args['work-dir'], filename='all.sh',
112
- command_lists = [
113
- ['./pre_compile_dep_shell_commands.sh'],
114
- ['./compile.sh'],
115
- ['./elaborate.sh'],
116
- ['./simulate.sh'],
117
- ])
118
-
119
- self.write_eda_config_and_args()
104
+ self.write_sh_scripts_to_work_dir(
105
+ compile_lists=self.vlog_commands,
106
+ elaborate_lists=self.vopt_commands,
107
+ simulate_lists=self.vsim_commands
108
+ )
120
109
 
121
110
  def compile(self):
122
111
  if self.args['stop-before-compile']: