0027. Firmware: L432 calibration pinned to the top flash page
Date: 2026-10-10
Reverses the rejection of 2026-10-05 recorded in
the CompassTag TODO, for
calibration only. The reasons given there for not pinning .persistent still
stand.
Context
STM32L432xC.ld placed .calibration at the first page boundary after the
code, so the table moved whenever the image crossed a page: 0x0800a800 at
4160d1e, 0x0800b000 at fw-v0.5, with 200 bytes left before the next move
at fw-v0.6. A calibrated board that is upgraded across such a move reads an
unwritten page, or leftover code bytes, as its constants, and nothing reports
it.
On 2026-10-05 the full U375 arrangement was built for the L432: calibration and
the NAND map pinned at the top of flash, and .persistent pinned from a 64 KB
code ceiling. It was rejected because it cost 10-15% of internal log capacity
on all four L432 targets to protect one, and because the agreed upgrade path was
a mass erase, which destroys calibration wherever it sits.
CompassTag is now being prepared for distribution, and its calibration record is gaining accelerometer constants. Calibrated units will be upgraded after they leave the bench, and recalibrating each one at every upgrade is a cost the mass-erase policy did not have to weigh when every unit was on the bench.
Decision
STM32L432xC.ldplaces.calibrationat the topTAG_CALIBRATION_PAGESflash pages, a fixed address. The default is zero.- The CompassTag family sets
TAG_CALIBRATION_PAGES=1from itsfamily.mk, putting the table at0x0803f800(page 127) on all three variants. .persistentstill starts at the first page after the code and now ends at the calibration pages. Its start still moves with code size.- The upgrade for a calibrated L432 tag is
flash_release.py --keep-calibration: erase pages 0 through the one below__calibration_start__, then program, in one programmer invocation. That clears the moving.persistent, so stale stored configuration or state is never read, and keeps calibration. - The release that introduces this layout also changes the calibration record
format, so it is installed with a mass erase (
--erase) and recalibration. That is a one-time step.
Alternatives considered
- Pin
.persistentas well (the 2026-10-05 change). It would make a plain program safe, but it costs 10-15% of log capacity on every L432 target. Erasing the floating region is cheaper. - Pin calibration on every L432 target. It charges BitTag, PresTag and UIUCTag a page each for a region they never use. With the zero default their layout is unchanged.
- Keep mass erase for every upgrade. Correct, but it costs a recalibration per unit per upgrade once units are distributed.
- Only add an ASSERT on the gap between code and calibration. It turns a silent move into a build failure, but the table still has to move eventually. The pinned layout includes an equivalent ASSERT anyway.
Evidence
Built from the same tree before and after the change:
- BitTag, PresTag, PresTagRaw, BitPresTag, BitPresTagMX25R, BitTagNG and UIUCTag
produce byte-identical
.binimages. Their.persistentbounds are unchanged; only their empty calibration symbols moved, to the end of flash. - On CompassTag, CompassTagAT25Breakout and CompassTagAT25,
.persistentmoves down one page and keeps its size, because the calibration page that used to sit between it and the code now sits above it. Internal log capacity is unchanged. flash_release.py --keep-calibrationcomputes-e [0 126]for the CompassTag images and refuses images that store no calibration.- On hardware (CompassTagAT25, UUID 203633324B4250060022005E, 3.3 V tagbase,
2026-10-10): a calibrated tag running
b7b06071+dirty was upgraded to0c88e7bcwith--keep-calibration. Page 127 read back byte-identical to a snapshot taken before the flash, page 126 was erased,tag-testpassed, and qtcalibrate's Load returned the saved magnetometer and accelerometer constants from the new firmware.
Consequences
- Upgrades that keep the calibration format no longer cost a recalibration on CompassTag. They do cost the stored configuration and the logs, as they did before.
- A family that adds a
.calibrationtable on the L432 must setTAG_CALIBRATION_PAGES, or the link fails. - A change to the calibration record format still requires a mass erase, because the bytes stay at the same address under a different meaning.