opencos-eda 0.2.56__py3-none-any.whl → 0.3.0__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 (39) hide show
  1. opencos/_version.py +6 -3
  2. opencos/_waves_pkg.sv +34 -2
  3. opencos/commands/build.py +1 -0
  4. opencos/commands/export.py +1 -0
  5. opencos/commands/flist.py +1 -0
  6. opencos/commands/lec.py +5 -0
  7. opencos/commands/multi.py +4 -0
  8. opencos/commands/proj.py +1 -0
  9. opencos/commands/shell.py +4 -0
  10. opencos/commands/sim.py +47 -1
  11. opencos/commands/synth.py +4 -0
  12. opencos/deps/defaults.py +15 -7
  13. opencos/deps/deps_commands.py +84 -74
  14. opencos/deps_schema.py +3 -0
  15. opencos/eda.py +15 -2
  16. opencos/eda_base.py +61 -16
  17. opencos/eda_config.py +35 -1
  18. opencos/eda_config_defaults.yml +3 -1
  19. opencos/export_helper.py +5 -1
  20. opencos/tests/deps_files/command_order/DEPS.yml +11 -0
  21. opencos/tests/helpers.py +57 -19
  22. opencos/tests/test_deps_helpers.py +37 -25
  23. opencos/tests/test_eda.py +26 -60
  24. opencos/tools/cocotb.py +25 -0
  25. opencos/tools/modelsim_ase.py +65 -16
  26. opencos/tools/riviera.py +31 -6
  27. opencos/tools/slang_yosys.py +4 -1
  28. opencos/tools/verilator.py +28 -38
  29. opencos/tools/yosys.py +50 -20
  30. opencos/util.py +63 -12
  31. opencos/utils/vscode_helper.py +1 -1
  32. opencos/utils/vsim_helper.py +2 -3
  33. {opencos_eda-0.2.56.dist-info → opencos_eda-0.3.0.dist-info}/METADATA +2 -1
  34. {opencos_eda-0.2.56.dist-info → opencos_eda-0.3.0.dist-info}/RECORD +39 -39
  35. {opencos_eda-0.2.56.dist-info → opencos_eda-0.3.0.dist-info}/WHEEL +0 -0
  36. {opencos_eda-0.2.56.dist-info → opencos_eda-0.3.0.dist-info}/entry_points.txt +0 -0
  37. {opencos_eda-0.2.56.dist-info → opencos_eda-0.3.0.dist-info}/licenses/LICENSE +0 -0
  38. {opencos_eda-0.2.56.dist-info → opencos_eda-0.3.0.dist-info}/licenses/LICENSE.spdx +0 -0
  39. {opencos_eda-0.2.56.dist-info → opencos_eda-0.3.0.dist-info}/top_level.txt +0 -0
opencos/tools/cocotb.py CHANGED
@@ -100,9 +100,34 @@ class CommandSimCocotb(CommandSim, ToolCocotb):
100
100
  def set_tool_defines(self):
101
101
  ToolCocotb.set_tool_defines(self)
102
102
 
103
+ def help( # pylint: disable=dangerous-default-value
104
+ self, tokens: list = [], no_targets: bool = False
105
+ ) -> None:
106
+ '''Override for Command.help(...)'''
107
+
108
+ super().help(tokens=tokens, no_targets=no_targets)
109
+ self._warn_if_simulator_not_present()
110
+
111
+ def _warn_if_simulator_not_present(self) -> None:
112
+ '''Warn if --cocotb-simulator is not set, or if the exe is not in PATH'''
113
+
114
+ simulator = self.args['cocotb-simulator']
115
+ if not simulator:
116
+ util.warning('--cocotb-simulator is not set, a simulation cannot be run with'
117
+ 'this arg value')
118
+ return
119
+ exe = shutil.which(simulator)
120
+ if not exe:
121
+ util.warning(f'--cocotb-simulator={simulator}, {simulator} is not present in PATH',
122
+ 'a simulation cannot be run with this arg value')
123
+
124
+
103
125
  def prepare_compile(self):
104
126
  self.set_tool_defines()
105
127
 
