Flash
module reference#include <flash.h>Makefile :
flash
is already included by default
This module gives access to the non-volatile Flash memory embedded in the microcontroller, where the static data and the code are stored.
The total size of Flash memory is device-dependant (from 128kB to 512kB) but the array is always divided in pages of 512 bytes. On the 256kB model used by Carbide, this means 512 pages in total. The granularity when writing or erasing data in the Flash is one page : even when changing a single byte, the whole page has to be erased and written back.
The Flash memory is a special kind of non-volatile memory that only allows the bit to be changed from 1 to 0. By default, all the bits are set to 1 (the array is filled with 0xFF bytes) and data can be written by changing some of them to 0. In order to revert the state of the bits and write another value, the whole page must be reset.
The Flash memory is accessed at address 0x00000000
in the global memory map. Reading data from Flash is done by using the simple read()
and readPage()
functions, which will directly access the array. However, because of the reasons detailed above, writing data is a bit more complex. The data is not written directly in the array but goes into a dedicated Page Buffer, which can later be copied into the main array. All of this is done automatically by the writePage()
function.
To sum up, here are the two most common scenarii :
read()
uint32_t address = 0x4242; uint32_t buffer[Flash::FLASH_PAGE_SIZE_WORDS]; Flash::readPage(address, buffer); // Modify buffer... Flash::writePage(address, buffer);If the data you want to write spans over multiple pages, you have to repeat this process for each page.
Beside the main Flash array, there is an additional page, called the User Page, that can be used to store any data, such as a serial number or configuration data. This page is not erased when the rest of the array is erased, which is convenient when prototyping. The user page is accessed using a dedicated set of functions : readUserPage()
, eraseUserPage()
and writeUserPage()
. The first 2 words (8 bytes) of this page are reserved for the configuration of some other modules and should not be overwritten (see the datasheet §14.11.2 First Word of the User Page and 14.11.3 Second Word of the User Page; be careful as the "second word" is actually at offset 0 and the "first word" is at offset 4). The recommended procedure to write the User Page is to perform a Read-Erase-Write sequence without modifying the first 2 words.
The Flash also offers another memory space outside the main array in the form of a 32-bit register used to store fuses. A fuse is simply a non-volatile bit that can be set or reset at will to store an information. Out of these 32 fuses, 16 are used by the Flash controller to lock some parts of the array and prevent modifications, while the other 16 are called General-Purpose Fuses and can be freely used by your program. However, keep in mind that some of these fuses are already used by the library, especially the bootloader, and should not be overwritten. Take a look at the BOOTLOADER_N_RESERVED_FUSES
constant for more information.
Accessing the Flash memory array from your program can be extremely useful in certain applications, but remember that this memory is slower and more fragile than the RAM. Even when running at the full 48MHz, programming or erasing a page takes around 5ms. The endurance ratings indicate only 100 000 page write minimum (see §42.8 Flash Characteristics in the datasheet), which can be reached quickly if your program constantly writes into the array. Also, make sure not to overwrite any useful data when writing pages, such as your code (this would lead to really strange behaviour).
Keep in mind that this memory is primarily designed to keep your program code and static data (such as default values), so if your program needs to retain large amounts of data even when the power is lost (for example, if you are building a data logger), consider using a dedicated external EEPROM or Flash memory with higher capacity and better endurance ratings. And in any case, try to design caching algorithms adapted to your specific application and access pattern in order to minimise write cycles (e.g. for a data logger, don't write each sensor value as it comes, fill up a buffer in RAM and write a whole page of data at once).
true
if the memory controller is currently executing an operation, such as a page write or page erase.data[]
must be at least FLASH_PAGE_SIZE_WORDS
long.0xFF
.writePage()
.data[]
must be at least FLASH_PAGE_SIZE_WORDS
long.data[]
must be at least FLASH_PAGE_SIZE_WORDS
long.0xFF
.data[]
must be at least FLASH_PAGE_SIZE_WORDS
long. As explained in the module description, be careful with the first word of this page as it contains configuration data for the microcontroller.fuse
must be between 0 and 47, however, some fuses might already be used by the bootloader : to be safe, the fuses that you should consider available start after BOOTLOADER_N_RESERVED_FUSES
.The Flash memory controller is quite complex and there are a few advanced features that are not implemented:
#ifndef _FLASH_H_ #define _FLASH_H_ #include <stdint.h> // This module gives access to the Flash memory embedded in the chip namespace Flash { // Config registers memory space base address and flash memory base address const uint32_t FLASH_BASE = 0x400A0000; const uint32_t FLASH_ARRAY_BASE = 0x00000000; const uint32_t USER_PAGE_BASE = FLASH_ARRAY_BASE + 0x00800000; const int FLASH_PAGE_SIZE_BYTES = 512; // bytes const int FLASH_PAGE_SIZE_WORDS = 128; // words // N_FLASH_PAGES is a preprocessor word defined in the library Makefile which depend on CHIP_MODEL const int FLASH_PAGES = N_FLASH_PAGES; // pages // Register offsets const uint32_t OFFSET_FCR = 0x00; // Flash Control Register const uint32_t OFFSET_FCMD = 0x04; // Flash Command Register const uint32_t OFFSET_FSR = 0x08; // Flash Status Register const uint32_t OFFSET_FPR = 0x0C; // Flash Parameter Register const uint32_t OFFSET_FVR = 0x10; // Flash Version Register const uint32_t OFFSET_FGPFRHI = 0x14; // Flash General Purpose Fuse Register Hi -- not implemented const uint32_t OFFSET_FGPFRLO = 0x18; // Flash General Purpose Fuse Register Lo const uint32_t OFFSET_CTRL = 0x408; // PicoCache Control Register const uint32_t OFFSET_SR = 0x40C; // PicoCache Status Register const uint32_t OFFSET_MAINT0 = 0x420; // PicoCache Maintenance Register 0 const uint32_t OFFSET_MAINT1 = 0x424; // PicoCache Maintenance Register 1 const uint32_t OFFSET_MCFG = 0x428; // PicoCache Monitor Configuration Register const uint32_t OFFSET_MEN = 0x42C; // PicoCache Monitor Enable Register const uint32_t OFFSET_MCTRL = 0x430; // PicoCache Monitor Control Register const uint32_t OFFSET_MSR = 0x434; // PicoCache Monitor Status Register const uint32_t OFFSET_PVR = 0x4FC; // Version Register // Constants const uint32_t FSR_FRDY = 0; const uint32_t FSR_HSMODE = 6; const uint32_t FCMD_CMD = 0; const uint32_t FCMD_CMD_NOP = 0; const uint32_t FCMD_CMD_WP = 1; const uint32_t FCMD_CMD_EP = 2; const uint32_t FCMD_CMD_CPB = 3; const uint32_t FCMD_CMD_LP = 4; const uint32_t FCMD_CMD_UP = 5; const uint32_t FCMD_CMD_EA = 6; const uint32_t FCMD_CMD_WGPB = 7; const uint32_t FCMD_CMD_EGPB = 8; const uint32_t FCMD_CMD_SSB = 9; const uint32_t FCMD_CMD_PGPFB = 10; const uint32_t FCMD_CMD_EAGPF = 11; const uint32_t FCMD_CMD_QPR = 12; const uint32_t FCMD_CMD_WUP = 13; const uint32_t FCMD_CMD_EUP = 14; const uint32_t FCMD_CMD_QPRUP = 15; const uint32_t FCMD_CMD_HSEN = 16; const uint32_t FCMD_CMD_HSDIS = 17; const uint32_t FCMD_PAGEN = 8; const uint32_t FCMD_KEY = 0xA5 << 24; // General-purpose fuses using Fuse = uint8_t; const int N_FUSES = 16; // These fuses are used by the bootloader and are reserved if the bootloader is enabled // They need to be defined here for the Core::resetToBootloader() helper function const Fuse FUSE_BOOTLOADER_FW_READY = 0; const Fuse FUSE_BOOTLOADER_FORCE = 1; const Fuse FUSE_BOOTLOADER_SKIP_TIMEOUT = 2; const int BOOTLOADER_N_RESERVED_FUSES = 3; // Module API bool isReady(); uint32_t read(uint32_t address); void readPage(int page, uint32_t data[]); void erasePage(int page); void clearPageBuffer(); void writePage(int page, const uint32_t data[]); void readUserPage(uint32_t data[]); void eraseUserPage(); void writeUserPage(const uint32_t data[]); void writeFuse(Fuse fuse, bool state); bool getFuse(Fuse fuse); void enableHighSpeedMode(); void disableHighSpeedMode(); } #endif
#include "flash.h" namespace Flash { bool isReady() { return (*(volatile uint32_t*)(FLASH_BASE + OFFSET_FSR)) & (1 << FSR_FRDY); } uint32_t read(uint32_t address) { // Wait for the flash to be ready while (!isReady()); // Return the word at the specified address return (*(volatile uint32_t*)(FLASH_ARRAY_BASE + address)); } void readPage(int page, uint32_t data[]) { // Wait for the flash to be ready while (!isReady()); // Copy the buffer to the page buffer for (int i = 0; i < FLASH_PAGE_SIZE_WORDS; i++) { data[i] = (*(volatile uint32_t*)(FLASH_ARRAY_BASE + page * FLASH_PAGE_SIZE_BYTES + i * 4)); } } void erasePage(int page) { // Wait for the flash to be ready while (!isReady()); // FCMD (Flash Command Register) : issue an Erase Page command (*(volatile uint32_t*)(FLASH_BASE + OFFSET_FCMD)) = FCMD_CMD_EP << FCMD_CMD // CMD : command code to issue (EP = Erase Page) | page << FCMD_PAGEN // PAGEN : page number | FCMD_KEY; // KEY : write protection key } void clearPageBuffer() { // Wait for the flash to be ready while (!isReady()); // FCMD (Flash Command Register) : issue a Clear Page Buffer command (*(volatile uint32_t*)(FLASH_BASE + OFFSET_FCMD)) = FCMD_CMD_CPB << FCMD_CMD // CMD : command code to issue (CPB = Clear Page Buffer) | FCMD_KEY; // KEY : write protection key } void writePage(int page, const uint32_t data[]) { // The flash technology only allows 1-to-0 transitions, so the // page and the buffer must first be cleared (set to 1) erasePage(page); clearPageBuffer(); // Wait for the flash to be ready while (!isReady()); // Copy the buffer to the page buffer for (int i = 0; i < FLASH_PAGE_SIZE_WORDS; i++) { (*(volatile uint32_t*)(FLASH_ARRAY_BASE + page * FLASH_PAGE_SIZE_BYTES + i * 4)) = data[i]; } // FCMD (Flash Command Register) : issue a Write Page command (*(volatile uint32_t*)(FLASH_BASE + OFFSET_FCMD)) = FCMD_CMD_WP << FCMD_CMD // CMD : command code to issue (WP = Write Page) | page << FCMD_PAGEN // PAGEN : page number | FCMD_KEY; // KEY : write protection key } void readUserPage(uint32_t data[]) { // Wait for the flash to be ready while (!isReady()); // Copy the buffer to the page buffer for (int i = 0; i < FLASH_PAGE_SIZE_WORDS; i++) { data[i] = (*(volatile uint32_t*)(USER_PAGE_BASE + i * 4)); } } void eraseUserPage() { // Wait for the flash to be ready while (!isReady()); // FCMD (Flash Command Register) : issue an Erase User Page command (*(volatile uint32_t*)(FLASH_BASE + OFFSET_FCMD)) = FCMD_CMD_EUP << FCMD_CMD // CMD : command code to issue (EUP = Erase User Page) | FCMD_KEY; // KEY : write protection key } void writeUserPage(const uint32_t data[]) { // The flash technology only allows 1-to-0 transitions, so the // page and the buffer must first be cleared (set to 1) eraseUserPage(); clearPageBuffer(); // Wait for the flash to be ready while (!isReady()); // Copy the buffer to the page buffer for (int i = 0; i < FLASH_PAGE_SIZE_WORDS; i++) { (*(volatile uint32_t*)(USER_PAGE_BASE + i * 4)) = data[i]; } // FCMD (Flash Command Register) : issue a Write User Page command (*(volatile uint32_t*)(FLASH_BASE + OFFSET_FCMD)) = FCMD_CMD_WUP << FCMD_CMD // CMD : command code to issue (WUP = Write User Page) | FCMD_KEY; // KEY : write protection key } void writeFuse(Fuse fuse, bool state) { // Wait for the flash to be ready while (!isReady()); // Check the fuse number if (fuse > N_FUSES) { return; } // The usable fuses are actually between 16 and 31, because the first 16 bits are lock bits fuse += 16; // FCMD (Flash Command Register) : issue a Write User Page command (*(volatile uint32_t*)(FLASH_BASE + OFFSET_FCMD)) = (state ? FCMD_CMD_WGPB : FCMD_CMD_EGPB) << FCMD_CMD // CMD : command code to issue (WGPB = Write General Purpose Bit) | fuse << FCMD_PAGEN // PAGEN : fuse number | FCMD_KEY; // KEY : write protection key } bool getFuse(Fuse fuse) { // Wait for the flash to be ready while (!isReady()); // Check the fuse number if (fuse > N_FUSES) { return false; } // The usable fuses are actually between 16 and 31, because the first 16 bits are lock bits fuse += 16; bool high = false; if (fuse >= 32) { high = true; fuse -= 32; } return !(((*(volatile uint32_t*)(FLASH_BASE + (high ? OFFSET_FGPFRHI : OFFSET_FGPFRLO))) >> fuse) & 0x01); } void enableHighSpeedMode() { // FCMD (Flash Command Register) : issue a High Speed Enable command (*(volatile uint32_t*)(FLASH_BASE + OFFSET_FCMD)) = FCMD_CMD_HSEN << FCMD_CMD // CMD : command code to issue (HSEN = High Speed Enable) | FCMD_KEY; // KEY : write protection key // Wait untile HS mode is enabled while (!((*(volatile uint32_t*)(FLASH_BASE + OFFSET_FSR)) & (1 << FSR_HSMODE))); } void disableHighSpeedMode() { // FCMD (Flash Command Register) : issue a High Speed Disable command (*(volatile uint32_t*)(FLASH_BASE + OFFSET_FCMD)) = FCMD_CMD_HSDIS << FCMD_CMD // CMD : command code to issue (HSEN = High Speed Disable) | FCMD_KEY; // KEY : write protection key } }