Skip to content

Commit

Permalink
Use generics for reusable timer implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
KoviRobi committed Dec 30, 2023
1 parent bf19dd6 commit 7a38a60
Show file tree
Hide file tree
Showing 9 changed files with 498 additions and 451 deletions.
2 changes: 1 addition & 1 deletion examples/rp2040/minimal/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 = ["systick"] }
lilos = { path = "../../../os", default-features = false, features = ["timer", "systick"] }
panic-halt = "0.2.0"
rp2040-pac = {version = "0.3", features = ["rt"]}
rp2040-boot2 = "0.2"
Expand Down
3 changes: 2 additions & 1 deletion examples/rp2040/minimal/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ fn main() -> ! {
// peripherals `p` from the enclosing stack frame.
loop {
p.SIO.gpio_out_xor.write(|w| unsafe { w.bits(1 << 25) });
gate.next_time().await;
gate.next_time(&lilos::time::SysTickTimer).await;
}
});

Expand All @@ -68,5 +68,6 @@ fn main() -> ! {
lilos::exec::run_tasks(
&mut [blink], // <-- array of tasks
lilos::exec::ALL_TASKS, // <-- which to start initially
&lilos::time::SysTickTimer,
)
}
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 = ["systick", "2core"] }
lilos = { path = "../../../os", default-features = false, features = ["timer", "2core"] }
panic-halt = "0.2.0"
rp2040-boot2 = "0.2"
rp2040-hal = { version = "0.9.1", features = ["critical-section-impl"] }
Expand Down
82 changes: 45 additions & 37 deletions examples/rp2040/multicore/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,11 @@ 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;

type Instant = hal::fugit::Instant<u64, 1, 1_000_000>;

// For RP2040, we need to include a bootloader. The general Cargo build process
// doesn't have great support for this, so we included it as a binary constant.
#[link_section = ".boot_loader"]
Expand All @@ -51,23 +52,16 @@ fn cpu_core_id() -> u16 {
hal::Sio::core() as u16
}

fn tick<const NOM: u32, const DENOM: u32>(
) -> hal::fugit::Duration<u64, NOM, DENOM> {
fn tick() -> Instant {
let timer = unsafe { &*pac::TIMER::ptr() };
loop {
Instant::from_ticks(loop {
let e = timer.timerawh.read().bits();
let t = timer.timerawl.read().bits();
let e2 = timer.timerawh.read().bits();
if e == e2 {
break ((e as u64) << 32) | (t as u64);
}
}
.micros()
}

#[export_name = "lilos::time::now"]
fn now() -> u64 {
tick::<1, 1_000>().to_millis()
})
}

/// We mostly just need to not enter an infinite loop, which is what the
Expand All @@ -85,10 +79,10 @@ fn SysTick() {
}
}

fn make_idle_task<'a, const NOM: u32, const DENOM: u32>(
fn make_idle_task<'a>(
core: &'a mut cortex_m::Peripherals,
timer_list: Pin<&'a List<TickTime>>,
sys_clk: hal::fugit::Rate<u32, NOM, DENOM>,
timer_list: Pin<&'a List<Instant>>,
cycles_per_us: u32,
) -> impl FnMut() + 'a {
// Make it so that `wfe` waits for masked interrupts as well as events --
// the problem is that the idle-task is called with interrupts disabled (to
Expand All @@ -101,24 +95,22 @@ fn make_idle_task<'a, const NOM: u32, const DENOM: u32>(
core.SCB.scr.modify(|scr| scr | SEVONPEND);
}

let cycles_per_millisecond = sys_clk.to_kHz();
// 24-bit timer
let max_sleep_ms = ((1 << 24) - 1) / cycles_per_millisecond;
let max_sleep_us = ((1 << 24) - 1) / cycles_per_us;
core.SYST.set_clock_source(SystClkSource::Core);

move || {
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
Some(wake_at) => {
let now = tick();
if wake_at > now {
let wake_in_us = u64::min(
max_sleep_us as u64,
(wake_at - now).to_micros(),
);
let wake_in_ticks = wake_in_us as u32 * cycles_per_us;
// Setting zero to the reload register disables systick --
// systick is non-zero due to `wake_at > now`
core.SYST.set_reload(wake_in_ticks);
core.SYST.clear_current();
core.SYST.enable_interrupt();
Expand All @@ -136,7 +128,21 @@ fn make_idle_task<'a, const NOM: u32, const DENOM: u32>(
cortex_m::asm::wfe();
}
}
timer_list.wake_less_than(TickTime::from_millis_since_boot(now()));
}
}

struct Timer<'a> {
timer_list: Pin<&'a List<Instant>>,
}

impl<'a> lilos::time::Timer for Timer<'a> {
type Instant = Instant;
fn timer_list(&self) -> Pin<&'a List<Self::Instant>> {
self.timer_list
}

fn now(&self) -> Self::Instant {
tick()
}
}

Expand Down Expand Up @@ -192,10 +198,10 @@ fn main() -> ! {
let pac = unsafe { pac::Peripherals::steal() };
let mut sio = hal::Sio::new(pac.SIO);

lilos::create_list!(timer_list);
lilos::create_list!(timer_list, Instant::from_ticks(0));
let timer_list = timer_list.as_ref();

let idle_task = make_idle_task(&mut core, timer_list, sys_clk);
let timer = Timer { timer_list };
let idle_task = make_idle_task(&mut core, timer_list, sys_clk.to_MHz());

fifo::reset_read_fifo(&mut sio.fifo);

Expand All @@ -205,19 +211,16 @@ fn main() -> ! {
// Loop forever, blinking things. Note that this borrows the device
// peripherals `p` from the enclosing stack frame.
loop {
let delay = sio.fifo.read_async().await;
lilos::time::sleep_for(
timer_list,
lilos::time::Millis(delay as u64),
)
.await;
let delay = sio.fifo.read_async().await as u64;
lilos::time::sleep_for(&timer, delay.millis()).await;
led.toggle().unwrap();
}
});

lilos::exec::run_tasks_with_idle(
&mut [blink], // <-- array of tasks
lilos::exec::ALL_TASKS, // <-- which to start initially
&timer,
1,
idle_task,
)
Expand All @@ -240,10 +243,15 @@ fn main() -> ! {
}
});

lilos::create_list!(timer_list, Instant::from_ticks(0));
let timer_list = timer_list.as_ref();
let timer = Timer { timer_list };

// Set up and run the scheduler with a single task.
lilos::exec::run_tasks_with_idle(
&mut [compute_delay], // <-- array of tasks
lilos::exec::ALL_TASKS, // <-- which to start initially
&timer,
0,
// We use `SEV` to signal from the other core that we can send more
// data. See also the comment above on SEVONPEND
Expand Down
3 changes: 2 additions & 1 deletion os/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,10 @@ all-features = true
default-target = "thumbv7em-none-eabihf"

[features]
default = ["mutex", "spsc", "systick"]
default = ["mutex", "spsc", "timer", "systick"]
mutex = []
spsc = []
timer = []
systick = []
handoff = ["scopeguard"]
2core = []
Expand Down
Loading

0 comments on commit 7a38a60

Please sign in to comment.