[lint] Reformat cw_spiflash.py
Signed-off-by: Pirmin Vogel <vogelpi@lowrisc.org>
diff --git a/util/fpga/cw_spiflash.py b/util/fpga/cw_spiflash.py
index 4d56435..c710719 100644
--- a/util/fpga/cw_spiflash.py
+++ b/util/fpga/cw_spiflash.py
@@ -3,259 +3,271 @@
# SPDX-License-Identifier: Apache-2.0
"""Utilities used to download firmware images into the OpenTitan system."""
-import subprocess
import time
import hashlib
from functools import partial
+
def _uint_to_le(val, length):
- """Returns a byte array that represents an unsigned integer in little-endian format.
+ """Returns a byte array that represents an unsigned integer in little-endian format.
- Args:
- val: Unsigned integer to convert.
- length: Number of bytes.
+ Args:
+ val: Unsigned integer to convert.
+ length: Number of bytes.
- Returns:
- A byte array of ``length`` bytes that represents ``val`` in little-endian format.
- """
- return val.to_bytes(length=length, byteorder='little')
+ Returns:
+ A byte array of ``length`` bytes that represents ``val`` in little-endian format.
+ """
+ return val.to_bytes(length=length, byteorder='little')
+
def _le_to_uint(val):
- """Returns the unsigned integer represented by the given byte array in little-endian format.
+ """Returns the unsigned integer represented by the given byte array in little-endian format.
- Args:
- val: Byte array in little-endian format that represents an unsigned integer.
+ Args:
+ val: Byte array in little-endian format that represents an unsigned integer.
- Returns:
- The unsigned integer represented by the byte array ``val``.
- """
- return int.from_bytes(val, byteorder='little')
+ Returns:
+ The unsigned integer represented by the byte array ``val``.
+ """
+ return int.from_bytes(val, byteorder='little')
+
class _SpiFlashFrame:
- """A frame used for programming OpenTitan over the SPI interface.
+ """A frame used for programming OpenTitan over the SPI interface.
- In order to program OpenTitan with a binary image, the image is broken into ``PAYLOAD_SIZE``
- byte chunks, each of which is transmitted as a frame over the SPI interface. A frame consists
- of four parts:
- * Digest: A ``DIGEST_SIZE`` byte digest of the rest of the frame,
- * Frame number: A ``FRAME_NUMBER_SIZE`` byte little-endian representation of the frame number.
- MSB of the last frame's frame number is set to 1 to indicate the end of the image.
- * Flash offset: A ``FLASH_OFFSET_SIZE`` byte little-endian representation of the flash offset
- to start writing the payload of the frame.
- * Payload: ``PAYLOAD_SIZE`` byte actual binary payload in little-endian format.
+ In order to program OpenTitan with a binary image, the image is broken into ``PAYLOAD_SIZE``
+ byte chunks, each of which is transmitted as a frame over the SPI interface. A frame consists
+ of four parts:
+ * Digest: A ``DIGEST_SIZE`` byte digest of the rest of the frame,
+ * Frame number: A ``FRAME_NUMBER_SIZE`` byte little-endian representation of the frame number.
+ MSB of the last frame's frame number is set to 1 to indicate the end of the image.
+ * Flash offset: A ``FLASH_OFFSET_SIZE`` byte little-endian representation of the flash offset
+ to start writing the payload of the frame.
+ * Payload: ``PAYLOAD_SIZE`` byte actual binary payload in little-endian format.
- OpenTitan signals correct transmission of frame N-1 by transmitting its digest (computed over
- the entire frame, i.e. including the digest field of the frame) while it receives frame N.
+ OpenTitan signals correct transmission of frame N-1 by transmitting its digest (computed over
+ the entire frame, i.e. including the digest field of the frame) while it receives frame N.
- Attributes:
- HASH_FUNCTION: Hash function to use for digest computations.
- DIGEST_SIZE: Size of digests (inferred from ``HASH_FUNCTION``).
- FRAME_NUMBER_SIZE: Size of the frame number field in bytes.
- FLASH_OFFSET_SIZE: Size of the flash offset field in bytes.
- PAYLOAD_SIZE: Size of the frame payload in bytes.
- FRAME_SIZE: Size of the SPI Flash frame, depends on the Boot ROM, see spiflash_frame.h.
- is_second_frame: Indicates if this is the second frame.
- expected_ack: Expected acknowledgement value for a frame.
- """
- HASH_FUNCTION = hashlib.sha256
- DIGEST_SIZE = HASH_FUNCTION().digest_size
- FRAME_NUMBER_SIZE = 4
- FLASH_OFFSET_SIZE = 4
- FRAME_SIZE = 2048
- PAYLOAD_SIZE = FRAME_SIZE - DIGEST_SIZE - FRAME_NUMBER_SIZE - FLASH_OFFSET_SIZE
-
- def __init__(self, frame_number, flash_offset, payload):
- """Inits a _SpiFlashFrame.
-
- Also pads frame payload to ``PAYLOAD_SIZE`` bytes if needed.
-
- Args:
- frame_numer: Frame number.
- flash_offset: Flash offset.
- payload: Frame payload.
+ Attributes:
+ HASH_FUNCTION: Hash function to use for digest computations.
+ DIGEST_SIZE: Size of digests (inferred from ``HASH_FUNCTION``).
+ FRAME_NUMBER_SIZE: Size of the frame number field in bytes.
+ FLASH_OFFSET_SIZE: Size of the flash offset field in bytes.
+ PAYLOAD_SIZE: Size of the frame payload in bytes.
+ FRAME_SIZE: Size of the SPI Flash frame, depends on the Boot ROM, see spiflash_frame.h.
+ is_second_frame: Indicates if this is the second frame.
+ expected_ack: Expected acknowledgement value for a frame.
"""
- self._frame_number = _uint_to_le(frame_number, self.FRAME_NUMBER_SIZE)
- self._flash_offset = _uint_to_le(flash_offset, self.FLASH_OFFSET_SIZE)
- self._payload = payload
- padding = self.PAYLOAD_SIZE - len(self._payload)
- if padding > 0:
- self._payload = b''.join([self._payload, b'\xFF'*padding])
- self._digest = self.HASH_FUNCTION(b''.join([
- self._frame_number,
- self._flash_offset,
- self._payload,
- ])).digest()
+ HASH_FUNCTION = hashlib.sha256
+ DIGEST_SIZE = HASH_FUNCTION().digest_size
+ FRAME_NUMBER_SIZE = 4
+ FLASH_OFFSET_SIZE = 4
+ FRAME_SIZE = 2048
+ PAYLOAD_SIZE = FRAME_SIZE - DIGEST_SIZE - FRAME_NUMBER_SIZE - FLASH_OFFSET_SIZE
- def __bytes__(self):
- return b''.join([
- self._digest,
- self._frame_number,
- self._flash_offset,
- self._payload,
- ])
+ def __init__(self, frame_number, flash_offset, payload):
+ """Inits a _SpiFlashFrame.
- def __repr__(self):
- return f'frame 0x{_le_to_uint(self._frame_number):08X} @ 0x{_le_to_uint(self._flash_offset):08X}'
+ Also pads frame payload to ``PAYLOAD_SIZE`` bytes if needed.
- @property
- def is_second_frame(self):
- """Indicates if this is the second frame."""
- return _le_to_uint(self._frame_number) == 1
+ Args:
+ frame_numer: Frame number.
+ flash_offset: Flash offset.
+ payload: Frame payload.
+ """
+ self._frame_number = _uint_to_le(frame_number, self.FRAME_NUMBER_SIZE)
+ self._flash_offset = _uint_to_le(flash_offset, self.FLASH_OFFSET_SIZE)
+ self._payload = payload
+ padding = self.PAYLOAD_SIZE - len(self._payload)
+ if padding > 0:
+ self._payload = b''.join([self._payload, b'\xFF' * padding])
+ self._digest = self.HASH_FUNCTION(b''.join([
+ self._frame_number,
+ self._flash_offset,
+ self._payload,
+ ])).digest()
- @property
- def expected_ack(self):
- """Expected acknowledgement value for the frame."""
- return self.HASH_FUNCTION(bytes(self)).digest()
+ def __bytes__(self):
+ return b''.join([
+ self._digest,
+ self._frame_number,
+ self._flash_offset,
+ self._payload,
+ ])
+
+ def __repr__(self):
+ return f'frame 0x{_le_to_uint(self._frame_number):08X} @ \
+ 0x{_le_to_uint(self._flash_offset):08X}'
+
+ @property
+ def is_second_frame(self):
+ """Indicates if this is the second frame."""
+ return _le_to_uint(self._frame_number) == 1
+
+ @property
+ def expected_ack(self):
+ """Expected acknowledgement value for the frame."""
+ return self.HASH_FUNCTION(bytes(self)).digest()
+
class _Bootstrap:
- """Handles low-level details during programming OpenTitan using SAM3X/U on CW310/305.
+ """Handles low-level details during programming OpenTitan using SAM3X/U on CW310/305.
- Initializes pins, resets OpenTitan, transmits frames, and receives acknowledgements.
+ Initializes pins, resets OpenTitan, transmits frames, and receives acknowledgements.
- To use:
- >>> with _Bootstrap(fpga) as bootstrap:
- >>> ...
- >>> 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'
- # 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"):
- """Inits a _Bootstrap with a CW310/305.
-
- Args:
- fpga: CW310/305 to be programmed, a ``cw.capture.targets.CW310/305`` instance.
- board: can be ``CW310`` or ``CW305`` to distinguish the different board types.
+ To use:
+ >>> with _Bootstrap(fpga) as bootstrap:
+ >>> ...
+ >>> 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'
+ # Delays below are in seconds.
+ _BOOTSTRAP_DELAY = 0.1
+ _SECOND_FRAME_DELAY = 0.2
+ _INTER_FRAME_DELAY = 0.02
- # 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'
+ def __init__(self, fpga, board="CW305"):
+ """Inits a _Bootstrap with a CW310/305.
- # 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)
- # 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_enable(True)
+ Args:
+ fpga: CW310/305 to be programmed, a ``cw.capture.targets.CW310/305`` instance.
+ board: can be ``CW310`` or ``CW305`` to distinguish the different board types.
+ """
- 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)
- time.sleep(self._BOOTSTRAP_DELAY)
- self._fpga_io.pin_set_state(self._PIN_SRST, 1)
- time.sleep(self._BOOTSTRAP_DELAY)
+ # 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'
- def __enter__(self):
- """Starts bootstrapping."""
- self._fpga_io.pin_set_state(self._PIN_BOOTSTRAP, 1)
- self._reset_opentitan()
- self._fpga_io.pin_set_state(self._PIN_JTAG_SPI, 0)
- time.sleep(self._BOOTSTRAP_DELAY)
- return self
+ # 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)
+ # 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_enable(True)
- 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)
- time.sleep(self._BOOTSTRAP_DELAY)
+ 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)
+ time.sleep(self._BOOTSTRAP_DELAY)
+ self._fpga_io.pin_set_state(self._PIN_SRST, 1)
+ time.sleep(self._BOOTSTRAP_DELAY)
- def transfer(self, frame):
- """Transmits a frame over SPI and receives the acknowledgement for the previous frame."""
- # Wait longer after the first frame since it triggers a flash erase operation.
- if frame.is_second_frame:
- time.sleep(self._SECOND_FRAME_DELAY)
- else:
- time.sleep(self._INTER_FRAME_DELAY)
- print(f'Transferring {frame}.')
- # Acknowledgement is the same size as digests.
- return bytes(self._fpga_io.spi1_transfer(bytes(frame))[:_SpiFlashFrame.DIGEST_SIZE])
+ def __enter__(self):
+ """Starts bootstrapping."""
+ self._fpga_io.pin_set_state(self._PIN_BOOTSTRAP, 1)
+ self._reset_opentitan()
+ self._fpga_io.pin_set_state(self._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)
+ time.sleep(self._BOOTSTRAP_DELAY)
+
+ def transfer(self, frame):
+ """Transmits a frame over SPI and receives the acknowledgement for the previous frame."""
+ # Wait longer after the first frame since it triggers a flash erase operation.
+ if frame.is_second_frame:
+ time.sleep(self._SECOND_FRAME_DELAY)
+ else:
+ time.sleep(self._INTER_FRAME_DELAY)
+ print(f'Transferring {frame}.')
+ # Acknowledgement is the same size as digests.
+ return bytes(
+ self._fpga_io.spi1_transfer(
+ bytes(frame))[:_SpiFlashFrame.DIGEST_SIZE])
class SPIProgrammer:
- """Class for programming OpenTitan over the SPI interface of SAM3X/U on CW310/305."""
- 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
+ """Class for programming OpenTitan over the SPI interface of SAM3X/U on CW310/305."""
+ 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
- def run(self, fpga):
- """Programs OpenTitan over the SPI interface of SAM3X/U on CW310/305.
+ def run(self, fpga):
+ """Programs OpenTitan over the SPI interface of SAM3X/U on CW310/305.
- This implementation has two improvements over the `spiflash` tool under
- sw/host/spiflash:
- * Optimizes the happy path by checking the acknowledgement of frame N-1
- after transmitting frame N instead of transmitting an extra frame to check
- each frame's acknowledgement.
- * Sends an all 0xFF frame as the last frame to avoid cases where the last
- frame gets corrupted and halts the bootstrapping process.
+ This implementation has two improvements over the `spiflash` tool under
+ sw/host/spiflash:
+ * Optimizes the happy path by checking the acknowledgement of frame N-1
+ after transmitting frame N instead of transmitting an extra frame to check
+ each frame's acknowledgement.
+ * Sends an all 0xFF frame as the last frame to avoid cases where the last
+ frame gets corrupted and halts the bootstrapping process.
- Args:
- fpga: CW310/305 to be programmed, a ``cw.capture.targets.CW310/305`` instance.
- """
- with _Bootstrap(fpga, self._board) as bootstrap:
- frame_number = 0
- flash_offset = 0
- prev_frame = None
- print(f'Programming OpenTitan with "{self._firmware_path}"...')
- with open(self._firmware_path, mode='rb') as fw:
- # Read fixed-size blocks from the firmware image.
- # Note: The second argument ``b''`` to ``iter`` below is the sentinel value that
- # ends the loop, i.e. the value returned by ``fw.read`` at EOF.
- for payload in iter(partial(fw.read, _SpiFlashFrame.PAYLOAD_SIZE), b''):
- frame = _SpiFlashFrame(frame_number, flash_offset, payload)
- # We need to make sure that the previous frame is transmitted correctly before
- # proceeding with the next frame.
- while True:
- # Transmit frame N, receive acknowledgement for frame N-1.
- ack = bootstrap.transfer(frame)
- if prev_frame and prev_frame.expected_ack != ack:
- # When OpenTitan encounters a transmission error or an out of
- # sequence frame, it ignores the current frame and sends the ack of
- # the last successfully received frame. Since transmission errors
- # can occur in both directions, i.e. the previous frame sent by
- # SAM3U or the ack sent by OpenTitan, and we can't tell them
- # apart, we should re-transmit the previous frame and check the
- # ack sent by OpenTitan.
- print(f'Error transferring {prev_frame}.')
- ack = bootstrap.transfer(prev_frame)
- if ack == frame.expected_ack:
- # Ack sent by OpenTitan was corrupted. Proceed with the next frame.
- break
- else:
- # Previous frame sent by SAM3U was corrupted. Send current frame again.
- pass
- else:
- # Correct acknowledgement received or this is the first frame.
- break
- prev_frame = frame
- frame_number += 1
- flash_offset += _SpiFlashFrame.PAYLOAD_SIZE
- # Transmit an all 0xFF frame with the expected frame number to finish bootstrapping.
- bootstrap.transfer(_SpiFlashFrame(frame_number | (1<<31), flash_offset, b''))
+ Args:
+ fpga: CW310/305 to be programmed, a ``cw.capture.targets.CW310/305`` instance.
+ """
+ with _Bootstrap(fpga, self._board) as bootstrap:
+ frame_number = 0
+ flash_offset = 0
+ prev_frame = None
+ print(f'Programming OpenTitan with "{self._firmware_path}"...')
+ with open(self._firmware_path, mode='rb') as fw:
+ # Read fixed-size blocks from the firmware image.
+ # Note: The second argument ``b''`` to ``iter`` below is the sentinel value that
+ # ends the loop, i.e. the value returned by ``fw.read`` at EOF.
+ for payload in iter(
+ partial(fw.read, _SpiFlashFrame.PAYLOAD_SIZE), b''):
+ frame = _SpiFlashFrame(frame_number, flash_offset, payload)
+ # We need to make sure that the previous frame is transmitted correctly before
+ # proceeding with the next frame.
+ while True:
+ # Transmit frame N, receive acknowledgement for frame N-1.
+ ack = bootstrap.transfer(frame)
+ if prev_frame and prev_frame.expected_ack != ack:
+ # When OpenTitan encounters a transmission error or an out of
+ # sequence frame, it ignores the current frame and sends the ack of
+ # the last successfully received frame. Since transmission errors
+ # can occur in both directions, i.e. the previous frame sent by
+ # SAM3U or the ack sent by OpenTitan, and we can't tell them
+ # apart, we should re-transmit the previous frame and check the
+ # ack sent by OpenTitan.
+ print(f'Error transferring {prev_frame}.')
+ ack = bootstrap.transfer(prev_frame)
+ if ack == frame.expected_ack:
+ # Ack sent by OpenTitan was corrupted. Proceed with the next frame.
+ break
+ else:
+ # Previous frame sent by SAM3U was corrupted. Send current frame
+ # again.
+ pass
+ else:
+ # Correct acknowledgement received or this is the first frame.
+ break
+ prev_frame = frame
+ frame_number += 1
+ flash_offset += _SpiFlashFrame.PAYLOAD_SIZE
+ # Transmit an all 0xFF frame with the expected frame number to finish bootstrapping.
+ bootstrap.transfer(
+ _SpiFlashFrame(frame_number | (1 << 31), flash_offset, b''))