Skip to content

fix(flashparams): append unsaved deltas and compact at boot - #28532

Draft
dakejahl wants to merge 3 commits into
mainfrom
dakejahl/flashparams-boot-compact
Draft

fix(flashparams): append unsaved deltas and compact at boot#28532
dakejahl wants to merge 3 commits into
mainfrom
dakejahl/flashparams-boot-compact

Conversation

@dakejahl

@dakejahl dakejahl commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

Summary

fixes #28529

Flash param saves now append only the params that changed since the last save. The ~1 s sector erase, if it is needed, runs at boot before sensors and DShot start.

Problem

On STM32H7 boards with FLASH_BASED_PARAMS, a program or erase of the param sector stalls every instruction fetch from that same flash bank. Each autosave rewrote the full BSON document (tens of ms). That stall drops a high-rate IMU FIFO and DShot while the vehicle is disarmed. When the 128 KB sector filled, erasing it (~1 s) could still run after the vehicle was already up.

Solution

Each save appends only unsaved params, instead of rewriting the whole document. On load, PX4 replays those records in order.

A reset appends a BSON null for that param. Replay drops the override. The next compact omits it, so a later firmware default change does not come back as a user setting.

The slow sector erase moves to boot, before sensors and DShot start. PX4 rewrites the log as a single snapshot only when the blank tail cannot hold a batch of new saves, or when dead records sit in front of the live data (the layout left by the old writer). Saving the flight UUID on disarm does not force a 1 s erase on the next boot.

Compaction builds the snapshot in RAM before it erases the sector. The param-file lock is held across reset, import, and compact so autosave cannot write flash during the erase. If a later save still cannot fit, PX4 erases the sector while running. That path is last resort.

Before and after

The firmware image holds the defaults. RAM holds only overrides. Flash only needs to remember those overrides. On STM32H7, flash is programmed in 32-byte rows that cannot be rewritten, and a 128 KB erase stalls the CPU for about a second. The param sector shares a bank with the firmware, so a program or erase halts instruction fetch.

Before. Flash held one live snapshot of every override. Each save encoded the full set, marked the previous snapshot erased, and programmed a new copy after it. Load reset RAM to defaults and imported that one record. A reset did not need a marker: the next snapshot simply omitted the param. Every autosave allocated two RAM buffers the size of the full BSON (encoder plus write buffer). A typical vehicle is about 100 overrides (~2 KB BSON, ~4 KB peak). All 1668 ARK FPV params would be ~35 KB BSON and ~71 KB peak; that bound is not new.

After. Flash is a log. A boot snapshot is followed by small records of unsaved params. Load applies every live record in order. A reset must appear in the log as a BSON null, or replay would still apply the old override. Compact at boot collapses the log to one snapshot when the tail is too small, or when the old dead-record prefix is still there, using the same two-buffer peak as the old save path. In-service saves only allocate the delta.

Compatibility. New firmware reads the old single-snapshot layout, then compact rewrites it. Parameters are kept.

Old firmware reads only the first live record. If flash is still one snapshot (new firmware booted, compact ran, no save), a downgrade loads that snapshot. After any in-service save, including COM_FLIGHT_UUID on disarm, later deltas are invisible. An old-firmware save then marks the snapshot erased and tries to program over the deltas. On H7 that write fails, and the complete copy is already gone. A QGC reset on old firmware takes the same save path; it does not erase the sector and will not repair it.

Same-bank program of a full BSON snapshot stalls instruction fetch for tens of ms, which drops a high-rate IMU FIFO and DShot while disarmed. The 1 s sector erase belongs at boot, not on a wrap mid-session.

Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>
@github-actions github-actions Bot added kind:bug Something is broken or behaving incorrectly. scope:parameters Parameter definitions, metadata, migration, or defaults. labels Sep 3, 2026
@github-actions

github-actions Bot commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

🔎 FLASH Analysis

