opencos-eda 0.3.10__py3-none-any.whl → 0.3.11__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 (53) hide show
  1. opencos/commands/deps_help.py +63 -113
  2. opencos/commands/export.py +7 -2
  3. opencos/commands/multi.py +3 -3
  4. opencos/commands/sim.py +14 -15
  5. opencos/commands/synth.py +1 -2
  6. opencos/commands/upload.py +192 -4
  7. opencos/commands/waves.py +8 -8
  8. opencos/deps/deps_commands.py +6 -6
  9. opencos/deps/deps_processor.py +129 -50
  10. opencos/docs/Architecture.md +45 -0
  11. opencos/docs/ConnectingApps.md +29 -0
  12. opencos/docs/DEPS.md +199 -0
  13. opencos/docs/Debug.md +77 -0
  14. opencos/docs/DirectoryStructure.md +22 -0
  15. opencos/docs/Installation.md +117 -0
  16. opencos/docs/OcVivadoTcl.md +63 -0
  17. opencos/docs/OpenQuestions.md +7 -0
  18. opencos/docs/README.md +13 -0
  19. opencos/docs/RtlCodingStyle.md +54 -0
  20. opencos/docs/eda.md +147 -0
  21. opencos/docs/oc_cli.md +135 -0
  22. opencos/eda.py +132 -35
  23. opencos/eda_base.py +173 -47
  24. opencos/eda_config.py +56 -17
  25. opencos/eda_config_defaults.yml +21 -4
  26. opencos/files.py +26 -1
  27. opencos/tools/cocotb.py +5 -5
  28. opencos/tools/invio.py +2 -2
  29. opencos/tools/invio_yosys.py +2 -1
  30. opencos/tools/iverilog.py +3 -3
  31. opencos/tools/quartus.py +113 -115
  32. opencos/tools/questa_common.py +2 -2
  33. opencos/tools/riviera.py +3 -3
  34. opencos/tools/slang.py +11 -7
  35. opencos/tools/slang_yosys.py +1 -0
  36. opencos/tools/surelog.py +4 -3
  37. opencos/tools/verilator.py +4 -4
  38. opencos/tools/vivado.py +307 -176
  39. opencos/tools/yosys.py +4 -4
  40. opencos/util.py +6 -3
  41. opencos/utils/dict_helpers.py +31 -0
  42. opencos/utils/markup_helpers.py +2 -2
  43. opencos/utils/subprocess_helpers.py +3 -3
  44. opencos/utils/vscode_helper.py +2 -2
  45. opencos/utils/vsim_helper.py +16 -5
  46. {opencos_eda-0.3.10.dist-info → opencos_eda-0.3.11.dist-info}/METADATA +1 -1
  47. opencos_eda-0.3.11.dist-info/RECORD +94 -0
  48. opencos_eda-0.3.10.dist-info/RECORD +0 -81
  49. {opencos_eda-0.3.10.dist-info → opencos_eda-0.3.11.dist-info}/WHEEL +0 -0
  50. {opencos_eda-0.3.10.dist-info → opencos_eda-0.3.11.dist-info}/entry_points.txt +0 -0
  51. {opencos_eda-0.3.10.dist-info → opencos_eda-0.3.11.dist-info}/licenses/LICENSE +0 -0
  52. {opencos_eda-0.3.10.dist-info → opencos_eda-0.3.11.dist-info}/licenses/LICENSE.spdx +0 -0
  53. {opencos_eda-0.3.10.dist-info → opencos_eda-0.3.11.dist-info}/top_level.txt +0 -0
opencos/tools/quartus.py CHANGED
@@ -9,16 +9,14 @@ Used for Intel FPGA synthesis, place & route, and bitstream generation.
9
9
  import os
10
10
  import re
11
11
  import shlex
12
- import shutil
13
12
  import subprocess
14
-
15
13
  from pathlib import Path
16
14
 
