opencos-eda 0.2.55__py3-none-any.whl → 0.2.57__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/flist.py +10 -0
  2. opencos/commands/lec.py +4 -0
  3. opencos/commands/multi.py +4 -0
  4. opencos/commands/sim.py +31 -0
  5. opencos/deps/defaults.py +2 -0
  6. opencos/deps/deps_processor.py +39 -1
  7. opencos/deps_schema.py +22 -0
  8. opencos/eda.py +13 -1
  9. opencos/eda_base.py +108 -10
  10. opencos/eda_config.py +37 -2
  11. opencos/eda_config_defaults.yml +1 -0
  12. opencos/export_helper.py +20 -6
  13. opencos/hw/oc_cli.py +1 -1
  14. opencos/names.py +4 -1
  15. opencos/tests/helpers.py +9 -0
  16. opencos/tests/test_eda_synth.py +12 -0
  17. opencos/tools/cocotb.py +25 -0
  18. opencos/tools/invio_helpers.py +25 -4
  19. opencos/tools/iverilog.py +5 -0
  20. opencos/tools/modelsim_ase.py +52 -6
  21. opencos/tools/quartus.py +32 -6
  22. opencos/tools/riviera.py +17 -3
  23. opencos/tools/slang.py +5 -0
  24. opencos/tools/slang_yosys.py +13 -1
  25. opencos/tools/surelog.py +5 -0
  26. opencos/tools/verilator.py +5 -0
  27. opencos/tools/vivado.py +15 -2
  28. opencos/tools/yosys.py +50 -20
  29. opencos/utils/vsim_helper.py +1 -2
  30. {opencos_eda-0.2.55.dist-info → opencos_eda-0.2.57.dist-info}/METADATA +1 -1
  31. {opencos_eda-0.2.55.dist-info → opencos_eda-0.2.57.dist-info}/RECORD +36 -36
  32. {opencos_eda-0.2.55.dist-info → opencos_eda-0.2.57.dist-info}/WHEEL +0 -0
  33. {opencos_eda-0.2.55.dist-info → opencos_eda-0.2.57.dist-info}/entry_points.txt +0 -0
  34. {opencos_eda-0.2.55.dist-info → opencos_eda-0.2.57.dist-info}/licenses/LICENSE +0 -0
  35. {opencos_eda-0.2.55.dist-info → opencos_eda-0.2.57.dist-info}/licenses/LICENSE.spdx +0 -0
  36. {opencos_eda-0.2.55.dist-info → opencos_eda-0.2.57.dist-info}/top_level.txt +0 -0
opencos/tests/helpers.py CHANGED
@@ -39,6 +39,15 @@ def can_run_eda_command(*commands, config: dict) -> bool:
39
39
  if handler and getattr(handler, 'CHECK_REQUIRES', []):
40
40
  if not all(issubclass(handler, x) for x in getattr(handler, 'CHECK_REQUIRES', [])):
41
41
  return False
42
+
43
+ # We cannot run tools that have disable-auto set:
44
+ tool = getattr(handler, '_TOOL', '')
45
+ if handler and tool:
46
+ entry = config['auto_tools_order'][0].get(tool, {})
47
+ if entry and entry.get('disable-auto', False):
48
+ # This tool cannot automatically run our command.
49
+ return False
50
+
42
51
  runnable.append(True)
43
52
  return runnable and all(runnable)
44
53
 
@@ -8,6 +8,7 @@ import pytest
8
8
  from opencos import eda, eda_tool_helper
9
9
  from opencos.tests import helpers
10
10
  from opencos.tests.helpers import Helpers
11
+ from opencos.utils.markup_helpers import yaml_safe_load
11
12
 
12
13
 
13
14
  THISPATH = os.path.dirname(__file__)
@@ -108,6 +109,17 @@ class TestsSlangYosys(Helpers):
108
109
  rc = self.log_it(cmd_str, use_eda_wrap=False)
109
110
  assert rc == 0
110
111
 
112
+ # Since vanilla yosys won't use the SDC file, let's at least confirm
113
+ # that EDA used it and tracked it:
114
+ eda_config_yml_path = os.path.join(
115
+ os.getcwd(), 'eda.work', 'oclib_fifo_with_sdc.synth', 'eda_output_config.yml'
116
+ )
117
+ data = yaml_safe_load(eda_config_yml_path)
118
+ assert 'files_sdc' in data
119
+ assert data['files_sdc']
120
+ assert data['files_sdc'][0].endswith('oclib_fifo_yosys.sdc')
121
+
122
+
111
123
  @pytest.mark.skipif('vivado' not in tools_loaded, reason="requires vivado")
112
124
  @pytest.mark.skipif(not vivado_has_xpms(), reason="requires install to have XPMs")
113
125
  class TestsVivado(Helpers):
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'
@@ -61,6 +61,12 @@ def write_py_file(
61
61
  'from invio import init, define_macro, add_include_directory, \\',
62
62
  ' add_verilog_file, add_sv_file, elaborate, elaborate_pct, analyze, \\',
63
63
  ' print_instance_hierarchy, write_design, report_analyzed_files',
64
+ ]
65
+ if command_design_obj.parameters:
66
+ lines += [
67
+ 'from invio import get_parameters, report_parameters, replace_expression_of_parameter'
68
+ ]
69
+ lines += [
64
70
  'import os, shutil',
65
71
  '',
66
72
  'for p in ["invio"]:',
@@ -138,18 +144,34 @@ def write_py_file(
138
144
  tee_fpath = 'invio.log')]
139
145
  })
140
146
 
147
+ lines += [
148
+ 'assert analyze()',
149
+ ]
150
+
151
+ if command_design_obj.parameters:
152
+ lines += [
153
+ '',
154
+ f'new_parameters = {command_design_obj.parameters}',
155
+ 'for x in get_parameters():',
156
+ ' name_parts = x.full_name.split("::")',
157
+ ' mod, pname = name_parts[-2], name_parts[-1]',
158
+ f' if pname in new_parameters and mod == "{top}":',
159
+ ' new_value = str(new_parameters[pname]) # invio needs str type for all',
160
+ ' print(f"PARAMETER UPDATE: {mod}.{pname} ---> {new_value}")',
161
+ ' replace_expression_of_parameter(x, new_value)',
162
+ 'report_parameters()',
163
+ '',
164
+ ]
165
+
141
166
  if sim_lint:
142
167
  # lint (skip elaborate steps -- from eda.CommandLintInvio)
143
168
  lines += [
144
- 'assert analyze()',
145
- '',
146
169
  'report_analyzed_files()',
147
170
  'print_instance_hierarchy()',
148
171
  ]
149
172
  elif sim_elab:
150
173
  # elab (non-synthesis), runs the following (from eda.CommandElabInvio)
