opencos-eda 0.2.43__py3-none-any.whl → 0.2.45__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 (36) hide show
  1. opencos/commands/__init__.py +4 -0
  2. opencos/commands/export.py +0 -1
  3. opencos/commands/flist.py +4 -1
  4. opencos/commands/lec.py +103 -0
  5. opencos/commands/shell.py +202 -0
  6. opencos/commands/sim.py +1 -1
  7. opencos/deps_helpers.py +2 -0
  8. opencos/eda.py +6 -3
  9. opencos/eda_base.py +12 -4
  10. opencos/eda_config.py +1 -1
  11. opencos/eda_config_defaults.yml +22 -7
  12. opencos/eda_extract_targets.py +13 -3
  13. opencos/export_helper.py +6 -4
  14. opencos/files.py +1 -0
  15. opencos/tests/helpers.py +3 -2
  16. opencos/tests/test_deps_schema.py +3 -1
  17. opencos/tests/test_eda.py +19 -9
  18. opencos/tests/test_eda_synth.py +63 -2
  19. opencos/tools/invio_yosys.py +7 -7
  20. opencos/tools/iverilog.py +1 -1
  21. opencos/tools/riviera.py +1 -1
  22. opencos/tools/slang.py +1 -1
  23. opencos/tools/slang_yosys.py +29 -38
  24. opencos/tools/surelog.py +1 -1
  25. opencos/tools/tabbycad_yosys.py +1 -1
  26. opencos/tools/verilator.py +1 -1
  27. opencos/tools/vivado.py +30 -11
  28. opencos/tools/yosys.py +465 -25
  29. opencos/util.py +19 -12
  30. {opencos_eda-0.2.43.dist-info → opencos_eda-0.2.45.dist-info}/METADATA +1 -1
  31. {opencos_eda-0.2.43.dist-info → opencos_eda-0.2.45.dist-info}/RECORD +36 -34
  32. {opencos_eda-0.2.43.dist-info → opencos_eda-0.2.45.dist-info}/WHEEL +0 -0
  33. {opencos_eda-0.2.43.dist-info → opencos_eda-0.2.45.dist-info}/entry_points.txt +0 -0
  34. {opencos_eda-0.2.43.dist-info → opencos_eda-0.2.45.dist-info}/licenses/LICENSE +0 -0
  35. {opencos_eda-0.2.43.dist-info → opencos_eda-0.2.45.dist-info}/licenses/LICENSE.spdx +0 -0
  36. {opencos_eda-0.2.43.dist-info → opencos_eda-0.2.45.dist-info}/top_level.txt +0 -0
opencos/tests/test_eda.py CHANGED
@@ -692,14 +692,22 @@ class TestDepsReqs:
692
692
  assert rc != 0
693
693
 
694
694
 
695
- @pytest.mark.skipif(not can_run_eda_sim(), reason='no tool found to handle command: sim')
696
- def test_deps_command_order():
695
+ @pytest.mark.parametrize("command", ['sim', 'shell'])
696
+ def test_deps_command_order(command):
697
697
  '''Test for various "commands" within a DEPS target. This test checks that command
698
698
  order is preserved in the top-to-bottom deps order, meaning that eda.py has to collect
699
699
  all commands deps order, and then execute them in that order.'''
700
700
 
701
701
  chdir_remove_work_dir('deps_files/command_order')
702
- cmd_list = 'sim --stop-before-compile target_test'.split()
702
+ if command == 'sim' and not can_run_eda_sim():
703
+ pytest.skip(f'sim skipped, {can_run_eda_sim()=}')
704
+ return # skip/pass
705
+
706
+ if command == 'shell':
707
+ cmd_list = 'shell target_test'.split()
708
+ else:
709
+ cmd_list = 'sim --stop-before-compile target_test'.split()
710
+
703
711
  with open('eda.log', 'w', encoding='utf-8') as f:
704
712
  with redirect_stdout(f):
