Bootloader
Note
‘Bootloader’ is a bit of a misnomer, as what is currently implemented is more like a ‘bitstream selector’ (although a true USB bootloader is something in the works).
Interface
The bitstream selector allows you to arbitrarily select from one of 8 bitstreams after the Tiliqua powers on, without needing to connect a computer.
Put simply:
When tiliqua boots, you can select a bitstream with the encoder (either using the display output, or by reading the currently lit LED if no display is connected).
When you select a bitstream (press encoder), the FPGA reconfigures itself and enters the selected bitstream.
From the new bitstream, you can always go back to the bitstream selector by holding the encoder for 3sec (this is built into the logic of every bitstream).
Warning
The bitstream selector will not reboot correctly if you have
the dbg
USB port connected. The assumption is you’re flashing
development bitstreams to SRAM in that scenario anyway.
Setup and implementation
The bitstream selector consists of 2 key components that work together:
The RP2040 firmware (apfelbug - fork of dirtyJTAG)
The bootloader top-level bitstream.
The difficulty here is that the ECP5 multiboot mechanism only supports rebooting from one bitstream to a single fixed new address bootaddr
, which is not sufficient for arbitrary bitstream selection. It would in theory have been possible to set up a ‘bitstream chain’, however that would require all bitstreams to be correctly flashed with the correct addresses at all times, which is not ideal for development. So, the solution here is more complicated, but hopefully more robust to ‘less trustworthy’ bitstreams.
First-time setup
Build and flash the apfelbug project to the RP2040 (hold RP2040 BOOTSEL during power on, copy the
build/*.uf2
to the usb storage device and reset)- On the Tiliqua ECP5 SPI flash:
Using
openFPGALoader -c dirtyJtag <bitstream> -f o <offset>
flash
bootloader
bitstream to offset 0x0flash user bitstreams to 0x100000, 0x200000, 0x300000 … and so on (1MB spacing)
- DISCONNECT USB DBG port, reboot Tiliqua
Currently
apfelbug
only works correctly with the DBG connector DISCONNECTED or with the UART port open on Linux and CONNECTED. Do not have the USB DBG connected without the UART0 open withpicocom
or so.
Now when Tiliqua boots you will enter the bootloader. Use the encoder to select an image. Hold the encoder for >3sec in any image to go back to the bootloader.
Bitstream Manifest
By default the bootloader screen will report all bitstream names as <unknown>
. The bootloader
bitstream can optionally read from a JSON manifest stored at the end of SPI flash called a ‘Bitstream Manifest’. Such a manifest.json
file can be copied from gateware/src/top/bootloader/fw/example-manifest.json
. You can flash this to the end of the SPI flash so the bootloader knows what each bitstream should be called.
sudo openFPGALoader -c dirtyJtag -f -o 0xfff000 --file-type raw manifest.json
Note: this address 0xfff000
comes from src/tiliqua/tiliqua_soc.py
.
Assuming the bootloader bitstream is correctly already flashed to 0x0, this command will also reset the FPGA, re-enter the bootloader, and display the updated bitstream manifest.
At the moment, the bootloader itself does no verification that there are actually bitstreams flashed to each slot corresponding to the names. If a bitstream is not actually in a designated slot or is corrupt, selecting the bad slot will simply reboot the FPGA and re-enter the bootloader.
ECP5 implementation
The ECP5 bootloader
bitstream does nothing except tell the RP2040 that it wants to be rebooted into a new bitstream (over UART). However, the user bitstreams are responsible for asserting PROGRAMN when the encoder is held, to reconfigure back to the bootloader bitstream.
RP2040 implementation
apfelbug
firmware includes the same features as pico-dirtyjtag
(USB-JTAG and USB-UART bridge), with some additions:
UART traffic is inspected to look for keywords.
If a keyword is encountered e.g.
BITSTREAM1
, a pre-recorded JTAG stream stored on the RP2040’s SPI flash is decompressed and replayed. The JTAG streams are instances of the bootstub top-level bitstream. These are tiny bitstreams that are programmed directly into SRAM with the targetbootaddr
and PROGRAMN assertion.This facilitates ECP5 multiboot (jumping to arbitrary bitstreams) without needing to write to the ECP5’s SPI flash and exhausting write cycles.
Recording new JTAG streams for RP2040
TODO documentation, not necessary to change this for any ordinary usecase. Update this if needed for SoldierCrab R3.