128
+ # Check existence of cocotb-simulator:
129
+ self._warn_if_simulator_not_present()
130
+
106
131
  # Fix iverilog -> icarus
107
132
  if self.args['cocotb-simulator'] == 'iverilog':
108
133
  self.args['cocotb-simulator'] = 'icarus'
@@ -10,7 +10,7 @@ Note that this is for 32-bit Modelsim Student Edition. Consider using --tool=que
10
10
  import os
11
11
 
12
12
  from opencos import util
13
- from opencos.commands import CommandSim
13
+ from opencos.commands import sim, CommandSim
14
14
  from opencos.tools.questa import ToolQuesta
15
15
  from opencos.utils.str_helpers import sanitize_defines_for_sh
16
16
 
@@ -32,6 +32,14 @@ class CommandSimModelsimAse(CommandSim, ToolModelsimAse):
32
32
  self.args.update({
33
33
  'tool': self._TOOL, # override
34
34
  'gui': False,
35
+ 'vopt': self.use_vopt,
36
+ })
37
+ self.args_help.update({
38
+ 'vopt': (
39
+ 'Boolean to enable/disable use of vopt step prior to vsim step'
40
+ ' Note that vopt args can be controlled with --elab-args=<value1>'
41
+ ' --elab-args=<value2> ...'
42
+ )
35
43
  })
36
44
 
37
45
  def set_tool_defines(self):
@@ -158,7 +166,7 @@ class CommandSimModelsimAse(CommandSim, ToolModelsimAse):
158
166
  'compile-waivers',
159
167
  [ #defaults:
160
168
  '2275', # 2275 - Existing package 'foo_pkg' will be overwritten.
161
- ]):
169
+ ]) + self.args['compile-waivers']:
162
170
  vlog_dot_f_lines += ['-suppress', str(waiver)]
163
171
 
164
172
  if self.args['gui'] or self.args['waves']:
@@ -201,6 +209,44 @@ class CommandSimModelsimAse(CommandSim, ToolModelsimAse):
201
209
  with open(vlog_dot_f_fpath, 'w', encoding='utf-8') as f:
202
210
  f.writelines(line + "\n" for line in vlog_dot_f_lines)
203
211
 
212
+ def vopt_handle_parameters(self) -> (str, list):
213
+ '''Returns str for vopt or voptargs, and list of vopt tcl
214
+
215
+ Note this is used for self.use_vopt = True or False.
216
+ '''
217
+
218
+ voptargs_str = ''
219
+ vopt_do_lines = []
220
+
221
+ # Note that if self.use_vopt=True, we have to do some workarounds for how
222
+ # some questa-like tools behave for: tcl/.do + vopt arg processing
223
+ # This affects string based parameters that have spaces (vopt treats spaces unique args,
224
+ # vsim does not). Since we'd like to keep the vopt/vsim split into separate steps, we can
225
+ # work around this by setting tcl varaibles for each parameter.
226
+ if self.parameters:
227
+ if not self.use_vopt:
228
+ voptargs_str += ' ' + ' '.join(self.process_parameters_get_list(arg_prefix='-G'))
229
+ else:
230
+ for k,v in self.parameters.items():
231
+ s = sim.parameters_dict_get_command_list(params={k: v}, arg_prefix='')[0]
232
+ # At this point, s should be a str in form {k}={v}
233
+ if not s or '=' not in s:
234
+ continue
235
+ if ' ' in s:
236
+ # Instead of:
237
+ # vopt -GMyParam="hi bye"
238
+ # we'll do:
239
+ # set PARAMETERS(MyParam) "hi bye"
240
+ # vopt -GMyParam=$PARAMETERS(MyParam)
241
+ s = s.replace(f'{k}=', f'set PARAMETERS({k}) ')
242
+ vopt_do_lines.append(s)
243
+ voptargs_str += f' -G{k}=$PARAMETERS({k}) '
244
+ else:
245
+ voptargs_str += f' -G{s} '
246
+
247
+ return voptargs_str, vopt_do_lines
248
+
249
+
204
250
  def write_vsim_dot_do( # pylint: disable=too-many-locals
205
251
  self, dot_do_to_write: list
206
252
  ) -> None:
