Skip to content

Commit cb9bbfa

Browse files
nerdCopterclaude
andauthored
fix(flash/nand): NAND flash fixes; re-enable W25M02G on STELLARH7DEV (#1231)
* fix(flash/nand): fix 5 NAND flash bugs; re-enable W25M02G on STELLARH7DEV Fixes #1209 #1216 #1226 #1228 #1226 — flashPartitionSet off-by-one (flash.c) `flashPartitions == FLASH_MAX_PARTITIONS - 1` silently blocked the last valid slot; change to `>= FLASH_MAX_PARTITIONS`. #1216 — flashfsEraseAsync not wired to scheduler (fc_tasks.c) BF 4.5-m `taskMain` calls `flashfsEraseAsync()` under `#ifdef USE_FLASHFS`. EF was missing this call entirely; the async erase state machine (`FLASHFS_ERASING`) would never advance, stalling the erase dialog on all flash targets. #1209 — NAND erase blocks scheduler (flashfs.c) `flashfsEraseCompletely()` called `flashEraseCompletely()` for single- partition full-chip erases. On NAND (W25M02G: 2048 blocks × ~15ms each = ~30 s), this blocked the scheduler and disconnected USB. NOR flash has a hardware bulk-erase command that completes near-instantly; NAND does not. Fix: gate the synchronous NOR path on `flashType == FLASH_TYPE_NOR`. NAND always takes the async `FLASHFS_ERASING` sector-by-sector path through `flashfsEraseAsync()` now wired via #1216 fix. #1228a — w25n_pageProgramContinue only used buffers[0] (flash_w25n.c) Multi-buffer writes (bufferCount==2, used by flashfs for wrap-around across the circular write buffer) silently dropped buffers[1]. Added a second static segment (`segmentsBuffer2`) and link it when bufferCount>=2; total `bytesWritten` accumulated and returned/reported to callback correctly. #1228b — w25n_readBytes used `length` instead of `transferLength` (flash_w25n.c) The SPI and QuadSPI segments in `w25n_readBytes` passed `length` (unclamped) instead of `transferLength` (page-boundary-clamped). Reads past a page boundary returned data from the wrong region. #1228c — w25n_readBBLUT cmd[4] had 2 uninitialised bytes (flash_w25n.c) Changed `uint8_t cmd[4]; cmd[0]=...; cmd[1]=0;` to `uint8_t cmd[2] = { W25N_INSTRUCTION_READ_BBM_LUT, 0 };`. The W25N BBLUT command only needs 2 bytes; the extra 2 garbage bytes were transmitted to the device, shifting the 4-byte BBLUT record parsing. Re-enable W25M02G flash on STELLARH7DEV: the boot-hang (flashfsIdentifyStart- OfFreeSpace on bad-state chip) was misdiagnosed — real root cause was pinio_box reset wiring USER1-always-active to VTX. Flash disabled as workaround; now correct with async erase in place. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(flash_w25n): revert segmentsBuffer2 wrap-around — BF parity #1228a added segmentsBuffer2 to handle flashfs ring-buffer wrap-around (bufferCount=2) in a single DMA call. BF does not have this: it writes only buffers[0] and lets the ring buffer tail advance, so the wrapped portion becomes a normal bufferCount=1 write on the next scheduler tick. The EF addition created a stale-link bug: spiLinkSegments(dev, segmentsBuffer, segmentsBuffer2) sets segmentsBuffer[1].u.link -> segmentsBuffer2 on every wrap-around call. On the next call with bufferCount=1 and page NOT yet full (line 779 branch), segmentsBuffer[1] is never reset. The SPI DMA follows the stale link, reasserts CS, and sends segmentsBuffer2[0]'s stale bytes as an unintended SPI transaction. The W25N01G interprets byte 0 as an opcode -> premature PROGRAM_EXECUTE / WRITE_DISABLE / random state corruption -> corrupted BBL page -> Blackbox Explorer gap. Observed: exactly 2 gaps in a ~6-second BBL log on STELLARH7DEV (W25M02G, SPI4). BF has no gaps under identical conditions. Fix: drop segmentsBuffer2 entirely; always use a single segmentsBuffer and spiLinkSegments(dev, programSegment, segmentsBuffer). Wrap-around writes are handled transparently across two scheduler ticks as BF does. Also removes lastDataSegment indirection and the bufferCount>=2 branch; callbackArg uses bufferSizes[0] directly, matching BF's bytesWritten. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 6e50925 commit cb9bbfa

5 files changed

Lines changed: 19 additions & 16 deletions

File tree

src/main/drivers/flash.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -429,7 +429,7 @@ void flashPartitionSet(uint8_t type, uint32_t startSector, uint32_t endSector)
429429
flashPartition_t *entry = flashPartitionFindByType(type);
430430

431431
if (!entry) {
432-
if (flashPartitions == FLASH_MAX_PARTITIONS - 1) {
432+
if (flashPartitions >= FLASH_MAX_PARTITIONS) {
433433
return;
434434
}
435435
entry = &flashPartitionTable.partitions[flashPartitions++];

src/main/drivers/flash_w25n.c

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -886,7 +886,7 @@ int w25n_readBytes(flashDevice_t *fdevice, uint32_t address, uint8_t *buffer, ui
886886

887887
busSegment_t segments[] = {
888888
{.u.buffers = {cmd, NULL}, sizeof(cmd), false, NULL},
889-
{.u.buffers = {NULL, buffer}, length, true, NULL},
889+
{.u.buffers = {NULL, buffer}, transferLength, true, NULL},
890890
{.u.link = {NULL, NULL}, 0, true, NULL},
891891
};
892892

@@ -1041,10 +1041,7 @@ void w25n_readBBLUT(flashDevice_t *fdevice, bblut_t *bblut, int lutsize)
10411041
if (fdevice->io.mode == FLASHIO_SPI) {
10421042
extDevice_t *dev = fdevice->io.handle.dev;
10431043

1044-
uint8_t cmd[4];
1045-
1046-
cmd[0] = W25N_INSTRUCTION_READ_BBM_LUT;
1047-
cmd[1] = 0;
1044+
uint8_t cmd[2] = { W25N_INSTRUCTION_READ_BBM_LUT, 0 };
10481045

10491046
cb_context.bblut = &bblut[0];
10501047
cb_context.lutsize = lutsize;

src/main/fc/fc_tasks.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,9 @@ static void taskMain(timeUs_t currentTimeUs) {
131131
#ifdef USE_SDCARD
132132
afatfs_poll();
133133
#endif
134+
#ifdef USE_FLASHFS
135+
flashfsEraseAsync();
136+
#endif
134137
}
135138

136139
#ifdef USE_OSD_SLAVE

src/main/io/flashfs.c

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -126,12 +126,16 @@ static void flashfsSetTailAddress(uint32_t address)
126126
void flashfsEraseCompletely(void)
127127
{
128128
if (flashGeometry->sectors > 0 && flashPartitionCount() > 0) {
129-
// if there's a single FLASHFS partition and it uses the entire flash then do a full erase
130-
const bool doFullErase = (flashPartitionCount() == 1) && (FLASH_PARTITION_SECTOR_COUNT(flashPartition) == flashGeometry->sectors);
129+
// NOR: single full-partition can use hardware bulk erase (near-instant, non-blocking).
130+
// NAND: no bulk-erase command; block-by-block is required and takes 10–30 s.
131+
// Always use the async sector-by-sector path to avoid blocking the scheduler.
132+
const bool doFullErase = (flashPartitionCount() == 1) &&
133+
(FLASH_PARTITION_SECTOR_COUNT(flashPartition) == flashGeometry->sectors) &&
134+
(flashGeometry->flashType == FLASH_TYPE_NOR);
131135
if (doFullErase) {
132136
flashEraseCompletely();
133137
} else {
134-
// start asynchronous erase of all sectors
138+
// start asynchronous erase of all sectors (always used for NAND)
135139
eraseSectorCurrent = flashPartition->startSector;
136140
flashfsState = FLASHFS_ERASING;
137141
}

src/main/target/STELLARH7DEV/target.h

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -99,13 +99,12 @@
9999
#define I2C2_SDA PB11
100100

101101
// Flash: Winbond W25M02G (2×W25N01G NAND dies) on SPI4, CS=PC13.
102-
// Disabled pending resolution of #1209 (W25M02G NAND erase disconnects FC;
103-
// flashfsIdentifyStartOfFreeSpace hangs on bad-state chip, preventing boot).
104-
// Uncomment when H7 NAND flash is fixed:
105-
//#define USE_FLASH_W25M02G
106-
//#define FLASH_CS_PIN PC13
107-
//#define FLASH_SPI_INSTANCE SPI4
108-
//#define ENABLE_BLACKBOX_LOGGING_ON_SPIFLASH_BY_DEFAULT
102+
// Re-enabled: #1209 fixed via async erase (flashfsEraseAsync wired to taskMain,
103+
// NAND forced to async path in flashfsEraseCompletely). #1228 driver bugs fixed.
104+
#define USE_FLASH_W25M02G
105+
#define FLASH_CS_PIN PC13
106+
#define FLASH_SPI_INSTANCE SPI4
107+
#define ENABLE_BLACKBOX_LOGGING_ON_SPIFLASH_BY_DEFAULT
109108

110109
#define MAX7456_SPI_CS_PIN PB8
111110
#define MAX7456_SPI_INSTANCE SPI2

0 commit comments

Comments
 (0)