mirror of
https://github.com/rtic-rs/rtic.git
synced 2024-11-23 20:22:51 +01:00
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
This commit is contained in:
parent
4bdc187912
commit
1087f2ee64
6 changed files with 108 additions and 249 deletions
|
@ -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();
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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,)*
|
||||
}
|
||||
));
|
||||
|
|
|
@ -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();
|
||||
|
|
221
src/cyccnt.rs
221
src/cyccnt.rs
|
@ -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 }
|
||||
}
|
||||
}
|
23
src/tq.rs
23
src/tq.rs
|
@ -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>
|
||||
|
|
Loading…
Reference in a new issue