151
174
  lines += [
152
- 'assert analyze()',
153
175
  f"assert elaborate('{top}', pct_elaboration=True, forceBlackbox={blackbox_list})",
154
176
  'assert elaborate_pct()',
155
177
  '',
@@ -159,7 +181,6 @@ def write_py_file(
159
181
  else:
160
182
  # synthesis-style elab (from eda.CommandElabInvioYosys)
161
183
  lines += [
162
- 'assert analyze()',
163
184
  f"assert elaborate('{top}', rtl_elaboration=True, forceBlackbox={blackbox_list})",
164
185
  '',
165
186
  'report_analyzed_files()',
opencos/tools/iverilog.py CHANGED
@@ -136,6 +136,11 @@ class CommandSimIverilog(CommandSim, ToolIverilog):
136
136
  # +define+{k}={v}, but also for SystemVerilog plusargs
137
137
  command_list += [ '-D', f'{k}={sanitize_defines_for_sh(v)}' ]
138
138
 
139
+ # parameters
140
+ command_list.extend(
141
+ self.process_parameters_get_list(arg_prefix=f'-P{self.args["top"]}.')
142
+ )
143
+
139
144
  if not self.files_sv and not self.files_v:
140
145
  if not self.args['stop-before-compile']:
141
146
  self.error(f'{self.target=} {self.files_sv=} and {self.files_v=} are empty,',
@@ -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
 
@@ -94,7 +94,8 @@ class CommandSimModelsimAse(CommandSim, ToolModelsimAse):
94
94
  return
95
95
  if self.args['stop-after-compile']:
96
96
  vsim_command_lists = self.get_compile_command_lists()
97
- self.run_commands_check_logs(vsim_command_lists, log_filename='sim.log')
97
+ self.run_commands_check_logs(vsim_command_lists, log_filename='sim.log',
98
+ must_strings=['Errors: 0'], use_must_strings=False)
98
99
 
99
100
  def elaborate(self):
100
101
  if self.args['stop-before-compile']:
@@ -200,6 +201,44 @@ class CommandSimModelsimAse(CommandSim, ToolModelsimAse):
200
201
  with open(vlog_dot_f_fpath, 'w', encoding='utf-8') as f:
201
202
  f.writelines(line + "\n" for line in vlog_dot_f_lines)
202
203
 
204
+ def vopt_handle_parameters(self) -> (str, list):
205
+ '''Returns str for vopt or voptargs, and list of vopt tcl
206
+
207
+ Note this is used for self.use_vopt = True or False.
208
+ '''
209
+
210
+ voptargs_str = ''
211
+ vopt_do_lines = []
212
+
213
+ # Note that if self.use_vopt=True, we have to do some workarounds for how
214
+ # some questa-like tools behave for: tcl/.do + vopt arg processing
215
+ # This affects string based parameters that have spaces (vopt treats spaces unique args,
216
+ # vsim does not). Since we'd like to keep the vopt/vsim split into separate steps, we can
217
+ # work around this by setting tcl varaibles for each parameter.
218
+ if self.parameters:
219
+ if not self.use_vopt:
220
+ voptargs_str += ' ' + ' '.join(self.process_parameters_get_list(arg_prefix='-G'))
221
+ else:
222
+ for k,v in self.parameters.items():
223
+ s = sim.parameters_dict_get_command_list(params={k: v}, arg_prefix='')[0]
224
+ # At this point, s should be a str in form {k}={v}
225
+ if not s or '=' not in s:
226
+ continue
227
+ if ' ' in s:
228
+ # Instead of:
229
+ # vopt -GMyParam="hi bye"
230
+ # we'll do:
231
+ # set PARAMETERS(MyParam) "hi bye"
232
+ # vopt -GMyParam=$PARAMETERS(MyParam)
233
+ s = s.replace(f'{k}=', f'set PARAMETERS({k}) ')
234
+ vopt_do_lines.append(s)
235
+ voptargs_str += f' -G{k}=$PARAMETERS({k}) '
236
+ else:
237
+ voptargs_str += f' -G{s} '
238
+
239
+ return voptargs_str, vopt_do_lines
240
+
241
+
203
242
  def write_vsim_dot_do( # pylint: disable=too-many-locals
204
243
  self, dot_do_to_write: list
205
244
  ) -> None:
@@ -222,6 +261,14 @@ class CommandSimModelsimAse(CommandSim, ToolModelsimAse):
222
261
  typ='waveform', description='Modelsim/Questa Waveform WLF (Wave Log Format) file'
223
262
  )
224
263
 
264
+ vlog_do_lines = []
265
+ vsim_do_lines = []
266
+
267
+ # parameters, use helper method to get voptargs_str and vopt_do_lines
268
+ more_voptargs_str, vopt_do_lines = self.vopt_handle_parameters()
269
+ voptargs_str += more_voptargs_str
270
+
271
+
225
272
  # TODO(drew): support self.args['sim_libary', 'elab-args', sim-args'] (3 lists)
226
273
  # to add to vsim_one_liner.
227
274
 
@@ -246,7 +293,7 @@ class CommandSimModelsimAse(CommandSim, ToolModelsimAse):
246
293
 
247
294
  vsim_one_liner = vsim_one_liner.replace('\n', ' ')
248
295
 
249
- vlog_do_lines = [
296
+ vlog_do_lines += [
250
297
  "if {[file exists work]} { vdel -all work; }",
251
298
  "vlib work;",
252
299
  "quietly set qc 30;",
@@ -258,9 +305,8 @@ class CommandSimModelsimAse(CommandSim, ToolModelsimAse):
258
305
  "}",
259
306
  ]
260
307
 
261
- vopt_do_lines = []
262
308
  if self.use_vopt:
263
- vopt_do_lines = [
309
+ vopt_do_lines += [
264
310
  "if {[catch { " + vopt_one_liner + " } result] } {",
265
311
  " echo \"Caught $result\";",
266
312
  " if {[batch_mode]} {",
@@ -269,7 +315,7 @@ class CommandSimModelsimAse(CommandSim, ToolModelsimAse):
269
315
  "}",
270
316
  ]
271
317
 
272
- vsim_do_lines = [
318
+ vsim_do_lines += [
273
319
  "if {[catch { " + vsim_one_liner + " } result] } {",
274
320
  " echo \"Caught $result\";",
275
321
  " if {[batch_mode]} {",
opencos/tools/quartus.py CHANGED
@@ -19,6 +19,7 @@ from opencos.eda_base import Tool
19
19
  from opencos.commands import (
20
20
  CommandSynth, CommandBuild, CommandFList, CommandProj, CommandUpload, CommandOpen
21
21
  )
22
+ from opencos.utils.str_helpers import sanitize_defines_for_sh, strip_outer_quotes
22
23
 
23
24
  class ToolQuartus(Tool):
24
25
  '''ToolQuartus used by opencos.eda for --tool=quartus'''
@@ -225,6 +226,18 @@ class CommandSynthQuartus(CommandSynth, ToolQuartus):
225
226
  for incdir in self.incdirs:
226
227
  tcl_lines.append(f"set_global_assignment -name SEARCH_PATH \"{incdir}\"")
227
228
 
229
+ # Parameters --> set_parameter -name <Parameter_Name> <Value>
230
+ for k,v in self.parameters.items():
231
+ if not isinstance(v, (int, str)):
232
+ util.warning(f'parameter {k} has value: {v}, parameters must be int/string types')
233
+ if isinstance(v, int):
234
+ tcl_lines.append(f"set_parameter -name {k} {v}")
235
+ else:
236
+ v = strip_outer_quotes(v.strip('\n'))
237
+ v = '"' + v + '"'
238
+ tcl_lines.append(f"set_parameter -name {k} {sanitize_defines_for_sh(v)}")
239
+
240
+
228
241
  # Add all include directories as user libraries for better include resolution
229
242
  for incdir in self.incdirs:
230
243
  if os.path.exists(incdir):
@@ -307,7 +320,7 @@ class CommandBuildQuartus(CommandBuild, ToolQuartus):
307
320
  'flow-tcl-files': [],
308
321
  })
309
322
 
310
- def do_it(self) -> None: # pylint: disable=too-many-branches,too-many-statements
323
+ def do_it(self) -> None: # pylint: disable=too-many-branches,too-many-statements,too-many-locals
311
324
  # add defines for this job
312
325
  self.set_tool_defines()
313
326
  self.write_eda_config_and_args()
@@ -340,15 +353,25 @@ class CommandBuildQuartus(CommandBuild, ToolQuartus):
340
353
  # create an eda.flist_input.f that we'll pass to flist:
341
354
  with open(os.path.join(self.args['work-dir'], 'eda.flist_input.f'),
342
355
  'w', encoding='utf-8') as f:
356
+
357
+ # defines
358
+ for key,value in self.defines.items():
359
+ if value is None:
360
+ f.write(f"+define+{key}\n")
361
+ else:
362
+ f.write(shlex.quote(f"+define+{key}={value}") + "\n")
363
+
364
+ # incdirs:
365
+ for incdir in self.incdirs:
366
+ f.write(f'+incdir+{incdir}\n')
367
+
368
+ # files:
343
369
  f.write('\n'.join(self.files_v + self.files_sv + self.files_vhd + ['']))
370
+
371
+
344
372
  command_list.append('--input-file=eda.flist_input.f')
345
373
 
346
374
 
347
- for key,value in self.defines.items():
348
- if value is None:
349
- command_list += [ f"+define+{key}" ]
350
- else:
351
- command_list += [ shlex.quote(f"+define+{key}={value}") ]
352
375
 
353
376
  # Write out a .sh command for debug
354
377
  command_list = util.ShellCommandList(command_list, tee_fpath='run_eda_flist.log')
@@ -487,6 +510,9 @@ class CommandFListQuartus(CommandFList, ToolQuartus):
487
510
  def __init__(self, config: dict):
488
511
  CommandFList.__init__(self, config=config)
489
512
  ToolQuartus.__init__(self, config=self.config)
513
+ self.args.update({
514
+ 'emit-parameter': False
515
+ })
490
516
 
491
517
 
492
518
  class CommandProjQuartus(CommandProj, ToolQuartus):
opencos/tools/riviera.py CHANGED
@@ -74,6 +74,17 @@ class CommandSimRiviera(CommandSimModelsimAse, ToolRiviera):
74
74
  # Note: many of these we follow the same flow as CommandSimModelsimAse:
75
75
  # do_it, prepare_compile, compile, elaborate, simulate
76
76
 
77
+ def compile(self):
78
+ '''Override for CommandSimModelsimAse.compile() so we can set our own must_strings'''
79
+ if self.args['stop-before-compile']:
80
+ # don't run anything, save everyting we've already run in _prep_compile()
81
+ return
82
+ if self.args['stop-after-compile']:
83
+ vsim_command_lists = self.get_compile_command_lists()
84
+ self.run_commands_check_logs(vsim_command_lists, log_filename='sim.log',
85
+ must_strings=['Compile success 0 Errors'],
86
+ use_must_strings=False)
87
+
77
88
  def get_compile_command_lists(self, **kwargs) -> list:
78
89
  # This will also set up a compile.
79
90
  vsim_command_list = [
@@ -128,7 +139,6 @@ class CommandSimRiviera(CommandSimModelsimAse, ToolRiviera):
128
139
  if v is None:
129
140
  vlog_dot_f_lines += [ f'+define+{k}' ]
130
141
  else:
131
-
132
142
  # if the value v is a double-quoted string, such as v='"hi"', the
133
143
  # entire +define+NAME="hi" needs to wrapped in double quotes with the
134
144
  # value v double-quotes escaped: "+define+NAME=\"hi\""
@@ -170,11 +180,15 @@ class CommandSimRiviera(CommandSimModelsimAse, ToolRiviera):
170
180
 
171
181
  voptargs_str = ""
172
182
  if self.args['gui'] or self.args['waves'] or self.args['coverage']:
173
- voptargs_str = self.tool_config.get('simulate-waves-args',
174
- '+accb +accr +access +r+w')
183
+ voptargs_str += self.tool_config.get('simulate-waves-args',
184
+ '+accb +accr +access +r+w')
175
185
  if self.args['coverage']:
176
186
  voptargs_str += self.tool_config.get('coverage-args', '')
177
187
 
188
+ # parameters
189
+ if self.parameters:
190
+ voptargs_str += ' ' + ' '.join(self.process_parameters_get_list(arg_prefix='-G'))
191
+
178
192
  # TODO(drew): support self.args['sim_libary', 'elab-args', sim-args'] (3 lists)
179
193
  # to add to vsim_one_liner.
180
194
 
opencos/tools/slang.py CHANGED
@@ -143,6 +143,11 @@ class CommandElabSlang(CommandElab, ToolSlang):
143
143
  # --define-macro {k}={v}
144
144
  command_list.append( f'{k}={sanitize_defines_for_sh(v)}' )
145
145
 
146
+ # parameters
147
+ command_list.extend(
148
+ self.process_parameters_get_list(arg_prefix='-G ')
149
+ )
150
+
146
151
  # Because many elab target-name won't match the --top needed for
147
152
  # slang, we'll leave this to arg --slang-top:
148
153
  if self.args.get('slang-top', None):
@@ -10,6 +10,8 @@ import os
10
10
  from opencos import util
11
11
  from opencos.tools.yosys import ToolYosys, CommonSynthYosys, CommandLecYosys
12
12
 
13
+ from opencos.commands.sim import parameters_dict_get_command_list
14
+
13
15
  class ToolSlangYosys(ToolYosys):
14
16
  '''Uses slang.so in yosys plugins directory, called via yosys > plugin -i slang'''
15
17
  _TOOL = 'slang_yosys'
@@ -109,7 +111,17 @@ class CommandSynthSlangYosys(CommonSynthYosys, ToolSlangYosys):
109
111
  ]
110
112
 
111
113
  read_slang_cmd += self.get_yosys_read_verilog_defines_incdirs_files()
112
- read_slang_cmd.append(f'--top {self.args["top"]}')
114
+
115
+ # For slang step, need to resolve parameters too. We do NOT do this on
116
+ # subsquent yosys read_verilog steps.
117
+ read_slang_cmd += parameters_dict_get_command_list(
118
+ params=self.parameters, arg_prefix='-G '
119
+ )
120
+
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
+
113
125
  return ' '.join(read_slang_cmd)
114
126
 
115
127
 
opencos/tools/surelog.py CHANGED
@@ -109,6 +109,11 @@ class CommandElabSurelog(CommandElab, ToolSurelog):
109
109
  for value in self.incdirs:
110
110
  command_list.append('+incdir+' + value)
111
111
 
112
+ # parameters
113
+ command_list.extend(
114
+ self.process_parameters_get_list(arg_prefix='-P')
115
+ )
116
+
112
117
  # defines:
113
118
  for k,v in self.defines.items():
114
119
  if v is None:
@@ -244,6 +244,11 @@ class VerilatorSim(CommandSim, ToolVerilator):
244
244
  # +define+{k}={v}, but also for SystemVerilog plusargs
245
245
  verilate_command_list += [ f'+define+{k}={sanitize_defines_for_sh(v)}' ]
246
246
 
247
+ # parameters
248
+ verilate_command_list.extend(
249
+ self.process_parameters_get_list(arg_prefix='-G')
250
+ )
251
+
247
252
  if not self.files_sv and not self.files_v:
248
253
  if not self.args['stop-before-compile']:
249
254
  self.error(f'{self.target=} {self.files_sv=} and {self.files_v=} are empty,',
opencos/tools/vivado.py CHANGED
@@ -22,6 +22,9 @@ from opencos.eda_base import Tool
22
22
  from opencos.commands import CommandSim, CommandSynth, CommandProj, CommandBuild, \
23
23
  CommandFList, CommandUpload, CommandOpen
24
24
 
25
+ from opencos.commands import sim
26
+
27
+
25
28
  class ToolVivado(Tool):
26
29
  '''ToolVivado used by opencos.eda for --tool=vivado'''
27
30
 
@@ -214,6 +217,12 @@ class CommandSimVivado(CommandSim, ToolVivado):
214
217
  command_list[0] += ".bat"
215
218
  command_list += self.tool_config.get('elab-args',
216
219
  '-s snapshot -timescale 1ns/1ps --stats').split()
220
+
221
+ # parameters
222
+ command_list.extend(
223
+ self.process_parameters_get_list(arg_prefix='-generic_top ')
224
+ )
225
+
217
226
  if self.tool_config.get('elab-waves-args', ''):
218
227
  command_list += self.tool_config.get('elab-waves-args', '').split()
219
228
  elif self.args['gui'] and self.args['waves']:
@@ -412,6 +421,9 @@ class CommandSynthVivado(CommandSynth, ToolVivado):
412
421
  defines = ""
413
422
  for key, value in self.defines.items():
414
423
  defines += (f"-verilog_define {key}" + (" " if value is None else f"={value} "))
424
+ parameters = ' '.join(
425
+ sim.parameters_dict_get_command_list(params=self.parameters, arg_prefix='-generic ')
426
+ )
415
427
  incdirs = ' '.join([f'-include_dirs {x}' for x in self.incdirs])
416
428
  flatten = ""
417
429
  if self.args['flatten-all']:
@@ -457,10 +469,10 @@ class CommandSynthVivado(CommandSynth, ToolVivado):
457
469
  tcl_lines += [
458
470
  "# FIRST PASS -- auto_detect_xpm",
459
471
  "synth_design -rtl -rtl_skip_ip -rtl_skip_constraints -no_timing_driven -no_iobuf " \
460
- + f"-top {top} {incdirs} {defines} {v}",
472
+ + f"-top {top} {incdirs} {defines} {parameters} {v}",
461
473
  f"auto_detect_xpm {v} ",
462
474
  f"synth_design -no_iobuf -part {part} {flatten} -constrset constraints_1 " \
463
- + f"-top {top} {incdirs} {defines} {v}",
475
+ + f"-top {top} {incdirs} {defines} {parameters} {v}",
464
476
  f"write_verilog -force {top}.vg {v}",
465
477
  f"report_utilization -file {top}.flat.util.rpt {v}",
466
478
  f"report_utilization -file {top}.hier.util.rpt {v} -hierarchical " \
@@ -753,6 +765,7 @@ class CommandFListVivado(CommandFList, ToolVivado):
753
765
  CommandFList.__init__(self, config=config)
754
766
  ToolVivado.__init__(self, config=self.config)
755
767
  self.args['all-sv'] = False
768
+ self.args['emit-parameter'] = False
756
769
 
757
770
 
758
771
  class CommandUploadVivado(CommandUpload, ToolVivado):
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
  ]