Bootloader

class squishy.gateware.bootloader.SquishyBootloader(*args: Any, src_loc_at: int = 0, **kwargs: Any)

Squishy DFU Bootloader

This is the “top” module representing a Squishy DFU capable bootloader.

It provides DFU alt-modes for each flash slot, including the bootloader, as well as dispatch to the appropriate programming interface for the given platform.

For SquishyRev1 platforms, the method of programming is direct SPI flash, followed by an SB_WARMBOOT trigger.

For SquishyRev2 this is more complicated, as we have the supervisor MCU in the mix. First the image is written to the SPI PSRAM, a signal is then sent to the supervisor to reboot us and re-program us with the new bitstream.

Note

There needs to be some consideration for hardware platforms that support ephemeral programming, any transfers to that slot must be distinguished from a normal slot transfer, for Rev1 platforms this is not an issue, as there is no way of doing an ephemeral applet, however for Rev2, in order to try to tide wearing out flash with write cycles (even though they’re good for like, 100k cycles) we have an (optional?) onboard PSRAM that acts as both a cache for doing flash updates as well as doing hot-loading without actually touching flash.

This can be done mostly opaquely from the root of the bootloader module itself, other than having to properly name the ephemeral DFU slot, as all the machinery for updating the platform is within the target module for that anyway.

Warning

Currently there is no flash protection for the bootloader slot (slot 0), it is exposed by default, and treated like any other applet slot.

We also don’t have any checksums, which might be a bit problematic, but due to some platform limitations specifically due to Rev1 where we write directly into flash and don’t have a buffer that can be used and discarded, we write-over the slot as we update. This is particularly dangerous for the bootloader.

Parameters:
  • serial_number (str) – The device serial number to use.

  • revision (tuple[int, int]) – The device revision.

Variables:

serial_number (str) – The device serial number assigned.

class squishy.gateware.bootloader.rev1.Rev1(*args: Any, src_loc_at: int = 0, **kwargs: Any)
Parameters:

fifo (AsyncFIFO | None) – The storage FIFO.

Variables:
  • trigger_reboot (Signal) – Input: FPGA reboot trigger from DFU.

  • slot_selection (Signal(2)) – Input: Flash slot destination from DFU alt-mode.

  • dl_start (Signal) – Input: Start of a DFU transfer.

  • dl_finish (Signal) – Input: An acknowledgement of the dl_done signal

  • dl_ready (Signal) – Output: If the backing storage is ready for data.

  • dl_done (Signal) – Output: When the backing storage is done storing the data.

  • dl_size (Signal(16)) – Input: The size of the DFU transfer into the the FIFO

  • slot_changed (Signal) – Input: Raised when the DFU alt-mode is changed.

  • slot_ack (Signal) – Output: When the slot_changed signal was acted on.

Squishy Supervisor Bootloader Protocol:

If we are in an applet and wish to drop into the bootloader:

  1. FPGA raises ~SU_IRQ with the IRQ register value DFU set to 1

  2. Supervisor resets the FPGA configuration and loads the bootloader bitstream

  3. Supervisor then waits for the next IRQ event

  4. Supervisor then checks the IRQ register to make sure it is in_boot

When the FPGA enters bootloader:

  1. FPGA waits for a DFU upload, and if done stuffs it into the PSRAM
    1. FPGA sticks the destination slot for the DFU payload into the dest_slot half of the slots register

    2. FPGA writes the DFU payload size into the txlen register

  2. FPGA sets the IRQ Reason to `in_boot`*

  3. FPGA raises the ~SU_IRQ line to notify the supervisor we are in the bootloader*

  4. Supervisor reads the slots and txlen registers
    1. If the destination slot is not ephemeral:
      1. Supervisor erases the flash region mapped to that slot

      1. Supervisor write the contents of the PSRAM for txlen into the target flash slot

      1. Supervisor then writes into the FPGA control register that the erase/flash cycle is done

      1. Supervisor waits for the FPGA to tell it to reboot into a given slot

      1. FPGA triggers reboot on DFU detach to last written slot?

    2. If the destination slot is ephemeral:
      1. Supervisor resets the FPGA into configuration mode

      1. Read a block into our SPI buffer from the PSRAM

      1. While we have not written the full bitstream:

        α. Read at most buffers worth of bitstream data from PSRAM β. Dump buffer into FPGA configuration

      1. Check FPGA configuration status

      1. let the FPGA boot into new bitstream

class squishy.gateware.bootloader.rev2.Rev2(*args: Any, src_loc_at: int = 0, **kwargs: Any)
Parameters:

fifo (AsyncFIFO | None) – The storage FIFO.

Variables:
  • trigger_reboot (Signal) – FPGA reboot trigger from DFU.

  • slot_selection (Signal(2)) – Flash slot destination from DFU alt-mode.

  • dl_start (Signal) – Input: Start of a DFU transfer.

  • dl_finish (Signal) – Input: An acknowledgement of the dl_done signal

  • dl_ready (Signal) – Output: If the backing storage is ready for data.

  • dl_done (Signal) – Output: When the backing storage is done storing the data.

  • dl_reset_slot (Signal) – Input: Signals to the storage to reset the active slot.

  • dl_size (Signal(16)) – Input: The size of the DFU transfer into the the FIFO

  • slot_changed (Signal) – Input: Raised when the DFU alt-mode is changed.

  • slot_ack (Signal) – Output: When the slot_changed signal was acted on.