From 1087f2ee64a5be1aedf3b702ccb5d86cc64708d9 Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Thu, 11 Mar 2021 19:12:02 +0100 Subject: [PATCH] Added interface for cancel/reschedule Use wrapping add for marker No need to store handle to queue Remove unnecessary `SpawnHandle::new` Fix test Updated interface to follow proposal --- examples/t-schedule.rs | 23 +++- macros/src/codegen/module.rs | 74 ++++++++-- macros/src/codegen/timer_queue.rs | 11 +- macros/src/codegen/util.rs | 5 + src/cyccnt.rs | 221 ------------------------------ src/tq.rs | 23 ++-- 6 files changed, 108 insertions(+), 249 deletions(-) delete mode 100644 src/cyccnt.rs diff --git a/examples/t-schedule.rs b/examples/t-schedule.rs index bd0ab668b3..6b6245eb50 100644 --- a/examples/t-schedule.rs +++ b/examples/t-schedule.rs @@ -26,18 +26,29 @@ mod app { let mono = DwtSystick::new(&mut dcb, dwt, systick, 8_000_000); - let _: Result<(), ()> = foo::spawn_after(Seconds(1_u32)); - let _: Result<(), u32> = bar::spawn_after(Seconds(2_u32), 0); - let _: Result<(), (u32, u32)> = baz::spawn_after(Seconds(3_u32), 0, 1); + let a: Result = foo::spawn_after(Seconds(1_u32)); + if let Ok(handle) = a { + let _: Result = handle.reschedule_after(Seconds(1_u32)); + } + + let b: Result = bar::spawn_after(Seconds(2_u32), 0); + if let Ok(handle) = b { + let _: Result = handle.cancel(); + } + + let _: Result = + baz::spawn_after(Seconds(3_u32), 0, 1); (init::LateResources {}, init::Monotonics(mono)) } #[idle] fn idle(_: idle::Context) -> ! { - let _: Result<(), ()> = foo::spawn_at(MyMono::now() + Seconds(3_u32)); - let _: Result<(), u32> = bar::spawn_at(MyMono::now() + Seconds(4_u32), 0); - let _: Result<(), (u32, u32)> = baz::spawn_at(MyMono::now() + Seconds(5_u32), 0, 1); + let _: Result = foo::spawn_at(MyMono::now() + Seconds(3_u32)); + let _: Result = + bar::spawn_at(MyMono::now() + Seconds(4_u32), 0); + let _: Result = + baz::spawn_at(MyMono::now() + Seconds(5_u32), 0, 1); loop { cortex_m::asm::nop(); diff --git a/macros/src/codegen/module.rs b/macros/src/codegen/module.rs index 75435b549e..fb028e0119 100644 --- a/macros/src/codegen/module.rs +++ b/macros/src/codegen/module.rs @@ -264,15 +264,64 @@ pub fn codegen( }; let user_imports = &app.user_imports; + let tq_marker = util::mark_internal_ident(&util::timer_queue_marker_ident()); items.push(quote!( /// Holds methods related to this monotonic pub mod #m { + #[allow(unused_imports)] + use #app_path::#tq_marker; + #[allow(unused_imports)] + use #app_path::#t; #( #[allow(unused_imports)] #user_imports )* + pub struct SpawnHandle { + #[doc(hidden)] + marker: u32, + } + + // TODO: remove + impl core::fmt::Debug for SpawnHandle + { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + let handle = unsafe { &#app_path::#tq as *const _ as u32 }; + f.debug_struct("SpawnHandle") + .field("marker", &self.marker) + .field("handle", &handle) + .finish() + } + } + + impl SpawnHandle { + pub fn cancel(self) -> Result<#ty, ()> { + // TODO: Actually cancel... + // &mut #app_path::#tq; + + Err(()) + } + + #[inline] + pub fn reschedule_after(self, duration: D) -> Result + where D: rtic::time::duration::Duration + rtic::time::fixed_point::FixedPoint, + D::T: Into<<#app_path::#mono_type as rtic::time::Clock>::T>, + { + self.reschedule_at(#app_path::#m::now() + duration) + } + + pub fn reschedule_at(self, instant: rtic::time::Instant<#app_path::#mono_type>) -> Result + { + let _ = instant; + + // TODO: Actually reschedule... + // &mut #app_path::#tq; + + Err(()) + } + } + #(#cfgs)* /// Spawns the task after a set duration relative to the current time /// @@ -281,7 +330,7 @@ pub fn codegen( pub fn spawn_after( duration: D #(,#args)* - ) -> Result<(), #ty> + ) -> Result where D: rtic::time::duration::Duration + rtic::time::fixed_point::FixedPoint, D::T: Into<<#app_path::#mono_type as rtic::time::Clock>::T>, { @@ -300,7 +349,7 @@ pub fn codegen( pub fn spawn_at( instant: rtic::time::Instant<#app_path::#mono_type> #(,#args)* - ) -> Result<(), #ty> { + ) -> Result { unsafe { let input = #tupled; if let Some(index) = rtic::export::interrupt::free(|_| #app_path::#fq.dequeue()) { @@ -314,13 +363,17 @@ pub fn codegen( .as_mut_ptr() .write(instant); - let nr = rtic::export::NotReady { - instant, - index, - task: #app_path::#t::#name, - }; + rtic::export::interrupt::free(|_| { + let marker = #tq_marker; + let nr = rtic::export::NotReady { + instant, + index, + task: #app_path::#t::#name, + marker, + }; + + #tq_marker = #tq_marker.wrapping_add(1); - rtic::export::interrupt::free(|_| if let Some(mono) = #app_path::#m_ident.as_mut() { #app_path::#tq.enqueue_unchecked( nr, @@ -331,9 +384,10 @@ pub fn codegen( // We can only use the timer queue if `init` has returned, and it // writes the `Some(monotonic)` we are accessing here. core::hint::unreachable_unchecked() - }); + } - Ok(()) + Ok(SpawnHandle { marker }) + }) } else { Err(input) } diff --git a/macros/src/codegen/timer_queue.rs b/macros/src/codegen/timer_queue.rs index 82d0ac981e..33905516ef 100644 --- a/macros/src/codegen/timer_queue.rs +++ b/macros/src/codegen/timer_queue.rs @@ -9,6 +9,15 @@ pub fn codegen(app: &App, analysis: &Analysis, _extra: &Extra) -> Vec Vec Ident { Ident::new("interrupt", span) } +pub fn timer_queue_marker_ident() -> Ident { + let span = Span::call_site(); + Ident::new("TIMER_QUEUE_MARKER", span) +} + /// Whether `name` is an exception with configurable priority pub fn is_exception(name: &Ident) -> bool { let s = name.to_string(); diff --git a/src/cyccnt.rs b/src/cyccnt.rs deleted file mode 100644 index 8e07b00170..0000000000 --- a/src/cyccnt.rs +++ /dev/null @@ -1,221 +0,0 @@ -//! Data Watchpoint Trace (DWT) unit's CYCle CouNTer (CYCCNT) - -use core::{ - cmp::Ordering, - convert::{Infallible, TryInto}, - fmt, ops, -}; - -use cortex_m::peripheral::DWT; - -use crate::Fraction; - -/// A measurement of the CYCCNT. Opaque and useful only with `Duration` -/// -/// This data type is only available on ARMv7-M -/// -/// # Correctness -/// -/// Adding or subtracting a `Duration` of more than `(1 << 31)` cycles to an `Instant` effectively -/// makes it "wrap around" and creates an incorrect value. This is also true if the operation is -/// done in steps, e.g. `(instant + dur) + dur` where `dur` is `(1 << 30)` ticks. -#[derive(Clone, Copy, Eq, PartialEq)] -pub struct Instant { - inner: i32, -} - -impl Instant { - /// Returns an instant corresponding to "now" - /// - /// *HEADS UP* this function can, and will, return nonsensical values if called within `init`. - /// Only use it in `idle` and tasks. In `init`, use the `init::Context.start` field, or the - /// `CYCCNT::zero` function, instead of this function - pub fn now() -> Self { - Instant { - inner: DWT::get_cycle_count() as i32, - } - } - - /// Returns the amount of time elapsed since this instant was created. - pub fn elapsed(&self) -> Duration { - let diff = Instant::now().inner.wrapping_sub(self.inner); - assert!(diff >= 0, "instant now is earlier than self"); - Duration { inner: diff as u32 } - } - - /// Returns the amount of time elapsed from another instant to this one. - pub fn duration_since(&self, earlier: Instant) -> Duration { - let diff = self.inner.wrapping_sub(earlier.inner); - assert!(diff >= 0, "second instant is later than self"); - Duration { inner: diff as u32 } - } -} - -impl fmt::Debug for Instant { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_tuple("Instant") - .field(&(self.inner as u32)) - .finish() - } -} - -impl ops::AddAssign for Instant { - fn add_assign(&mut self, dur: Duration) { - // NOTE this is a debug assertion because there's no foolproof way to detect a wrap around; - // the user may write `(instant + dur) + dur` where `dur` is `(1<<31)-1` ticks. - debug_assert!(dur.inner < (1 << 31)); - self.inner = self.inner.wrapping_add(dur.inner as i32); - } -} - -impl ops::Add for Instant { - type Output = Self; - - fn add(mut self, dur: Duration) -> Self { - self += dur; - self - } -} - -impl ops::SubAssign for Instant { - fn sub_assign(&mut self, dur: Duration) { - // NOTE see the NOTE in `>::add_assign` - debug_assert!(dur.inner < (1 << 31)); - self.inner = self.inner.wrapping_sub(dur.inner as i32); - } -} - -impl ops::Sub for Instant { - type Output = Self; - - fn sub(mut self, dur: Duration) -> Self { - self -= dur; - self - } -} - -impl ops::Sub for Instant { - type Output = Duration; - - fn sub(self, other: Instant) -> Duration { - self.duration_since(other) - } -} - -impl Ord for Instant { - fn cmp(&self, rhs: &Self) -> Ordering { - self.inner.wrapping_sub(rhs.inner).cmp(&0) - } -} - -impl PartialOrd for Instant { - fn partial_cmp(&self, rhs: &Self) -> Option { - Some(self.cmp(rhs)) - } -} - -/// A `Duration` type to represent a span of time. -/// -/// This data type is only available on ARMv7-M -/// -/// # Correctness -/// -/// This type is *not* appropriate for representing time spans in the order of, or larger than, -/// seconds because it can hold a maximum of `(1 << 31)` "ticks" where each tick is the inverse of -/// the CPU frequency, which usually is dozens of MHz. -#[derive(Clone, Copy, Default, Eq, Ord, PartialEq, PartialOrd)] -pub struct Duration { - inner: u32, -} - -impl Duration { - /// Creates a new `Duration` from the specified number of clock cycles - pub fn from_cycles(cycles: u32) -> Self { - Duration { inner: cycles } - } - - /// Returns the total number of clock cycles contained by this `Duration` - pub fn as_cycles(&self) -> u32 { - self.inner - } -} - -impl TryInto for Duration { - type Error = Infallible; - - fn try_into(self) -> Result { - Ok(self.as_cycles()) - } -} - -impl ops::AddAssign for Duration { - fn add_assign(&mut self, dur: Duration) { - self.inner += dur.inner; - } -} - -impl ops::Add for Duration { - type Output = Self; - - fn add(self, other: Self) -> Self { - Duration { - inner: self.inner + other.inner, - } - } -} - -impl ops::SubAssign for Duration { - fn sub_assign(&mut self, rhs: Duration) { - self.inner -= rhs.inner; - } -} - -impl ops::Sub for Duration { - type Output = Self; - - fn sub(self, rhs: Self) -> Self { - Duration { - inner: self.inner - rhs.inner, - } - } -} - -/// Adds the `cycles` method to the `u32` type -/// -/// This trait is only available on ARMv7-M -pub trait U32Ext { - /// Converts the `u32` value into clock cycles - fn cycles(self) -> Duration; -} - -impl U32Ext for u32 { - fn cycles(self) -> Duration { - Duration { inner: self } - } -} - -/// Implementation of the `Monotonic` trait based on CYCle CouNTer -pub struct CYCCNT; - -impl crate::Monotonic for CYCCNT { - type Instant = Instant; - - fn ratio() -> Fraction { - Fraction { - numerator: 1, - denominator: 1, - } - } - - unsafe fn reset() { - (0xE0001004 as *mut u32).write_volatile(0) - } - - fn now() -> Instant { - Instant::now() - } - - fn zero() -> Instant { - Instant { inner: 0 } - } -} diff --git a/src/tq.rs b/src/tq.rs index 063bbd8e4a..f341d8cc3e 100644 --- a/src/tq.rs +++ b/src/tq.rs @@ -5,6 +5,15 @@ use crate::{ use core::cmp::Ordering; use heapless::{binary_heap::Min, ArrayLength, BinaryHeap}; +#[inline] +fn unwrapper(val: Result) -> T { + if let Ok(v) = val { + v + } else { + unreachable!("Your monotonic is not infallible") + } +} + pub struct TimerQueue(pub BinaryHeap, N, Min>) where Mono: Monotonic, @@ -66,15 +75,6 @@ where self.0.is_empty() } - #[inline] - fn unwrapper(val: Result) -> T { - if let Ok(v) = val { - v - } else { - unreachable!("Your monotonic is not infallible") - } - } - /// Dequeue a task from the TimerQueue #[inline] pub fn dequeue(&mut self, disable_interrupt: F, mono: &mut Mono) -> Option<(Task, u8)> @@ -84,7 +84,7 @@ where mono.clear_compare_flag(); if let Some(instant) = self.0.peek().map(|p| p.instant) { - if instant <= Self::unwrapper(Clock::try_now(mono)) { + if instant <= unwrapper(Clock::try_now(mono)) { // task became ready let nr = unsafe { self.0.pop_unchecked() }; @@ -97,7 +97,7 @@ where // dequeue. If the monotonic is fast enough it can happen that from the // read of now to the set of the compare, the time can overflow. This is to // guard against this. - if instant <= Self::unwrapper(Clock::try_now(mono)) { + if instant <= unwrapper(Clock::try_now(mono)) { let nr = unsafe { self.0.pop_unchecked() }; Some((nr.task, nr.index)) @@ -125,6 +125,7 @@ where pub index: u8, pub instant: Instant, pub task: Task, + pub marker: u32, } impl Eq for NotReady