Skip to content

Commit

Permalink
Allow custom timer_lists for 2core
Browse files Browse the repository at this point in the history
  • Loading branch information
KoviRobi committed Dec 30, 2023
1 parent 654724e commit bf19dd6
Show file tree
Hide file tree
Showing 4 changed files with 108 additions and 77 deletions.
2 changes: 1 addition & 1 deletion examples/rp2040/multicore/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ default-target = "thumbv6m-none-eabi"
[dependencies]
cortex-m = "0.7.4"
cortex-m-rt = "0.7.1"
lilos = { path = "../../../os", default-features = false, features = ["2core"] }
lilos = { path = "../../../os", default-features = false, features = ["systick", "2core"] }
panic-halt = "0.2.0"
rp2040-boot2 = "0.2"
rp2040-hal = { version = "0.9.1", features = ["critical-section-impl"] }
Expand Down
67 changes: 36 additions & 31 deletions examples/rp2040/multicore/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ use core::pin::{pin, Pin};
use cortex_m::peripheral::syst::SystClkSource;
use cortex_m_rt::exception;
use lilos::list::List;
use lilos::time::TickTime;

use embedded_hal::digital::v2::ToggleableOutputPin;

Expand Down Expand Up @@ -64,6 +65,7 @@ fn tick<const NOM: u32, const DENOM: u32>(
.micros()
}

#[export_name = "lilos::time::now"]
fn now() -> u64 {
tick::<1, 1_000>().to_millis()
}
Expand All @@ -85,7 +87,7 @@ fn SysTick() {

fn make_idle_task<'a, const NOM: u32, const DENOM: u32>(
core: &'a mut cortex_m::Peripherals,
timer_list: Pin<&'a List<u64>>,
timer_list: Pin<&'a List<TickTime>>,
sys_clk: hal::fugit::Rate<u32, NOM, DENOM>,
) -> impl FnMut() + 'a {
// Make it so that `wfe` waits for masked interrupts as well as events --
Expand All @@ -105,38 +107,37 @@ fn make_idle_task<'a, const NOM: u32, const DENOM: u32>(
core.SYST.set_clock_source(SystClkSource::Core);

