[fpga] Add loader tool for ChipWhipserer CW310 FPGA board
This tool enables the loading of OpenTitan bistreams and application
binaries to the CW310 FPGA board.
Signed-off-by: Pirmin Vogel <vogelpi@lowrisc.org>
diff --git a/util/fpga/cw310_loader.py b/util/fpga/cw310_loader.py
new file mode 100755
index 0000000..3d16747
--- /dev/null
+++ b/util/fpga/cw310_loader.py
@@ -0,0 +1,65 @@
+#!/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
+
+import argparse
+
+import cw_spiflash
+
+import chipwhisperer as cw
+from chipwhisperer.capture.targets.CW310 import CW310
+
+
+def main():
+
+ parser = argparse.ArgumentParser(
+ description=
+ 'Load OpenTitan Bitstream & Software to ChipWhisperer CW310 FPGA Board'
+ )
+
+ parser.add_argument('--bitstream',
+ '-b',
+ metavar='bitstream',
+ type=str,
+ help='Path to the FPGA .bit file to load',
+ default=None)
+
+ parser.add_argument('--firmware',
+ '-f',
+ metavar='firmware',
+ type=str,
+ help='Path to the software .bin file to load',
+ default=None)
+
+ args = parser.parse_args()
+
+ print("CW310 Loader: Attemping to find CW310 FPGA Board:")
+ if args.bitstream:
+ print(" Using bitstream :{}".format(args.bitstream))
+ else:
+ print(" No bitstream specified")
+ target = cw.target(None, CW310, bsfile=args.bitstream, slurp=False)
+
+ print("Board found, setting PLL2 to 100 MHz")
+
+ target.pll.pll_enable_set(True)
+ target.pll.pll_outenable_set(False, 0)
+ # Note: 1 and 2 seem to be reversed.
+ target.pll.pll_outenable_set(True, 1)
+ target.pll.pll_outenable_set(False, 2)
+
+ # Note: both 1 and 2 need to be set, even if only 1 is enabled.
+ target.pll.pll_outfreq_set(100E6, 1)
+ target.pll.pll_outfreq_set(100E6, 2)
+
+ if args.firmware:
+ print("INFO: Programming firmware file: {}".format(args.firmware))
+ prog = cw_spiflash.SPIProgrammer(args.firmware, "CW310")
+ prog.run(target)
+
+ print("Loading done.")
+
+
+if __name__ == "__main__":
+ main()
diff --git a/util/fpga/cw_spiflash.py b/util/fpga/cw_spiflash.py
index c710719..032169d 100644
--- a/util/fpga/cw_spiflash.py
+++ b/util/fpga/cw_spiflash.py
@@ -6,6 +6,7 @@
import time
import hashlib
from functools import partial
+from collections import namedtuple
def _uint_to_le(val, length):
@@ -122,21 +123,40 @@
>>> ack = bootstrap.transfer(frame)
>>> ...
"""
- # Default pin mapping for CW305 board.
- _PIN_SCK = 'USB_A13'
- _PIN_SDI = 'USB_A14'
- _PIN_SDO = 'USB_A15'
- _PIN_CS = 'USB_A16'
- _PIN_TRST = 'USB_A17'
- _PIN_SRST = 'USB_A18'
- _PIN_JTAG_SPI = 'USB_A19'
- _PIN_BOOTSTRAP = 'USB_A20'
+ # Pin mappings for CW305/CW310 boards.
+ PinMapping = namedtuple('PinMapping', ['PIN_SCK',
+ 'PIN_SDI',
+ 'PIN_SDO',
+ 'PIN_CS',
+ 'PIN_TRST',
+ 'PIN_SRST',
+ 'PIN_JTAG_SPI',
+ 'PIN_BOOTSTRAP'])
+ _PIN_MAPPINGS = {}
+ _PIN_MAPPINGS['CW305'] = PinMapping('USB_A13',
+ 'USB_A14',
+ 'USB_A15',
+ 'USB_A16',
+ 'USB_A17',
+ 'USB_A18',
+ 'USB_A19',
+ 'USB_A20')
+ _PIN_MAPPINGS['CW310'] = PinMapping('USB_SPI_SCK',
+ 'USB_SPI_COPI',
+ 'USB_SPI_CIPO',
+ 'USB_SPI_CS',
+ 'USB_A17',
+ 'USB_A18',
+ 'USB_A19',
+ 'USB_A16')
+ _board = 'CW305'
+
# Delays below are in seconds.
_BOOTSTRAP_DELAY = 0.1
_SECOND_FRAME_DELAY = 0.2
_INTER_FRAME_DELAY = 0.02
- def __init__(self, fpga, board="CW305"):
+ def __init__(self, fpga, board='CW305'):
"""Inits a _Bootstrap with a CW310/305.
Args:
@@ -144,53 +164,48 @@
board: can be ``CW310`` or ``CW305`` to distinguish the different board types.
"""
- # Change pin mapping to CW310 board.
- if board == "CW310":
- self._PIN_SCK = 'USB_SPI_SCK'
- self._PIN_SDI = 'USB_SPI_COPI'
- self._PIN_SDO = 'USB_SPI_CIPO'
- self._PIN_CS = 'USB_SPI_CS'
- self._PIN_TRST = 'USB_A17'
- self._PIN_SRST = 'USB_A18'
- self._PIN_JTAG_SPI = 'USB_A19'
- self._PIN_BOOTSTRAP = 'USB_A16'
+ # Check board type.
+ if board in ['CW305', 'CW310']:
+ self._board = board
+ else:
+ raise ValueError('board must be either CW305 or CW310, got ' + board)
# Configure JTAG and bootstrap pins.
self._fpga_io = fpga.gpio_mode()
- self._fpga_io.pin_set_output(self._PIN_TRST)
- self._fpga_io.pin_set_output(self._PIN_SRST)
- self._fpga_io.pin_set_output(self._PIN_JTAG_SPI)
- self._fpga_io.pin_set_output(self._PIN_BOOTSTRAP)
- self._fpga_io.pin_set_state(self._PIN_TRST, 1)
- self._fpga_io.pin_set_state(self._PIN_SRST, 1)
- self._fpga_io.pin_set_state(self._PIN_JTAG_SPI, 1)
- self._fpga_io.pin_set_state(self._PIN_BOOTSTRAP, 0)
+ self._fpga_io.pin_set_output(self._PIN_MAPPINGS[self._board].PIN_TRST)
+ self._fpga_io.pin_set_output(self._PIN_MAPPINGS[self._board].PIN_SRST)
+ self._fpga_io.pin_set_output(self._PIN_MAPPINGS[self._board].PIN_JTAG_SPI)
+ self._fpga_io.pin_set_output(self._PIN_MAPPINGS[self._board].PIN_BOOTSTRAP)
+ self._fpga_io.pin_set_state(self._PIN_MAPPINGS[self._board].PIN_TRST, 1)
+ self._fpga_io.pin_set_state(self._PIN_MAPPINGS[self._board].PIN_SRST, 1)
+ self._fpga_io.pin_set_state(self._PIN_MAPPINGS[self._board].PIN_JTAG_SPI, 1)
+ self._fpga_io.pin_set_state(self._PIN_MAPPINGS[self._board].PIN_BOOTSTRAP, 0)
# Initialize SPI pins.
- self._fpga_io.spi1_setpins(sck=self._PIN_SCK,
- sdo=self._PIN_SDI,
- sdi=self._PIN_SDO,
- cs=self._PIN_CS)
+ self._fpga_io.spi1_setpins(sck=self._PIN_MAPPINGS[self._board].PIN_SCK,
+ sdo=self._PIN_MAPPINGS[self._board].PIN_SDI,
+ sdi=self._PIN_MAPPINGS[self._board].PIN_SDO,
+ cs=self._PIN_MAPPINGS[self._board].PIN_CS)
self._fpga_io.spi1_enable(True)
def _reset_opentitan(self):
- self._fpga_io.pin_set_state(self._PIN_JTAG_SPI, 1)
- self._fpga_io.pin_set_state(self._PIN_SRST, 0)
+ self._fpga_io.pin_set_state(self._PIN_MAPPINGS[self._board].PIN_JTAG_SPI, 1)
+ self._fpga_io.pin_set_state(self._PIN_MAPPINGS[self._board].PIN_SRST, 0)
time.sleep(self._BOOTSTRAP_DELAY)
- self._fpga_io.pin_set_state(self._PIN_SRST, 1)
+ self._fpga_io.pin_set_state(self._PIN_MAPPINGS[self._board].PIN_SRST, 1)
time.sleep(self._BOOTSTRAP_DELAY)
def __enter__(self):
"""Starts bootstrapping."""
- self._fpga_io.pin_set_state(self._PIN_BOOTSTRAP, 1)
+ self._fpga_io.pin_set_state(self._PIN_MAPPINGS[self._board].PIN_BOOTSTRAP, 1)
self._reset_opentitan()
- self._fpga_io.pin_set_state(self._PIN_JTAG_SPI, 0)
+ self._fpga_io.pin_set_state(self._PIN_MAPPINGS[self._board].PIN_JTAG_SPI, 0)
time.sleep(self._BOOTSTRAP_DELAY)
return self
def __exit__(self, exc_type, exc_value, traceback):
"""Ends bootstrapping."""
- self._fpga_io.pin_set_state(self._PIN_BOOTSTRAP, 0)
- self._fpga_io.pin_set_state(self._PIN_JTAG_SPI, 1)
+ self._fpga_io.pin_set_state(self._PIN_MAPPINGS[self._board].PIN_BOOTSTRAP, 0)
+ self._fpga_io.pin_set_state(self._PIN_MAPPINGS[self._board].PIN_JTAG_SPI, 1)
time.sleep(self._BOOTSTRAP_DELAY)
def transfer(self, frame):
@@ -209,7 +224,7 @@
class SPIProgrammer:
"""Class for programming OpenTitan over the SPI interface of SAM3X/U on CW310/305."""
- def __init__(self, firmware_path, board="CW305"):
+ def __init__(self, firmware_path, board='CW305'):
"""Inits SPIProgrammer with the path of a firmware image and board name."""
self._firmware_path = firmware_path
self._board = board