rtic-rs / rtic

Real-Time Interrupt-driven Concurrency (RTIC) framework for ARM Cortex-M microcontrollers
https://rtic.rs
Apache License 2.0
1.81k stars 209 forks source link

Trouble using RTIC with Adafruit GCM4 #441

Closed kdsch closed 3 years ago

kdsch commented 3 years ago

I'm a newcomer to Rust and embedded Rust. There's a good chance I have made a basic mistake.

See my project (RTIC integration branch).

I expect a PWM signal on D13 (red LED). Without RTIC, it works. With RTIC, nothing happens.

I do not yet have a semihosting or breakpoint debugger setup, so my debugging options are extremely limited. Unfortunately, QEMU does not emulate the SAMD51 microcontroller.

For reference, target/rtic-expansion.rs:

#[allow(non_snake_case)]
fn init(c: init::Context) {
    c.spawn.foo().unwrap();
}
#[allow(non_snake_case)]
fn foo(_: foo::Context) {
    use rtic::Mutex as _;
    pwm();
}
#[allow(non_snake_case)]
#[doc = "Initialization function"]
pub mod init {
    #[doc = "Tasks that can be `spawn`-ed from this context"]
    #[derive(Clone, Copy)]
    pub struct Spawn {
        _not_send: core::marker::PhantomData<*mut ()>,
    }
    #[doc = r" Execution context"]
    pub struct Context {
        #[doc = r" Core (Cortex-M) peripherals"]
        pub core: rtic::export::Peripherals,
        #[doc = r" Device peripherals"]
        pub device: atsamd51p::Peripherals,
        #[doc = "Tasks that can be `spawn`-ed from this context"]
        pub spawn: Spawn,
    }
    impl Context {
        #[inline(always)]
        pub unsafe fn new(core: rtic::export::Peripherals) -> Self {
            Context {
                device: atsamd51p::Peripherals::steal(),
                core,
                spawn: Spawn {
                    _not_send: core::marker::PhantomData,
                },
            }
        }
    }
}
#[allow(non_snake_case)]
#[doc = "Software task"]
pub mod foo {
    #[doc = r" Execution context"]
    pub struct Context {}
    impl Context {
        #[inline(always)]
        pub unsafe fn new(priority: &rtic::export::Priority) -> Self {
            Context {}
        }
    }
}
#[doc = r" Implementation details"]
const APP: () = {
    #[doc = r" Always include the device crate which contains the vector table"]
    use atsamd51p as _;
    #[cfg(core = "1")]
    compile_error!("specified 1 core but tried to compile for more than 1 core");
    #[doc = r" Queue version of a free-list that keeps track of empty slots in"]
    #[doc = r" the following buffers"]
    static mut foo_S0_FQ: rtic::export::SCFQ<rtic::export::consts::U1> =
        rtic::export::Queue(unsafe { rtic::export::iQueue::u8_sc() });
    #[link_section = ".uninit.rtic0"]
    #[doc = r" Buffer that holds the inputs of a task"]
    static mut foo_S0_INPUTS: [core::mem::MaybeUninit<()>; 1] = [core::mem::MaybeUninit::uninit()];
    #[allow(non_camel_case_types)]
    #[derive(Clone, Copy)]
    #[doc = "Software tasks spawned from core #0 to be dispatched at priority level 1 by core #0"]
    enum R0_P1_S0_T {
        foo,
    }
    #[doc = "Queue of tasks sent by core #0 ready to be dispatched by core #0 at priority level 1"]
    static mut R0_P1_S0_RQ: rtic::export::SCRQ<R0_P1_S0_T, rtic::export::consts::U1> =
        rtic::export::Queue(unsafe { rtic::export::iQueue::u8_sc() });
    #[allow(non_snake_case)]
    #[doc = "Interrupt handler used by core #0 to dispatch tasks at priority 1"]
    #[no_mangle]
    unsafe fn SERCOM0_0() {
        #[doc = r" The priority of this interrupt handler"]
        const PRIORITY: u8 = 1u8;
        rtic::export::run(PRIORITY, || {
            while let Some((task, index)) = R0_P1_S0_RQ.split().1.dequeue() {
                match task {
                    R0_P1_S0_T::foo => {
                        let () = foo_S0_INPUTS
                            .get_unchecked(usize::from(index))
                            .as_ptr()
                            .read();
                        foo_S0_FQ.split().0.enqueue_unchecked(index);
                        let priority = &rtic::export::Priority::new(PRIORITY);
                        crate::foo(foo::Context::new(priority))
                    }
                }
            }
        });
    }
    impl init::Spawn {
        fn foo(&self) -> Result<(), ()> {
            unsafe {
                use rtic::Mutex as _;
                let input = ();
                if let Some(index) = foo_S0_FQ.dequeue() {
                    foo_S0_INPUTS
                        .get_unchecked_mut(usize::from(index))
                        .as_mut_ptr()
                        .write(input);
                    R0_P1_S0_RQ.enqueue_unchecked((R0_P1_S0_T::foo, index));
                    rtic::pend(atsamd51p::Interrupt::SERCOM0_0);
                    Ok(())
                } else {
                    Err(input)
                }
            }
        }
    }
    #[no_mangle]
    unsafe extern "C" fn main() -> ! {
        let _TODO: () = ();
        rtic::export::interrupt::disable();
        (0..1u8).for_each(|i| foo_S0_FQ.enqueue_unchecked(i));
        let mut core: rtic::export::Peripherals = rtic::export::Peripherals::steal().into();
        let _ = [(); ((1 << atsamd51p::NVIC_PRIO_BITS) - 1u8 as usize)];
        core.NVIC.set_priority(
            atsamd51p::Interrupt::SERCOM0_0,
            rtic::export::logical2hw(1u8, atsamd51p::NVIC_PRIO_BITS),
        );
        rtic::export::NVIC::unmask(atsamd51p::Interrupt::SERCOM0_0);
        core.SCB.scr.modify(|r| r | 1 << 1);
        let late = crate::init(init::Context::new(core.into()));
        rtic::export::interrupt::enable();
        loop {
            rtic::export::wfi()
        }
    }
};
kdsch commented 3 years ago

After carefully reading rtic-expansion.rs, I realized that RTIC is taking control of the peripherals (device: atsamd51p::Peripherals::steal(),) and making them accessible via Context. Somehow I had managed to ignore this critical aspect of RTIC usage!

After modifying my program to use the context parameter instead of calling take(), it works!