17
- from opencos import util, eda_base
18
- from opencos.eda_base import Tool
19
- from opencos.commands import (
20
- CommandSynth, CommandBuild, CommandFList, CommandProj, CommandUpload, CommandOpen
21
- )
15
+ from opencos import util
16
+ from opencos.commands import CommandSynth, CommandBuild, CommandFList, CommandProj, \
17
+ CommandUpload, CommandOpen
18
+ from opencos.eda_base import Tool, get_eda_exec
19
+ from opencos.files import safe_shutil_which
22
20
  from opencos.utils.str_helpers import sanitize_defines_for_sh, strip_outer_quotes
23
21
 
24
22
  class ToolQuartus(Tool):
@@ -48,16 +46,17 @@ class ToolQuartus(Tool):
48
46
  if self._VERSION:
49
47
  return self._VERSION
50
48
 
51
- path = shutil.which(self._EXE)
49
+ path = safe_shutil_which(self._EXE)
52
50
  if not path:
53
51
  self.error("Quartus not in path, need to install or add to $PATH",
54
52
  f"(looked for '{self._EXE}')")
55
53
  else:
56
54
  self.quartus_exe = path
57
55
  self.quartus_base_path, _ = os.path.split(path)
58
- self.quartus_gui_exe = shutil.which('quartus') # vs quartus_sh
59
-
60
56
 
57
+ self.quartus_gui_exe = safe_shutil_which(
58
+ os.path.join(self.quartus_base_path, 'quartus') # vs quartus_sh
59
+ )
61
60
 
62
61
  # Get version based on install path name or by running quartus_sh --version
63
62
  util.debug(f"quartus path = {self.quartus_exe}")
@@ -151,7 +150,6 @@ class CommandSynthQuartus(CommandSynth, ToolQuartus):
151
150
  self.write_tcl_file(tcl_file=tcl_file)
152
151
 
153
152
  # execute Quartus synthesis
154
- command_list_gui = [self.quartus_gui_exe, '-t', tcl_file]
155
153
  command_list = [
156
154
  self.quartus_exe, '-t', tcl_file
157
155
  ]
@@ -172,11 +170,7 @@ class CommandSynthQuartus(CommandSynth, ToolQuartus):
172
170
  typ='text', description='Quartus Synthesis Report'
173
171
  )
174
172
 
175
- if self.args['gui'] and self.quartus_gui_exe:
176
- self.exec(self.args['work-dir'], command_list_gui)
177
- else:
178
- self.exec(self.args['work-dir'], command_list)
179
-
173
+ self.exec(self.args['work-dir'], command_list)
180
174
 
181
175
  saved_qpf_filename = self.args["top"] + '.qpf'
182
176
  if not os.path.isfile(os.path.join(self.args['work-dir'], saved_qpf_filename)):
@@ -185,8 +179,13 @@ class CommandSynthQuartus(CommandSynth, ToolQuartus):
185
179
 
186
180
  util.info(f"Synthesis done, results are in: {self.args['work-dir']}")
187
181
 
188
- # Note: in GUI mode, if you ran: quaruts -t build.tcl, it will exit on completion,
189
- # so we'll re-open the project.
182
+ # Note: in GUI mode, if we were to run:
183
+ # ran: quaruts -t build.tcl
184
+ # it treats the tcl script as running "headless" as a pre-script, and won't open the
185
+ # GUI anyway, and will exit on completion,
186
+ # Instead we:
187
+ # 1. always run with quartus_sh, so text goes to stdout
188
+ # 2. we'll re-open the project in GUI mode, here:
190
189
  if self.args['gui'] and self.quartus_gui_exe:
191
190
  self.exec(
192
191
  work_dir=self.args['work-dir'],
@@ -212,19 +211,22 @@ class CommandSynthQuartus(CommandSynth, ToolQuartus):
212
211
  # Add source files (convert to relative paths and use forward slashes)
213
212
  # Note that default of self.args['all-sv'] is False so we should have added
214
213
  # all files to self.files_sv instead of files_v:
214
+ # Note that tcl uses POSIX paths, so \\ -> /
215
215
  for f in self.files_v:
216
- rel_path = os.path.relpath(f, self.args['work-dir']).replace('\\', '/')
216
+ rel_path = Path(os.path.relpath(f, self.args['work-dir'])).as_posix()
217
217
  tcl_lines.append(f"set_global_assignment -name VERILOG_FILE \"{rel_path}\"")
218
218
  for f in self.files_sv:
219
- rel_path = os.path.relpath(f, self.args['work-dir']).replace('\\', '/')
219
+ rel_path = Path(os.path.relpath(f, self.args['work-dir'])).as_posix()
220
220
  tcl_lines.append(f"set_global_assignment -name SYSTEMVERILOG_FILE \"{rel_path}\"")
221
221
  for f in self.files_vhd:
222
- rel_path = os.path.relpath(f, self.args['work-dir']).replace('\\', '/')
222
+ rel_path = Path(os.path.relpath(f, self.args['work-dir'])).as_posix()
223
223
  tcl_lines.append(f"set_global_assignment -name VHDL_FILE \"{rel_path}\"")
224
224
 
225
225
  # Add include directories - Quartus needs the base directory where "lib/" can be found
226
226
  for incdir in self.incdirs:
227
- tcl_lines.append(f"set_global_assignment -name SEARCH_PATH \"{incdir}\"")
227
+ tcl_lines.append(
228
+ f"set_global_assignment -name SEARCH_PATH \"{Path(incdir).as_posix()}\""
229
+ )
228
230
 
229
231
  # Parameters --> set_parameter -name <Parameter_Name> <Value>
230
232
  for k,v in self.parameters.items():
@@ -242,7 +244,7 @@ class CommandSynthQuartus(CommandSynth, ToolQuartus):
242
244
  for incdir in self.incdirs:
243
245
  if os.path.exists(incdir):
244
246
  tcl_lines.append(
245
- f"set_global_assignment -name USER_LIBRARIES \"{incdir}\""
247
+ f"set_global_assignment -name USER_LIBRARIES \"{Path(incdir).as_posix()}\""
246
248
  )
247
249
 
248
250
  # Add defines
@@ -268,7 +270,7 @@ class CommandSynthQuartus(CommandSynth, ToolQuartus):
268
270
  for f in sdc_files:
269
271
  for attr in ('SDC_FILE', 'SYN_SDC_FILE', 'RTL_SDC_FILE'):
270
272
  tcl_lines.extend([
271
- f"set_global_assignment -name {attr} \"{f}\""
273
+ f"set_global_assignment -name {attr} \"{Path(f).as_posix()}\""
272
274
  ])
273
275
  tcl_lines.append("set_global_assignment -name SYNTH_TIMING_DRIVEN_SYNTHESIS ON")
274
276
 
@@ -331,7 +333,7 @@ class CommandBuildQuartus(CommandBuild, ToolQuartus):
331
333
  f"design={self.args['design']}")
332
334
 
333
335
  command_list = [
334
- eda_base.get_eda_exec('flist'), 'flist',
336
+ get_eda_exec('flist'), 'flist',
335
337
  '--no-default-log',
336
338
  '--tool=' + self.args['tool'],
337
339
  '--force',
@@ -411,7 +413,7 @@ class CommandBuildQuartus(CommandBuild, ToolQuartus):
411
413
  fname_abs = os.path.abspath(fname)
412
414
  if not os.path.isfile(fname_abs):
413
415
  self.error(f'add-tcl-files: "{fname_abs}"; does not exist')
414
- build_tcl_lines.append(f'source {fname_abs}')
416
+ build_tcl_lines.append(f'source {Path(fname_abs).as_posix()}')
415
417
  build_tcl_lines.append('')
416
418
 
417
419
  # If we don't have any args for --flow-tcl-files, then use a default flow:
@@ -428,7 +430,7 @@ class CommandBuildQuartus(CommandBuild, ToolQuartus):
428
430
  fname_abs = os.path.abspath(fname)
429
431
  if not os.path.isfile(fname_abs):
430
432
  self.error(f'flow-tcl-files: "{fname_abs}"; does not exist')
431
- build_tcl_lines.append(f'source {fname_abs}')
433
+ build_tcl_lines.append(f'source {Path(fname_abs).as_posix()}')
432
434
  build_tcl_lines.append('')
433
435
 
434
436
  with open(build_tcl_file, 'w', encoding='utf-8') as ftcl:
@@ -552,20 +554,22 @@ class CommandProjQuartus(CommandProj, ToolQuartus):
552
554
  f"set_global_assignment -name TOP_LEVEL_ENTITY {top}",
553
555
  ]
554
556
 
555
- # Add source files
557
+ # Add source files, tcl prefers POSIX paths even in Windows Powershell.
556
558
  for f in self.files_v:
557
- rel_path = os.path.relpath(f, self.args['work-dir']).replace('\\', '/')
559
+ rel_path = Path(os.path.relpath(f, self.args['work-dir'])).as_posix()
558
560
  tcl_lines.append(f"set_global_assignment -name VERILOG_FILE \"{rel_path}\"")
559
561
  for f in self.files_sv:
560
- rel_path = os.path.relpath(f, self.args['work-dir']).replace('\\', '/')
562
+ rel_path = Path(os.path.relpath(f, self.args['work-dir'])).as_posix()
561
563
  tcl_lines.append(f"set_global_assignment -name SYSTEMVERILOG_FILE \"{rel_path}\"")
562
564
  for f in self.files_vhd:
563
- rel_path = os.path.relpath(f, self.args['work-dir']).replace('\\', '/')
565
+ rel_path = Path(os.path.relpath(f, self.args['work-dir'])).as_posix()
564
566
  tcl_lines.append(f"set_global_assignment -name VHDL_FILE \"{rel_path}\"")
565
567
 
566
568
  # Add include directories
567
569
  for incdir in self.incdirs:
568
- tcl_lines.append(f"set_global_assignment -name SEARCH_PATH \"{incdir}\"")
570
+ tcl_lines.append(
571
+ f"set_global_assignment -name SEARCH_PATH \"{Path(incdir).as_posix()}\""
572
+ )
569
573
 
570
574
  # Add defines
571
575
  for key, value in self.defines.items():
@@ -575,9 +579,10 @@ class CommandProjQuartus(CommandProj, ToolQuartus):
575
579
  tcl_lines.append(f"set_global_assignment -name VERILOG_MACRO \"{key}={value}\"")
576
580
 
577
581
  # Add constraints if available
578
- if self.files_sdc:
579
- for sdc_file in self.files_sdc:
580
- tcl_lines.append(f"set_global_assignment -name SDC_FILE \"{sdc_file}\"")
582
+ for sdc_file in self.files_sdc:
583
+ tcl_lines.append(
584
+ f"set_global_assignment -name SDC_FILE \"{Path(sdc_file).as_posix()}\""
585
+ )
581
586
 
