Add tool to program an FPGA To program FPGAs with a bitstream, we currently either need to rely on fusesoc, or open the Vivado GUI. This commit adds a command line tool to do the same, based on the TCL code available in fusesoc/edalize. Signed-off-by: Philipp Wagner <phw@lowrisc.org>
diff --git a/util/licence-checker.hjson b/util/licence-checker.hjson index 206f8e3..f800022 100644 --- a/util/licence-checker.hjson +++ b/util/licence-checker.hjson
@@ -58,6 +58,7 @@ # Site Assets 'site/**/assets/scss/**', 'site/landing/static/js/tiny-slider.js', + 'util/opentitan-pgm-fpga/vivado_pgm.tcl', # Code taken from Chromium, so covered by the BSD licence 'sw/otbn/code-snippets/modexp.S',
diff --git a/util/opentitan-pgm-fpga/LICENSE.edalize b/util/opentitan-pgm-fpga/LICENSE.edalize new file mode 100644 index 0000000..ffa88a7 --- /dev/null +++ b/util/opentitan-pgm-fpga/LICENSE.edalize
@@ -0,0 +1,25 @@ +BSD 2-Clause License + +Copyright (c) 2018, Olof Kindgren +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/util/opentitan-pgm-fpga/opentitan-pgm-fpga b/util/opentitan-pgm-fpga/opentitan-pgm-fpga new file mode 100755 index 0000000..fbefd4e --- /dev/null +++ b/util/opentitan-pgm-fpga/opentitan-pgm-fpga
@@ -0,0 +1,64 @@ +#!/usr/bin/env python3 +# Copyright lowRISC contributors. +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 +"""Program an FPGA with a bitstream +""" +import argparse +import os +import subprocess +import sys + + +def program_bitstream_with_vivado(part, bitstream, hw_target, jtag_freq): + tcl_file = os.path.join(os.path.dirname(__file__), 'vivado_pgm.tcl') + if not os.path.isfile(tcl_file): + raise FileNotFoundError('{} not found.'.format(tcl_file)) + + cmd_env = os.environ.copy() + if hw_target is not None: + cmd_env['HW_TARGET'] = hw_target + if jtag_freq is not None: + cmd_env['JTAG_FREQ'] = jtag_freq + + cmd = [ + 'vivado', '-quiet', '-nolog', '-notrace', '-mode', 'batch', '-source', + str(tcl_file), '-tclargs', part, bitstream + ] + try: + subprocess.run(cmd, check=True, env=cmd_env) + except FileNotFoundError as e: + print("Unable to run Vivado: %s. Is Vivado in your PATH?" % str(e), + file=sys.stderr) + return 1 + except subprocess.CalledProcessError as e: + print(str(e), file=sys.stderr) + return 1 + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument('--hw-target', + '-t', + required=False, + default=None, + help='Hardware target to use. Use the TCL command ' + 'get_hw_targets in Vivado to get a list of available ' + 'targets.') + parser.add_argument('--jtag-freq', + '-f', + required=False, + default=None, + help='JTAG frequency (in Hz)') + parser.add_argument('part', help='Device (part) to flash') + parser.add_argument('bitstream', help='Path to the bitstream file') + args = parser.parse_args() + + return program_bitstream_with_vivado(args.part, + args.bitstream, + hw_target=args.hw_target, + jtag_freq=args.jtag_freq) + + +if __name__ == "__main__": + sys.exit(main())
diff --git a/util/opentitan-pgm-fpga/vivado_pgm.tcl b/util/opentitan-pgm-fpga/vivado_pgm.tcl new file mode 100644 index 0000000..e10ab6e --- /dev/null +++ b/util/opentitan-pgm-fpga/vivado_pgm.tcl
@@ -0,0 +1,111 @@ +# Copyright lowRISC contributors +# Copyright edalize contributors +# Licensed under the 2-Clause BSD License, see LICENSE.edalize for details. +# SPDX-License-Identifier: BSD-2-Clause +# +# This file was produced by edalize from a template at +# https://github.com/olofk/edalize/blob/592154c50ac40ec86e3d954dd2d300b0573b2c37/edalize/templates/vivado/vivado-program.tcl.j2 + +set part [lindex $argv 0] +set bitstream [lindex $argv 1] + + +if {[info exists env(HW_TARGET)]} { + set explicit_hw_target $env(HW_TARGET) +} else { + set explicit_hw_target "" +} +if {[info exists env(JTAG_FREQ)]} { + set jtag_freq $env(JTAG_FREQ) +} else { + set jtag_freq "" +} + +puts "OpenTitan Xilinx FPGA Programming Tool" +puts "======================================" +puts "" +puts "INFO: Programming part $part with bitstream $bitstream" +if { $explicit_hw_target != "" } { + puts "INFO: Programming target $explicit_hw_target" +} + +# Connect to Xilinx Hardware Server +if { [ catch { open_hw_manager } ] } { open_hw } +connect_hw_server + +if { $explicit_hw_target == "" } { + set hw_targets [get_hw_targets] +} else { + set hw_targets [get_hw_targets $explicit_hw_target] +} + +if { [llength $hw_targets] == 0 } { + if { $explicit_hw_target == "" } { + puts "ERROR: Failed to find any targets" + } else { + puts "ERROR: Failed to find target: $target" + } +} + +# Find the first target and device that contains a FPGA $part. +set hw_device_found 0 +foreach hw_target $hw_targets { + puts "INFO: Trying to use hardware target $hw_target" + current_hw_target $hw_target + + # 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 { + # Close currently tried device, and try with next one. + puts "INFO: Part not found as part of $hw_target. Trying next device." + close_hw_target + } +} +if { $hw_device_found == 0 } { + puts "ERROR: None of the hardware targets included a $part FPGA part. \ + Check cables and ensure that jumpers are correct for JTAG programming." + exit 1 +} +puts "INFO: Programming bitstream to device $hw_device on target $hw_target." + +# Do the programming +current_hw_device $hw_device +set_property PROGRAM.FILE $bitstream [current_hw_device] +if {$jtag_freq != ""} { + set_property PARAM.FREQUENCY $jtag_freq [get_hw_targets $hw_target] +} +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."