@@ -214,21 +260,25 @@ class CommandSimModelsimAse(CommandSim, ToolModelsimAse):
214
260
 
215
261
  sim_plusargs_str = self._get_sim_plusargs_str()
216
262
  vsim_suppress_list_str = self._get_vsim_suppress_list_str()
263
+ vsim_ext_args = ' '.join(self.args.get('sim-args', []))
217
264
 
218
- voptargs_str = ""
265
+ voptargs_str = self.tool_config.get('elab-args', '')
266
+ voptargs_str += ' '.join(self.args.get('elab-args', []))
219
267
  if self.args['gui'] or self.args['waves']:
220
- voptargs_str = self.tool_config.get('simulate-waves-args', '+acc')
268
+ voptargs_str += ' ' + self.tool_config.get('simulate-waves-args', '+acc')
221
269
  util.artifacts.add_extension(
222
270
  search_paths=self.args['work-dir'], file_extension='wlf',
223
271
  typ='waveform', description='Modelsim/Questa Waveform WLF (Wave Log Format) file'
224
272
  )
225
273
 
226
- # parameters
227
- if self.parameters:
228
- voptargs_str += ' ' + ' '.join(self.process_parameters_get_list(arg_prefix='-G'))
274
+ # TODO(drew): support self.args['sim_libary'] (1 lists)
275
+ vlog_do_lines = []
276
+ vsim_do_lines = []
277
+
278
+ # parameters, use helper method to get voptargs_str and vopt_do_lines
279
+ more_voptargs_str, vopt_do_lines = self.vopt_handle_parameters()
280
+ voptargs_str += more_voptargs_str
229
281
 
230
- # TODO(drew): support self.args['sim_libary', 'elab-args', sim-args'] (3 lists)
231
- # to add to vsim_one_liner.
232
282
 
233
283
  vopt_one_liner = ""
234
284
  if self.use_vopt:
@@ -241,17 +291,17 @@ class CommandSimModelsimAse(CommandSim, ToolModelsimAse):
241
291
 
242
292
  vsim_one_liner = "vsim -onfinish stop" \
243
293
  + f" -sv_seed {self.args['seed']} {sim_plusargs_str} {vsim_suppress_list_str}" \
244
- + f" opt__{self.args['top']}"
294
+ + f" {vsim_ext_args} opt__{self.args['top']}"
245
295
  else:
246
296
  # vopt doesn't exist, use single vsim call after vlog call:
247
297
  vsim_one_liner = "vsim -onfinish stop" \
248
298
  + f" -sv_seed {self.args['seed']} {sim_plusargs_str} {vsim_suppress_list_str}" \
249
- + f" {voptargs_str} work.{self.args['top']}"
299
+ + f" {voptargs_str} {vsim_ext_args} work.{self.args['top']}"
250
300
 
251
301
 
252
302
  vsim_one_liner = vsim_one_liner.replace('\n', ' ')
253
303
 
254
- vlog_do_lines = [
304
+ vlog_do_lines += [
255
305
  "if {[file exists work]} { vdel -all work; }",
256
306
  "vlib work;",
257
307
  "quietly set qc 30;",
@@ -263,9 +313,8 @@ class CommandSimModelsimAse(CommandSim, ToolModelsimAse):
263
313
  "}",
264
314
  ]
265
315
 
266
- vopt_do_lines = []
267
316
  if self.use_vopt:
268
- vopt_do_lines = [
317
+ vopt_do_lines += [
269
318
  "if {[catch { " + vopt_one_liner + " } result] } {",
270
319
  " echo \"Caught $result\";",
271
320
  " if {[batch_mode]} {",
@@ -274,7 +323,7 @@ class CommandSimModelsimAse(CommandSim, ToolModelsimAse):
274
323
  "}",
275
324
  ]
276
325
 
277
- vsim_do_lines = [
326
+ vsim_do_lines += [
278
327
  "if {[catch { " + vsim_one_liner + " } result] } {",
279
328
  " echo \"Caught $result\";",
280
329
  " if {[batch_mode]} {",
@@ -352,7 +401,7 @@ class CommandSimModelsimAse(CommandSim, ToolModelsimAse):
352
401
  #defaults:
353
402
  '3009', # 3009: [TSCALE] - Module 'foo' does not have a timeunit/timeprecision
354
403
  # specification in effect, but other modules do.
355
- ]):
404
+ ]) + self.args['sim-waivers']:
356
405
  vsim_suppress_list += ['-suppress', str(waiver)]
