Quad SPI Flash Controller

Project maintainers


Name: qspiflash
Created: May 13, 2015
Updated: Mar 16, 2019
SVN Updated: Feb 4, 2019
SVN: Browse
Latest version: download (might take a bit to start...)
Statistics: View
Bugs: 1 reported / 1 solved
Star22you like it: star it!

Other project properties

Category:Communication controller
Development status:Stable
Additional info:Design done, FPGA proven, Specification done
WishBone compliant: Yes
WishBone version: B.4
License: GPL


This is a Quad-SPI Flash controller. It currently works for me on the 4MB Spansion flash found within a Basys-3 development board. The controller hides much, although not all, of the flash chip interactions from the user behind wishbone read and write accesses. Indeed, reading from this memory is as simple as reading from the wishbone!

For those not familiar with a Quad-SPI flash, the basic device is built upon a SPI interface. Such an interface consists of four wires: a chip select, a clock, a master out slave in (MOSI) serial line, and a master in slave out (MISO) serial line. When this proved to be not sufficiently fast enough, additional commands were added to the protocol in addition to two additional wires. Thus, for certain commands, all four wires (including both MOSI and MISO lines) become unidirectional sending data to or from the device. However, because of the legacy associated with the interface, the interface defaults to SPI mode and offers only some commands in four bit mode.

In a similar fashion, this controller was built upon a SPI flash controller. It supports a high speed read mode via pipelined wishbone read interactions. The controller supports two wishbone interfaces: a control interface and a data interface. The control interface supports four register accesses: an erase register that can be used to command erases on the chip, a status register that will read the status from the chip, a configuration register that reads the configuration from the chip, and a read only ID register. (This is all documented in the spec ... don't forget that a read from the configuration register is necessary to go into 4-bit mode!) Writes are as simple as disabling the write protect mechanism in the erase register, commanding an erase of the sector, and then writing one 256-byte page at a time. The data interface works much like you would expect, wishbone read cycles read from the data area of the device. Wishbone write cycles to the data area will cause a program cycle within the device.

As with other flash controllers, this controller attempts to encapsulate the low level interactions with the board into a higher level, simpler interaction.

Oh, one other feature: this implementation supports an interrupt line coming out of the controller for the purpose of informing anyone that is interested that an erase, program cycle, or write register command has completed. This keeps you from needing to poll the chip for the same information--even though that's what the controller needs to do under the hood in order to support the interrupt line.

EQSPI Flash Controller

I'm currently in the process of building an upgraded flash controller to support the flash on the Arty. This chip is newer and different, and although the controls are remarkably similar to the QSPI controller for the Spansion flash on the Basys-3 board, they are different enough to warrant an updated controller. The goals of this new controller include:

  1. Interaction that is similar to the last flash controller: Reads from the flash are as simple as reads from a wishbone bus, pipeline reads likewise. Reads from the controllers status register are always allowed (not the flash status register), even if the chip is currently busy writing. Writing to the device, assuming that the write protect is off, will be treated as programming commands and will start a programming cycle.
  2. All device control and status registers are available on the bus for programming. These include the status register, the nonvolatile configuration register, the volatile configuration register, and the enhanced non-volatile configuration register.
  3. Support a 200 MHz clock within the FPGA, 100 MHz SPI clock to the flash. (Please note, this doesn't necessarily speed up the flash transactions by that much ... there are more delay cycles as you move towards higher speeds. Further, there's more overhead logic to get to the device, so again it's not that much faster.)
  4. Provide full access to the flash's ID memory, as well as programmable access to the One-Time Programmable registers.
  5. Together with the ability to erase individual sectors (64kB), provide the ability to erase subsectors (4kB), and to choose between the two. (No capability is provided to erase the entire device ...)
  6. To lock the control registers down somewhat, via a safe mode within the Verilog, so that any writes to the configuration registers will leave the device in a configuration the controller will still support.
  7. The erase/write control register now requires a "key" to be set, lest an erase be commanded "accidentally".
Many of these features already work in a Verilator simulation. For example, using the new controller, I can read from the flash in either wishbone single mode or pipeline mode, and I can do it via Quad SPI Output mode or XIP (execute in place) mode or both. Reading the OTP and ID registers works, as does writing to the various status registers. Further, the system builds for a 200MHz clock (100 MHz flash clock), so it meets that piece of criteria.

20160722 Update: Everything works in simulation, now moving to the hardware. It looks like I'm sampling data on the wrong side of the clock, so I've got something to fix there. You'll know when it's been added, 'cause the new RTL file will be named eqspiflash.v.

20160725 Update: The controller now passes the obvious tests on the hardware at 200 MHz. More thorough testing is still needed, but it looks like this will work. Please feel free to pull the eqspiflash.v controller from the repository and test it yourself. Don't forget to set the number of dummy cycles to 8, via a write to the volatile configuration register.

20160811 Update: The hardware ended up creating an unpredictable delay. So I re-engineered the hardware connection (an example of which is found in the OpenArty project). The new connection, however, created a delay that the controller couldn't handle, so the controller, simulator, etc., have all been adjusted for the additional delays. The result works. I've read the default configuration from the Arty, erased it via this controller, programmed a new configuration into the controller, and then verified that it all works.

At this point, I'm going to declare the new controller working until a bug shows up to prove me wrong.