px4_fmu-v5x [Total VM Diff: -424 byte (-0.02 %)]
    FILE SIZE        VM SIZE    
 --------------  -------------- 
  +0.0%     +37  [ = ]       0    .debug_abbrev
  +0.0%      +8  [ = ]       0    .debug_aranges
  +0.0%     +12  [ = ]       0    .debug_frame
  -0.0% -2.32Ki  [ = ]       0    .debug_info
  -0.0% -1.56Ki  [ = ]       0    .debug_line
     +75%      +3  [ = ]       0    [Unmapped]
    -0.0% -1.56Ki  [ = ]       0    [section .debug_line]
  -0.0% -1.05Ki  [ = ]       0    .debug_loclists
    [NEW]      +2  [ = ]       0    [Unmapped]
    -0.0% -1.06Ki  [ = ]       0    [section .debug_loclists]
  -0.1%    -562  [ = ]       0    .debug_rnglists
  +0.0%     +74  [ = ]       0    .debug_str
  +0.0%     +32  [ = ]       0    .symtab
   -40.0%     -32  [ = ]       0    ___Z12get_orb_meta6ORB_ID_veneer
     +13%     +32  [ = ]       0    ___ZL19param_get_cplusplustPf.isra.0_veneer
     +33%     +16  [ = ]       0    __net_unlock_veneer
   -25.0%     -16  [ = ]       0    __stm32_i2c_setclock_veneer
     +67%     +32  [ = ]       0    param_import_callback()
   -25.0%     -16  [ = ]       0    param_reset_internal()
     +33%     +16  [ = ]       0    param_set_internal()
  +4.9%    +424  [ = ]       0    [Unmapped]
  -0.0%    -424  -0.0%    -424    .text
     +81%    +156   +81%    +156    param_import_callback()
     +32%     +84   +32%     +84    param_reset_all
     +10%     +60   +10%     +60    bson_decoder_next()
     +19%     +60   +19%     +60    param_reset_internal()
    +5.0%      +4  +5.0%      +4    init_print_load
    -6.0%     -32  -6.0%     -32    param_set_internal()
   -73.9%    -204 -73.9%    -204    param_load
   -60.7%    -260 -60.7%    -260    param_reset_specific
   -67.0%    -292 -67.0%    -292    param_reset_excludes
  -0.0% -5.32Ki  -0.0%    -424    TOTAL

px4_fmu-v6x [Total VM Diff: -424 byte (-0.02 %)]
    FILE SIZE        VM SIZE    
 --------------  -------------- 
  +0.0%     +37  [ = ]       0    .debug_abbrev
  +0.0%      +8  [ = ]       0    .debug_aranges
  +0.0%     +12  [ = ]       0    .debug_frame
  -0.0% -2.32Ki  [ = ]       0    .debug_info
  -0.0% -1.56Ki  [ = ]       0    .debug_line
     +50%      +2  [ = ]       0    [Unmapped]
    -0.0% -1.56Ki  [ = ]       0    [section .debug_line]
  -0.0% -1.04Ki  [ = ]       0    .debug_loclists
   -66.7%      -2  [ = ]       0    [Unmapped]
    -0.0% -1.04Ki  [ = ]       0    [section .debug_loclists]
  -0.1%    -561  [ = ]       0    .debug_rnglists
  +0.0%     +73  [ = ]       0    .debug_str
   -50.0%      -1  [ = ]       0    [Unmapped]
    +0.0%     +74  [ = ]       0    [section .debug_str]
  +0.0%     +32  [ = ]       0    .symtab
    -0.3%     -32  [ = ]       0    [section .symtab]
    +100%     +32  [ = ]       0    param_hash_check
     +67%     +32  [ = ]       0    param_import_callback()
   -25.0%     -16  [ = ]       0    param_reset_internal()
     +33%     +16  [ = ]       0    param_set_internal()
  +9.8%    +424  [ = ]       0    [Unmapped]
  -0.0%    -424  -0.0%    -424    .text
     +83%    +156   +83%    +156    param_import_callback()
     +32%     +84   +32%     +84    param_reset_all
     +10%     +60   +10%     +60    bson_decoder_next()
     +20%     +60   +20%     +60    param_reset_internal()
    +5.0%      +4  +5.0%      +4    init_print_load
    -1.4%      -4  -1.4%      -4    param_hash_check
    -5.3%     -28  -5.3%     -28    param_set_internal()
   -73.9%    -204 -73.9%    -204    param_load
   -60.7%    -260 -60.7%    -260    param_reset_specific
   -67.0%    -292 -67.0%    -292    param_reset_excludes
  -0.0% -5.30Ki  -0.0%    -424    TOTAL

Updated: 2026-09-04T01:47:13

param_reset_all() queued an autosave that could program flash during the boot compact erase. Reset without autosave under file_mutex, build the snapshot in RAM before erasing, and compact only when a burst of deltas would not fit so COM_FLIGHT_UUID is not a 1 s erase every flight.

Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>
Walk order was not write order on multi-sector maps, and a torn H7 header skipped the rest of the sector so a successful retry vanished. Resets encoded as the current default also came back as overrides after a firmware default change.

Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

kind:bug Something is broken or behaving incorrectly. scope:parameters Parameter definitions, metadata, migration, or defaults.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] FLASH_BASED_PARAMS save on STM32H7 stalls CPU, IMU times out and DShot drops

1 participant