Loading...
Searching...
No Matches
hardware_flash

Low level flash programming and erase API. More...

Functions

void flash_start_xip (void)
 Initialise QSPI interface and external QSPI devices for execute-in-place.
 
void flash_range_erase (uint32_t flash_offs, size_t count)
 Erase areas of flash.
 
void flash_range_program (uint32_t flash_offs, const uint8_t *data, size_t count)
 Program flash.
 
void flash_get_unique_id (uint8_t *id_out)
 Get flash unique 64 bit identifier.
 
void flash_do_cmd_cs (const uint8_t *txbuf, uint8_t *rxbuf, size_t count, uint cs)
 Execute bidirectional QSPI command.
 
static void flash_do_cmd (const uint8_t *txbuf, uint8_t *rxbuf, size_t count)
 Execute bidirectional flash command on chip select 0.
 

Detailed Description

Low level flash programming and erase API.

Note these functions are unsafe if you are using both cores, and the other is executing from flash concurrently with the operation. In this case, you must perform your own synchronisation to make sure that no XIP accesses take place during flash programming. One option is to use the lockout functions.

Likewise they are unsafe if you have interrupt handlers or an interrupt vector table in flash, so you must disable interrupts before calling in this case.

If PICO_NO_FLASH=1 is not defined (i.e. if the program is built to run from flash) then these functions will make a static copy of the second stage bootloader in SRAM, and use this to reenter execute-in-place mode after programming or erasing flash, so that they can safely be called from flash-resident code.

Example

#include <stdio.h>
#include <stdlib.h>
#include "pico/stdlib.h"
#include "pico/flash.h"
#include "hardware/flash.h"
// We're going to erase and reprogram a region 256k from the start of flash.
// Once done, we can access this at XIP_BASE + 256k.
#define FLASH_TARGET_OFFSET (256 * 1024)
const uint8_t *flash_target_contents = (const uint8_t *) (XIP_BASE + FLASH_TARGET_OFFSET);
void print_buf(const uint8_t *buf, size_t len) {
for (size_t i = 0; i < len; ++i) {
printf("%02x", buf[i]);
if (i % 16 == 15)
printf("\n");
else
printf(" ");
}
}
// This function will be called when it's safe to call flash_range_erase
static void call_flash_range_erase(void *param) {
uint32_t offset = (uint32_t)param;
flash_range_erase(offset, FLASH_SECTOR_SIZE);
}
// This function will be called when it's safe to call flash_range_program
static void call_flash_range_program(void *param) {
uint32_t offset = ((uintptr_t*)param)[0];
const uint8_t *data = (const uint8_t *)((uintptr_t*)param)[1];
flash_range_program(offset, data, FLASH_PAGE_SIZE);
}
int main() {
uint8_t random_data[FLASH_PAGE_SIZE];
for (uint i = 0; i < FLASH_PAGE_SIZE; ++i)
random_data[i] = rand() >> 16;
printf("Generated random data:\n");
print_buf(random_data, FLASH_PAGE_SIZE);
// Note that a whole number of sectors must be erased at a time.
printf("\nErasing target region...\n");
// Flash is "execute in place" and so will be in use when any code that is stored in flash runs, e.g. an interrupt handler
// or code running on a different core.
// Calling flash_range_erase or flash_range_program at the same time as flash is running code would cause a crash.
// flash_safe_execute disables interrupts and tries to cooperate with the other core to ensure flash is not in use
// See the documentation for flash_safe_execute and its assumptions and limitations
int rc = flash_safe_execute(call_flash_range_erase, (void*)FLASH_TARGET_OFFSET, UINT32_MAX);
hard_assert(rc == PICO_OK);
printf("Done. Read back target region:\n");
print_buf(flash_target_contents, FLASH_PAGE_SIZE);
printf("\nProgramming target region...\n");
uintptr_t params[] = { FLASH_TARGET_OFFSET, (uintptr_t)random_data};
rc = flash_safe_execute(call_flash_range_program, params, UINT32_MAX);
hard_assert(rc == PICO_OK);
printf("Done. Read back target region:\n");
print_buf(flash_target_contents, FLASH_PAGE_SIZE);
bool mismatch = false;
for (uint i = 0; i < FLASH_PAGE_SIZE; ++i) {
if (random_data[i] != flash_target_contents[i])
mismatch = true;
}
if (mismatch)
printf("Programming failed!\n");
else
printf("Programming successful!\n");
}
void __no_inline_not_in_flash_func() flash_range_program(uint32_t flash_offs, const uint8_t *data, size_t count)
Program flash.
Definition flash.c:242
void __no_inline_not_in_flash_func() flash_range_erase(uint32_t flash_offs, size_t count)
Erase areas of flash.
Definition flash.c:211
@ PICO_OK
No error; the operation succeeded.
Definition error.h:23
int flash_safe_execute(void(*func)(void *), void *param, uint32_t enter_exit_timeout_ms)
Execute a function with IRQs disabled and with the other core also not executing/reading flash.
Definition flash.c:75
bool stdio_init_all(void)
Initialize all of the present standard stdio types that are linked into the binary.
Definition stdio.c:207

