Skip to content

Commit

Permalink
Flashfs rolling erase (rotorflight#211)
Browse files Browse the repository at this point in the history
A new option `blackbox_rolling_erase` is added to enable
automatic rolling erase when flashfs volume is full.
  • Loading branch information
gongtao0607 committed Jan 18, 2025
1 parent 9ba29c1 commit c6dde11
Show file tree
Hide file tree
Showing 9 changed files with 176 additions and 16 deletions.
2 changes: 2 additions & 0 deletions src/main/cli/cli.c
Original file line number Diff line number Diff line change
Expand Up @@ -2682,10 +2682,12 @@ static void cliFlashInfo(const char *cmdName, char *cmdline)
FLASH_PARTITION_SECTOR_COUNT(flashPartition) * layout->sectorSize,
flashfsGetOffset()
);
#ifdef USE_FLASHFS_LOOP
cliPrintLinef("FlashFSLoop Head = 0x%08x, Tail = 0x%08x",
flashfsGetHeadAddress(),
flashfsGetTailAddress());
#endif
#endif
}


Expand Down
1 change: 1 addition & 0 deletions src/main/cli/settings.c
Original file line number Diff line number Diff line change
Expand Up @@ -805,6 +805,7 @@ const clivalue_t valueTable[] = {

#ifdef USE_FLASHFS_LOOP
{ "blackbox_initial_erase_kb", VAR_UINT16 | MASTER_VALUE, .config.minmaxUnsigned = { 0, UINT16_MAX }, PG_BLACKBOX_CONFIG, offsetof(blackboxConfig_t, initialEraseFreeSpaceKiB) },
{ "blackbox_rolling_erase", VAR_UINT8 | MASTER_VALUE | MODE_LOOKUP, .config.lookup = { TABLE_OFF_ON }, PG_BLACKBOX_CONFIG, offsetof(blackboxConfig_t, rollingErase) },
#endif
#endif

Expand Down
88 changes: 87 additions & 1 deletion src/main/io/flashfs.c
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,21 @@

#include "pg/blackbox.h"

/*
* How rolling erase works:
* 1. When start programming (flush) a page, if erase is needed and can be
* started, set FLASHFS_ROLLING_ERASE_PENDING.
* 2. No more flush can be done when FLASHFS_ROLLING_ERASE_PENDING.
* 3. When page program finishes, EraseAsync task picks up the erase task and
* sets FLASHFS_ROLLING_ERASING.
* 4. When erase finishes, EraseAsync task resets state to FLASHFS_IDLE.
*/
typedef enum {
FLASHFS_IDLE,
FLASHFS_ALL_ERASING,
FLASHFS_INITIAL_ERASING,
FLASHFS_ROLLING_ERASE_PENDING,
FLASHFS_ROLLING_ERASING,
} flashfsState_e;

static const flashPartition_t *flashPartition = NULL;
Expand Down Expand Up @@ -268,24 +279,78 @@ static uint32_t flashfsWriteBuffers(uint8_t const **buffers, uint32_t *bufferSiz

// It's OK to overwrite the buffer addresses/lengths being passed in

// If sync is true, block until the FLASH device is ready, otherwise return 0 if the device isn't ready
// If sync is true, block until the FLASH device is ready, otherwise return
// 0 if the device isn't ready
if (sync) {
while (!flashIsReady());
/*
* Wait for any flashfs erase to complete.
* Note: we shouldn't reach inside since there's no such real world
* scenario (sync = 1 and flashfsState != FLASHFS_IDLE).
*/
while (flashfsState != FLASHFS_IDLE) {
flashfsEraseAsync();
}
} else {
if (!flashIsReady()) {
return 0;
}
/*
* Also bail out if we are running any of the erases.
* There are a few cases:
* * FLASHFS_INITIAL_ERASING: logging is running ("switch" mode) and
* initial erase has triggered or running. We can't write.
* * FLASHFS_ROLLING_ERASE_PENDING: rolling erase is pending. We
* can't write. (If we write, those bytes will be discarded on
* erase).
* * FLASHFS_ROLLING_ERASING: technically we can write (under this
* state and flashIsReady()), because rolling erase erases only 1
* sector. For simplicity, we wait for the task to update the state.
* * FLASHFS_ALL_ERASING: we can't write. We may land between erase
* sectors.
*/
if (flashfsState != FLASHFS_IDLE) {
return 0;
}
}

// Are we at EOF already? Abort.
if (flashfsIsEOF()) {
#ifdef USE_FLASHFS_LOOP
// If EOF, request an rolling erase unconditionally
if (blackboxConfig()->rollingErase) {
flashfsState = FLASHFS_ROLLING_ERASE_PENDING;
}
#endif
return 0;
}

#ifdef CHECK_FLASH
checkFlashPtr = tailAddress;
#endif

#ifdef USE_FLASHFS_LOOP
// Check if rolling erase is needed. Why here? Because we can only
// erase when a page (aligned) program is completed.
const uint32_t freeSpace = flashfsSize - flashfsGetOffset();
if (blackboxConfig()->rollingErase &&
freeSpace < flashGeometry->sectorSize) {
// See if we are finishing a page.
uint32_t bytes_to_write = 0;
if (bufferCount > 0)
bytes_to_write += bufferSizes[0];
if (bufferCount > 1)
bytes_to_write += bufferSizes[1];
if (tailAddress / flashGeometry->pageSize !=
(tailAddress + bytes_to_write) / flashGeometry->pageSize) {
// We will write to or write over a page boundary. We can erase when
// this write is done.
flashfsState = FLASHFS_ROLLING_ERASE_PENDING;
}
}

#endif

flashPageProgramBegin(tailAddress, flashfsWriteCallback);

/* Mark that data has yet to be written. There is no race condition as the DMA engine is known
Expand Down Expand Up @@ -448,17 +513,36 @@ void flashfsEraseAsync(void)
if (initialEraseSectors > 0) {
flashEraseSector(headAddress);
initialEraseSectors--;
// We immediately set the head before erase is completed.
// This should be fine since write will be blocked until this
// state is cleared.
headAddress =
flashfsAddressShift(headAddress, flashGeometry->sectorSize);
LED1_TOGGLE;
} else {
flashfsState = FLASHFS_IDLE;
LED1_OFF;
}
} else if (flashfsState == FLASHFS_ROLLING_ERASE_PENDING) {
flashEraseSector(headAddress);
headAddress =
flashfsAddressShift(headAddress, flashGeometry->sectorSize);
flashfsState = FLASHFS_ROLLING_ERASING;
LED1_TOGGLE;
} else if (flashfsState == FLASHFS_ROLLING_ERASING) {
flashfsState = FLASHFS_IDLE;
LED1_OFF;
}
}
}

void flashfsSeekAbs(uint32_t offset)
{
flashfsFlushSync();

flashfsSetTailAddress(flashfsAddressShift(headAddress, offset));
}

void flashfsSeekPhysical(uint32_t offset)
{
flashfsFlushSync();
Expand Down Expand Up @@ -706,6 +790,8 @@ void flashfsInit(void)

// Start the file pointer off at the beginning of free space so caller can start writing immediately
flashfsSeekPhysical(flashfsIdentifyStartOfFreeSpace());

flashfsState = FLASHFS_IDLE;
}

#ifdef USE_FLASH_TOOLS
Expand Down
2 changes: 1 addition & 1 deletion src/main/io/flashfs.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ bool flashfsVerifyEntireFlash(void);

#ifdef USE_FLASHFS_LOOP
void flashfsLoopInitialErase();
#endif

uint32_t flashfsGetHeadAddress();
uint32_t flashfsGetTailAddress();
#endif
8 changes: 5 additions & 3 deletions src/main/msp/msp.c
Original file line number Diff line number Diff line change
Expand Up @@ -1708,13 +1708,15 @@ static bool mspProcessOutCommand(int16_t cmdMSP, sbuf_t *dst)
sbufWriteU16(dst, blackboxConfig()->denom);
sbufWriteU32(dst, blackboxConfig()->fields);
sbufWriteU16(dst, blackboxConfig()->initialEraseFreeSpaceKiB);
sbufWriteU8(dst, blackboxConfig()->rollingErase);
#else
sbufWriteU8(dst, 0); // Blackbox not supported
sbufWriteU8(dst, 0);
sbufWriteU8(dst, 0);
sbufWriteU16(dst, 0);
sbufWriteU32(dst, 0);
sbufWriteU16(dst, 0);
sbufWriteU8(dst, 0);
#endif
break;

Expand Down Expand Up @@ -2732,9 +2734,9 @@ static mspResult_e mspProcessInCommand(mspDescriptor_t srcDesc, int16_t cmdMSP,
blackboxConfigMutable()->mode = sbufReadU8(src);
blackboxConfigMutable()->denom = sbufReadU16(src);
blackboxConfigMutable()->fields = sbufReadU32(src);
if (sbufBytesRemaining(src) >= 2) {
blackboxConfigMutable()->initialEraseFreeSpaceKiB =
sbufReadU16(src);
if (sbufBytesRemaining(src) >= 3) {
blackboxConfigMutable()->initialEraseFreeSpaceKiB = sbufReadU16(src);
blackboxConfigMutable()->rollingErase = sbufReadU8(src);
}
}
break;
Expand Down
1 change: 1 addition & 0 deletions src/main/pg/blackbox.c
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ PG_RESET_TEMPLATE(blackboxConfig_t, blackboxConfig,
BIT(FLIGHT_LOG_FIELD_SELECT_MOTOR) |
BIT(FLIGHT_LOG_FIELD_SELECT_SERVO),
.initialEraseFreeSpaceKiB = 0,
.rollingErase = 0,
);

#endif
1 change: 1 addition & 0 deletions src/main/pg/blackbox.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ typedef struct blackboxConfig_s {
uint16_t denom;
uint32_t fields;
uint16_t initialEraseFreeSpaceKiB;
uint8_t rollingErase;
} blackboxConfig_t;

PG_DECLARE(blackboxConfig_t, blackboxConfig);
Loading

0 comments on commit c6dde11

Please sign in to comment.