opencos-eda 0.2.43__tar.gz → 0.2.44__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 (82) hide show
  1. {opencos_eda-0.2.43/opencos_eda.egg-info → opencos_eda-0.2.44}/PKG-INFO +1 -1
  2. {opencos_eda-0.2.43 → opencos_eda-0.2.44}/opencos/commands/export.py +0 -1
  3. {opencos_eda-0.2.43 → opencos_eda-0.2.44}/opencos/commands/flist.py +4 -1
  4. {opencos_eda-0.2.43 → opencos_eda-0.2.44}/opencos/deps_helpers.py +2 -0
  5. {opencos_eda-0.2.43 → opencos_eda-0.2.44}/opencos/eda.py +4 -3
  6. {opencos_eda-0.2.43 → opencos_eda-0.2.44}/opencos/eda_base.py +7 -1
  7. {opencos_eda-0.2.43 → opencos_eda-0.2.44}/opencos/eda_config.py +1 -1
  8. {opencos_eda-0.2.43 → opencos_eda-0.2.44}/opencos/eda_config_defaults.yml +6 -2
  9. {opencos_eda-0.2.43 → opencos_eda-0.2.44}/opencos/export_helper.py +6 -4
  10. {opencos_eda-0.2.43 → opencos_eda-0.2.44}/opencos/files.py +1 -0
  11. {opencos_eda-0.2.43 → opencos_eda-0.2.44}/opencos/tests/helpers.py +1 -1
  12. {opencos_eda-0.2.43 → opencos_eda-0.2.44}/opencos/tests/test_eda_synth.py +63 -2
  13. {opencos_eda-0.2.43 → opencos_eda-0.2.44}/opencos/tools/iverilog.py +1 -1
  14. {opencos_eda-0.2.43 → opencos_eda-0.2.44}/opencos/tools/riviera.py +1 -1
  15. {opencos_eda-0.2.43 → opencos_eda-0.2.44}/opencos/tools/slang.py +1 -1
  16. {opencos_eda-0.2.43 → opencos_eda-0.2.44}/opencos/tools/surelog.py +1 -1
  17. {opencos_eda-0.2.43 → opencos_eda-0.2.44}/opencos/tools/verilator.py +1 -1
  18. {opencos_eda-0.2.43 → opencos_eda-0.2.44}/opencos/tools/vivado.py +22 -4
  19. {opencos_eda-0.2.43 → opencos_eda-0.2.44}/opencos/tools/yosys.py +32 -13
  20. {opencos_eda-0.2.43 → opencos_eda-0.2.44}/opencos/util.py +5 -6
  21. {opencos_eda-0.2.43 → opencos_eda-0.2.44/opencos_eda.egg-info}/PKG-INFO +1 -1
  22. {opencos_eda-0.2.43 → opencos_eda-0.2.44}/pyproject.toml +1 -1
  23. {opencos_eda-0.2.43 → opencos_eda-0.2.44}/LICENSE +0 -0
  24. {opencos_eda-0.2.43 → opencos_eda-0.2.44}/LICENSE.spdx +0 -0
  25. {opencos_eda-0.2.43 → opencos_eda-0.2.44}/README.md +0 -0
  26. {opencos_eda-0.2.43 → opencos_eda-0.2.44}/opencos/__init__.py +0 -0
  27. {opencos_eda-0.2.43 → opencos_eda-0.2.44}/opencos/_version.py +0 -0
  28. {opencos_eda-0.2.43 → opencos_eda-0.2.44}/opencos/_waves_pkg.sv +0 -0
  29. {opencos_eda-0.2.43 → opencos_eda-0.2.44}/opencos/commands/__init__.py +0 -0
  30. {opencos_eda-0.2.43 → opencos_eda-0.2.44}/opencos/commands/build.py +0 -0
  31. {opencos_eda-0.2.43 → opencos_eda-0.2.44}/opencos/commands/elab.py +0 -0
  32. {opencos_eda-0.2.43 → opencos_eda-0.2.44}/opencos/commands/multi.py +0 -0
  33. {opencos_eda-0.2.43 → opencos_eda-0.2.44}/opencos/commands/open.py +0 -0
  34. {opencos_eda-0.2.43 → opencos_eda-0.2.44}/opencos/commands/proj.py +0 -0
  35. {opencos_eda-0.2.43 → opencos_eda-0.2.44}/opencos/commands/sim.py +0 -0
  36. {opencos_eda-0.2.43 → opencos_eda-0.2.44}/opencos/commands/sweep.py +0 -0
  37. {opencos_eda-0.2.43 → opencos_eda-0.2.44}/opencos/commands/synth.py +0 -0
  38. {opencos_eda-0.2.43 → opencos_eda-0.2.44}/opencos/commands/targets.py +0 -0
  39. {opencos_eda-0.2.43 → opencos_eda-0.2.44}/opencos/commands/upload.py +0 -0
  40. {opencos_eda-0.2.43 → opencos_eda-0.2.44}/opencos/commands/waves.py +0 -0
  41. {opencos_eda-0.2.43 → opencos_eda-0.2.44}/opencos/deps_schema.py +0 -0
  42. {opencos_eda-0.2.43 → opencos_eda-0.2.44}/opencos/eda_config_max_verilator_waivers.yml +0 -0
  43. {opencos_eda-0.2.43 → opencos_eda-0.2.44}/opencos/eda_config_reduced.yml +0 -0
  44. {opencos_eda-0.2.43 → opencos_eda-0.2.44}/opencos/eda_deps_bash_completion.bash +0 -0
  45. {opencos_eda-0.2.43 → opencos_eda-0.2.44}/opencos/eda_extract_targets.py +0 -0
  46. {opencos_eda-0.2.43 → opencos_eda-0.2.44}/opencos/eda_tool_helper.py +0 -0
  47. {opencos_eda-0.2.43 → opencos_eda-0.2.44}/opencos/export_json_convert.py +0 -0
  48. {opencos_eda-0.2.43 → opencos_eda-0.2.44}/opencos/names.py +0 -0
  49. {opencos_eda-0.2.43 → opencos_eda-0.2.44}/opencos/oc_cli.py +0 -0
  50. {opencos_eda-0.2.43 → opencos_eda-0.2.44}/opencos/pcie.py +0 -0
  51. {opencos_eda-0.2.43 → opencos_eda-0.2.44}/opencos/peakrdl_cleanup.py +0 -0
  52. {opencos_eda-0.2.43 → opencos_eda-0.2.44}/opencos/seed.py +0 -0
  53. {opencos_eda-0.2.43 → opencos_eda-0.2.44}/opencos/tests/__init__.py +0 -0
  54. {opencos_eda-0.2.43 → opencos_eda-0.2.44}/opencos/tests/custom_config.yml +0 -0
  55. {opencos_eda-0.2.43 → opencos_eda-0.2.44}/opencos/tests/deps_files/command_order/DEPS.yml +0 -0
  56. {opencos_eda-0.2.43 → opencos_eda-0.2.44}/opencos/tests/deps_files/error_msgs/DEPS.yml +0 -0
  57. {opencos_eda-0.2.43 → opencos_eda-0.2.44}/opencos/tests/deps_files/iverilog_test/DEPS.yml +0 -0
  58. {opencos_eda-0.2.43 → opencos_eda-0.2.44}/opencos/tests/deps_files/no_deps_here/DEPS.yml +0 -0
  59. {opencos_eda-0.2.43 → opencos_eda-0.2.44}/opencos/tests/deps_files/non_sv_reqs/DEPS.yml +0 -0
  60. {opencos_eda-0.2.43 → opencos_eda-0.2.44}/opencos/tests/deps_files/tags_with_tools/DEPS.yml +0 -0
  61. {opencos_eda-0.2.43 → opencos_eda-0.2.44}/opencos/tests/deps_files/test_err_fatal/DEPS.yml +0 -0
  62. {opencos_eda-0.2.43 → opencos_eda-0.2.44}/opencos/tests/test_build.py +0 -0
  63. {opencos_eda-0.2.43 → opencos_eda-0.2.44}/opencos/tests/test_deps_helpers.py +0 -0
  64. {opencos_eda-0.2.43 → opencos_eda-0.2.44}/opencos/tests/test_deps_schema.py +0 -0
  65. {opencos_eda-0.2.43 → opencos_eda-0.2.44}/opencos/tests/test_eda.py +0 -0
  66. {opencos_eda-0.2.43 → opencos_eda-0.2.44}/opencos/tests/test_eda_elab.py +0 -0
  67. {opencos_eda-0.2.43 → opencos_eda-0.2.44}/opencos/tests/test_oc_cli.py +0 -0
  68. {opencos_eda-0.2.43 → opencos_eda-0.2.44}/opencos/tests/test_tools.py +0 -0
  69. {opencos_eda-0.2.43 → opencos_eda-0.2.44}/opencos/tools/__init__.py +0 -0
  70. {opencos_eda-0.2.43 → opencos_eda-0.2.44}/opencos/tools/invio.py +0 -0
  71. {opencos_eda-0.2.43 → opencos_eda-0.2.44}/opencos/tools/invio_helpers.py +0 -0
  72. {opencos_eda-0.2.43 → opencos_eda-0.2.44}/opencos/tools/invio_yosys.py +0 -0
  73. {opencos_eda-0.2.43 → opencos_eda-0.2.44}/opencos/tools/modelsim_ase.py +0 -0
  74. {opencos_eda-0.2.43 → opencos_eda-0.2.44}/opencos/tools/questa.py +0 -0
  75. {opencos_eda-0.2.43 → opencos_eda-0.2.44}/opencos/tools/slang_yosys.py +0 -0
  76. {opencos_eda-0.2.43 → opencos_eda-0.2.44}/opencos/tools/tabbycad_yosys.py +0 -0
  77. {opencos_eda-0.2.43 → opencos_eda-0.2.44}/opencos_eda.egg-info/SOURCES.txt +0 -0
  78. {opencos_eda-0.2.43 → opencos_eda-0.2.44}/opencos_eda.egg-info/dependency_links.txt +0 -0
  79. {opencos_eda-0.2.43 → opencos_eda-0.2.44}/opencos_eda.egg-info/entry_points.txt +0 -0
  80. {opencos_eda-0.2.43 → opencos_eda-0.2.44}/opencos_eda.egg-info/requires.txt +0 -0
  81. {opencos_eda-0.2.43 → opencos_eda-0.2.44}/opencos_eda.egg-info/top_level.txt +0 -0
  82. {opencos_eda-0.2.43 → opencos_eda-0.2.44}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: opencos-eda
