Creating a Flash SPI programmer from scratch

I recently restarted playing with FPGAs, and found out about the Lattice iCE40 boards produced by Olimex. Despite already being kinda old designs (copyright marks on the boards mention 2016, almost 10 years ago!), they are very interesting. They feature an iCE40 FPGA (with options for 1k or 8k logic cells) paired with a generous amount of flash memory (2 MB, more than enough for storing the bitstream) and SRAM (512 KB). Another interesting point: they don't include any on-board programmers, which reduces the final price.

When I ordered the boards, I noticed that Olimex recommended buying one of their Arduino clones to use as a programmer, running a firmware they provide themselves. I decided to just buy that and save me some effort. It sounded perfect: it would arrive together with the FPGA boards and allow me to get started immediately. And that was what I did: I bought a few FPGA boards, the Arduino clone and a flat cable to connect them.

Everything arrived the next day from Bulgaria to Germany via DHL Express. So far, so good.

The Arduino clone is based on the popular ATmega32U4. I believe it is a clone of Arduino Leonardo. I've used AVRs for a long time, and understand these microcontrollers quite well. One of the big issues with them is that there is no separated memory for a boot loader; the boot loader (either the default one, or the Arduino one) is stored in the user flash space.

I never read the source code of the Arduino boot loader for these boards, or the USB library used by the Olimex-provided programmer firmware, but apparently there are some issues when jumping from the boot loader to user code and vice versa, resulting in incompatibilities with some hubs and computers. As I understand it, these issues are probably related to the flash memory layout I mentioned previously. And that was what happened with my USB hub and my Mac: resetting the device, either to the boot loader or to the programmer firmware after flashing, would frequently put my hub into an unusable state.

The second (and probably most reasonable) approach to this would be to just buy a FT2232H mini module, as the FT2232H chips are well known to be used together with iCE40 FPGAs. For example, it is the chip included in the popular iCEBreaker boards, and works with the iceprog tool shipped by icestorm. But my closer friends could probably already guess what I did (which is what I always do when such things happen to me): I deviated from the original goal, which was just to play with the FPGA boards, and started a new "side quest": developing a decent programmer for these boards, so I don't have to either reboot my Mac or unplug my USB hub every time I need to reset the programmer.

I soldered a stm32f0-usbd-devboard (this is a custom development board I developed. It contains a STM32F042, more details on its project page and in future blog posts), wired up a flat cable with a 10 pin IDC connector directly to the development board pins and started writing firmware for it.

The iCE40 FPGAs are quite simple. Despite some mention of JTAG on development boards and in the schematic symbols, apparently they don't really support using JTAG for programming. Every development board and real world project I could find was doing the same thing: write directly to the flash memory using SPI, while holding the FPGA reset pin down. I must admit that, while not ideal, I love the simplicity of this.

It took me around a weekend to write and optimize the firmware, to be able to write, verify, read and erase the flash memory via SPI, using the STM32 SPI peripheral with DMA and a timer. The firmware was written in bare metal C (no HAL/LL drivers, only CMSIS device headers), using usbd-fs-stm32 (a bare metal C implementation of USB 2.0 Full Speed device for STM32F0/STM32G4 I also wrote myself). Firmware implements a vendor USB HID interface (so that device drivers are not needed in any operating system) and includes a watchdog to reset the USB device whenever something goes wrong. As this was the first "high throughput" device I created using usbd-fs-stm32, this project helped catch several bugs and find out some optimization paths I'd probably not have discovered otherwise. I believe that this work already paid off 😄!

The command-line interface was written in Go, using rafaelmartins.com/p/usbhid (a cross-platform, pure-GO library to interact with USB HID devices I wrote myself; yes, I wrote most of the software used in this project over the past few years 😄). Right now it is very simple but does the job, and works fine on Linux, Mac and Windows.

I ended up ordering the FT2232H mini module later, for "scientific reasons" (a.k.a. comparing performance, cause why not), and my device performed quite decently. I won't show full numbers here, but I can say that it is several times faster than the Arduino firmware recommended by Olimex, and only marginally slower than the FT2232H mini module (which is a USB 2.0 HS device, while mine is a USB 2.0 FS device, then that's HUGE). I am quite satisfied with the outcome of this quick hack!

That is how iceflashprog was born!

iceflashprog

More details on the project page: https://rafaelmartins.com/p/iceflashprog/