705
713
  with redirect_stderr(f):
@@ -730,14 +738,16 @@ def test_deps_command_order():
730
738
  # Added check, we redirected to create eda.log earlier to confirm the targets worked,
731
739
  # but as a general eda.py check, all shell commands should create their own
732
740
  # {target}__shell_0.log file:
733
- work_dir = os.path.join(THISPATH, 'deps_files/command_order', 'eda.work', 'target_test.sim')
741
+ work_dir = os.path.join(
742
+ THISPATH, 'deps_files', 'command_order', 'eda.work', f'target_test.{command}'
743
+ )
734
744
  with open(os.path.join(work_dir, 'target_echo_hi__shell_0.log'), encoding='utf-8') as f:
735
745
  text = ''.join(f.readlines()).strip()
736
- assert text == 'hi'
746
+ assert text in ['hi', '"hi"', '\\"hi\\"']
737
747
  # Added check, one of the targets uses a custom 'tee' file name, instead of the default log.
738
748
  with open(os.path.join(work_dir, 'custom_tee_echo_bye.log'), encoding='utf-8') as f:
739
749
  text = ''.join(f.readlines()).strip()
740
- assert text == 'bye'
750
+ assert text in ['bye', '"bye"', '\\"bye\\"']
741
751
 
742
752
 
743
753
  @pytest.mark.skipif('verilator' not in tools_loaded, reason="requires verilator")
@@ -896,9 +906,9 @@ class TestDepsTags(Helpers):
896
906
  # b/c that should only apply in 'verilator' for this target.)
897
907
  exec_lines = self.get_log_lines_with('exec: ', logfile=logfile)
898
908
  assert len(exec_lines) == 3
899
- assert 'xvlog ' in exec_lines[0]
900
- assert 'xelab ' in exec_lines[1]
901
- assert 'xsim ' in exec_lines[2]
909
+ assert 'xvlog' in exec_lines[0]
910
+ assert 'xelab' in exec_lines[1]
911
+ assert 'xsim' in exec_lines[2]
902
912
  assert not self.is_in_log('--lint-only', logfile=logfile)
903
913
 
904
914
 
@@ -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
@@ -51,7 +51,7 @@ class CommandSynthInvioYosys(CommonSynthYosys, ToolInvioYosys):
51
51
  'invio-blackbox': 'List of modules that invio will blackbox prior to yosys',
52
52
  })
53
53
 
54
- def write_and_run_yosys_f_files(self, **kwargs) -> None:
54
+ def write_and_run_yosys_f_files(self) -> None:
55
55
 
56
56
  # Use helper module for Invio/Verific to save out Verilog-2001 from our
57
57
  # Verilog + SystemVerilog + VHDL file lists.
@@ -104,7 +104,7 @@ class CommandSynthInvioYosys(CommonSynthYosys, ToolInvioYosys):
104
104
  )
105
105
 
106
106
  # Optinally create and run a sta.f:
107
- sta_command_list = self.create_sta_f() # [] or util.ShellCommandList
107
+ sta_command_lists = self.create_sta_f() # [] or [util.ShellCommandList]
108
108
 
109
109
  # We create a run_yosys.sh wrapping these scripts, but we do not run this one.
110
110
  util.write_shell_command_file(
@@ -122,8 +122,7 @@ class CommandSynthInvioYosys(CommonSynthYosys, ToolInvioYosys):
122
122
  # Gives us bash commands with tee and pipstatus:
123
123
  invio_command_list,
124
124
  synth_command_list,
125
- sta_command_list,
126
- ],
125
+ ] + sta_command_lists,
127
126
  )
128
127
 
129
128
  # Do not run this if args['stop-before-compile'] is True
@@ -135,9 +134,10 @@ class CommandSynthInvioYosys(CommonSynthYosys, ToolInvioYosys):
135
134
  self.exec( work_dir=work_dir, command_list=synth_command_list,
136
135
  tee_fpath=synth_command_list.tee_fpath )
137
136
 
138
- if self.args['sta']:
139
- self.exec(work_dir=self.full_work_dir, command_list=sta_command_list,
140
- tee_fpath=sta_command_list.tee_fpath)
137
+ for x in sta_command_lists:
138
+ if self.args['sta'] and x:
139
+ self.exec(work_dir=self.full_work_dir, command_list=x,
140
+ tee_fpath=x.tee_fpath)
141
141
 
142
142
  if self.status == 0:
143
143
  util.info(f'yosys: wrote verilog to {self.yosys_v_path}')
opencos/tools/iverilog.py CHANGED
@@ -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]=}')
opencos/tools/riviera.py CHANGED
@@ -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
opencos/tools/slang.py CHANGED
@@ -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:
@@ -8,7 +8,7 @@ Contains classes for ToolSlangYosys, CommandSynthSlangYosys
8
8
  import os
9
9
 
10
10
  from opencos import util
11
- from opencos.tools.yosys import ToolYosys, CommonSynthYosys
11
+ from opencos.tools.yosys import ToolYosys, CommonSynthYosys, CommandLecYosys
12
12
 
13
13
  class ToolSlangYosys(ToolYosys):
14
14
  '''Uses slang.so in yosys plugins directory, called via yosys > plugin -i slang'''
@@ -37,7 +37,7 @@ class CommandSynthSlangYosys(CommonSynthYosys, ToolSlangYosys):
37
37
  self.slang_out_dir = ''
38
38
  self.slang_v_path = ''
39
39
 
40
- def write_and_run_yosys_f_files(self, **kwargs) -> None:
40
+ def write_and_run_yosys_f_files(self) -> None:
41
41
  '''
42
42
  1. Creates and runs: yosys.slang.f
43
43
  -- should create post_slang_ls.txt
@@ -54,7 +54,7 @@ class CommandSynthSlangYosys(CommonSynthYosys, ToolSlangYosys):
54
54
  self.blackbox_list += self.args.get('synth-blackbox', [])
55
55
  util.debug(f'slang_yosys: {self.blackbox_list=}')
56
56
 
57
- # create {work_dir} / yosys
57
+ # create {work_dir} / slang
58
58
  self.slang_out_dir = os.path.join(self.full_work_dir, 'slang')
59
59
  util.safe_mkdir(self.slang_out_dir)
60
60
 
@@ -62,13 +62,13 @@ class CommandSynthSlangYosys(CommonSynthYosys, ToolSlangYosys):
62
62
 
63
63
  # Run our created yosys.slang.f script
64
64
  # Note - this will always run, even if --stop-before-compile is set.
65
- slang_command_list = self._create_yosys_slang_f() # util.ShellCommandList
65
+ slang_command_list = self._create_and_run_yosys_slang_f() # util.ShellCommandList
66
66
 
67
67
  # Create and run yosys.synth.f
68
68
  synth_command_list = self.create_yosys_synth_f() # util.ShellCommandList
69
69
 
70
70
  # Optinally create and run a sta.f:
71
- sta_command_list = self.create_sta_f() # [] or util.ShellCommandList
71
+ sta_command_lists = self.create_sta_f() # [] or [util.ShellCommandList]
72
72
 
73
73
  # We create a run_yosys.sh wrapping these scripts, but we do not run this one.
74
74
  util.write_shell_command_file(
@@ -78,8 +78,7 @@ class CommandSynthSlangYosys(CommonSynthYosys, ToolSlangYosys):
78
78
  # Gives us bash commands with tee and pipstatus:
79
79
  slang_command_list,
80
80
  synth_command_list,
81
- sta_command_list,
82
- ],
81
+ ] + sta_command_lists,
83
82
  )
84
83
 
85
84
  # Do not run this if args['stop-before-compile'] is True
@@ -92,9 +91,10 @@ class CommandSynthSlangYosys(CommonSynthYosys, ToolSlangYosys):
92
91
  self.exec(work_dir=self.full_work_dir, command_list=synth_command_list,
93
92
  tee_fpath=synth_command_list.tee_fpath)
94
93
 
95
- if self.args['sta']:
96
- self.exec(work_dir=self.full_work_dir, command_list=sta_command_list,
97
- tee_fpath=sta_command_list.tee_fpath)
94
+ for x in sta_command_lists:
95
+ if self.args['sta'] and x:
96
+ self.exec(work_dir=self.full_work_dir, command_list=x,
97
+ tee_fpath=x.tee_fpath)
98
98
 
99
99
  if self.status == 0:
100
100
  util.info(f'yosys: wrote verilog to {self.yosys_v_path}')
@@ -108,36 +108,13 @@ class CommandSynthSlangYosys(CommonSynthYosys, ToolSlangYosys):
108
108
  '--best-effort-hierarchy',
109
109
  ]
110
110
 
111
- for name,value in self.defines.items():
112
- if not name:
113
- continue
114
- if name in ['SIMULATION']:
115
- continue
116
-
117
- if value is None:
118
- read_slang_cmd.append(f'--define-macro {name}')
119
- else:
120
- read_slang_cmd.append(f'--define-macro {name}={value}')
121
-
122
- # We must define SYNTHESIS for oclib_defines.vh to work correctly.
123
- if 'SYNTHESIS' not in self.defines:
124
- read_slang_cmd.append('--define-macro SYNTHESIS')
125
-
126
- for path in self.incdirs:
127
- read_slang_cmd.append(f'-I {path}')
128
-
129
- for path in self.files_v:
130
- read_slang_cmd.append(path)
131
-
132
- for path in self.files_sv:
133
- read_slang_cmd.append(path)
134
-
111
+ read_slang_cmd += self.get_yosys_read_verilog_defines_incdirs_files()
135
112
  read_slang_cmd.append(f'--top {self.args["top"]}')
136
113
  return ' '.join(read_slang_cmd)
137
114
 
138
115
 
139
- def _create_yosys_slang_f(self) -> util.ShellCommandList:
140
- '''Returns the util.ShellCommandList for: yosys --scriptfile yosys.slang.f'''
116
+ def _create_and_run_yosys_slang_f(self) -> util.ShellCommandList:
117
+ '''Runs, and Returns the util.ShellCommandList for: yosys --scriptfile yosys.slang.f'''
141
118
 
142
119
  script_slang_lines = [
143
120
  'plugin -i slang'
@@ -181,7 +158,7 @@ class CommandSynthSlangYosys(CommonSynthYosys, ToolSlangYosys):
181
158
  )
182
159
  return slang_command_list
183
160
 
184
- def _get_yosys_blackbox_list(self) -> list:
161
+ def get_yosys_blackbox_list(self) -> list:
185
162
  '''Based on the results in post_slang_ls.txt, create blackbox commands for
186
163
 
187
164
  yosys.synth.f script. Uses self.blackbox_list.
@@ -206,12 +183,14 @@ class CommandSynthSlangYosys(CommonSynthYosys, ToolSlangYosys):
206
183
  return yosys_blackbox_list
207
184
 
208
185
  def create_yosys_synth_f(self) -> util.ShellCommandList:
186
+ '''Overriden from CommonSynthYosys'''
187
+
209
188
  # Create yosys.synth.f
210
189
  yosys_synth_f_path = os.path.join(self.full_work_dir, 'yosys.synth.f')
211
190
 
212
191
  # Based on the results in post_slang_ls.txt, create blackbox commands for
213
192
  # yosys.synth.f script.
214
- yosys_blackbox_list = self._get_yosys_blackbox_list()
193
+ yosys_blackbox_list = self.get_yosys_blackbox_list()
215
194
 
216
195
  if self.args['liberty-file'] and not os.path.exists(self.args['liberty-file']):