3
- Version: 0.2.43
3
+ Version: 0.2.44
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
@@ -37,7 +37,6 @@ class CommandExport(CommandDesign):
37
37
  self, tokens: list, process_all: bool = True, pwd: str = os.getcwd()
38
38
  ) -> list:
39
39
 
40
- self.defines['OC_EXPORT'] = None
41
40
  unparsed = CommandDesign.process_tokens(
42
41
  self, tokens=tokens, process_all=process_all, pwd=pwd
43
42
  )
@@ -47,11 +47,14 @@ class CommandFList(CommandDesign):
47
47
  'no-quote-path' : False,
48
48
  'build-script' : "", # we don't want this to error either
49
49
 
50
- 'print-to-stdout': False, # do not save to file, print to stdout.
50
+ 'print-to-stdout': False,
51
51
 
52
52
  # ex: eda flist --print-to-stdout --emit-rel-path --quiet <target>
53
53
  'emit-rel-path' : False,
54
54
  })
55
+ self.args_help.update({
56
+ 'print-to-stdout': "do not save file, print to stdout",
57
+ })
55
58
 
56
59
  def process_tokens(
57
60
  self, tokens: list , process_all: bool = True, pwd: str = os.getcwd()
@@ -57,6 +57,8 @@ class Defaults:
57
57
  'with-tools',
58
58
  'with-args',
59
59
  'args',
60
+ 'deps',
61
+ 'reqs',
60
62
  'defines',
61
63
  'incdirs',
62
64
  'replace-config-tools',
@@ -320,9 +320,10 @@ def process_tokens(tokens: list, original_args: list, config: dict, interactive=
320
320
  for value in unparsed:
321
321
  if value in config['DEFAULT_HANDLERS'].keys():
322
322
  command = value
323
- if value in config['command_uses_no_tools']:
323
+ if not parsed.tool and value in config['command_tool_is_optional']:
324
+ # only do this if --tool was not set.
324
325
  run_auto_tool_setup = False
325
- unparsed.remove(value)
326
+ unparsed.remove(value) # remove command (flist, export, targets, etc)
326
327
  break
327
328
 
328
329
  if not interactive:
@@ -361,7 +362,7 @@ def process_tokens(tokens: list, original_args: list, config: dict, interactive=
361
362
  util.debug(f'{type(sco)=}')
362
363
  if not parsed.tool and \
363
364
  command not in config.get('command_determines_tool', []) and \
364
- command not in config.get('command_uses_no_tools', []):
365
+ command not in config.get('command_tool_is_optional', []):
365
366
  use_tool = which_tool(command, config)
366
367
  util.info(f"--tool not specified, using default for {command=}: {use_tool}")
367
368
 
@@ -686,6 +686,7 @@ class CommandDesign(Command):
686
686
  self.files_sv = []
687
687
  self.files_vhd = []
688
688
  self.files_cpp = []
689
+ self.files_sdc = []
689
690
  self.files_non_source = []
690
691
  self.files_caller_info = {}
691
692
  self.dep_shell_commands = [] # each list entry is a {}
@@ -760,7 +761,8 @@ class CommandDesign(Command):
760
761
  self.files.pop(key)
761
762
  self.files[new_key] = True
762
763
 
763
- my_file_lists_list = [self.files_v, self.files_sv, self.files_vhd, self.files_cpp]
764
+ my_file_lists_list = [self.files_v, self.files_sv, self.files_vhd, self.files_cpp,
765
+ self.files_sdc]
764
766
  for my_file_list in my_file_lists_list:
765
767
  for iter,value in enumerate(my_file_list):
766
768
  if value and type(value) is str and value.startswith(self._work_dir_add_srcs_path_string):
@@ -1029,6 +1031,7 @@ class CommandDesign(Command):
1029
1031
  sv_file_ext_list = known_file_ext_dict.get('systemverilog', [])
1030
1032
  vhdl_file_ext_list = known_file_ext_dict.get('vhdl', [])
1031
1033
  cpp_file_ext_list = known_file_ext_dict.get('cpp', [])
1034
+ sdc_file_ext_list = known_file_ext_dict.get('synth_constraints', [])
1032
1035
 
1033
1036
  if forced_extension:
1034
1037
  # If forced_extension='systemverilog', then use the first known extension for
@@ -1057,6 +1060,9 @@ class CommandDesign(Command):
1057
1060
  elif file_ext in cpp_file_ext_list:
1058
1061
  self.files_cpp.append(file_abspath)
1059
1062
  util.debug("Added C++ file %s as %s" % (filename, file_abspath))
1063
+ elif file_ext in sdc_file_ext_list:
1064
+ self.files_sdc.append(file_abspath)
1065
+ util.debug("Added SDC file %s as %s" % (filename, file_abspath))
1060
1066
  else:
1061
1067
  # unknown file extension. In these cases we link the file to the working directory
1062
1068
  # so it is available (for example, a .mem file that is expected to exist with relative path)
@@ -30,7 +30,7 @@ class Defaults:
30
30
  'vars',
31
31
  'file_extensions',
32
32
  'command_determines_tool',
33
- 'command_uses_no_tools',
33
+ 'command_tool_is_optional',
34
34
  'tools',
35
35
  'auto_tools_order',
36
36
  ])
@@ -60,6 +60,9 @@ file_extensions:
60
60
  - .vhdl
61
61
  cpp:
62
62
  - .cpp
63
+ synth_constraints:
64
+ - .sdc
65
+ - .xdc
63
66
 
64
67
  inferred_top:
65
68
  # file extensions that we can infer "top" module from, if --top omitted.
@@ -72,8 +75,9 @@ command_determines_tool:
72
75
  # eda commands that will self-determine the tool to use
73
76
  - waves
74
77
 
75
- command_uses_no_tools:
76
- # eda commands that do not use a tool at all, will skip auto_tools_order
78
+ command_tool_is_optional:
79
+ # eda commands that may not need to use a tool at all, will skip auto_tools_order if --tool=None (default)
80
+ - flist
77
81
  - export
78
82
  - targets
79
83
 
@@ -352,23 +352,25 @@ class ExportHelper:
352
352
  }
