I was wondering whether the ESP8266 SPI flash could be used for atomic (or sort-of-atomic) write operations - so I wanted to understand how the underlying SPI operations work.
|API call||SPI command||Note|
||Repeated status reads (command
||Single status read, test completed immediately|
Read on to learn more...
Some of the different SPI flash chips found on ESP8266 modules
- The ESP-12F on my WeMos D1 Mini uses a Winbond W25Q32BV
- The SparkFun Thing uses an Adesto AT25SF041
- A black ESP-01 I brought on ebay uses a Berg Micro 25q80a
These different chips seem to have enough commands in common that they can be substituted for one another.
Dual and Quad SPI
Traditional SPI has 'MOSI' and 'MISO' lines, with data travelling in opposite directions. If you don't know what MOSI and MISO mean, go review that wikipedia article otherwise the rest of this explanation won't make much sense!
The flash chips listed above support 'Dual SPI' which basically means, instead of MOSI and MISO always going in opposite directions, certain commands cause both lines to temporarily be used in the same direction. Here's an example:
The first byte on MOSI is
0xBB in hex - what the Winbond datasheet calls "Fast Read Dual I/O".
When that command is received, MISO changes direction for 16 clock cycles, allowing the master to output 24 bits of address data in 12 clock cycles followed by 8 bits of control options, sent in 4 clock cycles. You read the bytes MISO-MOSI-MISO-MOSI- so the address above is
0x3FB000 and the control options are
After this, MISO and MOSI change direction - both lines are used by the slave to send data to the master. In the example above, the data returned by the slave is all ones, or
Quad SPI adds an extra two lines between master and slave, and allowing for the transfer of four bits per clock cycle. When the Arduino IDE offers the choice between flash modes 'DIO' and 'QIO' flash this is what you're choosing between (whether you'll have the choice depends on the board you have selected).
The practical speed benefits of Quad SPI over Dual SPI seem suprisingly modest, according to speed test results reported on esp8266.com which reports read times reducing by only 14% when changing from dual to quad SPI.
Programs execute from SPI flash
As you may know, user programs can be up to a megabyte, but there are only 64 kilobytes of instruction memory. This means quite a lot of data gets shuttled over the SPI bus under normal operation.
I haven't looked into the details, but I assume the ESP8266 does some sort of multitasking, so it can look after all the wifi maintainance stuff in the background while the user's program runs. Anyway, in my tests I found that even when I put a long delay in my program, the SPI bus wasn't always silent during that time.
How I tested the SPI behaviour
Initially, I tried to use a 1.5MHz bandwidth two-channel scope to record MOSI and MISO, inferring the clock transitions from changes in the data lines. This didn't work very well, because you can't capture a 40MHz bus with a 1.5MHz scope; and if you slow the bus down far enough that you can capture it your program will keep resetting with watchdog timer failures; and even if you ignore those you'll most likely get a recording of code being loaded, as there's a lot of that going on over the bus.
So I moved to a 20MHz bandwidth 8-channel scope. This allowed me to also capture clock, chip select, and a pin I configured to act as a trigger when my test started.
I used an ESP-12F on a breakout board that exposes all pins (including the SPI flash pins)
Here's the program I used:
spi_flash_erase_sector is called to erase sector
0x3FB, producing the command 20 3F B0 00 on MOSI then repeated status checks (MOSI 05 00, MISO FF 02 and
FF 03) until the status reports the operation is complete (MISO
spi_flash_read is called to read 32 bits from start of that sector - address
0x3FB000. The scope capture is shown in the 'Dual SPI' section, as the read is performed with Dual SPI;
BB 74 00 FF FF on MOSI and
00 7C 00 FF FF on MISO. A read of
0xFFFFFFFF is the expected result for NAND flash that has just been erased.
spi_flash_write is called to write 32 bits to the same address; this produced 02 3F B0 00 F0 F0 F0 F0 on MOSI and
00 00 00 00 00 00 00 00 on MOSI, indicating that dual SPI was not used. Look how many clock cycles there are compared to the write! A status check (MOSI 05 00 MISO 00 00) returned immediately.
Raw scope capture
Want to look at the data yourself in more detail? You can download it as a 15 megabyte, 2 million row CSV - but you won't be able to load it in Excel because it's too big.