diff options
-rw-r--r-- | examples/t-schedule.rs | 23 | ||||
-rw-r--r-- | macros/src/codegen/module.rs | 74 | ||||
-rw-r--r-- | macros/src/codegen/timer_queue.rs | 11 | ||||
-rw-r--r-- | macros/src/codegen/util.rs | 5 | ||||
-rw-r--r-- | src/cyccnt.rs | 221 | ||||
-rw-r--r-- | src/tq.rs | 23 |
6 files changed, 108 insertions, 249 deletions
diff --git a/examples/t-schedule.rs b/examples/t-schedule.rs index bd0ab668..6b6245eb 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::MyMono::SpawnHandle, ()> = foo::spawn_after(Seconds(1_u32)); + if let Ok(handle) = a { + let _: Result<foo::MyMono::SpawnHandle, ()> = handle.reschedule_after(Seconds(1_u32)); + } + + let b: Result<bar::MyMono::SpawnHandle, u32> = bar::spawn_after(Seconds(2_u32), 0); + if let Ok(handle) = b { + let _: Result<u32, ()> = handle.cancel(); + } + + let _: Result<baz::MyMono::SpawnHandle, (u32, u32)> = + 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::MyMono::SpawnHandle, ()> = foo::spawn_at(MyMono::now() + Seconds(3_u32)); + let _: Result<bar::MyMono::SpawnHandle, u32> = + bar::spawn_at(MyMono::now() + Seconds(4_u32), 0); + let _: Result<baz::MyMono::SpawnHandle, (u32, u32)> = + 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 75435b54..fb028e01 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<D>(self, duration: D) -> Result<Self, ()> + 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<Self, ()> + { + 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<D>( duration: D #(,#args)* - ) -> Result<(), #ty> + ) -> Result<SpawnHandle, #ty> 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<SpawnHandle, #ty> { 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 82d0ac98..33905516 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<TokenStrea let mut items = vec![]; if !app.monotonics.is_empty() { + // Generate the marker counter used to track for `cancel` and `reschedule` + let tq_marker = util::mark_internal_ident(&util::timer_queue_marker_ident()); + items.push(quote!( + // #[doc = #doc] + #[doc(hidden)] + #[allow(non_camel_case_types)] + static mut #tq_marker: u32 = 0; + )); + let t = util::schedule_t_ident(); // Enumeration of `schedule`-able tasks @@ -32,7 +41,7 @@ pub fn codegen(app: &App, analysis: &Analysis, _extra: &Extra) -> Vec<TokenStrea #[doc(hidden)] #[allow(non_camel_case_types)] #[derive(Clone, Copy)] - enum #t { + pub enum #t { #(#variants,)* } )); diff --git a/macros/src/codegen/util.rs b/macros/src/codegen/util.rs index 6589f62d..9ccdbf78 100644 --- a/macros/src/codegen/util.rs +++ b/macros/src/codegen/util.rs @@ -89,6 +89,11 @@ pub fn interrupt_ident() -> 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 8e07b001..00000000 --- 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<Duration> 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<Duration> for Instant { - type Output = Self; - - fn add(mut self, dur: Duration) -> Self { - self += dur; - self - } -} - -impl ops::SubAssign<Duration> for Instant { - fn sub_assign(&mut self, dur: Duration) { - // NOTE see the NOTE in `<Instant as AddAssign<Duration>>::add_assign` - debug_assert!(dur.inner < (1 << 31)); - self.inner = self.inner.wrapping_sub(dur.inner as i32); - } -} - -impl ops::Sub<Duration> for Instant { - type Output = Self; - - fn sub(mut self, dur: Duration) -> Self { - self -= dur; - self - } -} - -impl ops::Sub<Instant> 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<Ordering> { - 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<u32> for Duration { - type Error = Infallible; - - fn try_into(self) -> Result<u32, Infallible> { - Ok(self.as_cycles()) - } -} - -impl ops::AddAssign for Duration { - fn add_assign(&mut self, dur: Duration) { - self.inner += dur.inner; - } -} - -impl ops::Add<Duration> 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<Duration> 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 } - } -} @@ -5,6 +5,15 @@ use crate::{ use core::cmp::Ordering; use heapless::{binary_heap::Min, ArrayLength, BinaryHeap}; +#[inline] +fn unwrapper<T, E>(val: Result<T, E>) -> T { + if let Ok(v) = val { + v + } else { + unreachable!("Your monotonic is not infallible") + } +} + pub struct TimerQueue<Mono, Task, N>(pub BinaryHeap<NotReady<Mono, Task>, N, Min>) where Mono: Monotonic, @@ -66,15 +75,6 @@ where self.0.is_empty() } - #[inline] - fn unwrapper<T, E>(val: Result<T, E>) -> T { - if let Ok(v) = val { - v - } else { - unreachable!("Your monotonic is not infallible") - } - } - /// Dequeue a task from the TimerQueue #[inline] pub fn dequeue<F>(&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<Mono>, pub task: Task, + pub marker: u32, } impl<Mono, Task> Eq for NotReady<Mono, Task> |