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.
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:
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.