Skip to content

Conversation

@Spooky-Firefox
Copy link

@Spooky-Firefox Spooky-Firefox commented Oct 3, 2025

Trampoline Feature for Hardware Tasks

This pull request introduces a new trampoline feature for hardware tasks in RTIC.
It allows an interrupt handler to pend another interrupt (the trampoline), which then executes the actual task code.
This approach improves interrupt handling flexibility and source masking safety (as some exceptions cannot be masked through the NVIC).

The implementation includes changes to parsing, code generation, and error checking for correct usage.
It also includes a small CI fix and a minor clippy compliance improvement to the rtic-monotonics crate.


Overview

Trampoline Behavior

Normally, when using the source masking backend, we expect that a lower-priority exception waits until the higher-priority task finishes:

  lock     |     _______________
           |     |             |
  exception|     |             | =========
  task     | ____| _  _  _  _  |_
           |_______________________________

However, because we artificially raise priority by disabling interrupts (not exceptions), the effective task priority remains unchanged.
This means that lower-priority exceptions can still preempt the task:

  lock     |     ______          ______
           |     |                    |
  exception|     |     ==========     | 
  task     | ____| _  _          _  _ |_
           |_______________________________

The trampoline mechanism solves this by pended interrupts that are properly masked by the source masking backend, preserving the Stack Resource Policy (SRP):

  lock     |     ______      ______
           |     |                |
  exception|     |     =pend=     | ~~trampoline irq~~
  task     | ____| _  _      _  _ |_
           |___________________________________________

As illustrated, there is a brief priority inversion when pended, but since no critical section is executed during this window, it is not problematic.
We also do not enforce a trampoline when shared resources are not used — these cases still involve a minor priority inversion, but since shared data is untouched, this trade-off was accepted.

For more discussion, see issue #1088.


Implementation Details

  • Added support for the trampoline argument in hardware task definitions, allowing an interrupt handler to pend another interrupt that executes the task logic.
    [1]
    [2]
  • Updated code generation: when a trampoline is specified, the original interrupt handler only pends the trampoline, which then runs the user’s task code.
    [1]
    [2]
  • Improved source masking logic to account for trampolines and added checks preventing illegal combinations of exceptions, trampolines, and shared resources.
    [1]
    [2]
    [3]

Documentation and Changelog Updates

  • Updated changelogs to include the trampoline feature and its interaction with source masking.
    [1]
    [2]

Miscellaneous Improvements

  • Changed modulo operations to is_multiple_of() in rtic-monotonics for clippy compliance.
    [1]
    [2]
  • Fixed a CI issue caused by a leading space in a test output file.

Summary

These changes collectively enhance the flexibility and safety of interrupt handling in RTIC while improving CI reliability and code linting compliance.

@Spooky-Firefox Spooky-Firefox marked this pull request as draft October 3, 2025 18:24
Olle Ronstad and others added 5 commits October 6, 2025 12:50
@Spooky-Firefox Spooky-Firefox marked this pull request as ready for review October 6, 2025 14:50
Copy link
Contributor

@AfoHT AfoHT left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As mentioned on Matrix nitpicks so far, generally good stuff :)

Comment on lines +1 to +11
//! examples/bouncy_trampoline.rs
#![no_std]
#![no_main]
#![deny(warnings)]
#![deny(unsafe_code)]
#![deny(missing_docs)]

use panic_semihosting as _;

// `examples/bouncy_trampoline.rs` testing trampoline feature
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nitpick: Filename and comment are disagreeing :)


### Changed

- changed (sysclk % timer_hz) == 0 to sysclk.is_multiple_of(timer_hz) to make clippy happy as required to CI.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CI always uses latest stable Rust, so new versions bring in new lints/defaults all the time :)

Thank you for dealing with this, could you split out the 'rtic-monotonics' changes into a separate commit? If you want you could even create a separate PR for this, to help your colleague, that can be fast-tracked into master branch that way :)

Great you added to the CHANGELOG, but please add new entries above, building like a stack. Please cleanup the commentary and the double ### Changed headers while you're at it :P

@FeldrinH
Copy link

FeldrinH commented Oct 21, 2025

Is there some rationale somewhere for why this approach was chosen to solve #1088? Out of the alternatives mentioned in the discussion, trampolines seem to me the most cumbersome. They introduce a new public API which has to be documented and maintained indefinitely, they require a free interrupt to be usable, and they add the extra complexity that the actual code no longer runs in the exception handler, which introduces a number of new edge cases for users (e.g. when reasoning about pending interrupts/exceptions).

@Spooky-Firefox
Copy link
Author

Is there some rationale somewhere for why this approach was chosen to solve #1088? Out of the alternatives mentioned in the discussion, trampolines seem to me the most cumbersome. They introduce a new public API which has to be documented and maintained indefinitely, they require a free interrupt to be usable, and they add the extra complexity that the actual code no longer runs in the exception handler, which introduces a number of new edge cases for users (e.g. when reasoning about pending interrupts/exceptions).

The trampoline approach was chosen because it avoids using a global critical section, which would block higher-priority tasks. I agree that the solution is not ideal—it introduces an additional public API that must be maintained and documented, and it currently requires a free interrupt (something that could be improved later by reusing existing dispatchers).

However, I don’t fully agree that it adds complexity for the user. With trampolines, all tasks now adhere to the Stack Resource Policy (SRP), so users no longer need to handle exceptions that violate SRP manually. This makes timing and priority behavior easier to reason about overall.

@FeldrinH
Copy link

However, I don’t fully agree that it adds complexity for the user. With trampolines, all tasks now adhere to the Stack Resource Policy (SRP), so users no longer need to handle exceptions that violate SRP manually. This makes timing and priority behavior easier to reason about overall.

Perhaps. I suppose it depends on the specific use case. I'm going to trust the judgement of developers more familiar with the RTIC codebase on this.

I wonder what the performance overhead of the trampoline is, but I'm not sure how to best measure it.

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.

3 participants