357
406
 
358
407
  return ' '.join(vsim_suppress_list)
opencos/tools/riviera.py CHANGED
@@ -59,6 +59,17 @@ class CommandSimRiviera(CommandSimModelsimAse, ToolRiviera):
59
59
  self.args.update({
60
60
  'tool': self._TOOL, # override
61
61
  'gui': False,
62
+ 'waves-fst': True,
63
+ 'waves-vcd': False,
64
+ })
65
+ self.args_help.update({
66
+ 'waves-fst': (
67
+ '(Default True) If using --waves, apply simulation runtime arg +trace.'
68
+ ' Note that if you do not have SV code using $dumpfile, eda will add'
69
+ ' _waves_pkg.sv to handle this for you with +trace runtime plusarg.'
70
+ ),
71
+ 'waves-vcd': 'If using --waves, apply simulation runtime arg +trace=vcd',
72
+ 'waves': 'Save a .asdb offline wavefile, can be used with --waves-fst or --waves-vcd',
62
73
  })
63
74
 
64
75
  def set_tool_defines(self):
@@ -76,6 +87,9 @@ class CommandSimRiviera(CommandSimModelsimAse, ToolRiviera):
76
87
 
77
88
  def compile(self):
78
89
  '''Override for CommandSimModelsimAse.compile() so we can set our own must_strings'''
90
+
91
+ self.add_waves_pkg_file()
92
+
79
93
  if self.args['stop-before-compile']:
80
94
  # don't run anything, save everyting we've already run in _prep_compile()
81
95
  return
@@ -126,7 +140,8 @@ class CommandSimRiviera(CommandSimModelsimAse, ToolRiviera):
126
140
  '-sv -input_ports net').split()
127
141
 
128
142
  # Add waivers from config.tool.riviera, convert to warning:
129
- for waiver in self.tool_config.get('compile-waivers', []):
143
+ for waiver in self.tool_config.get('compile-waivers', []) + \
144
+ self.args['compile-waivers']:
130
145
  vlog_dot_f_lines += [f'-err {waiver} W1']
131
146
 
132
147
  vlog_dot_f_fname = filename
@@ -167,6 +182,7 @@ class CommandSimRiviera(CommandSimModelsimAse, ToolRiviera):
167
182
  f.writelines(line + "\n" for line in vlog_dot_f_lines)
168
183
 
169
184
 
185
+
170
186
  def write_vsim_dot_do(self, dot_do_to_write: list) -> None:
171
187
  '''Writes files(s) based on dot_do_to_write(list of str)
172
188
 
@@ -177,9 +193,18 @@ class CommandSimRiviera(CommandSimModelsimAse, ToolRiviera):
177
193
  vsim_vlogonly_dot_do_fpath = os.path.join(self.args['work-dir'], 'vsim_vlogonly.do')
178
194
 
179
195
  sim_plusargs_str = self._get_sim_plusargs_str()
180
-
181
- voptargs_str = ""
182
- if self.args['gui'] or self.args['waves'] or self.args['coverage']:
196
+ vsim_ext_args = ' '.join(self.args.get('sim-args', []))
197
+
198
+ if self.args['waves'] and '+trace' not in sim_plusargs_str:
199
+ if self.args.get('waves-vcd', False):
200
+ sim_plusargs_str += ' +trace=vcd'
201
+ elif self.args.get('waves-fst', False):
202
+ sim_plusargs_str += ' +trace'
203
+
204
+ voptargs_str = self.tool_config.get('elab-args', '')
205
+ voptargs_str += ' '.join(self.args.get('elab-args', []))
206
+ if self.args['gui'] or self.args['waves'] or self.args['coverage']: # \
207
+ #or self.args['waves-asdb']:
183
208
  voptargs_str += self.tool_config.get('simulate-waves-args',
184
209
  '+accb +accr +access +r+w')
185
210
  if self.args['coverage']:
@@ -195,7 +220,7 @@ class CommandSimRiviera(CommandSimModelsimAse, ToolRiviera):
195
220
  vsim_one_liner = (
196
221
  "vsim"
197
222
  f" -sv_seed {self.args['seed']} {sim_plusargs_str}"
198
- f" {voptargs_str} work.{self.args['top']}"
223
+ f" {voptargs_str} {vsim_ext_args} work.{self.args['top']}"
199
224
  )
200
225
 
201
226
  vsim_one_liner = vsim_one_liner.replace('\n', ' ') # needs to be a one-liner
@@ -294,7 +319,7 @@ class CommandSimRiviera(CommandSimModelsimAse, ToolRiviera):
294
319
  for waiver in self.tool_config.get(
295
320
  'simulate-waivers', [
296
321
  #defaults: none
297
- ]):
322
+ ]) + self.args['sim-waivers']:
298
323
  vsim_suppress_list += ['-filter', str(waiver)]
299
324
 
300
325
  return ' '.join(vsim_suppress_list)
@@ -118,7 +118,10 @@ class CommandSynthSlangYosys(CommonSynthYosys, ToolSlangYosys):
118
118
  params=self.parameters, arg_prefix='-G '
119
119
  )
120
120
 
121
- read_slang_cmd.append(f'--top {self.args["top"]}')
121
+ # In case --top was not set:
122
+ if not any(x.startswith('--top') for x in read_slang_cmd):
123
+ read_slang_cmd.append(f'--top {self.args["top"]}')
124
+
122
125
  return ' '.join(read_slang_cmd)
123
126
 
124
127
 
@@ -76,6 +76,8 @@ class VerilatorSim(CommandSim, ToolVerilator):
76
76
  'gui': False,
77
77
  'tcl-file': None,
78
78
  'dump-vcd': False,
79
+ 'waves-fst': True,
80
+ 'waves-vcd': False,
79
81
  'lint-only': False,
80
82
  'cc-mode': False,
81
83
  'verilator-coverage-args': [],
@@ -84,12 +86,22 @@ class VerilatorSim(CommandSim, ToolVerilator):
84
86
  })
85
87
 
86
88
  self.args_help.update({
87
- 'waves': ('Include waveforms, if possible for Verilator by applying'
88
- ' simulation runtime arg +trace. User will need SV code to interpret the'
89
- 'plusarg and apply $dumpfile("dump.fst").'),
90
- 'dump-vcd': ('If using --waves, apply simulation runtime arg +trace=vcd. User'
91
- ' will need SV code to interpret the plusarg and apply'
92
- ' $dumpfile("dump.vcd").'),
89
+ 'waves': (
90
+ 'Include waveforms, if possible for Verilator by applying'
91
+ ' simulation runtime arg +trace. User will need SV code to interpret the'
92
+ ' plusarg and apply $dumpfile("dump.fst").'
93
+ ),
94
+ 'waves-fst': (
95
+ '(Default True) If using --waves, apply simulation runtime arg +trace.'
96
+ ' Note that if you do not have SV code using $dumpfile, eda will add'
97
+ ' _waves_pkg.sv to handle this for you with +trace runtime plusarg.'
98
+ ),
99
+ 'waves-vcd': (
100
+ 'If using --waves, apply simulation runtime arg +trace=vcd. User'
101
+ ' will need SV code to interpret the plusarg and apply'
102
+ ' $dumpfile("dump.vcd").'
103
+ ),
104
+ 'dump-vcd': 'Same as --waves-vcd',
93
105
  'lint-only': 'Run verilator with --lint-only, instead of --binary',
94
106
  'gui': 'Not supported for Verilator',
95
107
  'cc-mode': 'Run verilator with --cc, requires a sim_main.cpp or similar sources',
@@ -124,7 +136,7 @@ class VerilatorSim(CommandSim, ToolVerilator):
124
136
  if self.files_cpp:
125
137
  self.args['cc-mode'] = True
126
138
 
127
- self._add_verilator_waves_files()
139
+ self.add_waves_pkg_file()
128
140
 
129
141
  # Each of these should be a list of util.ShellCommandList()
130
142
  self.verilate_command_lists = self.get_compile_command_lists()
@@ -300,9 +312,10 @@ class VerilatorSim(CommandSim, ToolVerilator):
300
312
  # Built-in support for eda args --waves and/or --dump-vcd to become runtime
301
313
  # plusargs +trace or +trace=vcd, if +trace or +trace= was not already in our
302
314
  # plusargs.
303
- if self.args.get('dump-vcd', False):
315
+ if self.args.get('dump-vcd', False) or \
316
+ self.args.get('waves-vcd', False):
304
317
  sim_plusargs.append('+trace=vcd')
305
- else:
318
+ elif self.args.get('waves-fst', False):
306
319
  sim_plusargs.append('+trace')
307
320
 
308
321
  verilated_exec_command_list += config_sim_args + sim_plusargs + self.args['sim-args']
@@ -351,40 +364,17 @@ class VerilatorSim(CommandSim, ToolVerilator):
351
364
 
352
365
  return []
353
366
 
354
- def _add_verilator_waves_files(self) -> None:
367
+ def add_waves_pkg_file(self) -> None:
355
368
  '''If --waves present, and the user is missing any $dumpfile(), then adds a pre-written
