Skip to content

Commit

Permalink
Vivado: Retry opening device when programming
Browse files Browse the repository at this point in the history
Vivado 2018.3 is unreliable when opening the hw_device: after connecting
a (Nexys Video) FPGA board, the first connection attepmt usually fails.
The second attempt is successful. This behavior has been observed with
multiple different Nexys Video boards and on different PCs. The GUI
behaves identical to the scripted approach: two tries are needed in the
GUI as well.

To be able to reliably program an FPGA, this commit modifies the
programming script to try opening the hw_device for three times before
failing.

In the process of doing so, I took the liberty to improve the
programming approach in a couple more ways:

- The script now outputs more helpful information, and less confusing
  text. The executed commands are not written any more, as is the
  vivado.log file. This output was confusing, as it wasn't obvious which
  part of the output was actually executed (e.g. error handlers), and
  which of it was only in the source code.
  Instead, handwritten diagnostic messages are output, and a clear
  indication of success is given at the end of the process.
- The call to Vivado to program the device is done through the Makefile
  (like all other Vivado invocations), and not through edalize directly.
  This makes it easier to program the FPGA without a full set of core
  files available.
  For that purpose, the Makefile now contains a 'pgm' target.
- The programming TCL script is now unmodified and gets the part and
  bitstream arguments through command line arguments. This helps to
  reuse the programming TCL script for other purposes outside of
  edalize.

New programming output looks like this:

```
vivado -quiet -nolog -notrace -mode batch -source projname_pgm.tcl -tclargs xc7a200tsbg484-1 projname.bit
FuseSoC Xilinx FPGA Programming Tool
====================================

INFO: Programming part xc7a200tsbg484-1 with bitstream projname.bit
INFO: [Labtools 27-2285] Connecting to hw_server url TCP:localhost:3121
INFO: [Labtools 27-2222] Launching hw_server...
INFO: [Labtools 27-2221] Launch Output:

****** Xilinx hw_server v2018.3
  **** Build date : Dec  6 2018-23:53:53
    ** Copyright 1986-2018 Xilinx, Inc. All Rights Reserved.

INFO: Trying to use hardware target localhost:3121/xilinx_tcf/Digilent/210276A79425
INFO: [Labtoolstcl 44-466] Opening hw_target localhost:3121/xilinx_tcf/Digilent/210276A79425
INFO: Opened hardware target localhost:3121/xilinx_tcf/Digilent/210276A79425 on try 1.
INFO: Part not found as part of Unknown_Device_0. Trying next device.
INFO: [Labtoolstcl 44-464] Closing hw_target localhost:3121/xilinx_tcf/Digilent/210276A79425
INFO: Trying to use hardware target localhost:3121/xilinx_tcf/Digilent/210276A79425B
INFO: [Labtoolstcl 44-466] Opening hw_target localhost:3121/xilinx_tcf/Digilent/210276A79425B
INFO: Opened hardware target localhost:3121/xilinx_tcf/Digilent/210276A79425B on try 1.
INFO: Found xc7a200tsbg484-1 as part of xc7a200t_0.
INFO: Programming bitstream to device xc7a200t_0 on target localhost:3121/xilinx_tcf/Digilent/210276A79425B.
INFO: [Labtools 27-3164] End of startup status: HIGH
program_hw_devices: Time (s): cpu = 00:00:06 ; elapsed = 00:00:06 . Memory (MB): peak = 1418.855 ; gain = 0.000 ; free physical = 3462 ; free virtual = 7136
INFO: [Labtoolstcl 44-464] Closing hw_target localhost:3121/xilinx_tcf/Digilent/210276A79425B

INFO: SUCCESS! FPGA xc7a200tsbg484-1 successfully programmed with bitstream projname.bit.
```
  • Loading branch information
imphil authored and olofk committed Oct 28, 2019
1 parent d711fef commit 4138b6a
Show file tree
Hide file tree
Showing 7 changed files with 201 additions and 70 deletions.
11 changes: 9 additions & 2 deletions edalize/templates/vivado/vivado-makefile.j2
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
NAME := {{ name }}
BITSTREAM := {{ bitstream }}
PART := {{ part }}

all: $(NAME).bit
all: $(BITSTREAM)

$(NAME).bit: $(NAME)_run.tcl $(NAME).xpr
$(BITSTREAM): $(NAME)_run.tcl $(NAME).xpr
vivado -mode batch -source $^

$(NAME).xpr: $(NAME).tcl
Expand All @@ -15,3 +17,8 @@ $(NAME).runs/synth_1: $(NAME)_synth.tcl $(NAME).xpr
vivado -mode batch -source $^

synth: $(NAME).runs/synth_1

pgm: $(NAME)_pgm.tcl $(BITSTREAM)
vivado -quiet -nolog -notrace -mode batch -source $< -tclargs $(PART) $(BITSTREAM)

.PHONY: pgm
75 changes: 56 additions & 19 deletions edalize/templates/vivado/vivado-program.tcl.j2
Original file line number Diff line number Diff line change
@@ -1,36 +1,73 @@
# Auto-generated program tcl file

set bit {{ bitstream_name }}
set part {{ part }}
set part [lindex $argv 0]
set bitstream [lindex $argv 1]

puts "FuseSoC Xilinx FPGA Programming Tool"
puts "===================================="
puts ""
puts "INFO: Programming part $part with bitstream $bitstream"

# Connect to Xilinx Hardware Server
open_hw
connect_hw_server

set found 0

# Find the first target and device that contains a FPGA $part.
set hw_device_found 0
foreach { hw_target } [get_hw_targets] {
puts "INFO: Trying to use hardware target $hw_target"

current_hw_target $hw_target
if {[catch {open_hw_target} res_open_hw] == 0} {
foreach { hw_device } [get_hw_devices] {
if { [string first [get_property PART $hw_device] $part] == 0 } {
puts "Found hardware target with a ${part} device."
current_hw_device $hw_device
set found 1
break
}
}
if {$found} {break}

# Open hardware target
# The Vivado hardware server isn't always able to reliably open a target.
# Try three times before giving up.
set hw_target_opened 0
for {set open_hw_target_try 1} {$open_hw_target_try <= 3} {incr open_hw_target_try} {
if {[catch {open_hw_target} res_open_hw_target] == 0} {
set hw_target_opened 1
break
}
}
if { $hw_target_opened == 0 } {
puts "WARNING: Unable to open hardware target $hw_target after " \
"$open_hw_target_try tries. Skipping."
continue
}
puts "INFO: Opened hardware target $hw_target on try $open_hw_target_try."

# Iterate through all devices and find one which contains $part
foreach { hw_device } [get_hw_devices] {
if { [string first [get_property PART $hw_device] $part] == 0 } {
puts "INFO: Found $part as part of $hw_device."
current_hw_device $hw_device
set hw_device_found 1
break
}
}

if { $hw_device_found == 1 } {
break
} else {
puts "Catched $res_open_hw"
# Close currently tried device, and try with next one.
puts "INFO: Part not found as part of $hw_device. Trying next device."
close_hw_target
}
close_hw_target
}
if { $found == 0 } {
puts "Did not find board with a ${part} device."
if { $hw_device_found == 0 } {
puts "ERROR: None of the hardware targets included a $part FPGA part."
exit 1
}
puts "INFO: Programming bitstream to device $hw_device on target $hw_target."

set_property PROGRAM.FILE $bit [current_hw_device]
# Do the programming
current_hw_device $hw_device
set_property PROGRAM.FILE $bitstream [current_hw_device]
program_hw_devices [current_hw_device]

# Disconnect from Xilinx Hardware Server
close_hw_target
disconnect_hw_server

puts ""
puts "INFO: SUCCESS! FPGA $part successfully programmed with bitstream $bitstream."
13 changes: 6 additions & 7 deletions edalize/vivado.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ def get_version(self):

""" Configuration is the first phase of the build
This writes the project TCL files and Makefile. It first collects all
sources, IPs and contraints and then writes them to the TCL file along
sources, IPs and constraints and then writes them to the TCL file along
with the build steps.
"""
def configure_main(self):
Expand Down Expand Up @@ -88,7 +88,9 @@ def configure_main(self):

self.render_template('vivado-makefile.j2',
'Makefile',
{'name' : self.name})
{'name' : self.name,
'part' : self.tool_options.get('part', ""),
'bitstream' : self.name+'.bit'})

self.render_template('vivado-run.tcl.j2',
self.name+"_run.tcl")
Expand All @@ -97,9 +99,7 @@ def configure_main(self):
self.name+"_synth.tcl")

