Slow write speeds to RP2040 flash using LittleFileSystem/LittleFS

I have a Sparkfun Things Plus RP2040 with 16MB of onboard flash. I am trying to write a program which uses the flash as a buffer (let's say to store 5MB of data) before writing to an SD card. The idea was that using the flash memory would be less power hungry than constantly writing to the SD card.

I'm programming the RP2040 using Arduino IDE which runs MBedOS on the board. The problem I am having is that the write speeds are very slow. It takes ~3000ms to write 200kB of binary data. Ideally writing 200kB should take 1/10th of the time (300ms) for the application we have in mind. I have tested using LittleFileSystem which is included in MBedOS and Little_FS_MBed which is a similar Arduino library. I also tested on another board (Arduino Nano RP2040) with similar results.

My question is whether this is a fundamental limitation of the hardware or whether is there some other way to speed up writing data to flash memory?

The sketch used to test is below (an example adapted from here).

/**
*
* Test writing to the internal flash of the pico. 
**/

#include "LittleFileSystem.h"

#include "mbed.h"
#include <stdio.h>
#include <errno.h>
#include <functional>

#include "BlockDevice.h"

// Maximum number of elements in buffer
#define BUFFER_MAX_LEN 10
#define FORCE_REFORMAT true
// This will take the system's default block device
mbed::BlockDevice *bd = mbed::BlockDevice::get_default_instance();
// For example: HeapBlockDevice with size of 2048 bytes, read size 1, write size 1 and erase size 512.
//mbed::BlockDevice *bd = new mbed::HeapBlockDevice(4096, 1, 1, 512);

#define count 10000

//write 
uint16_t data[count];

void setup() {
  Serial.begin(9600);
  delay(1000);
  Serial.println("Inialise litte file system"); 

    // initialize digital pin LED_BUILTIN as an output.
  pinMode(LED_BUILTIN, OUTPUT);

  //mbed::LittleFileSystem fs("fs", bd, 2, 1024, 4096, 64);
  mbed::LittleFileSystem fs("fs");

  /*** Format the filesystem ***/

  Serial.println("Mounting the filesystem... ");
  //fflush(stdout);
  int err=0;
  //err = fs.mount(bd);

  Serial.println(bd->get_read_size()); 
  Serial.println(bd->get_erase_size()); 
  Serial.println(bd->get_program_size()); 

  Serial.println((err ? "Fail :(" : "OK"));

     if (err || FORCE_REFORMAT) {
        // Reformat if we can't mount the filesystem
        Serial.println("formatting... ");
        //fflush(stdout);
        err = fs.reformat(bd);
        Serial.println((err ? "Fail :(" : "OK"));
        if (err) {
            Serial.println(strerror(-err));
        }
    }


    /**** Write a file *****/

     // Open the numbers file
    Serial.println("Opening \"/fs/numbers.bin\"... ");
    fflush(stdout);
    FILE *f = fopen("/fs/numbers.bin", "rb+");
     Serial.println((!f ? "Fail :(" : "OK"));
    if (!f) {
        // Create the numbers file if it doesn't exist
        Serial.println("No file found, creating a new file... ");
        fflush(stdout);
        f = fopen("/fs/numbers.bin", "wb+");
        Serial.println((!f ? "Fail :(" : "OK"));
        if (!f) {
            Serial.println(strerror(-err));
        }
    }


    // for (int i = 0; i < 500; i++) {
    //         Serial.print("Writing numbers ");
    //         Serial.println(i);

    //         fflush(stdout);
    //         err = fprintf(f, "    %d\n", i);
    //         if (err < 0) {
    //             printf("Fail :(\n");
    //             Serial.println(strerror(-err));
    //         }
    // }

    //write some data to a file      
    unsigned long time1 = millis();
    for (int i = 0; i < 10; i++) {
      fwrite (data , sizeof(uint16_t), count, f);
    }
    unsigned long totalTime = millis()-time1;

    Serial.print("Time to write: ");
    Serial.println(totalTime); // prints the time it took to write to the SD card


    //test seeking file
    Serial.println("Seeking file... ");
    fflush(stdout);
    err = fseek(f, 0, SEEK_SET);
    Serial.println((err < 0 ? "Fail :(" : "OK"));
    if (err < 0) {
          Serial.println(strerror(-err));
    }


    
    /**** Determine the size of the file ***/
    Serial.println("Calculating the size of the file... ");
    fflush(stdout);
    err = fseek(f, 0L, SEEK_END);
    Serial.println((err < 0 ? "Fail :(" : "OK"));
    if (err < 0) {
          Serial.println(strerror(-err));
    }
    int sz = ftell(f);
    Serial.print("Size is: ");
    Serial.println(sz);


    // Close the file which also flushes any cached writes
    Serial.println("Closing \"/fs/numbers1.txt\"... ");
    fflush(stdout);
    err = fclose(f);
    Serial.println((err < 0 ? "Fail :(" : "OK"));
    if (err < 0) {
            Serial.println(strerror(-err));
    }


    /*** Read what is in the root directory ****/

        // Display the root directory
    Serial.println("Opening the root directory... ");
    fflush(stdout);
    DIR *d = opendir("/fs/");
    Serial.println((!d ? "Fail :(" : "OK"));
    if (!d) {
        Serial.println(strerror(errno));
    }

    Serial.println("root directory:");
    while (true) {
        struct dirent *e = readdir(d);
        if (!e) {
            break;
        }

        Serial.println(e->d_name);
    }



       // Tidy up
    Serial.println("Unmounting... ");
    //fflush(stdout);
    err = fs.unmount();
    Serial.println((err < 0 ? "Fail :(" : "OK"));
    if (err < 0) {
            Serial.println(strerror(-err));
    }
        
}

void loop() {
  // put your main code here, to run repeatedly:
  digitalWrite(LED_BUILTIN, HIGH);   // turn the LED on (HIGH is the voltage level)
  delay(500);                       // wait for a second
  digitalWrite(LED_BUILTIN, LOW);    // turn the LED off by making the voltage LOW
  delay(500);        

}

The output is...

Inialise litte file system
Mounting the filesystem...
OK
formatting...
OK
Opening "/fs/numbers.bin"...
Fail
No file found, creating a new file...
OK
Time to write: 3534 <-milliseconds
Seeking file...
OK
Calculating the size of the file...
OK
Size is: 200000
Closing "/fs/numbers1.txt"...

what's the purpose ?

1 Like

Good point - that was redundant code I was using to test - I have amended the code in the original post.

Any performance increase? also you need to flush (or close) the file before you measure the time as a buffer could still be in memory.

1 Like

Thanks, but no performance increase with or without flush. I wonder whether the flash memory is just fundamentally slow to write to?

it is not fast for sure, block deletion, directory-level garbage collection, .... it's costly.

1 Like

So is the LittlefileSystem approach shown above basically correct and this is just a fundamental limitation of the flash memory on RP2040 (and other) devices?

I've not played with the RP2040 but yes, I think that in general writing the flash is not a super fast process. The SD card will also have its own challenges and possible garbage collection to prevent wear-out

1 Like

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.