356
369
  SystemVerilog package to their source code.
357
370
  '''
358
- if not self.args['waves']:
359
- return
360
371
  if self.args['cc-mode']:
361
- # We won't do this if the user bround a .cpp file and verilator not
372
+ # We won't do this if the user brought a .cpp file and verilator not
362
373
  # called with --binary.
363
374
  return
364
- found_dumpfile = False
365
- for fname in self.files_v + self.files_sv:
366
- if found_dumpfile:
367
- break
368
- with open(fname, encoding='utf-8') as f:
369
- for line in f.readlines():
370
- if '$dumpfile' in line:
371
- found_dumpfile = True
372
- break
373
- if not found_dumpfile:
374
- thispath = os.path.dirname(__file__)
375
- file_to_add = os.path.join(thispath, '..', '_waves_pkg.sv')
376
- util.info(f'--waves arg present, no $dumpfile found, adding SV file: {file_to_add}')
377
- self.add_file(file_to_add)
378
-
379
- # register .vcd or .fst artifacts:
380
- util.artifacts.add_extension(
381
- search_paths=self.args['work-dir'], file_extension='fst',
382
- typ='waveform', description='Simulation Waveform FST (Fast Signal Trace) file'
383
- )
384
- util.artifacts.add_extension(
385
- search_paths=self.args['work-dir'], file_extension='vcd',
386
- typ='waveform', description='Simulation Waveform VCD (Value Change Dump) file'
387
- )
375
+
376
+ super().add_waves_pkg_file() # call from CommandSim
377
+
388
378
 
389
379
 
390
380
  def _get_start_verilator_command_list(self, lint_only: bool = False) -> list:
@@ -415,7 +405,7 @@ class VerilatorSim(CommandSim, ToolVerilator):
415
405
  [ #defaults:
416
406
  'CASEINCOMPLETE',
417
407
  'TIMESCALEMOD', # If one file has `timescale, then they all must
418
- ]):
408
+ ]) + self.args['compile-waivers']:
419
409
  ret.append(f'-Wno-{waiver}')
420
410
  return ret
421
411
 
opencos/tools/yosys.py CHANGED
@@ -536,7 +536,6 @@ class CommandLecYosys(CommandLec, ToolYosys):
536
536
 
537
537
  self.synth_designs_tops = [None, None]
538
538
  self.synth_designs_fpaths = [None, None]
539
- self.synth_design_top_module_names = [None, None]
540
539
 
541
540
  def get_synth_result_fpath(self, target: str) -> str:
542
541
  '''Overridden from CommandLec'''
@@ -547,7 +546,7 @@ class CommandLecYosys(CommandLec, ToolYosys):
547
546
  def get_synth_command_list(self, design_num: int) -> list:
548
547
  '''Returns one of the synthesis command lists, for design_num=0 or 1'''
549
548
 
550
- if not design_num in [0, 1]:
549
+ if not design_num in (0, 1):
551
550
  self.error(f'{design_num=} we only support LEC on designs 0 and 1')
552
551
 
553
552
  synth_cmd_list = [
@@ -561,31 +560,43 @@ class CommandLecYosys(CommandLec, ToolYosys):
561
560
  synth_cmd_list += [
562
561
  '--work-dir=' + self.synth_work_dirs[design_num],
563
562
  self.args['designs'][design_num],
564
- f'--rename-module=Design{design_num + 1}'
563
+ f'--rename-module=Design{design_num + 1}',
565
564
  ]
566
565
 
567
- self.synth_design_top_module_names[design_num] = f'Design{design_num + 1}'
566
+ if self.args['flatten-all']:
567
+ # We have to do this or may get conflicts on black-boxed modules, but can
568
+ # be avoided with --no-flatten-all.
569
+ synth_cmd_list.append(
570
+ '--flatten-all'
571
+ )
572
+
573
+ self.synth_designs_tops[design_num] = f'Design{design_num + 1}'
568
574
 
569
575
  return synth_cmd_list
570
576
 
571
577
 
572
- def get_synth_top_from_output_config(self, design_num: int) -> str:
573
- '''Returns the top name given the design number that we synthesized'''
578
+ def get_synth_top_from_output_config(self, design_num: int) -> (str, str):
579
+ '''Returns the (orignal top name, module name) tuple given the design number
580
+
581
+ that we synthesized'''
574
582
 
575
583
  work_dir = self.synth_work_dirs[design_num]
576
584
  output_cfg_fpath = os.path.join(work_dir, eda_config.EDA_OUTPUT_CONFIG_FNAME)
577
585
  data = yaml_safe_load(output_cfg_fpath)
578
586
  top = data.get('args', {}).get('top', '')
579
- if not top:
587
+ rename_module = data.get('args', {}).get('rename-module', '')
588
+ if not top and not rename_module:
580
589
  self.error(f'"top" not found in synth run from {work_dir=} in',
581
590
  f'config {output_cfg_fpath}')
582
- return top
591
+ if not rename_module:
592
+ return top, top
593
+ return top, rename_module
583
594
 
584
595
 
585
596
  def get_synth_results_fpath(self, design_num: int, top: str) -> str:
586
- '''Returns the synthesized .v file fpath'''
597
+ '''Returns the synthesized .v file fpath, using orignal top (not renamed)'''
587
598
  if not top:
588
- top = self.get_synth_top_from_output_config(design_num=design_num)
599
+ top, _ = self.get_synth_top_from_output_config(design_num=design_num)
589
600
 
590
601
  work_dir = self.synth_work_dirs[design_num]
591
602
  fpath = os.path.join(work_dir, 'yosys', f'{top}.v')
@@ -594,7 +605,7 @@ class CommandLecYosys(CommandLec, ToolYosys):
594
605
  return fpath
595
606
 
596
607
 
597
- def do_it(self) -> None:
608
+ def do_it(self) -> None: # pylint: disable=too-many-locals,too-many-statements,too-many-branches
598
609
  self.set_tool_defines()
599
610
  self.write_eda_config_and_args()
600
611
 
@@ -622,6 +633,9 @@ class CommandLecYosys(CommandLec, ToolYosys):
622
633
  self.exec(work_dir=self.args['work-dir'], command_list=x,
623
634
  tee_fpath=x.tee_fpath)
624
635
 
636
+ util.info(f'LEC ran via --yosys-scriptfile: {self.args["yosys-scriptfile"]}')
637
+ return
638
+
625
639
 
626
640
  if self.args['synth']:
627
641
  synth1_cmd_list = self.get_synth_command_list(design_num=0)
@@ -636,23 +650,23 @@ class CommandLecYosys(CommandLec, ToolYosys):
636
650
  self.exec(pwd, synth2_cmd_list, background=True)
637
651
  util.info(f'Finished with 2nd LEC synthesis {self.args["designs"][1]}')
638
652
 
639
- self.synth_designs_tops = [
640
- self.get_synth_top_from_output_config(design_num=0),
641
- self.get_synth_top_from_output_config(design_num=1)
642
- ]
653
+
654
+ top0, module0 = self.get_synth_top_from_output_config(design_num=0)
655
+ top1, module1 = self.get_synth_top_from_output_config(design_num=1)
656
+ self.synth_designs_tops = [module0, module1]
643
657
  util.info(f'Design tops: {self.synth_designs_tops}')
644
658
 
645
659
  # read the output config
646
660
  self.synth_designs_fpaths = [
647
661
  os.path.abspath(
648
- self.get_synth_results_fpath(design_num=0, top=self.synth_designs_tops[0])),
662
+ self.get_synth_results_fpath(design_num=0, top=top0)),
649
663
  os.path.abspath(
650
- self.get_synth_results_fpath(design_num=1, top=self.synth_designs_tops[1]))
664
+ self.get_synth_results_fpath(design_num=1, top=top1))
651
665
  ]
652
666
  util.info(f'Design tops: {self.synth_designs_fpaths}')
653
667
 
654
668
  else:
655
- # don't run synthesis, need the two top level .v files in
669
+ # don't run synthesis, need the two top level .v|.sv files in
656
670
  # self.synth_designs_fpaths, and need the two top module names in
657
671
  # self.synth_designs_tops
658
672
  self.synth_designs_fpaths = [
@@ -660,11 +674,19 @@ class CommandLecYosys(CommandLec, ToolYosys):
660
674
  os.path.abspath(self.args['designs'][1])
661
675
  ]
662
676
 
677
+ for i in (0, 1):
678
+ if not os.path.isfile(self.args['designs'][i]):
679
+ self.error(
680
+ 'Using synth=False (--no-synth) --designs=<value> must be a single',
681
+ f'filename, however {self.args["designs"][i]} does not exist'
682
+ )
683
+
663
684
  path, fname = os.path.split(self.synth_designs_fpaths[0])
664
685
  module_guess, _ = os.path.splitext(fname)
665
686
  top1 = util.get_inferred_top_module_name(
666
687
  module_guess=module_guess, module_fpath=self.synth_designs_fpaths[0]
667
688
  )
689
+ util.debug(f'design1 {module_guess=} {fname=} {path=}')
668
690
  util.info(f'design1 top module name = {top1} (from {path} / {fname})')
669
691
 
670
692
  path, fname = os.path.split(self.synth_designs_fpaths[1])
@@ -672,6 +694,7 @@ class CommandLecYosys(CommandLec, ToolYosys):
672
694
  top2 = util.get_inferred_top_module_name(
673
695
  module_guess=module_guess, module_fpath=self.synth_designs_fpaths[1]
674
696
  )
697
+ util.debug(f'design2 {module_guess=} {fname=} {path=}')
675
698
  util.info(f'design2 top module name = {top2} (from {path} / {fname})')
676
699
 
677
700
  self.synth_designs_tops = [top1, top2]
@@ -691,13 +714,20 @@ class CommandLecYosys(CommandLec, ToolYosys):
691
714
  ]
692
715
  else:
693
716
  self.error(f' --pre-read-verilog file {x} does not exist')
717
+
718
+ nooverwrite = ''
719
+ if self.args['synth'] and not self.args['flatten-all']:
720
+ # If we don't flatten-all from synthesis, and we had to run synthesis,
721
+ # then read the 2nd file with -overwrite
722
+ nooverwrite = '-nooverwrite'
723
+
694
724
  lec_cmd_f_list += [
695
725
  '# Design1 (module):',
696
726
  f'read_verilog -sv -icells {self.synth_designs_fpaths[0]}',
697
727
  '# Design2 (module):',
698
- f'read_verilog -sv -icells {self.synth_designs_fpaths[1]}',
728
+ f'read_verilog -sv -icells {self.synth_designs_fpaths[1]} {nooverwrite}',
699
729
  'clk2fflogic;',
700
- f'miter -equiv -flatten {" ".join(self.synth_design_top_module_names)} miter',
730
+ f'miter -equiv -flatten {" ".join(self.synth_designs_tops)} miter',
701
731
  ('sat -seq 50 -verify -prove trigger 0 -show-all -show-inputs -show-outputs'
702
732
  ' -set-init-zero miter'),
703
733
  ]