I ended up going with littlefs, and after a bit of struggling managed to get a 6mb filesystem going on the qspi flash. It boots up and formats way faster than I expected, but I guess my flash was mostly blank.
// ##:::::::'####:'########:'########:'##:::::::'########:'########::'######::
// ##:::::::. ##::... ##..::... ##..:: ##::::::: ##.....:: ##.....::'##... ##:
// ##:::::::: ##::::: ##::::::: ##:::: ##::::::: ##::::::: ##::::::: ##:::..::
// ##:::::::: ##::::: ##::::::: ##:::: ##::::::: ######::: ######:::. ######::
// ##:::::::: ##::::: ##::::::: ##:::: ##::::::: ##...:::: ##...:::::..... ##:
// ##:::::::: ##::::: ##::::::: ##:::: ##::::::: ##::::::: ##:::::::'##::: ##:
// ########:'####:::: ##::::::: ##:::: ########: ########: ##:::::::. ######::
// ........::....:::::..::::::::..:::::........::........::..:::::::::......::
#include "main.h"
#include "littlefs/lfs.h"
// variables used by the filesystem
lfs_t lfs;
lfs_file_t file;
//*** LITTLE FS PROTOTYPES *******************************
int lfs_read(const struct lfs_config *c, lfs_block_t block,
lfs_off_t off, void *buffer, lfs_size_t size);
int lfs_prog(const struct lfs_config *c, lfs_block_t block,
lfs_off_t off, const void *buffer, lfs_size_t size);
int lfs_erase(const struct lfs_config *c, lfs_block_t block);
int lfs_sync(const struct lfs_config *c);
//*** DAISY SEED LITTLEFS SETTINGS *******************************
#define LFS_BLOCK_SIZE 4096 // One erase sector
#define LFS_BLOCK_COUNT (6 * 1024 * 1024 / LFS_BLOCK_SIZE) // 6MB total, leaving 2mb untouched
#define LFS_READ_SIZE 256 // Smallest flash read unit (could be smaller on Daisy)
#define LFS_PROG_SIZE 256 // Smallest flash write unit
#define LFS_CACHE_SIZE 256 // Cache one full read/write
#define LFS_LOOKAHEAD_SIZE 256 // Block allocation table
#define LFS_BLOCK_CYCLES 1000 // Default wear-leveling
const struct lfs_config cfg = {
.context = NULL, // Optionally store some sort of context handle here
.read = lfs_read,
.prog = lfs_prog,
.erase = lfs_erase,
.sync = lfs_sync,
.read_size = LFS_READ_SIZE,
.prog_size = LFS_PROG_SIZE,
.block_size = LFS_BLOCK_SIZE,
.block_count = LFS_BLOCK_COUNT,
.block_cycles = LFS_BLOCK_CYCLES,
.cache_size = LFS_CACHE_SIZE,
.lookahead_size = LFS_LOOKAHEAD_SIZE,
};
#define QSPI_BASE_ADDR 0x90000000 // Flash is memory mapped here
#define LFS_BASE_ADDR 0x200000 // leave first two megabytes untouched, no other offset
// needed for erase and write
#define MEM_MAP_LFS_BASE_ADDR LFS_BASE_ADDR + QSPI_BASE_ADDR // memory mapped location, needed for read
//******************************************
//******** READ **************************
//******************************************
int lfs_read(const struct lfs_config *c,
lfs_block_t block,
lfs_off_t off,
void *buffer,
lfs_size_t size)
{
// Sanity checks
if ((off + size) > c->block_size)
{
return -EINVAL; // Trying to read past block boundary
}
// Compute address for memory-mapped QSPI flash
uint8_t *flash_ptr = (uint8_t *)(MEM_MAP_LFS_BASE_ADDR + (block * c->block_size) + off);
dsy_dma_invalidate_cache_for_buffer(flash_ptr, size);
memcpy(buffer, flash_ptr, size);
return 0;
}
//******************************************
//******** WRITE *************************
//******************************************
int lfs_prog(const struct lfs_config *c,
lfs_block_t block,
lfs_off_t off,
const void *buffer,
lfs_size_t size)
{
uint32_t addr = LFS_BASE_ADDR + block * c->block_size + off; //LFS_BASE_ADDR and goes up (no mem map)
// Daisy Seed flash page write function (4k)
if (hw.qspi.WritePage(addr, size, (uint8_t *)buffer) != QSPIHandle::Result::OK)
{
return -EIO;
}
return 0;
}
//******************************************
//******** ERASE *************************
//******************************************
int lfs_erase(const struct lfs_config *c, lfs_block_t block)
{
uint32_t addr = LFS_BASE_ADDR + block * c->block_size; //LFS_BASE_ADDR and up (no mem map)
if (hw.qspi.EraseSector(addr) != QSPIHandle::Result::OK)
{
return -EIO;
}
return 0;
}
//******************************************
//******** SYNC **************************
//******************************************
int lfs_sync(const struct lfs_config *c)
{
// Wait for last write/erase to complete
// Not needed on Daisy Seed
return 0;
}
//******************************************
//******************************************
//********** TEST ************************
//******************************************
//******************************************
void init_littlefs()
{
hw.PrintLine("Mounting Filesystem");
// mount the filesystem
int err = lfs_mount(&lfs, &cfg);
// reformat if we can't mount the filesystem
// this should only happen on the first boot
if (err)
{
hw.PrintLine("Formatting Flash");
err = lfs_format(&lfs, &cfg);
err = lfs_mount(&lfs, &cfg);
hw.PrintLine("Format Complete");
}
// read current count
uint32_t boot_count = 0;
err = lfs_file_open(&lfs, &file, "boot_count", LFS_O_RDWR | LFS_O_CREAT);
err = lfs_file_read(&lfs, &file, &boot_count, sizeof(boot_count));
// update boot count
boot_count += 1;
lfs_file_rewind(&lfs, &file);
lfs_file_write(&lfs, &file, &boot_count, sizeof(boot_count));
// remember the storage is not updated until the file is closed successfully
lfs_file_close(&lfs, &file);
// release any resources we were using
hw.PrintLine("Unmounting");
lfs_unmount(&lfs);
// print the boot count
hw.PrintLine("boot_count: %d\n", boot_count);
}