582
587
  tcl_lines += [
583
588
  "project_close",
@@ -601,116 +606,109 @@ class CommandProjQuartus(CommandProj, ToolQuartus):
601
606
  class CommandUploadQuartus(CommandUpload, ToolQuartus):
602
607
  '''CommandUploadQuartus is a command handler for: eda upload --tool=quartus'''
603
608
 
609
+ SUPPORTED_BIT_EXT = ['.sof']
610
+
604
611
  def __init__(self, config: dict):
605
612
  CommandUpload.__init__(self, config)
606
613
  ToolQuartus.__init__(self, config=self.config)
607
614
  # add args specific to this tool
608
615
  self.args.update({
609
- 'sof-file': "",
610
616
  'cable': "1",
611
617
  'device': "1",
612
618
  'list-cables': False,
613
619
  'list-devices': False,
614
- 'list-sof-files': False,
615
- 'tcl-file': "upload.tcl",
616
- 'log-file': "upload.log",
617
620
  })
618
621
  self.args_help.update({
619
- 'sof-file': 'SOF file to upload (auto-detected if not specified)',
620
622
  'cable': 'Cable number to use for programming',
621
623
  'device': 'Device number on the cable',
622
624
  'list-cables': 'List available programming cables',
623
625
  'list-devices': 'List available devices on cable',
624
- 'list-sof-files': 'List available SOF files',
625
- 'tcl-file': 'name of TCL file to be created for upload',
626
- 'log-file': 'log file for upload operation',
627
626
  })
628
627
 
628
+ # Support mulitple arg keys for bitfile and list-bitfiles, so
629
+ # --sof-file and --list-sof-files work the same.
630
+ self.args_args.update({
631
+ 'bitfile': ['sof-file'],
632
+ 'list-bitfiles': ['list-sof-files'],
633
+ })
634
+ self.args_help.update({
635
+ 'bitfile': 'SOF file to upload (auto-detected if not specified)',
636
+ 'list-bitfiles': 'List available SOF files',
637
+ })
638
+
639
+
629
640
  def do_it(self): # pylint: disable=too-many-branches,too-many-statements,too-many-locals
641
+ '''
642
+ Note this is called directly by opencos.commands.CommandUpload, based
643
+ on which bitfile(s) were found, or if --tool=quartus was set
644
+
645
+ We do not need to handle --list-bitfiles, was handled by CommandUpload.
646
+ '''
647
+
630
648
  # add defines for this job
631
649
  self.set_tool_defines()
632
650
  self.write_eda_config_and_args()
633
651
 
652
+ # Find quartus_pgm executable, we'll want the one from the same path
653
+ # that was used for our self._EXE (ToolQuartus).
654
+ quartus_pgm = safe_shutil_which(os.path.join(self.quartus_base_path, 'quartus_pgm'))
655
+ if not quartus_pgm:
656
+ self.error("quartus_pgm not found in PATH")
657
+ return
658
+
659
+ # Handle --list-cables
660
+ if self.args['list-cables']:
661
+ util.info("Listing available cables...")
662
+ command_list = [quartus_pgm, '--auto']
663
+ _, stdout, _ = self.exec(self.args['work-dir'], command_list)
664
+ util.info("Available cables listed above")
665
+ return
666
+
634
667
  sof_file = None
635
- if self.args['sof-file']:
636
- if os.path.isfile(self.args['sof-file']):
637
- sof_file = self.args['sof-file']
638
- else:
639
- self.error(f"Specified SOF file does not exist: {self.args['sof-file']}")
668
+ if self.args['bitfile']:
669
+ if os.path.isfile(self.args['bitfile']):
670
+ sof_file = self.args['bitfile']
671
+
672
+ # self.bitfiles was already set by CommandUpload.process_tokens()
673
+ if len(self.bitfiles) == 1:
674
+ sof_file = self.bitfiles[0]
640
675
 
641
676
  # Auto-discover SOF file if not specified
642
- if not sof_file and not self.args['list-cables'] and not self.args['list-devices']:
643
- sof_files = []
644
- util.debug(f"Looking for SOF files in {os.path.abspath('.')}")
645
- for root, _, files in os.walk("."):
646
- for f in files:
647
- if f.endswith(".sof"):
648
- fullpath = os.path.abspath(os.path.join(root, f))
649
- sof_files.append(fullpath)
650
- util.info(f"Found SOF file: {fullpath}")
651
-
652
- if len(sof_files) == 1:
653
- sof_file = sof_files[0]
654
- elif len(sof_files) > 1:
655
- if self.args['list-sof-files']:
656
- util.info("Multiple SOF files found:")
657
- for sf in sof_files:
658
- util.info(f" {sf}")
659
- return
660
- self.error("Multiple SOF files found, please specify --sof-file")
661
- elif not sof_files:
662
- if self.args['list-sof-files']:
663
- util.info("No SOF files found")
664
- return
665
- self.error("No SOF files found")
666
-
667
- # Generate TCL script
668
- script_file = Path(self.args['tcl-file'])
669
-
670
- try:
671
- with script_file.open("w", encoding="utf-8") as fout:
672
- fout.write('load_package quartus_pgm\n')
673
-
674
- if self.args['list-cables']:
675
- fout.write('foreach cable [get_hardware_names] {\n')
676
- fout.write(' puts "Cable: $cable"\n')
677
- fout.write('}\n')
678
-
679
- if self.args['list-devices']:
680
- cable_idx = int(self.args["cable"]) - 1
681
- fout.write(f'set cable [lindex [get_hardware_names] {cable_idx}]\n')
682
- fout.write('foreach device [get_device_names -hardware_name $cable] {\n')
683
- fout.write(' puts "Device: $device"\n')
684
- fout.write('}\n')
685
-
686
- if sof_file:
687
- cable_idx2 = int(self.args["cable"]) - 1
688
- device_idx = int(self.args["device"]) - 1
689
- fout.write(f'set cable [lindex [get_hardware_names] {cable_idx2}]\n')
690
- device_cmd = (
691
- f'set device [lindex [get_device_names -hardware_name $cable] {device_idx}]'
692
- )
693
- fout.write(device_cmd)
694
- fout.write('set_global_assignment -name USE_CONFIGURATION_DEVICE OFF\n')
695
- fout.write('execute_flow -compile\n')
696
- fout.write(f'quartus_pgm -c $cable -m jtag -o "p;{sof_file}@$device"\n')
697
-
698
- except Exception as exc:
699
- self.error(f"Cannot create {script_file}: {exc}")
700
-
701
- if sof_file:
702
- util.info(f"Programming with SOF file: {sof_file}")
703
- else:
704
- util.info("Listing cables/devices only")
677
+ if not sof_file:
678
+ # CommandUpload already displayed them, and exited on --list-bitfiles.
679
+ if len(self.bitfiles) > 1:
680
+ self.error("Multiple SOF files found, please specify --sof-file or --bitfile",
681
+ "or use a different search pattern")
682
+ return
683
+
684
+ self.error("No SOF files found")
685
+ return
686
+
687
+ util.info(f"Programming with SOF file: {sof_file}")
688
+
705
689
 
706
690
  # Execute Quartus programmer
691
+ # Format: quartus_pgm -c <cable> -m jtag -o "p;<sof_file>@<device>"
692
+ cable = self.args['cable']
693
+ device = self.args['device']
694
+ operation = f"p;{sof_file}@{device}"
695
+
707
696
  command_list = [
708
- self.quartus_exe, '-t', str(script_file)
697
+ quartus_pgm, '-c', cable, '-m', 'jtag', '-o', operation
709
698
  ]
710
- if not util.args['verbose']:
711
- command_list.append('-q')
712
699
 
713
- self.exec(self.args['work-dir'], command_list)
700
+ _, stdout, _ = self.exec(self.args['work-dir'], command_list)
701
+
702
+ # Do some log scraping
703
+ for line in stdout.split('\n'):
704
+ if any(x in line for x in ('Warning', 'WARNING')):
705
+ self.tool_warning_count += 1
706
+ elif any(x in line for x in ('Error', 'ERROR')):
707
+ self.tool_error_count += 1
708
+
709
+ self.report_tool_warn_error_counts()
710
+ self.report_pass_fail()
711
+
714
712
  util.info("Upload operation completed")
715
713
 
716
714
 
@@ -11,11 +11,11 @@ Contains classes for ToolQuesta, and CommonSimQuesta.
11
11
 
12
12
  import os
13
13
  import re
14
- import shutil
15
14
 
16
15
  from opencos import util
17
16
  from opencos.commands import sim, CommandSim, CommandFList
18
17
  from opencos.eda_base import Tool
18
+ from opencos.files import safe_shutil_which
19
19
  from opencos.utils.str_helpers import sanitize_defines_for_sh
20
20
 
21
21
  class ToolQuesta(Tool):
@@ -37,7 +37,7 @@ class ToolQuesta(Tool):
37
37
  def get_versions(self) -> str:
38
38
  if self._VERSION:
39
39
  return self._VERSION
40
- path = shutil.which(self._EXE)
40
+ path = safe_shutil_which(self._EXE)
41
41
  if not path:
42
42
  self.error(f"{self._EXE} not in path, need to setup",
43
43
  "(i.e. source /opt/intelFPGA_pro/23.4/settings64.sh")
opencos/tools/riviera.py CHANGED
@@ -7,12 +7,12 @@ Contains classes for ToolRiviera, CommandSimRiviera, CommandElabRiviera.
7
7
  # pylint: disable=R0801 # (duplicate code in derived classes, such as if-condition return.)
8
8
 
9
9
  import os
10
- import shutil
11
10
  import subprocess
12
11
 
13
12
  from opencos import util
14
- from opencos.tools.questa_common import ToolQuesta, CommonSimQuesta
15
13
  from opencos.commands import CommandFList
14
+ from opencos.files import safe_shutil_which
15
+ from opencos.tools.questa_common import ToolQuesta, CommonSimQuesta
16
16
  from opencos.utils.str_helpers import sanitize_defines_for_sh
17
17
  from opencos.utils import status_constants
18
18
 
@@ -27,7 +27,7 @@ class ToolRiviera(ToolQuesta):
27
27
  def get_versions(self) -> str:
28
28
  if self._VERSION:
29
29
  return self._VERSION
30
- path = shutil.which(self._EXE)
30
+ path = safe_shutil_which(self._EXE)
31
31
  if not path:
32
32
  self.error(f"{self._EXE} not in path, need to setup or add to PATH")
33
33
  util.debug(f"{path=}")
opencos/tools/slang.py CHANGED
@@ -6,12 +6,12 @@ Contains classes for ToolSlang, CommandElabSlang
6
6
  # pylint: disable=R0801 # (calling functions with same arguments)
7
7
 
8
8
  import os
9
- import shutil
10
9
  import subprocess
11
10
 
12
11
  from opencos import util
13
- from opencos.eda_base import Tool
14
12
  from opencos.commands import CommandElab
13
+ from opencos.eda_base import Tool
14
+ from opencos.files import safe_shutil_which
15
15
  from opencos.utils.str_helpers import sanitize_defines_for_sh
16
16
 
17
17
 
@@ -30,14 +30,18 @@ class ToolSlang(Tool):
30
30
  def get_versions(self) -> str:
31
31
  if self._VERSION:
32
32
  return self._VERSION
33
- path = shutil.which(self._EXE)
33
+ path = safe_shutil_which(self._EXE)
34
34
  if not path:
35
35
  self.error(f'"{self._EXE}" not in path, need to get it ({self._URL}')
36
36
  else:
37
37
  self.slang_exe = path
38
38
  self.slang_base_path, _ = os.path.split(path)
39
- self.slang_tidy_exe = os.path.join(self.slang_base_path, 'slang-tidy')
40
- self.slang_hier_exe = os.path.join(self.slang_base_path, 'slang-hier')
39
+ self.slang_tidy_exe = safe_shutil_which(
40
+ os.path.join(self.slang_base_path, 'slang-tidy')
41
+ )
42
+ self.slang_hier_exe = safe_shutil_which(
43
+ os.path.join(self.slang_base_path, 'slang-hier')
44
+ )
41
45
 
42
46
  version_ret = subprocess.run(
43
47
  [self.slang_exe, '--version'],
@@ -204,7 +208,7 @@ class CommandElabSlang(CommandElab, ToolSlang):
204
208
  command_list = [self.slang_exe]
205
209
 
206
210
  if self.args['tidy']:
207
- if not shutil.which(self.slang_tidy_exe):
211
+ if not safe_shutil_which(self.slang_tidy_exe):
208
212
  util.warning("Running tool slang with --tidy, but 'slang-tidy'",
209
213
  "not in PATH, using 'slang' instead")
210
214
  else:
@@ -213,7 +217,7 @@ class CommandElabSlang(CommandElab, ToolSlang):
213
217
  if self.args['hier']:
214
218
  if self.args['tidy']:
215
219
  util.warning('Running with --tidy and --heir, will attempt to use slang-hier')
216
- elif not shutil.which(self.slang_hier_exe):
220
+ elif not safe_shutil_which(self.slang_hier_exe):
217
221
  util.warning("Running tool slang with --hier, but 'slang-hier'",
218
222
  "not in PATH, using 'slang' instead")
219
223
  else:
@@ -119,6 +119,7 @@ class CommandSynthSlangYosys(CommonSynthYosys, ToolSlangYosys):
119
119
  )
120
120
 
121
121
  # In case --top was not set:
122
+ # TODO(drew): Can we skip this if it was an inferred top???
122
123
  if not any(x.startswith('--top') for x in read_slang_cmd):
123
124
  read_slang_cmd.append(f'--top {self.args["top"]}')
124
125
 
opencos/tools/surelog.py CHANGED
@@ -3,12 +3,13 @@
3
3
  Contains classes for ToolSurelog, CommandElabSurelog
4
4
  '''
5
5
 
6
- import shutil
6
+
7
7
  import subprocess
8
8
 
9
9
  from opencos import util
10
- from opencos.eda_base import Tool
11
10
  from opencos.commands import CommandElab
11
+ from opencos.eda_base import Tool
12
+ from opencos.files import safe_shutil_which
12
13
  from opencos.utils.str_helpers import sanitize_defines_for_sh
13
14
 
14
15
 
@@ -24,7 +25,7 @@ class ToolSurelog(Tool):
24
25
  def get_versions(self) -> str:
25
26
  if self._VERSION:
26
27
  return self._VERSION
27
- path = shutil.which(self._EXE)
28
+ path = safe_shutil_which(self._EXE)
28
29
  if not path:
29
30
  self.error(f'"{self._EXE}" not in path, need to get it ({self._URL})')
30
31
  else:
@@ -7,13 +7,13 @@ Contains classes for ToolVerilator and VerilatorSim, VerilatorElab.
7
7
 
8
8
  import multiprocessing
9
9
  import os
10
- import shutil
11
10
  import subprocess
12
11
 
13
12
 
14
13
  from opencos import util
15
- from opencos.eda_base import Tool
16
14
  from opencos.commands import CommandSim
15
+ from opencos.eda_base import Tool
16
+ from opencos.files import safe_shutil_which
17
17
  from opencos.utils.str_helpers import sanitize_defines_for_sh
18
18
 
19
19
  class ToolVerilator(Tool):
@@ -31,7 +31,7 @@ class ToolVerilator(Tool):
31
31
  if self._VERSION:
32
32
  return self._VERSION
33
33
  # __init__ would have set self.EXE to full path.
34
- path = shutil.which(self._EXE)
34
+ path = safe_shutil_which(self._EXE)
35
35
  if not path:
36
36
  self.error(f'"{self._EXE}" not in path or not installed, see {self._URL})')
37
37
  else:
@@ -40,7 +40,7 @@ class ToolVerilator(Tool):
40
40
 
41
41
  # Let's get the verilator_coverage path from the same place as verilator.
42
42
  if path:
43
- self.verilator_coverage_exe = shutil.which(
43
+ self.verilator_coverage_exe = safe_shutil_which(
44
44
  os.path.join(self.verilator_base_path, 'verilator_coverage')
45
45
  )
46
46
  if not self.verilator_coverage_exe: