Repository navigation
Conversation
…boot The freefile is created once, on the largest free block of the card and at most 4 GB (the largest FAT32 file), and reused as it is at every later boot. Its size is a whole number of superclusters plus one cluster, so once the logs have taken the superclusters one cluster is left: the card reads as full and the blackbox stops logging until the card is formatted, although the rest of a card larger than 4 GB and any log deleted from a PC are free. When the freefile is below 2 GB at boot, search the FAT for the largest free block as on the first boot, and if that is larger, free the old freefile's clusters and move the freefile there. Larger freefiles are reused without a search, as before.
…w it Logs take their clusters from the start of the freefile, so it only ever shrank until the next format, and one log could never grow past what was left of it at boot. On a card larger than 4 GB the clusters that follow the freefile are normally free, so at boot extend it over them, up to the largest file size, leaving AFATFS_FREEFILE_LEAVE_CLUSTERS for regular files as a new freefile does. Only a freefile that is still below 2 GB afterwards is moved to the largest free block. The new clusters are chained and flushed before the freefile's last cluster points at them, and the new size is saved after that, so a power cut leaves at worst unreferenced clusters, never a chain that runs into free ones.
After the skip the search went on reading the FAT sector of the freefile's first cluster while the cluster number had moved past it, so the entries it examined belonged to other clusters. It could only matter when those sectors were already cached, and the search at boot now runs with the freefile in place.
afatfs_FATFillWithPattern() counted a final FAT sector it only writes part of among the sectors to pre-erase. That sector has to be read first, and on an SD card on SPI the read ends the multiple-block write started with ACMD23 before it reaches that block; the SD specification leaves an unwritten pre-erased block undefined, erased or old. Its other entries, which can belong to the next file, could then be read back erased. A new freefile ends that way (whole superclusters plus one cluster), and so does every extension of it.
afatfs_getFreeBufferSpace() counted every unlocked sector that was empty or in sync, but afatfs_allocateCacheSector() never evicts one with a retain count: a contiguous file keeps its directory entry retained for as long as it is open, and the blackbox log and the free file are both contiguous. With a log open, two of the 8 sectors were reported as space the blackbox could write into, and could not be. Measured on a TBS Lucid H7 while the SD card held a write for 54 ms: afatfs_fwrite() refused bytes while this function still reported 1024 bytes free.
An SD card can hold a write for tens of milliseconds (the standard
allows 250). The blackbox keeps writing into the other cache sectors
meanwhile, and once none is left it drops what it writes. With 8
sectors, and 3 of them never free while a log is open, the cache held
about 30 ms of a 1 kHz log.
Measured on a TBS Lucid H7 whose card holds a write for 54 ms every
768 KB once it runs out of prepared blocks, 120 s logs:
sectors 1 kHz log (85 kB/s) 2 kHz log (170 kB/s)
rows lost rows lost
8 379 2178
12 8 1282
16 0 703
24 0 0
32 0 0 (also over a 20 minute log)
Now 32 sectors on H7 and 24 on F7, where RAM is plenty. On F4 with the
card on SPI the cache moves to CCM with 24 sectors: RAM is short there,
CCM mostly unused, and the CPU writes the card, so no DMA needs to
reach it. Elsewhere (F4 with SDIO, AT32) it stays at 8. A target can
set AFATFS_NUM_CACHE_SECTORS itself. Targets without an SD card do not
link the cache.
Walking the larger cache costs afatfs_poll() about 8 ns per sector on
the H7, in the scheduler's idle loop.
… card has room With the freefile refilled at boot, every power cycle starts with up to 4 GB for logs, but within one power cycle the freefile only shrinks: when it runs out, or a log reaches the largest FAT32 file, the blackbox stops for the rest of the flight although the card may have room. Behind USE_BLACKBOX_SDCARD_SPLIT, on by default on targets with more than 512 KB of flash, afatfs refills the freefile in the background once it is below 256 MB: it extends it over the free clusters that follow it, 16 MB at a time, and when nothing free follows it searches the FAT for the largest free block, and moves the freefile there once it has run out and the log that used it has been closed. Regular cluster allocations wait while clusters are being claimed, and a contiguous file can no longer grow past the largest file size. The blackbox closes a log that can't grow when afatfs can make room, instead of stopping, and the usual start path opens the next file once there is room, without waiting for a serial port before the header. The card only counts as full once no new file can get room either.
…a full card stops as before A log created while the freefile was being refilled could not grow, so the blackbox split it at once and, with no frame logged, deleted it. Deleting a contiguous file with no clusters ran the contiguous erase from cluster 0 up to the freefile. Truncating a file with no clusters now only saves its directory entry, and a split log's new file waits until the freefile has room for its first supercluster, as long as the refill can still bring some: when it can't, the file opens as without the option and the full card stops the log. When a regular cluster allocation finds no free cluster, the card is full for more than the freefile: the refill no longer clears that, and the blackbox stops as it did without the option.
|
ⓘ Qodo reviews are paused because the subscription is no longer active. Ask your workspace admin to reactivate the subscription to resume reviews. Manage billing |
PR Summary by QodoContinue SD blackbox logging in a new file when a log cannot grow
AI Description
Diagram
High-Level Assessment
Files changed (6)
|
Code Review by Qodo
1.
|
…f retrying forever
|
RAM / Flash usage vs. base commit
See RAM/flash optimization guide for techniques to reduce usage. |
|
Test firmware build ready — commit Download firmware for PR #12060 251 targets built. Find your board's
|
9da70af to
b0cf99d
Compare
b0cf99d to
1747ab4
Compare
…rectory entry each step A power cut while the freefile was regrown or moved at boot left whatever chain was in flight as a lost FAT chain: up to 4 GB of new chain, and up to 2 GB of the old freefile when moving it. Three cuts on a TBS Lucid H7 Wing left three lost chains of 650 MB on average. The new chain now grows in 256 MB blocks, each one chained, linked, saved and flushed before the next. A moved freefile gives its old clusters back the same way from its end: the smaller size is saved, then the chain is cut there, then the tail is freed. A cut strands at most one block either way.
… end and freeing from the front
…eature/blackbox-sd-split
|
Tests of the items listed as untested above, and three commits on top. Several moves in one flight, on a second TBS Lucid H7 Wing (SDIO, its own card), with a bench build that fills the card's FAT. The freefile was cut to 24 MB, and FAT-only files left 21 free blocks of 400 clusters (12.5 MB) on the card. Then I logged at 1 kHz (63 kB/s) for 15 minutes, three times.
A really full card, on 2026-10-07 with this PR before #12056 was merged in: FAT-only files left only holes of 64 clusters. The log stopped where the freefile ran out, without splitting, and decodes with no iteration missing; a new log on the full card then stays empty, as without this PR. The other two commits:
Flash against f63952b: +80 B on MATEKF722SE (98.67%), +264 B on MATEKF405SE, +224 and +208 B on KAKUTEF7 and KAKUTEF7HDV, +240 B on the H743, +296 B on an AT32F435; ITCM and RAM unchanged. The real 4 GB limit is still untested. |
|
I was thinking that as you noted, 4 GiB is about 14 hours of flight, so maybe it would be simpler to make it a circular buffer -- when you hit the end, go back to the beginning. So it never runs out of space. You always have the last ~14 hours of flight on the CD card. Actually implementing that, it wasn't as simple as I was thinking, but it is about half as much code as as what's here. Which of the two do you think is best? |
|
@sensei-hacker thanks for checking it out. The circular buffer isn't a bad idea at all, but I personally like to use all the space on the SD card, and that's just my own preference. About the three afatfs bugs you found, do they happen on the base branch too? If so, I think they deserve a PR of their own. |
…new log regularFull stayed set after the regular allocation that failed, so the room freed when blackbox deleted an empty log was never offered to the next one until the card was mounted again.
|
The deltas include the PRs this one builds on, against an older base: the RAM is #12057's larger afatfs cache (24 sectors in CCM on F4 with the card on SPI, 24 on F7 and 32 on H7 in RAM, where there were 8), and the flash is mostly #12056 and #12057. MATEKF722 has 512 KB of flash and doesn't build this PR's code ( |
Builds on #12056 and #12057. The first commits are #12056 (the freefile refilled at boot) and #12057 (the SD cache): the refill writes to the FAT while a new log's header goes out, and without #12057's corrected count of free buffer space the header can lose bytes. On the bench 2 of 7 files lost part of their header without it, 0 of 7 with it. Then come this PR's commits, and copies of the later fixes of #12056 and #12057.
With #12056 the freefile is refilled at boot, so every power cycle starts with up to 4 GB for logs. Within one power cycle it still only shrinks: when it runs out, or a log reaches 4 GB (the largest FAT32 file), the blackbox stops for the rest of the flight although the card may have plenty of room. PX4 handles the same FAT32 limit by closing the log and starting a new file (
SDLOG_MAX_SIZE); this does the same for INAV's SD blackbox.The change
It is a build option,
USE_BLACKBOX_SDCARD_SPLIT, defined with the other larger optional features incommon.h, so it is on for targets with more than 512 KB of flash and off on the 512 KB F7 targets.afatfs, in the background (
afatfs_poll), once the freefile is below 256 MB:Regular cluster allocations wait while clusters are being claimed, and a claim doesn't start while one of them may be under way, so the two can't take the same cluster. A contiguous file can no longer grow past 4 GB either; with the freefile refilled it now could.
Blackbox: when the log file can't grow any more but afatfs can make room, it closes the log instead of stopping, and the usual start path opens the next
LOGxxxxx.TXTas soon as there is room. Every iteration spent on the new file's header is a gap in the log, so while it is sent the blackbox skips the 100 ms wait for a serial port and takes several header steps per iteration, within 50 us, as long as the SD card cache has 2 KB to spare. That margin matters:afatfs_getFreeBufferSpace()counts the cache sectors held for open files as free, and without it the header outran the card and lost its sysinfo lines. The SD card only counts as full (OSD, MAVLink, arming a new log) once no new file can get room either.Two paths the split takes and today's blackbox doesn't:
Testing
TBS Lucid H7 Wing (H743, SDIO), 16 GB card, logging at 2 kHz (about 170 KB/s), with bench-only CLI commands to shrink the freefile (freeing its tail in the FAT, optionally with an occupied cluster right after it), to pause the refill, and to set a fake file size limit.
Repeated after the review fixes on top of the other SD PRs (cache, pause, block writes): every file decoded with its full header and 0% of iterations missing; the in-flight extension kept one file (81,382 frames in 41 s), the move split at exactly 12 MB with a 27 ms gap, the fake limit gave three 8 MB files with 16-20 ms gaps, and after each of three reboots the freefile was back to 4,190,240 KB with a contiguous chain. Before the fixes, and with this PR alone, every file decoded with 0 to 1% of iterations missing, the same as without a split on this card (its 54 ms write stalls, which the cache and pause PRs deal with). The background search ran while logging with no visible effect on the frame rate. With one header step per iteration, as the blackbox does at arming, the gaps were 83 and 68 ms. The PID task's longest run over the three-file test was 145 us, against 134 us with one step per iteration.
In the three 15-minute runs one or two files had gaps, 3.4 to 13.9 s in all, from writes turned down because the cache was full. A bench build timestamped the refill's phases and the turned-down writes: the refill was idle at every gap, far from any search or move, so these are that card's own write stalls of 0.2 to 0.6 s (#12058 marks them as pauses). Afterwards the FAT-only files were removed, and the 57 logs already on the card had the same CRC as before.
USE_TERRAINwith a refill running, on the same board with a terrain file on its card and HITL flying a route over it: with the refill as it was, fifteen 16 MB claims took 0.5 s at the start, before terrain began to read; terrain then got 40 of 40 accesses, loaded its 13 blocks, and the log had no gap. With the block search slowed down on purpose to last the whole flight, terrain got the card only once the search ended, 200 s later; with d655443 (below), 0.2 s after the search began, and it loaded all 13 blocks on time. The log had no gap either way.Flash: +1,212 B on TBS_LUCID_H7_WING, +1,380 B on MATEKH743, +1,424 B on MATEKF405SE; MATEKF722SE unchanged (option off). SITL builds.
Not tested:
If anyone logs long flights to an SD card, especially on an F4 or F7 board with the SD on SPI, I'd like to know whether the log continues in a new file and how long the gap is.
After review
Built on F4, F7, H7, AT32 and SITL; KAKUTEF7 and KAKUTEF7HDV fit their ITCM. On the TBS Lucid H7 Wing after review: extension in flight (one file), a move to another block (gap 29 ms) and a fake 8 MB file limit (three files, gaps 17 and 18 ms), no iteration missing in any file, FAT chains contiguous.
After merging #12056's change, on the same board: six resets at set points of a boot that moved the freefile (two in the FAT search, one while the old freefile was given back, two while the new one grew, one after the work) each left a freefile whose chain was as long as its size; the extension in flight kept one file (81,450 frames in 41 s) and the move continued in a new file with a 29 ms gap, no iteration missing in either file. Flash +272 B on MATEKF722SE (98.66%), +408 B on MATEKF405SE, +480 B on TBS_LUCID_H7_WING; ITCM unchanged on KAKUTEF7 and KAKUTEF7HDV.
On 2026-10-08, three more commits:
Flash against f63952b: +80 B on MATEKF722SE (98.67%), +264 B on MATEKF405SE, +224 and +208 B on KAKUTEF7 and KAKUTEF7HDV, +240 B on an H743, +296 B on an AT32F435; ITCM and RAM unchanged.
Related PRs
blackboxUpdate()and conflict with this one inblackbox.c. Whichever is merged second, I'll rebase it.On 2026-10-19: ed03967 clears
regularFullwhen a file is deleted, so the room freed when the blackbox deletes an empty log goes to the next log without a new mount (from Qodo's review of #12158).