Interrupt configuration
Interrupts are core to the operation of RTFM applications. Correctly setting interrupt priorities and ensuring they remain fixed at runtime is a requisite for the memory safety of the application.
The RTFM framework exposes interrupt priorities as something that is declared at
compile time. However, this static configuration must be programmed into the
relevant registers during the initialization of the application. The interrupt
configuration is done before the init
function runs.
This example gives you an idea of the code that the RTFM framework runs:
# #![allow(unused_variables)] #fn main() { #[rtfm::app(device = ..)] const APP: () = { #[init] fn init(c: init::Context) { // .. user code .. } #[idle] fn idle(c: idle::Context) -> ! { // .. user code .. } #[interrupt(binds = UART0, priority = 2)] fn foo(c: foo::Context) { // .. user code .. } }; #}
The framework generates an entry point that looks like this:
// the real entry point of the program #[no_mangle] unsafe fn main() -> ! { // transforms a logical priority into a hardware / NVIC priority fn logical2hw(priority: u8) -> u8 { // this value comes from the device crate const NVIC_PRIO_BITS: u8 = ..; // the NVIC encodes priority in the higher bits of a bit // also a bigger numbers means lower priority ((1 << NVIC_PRIORITY_BITS) - priority) << (8 - NVIC_PRIO_BITS) } cortex_m::interrupt::disable(); let mut core = cortex_m::Peripheral::steal(); core.NVIC.enable(Interrupt::UART0); // value specified by the user let uart0_prio = 2; // check at compile time that the specified priority is within the supported range let _ = [(); (1 << NVIC_PRIORITY_BITS) - (uart0_prio as usize)]; core.NVIC.set_priority(Interrupt::UART0, logical2hw(uart0_prio)); // call into user code init(/* .. */); // .. cortex_m::interrupt::enable(); // call into user code idle(/* .. */) }