Function Documentation

◆ flash_do_cmd()

static void flash_do_cmd ( const uint8_t *  txbuf,
uint8_t *  rxbuf,
size_t  count 
)
inlinestatic

Execute bidirectional flash command on chip select 0.

See flash_do_cmd_cs for more details.

Parameters
txbufPointer to a byte buffer which will be transmitted to the flash
rxbufPointer to a byte buffer where data received from the flash will be written. txbuf and rxbuf may be the same buffer.
countLength in bytes of txbuf and of rxbuf

◆ flash_do_cmd_cs()

void flash_do_cmd_cs ( const uint8_t *  txbuf,
uint8_t *  rxbuf,
size_t  count,
uint  cs 
)

Execute bidirectional QSPI command.

Low-level function to execute a serial command on a device attached to the QSPI interface. Bytes are simultaneously transmitted and received from txbuf and to rxbuf. Therefore, both buffers must be the same length, count, which is the length of the overall transaction. This is useful for reading metadata from the chip, such as device ID or SFDP parameters.

The XIP cache is flushed following each command, in case flash state has been modified. Like other hardware_flash functions, the flash is not accessible for execute-in-place transfers whilst the command is in progress, so entering a flash-resident interrupt handler or executing flash code on the second core concurrently will be fatal. To avoid these pitfalls it is recommended that this function only be used to extract flash metadata during startup, before the main application begins to run: see the implementation of pico_get_unique_id() for an example of this.

On RP2040 the chip select index is ignored, as there is only one chip select.

Parameters
txbufPointer to a byte buffer which will be transmitted
rxbufPointer to a byte buffer where received data will be written. txbuf and rxbuf may be the same buffer.
countLength in bytes of txbuf and of rxbuf
csChip select index

◆ flash_get_unique_id()

void flash_get_unique_id ( uint8_t *  id_out)

Get flash unique 64 bit identifier.

Use a standard 4Bh RUID instruction to retrieve the 64 bit unique identifier from a flash device attached to the QSPI interface. Since there is a 1:1 association between the MCU and this flash, this also serves as a unique identifier for the board.

Parameters
id_outPointer to an 8-byte buffer to which the ID will be written

◆ flash_range_erase()

void flash_range_erase ( uint32_t  flash_offs,
size_t  count 
)

Erase areas of flash.

Parameters
flash_offsOffset into flash, in bytes, to start the erase. Must be aligned to a 4096-byte flash sector.
countNumber of bytes to be erased. Must be a multiple of 4096 bytes (one sector).
Note
Erasing a flash sector sets all the bits in all the pages in that sector to one. You can then "program" flash pages in the sector to turn some of the bits to zero. Once a bit is set to zero it can only be changed back to one by erasing the whole sector again.

◆ flash_range_program()

void flash_range_program ( uint32_t  flash_offs,
const uint8_t *  data,
size_t  count 
)

Program flash.

Parameters
flash_offsFlash address of the first byte to be programmed. Must be aligned to a 256-byte flash page.
dataPointer to the data to program into flash
countNumber of bytes to program. Must be a multiple of 256 bytes (one page).
Note
: Programming a flash page effectively changes some of the bits from one to zero. The only way to change a zero bit back to one is to "erase" the whole sector that the page resides in. So you may need to make sure you have called flash_range_erase before calling flash_range_program.

◆ flash_start_xip()

void flash_start_xip ( void  )

Initialise QSPI interface and external QSPI devices for execute-in-place.

This function performs the same first-time flash setup that would normally occur over the course of the bootrom locating a flash binary and booting it, and that flash binary executing the SDK crt0. Specifically:

  • Initialise QSPI pads to their default states, and (non-RP2040) disable pad isolation latches
  • Issue a hardcoded sequence to attached QSPI devices to return them to a serial command state
  • Flush the XIP cache
  • Configure the QSPI interface for low-speed 03h reads
  • If this is not a PICO_NO_FLASH=1 binary:
    • (RP2040) load a boot2 stage from the first 256 bytes of RAM and execute it
    • (non-RP2040) execute an XIP setup function stored in boot RAM by either the bootrom or by crt0

This is mostly useful for initialising flash on a PICO_NO_FLASH=1 binary. (In spite of the name, this binary type really means "preloaded to RAM" and there may still be a flash device.)

This function does not preserve the QSPI interface state or pad state. This is in contrast to most other functions in this library, which preserve at least the QSPI pad state. However, on RP2350 it does preserve the QMI window 1 configuration if you have not opted into bootrom CS1 support via FLASH_DEVINFO.