Using the littlefs file system in RP2040's multicore mode

The program of the RP2040 is stored in the SPI Flash, and during runtime, the program in the Flash is randomly read into the internal cache for execution. This does not pose a problem when working in single-core mode. However, in multicore mode, if one core is executing the program while another core is writing to the Flash (for example, using the littlefs file system to manage the remaining space in the SPI Flash that does not store the program), it may cause simultaneous reading and writing of the Flash, leading to the program on one or both cores hanging.

In common scenarios, writing to the Flash only occurs on one core. For example, Core 0 (the default startup core) handles complex operations such as the user interface, while Core 1 is used as a digital signal processor, handling only signal chain-related tasks. In this case, the issue can be easily resolved with the following method.

#include "pico/multicore.h"
#include "hardware/irq.h"

volatile int Pause_core1=0;
volatile int Core1_paused=1; //set as "paused" initially (before core 1 starts)

//call on core1 to pause at Pause_core1
//must be called in a __time_critical_func
void __time_critical_func(core1_pause)(void){
    if (Pause_core1){
        int s=save_and_disable_interrupts();
        Core1_paused=1;
        while(Pause_core1){
            tight_loop_contents();
        }
        // Necessary re-init codes for resume processing here
        // .........
        // .........
        restore_interrupts_from_disabled(s);
        Core1_paused=0;
    }   
}

//call on core0 to stop core1
void core1_stop(void){
    Pause_core1=1;   //pause core1
    while (!Core1_paused){    //wait until core1 paused
        tight_loop_contents();
    }
}

//call on core0 to resume core1
void core1_resume(void){
    Pause_core1=0;   //resume core1
}

//core1_process() must be a __time_critical_func
//but it can call normal xip functions
void __time_critical_func(core1_process)(void){
    while(1){
	    // ... Signal processing codes ...
	    // ...............................
	    // ...............................
	    core1_pause();
    }
}

//call on core0 to launch core1_process
void core1_launch(void){
	Core1_paused=0;
    multicore_launch_core1(core1_process);
}

Before writing to the Flash, use core1_stop() to pause the operation of Core 1, and after writing to the Flash, use core1_resume() to resume the operation of Core 1. This allows for safe writing to the Flash. For example (modified from here):


static int pico_flash_prog(const struct lfs_config *c, 
    lfs_block_t block, lfs_off_t off, const void* buffer, lfs_size_t size) {
    LFS_ASSERT(block < c->block_count);
    // program with SDK
    core1_stop();    // stop core1 when writing to flash (read is OK)    
    uint32_t p = (uint32_t)FS_BASE + (block * c->block_size) + off;
    uint32_t ints = save_and_disable_interrupts();
    flash_range_program(p, buffer, size);
    restore_interrupts(ints);
    core1_resume();	// resume core1
    return LFS_ERR_OK;
}

static int pico_flash_erase(const struct lfs_config *c, lfs_block_t block) {
    LFS_ASSERT(block < c->block_count);
    // erase with SDK
    core1_stop();    // stop core1 when writing to flash (read is OK)    
    uint32_t p = (uint32_t)FS_BASE + block * c->block_size;
    uint32_t ints = save_and_disable_interrupts();
    flash_range_erase(p, c->block_size);
    restore_interrupts(ints);
    core1_resume();	//resume core1
    return LFS_ERR_OK;
}