353
353
  }
354
354
 
355
- if len(deps_file_args) > 0:
355
+
356
+ if deps_file_args:
356
357
  data[self.target]['args'] = deps_file_args.copy()
357
358
 
358
359
  if self.args.get('top', None):
359
360
  data[self.target]['top'] = self.args['top']
360
361
 
361
- if len(self.cmd_design_obj.defines.keys()) > 0:
362
+ if self.cmd_design_obj.defines:
362
363
  data[self.target]['defines'] = self.cmd_design_obj.defines.copy()
363
364
  for define in _remove_DEPS_yml_defines:
364
365
  # Remove defines keys for OC_ROOT and OC_SEED. Change OC_SEED to _ORIG_OC_SEED
365
366
  if define in data[self.target]['defines']:
366
367
  data[self.target]['defines'].pop(define)
367
368
 
368
- if len(self.cmd_design_obj.files_non_source) > 0:
369
+ reqs_fullpath_list = self.included_files + self.cmd_design_obj.files_non_source
370
+ if reqs_fullpath_list:
369
371
  # Need to strip path information from non-source files:
370
372
  data[self.target]['reqs'] = list()
371
- for fullpath in self.cmd_design_obj.files_non_source:
373
+ for fullpath in reqs_fullpath_list:
372
374
  filename = os.path.split(fullpath)[1]