move || {
if let Some(wakeup_at_ms) = timer_list.peek() {
let wakeup_in_ms =
u64::min(max_sleep_ms as u64, wakeup_at_ms - now());
if wakeup_in_ms > 0 {
let wakeup_in_ticks =
wakeup_in_ms as u32 * cycles_per_millisecond;
// Setting zero to the reload register disables systick
core.SYST.set_reload(wakeup_in_ticks);
core.SYST.clear_current();
core.SYST.enable_interrupt();
core.SYST.enable_counter();
match timer_list.peek() {
Some(wake_at_ms) => {
let wake_at_ms_u64 = wake_at_ms
.millis_since(TickTime::from_millis_since_boot(0))
.0;
let now_ms = now();
if wake_at_ms_u64 > now_ms {
let wake_in_ms =
u64::min(max_sleep_ms as u64, wake_at_ms_u64 - now_ms);
let wake_in_ticks =
wake_in_ms as u32 * cycles_per_millisecond;
// Setting zero to the reload register disables systick
core.SYST.set_reload(wake_in_ticks);
core.SYST.clear_current();
core.SYST.enable_interrupt();
core.SYST.enable_counter();
// We use `SEV` to signal from the other core that we can
// send more data. See also the comment above on SEVONPEND
cortex_m::asm::wfe();
} else {
// We just missed a timer, don't idle
}
}
None => {
// We use `SEV` to signal from the other core that we can send
// more data. See also the comment above on SEVONPEND
cortex_m::asm::wfe();
}
}
// We use `SEV` to signal from the other core that we can send more
// data. See also the comment above on SEVONPEND
cortex_m::asm::wfe();
timer_list.wake_less_than(now());
}
}

pub async fn sleep_until(timer_list: Pin<&List<u64>>, deadline: u64) {
// TODO: this early return means we can't simply return the insert_and_wait
// future below, which is costing us some bytes of text.
if now() >= deadline {
return;
timer_list.wake_less_than(TickTime::from_millis_since_boot(now()));
}

lilos::create_node!(node, deadline, lilos::exec::noop_waker());

// Insert our node into the pending timer list. If we get cancelled, the
// node will detach itself as it's being dropped.
timer_list.insert_and_wait(node.as_mut()).await;
}

#[bsp::entry]
Expand Down Expand Up @@ -205,7 +206,11 @@ fn main() -> ! {
// peripherals `p` from the enclosing stack frame.
loop {
let delay = sio.fifo.read_async().await;
sleep_until(timer_list, now() + delay as u64).await;
lilos::time::sleep_for(
timer_list,
lilos::time::Millis(delay as u64),
)
.await;
led.toggle().unwrap();
}
});
Expand Down
38 changes: 7 additions & 31 deletions os/src/exec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ use portable_atomic::{AtomicUsize, Ordering};
// still have some intimate dependencies between the modules. You'll see a few
// other cfg(feature = "systick") lines below.
cfg_if::cfg_if! {
if #[cfg(feature = "systick")] {
if #[cfg(all(feature = "systick", not(feature = "2core")))] {
use portable_atomic::AtomicPtr;

use crate::cheap_assert;
Expand Down Expand Up @@ -520,17 +520,11 @@ pub unsafe fn run_tasks_with_preemption_and_idle(
WAKE_BITS.fetch_or(initial_mask & this_mask, Ordering::SeqCst);

// TODO make this list static for more predictable memory usage
#[cfg(feature = "systick")]
#[cfg(all(feature = "systick", not(feature = "2core")))]
{
create_list!(timer_list);

#[cfg(not(feature = "2core"))]
let tl_ref = &TIMER_LIST;

#[cfg(feature = "2core")]
let tl_ref = &TIMER_LIST[core as usize];

let old_list = tl_ref.swap(
let old_list = TIMER_LIST.swap(
// Safety: since we've gotten a &mut, we hold the only reference, so
// it's safe for us to smuggle it through a pointer and reborrow it as
// shared.
Expand All @@ -543,7 +537,7 @@ pub unsafe fn run_tasks_with_preemption_and_idle(

loop {
interrupts.scope(|| {
#[cfg(feature = "systick")]
#[cfg(all(feature = "systick", not(feature = "2core")))]
{
// Scan for any expired timers.
with_timer_list(|tl| tl.wake_less_than(TickTime::now()));
Expand Down Expand Up @@ -934,18 +928,9 @@ pub fn wake_task_by_index(index: usize) {
static TIMER_LIST: AtomicPtr<List<TickTime>> =
AtomicPtr::new(core::ptr::null_mut());

#[cfg(all(feature = "systick", feature = "2core"))]
static TIMER_LIST: [AtomicPtr<List<TickTime>>; 2] = {
// See https://github.com/rust-lang/rust-clippy/issues/7665
#[allow(clippy::declare_interior_mutable_const)]
const INIT: AtomicPtr<List<TickTime>> =
AtomicPtr::new(core::ptr::null_mut());
[INIT; 2]
};

/// Panics if called from an interrupt service routine (ISR). This is used to
/// prevent OS features that are unavailable to ISRs from being used in ISRs.
#[cfg(feature = "systick")]
#[cfg(all(feature = "systick", not(feature = "2core")))]
fn assert_not_in_isr() {
let psr_value = cortex_m::register::apsr::read().bits();
// Bottom 9 bits are the exception number, which are 0 in Thread mode.
Expand All @@ -963,24 +948,15 @@ fn assert_not_in_isr() {
/// - Must not be called from an interrupt.
/// - Must only be called with a timer list available, which is to say, from
/// within a task.
#[cfg(feature = "systick")]
#[cfg(all(feature = "systick", not(feature = "2core")))]
pub(crate) fn with_timer_list<R>(
body: impl FnOnce(Pin<&List<TickTime>>) -> R,
) -> R {
// Prevent this from being used from interrupt context.
assert_not_in_isr();

let list_ref = {
#[cfg(not(feature = "2core"))]
let tl_ref = &TIMER_LIST;

#[cfg(feature = "2core")]
let tl_ref = {
let core = unsafe { cpu_core_id() };
&TIMER_LIST[core as usize]
};

let tlptr = tl_ref.load(Ordering::Acquire);
let tlptr = TIMER_LIST.load(Ordering::Acquire);
// If this assertion fails, it's a sign that one of the timer-aware OS
// primitives (likely a `sleep_*`) has been used without the OS actually
// running.
Expand Down
78 changes: 64 additions & 14 deletions os/src/time.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,13 +77,17 @@
//! Currently there's no example of how to do this in the repo. If you need
//! this, please file an issue.

#[cfg(feature = "2core")]
use crate::list::List;

use core::future::Future;
use core::ops::{Add, AddAssign};
use core::pin::Pin;
use core::task::{Context, Poll};
use core::time::Duration;

use cortex_m::peripheral::{syst::SystClkSource, SYST};
#[cfg(not(feature = "2core"))]
use cortex_m_rt::exception;
use pin_project_lite::pin_project;

Expand Down Expand Up @@ -116,6 +120,7 @@ pub struct TickTime(u64);

impl TickTime {
/// Retrieves the current value of the tick counter.
#[cfg(not(feature = "2core"))]
pub fn now() -> Self {
// This loop will only repeat if e != e2, which means we raced the
// systick ISR. Since that ISR only occurs once per millisecond, this
Expand All @@ -130,6 +135,15 @@ impl TickTime {
}
}

#[cfg(feature = "2core")]
pub fn now() -> Self {
extern "C" {
#[link_name = "lilos::time::now"]
fn now() -> u64;
}
Self(unsafe { now() })
}

/// Constructs a `TickTime` value describing a certain number of
/// milliseconds since the executor booted.
pub fn from_millis_since_boot(m: u64) -> Self {
Expand Down Expand Up @@ -276,7 +290,10 @@ impl From<u64> for Millis {
/// **Cancel safety:** Strict.
///
/// Dropping this future does nothing in particular.
pub async fn sleep_until(deadline: TickTime) {
pub async fn sleep_until(
#[cfg(feature = "2core")] timer_list: Pin<&List<TickTime>>,
deadline: TickTime,
) {
// TODO: this early return means we can't simply return the insert_and_wait
// future below, which is costing us some bytes of text.
if TickTime::now() >= deadline {
Expand All @@ -287,7 +304,13 @@ pub async fn sleep_until(deadline: TickTime) {

// Insert our node into the pending timer list. If we get cancelled, the
// node will detach itself as it's being dropped.
crate::exec::with_timer_list(|tl| tl.insert_and_wait(node.as_mut())).await
cfg_if::cfg_if! {
if #[cfg(not(feature = "2core"))] {
crate::exec::with_timer_list(|tl| tl.insert_and_wait(node.as_mut())).await
} else {
timer_list.insert_and_wait(node.as_mut()).await
}
}
}

/// Sleeps until the system time has increased by `d`.
Expand All @@ -309,11 +332,18 @@ pub async fn sleep_until(deadline: TickTime) {
/// **Cancel safety:** Strict.
///
/// Dropping this future does nothing in particular.
pub fn sleep_for<D>(d: D) -> impl Future<Output = ()>
pub fn sleep_for<'a, D>(
#[cfg(feature = "2core")] timer_list: Pin<&'a List<TickTime>>,
d: D,
) -> impl Future<Output = ()> + 'a
where
TickTime: Add<D, Output = TickTime>,
{
sleep_until(TickTime::now() + d)
sleep_until(
#[cfg(feature = "2core")]
timer_list,
TickTime::now() + d,
)
}

/// Alters a future to impose a deadline on its completion.
Expand All @@ -335,15 +365,20 @@ where
/// In this case, `await` drops the future as soon as it resolves (as always),
/// which means the nested `some_operation()` future will be promptly dropped
/// when we notice that the deadline has been met or exceeded.
pub fn with_deadline<F>(
pub fn with_deadline<'a, F>(
#[cfg(feature = "2core")] timer_list: Pin<&'a List<TickTime>>,
deadline: TickTime,
code: F,
) -> impl Future<Output = Option<F::Output>>
) -> impl Future<Output = Option<F::Output>> + 'a
where
F: Future,
F: Future + 'a,
{
TimeLimited {
limiter: sleep_until(deadline),
limiter: sleep_until(
#[cfg(feature = "2core")]
timer_list,
deadline,
),
process: code,
}
}
Expand All @@ -356,15 +391,21 @@ where
/// as the deadline for the returned future.
///
/// See [`with_deadline`] for more details.
pub fn with_timeout<D, F>(
pub fn with_timeout<'a, D, F>(
#[cfg(feature = "2core")] timer_list: Pin<&'a List<TickTime>>,
timeout: D,
code: F,
) -> impl Future<Output = Option<F::Output>>
) -> impl Future<Output = Option<F::Output>> + 'a
where
F: Future,
F: Future + 'a,
TickTime: Add<D, Output = TickTime>,
{
with_deadline(TickTime::now() + timeout, code)
with_deadline(
#[cfg(feature = "2core")]
timer_list,
TickTime::now() + timeout,
code,
)
}

pin_project! {
Expand Down Expand Up @@ -479,8 +520,16 @@ impl PeriodicGate {
/// **Cancel safety:** Strict.
///
/// Dropping this future does nothing in particular.
pub async fn next_time(&mut self) {
sleep_until(self.next).await;
pub async fn next_time(
&mut self,
#[cfg(feature = "2core")] timer_list: Pin<&List<TickTime>>,
) {
sleep_until(
#[cfg(feature = "2core")]
timer_list,
self.next,
)
.await;
self.next += self.interval;
}
}
Expand All @@ -489,6 +538,7 @@ impl PeriodicGate {
/// code in `exec` for that.
#[doc(hidden)]
#[exception]
#[cfg(all(feature = "systick", not(feature = "2core")))]
fn SysTick() {
if TICK.fetch_add(1, Ordering::Release) == core::u32::MAX {
EPOCH.fetch_add(1, Ordering::Release);
Expand Down

0 comments on commit bf19dd6

Please sign in to comment.