self.render_template('vivado-program.tcl.j2',
self.name+"_pgm.tcl",
{'part' : self.tool_options.get('part', ""),
'bitstream_name' : self.name+'.bit'})
self.name+"_pgm.tcl")

def src_file_filter(self, f):
def _vhdl_source(f):
Expand Down Expand Up @@ -153,5 +153,4 @@ def run_main(self):
elif self.tool_options['pnr'] == 'none':
return

self._run_tool('vivado', ['-mode', 'batch', '-source', self.name+"_pgm.tcl"])

self._run_tool('make', ['pgm'])
11 changes: 9 additions & 2 deletions tests/test_vivado/Makefile
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
NAME := test_vivado_0
BITSTREAM := test_vivado_0.bit
PART := xc7a35tcsg324-1

all: $(NAME).bit
all: $(BITSTREAM)

$(NAME).bit: $(NAME)_run.tcl $(NAME).xpr
$(BITSTREAM): $(NAME)_run.tcl $(NAME).xpr
vivado -mode batch -source $^

$(NAME).xpr: $(NAME).tcl
Expand All @@ -15,3 +17,8 @@ $(NAME).runs/synth_1: $(NAME)_synth.tcl $(NAME).xpr
vivado -mode batch -source $^

synth: $(NAME).runs/synth_1

pgm: $(NAME)_pgm.tcl $(BITSTREAM)
vivado -quiet -nolog -notrace -mode batch -source $< -tclargs $(PART) $(BITSTREAM)

.PHONY: pgm
11 changes: 9 additions & 2 deletions tests/test_vivado/minimal/Makefile
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
NAME := test_vivado_minimal_0
BITSTREAM := test_vivado_minimal_0.bit
PART := xc7a35tcsg324-1

all: $(NAME).bit
all: $(BITSTREAM)

$(NAME).bit: $(NAME)_run.tcl $(NAME).xpr
$(BITSTREAM): $(NAME)_run.tcl $(NAME).xpr
vivado -mode batch -source $^

$(NAME).xpr: $(NAME).tcl
Expand All @@ -15,3 +17,8 @@ $(NAME).runs/synth_1: $(NAME)_synth.tcl $(NAME).xpr
vivado -mode batch -source $^

synth: $(NAME).runs/synth_1

pgm: $(NAME)_pgm.tcl $(BITSTREAM)
vivado -quiet -nolog -notrace -mode batch -source $< -tclargs $(PART) $(BITSTREAM)

.PHONY: pgm
75 changes: 56 additions & 19 deletions tests/test_vivado/minimal/test_vivado_minimal_0_pgm.tcl
Original file line number Diff line number Diff line change
@@ -1,36 +1,73 @@
# Auto-generated program tcl file

set bit test_vivado_minimal_0.bit
set part xc7a35tcsg324-1
set part [lindex $argv 0]
set bitstream [lindex $argv 1]

puts "FuseSoC Xilinx FPGA Programming Tool"
puts "===================================="
puts ""
puts "INFO: Programming part $part with bitstream $bitstream"

# Connect to Xilinx Hardware Server
open_hw
connect_hw_server

set found 0

