opencos-eda 0.3.10__py3-none-any.whl → 0.3.12__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 +63 -113
- opencos/commands/export.py +7 -2
- opencos/commands/multi.py +4 -4
- opencos/commands/sim.py +14 -15
- opencos/commands/sweep.py +1 -1
- opencos/commands/synth.py +1 -2
- opencos/commands/upload.py +192 -4
- opencos/commands/waves.py +52 -8
- opencos/deps/deps_commands.py +6 -6
- opencos/deps/deps_processor.py +129 -50
- opencos/docs/Architecture.md +45 -0
- opencos/docs/ConnectingApps.md +29 -0
- opencos/docs/DEPS.md +199 -0
- opencos/docs/Debug.md +77 -0
- opencos/docs/DirectoryStructure.md +22 -0
- opencos/docs/Installation.md +117 -0
- opencos/docs/OcVivadoTcl.md +63 -0
- opencos/docs/OpenQuestions.md +7 -0
- opencos/docs/README.md +13 -0
- opencos/docs/RtlCodingStyle.md +54 -0
- opencos/docs/eda.md +147 -0
- opencos/docs/oc_cli.md +135 -0
- opencos/eda.py +358 -155
- opencos/eda_base.py +187 -60
- opencos/eda_config.py +70 -35
- opencos/eda_config_defaults.yml +310 -186
- opencos/eda_config_reduced.yml +19 -39
- opencos/eda_tool_helper.py +190 -21
- opencos/files.py +26 -1
- opencos/tools/cocotb.py +11 -23
- opencos/tools/invio.py +2 -2
- opencos/tools/invio_yosys.py +2 -1
- opencos/tools/iverilog.py +3 -3
- opencos/tools/modelsim_ase.py +1 -1
- opencos/tools/quartus.py +172 -137
- opencos/tools/questa_common.py +50 -9
- opencos/tools/riviera.py +5 -4
- opencos/tools/slang.py +14 -10
- opencos/tools/slang_yosys.py +1 -0
- opencos/tools/surelog.py +7 -6
- opencos/tools/verilator.py +9 -7
- opencos/tools/vivado.py +315 -180
- opencos/tools/yosys.py +5 -5
- opencos/util.py +6 -3
- opencos/utils/dict_helpers.py +31 -0
- opencos/utils/markup_helpers.py +2 -2
- opencos/utils/str_helpers.py +38 -0
- opencos/utils/subprocess_helpers.py +3 -3
- opencos/utils/vscode_helper.py +2 -2
- opencos/utils/vsim_helper.py +16 -5
- {opencos_eda-0.3.10.dist-info → opencos_eda-0.3.12.dist-info}/METADATA +1 -1
- opencos_eda-0.3.12.dist-info/RECORD +93 -0
- opencos/eda_config_max_verilator_waivers.yml +0 -39
- opencos_eda-0.3.10.dist-info/RECORD +0 -81
- {opencos_eda-0.3.10.dist-info → opencos_eda-0.3.12.dist-info}/WHEEL +0 -0
- {opencos_eda-0.3.10.dist-info → opencos_eda-0.3.12.dist-info}/entry_points.txt +0 -0
- {opencos_eda-0.3.10.dist-info → opencos_eda-0.3.12.dist-info}/licenses/LICENSE +0 -0
- {opencos_eda-0.3.10.dist-info → opencos_eda-0.3.12.dist-info}/licenses/LICENSE.spdx +0 -0
- {opencos_eda-0.3.10.dist-info → opencos_eda-0.3.12.dist-info}/top_level.txt +0 -0
opencos/tools/vivado.py
CHANGED
|
@@ -10,19 +10,14 @@ upload, flist, open, proj.
|
|
|
10
10
|
import os
|
|
11
11
|
import re
|
|
12
12
|
import shlex
|
|
13
|
-
import shutil
|
|
14
|
-
import sys
|
|
15
13
|
|
|
16
|
-
from datetime import datetime
|
|
17
14
|
from pathlib import Path
|
|
18
15
|
|
|
19
16
|
from opencos import util, eda_base
|
|
20
|
-
from opencos.
|
|
21
|
-
|
|
22
|
-
from opencos.commands import CommandSim, CommandSynth, CommandProj, CommandBuild, \
|
|
17
|
+
from opencos.commands import sim, CommandSim, CommandSynth, CommandProj, CommandBuild, \
|
|
23
18
|
CommandFList, CommandUpload, CommandOpen
|
|
24
|
-
|
|
25
|
-
from opencos.
|
|
19
|
+
from opencos.eda_base import Tool
|
|
20
|
+
from opencos.files import safe_shutil_which
|
|
26
21
|
|
|
27
22
|
|
|
28
23
|
class ToolVivado(Tool):
|
|
@@ -52,7 +47,7 @@ class ToolVivado(Tool):
|
|
|
52
47
|
if self._VERSION:
|
|
53
48
|
return self._VERSION
|
|
54
49
|
|
|
55
|
-
path =
|
|
50
|
+
path = safe_shutil_which(self._EXE)
|
|
56
51
|
if not path:
|
|
57
52
|
self.error("Vivado not in path, need to install or add to $PATH",
|
|
58
53
|
f"(looked for '{self._EXE}')")
|
|
@@ -61,10 +56,14 @@ class ToolVivado(Tool):
|
|
|
61
56
|
self.vivado_base_path, _ = os.path.split(path)
|
|
62
57
|
|
|
63
58
|
xilinx_vivado = os.environ.get('XILINX_VIVADO')
|
|
64
|
-
|
|
59
|
+
|
|
60
|
+
# Since get_versions() doesn't have a quiet mode, and it's called as part
|
|
61
|
+
# of opencos.eda.init_config(..), let's only show an information message
|
|
62
|
+
# if XILINX_VIVADO env is set and it doesn't match the Path/bin/vivado[.bat|.exe]
|
|
63
|
+
if xilinx_vivado and \
|
|
65
64
|
os.path.abspath(os.path.join(xilinx_vivado, 'bin')) != \
|
|
66
65
|
os.path.abspath(os.path.dirname(self.vivado_exe)):
|
|
67
|
-
util.info("environment for XILINX_VIVADO is
|
|
66
|
+
util.info("environment for XILINX_VIVADO is set, and doesn't match the vivado path:",
|
|
68
67
|
f"XILINX_VIVADO={xilinx_vivado} EXE PATH={self.vivado_exe}")
|
|
69
68
|
|
|
70
69
|
# Get version based on install path name. Calling vivado -verison is too slow.
|
|
@@ -74,7 +73,7 @@ class ToolVivado(Tool):
|
|
|
74
73
|
version = m.group(1) + '.' + m.group(2)
|
|
75
74
|
self._VERSION = version
|
|
76
75
|
else:
|
|
77
|
-
|
|
76
|
+
util.warning("Vivado path doesn't specificy version, expecting (dddd.d)")
|
|
78
77
|
|
|
79
78
|
if version:
|
|
80
79
|
numbers_list = version.split('.')
|
|
@@ -82,7 +81,7 @@ class ToolVivado(Tool):
|
|
|
82
81
|
self.vivado_release = int(numbers_list[1])
|
|
83
82
|
self.vivado_version = float(numbers_list[0] + '.' + numbers_list[1])
|
|
84
83
|
else:
|
|
85
|
-
|
|
84
|
+
util.warning(f"Vivado version not found, vivado path = {self.vivado_exe}")
|
|
86
85
|
return self._VERSION
|
|
87
86
|
|
|
88
87
|
|
|
@@ -120,7 +119,10 @@ class ToolVivado(Tool):
|
|
|
120
119
|
|
|
121
120
|
|
|
122
121
|
class CommandSimVivado(CommandSim, ToolVivado):
|
|
123
|
-
'''CommandSimVivado is a command handler for: eda sim --tool=vivado, uses xvlog, xelab, xsim
|
|
122
|
+
'''CommandSimVivado is a command handler for: eda sim --tool=vivado, uses xvlog, xelab, xsim
|
|
123
|
+
|
|
124
|
+
Note that we attempt to run a generated .tcl script within vivado, that will perform the
|
|
125
|
+
3-step compile/elaborate/simulate steps'''
|
|
124
126
|
|
|
125
127
|
def __init__(self, config: dict):
|
|
126
128
|
CommandSim.__init__(self, config)
|
|
@@ -141,9 +143,19 @@ class CommandSimVivado(CommandSim, ToolVivado):
|
|
|
141
143
|
})
|
|
142
144
|
|
|
143
145
|
self.sim_libraries = self.tool_config.get('sim-libraries', [])
|
|
144
|
-
|
|
145
|
-
self.
|
|
146
|
-
|
|
146
|
+
|
|
147
|
+
self.vivado_tcl = {
|
|
148
|
+
'xvlog': [],
|
|
149
|
+
'xelab': [],
|
|
150
|
+
'xsim': [],
|
|
151
|
+
'exe_list': [],
|
|
152
|
+
'check_logs': [],
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
# Note this is the syntax to have your Vivado tcl print to
|
|
156
|
+
# stdout, which we do for xvlog, xelab always, and even for xsim if in
|
|
157
|
+
# --gui or not GUI
|
|
158
|
+
self.tcl_exec_pipe = ['>@stdout', '2>@stderr']
|
|
147
159
|
|
|
148
160
|
|
|
149
161
|
def set_tool_defines(self):
|
|
@@ -153,72 +165,144 @@ class CommandSimVivado(CommandSim, ToolVivado):
|
|
|
153
165
|
|
|
154
166
|
def prepare_compile(self):
|
|
155
167
|
self.set_tool_defines()
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
self.
|
|
159
|
-
self.
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
168
|
+
|
|
169
|
+
# Don't use the return values, these will set values in self.vivado_tcl:
|
|
170
|
+
self.get_compile_command_lists()
|
|
171
|
+
self.get_elaborate_command_lists()
|
|
172
|
+
self.get_simulate_command_lists()
|
|
173
|
+
|
|
174
|
+
# We will always run a generated .tcl scirpt from vivado as:
|
|
175
|
+
# one command line call for:
|
|
176
|
+
# vivado -mode batch -source all_vivado.tcl
|
|
177
|
+
# So we have to create all_vivado.tcl based on our commands that we saved
|
|
178
|
+
# in self.vivado_tcl:
|
|
179
|
+
tclfname = os.path.abspath(
|
|
180
|
+
os.path.join(self.args['work-dir'], 'all_vivado.tcl')
|
|
163
181
|
)
|
|
182
|
+
with open(tclfname, 'w', encoding='utf-8' ) as ftcl:
|
|
183
|
+
for line in self.vivado_tcl['xvlog']:
|
|
184
|
+
if self.args['stop-before-compile']:
|
|
185
|
+
ftcl.write('# ')
|
|
186
|
+
ftcl.write(line)
|
|
187
|
+
ftcl.write('\n\n')
|
|
188
|
+
for line in self.vivado_tcl['xelab']:
|
|
189
|
+
if any(self.args[x] for x in ('stop-before-compile', 'stop-after-compile')):
|
|
190
|
+
ftcl.write('# ')
|
|
191
|
+
ftcl.write(line)
|
|
192
|
+
ftcl.write('\n\n')
|
|
193
|
+
for line in self.vivado_tcl['xsim']:
|
|
194
|
+
if any(self.args[x] for x in ('stop-before-compile', 'stop-after-compile',
|
|
195
|
+
'stop-after-elaborate')):
|
|
196
|
+
ftcl.write('# ')
|
|
197
|
+
ftcl.write(line)
|
|
198
|
+
ftcl.write('\n\n')
|
|
199
|
+
|
|
200
|
+
# We will alwyas run this in -mode batch, the xsim call in
|
|
201
|
+
# all_vivado.tcl will have -gui in it:
|
|
202
|
+
self.vivado_tcl['exe_list'] = [
|
|
203
|
+
self.vivado_exe, '-mode', 'batch', '-source', 'all_vivado.tcl'
|
|
204
|
+
]
|
|
164
205
|
|
|
165
|
-
|
|
206
|
+
# Since we run this in a single vivado call, we don't get the automatic
|
|
207
|
+
# log checking from sim.run_commands_check_logs(..), so we need to remember
|
|
208
|
+
# which logs to check:
|
|
166
209
|
if self.args['stop-before-compile']:
|
|
167
|
-
|
|
168
|
-
self.
|
|
169
|
-
self.
|
|
210
|
+
self.vivado_tcl['check_logs'] = []
|
|
211
|
+
elif self.args['stop-after-compile']:
|
|
212
|
+
self.vivado_tcl['check_logs'] = ['vivado.log', 'xvlog.log']
|
|
213
|
+
elif self.args['stop-after-elaborate']:
|
|
214
|
+
self.vivado_tcl['check_logs'] = ['vivado.log', 'xvlog.log', 'xelab.log']
|
|
215
|
+
else:
|
|
216
|
+
self.vivado_tcl['check_logs'] = ['vivado.log', 'xvlog.log', 'xelab.log', 'xsim.log']
|
|
217
|
+
|
|
218
|
+
self.write_sh_scripts_to_work_dir(
|
|
219
|
+
compile_lists=[],
|
|
220
|
+
elaborate_lists=[],
|
|
221
|
+
simulate_lists=[self.vivado_tcl['exe_list']],
|
|
222
|
+
compile_line_breaks=False
|
|
170
223
|
)
|
|
171
224
|
|
|
225
|
+
|
|
226
|
+
def compile(self):
|
|
227
|
+
# handled in simulate()
|
|
228
|
+
return
|
|
229
|
+
|
|
172
230
|
def elaborate(self):
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
# In this flow, we need to run compile + elaborate separately (unlike ModelsimASE)
|
|
176
|
-
self.run_commands_check_logs(
|
|
177
|
-
self.xelab_commands, check_logs=True, log_filename='xelab.log',
|
|
178
|
-
must_strings=['Built simulation snapshot snapshot']
|
|
179
|
-
)
|
|
231
|
+
# handled in simulate()
|
|
232
|
+
return
|
|
180
233
|
|
|
181
234
|
def simulate(self):
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
235
|
+
# even though we run one command line call (vivado -mode batch -sourc TCLFILE)
|
|
236
|
+
# it still saves xvlog.log, xelab.log, xsim.log, and a (useless) vivado.log.
|
|
237
|
+
# we'll have to manually check the logs for errors if this was run in --gui:
|
|
185
238
|
self.run_commands_check_logs(
|
|
186
|
-
self.
|
|
239
|
+
[self.vivado_tcl['exe_list']], check_logs=False, log_filename='vivado.log'
|
|
187
240
|
)
|
|
188
241
|
|
|
242
|
+
for log_fname in self.vivado_tcl['check_logs']:
|
|
243
|
+
filename = os.path.join(self.args['work-dir'], log_fname)
|
|
244
|
+
self.check_logs_for_errors(filename=filename)
|
|
245
|
+
|
|
246
|
+
|
|
189
247
|
def get_compile_command_lists(self, **kwargs) -> list:
|
|
248
|
+
'''Override from sim.CommandSim - which expects a return type list.
|
|
249
|
+
|
|
250
|
+
Since we run all in Vivado tcl, we always return an empty list,
|
|
251
|
+
and instead save in self.vivado_tcl['xvlog'].
|
|
252
|
+
'''
|
|
190
253
|
self.set_tool_defines()
|
|
191
|
-
ret = [] # list of (list of ['xvlog', arg0, arg1, ..])
|
|
192
254
|
|
|
193
255
|
if self.args['add-glbl-v']:
|
|
194
256
|
self._add_glbl_v()
|
|
195
257
|
|
|
196
258
|
# compile verilog
|
|
197
259
|
if self.files_v:
|
|
198
|
-
|
|
199
|
-
self.get_xvlog_commands(files_list=self.files_v, typ='v')
|
|
200
|
-
)
|
|
260
|
+
self.add_xvlog_commands(files_list=self.files_v, typ='v')
|
|
201
261
|
|
|
202
262
|
# compile systemverilog
|
|
203
263
|
if self.files_sv:
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
264
|
+
self.add_xvlog_commands(files_list=self.files_sv, typ='sv')
|
|
265
|
+
|
|
266
|
+
return []
|
|
267
|
+
|
|
268
|
+
def process_parameters_get_list(self, arg_prefix: str = '-G') -> list:
|
|
269
|
+
'''Override from sim.CommandSim
|
|
270
|
+
|
|
271
|
+
custom handler for parameters, instead of the one in sim.py
|
|
272
|
+
all will be passed as -generic_top "k=v", avoids all unknown shell or bash
|
|
273
|
+
behavior in windows.
|
|
274
|
+
'''
|
|
275
|
+
|
|
276
|
+
# ignore arg_prefix (use -generic_top):
|
|
277
|
+
ret = []
|
|
278
|
+
for k,v in self.parameters.items():
|
|
279
|
+
if not isinstance(v, (int, str)):
|
|
280
|
+
util.warning(
|
|
281
|
+
f'parameter {k} has value: {v}, parameters must be int/string types'
|
|
282
|
+
)
|
|
283
|
+
if isinstance(v, int):
|
|
284
|
+
ret.extend(['-generic_top', f'"{k}={v}"'])
|
|
285
|
+
else:
|
|
286
|
+
v = f'{v}'.strip('\n') # stringify
|
|
287
|
+
# Because we're writing to a .tcl file, \" will become ", and \\\" will become \"
|
|
288
|
+
# we want \" in the final file.
|
|
289
|
+
v = v.replace('"', '\\\"')
|
|
290
|
+
ret.extend(['-generic_top', f'"{k}={v}"'])
|
|
291
|
+
return ret
|
|
207
292
|
|
|
208
|
-
return ret # list of lists
|
|
209
293
|
|
|
210
294
|
def get_elaborate_command_lists(self, **kwargs) -> list:
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
295
|
+
'''Override from sim.CommandSim - which expects a return type list.
|
|
296
|
+
|
|
297
|
+
Since we run all in Vivado tcl, we always return an empty list,
|
|
298
|
+
and instead save in self.vivado_tcl['xelab'].
|
|
299
|
+
'''
|
|
300
|
+
|
|
301
|
+
command_list = ['exec', 'xelab', self.args['top']]
|
|
302
|
+
|
|
303
|
+
command_list += self.tool_config.get(
|
|
304
|
+
'elab-args', '-s snapshot -timescale 1ns/1ps --stats').split()
|
|
220
305
|
|
|
221
|
-
# parameters
|
|
222
306
|
command_list.extend(
|
|
223
307
|
self.process_parameters_get_list(arg_prefix='-generic_top ')
|
|
224
308
|
)
|
|
@@ -241,11 +325,33 @@ class CommandSimVivado(CommandSim, ToolVivado):
|
|
|
241
325
|
command_list += ['-L', x]
|
|
242
326
|
command_list += ['glbl']
|
|
243
327
|
command_list += self.args['elab-args']
|
|
244
|
-
|
|
328
|
+
|
|
329
|
+
# For Windows compatibility, we have some issues with command/Powershell passing args
|
|
330
|
+
# so as a workaround we'll create a .tcl script xelab_vivado.tcl:
|
|
331
|
+
# exec {command_list} >@stdout 2>@stderr
|
|
332
|
+
# with the caveat that it needs POSIX style paths in the vivado tclsh,
|
|
333
|
+
# and we'll return the list to run it:
|
|
334
|
+
# and we'll save this to self.vivado_tcl['xelab']
|
|
335
|
+
command_list += self.tcl_exec_pipe
|
|
336
|
+
tclfname = os.path.abspath(
|
|
337
|
+
os.path.join(self.args['work-dir'], 'xelab_vivado.tcl')
|
|
338
|
+
)
|
|
339
|
+
line = ' '.join(command_list).replace('\n', ' ')
|
|
340
|
+
self.vivado_tcl['xelab'].append(line)
|
|
341
|
+
with open(tclfname, 'w', encoding='utf-8' ) as ftcl:
|
|
342
|
+
ftcl.write(line)
|
|
343
|
+
# return list that will go unused
|
|
344
|
+
return [ ]
|
|
345
|
+
|
|
245
346
|
|
|
246
347
|
def get_simulate_command_lists(self, **kwargs) -> list:
|
|
247
|
-
|
|
248
|
-
|
|
348
|
+
'''Override from sim.CommandSim - which expects a return type list.
|
|
349
|
+
|
|
350
|
+
Since we run all in Vivado tcl, we always return an empty list,
|
|
351
|
+
and instead save in self.vivado_tcl['xsim'].
|
|
352
|
+
'''
|
|
353
|
+
# create TCL for in-simulation
|
|
354
|
+
sim_tcl_name = os.path.abspath(os.path.join(self.args['work-dir'], self.args['tcl-file']))
|
|
249
355
|
|
|
250
356
|
if self.args['waves']:
|
|
251
357
|
util.artifacts.add_extension(
|
|
@@ -253,8 +359,7 @@ class CommandSimVivado(CommandSim, ToolVivado):
|
|
|
253
359
|
typ='waveform', description='Vivado XSim Waveform WDB (Wave DataBase) file'
|
|
254
360
|
)
|
|
255
361
|
|
|
256
|
-
|
|
257
|
-
with open( tcl_name, 'w', encoding='utf-8' ) as fo:
|
|
362
|
+
with open( sim_tcl_name, 'w', encoding='utf-8' ) as fo:
|
|
258
363
|
if self.args['waves']:
|
|
259
364
|
if self.args['waves-start']:
|
|
260
365
|
print(f"run {self.args['waves-start']} ns", file=fo)
|
|
@@ -268,7 +373,10 @@ class CommandSimVivado(CommandSim, ToolVivado):
|
|
|
268
373
|
assert isinstance(self.args["sim-plusargs"], list), \
|
|
269
374
|
f'{self.target=} {type(self.args["sim-plusargs"])=} but must be list'
|
|
270
375
|
|
|
271
|
-
# xsim uses: --testplusarg
|
|
376
|
+
# xsim uses: --testplusarg name=value
|
|
377
|
+
# Note - this was problematic in Windows Powershell for --testplusarg "name=value"
|
|
378
|
+
# --testplusarg "name" would work, but the only way for xsim to parse it was to
|
|
379
|
+
# run it in vivado's tclsh.
|
|
272
380
|
xsim_plusargs_list = []
|
|
273
381
|
for x in self.args['sim-plusargs']:
|
|
274
382
|
xsim_plusargs_list.append('--testplusarg')
|
|
@@ -278,61 +386,104 @@ class CommandSimVivado(CommandSim, ToolVivado):
|
|
|
278
386
|
xsim_plusargs_list.append(f'\"{x}\"')
|
|
279
387
|
|
|
280
388
|
# execute snapshot
|
|
281
|
-
command_list = [
|
|
282
|
-
if sys.platform == "win32":
|
|
283
|
-
command_list[0] += ".bat"
|
|
389
|
+
command_list = ['exec', 'xsim']
|
|
284
390
|
command_list += self.tool_config.get('simulate-args', 'snapshot --stats').split()
|
|
285
391
|
if self.args['gui'] and not self.args['test-mode']:
|
|
286
392
|
command_list += ['-gui']
|
|
287
393
|
command_list += [
|
|
288
|
-
'--tclbatch',
|
|
394
|
+
'--tclbatch', Path(sim_tcl_name).as_posix(), # running in tclsh needs POSIX
|
|
289
395
|
"--sv_seed", sv_seed
|
|
290
396
|
]
|
|
291
397
|
command_list += xsim_plusargs_list
|
|
292
398
|
command_list += self.args['sim-args']
|
|
293
|
-
|
|
399
|
+
|
|
400
|
+
# For Windows compatibility we have some issues with command/Powershell passing args
|
|
401
|
+
# so as a workaround we'll create a .tcl script xsim_vivado.tcl:
|
|
402
|
+
# exec {command_list} >@stdout 2>@stderr
|
|
403
|
+
# with the caveat that it needs POSIX style paths in the vivado tclsh,
|
|
404
|
+
# and we'll save this to self.vivado_tcl['xsim']
|
|
405
|
+
command_list += self.tcl_exec_pipe
|
|
406
|
+
|
|
407
|
+
tclfname = os.path.abspath(
|
|
408
|
+
os.path.join(self.args['work-dir'], 'xsim_vivado.tcl')
|
|
409
|
+
)
|
|
410
|
+
line = ' '.join(command_list).replace('\n', ' ')
|
|
411
|
+
self.vivado_tcl['xsim'].append(line)
|
|
412
|
+
with open(tclfname, 'w', encoding='utf-8' ) as ftcl:
|
|
413
|
+
ftcl.write(line)
|
|
414
|
+
|
|
415
|
+
# Need to return list, will go unused.
|
|
416
|
+
return []
|
|
294
417
|
|
|
295
418
|
def get_post_simulate_command_lists(self, **kwargs) -> list:
|
|
296
419
|
return []
|
|
297
420
|
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
command_list
|
|
330
|
-
|
|
421
|
+
|
|
422
|
+
def add_xvlog_commands( # pylint: disable=too-many-branches
|
|
423
|
+
self, files_list: list, typ: str = 'sv'
|
|
424
|
+
) -> None:
|
|
425
|
+
'''Returns None, because we'll save the results in self.vivado_tcl['xvlog'].
|
|
426
|
+
|
|
427
|
+
Vivado still treats .v files like Verilog-2001, so we split xvlog into .v and .sv sections,
|
|
428
|
+
as two entries in self.vivado_tcl['xvlog'] if self.args['all-sv'] is False.
|
|
429
|
+
'''
|
|
430
|
+
|
|
431
|
+
|
|
432
|
+
if not files_list:
|
|
433
|
+
return
|
|
434
|
+
|
|
435
|
+
# For Windows compatibility we have some issues with command/Powershell passing args
|
|
436
|
+
# so as a workaround we'll create a .tcl script xvlog_sv_vivado.tcl:
|
|
437
|
+
# exec {command_list} >@stdout 2>@stderr
|
|
438
|
+
# with the caveat that it needs POSIX style paths in the vivado tclsh,
|
|
439
|
+
# and we'll save this to self.vivado_tcl['xvlog']
|
|
440
|
+
command_list = ['exec', 'xvlog']
|
|
441
|
+
if typ == 'sv':
|
|
442
|
+
command_list.append('-sv')
|
|
443
|
+
if self.args['uvm']:
|
|
444
|
+
command_list.extend(['-L', 'uvm'])
|
|
445
|
+
command_list += self.tool_config.get('compile-args', '').split()
|
|
446
|
+
if util.args['verbose']:
|
|
447
|
+
command_list += ['-v', '2']
|
|
448
|
+
for value in self.incdirs:
|
|
449
|
+
command_list.append('-i')
|
|
450
|
+
command_list.append(Path(value).as_posix())
|
|
451
|
+
for key, value in self.defines.items():
|
|
452
|
+
command_list.append('-d')
|
|
453
|
+
if value is not None:
|
|
454
|
+
# Because we're writing to a .tcl file, \" will become ", and \\\" will become \"
|
|
455
|
+
# we want \" in the final file. Parameters need to act the same way as defines:
|
|
456
|
+
value = f'{value}'.replace('"', '\\\"')
|
|
457
|
+
|
|
458
|
+
if value is None:
|
|
459
|
+
command_list.append(key)
|
|
460
|
+
else:
|
|
461
|
+
command_list.append(f"\"{key}={value}\"")
|
|
462
|
+
|
|
463
|
+
command_list += self.args['compile-args']
|
|
464
|
+
|
|
465
|
+
# Convert these to POSIX (even though we might be in Windows) b/c that's what Vivado
|
|
466
|
+
# tclsh needs
|
|
467
|
+
command_list += [Path(fpath).as_posix() for fpath in files_list]
|
|
468
|
+
command_list += self.tcl_exec_pipe
|
|
469
|
+
|
|
470
|
+
tclfname = os.path.abspath(
|
|
471
|
+
os.path.join(self.args['work-dir'], f'xvlog_{typ}_vivado.tcl')
|
|
472
|
+
)
|
|
473
|
+
|
|
474
|
+
# Make a one-liner from command line, save in self.vivado_tcl['xvlog']:
|
|
475
|
+
line = ' '.join(command_list).replace('\n', ' ')
|
|
476
|
+
self.vivado_tcl['xvlog'].append(line)
|
|
477
|
+
with open(tclfname, 'w', encoding='utf-8' ) as ftcl:
|
|
478
|
+
ftcl.write(line)
|
|
479
|
+
ftcl.write('\n')
|
|
331
480
|
|
|
332
481
|
|
|
333
482
|
def _add_glbl_v(self):
|
|
334
483
|
'''Adds glbl.v from Vivado's install path to self.files_v'''
|
|
335
|
-
glbl_v = self.vivado_base_path.replace(
|
|
484
|
+
glbl_v = self.vivado_base_path.replace(
|
|
485
|
+
'bin', os.path.join('data', 'verilog', 'src', 'glbl.v')
|
|
486
|
+
)
|
|
336
487
|
if any(x.endswith('glbl.v') for x in self.files_v):
|
|
337
488
|
util.warning(f'--add-glbl-v: Not adding {glbl_v=} b/c glbl.v already in',
|
|
338
489
|
f'{self.files_v=}')
|
|
@@ -349,6 +500,8 @@ class CommandSimVivado(CommandSim, ToolVivado):
|
|
|
349
500
|
sim.log or compile.log
|
|
350
501
|
'''
|
|
351
502
|
_, leafname = os.path.split(name)
|
|
503
|
+
if leafname == 'vivado.log':
|
|
504
|
+
description = 'Vivado XSim log from stdout/stderr'
|
|
352
505
|
if leafname == 'xsim.log':
|
|
353
506
|
description = 'Vivado XSim simulation step (3/3) log from stdout/stderr'
|
|
354
507
|
elif leafname == 'xelab.log':
|
|
@@ -429,7 +582,7 @@ class CommandSynthVivado(CommandSynth, ToolVivado):
|
|
|
429
582
|
parameters = ' '.join(
|
|
430
583
|
sim.parameters_dict_get_command_list(params=self.parameters, arg_prefix='-generic ')
|
|
431
584
|
)
|
|
432
|
-
incdirs = ' '.join([f'-include_dirs {x}' for x in self.incdirs])
|
|
585
|
+
incdirs = ' '.join([f'-include_dirs {Path(x).as_posix()}' for x in self.incdirs])
|
|
433
586
|
flatten = ""
|
|
434
587
|
if self.args['flatten-all']:
|
|
435
588
|
flatten = "-flatten_hierarchy full"
|
|
@@ -437,12 +590,14 @@ class CommandSynthVivado(CommandSynth, ToolVivado):
|
|
|
437
590
|
flatten = "-flatten_hierarchy none"
|
|
438
591
|
|
|
439
592
|
tcl_lines = []
|
|
593
|
+
# Note - if we're in Windows, the vivado tcl that processes these lines expects POSIX
|
|
594
|
+
# paths, not command/Powershell paths. so we will use Path(str).as_posix()
|
|
440
595
|
for f in self.files_v:
|
|
441
|
-
tcl_lines.append(f"read_verilog {f}")
|
|
596
|
+
tcl_lines.append(f"read_verilog {Path(f).as_posix()}")
|
|
442
597
|
for f in self.files_sv:
|
|
443
|
-
tcl_lines.append(f"read_verilog -sv {f}")
|
|
598
|
+
tcl_lines.append(f"read_verilog -sv {Path(f).as_posix()}")
|
|
444
599
|
for f in self.files_vhd:
|
|
445
|
-
tcl_lines.append(f"add_file {f}")
|
|
600
|
+
tcl_lines.append(f"add_file {Path(f).as_posix()}")
|
|
446
601
|
|
|
447
602
|
part = self.args['part']
|
|
448
603
|
top = self.args['top']
|
|
@@ -465,11 +620,11 @@ class CommandSynthVivado(CommandSynth, ToolVivado):
|
|
|
465
620
|
for _file in self.files_sdc:
|
|
466
621
|
# NOTE - sdc files cannot (yet) be attached to other modules.
|
|
467
622
|
tcl_lines += [
|
|
468
|
-
f"add_files -fileset constraints_1 {_file} {v}",
|
|
623
|
+
f"add_files -fileset constraints_1 {Path(_file).as_posix()} {v}",
|
|
469
624
|
]
|
|
470
625
|
if xdc_file:
|
|
471
626
|
tcl_lines += [
|
|
472
|
-
f"add_files -fileset constraints_1 {xdc_file} {v}",
|
|
627
|
+
f"add_files -fileset constraints_1 {Path(xdc_file).as_posix()} {v}",
|
|
473
628
|
]
|
|
474
629
|
tcl_lines += [
|
|
475
630
|
"# FIRST PASS -- auto_detect_xpm",
|
|
@@ -513,14 +668,14 @@ class CommandSynthVivado(CommandSynth, ToolVivado):
|
|
|
513
668
|
|
|
514
669
|
if default_xdc:
|
|
515
670
|
tcl_lines += [
|
|
516
|
-
f"puts \"(Used default XDC: {xdc_file})\"",
|
|
671
|
+
f"puts \"(Used default XDC: {Path(xdc_file).as_posix()})\"",
|
|
517
672
|
f"puts \"DEF CLOCK NS : [format %.3f {self.args['clock-ns']}]\"",
|
|
518
673
|
f"puts \"DEF IDELAY NS : [format %.3f {self.args['idelay-ns']}]\"",
|
|
519
674
|
f"puts \"DEF ODELAY NS : [format %.3f {self.args['odelay-ns']}]\"",
|
|
520
675
|
]
|
|
521
676
|
else:
|
|
522
677
|
tcl_lines += [
|
|
523
|
-
f"puts \"(Used provided XDC: {xdc_file})\"",
|
|
678
|
+
f"puts \"(Used provided XDC: {Path(xdc_file).as_posix()})\"",
|
|
524
679
|
]
|
|
525
680
|
tcl_lines += [
|
|
526
681
|
"puts \"\"",
|
|
@@ -592,13 +747,13 @@ class CommandProjVivado(CommandProj, ToolVivado):
|
|
|
592
747
|
tcl_file = os.path.abspath(os.path.join(self.args['work-dir'], self.args['tcl-file']))
|
|
593
748
|
v = self.get_vivado_tcl_verbose_arg()
|
|
594
749
|
|
|
595
|
-
incdirs = " ".join(self.incdirs)
|
|
750
|
+
incdirs = " ".join([Path(x).as_posix() for x in self.incdirs])
|
|
596
751
|
defines = ""
|
|
597
752
|
for key, value in self.defines.items():
|
|
598
753
|
defines += (f"{key} " if value is None else f"{key}={value} ")
|
|
599
754
|
|
|
600
755
|
tcl_lines = [
|
|
601
|
-
f"create_project {self.args['top']}_proj {self.args['work-dir']} {v}"
|
|
756
|
+
f"create_project {self.args['top']}_proj {Path(self.args['work-dir']).as_posix()} {v}"
|
|
602
757
|
]
|
|
603
758
|
|
|
604
759
|
if self.args['oc-vivado-tcl'] and oc_root:
|
|
@@ -630,7 +785,7 @@ class CommandProjVivado(CommandProj, ToolVivado):
|
|
|
630
785
|
else:
|
|
631
786
|
fileset = "sources_1"
|
|
632
787
|
tcl_lines += [
|
|
633
|
-
f"add_files -norecurse {f} -fileset [get_filesets {fileset}]"
|
|
788
|
+
f"add_files -norecurse {Path(f).as_posix()} -fileset [get_filesets {fileset}]"
|
|
634
789
|
]
|
|
635
790
|
|
|
636
791
|
tcl_lines += [
|
|
@@ -776,16 +931,16 @@ class CommandFListVivado(CommandFList, ToolVivado):
|
|
|
776
931
|
class CommandUploadVivado(CommandUpload, ToolVivado):
|
|
777
932
|
'''CommandUploadVivado is a command handler for: eda upload --tool=vivado'''
|
|
778
933
|
|
|
934
|
+
SUPPORTED_BIT_EXT = ['.bit']
|
|
935
|
+
|
|
779
936
|
def __init__(self, config: dict):
|
|
780
937
|
CommandUpload.__init__(self, config)
|
|
781
938
|
ToolVivado.__init__(self, config=self.config)
|
|
782
939
|
# add args specific to this tool
|
|
783
940
|
self.args.update({
|
|
784
941
|
'gui': False,
|
|
785
|
-
'bitfile': "",
|
|
786
942
|
'list-usbs': False,
|
|
787
943
|
'list-devices': False,
|
|
788
|
-
'list-bitfiles': False,
|
|
789
944
|
'usb': -1,
|
|
790
945
|
'device': -1,
|
|
791
946
|
'host': "localhost",
|
|
@@ -795,82 +950,51 @@ class CommandUploadVivado(CommandUpload, ToolVivado):
|
|
|
795
950
|
'log-file': "eda_upload.log",
|
|
796
951
|
'test-mode': False,
|
|
797
952
|
})
|
|
798
|
-
# TODO(drew): Add self.args_help.update({...})
|
|
799
953
|
|
|
800
|
-
|
|
801
|
-
|
|
954
|
+
# TODO(drew): Complete self.args_help.update({...})
|
|
955
|
+
self.args_help.update({
|
|
956
|
+
'bitfile': 'BIT file to upload (auto-detected if not specified)',
|
|
957
|
+
'list-bitfiles': 'List available BIT files',
|
|
958
|
+
})
|
|
959
|
+
|
|
960
|
+
|
|
802
961
|
def do_it(self): # pylint: disable=too-many-locals,too-many-branches,too-many-statements
|
|
962
|
+
'''
|
|
963
|
+
Note this is called directly by opencos.commands.CommandUpload, based
|
|
964
|
+
on which bitfile(s) were found, or if --tool=vivado was set
|
|
965
|
+
|
|
966
|
+
We do not need to handle --list-bitfiles, was handled by CommandUpload.
|
|
967
|
+
'''
|
|
968
|
+
|
|
803
969
|
# add defines for this job
|
|
804
970
|
self.set_tool_defines()
|
|
805
971
|
self.write_eda_config_and_args()
|
|
806
972
|
|
|
807
973
|
bitfile = None
|
|
808
|
-
targets = []
|
|
809
974
|
if self.args['bitfile']:
|
|
810
975
|
if os.path.isfile(self.args['bitfile']):
|
|
811
976
|
bitfile = self.args['bitfile']
|
|
812
|
-
else:
|
|
813
|
-
# Not a file, treat as search pattern
|
|
814
|
-
targets = [self.args['bitfile']]
|
|
815
|
-
|
|
816
|
-
# TODO(drew): It might be nice to use positional args (supported by
|
|
817
|
-
# eda_base.Command) here, and in multi.py, so we don't accidentally
|
|
818
|
-
# grab errant --arg style strings as potential filenames or target patterns
|
|
819
|
-
for f in self.unparsed_args:
|
|
820
|
-
# self.unparsed_args are leftovers from Command.process_tokens(..)
|
|
821
|
-
if os.path.isfile(f):
|
|
822
|
-
if bitfile is None:
|
|
823
|
-
bitfile = f
|
|
824
|
-
else:
|
|
825
|
-
util.error("Too many bitfiles provided")
|
|
826
|
-
if not self.args['test-mode']:
|
|
827
|
-
sys.exit(0)
|
|
828
|
-
else:
|
|
829
|
-
# Not a file, treat as search pattern
|
|
830
|
-
targets.append(f)
|
|
831
|
-
|
|
832
|
-
# Auto-discover bitfile logic (for when we have no bitfile, and we
|
|
833
|
-
# weren't called just to listdevice/listusb)
|
|
834
|
-
if self.args['list-bitfiles'] or \
|
|
835
|
-
(not bitfile and not self.args['list-devices'] and not self.args['list-usbs']):
|
|
836
|
-
bitfiles: list[Path] = []
|
|
837
977
|
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
if f.endswith(".bit"):
|
|
842
|
-
fullpath = os.path.abspath(Path(root) / f)
|
|
843
|
-
if fullpath not in bitfiles:
|
|
844
|
-
bitfiles.append(fullpath)
|
|
845
|
-
|
|
846
|
-
matched: list[Path] = []
|
|
847
|
-
for cand in bitfiles:
|
|
848
|
-
util.debug(f"Looking for {cand=} in {targets=}")
|
|
849
|
-
passing = all(re.search(t, str(cand)) for t in targets)
|
|
850
|
-
if passing:
|
|
851
|
-
matched.append(cand)
|
|
852
|
-
mod_time_string = datetime.fromtimestamp(
|
|
853
|
-
os.path.getmtime(cand)).strftime('%Y-%m-%d %H:%M:%S')
|
|
854
|
-
util.info(f"Found matching bitfile: {mod_time_string} : {cand}")
|
|
855
|
-
|
|
856
|
-
if len(matched) > 1:
|
|
857
|
-
if self.args['list-bitfiles']:
|
|
858
|
-
util.info("Too many matches to continue without adding search terms. Done.")
|
|
859
|
-
if not self.args['test-mode']:
|
|
860
|
-
sys.exit(0)
|
|
861
|
-
else:
|
|
862
|
-
util.error("Too many matches, please add search terms...")
|
|
978
|
+
# self.bitfiles was already set by CommandUpload.process_tokens()
|
|
979
|
+
if len(self.bitfiles) == 1:
|
|
980
|
+
bitfile = self.bitfiles[0]
|
|
863
981
|
|
|
864
|
-
if not matched and bitfile is None:
|
|
865
|
-
if self.args['list-bitfiles']:
|
|
866
|
-
util.info("No matching bitfiles found, done.")
|
|
867
|
-
if not self.args['test-mode']:
|
|
868
|
-
sys.exit(0)
|
|
869
|
-
else:
|
|
870
|
-
util.error("Failed to find a matching bitfile")
|
|
871
982
|
|
|
872
|
-
|
|
873
|
-
|
|
983
|
+
# Auto-discover bitfile logic (for when we have no bitfile, and we
|
|
984
|
+
# weren't called just to listdevice/listusb)
|
|
985
|
+
if not bitfile and not self.args['list-devices'] and not self.args['list-usbs']:
|
|
986
|
+
|
|
987
|
+
# CommandUpload already displayed them, and exited on --list-bitfiles.
|
|
988
|
+
if len(self.bitfiles) > 1:
|
|
989
|
+
util.warning("Too many matches to continue without adding search terms,",
|
|
990
|
+
"upload not performed.")
|
|
991
|
+
if not self.args['test-mode']:
|
|
992
|
+
self.error('Upload not performed, multiple bit files found, please add',
|
|
993
|
+
'search terms...')
|
|
994
|
+
return
|
|
995
|
+
|
|
996
|
+
self.error("Failed to find a matching bitfile")
|
|
997
|
+
return
|
|
874
998
|
|
|
875
999
|
# ── Generate TCL script ───────────────────────────────────────────────────
|
|
876
1000
|
script_file = Path(self.args['tcl-file'])
|
|
@@ -945,10 +1069,11 @@ class CommandUploadVivado(CommandUpload, ToolVivado):
|
|
|
945
1069
|
w('refresh_hw_device -update_hw_probes false -quiet $hw_device\n')
|
|
946
1070
|
|
|
947
1071
|
if bitfile is not None:
|
|
948
|
-
w('set_property PROGRAM.FILE {' + bitfile + '} $hw_device\n')
|
|
1072
|
+
w('set_property PROGRAM.FILE {' + Path(bitfile).as_posix() + '} $hw_device\n')
|
|
949
1073
|
w('program_hw_devices [current_hw_device]\n')
|
|
950
1074
|
|
|
951
1075
|
w('close_hw_target\n')
|
|
1076
|
+
w('quit\n')
|
|
952
1077
|
|
|
953
1078
|
except OSError as exc:
|
|
954
1079
|
util.error(f"Cannot create {script_file}: {exc}")
|
|
@@ -971,11 +1096,21 @@ class CommandUploadVivado(CommandUpload, ToolVivado):
|
|
|
971
1096
|
]
|
|
972
1097
|
if not util.args['verbose']:
|
|
973
1098
|
command_list.append('-notrace')
|
|
974
|
-
self.exec(Path(util.getcwd()), command_list)
|
|
1099
|
+
_, stdout, _ = self.exec(Path(util.getcwd()), command_list)
|
|
1100
|
+
|
|
1101
|
+
# Do some log scraping
|
|
1102
|
+
for line in stdout.split('\n'):
|
|
1103
|
+
if line.startswith('WARNING:'):
|
|
1104
|
+
self.tool_warning_count += 1
|
|
1105
|
+
elif line.startswith('ERROR:') or line.startswith('[ERROR]'):
|
|
1106
|
+
self.tool_error_count += 1
|
|
975
1107
|
|
|
976
1108
|
if not self.args['keep']:
|
|
977
1109
|
os.unlink(self.args['tcl-file'])
|
|
978
1110
|
|
|
1111
|
+
self.report_tool_warn_error_counts()
|
|
1112
|
+
self.report_pass_fail()
|
|
1113
|
+
|
|
979
1114
|
util.info("Upload done")
|
|
980
1115
|
|
|
981
1116
|
|