373
375
  data[self.target]['reqs'].append(filename)
374
376
 
@@ -23,6 +23,7 @@ FORCE_PREFIX_DICT = {
23
23
  'v@': 'verilog',
24
24
  'vhdl@': 'vhdl',
25
25
  'cpp@': 'cpp',
26
+ 'sdc@': 'synth_constraints',
26
27
  }
27
28
 
28
29
  ALL_FORCED_PREFIXES = set(list(FORCE_PREFIX_DICT.keys()))
@@ -128,7 +128,7 @@ class Helpers:
128
128
  '''Changes directory to self.DEFAULT_DIR and removes eda.work, eda.export paths'''
129
129
  chdir_remove_work_dir('', self.DEFAULT_DIR)
130
130
 
131
- def log_it(self, command_str:str, logfile=None, use_eda_wrap=True):
131
+ def log_it(self, command_str:str, logfile=None, use_eda_wrap=True) -> int:
132
132
  '''Replacement for calling eda.main or eda_wrap, when you want an internal logfile
133
133
 
134
134
  Usage:
@@ -1,17 +1,20 @@
1
1
  '''pytests for: eda [multi|tools-multi] synth [args] <target(s)>'''
2
2
 
3
3
  import os
4
+ import shutil
5
+
4
6
  import pytest
5
7
 
6
8
  from opencos import eda, eda_tool_helper
7
9
  from opencos.tests import helpers
10
+ from opencos.tests.helpers import Helpers
8
11
 
9
12
 
10
- thispath = os.path.dirname(__file__)
13
+ THISPATH = os.path.dirname(__file__)
11
14
 
12
15
  def chdir_remove_work_dir(relpath):
13
16
  '''Changes dir to relpath, removes the work directories (eda.work, eda.export*)'''
14
- return helpers.chdir_remove_work_dir(thispath, relpath)
17
+ return helpers.chdir_remove_work_dir(THISPATH, relpath)
15
18
 
16
19
  # Figure out what tools the system has available, without calling eda.main(..)
17
20
  config, tools_loaded = eda_tool_helper.get_config_and_tools_loaded()
@@ -75,3 +78,61 @@ class Tests:
75
78
  rc = eda.main(*cmdlist)
76
79
  print(f'{rc=}')
77
80
  assert rc == 0
81
+
82
+
83
+ def vivado_has_xpms() -> bool:
84
+ '''Returns True if Vivado is installed and has visibility to XPMs'''
85
+ if 'vivado' not in tools_loaded:
86
+ return False
87
+ vivado_exe = shutil.which('vivado')
88
+ vivado_bin_path, _ = os.path.split(vivado_exe)
89
+ vivado_base_path, _ = os.path.split(vivado_bin_path) # strip bin/vivado
90
+
91
+ return os.path.exists(os.path.join(vivado_base_path, 'data', 'ip', 'xpm'))
92
+
93
+
94
+ @pytest.mark.skipif(
95
+ 'slang_yosys' not in tools_loaded, reason="requires slang_yosys for synth"
96
+ )
97
+ class TestsSlangYosys(Helpers):
98
+ '''Tests that require tool=slang_yosys to be available'''
99
+
100
+ def test_sdc_file(self):
101
+ '''Test for 'eda synth' on oclib_fifo_with_sdc
102
+
103
+ This does not use the actual .sdc file, but it also shouldn't fail with
104
+ that file in the 'deps' list (CommandDesign should track it correctly)
105
+ '''
106
+ chdir_remove_work_dir('deps_files/test_sdc_files')
107
+ cmd_str = 'synth --tool=slang_yosys oclib_fifo_with_sdc'
108
+ rc = self.log_it(cmd_str, use_eda_wrap=False)
109
+ assert rc == 0
110
+
111
+ @pytest.mark.skipif('vivado' not in tools_loaded, reason="requires vivado")
112
+ @pytest.mark.skipif(not vivado_has_xpms(), reason="requires install to have XPMs")
113
+ class TestsVivado(Helpers):
114
+ '''Tests that require tool=vivado with XPMs available for synthesis'''
115
+
116
+ def test_sdc_file(self):
117
+ '''Test for 'eda synth' on oclib_fifo_with_sdc
118
+
119
+ And check that the .sdc file was used and not the default generated .xdc
120
+ file
121
+ '''
122
+ chdir_remove_work_dir('deps_files/test_sdc_files')
123
+ cmd_str = 'synth --tool=vivado oclib_fifo_with_sdc'
124
+ rc = self.log_it(cmd_str, use_eda_wrap=False)
125
+ assert rc == 0
126
+
127
+ # apparently this doesn't get saved in the eda.log. That's not great,
128
+ # but we can check the <target>.synth.log file.
129
+ synth_log = os.path.join(
130
+ THISPATH, 'deps_files', 'test_sdc_files',
131
+ 'eda.work', 'oclib_fifo_with_sdc.synth', 'oclib_fifo.synth.log'
132
+ )
133
+ sdc_lines = self.get_log_lines_with('.sdc', logfile=synth_log)
134
+ assert sdc_lines
135
+ for sdc_line in sdc_lines:
136
+ assert 'oclib_fifo_vivado.sdc' in sdc_line
137
+ assert 'test_sdc_files' in sdc_line
138
+ assert 'eda.work' not in sdc_line
@@ -37,7 +37,7 @@ class ToolIverilog(Tool):
37
37
  iverilog_version_ret = subprocess.run(
38
38
  [self.iverilog_exe, '-v'], capture_output=True, check=False
39
39
  )
40
- lines = iverilog_version_ret.stdout.decode("utf-8").split('\n')
40
+ lines = iverilog_version_ret.stdout.decode("utf-8", errors="replace").split('\n')
41
41
  words = lines[0].split() # 'Icarus Verilog version 13.0 (devel) (s20221226-568-g62727e8b2)'
42
42
  version = words[3]
43
43
  util.debug(f'{iverilog_path=} {lines[0]=}')
@@ -36,7 +36,7 @@ class ToolRiviera(ToolModelsimAse):
36
36
  capture_output=True,
37
37
  check=False
38
38
  )
39
- stdout = version_ret.stdout.decode('utf-8').rstrip()
39
+ stdout = version_ret.stdout.decode('utf-8', errors='replace').rstrip()
40
40
 
41
41
  # Expect:
42
42
  # Aldec, Inc. Riviera-PRO version 2025.04.139.9738 built for Linux64 on May 30, 2025
@@ -44,7 +44,7 @@ class ToolSlang(Tool):
44
44
  capture_output=True,
45
45
  check=False
46
46
  )
47
- stdout = version_ret.stdout.decode('utf-8')
47
+ stdout = version_ret.stdout.decode('utf-8', errors='replace')
48
48
  util.debug(f'{path=} {version_ret=}')
49
49
  words = stdout.split() # slang version 8.0.6+b4a74b00
50
50
  if len(words) < 3:
@@ -33,7 +33,7 @@ class ToolSurelog(Tool):
33
33
  version_ret = subprocess.run(
34
34
  [self.surelog_exe, '--version'], capture_output=True, check=False
35
35
  )
36
- stdout = version_ret.stdout.decode('utf-8')
36
+ stdout = version_ret.stdout.decode('utf-8', errors='replace')
37
37
  util.debug(f'{path=} {version_ret=}')
38
38
  words = stdout.split() # VERSION: 1.84 (first line)
39
39
  if len(words) < 2:
@@ -49,7 +49,7 @@ class ToolVerilator(Tool):
49
49
  capture_output=True,
50
50
  check=False
51
51
  )
52
- stdout = version_ret.stdout.decode('utf-8')
52
+ stdout = version_ret.stdout.decode('utf-8', errors='replace')
53
53
  util.debug(f'{path=} {version_ret=}')
54
54
  words = stdout.split() # 'Verilator 5.027 devel rev v5.026-92-g403a197e2
55
55
  if len(words) < 1:
@@ -70,7 +70,7 @@ class ToolVivado(Tool):
70
70
  #try:
71
71
  # # Get version from vivado -version, or xsim --version:
72
72
  # vivado_ret = subprocess.run(['vivado', '-version'], capture_output=True)
73
- # lines = vivado_ret.stdout.decode('utf-8').split('\n')
73
+ # lines = vivado_ret.stdout.decode('utf-8', errors='replace').split('\n')
74
74
  # words = lines[0].split() # vivado v2024.2.1 (64-bit)
75
75
  # version = words[1][1:] # 2024.2.1
76
76
  # self._VERSION = version
@@ -388,9 +388,14 @@ class CommandSynthVivado(CommandSynth, ToolVivado):
388
388
  self.exec(self.args['work-dir'], command_list)
389
389
 
390
390
 
391
- def write_tcl_file(self, tcl_file: str) -> None:
391
+ def write_tcl_file( # pylint: disable=too-many-locals,too-many-branches
392
+ self, tcl_file: str
393
+ ) -> None:
392
394
  '''Writes synthesis capable Vivado tcl file to filepath 'tcl_file'.'''
393
395
 
396
+ # TODO(drew): This method needs to be broken up to avoid the pylint
397
+ # waivers.
398
+
394
399
  v = self.get_vivado_tcl_verbose_arg()
395
400
 
396
401
  defines = ""
@@ -416,9 +421,12 @@ class CommandSynthVivado(CommandSynth, ToolVivado):
416
421
  part = self.args['part']
417
422
  top = self.args['top']
418
423
 
424
+ default_xdc = False
419
425
  if self.args['xdc'] != "":
420
- default_xdc = False
421
426
  xdc_file = os.path.abspath(self.args['xdc'])
427
+ elif self.files_sdc:
428
+ # Use files from DEPS target or command line.
429
+ xdc_file = ''
422
430
  else:
423
431
  default_xdc = True
424
432
  xdc_file = os.path.abspath(os.path.join(self.args['work-dir'],
@@ -427,7 +435,17 @@ class CommandSynthVivado(CommandSynth, ToolVivado):
427
435
 
428
436
  tcl_lines += [
429
437
  f"create_fileset -constrset constraints_1 {v}",
430
- f"add_files -fileset constraints_1 {xdc_file} {v}",
438
+ ]
439
+ for _file in self.files_sdc:
440
+ # NOTE - sdc files cannot (yet) be attached to other modules.
441
+ tcl_lines += [
442
+ f"add_files -fileset constraints_1 {_file} {v}",
443
+ ]
444
+ if xdc_file:
445
+ tcl_lines += [
446
+ f"add_files -fileset constraints_1 {xdc_file} {v}",
447
+ ]
448
+ tcl_lines += [
431
449
  "# FIRST PASS -- auto_detect_xpm",
432
450
  "synth_design -rtl -rtl_skip_ip -rtl_skip_constraints -no_timing_driven -no_iobuf " \
433
451
  + f"-top {top} {incdirs} {defines} {v}",
@@ -46,7 +46,7 @@ class ToolYosys(Tool):
46
46
  [self.sta_exe, '-version'], capture_output=True, check=False
47
47
  )
48
48
  util.debug(f'{self.yosys_exe} {sta_version_ret=}')
49
- sta_ver = sta_version_ret.stdout.decode('utf-8').split()[0]
49
+ sta_ver = sta_version_ret.stdout.decode('utf-8', errors='replace').split()[0]
50
50
  if sta_ver:
51
51
  self.sta_version = sta_ver
52
52
 
@@ -56,7 +56,7 @@ class ToolYosys(Tool):
56
56
  util.debug(f'{self.yosys_exe} {version_ret=}')
57
57
 
58
58
  # Yosys 0.48 (git sha1 aaa534749, clang++ 14.0.0-1ubuntu1.1 -fPIC -O3)
59
- words = version_ret.stdout.decode('utf-8').split()
59
+ words = version_ret.stdout.decode('utf-8', errors='replace').split()
60
60
 
61
61
  if len(words) < 2:
62
62
  self.error(f'{self.yosys_exe} --version: returned unexpected str {version_ret=}')
@@ -93,16 +93,27 @@ class CommonSynthYosys(CommandSynth, ToolYosys):
93
93
  'yosys-blackbox': [], # list of modules that yosys will blackbox.
94
94
  })
95
95
  self.args_help.update({
96
- 'sta': 'After running Yosys, run "sta" with --liberty-file.' \
97
- + ' sta can be installed via: https://github.com/The-OpenROAD-Project/OpenSTA',
98
- 'sdc-file': '.sdc file to use with --sta, if not present will use auto constraints',
99
- 'liberty-file': 'Single liberty file for synthesis and sta,' \
100
- + ' for example: github/OpenSTA/examples/nangate45_slow.lib.gz',
96
+ 'sta': (
97
+ 'After running Yosys, run "sta" with --liberty-file.'
98
+ ' sta can be installed via: https://github.com/The-OpenROAD-Project/OpenSTA'
99
+ ),
100
+ 'sdc-file': (
101
+ '.sdc file to use with --sta, if not present will use auto constraints.'
102
+ ' Note you can have .sdc files in "deps" of DEPS.yml targets.'
103
+ ),
104
+ 'liberty-file': (
105
+ 'Single liberty file for synthesis and sta,'
106
+ ' for example: github/OpenSTA/examples/nangate45_slow.lib.gz'
107
+ ),
101
108
  'yosys-synth': 'The synth command provided to Yosys, see: yosys help.',
102
- 'yosys-pre-synth': 'Yosys commands performed prior to running "synth"' \
103
- + ' (or eda arg value for --yosys-synth)',
104
- 'yosys-blackbox': 'List of modules that yosys will blackbox, likely will need these' \
105
- + ' in Verilog-2001 for yosys to read outside of slang and synth',
109
+ 'yosys-pre-synth': (
110
+ 'Yosys commands performed prior to running "synth"'
111
+ ' (or eda arg value for --yosys-synth)'
112
+ ),
113
+ 'yosys-blackbox': (
114
+ 'List of modules that yosys will blackbox, likely will need these'
115
+ ' in Verilog-2001 for yosys to read outside of slang and synth'
116
+ ),
106
117
  })
107
118
 
108
119
  self.yosys_out_dir = ''
@@ -193,6 +204,9 @@ class CommonSynthYosys(CommandSynth, ToolYosys):
193
204
  # Need to create sta.f:
194
205
  if self.args['sdc-file']:
195
206
  sdc_path = self.args['sdc-file']
207
+ elif self.files_sdc:
208
+ # Use files from DEPS target or command line.
209
+ sdc_path = ''
196
210
  else:
197
211
  # Need to create sdc.f:
198
212
  sdc_path = 'sdc.f'
@@ -204,9 +218,14 @@ class CommonSynthYosys(CommandSynth, ToolYosys):
204
218
  'read_liberty ' + self.args['liberty-file'],
205
219
  'read_verilog ' + self.yosys_v_path,
206
220
  'link_design ' + self.args['top'],
207
- 'read_sdc ' + sdc_path,
208
- 'report_checks',
209
221
  ]
222
+ for _file in self.files_sdc:
223
+ lines.append('read_sdc ' + _file)
224
+ if sdc_path:
225
+ lines.append('read_sdc ' + sdc_path)
226
+
227
+ lines.append('report_checks')
228
+
210
229
  f.write('\n'.join(lines))
211
230
 
212
231
  return util.ShellCommandList(
@@ -33,11 +33,10 @@ args = {
33
33
  'errors' : 0,
34
34
  }
35
35
 
36
-
37
- def strip_all_quotes(s:str):
36
+ def strip_all_quotes(s: str) -> str:
38
37
  return s.replace("'", '').replace('"', '')
39
38
 
40
- def strip_outer_quotes(s:str):
39
+ def strip_outer_quotes(s: str) -> str:
41
40
  ret = str(s)
42
41
  while (ret.startswith("'") and ret.endswith("'")) or \
43
42
  (ret.startswith('"') and ret.endswith('"')):
@@ -732,7 +731,7 @@ def subprocess_run_background(work_dir, command_list, background=True, fake:bool
732
731
  stderr = ''
733
732
  with open(tee_fpath, 'w') as f:
734
733
  for line in iter(proc.stdout.readline, b''):
735
- line = line.rstrip().decode("utf-8")
734
+ line = line.rstrip().decode("utf-8", errors="replace")
736
735
  if not background:
737
736
  print(line)
738
737
  f.write(line + '\n')
@@ -749,8 +748,8 @@ def subprocess_run_background(work_dir, command_list, background=True, fake:bool
749
748
  stdout, stderr = proc.communicate()
750
749
  rc = proc.returncode
751
750
 
752
- stdout = stdout.decode('utf-8') if stdout else ""
753
- stderr = stderr.decode('utf-8') if stderr else ""
751
+ stdout = stdout.decode('utf-8', errors="replace") if stdout else ""
752
+ stderr = stderr.decode('utf-8', errors="replace") if stderr else ""
754
753
  debug(f"shell_run_background: {rc=}")
755
754
  if stdout:
756
755
  for lineno, line in enumerate(stdout.strip().split('\n')):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: opencos-eda
3
- Version: 0.2.43
3
+ Version: 0.2.44
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
@@ -2,7 +2,7 @@
2
2
 
3
3
  [project]
4
4
  name = "opencos-eda"
5
- version = "0.2.43"
5
+ version = "0.2.44"
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