Skip to content

Derive the timer interrupt context index from the timer table - #11901

Open
Raffi1202 wants to merge 1 commit into
iNavFlight:maintenance-10.xfrom
Raffi1202:fix/timer-context-bounds
Open

Raffi1202 wants to merge 1 commit into
iNavFlight:maintenance-10.xfrom
Raffi1202:fix/timer-context-bounds

Conversation

@Raffi1202

@Raffi1202 Raffi1202 commented Sep 10, 2026

Copy link
Copy Markdown
Contributor

Problem

On STM32H7 the timer IRQ handlers index timerCtx[] with timer number - 1, but H7 has no TIM9..TIM11 and its table is packed into 14 slots. TIM15_IRQHandler..TIM17_IRQHandler therefore read timerCtx[14..16], past the end of the array, and the TIM12..TIM14 handlers get the context of TIM15..TIM17. Assigning PPM/PWM RC input or ESC RPM telemetry to a TIM15/16/17 pad on an H7 board enables that IRQ and can hard fault (#11368, found by code analysis; the reporter has no H7 board). AT32F43x has the same defect: TMR20_CH_IRQHandler reads timerCtx[19] in a 15-entry array.

Cause

src/main/drivers/timer_impl.h:61 on maintenance-10.x (also :54-55, and :28-29/:35 for AT32): impl_timerCaptureCompareHandler(TIM ## i, timerCtx[i - 1]);. The H7 table timer_stm32h7xx.c:43-48 places TIM12..TIM17 in slots 8..13. timer_impl_hal.c:254 calls timerCtx->ch[0].cb->callbackEdge guarded only by a NULL check on timerCtx itself (:235), so a foreign or out-of-bounds context faults.

Change

timer.h gains TIMER_INDEX(n) per MCU family (n-1 on F4/F7; n<9 ? n-1 : n-4 on H7; n<20 ? n-1 : 14 on AT32F43x). The four platform tables use it as their array designators and both handler macros in timer_impl.h use timerCtx[TIMER_INDEX(i)], so table and ISR share one mapping. A STATIC_ASSERT in each handler macro rejects an index outside HARDWARE_TIMER_DEFINITION_COUNT, each platform file asserts its last timer lands in the last slot, and timer_impl.h includes common/utils.h for STATIC_ASSERT. F4/F7 indices are unchanged.

Test

Not run on hardware. Cause verified by reading timer_impl.h:61, timer_stm32h7xx.c:43-48 and timer_impl_hal.c:235-254 on maintenance-10.x. The upstream "Build firmware" run is waiting for maintainer approval: https://github.com/iNavFlight/inav/actions/runs/34511424067. Built by fork CI: pending. Compiled for all targets and the four SITL builds on the fork, green: https://github.com/Raffi1202/inav/actions/runs/34770670426

Flash / RAM

Builds clean on all targets. No size comparison yet: the fork build has no baseline for this branch, and the upstream size report runs once CI is released for this PR.

Docs

No documentation change needed: internal driver indexing with no setting, CLI or user-visible option; the only docs mention of HARDWARE_TIMER_DEFINITION_COUNT (docs/development/msp/README.md:3451) is unaffected.

The timer IRQ handler macros indexed timerCtx[] with (timer number - 1),
which assumes the hardware timers are numbered without gaps. STM32H7 has
no TIM9..TIM11 and AT32F43x adds TMR20 after TMR14, so their tables are
packed and the handlers for the timers behind the gap picked up a foreign
context or read past the end of the array.

Add TIMER_INDEX(), a per MCU compile time mapping from timer number to
table slot, and use it both for timerDefinitions[] and for the handlers,
so both sides can no longer drift apart. A STATIC_ASSERT in the handler
macros keeps an out of range index from compiling.

Fixes iNavFlight#11368
@Raffi1202
Raffi1202 marked this pull request as ready for review September 11, 2026 16:00
@qodo-code-review

Copy link
Copy Markdown
Contributor

ⓘ Qodo reviews are paused because the subscription is no longer active. Ask your workspace admin to reactivate the subscription to resume reviews. Manage billing

@qodo-free-for-open-source-projects

Copy link
Copy Markdown

PR Summary by Qodo

Derive timer interrupt context indices from platform timer tables

🐞 Bug fix 🕐 20-40 Minutes

Grey Divider

AI Description

• Maps hardware timer numbers to packed context slots for every supported MCU family.
• Prevents H7 and AT32 interrupt handlers from using foreign or out-of-bounds contexts.
• Adds compile-time guards to keep timer tables and IRQ handlers synchronized.
Diagram

graph TD
    IRQ["Timer IRQ"] --> Handler["IRQ Handler"] --> Index["TIMER_INDEX"] --> Context["timerCtx Slot"] --> Callback["Channel Callback"]
    Index --> Tables["Platform Tables"]
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Runtime lookupTimerIndex mapping
  • ➕ Reuses the existing timer-pointer lookup logic.
  • ➕ Avoids maintaining platform-specific arithmetic mappings.
  • ➖ Adds a linear search to every timer interrupt.
  • ➖ Provides weaker compile-time guarantees for handler indices.
  • ➖ Increases latency in timing-sensitive ISR paths.

Recommendation: Keep the PR's compile-time TIMER_INDEX mapping. Sharing it between table designators and handler macros removes the original duplicated indexing assumptions without adding ISR overhead, while static assertions expose unsupported mappings during compilation. A runtime lookup is simpler conceptually but inappropriate for the latency-sensitive interrupt path.

Files changed (6) +96 / -64

Bug fix (6) +96 / -64
timer.hDefine platform-specific packed timer index mappings +8/-0

Define platform-specific packed timer index mappings

• Adds TIMER_INDEX mappings beside each hardware timer count. Contiguous F4/F7 timers retain n-1 indexing, while H7 and AT32 mappings account for numbering gaps.

src/main/drivers/timer.h

timer_at32f43x.cIndex AT32 timer definitions through TIMER_INDEX +18/-15

Index AT32 timer definitions through TIMER_INDEX

• Replaces hand-written array slots with TIMER_INDEX designators, including mapping TMR20 into slot 14. Adds a static assertion that the final timer occupies the final context slot.

src/main/drivers/timer_at32f43x.c

timer_impl.hMap IRQ handlers to bounded packed timer contexts +18/-6

Map IRQ handlers to bounded packed timer contexts

• Changes AT32 and STM32 single- and dual-timer handler macros to select timerCtx through TIMER_INDEX. Adds compile-time bounds checks and includes the utility header providing STATIC_ASSERT.

src/main/drivers/timer_impl.h

timer_stm32f4xx.cUse shared mappings for F4 timer table slots +18/-15

Use shared mappings for F4 timer table slots

• Rewrites F4 timer table designators to use TIMER_INDEX without changing contiguous slot assignments. Adds a static assertion for the final TIM14 slot.

src/main/drivers/timer_stm32f4xx.c

timer_stm32f7xx.cUse shared mappings for F7 timer table slots +17/-14

Use shared mappings for F7 timer table slots

• Replaces explicit F7 array indices with TIMER_INDEX designators while preserving existing assignments. Adds a static assertion that TIM14 maps to the table's final slot.

src/main/drivers/timer_stm32f7xx.c

timer_stm32h7xx.cMap noncontiguous H7 timers into packed slots +17/-14

Map noncontiguous H7 timers into packed slots

• Uses TIMER_INDEX for the packed H7 table containing TIM1-TIM8 and TIM12-TIM17. Adds a static assertion confirming TIM17 maps to the final allocated slot.

src/main/drivers/timer_stm32h7xx.c

@qodo-free-for-open-source-projects

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 📎 Requirement gaps (0)

Grey Divider

Great, no issues found!

Qodo reviewed your code and found no material issues that require review

Grey Divider

Tip of the day
💡 Did you know, you can switch off images and animations for a plain-text comment

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants