An SPI flash and iCE40UP FPGA programmer

      No Comments on An SPI flash and iCE40UP FPGA programmer

Before designing my iCE40UP development board, I knew I would need some kind of programmer for the SPI flash memory that would hold the design it ran. I’d already discounted the idea of using an off-the-shelf programmer: it would add more cost to the project, and it was a far too “easy” solution. Therefore I was going to build my own, in a similar fashion to how I built my own programmer for AT28C256 (PDF) 32K x 8 parallel EEPROMs, more then a decade ago.

The question was how to build a programmer for SPI flashes and possibly, later on, the FPGA attached to the development board? This basically breaks down into two choices, one of hardware and one of software.

In terms of hardware, the only problem was the amount of choice. Ignoring unusual solutions, like driving an SPI bus directly from a PC using something like a USB IO board like this one, it came down down to a choice of microcontroller. Any one of the following would do the job just fine:

  • An AVR based controller with a suitable amount of inbuilt SRAM, flash and IO pins
  • Some kind of ARM based controller, possibly from the STM32 family
  • An ESP32 board
  • Something built with a Pi Pico board

For each of these solutions there were two roads to consider: using an off the shelf board, or designing my own board that featured the controller.

Straightaway I discounted using an AVR. While I like the parts, I would not have learned anything new taking that path.

The ESP32s are fantastic. I’ve used an ESP32 board in my e-paper Home Assistant display project and was vaguely familiar with how I might use one in this kind of project. The problem was it felt like overkill. These boards always have networking capabilities, which I wouldn’t need here.

Whilst I do want to explore how to go about building my own board featuring a modern, 32 bit controller, I decided that this was not the project to take on that work; an off-the-shelf board was the way to go.

In summary either a board (there are literally hundreds to choose from) based around the STM32 or a Pi Pico would have done the job. I the end I settled on a Pi Pico.

The Pi Pico is built around the RP2040 (PDF), an ARM based controller custom designed by the Raspberry Pi Foundation. It has the following high-level features:

  • Dual-core cortex M0+
  • 264K SRAM
  • External Quad-SPI Flash with eXecute In Place (XIP)
  • On-board USB1.1 (device or host)
  • 30 multi-function General Purpose IO (4 can be used for ADC)
    ◦ 1.8-3.3V IO Voltage
  • 12-bit 500ksps Analogue to Digital Converter (ADC)
  • Various digital peripherals
    ◦ 2x UART, 2x I2C, 2x SPI, up to 16 PWM channels
  • Dual Programmable IO (PIO) peripherals
    ◦ Flexible, user-programmable high-speed IO

Basically it has everything you’d expect from a modern MCU, though curiously it has no onboard flash to speak of, and thus firmware must live in an external flash.

One interesting feature it has is what the datasheet terms PIO, programmed IO. This is not IO lines controlled by the controller, but IO that runs on a dedicated IO processor. This affords the user all kinds of new possibilities, including operating as a WS2812 controller without any processor time allocated to the task. This does require learning new concepts however. It reminded me a little of the Amiga’s copper when I first read about it.

There are several variants of the Pi Pico board, but the two prominent ones are the original Pi Pico and the Pi Pico W, which adds a Wi-Fi controller. For this project I wouldn’t need any networking.

The first steps in writing my own firmware to program an SPI flash was to get the SDK installed. This was actually borderline trivial and I simply followed the organised and easy to follow instructions (PDF). Overall I was pleased that unlike the ESP32 SDK (ESP-IDF) the Pico SDK was fairly transparent and conventional. Whilst cmake is the recommend build system, it is also popular to build Pico projects with plain old Make, if desired.

After getting a trivial LED flasher loaded into the Pico and verifying that I could get data in to and out of the USB port when presenting as a serial text interface, it was time to build a simple test rig on breadboard:

The SPI flash I chose to start with was the one I had closest to hand, as used on my VGA graphics card: a EPCQ32A (PDF). I attached it to a DIP8 adapter board, and hooked up the SPI connections to the Pi Pico: /CS, MISO, MOSI. the clock and Vcc/GND. The connections were per the Active Serial x1 (AS x1) mode, with the DATA3 pin attached to the Vcc, DATA0 being used as MOSI, and DATA1 being used as MISO. I also attached, to DIP8 adapters, two other SPI flashes I had laying about:

  • Winbond W25Q64FW (PDF)
  • Macronix MX25L3233F (PDF), which I removed from an old photo-frame, just to see if it would work

