Using the SPI Flash
The C0-microSD+ carries 16 MiB of SPI flash. The module boots from it and stores your device application in it, and the space left over is yours to use as non-volatile storage. Data that you write there survives a power cycle.
This page covers using that storage from your device application. For the rules that govern every write to the flash, including the erase behavior that makes flash different from memory, see SPI Flash Write and Erase Semantics.
Reaching the flash from your application
The flash is memory-mapped, so your device application reads and writes it with ordinary loads and stores. There is no driver to install, and the Hardware Abstraction Layer has no flash API.
Store your data in the user data region, which runs from 0x00200000 to 0x00FFFFFC. The
bitstream region below it holds the image that the module boots from, and the module locks
that region against writes. Your application cannot lift that lock. The hardware forces the
unlock key to 0x00000000 whenever the core is running, so the region is unconditionally
locked for as long as your application executes.
Your application maintains access to the flash for as long as it runs. The host does not. While the core is running, the SD interface cannot access the SPI flash or the flash OTP window, so a host that wants to read or write the flash has to stop the core first. See SD access while the core runs.
Choosing where to store data
Your device application binary sits at the start of the user data region, at 0x00200000.
Nothing in the module protects it, and every write erases a whole 4 KiB sector, so a write
placed too close to the binary corrupts it.
Place your data well above the binary. The last megabyte of the user data region, from
0x00F00000, is a safe home for it and leaves the binary room to grow.
An xip build runs directly from the user data region rather than from the LRAM. A device
application in an xip build that writes over its own image erases the code that it is
executing. Keep every write well clear of the binary.
Reading data back
The core caches what it reads from the flash, and a write can change far more of the flash than the bytes it names. A write that starts on a 4 KiB-aligned address erases that whole sector before it writes, and a write long enough to cross into the next sector erases that sector also. Every cached line from an erased sector is stale afterwards, including lines holding bytes your application never touched, so a later read can return data the flash no longer holds. See SPI Flash Write and Erase Semantics.
Invalidate the sector after your application writes to it, and before it reads any part of
that sector back. cacheInvalidateRange() drops every cache line covering the range.
#include "cache.h"
/*
* The write erased the whole sector, so drop every line covering it
* before reading any part of it back.
*/
cacheInvalidateRange((const void *) kSectorAddress, kSectorSizeBytes);
value = *(volatile uint32_t *) kSectorAddress;
This applies to writes your own application makes. A host cannot change the flash while your application is running, so there is no case where a sector goes stale from a host write.
A dmaTryRun() transfer invalidates the range it wrote. When the destination is in the flash,
the erase still covers the whole surrounding sector. You need to programmatically invalidate the rest of that sector.
See
Using the DMA Engine and the Cache.
Surviving power loss
A flash write that loses power part way through leaves that sector holding neither its old contents nor its new contents. Write your data in a form that you can check, for example by storing a checksum next to it, and keep the previous copy in a different sector until the new copy verifies. If your device application reads back a sector that it cannot validate, fall back to the previous copy rather than trusting a partial write.
Next steps
- Host Interface and Protocol, the write and erase rules that govern every access to the flash.
- Using the Hardware Abstraction Layer, the C library that your device application uses to reach the registers and buffers.
- Using the DMA Engine and the Cache, the cache-invalidation functions this page calls, and the DMA engine that moves bulk data.
- Exchanging Data With the C0-microSD+, moving distributional data between the host and the module.