Repository navigation
AP_Baro: bound the ICP201XX init waits so a dead sensor cannot hang boot - #34356
peterbarker wants to merge 1 commit into
Conversation
Previous review (2026-09-10)Automated review note — AI-generated (Claude), validated against the live diff. Please sanity-check before acting. Reviewed at head No blockers. The change is correct and worth merging — contrary to your own "may not be worth merging" note, an unbounded poll on a probe path is a genuine brick-the-board hazard, and CI is 101/101 green. An independent cold Codex pass returned APPROVE. Two things are worth fixing first, both about the justification rather than the code. 1. ISSUE — the bounds are on iteration count, not elapsed time, so the "100ms / 10ms / 2s" figures are floors, not ceilings
Every iteration calls
So one
About 95 s of blocked boot worst case against the ~2.2 s implied. It is still finite, so the PR's goal is met and this is not a regression. The cold pass reached the same conclusion independently, phrasing it as "the attempt caps are finite, but their delay totals exclude bus transactions, retries and scheduling overhead." Honest caveat: a device that simply NAKs returns Suggested fix: switch the three loops to an elapsed-time bound — the idiom this very library already uses two files away, 2. ISSUE — the ODR justification in the comment and commit message is wrong on both counts
The 2 s bound is fine for the mode actually configured. It is the written reasoning that is wrong, and it is exactly the thing that misleads whoever next changes Suggested fix: cite the configured mode (" 3. NOTE — the Renode ICP201XX model can never satisfy
|
9eb8b2b to
22b88ac
Compare
Three waits in the ICP201XX init path had no exit: mode_select() spun on the device-status sync bit, read_otp_data() spun on the OTP status, and wait_read() polled FIFO_FILL until 14 packets had arrived and again until one had. A part that answers the ID probe and then stops responding, or never fills its FIFO, therefore kept init() there for ever. Nothing recovers from that. On every board except those defining HAL_EARLY_WATCHDOG_INIT the watchdog is not started until setup() has returned, and where it is started early the scheduler keeps patting it while the system is still initialising. The result is a board with no MAVLink, no console message and no reboot. Bound each wait by elapsed time and return false on expiry: 100ms for mode sync, 50ms for an OTP read, and 2s for each FIFO wait. A bound on the number of polls would not do. Each read_reg() can take several times the sleep between polls while the I2C layer retries against a part that clock-stretches or holds SDA, and the device keeps its default retries until init() succeeds, so a count would understate how long a dead part can block the boot. The FIFO limit is set against the configured mode, OP_MODE1 at 120Hz, where the 14 settling packets take about 117ms; the comment notes that a slower mode would need it raised. wait_read() now returns bool, init() takes its failed path on a timeout, and boot_sequence() checks the mode_select() results it ignored, so the other barometers on the board carry on. Verified under Renode on YJUAV_A6SE_H743, whose emulated ICP201XX reports a FIFO fill of 1 and so can never satisfy the first wait. The firmware built from this commit reached a MAVLink heartbeat after 32s of wall time; the same board built from the parent produced none in 180s. The two builds differ only by this commit.
22b88ac to
25b6749
Compare
|
Automated review note — AI-generated (Claude), validated against the live diff. Please sanity-check before acting. Re-reviewed at head No blockers. The verdict moves to APPROVE (it was COMMENT). Both issues from my previous comment are fixed, and so are the style points. What's left: the pre-existing bug you've deferred, a nit, and a small note on the PR description. Resolved
Still open (not blocking)
New (optional)
Also checked:
CI at this head: 101 passing, 0 failing. |
Summary
Bound the three ICP201XX initialisation waits by elapsed time, so a sensor that answers the ID probe and then stops responding, or never fills its FIFO, fails its probe instead of hanging the boot for ever.
Classification & Testing (check all that apply and add your own)
Tested under Renode on YJUAV_A6SE_H743. The emulated ICP201XX in the harness reports a FIFO fill of 1, so it can never satisfy the driver's first wait, which makes it a faithful stand-in for a part that probes and then never fills.
Tools/scripts/check_branch_conventions.pyThis has not been exercised against a real failing part. The fault was found in emulation, and on hardware the sensor has always come up normally.
Description
Three waits reachable from
AP_Baro_ICP201XX::init()had no exit:mode_select()polled the device-status register until its sync bit was set.read_otp_data()polled the OTP status register until it read zero.wait_read()polledFIFO_FILLuntil 14 packets had arrived, and again after a flush until one had.Each loop inspected only the value it polled and never whether the read succeeded, so a part that answers the ID probe and then stops acknowledging, or keeps acknowledging but never fills its FIFO, holds
init()there indefinitely.Nothing recovers from that. On every board except those defining
HAL_EARLY_WATCHDOG_INIT, currently only Here4FC,setup()runs before the watchdog is started at all, so there is nothing to fire. Where the watchdog is started early, the scheduler keeps patting it until the system is marked initialised. Either way the board sits there with no MAVLink, no console message and no reboot, which is a much worse failure than a missing barometer.Each wait now gives up after a fixed elapsed time and returns false: 100 ms for mode sync, 50 ms for an OTP read, and 2 s for each FIFO wait.
wait_read()returnsbool,init()takes its existing failed path on a timeout, andboot_sequence()now checks themode_select()results it previously ignored. The probe fails cleanly and the other barometers on the board carry on.Why elapsed time and not a count of polls. Each
read_reg()can take several times the sleep between polls. The I2C layer defaults to three attempts per transaction with a 4 ms minimum timeout, andinit()only callsset_retries(0)after everything has succeeded, so during initialisation a part that clock-stretches or holds SDA makes every poll expensive. A limit on the number of polls would therefore understate how long a dead part can block the boot. Bounding by elapsed time makes the stated figures real ceilings, give or take the duration of the last bus read in each loop.Worst case. A failed
mode_select()returns at once, the three OTP reads run as a group, and the two FIFO waits dominate. A dead part can now hold the boot for at most about four seconds, if it satisfies the first FIFO wait just before its limit and then fails the second.Where the FIFO limit comes from. The driver configures
OP_MODE1, which runs at 120 Hz, so the 14 settling packets take about 117 ms and the first packet after a flush about 8 ms. Two seconds is a wide margin for both.OP_MODE3runs at 2 Hz and would need about 7 s just to settle, so the constant would have to be raised if the configured mode were ever slowed. The comment on the constant says so, so the next person to change_op_modefinds it.Points worth knowing:
boot_sequence()the OTP boot-done flag is written only when reading its status register fails, so on the success path it is never set and the OTP boot sequence re-runs on every boot of an A1 part. That predates this change and is not a hang, so it belongs in its own PR.This PR was AI-assisted.