Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Executor alarm missed in nrf time driver #3672

Closed
lulf opened this issue Dec 20, 2024 · 1 comment · Fixed by #3705
Closed

Executor alarm missed in nrf time driver #3672

lulf opened this issue Dec 20, 2024 · 1 comment · Fixed by #3705

Comments

@lulf
Copy link
Member

lulf commented Dec 20, 2024

At Akiles we have have seen some cases where a custom critical section that unmasks certain interrupts (see https://github.com/alexmoon/nrf-sdc/blob/main/nrf-mpsl/src/critical_section_impl.rs#L8), causing the nrf time driver alarm to be missed.

The following code makes the assumption that the time between now() and writing the cc register takes less than 3 ticks: https://github.com/embassy-rs/embassy/blob/main/embassy-nrf/src/time_driver.rs#L212-L252

When using a custom critical section that preempts the time driver in set_alarm, combined with a task that uses a short delay (100 microseconds) wanting to schedule the alarm close to 3 or 4 ticks in the future, it may cause the time driver to miss setting the alarm, causing the executor to not be alarmed of the next task to run.

@Dirbaio
Copy link
Member

Dirbaio commented Dec 20, 2024

repro:

do these changes to Cargo.toml

[dependencies]
cortex-m = { version = "0.7.6", features = ["inline-asm"] }  # remove critical-section-single-core
nrf-mpsl = { git = "https://github.com/alexmoon/nrf-sdc", features = ["critical-section-impl", "nrf52840"] }

[patch.crates-io]
embassy-futures = { path = "../../embassy-futures" }
embassy-sync = { path = "../../embassy-sync" }
embassy-executor = { path = "../../embassy-executor" }
embassy-time = { path = "../../embassy-time" }
embassy-nrf = { path = "../../embassy-nrf" }
embassy-net = { path = "../../embassy-net" }
embassy-usb = { path = "../../embassy-usb" }
embassy-net-esp-hosted = { path = "../../embassy-net-esp-hosted" }
embassy-net-enc28j60 = { path = "../../embassy-net-enc28j60" }

then run

#![no_std]
#![no_main]

use core::mem;

use defmt::{info, unwrap};
use embassy_executor::Spawner;
use embassy_nrf::interrupt;
use embassy_nrf::pac;
use embassy_nrf::peripherals::TIMER0;
use embassy_nrf::{
    interrupt::InterruptExt,
    timer::{Frequency, Timer},
};
use embassy_time::{Duration, Timer as LolTimer};
use {defmt_rtt as _, panic_probe as _};

use nrf_mpsl::{self as _};

static mut CYCLES: u32 = 100;

#[interrupt]
unsafe fn TIMER0() {
    cortex_m::asm::delay(100000); // 3ms

    pac::TIMER0.events_compare(0).write_value(0);

    let timer: Timer<'_, TIMER0> = mem::transmute(());

    CYCLES += 10;
    timer.cc(0).write(CYCLES);
    timer.clear();
    timer.start();
}

#[embassy_executor::main]
async fn main(spawner: Spawner) {
    let p = embassy_nrf::init(Default::default());

    unsafe { interrupt::TIMER0.enable() }

    let timer = Timer::new(p.TIMER0);
    pac::TIMER0.intenset().write(|w| w.set_compare(0, true));
    timer.set_frequency(Frequency::F16MHz);
    timer.clear();
    timer.cc(0).write(10000);
    timer.cc(0).short_compare_stop();
    timer.start();

    loop {
        info!("boo");
        LolTimer::after(Duration::from_ticks(4)).await;
    }
}

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 a pull request may close this issue.

2 participants