opencos-eda 0.3.9__py3-none-any.whl → 0.3.11__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 +89 -120
- opencos/commands/export.py +7 -2
- opencos/commands/multi.py +3 -3
- opencos/commands/sim.py +14 -16
- opencos/commands/synth.py +1 -2
- opencos/commands/upload.py +192 -4
- opencos/commands/waves.py +8 -8
- opencos/deps/deps_commands.py +6 -6
- opencos/deps/deps_file.py +82 -79
- 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 +175 -41
- opencos/eda_base.py +180 -50
- opencos/eda_config.py +62 -16
- opencos/eda_config_defaults.yml +21 -4
- opencos/eda_deps_bash_completion.bash +37 -15
- opencos/files.py +26 -1
- opencos/tools/cocotb.py +5 -5
- opencos/tools/invio.py +2 -2
- opencos/tools/invio_yosys.py +2 -1
- opencos/tools/iverilog.py +3 -3
- opencos/tools/quartus.py +113 -115
- opencos/tools/questa_common.py +3 -4
- opencos/tools/riviera.py +3 -3
- opencos/tools/slang.py +11 -7
- opencos/tools/slang_yosys.py +1 -0
- opencos/tools/surelog.py +4 -3
- opencos/tools/verilator.py +5 -4
- opencos/tools/vivado.py +307 -176
- opencos/tools/yosys.py +4 -4
- opencos/util.py +6 -3
- opencos/utils/dict_helpers.py +31 -0
- opencos/utils/markup_helpers.py +2 -2
- opencos/utils/str_helpers.py +7 -0
- opencos/utils/subprocess_helpers.py +3 -3
- opencos/utils/vscode_helper.py +2 -2
- opencos/utils/vsim_helper.py +58 -22
- {opencos_eda-0.3.9.dist-info → opencos_eda-0.3.11.dist-info}/METADATA +1 -1
- opencos_eda-0.3.11.dist-info/RECORD +94 -0
- {opencos_eda-0.3.9.dist-info → opencos_eda-0.3.11.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.9.dist-info/RECORD +0 -99
- {opencos_eda-0.3.9.dist-info → opencos_eda-0.3.11.dist-info}/WHEEL +0 -0
- {opencos_eda-0.3.9.dist-info → opencos_eda-0.3.11.dist-info}/licenses/LICENSE +0 -0
- {opencos_eda-0.3.9.dist-info → opencos_eda-0.3.11.dist-info}/licenses/LICENSE.spdx +0 -0
- {opencos_eda-0.3.9.dist-info → opencos_eda-0.3.11.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}')")
|
|
@@ -120,7 +115,10 @@ class ToolVivado(Tool):
|
|
|
120
115
|
|
|
121
116
|
|
|
122
117
|
class CommandSimVivado(CommandSim, ToolVivado):
|
|
123
|
-
'''CommandSimVivado is a command handler for: eda sim --tool=vivado, uses xvlog, xelab, xsim
|
|
118
|
+
'''CommandSimVivado is a command handler for: eda sim --tool=vivado, uses xvlog, xelab, xsim
|
|
119
|
+
|
|
120
|
+
Note that we attempt to run a generated .tcl script within vivado, that will perform the
|
|
121
|
+
3-step compile/elaborate/simulate steps'''
|
|
124
122
|
|
|
125
123
|
def __init__(self, config: dict):
|
|
126
124
|
CommandSim.__init__(self, config)
|
|
@@ -141,9 +139,19 @@ class CommandSimVivado(CommandSim, ToolVivado):
|
|
|
141
139
|
})
|
|
142
140
|
|
|
143
141
|
self.sim_libraries = self.tool_config.get('sim-libraries', [])
|
|
144
|
-
|
|
145
|
-
self.
|
|
146
|
-
|
|
142
|
+
|
|
143
|
+
self.vivado_tcl = {
|
|
144
|
+
'xvlog': [],
|
|
145
|
+
'xelab': [],
|
|
146
|
+
'xsim': [],
|
|
147
|
+
'exe_list': [],
|
|
148
|
+
'check_logs': [],
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
# Note this is the syntax to have your Vivado tcl print to
|
|
152
|
+
# stdout, which we do for xvlog, xelab always, and even for xsim if in
|
|
153
|
+
# --gui or not GUI
|
|
154
|
+
self.tcl_exec_pipe = ['>@stdout', '2>@stderr']
|
|
147
155
|
|
|
148
156
|
|
|
149
157
|
def set_tool_defines(self):
|
|
@@ -153,72 +161,144 @@ class CommandSimVivado(CommandSim, ToolVivado):
|
|
|
153
161
|
|
|
154
162
|
def prepare_compile(self):
|
|
155
163
|
self.set_tool_defines()
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
self.
|
|
159
|
-
self.
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
164
|
+
|
|
165
|
+
# Don't use the return values, these will set values in self.vivado_tcl:
|
|
166
|
+
self.get_compile_command_lists()
|
|
167
|
+
self.get_elaborate_command_lists()
|
|
168
|
+
self.get_simulate_command_lists()
|
|
169
|
+
|
|
170
|
+
# We will always run a generated .tcl scirpt from vivado as:
|
|
171
|
+
# one command line call for:
|
|
172
|
+
# vivado -mode batch -source all_vivado.tcl
|
|
173
|
+
# So we have to create all_vivado.tcl based on our commands that we saved
|
|
174
|
+
# in self.vivado_tcl:
|
|
175
|
+
tclfname = os.path.abspath(
|
|
176
|
+
os.path.join(self.args['work-dir'], 'all_vivado.tcl')
|
|
163
177
|
)
|
|
178
|
+
with open(tclfname, 'w', encoding='utf-8' ) as ftcl:
|
|
179
|
+
for line in self.vivado_tcl['xvlog']:
|
|
180
|
+
if self.args['stop-before-compile']:
|
|
181
|
+
ftcl.write('# ')
|
|
182
|
+
ftcl.write(line)
|
|
183
|
+
ftcl.write('\n\n')
|
|
184
|
+
for line in self.vivado_tcl['xelab']:
|
|
185
|
+
if any(self.args[x] for x in ('stop-before-compile', 'stop-after-compile')):
|
|
186
|
+
ftcl.write('# ')
|
|
187
|
+
ftcl.write(line)
|
|
188
|
+
ftcl.write('\n\n')
|
|
189
|
+
for line in self.vivado_tcl['xsim']:
|
|
190
|
+
if any(self.args[x] for x in ('stop-before-compile', 'stop-after-compile',
|
|
191
|
+
'stop-after-elaborate')):
|
|
192
|
+
ftcl.write('# ')
|
|
193
|
+
ftcl.write(line)
|
|
194
|
+
ftcl.write('\n\n')
|
|
195
|
+
|
|
196
|
+
# We will alwyas run this in -mode batch, the xsim call in
|
|
197
|
+
# all_vivado.tcl will have -gui in it:
|
|
198
|
+
self.vivado_tcl['exe_list'] = [
|
|
199
|
+
self.vivado_exe, '-mode', 'batch', '-source', 'all_vivado.tcl'
|
|
200
|
+
]
|
|
164
201
|
|
|
165
|
-
|
|
202
|
+
# Since we run this in a single vivado call, we don't get the automatic
|
|
203
|
+
# log checking from sim.run_commands_check_logs(..), so we need to remember
|
|
204
|
+
# which logs to check:
|
|
166
205
|
if self.args['stop-before-compile']:
|
|
167
|
-
|
|
168
|
-
self.
|
|
169
|
-
self.
|
|
206
|
+
self.vivado_tcl['check_logs'] = []
|
|
207
|
+
elif self.args['stop-after-compile']:
|
|
208
|
+
self.vivado_tcl['check_logs'] = ['vivado.log', 'xvlog.log']
|
|
209
|
+
elif self.args['stop-after-elaborate']:
|
|
210
|
+
self.vivado_tcl['check_logs'] = ['vivado.log', 'xvlog.log', 'xelab.log']
|
|
211
|
+
else:
|
|
212
|
+
self.vivado_tcl['check_logs'] = ['vivado.log', 'xvlog.log', 'xelab.log', 'xsim.log']
|
|
213
|
+
|
|
214
|
+
self.write_sh_scripts_to_work_dir(
|
|
215
|
+
compile_lists=[],
|
|
216
|
+
elaborate_lists=[],
|
|
217
|
+
simulate_lists=[self.vivado_tcl['exe_list']],
|
|
218
|
+
compile_line_breaks=False
|
|
170
219
|
)
|
|
171
220
|
|
|
221
|
+
|
|
222
|
+
def compile(self):
|
|
223
|
+
# handled in simulate()
|
|
224
|
+
return
|
|
225
|
+
|
|
172
226
|
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
|
-
)
|
|
227
|
+
# handled in simulate()
|
|
228
|
+
return
|
|
180
229
|
|
|
181
230
|
def simulate(self):
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
231
|
+
# even though we run one command line call (vivado -mode batch -sourc TCLFILE)
|
|
232
|
+
# it still saves xvlog.log, xelab.log, xsim.log, and a (useless) vivado.log.
|
|
233
|
+
# we'll have to manually check the logs for errors if this was run in --gui:
|
|
185
234
|
self.run_commands_check_logs(
|
|
186
|
-
self.
|
|
235
|
+
[self.vivado_tcl['exe_list']], check_logs=False, log_filename='vivado.log'
|
|
187
236
|
)
|
|
188
237
|
|
|
238
|
+
for log_fname in self.vivado_tcl['check_logs']:
|
|
239
|
+
filename = os.path.join(self.args['work-dir'], log_fname)
|
|
240
|
+
self.check_logs_for_errors(filename=filename)
|
|
241
|
+
|
|
242
|
+
|
|
189
243
|
def get_compile_command_lists(self, **kwargs) -> list:
|
|
244
|
+
'''Override from sim.CommandSim - which expects a return type list.
|
|
245
|
+
|
|
246
|
+
Since we run all in Vivado tcl, we always return an empty list,
|
|
247
|
+
and instead save in self.vivado_tcl['xvlog'].
|
|
248
|
+
'''
|
|
190
249
|
self.set_tool_defines()
|
|
191
|
-
ret = [] # list of (list of ['xvlog', arg0, arg1, ..])
|
|
192
250
|
|
|
193
251
|
if self.args['add-glbl-v']:
|
|
194
252
|
self._add_glbl_v()
|
|
195
253
|
|
|
196
254
|
# compile verilog
|
|
197
255
|
if self.files_v:
|
|
198
|
-
|
|
199
|
-
self.get_xvlog_commands(files_list=self.files_v, typ='v')
|
|
200
|
-
)
|
|
256
|
+
self.add_xvlog_commands(files_list=self.files_v, typ='v')
|
|
201
257
|
|
|
202
258
|
# compile systemverilog
|
|
203
259
|
if self.files_sv:
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
260
|
+
self.add_xvlog_commands(files_list=self.files_sv, typ='sv')
|
|
261
|
+
|
|
262
|
+
return []
|
|
263
|
+
|
|
264
|
+
def process_parameters_get_list(self, arg_prefix: str = '-G') -> list:
|
|
265
|
+
'''Override from sim.CommandSim
|
|
266
|
+
|
|
267
|
+
custom handler for parameters, instead of the one in sim.py
|
|
268
|
+
all will be passed as -generic_top "k=v", avoids all unknown shell or bash
|
|
269
|
+
behavior in windows.
|
|
270
|
+
'''
|
|
271
|
+
|
|
272
|
+
# ignore arg_prefix (use -generic_top):
|
|
273
|
+
ret = []
|
|
274
|
+
for k,v in self.parameters.items():
|
|
275
|
+
if not isinstance(v, (int, str)):
|
|
276
|
+
util.warning(
|
|
277
|
+
f'parameter {k} has value: {v}, parameters must be int/string types'
|
|
278
|
+
)
|
|
279
|
+
if isinstance(v, int):
|
|
280
|
+
ret.extend(['-generic_top', f'"{k}={v}"'])
|
|
281
|
+
else:
|
|
282
|
+
v = f'{v}'.strip('\n') # stringify
|
|
283
|
+
# Because we're writing to a .tcl file, \" will become ", and \\\" will become \"
|
|
284
|
+
# we want \" in the final file.
|
|
285
|
+
v = v.replace('"', '\\\"')
|
|
286
|
+
ret.extend(['-generic_top', f'"{k}={v}"'])
|
|
287
|
+
return ret
|
|
207
288
|
|
|
208
|
-
return ret # list of lists
|
|
209
289
|
|
|
210
290
|
def get_elaborate_command_lists(self, **kwargs) -> list:
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
291
|
+
'''Override from sim.CommandSim - which expects a return type list.
|
|
292
|
+
|
|
293
|
+
Since we run all in Vivado tcl, we always return an empty list,
|
|
294
|
+
and instead save in self.vivado_tcl['xelab'].
|
|
295
|
+
'''
|
|
296
|
+
|
|
297
|
+
command_list = ['exec', 'xelab', self.args['top']]
|
|
298
|
+
|
|
299
|
+
command_list += self.tool_config.get(
|
|
300
|
+
'elab-args', '-s snapshot -timescale 1ns/1ps --stats').split()
|
|
220
301
|
|
|
221
|
-
# parameters
|
|
222
302
|
command_list.extend(
|
|
223
303
|
self.process_parameters_get_list(arg_prefix='-generic_top ')
|
|
224
304
|
)
|
|
@@ -241,11 +321,33 @@ class CommandSimVivado(CommandSim, ToolVivado):
|
|
|
241
321
|
command_list += ['-L', x]
|
|
242
322
|
command_list += ['glbl']
|
|
243
323
|
command_list += self.args['elab-args']
|
|
244
|
-
|
|
324
|
+
|
|
325
|
+
# For Windows compatibility, we have some issues with command/Powershell passing args
|
|
326
|
+
# so as a workaround we'll create a .tcl script xelab_vivado.tcl:
|
|
327
|
+
# exec {command_list} >@stdout 2>@stderr
|
|
328
|
+
# with the caveat that it needs POSIX style paths in the vivado tclsh,
|
|
329
|
+
# and we'll return the list to run it:
|
|
330
|
+
# and we'll save this to self.vivado_tcl['xelab']
|
|
331
|
+
command_list += self.tcl_exec_pipe
|
|
332
|
+
tclfname = os.path.abspath(
|
|
333
|
+
os.path.join(self.args['work-dir'], 'xelab_vivado.tcl')
|
|
334
|
+
)
|
|
335
|
+
line = ' '.join(command_list).replace('\n', ' ')
|
|
336
|
+
self.vivado_tcl['xelab'].append(line)
|
|
337
|
+
with open(tclfname, 'w', encoding='utf-8' ) as ftcl:
|
|
338
|
+
ftcl.write(line)
|
|
339
|
+
# return list that will go unused
|
|
340
|
+
return [ ]
|
|
341
|
+
|
|
245
342
|
|
|
246
343
|
def get_simulate_command_lists(self, **kwargs) -> list:
|
|
247
|
-
|
|
248
|
-
|
|
344
|
+
'''Override from sim.CommandSim - which expects a return type list.
|
|
345
|
+
|
|
346
|
+
Since we run all in Vivado tcl, we always return an empty list,
|
|
347
|
+
and instead save in self.vivado_tcl['xsim'].
|
|
348
|
+
'''
|
|
349
|
+
# create TCL for in-simulation
|
|
350
|
+
sim_tcl_name = os.path.abspath(os.path.join(self.args['work-dir'], self.args['tcl-file']))
|
|
249
351
|
|
|
250
352
|
if self.args['waves']:
|
|
251
353
|
util.artifacts.add_extension(
|
|
@@ -253,8 +355,7 @@ class CommandSimVivado(CommandSim, ToolVivado):
|
|
|
253
355
|
typ='waveform', description='Vivado XSim Waveform WDB (Wave DataBase) file'
|
|
254
356
|
)
|
|
255
357
|
|
|
256
|
-
|
|
257
|
-
with open( tcl_name, 'w', encoding='utf-8' ) as fo:
|
|
358
|
+
with open( sim_tcl_name, 'w', encoding='utf-8' ) as fo:
|
|
258
359
|
if self.args['waves']:
|
|
259
360
|
if self.args['waves-start']:
|
|
260
361
|
print(f"run {self.args['waves-start']} ns", file=fo)
|
|
@@ -268,7 +369,10 @@ class CommandSimVivado(CommandSim, ToolVivado):
|
|
|
268
369
|
assert isinstance(self.args["sim-plusargs"], list), \
|
|
269
370
|
f'{self.target=} {type(self.args["sim-plusargs"])=} but must be list'
|
|
270
371
|
|
|
271
|
-
# xsim uses: --testplusarg
|
|
372
|
+
# xsim uses: --testplusarg name=value
|
|
373
|
+
# Note - this was problematic in Windows Powershell for --testplusarg "name=value"
|
|
374
|
+
# --testplusarg "name" would work, but the only way for xsim to parse it was to
|
|
375
|
+
# run it in vivado's tclsh.
|
|
272
376
|
xsim_plusargs_list = []
|
|
273
377
|
for x in self.args['sim-plusargs']:
|
|
274
378
|
xsim_plusargs_list.append('--testplusarg')
|
|
@@ -278,61 +382,104 @@ class CommandSimVivado(CommandSim, ToolVivado):
|
|
|
278
382
|
xsim_plusargs_list.append(f'\"{x}\"')
|
|
279
383
|
|
|
280
384
|
# execute snapshot
|
|
281
|
-
command_list = [
|
|
282
|
-
if sys.platform == "win32":
|
|
283
|
-
command_list[0] += ".bat"
|
|
385
|
+
command_list = ['exec', 'xsim']
|
|
284
386
|
command_list += self.tool_config.get('simulate-args', 'snapshot --stats').split()
|
|
285
387
|
if self.args['gui'] and not self.args['test-mode']:
|
|
286
388
|
command_list += ['-gui']
|
|
287
389
|
command_list += [
|
|
288
|
-
'--tclbatch',
|
|
390
|
+
'--tclbatch', Path(sim_tcl_name).as_posix(), # running in tclsh needs POSIX
|
|
289
391
|
"--sv_seed", sv_seed
|
|
290
392
|
]
|
|
291
393
|
command_list += xsim_plusargs_list
|
|
292
394
|
command_list += self.args['sim-args']
|
|
293
|
-
|
|
395
|
+
|
|
396
|
+
# For Windows compatibility we have some issues with command/Powershell passing args
|
|
397
|
+
# so as a workaround we'll create a .tcl script xsim_vivado.tcl:
|
|
398
|
+
# exec {command_list} >@stdout 2>@stderr
|
|
399
|
+
# with the caveat that it needs POSIX style paths in the vivado tclsh,
|
|
400
|
+
# and we'll save this to self.vivado_tcl['xsim']
|
|
401
|
+
command_list += self.tcl_exec_pipe
|
|
402
|
+
|
|
403
|
+
tclfname = os.path.abspath(
|
|
404
|
+
os.path.join(self.args['work-dir'], 'xsim_vivado.tcl')
|
|
405
|
+
)
|
|
406
|
+
line = ' '.join(command_list).replace('\n', ' ')
|
|
407
|
+
self.vivado_tcl['xsim'].append(line)
|
|
408
|
+
with open(tclfname, 'w', encoding='utf-8' ) as ftcl:
|
|
409
|
+
ftcl.write(line)
|
|
410
|
+
|
|
411
|
+
# Need to return list, will go unused.
|
|
412
|
+
return []
|
|
294
413
|
|
|
295
414
|
def get_post_simulate_command_lists(self, **kwargs) -> list:
|
|
296
415
|
return []
|
|
297
416
|
|
|
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
|
-
|
|
417
|
+
|
|
418
|
+
def add_xvlog_commands( # pylint: disable=too-many-branches
|
|
419
|
+
self, files_list: list, typ: str = 'sv'
|
|
420
|
+
) -> None:
|
|
421
|
+
'''Returns None, because we'll save the results in self.vivado_tcl['xvlog'].
|
|
422
|
+
|
|
423
|
+
Vivado still treats .v files like Verilog-2001, so we split xvlog into .v and .sv sections,
|
|
424
|
+
as two entries in self.vivado_tcl['xvlog'] if self.args['all-sv'] is False.
|
|
425
|
+
'''
|
|
426
|
+
|
|
427
|
+
|
|
428
|
+
if not files_list:
|
|
429
|
+
return
|
|
430
|
+
|
|
431
|
+
# For Windows compatibility we have some issues with command/Powershell passing args
|
|
432
|
+
# so as a workaround we'll create a .tcl script xvlog_sv_vivado.tcl:
|
|
433
|
+
# exec {command_list} >@stdout 2>@stderr
|
|
434
|
+
# with the caveat that it needs POSIX style paths in the vivado tclsh,
|
|
435
|
+
# and we'll save this to self.vivado_tcl['xvlog']
|
|
436
|
+
command_list = ['exec', 'xvlog']
|
|
437
|
+
if typ == 'sv':
|
|
438
|
+
command_list.append('-sv')
|
|
439
|
+
if self.args['uvm']:
|
|
440
|
+
command_list.extend(['-L', 'uvm'])
|
|
441
|
+
command_list += self.tool_config.get('compile-args', '').split()
|
|
442
|
+
if util.args['verbose']:
|
|
443
|
+
command_list += ['-v', '2']
|
|
444
|
+
for value in self.incdirs:
|
|
445
|
+
command_list.append('-i')
|
|
446
|
+
command_list.append(Path(value).as_posix())
|
|
447
|
+
for key, value in self.defines.items():
|
|
448
|
+
command_list.append('-d')
|
|
449
|
+
if value is not None:
|
|
450
|
+
# Because we're writing to a .tcl file, \" will become ", and \\\" will become \"
|
|
451
|
+
# we want \" in the final file. Parameters need to act the same way as defines:
|
|
452
|
+
value = f'{value}'.replace('"', '\\\"')
|
|
453
|
+
|
|
454
|
+
if value is None:
|
|
455
|
+
command_list.append(key)
|
|
456
|
+
else:
|
|
457
|
+
command_list.append(f"\"{key}={value}\"")
|
|
458
|
+
|
|
459
|
+
command_list += self.args['compile-args']
|
|
460
|
+
|
|
461
|
+
# Convert these to POSIX (even though we might be in Windows) b/c that's what Vivado
|
|
462
|
+
# tclsh needs
|
|
463
|
+
command_list += [Path(fpath).as_posix() for fpath in files_list]
|
|
464
|
+
command_list += self.tcl_exec_pipe
|
|
465
|
+
|
|
466
|
+
tclfname = os.path.abspath(
|
|
467
|
+
os.path.join(self.args['work-dir'], f'xvlog_{typ}_vivado.tcl')
|
|
468
|
+
)
|
|
469
|
+
|
|
470
|
+
# Make a one-liner from command line, save in self.vivado_tcl['xvlog']:
|
|
471
|
+
line = ' '.join(command_list).replace('\n', ' ')
|
|
472
|
+
self.vivado_tcl['xvlog'].append(line)
|
|
473
|
+
with open(tclfname, 'w', encoding='utf-8' ) as ftcl:
|
|
474
|
+
ftcl.write(line)
|
|
475
|
+
ftcl.write('\n')
|
|
331
476
|
|
|
332
477
|
|
|
333
478
|
def _add_glbl_v(self):
|
|
334
479
|
'''Adds glbl.v from Vivado's install path to self.files_v'''
|
|
335
|
-
glbl_v = self.vivado_base_path.replace(
|
|
480
|
+
glbl_v = self.vivado_base_path.replace(
|
|
481
|
+
'bin', os.path.join('data', 'verilog', 'src', 'glbl.v')
|
|
482
|
+
)
|
|
336
483
|
if any(x.endswith('glbl.v') for x in self.files_v):
|
|
337
484
|
util.warning(f'--add-glbl-v: Not adding {glbl_v=} b/c glbl.v already in',
|
|
338
485
|
f'{self.files_v=}')
|
|
@@ -349,6 +496,8 @@ class CommandSimVivado(CommandSim, ToolVivado):
|
|
|
349
496
|
sim.log or compile.log
|
|
350
497
|
'''
|
|
351
498
|
_, leafname = os.path.split(name)
|
|
499
|
+
if leafname == 'vivado.log':
|
|
500
|
+
description = 'Vivado XSim log from stdout/stderr'
|
|
352
501
|
if leafname == 'xsim.log':
|
|
353
502
|
description = 'Vivado XSim simulation step (3/3) log from stdout/stderr'
|
|
354
503
|
elif leafname == 'xelab.log':
|
|
@@ -429,7 +578,7 @@ class CommandSynthVivado(CommandSynth, ToolVivado):
|
|
|
429
578
|
parameters = ' '.join(
|
|
430
579
|
sim.parameters_dict_get_command_list(params=self.parameters, arg_prefix='-generic ')
|
|
431
580
|
)
|
|
432
|
-
incdirs = ' '.join([f'-include_dirs {x}' for x in self.incdirs])
|
|
581
|
+
incdirs = ' '.join([f'-include_dirs {Path(x).as_posix()}' for x in self.incdirs])
|
|
433
582
|
flatten = ""
|
|
434
583
|
if self.args['flatten-all']:
|
|
435
584
|
flatten = "-flatten_hierarchy full"
|
|
@@ -437,12 +586,14 @@ class CommandSynthVivado(CommandSynth, ToolVivado):
|
|
|
437
586
|
flatten = "-flatten_hierarchy none"
|
|
438
587
|
|
|
439
588
|
tcl_lines = []
|
|
589
|
+
# Note - if we're in Windows, the vivado tcl that processes these lines expects POSIX
|
|
590
|
+
# paths, not command/Powershell paths. so we will use Path(str).as_posix()
|
|
440
591
|
for f in self.files_v:
|
|
441
|
-
tcl_lines.append(f"read_verilog {f}")
|
|
592
|
+
tcl_lines.append(f"read_verilog {Path(f).as_posix()}")
|
|
442
593
|
for f in self.files_sv:
|
|
443
|
-
tcl_lines.append(f"read_verilog -sv {f}")
|
|
594
|
+
tcl_lines.append(f"read_verilog -sv {Path(f).as_posix()}")
|
|
444
595
|
for f in self.files_vhd:
|
|
445
|
-
tcl_lines.append(f"add_file {f}")
|
|
596
|
+
tcl_lines.append(f"add_file {Path(f).as_posix()}")
|
|
446
597
|
|
|
447
598
|
part = self.args['part']
|
|
448
599
|
top = self.args['top']
|
|
@@ -465,11 +616,11 @@ class CommandSynthVivado(CommandSynth, ToolVivado):
|
|
|
465
616
|
for _file in self.files_sdc:
|
|
466
617
|
# NOTE - sdc files cannot (yet) be attached to other modules.
|
|
467
618
|
tcl_lines += [
|
|
468
|
-
f"add_files -fileset constraints_1 {_file} {v}",
|
|
619
|
+
f"add_files -fileset constraints_1 {Path(_file).as_posix()} {v}",
|
|
469
620
|
]
|
|
470
621
|
if xdc_file:
|
|
471
622
|
tcl_lines += [
|
|
472
|
-
f"add_files -fileset constraints_1 {xdc_file} {v}",
|
|
623
|
+
f"add_files -fileset constraints_1 {Path(xdc_file).as_posix()} {v}",
|
|
473
624
|
]
|
|
474
625
|
tcl_lines += [
|
|
475
626
|
"# FIRST PASS -- auto_detect_xpm",
|
|
@@ -513,14 +664,14 @@ class CommandSynthVivado(CommandSynth, ToolVivado):
|
|
|
513
664
|
|
|
514
665
|
if default_xdc:
|
|
515
666
|
tcl_lines += [
|
|
516
|
-
f"puts \"(Used default XDC: {xdc_file})\"",
|
|
667
|
+
f"puts \"(Used default XDC: {Path(xdc_file).as_posix()})\"",
|
|
517
668
|
f"puts \"DEF CLOCK NS : [format %.3f {self.args['clock-ns']}]\"",
|
|
518
669
|
f"puts \"DEF IDELAY NS : [format %.3f {self.args['idelay-ns']}]\"",
|
|
519
670
|
f"puts \"DEF ODELAY NS : [format %.3f {self.args['odelay-ns']}]\"",
|
|
520
671
|
]
|
|
521
672
|
else:
|
|
522
673
|
tcl_lines += [
|
|
523
|
-
f"puts \"(Used provided XDC: {xdc_file})\"",
|
|
674
|
+
f"puts \"(Used provided XDC: {Path(xdc_file).as_posix()})\"",
|
|
524
675
|
]
|
|
525
676
|
tcl_lines += [
|
|
526
677
|
"puts \"\"",
|
|
@@ -592,13 +743,13 @@ class CommandProjVivado(CommandProj, ToolVivado):
|
|
|
592
743
|
tcl_file = os.path.abspath(os.path.join(self.args['work-dir'], self.args['tcl-file']))
|
|
593
744
|
v = self.get_vivado_tcl_verbose_arg()
|
|
594
745
|
|
|
595
|
-
incdirs = " ".join(self.incdirs)
|
|
746
|
+
incdirs = " ".join([Path(x).as_posix() for x in self.incdirs])
|
|
596
747
|
defines = ""
|
|
597
748
|
for key, value in self.defines.items():
|
|
598
749
|
defines += (f"{key} " if value is None else f"{key}={value} ")
|
|
599
750
|
|
|
600
751
|
tcl_lines = [
|
|
601
|
-
f"create_project {self.args['top']}_proj {self.args['work-dir']} {v}"
|
|
752
|
+
f"create_project {self.args['top']}_proj {Path(self.args['work-dir']).as_posix()} {v}"
|
|
602
753
|
]
|
|
603
754
|
|
|
604
755
|
if self.args['oc-vivado-tcl'] and oc_root:
|
|
@@ -630,7 +781,7 @@ class CommandProjVivado(CommandProj, ToolVivado):
|
|
|
630
781
|
else:
|
|
631
782
|
fileset = "sources_1"
|
|
632
783
|
tcl_lines += [
|
|
633
|
-
f"add_files -norecurse {f} -fileset [get_filesets {fileset}]"
|
|
784
|
+
f"add_files -norecurse {Path(f).as_posix()} -fileset [get_filesets {fileset}]"
|
|
634
785
|
]
|
|
635
786
|
|
|
636
787
|
tcl_lines += [
|
|
@@ -776,16 +927,16 @@ class CommandFListVivado(CommandFList, ToolVivado):
|
|
|
776
927
|
class CommandUploadVivado(CommandUpload, ToolVivado):
|
|
777
928
|
'''CommandUploadVivado is a command handler for: eda upload --tool=vivado'''
|
|
778
929
|
|
|
930
|
+
SUPPORTED_BIT_EXT = ['.bit']
|
|
931
|
+
|
|
779
932
|
def __init__(self, config: dict):
|
|
780
933
|
CommandUpload.__init__(self, config)
|
|
781
934
|
ToolVivado.__init__(self, config=self.config)
|
|
782
935
|
# add args specific to this tool
|
|
783
936
|
self.args.update({
|
|
784
937
|
'gui': False,
|
|
785
|
-
'bitfile': "",
|
|
786
938
|
'list-usbs': False,
|
|
787
939
|
'list-devices': False,
|
|
788
|
-
'list-bitfiles': False,
|
|
789
940
|
'usb': -1,
|
|
790
941
|
'device': -1,
|
|
791
942
|
'host': "localhost",
|
|
@@ -795,82 +946,51 @@ class CommandUploadVivado(CommandUpload, ToolVivado):
|
|
|
795
946
|
'log-file': "eda_upload.log",
|
|
796
947
|
'test-mode': False,
|
|
797
948
|
})
|
|
798
|
-
# TODO(drew): Add self.args_help.update({...})
|
|
799
949
|
|
|
800
|
-
|
|
801
|
-
|
|
950
|
+
# TODO(drew): Complete self.args_help.update({...})
|
|
951
|
+
self.args_help.update({
|
|
952
|
+
'bitfile': 'BIT file to upload (auto-detected if not specified)',
|
|
953
|
+
'list-bitfiles': 'List available BIT files',
|
|
954
|
+
})
|
|
955
|
+
|
|
956
|
+
|
|
802
957
|
def do_it(self): # pylint: disable=too-many-locals,too-many-branches,too-many-statements
|
|
958
|
+
'''
|
|
959
|
+
Note this is called directly by opencos.commands.CommandUpload, based
|
|
960
|
+
on which bitfile(s) were found, or if --tool=vivado was set
|
|
961
|
+
|
|
962
|
+
We do not need to handle --list-bitfiles, was handled by CommandUpload.
|
|
963
|
+
'''
|
|
964
|
+
|
|
803
965
|
# add defines for this job
|
|
804
966
|
self.set_tool_defines()
|
|
805
967
|
self.write_eda_config_and_args()
|
|
806
968
|
|
|
807
969
|
bitfile = None
|
|
808
|
-
targets = []
|
|
809
970
|
if self.args['bitfile']:
|
|
810
971
|
if os.path.isfile(self.args['bitfile']):
|
|
811
972
|
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
973
|
|
|
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...")
|
|
974
|
+
# self.bitfiles was already set by CommandUpload.process_tokens()
|
|
975
|
+
if len(self.bitfiles) == 1:
|
|
976
|
+
bitfile = self.bitfiles[0]
|
|
863
977
|
|
|
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
978
|
|
|
872
|
-
|
|
873
|
-
|
|
979
|
+
# Auto-discover bitfile logic (for when we have no bitfile, and we
|
|
980
|
+
# weren't called just to listdevice/listusb)
|
|
981
|
+
if not bitfile and not self.args['list-devices'] and not self.args['list-usbs']:
|
|
982
|
+
|
|
983
|
+
# CommandUpload already displayed them, and exited on --list-bitfiles.
|
|
984
|
+
if len(self.bitfiles) > 1:
|
|
985
|
+
util.warning("Too many matches to continue without adding search terms,",
|
|
986
|
+
"upload not performed.")
|
|
987
|
+
if not self.args['test-mode']:
|
|
988
|
+
self.error('Upload not performed, multiple bit files found, please add',
|
|
989
|
+
'search terms...')
|
|
990
|
+
return
|
|
991
|
+
|
|
992
|
+
self.error("Failed to find a matching bitfile")
|
|
993
|
+
return
|
|
874
994
|
|
|
875
995
|
# ── Generate TCL script ───────────────────────────────────────────────────
|
|
876
996
|
script_file = Path(self.args['tcl-file'])
|
|
@@ -945,10 +1065,11 @@ class CommandUploadVivado(CommandUpload, ToolVivado):
|
|
|
945
1065
|
w('refresh_hw_device -update_hw_probes false -quiet $hw_device\n')
|
|
946
1066
|
|
|
947
1067
|
if bitfile is not None:
|
|
948
|
-
w('set_property PROGRAM.FILE {' + bitfile + '} $hw_device\n')
|
|
1068
|
+
w('set_property PROGRAM.FILE {' + Path(bitfile).as_posix() + '} $hw_device\n')
|
|
949
1069
|
w('program_hw_devices [current_hw_device]\n')
|
|
950
1070
|
|
|
951
1071
|
w('close_hw_target\n')
|
|
1072
|
+
w('quit\n')
|
|
952
1073
|
|
|
953
1074
|
except OSError as exc:
|
|
954
1075
|
util.error(f"Cannot create {script_file}: {exc}")
|
|
@@ -971,11 +1092,21 @@ class CommandUploadVivado(CommandUpload, ToolVivado):
|
|
|
971
1092
|
]
|
|
972
1093
|
if not util.args['verbose']:
|
|
973
1094
|
command_list.append('-notrace')
|
|
974
|
-
self.exec(Path(util.getcwd()), command_list)
|
|
1095
|
+
_, stdout, _ = self.exec(Path(util.getcwd()), command_list)
|
|
1096
|
+
|
|
1097
|
+
# Do some log scraping
|
|
1098
|
+
for line in stdout.split('\n'):
|
|
1099
|
+
if line.startswith('WARNING:'):
|
|
1100
|
+
self.tool_warning_count += 1
|
|
1101
|
+
elif line.startswith('ERROR:') or line.startswith('[ERROR]'):
|
|
1102
|
+
self.tool_error_count += 1
|
|
975
1103
|
|
|
976
1104
|
if not self.args['keep']:
|
|
977
1105
|
os.unlink(self.args['tcl-file'])
|
|
978
1106
|
|
|
1107
|
+
self.report_tool_warn_error_counts()
|
|
1108
|
+
self.report_pass_fail()
|
|
1109
|
+
|
|
979
1110
|
util.info("Upload done")
|
|
980
1111
|
|
|
981
1112
|
|