217
196
  self.error(f'--liberty-file={self.args["liberty-file"]} file does not exist')
@@ -251,3 +230,15 @@ class CommandElabSlangYosys(CommandSynthSlangYosys): # pylint: disable=too-many-
251
230
  'stop-before-compile': True,
252
231
  'lint': True
253
232
  })
233
+
234
+ class CommandLecSlangYosys(CommandLecYosys, ToolSlangYosys): # pylint: disable=too-many-ancestors
235
+ '''CommandHandler for: eda lec --tool=slang_yosys
236
+
237
+ All steps from CommandLecYosys are re-used, except that using ToolSlangYosys
238
+ instead of ToolYosys. This is necessary so the default --synth arg will
239
+ synthesize the two designs using slang_yosys for the tool instead of yosys.
240
+ '''
241
+
242
+ def __init__(self, config: dict):
243
+ CommandLecYosys.__init__(self, config)
244
+ ToolSlangYosys.__init__(self, config=self.config)
opencos/tools/surelog.py CHANGED
@@ -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:
@@ -36,7 +36,7 @@ class CommandSynthTabbyCadYosys(CommonSynthYosys, ToolTabbyCadYosys):
36
36
  ToolTabbyCadYosys.__init__(self, config=self.config)
37
37
 
38
38
 
39
- def write_and_run_yosys_f_files(self, **kwargs) -> None:
39
+ def write_and_run_yosys_f_files(self) -> None:
40
40
  '''
41
41
  1. Creates and runs: yosys.verific.f
42
42
  -- should create post_verific_ls.txt
@@ -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:
opencos/tools/vivado.py CHANGED
@@ -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}",
@@ -631,10 +649,10 @@ class CommandBuildVivado(CommandBuild, ToolVivado):
631
649
  eda_path = eda_base.get_eda_exec('flist')
632
650
  command_list = [
633
651
  eda_path, 'flist',
634
- '--tool', self.args['tool'],
652
+ '--tool=' + self.args['tool'],
635
653
  self.args['top-path'],
636
654
  '--force',
637
- '--out', flist_file,
655
+ '--out=' + flist_file,
638
656
  #'--no-emit-incdir',
639
657
  #'--no-single-quote-define', # Needed to run in Command.exec( ... shell=False)
640
658
  '--no-quote-define',
@@ -644,11 +662,11 @@ class CommandBuildVivado(CommandBuild, ToolVivado):
644
662
  '--no-equal-define',
645
663
  '--bracket-quote-path',
646
664
  # on --prefix- items, use shlex.quote(str) so spaces work with subprocess shell=False:
647
- '--prefix-incdir', shlex.quote("oc_set_project_incdir "),
648
- '--prefix-define', shlex.quote("oc_set_project_define "),
649
- '--prefix-sv', shlex.quote("add_files -norecurse "),
650
- '--prefix-v', shlex.quote("add_files -norecurse "),
651
- '--prefix-vhd', shlex.quote("add_files -norecurse "),
665
+ '--prefix-incdir=' + shlex.quote("oc_set_project_incdir "),
666
+ '--prefix-define=' + shlex.quote("oc_set_project_define "),
667
+ '--prefix-sv=' + shlex.quote("add_files -norecurse "),
668
+ '--prefix-v=' + shlex.quote("add_files -norecurse "),
669
+ '--prefix-vhd=' + shlex.quote("add_files -norecurse "),
652
670
  ]
653
671
  for key,value in self.defines.items():
654
672
  if value is None:
@@ -656,6 +674,7 @@ class CommandBuildVivado(CommandBuild, ToolVivado):
656
674
  else:
657
675
  command_list += [ shlex.quote(f"+define+{key}={value}") ]
658
676
  cwd = util.getcwd()
677
+ util.debug(f"CommandBuildVivado: {cwd=}")
659
678
 
660
679
 
661
680
  # Write out a .sh command, but only for debug, it is not run.