opencos-eda 0.3.8__py3-none-any.whl → 0.3.10__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.
- opencos/commands/deps_help.py +40 -21
- opencos/commands/sim.py +0 -1
- opencos/deps/deps_file.py +82 -79
- opencos/eda.py +108 -17
- opencos/eda_base.py +8 -4
- opencos/eda_config.py +8 -1
- opencos/eda_config_defaults.yml +14 -5
- opencos/eda_deps_bash_completion.bash +37 -15
- opencos/tools/modelsim_ase.py +19 -378
- opencos/tools/questa.py +42 -247
- opencos/tools/questa_common.py +480 -0
- opencos/tools/questa_fe.py +84 -0
- opencos/tools/questa_fse.py +7 -8
- opencos/tools/riviera.py +27 -10
- opencos/tools/verilator.py +1 -0
- opencos/utils/str_helpers.py +7 -0
- opencos/utils/vsim_helper.py +53 -21
- {opencos_eda-0.3.8.dist-info → opencos_eda-0.3.10.dist-info}/METADATA +2 -1
- {opencos_eda-0.3.8.dist-info → opencos_eda-0.3.10.dist-info}/RECORD +24 -40
- {opencos_eda-0.3.8.dist-info → opencos_eda-0.3.10.dist-info}/entry_points.txt +1 -0
- opencos/tests/__init__.py +0 -0
- opencos/tests/custom_config.yml +0 -13
- opencos/tests/deps_files/command_order/DEPS.yml +0 -44
- opencos/tests/deps_files/error_msgs/DEPS.yml +0 -55
- opencos/tests/deps_files/iverilog_test/DEPS.yml +0 -4
- opencos/tests/deps_files/no_deps_here/DEPS.yml +0 -0
- opencos/tests/deps_files/non_sv_reqs/DEPS.yml +0 -50
- opencos/tests/deps_files/tags_with_tools/DEPS.yml +0 -54
- opencos/tests/deps_files/test_err_fatal/DEPS.yml +0 -4
- opencos/tests/helpers.py +0 -354
- opencos/tests/test_build.py +0 -12
- opencos/tests/test_deps_helpers.py +0 -207
- opencos/tests/test_deps_schema.py +0 -30
- opencos/tests/test_eda.py +0 -921
- opencos/tests/test_eda_elab.py +0 -110
- opencos/tests/test_eda_synth.py +0 -150
- opencos/tests/test_oc_cli.py +0 -25
- opencos/tests/test_tools.py +0 -404
- {opencos_eda-0.3.8.dist-info → opencos_eda-0.3.10.dist-info}/WHEEL +0 -0
- {opencos_eda-0.3.8.dist-info → opencos_eda-0.3.10.dist-info}/licenses/LICENSE +0 -0
- {opencos_eda-0.3.8.dist-info → opencos_eda-0.3.10.dist-info}/licenses/LICENSE.spdx +0 -0
- {opencos_eda-0.3.8.dist-info → opencos_eda-0.3.10.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,480 @@
|
|
|
1
|
+
''' opencos.tools.questa - Used by opencos.eda for sim/elab commands w/ --tool=questa.
|
|
2
|
+
|
|
3
|
+
Contains classes for ToolQuesta, and CommonSimQuesta.
|
|
4
|
+
|
|
5
|
+
'''
|
|
6
|
+
|
|
7
|
+
# pylint: disable=R0801 # (setting similar, but not identical, self.defines key/value pairs)
|
|
8
|
+
|
|
9
|
+
# TODO(drew): fix these pylint eventually:
|
|
10
|
+
# pylint: disable=too-many-branches
|
|
11
|
+
|
|
12
|
+
import os
|
|
13
|
+
import re
|
|
14
|
+
import shutil
|
|
15
|
+
|
|
16
|
+
from opencos import util
|
|
17
|
+
from opencos.commands import sim, CommandSim, CommandFList
|
|
18
|
+
from opencos.eda_base import Tool
|
|
19
|
+
from opencos.utils.str_helpers import sanitize_defines_for_sh
|
|
20
|
+
|
|
21
|
+
class ToolQuesta(Tool):
|
|
22
|
+
'''Base class for CommandSimQuesta, collects version information about qrun'''
|
|
23
|
+
|
|
24
|
+
_TOOL = 'questa'
|
|
25
|
+
_EXE = 'vsim'
|
|
26
|
+
|
|
27
|
+
starter_edition = False
|
|
28
|
+
use_vopt = False # set manually or by get_versions() (after __init__ has set self._EXE)
|
|
29
|
+
sim_exe = '' # vsim or qrun
|
|
30
|
+
sim_exe_base_path = ''
|
|
31
|
+
questa_major = None
|
|
32
|
+
questa_minor = None
|
|
33
|
+
|
|
34
|
+
def __init__(self, config: dict):
|
|
35
|
+
super().__init__(config=config)
|
|
36
|
+
|
|
37
|
+
def get_versions(self) -> str:
|
|
38
|
+
if self._VERSION:
|
|
39
|
+
return self._VERSION
|
|
40
|
+
path = shutil.which(self._EXE)
|
|
41
|
+
if not path:
|
|
42
|
+
self.error(f"{self._EXE} not in path, need to setup",
|
|
43
|
+
"(i.e. source /opt/intelFPGA_pro/23.4/settings64.sh")
|
|
44
|
+
util.debug(f"{path=}")
|
|
45
|
+
if self._EXE.endswith('qrun') and \
|
|
46
|
+
any(x in path for x in ('modelsim_ase', 'questa_fse')):
|
|
47
|
+
util.warning(f"{self._EXE=} Questa path is for starter edition",
|
|
48
|
+
"(modelsim_ase, questa_fse), consider using --tool=modelsim_ase",
|
|
49
|
+
"or --tool=questa_fse, or similar")
|
|
50
|
+
else:
|
|
51
|
+
self.sim_exe = path
|
|
52
|
+
self.sim_exe_base_path, _ = os.path.split(path)
|
|
53
|
+
|
|
54
|
+
m = re.search(r'(\d+)\.(\d+)', path)
|
|
55
|
+
if m:
|
|
56
|
+
self.questa_major = int(m.group(1))
|
|
57
|
+
self.questa_minor = int(m.group(2))
|
|
58
|
+
self._VERSION = str(self.questa_major) + '.' + str(self.questa_minor)
|
|
59
|
+
else:
|
|
60
|
+
self.error("Questa path doesn't specificy version, expecting (d+.d+)")
|
|
61
|
+
return self._VERSION
|
|
62
|
+
|
|
63
|
+
def set_tool_defines(self):
|
|
64
|
+
# Will only be called from an object which also inherits from CommandDesign,
|
|
65
|
+
# i.e. has self.defines
|
|
66
|
+
self.defines['OC_TOOL_QUESTA'] = None
|
|
67
|
+
self.defines[f'OC_TOOL_QUESTA_{self.questa_major:d}_{self.questa_minor:d}'] = None
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
class CommonSimQuesta(CommandSim, ToolQuesta):
|
|
72
|
+
'''CommonSimQuesta is a the base command handler for:
|
|
73
|
+
|
|
74
|
+
eda sim --tool=[modelsim_ase|questa|questa_fse]
|
|
75
|
+
'''
|
|
76
|
+
|
|
77
|
+
def __init__(self, config: dict):
|
|
78
|
+
CommandSim.__init__(self, config=config)
|
|
79
|
+
ToolQuesta.__init__(self, config=self.config)
|
|
80
|
+
self.shell_command = os.path.join(self.sim_exe_base_path, 'vsim')
|
|
81
|
+
self.starter_edition = True
|
|
82
|
+
self.args.update({
|
|
83
|
+
'tool': self._TOOL, # override
|
|
84
|
+
'gui': False,
|
|
85
|
+
'vopt': self.use_vopt,
|
|
86
|
+
})
|
|
87
|
+
self.args_help.update({
|
|
88
|
+
'vopt': (
|
|
89
|
+
'Boolean to enable/disable use of vopt step prior to vsim step'
|
|
90
|
+
' Note that vopt args can be controlled with --elab-args=<value1>'
|
|
91
|
+
' --elab-args=<value2> ...'
|
|
92
|
+
)
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def run_in_batch_mode(self) -> bool:
|
|
97
|
+
'''Returns bool if we should run in batch mode (-c) from command line'''
|
|
98
|
+
if self.args['test-mode']:
|
|
99
|
+
return True
|
|
100
|
+
if self.args['gui']:
|
|
101
|
+
return False
|
|
102
|
+
return True
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def prepare_compile(self):
|
|
106
|
+
self.set_tool_defines()
|
|
107
|
+
self.write_vlog_dot_f()
|
|
108
|
+
self.write_vsim_dot_do(dot_do_to_write='all')
|
|
109
|
+
|
|
110
|
+
vsim_command_lists = self.get_compile_command_lists()
|
|
111
|
+
util.write_shell_command_file(
|
|
112
|
+
dirpath=self.args['work-dir'],
|
|
113
|
+
filename='compile_only.sh',
|
|
114
|
+
command_lists=vsim_command_lists
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
vsim_command_lists = self.get_elaborate_command_lists()
|
|
118
|
+
util.write_shell_command_file(
|
|
119
|
+
dirpath=self.args['work-dir'],
|
|
120
|
+
filename='compile_elaborate_only.sh',
|
|
121
|
+
command_lists=vsim_command_lists
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
# Write simulate.sh and all.sh to work-dir:
|
|
125
|
+
vsim_command_lists = self.get_simulate_command_lists()
|
|
126
|
+
self.write_sh_scripts_to_work_dir(
|
|
127
|
+
compile_lists=[], elaborate_lists=[], simulate_lists=vsim_command_lists
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
def compile(self):
|
|
131
|
+
if self.args['stop-before-compile']:
|
|
132
|
+
# don't run anything, save everyting we've already run in _prep_compile()
|
|
133
|
+
return
|
|
134
|
+
if self.args['stop-after-compile']:
|
|
135
|
+
vsim_command_lists = self.get_compile_command_lists()
|
|
136
|
+
self.run_commands_check_logs(vsim_command_lists, log_filename='sim.log',
|
|
137
|
+
must_strings=['Errors: 0'], use_must_strings=False)
|
|
138
|
+
|
|
139
|
+
def elaborate(self):
|
|
140
|
+
if self.args['stop-before-compile']:
|
|
141
|
+
return
|
|
142
|
+
if self.args['stop-after-compile']:
|
|
143
|
+
return
|
|
144
|
+
if self.args['stop-after-elaborate']:
|
|
145
|
+
# only run this if we stop after elaborate (simulate run it all)
|
|
146
|
+
vsim_command_lists = self.get_elaborate_command_lists()
|
|
147
|
+
self.run_commands_check_logs(vsim_command_lists, log_filename='sim.log')
|
|
148
|
+
|
|
149
|
+
def simulate(self):
|
|
150
|
+
if self.args['stop-before-compile'] or self.args['stop-after-compile'] or \
|
|
151
|
+
self.args['stop-after-elaborate']:
|
|
152
|
+
# don't run this if we're stopping before/after compile/elab
|
|
153
|
+
return
|
|
154
|
+
vsim_command_lists = self.get_simulate_command_lists()
|
|
155
|
+
self.run_commands_check_logs(vsim_command_lists, log_filename='sim.log')
|
|
156
|
+
|
|
157
|
+
def get_compile_command_lists(self, **kwargs) -> list:
|
|
158
|
+
# This will also set up a compile.
|
|
159
|
+
vsim_command_list = [
|
|
160
|
+
self.sim_exe,
|
|
161
|
+
'-c' if self.run_in_batch_mode() else '',
|
|
162
|
+
'-do', 'vsim_vlogonly.do', '-logfile', 'sim.log',
|
|
163
|
+
]
|
|
164
|
+
return [vsim_command_list]
|
|
165
|
+
|
|
166
|
+
def get_elaborate_command_lists(self, **kwargs) -> list:
|
|
167
|
+
# This will also set up a compile, for vlog + vsim (0 time)
|
|
168
|
+
vsim_command_list = [
|
|
169
|
+
self.sim_exe,
|
|
170
|
+
'-c' if self.run_in_batch_mode() else '',
|
|
171
|
+
'-do', 'vsim_lintonly.do', '-logfile', 'sim.log',
|
|
172
|
+
]
|
|
173
|
+
return [vsim_command_list]
|
|
174
|
+
|
|
175
|
+
def get_simulate_command_lists(self, **kwargs) -> list:
|
|
176
|
+
# This will also set up a compile, for vlog + vsim (with run -a)
|
|
177
|
+
vsim_command_list = [
|
|
178
|
+
self.sim_exe,
|
|
179
|
+
'-c' if self.run_in_batch_mode() else '',
|
|
180
|
+
'-do', 'vsim.do', '-logfile', 'sim.log',
|
|
181
|
+
]
|
|
182
|
+
return [vsim_command_list]
|
|
183
|
+
|
|
184
|
+
def get_post_simulate_command_lists(self, **kwargs) -> list:
|
|
185
|
+
return []
|
|
186
|
+
|
|
187
|
+
def write_vlog_dot_f(self, filename='vlog.f') -> None:
|
|
188
|
+
'''Returns none, creates filename (str) for a vlog.f'''
|
|
189
|
+
vlog_dot_f_lines = []
|
|
190
|
+
|
|
191
|
+
# Add compile args from config.tool.TOOL (questa, etc):
|
|
192
|
+
vlog_dot_f_lines += self.tool_config.get(
|
|
193
|
+
'compile-args',
|
|
194
|
+
'-sv -svinputport=net -lint').split()
|
|
195
|
+
# Add waivers from config.tool.TOOL (questa, modelsim_ase, etc)
|
|
196
|
+
for waiver in self.tool_config.get(
|
|
197
|
+
'compile-waivers',
|
|
198
|
+
[ #defaults:
|
|
199
|
+
'2275', # 2275 - Existing package 'foo_pkg' will be overwritten.
|
|
200
|
+
]) + self.args['compile-waivers']:
|
|
201
|
+
vlog_dot_f_lines += ['-suppress', str(waiver)]
|
|
202
|
+
|
|
203
|
+
if self.args['gui'] or self.args['waves']:
|
|
204
|
+
vlog_dot_f_lines += self.tool_config.get('compile-waves-args', '').split()
|
|
205
|
+
|
|
206
|
+
vlog_dot_f_fname = filename
|
|
207
|
+
vlog_dot_f_fpath = os.path.join(self.args['work-dir'], vlog_dot_f_fname)
|
|
208
|
+
|
|
209
|
+
for value in self.incdirs:
|
|
210
|
+
vlog_dot_f_lines += [ f"+incdir+{value}" ]
|
|
211
|
+
|
|
212
|
+
for k,v in self.defines.items():
|
|
213
|
+
if v is None:
|
|
214
|
+
vlog_dot_f_lines += [ f'+define+{k}' ]
|
|
215
|
+
else:
|
|
216
|
+
|
|
217
|
+
# if the value v is a double-quoted string, such as v='"hi"', the
|
|
218
|
+
# entire +define+NAME="hi" needs to wrapped in double quotes with the
|
|
219
|
+
# value v double-quotes escaped: "+define+NAME=\"hi\""
|
|
220
|
+
if isinstance(v, str) and v.startswith('"') and v.endswith('"'):
|
|
221
|
+
str_v = v.replace('"', '\\"')
|
|
222
|
+
vlog_dot_f_lines += [ f'"+define+{k}={str_v}"' ]
|
|
223
|
+
else:
|
|
224
|
+
# Generally we should only support int and str python types passed as
|
|
225
|
+
# +define+{k}={v}, but also for SystemVerilog plusargs
|
|
226
|
+
vlog_dot_f_lines += [ f'+define+{k}={sanitize_defines_for_sh(v)}' ]
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
vlog_dot_f_lines += self.args['compile-args']
|
|
230
|
+
|
|
231
|
+
vlog_dot_f_lines += [
|
|
232
|
+
'-source',
|
|
233
|
+
] + list(self.files_sv) + list(self.files_v)
|
|
234
|
+
|
|
235
|
+
if not self.files_sv and not self.files_v:
|
|
236
|
+
if not self.args['stop-before-compile']:
|
|
237
|
+
self.error(f'{self.target=} {self.files_sv=} and {self.files_v=} are empty,',
|
|
238
|
+
'cannot create a valid vlog.f')
|
|
239
|
+
|
|
240
|
+
with open(vlog_dot_f_fpath, 'w', encoding='utf-8') as f:
|
|
241
|
+
f.writelines(line + "\n" for line in vlog_dot_f_lines)
|
|
242
|
+
|
|
243
|
+
def vopt_handle_parameters(self) -> (str, list):
|
|
244
|
+
'''Returns str for vopt or voptargs, and list of vopt tcl
|
|
245
|
+
|
|
246
|
+
Note this is used for self.use_vopt = True or False.
|
|
247
|
+
'''
|
|
248
|
+
|
|
249
|
+
voptargs_str = ''
|
|
250
|
+
vopt_do_lines = []
|
|
251
|
+
|
|
252
|
+
# Note that if self.use_vopt=True, we have to do some workarounds for how
|
|
253
|
+
# some questa-like tools behave for: tcl/.do + vopt arg processing
|
|
254
|
+
# This affects string based parameters that have spaces (vopt treats spaces unique args,
|
|
255
|
+
# vsim does not). Since we'd like to keep the vopt/vsim split into separate steps, we can
|
|
256
|
+
# work around this by setting tcl varaibles for each parameter.
|
|
257
|
+
if self.parameters:
|
|
258
|
+
if not self.use_vopt:
|
|
259
|
+
voptargs_str += ' ' + ' '.join(self.process_parameters_get_list(arg_prefix='-G'))
|
|
260
|
+
else:
|
|
261
|
+
for k,v in self.parameters.items():
|
|
262
|
+
s = sim.parameters_dict_get_command_list(params={k: v}, arg_prefix='')[0]
|
|
263
|
+
# At this point, s should be a str in form {k}={v}
|
|
264
|
+
if not s or '=' not in s:
|
|
265
|
+
continue
|
|
266
|
+
if ' ' in s:
|
|
267
|
+
# Instead of:
|
|
268
|
+
# vopt -GMyParam="hi bye"
|
|
269
|
+
# we'll do:
|
|
270
|
+
# set PARAMETERS(MyParam) "hi bye"
|
|
271
|
+
# vopt -GMyParam=$PARAMETERS(MyParam)
|
|
272
|
+
s = s.replace(f'{k}=', f'set PARAMETERS({k}) ')
|
|
273
|
+
vopt_do_lines.append(s)
|
|
274
|
+
voptargs_str += f' -G{k}=$PARAMETERS({k}) '
|
|
275
|
+
else:
|
|
276
|
+
voptargs_str += f' -G{s} '
|
|
277
|
+
|
|
278
|
+
return voptargs_str, vopt_do_lines
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
def write_vsim_dot_do( # pylint: disable=too-many-locals
|
|
282
|
+
self, dot_do_to_write: list
|
|
283
|
+
) -> None:
|
|
284
|
+
'''Writes files(s) based on dot_do_to_write(list of str)
|
|
285
|
+
|
|
286
|
+
list arg values can be empty (all) or have items 'all', 'sim', 'lint', 'vlog'.'''
|
|
287
|
+
|
|
288
|
+
vsim_dot_do_fpath = os.path.join(self.args['work-dir'], 'vsim.do')
|
|
289
|
+
vsim_lintonly_dot_do_fpath = os.path.join(self.args['work-dir'], 'vsim_lintonly.do')
|
|
290
|
+
vsim_vlogonly_dot_do_fpath = os.path.join(self.args['work-dir'], 'vsim_vlogonly.do')
|
|
291
|
+
|
|
292
|
+
sim_plusargs_str = self._get_sim_plusargs_str()
|
|
293
|
+
vsim_suppress_list_str = self._get_vsim_suppress_list_str()
|
|
294
|
+
vsim_ext_args = ' '.join(self.args.get('sim-args', []))
|
|
295
|
+
|
|
296
|
+
voptargs_str = self.tool_config.get('elab-args', '')
|
|
297
|
+
voptargs_str += ' '.join(self.args.get('elab-args', []))
|
|
298
|
+
if self.args['gui'] or self.args['waves']:
|
|
299
|
+
voptargs_str += ' ' + self.tool_config.get('simulate-waves-args', '+acc')
|
|
300
|
+
util.artifacts.add_extension(
|
|
301
|
+
search_paths=self.args['work-dir'], file_extension='wlf',
|
|
302
|
+
typ='waveform', description='Modelsim/Questa Waveform WLF (Wave Log Format) file'
|
|
303
|
+
)
|
|
304
|
+
|
|
305
|
+
# TODO(drew): support self.args['sim_libary'] (1 lists)
|
|
306
|
+
vlog_do_lines = []
|
|
307
|
+
vsim_do_lines = []
|
|
308
|
+
|
|
309
|
+
# parameters, use helper method to get voptargs_str and vopt_do_lines
|
|
310
|
+
more_voptargs_str, vopt_do_lines = self.vopt_handle_parameters()
|
|
311
|
+
voptargs_str += more_voptargs_str
|
|
312
|
+
|
|
313
|
+
|
|
314
|
+
vopt_one_liner = ""
|
|
315
|
+
if self.use_vopt:
|
|
316
|
+
vopt_one_liner = (
|
|
317
|
+
f"vopt {voptargs_str} work.{self.args['top']} -o opt__{self.args['top']}"
|
|
318
|
+
)
|
|
319
|
+
vopt_one_liner = vopt_one_liner.replace('\n', ' ') # needs to be a one-liner
|
|
320
|
+
# vopt doesn't need -voptargs=(value) like vsim does, simply use (value).
|
|
321
|
+
vopt_one_liner = vopt_one_liner.replace('-voptargs=', '')
|
|
322
|
+
|
|
323
|
+
vsim_one_liner = "vsim -onfinish stop" \
|
|
324
|
+
+ f" -sv_seed {self.args['seed']} {sim_plusargs_str} {vsim_suppress_list_str}" \
|
|
325
|
+
+ f" {vsim_ext_args} opt__{self.args['top']}"
|
|
326
|
+
else:
|
|
327
|
+
# vopt doesn't exist, use single vsim call after vlog call:
|
|
328
|
+
vsim_one_liner = "vsim -onfinish stop" \
|
|
329
|
+
+ f" -sv_seed {self.args['seed']} {sim_plusargs_str} {vsim_suppress_list_str}" \
|
|
330
|
+
+ f" {voptargs_str} {vsim_ext_args} work.{self.args['top']}"
|
|
331
|
+
|
|
332
|
+
|
|
333
|
+
vsim_one_liner = vsim_one_liner.replace('\n', ' ')
|
|
334
|
+
|
|
335
|
+
vlog_do_lines += [
|
|
336
|
+
"if {[file exists work]} { vdel -all work; }",
|
|
337
|
+
"vlib work;",
|
|
338
|
+
"quietly set qc 30;",
|
|
339
|
+
"if {[catch {vlog -f vlog.f} result]} {",
|
|
340
|
+
" echo \"Caught $result \";",
|
|
341
|
+
" if {[batch_mode]} {",
|
|
342
|
+
" quit -f -code 20;",
|
|
343
|
+
" }",
|
|
344
|
+
"}",
|
|
345
|
+
]
|
|
346
|
+
|
|
347
|
+
if self.use_vopt:
|
|
348
|
+
vopt_do_lines += [
|
|
349
|
+
"if {[catch { " + vopt_one_liner + " } result] } {",
|
|
350
|
+
" echo \"Caught $result\";",
|
|
351
|
+
" if {[batch_mode]} {",
|
|
352
|
+
" quit -f -code 19;",
|
|
353
|
+
" }",
|
|
354
|
+
"}",
|
|
355
|
+
]
|
|
356
|
+
|
|
357
|
+
vsim_do_lines += [
|
|
358
|
+
"if {[catch { " + vsim_one_liner + " } result] } {",
|
|
359
|
+
" echo \"Caught $result\";",
|
|
360
|
+
" if {[batch_mode]} {",
|
|
361
|
+
" quit -f -code 18;",
|
|
362
|
+
" }",
|
|
363
|
+
"}",
|
|
364
|
+
]
|
|
365
|
+
|
|
366
|
+
vsim_vlogonly_dot_do_lines = vlog_do_lines + [
|
|
367
|
+
"if {[batch_mode]} {",
|
|
368
|
+
" quit -f -code 0;",
|
|
369
|
+
"}",
|
|
370
|
+
]
|
|
371
|
+
|
|
372
|
+
final_check_teststatus_do_lines = [
|
|
373
|
+
"set TestStatus [coverage attribute -name SEED -name TESTSTATUS];",
|
|
374
|
+
"if {[regexp \"TESTSTATUS += 0\" $TestStatus]} {",
|
|
375
|
+
" quietly set qc 0;",
|
|
376
|
+
"} elseif {[regexp \"TESTSTATUS += 1\" $TestStatus]} {",
|
|
377
|
+
" quietly set qc 0;",
|
|
378
|
+
"} else {",
|
|
379
|
+
" quietly set qc 2;",
|
|
380
|
+
"}",
|
|
381
|
+
"if {[batch_mode]} {",
|
|
382
|
+
" quit -f -code $qc;",
|
|
383
|
+
"}",
|
|
384
|
+
]
|
|
385
|
+
|
|
386
|
+
# final vlog/vopt/vsim lint-only .do command (want to make sure it can completely
|
|
387
|
+
# build for 'elab' style eda job), runs for 0ns, logs nothing for a waveform, quits
|
|
388
|
+
vsim_lintonly_dot_do_lines = vlog_do_lines + vopt_do_lines + vsim_do_lines \
|
|
389
|
+
+ final_check_teststatus_do_lines
|
|
390
|
+
|
|
391
|
+
# final vlog/opt/vsim full simulation .do command.
|
|
392
|
+
vsim_dot_do_lines = vlog_do_lines + vopt_do_lines + vsim_do_lines + [
|
|
393
|
+
"onbreak { resume; };",
|
|
394
|
+
"catch {log -r *};",
|
|
395
|
+
"run -a;",
|
|
396
|
+
] + final_check_teststatus_do_lines
|
|
397
|
+
|
|
398
|
+
write_all = len(dot_do_to_write) == 0 or 'all' in dot_do_to_write
|
|
399
|
+
if write_all or 'sim' in dot_do_to_write:
|
|
400
|
+
with open(vsim_dot_do_fpath, 'w', encoding='utf-8') as f:
|
|
401
|
+
f.writelines(line + "\n" for line in vsim_dot_do_lines)
|
|
402
|
+
|
|
403
|
+
if write_all or 'lint' in dot_do_to_write:
|
|
404
|
+
with open(vsim_lintonly_dot_do_fpath, 'w', encoding='utf-8') as f:
|
|
405
|
+
f.writelines(line + "\n" for line in vsim_lintonly_dot_do_lines)
|
|
406
|
+
|
|
407
|
+
if write_all or 'vlog' in dot_do_to_write:
|
|
408
|
+
with open(vsim_vlogonly_dot_do_fpath, 'w', encoding='utf-8') as f:
|
|
409
|
+
f.writelines(line + "\n" for line in vsim_vlogonly_dot_do_lines)
|
|
410
|
+
|
|
411
|
+
|
|
412
|
+
def _get_sim_plusargs_str(self) -> str:
|
|
413
|
+
sim_plusargs = []
|
|
414
|
+
|
|
415
|
+
assert isinstance(self.args["sim-plusargs"], list), \
|
|
416
|
+
f'{self.target=} {type(self.args["sim-plusargs"])=} but must be list'
|
|
417
|
+
|
|
418
|
+
for x in self.args['sim-plusargs']:
|
|
419
|
+
# For vsim we need to add a +key=value if the + is missing
|
|
420
|
+
if x[0] != '+':
|
|
421
|
+
x = f'+{x}'
|
|
422
|
+
sim_plusargs.append(x)
|
|
423
|
+
|
|
424
|
+
return ' '.join(sim_plusargs)
|
|
425
|
+
|
|
426
|
+
|
|
427
|
+
def _get_vsim_suppress_list_str(self) -> str:
|
|
428
|
+
vsim_suppress_list = []
|
|
429
|
+
# Add waivers from config.tool.TOOL:
|
|
430
|
+
for waiver in self.tool_config.get(
|
|
431
|
+
'simulate-waivers', [
|
|
432
|
+
#defaults:
|
|
433
|
+
'3009', # 3009: [TSCALE] - Module 'foo' does not have a timeunit/timeprecision
|
|
434
|
+
# specification in effect, but other modules do.
|
|
435
|
+
]) + self.args['sim-waivers']:
|
|
436
|
+
vsim_suppress_list += ['-suppress', str(waiver)]
|
|
437
|
+
|
|
438
|
+
return ' '.join(vsim_suppress_list)
|
|
439
|
+
|
|
440
|
+
|
|
441
|
+
def artifacts_add(self, name: str, typ: str, description: str) -> None:
|
|
442
|
+
'''Override from Command.artifacts_add, so we can catch known file
|
|
443
|
+
|
|
444
|
+
names to make their typ/description better, such as CommandSim using
|
|
445
|
+
sim.log
|
|
446
|
+
'''
|
|
447
|
+
_, leafname = os.path.split(name)
|
|
448
|
+
if leafname == 'sim.log':
|
|
449
|
+
description = 'Modelsim/Questa Transcript log file'
|
|
450
|
+
|
|
451
|
+
super().artifacts_add(name=name, typ=typ, description=description)
|
|
452
|
+
|
|
453
|
+
|
|
454
|
+
class CommonElabQuesta(CommonSimQuesta):
|
|
455
|
+
'''CommonElabQuesta is a command handler for: eda elab --tool=(questa family)'''
|
|
456
|
+
|
|
457
|
+
command_name = 'elab'
|
|
458
|
+
|
|
459
|
+
def __init__(self, config:dict):
|
|
460
|
+
super().__init__(config)
|
|
461
|
+
self.args['stop-after-elaborate'] = True
|
|
462
|
+
|
|
463
|
+
|
|
464
|
+
class CommonLintQuesta(CommonSimQuesta):
|
|
465
|
+
'''CommonSimQuesta is a command handler for: eda lint --tool=(questa family)'''
|
|
466
|
+
|
|
467
|
+
command_name = 'lint'
|
|
468
|
+
|
|
469
|
+
def __init__(self, config:dict):
|
|
470
|
+
super().__init__(config)
|
|
471
|
+
self.args['stop-after-compile'] = True
|
|
472
|
+
self.args['stop-after-elaborate'] = True
|
|
473
|
+
|
|
474
|
+
|
|
475
|
+
class CommonFListQuesta(CommandFList, ToolQuesta):
|
|
476
|
+
'''CommonFListQuesta is a command handler for: eda flist --tool=(questa family)'''
|
|
477
|
+
|
|
478
|
+
def __init__(self, config: dict):
|
|
479
|
+
CommandFList.__init__(self, config=config)
|
|
480
|
+
ToolQuesta.__init__(self, config=self.config)
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
''' opencos.tools.questa - Used by opencos.eda for sim/elab commands w/ --tool=questa.
|
|
2
|
+
|
|
3
|
+
Contains classes for CommandSimQuesta, CommandElabQuesta.
|
|
4
|
+
For: Questa Intel (FPGA) Edition-64 vsim 20XX.X Simulator
|
|
5
|
+
|
|
6
|
+
Note - NOT the starter edition (see questa_fse.py)
|
|
7
|
+
|
|
8
|
+
'''
|
|
9
|
+
|
|
10
|
+
# pylint: disable=R0801 # (setting similar, but not identical, self.defines key/value pairs)
|
|
11
|
+
|
|
12
|
+
# TODO(drew): fix these pylint eventually:
|
|
13
|
+
# pylint: disable=too-many-branches, too-many-ancestors
|
|
14
|
+
|
|
15
|
+
import os
|
|
16
|
+
|
|
17
|
+
from opencos.tools.questa_common import CommonSimQuesta, CommonFListQuesta
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class CommandSimQuestaFe(CommonSimQuesta):
|
|
21
|
+
'''CommandSimQuestaFe is a command handler for: eda sim --tool=questa_fe
|
|
22
|
+
|
|
23
|
+
Note this inherits 99% from CommonSimQuesta for command handling
|
|
24
|
+
'''
|
|
25
|
+
_TOOL = 'questa_fe'
|
|
26
|
+
_EXE = 'vsim'
|
|
27
|
+
use_vopt = True
|
|
28
|
+
|
|
29
|
+
def __init__(self, config: dict):
|
|
30
|
+
# this will setup with self._TOOL = questa, optionally repair it later
|
|
31
|
+
CommonSimQuesta.__init__(self, config=config)
|
|
32
|
+
|
|
33
|
+
# repairs: override self._TOOL, and run get_versions() again.
|
|
34
|
+
self._TOOL = 'questa_fe'
|
|
35
|
+
|
|
36
|
+
self.shell_command = os.path.join(self.sim_exe_base_path, 'vsim')
|
|
37
|
+
self.starter_edition = False
|
|
38
|
+
self.args.update({
|
|
39
|
+
'tool': self._TOOL, # override
|
|
40
|
+
'gui': False,
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def set_tool_defines(self):
|
|
45
|
+
'''Override from questa.ToolQuesta'''
|
|
46
|
+
# Update any defines from config.tools.questa_fse:
|
|
47
|
+
self.defines.update(
|
|
48
|
+
self.tool_config.get(
|
|
49
|
+
'defines',
|
|
50
|
+
# defaults, if not set:
|
|
51
|
+
{
|
|
52
|
+
'OC_TOOL_QUESTA_FE': 1
|
|
53
|
+
}
|
|
54
|
+
)
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class CommandElabQuestaFe(CommandSimQuestaFe):
|
|
59
|
+
'''CommandElabQuesta is a command handler for: eda elab --tool=questa_fe'''
|
|
60
|
+
|
|
61
|
+
command_name = 'elab'
|
|
62
|
+
|
|
63
|
+
def __init__(self, config:dict):
|
|
64
|
+
super().__init__(config)
|
|
65
|
+
self.args['stop-after-elaborate'] = True
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
class CommandLintQuestaFe(CommandSimQuestaFe):
|
|
69
|
+
'''CommandLintQuesta is a command handler for: eda lint --tool=questa_fe'''
|
|
70
|
+
|
|
71
|
+
command_name = 'lint'
|
|
72
|
+
|
|
73
|
+
def __init__(self, config:dict):
|
|
74
|
+
super().__init__(config)
|
|
75
|
+
self.args['stop-after-compile'] = True
|
|
76
|
+
self.args['stop-after-elaborate'] = True
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
class CommandFListQuestaFe(CommonFListQuesta):
|
|
80
|
+
'''CommandFListQuesta is a command handler for: eda flist --tool=questa'''
|
|
81
|
+
|
|
82
|
+
def __init__(self, config: dict):
|
|
83
|
+
CommonFListQuesta.__init__(self, config=config)
|
|
84
|
+
self._TOOL = 'questa_fse'
|
opencos/tools/questa_fse.py
CHANGED
|
@@ -10,23 +10,22 @@ For: Questa Intel Starter FPGA Edition-64 vsim 20XX.X Simulator
|
|
|
10
10
|
|
|
11
11
|
import os
|
|
12
12
|
|
|
13
|
-
from opencos.tools.
|
|
14
|
-
from opencos.tools.questa import CommandFListQuesta
|
|
13
|
+
from opencos.tools.questa_common import CommonSimQuesta, CommonFListQuesta
|
|
15
14
|
|
|
16
15
|
|
|
17
|
-
class CommandSimQuestaFse(
|
|
16
|
+
class CommandSimQuestaFse(CommonSimQuesta):
|
|
18
17
|
'''CommandSimQuestaFse is a command handler for: eda sim --tool=questa_fse
|
|
19
18
|
|
|
20
|
-
Note this inherits 99% from
|
|
19
|
+
Note this inherits 99% from CommonSimQuesta for command handling
|
|
21
20
|
'''
|
|
22
21
|
_TOOL = 'questa_fse'
|
|
23
22
|
_EXE = 'vsim'
|
|
24
23
|
use_vopt = True
|
|
25
24
|
|
|
26
25
|
def __init__(self, config: dict):
|
|
27
|
-
# this will setup with self._TOOL =
|
|
26
|
+
# this will setup with self._TOOL = questa, which is not ideal so
|
|
28
27
|
# we have to repait it later.
|
|
29
|
-
|
|
28
|
+
CommonSimQuesta.__init__(self, config=config)
|
|
30
29
|
|
|
31
30
|
# repairs: override self._TOOL, and run get_versions() again.
|
|
32
31
|
self._TOOL = 'questa_fse'
|
|
@@ -74,9 +73,9 @@ class CommandLintQuestaFse(CommandSimQuestaFse):
|
|
|
74
73
|
self.args['stop-after-elaborate'] = True
|
|
75
74
|
|
|
76
75
|
|
|
77
|
-
class CommandFListQuestaFse(
|
|
76
|
+
class CommandFListQuestaFse(CommonFListQuesta):
|
|
78
77
|
'''CommandFListQuestaFse is a command handler for: eda flist --tool=questa_fse'''
|
|
79
78
|
|
|
80
79
|
def __init__(self, config: dict):
|
|
81
|
-
|
|
80
|
+
CommonFListQuesta.__init__(self, config=config)
|
|
82
81
|
self._TOOL = 'questa_fse'
|
opencos/tools/riviera.py
CHANGED
|
@@ -11,11 +11,12 @@ import shutil
|
|
|
11
11
|
import subprocess
|
|
12
12
|
|
|
13
13
|
from opencos import util
|
|
14
|
-
from opencos.tools.
|
|
14
|
+
from opencos.tools.questa_common import ToolQuesta, CommonSimQuesta
|
|
15
|
+
from opencos.commands import CommandFList
|
|
15
16
|
from opencos.utils.str_helpers import sanitize_defines_for_sh
|
|
16
17
|
from opencos.utils import status_constants
|
|
17
18
|
|
|
18
|
-
class ToolRiviera(
|
|
19
|
+
class ToolRiviera(ToolQuesta):
|
|
19
20
|
'''ToolRiviera used by opencos.eda for --tool=riviera'''
|
|
20
21
|
|
|
21
22
|
_TOOL = 'riviera'
|
|
@@ -60,14 +61,20 @@ class ToolRiviera(ToolModelsimAse):
|
|
|
60
61
|
return self._VERSION
|
|
61
62
|
|
|
62
63
|
|
|
63
|
-
class CommandSimRiviera(
|
|
64
|
+
class CommandSimRiviera(CommonSimQuesta, ToolRiviera):
|
|
64
65
|
'''CommandSimRiviera is a command handler for: eda sim --tool=riviera'''
|
|
65
66
|
|
|
66
67
|
def __init__(self, config: dict):
|
|
67
|
-
|
|
68
|
+
CommonSimQuesta.__init__(self, config=config)
|
|
69
|
+
|
|
70
|
+
# This technically inherits ToolQuesta and ToolRiviera, but ToolRiviera will
|
|
71
|
+
# override get_versions(). Just be careful on using issinstance(cls_obj, ToolQuesta),
|
|
72
|
+
# and instead use self._TOOL, or self.args['tool']
|
|
68
73
|
ToolRiviera.__init__(self, config=self.config)
|
|
74
|
+
|
|
69
75
|
self.shell_command = os.path.join(self.sim_exe_base_path, 'vsim')
|
|
70
|
-
self.starter_edition =
|
|
76
|
+
self.starter_edition = False
|
|
77
|
+
|
|
71
78
|
self.args.update({
|
|
72
79
|
'tool': self._TOOL, # override
|
|
73
80
|
'gui': False,
|
|
@@ -220,7 +227,7 @@ class CommandSimRiviera(CommandSimModelsimAse, ToolRiviera):
|
|
|
220
227
|
|
|
221
228
|
|
|
222
229
|
|
|
223
|
-
def write_vsim_dot_do( # pylint: disable=too-many-branches
|
|
230
|
+
def write_vsim_dot_do( # pylint: disable=too-many-branches,too-many-locals
|
|
224
231
|
self, dot_do_to_write: list
|
|
225
232
|
) -> None:
|
|
226
233
|
'''Writes files(s) based on dot_do_to_write(list of str)
|
|
@@ -247,19 +254,22 @@ class CommandSimRiviera(CommandSimModelsimAse, ToolRiviera):
|
|
|
247
254
|
voptargs_str += self.tool_config.get('simulate-waves-args',
|
|
248
255
|
'+accb +accr +access +r+w')
|
|
249
256
|
if self.args['coverage']:
|
|
250
|
-
voptargs_str += self.tool_config.get('coverage-args', '')
|
|
257
|
+
voptargs_str += ' ' + self.tool_config.get('coverage-args', '')
|
|
258
|
+
|
|
259
|
+
if self.args['uvm']:
|
|
260
|
+
if all(x not in voptargs_str for x in ('+access +r', '+access +w+r')):
|
|
261
|
+
voptargs_str += ' +access +r'
|
|
251
262
|
|
|
252
263
|
# parameters
|
|
253
264
|
if self.parameters:
|
|
254
265
|
voptargs_str += ' ' + ' '.join(self.process_parameters_get_list(arg_prefix='-G'))
|
|
255
266
|
|
|
256
|
-
|
|
257
|
-
# to add to vsim_one_liner.
|
|
267
|
+
vsim_libs = ' '.join([f'-l {x}' for x in self.args['sim-library']])
|
|
258
268
|
|
|
259
269
|
vsim_one_liner = (
|
|
260
270
|
"vsim"
|
|
261
271
|
f" -sv_seed {self.args['seed']} {sim_plusargs_str}"
|
|
262
|
-
f" {voptargs_str} {vsim_ext_args} work.{self.args['top']}"
|
|
272
|
+
f" {voptargs_str} {vsim_ext_args} {vsim_libs} work.{self.args['top']}"
|
|
263
273
|
)
|
|
264
274
|
|
|
265
275
|
vsim_one_liner = vsim_one_liner.replace('\n', ' ') # needs to be a one-liner
|
|
@@ -386,3 +396,10 @@ class CommandLintRiviera(CommandSimRiviera):
|
|
|
386
396
|
super().__init__(config)
|
|
387
397
|
self.args['stop-after-compile'] = True
|
|
388
398
|
self.args['stop-after-elaborate'] = True
|
|
399
|
+
|
|
400
|
+
class CommandFListRiviera(CommandFList, ToolRiviera):
|
|
401
|
+
'''CommonFListQuesta is a command handler for: eda flist --tool=riviera'''
|
|
402
|
+
|
|
403
|
+
def __init__(self, config: dict):
|
|
404
|
+
CommandFList.__init__(self, config=config)
|
|
405
|
+
ToolRiviera.__init__(self, config=self.config)
|