For a communication protocol, I had in mind something based loosely around my ages-old 6809 flashing mechanism. A few additions would be needed, as I wanted to have the protocol support an explicit read-back mode, whereby the current content of the flash could be written out to a file without modifying it, but broadly it was to be very similar. For writing to the flash:

  1. Client sends the write command byte (“w”)
  2. Programmer sends a banner, including the SPI flash device detected and its capacity in bytes
  3. Client sends the size of the file in 256 byte pages; count is send as a 4 byte quantity in little endian format, since this is the endianness of the RP2040
    1. For each block, send it
    2. Wait for the programmer to send a “proceed” byte
  4. Programmer sends back what it has programmed a 256 byte page at a time
  5. As usual, if the client detects an invalid page it does nothing except flag it to the user

There is a also a read command (“r”) which will skip the programming part and simply instructs the programmer to send to the client the content of the flash, a 256 byte page at a time.

There is one very interesting difference for this client program compared to all my earlier, similar, programs: this one is written in Go. Go has become my preferred language for all utilities like this. Writing this software in Go was fairly uneventful. The only 3rd party package needed was  github.com/bugst/go-serial for accessing the serial port.

In terms of writing the programmer side, this was written in old-fashioned C. I did ponder looking at Rust, but as this was a new controller (for me) I thought that would be pushing things into the unknown a bit too much.

The general approach was to look at the Intel datasheet and identify the actions that were needed to read and write the flash over SPI. The protocol used is described reasonably well by the datasheet. For instance, the device identification (0x9f) command will return a 3 byte sequence, unique to each device. My programmer firmware currently supports 9 devices from 5 different vendors, though I have only tested the parts I have, which is 3 different ones.

Another issue I had to deal with was that different vendors describe the protocol commands using subtly different terminology. But I did not discover any significant differences in the protocol across the different devices I tried that would have dirtied the code, which was a surprise. However it’s worth stating that my programmer is fairly simplistic and uses only a single SPI data in pin. More advanced (faster) programming modes might expose significant differences between the SPI flash ICs that are available.

One last feature was added to my flashing client code and the programmer code itself: the ability to program an iCE40 (PDF) flash directly. Doing this is useful when a rapid programming turn around is desired. Though programming the flash attached to my dev board is not exactly slow, probably about a minute or two, it does put a wear cycle on the flash.

The programming sequence is described in detail in the relevant Lattice datasheet (PDF) and is termed the SPI Slave Configuration Interface. It is fairly streightforward and will not be described in detail here. It’s basically a question of manipulating a trio of control pins and sending the data, blind, over SPI. Because the bitstream is not guaranteed to be a multiple of a page size in length it is sent in pages, but each page is prefixed by a length byte. When the pages have all been sent, the client sends a 0 length page. At the end of the sequence the FPGA will then pull a control pin low if the bitstream was successfully received and applied. For completeness the state of this pin is returned back to the client program, which displays its state.

Here’s a picture of my development board hooked up to the programmer:

Squinting at the jumpers towards the top right of he board you can see that they are set up for programming the iCE40. Here’s an example of the client program being used to program an SPI flash:

Not a very interesting transcript but there.

Overall I’m pretty pleased with my little SPI flash programmer. It will be usable with many  future projects, I’m sure. I learned a lot completing this project too; I’m especially enjoying using the Pi Picos and would considering building my own board around an RP2040, should the need arise.

The only issue with this whole project is the arrangement of pins used to connect the programmer to the target board. There are about eight individual wires, which are a nuisance to individually hook up. In the future I’ll switch to using an IDC box header, even if I have to devise my own “standard” pinout. However the current setup can at least be used to attach any random board.

The code, for both the programmer side (in C) and the client side (in Go) are in a repo in my GitHub account. The repository includes a README describing how to wire a Pi Pico up to an SPI flash and/or an iCE40 FPGA.

My next post, hopefully in a few days, will document another project I’ve been working on for quite a few months that brings together quite a few themes from what I’ve been looking at recently: an iCE40 FPGA, a Pi Pico, and Home Assistant….

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.