# Find the first target and device that contains a FPGA $part.
set hw_device_found 0
foreach { hw_target } [get_hw_targets] {
puts "INFO: Trying to use hardware target $hw_target"

current_hw_target $hw_target
if {[catch {open_hw_target} res_open_hw] == 0} {
foreach { hw_device } [get_hw_devices] {
if { [string first [get_property PART $hw_device] $part] == 0 } {
puts "Found hardware target with a ${part} device."
current_hw_device $hw_device
set found 1
break
}
}
if {$found} {break}

# Open hardware target
# The Vivado hardware server isn't always able to reliably open a target.
# Try three times before giving up.
set hw_target_opened 0
for {set open_hw_target_try 1} {$open_hw_target_try <= 3} {incr open_hw_target_try} {
if {[catch {open_hw_target} res_open_hw_target] == 0} {
set hw_target_opened 1
break
}
}
if { $hw_target_opened == 0 } {
puts "WARNING: Unable to open hardware target $hw_target after " \
"$open_hw_target_try tries. Skipping."
continue
}
puts "INFO: Opened hardware target $hw_target on try $open_hw_target_try."

# Iterate through all devices and find one which contains $part
foreach { hw_device } [get_hw_devices] {
if { [string first [get_property PART $hw_device] $part] == 0 } {
puts "INFO: Found $part as part of $hw_device."
current_hw_device $hw_device
set hw_device_found 1
break
}
}

if { $hw_device_found == 1 } {
break
} else {
puts "Catched $res_open_hw"
# Close currently tried device, and try with next one.
puts "INFO: Part not found as part of $hw_device. Trying next device."
close_hw_target
}
close_hw_target
}
if { $found == 0 } {
puts "Did not find board with a ${part} device."
if { $hw_device_found == 0 } {
puts "ERROR: None of the hardware targets included a $part FPGA part."
exit 1
}
puts "INFO: Programming bitstream to device $hw_device on target $hw_target."

set_property PROGRAM.FILE $bit [current_hw_device]
# Do the programming
current_hw_device $hw_device
set_property PROGRAM.FILE $bitstream [current_hw_device]
program_hw_devices [current_hw_device]

# Disconnect from Xilinx Hardware Server
close_hw_target
disconnect_hw_server

puts ""
puts "INFO: SUCCESS! FPGA $part successfully programmed with bitstream $bitstream."
75 changes: 56 additions & 19 deletions tests/test_vivado/test_vivado_0_pgm.tcl
Original file line number Diff line number Diff line change
@@ -1,36 +1,73 @@
# Auto-generated program tcl file

set bit test_vivado_0.bit
set part xc7a35tcsg324-1
set part [lindex $argv 0]
set bitstream [lindex $argv 1]

puts "FuseSoC Xilinx FPGA Programming Tool"
puts "===================================="
puts ""
puts "INFO: Programming part $part with bitstream $bitstream"

# Connect to Xilinx Hardware Server
open_hw
connect_hw_server

set found 0

# Find the first target and device that contains a FPGA $part.
set hw_device_found 0
foreach { hw_target } [get_hw_targets] {
puts "INFO: Trying to use hardware target $hw_target"

current_hw_target $hw_target
if {[catch {open_hw_target} res_open_hw] == 0} {
foreach { hw_device } [get_hw_devices] {
if { [string first [get_property PART $hw_device] $part] == 0 } {
puts "Found hardware target with a ${part} device."
current_hw_device $hw_device
set found 1
break
}
}
if {$found} {break}

# Open hardware target
# The Vivado hardware server isn't always able to reliably open a target.
# Try three times before giving up.
set hw_target_opened 0
for {set open_hw_target_try 1} {$open_hw_target_try <= 3} {incr open_hw_target_try} {
if {[catch {open_hw_target} res_open_hw_target] == 0} {
set hw_target_opened 1
break
}
}
if { $hw_target_opened == 0 } {
puts "WARNING: Unable to open hardware target $hw_target after " \
"$open_hw_target_try tries. Skipping."
continue
}
puts "INFO: Opened hardware target $hw_target on try $open_hw_target_try."

# Iterate through all devices and find one which contains $part
foreach { hw_device } [get_hw_devices] {
if { [string first [get_property PART $hw_device] $part] == 0 } {
puts "INFO: Found $part as part of $hw_device."
current_hw_device $hw_device
set hw_device_found 1
break
}
}

if { $hw_device_found == 1 } {
break
} else {
puts "Catched $res_open_hw"
# Close currently tried device, and try with next one.
puts "INFO: Part not found as part of $hw_device. Trying next device."
close_hw_target
}
close_hw_target
}
if { $found == 0 } {
puts "Did not find board with a ${part} device."
if { $hw_device_found == 0 } {
puts "ERROR: None of the hardware targets included a $part FPGA part."
exit 1
}
puts "INFO: Programming bitstream to device $hw_device on target $hw_target."

set_property PROGRAM.FILE $bit [current_hw_device]
# Do the programming
current_hw_device $hw_device
set_property PROGRAM.FILE $bitstream [current_hw_device]
program_hw_devices [current_hw_device]

# Disconnect from Xilinx Hardware Server
close_hw_target
disconnect_hw_server

puts ""
puts "INFO: SUCCESS! FPGA $part successfully programmed with bitstream $bitstream."

0 comments on commit 4138b6a

Please sign in to comment.