From e4319de3d526285381f5cc53e14f9a17d123a81a Mon Sep 17 00:00:00 2001 From: Andrey Zgarbul Date: Sat, 3 Apr 2021 20:30:34 +0300 Subject: [PATCH 001/423] const generics --- Cargo.toml | 3 +- book/en/src/internals/tasks.md | 8 ++-- examples/shared.rs | 12 ++---- examples/static.rs | 12 ++---- macros/src/codegen/dispatchers.rs | 6 +-- macros/src/codegen/software_tasks.rs | 10 ++--- macros/src/codegen/timer_queue.rs | 4 +- macros/src/codegen/util.rs | 15 +------- src/export.rs | 9 ++--- src/linked_list.rs | 57 ++++++++++------------------ src/tq.rs | 8 ++-- 11 files changed, 50 insertions(+), 94 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 44f5786c91..e3cb010b5c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -44,9 +44,8 @@ cortex-m = "0.7.0" cortex-m-rtic-macros = { path = "macros", version = "0.6.0-alpha.5" } rtic-monotonic = "0.1.0-alpha.2" rtic-core = "0.3.1" -heapless = "0.6.1" +heapless = "0.7.1" bare-metal = "1.0.0" -generic-array = "0.14" [dependencies.dwt-systick-monotonic] version = "0.1.0-alpha.3" diff --git a/book/en/src/internals/tasks.md b/book/en/src/internals/tasks.md index a533dc0c26..0407176d60 100644 --- a/book/en/src/internals/tasks.md +++ b/book/en/src/internals/tasks.md @@ -78,8 +78,8 @@ mod app { } // ready queue of the task dispatcher - // `U4` is a type-level integer that represents the capacity of this queue - static mut RQ1: Queue, U4> = Queue::new(); + // `5-1=4` represents the capacity of this queue + static mut RQ1: Queue, 5> = Queue::new(); // interrupt handler chosen to dispatch tasks at priority `1` #[no_mangle] @@ -153,7 +153,7 @@ mod app { // used to track how many more `bar` messages can be enqueued // `U2` is the capacity of the `bar` task; a max of two instances can be queued // this queue is filled by the framework before `init` runs - static mut bar_FQ: Queue<(), U2> = Queue::new(); + static mut bar_FQ: Queue<(), 3> = Queue::new(); // Priority ceiling for the consumer endpoint of `bar_FQ` const bar_FQ_CEILING: u8 = 2; @@ -227,7 +227,7 @@ mod app { // the free queue: used to track free slots in the `baz_INPUTS` array // this queue is initialized with values `0` and `1` before `init` is executed - static mut baz_FQ: Queue = Queue::new(); + static mut baz_FQ: Queue = Queue::new(); // Priority ceiling for the consumer endpoint of `baz_FQ` const baz_FQ_CEILING: u8 = 2; diff --git a/examples/shared.rs b/examples/shared.rs index c3fa07b9c0..9585c3885e 100644 --- a/examples/shared.rs +++ b/examples/shared.rs @@ -10,23 +10,19 @@ use panic_semihosting as _; #[rtic::app(device = lm3s6965)] mod app { use cortex_m_semihosting::{debug, hprintln}; - use heapless::{ - consts::*, - i, - spsc::{Consumer, Producer, Queue}, - }; + use heapless::spsc::{Consumer, Producer, Queue}; use lm3s6965::Interrupt; #[shared] struct Shared { - p: Producer<'static, u32, U4>, - c: Consumer<'static, u32, U4>, + p: Producer<'static, u32, 5>, + c: Consumer<'static, u32, 5>, } #[local] struct Local {} - #[init(local = [q: Queue = Queue(i::Queue::new())])] + #[init(local = [q: Queue = Queue::new()])] fn init(cx: init::Context) -> (Shared, Local, init::Monotonics) { let (p, c) = cx.local.q.split(); diff --git a/examples/static.rs b/examples/static.rs index f51c5f2d10..0ea5d2df80 100644 --- a/examples/static.rs +++ b/examples/static.rs @@ -11,23 +11,19 @@ use panic_semihosting as _; mod app { use cortex_m_semihosting::{debug, hprintln}; - use heapless::{ - consts::*, - i, - spsc::{Consumer, Producer, Queue}, - }; + use heapless::spsc::{Consumer, Producer, Queue}; use lm3s6965::Interrupt; #[shared] struct Shared { - p: Producer<'static, u32, U4>, - c: Consumer<'static, u32, U4>, + p: Producer<'static, u32, 5>, + c: Consumer<'static, u32, 5>, } #[local] struct Local {} - #[init(local = [q: Queue = Queue(i::Queue::new())])] + #[init(local = [q: Queue = Queue::new()])] fn init(cx: init::Context) -> (Shared, Local, init::Monotonics) { let (p, c) = cx.local.q.split(); diff --git a/macros/src/codegen/dispatchers.rs b/macros/src/codegen/dispatchers.rs index ac55003658..c239b0f828 100644 --- a/macros/src/codegen/dispatchers.rs +++ b/macros/src/codegen/dispatchers.rs @@ -42,15 +42,13 @@ pub fn codegen(app: &App, analysis: &Analysis, _extra: &Extra) -> Vec), - quote!(rtic::export::Queue(unsafe { - rtic::export::iQueue::u8_sc() - })), + quote!(rtic::export::Queue::new()), ) }; diff --git a/macros/src/codegen/software_tasks.rs b/macros/src/codegen/software_tasks.rs index cfd21e40d1..0b07335946 100644 --- a/macros/src/codegen/software_tasks.rs +++ b/macros/src/codegen/software_tasks.rs @@ -32,8 +32,8 @@ pub fn codegen( let (_, _, _, input_ty) = util::regroup_inputs(inputs); let cap = task.args.capacity; - let cap_lit = util::capacity_literal(cap); - let cap_ty = util::capacity_typenum(cap, true); + let cap_lit = util::capacity_literal(cap as usize); + let cap_lit_p1 = util::capacity_literal(cap as usize + 1); // Create free queues and inputs / instants buffers let fq = util::fq_ident(name); @@ -41,10 +41,8 @@ pub fn codegen( let (fq_ty, fq_expr, mk_uninit): (_, _, Box Option<_>>) = { ( - quote!(rtic::export::SCFQ<#cap_ty>), - quote!(rtic::export::Queue(unsafe { - rtic::export::iQueue::u8_sc() - })), + quote!(rtic::export::SCFQ<#cap_lit_p1>), + quote!(rtic::export::Queue::new()), Box::new(|| util::link_section_uninit()), ) }; diff --git a/macros/src/codegen/timer_queue.rs b/macros/src/codegen/timer_queue.rs index 9e30d1001d..abddbadcc0 100644 --- a/macros/src/codegen/timer_queue.rs +++ b/macros/src/codegen/timer_queue.rs @@ -62,12 +62,12 @@ pub fn codegen(app: &App, analysis: &Analysis, _extra: &Extra) -> Vec>); diff --git a/macros/src/codegen/util.rs b/macros/src/codegen/util.rs index 86bd69551d..c2330d46f8 100644 --- a/macros/src/codegen/util.rs +++ b/macros/src/codegen/util.rs @@ -8,23 +8,10 @@ use syn::{Attribute, Ident, LitInt, PatType}; use crate::check::Extra; /// Turns `capacity` into an unsuffixed integer literal -pub fn capacity_literal(capacity: u8) -> LitInt { +pub fn capacity_literal(capacity: usize) -> LitInt { LitInt::new(&capacity.to_string(), Span::call_site()) } -/// Turns `capacity` into a type-level (`typenum`) integer -pub fn capacity_typenum(capacity: u8, round_up_to_power_of_two: bool) -> TokenStream2 { - let capacity = if round_up_to_power_of_two { - capacity.checked_next_power_of_two().expect("UNREACHABLE") - } else { - capacity - }; - - let ident = Ident::new(&format!("U{}", capacity), Span::call_site()); - - quote!(rtic::export::consts::#ident) -} - /// Identifier for the free queue pub fn fq_ident(task: &Ident) -> Ident { Ident::new(&format!("{}_FQ", task.to_string()), Span::call_site()) diff --git a/src/export.rs b/src/export.rs index 91a4a5efba..e449ef41d5 100644 --- a/src/export.rs +++ b/src/export.rs @@ -13,13 +13,12 @@ pub use cortex_m::{ peripheral::{scb::SystemHandler, syst::SystClkSource, DWT, NVIC}, Peripherals, }; -use heapless::spsc::SingleCore; -pub use heapless::{consts, i::Queue as iQueue, spsc::Queue}; -pub use heapless::{i::BinaryHeap as iBinaryHeap, BinaryHeap}; +pub use heapless::spsc::Queue; +pub use heapless::BinaryHeap; pub use rtic_monotonic as monotonic; -pub type SCFQ = Queue; -pub type SCRQ = Queue<(T, u8), N, u8, SingleCore>; +pub type SCFQ = Queue; +pub type SCRQ = Queue<(T, u8), N>; #[cfg(armv7m)] #[inline(always)] diff --git a/src/linked_list.rs b/src/linked_list.rs index 9ea4d19f5c..bbb935f82b 100644 --- a/src/linked_list.rs +++ b/src/linked_list.rs @@ -3,8 +3,6 @@ use core::marker::PhantomData; use core::mem::MaybeUninit; use core::ops::{Deref, DerefMut}; use core::ptr; -pub use generic_array::ArrayLength; -use generic_array::GenericArray; #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] struct LinkedIndex(u16); @@ -37,21 +35,19 @@ pub struct Node { } /// Iterator for the linked list. -pub struct Iter<'a, T, Kind, N> +pub struct Iter<'a, T, Kind, const N: usize> where T: PartialEq + PartialOrd, Kind: kind::Kind, - N: ArrayLength>, { list: &'a LinkedList, index: LinkedIndex, } -impl<'a, T, Kind, N> Iterator for Iter<'a, T, Kind, N> +impl<'a, T, Kind, const N: usize> Iterator for Iter<'a, T, Kind, N> where T: PartialEq + PartialOrd, Kind: kind::Kind, - N: ArrayLength>, { type Item = &'a T; @@ -66,11 +62,10 @@ where } /// Comes from [`LinkedList::find_mut`]. -pub struct FindMut<'a, T, Kind, N> +pub struct FindMut<'a, T, Kind, const N: usize> where T: PartialEq + PartialOrd, Kind: kind::Kind, - N: ArrayLength>, { list: &'a mut LinkedList, is_head: bool, @@ -79,11 +74,10 @@ where maybe_changed: bool, } -impl<'a, T, Kind, N> FindMut<'a, T, Kind, N> +impl<'a, T, Kind, const N: usize> FindMut<'a, T, Kind, N> where T: PartialEq + PartialOrd, Kind: kind::Kind, - N: ArrayLength>, { fn pop_internal(&mut self) -> T { if self.is_head { @@ -122,11 +116,10 @@ where } } -impl Drop for FindMut<'_, T, Kind, N> +impl Drop for FindMut<'_, T, Kind, N> where T: PartialEq + PartialOrd, Kind: kind::Kind, - N: ArrayLength>, { fn drop(&mut self) { // Only resort the list if the element has changed @@ -137,11 +130,10 @@ where } } -impl Deref for FindMut<'_, T, Kind, N> +impl Deref for FindMut<'_, T, Kind, N> where T: PartialEq + PartialOrd, Kind: kind::Kind, - N: ArrayLength>, { type Target = T; @@ -150,11 +142,10 @@ where } } -impl DerefMut for FindMut<'_, T, Kind, N> +impl DerefMut for FindMut<'_, T, Kind, N> where T: PartialEq + PartialOrd, Kind: kind::Kind, - N: ArrayLength>, { fn deref_mut(&mut self) -> &mut Self::Target { self.maybe_changed = true; @@ -162,11 +153,10 @@ where } } -impl fmt::Debug for FindMut<'_, T, Kind, N> +impl fmt::Debug for FindMut<'_, T, Kind, N> where T: PartialEq + PartialOrd + core::fmt::Debug, Kind: kind::Kind, - N: ArrayLength>, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("FindMut") @@ -189,23 +179,21 @@ where } /// The linked list. -pub struct LinkedList +pub struct LinkedList where T: PartialEq + PartialOrd, Kind: kind::Kind, - N: ArrayLength>, { - list: MaybeUninit, N>>, + list: MaybeUninit<[Node; N]>, head: LinkedIndex, free: LinkedIndex, _kind: PhantomData, } -impl LinkedList +impl LinkedList where T: PartialEq + PartialOrd, Kind: kind::Kind, - N: ArrayLength>, { /// Internal helper to not do pointer arithmetic all over the place. #[inline] @@ -266,7 +254,7 @@ where _kind: PhantomData, }; - let len = N::U16; + let len = N as u16; let mut free = 0; if len == 0 { @@ -451,11 +439,10 @@ where } } -impl Drop for LinkedList +impl Drop for LinkedList where T: PartialEq + PartialOrd, Kind: kind::Kind, - N: ArrayLength>, { fn drop(&mut self) { let mut index = self.head; @@ -471,11 +458,10 @@ where } } -impl fmt::Debug for LinkedList +impl fmt::Debug for LinkedList where T: PartialEq + PartialOrd + core::fmt::Debug, Kind: kind::Kind, - N: ArrayLength>, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_list().entries(self.iter()).finish() @@ -518,11 +504,10 @@ pub mod kind { mod tests { // Note this useful idiom: importing names from outer (for mod tests) scope. use super::*; - use generic_array::typenum::consts::*; #[test] fn test_peek() { - let mut ll: LinkedList = LinkedList::new(); + let mut ll: LinkedList = LinkedList::new(); ll.push(1).unwrap(); assert_eq!(ll.peek().unwrap(), &1); @@ -533,7 +518,7 @@ mod tests { ll.push(3).unwrap(); assert_eq!(ll.peek().unwrap(), &3); - let mut ll: LinkedList = LinkedList::new(); + let mut ll: LinkedList = LinkedList::new(); ll.push(2).unwrap(); assert_eq!(ll.peek().unwrap(), &2); @@ -547,7 +532,7 @@ mod tests { #[test] fn test_full() { - let mut ll: LinkedList = LinkedList::new(); + let mut ll: LinkedList = LinkedList::new(); ll.push(1).unwrap(); ll.push(2).unwrap(); ll.push(3).unwrap(); @@ -557,14 +542,14 @@ mod tests { #[test] fn test_empty() { - let ll: LinkedList = LinkedList::new(); + let ll: LinkedList = LinkedList::new(); assert!(ll.is_empty()) } #[test] fn test_zero_size() { - let ll: LinkedList = LinkedList::new(); + let ll: LinkedList = LinkedList::new(); assert!(ll.is_empty()); assert!(ll.is_full()); @@ -572,7 +557,7 @@ mod tests { #[test] fn test_rejected_push() { - let mut ll: LinkedList = LinkedList::new(); + let mut ll: LinkedList = LinkedList::new(); ll.push(1).unwrap(); ll.push(2).unwrap(); ll.push(3).unwrap(); @@ -585,7 +570,7 @@ mod tests { #[test] fn test_updating() { - let mut ll: LinkedList = LinkedList::new(); + let mut ll: LinkedList = LinkedList::new(); ll.push(1).unwrap(); ll.push(2).unwrap(); ll.push(3).unwrap(); diff --git a/src/tq.rs b/src/tq.rs index 985e0f8817..cd44abe2bc 100644 --- a/src/tq.rs +++ b/src/tq.rs @@ -1,5 +1,5 @@ use crate::{ - linked_list::{ArrayLength, LinkedList, Min, Node}, + linked_list::{LinkedList, Min}, time::{Clock, Instant}, Monotonic, }; @@ -14,16 +14,14 @@ fn unwrapper(val: Result) -> T { } } -pub struct TimerQueue(pub LinkedList, Min, N>) +pub struct TimerQueue(pub LinkedList, Min, N>) where Mono: Monotonic, - N: ArrayLength>>, Task: Copy; -impl TimerQueue +impl TimerQueue where Mono: Monotonic, - N: ArrayLength>>, Task: Copy, { pub fn new() -> Self { From 5e92715d8cfce6dd91168ece7ce07502eda3145e Mon Sep 17 00:00:00 2001 From: mriise Date: Tue, 20 Jul 2021 01:13:24 -0700 Subject: [PATCH 002/423] fix pool example --- examples/pool.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/pool.rs b/examples/pool.rs index 63be899e8c..7c61dfba9f 100644 --- a/examples/pool.rs +++ b/examples/pool.rs @@ -42,14 +42,14 @@ mod app { #[task(binds = I2C0, priority = 2)] fn i2c0(_: i2c0::Context) { - // claim a memory block, leave it uninitialized and .. - let x = P::alloc().unwrap().freeze(); + // claim a memory block, initialize it and .. + let x = P::alloc().unwrap().init([0u8; 128]); // .. send it to the `foo` task foo::spawn(x).ok().unwrap(); // send another block to the task `bar` - bar::spawn(P::alloc().unwrap().freeze()).ok().unwrap(); + bar::spawn(P::alloc().unwrap().init([0u8; 128])).ok().unwrap(); } #[task] From bf80035aef21dd9c84a26ed47bbd1bb4a596952f Mon Sep 17 00:00:00 2001 From: Andrey Zgarbul Date: Tue, 20 Jul 2021 11:44:03 +0300 Subject: [PATCH 003/423] rustfmt --- examples/pool.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/examples/pool.rs b/examples/pool.rs index 7c61dfba9f..010ee44ecc 100644 --- a/examples/pool.rs +++ b/examples/pool.rs @@ -49,7 +49,9 @@ mod app { foo::spawn(x).ok().unwrap(); // send another block to the task `bar` - bar::spawn(P::alloc().unwrap().init([0u8; 128])).ok().unwrap(); + bar::spawn(P::alloc().unwrap().init([0u8; 128])) + .ok() + .unwrap(); } #[task] From c4c964de7bdb9f5cc313414d27803e71a057ac64 Mon Sep 17 00:00:00 2001 From: Valentin Ochs Date: Tue, 20 Jul 2021 14:37:13 +0200 Subject: [PATCH 004/423] Change misleading documentation left over by PR #464 --- book/en/src/internals/tasks.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/book/en/src/internals/tasks.md b/book/en/src/internals/tasks.md index 0407176d60..db7afad525 100644 --- a/book/en/src/internals/tasks.md +++ b/book/en/src/internals/tasks.md @@ -151,7 +151,7 @@ mod app { const RQ1_CEILING: u8 = 2; // used to track how many more `bar` messages can be enqueued - // `U2` is the capacity of the `bar` task; a max of two instances can be queued + // `3-1=2` represents the capacity of this queue // this queue is filled by the framework before `init` runs static mut bar_FQ: Queue<(), 3> = Queue::new(); From 5f7dc0b903a3d26a5bd49864dce42f3d91d3d2a8 Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Wed, 14 Jul 2021 12:44:25 +0200 Subject: [PATCH 005/423] update the 0.5.x -> 0.6.0 migration guide to use the new resources syntax I also reordered the sections to cover all the resource API first before covering the spawn API I've also added a section about the old `static mut` variable transform --- book/en/src/migration/migration_v5.md | 201 +++++++++++++++++--------- 1 file changed, 136 insertions(+), 65 deletions(-) diff --git a/book/en/src/migration/migration_v5.md b/book/en/src/migration/migration_v5.md index d3f2c9a645..154714e354 100644 --- a/book/en/src/migration/migration_v5.md +++ b/book/en/src/migration/migration_v5.md @@ -102,6 +102,134 @@ mod app { This works also for ram functions, see examples/ramfunc.rs +## Resources structs - `#[shared]`, `#[local]` + +Previously the RTIC resources had to be in in a struct named exactly "Resources": + +``` rust +struct Resources { + // Resources defined in here +} +``` + +With RTIC v0.6.0 the resources structs are annotated similarly like +`#[task]`, `#[init]`, `#[idle]`: with the attributes `#[shared]` and `#[local]` + +``` rust +#[shared] +struct MySharedResources { + // Resources shared between tasks are defined here +} + +#[local] +struct MyLocalResources { + // Resources defined here cannot be shared between tasks; each one is local to a single task +} +``` + +These structs can be freely named by the developer. + +## `shared` and `local` arguments in `#[task]`s + +In v0.6.0 resources are split between `shared` resources and `local` resources. +`#[task]`, `#[init]` and `#[idle]` no longer have a `resources` argument; they must now use the `shared` and `local` arguments. + +In v0.5.x: + +``` rust +struct Resources { + local_to_b: i64, + shared_by_a_and_b: i64, +} + +#[task(resources = [shared_by_a_and_b])] +fn a(_: a::Context) {} + +#[task(resources = [shared_by_a_and_b, local_to_b])] +fn b(_: b::Context) {} +``` + +In v0.6.0: + +``` rust +#[shared] +struct Shared { + shared_by_a_and_b: i64, +} + +#[local] +struct Local { + local_to_b: i64, +} + +#[task(shared = [shared_by_a_and_b])] +fn a(_: a::Context) {} + +#[task(shared = [shared_by_a_and_b], local = [local_to_b])] +fn b(_: b::Context) {} +``` + +## Symmetric locks + +Now RTIC utilizes symmetric locks, this means that the `lock` method need to be used for all `shared` resource access. In old code one could do the following as the high priority task has exclusive access to the resource: + +``` rust +#[task(priority = 2, resources = [r])] +fn foo(cx: foo::Context) { + cx.resources.r = /* ... */; +} + +#[task(resources = [r])] +fn bar(cx: bar::Context) { + cx.resources.r.lock(|r| r = /* ... */); +} +``` + +And with symmetric locks one needs to use locks in both tasks: + +``` rust +#[task(priority = 2, shared = [r])] +fn foo(cx: foo::Context) { + cx.shared.r.lock(|r| r = /* ... */); +} + +#[task(shared = [r])] +fn bar(cx: bar::Context) { + cx.shared.r.lock(|r| r = /* ... */); +} +``` + +Note that the performance does not change thanks to LLVM's optimizations which optimizes away unnecessary locks. + +## no `static mut` transform + +`static mut` variables are no longer transformed to safe `&'static mut` references. +Instead of that syntax, use the `local` argument in `#[init]`. + +v0.5.x code: + +``` rust +#[init] +fn init(_: init::Context) { + static mut BUFFER: [u8; 1024] = [0; 1024]; + let buffer: &'static mut [u8; 1024] = BUFFER; +} +``` + +v0.6.0 code: + +``` rust +#[init(local = [ + buffer: [u8; 1024] = [0; 1024] +// type ^^^^^^^^^^^^ ^^^^^^^^^ initial value +])] +fn init(cx: init::Context) -> (Shared, Local, init::Monotonics) { + let buffer: &'static mut [u8; 1024] = cx.local.buffer; + + (Shared {}, Local {}, init::Monotonics {}) +} +``` + ## Init always returns late resources In order to make the API more symmetric the #[init]-task always returns a late resource. @@ -125,48 +253,23 @@ to this: ``` rust #[rtic::app(device = lm3s6965)] mod app { + #[shared] + struct MySharedResources {} + + #[local] + struct MyLocalResources {} + #[init] - fn init(_: init::Context) -> (init::LateResources, init::Monotonics) { + fn init(_: init::Context) -> (MySharedResources, MyLocalResources, init::Monotonics) { rtic::pend(Interrupt::UART0); - (init::LateResources {}, init::Monotonics()) + (MySharedResources, MyLocalResources, init::Monotonics {}) } // [more code] } ``` -## Resources struct - `#[resources]` - -Previously the RTIC resources had to be in in a struct named exactly "Resources": - -``` rust -struct Resources { - // Resources defined in here -} -``` - -With RTIC v0.6.0 the resources struct is annotated similarly like -`#[task]`, `#[init]`, `#[idle]`: with an attribute `#[resources]` - -``` rust -#[resources] -struct Resources { - // Resources defined in here -} -``` - -In fact, the name of the struct is now up to the developer: - -``` rust -#[resources] -struct Whateveryouwant { - // Resources defined in here -} -``` - -would work equally well. - ## Spawn/schedule from anywhere With the new "spawn/schedule from anywhere", old code such as: @@ -201,37 +304,6 @@ fn bar(_c: bar::Context) { Note that the attributes `spawn` and `schedule` are no longer needed. -## Symmetric locks - -Now RTIC utilizes symmetric locks, this means that the `lock` method need to be used for all resource access. In old code one could do the following as the high priority task has exclusive access to the resource: - -``` rust -#[task(priority = 2, resources = [r])] -fn foo(cx: foo::Context) { - cx.resources.r = /* ... */; -} - -#[task(resources = [r])] -fn bar(cx: bar::Context) { - cx.resources.r.lock(|r| r = /* ... */); -} -``` - -And with symmetric locks one needs to use locks in both tasks: - -``` rust -#[task(priority = 2, resources = [r])] -fn foo(cx: foo::Context) { - cx.resources.r.lock(|r| r = /* ... */); -} - -#[task(resources = [r])] -fn bar(cx: bar::Context) { - cx.resources.r.lock(|r| r = /* ... */); -} -``` - -Note that the performance does not change thanks to LLVM's optimizations which optimizes away unnecessary locks. --- @@ -242,4 +314,3 @@ Note that the performance does not change thanks to LLVM's optimizations which o Both software and hardware tasks can now be defined external to the `mod app`. Previously this was possible only by implementing a trampoline calling out the task implementation. See examples `examples/extern_binds.rs` and `examples/extern_spawn.rs`. - From 18880406cb425bcd030f0b0aa9e67e8ac05bd852 Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Wed, 21 Jul 2021 10:14:00 +0200 Subject: [PATCH 006/423] use tuple struct syntax for Monotonics everywhere --- book/en/src/migration/migration_v5.md | 4 ++-- examples/smallest.rs | 2 +- examples/spawn.rs | 2 +- examples/spawn2.rs | 2 +- macros/src/tests/single.rs | 2 +- ui/exception-invalid.rs | 2 +- ui/extern-interrupt-not-enough.rs | 2 +- ui/extern-interrupt-used.rs | 2 +- ui/task-priority-too-high.rs | 2 +- 9 files changed, 10 insertions(+), 10 deletions(-) diff --git a/book/en/src/migration/migration_v5.md b/book/en/src/migration/migration_v5.md index 154714e354..210063bf5e 100644 --- a/book/en/src/migration/migration_v5.md +++ b/book/en/src/migration/migration_v5.md @@ -226,7 +226,7 @@ v0.6.0 code: fn init(cx: init::Context) -> (Shared, Local, init::Monotonics) { let buffer: &'static mut [u8; 1024] = cx.local.buffer; - (Shared {}, Local {}, init::Monotonics {}) + (Shared {}, Local {}, init::Monotonics()) } ``` @@ -263,7 +263,7 @@ mod app { fn init(_: init::Context) -> (MySharedResources, MyLocalResources, init::Monotonics) { rtic::pend(Interrupt::UART0); - (MySharedResources, MyLocalResources, init::Monotonics {}) + (MySharedResources, MyLocalResources, init::Monotonics()) } // [more code] diff --git a/examples/smallest.rs b/examples/smallest.rs index 6afcfbd4d0..31750e257a 100644 --- a/examples/smallest.rs +++ b/examples/smallest.rs @@ -16,6 +16,6 @@ mod app { #[init] fn init(_: init::Context) -> (Shared, Local, init::Monotonics) { - (Shared {}, Local {}, init::Monotonics {}) + (Shared {}, Local {}, init::Monotonics()) } } diff --git a/examples/spawn.rs b/examples/spawn.rs index bcb4fb235f..435cdf5697 100644 --- a/examples/spawn.rs +++ b/examples/spawn.rs @@ -21,7 +21,7 @@ mod app { fn init(_: init::Context) -> (Shared, Local, init::Monotonics) { foo::spawn(1, 2).unwrap(); - (Shared {}, Local {}, init::Monotonics {}) + (Shared {}, Local {}, init::Monotonics()) } #[task()] diff --git a/examples/spawn2.rs b/examples/spawn2.rs index ff9516a6af..ed285b70cf 100644 --- a/examples/spawn2.rs +++ b/examples/spawn2.rs @@ -21,7 +21,7 @@ mod app { fn init(_: init::Context) -> (Shared, Local, init::Monotonics) { foo::spawn(1, 2).unwrap(); - (Shared {}, Local {}, init::Monotonics {}) + (Shared {}, Local {}, init::Monotonics()) } #[task] diff --git a/macros/src/tests/single.rs b/macros/src/tests/single.rs index 27118856b7..f20c9ccbb3 100644 --- a/macros/src/tests/single.rs +++ b/macros/src/tests/single.rs @@ -18,7 +18,7 @@ fn analyze() { #[init] fn init(_: init::Context) -> (Shared, Local, init::Monotonics) { - (Shared {}, Local {}, init::Monotonics {}) + (Shared {}, Local {}, init::Monotonics()) } #[task(priority = 1)] diff --git a/ui/exception-invalid.rs b/ui/exception-invalid.rs index d899443b9b..07d3c21f54 100644 --- a/ui/exception-invalid.rs +++ b/ui/exception-invalid.rs @@ -10,7 +10,7 @@ mod app { #[init] fn init(cx: init::Context) -> (Shared, Local, init::Monotonics) { - (Shared {}, Local {}, init::Monotonics {}) + (Shared {}, Local {}, init::Monotonics()) } #[task(binds = NonMaskableInt)] diff --git a/ui/extern-interrupt-not-enough.rs b/ui/extern-interrupt-not-enough.rs index 6e7863475a..1dbe923c98 100644 --- a/ui/extern-interrupt-not-enough.rs +++ b/ui/extern-interrupt-not-enough.rs @@ -10,7 +10,7 @@ mod app { #[init] fn init(cx: init::Context) -> (Shared, Local, init::Monotonics) { - (Shared {}, Local {}, init::Monotonics {}) + (Shared {}, Local {}, init::Monotonics()) } #[task] diff --git a/ui/extern-interrupt-used.rs b/ui/extern-interrupt-used.rs index a22b85f41b..882d5e3ab0 100644 --- a/ui/extern-interrupt-used.rs +++ b/ui/extern-interrupt-used.rs @@ -10,7 +10,7 @@ mod app { #[init] fn init(cx: init::Context) -> (Shared, Local, init::Monotonics) { - (Shared {}, Local {}, init::Monotonics {}) + (Shared {}, Local {}, init::Monotonics()) } #[task(binds = UART0)] diff --git a/ui/task-priority-too-high.rs b/ui/task-priority-too-high.rs index 0d903d3a65..46ab561750 100644 --- a/ui/task-priority-too-high.rs +++ b/ui/task-priority-too-high.rs @@ -10,7 +10,7 @@ mod app { #[init] fn init(cx: init::Context) -> (Shared, Local, init::Monotonics) { - (Shared {}, Local {}, init::Monotonics {}) + (Shared {}, Local {}, init::Monotonics()) } #[task(binds = GPIOA, priority = 1)] From 6bf1c76d842b40cd1b24b4a517e42e624ade836f Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Wed, 21 Jul 2021 15:46:09 +0200 Subject: [PATCH 007/423] book/resources: do not use the lock API in the very first example instead stick to `#[local]` resources --- book/en/src/by-example/resources.md | 29 ++++++++------ examples/resource.rs | 61 +++++++++++++++++------------ 2 files changed, 53 insertions(+), 37 deletions(-) diff --git a/book/en/src/by-example/resources.md b/book/en/src/by-example/resources.md index 3a3e0b7660..b0e7590445 100644 --- a/book/en/src/by-example/resources.md +++ b/book/en/src/by-example/resources.md @@ -7,9 +7,11 @@ Resources are data visible only to functions declared within the `#[app]` module. The framework gives the user complete control over which context can access which resource. -All resources are declared as a single `struct` within the `#[app]` -module. Each field in the structure corresponds to a different resource. -The `struct` must be annotated with the following attribute: `#[resources]`. +All resources are declared as *two* `struct`s within the `#[app]` module. +Each field in these structures corresponds to a different resource. +One `struct` must be annotated with the attribute `#[local]`. +The other `struct` must be annotated with the attribute `#[shared]`. +The difference between these two sets of resources will be covered later. Resources can optionally be given an initial value using the `#[init]` attribute. Resources that are not given an initial value are referred to as @@ -17,12 +19,13 @@ attribute. Resources that are not given an initial value are referred to as page. Each context (task handler, `init` or `idle`) must declare the resources it -intends to access in its corresponding metadata attribute using the `resources` -argument. This argument takes a list of resource names as its value. The listed -resources are made available to the context under the `resources` field of the -`Context` structure. +intends to access in its corresponding metadata attribute using either the +`local` or `shared` argument. This argument takes a list of resource names as +its value. The listed resources are made available to the context under the +`local` and `shared` fields of the `Context` structure. -The example application shown below contains two interrupt handlers that share access to a resource named `shared`. +The example application shown below contains two interrupt handlers. +Each handler has access to its own `#[local]` resource. ``` rust {{#include ../../../../examples/resource.rs}} @@ -33,13 +36,14 @@ $ cargo run --example resource {{#include ../../../../ci/expected/resource.run}} ``` -Note that the `shared` resource cannot be accessed from `idle`. Attempting to do so results in a compile error. +A `#[local]` resource cannot be accessed from outside the task it was associated to in a `#[task]` attribute. +Assigning the same `#[local]` resource to more than one task is a compile-time error. ## `lock` -Critical sections are required to access shared mutable data in a data race-free manner. +Critical sections are required to access `#[shared]` resources in a data race-free manner. -The `resources` field of the passed `Context` implements the [`Mutex`] trait for each shared resource accessible to the task. +The `shared` field of the passed `Context` implements the [`Mutex`] trait for each shared resource accessible to the task. The only method on this trait, [`lock`], runs its closure argument in a critical section. @@ -91,7 +95,7 @@ $ cargo run --example late {{#include ../../../../ci/expected/late.run}} ``` -## Only shared access +## Only shared (`&-`) access By default the framework assumes that all tasks require exclusive access (`&mut-`) to resources but it is possible to specify that a task only requires shared access (`&-`) to a resource using the `&resource_name` syntax in the `resources` list. @@ -121,4 +125,3 @@ There exists two other options dealing with resources this is safe. * `#[task_local]`: there must be only one task using this resource, similar to a `static mut` task local resource, but (optionally) set-up by init. - diff --git a/examples/resource.rs b/examples/resource.rs index 2c7dffe334..260f67535b 100644 --- a/examples/resource.rs +++ b/examples/resource.rs @@ -13,55 +13,68 @@ mod app { use lm3s6965::Interrupt; #[shared] - struct Shared { - shared: u32, - } + struct Shared {} #[local] - struct Local {} + struct Local { + local_to_uart0: i64, + local_to_uart1: i64, + } #[init] fn init(_: init::Context) -> (Shared, Local, init::Monotonics) { rtic::pend(Interrupt::UART0); rtic::pend(Interrupt::UART1); - (Shared { shared: 0 }, Local {}, init::Monotonics()) + ( + Shared {}, + Local { + local_to_uart0: 0, + local_to_uart1: 0, + }, + init::Monotonics(), + ) } - // `shared` cannot be accessed from this context + // `#[local]` resources cannot be accessed from this context #[idle] fn idle(_cx: idle::Context) -> ! { debug::exit(debug::EXIT_SUCCESS); - // error: no `shared` field in `idle::Context` - // _cx.shared.shared += 1; + // error: no `local` field in `idle::Context` + // _cx.local.local_to_uart0 += 1; + + // error: no `local` field in `idle::Context` + // _cx.local.local_to_uart1 += 1; loop { cortex_m::asm::nop(); } } - // `shared` can be accessed from this context + // `local_to_uart0` can only be accessed from this context // defaults to priority 1 - #[task(binds = UART0, shared = [shared])] - fn uart0(mut cx: uart0::Context) { - let shared = cx.shared.shared.lock(|shared| { - *shared += 1; - *shared - }); + #[task(binds = UART0, local = [local_to_uart0])] + fn uart0(cx: uart0::Context) { + *cx.local.local_to_uart0 += 1; + let local_to_uart0 = cx.local.local_to_uart0; - hprintln!("UART0: shared = {}", shared).unwrap(); + // error: no `local_to_uart1` field in `uart0::LocalResources` + cx.local.local_to_uart1 += 1; + + hprintln!("UART0: local_to_uart0 = {}", local_to_uart0).unwrap(); } - // `shared` can be accessed from this context + // `shared` can only be accessed from this context // explicitly set to priority 2 - #[task(binds = UART1, shared = [shared], priority = 2)] - fn uart1(mut cx: uart1::Context) { - let shared = cx.shared.shared.lock(|shared| { - *shared += 1; - *shared - }); + #[task(binds = UART1, local = [local_to_uart1], priority = 2)] + fn uart1(cx: uart1::Context) { + *cx.local.local_to_uart1 += 1; + let local_to_uart1 = cx.local.local_to_uart1; - hprintln!("UART1: shared = {}", shared).unwrap(); + // error: no `local_to_uart0` field in `uart1::LocalResources` + // cx.local.local_to_uart0 += 1; + + hprintln!("UART1: local_to_uart1 = {}", local_to_uart1).unwrap(); } } From cd4e8183f658a52dc9c3aafc789a2f87a5c6345e Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Wed, 21 Jul 2021 15:59:08 +0200 Subject: [PATCH 008/423] book/resources: remove mentions of the field attribute #[init()] it no longer exists. all resources are now late resources --- book/en/src/by-example/resources.md | 30 +++++------------------------ examples/resource.rs | 1 + 2 files changed, 6 insertions(+), 25 deletions(-) diff --git a/book/en/src/by-example/resources.md b/book/en/src/by-example/resources.md index b0e7590445..11ba4cef90 100644 --- a/book/en/src/by-example/resources.md +++ b/book/en/src/by-example/resources.md @@ -13,17 +13,16 @@ One `struct` must be annotated with the attribute `#[local]`. The other `struct` must be annotated with the attribute `#[shared]`. The difference between these two sets of resources will be covered later. -Resources can optionally be given an initial value using the `#[init]` -attribute. Resources that are not given an initial value are referred to as -*late* resources and are covered in more detail in a follow-up section in this -page. - Each context (task handler, `init` or `idle`) must declare the resources it intends to access in its corresponding metadata attribute using either the `local` or `shared` argument. This argument takes a list of resource names as its value. The listed resources are made available to the context under the `local` and `shared` fields of the `Context` structure. +All resources are initialized at runtime, after the `#[init]` function returns. +The `#[init]` function must return the initial values for all resources; hence its return type includes the types of the `#[shared]` and `#[local]` structs. +Because resources are uninitialized during the execution of the `#[init]` function, they cannot be accessed within the `#[init]` function. + The example application shown below contains two interrupt handlers. Each handler has access to its own `#[local]` resource. @@ -56,7 +55,7 @@ The critical section created by the `lock` API is based on dynamic priorities: i [icpp]: https://en.wikipedia.org/wiki/Priority_ceiling_protocol [srp]: https://en.wikipedia.org/wiki/Stack_Resource_Policy -In the example below we have three interrupt handlers with priorities ranging from one to three. The two handlers with the lower priorities contend for the `shared` resource and need to lock the resource for accessing the data. The highest priority handler, which do nat access the `shared` resource, is free to preempt the critical section created by the +In the example below we have three interrupt handlers with priorities ranging from one to three. The two handlers with the lower priorities contend for the `shared` resource and need to lock the resource for accessing the data. The highest priority handler, which do not access the `shared` resource, is free to preempt the critical section created by the lowest priority handler. ``` rust @@ -76,25 +75,6 @@ As an extension to `lock`, and to reduce rightward drift, locks can be taken as {{#include ../../../../examples/multilock.rs}} ``` -## Late resources - -Late resources are resources that are not given an initial value at compile time using the `#[init]` attribute but instead are initialized at runtime using the `init::LateResources` values returned by the `init` function. - -Late resources are useful e.g., to *move* (as in transferring the ownership of) peripherals initialized in `init` into tasks. - -The example below uses late resources to establish a lockless, one-way channel between the `UART0` interrupt handler and the `idle` task. A single producer single consumer [`Queue`] is used as the channel. The queue is split into consumer and producer end points in `init` and then each end point is stored in a different resource; `UART0` owns the producer resource and `idle` owns the consumer resource. - -[`Queue`]: ../../../api/heapless/spsc/struct.Queue.html - -``` rust -{{#include ../../../../examples/late.rs}} -``` - -``` console -$ cargo run --example late -{{#include ../../../../ci/expected/late.run}} -``` - ## Only shared (`&-`) access By default the framework assumes that all tasks require exclusive access (`&mut-`) to resources but it is possible to specify that a task only requires shared access (`&-`) to a resource using the `&resource_name` syntax in the `resources` list. diff --git a/examples/resource.rs b/examples/resource.rs index 260f67535b..693ce89618 100644 --- a/examples/resource.rs +++ b/examples/resource.rs @@ -28,6 +28,7 @@ mod app { ( Shared {}, + // initial values for the `#[local]` resources Local { local_to_uart0: 0, local_to_uart1: 0, From ae1f9008a4b8b4e12d4580e25f240d9ee352e6a4 Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Thu, 22 Jul 2021 08:28:11 +0200 Subject: [PATCH 009/423] comment out line that doesn't compile --- examples/resource.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/resource.rs b/examples/resource.rs index 693ce89618..dca0b37034 100644 --- a/examples/resource.rs +++ b/examples/resource.rs @@ -61,7 +61,7 @@ mod app { let local_to_uart0 = cx.local.local_to_uart0; // error: no `local_to_uart1` field in `uart0::LocalResources` - cx.local.local_to_uart1 += 1; + // cx.local.local_to_uart1 += 1; hprintln!("UART0: local_to_uart0 = {}", local_to_uart0).unwrap(); } From af631719f46f2669dad98461c9fdacbf7d16f83f Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Thu, 22 Jul 2021 08:42:44 +0200 Subject: [PATCH 010/423] update expected example output --- ci/expected/resource.run | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ci/expected/resource.run b/ci/expected/resource.run index ca12f0ed5e..87e2b37b57 100644 --- a/ci/expected/resource.run +++ b/ci/expected/resource.run @@ -1,2 +1,2 @@ -UART1: shared = 1 -UART0: shared = 2 +UART1: local_to_uart1 = 1 +UART0: local_to_uart0 = 2 From f9a7efb235854bcb73390988f43b2fae0868e99a Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Thu, 22 Jul 2021 08:58:17 +0200 Subject: [PATCH 011/423] update expected example output (take 2) --- ci/expected/resource.run | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ci/expected/resource.run b/ci/expected/resource.run index 87e2b37b57..9b60960f44 100644 --- a/ci/expected/resource.run +++ b/ci/expected/resource.run @@ -1,2 +1,2 @@ UART1: local_to_uart1 = 1 -UART0: local_to_uart0 = 2 +UART0: local_to_uart0 = 1 From 5805a05fac2fc5824009586b3ee2fd36dc27fbbf Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Thu, 22 Jul 2021 09:17:39 +0200 Subject: [PATCH 012/423] book/resources: rm #[task_local] mention; add #[lock_free] example the #[task_local] attribute was removed --- book/en/src/by-example/resources.md | 18 +++++---- ci/expected/lock-free.run | 14 +++++++ examples/lock-free.rs | 60 +++++++++++++++++++++++++++++ 3 files changed, 85 insertions(+), 7 deletions(-) create mode 100644 ci/expected/lock-free.run create mode 100644 examples/lock-free.rs diff --git a/book/en/src/by-example/resources.md b/book/en/src/by-example/resources.md index 11ba4cef90..855cde93a6 100644 --- a/book/en/src/by-example/resources.md +++ b/book/en/src/by-example/resources.md @@ -97,11 +97,15 @@ $ cargo run --example only-shared-access ## Lock-free resource access of mutable resources -There exists two other options dealing with resources +A critical section is *not* required to access a `#[shared]` resource that's only accessed by tasks running at the *same* priority. +In this case, you can opt out of the `lock` API by adding the `#[lock_free]` field-level attribute to the resource declaration (see example below). +Note that this is merely a convenience: if you do use the `lock` API, at runtime the framework will *not* produce a critical section. -* `#[lock_free]`: there might be several tasks with the same priority - accessing the resource without critical section. Since tasks with the - same priority never can preempt another task on the same priority - this is safe. -* `#[task_local]`: there must be only one task using this resource, - similar to a `static mut` task local resource, but (optionally) set-up by init. +``` rust +{{#include ../../../../examples/lock-free.rs}} +``` + +``` console +$ cargo run --example lock-free +{{#include ../../../../ci/expected/lock-free.run}} +``` diff --git a/ci/expected/lock-free.run b/ci/expected/lock-free.run new file mode 100644 index 0000000000..56f47a0be4 --- /dev/null +++ b/ci/expected/lock-free.run @@ -0,0 +1,14 @@ +GPIOA/start + GPIOA/counter = 1 +GPIOA/end +GPIOB/start + GPIOB/counter = 2 +GPIOB/end +GPIOA/start + GPIOA/counter = 3 +GPIOA/end +GPIOB/start + GPIOB/counter = 4 +GPIOB/end +GPIOA/start + GPIOA/counter = 5 diff --git a/examples/lock-free.rs b/examples/lock-free.rs new file mode 100644 index 0000000000..db74c7d8b0 --- /dev/null +++ b/examples/lock-free.rs @@ -0,0 +1,60 @@ +//! examples/lock-free.rs + +#![deny(unsafe_code)] +#![deny(warnings)] +#![no_main] +#![no_std] + +use panic_semihosting as _; + +#[rtic::app(device = lm3s6965)] +mod app { + use cortex_m_semihosting::{debug, hprintln}; + use lm3s6965::Interrupt; + + #[shared] + struct Shared { + #[lock_free] // <- lock-free shared resource + counter: u64, + } + + #[local] + struct Local {} + + #[init] + fn init(_: init::Context) -> (Shared, Local, init::Monotonics) { + rtic::pend(Interrupt::GPIOA); + + (Shared { counter: 0 }, Local {}, init::Monotonics()) + } + + #[task(binds = GPIOA, shared = [counter])] // <- same priority + fn gpioa(c: gpioa::Context) { + hprintln!("GPIOA/start").unwrap(); + rtic::pend(Interrupt::GPIOB); + + *c.shared.counter += 1; // <- no lock API required + let counter = *c.shared.counter; + hprintln!(" GPIOA/counter = {}", counter).unwrap(); + + if counter == 5 { + debug::exit(debug::EXIT_SUCCESS); + } + hprintln!("GPIOA/end").unwrap(); + } + + #[task(binds = GPIOB, shared = [counter])] // <- same priority + fn gpiob(c: gpiob::Context) { + hprintln!("GPIOB/start").unwrap(); + rtic::pend(Interrupt::GPIOA); + + *c.shared.counter += 1; // <- no lock API required + let counter = *c.shared.counter; + hprintln!(" GPIOB/counter = {}", counter).unwrap(); + + if counter == 5 { + debug::exit(debug::EXIT_SUCCESS); + } + hprintln!("GPIOB/end").unwrap(); + } +} From a7ed040799f5d2c2a2a7021fef840a6717d88d44 Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Thu, 22 Jul 2021 09:27:42 +0200 Subject: [PATCH 013/423] migration/0.5: cover #[lock_free] I think this completes #488 --- book/en/src/migration/migration_v5.md | 43 +++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/book/en/src/migration/migration_v5.md b/book/en/src/migration/migration_v5.md index 210063bf5e..24353d2a31 100644 --- a/book/en/src/migration/migration_v5.md +++ b/book/en/src/migration/migration_v5.md @@ -201,6 +201,49 @@ fn bar(cx: bar::Context) { Note that the performance does not change thanks to LLVM's optimizations which optimizes away unnecessary locks. +## Lock-free resource access + +In RTIC 0.5 resources shared by tasks running at the same priority could be accessed *without* the `lock` API. +This is still possible in 0.6: the `#[shared]` resource must be annotated with the field-level `#[lock_free]` attribute. + +v0.5 code: + +``` rust +struct Resources { + counter: u64, +} + +#[task(resources = [counter])] +fn a(cx: a::Context) { + *cx.resources.counter += 1; +} + +#[task(resources = [counter])] +fn b(cx: b::Context) { + *cx.resources.counter += 1; +} +``` + +v0.6 code: + +``` rust +#[shared] +struct Shared { + #[lock_free] + counter: u64, +} + +#[task(shared = [counter])] +fn a(cx: a::Context) { + *cx.shared.counter += 1; +} + +#[task(shared = [counter])] +fn b(cx: b::Context) { + *cx.shared.counter += 1; +} +``` + ## no `static mut` transform `static mut` variables are no longer transformed to safe `&'static mut` references. From 5f395658f03ccd092d9e17edc949804012869157 Mon Sep 17 00:00:00 2001 From: Alex Crawford Date: Thu, 22 Jul 2021 00:47:00 -0700 Subject: [PATCH 014/423] Propogate the task attributes to the spawn handles This allows tasks to be gated by `cfg` attributes when also using monotonics. For example: ```rust #[cfg(feature = "logging")] #[task(shared = [logger])] fn logger_init(mut cx: logger_init::Context) { /* ... */ } ``` Without this change, the reschedule_at() implementation is unconditionally included even though it references the SpawnHandle from its task module, which is _conditionally_ included. This resulted in compiler errors like the following: ``` error[E0433]: failed to resolve: use of undeclared crate or module `logger_init` --> src/main.rs:243:8 | 243 | fn logger_init(mut cx: logger_init::Context) { | ^^^^^^^^^^^ use of undeclared crate or module `logger_init` ``` --- macros/src/codegen/module.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/macros/src/codegen/module.rs b/macros/src/codegen/module.rs index a59d6628d6..c7092bd370 100644 --- a/macros/src/codegen/module.rs +++ b/macros/src/codegen/module.rs @@ -317,11 +317,13 @@ pub fn codegen( )); items.push(quote!( + #(#cfgs)* pub struct #internal_spawn_handle_ident { #[doc(hidden)] marker: u32, } + #(#cfgs)* impl #internal_spawn_handle_ident { pub fn cancel(self) -> Result<#ty, ()> { rtic::export::interrupt::free(|_| unsafe { From 3eac8b91cfd37c27e75da5c7e21e394d10f97adf Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Thu, 22 Jul 2021 10:35:09 +0200 Subject: [PATCH 015/423] book/resources: highlight that `#[lock_free]` includes a compile-time check for the "same priority requirement"; this prevents data races --- book/en/src/by-example/resources.md | 1 + 1 file changed, 1 insertion(+) diff --git a/book/en/src/by-example/resources.md b/book/en/src/by-example/resources.md index 855cde93a6..4f6c3c317c 100644 --- a/book/en/src/by-example/resources.md +++ b/book/en/src/by-example/resources.md @@ -100,6 +100,7 @@ $ cargo run --example only-shared-access A critical section is *not* required to access a `#[shared]` resource that's only accessed by tasks running at the *same* priority. In this case, you can opt out of the `lock` API by adding the `#[lock_free]` field-level attribute to the resource declaration (see example below). Note that this is merely a convenience: if you do use the `lock` API, at runtime the framework will *not* produce a critical section. +Also worth noting: using `#[lock_free]` on resources shared by tasks running at different priorities will result in a *compile-time* error -- not using the `lock` API would be a data race in that case. ``` rust {{#include ../../../../examples/lock-free.rs}} From 1e2fb2eeb2f57ff61aaf1298a9e4975c8fa8dd73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Tj=C3=A4der?= Date: Tue, 27 Jul 2021 19:35:35 +0200 Subject: [PATCH 016/423] Add links to RTIC book dev version and rtic-examples --- README.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 15f6af49a2..78b6fa86ab 100644 --- a/README.md +++ b/README.md @@ -52,10 +52,14 @@ Formerly known as Real-Time For the Masses. - Applications must be written using the 2018 edition. -## [User documentation](https://rtic.rs) +## [User documentation](https://rtic.rs) - [(Development version)](https://rtic.rs/dev) ## [API reference](https://rtic.rs/stable/api/) +## [Community provided examples repo][examples] + +[examples]: https://github.com/rtic-rs/rtic-examples + ## Chat Join us and talk about RTIC in the [Matrix room][matrix-room]. From 5477ae288fb6df0484aa6e4d8acb13cdf0423b22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Tj=C3=A4der?= Date: Tue, 27 Jul 2021 20:04:56 +0200 Subject: [PATCH 017/423] Also link to `rtic-examples` in the tips section of the book --- book/en/src/by-example/tips.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/book/en/src/by-example/tips.md b/book/en/src/by-example/tips.md index f537173d58..e292634bb1 100644 --- a/book/en/src/by-example/tips.md +++ b/book/en/src/by-example/tips.md @@ -1,5 +1,9 @@ # Tips & tricks +For complete RTIC examples see the [rtic-examples][rtic-examples] repository. + +[rtic-examples]: https://github.com/rtic-rs/rtic-examples + ## Generics All resource proxies implement the `rtic::Mutex` trait. From 007665eeee37cb9ef6f6bd9ef9a477fbf83fd520 Mon Sep 17 00:00:00 2001 From: CuriouslyCurious Date: Wed, 28 Jul 2021 13:55:12 +0200 Subject: [PATCH 018/423] book: Add note to remember to choose target --- book/en/src/by-example/app.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/book/en/src/by-example/app.md b/book/en/src/by-example/app.md index 50f2842cac..d23c2619aa 100644 --- a/book/en/src/by-example/app.md +++ b/book/en/src/by-example/app.md @@ -53,6 +53,10 @@ $ cargo run --example init {{#include ../../../../ci/expected/init.run}} ``` +> **NOTE**: Remember to always specify your chosen target device or configure +> one to be used by default in `.cargo/config.toml`. In this case, we use +> a Cortex M3 running in QEMU so the target is `thumbv7m-none-eabi`. + ## `idle` A function marked with the `idle` attribute can optionally appear in the @@ -120,7 +124,7 @@ crate. When the `priority` argument is omitted, the priority is assumed to be `1`. The `idle` task has a non-configurable static priority of `0`, the lowest priority. > A higher number means a higher priority in RTIC, which is the opposite from what -> Cortex-M does in the NVIC peripheral. +> Cortex-M does in the NVIC peripheral. > Explicitly, this means that number `10` has a **higher** priority than number `9`. When several tasks are ready to be executed the one with highest static From e90e6332f69cde78c902ba554b3d2930ab173124 Mon Sep 17 00:00:00 2001 From: CuriouslyCurious Date: Thu, 29 Jul 2021 00:29:34 +0200 Subject: [PATCH 019/423] book: Clarify target notice --- book/en/src/by-example/app.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/book/en/src/by-example/app.md b/book/en/src/by-example/app.md index d23c2619aa..e8e1d7a62e 100644 --- a/book/en/src/by-example/app.md +++ b/book/en/src/by-example/app.md @@ -53,9 +53,10 @@ $ cargo run --example init {{#include ../../../../ci/expected/init.run}} ``` -> **NOTE**: Remember to always specify your chosen target device or configure -> one to be used by default in `.cargo/config.toml`. In this case, we use -> a Cortex M3 running in QEMU so the target is `thumbv7m-none-eabi`. +> **NOTE**: Remember to specify your chosen target device by passing a target +> triple to cargo (e.g `cargo run --example init --target thumbv7m-none-eabi`) or +> configure a device to be used by default when building the examples in `.cargo/config.toml`. +> In this case, we use a Cortex M3 emulated in QEMU so the target is `thumbv7m-none-eabi`. ## `idle` From 7a977f3fa99adcdf160761bf96268e6c89dd27fb Mon Sep 17 00:00:00 2001 From: CuriouslyCurious Date: Thu, 29 Jul 2021 13:58:21 +0200 Subject: [PATCH 020/423] book: Add link to new.md --- book/en/src/by-example/app.md | 27 +-------------------------- 1 file changed, 1 insertion(+), 26 deletions(-) diff --git a/book/en/src/by-example/app.md b/book/en/src/by-example/app.md index e8e1d7a62e..c84f7a41c9 100644 --- a/book/en/src/by-example/app.md +++ b/book/en/src/by-example/app.md @@ -6,32 +6,6 @@ This is the smallest possible RTIC application: {{#include ../../../../examples/smallest.rs}} ``` -All RTIC applications use the [`app`] attribute (`#[app(..)]`). This attribute -must be applied to a `mod`-item. The `app` attribute has a mandatory `device` -argument that takes a *path* as a value. This must be a full path pointing to a -*peripheral access crate* (PAC) generated using [`svd2rust`] **v0.14.x** or -newer. More details can be found in the [Starting a new project](./new.md) -section. - -The `app` attribute will expand into a suitable entry point so it's not required -to use the [`cortex_m_rt::entry`] attribute. - -[`app`]: ../../../api/cortex_m_rtic_macros/attr.app.html -[`svd2rust`]: https://crates.io/crates/svd2rust -[`cortex_m_rt::entry`]: ../../../api/cortex_m_rt_macros/attr.entry.html - -## `init` - -Within the `app` module the attribute expects to find an initialization -function marked with the `init` attribute. This function must have -signature `fn(init::Context) -> (init::LateResources, init::Monotonics)`. - -This initialization function will be the first part of the application to run. -The `init` function will run *with interrupts disabled* and has exclusive access -to Cortex-M where the `bare_metal::CriticalSection` token is available as `cs`. -And optionally, device specific peripherals through the `core` and `device` fields -of `init::Context`. - `static mut` variables declared at the beginning of `init` will be transformed into `&'static mut` references that are safe to access. Notice, this feature may be deprecated in next release, see `task_local` resources. @@ -57,6 +31,7 @@ $ cargo run --example init > triple to cargo (e.g `cargo run --example init --target thumbv7m-none-eabi`) or > configure a device to be used by default when building the examples in `.cargo/config.toml`. > In this case, we use a Cortex M3 emulated in QEMU so the target is `thumbv7m-none-eabi`. +> See [`Starting a new project`](./new.md) for more info. ## `idle` From ae7b444f3d7e4a41f45aea47be3f14180028f36b Mon Sep 17 00:00:00 2001 From: CuriouslyCurious Date: Fri, 30 Jul 2021 12:43:35 +0200 Subject: [PATCH 021/423] Revert "book: Add link to new.md" This reverts commit 7a977f3fa99adcdf160761bf96268e6c89dd27fb. --- book/en/src/by-example/app.md | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/book/en/src/by-example/app.md b/book/en/src/by-example/app.md index c84f7a41c9..e8e1d7a62e 100644 --- a/book/en/src/by-example/app.md +++ b/book/en/src/by-example/app.md @@ -6,6 +6,32 @@ This is the smallest possible RTIC application: {{#include ../../../../examples/smallest.rs}} ``` +All RTIC applications use the [`app`] attribute (`#[app(..)]`). This attribute +must be applied to a `mod`-item. The `app` attribute has a mandatory `device` +argument that takes a *path* as a value. This must be a full path pointing to a +*peripheral access crate* (PAC) generated using [`svd2rust`] **v0.14.x** or +newer. More details can be found in the [Starting a new project](./new.md) +section. + +The `app` attribute will expand into a suitable entry point so it's not required +to use the [`cortex_m_rt::entry`] attribute. + +[`app`]: ../../../api/cortex_m_rtic_macros/attr.app.html +[`svd2rust`]: https://crates.io/crates/svd2rust +[`cortex_m_rt::entry`]: ../../../api/cortex_m_rt_macros/attr.entry.html + +## `init` + +Within the `app` module the attribute expects to find an initialization +function marked with the `init` attribute. This function must have +signature `fn(init::Context) -> (init::LateResources, init::Monotonics)`. + +This initialization function will be the first part of the application to run. +The `init` function will run *with interrupts disabled* and has exclusive access +to Cortex-M where the `bare_metal::CriticalSection` token is available as `cs`. +And optionally, device specific peripherals through the `core` and `device` fields +of `init::Context`. + `static mut` variables declared at the beginning of `init` will be transformed into `&'static mut` references that are safe to access. Notice, this feature may be deprecated in next release, see `task_local` resources. @@ -31,7 +57,6 @@ $ cargo run --example init > triple to cargo (e.g `cargo run --example init --target thumbv7m-none-eabi`) or > configure a device to be used by default when building the examples in `.cargo/config.toml`. > In this case, we use a Cortex M3 emulated in QEMU so the target is `thumbv7m-none-eabi`. -> See [`Starting a new project`](./new.md) for more info. ## `idle` From f6f61f95a69786458a4fd06aa7fa33256b9e4603 Mon Sep 17 00:00:00 2001 From: CuriouslyCurious Date: Fri, 30 Jul 2021 12:44:35 +0200 Subject: [PATCH 022/423] book: Properly update the note with a link --- book/en/src/by-example/app.md | 1 + 1 file changed, 1 insertion(+) diff --git a/book/en/src/by-example/app.md b/book/en/src/by-example/app.md index e8e1d7a62e..5fd1c25051 100644 --- a/book/en/src/by-example/app.md +++ b/book/en/src/by-example/app.md @@ -57,6 +57,7 @@ $ cargo run --example init > triple to cargo (e.g `cargo run --example init --target thumbv7m-none-eabi`) or > configure a device to be used by default when building the examples in `.cargo/config.toml`. > In this case, we use a Cortex M3 emulated in QEMU so the target is `thumbv7m-none-eabi`. +> See [`Starting a new project`](./new.md) for more info. ## `idle` From fe1de5cbf784d2d261e4c0a08429985a0ab21a5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Tj=C3=A4der?= Date: Mon, 2 Aug 2021 14:56:15 +0200 Subject: [PATCH 023/423] GHA: Fix 1.54 formatting change --- ui/task-priority-too-high.stderr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/task-priority-too-high.stderr b/ui/task-priority-too-high.stderr index 984d3fac24..e0978f7188 100644 --- a/ui/task-priority-too-high.stderr +++ b/ui/task-priority-too-high.stderr @@ -4,4 +4,4 @@ error[E0080]: evaluation of constant value failed 3 | #[rtic::app(device = lm3s6965)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ attempt to compute `8_usize - 9_usize`, which would overflow | - = note: this error originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info) + = note: this error originates in the attribute macro `rtic::app` (in Nightly builds, run with -Z macro-backtrace for more info) From 9134f7d36a46032e7223ebed45b4bc7b2eea5fd3 Mon Sep 17 00:00:00 2001 From: Andrey Zgarbul Date: Tue, 3 Aug 2021 22:40:33 +0300 Subject: [PATCH 024/423] update russian book --- README_ru.md | 6 + book/ru/src/by-example/app.md | 8 +- book/ru/src/by-example/resources.md | 87 ++++--- book/ru/src/by-example/tips.md | 4 + book/ru/src/internals/late-resources.md | 3 +- book/ru/src/internals/tasks.md | 10 +- book/ru/src/migration/migration_v5.md | 295 ++++++++++++++++++------ 7 files changed, 288 insertions(+), 125 deletions(-) diff --git a/README_ru.md b/README_ru.md index 918d03e3d3..49e66d1d71 100644 --- a/README_ru.md +++ b/README_ru.md @@ -50,10 +50,16 @@ - Приложения должны быть написаны в редакции 2018. +## [Руководство пользователя](https://rtic.rs) - [(Версия в разработке)](https://rtic.rs/dev) + ## [Документация пользователя](https://rtic.rs) ## [Справочник по API](https://rtic.rs/stable/api/) +## [Сборник примеров, предоставляемы сообществом][examples] + +[examples]: https://github.com/rtic-rs/rtic-examples + ## Чат Присоединяйтесь к нам, чтобы говорить о RTIC [в Matrix-комнате][matrix-room]. diff --git a/book/ru/src/by-example/app.md b/book/ru/src/by-example/app.md index 628819adcd..5beca23930 100644 --- a/book/ru/src/by-example/app.md +++ b/book/ru/src/by-example/app.md @@ -23,7 +23,7 @@ Внутри модуля `app` атрибут ожидает найти функцию инициализации, помеченную атрибутом `init`. Эта функция должна иметь сигнатуру -`fn(init::Context) [-> init::LateResources]` (возвращаемый тип нужен не всегда). +`fn(init::Context) (-> init::LateResources, init::Monotonics)`. Эта функция инициализации будет первой частью программы, выполняемой при запуске. Функция `init` будет запущена *с отключенными прерываниями* и будет иметь эксклюзивный доступ @@ -54,6 +54,12 @@ $ cargo run --example init {{#include ../../../../ci/expected/init.run}} ``` +> **ПРИМЕЧАНИЕ**: Не забывайте указывать выбранное вами целевое устройство, передавая параметр target +> в cargo (например `cargo run --example init --target thumbv7m-none-eabi`) или +> настроив устройство, используемое по умолчанию для сборки примеров в `.cargo/config.toml`. +> В нашем случае используется Cortex M3, эмулируемый с помощью QEMU, поэтому пишем `thumbv7m-none-eabi`. +> Смотрите [`Создание нового проекта`](./new.md) для большей информации. + ## `idle` Функцию, помеченную атрибутом `idle` может опционально добавить в модуль. diff --git a/book/ru/src/by-example/resources.md b/book/ru/src/by-example/resources.md index 70f798d25e..ed8904ba93 100644 --- a/book/ru/src/by-example/resources.md +++ b/book/ru/src/by-example/resources.md @@ -7,21 +7,26 @@ Фреймворк дает пользователю полный контроль за тем, какой контекст может получить доступ к какому ресурсу. -Все ресурсы определены в одной структуре внутри модуля `#[app]`. -Каждое поле структуры соответствует отдельному ресурсу. -`struct`-ура должна быть аннотирована следующим атрибутом: `#[resources]`. - -Ресурсам могут быть опционально даны начальные значения с помощью атрибута `#[init]`. -Ресурсы, которым не передано начально значение, называются -*поздними* ресурсами, более детально они описаны в одном из разделов на этой странице. +Все ресурсы определены в *двух* структурах внутри модуля `#[app]`. +Каждое поле этих структур соответствует отдельному ресурсу. +Одна `struct`-ура должна быть аннотирована атрибутом `#[local]`. +Другая `struct`-ура должна быть аннотирована атрибутом `#[shared]`. +Разница между этими двумя множествами ресурсов будет описана познее. Каждый контекс (задача-обработчик, `init` или `idle`) должен указать ресурсы, к которым он намерен обращаться, в соответсятвующем ему атрибуте с метаданными, используя -аргумент `resources`. Этот аргумент принимает список имен ресурсов в качестве значения. -Перечисленные ресурсы становятся доступны в контексте через поле `resources` структуры `Context`. +либо аргумент `local`, либо `shared`. Этот аргумент принимает список имен ресурсов в качестве значения. +Перечисленные ресурсы становятся доступны в контексте через поля `local` и `shared` структуры `Context`. -Пример программы, показанной ниже содержит два обработчика прерывания, которые разделяют -доступ к ресурсу под названием `shared`. +Во время выполнения при выходе из функции `#[init]` все ресурсы инициализированы. +Функция `#[init]` должна возвращать начальные значения для всех ресурсов; +отсюда следует, что тип возвращаемого ею значения включает типы +структур `#[shared]` и `#[local]`. +Поскольку ресурсы инициализированы в ходе функции `#[init]`, к ним нельзя +получить доступ внетри функции `#[init]`. + +Пример программы, показанной ниже содержит два обработчика прерывания. +Каждый обработчик имеет доступ к его собственному `#[local]` ресурсу. ``` rust {{#include ../../../../examples/resource.rs}} @@ -32,15 +37,17 @@ $ cargo run --example resource {{#include ../../../../ci/expected/resource.run}} ``` -Заметьте, что к ресурсу `shared` нельзя получить доступ из `idle`. Попытка сделать это -приведет к ошибке компиляции. +К ресурсу `#[local]` нельзя получить доступ извне задачи к которой он +привязан атрибутом `#[task]`. +Попытка обращения к одному и тому же ресурсу `#[local]` из более чем одной +задачи - ошибка компиляции. ## `lock` -Критические секции необходимы для разделения изменяемых данных таким образом, +Критические секции необходимы для доступа к ресурсам `#[shared]` таким образом, чтобы избежать гонок данных. -Поле `resources`, передаваемого `Context` реализует трейт [`Mutex`] для каждого разделяемого +Поле `shared`, передаваемого `Context` реализует трейт [`Mutex`] для каждого разделяемого ресурса, доступного задаче. Единственный метод этого трейта, [`lock`], запускает свой аргумент-замыкание в критической секции. @@ -81,33 +88,7 @@ $ cargo run --example lock {{#include ../../../../examples/multilock.rs}} ``` -## Поздние ресурсы - -Поздние ресурсы - такие ресурсы, которым не передано начальное значение во время компиляции -с помощью атрибута `#[init]`, но которые вместо этого инициализируются во время выполнения -с помощью значений из структуры `init::LateResources`, возвращаемой функцией `init`. - -Поздние ресурсы полезны, например, для *move* (передача владения) периферии, -инициализированной в `init`, в задачи. - -Пример ниже использует поздние ресурсы, чтобы установить неблокируемый односторонний канал -между обработчиком прерывания `UART0` и задачей `idle`. Для канала использована очередь типа -один производитель-один потребитель [`Queue`]. Структура очереди разделяется на потребителя -и производителя в `init`, а затем каждая из частей располагается в отдельном ресурсу; -`UART0` владеет ресурсом производителя, а `idle` владеет ресурсом потребителя. - -[`Queue`]: ../../../api/heapless/spsc/struct.Queue.html - -``` rust -{{#include ../../../../examples/late.rs}} -``` - -``` console -$ cargo run --example late -{{#include ../../../../ci/expected/late.run}} -``` - -## Только разделяемый доступ +## Только разделяемый (`&-`) доступ По-умолчанию фреймворк предполагает, что все задачи требуют эксклюзивный доступ (`&mut-`) к ресурсам, но возможно указать, что задаче достаточен разделяемый доступ (`&-`) к ресурсы с помощью синтакисиса @@ -139,11 +120,21 @@ $ cargo run --example only-shared-access ## Неблокируемый доступ к изменяемым ресурсам -Есть две других возможности доступа к ресурсам +Критическая секция *не* требуется для доступа к ресурсу `#[shared]`, +к которому обращаются только из задач с *одинаковым* приоритетом. +В этом случае вы можете избежать `lock` API, добавив атрибут поля `#[lock_free]` при объявдении ресурса (смотреть пример ниже). +Заметьте, что это лишь для удобства: даже если вы используете `lock` API, +во время выполнения фреймворк *не* создаст критическую секцию. +Еще одно ценное замечание: использование `#[lock_free]` на ресурсах, +разделяемых задачами, запускаемыми с разными приоритетами +приведет к ошибке *компиляции* -- не импользование `lock` API может +привести к гонке данных в этом случае. -* `#[lock_free]`: могут быть несколько задач с одинаковым приоритетом, - получающие доступ к ресурсу без критических секций. Так как задачи с - одинаковым приоритетом никогда не могут вытеснить друг друга, это безопасно. -* `#[task_local]`: в этом случае должна быть только одна задача, использующая - этот ресурс, так же как локальный `static mut` ресурс задачи, но (опционально) устанавливаемая с в init. +``` rust +{{#include ../../../../examples/lock-free.rs}} +``` +``` console +$ cargo run --example lock-free +{{#include ../../../../ci/expected/lock-free.run}} +``` \ No newline at end of file diff --git a/book/ru/src/by-example/tips.md b/book/ru/src/by-example/tips.md index cf66c4b741..f19cfee9d8 100644 --- a/book/ru/src/by-example/tips.md +++ b/book/ru/src/by-example/tips.md @@ -1,5 +1,9 @@ # Советы и хитрости +Полные примеры для RTIC смотрите в репозитарии [rtic-examples][rtic-examples]. + +[rtic-examples]: https://github.com/rtic-rs/rtic-examples + ## Обобщенное программирование (Generics) Все объекты, предоставляющие ресурысы реализуют трейт `rtic::Mutex`. diff --git a/book/ru/src/internals/late-resources.md b/book/ru/src/internals/late-resources.md index 0fad0aecf8..146c438d70 100644 --- a/book/ru/src/internals/late-resources.md +++ b/book/ru/src/internals/late-resources.md @@ -103,8 +103,7 @@ mod app { } ``` -Важная деталь здесь то, что `interrupt::enable` ведет себя как like a *compiler -fence*, которое не дает компилятору пореставить запись в `X` *после* +Важная деталь здесь то, что `interrupt::enable` ведет себя как *барьер компиляции*, который не дает компилятору переставить запись в `X` *после* `interrupt::enable`. Если бы компилятор мог делать такие перестановки появились бы гонки данных между этой записью и любой операцией `foo`, взаимодействующей с `X`. diff --git a/book/ru/src/internals/tasks.md b/book/ru/src/internals/tasks.md index 665032515c..01380ba907 100644 --- a/book/ru/src/internals/tasks.md +++ b/book/ru/src/internals/tasks.md @@ -79,8 +79,8 @@ mod app { } // очередь готовности диспетчера задач - // `U4` - целое число, представляющее собой емкость этой очереди - static mut RQ1: Queue, U4> = Queue::new(); + // `5-1=4` - представляет собой емкость этой очереди + static mut RQ1: Queue, 5> = Queue::new(); // обработчик прерывания, выбранный для диспетчеризации задач с приоритетом `1` #[no_mangle] @@ -151,9 +151,9 @@ mod app { const RQ1_CEILING: u8 = 2; // используется, чтобы отследить сколько еще сообщений для `bar` можно поставить в очередь - // `U2` - емкость задачи `bar`; максимум 2 экземпляра можно добавить в очередь + // `3-1=2` - емкость задачи `bar`; максимум 2 экземпляра можно добавить в очередь // эта очередь заполняется фреймворком до того, как запустится `init` - static mut bar_FQ: Queue<(), U2> = Queue::new(); + static mut bar_FQ: Queue<(), 3> = Queue::new(); // Поиск максимального приоритета для конечного потребителя `bar_FQ` const bar_FQ_CEILING: u8 = 2; @@ -227,7 +227,7 @@ mod app { // список свободной памяти: используется для отслеживания свободных ячеек в массиве `baz_INPUTS` // эта очередь инициализируется значениями `0` и `1` перед запуском `init` - static mut baz_FQ: Queue = Queue::new(); + static mut baz_FQ: Queue = Queue::new(); // Поиск максимального приоритета для конечного потребителя `baz_FQ` const baz_FQ_CEILING: u8 = 2; diff --git a/book/ru/src/migration/migration_v5.md b/book/ru/src/migration/migration_v5.md index 04aedc5f81..870c20fd62 100644 --- a/book/ru/src/migration/migration_v5.md +++ b/book/ru/src/migration/migration_v5.md @@ -30,7 +30,46 @@ mod app { Так как теперь используется обычный модуль Rust, это значит, что можно использовать обычный пользовательский код в этом модуле. -Также жто значит, что `use`-выражения для ресурсов (и т.п.) могут понадобиться. +Также это значит, что `use`-выражения для ресурсов, используемые +в пользовательском коде должны быть перемещены внутрь `mod app`, +либо на них можно сослаться с помощью `super`. Например, измените: + +```rust +use some_crate::some_func; + +#[rtic::app(/* .. */)] +const APP: () = { + fn func() { + some_crate::some_func(); + } +}; +``` + +на + +```rust +#[rtic::app(/* .. */)] +mod app { + use some_crate::some_func; + + fn func() { + some_crate::some_func(); + } +} +``` + +или + +```rust +use some_crate::some_func; + +#[rtic::app(/* .. */)] +mod app { + fn func() { + super::some_crate::some_func(); + } +} +``` ## Перенос диспетчеров из `extern "C"` в аргументы app. @@ -63,6 +102,182 @@ mod app { Это работает и для ОЗУ-функций, см. examples/ramfunc.rs +## Структуры ресурсов - `#[shared]`, `#[local]` + +Ранее ресурсы RTIC должны были размещаться в структуре с именем "Resources": + +``` rust +struct Resources { + // Ресурсы определяются здесь +} +``` + +Начиная с RTIC v0.6.0 структуры ресурсов аннотируются подобно +`#[task]`, `#[init]`, `#[idle]`: аттрибутами `#[shared]` и `#[local]` + +``` rust +#[shared] +struct MySharedResources { + // Разделяемые задачами ресурсы определены здесь +} + +#[local] +struct MyLocalResources { + // Ресурсы, определенные здесь нельзя передавать между задачами; каждый из них локальный для единственной задачи +} +``` + +Эти структуры разработчик может называть по своему желанию. + +## `shared` и `local` аргументы в `#[task]`'ах + +В v0.6.0 ресурсы разделены на `shared` ресурсы и `local` ресурсы. +`#[task]`, `#[init]` и `#[idle]` больше не имеют аргумента `resources`; +они должны использовать аргументы `shared` и `local`. + +В v0.5.x: + +``` rust +struct Resources { + local_to_b: i64, + shared_by_a_and_b: i64, +} + +#[task(resources = [shared_by_a_and_b])] +fn a(_: a::Context) {} + +#[task(resources = [shared_by_a_and_b, local_to_b])] +fn b(_: b::Context) {} +``` + +В v0.6.0: + +``` rust +#[shared] +struct Shared { + shared_by_a_and_b: i64, +} + +#[local] +struct Local { + local_to_b: i64, +} + +#[task(shared = [shared_by_a_and_b])] +fn a(_: a::Context) {} + +#[task(shared = [shared_by_a_and_b], local = [local_to_b])] +fn b(_: b::Context) {} +``` + +## Симметричные блокировки + +Теперь RTIC использует симметричные блокировки, это значит, что метод `lock` нужно использовать для +всех доступов к `shared` ресурсам. Поскольку высокоприоритетные задачи имеют эксклюзивный доступ к ресурсу, +в старом коде можно было следующее: + +``` rust +#[task(priority = 2, resources = [r])] +fn foo(cx: foo::Context) { + cx.resources.r = /* ... */; +} + +#[task(resources = [r])] +fn bar(cx: bar::Context) { + cx.resources.r.lock(|r| r = /* ... */); +} +``` + +С симметричными блокировками нужно вызывать `lock` для обоих задач: + +``` rust +#[task(priority = 2, shared = [r])] +fn foo(cx: foo::Context) { + cx.shared.r.lock(|r| r = /* ... */); +} + +#[task(shared = [r])] +fn bar(cx: bar::Context) { + cx.shared.r.lock(|r| r = /* ... */); +} +``` + +Заметьте, что скорость работы не изменяется благодаря оптимизациям LLVM, которые убирают ненужные блокировки. + +## Неблокирующий доступ к ресурсам + +В RTIC 0.5 к ресурсам разделяемым задачами, запускаемыми с одинаковым +приоритетом, можно получить доступ *без* `lock` API. +Это все еще возможно в 0.6: ресурс `#[shared]` должен быть аннотирован +аттрибутом поля `#[lock_free]`. + +v0.5 код: + +``` rust +struct Resources { + counter: u64, +} + +#[task(resources = [counter])] +fn a(cx: a::Context) { + *cx.resources.counter += 1; +} + +#[task(resources = [counter])] +fn b(cx: b::Context) { + *cx.resources.counter += 1; +} +``` + +v0.6 код: + +``` rust +#[shared] +struct Shared { + #[lock_free] + counter: u64, +} + +#[task(shared = [counter])] +fn a(cx: a::Context) { + *cx.shared.counter += 1; +} + +#[task(shared = [counter])] +fn b(cx: b::Context) { + *cx.shared.counter += 1; +} +``` + +## нет преобразования `static mut` + +`static mut` переменные больше не преобразуются в безопасные `&'static mut` ссылки. +Вместо этого синтаксиса используйте аргумент `local` в `#[init]`. + +v0.5.x code: + +``` rust +#[init] +fn init(_: init::Context) { + static mut BUFFER: [u8; 1024] = [0; 1024]; + let buffer: &'static mut [u8; 1024] = BUFFER; +} +``` + +v0.6.0 code: + +``` rust +#[init(local = [ + buffer: [u8; 1024] = [0; 1024] +// type ^^^^^^^^^^^^ ^^^^^^^^^ initial value +])] +fn init(cx: init::Context) -> (Shared, Local, init::Monotonics) { + let buffer: &'static mut [u8; 1024] = cx.local.buffer; + + (Shared {}, Local {}, init::Monotonics()) +} +``` + ## Init всегда возвращает поздние ресурсы С целью сделать API более симметричным задача #[init] всегда возвращает поздние ресурсы. @@ -83,51 +298,27 @@ mod app { на это: + ``` rust #[rtic::app(device = lm3s6965)] mod app { + #[shared] + struct MySharedResources {} + + #[local] + struct MyLocalResources {} + #[init] - fn init(_: init::Context) -> init::LateResources { + fn init(_: init::Context) -> (MySharedResources, MyLocalResources, init::Monotonics) { rtic::pend(Interrupt::UART0); - init::LateResources {} + (MySharedResources, MyLocalResources, init::Monotonics()) } - // [еще код] + // [more code] } ``` -## Структура Resources - `#[resources]` - -Ранее ресурсы RTIC должны были располагаться в структуре с именем "Resources": - -``` rust -struct Resources { - // Ресурсы определены здесь -} -``` - -В RTIC v0.6.0 структура ресурсов аннотируется также, как и -`#[task]`, `#[init]`, `#[idle]`: атрибутом `#[resources]` - -``` rust -#[resources] -struct Resources { - // Ресурсы определены здесь -} -``` - -На самом деле, имя структуры предоставлено на усмотрение разработчика: - -``` rust -#[resources] -struct Whateveryouwant { - // Ресурсы определены здесь -} -``` - -будет работать так же хороршо. - ## Вызов/планирование откуда угодно С этой новой возвожностью, старый код, такой как: @@ -161,40 +352,6 @@ fn bar(_c: bar::Context) { Заметьте, что атрибуты `spawn` и `schedule` больше не нужны. -## Симметричные блокировки - -Теперь RTIC использует симметричные блокировки, это значит, что метод `lock` нужно использовать для -всех доступов к ресурсам. Поскольку высокоприоритетные задачи имеют эксклюзивный доступ к ресурсу, -в старом коде можно было следующее: - -``` rust -#[task(priority = 2, resources = [r])] -fn foo(cx: foo::Context) { - cx.resources.r = /* ... */; -} - -#[task(resources = [r])] -fn bar(cx: bar::Context) { - cx.resources.r.lock(|r| r = /* ... */); -} -``` - -С симметричными блокировками нужно вызывать `lock` для обоих задач: - -``` rust -#[task(priority = 2, resources = [r])] -fn foo(cx: foo::Context) { - cx.resources.r.lock(|r| r = /* ... */); -} - -#[task(resources = [r])] -fn bar(cx: bar::Context) { - cx.resources.r.lock(|r| r = /* ... */); -} -``` - -Заметьте, что скорость работы не изменяется благодаря оптимизациям LLVM, которые убирают ненужные блокировки. - --- ## Дополнительно From 50ff1bfae41e8643e609d8d85203fe20b1071253 Mon Sep 17 00:00:00 2001 From: Jorge Iglesias Garcia <44316552+jorgeig-space@users.noreply.github.com> Date: Wed, 11 Aug 2021 00:50:37 +0800 Subject: [PATCH 025/423] Update build.yml Add a "test" branch --- .github/workflows/build.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4d7ed950e6..031c92edd1 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -6,6 +6,7 @@ on: - master - staging - trying + - test env: CARGO_TERM_COLOR: always From c5585271e6434cb509b560b90c6224c6e83c4867 Mon Sep 17 00:00:00 2001 From: Jorgeig Date: Wed, 11 Aug 2021 11:27:27 +0800 Subject: [PATCH 026/423] Add branches to CI --- .github/workflows/build.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4d7ed950e6..fd3c2de187 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -6,6 +6,8 @@ on: - master - staging - trying + - test + - v0.5.7-as-released env: CARGO_TERM_COLOR: always From bc3eb5c54784c32ccfff404dba58a27d5a47f04e Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Mon, 16 Aug 2021 15:37:39 +0200 Subject: [PATCH 027/423] Remove linked list impl - use heapless, linked list init now const fn --- Cargo.toml | 2 +- macros/src/codegen/module.rs | 6 +- macros/src/codegen/pre_init.rs | 10 +- macros/src/codegen/timer_queue.rs | 13 +- src/export.rs | 1 + src/lib.rs | 2 - src/linked_list.rs | 597 ------------------------------ src/tq.rs | 10 +- 8 files changed, 16 insertions(+), 625 deletions(-) delete mode 100644 src/linked_list.rs diff --git a/Cargo.toml b/Cargo.toml index e3cb010b5c..ed0312df1e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -44,7 +44,7 @@ cortex-m = "0.7.0" cortex-m-rtic-macros = { path = "macros", version = "0.6.0-alpha.5" } rtic-monotonic = "0.1.0-alpha.2" rtic-core = "0.3.1" -heapless = "0.7.1" +heapless = "0.7.5" bare-metal = "1.0.0" [dependencies.dwt-systick-monotonic] diff --git a/macros/src/codegen/module.rs b/macros/src/codegen/module.rs index c7092bd370..d3afb27beb 100644 --- a/macros/src/codegen/module.rs +++ b/macros/src/codegen/module.rs @@ -327,7 +327,7 @@ pub fn codegen( impl #internal_spawn_handle_ident { pub fn cancel(self) -> Result<#ty, ()> { rtic::export::interrupt::free(|_| unsafe { - let tq = &mut *#tq.get_mut_unchecked().as_mut_ptr(); + let tq = #tq.get_mut_unchecked(); if let Some((_task, index)) = tq.cancel_marker(self.marker) { // Get the message let msg = #inputs @@ -359,7 +359,7 @@ pub fn codegen( let marker = *#tq_marker.get_mut_unchecked(); *#tq_marker.get_mut_unchecked() = #tq_marker.get_mut_unchecked().wrapping_add(1); - let tq = &mut *#tq.get_mut_unchecked().as_mut_ptr(); + let tq = #tq.get_mut_unchecked(); tq.update_marker(self.marker, marker, instant, || #pend).map(|_| #name::#m::SpawnHandle { marker }) }) @@ -420,7 +420,7 @@ pub fn codegen( *#tq_marker.get_mut_unchecked() = #tq_marker.get_mut_unchecked().wrapping_add(1); - let tq = &mut *#tq.get_mut_unchecked().as_mut_ptr(); + let tq = #tq.get_mut_unchecked(); tq.enqueue_unchecked( nr, diff --git a/macros/src/codegen/pre_init.rs b/macros/src/codegen/pre_init.rs index ae628f6eb9..5b1fdf3e75 100644 --- a/macros/src/codegen/pre_init.rs +++ b/macros/src/codegen/pre_init.rs @@ -77,18 +77,10 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec Vec>); + let n = util::capacity_literal(cap); + let tq_ty = quote!(rtic::export::TimerQueue<#mono_type, #t, #n>); // For future use // let doc = format!(" RTIC internal: {}:{}", file!(), line!()); items.push(quote!( #[doc(hidden)] static #tq: rtic::RacyCell<#tq_ty> = - rtic::RacyCell::new(core::mem::MaybeUninit::uninit()); + rtic::RacyCell::new(rtic::export::TimerQueue(rtic::export::SortedLinkedList::new_u16())); )); let mono = util::monotonic_ident(&monotonic_name); @@ -138,7 +137,7 @@ pub fn codegen(app: &App, analysis: &Analysis, _extra: &Extra) -> Vec Self { - LinkedIndex(value) - } - - #[inline] - const fn none() -> Self { - LinkedIndex(u16::MAX) - } - - #[inline] - const fn option(self) -> Option { - if self.0 == u16::MAX { - None - } else { - Some(self.0) - } - } -} - -/// A node in the linked list. -pub struct Node { - val: MaybeUninit, - next: LinkedIndex, -} - -/// Iterator for the linked list. -pub struct Iter<'a, T, Kind, const N: usize> -where - T: PartialEq + PartialOrd, - Kind: kind::Kind, -{ - list: &'a LinkedList, - index: LinkedIndex, -} - -impl<'a, T, Kind, const N: usize> Iterator for Iter<'a, T, Kind, N> -where - T: PartialEq + PartialOrd, - Kind: kind::Kind, -{ - type Item = &'a T; - - fn next(&mut self) -> Option { - let index = self.index.option()?; - - let node = self.list.node_at(index as usize); - self.index = node.next; - - Some(self.list.read_data_in_node_at(index as usize)) - } -} - -/// Comes from [`LinkedList::find_mut`]. -pub struct FindMut<'a, T, Kind, const N: usize> -where - T: PartialEq + PartialOrd, - Kind: kind::Kind, -{ - list: &'a mut LinkedList, - is_head: bool, - prev_index: LinkedIndex, - index: LinkedIndex, - maybe_changed: bool, -} - -impl<'a, T, Kind, const N: usize> FindMut<'a, T, Kind, N> -where - T: PartialEq + PartialOrd, - Kind: kind::Kind, -{ - fn pop_internal(&mut self) -> T { - if self.is_head { - // If it is the head element, we can do a normal pop - unsafe { self.list.pop_unchecked() } - } else { - // Somewhere in the list - - // Re-point the previous index - self.list.node_at_mut(self.prev_index.0 as usize).next = - self.list.node_at_mut(self.index.0 as usize).next; - - // Release the index into the free queue - self.list.node_at_mut(self.index.0 as usize).next = self.list.free; - self.list.free = self.index; - - self.list.extract_data_in_node_at(self.index.0 as usize) - } - } - - /// This will pop the element from the list. - /// - /// Complexity is O(1). - #[inline] - pub fn pop(mut self) -> T { - self.pop_internal() - } - - /// This will resort the element into the correct position in the list in needed. - /// Same as calling `drop`. - /// - /// Complexity is worst-case O(N). - #[inline] - pub fn finish(self) { - drop(self) - } -} - -impl Drop for FindMut<'_, T, Kind, N> -where - T: PartialEq + PartialOrd, - Kind: kind::Kind, -{ - fn drop(&mut self) { - // Only resort the list if the element has changed - if self.maybe_changed { - let val = self.pop_internal(); - unsafe { self.list.push_unchecked(val) }; - } - } -} - -impl Deref for FindMut<'_, T, Kind, N> -where - T: PartialEq + PartialOrd, - Kind: kind::Kind, -{ - type Target = T; - - fn deref(&self) -> &Self::Target { - self.list.read_data_in_node_at(self.index.0 as usize) - } -} - -impl DerefMut for FindMut<'_, T, Kind, N> -where - T: PartialEq + PartialOrd, - Kind: kind::Kind, -{ - fn deref_mut(&mut self) -> &mut Self::Target { - self.maybe_changed = true; - self.list.read_mut_data_in_node_at(self.index.0 as usize) - } -} - -impl fmt::Debug for FindMut<'_, T, Kind, N> -where - T: PartialEq + PartialOrd + core::fmt::Debug, - Kind: kind::Kind, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("FindMut") - .field("prev_index", &self.prev_index) - .field("index", &self.index) - .field( - "prev_value", - &self - .list - .read_data_in_node_at(self.prev_index.option().unwrap() as usize), - ) - .field( - "value", - &self - .list - .read_data_in_node_at(self.index.option().unwrap() as usize), - ) - .finish() - } -} - -/// The linked list. -pub struct LinkedList -where - T: PartialEq + PartialOrd, - Kind: kind::Kind, -{ - list: MaybeUninit<[Node; N]>, - head: LinkedIndex, - free: LinkedIndex, - _kind: PhantomData, -} - -impl LinkedList -where - T: PartialEq + PartialOrd, - Kind: kind::Kind, -{ - /// Internal helper to not do pointer arithmetic all over the place. - #[inline] - fn node_at(&self, index: usize) -> &Node { - // Safety: The entire `self.list` is initialized in `new`, which makes this safe. - unsafe { &*(self.list.as_ptr() as *const Node).add(index) } - } - - /// Internal helper to not do pointer arithmetic all over the place. - #[inline] - fn node_at_mut(&mut self, index: usize) -> &mut Node { - // Safety: The entire `self.list` is initialized in `new`, which makes this safe. - unsafe { &mut *(self.list.as_mut_ptr() as *mut Node).add(index) } - } - - /// Internal helper to not do pointer arithmetic all over the place. - #[inline] - fn write_data_in_node_at(&mut self, index: usize, data: T) { - unsafe { - self.node_at_mut(index).val.as_mut_ptr().write(data); - } - } - - /// Internal helper to not do pointer arithmetic all over the place. - #[inline] - fn read_data_in_node_at(&self, index: usize) -> &T { - unsafe { &*self.node_at(index).val.as_ptr() } - } - - /// Internal helper to not do pointer arithmetic all over the place. - #[inline] - fn read_mut_data_in_node_at(&mut self, index: usize) -> &mut T { - unsafe { &mut *self.node_at_mut(index).val.as_mut_ptr() } - } - - /// Internal helper to not do pointer arithmetic all over the place. - #[inline] - fn extract_data_in_node_at(&mut self, index: usize) -> T { - unsafe { self.node_at(index).val.as_ptr().read() } - } - - /// Internal helper to not do pointer arithmetic all over the place. - /// Safety: This can overwrite existing allocated nodes if used improperly, meaning their - /// `Drop` methods won't run. - #[inline] - unsafe fn write_node_at(&mut self, index: usize, node: Node) { - (self.list.as_mut_ptr() as *mut Node) - .add(index) - .write(node) - } - - /// Create a new linked list. - pub fn new() -> Self { - let mut list = LinkedList { - list: MaybeUninit::uninit(), - head: LinkedIndex::none(), - free: unsafe { LinkedIndex::new_unchecked(0) }, - _kind: PhantomData, - }; - - let len = N as u16; - let mut free = 0; - - if len == 0 { - list.free = LinkedIndex::none(); - return list; - } - - // Initialize indexes - while free < len - 1 { - unsafe { - list.write_node_at( - free as usize, - Node { - val: MaybeUninit::uninit(), - next: LinkedIndex::new_unchecked(free + 1), - }, - ); - } - free += 1; - } - - // Initialize final index - unsafe { - list.write_node_at( - free as usize, - Node { - val: MaybeUninit::uninit(), - next: LinkedIndex::none(), - }, - ); - } - - list - } - - /// Push unchecked - /// - /// Complexity is O(N). - /// - /// # Safety - /// - /// Assumes that the list is not full. - pub unsafe fn push_unchecked(&mut self, value: T) { - let new = self.free.0; - // Store the data and update the next free spot - self.write_data_in_node_at(new as usize, value); - self.free = self.node_at(new as usize).next; - - if let Some(head) = self.head.option() { - // Check if we need to replace head - if self - .read_data_in_node_at(head as usize) - .partial_cmp(self.read_data_in_node_at(new as usize)) - != Kind::ordering() - { - self.node_at_mut(new as usize).next = self.head; - self.head = LinkedIndex::new_unchecked(new); - } else { - // It's not head, search the list for the correct placement - let mut current = head; - - while let Some(next) = self.node_at(current as usize).next.option() { - if self - .read_data_in_node_at(next as usize) - .partial_cmp(self.read_data_in_node_at(new as usize)) - != Kind::ordering() - { - break; - } - - current = next; - } - - self.node_at_mut(new as usize).next = self.node_at(current as usize).next; - self.node_at_mut(current as usize).next = LinkedIndex::new_unchecked(new); - } - } else { - self.node_at_mut(new as usize).next = self.head; - self.head = LinkedIndex::new_unchecked(new); - } - } - - /// Pushes an element to the linked list and sorts it into place. - /// - /// Complexity is O(N). - pub fn push(&mut self, value: T) -> Result<(), T> { - if !self.is_full() { - Ok(unsafe { self.push_unchecked(value) }) - } else { - Err(value) - } - } - - /// Get an iterator over the sorted list. - pub fn iter(&self) -> Iter<'_, T, Kind, N> { - Iter { - list: self, - index: self.head, - } - } - - /// Find an element in the list. - pub fn find_mut(&mut self, mut f: F) -> Option> - where - F: FnMut(&T) -> bool, - { - let head = self.head.option()?; - - // Special-case, first element - if f(self.read_data_in_node_at(head as usize)) { - return Some(FindMut { - is_head: true, - prev_index: LinkedIndex::none(), - index: self.head, - list: self, - maybe_changed: false, - }); - } - - let mut current = head; - - while let Some(next) = self.node_at(current as usize).next.option() { - if f(self.read_data_in_node_at(next as usize)) { - return Some(FindMut { - is_head: false, - prev_index: unsafe { LinkedIndex::new_unchecked(current) }, - index: unsafe { LinkedIndex::new_unchecked(next) }, - list: self, - maybe_changed: false, - }); - } - - current = next; - } - - None - } - - /// Peek at the first element. - pub fn peek(&self) -> Option<&T> { - self.head - .option() - .map(|head| self.read_data_in_node_at(head as usize)) - } - - /// Pop unchecked - /// - /// # Safety - /// - /// Assumes that the list is not empty. - pub unsafe fn pop_unchecked(&mut self) -> T { - let head = self.head.0; - let current = head; - self.head = self.node_at(head as usize).next; - self.node_at_mut(current as usize).next = self.free; - self.free = LinkedIndex::new_unchecked(current); - - self.extract_data_in_node_at(current as usize) - } - - /// Pops the first element in the list. - /// - /// Complexity is O(1). - pub fn pop(&mut self) -> Result { - if !self.is_empty() { - Ok(unsafe { self.pop_unchecked() }) - } else { - Err(()) - } - } - - /// Checks if the linked list is full. - #[inline] - pub fn is_full(&self) -> bool { - self.free.option().is_none() - } - - /// Checks if the linked list is empty. - #[inline] - pub fn is_empty(&self) -> bool { - self.head.option().is_none() - } -} - -impl Drop for LinkedList -where - T: PartialEq + PartialOrd, - Kind: kind::Kind, -{ - fn drop(&mut self) { - let mut index = self.head; - - while let Some(i) = index.option() { - let node = self.node_at_mut(i as usize); - index = node.next; - - unsafe { - ptr::drop_in_place(node.val.as_mut_ptr()); - } - } - } -} - -impl fmt::Debug for LinkedList -where - T: PartialEq + PartialOrd + core::fmt::Debug, - Kind: kind::Kind, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_list().entries(self.iter()).finish() - } -} - -/// Min sorted linked list. -pub struct Min; - -/// Max sorted linked list. -pub struct Max; - -/// Sealed traits and implementations for `linked_list` -pub mod kind { - use super::{Max, Min}; - use core::cmp::Ordering; - - /// The linked list kind: min first or max first - pub unsafe trait Kind { - #[doc(hidden)] - fn ordering() -> Option; - } - - unsafe impl Kind for Min { - #[inline] - fn ordering() -> Option { - Some(Ordering::Less) - } - } - - unsafe impl Kind for Max { - #[inline] - fn ordering() -> Option { - Some(Ordering::Greater) - } - } -} - -#[cfg(test)] -mod tests { - // Note this useful idiom: importing names from outer (for mod tests) scope. - use super::*; - - #[test] - fn test_peek() { - let mut ll: LinkedList = LinkedList::new(); - - ll.push(1).unwrap(); - assert_eq!(ll.peek().unwrap(), &1); - - ll.push(2).unwrap(); - assert_eq!(ll.peek().unwrap(), &2); - - ll.push(3).unwrap(); - assert_eq!(ll.peek().unwrap(), &3); - - let mut ll: LinkedList = LinkedList::new(); - - ll.push(2).unwrap(); - assert_eq!(ll.peek().unwrap(), &2); - - ll.push(1).unwrap(); - assert_eq!(ll.peek().unwrap(), &1); - - ll.push(3).unwrap(); - assert_eq!(ll.peek().unwrap(), &1); - } - - #[test] - fn test_full() { - let mut ll: LinkedList = LinkedList::new(); - ll.push(1).unwrap(); - ll.push(2).unwrap(); - ll.push(3).unwrap(); - - assert!(ll.is_full()) - } - - #[test] - fn test_empty() { - let ll: LinkedList = LinkedList::new(); - - assert!(ll.is_empty()) - } - - #[test] - fn test_zero_size() { - let ll: LinkedList = LinkedList::new(); - - assert!(ll.is_empty()); - assert!(ll.is_full()); - } - - #[test] - fn test_rejected_push() { - let mut ll: LinkedList = LinkedList::new(); - ll.push(1).unwrap(); - ll.push(2).unwrap(); - ll.push(3).unwrap(); - - // This won't fit - let r = ll.push(4); - - assert_eq!(r, Err(4)); - } - - #[test] - fn test_updating() { - let mut ll: LinkedList = LinkedList::new(); - ll.push(1).unwrap(); - ll.push(2).unwrap(); - ll.push(3).unwrap(); - - let mut find = ll.find_mut(|v| *v == 2).unwrap(); - - *find += 1000; - find.finish(); - - assert_eq!(ll.peek().unwrap(), &1002); - - let mut find = ll.find_mut(|v| *v == 3).unwrap(); - - *find += 1000; - find.finish(); - - assert_eq!(ll.peek().unwrap(), &1003); - - // Remove largest element - ll.find_mut(|v| *v == 1003).unwrap().pop(); - - assert_eq!(ll.peek().unwrap(), &1002); - } -} diff --git a/src/tq.rs b/src/tq.rs index cd44abe2bc..dcaccc9e7d 100644 --- a/src/tq.rs +++ b/src/tq.rs @@ -1,9 +1,9 @@ use crate::{ - linked_list::{LinkedList, Min}, time::{Clock, Instant}, Monotonic, }; use core::cmp::Ordering; +use heapless::sorted_linked_list::{LinkedIndexU16, Min, SortedLinkedList}; #[inline(always)] fn unwrapper(val: Result) -> T { @@ -14,7 +14,9 @@ fn unwrapper(val: Result) -> T { } } -pub struct TimerQueue(pub LinkedList, Min, N>) +pub struct TimerQueue( + pub SortedLinkedList, LinkedIndexU16, Min, N>, +) where Mono: Monotonic, Task: Copy; @@ -24,10 +26,6 @@ where Mono: Monotonic, Task: Copy, { - pub fn new() -> Self { - TimerQueue(LinkedList::new()) - } - /// # Safety /// /// Writing to memory with a transmute in order to enable From 13f7516a4d88f15763b7186cc3a95ccb3cfbd5c0 Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Thu, 19 Aug 2021 08:21:18 +0200 Subject: [PATCH 028/423] Fixed some lints from Rust Analyzer with experimental proc-macros --- macros/src/codegen/dispatchers.rs | 3 +++ macros/src/codegen/local_resources.rs | 2 ++ macros/src/codegen/module.rs | 5 +++++ macros/src/codegen/shared_resources.rs | 1 + macros/src/codegen/software_tasks.rs | 6 ++++++ macros/src/codegen/timer_queue.rs | 5 +++++ 6 files changed, 22 insertions(+) diff --git a/macros/src/codegen/dispatchers.rs b/macros/src/codegen/dispatchers.rs index c239b0f828..cfd8ee95a6 100644 --- a/macros/src/codegen/dispatchers.rs +++ b/macros/src/codegen/dispatchers.rs @@ -33,6 +33,7 @@ pub fn codegen(app: &App, analysis: &Analysis, _extra: &Extra) -> Vec Vec = rtic::RacyCell::new(#rq_expr); )); diff --git a/macros/src/codegen/local_resources.rs b/macros/src/codegen/local_resources.rs index a9ffa925b1..011d88671b 100644 --- a/macros/src/codegen/local_resources.rs +++ b/macros/src/codegen/local_resources.rs @@ -33,6 +33,7 @@ pub fn codegen( // For future use // let doc = format!(" RTIC internal: {}:{}", file!(), line!()); mod_app.push(quote!( + #[allow(non_camel_case_types)] #[allow(non_upper_case_globals)] // #[doc = #doc] #[doc(hidden)] @@ -58,6 +59,7 @@ pub fn codegen( // For future use // let doc = format!(" RTIC internal: {}:{}", file!(), line!()); mod_app.push(quote!( + #[allow(non_camel_case_types)] #[allow(non_upper_case_globals)] // #[doc = #doc] #[doc(hidden)] diff --git a/macros/src/codegen/module.rs b/macros/src/codegen/module.rs index d3afb27beb..cac89c7d76 100644 --- a/macros/src/codegen/module.rs +++ b/macros/src/codegen/module.rs @@ -131,6 +131,7 @@ pub fn codegen( items.push(quote!( /// Monotonics used by the system #[allow(non_snake_case)] + #[allow(non_camel_case_types)] pub struct #internal_monotonics_ident( #(pub #monotonic_types),* ); @@ -178,6 +179,8 @@ pub fn codegen( items.push(quote!( #(#cfgs)* /// Execution context + #[allow(non_snake_case)] + #[allow(non_camel_case_types)] pub struct #internal_context_name<#lt> { #(#fields,)* } @@ -318,6 +321,8 @@ pub fn codegen( items.push(quote!( #(#cfgs)* + #[allow(non_snake_case)] + #[allow(non_camel_case_types)] pub struct #internal_spawn_handle_ident { #[doc(hidden)] marker: u32, diff --git a/macros/src/codegen/shared_resources.rs b/macros/src/codegen/shared_resources.rs index 181832fd58..479ca93e9c 100644 --- a/macros/src/codegen/shared_resources.rs +++ b/macros/src/codegen/shared_resources.rs @@ -30,6 +30,7 @@ pub fn codegen( // For future use // let doc = format!(" RTIC internal: {}:{}", file!(), line!()); mod_app.push(quote!( + #[allow(non_camel_case_types)] #[allow(non_upper_case_globals)] // #[doc = #doc] #[doc(hidden)] diff --git a/macros/src/codegen/software_tasks.rs b/macros/src/codegen/software_tasks.rs index 0b07335946..17399b5031 100644 --- a/macros/src/codegen/software_tasks.rs +++ b/macros/src/codegen/software_tasks.rs @@ -49,6 +49,8 @@ pub fn codegen( mod_app.push(quote!( // /// Queue version of a free-list that keeps track of empty slots in // /// the following buffers + #[allow(non_camel_case_types)] + #[allow(non_upper_case_globals)] #[doc(hidden)] static #fq: rtic::RacyCell<#fq_ty> = rtic::RacyCell::new(#fq_expr); )); @@ -69,6 +71,8 @@ pub fn codegen( #uninit // /// Buffer that holds the instants associated to the inputs of a task // #[doc = #doc] + #[allow(non_camel_case_types)] + #[allow(non_upper_case_globals)] #[doc(hidden)] static #instants: rtic::RacyCell<[core::mem::MaybeUninit>; #cap_lit]> = @@ -82,6 +86,8 @@ pub fn codegen( mod_app.push(quote!( #uninit // /// Buffer that holds the inputs of a task + #[allow(non_camel_case_types)] + #[allow(non_upper_case_globals)] #[doc(hidden)] static #inputs_ident: rtic::RacyCell<[core::mem::MaybeUninit<#input_ty>; #cap_lit]> = rtic::RacyCell::new([#(#elems,)*]); diff --git a/macros/src/codegen/timer_queue.rs b/macros/src/codegen/timer_queue.rs index bf896ee654..e805292a7f 100644 --- a/macros/src/codegen/timer_queue.rs +++ b/macros/src/codegen/timer_queue.rs @@ -15,6 +15,7 @@ pub fn codegen(app: &App, analysis: &Analysis, _extra: &Extra) -> Vec = rtic::RacyCell::new(0); )); @@ -74,6 +75,8 @@ pub fn codegen(app: &App, analysis: &Analysis, _extra: &Extra) -> Vec = rtic::RacyCell::new(rtic::export::TimerQueue(rtic::export::SortedLinkedList::new_u16())); )); @@ -85,6 +88,8 @@ pub fn codegen(app: &App, analysis: &Analysis, _extra: &Extra) -> Vec> = rtic::RacyCell::new(None); )); } From 018e4a121f4c338ec66e58dcf210b78d7ab35668 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Tj=C3=A4der?= Date: Thu, 19 Aug 2021 13:02:17 +0200 Subject: [PATCH 029/423] Silence rust-analyzer warnings on internal types --- macros/src/codegen/local_resources_struct.rs | 1 + macros/src/codegen/shared_resources_struct.rs | 1 + 2 files changed, 2 insertions(+) diff --git a/macros/src/codegen/local_resources_struct.rs b/macros/src/codegen/local_resources_struct.rs index 4bd9e2a45d..d59918d7a4 100644 --- a/macros/src/codegen/local_resources_struct.rs +++ b/macros/src/codegen/local_resources_struct.rs @@ -89,6 +89,7 @@ pub fn codegen(ctxt: Context, needs_lt: &mut bool, app: &App) -> (TokenStream2, let ident = util::mark_internal_ident(&ident); let item = quote!( #[allow(non_snake_case)] + #[allow(non_camel_case_types)] #[doc = #doc] pub struct #ident<#lt> { #(#fields,)* diff --git a/macros/src/codegen/shared_resources_struct.rs b/macros/src/codegen/shared_resources_struct.rs index 301bef738d..fa344bb7a2 100644 --- a/macros/src/codegen/shared_resources_struct.rs +++ b/macros/src/codegen/shared_resources_struct.rs @@ -105,6 +105,7 @@ pub fn codegen(ctxt: Context, needs_lt: &mut bool, app: &App) -> (TokenStream2, let ident = util::mark_internal_ident(&ident); let item = quote!( #[allow(non_snake_case)] + #[allow(non_camel_case_types)] #[doc = #doc] pub struct #ident<#lt> { #(#fields,)* From cdbd8a2cede668e1181030bd7c439592a8f0e980 Mon Sep 17 00:00:00 2001 From: datdenkikniet Date: Thu, 19 Aug 2021 21:32:12 +0200 Subject: [PATCH 030/423] Use `mark_internal_name` by default for methods in `util` to make usage of these functions more straightforward. fq_ident is always internal rq_ident is always internal monotonic_ident is always internal inputs_ident is always internal local_resources_ident is always internal shared_resources_ident is always internal monotonic_instants_ident is always internal tq_ident is always internal timer_queue_marker_ident is always internal static_shared_resource_ident is always internal static_local_resource_ident is always internal declared_static_local_resource_ident is always internal Only names, not idents, are now marked as internal Use same rtic internal everywhere --- macros/src/codegen.rs | 1 - macros/src/codegen/dispatchers.rs | 3 - macros/src/codegen/local_resources.rs | 7 +- macros/src/codegen/local_resources_struct.rs | 7 +- macros/src/codegen/module.rs | 10 +-- macros/src/codegen/post_init.rs | 5 +- macros/src/codegen/pre_init.rs | 1 - macros/src/codegen/shared_resources.rs | 2 +- macros/src/codegen/shared_resources_struct.rs | 3 +- macros/src/codegen/software_tasks.rs | 3 - macros/src/codegen/timer_queue.rs | 6 +- macros/src/codegen/util.rs | 81 +++++++------------ 12 files changed, 40 insertions(+), 89 deletions(-) diff --git a/macros/src/codegen.rs b/macros/src/codegen.rs index 6920031add..63a4e3cadc 100644 --- a/macros/src/codegen.rs +++ b/macros/src/codegen.rs @@ -108,7 +108,6 @@ pub fn app(app: &App, analysis: &Analysis, extra: &Extra) -> TokenStream2 { let name = &monotonic.ident; let name_str = &name.to_string(); let ident = util::monotonic_ident(&name_str); - let ident = util::mark_internal_ident(&ident); let panic_str = &format!( "Use of monotonic '{}' before it was passed to the runtime", name_str diff --git a/macros/src/codegen/dispatchers.rs b/macros/src/codegen/dispatchers.rs index cfd8ee95a6..57103acd0b 100644 --- a/macros/src/codegen/dispatchers.rs +++ b/macros/src/codegen/dispatchers.rs @@ -45,7 +45,6 @@ pub fn codegen(app: &App, analysis: &Analysis, _extra: &Extra) -> Vec), @@ -72,9 +71,7 @@ pub fn codegen(app: &App, analysis: &Analysis, _extra: &Extra) -> Vec (TokenStream2, }; let mangled_name = if matches!(task_local, TaskLocal::External) { - util::mark_internal_ident(&util::static_local_resource_ident(name)) + util::static_local_resource_ident(name) } else { - util::mark_internal_ident(&util::declared_static_local_resource_ident( - name, &task_name, - )) + util::declared_static_local_resource_ident(name, &task_name) }; fields.push(quote!( @@ -86,7 +84,6 @@ pub fn codegen(ctxt: Context, needs_lt: &mut bool, app: &App) -> (TokenStream2, let doc = format!("Local resources `{}` has access to", ctxt.ident(app)); let ident = util::local_resources_ident(ctxt, app); - let ident = util::mark_internal_ident(&ident); let item = quote!( #[allow(non_snake_case)] #[allow(non_camel_case_types)] diff --git a/macros/src/codegen/module.rs b/macros/src/codegen/module.rs index cac89c7d76..ddaf1e52ae 100644 --- a/macros/src/codegen/module.rs +++ b/macros/src/codegen/module.rs @@ -67,7 +67,6 @@ pub fn codegen( if ctxt.has_local_resources(app) { let ident = util::local_resources_ident(ctxt, app); - let ident = util::mark_internal_ident(&ident); let lt = if local_resources_tick { lt = Some(quote!('a)); Some(quote!('a)) @@ -90,7 +89,6 @@ pub fn codegen( if ctxt.has_shared_resources(app) { let ident = util::shared_resources_ident(ctxt, app); - let ident = util::mark_internal_ident(&ident); let lt = if shared_resources_tick { lt = Some(quote!('a)); Some(quote!('a)) @@ -212,11 +210,8 @@ pub fn codegen( let args = &args; let tupled = &tupled; let fq = util::fq_ident(name); - let fq = util::mark_internal_ident(&fq); let rq = util::rq_ident(priority); - let rq = util::mark_internal_ident(&rq); let inputs = util::inputs_ident(name); - let inputs = util::mark_internal_ident(&inputs); let device = &extra.device; let enum_ = util::interrupt_ident(); @@ -266,16 +261,13 @@ pub fn codegen( // Schedule caller for (_, monotonic) in &app.monotonics { let instants = util::monotonic_instants_ident(name, &monotonic.ident); - let instants = util::mark_internal_ident(&instants); let monotonic_name = monotonic.ident.to_string(); let tq = util::tq_ident(&monotonic.ident.to_string()); - let tq = util::mark_internal_ident(&tq); let t = util::schedule_t_ident(); let m = &monotonic.ident; let mono_type = &monotonic.ident; let m_ident = util::monotonic_ident(&monotonic_name); - let m_ident = util::mark_internal_ident(&m_ident); let m_isr = &monotonic.args.binds; let enum_ = util::interrupt_ident(); @@ -293,7 +285,7 @@ pub fn codegen( ) }; - let tq_marker = util::mark_internal_ident(&util::timer_queue_marker_ident()); + let tq_marker = &util::timer_queue_marker_ident(); // For future use // let doc = format!(" RTIC internal: {}:{}", file!(), line!()); diff --git a/macros/src/codegen/post_init.rs b/macros/src/codegen/post_init.rs index 161068d2bd..5624b20a72 100644 --- a/macros/src/codegen/post_init.rs +++ b/macros/src/codegen/post_init.rs @@ -11,7 +11,7 @@ pub fn codegen(app: &App, analysis: &Analysis) -> Vec { // Initialize shared resources for (name, res) in &app.shared_resources { - let mangled_name = util::mark_internal_ident(&util::static_shared_resource_ident(name)); + let mangled_name = util::static_shared_resource_ident(name); // If it's live let cfgs = res.cfgs.clone(); if analysis.shared_resource_locations.get(name).is_some() { @@ -29,7 +29,7 @@ pub fn codegen(app: &App, analysis: &Analysis) -> Vec { // Initialize local resources for (name, res) in &app.local_resources { - let mangled_name = util::mark_internal_ident(&util::static_local_resource_ident(name)); + let mangled_name = util::static_local_resource_ident(name); // If it's live let cfgs = res.cfgs.clone(); if analysis.local_resource_locations.get(name).is_some() { @@ -58,7 +58,6 @@ pub fn codegen(app: &App, analysis: &Analysis) -> Vec { // Store the monotonic let name = util::monotonic_ident(&monotonic.to_string()); - let name = util::mark_internal_ident(&name); stmts.push(quote!(*#name.get_mut_unchecked() = Some(monotonics.#idx);)); } diff --git a/macros/src/codegen/pre_init.rs b/macros/src/codegen/pre_init.rs index 5b1fdf3e75..d3c4f54d9e 100644 --- a/macros/src/codegen/pre_init.rs +++ b/macros/src/codegen/pre_init.rs @@ -17,7 +17,6 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec (TokenStream2, None }; let ty = &res.ty; - let mangled_name = util::mark_internal_ident(&util::static_shared_resource_ident(&name)); + let mangled_name = util::static_shared_resource_ident(&name); if !res.properties.lock_free { if access.is_shared() { @@ -102,7 +102,6 @@ pub fn codegen(ctxt: Context, needs_lt: &mut bool, app: &App) -> (TokenStream2, let doc = format!("Shared resources `{}` has access to", ctxt.ident(app)); let ident = util::shared_resources_ident(ctxt, app); - let ident = util::mark_internal_ident(&ident); let item = quote!( #[allow(non_snake_case)] #[allow(non_camel_case_types)] diff --git a/macros/src/codegen/software_tasks.rs b/macros/src/codegen/software_tasks.rs index 17399b5031..1669a3fb1e 100644 --- a/macros/src/codegen/software_tasks.rs +++ b/macros/src/codegen/software_tasks.rs @@ -37,7 +37,6 @@ pub fn codegen( // Create free queues and inputs / instants buffers let fq = util::fq_ident(name); - let fq = util::mark_internal_ident(&fq); let (fq_ty, fq_expr, mk_uninit): (_, _, Box Option<_>>) = { ( @@ -61,7 +60,6 @@ pub fn codegen( for (_, monotonic) in &app.monotonics { let instants = util::monotonic_instants_ident(name, &monotonic.ident); - let instants = util::mark_internal_ident(&instants); let mono_type = &monotonic.ty; let uninit = mk_uninit(); @@ -82,7 +80,6 @@ pub fn codegen( let uninit = mk_uninit(); let inputs_ident = util::inputs_ident(name); - let inputs_ident = util::mark_internal_ident(&inputs_ident); mod_app.push(quote!( #uninit // /// Buffer that holds the inputs of a task diff --git a/macros/src/codegen/timer_queue.rs b/macros/src/codegen/timer_queue.rs index e805292a7f..fdfa6381a7 100644 --- a/macros/src/codegen/timer_queue.rs +++ b/macros/src/codegen/timer_queue.rs @@ -10,7 +10,7 @@ pub fn codegen(app: &App, analysis: &Analysis, _extra: &Extra) -> Vec Vec Vec Vec LitInt { LitInt::new(&capacity.to_string(), Span::call_site()) @@ -14,7 +16,7 @@ pub fn capacity_literal(capacity: usize) -> LitInt { /// Identifier for the free queue pub fn fq_ident(task: &Ident) -> Ident { - Ident::new(&format!("{}_FQ", task.to_string()), Span::call_site()) + mark_internal_name(&format!("{}_FQ", task.to_string())) } /// Generates a `Mutex` implementation @@ -60,15 +62,12 @@ pub fn impl_mutex( /// Generates an identifier for the `INPUTS` buffer (`spawn` & `schedule` API) pub fn inputs_ident(task: &Ident) -> Ident { - Ident::new(&format!("{}_INPUTS", task), Span::call_site()) + mark_internal_name(&format!("{}_INPUTS", task)) } /// Generates an identifier for the `INSTANTS` buffer (`schedule` API) pub fn monotonic_instants_ident(task: &Ident, monotonic: &Ident) -> Ident { - Ident::new( - &format!("{}_{}_INSTANTS", task, monotonic), - Span::call_site(), - ) + mark_internal_name(&format!("{}_{}_INSTANTS", task, monotonic)) } pub fn interrupt_ident() -> Ident { @@ -77,8 +76,7 @@ pub fn interrupt_ident() -> Ident { } pub fn timer_queue_marker_ident() -> Ident { - let span = Span::call_site(); - Ident::new("TIMER_QUEUE_MARKER", span) + mark_internal_name(&"TIMER_QUEUE_MARKER") } /// Whether `name` is an exception with configurable priority @@ -98,38 +96,24 @@ pub fn is_exception(name: &Ident) -> bool { ) } +/// Mark a name as internal +pub fn mark_internal_name(name: &str) -> Ident { + Ident::new(&format!("{}_{}", RTIC_INTERNAL, name), Span::call_site()) +} + /// Generate an internal identifier for monotonics pub fn internal_monotonics_ident(task: &Ident, monotonic: &Ident, ident_name: &str) -> Ident { - Ident::new( - &format!( - "__rtic_internal_{}_{}_{}", - task.to_string(), - monotonic.to_string(), - ident_name, - ), - Span::call_site(), - ) + mark_internal_name(&format!( + "{}_{}_{}", + task.to_string(), + monotonic.to_string(), + ident_name, + )) } /// Generate an internal identifier for tasks pub fn internal_task_ident(task: &Ident, ident_name: &str) -> Ident { - Ident::new( - &format!("__rtic_internal_{}_{}", task.to_string(), ident_name,), - Span::call_site(), - ) -} - -/// Mark an ident as internal -pub fn mark_internal_ident(ident: &Ident) -> Ident { - Ident::new( - &format!("__rtic_internal_{}", ident.to_string()), - Span::call_site(), - ) -} - -/// Mark an ident as internal -pub fn mark_internal_name(name: &str) -> Ident { - Ident::new(&format!("__rtic_internal_{}", name), Span::call_site()) + mark_internal_name(&format!("{}_{}", task.to_string(), ident_name)) } fn link_section_index() -> usize { @@ -215,7 +199,7 @@ pub fn shared_resources_ident(ctxt: Context, app: &App) -> Ident { s.push_str("SharedResources"); - Ident::new(&s, Span::call_site()) + mark_internal_name(&s) } /// Generates a pre-reexport identifier for the "local resources" struct @@ -228,7 +212,7 @@ pub fn local_resources_ident(ctxt: Context, app: &App) -> Ident { s.push_str("LocalResources"); - Ident::new(&s, Span::call_site()) + mark_internal_name(&s) } /// Generates an identifier for a ready queue @@ -236,7 +220,7 @@ pub fn local_resources_ident(ctxt: Context, app: &App) -> Ident { /// There may be several task dispatchers, one for each priority level. /// The ready queues are SPSC queues pub fn rq_ident(priority: u8) -> Ident { - Ident::new(&format!("P{}_RQ", priority), Span::call_site()) + mark_internal_name(&format!("P{}_RQ", priority)) } /// Generates an identifier for the `enum` of `schedule`-able tasks @@ -260,33 +244,28 @@ pub fn suffixed(name: &str) -> Ident { /// Generates an identifier for a timer queue pub fn tq_ident(name: &str) -> Ident { - Ident::new(&format!("TQ_{}", name), Span::call_site()) + mark_internal_name(&format!("TQ_{}", name)) } /// Generates an identifier for monotonic timer storage pub fn monotonic_ident(name: &str) -> Ident { - Ident::new(&format!("MONOTONIC_STORAGE_{}", name), Span::call_site()) + mark_internal_name(&format!("MONOTONIC_STORAGE_{}", name)) } pub fn static_shared_resource_ident(name: &Ident) -> Ident { - Ident::new( - &format!("shared_resource_{}", name.to_string()), - Span::call_site(), - ) + mark_internal_name(&format!("shared_resource_{}", name.to_string())) } pub fn static_local_resource_ident(name: &Ident) -> Ident { - Ident::new( - &format!("local_resource_{}", name.to_string()), - Span::call_site(), - ) + mark_internal_name(&format!("local_resource_{}", name.to_string())) } pub fn declared_static_local_resource_ident(name: &Ident, task_name: &Ident) -> Ident { - Ident::new( - &format!("local_{}_{}", task_name.to_string(), name.to_string()), - Span::call_site(), - ) + mark_internal_name(&format!( + "local_{}_{}", + task_name.to_string(), + name.to_string() + )) } /// The name to get better RT flag errors From 52dc324aa7eafd855a22a95248feab70f1c1a19d Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Fri, 20 Aug 2021 09:21:02 +0200 Subject: [PATCH 031/423] More rustanalyzer lint fixes --- macros/src/codegen/module.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/macros/src/codegen/module.rs b/macros/src/codegen/module.rs index cac89c7d76..656ae6c8a4 100644 --- a/macros/src/codegen/module.rs +++ b/macros/src/codegen/module.rs @@ -376,6 +376,7 @@ pub fn codegen( /// /// This will use the time `Instant::new(0)` as baseline if called in `#[init]`, /// so if you use a non-resetable timer use `spawn_at` when in `#[init]` + #[allow(non_snake_case)] pub fn #internal_spawn_after_ident( duration: D #(,#args)* @@ -395,6 +396,7 @@ pub fn codegen( #(#cfgs)* /// Spawns the task at a fixed time instant + #[allow(non_snake_case)] pub fn #internal_spawn_at_ident( instant: rtic::time::Instant<#mono_type> #(,#args)* From 81a7722ab637fa1699c3eb22b38daa4269572a0c Mon Sep 17 00:00:00 2001 From: Alex Martens Date: Thu, 26 Aug 2021 14:29:18 -0700 Subject: [PATCH 032/423] Fix link for SLEEPONEXIT --- book/en/src/by-example/app.md | 2 +- book/ru/src/by-example/app.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/book/en/src/by-example/app.md b/book/en/src/by-example/app.md index 5fd1c25051..04535c140e 100644 --- a/book/en/src/by-example/app.md +++ b/book/en/src/by-example/app.md @@ -72,7 +72,7 @@ so it must run forever. When no `idle` function is declared, the runtime sets the [SLEEPONEXIT] bit and then sends the microcontroller to sleep after running `init`. -[SLEEPONEXIT]: https://developer.arm.com/docs/100737/0100/power-management/sleep-mode/sleep-on-exit-bit +[SLEEPONEXIT]: https://developer.arm.com/docs/100737/0100/Power-management/Sleep-mode/Sleep-on-exit-bit Like in `init`, `static mut` variables will be transformed into `&'static mut` references that are safe to access. Notice, this feature may be deprecated in the next release, see `task_local` resources. diff --git a/book/ru/src/by-example/app.md b/book/ru/src/by-example/app.md index 5beca23930..c2b518fd88 100644 --- a/book/ru/src/by-example/app.md +++ b/book/ru/src/by-example/app.md @@ -73,7 +73,7 @@ $ cargo run --example init Если функция `idle` не определена, среда вполнения устанавливает бит [SLEEPONEXIT], а затем отправляет микроконтроллер в сон после запуска `init`. -[SLEEPONEXIT]: https://developer.arm.com/docs/100737/0100/power-management/sleep-mode/sleep-on-exit-bit +[SLEEPONEXIT]: https://developer.arm.com/docs/100737/0100/Power-management/Sleep-mode/Sleep-on-exit-bit Как и в `init`, `static mut` переменные будут трансформированы в `&'static mut` ссылки, безопасные для доступа. Обратите внимание, данная возможность может From 2457af1612068ba1ba8002fc4f8c254bd4a55022 Mon Sep 17 00:00:00 2001 From: Jorge Iglesias Garcia <44316552+jorgeig-space@users.noreply.github.com> Date: Fri, 27 Aug 2021 11:24:30 +0800 Subject: [PATCH 033/423] Update README.md --- README.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/README.md b/README.md index 78b6fa86ab..de7ab541dc 100644 --- a/README.md +++ b/README.md @@ -52,6 +52,16 @@ Formerly known as Real-Time For the Masses. - Applications must be written using the 2018 edition. +### `cortex-m` 0.6 vs 0.7 in RTIC 0.5.x + +`cortex-m` 0.7 started using trait `InterruptNumber` for interrupts instead of `Nr` from `bare-metal`. In order to preserve backwards compatibility, RTIC 0.5.x will keep using `cortex-m` 0.6 by default. `cortex-m` 0.7 can be enabled using the feature `cortex-m-7` and disabling default features, e.g. on your `Cargo.toml`: + +``` +cortex-m-rtic = { version = "0.5.8", default-features = false, features = ["cortex-m-7"] } +``` + +RTIC 0.6 already uses `cortex-m` 0.7 by default. + ## [User documentation](https://rtic.rs) - [(Development version)](https://rtic.rs/dev) ## [API reference](https://rtic.rs/stable/api/) From 58c2b6c829ece84bf66b2c9dbbf42c7249201d2e Mon Sep 17 00:00:00 2001 From: Jorge Iglesias Garcia <44316552+jorgeig-space@users.noreply.github.com> Date: Fri, 27 Aug 2021 11:55:20 +0800 Subject: [PATCH 034/423] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index de7ab541dc..b9bfb393be 100644 --- a/README.md +++ b/README.md @@ -52,9 +52,9 @@ Formerly known as Real-Time For the Masses. - Applications must be written using the 2018 edition. -### `cortex-m` 0.6 vs 0.7 in RTIC 0.5.x +### Crate `cortex-m` 0.6 vs 0.7 in RTIC 0.5.x -`cortex-m` 0.7 started using trait `InterruptNumber` for interrupts instead of `Nr` from `bare-metal`. In order to preserve backwards compatibility, RTIC 0.5.x will keep using `cortex-m` 0.6 by default. `cortex-m` 0.7 can be enabled using the feature `cortex-m-7` and disabling default features, e.g. on your `Cargo.toml`: +The crate `cortex-m` 0.7 started using trait `InterruptNumber` for interrupts instead of `Nr` from `bare-metal`. In order to preserve backwards compatibility, RTIC 0.5.x will keep using `cortex-m` 0.6 by default. `cortex-m` 0.7 can be enabled using the feature `cortex-m-7` and disabling default features: ``` cortex-m-rtic = { version = "0.5.8", default-features = false, features = ["cortex-m-7"] } From eec2e9c154e421f7e12070d40a925e9e2c689986 Mon Sep 17 00:00:00 2001 From: Jorge Iglesias Garcia <44316552+jorgeig-space@users.noreply.github.com> Date: Fri, 27 Aug 2021 12:00:33 +0800 Subject: [PATCH 035/423] Revert CI changes --- .github/workflows/build.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index fd3c2de187..4d7ed950e6 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -6,8 +6,6 @@ on: - master - staging - trying - - test - - v0.5.7-as-released env: CARGO_TERM_COLOR: always From 1a49d67490e3c1b9cb640d459890f4c880a52096 Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Tue, 31 Aug 2021 19:50:50 +0200 Subject: [PATCH 036/423] validate unused dispatchers closes #521 --- macros/src/codegen/pre_init.rs | 11 +++++++---- ui/unknown-interrupt.rs | 15 +++++++++++++++ ui/unknown-interrupt.stderr | 5 +++++ 3 files changed, 27 insertions(+), 4 deletions(-) create mode 100644 ui/unknown-interrupt.rs create mode 100644 ui/unknown-interrupt.stderr diff --git a/macros/src/codegen/pre_init.rs b/macros/src/codegen/pre_init.rs index d3c4f54d9e..626d17a0a6 100644 --- a/macros/src/codegen/pre_init.rs +++ b/macros/src/codegen/pre_init.rs @@ -31,6 +31,13 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec Vec Vec (Shared, Local, init::Monotonics) { + (Shared {}, Local {}, init::Monotonics()) + } +} diff --git a/ui/unknown-interrupt.stderr b/ui/unknown-interrupt.stderr new file mode 100644 index 0000000000..d22310223f --- /dev/null +++ b/ui/unknown-interrupt.stderr @@ -0,0 +1,5 @@ +error[E0599]: no variant or associated item named `UnknownInterrupt` found for enum `Interrupt` in the current scope + --> examples/unknown-interrupt.rs:3:47 + | +3 | #[rtic::app(device = lm3s6965, dispatchers = [UnknownInterrupt])] + | ^^^^^^^^^^^^^^^^ variant or associated item not found in `Interrupt` \ No newline at end of file From 38bd29779eac57e6ad7961cd57702434a348e74c Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Tue, 31 Aug 2021 19:53:55 +0200 Subject: [PATCH 037/423] style fix --- macros/src/codegen/pre_init.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/macros/src/codegen/pre_init.rs b/macros/src/codegen/pre_init.rs index 626d17a0a6..69f16fe376 100644 --- a/macros/src/codegen/pre_init.rs +++ b/macros/src/codegen/pre_init.rs @@ -32,7 +32,7 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec Date: Tue, 31 Aug 2021 19:54:48 +0200 Subject: [PATCH 038/423] fix UI test --- ui/unknown-interrupt.stderr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/unknown-interrupt.stderr b/ui/unknown-interrupt.stderr index d22310223f..efda28797f 100644 --- a/ui/unknown-interrupt.stderr +++ b/ui/unknown-interrupt.stderr @@ -1,5 +1,5 @@ error[E0599]: no variant or associated item named `UnknownInterrupt` found for enum `Interrupt` in the current scope - --> examples/unknown-interrupt.rs:3:47 + --> $DIR/unknown-interrupt.rs:3:47 | 3 | #[rtic::app(device = lm3s6965, dispatchers = [UnknownInterrupt])] | ^^^^^^^^^^^^^^^^ variant or associated item not found in `Interrupt` \ No newline at end of file From da1fd6166e893482f99f16714a1c718b445c9579 Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Tue, 31 Aug 2021 20:01:10 +0200 Subject: [PATCH 039/423] fix UI test, take 2 --- ui/unknown-interrupt.stderr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/unknown-interrupt.stderr b/ui/unknown-interrupt.stderr index efda28797f..c7d32699dc 100644 --- a/ui/unknown-interrupt.stderr +++ b/ui/unknown-interrupt.stderr @@ -2,4 +2,4 @@ error[E0599]: no variant or associated item named `UnknownInterrupt` found for e --> $DIR/unknown-interrupt.rs:3:47 | 3 | #[rtic::app(device = lm3s6965, dispatchers = [UnknownInterrupt])] - | ^^^^^^^^^^^^^^^^ variant or associated item not found in `Interrupt` \ No newline at end of file + | ^^^^^^^^^^^^^^^^ variant or associated item not found in `Interrupt` From addb08607007aac915ac33984a1816a5aa353ced Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Tue, 14 Sep 2021 16:13:28 +0200 Subject: [PATCH 040/423] Cleanup export and actually use rtic::export, made fn init inline --- macros/src/codegen/init.rs | 1 + macros/src/codegen/module.rs | 2 +- src/export.rs | 7 ++++--- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/macros/src/codegen/init.rs b/macros/src/codegen/init.rs index b6d3f72ef0..2de3e73481 100644 --- a/macros/src/codegen/init.rs +++ b/macros/src/codegen/init.rs @@ -77,6 +77,7 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> CodegenResult { let user_init = quote!( #(#attrs)* + #[inline(always)] #[allow(non_snake_case)] fn #name(#context: #name::Context) -> (#user_init_return) { #(#stmts)* diff --git a/macros/src/codegen/module.rs b/macros/src/codegen/module.rs index 4db2c0c2f4..17bc34d363 100644 --- a/macros/src/codegen/module.rs +++ b/macros/src/codegen/module.rs @@ -275,7 +275,7 @@ pub fn codegen( ( quote!(core::mem::transmute::<_, cortex_m::peripheral::SYST>(()) .enable_interrupt()), - quote!(cortex_m::peripheral::SCB::set_pendst()), + quote!(rtic::export::SCB::set_pendst()), ) } else { let rt_err = util::rt_err_ident(); diff --git a/src/export.rs b/src/export.rs index 927e951e0a..8fdcb67ef8 100644 --- a/src/export.rs +++ b/src/export.rs @@ -5,12 +5,10 @@ use core::{ pub use crate::tq::{NotReady, TimerQueue}; pub use bare_metal::CriticalSection; -#[cfg(armv7m)] -pub use cortex_m::register::basepri; pub use cortex_m::{ asm::wfi, interrupt, - peripheral::{scb::SystemHandler, syst::SystClkSource, DWT, NVIC}, + peripheral::{scb::SystemHandler, DWT, NVIC, SCB}, Peripherals, }; pub use heapless::sorted_linked_list::SortedLinkedList; @@ -21,6 +19,9 @@ pub use rtic_monotonic as monotonic; pub type SCFQ = Queue; pub type SCRQ = Queue<(T, u8), N>; +#[cfg(armv7m)] +use cortex_m::register::basepri; + #[cfg(armv7m)] #[inline(always)] pub fn run(priority: u8, f: F) From d172df6f0a9e105fbb501dc2c044ab685a269246 Mon Sep 17 00:00:00 2001 From: Lotte Steenbrink Date: Thu, 26 Aug 2021 10:58:59 +0200 Subject: [PATCH 041/423] implement run-pass tests as xtasks ` --- .cargo/config | 3 + .github/workflows/build.yml | 135 +------------ Cargo.toml | 1 + README.md | 10 + xtask/Cargo.toml | 10 + xtask/src/build.rs | 37 ++++ xtask/src/command.rs | 162 +++++++++++++++ xtask/src/main.rs | 385 ++++++++++++++++++++++++++++++++++++ 8 files changed, 611 insertions(+), 132 deletions(-) create mode 100644 xtask/Cargo.toml create mode 100644 xtask/src/build.rs create mode 100644 xtask/src/command.rs create mode 100644 xtask/src/main.rs diff --git a/.cargo/config b/.cargo/config index d095766400..d70faef427 100644 --- a/.cargo/config +++ b/.cargo/config @@ -1,3 +1,6 @@ +[alias] +xtask = "run --package xtask --" + [target.thumbv6m-none-eabi] runner = "qemu-system-arm -cpu cortex-m3 -machine lm3s6965evb -nographic -semihosting-config enable=on,target=native -kernel" diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4d7ed950e6..fd8c073ac3 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -198,139 +198,10 @@ jobs: sudo apt update sudo apt install -y qemu-system-arm + - name: Run-pass tests - run: | - # Print the path - echo $PATH - - arm_example() { - local COMMAND=$1 - local EXAMPLE=$2 - local BUILD_MODE=$3 - local FEATURES=$4 - local BUILD_NUM=$5 - - if [ $BUILD_MODE = "release" ]; then - local RELEASE_FLAG="--release" - else - local RELEASE_FLAG="" - fi - - if [ -n "$FEATURES" ]; then - local FEATURES_FLAG="--features $FEATURES" - local FEATURES_STR=${FEATURES/,/_}_ - else - local FEATURES_FLAG="" - local FEATURES_STR="" - fi - local CARGO_FLAGS="--example $EXAMPLE --target ${{ matrix.target }} $RELEASE_FLAG $FEATURES_FLAG" - - if [ $COMMAND = "run" ]; then - cargo $COMMAND $CARGO_FLAGS | diff -u ci/expected/$EXAMPLE.run - - else - cargo $COMMAND $CARGO_FLAGS - fi - cargo objcopy $CARGO_FLAGS -- -O ihex ci/builds/${EXAMPLE}_${FEATURES_STR}${BUILD_MODE}_${BUILD_NUM}.hex - } - - mkdir -p ci/builds - exs=( - idle - init - hardware - preempt - binds - - resource - lock - multilock - only-shared-access - - task - message - capacity - - not-sync - - generics - pool - ramfunc - - peripherals-taken - ) - - for ex in ${exs[@]}; do - if [ $ex = pool ]; then - if [ ${{ matrix.target }} = thumbv6m-none-eabi ]; then - continue - fi - - td=$(mktemp -d) - - cargo run --example $ex --target ${{ matrix.target }} --features __v7 >\ - $td/pool.run - grep 'foo(0x2' $td/pool.run - grep 'bar(0x2' $td/pool.run - cargo objcopy --example $ex --target ${{ matrix.target }} --features __v7 -- -O ihex ci/builds/${ex}___v7_debug_1.hex - - cargo run --example $ex --target ${{ matrix.target }} --features __v7 --release >\ - $td/pool.run - grep 'foo(0x2' $td/pool.run - grep 'bar(0x2' $td/pool.run - cargo objcopy --example $ex --target ${{ matrix.target }} --features __v7 --release -- -O ihex ci/builds/${ex}___v7_release_1.hex - - rm -rf $td - - continue - fi - - if [ $ex = types ]; then - if [ ${{ matrix.target }} = thumbv6m-none-eabi ]; then - continue - fi - - arm_example "run" $ex "debug" "__v7" "1" - arm_example "run" $ex "release" "__v7" "1" - - continue - fi - - arm_example "run" $ex "debug" "" "1" - if [ $ex = types ]; then - arm_example "run" $ex "release" "" "1" - else - arm_example "build" $ex "release" "" "1" - fi - done - - built=() - cargo clean - for ex in ${exs[@]}; do - if [ $ex = types ] || [ $ex = pool ]; then - if [ ${{ matrix.target }} = thumbv6m-none-eabi ]; then - continue - fi - - arm_example "build" $ex "debug" "__v7" "2" - cmp ci/builds/${ex}___v7_debug_1.hex \ - ci/builds/${ex}___v7_debug_2.hex - arm_example "build" $ex "release" "__v7" "2" - cmp ci/builds/${ex}___v7_release_1.hex \ - ci/builds/${ex}___v7_release_2.hex - else - arm_example "build" $ex "debug" "" "2" - cmp ci/builds/${ex}_debug_1.hex \ - ci/builds/${ex}_debug_2.hex - arm_example "build" $ex "release" "" "2" - cmp ci/builds/${ex}_release_1.hex \ - ci/builds/${ex}_release_2.hex - fi - - built+=( $ex ) - done - - ( cd target/${{ matrix.target }}/release/examples/ && size ${built[@]} ) - + run: + cargo xtask --target ${{ matrix.target }} # Check the correctness of macros/ crate checkmacros: diff --git a/Cargo.toml b/Cargo.toml index ed0312df1e..5506a58912 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -76,6 +76,7 @@ lto = true [workspace] members = [ "macros", + "xtask", ] # do not optimize proc-macro deps or build scripts diff --git a/README.md b/README.md index b9bfb393be..e5baea733a 100644 --- a/README.md +++ b/README.md @@ -86,6 +86,16 @@ New features and big changes should go through the RFC process in the [rfcs]: https://github.com/rtic-rs/rfcs +## Running tests locally + +To check all `Run-pass tests` locally on your `thumbv6m-none-eabi` or `thumbv7m-none-eabi` target device, run + +```console +$ cargo xtask --target +# ˆˆˆˆˆˆˆˆˆˆˆˆ +# e.g. thumbv7m-none-eabi +``` + ## Acknowledgments This crate is based on the [Real-Time For the Masses language][rtfm-lang] diff --git a/xtask/Cargo.toml b/xtask/Cargo.toml new file mode 100644 index 0000000000..fa7fd17940 --- /dev/null +++ b/xtask/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "xtask" +version = "0.1.0" +edition = "2018" + +[dependencies] +anyhow = "1.0.43" +os_pipe = "0.9.2" +structopt = "0.3.22" +tempdir = "0.3.7" \ No newline at end of file diff --git a/xtask/src/build.rs b/xtask/src/build.rs new file mode 100644 index 0000000000..11666ad4fa --- /dev/null +++ b/xtask/src/build.rs @@ -0,0 +1,37 @@ +use std::path::PathBuf; + +use crate::{command::BuildMode, TestRunError}; + +pub fn build_hexpath( + example: &str, + features: Option<&str>, + build_mode: BuildMode, + build_num: u32, +) -> anyhow::Result { + let features = match features { + Some(f) => f, + None => "", + }; + + let filename = format!("{}_{}_{}_{}.hex", example, features, build_mode, build_num); + ["ci", "builds", &filename] + .iter() + .collect::() + .into_os_string() + .into_string() + .map_err(|e| anyhow::Error::new(TestRunError::PathConversionError(e))) +} + +pub fn compare_builds(file_1: String, file_2: String) -> anyhow::Result<()> { + let buf_1 = std::fs::read_to_string(file_1.clone())?; + let buf_2 = std::fs::read_to_string(file_2.clone())?; + + if buf_1 != buf_2 { + return Err(anyhow::Error::new(TestRunError::FileCmpError { + file_1, + file_2, + })); + } + + Ok(()) +} diff --git a/xtask/src/command.rs b/xtask/src/command.rs new file mode 100644 index 0000000000..8bf49849de --- /dev/null +++ b/xtask/src/command.rs @@ -0,0 +1,162 @@ +use crate::RunResult; +use core::fmt; +use os_pipe::pipe; +use std::{fs::File, io::Read, path::Path, process::Command}; + +#[derive(Debug, Clone, Copy, PartialEq)] +pub enum BuildMode { + Release, + Debug, +} + +pub enum CargoCommand<'a> { + Run { + example: &'a str, + target: &'a str, + features: Option<&'a str>, + mode: BuildMode, + }, + Build { + example: &'a str, + target: &'a str, + features: Option<&'a str>, + mode: BuildMode, + }, + Objcopy { + example: &'a str, + target: &'a str, + features: Option<&'a str>, + ihex: &'a str, + }, + Size { + example_paths: Vec<&'a Path>, + }, + Clean, +} + +impl<'a> CargoCommand<'a> { + fn name(&self) -> &str { + match self { + CargoCommand::Run { .. } => "run", + CargoCommand::Size { example_paths: _ } => "rust-size", + CargoCommand::Clean => "clean", + CargoCommand::Build { .. } => "build", + CargoCommand::Objcopy { .. } => "objcopy", + } + } + + pub fn args(&self) -> Vec<&str> { + match self { + CargoCommand::Run { + example, + target, + features, + mode, + } + | CargoCommand::Build { + example, + target, + features, + mode, + } => { + let mut args = vec![self.name(), "--example", example, "--target", target]; + + if let Some(feature_name) = features { + args.extend_from_slice(&["--features", feature_name]); + } + if let Some(flag) = mode.to_flag() { + args.push(flag); + } + args + } + CargoCommand::Size { example_paths } => { + example_paths.iter().map(|p| p.to_str().unwrap()).collect() + } + CargoCommand::Clean => vec!["clean"], + CargoCommand::Objcopy { + example, + target, + features, + ihex, + } => { + let mut args = vec![self.name(), "--example", example, "--target", target]; + + if let Some(feature_name) = features { + args.extend_from_slice(&["--features", feature_name]); + } + + // this always needs to go at the end + args.extend_from_slice(&["--", "-O", "ihex", ihex]); + args + } + } + } + + pub fn command(&self) -> &str { + match self { + // we need to cheat a little here: + // `cargo size` can't be ran on multiple files, so we're using `rust-size` instead – + // which isn't a command that starts wizh `cargo`. So we're sneakily swapping them out :) + CargoCommand::Size { .. } => "rust-size", + _ => "cargo", + } + } +} + +impl BuildMode { + pub fn to_flag(&self) -> Option<&str> { + match self { + BuildMode::Release => Some("--release"), + BuildMode::Debug => None, + } + } +} + +impl fmt::Display for BuildMode { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let cmd = match self { + BuildMode::Release => "release", + BuildMode::Debug => "debug", + }; + + write!(f, "{}", cmd) + } +} + +pub fn run_command(command: &CargoCommand) -> anyhow::Result { + let (mut reader, writer) = pipe()?; + println!("👟 {} {}", command.command(), command.args().join(" ")); + + let mut handle = Command::new(command.command()) + .args(command.args()) + .stdout(writer) + .spawn()?; + + // retrieve output and clean up + let mut output = String::new(); + reader.read_to_string(&mut output)?; + let exit_status = handle.wait()?; + + Ok(RunResult { + exit_status, + output, + }) +} + +/// Check if `run` was sucessful. +/// returns Ok in case the run went as expected, +/// Err otherwise +pub fn run_successful(run: &RunResult, expected_output_file: String) -> anyhow::Result<()> { + let mut file_handle = File::open(expected_output_file)?; + let mut expected_output = String::new(); + file_handle.read_to_string(&mut expected_output)?; + if expected_output == run.output && run.exit_status.success() { + Ok(()) + } else { + Err(anyhow::anyhow!( + "Run failed with exit status {}: {}", + run.exit_status, + run.output + )) + } +} diff --git a/xtask/src/main.rs b/xtask/src/main.rs new file mode 100644 index 0000000000..c1d4905fa8 --- /dev/null +++ b/xtask/src/main.rs @@ -0,0 +1,385 @@ +mod build; +mod command; + +use anyhow::bail; +use core::fmt; +use std::{ + env, + error::Error, + ffi::OsString, + path::{Path, PathBuf}, + process, + process::ExitStatus, + str, +}; +use structopt::StructOpt; + +use crate::{ + build::{build_hexpath, compare_builds}, + command::{run_command, run_successful, BuildMode, CargoCommand}, +}; + +const ARMV6M: &str = "thumbv6m-none-eabi"; +const ARMV7M: &str = "thumbv7m-none-eabi"; + +#[derive(Debug, StructOpt)] +struct Options { + #[structopt(short, long)] + target: String, +} + +#[derive(Debug)] +pub struct RunResult { + exit_status: ExitStatus, + output: String, +} + +#[derive(Debug)] +enum TestRunError { + FileCmpError { file_1: String, file_2: String }, + PathConversionError(OsString), + CommandError(RunResult), + IncompatibleCommand, +} + +impl fmt::Display for TestRunError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + TestRunError::FileCmpError { file_1, file_2 } => { + write!(f, "Differing output in Files: {} {}", file_1, file_2) + } + TestRunError::CommandError(e) => { + write!( + f, + "Command failed with exit status {}: {}", + e.exit_status, e.output + ) + } + TestRunError::PathConversionError(p) => { + write!(f, "Can't convert path from `OsString` to `String`: {:?}", p) + } + TestRunError::IncompatibleCommand => { + write!(f, "Can't run that command in this context") + } + } + } +} + +impl Error for TestRunError {} + +fn main() -> anyhow::Result<()> { + let execution_dir = env::current_dir()?; + if execution_dir.file_name().unwrap() != "cortex-m-rtic" { + bail!("xtasks can only be executed from the root of the `cortex-m-rtic` repository"); + } + + let targets = [ARMV7M, ARMV6M]; + let examples = &[ + "idle", + "init", + "hardware", + "preempt", + "binds", + "resource", + "lock", + "multilock", + "only-shared-access", + "task", + "message", + "capacity", + "not-sync", + "generics", + "pool", + "ramfunc", + "peripherals-taken", + ]; + + let opts = Options::from_args(); + let target = &opts.target; + + if target == "all" { + for t in targets { + run_test(t, examples)?; + build_test(t, examples)?; + } + } else if targets.contains(&target.as_str()) { + run_test(&target, examples)?; + build_test(&target, examples)?; + } else { + eprintln!( + "The target you specified is not available. Available targets are:\ + \n{:?}\n\ + as well as `all` (testing on all of the above)", + targets + ); + process::exit(1); + } + + Ok(()) +} + +fn run_test(target: &str, examples: &[&str]) -> anyhow::Result<()> { + for example in examples { + match *example { + "pool" => { + if target != ARMV6M { + // check this one manually because addresses printed in `pool.run` may vary + let features_v7 = Some("__v7"); + + let debug_run_result = run_command(&CargoCommand::Run { + example, + target, + features: features_v7, + mode: BuildMode::Debug, + })?; + + if debug_run_result.exit_status.success() { + print_from_output("foo(0x2", &debug_run_result.output); + print_from_output("bar(0x2", &debug_run_result.output); + } + + let hexpath = &build_hexpath(*example, features_v7, BuildMode::Debug, 1)?; + + run_command(&CargoCommand::Objcopy { + example, + target, + features: features_v7, + ihex: hexpath, + })?; + + let release_run_result = run_command(&CargoCommand::Run { + example, + target, + features: features_v7, + mode: BuildMode::Release, + })?; + + if release_run_result.exit_status.success() { + print_from_output("foo(0x2", &release_run_result.output); + print_from_output("bar(0x2", &release_run_result.output); + } + + let hexpath = &build_hexpath(*example, features_v7, BuildMode::Release, 1)?; + run_command(&CargoCommand::Objcopy { + example, + target, + features: features_v7, + ihex: hexpath, + })?; + } + } + "types" => { + let features_v7 = Some("__v7"); + + // TODO this example doesn't exist anymore, can we remove this case? + if target != ARMV6M { + arm_example( + &CargoCommand::Run { + example, + target, + features: features_v7, + mode: BuildMode::Debug, + }, + 1, + )?; + arm_example( + &CargoCommand::Run { + example, + target, + features: features_v7, + mode: BuildMode::Release, + }, + 1, + )?; + } + } + _ => { + arm_example( + &CargoCommand::Run { + example, + target, + features: None, + mode: BuildMode::Debug, + }, + 1, + )?; + + if *example == "types" { + arm_example( + &CargoCommand::Run { + example, + target, + features: None, + mode: BuildMode::Release, + }, + 1, + )?; + } else { + arm_example( + &CargoCommand::Build { + example, + target, + features: None, + mode: BuildMode::Release, + }, + 1, + )?; + } + } + } + } + + Ok(()) +} + +// run example binary `example` +fn arm_example(command: &CargoCommand, build_num: u32) -> anyhow::Result<()> { + match *command { + CargoCommand::Run { + example, + target, + features, + mode, + } + | CargoCommand::Build { + example, + target, + features, + mode, + } => { + let run_file = format!("{}.run", example); + let expected_output_file = ["ci", "expected", &run_file] + .iter() + .collect::() + .into_os_string() + .into_string() + .map_err(|e| TestRunError::PathConversionError(e))?; + + // command is either build or run + let cargo_run_result = run_command(&command)?; + println!("{}", cargo_run_result.output); + + match &command { + CargoCommand::Run { .. } => { + if run_successful(&cargo_run_result, expected_output_file).is_err() { + return Err(anyhow::Error::new(TestRunError::CommandError( + cargo_run_result, + ))); + } + } + _ => (), + } + + // now, prepare to objcopy + let hexpath = build_hexpath(example, features, mode, build_num)?; + + run_command(&CargoCommand::Objcopy { + example, + target, + features, + ihex: &hexpath, + })?; + + Ok(()) + } + _ => Err(anyhow::Error::new(TestRunError::IncompatibleCommand)), + } +} + +fn build_test(target: &str, examples: &[&str]) -> anyhow::Result<()> { + run_command(&CargoCommand::Clean)?; + + let mut built = vec![]; + let build_path: PathBuf = ["target", target, "debug", "examples"].iter().collect(); + + for example in examples { + match *example { + "pool" | "types" => { + if target != ARMV6M { + let features_v7 = Some("__v7"); + + arm_example( + &CargoCommand::Build { + target, + example, + mode: BuildMode::Debug, + features: features_v7, + }, + 2, + )?; + let file_1 = build_hexpath(example, features_v7, BuildMode::Debug, 1)?; + let file_2 = build_hexpath(example, features_v7, BuildMode::Debug, 2)?; + + compare_builds(file_1, file_2)?; + + arm_example( + &CargoCommand::Build { + target, + example, + mode: BuildMode::Release, + features: features_v7, + }, + 2, + )?; + let file_1 = build_hexpath(example, features_v7, BuildMode::Release, 1)?; + let file_2 = build_hexpath(example, features_v7, BuildMode::Release, 2)?; + + compare_builds(file_1, file_2)?; + + built.push(build_path.join(example)); + } + } + _ => { + let no_features = None; + arm_example( + &CargoCommand::Build { + target, + example, + mode: BuildMode::Debug, + features: no_features, + }, + 2, + )?; + let file_1 = build_hexpath(example, no_features, BuildMode::Debug, 1)?; + let file_2 = build_hexpath(example, no_features, BuildMode::Debug, 2)?; + + compare_builds(file_1, file_2)?; + + arm_example( + &CargoCommand::Build { + target, + example, + mode: BuildMode::Release, + features: no_features, + }, + 2, + )?; + let file_1 = build_hexpath(example, no_features, BuildMode::Release, 1)?; + let file_2 = build_hexpath(example, no_features, BuildMode::Release, 2)?; + + compare_builds(file_1, file_2)?; + + built.push(build_path.join(example)); + } + } + } + + let example_paths: Vec<&Path> = built.iter().map(|p| p.as_path()).collect(); + let size_run_result = run_command(&CargoCommand::Size { example_paths })?; + + if size_run_result.exit_status.success() { + println!("{}", size_run_result.output); + } + + Ok(()) +} + +/// Check if lines in `output` contain `pattern` and print matching lines +fn print_from_output(pattern: &str, lines: &str) { + let lines = lines.split("\n"); + for line in lines { + if line.contains(pattern) { + println!("{}", line); + } + } +} From 7ce4391e4e6bce03d42962266fe0587922e13571 Mon Sep 17 00:00:00 2001 From: Lotte Steenbrink Date: Fri, 17 Sep 2021 15:44:33 +0200 Subject: [PATCH 042/423] improve xtask repo root check to not break our fork CI# --- xtask/src/main.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/xtask/src/main.rs b/xtask/src/main.rs index c1d4905fa8..4d582ddd26 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs @@ -4,7 +4,6 @@ mod command; use anyhow::bail; use core::fmt; use std::{ - env, error::Error, ffi::OsString, path::{Path, PathBuf}, @@ -68,8 +67,10 @@ impl fmt::Display for TestRunError { impl Error for TestRunError {} fn main() -> anyhow::Result<()> { - let execution_dir = env::current_dir()?; - if execution_dir.file_name().unwrap() != "cortex-m-rtic" { + // if there's an `xtask` folder, we're *probably* at the root of this repo (we can't just + // check the name of `env::current_dir()` because people might clone it into a different name) + let probably_running_from_repo_root = Path::new("./xtask").exists(); + if probably_running_from_repo_root == false { bail!("xtasks can only be executed from the root of the `cortex-m-rtic` repository"); } From 7f45254e3939af5aa940c65e52c63fa83b93c16d Mon Sep 17 00:00:00 2001 From: Lotte Steenbrink Date: Mon, 20 Sep 2021 17:35:15 +0200 Subject: [PATCH 043/423] start with a clean ci/builds always --- xtask/src/build.rs | 26 +++++++++++++++++++++----- xtask/src/main.rs | 4 +++- 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/xtask/src/build.rs b/xtask/src/build.rs index 11666ad4fa..a8c19aac73 100644 --- a/xtask/src/build.rs +++ b/xtask/src/build.rs @@ -1,7 +1,22 @@ -use std::path::PathBuf; +use std::{ + fs, + path::{Path, PathBuf}, +}; use crate::{command::BuildMode, TestRunError}; +const HEX_BUILD_ROOT: &str = "ci/builds"; + +/// make sure we're starting with a clean,but existing slate +pub fn init_build_dir() -> anyhow::Result<()> { + if Path::new(HEX_BUILD_ROOT).exists() { + fs::remove_dir_all(HEX_BUILD_ROOT) + .map_err(|_| anyhow::anyhow!("Could not clear out directory: {}", HEX_BUILD_ROOT))?; + } + fs::create_dir_all(HEX_BUILD_ROOT) + .map_err(|_| anyhow::anyhow!("Could not create directory: {}", HEX_BUILD_ROOT)) +} + pub fn build_hexpath( example: &str, features: Option<&str>, @@ -14,10 +29,11 @@ pub fn build_hexpath( }; let filename = format!("{}_{}_{}_{}.hex", example, features, build_mode, build_num); - ["ci", "builds", &filename] - .iter() - .collect::() - .into_os_string() + + let mut path = PathBuf::from(HEX_BUILD_ROOT); + path.push(filename); + + path.into_os_string() .into_string() .map_err(|e| anyhow::Error::new(TestRunError::PathConversionError(e))) } diff --git a/xtask/src/main.rs b/xtask/src/main.rs index 4d582ddd26..3243b98e20 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs @@ -14,7 +14,7 @@ use std::{ use structopt::StructOpt; use crate::{ - build::{build_hexpath, compare_builds}, + build::{build_hexpath, compare_builds, init_build_dir}, command::{run_command, run_successful, BuildMode, CargoCommand}, }; @@ -98,6 +98,8 @@ fn main() -> anyhow::Result<()> { let opts = Options::from_args(); let target = &opts.target; + init_build_dir()?; + if target == "all" { for t in targets { run_test(t, examples)?; From b71df58f2fb4ed85d4c8cf806d5837ce63c73f31 Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Wed, 22 Sep 2021 13:22:45 +0200 Subject: [PATCH 044/423] The great docs update --- .github/workflows/build.yml | 67 +--- Cargo.toml | 31 +- book/en/src/SUMMARY.md | 26 +- book/en/src/awesome_rtic.md | 1 + book/en/src/by-example.md | 9 +- book/en/src/by-example/app.md | 152 +-------- book/en/src/by-example/app_idle.md | 27 ++ book/en/src/by-example/app_init.md | 28 ++ book/en/src/by-example/app_minimal.md | 7 + book/en/src/by-example/app_priorities.md | 45 +++ book/en/src/by-example/app_task.md | 7 + book/en/src/by-example/hardware_tasks.md | 24 ++ book/en/src/by-example/message_passing.md | 14 + book/en/src/by-example/monotonic.md | 55 +++ book/en/src/by-example/new.md | 84 ----- book/en/src/by-example/resources.md | 137 +++++--- book/en/src/by-example/software_tasks.md | 16 + book/en/src/by-example/starting_a_project.md | 14 + book/en/src/by-example/tasks.md | 118 ------- book/en/src/by-example/timer-queue.md | 113 ------- book/en/src/by-example/tips.md | 175 +--------- book/en/src/by-example/tips_destructureing.md | 13 + book/en/src/by-example/tips_from_ram.md | 45 +++ book/en/src/by-example/tips_indirection.md | 26 ++ book/en/src/by-example/tips_monotonic_impl.md | 59 ++++ .../src/by-example/tips_static_lifetimes.md | 24 ++ book/en/src/by-example/tips_view_code.md | 48 +++ book/en/src/by-example/types-send-sync.md | 51 --- book/en/src/preface.md | 4 +- ci/expected/big-struct-opt.run | 0 ci/expected/cancel-reschedule.run | 3 + ci/expected/cfg-whole-task.run | 0 ci/expected/common.run | 0 ci/expected/declared_locals.run | 0 ci/expected/destructure.run | 2 + ci/expected/extern_binds.run | 4 + ci/expected/extern_spawn.run | 2 + ci/expected/locals.run | 3 + ci/expected/lock-free.run | 16 +- ci/expected/message_passing.run | 3 + ci/expected/multilock.run | 5 +- ci/expected/only-shared-access.run | 4 +- ci/expected/periodic.run | 7 +- ci/expected/pool.run | 4 +- ci/expected/preempt.run | 10 +- ci/expected/resource-user-struct.run | 2 + ci/expected/schedule.run | 7 +- ci/expected/shared.run | 1 + ci/expected/smallest.run | 0 ci/expected/spawn.run | 2 + ci/expected/static.run | 3 + ci/expected/t-binds.run | 0 ci/expected/t-cfg-resources.run | 0 ci/expected/t-htask-main.run | 0 ci/expected/t-idle-main.run | 0 ci/expected/t-late-not-send.run | 0 ci/expected/t-schedule.run | 0 ci/expected/t-spawn.run | 0 examples/big-struct-opt.rs | 3 + examples/binds.rs | 2 +- examples/cancel-reschedule.rs | 74 ++++ examples/capacity.rs | 2 +- examples/cfg-whole-task.rs | 2 +- examples/common.rs | 101 ++++++ examples/declared_locals.rs | 46 +++ examples/destructure.rs | 30 +- examples/double_schedule.rs | 46 --- examples/extern_binds.rs | 2 +- examples/extern_spawn.rs | 2 +- examples/generics.rs | 2 +- examples/hardware.rs | 2 +- examples/idle.rs | 2 +- examples/init.rs | 2 +- examples/locals.rs | 86 +++++ examples/lock-free.rs | 33 +- examples/lock.rs | 29 +- examples/message.rs | 2 +- examples/{spawn2.rs => message_passing.rs} | 16 +- examples/multilock.rs | 46 +-- examples/not-sync.rs | 2 +- examples/only-shared-access.rs | 23 +- examples/periodic.rs | 28 +- examples/peripherals-taken.rs | 2 +- examples/pool.rs | 2 +- examples/preempt.rs | 33 +- examples/ramfunc.rs | 2 +- examples/resource-user-struct.rs | 2 +- examples/resource.rs | 81 ----- examples/schedule.rs | 39 ++- examples/shared.rs | 2 +- examples/smallest.rs | 3 + examples/spawn.rs | 15 +- examples/static.rs | 46 +-- examples/t-binds.rs | 4 + examples/t-cfg-resources.rs | 4 + examples/t-htask-main.rs | 2 +- examples/t-idle-main.rs | 2 +- examples/t-late-not-send.rs | 2 + examples/t-schedule.rs | 21 +- examples/t-spawn.rs | 4 + examples/task.rs | 2 +- macros/src/codegen/module.rs | 7 + xtask/Cargo.toml | 1 - xtask/src/build.rs | 10 +- xtask/src/command.rs | 33 +- xtask/src/main.rs | 315 ++++++------------ 106 files changed, 1286 insertions(+), 1429 deletions(-) create mode 100644 book/en/src/awesome_rtic.md create mode 100644 book/en/src/by-example/app_idle.md create mode 100644 book/en/src/by-example/app_init.md create mode 100644 book/en/src/by-example/app_minimal.md create mode 100644 book/en/src/by-example/app_priorities.md create mode 100644 book/en/src/by-example/app_task.md create mode 100644 book/en/src/by-example/hardware_tasks.md create mode 100644 book/en/src/by-example/message_passing.md create mode 100644 book/en/src/by-example/monotonic.md delete mode 100644 book/en/src/by-example/new.md create mode 100644 book/en/src/by-example/software_tasks.md create mode 100644 book/en/src/by-example/starting_a_project.md delete mode 100644 book/en/src/by-example/tasks.md delete mode 100644 book/en/src/by-example/timer-queue.md create mode 100644 book/en/src/by-example/tips_destructureing.md create mode 100644 book/en/src/by-example/tips_from_ram.md create mode 100644 book/en/src/by-example/tips_indirection.md create mode 100644 book/en/src/by-example/tips_monotonic_impl.md create mode 100644 book/en/src/by-example/tips_static_lifetimes.md create mode 100644 book/en/src/by-example/tips_view_code.md delete mode 100644 book/en/src/by-example/types-send-sync.md create mode 100644 ci/expected/big-struct-opt.run create mode 100644 ci/expected/cancel-reschedule.run create mode 100644 ci/expected/cfg-whole-task.run create mode 100644 ci/expected/common.run create mode 100644 ci/expected/declared_locals.run create mode 100644 ci/expected/destructure.run create mode 100644 ci/expected/extern_binds.run create mode 100644 ci/expected/extern_spawn.run create mode 100644 ci/expected/locals.run create mode 100644 ci/expected/message_passing.run create mode 100644 ci/expected/resource-user-struct.run create mode 100644 ci/expected/shared.run create mode 100644 ci/expected/smallest.run create mode 100644 ci/expected/spawn.run create mode 100644 ci/expected/static.run create mode 100644 ci/expected/t-binds.run create mode 100644 ci/expected/t-cfg-resources.run create mode 100644 ci/expected/t-htask-main.run create mode 100644 ci/expected/t-idle-main.run create mode 100644 ci/expected/t-late-not-send.run create mode 100644 ci/expected/t-schedule.run create mode 100644 ci/expected/t-spawn.run create mode 100644 examples/cancel-reschedule.rs create mode 100644 examples/common.rs create mode 100644 examples/declared_locals.rs delete mode 100644 examples/double_schedule.rs create mode 100644 examples/locals.rs rename examples/{spawn2.rs => message_passing.rs} (67%) delete mode 100644 examples/resource.rs diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index fd8c073ac3..438bedf4f7 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -132,13 +132,11 @@ jobs: - name: Check the examples if: matrix.target == 'thumbv7m-none-eabi' - env: - V7: __v7 uses: actions-rs/cargo@v1 with: use-cross: false command: check - args: --examples --target=${{ matrix.target }} --features ${{ env.V7 }} + args: --examples --target=${{ matrix.target }} # Verify the example output with run-pass tests testexamples: @@ -304,9 +302,15 @@ jobs: args: --manifest-path macros/Cargo.toml --target=${{ matrix.target }} # Run test suite for thumbv7m - testv7: - name: testv7 + tests: + name: tests runs-on: ubuntu-20.04 + strategy: + matrix: + target: + - x86_64-unknown-linux-gnu + toolchain: + - stable steps: - name: Checkout uses: actions/checkout@v2 @@ -334,56 +338,15 @@ jobs: - name: Install Rust uses: actions-rs/toolchain@v1 with: - toolchain: stable - target: thumbv7m-none-eabi + toolchain: ${{ matrix.toolchain }} + target: ${{ matrix.target }} override: true - uses: actions-rs/cargo@v1 with: use-cross: false command: test - args: --test tests --features __v7 - - # Run test suite for thumbv6m - testv6: - name: testv6 - runs-on: ubuntu-20.04 - steps: - - name: Checkout - uses: actions/checkout@v2 - - - name: Cache cargo dependencies - uses: actions/cache@v2 - with: - path: | - - ~/.cargo/bin/ - - ~/.cargo/registry/index/ - - ~/.cargo/registry/cache/ - - ~/.cargo/git/db/ - key: ${{ runner.OS }}-cargo-${{ hashFiles('**/Cargo.lock') }} - restore-keys: | - ${{ runner.OS }}-cargo- - - - name: Cache build output dependencies - uses: actions/cache@v2 - with: - path: target - key: ${{ runner.OS }}-build-${{ hashFiles('**/Cargo.lock') }} - restore-keys: | - ${{ runner.OS }}-build- - - - name: Install Rust - uses: actions-rs/toolchain@v1 - with: - toolchain: stable - target: thumbv6m-none-eabi - override: true - - - uses: actions-rs/cargo@v1 - with: - use-cross: false - command: test - args: --test tests + args: --test tests --target=${{ matrix.target }} # Build documentation, check links docs: @@ -506,8 +469,7 @@ jobs: - testexamples - checkmacros - testmacros - - testv7 - - testv6 + - tests - docs - mdbook # Only run this when pushing to master branch @@ -624,8 +586,7 @@ jobs: - testexamples - checkmacros - testmacros - - testv7 - - testv6 + - tests - docs - mdbook runs-on: ubuntu-20.04 diff --git a/Cargo.toml b/Cargo.toml index 5506a58912..db55060c96 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,44 +19,21 @@ version = "0.6.0-alpha.5" [lib] name = "rtic" -[[example]] -name = "periodic" -required-features = ["__v7"] - -[[example]] -name = "pool" -required-features = ["__v7"] - -[[example]] -name = "schedule" -required-features = ["__v7"] - -[[example]] -name = "t-schedule" -required-features = ["__v7"] - -[[example]] -name = "double_schedule" -required-features = ["__v7"] - [dependencies] cortex-m = "0.7.0" cortex-m-rtic-macros = { path = "macros", version = "0.6.0-alpha.5" } rtic-monotonic = "0.1.0-alpha.2" rtic-core = "0.3.1" -heapless = "0.7.5" +heapless = "0.7.7" bare-metal = "1.0.0" -[dependencies.dwt-systick-monotonic] -version = "0.1.0-alpha.3" -optional = true - [build-dependencies] version_check = "0.9" [dev-dependencies] lm3s6965 = "0.1.3" cortex-m-semihosting = "0.3.3" +systick-monotonic = "0.1.0-alpha.0" [dev-dependencies.panic-semihosting] features = ["exit"] @@ -65,10 +42,6 @@ version = "0.5.2" [target.x86_64-unknown-linux-gnu.dev-dependencies] trybuild = "1" -[features] -# used for testing this crate; do not use in applications -__v7 = ["dwt-systick-monotonic"] - [profile.release] codegen-units = 1 lto = true diff --git a/book/en/src/SUMMARY.md b/book/en/src/SUMMARY.md index 68d8e2b03f..22a10ac873 100644 --- a/book/en/src/SUMMARY.md +++ b/book/en/src/SUMMARY.md @@ -3,13 +3,27 @@ [Preface](./preface.md) - [RTIC by example](./by-example.md) - - [The `app` attribute](./by-example/app.md) + - [The `app`](./by-example/app.md) + - [App initialization](./by-example/app_init.md) - [Resources](./by-example/resources.md) - - [Software tasks](./by-example/tasks.md) - - [Timer queue](./by-example/timer-queue.md) - - [Types, Send and Sync](./by-example/types-send-sync.md) - - [Starting a new project](./by-example/new.md) - - [Tips & tricks](./by-example/tips.md) + - [The background task](./by-example/app_idle.md) + - [Defining tasks](./by-example/app_task.md) + - [Software tasks & `spawn`](./by-example/software_tasks.md) + - [Message passing & `capacity`](./by-example/message_passing.md) + - [Hardware tasks](./by-example/hardware_tasks.md) + - [Task priorities](./by-example/app_priorities.md) + - [Monotonic & `spawn_{at/after}`](./by-example/monotonic.md) + - [Starting a new project](./by-example/starting_a_project.md) + - [The minimal app](./by-example/app_minimal.md) + - [Tips & Tricks](./by-example/tips.md) + - [Implementing Monotonic](./by-example/tips_monotonic_impl.md) + - [Resource de-structure-ing](./by-example/tips_destructureing.md) + - [Using indirection](./by-example/tips_indirection.md) + - [`'static` super-powers](./by-example/tips_static_lifetimes.md) + - [Inspecting generated code](./by-example/tips_view_code.md) + - [Running tasks from RAM](./by-example/tips_from_ram.md) + +- [Awesome RTIC examples](./awesome_rtic.md) - [Migration Guides](./migration.md) - [v0.5.x to v0.6.x](./migration/migration_v5.md) - [v0.4.x to v0.5.x](./migration/migration_v4.md) diff --git a/book/en/src/awesome_rtic.md b/book/en/src/awesome_rtic.md new file mode 100644 index 0000000000..925cd3fd96 --- /dev/null +++ b/book/en/src/awesome_rtic.md @@ -0,0 +1 @@ +# Awesome RTIC examples diff --git a/book/en/src/by-example.md b/book/en/src/by-example.md index e4441fd962..fef6872e49 100644 --- a/book/en/src/by-example.md +++ b/book/en/src/by-example.md @@ -4,7 +4,7 @@ This part of the book introduces the Real-Time Interrupt-driven Concurrency (RTI to new users by walking them through examples of increasing complexity. All examples in this part of the book can be found in the GitHub [repository] of -the project, and most of the examples can be run on QEMU so no special hardware +the project. The examples can be run on QEMU (emulating a Cortex M3 target) so no special hardware is required to follow along. [repository]: https://github.com/rtic-rs/cortex-m-rtic @@ -15,10 +15,3 @@ embedded development environment that includes QEMU. [the embedded Rust book]: https://rust-embedded.github.io/book/intro/install.html -## Real World Examples - -The following are examples of RTFM being used in real world projects. - -### RTFM V0.4.2 - -- [etrombly/sandbox](https://github.com/etrombly/sandbox/tree/41d423bcdd0d8e42fd46b79771400a8ca349af55). A hardware zen garden that draws patterns in sand. Patterns are sent over serial using G-code. diff --git a/book/en/src/by-example/app.md b/book/en/src/by-example/app.md index 04535c140e..09f3371e26 100644 --- a/book/en/src/by-example/app.md +++ b/book/en/src/by-example/app.md @@ -1,17 +1,13 @@ -# The `app` attribute +# The `#[app]` attribute and an RTIC application -This is the smallest possible RTIC application: - -``` rust -{{#include ../../../../examples/smallest.rs}} -``` +## Requirements on the `app` attribute All RTIC applications use the [`app`] attribute (`#[app(..)]`). This attribute -must be applied to a `mod`-item. The `app` attribute has a mandatory `device` +must be applied to a `mod`-item containing the RTIC application. The `app` +attribute has a mandatory `device` argument that takes a *path* as a value. This must be a full path pointing to a *peripheral access crate* (PAC) generated using [`svd2rust`] **v0.14.x** or -newer. More details can be found in the [Starting a new project](./new.md) -section. +newer. The `app` attribute will expand into a suitable entry point so it's not required to use the [`cortex_m_rt::entry`] attribute. @@ -20,143 +16,11 @@ to use the [`cortex_m_rt::entry`] attribute. [`svd2rust`]: https://crates.io/crates/svd2rust [`cortex_m_rt::entry`]: ../../../api/cortex_m_rt_macros/attr.entry.html -## `init` +## An RTIC application example -Within the `app` module the attribute expects to find an initialization -function marked with the `init` attribute. This function must have -signature `fn(init::Context) -> (init::LateResources, init::Monotonics)`. - -This initialization function will be the first part of the application to run. -The `init` function will run *with interrupts disabled* and has exclusive access -to Cortex-M where the `bare_metal::CriticalSection` token is available as `cs`. -And optionally, device specific peripherals through the `core` and `device` fields -of `init::Context`. - -`static mut` variables declared at the beginning of `init` will be transformed -into `&'static mut` references that are safe to access. Notice, this feature may be deprecated in next release, see `task_local` resources. - -[`rtic::Peripherals`]: ../../api/rtic/struct.Peripherals.html - -The example below shows the types of the `core`, `device` and `cs` fields, and -showcases safe access to a `static mut` variable. The `device` field is only -available when the `peripherals` argument is set to `true` (default). In the rare case you want to implement an ultra-slim application you can explicitly set `peripherals` to `false`. +To give a flavor of RTIC, the following example contains commonly used features. In the following sections we will go through each feature in detail. ``` rust -{{#include ../../../../examples/init.rs}} +{{#include ../../../../examples/common.rs}} ``` -Running the example will print `init` to the console and then exit the QEMU -process. - -``` console -$ cargo run --example init -{{#include ../../../../ci/expected/init.run}} -``` - -> **NOTE**: Remember to specify your chosen target device by passing a target -> triple to cargo (e.g `cargo run --example init --target thumbv7m-none-eabi`) or -> configure a device to be used by default when building the examples in `.cargo/config.toml`. -> In this case, we use a Cortex M3 emulated in QEMU so the target is `thumbv7m-none-eabi`. -> See [`Starting a new project`](./new.md) for more info. - -## `idle` - -A function marked with the `idle` attribute can optionally appear in the -module. This function is used as the special *idle task* and must have -signature `fn(idle::Context) - > !`. - -When present, the runtime will execute the `idle` task after `init`. Unlike -`init`, `idle` will run *with interrupts enabled* and it's not allowed to return -so it must run forever. - -When no `idle` function is declared, the runtime sets the [SLEEPONEXIT] bit and -then sends the microcontroller to sleep after running `init`. - -[SLEEPONEXIT]: https://developer.arm.com/docs/100737/0100/Power-management/Sleep-mode/Sleep-on-exit-bit - -Like in `init`, `static mut` variables will be transformed into `&'static mut` -references that are safe to access. Notice, this feature may be deprecated in the next release, see `task_local` resources. - -The example below shows that `idle` runs after `init`. - -**Note:** The `loop {}` in idle cannot be empty as this will crash the microcontroller due to -LLVM compiling empty loops to an `UDF` instruction in release mode. To avoid UB, the loop needs to imply a "side-effect" by inserting an assembly instruction (e.g., `WFI`) or a `continue`. - -``` rust -{{#include ../../../../examples/idle.rs}} -``` - -``` console -$ cargo run --example idle -{{#include ../../../../ci/expected/idle.run}} -``` - -## Hardware tasks - -To declare interrupt handlers the framework provides a `#[task]` attribute that -can be attached to functions. This attribute takes a `binds` argument whose -value is the name of the interrupt to which the handler will be bound to; the -function adorned with this attribute becomes the interrupt handler. Within the -framework these type of tasks are referred to as *hardware* tasks, because they -start executing in reaction to a hardware event. - -The example below demonstrates the use of the `#[task]` attribute to declare an -interrupt handler. Like in the case of `#[init]` and `#[idle]` local `static -mut` variables are safe to use within a hardware task. - -``` rust -{{#include ../../../../examples/hardware.rs}} -``` - -``` console -$ cargo run --example hardware -{{#include ../../../../ci/expected/hardware.run}} -``` - -So far all the RTIC applications we have seen look no different than the -applications one can write using only the `cortex-m-rt` crate. From this point -we start introducing features unique to RTIC. - -## Priorities - -The static priority of each handler can be declared in the `task` attribute -using the `priority` argument. Tasks can have priorities in the range `1..=(1 << -NVIC_PRIO_BITS)` where `NVIC_PRIO_BITS` is a constant defined in the `device` -crate. When the `priority` argument is omitted, the priority is assumed to be -`1`. The `idle` task has a non-configurable static priority of `0`, the lowest priority. - -> A higher number means a higher priority in RTIC, which is the opposite from what -> Cortex-M does in the NVIC peripheral. -> Explicitly, this means that number `10` has a **higher** priority than number `9`. - -When several tasks are ready to be executed the one with highest static -priority will be executed first. Task prioritization can be observed in the -following scenario: an interrupt signal arrives during the execution of a low -priority task; the signal puts the higher priority task in the pending state. -The difference in priority results in the higher priority task preempting the -lower priority one: the execution of the lower priority task is suspended and -the higher priority task is executed to completion. Once the higher priority -task has terminated the lower priority task is resumed. - -The following example showcases the priority based scheduling of tasks. - -``` rust -{{#include ../../../../examples/preempt.rs}} -``` - -``` console -$ cargo run --example preempt -{{#include ../../../../ci/expected/preempt.run}} -``` - -Note that the task `gpiob` does *not* preempt task `gpioc` because its priority -is the *same* as `gpioc`'s. However, once `gpioc` returns, the execution of -task `gpiob` is prioritized over `gpioa` due to its higher priority. `gpioa` -is resumed only after `gpiob` returns. - -One more note about priorities: choosing a priority higher than what the device -supports (that is `1 << NVIC_PRIO_BITS`) will result in a compile error. Due to -limitations in the language, the error message is currently far from helpful: it -will say something along the lines of "evaluation of constant value failed" and -the span of the error will *not* point out to the problematic interrupt value -- -we are sorry about this! diff --git a/book/en/src/by-example/app_idle.md b/book/en/src/by-example/app_idle.md new file mode 100644 index 0000000000..1eb1472204 --- /dev/null +++ b/book/en/src/by-example/app_idle.md @@ -0,0 +1,27 @@ +# The background task `#[idle]` + +A function marked with the `idle` attribute can optionally appear in the +module. This function is used as the special *idle task* and must have +signature `fn(idle::Context) -> !`. + +When present, the runtime will execute the `idle` task after `init`. Unlike +`init`, `idle` will run *with interrupts enabled* and it's not allowed to return +so it must run forever. + +When no `idle` function is declared, the runtime sets the [SLEEPONEXIT] bit and +then sends the microcontroller to sleep after running `init`. + +[SLEEPONEXIT]: https://developer.arm.com/docs/100737/0100/power-management/sleep-mode/sleep-on-exit-bit + +Like in `init`, locally declared resources will have `'static` lifetimes that are safe to access. + +The example below shows that `idle` runs after `init`. + +``` rust +{{#include ../../../../examples/idle.rs}} +``` + +``` console +$ cargo run --target thumbv7m-none-eabi --example idle +{{#include ../../../../ci/expected/idle.run}} +``` diff --git a/book/en/src/by-example/app_init.md b/book/en/src/by-example/app_init.md new file mode 100644 index 0000000000..7a73e1bce7 --- /dev/null +++ b/book/en/src/by-example/app_init.md @@ -0,0 +1,28 @@ +# App initialization and `#[init]` + +An RTIC application is required an `init` task setting up the system. The corresponding function must have the signature `fn(init::Context) -> (Shared, Local, init::Monotonics)`, where `Shared` and `Local` are the resource structures defined by the user. + +On system reset, the `init` task is executed (after the optionally defined `pre-init` and internal RTIC initialization). The `init` task runs *with interrupts disabled* and has exclusive access to Cortex-M (the `bare_metal::CriticalSection` token is available as `cs`) while device specific peripherals are available through the `core` and `device` fields of `init::Context`. + +## Example + +The example below shows the types of the `core`, `device` and `cs` fields, and showcases the use of a `local` variable with `'static` lifetime. As we will see later, such variables can later be delegated from `init` to other tasks of the RTIC application. + +The `device` field is only available when the `peripherals` argument is set to `true` (which is the default). In the rare case you want to implement an ultra-slim application you can explicitly set `peripherals` to `false`. + +``` rust +{{#include ../../../../examples/init.rs}} +``` + +Running the example will print `init` to the console and then exit the QEMU process. + +``` console +$ cargo run --target thumbv7m-none-eabi --example init +{{#include ../../../../ci/expected/init.run}} +``` + +> **NOTE**: You can choose target device by passing a target +> triple to cargo (e.g `cargo run --example init --target thumbv7m-none-eabi`) or +> configure a default target in `.cargo/config.toml`. +> +> For running the examples, we use a Cortex M3 emulated in QEMU so the target is `thumbv7m-none-eabi`. diff --git a/book/en/src/by-example/app_minimal.md b/book/en/src/by-example/app_minimal.md new file mode 100644 index 0000000000..d0ff40a303 --- /dev/null +++ b/book/en/src/by-example/app_minimal.md @@ -0,0 +1,7 @@ +# The minimal app + +This is the smallest possible RTIC application: + +``` rust +{{#include ../../../../examples/smallest.rs}} +``` diff --git a/book/en/src/by-example/app_priorities.md b/book/en/src/by-example/app_priorities.md new file mode 100644 index 0000000000..934359d5d7 --- /dev/null +++ b/book/en/src/by-example/app_priorities.md @@ -0,0 +1,45 @@ +# Task priorities + +## Priorities + +The static priority of each handler can be declared in the `task` attribute +using the `priority` argument. For Cortex-M, tasks can have priorities in the range `1..=(1 << +NVIC_PRIO_BITS)` where `NVIC_PRIO_BITS` is a constant defined in the `device` +crate. When the `priority` argument is omitted, the priority is assumed to be +`1`. The `idle` task has a non-configurable static priority of `0`, the lowest priority. + +> A higher number means a higher priority in RTIC, which is the opposite from what +> Cortex-M does in the NVIC peripheral. +> Explicitly, this means that number `10` has a **higher** priority than number `9`. + +When several tasks are ready to be executed the one with highest static +priority will be executed first. Task prioritization can be observed in the +following scenario: during the execution of a low +priority task a higher priority task is spawned; this puts the higher priority task in the pending state. +The difference in priority results in the higher priority task preempting the +lower priority one: the execution of the lower priority task is suspended and +the higher priority task is executed to completion. Once the higher priority +task has terminated the lower priority task is resumed. + +The following example showcases the priority based scheduling of tasks. + +``` rust +{{#include ../../../../examples/preempt.rs}} +``` + +``` console +$ cargo run --target thumbv7m-none-eabi --example preempt +{{#include ../../../../ci/expected/preempt.run}} +``` + +Note that the task `bar` does *not* preempt task `baz` because its priority +is the *same* as `baz`'s. However, once `baz` returns, the execution of +task `bar` is prioritized over `foo` due to its higher priority. `foo` +is resumed only after `bar` returns. + +One more note about priorities: choosing a priority higher than what the device +supports will result in a compile error. Due to +limitations in the language, the error message is currently far from helpful: it +will say something along the lines of "evaluation of constant value failed" and +the span of the error will *not* point out to the problematic interrupt value -- +we are sorry about this! diff --git a/book/en/src/by-example/app_task.md b/book/en/src/by-example/app_task.md new file mode 100644 index 0000000000..a5c8b171a2 --- /dev/null +++ b/book/en/src/by-example/app_task.md @@ -0,0 +1,7 @@ +# Defining tasks with `#[task]` + +Tasks, defined with `#[task]`, are the main mechanism of getting work done in RTIC. Every task can be spawned, now or later, be sent messages (message passing) and be given priorities for preemptive multitasking. + +There are two kinds of tasks, software tasks and hardware tasks, and the difference is that hardware tasks are bound to a specific interrupt vector in the MCU while software tasks are not. This means that if a hardware task is bound to the UART's RX interrupt the task will run every time a character is received. + +In the coming pages we will explore both tasks and the different options available. diff --git a/book/en/src/by-example/hardware_tasks.md b/book/en/src/by-example/hardware_tasks.md new file mode 100644 index 0000000000..5f7b26fee9 --- /dev/null +++ b/book/en/src/by-example/hardware_tasks.md @@ -0,0 +1,24 @@ +# Hardware tasks + +To declare interrupt handlers the `#[task]` attribute takes a `binds = InterruptName` argument whose +value is the name of the interrupt to which the handler will be bound to; the +function used with this attribute becomes the interrupt handler. Within the +framework these type of tasks are referred to as *hardware* tasks, because they +start executing in reaction to a hardware event. + +Providing an interrupt name that does not exist will cause a compile error to help with accidental +errors. + +The example below demonstrates the use of the `#[task]` attribute to declare an +interrupt handler. Like in the case of `#[init]` and `#[idle]` local `static +mut` variables are safe to use within a hardware task. + +``` rust +{{#include ../../../../examples/hardware.rs}} +``` + +``` console +$ cargo run --target thumbv7m-none-eabi --example hardware +{{#include ../../../../ci/expected/hardware.run}} +``` + diff --git a/book/en/src/by-example/message_passing.md b/book/en/src/by-example/message_passing.md new file mode 100644 index 0000000000..b80ae03cde --- /dev/null +++ b/book/en/src/by-example/message_passing.md @@ -0,0 +1,14 @@ +# Message passing & capacity + +Software tasks have support for message passing, this means that they can be spawned with an argument +as `foo::spawn(1)` which will run the task `foo` with the argument `1`. The number of arguments is not +limited and is exemplified in the following: + +``` rust +{{#include ../../../../examples/message_passing.rs}} +``` + +``` console +$ cargo run --target thumbv7m-none-eabi --example message_passing +{{#include ../../../../ci/expected/message_passing.run}} +``` diff --git a/book/en/src/by-example/monotonic.md b/book/en/src/by-example/monotonic.md new file mode 100644 index 0000000000..c2a5d86cb9 --- /dev/null +++ b/book/en/src/by-example/monotonic.md @@ -0,0 +1,55 @@ +# Monotonic & spawn_{at/after} + +The understanding of time is an important concept in embedded systems, and to be able to run tasks +based on time is very useful. For this use-case the framework provides the static methods +`task::spawn_after(/* duration */)` and `task::spawn_at(/* specific time instant */)`. +Mostly one uses `spawn_after`, but in cases where it's needed to have spawns happen without drift or +to a fixed baseline `spawn_at` is available. + +To support this the `#[monotonic]` attribute exists which is applied to a type alias definition. +This type alias must point to a type which implements the [`rtic_monotonic::Monotonic`] trait. +This is generally some timer which handles the timing of the system. One or more monotonics can be +used in the same system, for example a slow timer that is used to wake the system from sleep and another +that is used for high granularity scheduling while the system is awake. + +[`rtic_monotonic::Monotonic`]: https://docs.rs/rtic-monotonic + +The attribute has one required parameter and two optional parameters, `binds`, `default` and +`priority` respectively. `binds = InterruptName` defines which interrupt vector is associated to +the timer's interrupt, `default = true` enables a shorthand API when spawning and accessing the +time (`monotonics::now()` vs `monotonics::MyMono::now()`), and `priority` sets the priority the +interrupt vector has. + +> By default `priority` is set to the **maximum priority** of the system but a lower priority +> can be selected if a high priority task cannot take the jitter introduced by the scheduling. +> This can however introduce jitter and delays into the scheduling, making it a trade-off. + +Finally, the monotonics must be initialized in `#[init]` and returned in the `init::Monotonic( ... )` tuple. +This moves the monotonics into the active state which makes it possible to use them. + +An example is provided below: + +``` rust +{{#include ../../../../examples/schedule.rs}} +``` + +``` console +$ cargo run --target thumbv7m-none-eabi --example message +{{#include ../../../../ci/expected/schedule.run}} +``` + +## Canceling or rescheduling a scheduled task + +Tasks spawned using `task::spawn_after` and `task::spawn_at` has as returns a `SpawnHandle`, +where the `SpawnHandle` can be used to cancel or reschedule a task that will run in the future. +If `cancel` or `reschedule_at`/`reschedule_after` returns an `Err` it means that the operation was +too late and that the task is already sent for execution. The following example shows this in action: + +``` rust +{{#include ../../../../examples/cancel-reschedule.rs}} +``` + +``` console +$ cargo run --target thumbv7m-none-eabi --example message +{{#include ../../../../ci/expected/cancel-reschedule.run}} +``` diff --git a/book/en/src/by-example/new.md b/book/en/src/by-example/new.md deleted file mode 100644 index 59a8531356..0000000000 --- a/book/en/src/by-example/new.md +++ /dev/null @@ -1,84 +0,0 @@ -# Starting a new project - -Now that you have learned about the main features of the RTIC framework you can -try it out on your hardware by following these instructions. - -1. Instantiate the [`cortex-m-quickstart`] template. - -[`cortex-m-quickstart`]: https://github.com/rust-embedded/cortex-m-quickstart#cortex-m-quickstart - -``` console -$ # for example using `cargo-generate` -$ cargo generate \ - --git https://github.com/rust-embedded/cortex-m-quickstart \ - --name app - -$ # follow the rest of the instructions -``` - -2. Add a peripheral access crate (PAC) that was generated using [`svd2rust`] - **v0.14.x**, or a board support crate that depends on one such PAC as a - dependency. Make sure that the `rt` feature of the crate is enabled. - -[`svd2rust`]: https://crates.io/crates/svd2rust - -In this example, I'll use the [`lm3s6965`] device crate. This device crate -doesn't have an `rt` Cargo feature; that feature is always enabled. - -[`lm3s6965`]: https://crates.io/crates/lm3s6965 - -This device crate provides a linker script with the memory layout of the target -device so `memory.x` and `build.rs` need to be removed. - -``` console -$ cargo add lm3s6965 --vers 0.1.3 - -$ rm memory.x build.rs -``` - -3. Add the `cortex-m-rtic` crate as a dependency. - -``` console -$ cargo add cortex-m-rtic --allow-prerelease -``` - -4. Write your RTIC application. - -Here I'll use the `init` example from the `cortex-m-rtic` crate. - -The examples are found in the `examples` folder, and the contents -of `init.rs` is shown here: - -``` console -{{#include ../../../../examples/init.rs}} -``` - -The `init` example uses the `lm3s6965` device. Remember to adjust the `device` -argument in the app macro attribute to match the path of your PAC crate, if -different, and add peripherals or other arguments if needed. Although aliases -can be used, this needs to be a full path (from the crate root). For many -devices, it is common for the HAL implementation crate (aliased as `hal`) or -Board Support crate to re-export the PAC as `pac`, leading to a pattern similar -to the below: - -```rust -use abcd123_hal as hal; -//... - -#[rtic::app(device = crate::hal::pac, peripherals = true, monotonic = rtic::cyccnt::CYCCNT)] -mod app { /*...*/ } -``` - -The `init` example also depends on the `panic-semihosting` crate: - -``` console -$ cargo add panic-semihosting -``` - -5. Build it, flash it and run it. - -``` console -$ # NOTE: I have uncommented the `runner` option in `.cargo/config` -$ cargo run -{{#include ../../../../ci/expected/init.run}} -``` diff --git a/book/en/src/by-example/resources.md b/book/en/src/by-example/resources.md index 4f6c3c317c..71092b2fd2 100644 --- a/book/en/src/by-example/resources.md +++ b/book/en/src/by-example/resources.md @@ -1,112 +1,157 @@ -# Resources +# Resource usage -The framework provides an abstraction to share data between any of the contexts -we saw in the previous section (task handlers, `init` and `idle`): resources. +The RTIC framework manages shared and task local resources which allows data to be persistently +stored and safely accessed without the use of unsafe code. -Resources are data visible only to functions declared within the `#[app]` -module. The framework gives the user complete control over which context -can access which resource. +RTIC resources are visible only to functions declared within the `#[app]` module and the framework +gives the user complete control (on a per-task basis) over resource accessibility. -All resources are declared as *two* `struct`s within the `#[app]` module. -Each field in these structures corresponds to a different resource. -One `struct` must be annotated with the attribute `#[local]`. -The other `struct` must be annotated with the attribute `#[shared]`. -The difference between these two sets of resources will be covered later. +System wide resources are declared as **two** `struct`'s within the `#[app]` module annotated with +the attribute `#[local]` and `#[shared]` respectively. Each field in these structures corresponds +to a different resource (identified by field name). The difference between these two sets of +resources will be covered below. -Each context (task handler, `init` or `idle`) must declare the resources it -intends to access in its corresponding metadata attribute using either the -`local` or `shared` argument. This argument takes a list of resource names as -its value. The listed resources are made available to the context under the -`local` and `shared` fields of the `Context` structure. +Each task must declare the resources it intends to access in its corresponding metadata attribute +using the `local` and `shared` arguments. Each argument takes a list of resource identifiers. The +listed resources are made available to the context under the `local` and `shared` fields of the +`Context` structure. -All resources are initialized at runtime, after the `#[init]` function returns. -The `#[init]` function must return the initial values for all resources; hence its return type includes the types of the `#[shared]` and `#[local]` structs. -Because resources are uninitialized during the execution of the `#[init]` function, they cannot be accessed within the `#[init]` function. +The `init` task returns the initial values for the system wide (`#[shared]` and `#[local]`) +resources, and the set of initialized timers used by the application. The monotonic timers will be +further discussed in [Monotonic & `spawn_{at/after}`](./monotonic.md). -The example application shown below contains two interrupt handlers. -Each handler has access to its own `#[local]` resource. +## `#[local]` resources + +`#[local]` resources are locally accessible to a specific task, meaning that only that task can +access the resource and does so without locks or critical sections. This allows for the resources, +commonly drivers or large objects, to be initialized in `#[init]` and then be passed to a specific +task. + +The example application shown below contains two tasks where each task has access to its own +`#[local]` resource, plus that the `idle` task has its own `#[local]` as well. ``` rust -{{#include ../../../../examples/resource.rs}} +{{#include ../../../../examples/locals.rs}} ``` ``` console -$ cargo run --example resource -{{#include ../../../../ci/expected/resource.run}} +$ cargo run --target thumbv7m-none-eabi --example locals +{{#include ../../../../ci/expected/locals.run}} ``` A `#[local]` resource cannot be accessed from outside the task it was associated to in a `#[task]` attribute. Assigning the same `#[local]` resource to more than one task is a compile-time error. -## `lock` +### Task local initialized resources -Critical sections are required to access `#[shared]` resources in a data race-free manner. +A special use-case of local resources are the ones specified directly in the resource claim, +`#[task(local = [my_var: TYPE = INITIAL_VALUE, ...])]`, this allows for creating locals which do no need to be +initialized in `#[init]`. +Moreover local resources in `#[init]` and `#[idle]` have `'static` lifetimes, this is safe since both are not re-entrant. -The `shared` field of the passed `Context` implements the [`Mutex`] trait for each shared resource accessible to the task. +In the example below the different uses and lifetimes are shown: -The only method on this trait, [`lock`], runs its closure argument in a critical section. +``` rust +{{#include ../../../../examples/declared_locals.rs}} +``` + + + +## `#[shared]` resources and `lock` + +Critical sections are required to access `#[shared]` resources in a data race-free manner and to +achieve this the `shared` field of the passed `Context` implements the [`Mutex`] trait for each +shared resource accessible to the task. This trait has only one method, [`lock`], which runs its +closure argument in a critical section. [`Mutex`]: ../../../api/rtic/trait.Mutex.html [`lock`]: ../../../api/rtic/trait.Mutex.html#method.lock -The critical section created by the `lock` API is based on dynamic priorities: it temporarily raises the dynamic priority of the context to a *ceiling* priority that prevents other tasks from preempting the critical section. This synchronization protocol is known as the [Immediate Ceiling Priority Protocol -(ICPP)][icpp], and complies with [Stack Resource Policy(SRP)][srp] based scheduling of RTIC. +The critical section created by the `lock` API is based on dynamic priorities: it temporarily +raises the dynamic priority of the context to a *ceiling* priority that prevents other tasks from +preempting the critical section. This synchronization protocol is known as the +[Immediate Ceiling Priority Protocol (ICPP)][icpp], and complies with +[Stack Resource Policy (SRP)][srp] based scheduling of RTIC. [icpp]: https://en.wikipedia.org/wiki/Priority_ceiling_protocol [srp]: https://en.wikipedia.org/wiki/Stack_Resource_Policy -In the example below we have three interrupt handlers with priorities ranging from one to three. The two handlers with the lower priorities contend for the `shared` resource and need to lock the resource for accessing the data. The highest priority handler, which do not access the `shared` resource, is free to preempt the critical section created by the -lowest priority handler. +In the example below we have three interrupt handlers with priorities ranging from one to three. +The two handlers with the lower priorities contend for the `shared` resource and need to lock the +resource for accessing the data. The highest priority handler, which do not access the `shared` +resource, is free to preempt the critical section created by the lowest priority handler. ``` rust {{#include ../../../../examples/lock.rs}} ``` ``` console -$ cargo run --example lock +$ cargo run --target thumbv7m-none-eabi --example lock {{#include ../../../../ci/expected/lock.run}} ``` ## Multi-lock -As an extension to `lock`, and to reduce rightward drift, locks can be taken as tuples. The following examples shows this in use: +As an extension to `lock`, and to reduce rightward drift, locks can be taken as tuples. The +following examples shows this in use: ``` rust {{#include ../../../../examples/multilock.rs}} ``` +``` console +$ cargo run --target thumbv7m-none-eabi --example multilock +{{#include ../../../../ci/expected/multilock.run}} +``` + ## Only shared (`&-`) access -By default the framework assumes that all tasks require exclusive access (`&mut-`) to resources but it is possible to specify that a task only requires shared access (`&-`) to a resource using the `&resource_name` syntax in the `resources` list. +By default the framework assumes that all tasks require exclusive access (`&mut-`) to resources but +it is possible to specify that a task only requires shared access (`&-`) to a resource using the +`&resource_name` syntax in the `shared` list. -The advantage of specifying shared access (`&-`) to a resource is that no locks are required to access the resource even if the resource is contended by several tasks running at different priorities. The downside is that the task only gets a shared reference (`&-`) to the resource, limiting the operations it can perform on it, but where a shared reference is enough this approach reduces the number of required locks. In addition to simple immutable data, this shared access can be useful where the resource type safely implements interior mutability, with -appropriate locking or atomic operations of its own. +The advantage of specifying shared access (`&-`) to a resource is that no locks are required to +access the resource even if the resource is contended by several tasks running at different +priorities. The downside is that the task only gets a shared reference (`&-`) to the resource, +limiting the operations it can perform on it, but where a shared reference is enough this approach +reduces the number of required locks. In addition to simple immutable data, this shared access can +be useful where the resource type safely implements interior mutability, with appropriate locking +or atomic operations of its own. -Note that in this release of RTIC it is not possible to request both exclusive access (`&mut-`) and shared access (`&-`) to the *same* resource from different tasks. Attempting to do so will result in a compile error. +Note that in this release of RTIC it is not possible to request both exclusive access (`&mut-`) +and shared access (`&-`) to the *same* resource from different tasks. Attempting to do so will +result in a compile error. -In the example below a key (e.g. a cryptographic key) is loaded (or created) at runtime and then used from two tasks that run at different priorities without any kind of lock. +In the example below a key (e.g. a cryptographic key) is loaded (or created) at runtime and then +used from two tasks that run at different priorities without any kind of lock. ``` rust {{#include ../../../../examples/only-shared-access.rs}} ``` ``` console -$ cargo run --example only-shared-access +$ cargo run --target thumbv7m-none-eabi --example only-shared-access {{#include ../../../../ci/expected/only-shared-access.run}} ``` -## Lock-free resource access of mutable resources +## Lock-free resource access of shared resources -A critical section is *not* required to access a `#[shared]` resource that's only accessed by tasks running at the *same* priority. -In this case, you can opt out of the `lock` API by adding the `#[lock_free]` field-level attribute to the resource declaration (see example below). -Note that this is merely a convenience: if you do use the `lock` API, at runtime the framework will *not* produce a critical section. -Also worth noting: using `#[lock_free]` on resources shared by tasks running at different priorities will result in a *compile-time* error -- not using the `lock` API would be a data race in that case. +A critical section is *not* required to access a `#[shared]` resource that's only accessed by tasks +running at the *same* priority. In this case, you can opt out of the `lock` API by adding the +`#[lock_free]` field-level attribute to the resource declaration (see example below). Note that +this is merely a convenience: if you do use the `lock` API, at runtime the framework will +**not** produce a critical section. Also worth noting: using `#[lock_free]` on resources shared by +tasks running at different priorities will result in a *compile-time* error -- not using the `lock` +API would be a data race in that case. ``` rust {{#include ../../../../examples/lock-free.rs}} ``` ``` console -$ cargo run --example lock-free +$ cargo run --target thumbv7m-none-eabi --example lock-free {{#include ../../../../ci/expected/lock-free.run}} ``` diff --git a/book/en/src/by-example/software_tasks.md b/book/en/src/by-example/software_tasks.md new file mode 100644 index 0000000000..0c9b62ee7c --- /dev/null +++ b/book/en/src/by-example/software_tasks.md @@ -0,0 +1,16 @@ +# Software tasks & spawn + +To declare tasks in the framework the `#[task]` attribute is used on a function. +By default these tasks are referred to as software tasks as they do not have a direct coupling to +an interrupt handler. Software tasks can be spawned (started) using the `task_name::spawn()` static +method which will directly run the task given that there are no higher priority tasks running. +This is exemplified in the following: + +``` rust +{{#include ../../../../examples/spawn.rs}} +``` + +``` console +$ cargo run --target thumbv7m-none-eabi --example spawn +{{#include ../../../../ci/expected/spawn.run}} +``` diff --git a/book/en/src/by-example/starting_a_project.md b/book/en/src/by-example/starting_a_project.md new file mode 100644 index 0000000000..b50ac4a9ef --- /dev/null +++ b/book/en/src/by-example/starting_a_project.md @@ -0,0 +1,14 @@ +# Starting a new project + +When starting an RTIC project from scratch it is recommended to follow RTIC's [`defmt-app-template`]. + +[`defmt-app-template`]: https://github.com/rtic-rs/defmt-app-template + +This will give you an RTIC application with support for RTT logging with [`defmt`] and stack overflow +protection using [`flip-link`]. There are also an multitude of examples available provided by the community: + +- [https://github.com/kalkyl/f411-rtic](https://github.com/kalkyl/f411-rtic) +- ... More to come + +[`defmt`]: https://github.com/knurling-rs/defmt/ +[`flip-link`]: https://github.com/knurling-rs/flip-link/ diff --git a/book/en/src/by-example/tasks.md b/book/en/src/by-example/tasks.md deleted file mode 100644 index 8558a542a1..0000000000 --- a/book/en/src/by-example/tasks.md +++ /dev/null @@ -1,118 +0,0 @@ -# Software tasks - -In addition to hardware tasks, which are invoked by the hardware in response to -hardware events, RTIC also supports *software* tasks which can be spawned by the -application from any execution context. - -Software tasks can also be assigned priorities and, under the hood, are -dispatched from interrupt handlers. RTIC requires that free interrupts are -declared in the `dispatchers` app argument when using software tasks; some of these free -interrupts will be used to dispatch the software tasks. An advantage of software -tasks over hardware tasks is that many tasks can be mapped to a single interrupt -handler. - -Software tasks are also declared using the `task` attribute but the `binds` -argument must be omitted. - -The example below showcases three software tasks that run at 2 different -priorities. The three software tasks are mapped to 2 interrupts handlers. - -``` rust -{{#include ../../../../examples/task.rs}} -``` - -``` console -$ cargo run --example task -{{#include ../../../../ci/expected/task.run}} -``` - -## Message passing - -The other advantage of software tasks is that messages can be passed to these -tasks when spawning them. The type of the message payload must be specified in -the signature of the task handler. - -The example below showcases three tasks, two of them expect a message. - -``` rust -{{#include ../../../../examples/message.rs}} -``` - -``` console -$ cargo run --example message -{{#include ../../../../ci/expected/message.run}} -``` - -## Capacity - -RTIC does *not* perform any form of heap-based memory allocation. The memory -required to store messages is statically reserved. By default the framework -minimizes the memory footprint of the application so each task has a message -"capacity" of 1: meaning that at most one message can be posted to the task -before it gets a chance to run. This default can be overridden for each task -using the `capacity` argument. This argument takes a positive integer that -indicates how many messages the task message buffer can hold. - -The example below sets the capacity of the software task `foo` to 4. If the -capacity is not specified then the second `spawn.foo` call in `UART0` would -fail (panic). - -``` rust -{{#include ../../../../examples/capacity.rs}} -``` - -``` console -$ cargo run --example capacity -{{#include ../../../../ci/expected/capacity.run}} -``` - -## Error handling - -The `spawn` API returns the `Err` variant when there's no space to send the -message. In most scenarios spawning errors are handled in one of two ways: - -- Panicking, using `unwrap`, `expect`, etc. This approach is used to catch the - programmer error (i.e. bug) of selecting a capacity that was too small. When - this panic is encountered during testing choosing a bigger capacity and - recompiling the program may fix the issue but sometimes it's necessary to dig - deeper and perform a timing analysis of the application to check if the - platform can deal with peak payload or if the processor needs to be replaced - with a faster one. - -- Ignoring the result. In soft real-time and non real-time applications it may - be OK to occasionally lose data or fail to respond to some events during event - bursts. In those scenarios silently letting a `spawn` call fail may be - acceptable. - -It should be noted that retrying a `spawn` call is usually the wrong approach as -this operation will likely never succeed in practice. Because there are only -context switches towards *higher* priority tasks retrying the `spawn` call of a -lower priority task will never let the scheduler dispatch said task meaning that -its message buffer will never be emptied. This situation is depicted in the -following snippet: - -``` rust -#[rtic::app(..)] -mod app { - #[init(spawn = [foo, bar])] - fn init(cx: init::Context) { - cx.spawn.foo().unwrap(); - cx.spawn.bar().unwrap(); - } - - #[task(priority = 2, spawn = [bar])] - fn foo(cx: foo::Context) { - // .. - - // the program will get stuck here - while cx.spawn.bar(payload).is_err() { - // retry the spawn call if it failed - } - } - - #[task(priority = 1)] - fn bar(cx: bar::Context, payload: i32) { - // .. - } -} -``` diff --git a/book/en/src/by-example/timer-queue.md b/book/en/src/by-example/timer-queue.md deleted file mode 100644 index 2964175055..0000000000 --- a/book/en/src/by-example/timer-queue.md +++ /dev/null @@ -1,113 +0,0 @@ -# Timer queue - -In contrast with the `spawn` API, which immediately spawns a software task onto -the scheduler, the `schedule` API can be used to schedule a task to run some -time in the future. - -To use the `schedule` API a monotonic timer must be first defined using the -`monotonic` argument of the `#[app]` attribute. This argument takes a path to a -type that implements the [`Monotonic`] trait. The associated type, `Instant`, of -this trait represents a timestamp in arbitrary units and it's used extensively -in the `schedule` API -- it is suggested to model this type after [the one in -the standard library][std-instant]. - -Although not shown in the trait definition (due to limitations in the trait / -type system) the subtraction of two `Instant`s should return some `Duration` -type (see [`core::time::Duration`]) and this `Duration` type must implement the -`TryInto` trait. The implementation of this trait must convert the -`Duration` value, which uses some arbitrary unit of time, into the "system timer -(SYST) clock cycles" time unit. The result of the conversion must be a 32-bit -integer. If the result of the conversion doesn't fit in a 32-bit number then the -operation must return an error, any error type. - -[`Monotonic`]: ../../../api/rtic/trait.Monotonic.html -[std-instant]: https://doc.rust-lang.org/std/time/struct.Instant.html -[`core::time::Duration`]: https://doc.rust-lang.org/core/time/struct.Duration.html - -For ARMv7+ targets the `rtic` crate provides a `Monotonic` implementation based -on the built-in CYCle CouNTer (CYCCNT). Note that this is a 32-bit timer clocked -at the frequency of the CPU and as such it is not suitable for tracking time -spans in the order of seconds. - -When scheduling a task the (user-defined) `Instant` at which the task should be -executed must be passed as the first argument of the `schedule` invocation. - -Additionally, the chosen `monotonic` timer must be configured and initialized -during the `#[init]` phase. Note that this is *also* the case if you choose to -use the `CYCCNT` provided by the `cortex-m-rtic` crate. - -The example below schedules two tasks from `init`: `foo` and `bar`. `foo` is -scheduled to run 8 million clock cycles in the future. Next, `bar` is scheduled -to run 4 million clock cycles in the future. Thus `bar` runs before `foo` since -it was scheduled to run first. - -> **IMPORTANT**: The examples that use the `schedule` API or the `Instant` -> abstraction will **not** properly work on QEMU because the Cortex-M cycle -> counter functionality has not been implemented in `qemu-system-arm`. - -``` rust -{{#include ../../../../examples/schedule.rs}} -``` - -Running the program on real hardware produces the following output in the -console: - -``` text -{{#include ../../../../ci/expected/schedule.run}} -``` - -When the `schedule` API is being used the runtime internally uses the `SysTick` -interrupt handler and the system timer peripheral (`SYST`) so neither can be -used by the application. This is accomplished by changing the type of -`init::Context.core` from `cortex_m::Peripherals` to `rtic::Peripherals`. The -latter structure contains all the fields of the former minus the `SYST` one. - -## Periodic tasks - -Software tasks have access to the `Instant` at which they were scheduled to run -through the `scheduled` variable. This information and the `schedule` API can be -used to implement periodic tasks as shown in the example below. - -``` rust -{{#include ../../../../examples/periodic.rs}} -``` - -This is the output produced by the example. Note that there is zero drift / -jitter even though `schedule.foo` was invoked at the *end* of `foo`. Using -`Instant::now` instead of `scheduled` would have resulted in drift / jitter. - -``` text -{{#include ../../../../ci/expected/periodic.run}} -``` - -## Baseline - -For the tasks scheduled from `init` we have exact information about their -`scheduled` time. For hardware tasks there's no `scheduled` time because these -tasks are asynchronous in nature. For hardware tasks the runtime provides a -`start` time, which indicates the time at which the task handler started -executing. - -Note that `start` is **not** equal to the arrival time of the event that fired -the task. Depending on the priority of the task and the load of the system the -`start` time could be very far off from the event arrival time. - -What do you think will be the value of `scheduled` for software tasks that are -*spawned* instead of scheduled? The answer is that spawned tasks inherit the -*baseline* time of the context that spawned it. The baseline of hardware tasks -is their `start` time, the baseline of software tasks is their `scheduled` time -and the baseline of `init` is the system start time or time zero -(`Instant::zero()`). `idle` doesn't really have a baseline but tasks spawned -from it will use `Instant::now()` as their baseline time. - -The example below showcases the different meanings of the *baseline*. - -``` rust -{{#include ../../../../examples/baseline.rs}} -``` - -Running the program on real hardware produces the following output in the console: - -``` text -{{#include ../../../../ci/expected/baseline.run}} -``` diff --git a/book/en/src/by-example/tips.md b/book/en/src/by-example/tips.md index e292634bb1..18d59915bd 100644 --- a/book/en/src/by-example/tips.md +++ b/book/en/src/by-example/tips.md @@ -1,176 +1,3 @@ # Tips & tricks -For complete RTIC examples see the [rtic-examples][rtic-examples] repository. - -[rtic-examples]: https://github.com/rtic-rs/rtic-examples - -## Generics - -All resource proxies implement the `rtic::Mutex` trait. -If a resource does not implement this, one can wrap it in the [`rtic::Exclusive`] -newtype which does implement the `Mutex` trait. With the help of this newtype -one can write a generic function that operates on generic resources and call it -from different tasks to perform some operation on the same set of resources. -Here's one such example: - -[`rtic::Exclusive`]: ../../../api/rtic/struct.Exclusive.html - -``` rust -{{#include ../../../../examples/generics.rs}} -``` - -``` console -$ cargo run --example generics -{{#include ../../../../ci/expected/generics.run}} -``` - -## Conditional compilation - -You can use conditional compilation (`#[cfg]`) on resources (the fields of -`#[resources] struct Resources`) and tasks (the `fn` items). -The effect of using `#[cfg]` attributes is that the resource / task -will *not* be available through the corresponding `Context` `struct` -if the condition doesn't hold. - -The example below logs a message whenever the `foo` task is spawned, but only if -the program has been compiled using the `dev` profile. - -``` rust -{{#include ../../../../examples/cfg.rs}} -``` - -``` console -$ cargo run --example cfg --release - -$ cargo run --example cfg -{{#include ../../../../ci/expected/cfg.run}} -``` - -## Running tasks from RAM - -The main goal of moving the specification of RTIC applications to attributes in -RTIC v0.4.0 was to allow inter-operation with other attributes. For example, the -`link_section` attribute can be applied to tasks to place them in RAM; this can -improve performance in some cases. - -> **IMPORTANT**: In general, the `link_section`, `export_name` and `no_mangle` -> attributes are very powerful but also easy to misuse. Incorrectly using any of -> these attributes can cause undefined behavior; you should always prefer to use -> safe, higher level attributes around them like `cortex-m-rt`'s `interrupt` and -> `exception` attributes. -> -> In the particular case of RAM functions there's no -> safe abstraction for it in `cortex-m-rt` v0.6.5 but there's an [RFC] for -> adding a `ramfunc` attribute in a future release. - -[RFC]: https://github.com/rust-embedded/cortex-m-rt/pull/100 - -The example below shows how to place the higher priority task, `bar`, in RAM. - -``` rust -{{#include ../../../../examples/ramfunc.rs}} -``` - -Running this program produces the expected output. - -``` console -$ cargo run --example ramfunc -{{#include ../../../../ci/expected/ramfunc.run}} -``` - -One can look at the output of `cargo-nm` to confirm that `bar` ended in RAM -(`0x2000_0000`), whereas `foo` ended in Flash (`0x0000_0000`). - -``` console -$ cargo nm --example ramfunc --release | grep ' foo::' -{{#include ../../../../ci/expected/ramfunc.grep.foo}} -``` - -``` console -$ cargo nm --example ramfunc --release | grep ' bar::' -{{#include ../../../../ci/expected/ramfunc.grep.bar}} -``` - -## Indirection for faster message passing - -Message passing always involves copying the payload from the sender into a -static variable and then from the static variable into the receiver. Thus -sending a large buffer, like a `[u8; 128]`, as a message involves two expensive -`memcpy`s. To minimize the message passing overhead one can use indirection: -instead of sending the buffer by value, one can send an owning pointer into the -buffer. - -One can use a global allocator to achieve indirection (`alloc::Box`, -`alloc::Rc`, etc.), which requires using the nightly channel as of Rust v1.37.0, -or one can use a statically allocated memory pool like [`heapless::Pool`]. - -[`heapless::Pool`]: https://docs.rs/heapless/0.5.0/heapless/pool/index.html - -Here's an example where `heapless::Pool` is used to "box" buffers of 128 bytes. - -``` rust -{{#include ../../../../examples/pool.rs}} -``` - -``` console -$ cargo run --example pool -{{#include ../../../../ci/expected/pool.run}} -``` - -## Inspecting the expanded code - -`#[rtic::app]` is a procedural macro that produces support code. If for some -reason you need to inspect the code generated by this macro you have two -options: - -You can inspect the file `rtic-expansion.rs` inside the `target` directory. This -file contains the expansion of the `#[rtic::app]` item (not your whole program!) -of the *last built* (via `cargo build` or `cargo check`) RTIC application. The -expanded code is not pretty printed by default so you'll want to run `rustfmt` -on it before you read it. - -``` console -$ cargo build --example foo - -$ rustfmt target/rtic-expansion.rs - -$ tail target/rtic-expansion.rs -``` - -``` rust -#[doc = r" Implementation details"] -mod app { - #[doc = r" Always include the device crate which contains the vector table"] - use lm3s6965 as _; - #[no_mangle] - unsafe extern "C" fn main() -> ! { - rtic::export::interrupt::disable(); - let mut core: rtic::export::Peripherals = core::mem::transmute(()); - core.SCB.scr.modify(|r| r | 1 << 1); - rtic::export::interrupt::enable(); - loop { - rtic::export::wfi() - } - } -} -``` - -Or, you can use the [`cargo-expand`] sub-command. This sub-command will expand -*all* the macros, including the `#[rtic::app]` attribute, and modules in your -crate and print the output to the console. - -[`cargo-expand`]: https://crates.io/crates/cargo-expand - -``` console -$ # produces the same output as before -$ cargo expand --example smallest | tail -``` - -## Resource de-structure-ing - -When having a task taking multiple resources it can help in readability to split -up the resource struct. Here are two examples on how this can be done: - -``` rust -{{#include ../../../../examples/destructure.rs}} -``` +In this section we will explore common tips & tricks related to using RTIC. diff --git a/book/en/src/by-example/tips_destructureing.md b/book/en/src/by-example/tips_destructureing.md new file mode 100644 index 0000000000..7b864c4666 --- /dev/null +++ b/book/en/src/by-example/tips_destructureing.md @@ -0,0 +1,13 @@ +# Resource de-structure-ing + +When having a task taking multiple resources it can help in readability to split +up the resource struct. Here are two examples on how this can be done: + +``` rust +{{#include ../../../../examples/destructure.rs}} +``` + +``` console +$ cargo run --target thumbv7m-none-eabi --example destructure +{{#include ../../../../ci/expected/destructure.run}} +``` diff --git a/book/en/src/by-example/tips_from_ram.md b/book/en/src/by-example/tips_from_ram.md new file mode 100644 index 0000000000..6aef2f704e --- /dev/null +++ b/book/en/src/by-example/tips_from_ram.md @@ -0,0 +1,45 @@ +# Running tasks from RAM + +The main goal of moving the specification of RTIC applications to attributes in +RTIC v0.4.0 was to allow inter-operation with other attributes. For example, the +`link_section` attribute can be applied to tasks to place them in RAM; this can +improve performance in some cases. + +> **IMPORTANT**: In general, the `link_section`, `export_name` and `no_mangle` +> attributes are very powerful but also easy to misuse. Incorrectly using any of +> these attributes can cause undefined behavior; you should always prefer to use +> safe, higher level attributes around them like `cortex-m-rt`'s `interrupt` and +> `exception` attributes. +> +> In the particular case of RAM functions there's no +> safe abstraction for it in `cortex-m-rt` v0.6.5 but there's an [RFC] for +> adding a `ramfunc` attribute in a future release. + +[RFC]: https://github.com/rust-embedded/cortex-m-rt/pull/100 + +The example below shows how to place the higher priority task, `bar`, in RAM. + +``` rust +{{#include ../../../../examples/ramfunc.rs}} +``` + +Running this program produces the expected output. + +``` console +$ cargo run --target thumbv7m-none-eabi --example ramfunc +{{#include ../../../../ci/expected/ramfunc.run}} +``` + +One can look at the output of `cargo-nm` to confirm that `bar` ended in RAM +(`0x2000_0000`), whereas `foo` ended in Flash (`0x0000_0000`). + +``` console +$ cargo nm --example ramfunc --release | grep ' foo::' +{{#include ../../../../ci/expected/ramfunc.grep.foo}} +``` + +``` console +$ cargo nm --example ramfunc --release | grep ' bar::' +{{#include ../../../../ci/expected/ramfunc.grep.bar}} +``` + diff --git a/book/en/src/by-example/tips_indirection.md b/book/en/src/by-example/tips_indirection.md new file mode 100644 index 0000000000..22c5774630 --- /dev/null +++ b/book/en/src/by-example/tips_indirection.md @@ -0,0 +1,26 @@ +# Using indirection for faster message passing + +Message passing always involves copying the payload from the sender into a +static variable and then from the static variable into the receiver. Thus +sending a large buffer, like a `[u8; 128]`, as a message involves two expensive +`memcpy`s. To minimize the message passing overhead one can use indirection: +instead of sending the buffer by value, one can send an owning pointer into the +buffer. + +One can use a global allocator to achieve indirection (`alloc::Box`, +`alloc::Rc`, etc.), which requires using the nightly channel as of Rust v1.37.0, +or one can use a statically allocated memory pool like [`heapless::Pool`]. + +[`heapless::Pool`]: https://docs.rs/heapless/0.5.0/heapless/pool/index.html + +Here's an example where `heapless::Pool` is used to "box" buffers of 128 bytes. + +``` rust +{{#include ../../../../examples/pool.rs}} +``` + +``` console +$ cargo run --target thumbv7m-none-eabi --example pool +{{#include ../../../../ci/expected/pool.run}} +``` + diff --git a/book/en/src/by-example/tips_monotonic_impl.md b/book/en/src/by-example/tips_monotonic_impl.md new file mode 100644 index 0000000000..ad04ef0a69 --- /dev/null +++ b/book/en/src/by-example/tips_monotonic_impl.md @@ -0,0 +1,59 @@ +# Implementing a `Monotonic` timer for scheduling + +The framework is very flexible in that it can utilize any timer which has compare-match and (optional) +overflow interrupts for scheduling. The only thing needed to make a timer usable with RTIC is to +implement the [`rtic_monotonic::Monotonic`] trait. + +Implementing time that supports a vast range is generally **very** difficult, and in RTIC 0.5 it was a +common problem how to implement time handling and not get stuck in weird special cases. Moreover +it was difficult to understand the relation between time and the timers used for scheduling. From +RTIC 0.6 we have moved to use [`embedded_time`] as the basis for all time-based operation and +abstraction of clocks. This is why from RTIC 0.6 it is almost trivial to implement the `Monotonic` +trait and use any timer in a system for scheduling. + +The trait documents the requirements for each method, however a small PoC implementation is provided +below. + +[`rtic_monotonic::Monotonic`]: https://docs.rs/rtic-monotonic/ +[`embedded_time`]: https://docs.rs/embedded_time/ + +```rust +use rtic_monotonic::{embedded_time::clock::Error, Clock, Fraction, Instant, Monotonic}; + +/// Example wrapper struct for a timer +pub struct Timer { + tim: TIM2, +} + +impl Clock for Timer { + const SCALING_FACTOR: Fraction = Fraction::new(1, FREQ); + type T = u32; + + #[inline(always)] + fn try_now(&self) -> Result, Error> { + Ok(Instant::new(Self::count())) + } +} + +impl Monotonic for Timer { + unsafe fn reset(&mut self) { + // Reset timer counter + self.tim.cnt.write(|_, w| w.bits(0)); + + // Since reset is only called once, we use it to enable + // the interrupt generation bit. + self.tim.dier.modify(|_, w| w.cc1ie().set_bit()); + } + + // Use Compare channel 1 for Monotonic + fn set_compare(&mut self, instant: &Instant) { + self.tim + .ccr1 + .write(|w| w.ccr().bits(instant.duration_since_epoch().integer())); + } + + fn clear_compare_flag(&mut self) { + self.tim.sr.modify(|_, w| w.cc1if().clear_bit()); + } +} +``` diff --git a/book/en/src/by-example/tips_static_lifetimes.md b/book/en/src/by-example/tips_static_lifetimes.md new file mode 100644 index 0000000000..3ea08166e4 --- /dev/null +++ b/book/en/src/by-example/tips_static_lifetimes.md @@ -0,0 +1,24 @@ +# 'static super-powers + +As discussed earlier `local` resources are given `'static` lifetime in `#[init]` and `#[idle]`, +this can be used to allocate an object and then split it up or give the pre-allocated object to a +task, driver or some other object. +This is very useful when needing to allocate memory for drivers, such as USB drivers, and using +data structures that can be split such as [`heapless::spsc::Queue`]. + +In the following example an [`heapless::spsc::Queue`] is given to two different tasks for lock-free access +to the shared queue. + +[`heapless::spsc::Queue`]: https://docs.rs/heapless/0.7.5/heapless/spsc/struct.Queue.html + + +``` rust +{{#include ../../../../examples/static.rs}} +``` + +Running this program produces the expected output. + +``` console +$ cargo run --target thumbv7m-none-eabi --example static +{{#include ../../../../ci/expected/static.run}} +``` diff --git a/book/en/src/by-example/tips_view_code.md b/book/en/src/by-example/tips_view_code.md new file mode 100644 index 0000000000..8f0d86b591 --- /dev/null +++ b/book/en/src/by-example/tips_view_code.md @@ -0,0 +1,48 @@ +# Inspecting generated code + +`#[rtic::app]` is a procedural macro that produces support code. If for some +reason you need to inspect the code generated by this macro you have two +options: + +You can inspect the file `rtic-expansion.rs` inside the `target` directory. This +file contains the expansion of the `#[rtic::app]` item (not your whole program!) +of the *last built* (via `cargo build` or `cargo check`) RTIC application. The +expanded code is not pretty printed by default so you'll want to run `rustfmt` +on it before you read it. + +``` console +$ cargo build --example foo + +$ rustfmt target/rtic-expansion.rs + +$ tail target/rtic-expansion.rs +``` + +``` rust +#[doc = r" Implementation details"] +mod app { + #[doc = r" Always include the device crate which contains the vector table"] + use lm3s6965 as _; + #[no_mangle] + unsafe extern "C" fn main() -> ! { + rtic::export::interrupt::disable(); + let mut core: rtic::export::Peripherals = core::mem::transmute(()); + core.SCB.scr.modify(|r| r | 1 << 1); + rtic::export::interrupt::enable(); + loop { + rtic::export::wfi() + } + } +} +``` + +Or, you can use the [`cargo-expand`] sub-command. This sub-command will expand +*all* the macros, including the `#[rtic::app]` attribute, and modules in your +crate and print the output to the console. + +[`cargo-expand`]: https://crates.io/crates/cargo-expand + +``` console +$ # produces the same output as before +$ cargo expand --example smallest | tail +``` diff --git a/book/en/src/by-example/types-send-sync.md b/book/en/src/by-example/types-send-sync.md deleted file mode 100644 index de812a60f4..0000000000 --- a/book/en/src/by-example/types-send-sync.md +++ /dev/null @@ -1,51 +0,0 @@ -# Types, Send and Sync - -Every function within the `app` module has a `Context` structure as its -first parameter. All the fields of these structures have predictable, -non-anonymous types so you can write plain functions that take them as arguments. - -The API reference specifies how these types are generated from the input. You -can also generate documentation for your binary crate (`cargo doc --bin `); -in the documentation you'll find `Context` structs (e.g. `init::Context` and -`idle::Context`). - -The example below shows the different types generates by the `app` attribute. - -``` rust -{{#include ../../../../examples/types.rs}} -``` - -## `Send` - -[`Send`] is a marker trait for "types that can be transferred across thread -boundaries", according to its definition in `core`. In the context of RTIC the -`Send` trait is only required where it's possible to transfer a value between -tasks that run at *different* priorities. This occurs in a few places: in -message passing, in shared resources and in the initialization of late -resources. - -[`Send`]: https://doc.rust-lang.org/core/marker/trait.Send.html - -The `app` attribute will enforce that `Send` is implemented where required so -you don't need to worry much about it. Currently all types that are passed need -to be `Send` in RTIC, however this restriction might be relaxed in the future. - -## `Sync` - -Similarly, [`Sync`] is a marker trait for "types for which it is safe to share -references between threads", according to its definition in `core`. In the -context of RTIC the `Sync` trait is only required where it's possible for two, -or more, tasks that run at different priorities and may get a shared reference -(`&-`) to a resource. This only occurs with shared access (`&-`) resources. - -[`Sync`]: https://doc.rust-lang.org/core/marker/trait.Sync.html - -The `app` attribute will enforce that `Sync` is implemented where required but -it's important to know where the `Sync` bound is not required: shared access -(`&-`) resources contended by tasks that run at the *same* priority. - -The example below shows where a type that doesn't implement `Sync` can be used. - -``` rust -{{#include ../../../../examples/not-sync.rs}} -``` diff --git a/book/en/src/preface.md b/book/en/src/preface.md index 1fd37b72ec..e81542c997 100644 --- a/book/en/src/preface.md +++ b/book/en/src/preface.md @@ -21,6 +21,6 @@ This is the documentation of v0.6.x of RTIC; for the documentation of version * v0.5.x go [here](/0.5). * v0.4.x go [here](/0.4). -{{#include ../../../README.md:7:46}} +{{#include ../../../README.md:7:47}} -{{#include ../../../README.md:52:}} +{{#include ../../../README.md:48:}} diff --git a/ci/expected/big-struct-opt.run b/ci/expected/big-struct-opt.run new file mode 100644 index 0000000000..e69de29bb2 diff --git a/ci/expected/cancel-reschedule.run b/ci/expected/cancel-reschedule.run new file mode 100644 index 0000000000..5a947526f4 --- /dev/null +++ b/ci/expected/cancel-reschedule.run @@ -0,0 +1,3 @@ +init +foo +bar diff --git a/ci/expected/cfg-whole-task.run b/ci/expected/cfg-whole-task.run new file mode 100644 index 0000000000..e69de29bb2 diff --git a/ci/expected/common.run b/ci/expected/common.run new file mode 100644 index 0000000000..e69de29bb2 diff --git a/ci/expected/declared_locals.run b/ci/expected/declared_locals.run new file mode 100644 index 0000000000..e69de29bb2 diff --git a/ci/expected/destructure.run b/ci/expected/destructure.run new file mode 100644 index 0000000000..b9b7cc90cc --- /dev/null +++ b/ci/expected/destructure.run @@ -0,0 +1,2 @@ +foo: a = 0, b = 0, c = 0 +bar: a = 0, b = 0, c = 0 diff --git a/ci/expected/extern_binds.run b/ci/expected/extern_binds.run new file mode 100644 index 0000000000..9d925d52b4 --- /dev/null +++ b/ci/expected/extern_binds.run @@ -0,0 +1,4 @@ +init +foo called +idle +foo called diff --git a/ci/expected/extern_spawn.run b/ci/expected/extern_spawn.run new file mode 100644 index 0000000000..2f8c74f6a4 --- /dev/null +++ b/ci/expected/extern_spawn.run @@ -0,0 +1,2 @@ +foo 1, 2 +foo 2, 3 diff --git a/ci/expected/locals.run b/ci/expected/locals.run new file mode 100644 index 0000000000..bf1d207698 --- /dev/null +++ b/ci/expected/locals.run @@ -0,0 +1,3 @@ +foo: local_to_foo = 1 +bar: local_to_bar = 1 +idle: local_to_idle = 1 diff --git a/ci/expected/lock-free.run b/ci/expected/lock-free.run index 56f47a0be4..18de0eca83 100644 --- a/ci/expected/lock-free.run +++ b/ci/expected/lock-free.run @@ -1,14 +1,2 @@ -GPIOA/start - GPIOA/counter = 1 -GPIOA/end -GPIOB/start - GPIOB/counter = 2 -GPIOB/end -GPIOA/start - GPIOA/counter = 3 -GPIOA/end -GPIOB/start - GPIOB/counter = 4 -GPIOB/end -GPIOA/start - GPIOA/counter = 5 + foo = 1 + bar = 2 diff --git a/ci/expected/message_passing.run b/ci/expected/message_passing.run new file mode 100644 index 0000000000..a1448d8da5 --- /dev/null +++ b/ci/expected/message_passing.run @@ -0,0 +1,3 @@ +foo 1, 1 +foo 1, 2 +foo 2, 3 diff --git a/ci/expected/multilock.run b/ci/expected/multilock.run index 10a377c5cf..dd8c1f297c 100644 --- a/ci/expected/multilock.run +++ b/ci/expected/multilock.run @@ -1,4 +1 @@ -Multiple single locks -Multiple single locks, s1: 1, s2: 1, s3: 1 -Multilock! -Multiple single locks, s1: 2, s2: 2, s3: 2 +Multiple locks, s1: 1, s2: 1, s3: 1 diff --git a/ci/expected/only-shared-access.run b/ci/expected/only-shared-access.run index 1d4eed0051..dcc73e648d 100644 --- a/ci/expected/only-shared-access.run +++ b/ci/expected/only-shared-access.run @@ -1,2 +1,2 @@ -UART1(key = 0xdeadbeef) -UART0(key = 0xdeadbeef) +bar(key = 0xdeadbeef) +foo(key = 0xdeadbeef) diff --git a/ci/expected/periodic.run b/ci/expected/periodic.run index 11414c5354..a1f894413e 100644 --- a/ci/expected/periodic.run +++ b/ci/expected/periodic.run @@ -1,3 +1,4 @@ -foo(scheduled = Instant(8000000), now = Instant(8000196)) -foo(scheduled = Instant(16000000), now = Instant(16000196)) -foo(scheduled = Instant(24000000), now = Instant(24000196)) \ No newline at end of file +foo +foo +foo +foo diff --git a/ci/expected/pool.run b/ci/expected/pool.run index 040dcee888..81f79d41e9 100644 --- a/ci/expected/pool.run +++ b/ci/expected/pool.run @@ -1,2 +1,2 @@ -bar(0x2000008c) -foo(0x20000110) +bar(0x20000088) +foo(0x2000010c) diff --git a/ci/expected/preempt.run b/ci/expected/preempt.run index 87777410c5..932b2b3200 100644 --- a/ci/expected/preempt.run +++ b/ci/expected/preempt.run @@ -1,5 +1,5 @@ -GPIOA - start - GPIOC - start - GPIOC - end - GPIOB -GPIOA - end +foo - start + baz - start + baz - end + bar +foo - end diff --git a/ci/expected/resource-user-struct.run b/ci/expected/resource-user-struct.run new file mode 100644 index 0000000000..a587a94207 --- /dev/null +++ b/ci/expected/resource-user-struct.run @@ -0,0 +1,2 @@ +UART0: shared = 1 +UART1: shared = 2 diff --git a/ci/expected/schedule.run b/ci/expected/schedule.run index 9facc71ada..1dbd445c7d 100644 --- a/ci/expected/schedule.run +++ b/ci/expected/schedule.run @@ -1,3 +1,4 @@ -init @ Instant(0) -bar @ Instant(4000236) -foo @ Instant(8000173) \ No newline at end of file +init +foo +bar +baz diff --git a/ci/expected/shared.run b/ci/expected/shared.run new file mode 100644 index 0000000000..6d3d3e43e5 --- /dev/null +++ b/ci/expected/shared.run @@ -0,0 +1 @@ +received message: 42 diff --git a/ci/expected/smallest.run b/ci/expected/smallest.run new file mode 100644 index 0000000000..e69de29bb2 diff --git a/ci/expected/spawn.run b/ci/expected/spawn.run new file mode 100644 index 0000000000..240cd18f37 --- /dev/null +++ b/ci/expected/spawn.run @@ -0,0 +1,2 @@ +init +foo diff --git a/ci/expected/static.run b/ci/expected/static.run new file mode 100644 index 0000000000..3d3f46f674 --- /dev/null +++ b/ci/expected/static.run @@ -0,0 +1,3 @@ +received message: 1 +received message: 2 +received message: 3 diff --git a/ci/expected/t-binds.run b/ci/expected/t-binds.run new file mode 100644 index 0000000000..e69de29bb2 diff --git a/ci/expected/t-cfg-resources.run b/ci/expected/t-cfg-resources.run new file mode 100644 index 0000000000..e69de29bb2 diff --git a/ci/expected/t-htask-main.run b/ci/expected/t-htask-main.run new file mode 100644 index 0000000000..e69de29bb2 diff --git a/ci/expected/t-idle-main.run b/ci/expected/t-idle-main.run new file mode 100644 index 0000000000..e69de29bb2 diff --git a/ci/expected/t-late-not-send.run b/ci/expected/t-late-not-send.run new file mode 100644 index 0000000000..e69de29bb2 diff --git a/ci/expected/t-schedule.run b/ci/expected/t-schedule.run new file mode 100644 index 0000000000..e69de29bb2 diff --git a/ci/expected/t-spawn.run b/ci/expected/t-spawn.run new file mode 100644 index 0000000000..e69de29bb2 diff --git a/examples/big-struct-opt.rs b/examples/big-struct-opt.rs index 2d0cc83d9e..bbc2535a39 100644 --- a/examples/big-struct-opt.rs +++ b/examples/big-struct-opt.rs @@ -24,6 +24,7 @@ impl BigStruct { mod app { use super::BigStruct; use core::mem::MaybeUninit; + use cortex_m_semihosting::debug; #[shared] struct Shared { @@ -41,6 +42,8 @@ mod app { &mut *cx.local.bs.as_mut_ptr() }; + debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator + ( Shared { // assign the reference so we can use the resource diff --git a/examples/binds.rs b/examples/binds.rs index 0b30af6585..56565cbec9 100644 --- a/examples/binds.rs +++ b/examples/binds.rs @@ -34,7 +34,7 @@ mod app { rtic::pend(Interrupt::UART0); - debug::exit(debug::EXIT_SUCCESS); + debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator loop { cortex_m::asm::nop(); diff --git a/examples/cancel-reschedule.rs b/examples/cancel-reschedule.rs new file mode 100644 index 0000000000..c5ef2e739d --- /dev/null +++ b/examples/cancel-reschedule.rs @@ -0,0 +1,74 @@ +//! examples/cancel-reschedule.rs + +#![deny(unsafe_code)] +#![deny(warnings)] +#![no_main] +#![no_std] + +use panic_semihosting as _; + +#[rtic::app(device = lm3s6965, dispatchers = [SSI0])] +mod app { + use cortex_m_semihosting::{debug, hprintln}; + use rtic::time::duration::*; + use systick_monotonic::Systick; + + #[monotonic(binds = SysTick, default = true)] + type MyMono = Systick<100>; // 100 Hz / 10 ms granularity + + #[shared] + struct Shared {} + + #[local] + struct Local {} + + #[init] + fn init(cx: init::Context) -> (Shared, Local, init::Monotonics) { + let systick = cx.core.SYST; + + // Initialize the monotonic + let mono = Systick::new(systick, 12_000_000); + + hprintln!("init").ok(); + + // Schedule `foo` to run 1 second in the future + foo::spawn_after(1.seconds()).unwrap(); + + ( + Shared {}, + Local {}, + init::Monotonics(mono), // Give the monotonic to RTIC + ) + } + + #[task] + fn foo(_: foo::Context) { + hprintln!("foo").ok(); + + // Schedule `bar` to run 2 seconds in the future (1 second after foo runs) + let spawn_handle = baz::spawn_after(2.seconds()).unwrap(); + bar::spawn_after(1.seconds(), spawn_handle, false).unwrap(); // Change to true + } + + #[task] + fn bar(_: bar::Context, baz_handle: baz::SpawnHandle, do_reschedule: bool) { + hprintln!("bar").ok(); + + if do_reschedule { + // Reschedule baz 2 seconds from now, instead of the original 1 second + // from now. + baz_handle.reschedule_after(2.seconds()).unwrap(); + // Or baz_handle.reschedule_at(/* time */) + } else { + // Or cancel it + baz_handle.cancel().unwrap(); + debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator + } + } + + #[task] + fn baz(_: baz::Context) { + hprintln!("baz").ok(); + debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator + } +} diff --git a/examples/capacity.rs b/examples/capacity.rs index ea1613f76e..a617269869 100644 --- a/examples/capacity.rs +++ b/examples/capacity.rs @@ -44,6 +44,6 @@ mod app { fn bar(_: bar::Context) { hprintln!("bar").unwrap(); - debug::exit(debug::EXIT_SUCCESS); + debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator } } diff --git a/examples/cfg-whole-task.rs b/examples/cfg-whole-task.rs index 3fbdb2d12b..213fe13f92 100644 --- a/examples/cfg-whole-task.rs +++ b/examples/cfg-whole-task.rs @@ -43,7 +43,7 @@ mod app { #[idle] fn idle(_: idle::Context) -> ! { - debug::exit(debug::EXIT_SUCCESS); + debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator loop { cortex_m::asm::nop(); diff --git a/examples/common.rs b/examples/common.rs new file mode 100644 index 0000000000..770a0ae557 --- /dev/null +++ b/examples/common.rs @@ -0,0 +1,101 @@ +//! examples/common.rs + +#![deny(unsafe_code)] +#![deny(warnings)] +#![no_main] +#![no_std] + +use panic_semihosting as _; + +#[rtic::app(device = lm3s6965, dispatchers = [SSI0, QEI0])] +mod app { + use cortex_m_semihosting::{debug, hprintln}; + use rtic::time::duration::*; + use systick_monotonic::Systick; // Implements the `Monotonic` trait // Time helpers, such as `N.seconds()` + + // A monotonic timer to enable scheduling in RTIC + #[monotonic(binds = SysTick, default = true)] + type MyMono = Systick<100>; // 100 Hz / 10 ms granularity + + // Resources shared between tasks + #[shared] + struct Shared { + s1: u32, + s2: i32, + } + + // Local resources to specific tasks (cannot be shared) + #[local] + struct Local { + l1: u8, + l2: i8, + } + + #[init] + fn init(cx: init::Context) -> (Shared, Local, init::Monotonics) { + let systick = cx.core.SYST; + let mono = Systick::new(systick, 12_000_000); + + // Spawn the task `foo` directly after `init` finishes + foo::spawn().unwrap(); + + // Spawn the task `bar` 1 second after `init` finishes, this is enabled + // by the `#[monotonic(..)]` above + bar::spawn_after(1.seconds()).unwrap(); + + debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator + + ( + // Initialization of shared resources + Shared { s1: 0, s2: 1 }, + // Initialization of task local resources + Local { l1: 2, l2: 3 }, + // Move the monotonic timer to the RTIC run-time, this enables + // scheduling + init::Monotonics(mono), + ) + } + + // Background task, runs whenever no other tasks are running + #[idle] + fn idle(_: idle::Context) -> ! { + loop { + continue; + } + } + + // Software task, not bound to a hardware interrupt. + // This task takes the task local resource `l1` + // The resources `s1` and `s2` are shared between all other tasks. + #[task(shared = [s1, s2], local = [l1])] + fn foo(_: foo::Context) { + // This task is only spawned once in `init`, hence this task will run + // only once + + hprintln!("foo").ok(); + } + + // Software task, also not bound to a hardware interrupt + // This task takes the task local resource `l2` + // The resources `s1` and `s2` are shared between all other tasks. + #[task(shared = [s1, s2], local = [l2])] + fn bar(_: bar::Context) { + hprintln!("bar").ok(); + + // Run `bar` once per second + bar::spawn_after(1.seconds()).unwrap(); + } + + // Hardware task, bound to a hardware interrupt + // The resources `s1` and `s2` are shared between all other tasks. + #[task(binds = UART0, priority = 3, shared = [s1, s2])] + fn uart0_interrupt(_: uart0_interrupt::Context) { + // This task is bound to the interrupt `UART0` and will run + // whenever the interrupt fires + + // Note that RTIC does NOT clear the interrupt flag, this is up to the + // user + + hprintln!("UART0 interrupt!").ok(); + } +} diff --git a/examples/declared_locals.rs b/examples/declared_locals.rs new file mode 100644 index 0000000000..52d354bc9a --- /dev/null +++ b/examples/declared_locals.rs @@ -0,0 +1,46 @@ +//! examples/declared_locals.rs + +#![deny(unsafe_code)] +#![deny(warnings)] +#![no_main] +#![no_std] + +use panic_semihosting as _; + +#[rtic::app(device = lm3s6965, dispatchers = [UART0])] +mod app { + use cortex_m_semihosting::debug; + + #[shared] + struct Shared {} + + #[local] + struct Local {} + + #[init(local = [a: u32 = 0])] + fn init(cx: init::Context) -> (Shared, Local, init::Monotonics) { + // Locals in `#[init]` have 'static lifetime + let _a: &'static mut u32 = cx.local.a; + + debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator + + (Shared {}, Local {}, init::Monotonics()) + } + + #[idle(local = [a: u32 = 0])] + fn idle(cx: idle::Context) -> ! { + // Locals in `#[idle]` have 'static lifetime + let _a: &'static mut u32 = cx.local.a; + + loop {} + } + + #[task(local = [a: u32 = 0])] + fn foo(cx: foo::Context) { + // Locals in `#[task]`s have a local lifetime + let _a: &mut u32 = cx.local.a; + + // error: explicit lifetime required in the type of `cx` + // let _a: &'static mut u32 = cx.local.a; + } +} diff --git a/examples/destructure.rs b/examples/destructure.rs index 984c9b8aab..6019c225cc 100644 --- a/examples/destructure.rs +++ b/examples/destructure.rs @@ -7,14 +7,12 @@ use panic_semihosting as _; -#[rtic::app(device = lm3s6965)] +#[rtic::app(device = lm3s6965, dispatchers = [UART0])] mod app { - use cortex_m_semihosting::hprintln; - use lm3s6965::Interrupt; + use cortex_m_semihosting::{debug, hprintln}; #[shared] struct Shared { - // Some resources to work with a: u32, b: u32, c: u32, @@ -25,27 +23,33 @@ mod app { #[init] fn init(_: init::Context) -> (Shared, Local, init::Monotonics) { - rtic::pend(Interrupt::UART0); - rtic::pend(Interrupt::UART1); + foo::spawn().unwrap(); + bar::spawn().unwrap(); (Shared { a: 0, b: 0, c: 0 }, Local {}, init::Monotonics()) } + #[idle] + fn idle(_: idle::Context) -> ! { + debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator + loop {} + } + // Direct destructure - #[task(binds = UART0, shared = [&a, &b, &c])] - fn uart0(cx: uart0::Context) { + #[task(shared = [&a, &b, &c])] + fn foo(cx: foo::Context) { let a = cx.shared.a; let b = cx.shared.b; let c = cx.shared.c; - hprintln!("UART0: a = {}, b = {}, c = {}", a, b, c).unwrap(); + hprintln!("foo: a = {}, b = {}, c = {}", a, b, c).unwrap(); } // De-structure-ing syntax - #[task(binds = UART1, shared = [&a, &b, &c])] - fn uart1(cx: uart1::Context) { - let uart1::SharedResources { a, b, c } = cx.shared; + #[task(shared = [&a, &b, &c])] + fn bar(cx: bar::Context) { + let bar::SharedResources { a, b, c } = cx.shared; - hprintln!("UART0: a = {}, b = {}, c = {}", a, b, c).unwrap(); + hprintln!("bar: a = {}, b = {}, c = {}", a, b, c).unwrap(); } } diff --git a/examples/double_schedule.rs b/examples/double_schedule.rs deleted file mode 100644 index 6f24297e62..0000000000 --- a/examples/double_schedule.rs +++ /dev/null @@ -1,46 +0,0 @@ -//! examples/double_schedule.rs - -#![deny(unsafe_code)] -#![deny(warnings)] -#![no_main] -#![no_std] - -use panic_semihosting as _; - -#[rtic::app(device = lm3s6965, dispatchers = [SSI0])] -mod app { - use dwt_systick_monotonic::DwtSystick; - use rtic::time::duration::Seconds; - - #[monotonic(binds = SysTick, default = true)] - type MyMono = DwtSystick<8_000_000>; // 8 MHz - - #[shared] - struct Shared {} - - #[local] - struct Local {} - - #[init] - fn init(cx: init::Context) -> (Shared, Local, init::Monotonics) { - task1::spawn().ok(); - - let mut dcb = cx.core.DCB; - let dwt = cx.core.DWT; - let systick = cx.core.SYST; - - let mono = DwtSystick::new(&mut dcb, dwt, systick, 8_000_000); - - (Shared {}, Local {}, init::Monotonics(mono)) - } - - #[task] - fn task1(_cx: task1::Context) { - task2::spawn_after(Seconds(1_u32)).ok(); - } - - #[task] - fn task2(_cx: task2::Context) { - task1::spawn_after(Seconds(1_u32)).ok(); - } -} diff --git a/examples/extern_binds.rs b/examples/extern_binds.rs index ce4bc17ee4..4dc6633c5d 100644 --- a/examples/extern_binds.rs +++ b/examples/extern_binds.rs @@ -40,7 +40,7 @@ mod app { rtic::pend(Interrupt::UART0); - debug::exit(debug::EXIT_SUCCESS); + debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator loop { cortex_m::asm::nop(); diff --git a/examples/extern_spawn.rs b/examples/extern_spawn.rs index d035fe7fa6..7f9b5a5f9b 100644 --- a/examples/extern_spawn.rs +++ b/examples/extern_spawn.rs @@ -12,7 +12,7 @@ use panic_semihosting as _; fn foo(_c: app::foo::Context, x: i32, y: u32) { hprintln!("foo {}, {}", x, y).unwrap(); if x == 2 { - debug::exit(debug::EXIT_SUCCESS); + debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator } app::foo::spawn(2, 3).unwrap(); } diff --git a/examples/generics.rs b/examples/generics.rs index b2c59a08a7..72b861ba91 100644 --- a/examples/generics.rs +++ b/examples/generics.rs @@ -39,7 +39,7 @@ mod app { rtic::pend(Interrupt::UART1); - debug::exit(debug::EXIT_SUCCESS); + debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator } #[task(binds = UART1, priority = 2, shared = [shared], local = [state: u32 = 0])] diff --git a/examples/hardware.rs b/examples/hardware.rs index 5dff82221c..60632247fb 100644 --- a/examples/hardware.rs +++ b/examples/hardware.rs @@ -37,7 +37,7 @@ mod app { rtic::pend(Interrupt::UART0); - debug::exit(debug::EXIT_SUCCESS); + debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator loop { cortex_m::asm::nop(); diff --git a/examples/idle.rs b/examples/idle.rs index 34c861b9ac..55d6b15352 100644 --- a/examples/idle.rs +++ b/examples/idle.rs @@ -31,7 +31,7 @@ mod app { hprintln!("idle").unwrap(); - debug::exit(debug::EXIT_SUCCESS); + debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator loop { cortex_m::asm::nop(); diff --git a/examples/init.rs b/examples/init.rs index 97e3c513f7..b8a5bc5b98 100644 --- a/examples/init.rs +++ b/examples/init.rs @@ -34,7 +34,7 @@ mod app { hprintln!("init").unwrap(); - debug::exit(debug::EXIT_SUCCESS); + debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator (Shared {}, Local {}, init::Monotonics()) } diff --git a/examples/locals.rs b/examples/locals.rs new file mode 100644 index 0000000000..eeb7fb75d1 --- /dev/null +++ b/examples/locals.rs @@ -0,0 +1,86 @@ +//! examples/locals.rs + +#![deny(unsafe_code)] +#![deny(warnings)] +#![no_main] +#![no_std] + +use panic_semihosting as _; + +#[rtic::app(device = lm3s6965, dispatchers = [UART0, UART1])] +mod app { + use cortex_m_semihosting::{debug, hprintln}; + + #[shared] + struct Shared {} + + #[local] + struct Local { + local_to_foo: i64, + local_to_bar: i64, + local_to_idle: i64, + } + + // `#[init]` cannot access locals from the `#[local]` struct as they are initialized here. + #[init] + fn init(_: init::Context) -> (Shared, Local, init::Monotonics) { + foo::spawn().unwrap(); + bar::spawn().unwrap(); + + ( + Shared {}, + // initial values for the `#[local]` resources + Local { + local_to_foo: 0, + local_to_bar: 0, + local_to_idle: 0, + }, + init::Monotonics(), + ) + } + + // `local_to_idle` can only be accessed from this context + #[idle(local = [local_to_idle])] + fn idle(cx: idle::Context) -> ! { + let local_to_idle = cx.local.local_to_idle; + *local_to_idle += 1; + + hprintln!("idle: local_to_idle = {}", local_to_idle).unwrap(); + + debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator + + // error: no `local_to_foo` field in `idle::LocalResources` + // _cx.local.local_to_foo += 1; + + // error: no `local_to_bar` field in `idle::LocalResources` + // _cx.local.local_to_bar += 1; + + loop { + cortex_m::asm::nop(); + } + } + + // `local_to_foo` can only be accessed from this context + #[task(local = [local_to_foo])] + fn foo(cx: foo::Context) { + let local_to_foo = cx.local.local_to_foo; + *local_to_foo += 1; + + // error: no `local_to_bar` field in `foo::LocalResources` + // cx.local.local_to_bar += 1; + + hprintln!("foo: local_to_foo = {}", local_to_foo).unwrap(); + } + + // `shared` can only be accessed from this context + #[task(local = [local_to_bar])] + fn bar(cx: bar::Context) { + let local_to_bar = cx.local.local_to_bar; + *local_to_bar += 1; + + // error: no `local_to_foo` field in `bar::LocalResources` + // cx.local.local_to_foo += 1; + + hprintln!("bar: local_to_bar = {}", local_to_bar).unwrap(); + } +} diff --git a/examples/lock-free.rs b/examples/lock-free.rs index db74c7d8b0..ea6ff1bf37 100644 --- a/examples/lock-free.rs +++ b/examples/lock-free.rs @@ -7,10 +7,9 @@ use panic_semihosting as _; -#[rtic::app(device = lm3s6965)] +#[rtic::app(device = lm3s6965, dispatchers = [GPIOA])] mod app { use cortex_m_semihosting::{debug, hprintln}; - use lm3s6965::Interrupt; #[shared] struct Shared { @@ -23,38 +22,28 @@ mod app { #[init] fn init(_: init::Context) -> (Shared, Local, init::Monotonics) { - rtic::pend(Interrupt::GPIOA); + foo::spawn().unwrap(); (Shared { counter: 0 }, Local {}, init::Monotonics()) } - #[task(binds = GPIOA, shared = [counter])] // <- same priority - fn gpioa(c: gpioa::Context) { - hprintln!("GPIOA/start").unwrap(); - rtic::pend(Interrupt::GPIOB); + #[task(shared = [counter])] // <- same priority + fn foo(c: foo::Context) { + bar::spawn().unwrap(); *c.shared.counter += 1; // <- no lock API required let counter = *c.shared.counter; - hprintln!(" GPIOA/counter = {}", counter).unwrap(); - - if counter == 5 { - debug::exit(debug::EXIT_SUCCESS); - } - hprintln!("GPIOA/end").unwrap(); + hprintln!(" foo = {}", counter).unwrap(); } - #[task(binds = GPIOB, shared = [counter])] // <- same priority - fn gpiob(c: gpiob::Context) { - hprintln!("GPIOB/start").unwrap(); - rtic::pend(Interrupt::GPIOA); + #[task(shared = [counter])] // <- same priority + fn bar(c: bar::Context) { + foo::spawn().unwrap(); *c.shared.counter += 1; // <- no lock API required let counter = *c.shared.counter; - hprintln!(" GPIOB/counter = {}", counter).unwrap(); + hprintln!(" bar = {}", counter).unwrap(); - if counter == 5 { - debug::exit(debug::EXIT_SUCCESS); - } - hprintln!("GPIOB/end").unwrap(); + debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator } } diff --git a/examples/lock.rs b/examples/lock.rs index aeadd295b6..f1a16968ce 100644 --- a/examples/lock.rs +++ b/examples/lock.rs @@ -7,10 +7,9 @@ use panic_semihosting as _; -#[rtic::app(device = lm3s6965)] +#[rtic::app(device = lm3s6965, dispatchers = [GPIOA, GPIOB, GPIOC])] mod app { use cortex_m_semihosting::{debug, hprintln}; - use lm3s6965::Interrupt; #[shared] struct Shared { @@ -22,14 +21,14 @@ mod app { #[init] fn init(_: init::Context) -> (Shared, Local, init::Monotonics) { - rtic::pend(Interrupt::GPIOA); + foo::spawn().unwrap(); (Shared { shared: 0 }, Local {}, init::Monotonics()) } // when omitted priority is assumed to be `1` - #[task(binds = GPIOA, shared = [shared])] - fn gpioa(mut c: gpioa::Context) { + #[task(shared = [shared])] + fn foo(mut c: foo::Context) { hprintln!("A").unwrap(); // the lower priority task requires a critical section to access the data @@ -37,24 +36,24 @@ mod app { // data can only be modified within this critical section (closure) *shared += 1; - // GPIOB will *not* run right now due to the critical section - rtic::pend(Interrupt::GPIOB); + // bar will *not* run right now due to the critical section + bar::spawn().unwrap(); hprintln!("B - shared = {}", *shared).unwrap(); - // GPIOC does not contend for `shared` so it's allowed to run now - rtic::pend(Interrupt::GPIOC); + // baz does not contend for `shared` so it's allowed to run now + baz::spawn().unwrap(); }); - // critical section is over: GPIOB can now start + // critical section is over: bar can now start hprintln!("E").unwrap(); - debug::exit(debug::EXIT_SUCCESS); + debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator } - #[task(binds = GPIOB, priority = 2, shared = [shared])] - fn gpiob(mut c: gpiob::Context) { + #[task(priority = 2, shared = [shared])] + fn bar(mut c: bar::Context) { // the higher priority task does still need a critical section let shared = c.shared.shared.lock(|shared| { *shared += 1; @@ -65,8 +64,8 @@ mod app { hprintln!("D - shared = {}", shared).unwrap(); } - #[task(binds = GPIOC, priority = 3)] - fn gpioc(_: gpioc::Context) { + #[task(priority = 3)] + fn baz(_: baz::Context) { hprintln!("C").unwrap(); } } diff --git a/examples/message.rs b/examples/message.rs index 7318d4b770..76c5675aaa 100644 --- a/examples/message.rs +++ b/examples/message.rs @@ -44,7 +44,7 @@ mod app { hprintln!("baz({}, {})", x, y).unwrap(); if x + y > 4 { - debug::exit(debug::EXIT_SUCCESS); + debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator } foo::spawn().unwrap(); diff --git a/examples/spawn2.rs b/examples/message_passing.rs similarity index 67% rename from examples/spawn2.rs rename to examples/message_passing.rs index ed285b70cf..ffa9537127 100644 --- a/examples/spawn2.rs +++ b/examples/message_passing.rs @@ -1,4 +1,4 @@ -//! examples/spawn2.rs +//! examples/message_passing.rs #![deny(unsafe_code)] #![deny(warnings)] @@ -19,23 +19,19 @@ mod app { #[init] fn init(_: init::Context) -> (Shared, Local, init::Monotonics) { + foo::spawn(1, 1).unwrap(); foo::spawn(1, 2).unwrap(); + foo::spawn(2, 3).unwrap(); + assert!(foo::spawn(1, 4).is_err()); // The capacity of `foo` is reached (Shared {}, Local {}, init::Monotonics()) } - #[task] + #[task(capacity = 3)] fn foo(_c: foo::Context, x: i32, y: u32) { hprintln!("foo {}, {}", x, y).unwrap(); if x == 2 { - debug::exit(debug::EXIT_SUCCESS); + debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator } - foo2::spawn(2).unwrap(); - } - - #[task] - fn foo2(_c: foo2::Context, x: i32) { - hprintln!("foo2 {}", x).unwrap(); - foo::spawn(x, 0).unwrap(); } } diff --git a/examples/multilock.rs b/examples/multilock.rs index 7d8d7d246a..d99bae695e 100644 --- a/examples/multilock.rs +++ b/examples/multilock.rs @@ -1,6 +1,4 @@ //! examples/mutlilock.rs -//! -//! The multi-lock feature example. #![deny(unsafe_code)] #![deny(warnings)] @@ -9,10 +7,9 @@ use panic_semihosting as _; -#[rtic::app(device = lm3s6965)] +#[rtic::app(device = lm3s6965, dispatchers = [GPIOA])] mod app { use cortex_m_semihosting::{debug, hprintln}; - use lm3s6965::Interrupt; #[shared] struct Shared { @@ -26,7 +23,7 @@ mod app { #[init] fn init(_: init::Context) -> (Shared, Local, init::Monotonics) { - rtic::pend(Interrupt::GPIOA); + locks::spawn().unwrap(); ( Shared { @@ -40,47 +37,20 @@ mod app { } // when omitted priority is assumed to be `1` - #[task(binds = GPIOA, shared = [shared1, shared2, shared3])] + #[task(shared = [shared1, shared2, shared3])] fn locks(c: locks::Context) { - let mut s1 = c.shared.shared1; - let mut s2 = c.shared.shared2; - let mut s3 = c.shared.shared3; - - hprintln!("Multiple single locks").unwrap(); - s1.lock(|s1| { - s2.lock(|s2| { - s3.lock(|s3| { - *s1 += 1; - *s2 += 1; - *s3 += 1; - - hprintln!( - "Multiple single locks, s1: {}, s2: {}, s3: {}", - *s1, - *s2, - *s3 - ) - .unwrap(); - }) - }) - }); - - hprintln!("Multilock!").unwrap(); + let s1 = c.shared.shared1; + let s2 = c.shared.shared2; + let s3 = c.shared.shared3; (s1, s2, s3).lock(|s1, s2, s3| { *s1 += 1; *s2 += 1; *s3 += 1; - hprintln!( - "Multiple single locks, s1: {}, s2: {}, s3: {}", - *s1, - *s2, - *s3 - ) - .unwrap(); + hprintln!("Multiple locks, s1: {}, s2: {}, s3: {}", *s1, *s2, *s3).unwrap(); }); - debug::exit(debug::EXIT_SUCCESS); + debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator } } diff --git a/examples/not-sync.rs b/examples/not-sync.rs index 1510e5041d..aa79ad5626 100644 --- a/examples/not-sync.rs +++ b/examples/not-sync.rs @@ -30,7 +30,7 @@ mod app { #[init] fn init(_: init::Context) -> (Shared, Local, init::Monotonics) { - debug::exit(debug::EXIT_SUCCESS); + debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator ( Shared { diff --git a/examples/only-shared-access.rs b/examples/only-shared-access.rs index e3f1dbd312..8b0a77ef8c 100644 --- a/examples/only-shared-access.rs +++ b/examples/only-shared-access.rs @@ -1,4 +1,4 @@ -//! examples/static.rs +//! examples/only-shared-access.rs #![deny(unsafe_code)] #![deny(warnings)] @@ -7,10 +7,9 @@ use panic_semihosting as _; -#[rtic::app(device = lm3s6965)] +#[rtic::app(device = lm3s6965, dispatchers = [UART0, UART1])] mod app { use cortex_m_semihosting::{debug, hprintln}; - use lm3s6965::Interrupt; #[shared] struct Shared { @@ -22,22 +21,22 @@ mod app { #[init] fn init(_: init::Context) -> (Shared, Local, init::Monotonics) { - rtic::pend(Interrupt::UART0); - rtic::pend(Interrupt::UART1); + foo::spawn().unwrap(); + bar::spawn().unwrap(); (Shared { key: 0xdeadbeef }, Local {}, init::Monotonics()) } - #[task(binds = UART0, shared = [&key])] - fn uart0(cx: uart0::Context) { + #[task(shared = [&key])] + fn foo(cx: foo::Context) { let key: &u32 = cx.shared.key; - hprintln!("UART0(key = {:#x})", key).unwrap(); + hprintln!("foo(key = {:#x})", key).unwrap(); - debug::exit(debug::EXIT_SUCCESS); + debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator } - #[task(binds = UART1, priority = 2, shared = [&key])] - fn uart1(cx: uart1::Context) { - hprintln!("UART1(key = {:#x})", cx.shared.key).unwrap(); + #[task(priority = 2, shared = [&key])] + fn bar(cx: bar::Context) { + hprintln!("bar(key = {:#x})", cx.shared.key).unwrap(); } } diff --git a/examples/periodic.rs b/examples/periodic.rs index b18688393f..74c240c25e 100644 --- a/examples/periodic.rs +++ b/examples/periodic.rs @@ -10,11 +10,12 @@ use panic_semihosting as _; // NOTE: does NOT work on QEMU! #[rtic::app(device = lm3s6965, dispatchers = [SSI0])] mod app { - use dwt_systick_monotonic::DwtSystick; - use rtic::time::duration::Seconds; + use cortex_m_semihosting::{debug, hprintln}; + use rtic::time::duration::*; + use systick_monotonic::Systick; #[monotonic(binds = SysTick, default = true)] - type MyMono = DwtSystick<8_000_000>; // 8 MHz + type MyMono = Systick<100>; // 100 Hz / 10 ms granularity #[shared] struct Shared {} @@ -24,20 +25,25 @@ mod app { #[init] fn init(cx: init::Context) -> (Shared, Local, init::Monotonics) { - let mut dcb = cx.core.DCB; - let dwt = cx.core.DWT; let systick = cx.core.SYST; - let mono = DwtSystick::new(&mut dcb, dwt, systick, 8_000_000); + let mono = Systick::new(systick, 12_000_000); - foo::spawn_after(Seconds(1_u32)).unwrap(); + foo::spawn_after(1.seconds()).unwrap(); (Shared {}, Local {}, init::Monotonics(mono)) } - #[task] - fn foo(_cx: foo::Context) { - // Periodic - foo::spawn_after(Seconds(1_u32)).unwrap(); + #[task(local = [cnt: u32 = 0])] + fn foo(cx: foo::Context) { + hprintln!("foo").ok(); + *cx.local.cnt += 1; + + if *cx.local.cnt == 4 { + debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator + } + + // Periodic ever 1 seconds + foo::spawn_after(1.seconds()).unwrap(); } } diff --git a/examples/peripherals-taken.rs b/examples/peripherals-taken.rs index cb90319ee7..d542c0e64d 100644 --- a/examples/peripherals-taken.rs +++ b/examples/peripherals-taken.rs @@ -18,7 +18,7 @@ mod app { #[init] fn init(_: init::Context) -> (Shared, Local, init::Monotonics) { assert!(cortex_m::Peripherals::take().is_none()); - debug::exit(debug::EXIT_SUCCESS); + debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator (Shared {}, Local {}, init::Monotonics()) } diff --git a/examples/pool.rs b/examples/pool.rs index 010ee44ecc..d59bd91607 100644 --- a/examples/pool.rs +++ b/examples/pool.rs @@ -61,7 +61,7 @@ mod app { // explicitly return the block to the pool drop(x); - debug::exit(debug::EXIT_SUCCESS); + debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator } #[task(priority = 2)] diff --git a/examples/preempt.rs b/examples/preempt.rs index 8d9f9ead47..d0c8cc7d3f 100644 --- a/examples/preempt.rs +++ b/examples/preempt.rs @@ -6,10 +6,9 @@ use panic_semihosting as _; use rtic::app; -#[app(device = lm3s6965)] +#[app(device = lm3s6965, dispatchers = [SSI0, QEI0])] mod app { use cortex_m_semihosting::{debug, hprintln}; - use lm3s6965::Interrupt; #[shared] struct Shared {} @@ -19,28 +18,28 @@ mod app { #[init] fn init(_: init::Context) -> (Shared, Local, init::Monotonics) { - rtic::pend(Interrupt::GPIOA); + foo::spawn().unwrap(); (Shared {}, Local {}, init::Monotonics()) } - #[task(binds = GPIOA, priority = 1)] - fn gpioa(_: gpioa::Context) { - hprintln!("GPIOA - start").unwrap(); - rtic::pend(Interrupt::GPIOC); - hprintln!("GPIOA - end").unwrap(); - debug::exit(debug::EXIT_SUCCESS); + #[task(priority = 1)] + fn foo(_: foo::Context) { + hprintln!("foo - start").unwrap(); + baz::spawn().unwrap(); + hprintln!("foo - end").unwrap(); + debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator } - #[task(binds = GPIOB, priority = 2)] - fn gpiob(_: gpiob::Context) { - hprintln!(" GPIOB").unwrap(); + #[task(priority = 2)] + fn bar(_: bar::Context) { + hprintln!(" bar").unwrap(); } - #[task(binds = GPIOC, priority = 2)] - fn gpioc(_: gpioc::Context) { - hprintln!(" GPIOC - start").unwrap(); - rtic::pend(Interrupt::GPIOB); - hprintln!(" GPIOC - end").unwrap(); + #[task(priority = 2)] + fn baz(_: baz::Context) { + hprintln!(" baz - start").unwrap(); + bar::spawn().unwrap(); + hprintln!(" baz - end").unwrap(); } } diff --git a/examples/ramfunc.rs b/examples/ramfunc.rs index ecff85300b..54acd7e84c 100644 --- a/examples/ramfunc.rs +++ b/examples/ramfunc.rs @@ -36,7 +36,7 @@ mod app { fn foo(_: foo::Context) { hprintln!("foo").unwrap(); - debug::exit(debug::EXIT_SUCCESS); + debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator } // run this task from RAM diff --git a/examples/resource-user-struct.rs b/examples/resource-user-struct.rs index 1ebaa59eec..ae1918d05d 100644 --- a/examples/resource-user-struct.rs +++ b/examples/resource-user-struct.rs @@ -39,7 +39,7 @@ mod app { // `shared` cannot be accessed from this context #[idle] fn idle(_cx: idle::Context) -> ! { - debug::exit(debug::EXIT_SUCCESS); + debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator // error: no `shared` field in `idle::Context` // _cx.shared.shared += 1; diff --git a/examples/resource.rs b/examples/resource.rs deleted file mode 100644 index dca0b37034..0000000000 --- a/examples/resource.rs +++ /dev/null @@ -1,81 +0,0 @@ -//! examples/resource.rs - -#![deny(unsafe_code)] -#![deny(warnings)] -#![no_main] -#![no_std] - -use panic_semihosting as _; - -#[rtic::app(device = lm3s6965)] -mod app { - use cortex_m_semihosting::{debug, hprintln}; - use lm3s6965::Interrupt; - - #[shared] - struct Shared {} - - #[local] - struct Local { - local_to_uart0: i64, - local_to_uart1: i64, - } - - #[init] - fn init(_: init::Context) -> (Shared, Local, init::Monotonics) { - rtic::pend(Interrupt::UART0); - rtic::pend(Interrupt::UART1); - - ( - Shared {}, - // initial values for the `#[local]` resources - Local { - local_to_uart0: 0, - local_to_uart1: 0, - }, - init::Monotonics(), - ) - } - - // `#[local]` resources cannot be accessed from this context - #[idle] - fn idle(_cx: idle::Context) -> ! { - debug::exit(debug::EXIT_SUCCESS); - - // error: no `local` field in `idle::Context` - // _cx.local.local_to_uart0 += 1; - - // error: no `local` field in `idle::Context` - // _cx.local.local_to_uart1 += 1; - - loop { - cortex_m::asm::nop(); - } - } - - // `local_to_uart0` can only be accessed from this context - // defaults to priority 1 - #[task(binds = UART0, local = [local_to_uart0])] - fn uart0(cx: uart0::Context) { - *cx.local.local_to_uart0 += 1; - let local_to_uart0 = cx.local.local_to_uart0; - - // error: no `local_to_uart1` field in `uart0::LocalResources` - // cx.local.local_to_uart1 += 1; - - hprintln!("UART0: local_to_uart0 = {}", local_to_uart0).unwrap(); - } - - // `shared` can only be accessed from this context - // explicitly set to priority 2 - #[task(binds = UART1, local = [local_to_uart1], priority = 2)] - fn uart1(cx: uart1::Context) { - *cx.local.local_to_uart1 += 1; - let local_to_uart1 = cx.local.local_to_uart1; - - // error: no `local_to_uart0` field in `uart1::LocalResources` - // cx.local.local_to_uart0 += 1; - - hprintln!("UART1: local_to_uart1 = {}", local_to_uart1).unwrap(); - } -} diff --git a/examples/schedule.rs b/examples/schedule.rs index f62f24a7eb..669c67c967 100644 --- a/examples/schedule.rs +++ b/examples/schedule.rs @@ -7,17 +7,14 @@ use panic_semihosting as _; -// NOTE: does NOT work on QEMU! #[rtic::app(device = lm3s6965, dispatchers = [SSI0])] mod app { - use cortex_m_semihosting::hprintln; - use dwt_systick_monotonic::DwtSystick; - use rtic::time::duration::Seconds; - - const MONO_HZ: u32 = 8_000_000; // 8 MHz + use cortex_m_semihosting::{debug, hprintln}; + use rtic::time::duration::*; + use systick_monotonic::Systick; #[monotonic(binds = SysTick, default = true)] - type MyMono = DwtSystick; + type MyMono = Systick<100>; // 100 Hz / 10 ms granularity #[shared] struct Shared {} @@ -27,30 +24,42 @@ mod app { #[init] fn init(cx: init::Context) -> (Shared, Local, init::Monotonics) { - let mut dcb = cx.core.DCB; - let dwt = cx.core.DWT; let systick = cx.core.SYST; - let mono = DwtSystick::new(&mut dcb, dwt, systick, 8_000_000); + // Initialize the monotonic + let mono = Systick::new(systick, 12_000_000); hprintln!("init").ok(); // Schedule `foo` to run 1 second in the future - foo::spawn_after(Seconds(1_u32)).ok(); + foo::spawn_after(1.seconds()).unwrap(); - // Schedule `bar` to run 2 seconds in the future - bar::spawn_after(Seconds(2_u32)).ok(); - - (Shared {}, Local {}, init::Monotonics(mono)) + ( + Shared {}, + Local {}, + init::Monotonics(mono), // Give the monotonic to RTIC + ) } #[task] fn foo(_: foo::Context) { hprintln!("foo").ok(); + + // Schedule `bar` to run 2 seconds in the future (1 second after foo runs) + bar::spawn_after(1.seconds()).unwrap(); } #[task] fn bar(_: bar::Context) { hprintln!("bar").ok(); + + // Schedule `baz` to run 1 seconds from now, but with a specific time instant. + baz::spawn_at(monotonics::now() + 1.seconds()).unwrap(); + } + + #[task] + fn baz(_: baz::Context) { + hprintln!("baz").ok(); + debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator } } diff --git a/examples/shared.rs b/examples/shared.rs index 9585c3885e..d87dca5263 100644 --- a/examples/shared.rs +++ b/examples/shared.rs @@ -36,7 +36,7 @@ mod app { if let Some(byte) = c.shared.c.lock(|c| c.dequeue()) { hprintln!("received message: {}", byte).unwrap(); - debug::exit(debug::EXIT_SUCCESS); + debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator } else { rtic::pend(Interrupt::UART0); } diff --git a/examples/smallest.rs b/examples/smallest.rs index 31750e257a..b121fcff88 100644 --- a/examples/smallest.rs +++ b/examples/smallest.rs @@ -8,6 +8,8 @@ use rtic::app; #[app(device = lm3s6965)] mod app { + use cortex_m_semihosting::debug; + #[shared] struct Shared {} @@ -16,6 +18,7 @@ mod app { #[init] fn init(_: init::Context) -> (Shared, Local, init::Monotonics) { + debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator (Shared {}, Local {}, init::Monotonics()) } } diff --git a/examples/spawn.rs b/examples/spawn.rs index 435cdf5697..2db1ab8a28 100644 --- a/examples/spawn.rs +++ b/examples/spawn.rs @@ -19,17 +19,16 @@ mod app { #[init] fn init(_: init::Context) -> (Shared, Local, init::Monotonics) { - foo::spawn(1, 2).unwrap(); + hprintln!("init").unwrap(); + foo::spawn().unwrap(); (Shared {}, Local {}, init::Monotonics()) } - #[task()] - fn foo(_c: foo::Context, x: i32, y: u32) { - hprintln!("foo {}, {}", x, y).unwrap(); - if x == 2 { - debug::exit(debug::EXIT_SUCCESS); - } - foo::spawn(2, 3).unwrap(); + #[task] + fn foo(_: foo::Context) { + hprintln!("foo").unwrap(); + + debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator } } diff --git a/examples/static.rs b/examples/static.rs index 0ea5d2df80..c9aa6046b5 100644 --- a/examples/static.rs +++ b/examples/static.rs @@ -7,45 +7,53 @@ use panic_semihosting as _; -#[rtic::app(device = lm3s6965)] +#[rtic::app(device = lm3s6965, dispatchers = [UART0])] mod app { - use cortex_m_semihosting::{debug, hprintln}; use heapless::spsc::{Consumer, Producer, Queue}; - use lm3s6965::Interrupt; #[shared] - struct Shared { + struct Shared {} + + #[local] + struct Local { p: Producer<'static, u32, 5>, c: Consumer<'static, u32, 5>, } - #[local] - struct Local {} - #[init(local = [q: Queue = Queue::new()])] fn init(cx: init::Context) -> (Shared, Local, init::Monotonics) { + // q has 'static life-time so after the split and return of `init` + // it will continue to exist and be allocated let (p, c) = cx.local.q.split(); - (Shared { p, c }, Local {}, init::Monotonics()) + foo::spawn().unwrap(); + + (Shared {}, Local { p, c }, init::Monotonics()) } - #[idle(shared = [c])] - fn idle(mut c: idle::Context) -> ! { + #[idle(local = [c])] + fn idle(c: idle::Context) -> ! { loop { - if let Some(byte) = c.shared.c.lock(|c| c.dequeue()) { - hprintln!("received message: {}", byte).unwrap(); + // Lock-free access to the same underlying queue! + if let Some(data) = c.local.c.dequeue() { + hprintln!("received message: {}", data).unwrap(); - debug::exit(debug::EXIT_SUCCESS); - } else { - rtic::pend(Interrupt::UART0); + // Run foo until data + if data == 3 { + debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator + } else { + foo::spawn().unwrap(); + } } } } - #[task(binds = UART0, shared = [p], local = [kalle: u32 = 0])] - fn uart0(mut c: uart0::Context) { - *c.local.kalle += 1; - c.shared.p.lock(|p| p.enqueue(42).unwrap()); + #[task(local = [p, state: u32 = 0])] + fn foo(c: foo::Context) { + *c.local.state += 1; + + // Lock-free access to the same underlying queue! + c.local.p.enqueue(*c.local.state).unwrap(); } } diff --git a/examples/t-binds.rs b/examples/t-binds.rs index 2c405ea035..12479c0ad4 100644 --- a/examples/t-binds.rs +++ b/examples/t-binds.rs @@ -9,6 +9,8 @@ use panic_semihosting as _; #[rtic::app(device = lm3s6965)] mod app { + use cortex_m_semihosting::debug; + #[shared] struct Shared {} @@ -17,6 +19,8 @@ mod app { #[init] fn init(_: init::Context) -> (Shared, Local, init::Monotonics) { + debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator + (Shared {}, Local {}, init::Monotonics()) } diff --git a/examples/t-cfg-resources.rs b/examples/t-cfg-resources.rs index 3b06f0e097..99c97ba5e1 100644 --- a/examples/t-cfg-resources.rs +++ b/examples/t-cfg-resources.rs @@ -7,6 +7,8 @@ use panic_semihosting as _; #[rtic::app(device = lm3s6965)] mod app { + use cortex_m_semihosting::debug; + #[shared] struct Shared { // A conditionally compiled resource behind feature_x @@ -19,6 +21,8 @@ mod app { #[init] fn init(_: init::Context) -> (Shared, Local, init::Monotonics) { + debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator + ( Shared { #[cfg(feature = "feature_x")] diff --git a/examples/t-htask-main.rs b/examples/t-htask-main.rs index 39404322a3..37189faf76 100644 --- a/examples/t-htask-main.rs +++ b/examples/t-htask-main.rs @@ -24,6 +24,6 @@ mod app { #[task(binds = UART0)] fn taskmain(_: taskmain::Context) { - debug::exit(debug::EXIT_SUCCESS); + debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator } } diff --git a/examples/t-idle-main.rs b/examples/t-idle-main.rs index c649a97389..1adc9bf044 100644 --- a/examples/t-idle-main.rs +++ b/examples/t-idle-main.rs @@ -22,7 +22,7 @@ mod app { #[idle] fn taskmain(_: taskmain::Context) -> ! { - debug::exit(debug::EXIT_SUCCESS); + debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator loop { cortex_m::asm::nop(); } diff --git a/examples/t-late-not-send.rs b/examples/t-late-not-send.rs index 7408a1eb23..06aedaa2ee 100644 --- a/examples/t-late-not-send.rs +++ b/examples/t-late-not-send.rs @@ -15,6 +15,7 @@ pub struct NotSend { mod app { use super::NotSend; use core::marker::PhantomData; + use cortex_m_semihosting::debug; #[shared] struct Shared { @@ -39,6 +40,7 @@ mod app { #[idle(shared = [x, y])] fn idle(_: idle::Context) -> ! { + debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator loop { cortex_m::asm::nop(); } diff --git a/examples/t-schedule.rs b/examples/t-schedule.rs index 6708c68977..5530ec6a8b 100644 --- a/examples/t-schedule.rs +++ b/examples/t-schedule.rs @@ -9,11 +9,12 @@ use panic_semihosting as _; #[rtic::app(device = lm3s6965, dispatchers = [SSI0])] mod app { - use dwt_systick_monotonic::DwtSystick; + use cortex_m_semihosting::debug; use rtic::time::duration::Seconds; + use systick_monotonic::Systick; #[monotonic(binds = SysTick, default = true)] - type MyMono = DwtSystick<8_000_000>; // 8 MHz + type MyMono = Systick<100>; // 100 Hz / 10 ms granularity #[shared] struct Shared {} @@ -23,12 +24,17 @@ mod app { #[init] fn init(cx: init::Context) -> (Shared, Local, init::Monotonics) { - let mut dcb = cx.core.DCB; - let dwt = cx.core.DWT; let systick = cx.core.SYST; - let mono = DwtSystick::new(&mut dcb, dwt, systick, 8_000_000); + let mono = Systick::new(systick, 12_000_000); + debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator + + (Shared {}, Local {}, init::Monotonics(mono)) + } + + #[idle] + fn idle(_: idle::Context) -> ! { // Task without message passing // Not default @@ -120,11 +126,6 @@ mod app { let handle: Result = baz::spawn_after(Seconds(1_u32), 0, 1); let _: Result<(u32, u32), ()> = handle.unwrap().cancel(); - (Shared {}, Local {}, init::Monotonics(mono)) - } - - #[idle] - fn idle(_: idle::Context) -> ! { loop { cortex_m::asm::nop(); } diff --git a/examples/t-spawn.rs b/examples/t-spawn.rs index 0f98592e0f..2bd771d7f6 100644 --- a/examples/t-spawn.rs +++ b/examples/t-spawn.rs @@ -9,6 +9,8 @@ use panic_semihosting as _; #[rtic::app(device = lm3s6965, dispatchers = [SSI0])] mod app { + use cortex_m_semihosting::debug; + #[shared] struct Shared {} @@ -21,6 +23,8 @@ mod app { let _: Result<(), u32> = bar::spawn(0); let _: Result<(), (u32, u32)> = baz::spawn(0, 1); + debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator + (Shared {}, Local {}, init::Monotonics()) } diff --git a/examples/task.rs b/examples/task.rs index bec7b1ab40..2c53aa2359 100644 --- a/examples/task.rs +++ b/examples/task.rs @@ -46,7 +46,7 @@ mod app { fn bar(_: bar::Context) { hprintln!("bar").unwrap(); - debug::exit(debug::EXIT_SUCCESS); + debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator } #[task(priority = 2)] diff --git a/macros/src/codegen/module.rs b/macros/src/codegen/module.rs index 17bc34d363..6011c9bc04 100644 --- a/macros/src/codegen/module.rs +++ b/macros/src/codegen/module.rs @@ -270,6 +270,7 @@ pub fn codegen( let m_ident = util::monotonic_ident(&monotonic_name); let m_isr = &monotonic.args.binds; let enum_ = util::interrupt_ident(); + let spawn_handle_string = format!("{}::SpawnHandle", m.to_string()); let (enable_interrupt, pend) = if &*m_isr.to_string() == "SysTick" { ( @@ -320,6 +321,12 @@ pub fn codegen( marker: u32, } + impl core::fmt::Debug for #internal_spawn_handle_ident { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_struct(#spawn_handle_string).finish() + } + } + #(#cfgs)* impl #internal_spawn_handle_ident { pub fn cancel(self) -> Result<#ty, ()> { diff --git a/xtask/Cargo.toml b/xtask/Cargo.toml index fa7fd17940..33e6b3ad60 100644 --- a/xtask/Cargo.toml +++ b/xtask/Cargo.toml @@ -7,4 +7,3 @@ edition = "2018" anyhow = "1.0.43" os_pipe = "0.9.2" structopt = "0.3.22" -tempdir = "0.3.7" \ No newline at end of file diff --git a/xtask/src/build.rs b/xtask/src/build.rs index a8c19aac73..904e9177c0 100644 --- a/xtask/src/build.rs +++ b/xtask/src/build.rs @@ -38,14 +38,14 @@ pub fn build_hexpath( .map_err(|e| anyhow::Error::new(TestRunError::PathConversionError(e))) } -pub fn compare_builds(file_1: String, file_2: String) -> anyhow::Result<()> { - let buf_1 = std::fs::read_to_string(file_1.clone())?; - let buf_2 = std::fs::read_to_string(file_2.clone())?; +pub fn compare_builds(expected: String, got: String) -> anyhow::Result<()> { + let buf_1 = std::fs::read_to_string(expected.clone())?; + let buf_2 = std::fs::read_to_string(got.clone())?; if buf_1 != buf_2 { return Err(anyhow::Error::new(TestRunError::FileCmpError { - file_1, - file_2, + expected, + got, })); } diff --git a/xtask/src/command.rs b/xtask/src/command.rs index 8bf49849de..d94a7ab3d0 100644 --- a/xtask/src/command.rs +++ b/xtask/src/command.rs @@ -1,14 +1,16 @@ -use crate::RunResult; +use crate::{RunResult, TestRunError}; use core::fmt; use os_pipe::pipe; use std::{fs::File, io::Read, path::Path, process::Command}; +#[allow(dead_code)] #[derive(Debug, Clone, Copy, PartialEq)] pub enum BuildMode { Release, Debug, } +#[derive(Debug)] pub enum CargoCommand<'a> { Run { example: &'a str, @@ -146,17 +148,26 @@ pub fn run_command(command: &CargoCommand) -> anyhow::Result { /// Check if `run` was sucessful. /// returns Ok in case the run went as expected, /// Err otherwise -pub fn run_successful(run: &RunResult, expected_output_file: String) -> anyhow::Result<()> { - let mut file_handle = File::open(expected_output_file)?; +pub fn run_successful(run: &RunResult, expected_output_file: String) -> Result<(), TestRunError> { + let mut file_handle = + File::open(expected_output_file.clone()).map_err(|_| TestRunError::FileError { + file: expected_output_file.clone(), + })?; let mut expected_output = String::new(); - file_handle.read_to_string(&mut expected_output)?; - if expected_output == run.output && run.exit_status.success() { - Ok(()) + file_handle + .read_to_string(&mut expected_output) + .map_err(|_| TestRunError::FileError { + file: expected_output_file.clone(), + })?; + + if expected_output != run.output { + Err(TestRunError::FileCmpError { + expected: expected_output.clone(), + got: run.output.clone(), + }) + } else if !run.exit_status.success() { + Err(TestRunError::CommandError(run.clone())) } else { - Err(anyhow::anyhow!( - "Run failed with exit status {}: {}", - run.exit_status, - run.output - )) + Ok(()) } } diff --git a/xtask/src/main.rs b/xtask/src/main.rs index 3243b98e20..ad8719ad53 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs @@ -27,15 +27,16 @@ struct Options { target: String, } -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct RunResult { exit_status: ExitStatus, output: String, } #[derive(Debug)] -enum TestRunError { - FileCmpError { file_1: String, file_2: String }, +pub enum TestRunError { + FileCmpError { expected: String, got: String }, + FileError { file: String }, PathConversionError(OsString), CommandError(RunResult), IncompatibleCommand, @@ -44,8 +45,17 @@ enum TestRunError { impl fmt::Display for TestRunError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - TestRunError::FileCmpError { file_1, file_2 } => { - write!(f, "Differing output in Files: {} {}", file_1, file_2) + TestRunError::FileCmpError { expected, got } => { + writeln!(f, "Differing output in files.")?; + writeln!(f, "")?; + writeln!(f, "Expected:")?; + writeln!(f, "{}", expected)?; + writeln!(f, "")?; + writeln!(f, "Got:")?; + write!(f, "{}", got) + } + TestRunError::FileError { file } => { + write!(f, "File error on: {}", file) } TestRunError::CommandError(e) => { write!( @@ -75,25 +85,32 @@ fn main() -> anyhow::Result<()> { } let targets = [ARMV7M, ARMV6M]; - let examples = &[ - "idle", - "init", - "hardware", - "preempt", - "binds", - "resource", - "lock", - "multilock", - "only-shared-access", - "task", - "message", - "capacity", - "not-sync", - "generics", - "pool", - "ramfunc", - "peripherals-taken", - ]; + + let examples: Vec<_> = std::fs::read_dir("./examples")? + .filter_map(|path| { + path.map(|p| p.path().file_stem().unwrap().to_str().unwrap().to_string()) + .ok() + }) + .collect(); + + // let examples = &[ + // "idle", + // "init", + // "hardware", + // "preempt", + // "binds", + // "lock", + // "multilock", + // "only-shared-access", + // "task", + // "message", + // "capacity", + // "not-sync", + // "generics", + // "pool", + // "ramfunc", + // "peripherals-taken", + // ]; let opts = Options::from_args(); let target = &opts.target; @@ -102,12 +119,12 @@ fn main() -> anyhow::Result<()> { if target == "all" { for t in targets { - run_test(t, examples)?; - build_test(t, examples)?; + run_test(t, &examples)?; + build_test(t, &examples)?; } } else if targets.contains(&target.as_str()) { - run_test(&target, examples)?; - build_test(&target, examples)?; + run_test(&target, &examples)?; + build_test(&target, &examples)?; } else { eprintln!( "The target you specified is not available. Available targets are:\ @@ -121,115 +138,26 @@ fn main() -> anyhow::Result<()> { Ok(()) } -fn run_test(target: &str, examples: &[&str]) -> anyhow::Result<()> { +fn run_test(target: &str, examples: &[String]) -> anyhow::Result<()> { for example in examples { - match *example { - "pool" => { - if target != ARMV6M { - // check this one manually because addresses printed in `pool.run` may vary - let features_v7 = Some("__v7"); + let cmd = CargoCommand::Run { + example, + target, + features: None, + mode: BuildMode::Release, + }; - let debug_run_result = run_command(&CargoCommand::Run { - example, - target, - features: features_v7, - mode: BuildMode::Debug, - })?; + arm_example(&cmd, 1)?; - if debug_run_result.exit_status.success() { - print_from_output("foo(0x2", &debug_run_result.output); - print_from_output("bar(0x2", &debug_run_result.output); - } - - let hexpath = &build_hexpath(*example, features_v7, BuildMode::Debug, 1)?; - - run_command(&CargoCommand::Objcopy { - example, - target, - features: features_v7, - ihex: hexpath, - })?; - - let release_run_result = run_command(&CargoCommand::Run { - example, - target, - features: features_v7, - mode: BuildMode::Release, - })?; - - if release_run_result.exit_status.success() { - print_from_output("foo(0x2", &release_run_result.output); - print_from_output("bar(0x2", &release_run_result.output); - } - - let hexpath = &build_hexpath(*example, features_v7, BuildMode::Release, 1)?; - run_command(&CargoCommand::Objcopy { - example, - target, - features: features_v7, - ihex: hexpath, - })?; - } - } - "types" => { - let features_v7 = Some("__v7"); - - // TODO this example doesn't exist anymore, can we remove this case? - if target != ARMV6M { - arm_example( - &CargoCommand::Run { - example, - target, - features: features_v7, - mode: BuildMode::Debug, - }, - 1, - )?; - arm_example( - &CargoCommand::Run { - example, - target, - features: features_v7, - mode: BuildMode::Release, - }, - 1, - )?; - } - } - _ => { - arm_example( - &CargoCommand::Run { - example, - target, - features: None, - mode: BuildMode::Debug, - }, - 1, - )?; - - if *example == "types" { - arm_example( - &CargoCommand::Run { - example, - target, - features: None, - mode: BuildMode::Release, - }, - 1, - )?; - } else { - arm_example( - &CargoCommand::Build { - example, - target, - features: None, - mode: BuildMode::Release, - }, - 1, - )?; - } - } - } + arm_example( + &CargoCommand::Build { + example, + target, + features: None, + mode: BuildMode::Release, + }, + 1, + )?; } Ok(()) @@ -264,11 +192,7 @@ fn arm_example(command: &CargoCommand, build_num: u32) -> anyhow::Result<()> { match &command { CargoCommand::Run { .. } => { - if run_successful(&cargo_run_result, expected_output_file).is_err() { - return Err(anyhow::Error::new(TestRunError::CommandError( - cargo_run_result, - ))); - } + run_successful(&cargo_run_result, expected_output_file)?; } _ => (), } @@ -289,82 +213,43 @@ fn arm_example(command: &CargoCommand, build_num: u32) -> anyhow::Result<()> { } } -fn build_test(target: &str, examples: &[&str]) -> anyhow::Result<()> { +fn build_test(target: &str, examples: &[String]) -> anyhow::Result<()> { run_command(&CargoCommand::Clean)?; let mut built = vec![]; - let build_path: PathBuf = ["target", target, "debug", "examples"].iter().collect(); + let build_path: PathBuf = ["target", target, "release", "examples"].iter().collect(); for example in examples { - match *example { - "pool" | "types" => { - if target != ARMV6M { - let features_v7 = Some("__v7"); + let no_features = None; + arm_example( + &CargoCommand::Build { + target, + example, + mode: BuildMode::Release, + features: no_features, + }, + 2, + )?; + let expected = build_hexpath(example, no_features, BuildMode::Release, 1)?; + let got = build_hexpath(example, no_features, BuildMode::Release, 2)?; - arm_example( - &CargoCommand::Build { - target, - example, - mode: BuildMode::Debug, - features: features_v7, - }, - 2, - )?; - let file_1 = build_hexpath(example, features_v7, BuildMode::Debug, 1)?; - let file_2 = build_hexpath(example, features_v7, BuildMode::Debug, 2)?; + compare_builds(expected, got)?; - compare_builds(file_1, file_2)?; + arm_example( + &CargoCommand::Build { + target, + example, + mode: BuildMode::Release, + features: no_features, + }, + 2, + )?; + let expected = build_hexpath(example, no_features, BuildMode::Release, 1)?; + let got = build_hexpath(example, no_features, BuildMode::Release, 2)?; - arm_example( - &CargoCommand::Build { - target, - example, - mode: BuildMode::Release, - features: features_v7, - }, - 2, - )?; - let file_1 = build_hexpath(example, features_v7, BuildMode::Release, 1)?; - let file_2 = build_hexpath(example, features_v7, BuildMode::Release, 2)?; + compare_builds(expected, got)?; - compare_builds(file_1, file_2)?; - - built.push(build_path.join(example)); - } - } - _ => { - let no_features = None; - arm_example( - &CargoCommand::Build { - target, - example, - mode: BuildMode::Debug, - features: no_features, - }, - 2, - )?; - let file_1 = build_hexpath(example, no_features, BuildMode::Debug, 1)?; - let file_2 = build_hexpath(example, no_features, BuildMode::Debug, 2)?; - - compare_builds(file_1, file_2)?; - - arm_example( - &CargoCommand::Build { - target, - example, - mode: BuildMode::Release, - features: no_features, - }, - 2, - )?; - let file_1 = build_hexpath(example, no_features, BuildMode::Release, 1)?; - let file_2 = build_hexpath(example, no_features, BuildMode::Release, 2)?; - - compare_builds(file_1, file_2)?; - - built.push(build_path.join(example)); - } - } + built.push(build_path.join(example)); } let example_paths: Vec<&Path> = built.iter().map(|p| p.as_path()).collect(); @@ -377,12 +262,12 @@ fn build_test(target: &str, examples: &[&str]) -> anyhow::Result<()> { Ok(()) } -/// Check if lines in `output` contain `pattern` and print matching lines -fn print_from_output(pattern: &str, lines: &str) { - let lines = lines.split("\n"); - for line in lines { - if line.contains(pattern) { - println!("{}", line); - } - } -} +// /// Check if lines in `output` contain `pattern` and print matching lines +// fn print_from_output(pattern: &str, lines: &str) { +// let lines = lines.split("\n"); +// for line in lines { +// if line.contains(pattern) { +// println!("{}", line); +// } +// } +// } From 63c6a6afc033432f58d1ec3de39640c584553153 Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Mon, 27 Sep 2021 10:12:32 +0200 Subject: [PATCH 045/423] More docs updates --- README.md | 2 -- book/en/src/SUMMARY.md | 8 ++++---- book/en/src/by-example/app_init.md | 2 +- book/en/src/by-example/hardware_tasks.md | 7 +++++-- book/en/src/by-example/software_tasks.md | 11 +++++++++++ 5 files changed, 21 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index e5baea733a..f81f40409d 100644 --- a/README.md +++ b/README.md @@ -48,8 +48,6 @@ Formerly known as Real-Time For the Masses. ## Requirements -- Rust 1.36.0+ - - Applications must be written using the 2018 edition. ### Crate `cortex-m` 0.6 vs 0.7 in RTIC 0.5.x diff --git a/book/en/src/SUMMARY.md b/book/en/src/SUMMARY.md index 22a10ac873..397bb54729 100644 --- a/book/en/src/SUMMARY.md +++ b/book/en/src/SUMMARY.md @@ -4,13 +4,13 @@ - [RTIC by example](./by-example.md) - [The `app`](./by-example/app.md) - - [App initialization](./by-example/app_init.md) - [Resources](./by-example/resources.md) - - [The background task](./by-example/app_idle.md) + - [The init task](./by-example/app_init.md) + - [The idle task](./by-example/app_idle.md) - [Defining tasks](./by-example/app_task.md) + - [Hardware tasks](./by-example/hardware_tasks.md) - [Software tasks & `spawn`](./by-example/software_tasks.md) - [Message passing & `capacity`](./by-example/message_passing.md) - - [Hardware tasks](./by-example/hardware_tasks.md) - [Task priorities](./by-example/app_priorities.md) - [Monotonic & `spawn_{at/after}`](./by-example/monotonic.md) - [Starting a new project](./by-example/starting_a_project.md) @@ -18,7 +18,7 @@ - [Tips & Tricks](./by-example/tips.md) - [Implementing Monotonic](./by-example/tips_monotonic_impl.md) - [Resource de-structure-ing](./by-example/tips_destructureing.md) - - [Using indirection](./by-example/tips_indirection.md) + - [Avoid copies when message passing](./by-example/tips_indirection.md) - [`'static` super-powers](./by-example/tips_static_lifetimes.md) - [Inspecting generated code](./by-example/tips_view_code.md) - [Running tasks from RAM](./by-example/tips_from_ram.md) diff --git a/book/en/src/by-example/app_init.md b/book/en/src/by-example/app_init.md index 7a73e1bce7..3112ccf9e1 100644 --- a/book/en/src/by-example/app_init.md +++ b/book/en/src/by-example/app_init.md @@ -1,4 +1,4 @@ -# App initialization and `#[init]` +# App initialization and the `#[init]` task An RTIC application is required an `init` task setting up the system. The corresponding function must have the signature `fn(init::Context) -> (Shared, Local, init::Monotonics)`, where `Shared` and `Local` are the resource structures defined by the user. diff --git a/book/en/src/by-example/hardware_tasks.md b/book/en/src/by-example/hardware_tasks.md index 5f7b26fee9..cb0cf9f68a 100644 --- a/book/en/src/by-example/hardware_tasks.md +++ b/book/en/src/by-example/hardware_tasks.md @@ -1,5 +1,9 @@ # Hardware tasks +In it's core RTIC is based on using the interrupt controller in the hardware to do scheduling and +run tasks, as all tasks in the framework are run as interrupt handlers (except `#[init]` and +`#[idle]`). This also means that you can directly bind tasks to interrupt handlers. + To declare interrupt handlers the `#[task]` attribute takes a `binds = InterruptName` argument whose value is the name of the interrupt to which the handler will be bound to; the function used with this attribute becomes the interrupt handler. Within the @@ -10,8 +14,7 @@ Providing an interrupt name that does not exist will cause a compile error to he errors. The example below demonstrates the use of the `#[task]` attribute to declare an -interrupt handler. Like in the case of `#[init]` and `#[idle]` local `static -mut` variables are safe to use within a hardware task. +interrupt handler. ``` rust {{#include ../../../../examples/hardware.rs}} diff --git a/book/en/src/by-example/software_tasks.md b/book/en/src/by-example/software_tasks.md index 0c9b62ee7c..f78efea9c3 100644 --- a/book/en/src/by-example/software_tasks.md +++ b/book/en/src/by-example/software_tasks.md @@ -1,9 +1,20 @@ # Software tasks & spawn +Software tasks, as hardware tasks, are run as interrupt handlers where all software tasks at the +same priority shares a "free" interrupt handler to run from, called a dispatcher. These free +interrupts are interrupt vectors not used by hardware tasks. + To declare tasks in the framework the `#[task]` attribute is used on a function. By default these tasks are referred to as software tasks as they do not have a direct coupling to an interrupt handler. Software tasks can be spawned (started) using the `task_name::spawn()` static method which will directly run the task given that there are no higher priority tasks running. + +To indicate to the framework which interrupts are free for use to dispatch software tasks with the +`#[app]` attribute has a `dispatchers = [FreeInterrupt1, FreeInterrupt2, ...]` argument. You need +to provide as many dispatchers as there are priority levels used by software tasks, as an +dispatcher is assigned per interrupt level. The framework will also give a compile error if there +are not enough dispatchers provided. + This is exemplified in the following: ``` rust From 9a0d27a91e83cfd2847c9f389bb92695f384c66c Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Tue, 21 Sep 2021 17:00:16 +0200 Subject: [PATCH 046/423] Updated codegen for the updated syntax (default monotonic priority) --- macros/Cargo.toml | 2 +- macros/src/codegen/pre_init.rs | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/macros/Cargo.toml b/macros/Cargo.toml index 56bfd5cdc2..de860b1c94 100644 --- a/macros/Cargo.toml +++ b/macros/Cargo.toml @@ -22,4 +22,4 @@ proc-macro2 = "1" proc-macro-error = "1" quote = "1" syn = "1" -rtic-syntax = "0.5.0-alpha.4" +rtic-syntax = "0.5.0-rc.1" diff --git a/macros/src/codegen/pre_init.rs b/macros/src/codegen/pre_init.rs index 69f16fe376..eb216d8657 100644 --- a/macros/src/codegen/pre_init.rs +++ b/macros/src/codegen/pre_init.rs @@ -83,7 +83,11 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec Date: Mon, 27 Sep 2021 11:57:43 +0200 Subject: [PATCH 047/423] Fixing bad english --- book/en/src/by-example/hardware_tasks.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/book/en/src/by-example/hardware_tasks.md b/book/en/src/by-example/hardware_tasks.md index cb0cf9f68a..d5968761dc 100644 --- a/book/en/src/by-example/hardware_tasks.md +++ b/book/en/src/by-example/hardware_tasks.md @@ -1,6 +1,6 @@ # Hardware tasks -In it's core RTIC is based on using the interrupt controller in the hardware to do scheduling and +At its core RTIC is based on using the interrupt controller in the hardware to do scheduling and run tasks, as all tasks in the framework are run as interrupt handlers (except `#[init]` and `#[idle]`). This also means that you can directly bind tasks to interrupt handlers. From cdab00a0c63dc8fa6a69877799af665564170b9b Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Mon, 27 Sep 2021 15:29:42 +0200 Subject: [PATCH 048/423] Fix a bug in the timer queue due to comparison bug in embedded-time --- src/tq.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/tq.rs b/src/tq.rs index dcaccc9e7d..44f8dd4cbb 100644 --- a/src/tq.rs +++ b/src/tq.rs @@ -111,7 +111,9 @@ where mono.clear_compare_flag(); if let Some(instant) = self.0.peek().map(|p| p.instant) { - if instant <= unwrapper(Clock::try_now(mono)) { + let now = unwrapper(Clock::try_now(mono)); + // This if statement is like this and not <= due to a bug in embedded-time + if instant < now || instant == now { // task became ready let nr = unsafe { self.0.pop_unchecked() }; @@ -124,7 +126,8 @@ 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 <= unwrapper(Clock::try_now(mono)) { + let now = unwrapper(Clock::try_now(mono)); + if instant < now || instant == now { let nr = unsafe { self.0.pop_unchecked() }; Some((nr.task, nr.index)) From ea8efa4831915c8ec33b8c4ce8a8c823326f3d72 Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Mon, 27 Sep 2021 12:01:31 +0200 Subject: [PATCH 049/423] Preparing 0.6.0-rc.1 --- CHANGELOG.md | 6 ++++++ Cargo.toml | 8 ++++---- macros/Cargo.toml | 2 +- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4a99b58465..9359a5f11a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,12 @@ This project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +## [v0.6.0-rc.1] - 2021-09-27 + +- Documentation updates +- Monotonic handlers default to maximum priority instead of minimum (to follow RTIC 0.5) +- Better support for `rust-analyzer` + ## [v0.6.0-alpha.5] - 2021-07-09 ### Changed diff --git a/Cargo.toml b/Cargo.toml index db55060c96..c73e3c8764 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,15 +14,15 @@ name = "cortex-m-rtic" readme = "README.md" repository = "https://github.com/rtic-rs/cortex-m-rtic" -version = "0.6.0-alpha.5" +version = "0.6.0-rc.1" [lib] name = "rtic" [dependencies] cortex-m = "0.7.0" -cortex-m-rtic-macros = { path = "macros", version = "0.6.0-alpha.5" } -rtic-monotonic = "0.1.0-alpha.2" +cortex-m-rtic-macros = { path = "macros", version = "0.6.0-rc.1" } +rtic-monotonic = "0.1.0-rc.1" rtic-core = "0.3.1" heapless = "0.7.7" bare-metal = "1.0.0" @@ -33,7 +33,7 @@ version_check = "0.9" [dev-dependencies] lm3s6965 = "0.1.3" cortex-m-semihosting = "0.3.3" -systick-monotonic = "0.1.0-alpha.0" +systick-monotonic = "0.1.0-rc.1" [dev-dependencies.panic-semihosting] features = ["exit"] diff --git a/macros/Cargo.toml b/macros/Cargo.toml index de860b1c94..4de1e3e42c 100644 --- a/macros/Cargo.toml +++ b/macros/Cargo.toml @@ -12,7 +12,7 @@ license = "MIT OR Apache-2.0" name = "cortex-m-rtic-macros" readme = "../README.md" repository = "https://github.com/rtic-rs/cortex-m-rtic" -version = "0.6.0-alpha.5" +version = "0.6.0-rc.1" [lib] proc-macro = true From 7c6588e6bdacf7de161fd5e6bcd7ab969d10a240 Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Tue, 28 Sep 2021 10:18:43 +0200 Subject: [PATCH 050/423] Fix export of SYST --- macros/src/codegen/module.rs | 3 +-- macros/src/codegen/pre_init.rs | 2 +- macros/src/codegen/timer_queue.rs | 2 +- src/export.rs | 2 +- 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/macros/src/codegen/module.rs b/macros/src/codegen/module.rs index 6011c9bc04..5e0827ca9d 100644 --- a/macros/src/codegen/module.rs +++ b/macros/src/codegen/module.rs @@ -274,8 +274,7 @@ pub fn codegen( let (enable_interrupt, pend) = if &*m_isr.to_string() == "SysTick" { ( - quote!(core::mem::transmute::<_, cortex_m::peripheral::SYST>(()) - .enable_interrupt()), + quote!(core::mem::transmute::<_, rtic::export::SYST>(()).enable_interrupt()), quote!(rtic::export::SCB::set_pendst()), ) } else { diff --git a/macros/src/codegen/pre_init.rs b/macros/src/codegen/pre_init.rs index eb216d8657..3017c08e3d 100644 --- a/macros/src/codegen/pre_init.rs +++ b/macros/src/codegen/pre_init.rs @@ -104,7 +104,7 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec::DISABLE_INTERRUPT_ON_EMPTY_QUEUE { - core::mem::transmute::<_, cortex_m::peripheral::SYST>(()) + core::mem::transmute::<_, rtic::export::SYST>(()) .enable_interrupt(); } )); diff --git a/macros/src/codegen/timer_queue.rs b/macros/src/codegen/timer_queue.rs index fdfa6381a7..896b3a83f6 100644 --- a/macros/src/codegen/timer_queue.rs +++ b/macros/src/codegen/timer_queue.rs @@ -127,7 +127,7 @@ pub fn codegen(app: &App, analysis: &Analysis, _extra: &Extra) -> Vec(()).disable_interrupt()) + quote!(core::mem::transmute::<_, rtic::export::SYST>(()).disable_interrupt()) } else { quote!(rtic::export::NVIC::mask(#rt_err::#enum_::#bound_interrupt)) }; diff --git a/src/export.rs b/src/export.rs index 8fdcb67ef8..48a7d06ec4 100644 --- a/src/export.rs +++ b/src/export.rs @@ -8,7 +8,7 @@ pub use bare_metal::CriticalSection; pub use cortex_m::{ asm::wfi, interrupt, - peripheral::{scb::SystemHandler, DWT, NVIC, SCB}, + peripheral::{scb::SystemHandler, DWT, NVIC, SCB, SYST}, Peripherals, }; pub use heapless::sorted_linked_list::SortedLinkedList; From 552be420cba58a6d4b19bf341f40559ebab5be59 Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Tue, 28 Sep 2021 10:56:36 +0200 Subject: [PATCH 051/423] Prepare rc.2 release --- CHANGELOG.md | 4 ++++ Cargo.toml | 4 ++-- macros/Cargo.toml | 2 +- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9359a5f11a..61e12556d0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,10 @@ This project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +## [v0.6.0-rc.2] - 2021-09-28 + +- Fixed issue with `cortex_m` being used by the codegen instead of using the `rtic::export::...` which could make an app not compile if Systick is used and the user did not have the cortex-m crate as a dependency + ## [v0.6.0-rc.1] - 2021-09-27 - Documentation updates diff --git a/Cargo.toml b/Cargo.toml index c73e3c8764..3f32a80eee 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,14 +14,14 @@ name = "cortex-m-rtic" readme = "README.md" repository = "https://github.com/rtic-rs/cortex-m-rtic" -version = "0.6.0-rc.1" +version = "0.6.0-rc.2" [lib] name = "rtic" [dependencies] cortex-m = "0.7.0" -cortex-m-rtic-macros = { path = "macros", version = "0.6.0-rc.1" } +cortex-m-rtic-macros = { path = "macros", version = "0.6.0-rc.2" } rtic-monotonic = "0.1.0-rc.1" rtic-core = "0.3.1" heapless = "0.7.7" diff --git a/macros/Cargo.toml b/macros/Cargo.toml index 4de1e3e42c..986e22b038 100644 --- a/macros/Cargo.toml +++ b/macros/Cargo.toml @@ -12,7 +12,7 @@ license = "MIT OR Apache-2.0" name = "cortex-m-rtic-macros" readme = "../README.md" repository = "https://github.com/rtic-rs/cortex-m-rtic" -version = "0.6.0-rc.1" +version = "0.6.0-rc.2" [lib] proc-macro = true From 56a423cdbe17d9d8c099da5ae25adcdd642f7cc9 Mon Sep 17 00:00:00 2001 From: Per Lindgren Date: Wed, 13 Oct 2021 10:37:52 +0200 Subject: [PATCH 052/423] remove outdated comment --- examples/periodic.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/examples/periodic.rs b/examples/periodic.rs index 74c240c25e..3066def722 100644 --- a/examples/periodic.rs +++ b/examples/periodic.rs @@ -7,7 +7,6 @@ use panic_semihosting as _; -// NOTE: does NOT work on QEMU! #[rtic::app(device = lm3s6965, dispatchers = [SSI0])] mod app { use cortex_m_semihosting::{debug, hprintln}; From 8065d741aceb96ea06e70afce05408e334a977b5 Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Tue, 2 Nov 2021 13:41:12 +0100 Subject: [PATCH 053/423] Fixed aliasing issue due to RacyCell implementation --- macros/src/codegen.rs | 2 +- macros/src/codegen/dispatchers.rs | 8 ++-- macros/src/codegen/local_resources_struct.rs | 4 +- macros/src/codegen/module.rs | 42 +++++++++---------- macros/src/codegen/post_init.rs | 12 +++--- macros/src/codegen/pre_init.rs | 2 +- macros/src/codegen/shared_resources.rs | 2 +- macros/src/codegen/shared_resources_struct.rs | 4 +- macros/src/codegen/timer_queue.rs | 8 ++-- src/lib.rs | 12 +++--- 10 files changed, 47 insertions(+), 49 deletions(-) diff --git a/macros/src/codegen.rs b/macros/src/codegen.rs index 63a4e3cadc..f07326ba47 100644 --- a/macros/src/codegen.rs +++ b/macros/src/codegen.rs @@ -135,7 +135,7 @@ pub fn app(app: &App, analysis: &Analysis, extra: &Extra) -> TokenStream2 { rtic::export::interrupt::free(|_| { use rtic::Monotonic as _; use rtic::time::Clock as _; - if let Some(m) = unsafe{ super::super::#ident.get_mut_unchecked() } { + if let Some(m) = unsafe{ &mut *super::super::#ident.get_mut() } { if let Ok(v) = m.try_now() { v } else { diff --git a/macros/src/codegen/dispatchers.rs b/macros/src/codegen/dispatchers.rs index 57103acd0b..a90a97c773 100644 --- a/macros/src/codegen/dispatchers.rs +++ b/macros/src/codegen/dispatchers.rs @@ -78,12 +78,12 @@ pub fn codegen(app: &App, analysis: &Analysis, _extra: &Extra) -> Vec { let #tupled = - #inputs - .get_unchecked() + (&*#inputs + .get()) .get_unchecked(usize::from(index)) .as_ptr() .read(); - #fq.get_mut_unchecked().split().0.enqueue_unchecked(index); + (&mut *#fq.get_mut()).split().0.enqueue_unchecked(index); let priority = &rtic::export::Priority::new(PRIORITY); #name( #name::Context::new(priority) @@ -95,7 +95,7 @@ pub fn codegen(app: &App, analysis: &Analysis, _extra: &Extra) -> Vec>(); stmts.push(quote!( - while let Some((task, index)) = #rq.get_mut_unchecked().split().1.dequeue() { + while let Some((task, index)) = (&mut *#rq.get_mut()).split().1.dequeue() { match task { #(#arms)* } diff --git a/macros/src/codegen/local_resources_struct.rs b/macros/src/codegen/local_resources_struct.rs index cdbfcccac9..10bde5fefb 100644 --- a/macros/src/codegen/local_resources_struct.rs +++ b/macros/src/codegen/local_resources_struct.rs @@ -57,9 +57,9 @@ pub fn codegen(ctxt: Context, needs_lt: &mut bool, app: &App) -> (TokenStream2, let expr = if is_declared { // If the local resources is already initialized, we only need to access its value and // not go through an `MaybeUninit` - quote!(#mangled_name.get_mut_unchecked()) + quote!(&mut *#mangled_name.get_mut()) } else { - quote!(&mut *#mangled_name.get_mut_unchecked().as_mut_ptr()) + quote!(&mut *(&mut *#mangled_name.get_mut()).as_mut_ptr()) }; values.push(quote!( diff --git a/macros/src/codegen/module.rs b/macros/src/codegen/module.rs index 5e0827ca9d..c59a814c6e 100644 --- a/macros/src/codegen/module.rs +++ b/macros/src/codegen/module.rs @@ -232,15 +232,15 @@ pub fn codegen( let input = #tupled; unsafe { - if let Some(index) = rtic::export::interrupt::free(|_| #fq.get_mut_unchecked().dequeue()) { - #inputs - .get_mut_unchecked() + if let Some(index) = rtic::export::interrupt::free(|_| (&mut *#fq.get_mut()).dequeue()) { + (&mut *#inputs + .get_mut()) .get_unchecked_mut(usize::from(index)) .as_mut_ptr() .write(input); rtic::export::interrupt::free(|_| { - #rq.get_mut_unchecked().enqueue_unchecked((#t::#name, index)); + (&mut *#rq.get_mut()).enqueue_unchecked((#t::#name, index)); }); rtic::pend(#device::#enum_::#interrupt); @@ -330,16 +330,16 @@ pub fn codegen( impl #internal_spawn_handle_ident { pub fn cancel(self) -> Result<#ty, ()> { rtic::export::interrupt::free(|_| unsafe { - let tq = #tq.get_mut_unchecked(); + let tq = &mut *#tq.get_mut(); if let Some((_task, index)) = tq.cancel_marker(self.marker) { // Get the message - let msg = #inputs - .get_unchecked() + let msg = (&*#inputs + .get()) .get_unchecked(usize::from(index)) .as_ptr() .read(); // Return the index to the free queue - #fq.get_mut_unchecked().split().0.enqueue_unchecked(index); + (&mut *#fq.get_mut()).split().0.enqueue_unchecked(index); Ok(msg) } else { @@ -359,10 +359,10 @@ pub fn codegen( pub fn reschedule_at(self, instant: rtic::time::Instant<#mono_type>) -> Result { rtic::export::interrupt::free(|_| unsafe { - let marker = *#tq_marker.get_mut_unchecked(); - *#tq_marker.get_mut_unchecked() = #tq_marker.get_mut_unchecked().wrapping_add(1); + let marker = #tq_marker.get().read(); + #tq_marker.get_mut().write(marker.wrapping_add(1)); - let tq = #tq.get_mut_unchecked(); + let tq = (&mut *#tq.get_mut()); tq.update_marker(self.marker, marker, instant, || #pend).map(|_| #name::#m::SpawnHandle { marker }) }) @@ -383,7 +383,7 @@ pub fn codegen( D::T: Into<<#mono_type as rtic::time::Clock>::T>, { - let instant = if rtic::export::interrupt::free(|_| unsafe { #m_ident.get_mut_unchecked().is_none() }) { + let instant = if rtic::export::interrupt::free(|_| unsafe { (&*#m_ident.get()).is_none() }) { rtic::time::Instant::new(0) } else { monotonics::#m::now() @@ -401,21 +401,21 @@ pub fn codegen( ) -> Result<#name::#m::SpawnHandle, #ty> { unsafe { let input = #tupled; - if let Some(index) = rtic::export::interrupt::free(|_| #fq.get_mut_unchecked().dequeue()) { - #inputs - .get_mut_unchecked() + if let Some(index) = rtic::export::interrupt::free(|_| (&mut *#fq.get_mut()).dequeue()) { + (&mut *#inputs + .get_mut()) .get_unchecked_mut(usize::from(index)) .as_mut_ptr() .write(input); - #instants - .get_mut_unchecked() + (&mut *#instants + .get_mut()) .get_unchecked_mut(usize::from(index)) .as_mut_ptr() .write(instant); rtic::export::interrupt::free(|_| { - let marker = *#tq_marker.get_mut_unchecked(); + let marker = #tq_marker.get().read(); let nr = rtic::export::NotReady { instant, index, @@ -423,15 +423,15 @@ pub fn codegen( marker, }; - *#tq_marker.get_mut_unchecked() = #tq_marker.get_mut_unchecked().wrapping_add(1); + #tq_marker.get_mut().write(#tq_marker.get().read().wrapping_add(1)); - let tq = #tq.get_mut_unchecked(); + let tq = &mut *#tq.get_mut(); tq.enqueue_unchecked( nr, || #enable_interrupt, || #pend, - #m_ident.get_mut_unchecked().as_mut()); + (&mut *#m_ident.get_mut()).as_mut()); Ok(#name::#m::SpawnHandle { marker }) }) diff --git a/macros/src/codegen/post_init.rs b/macros/src/codegen/post_init.rs index 5624b20a72..b3359ab468 100644 --- a/macros/src/codegen/post_init.rs +++ b/macros/src/codegen/post_init.rs @@ -19,10 +19,9 @@ pub fn codegen(app: &App, analysis: &Analysis) -> Vec { // We include the cfgs #(#cfgs)* // Resource is a RacyCell> - // - `get_mut_unchecked` to obtain `MaybeUninit` - // - `as_mut_ptr` to obtain a raw pointer to `MaybeUninit` + // - `get_mut` to obtain a raw pointer to `MaybeUninit` // - `write` the defined value for the late resource T - #mangled_name.get_mut_unchecked().as_mut_ptr().write(shared_resources.#name); + (&mut *#mangled_name.get_mut()).as_mut_ptr().write(shared_resources.#name); )); } } @@ -37,10 +36,9 @@ pub fn codegen(app: &App, analysis: &Analysis) -> Vec { // We include the cfgs #(#cfgs)* // Resource is a RacyCell> - // - `get_mut_unchecked` to obtain `MaybeUninit` - // - `as_mut_ptr` to obtain a raw pointer to `MaybeUninit` + // - `get_mut` to obtain a raw pointer to `MaybeUninit` // - `write` the defined value for the late resource T - #mangled_name.get_mut_unchecked().as_mut_ptr().write(local_resources.#name); + (&mut *#mangled_name.get_mut()).as_mut_ptr().write(local_resources.#name); )); } } @@ -58,7 +56,7 @@ pub fn codegen(app: &App, analysis: &Analysis) -> Vec { // Store the monotonic let name = util::monotonic_ident(&monotonic.to_string()); - stmts.push(quote!(*#name.get_mut_unchecked() = Some(monotonics.#idx);)); + stmts.push(quote!(#name.get_mut().write(Some(monotonics.#idx));)); } // Enable the interrupts -- this completes the `init`-ialization phase diff --git a/macros/src/codegen/pre_init.rs b/macros/src/codegen/pre_init.rs index 3017c08e3d..42cc055298 100644 --- a/macros/src/codegen/pre_init.rs +++ b/macros/src/codegen/pre_init.rs @@ -19,7 +19,7 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec (TokenStream2, } let expr = if access.is_exclusive() { - quote!(&mut *#mangled_name.get_mut_unchecked().as_mut_ptr()) + quote!(&mut *(&mut *#mangled_name.get_mut()).as_mut_ptr()) } else { - quote!(&*#mangled_name.get_unchecked().as_ptr()) + quote!(&*(&*#mangled_name.get()).as_ptr()) }; values.push(quote!( diff --git a/macros/src/codegen/timer_queue.rs b/macros/src/codegen/timer_queue.rs index 896b3a83f6..2a344d256b 100644 --- a/macros/src/codegen/timer_queue.rs +++ b/macros/src/codegen/timer_queue.rs @@ -117,7 +117,7 @@ pub fn codegen(app: &App, analysis: &Analysis, _extra: &Extra) -> Vec { - rtic::export::interrupt::free(|_| #rq.get_mut_unchecked().split().0.enqueue_unchecked((#rqt::#name, index))); + rtic::export::interrupt::free(|_| (&mut *#rq.get_mut()).split().0.enqueue_unchecked((#rqt::#name, index))); #pend } @@ -137,8 +137,8 @@ pub fn codegen(app: &App, analysis: &Analysis, _extra: &Extra) -> Vec Vec RacyCell { RacyCell(UnsafeCell::new(value)) } - /// Get `&mut T` + /// Get `*mut T` #[inline(always)] - pub unsafe fn get_mut_unchecked(&self) -> &mut T { - &mut *self.0.get() + pub unsafe fn get_mut(&self) -> *mut T { + self.0.get() } - /// Get `&T` + /// Get `*const T` #[inline(always)] - pub unsafe fn get_unchecked(&self) -> &T { - &*self.0.get() + pub unsafe fn get(&self) -> *const T { + self.0.get() } } From 1e2ab78a3fad8f383e5949dd0a11aaaaf03467f1 Mon Sep 17 00:00:00 2001 From: Per Lindgren Date: Tue, 2 Nov 2021 19:47:14 +0100 Subject: [PATCH 054/423] added doc for RacyCell --- src/lib.rs | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index d5505b03cf..8463442acf 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -59,6 +59,27 @@ where use core::cell::UnsafeCell; /// Internal replacement for `static mut T` +/// +/// Used to represent RTIC Resources +/// +/// Soundness: +/// 1) Unsafe API for internal use only +/// 2) get_mut(&self) -> *mut T +/// returns a raw mutable pointer to the inner T +/// casting to &mut T is under control of RTIC +/// RTIC ensures &mut T to be unique under Rust aliasing rules. +/// +/// Implementation uses the underlying UnsafeCell +/// self.0.get() -> *mut T +/// +/// 3) get(&self) -> *const T +/// returns a raw immutable (const) pointer to the inner T +/// casting to &T is under control of RTIC +/// RTIC ensures &T to be shared under Rust aliasing rules. +/// +/// Implementation uses the underlying UnsafeCell +/// self.0.get() -> *mut T, demoted to *const T +/// #[repr(transparent)] pub struct RacyCell(UnsafeCell); From d3d66c97aef5eeb06ea57dac8200c43f6720e1c1 Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Wed, 3 Nov 2021 08:26:45 +0100 Subject: [PATCH 055/423] Cleanup of resource initialization, no need to dereference --- macros/src/codegen/post_init.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/macros/src/codegen/post_init.rs b/macros/src/codegen/post_init.rs index b3359ab468..07fbd03c9c 100644 --- a/macros/src/codegen/post_init.rs +++ b/macros/src/codegen/post_init.rs @@ -21,7 +21,7 @@ pub fn codegen(app: &App, analysis: &Analysis) -> Vec { // Resource is a RacyCell> // - `get_mut` to obtain a raw pointer to `MaybeUninit` // - `write` the defined value for the late resource T - (&mut *#mangled_name.get_mut()).as_mut_ptr().write(shared_resources.#name); + #mangled_name.get_mut().write(core::mem::MaybeUninit::new(shared_resources.#name)); )); } } @@ -38,7 +38,7 @@ pub fn codegen(app: &App, analysis: &Analysis) -> Vec { // Resource is a RacyCell> // - `get_mut` to obtain a raw pointer to `MaybeUninit` // - `write` the defined value for the late resource T - (&mut *#mangled_name.get_mut()).as_mut_ptr().write(local_resources.#name); + #mangled_name.get_mut().write(core::mem::MaybeUninit::new(local_resources.#name)); )); } } From 50017b96f04ff095e143fd19bb8487e86adae28f Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Wed, 3 Nov 2021 08:27:05 +0100 Subject: [PATCH 056/423] Fixed aliasing in lock impl --- macros/src/codegen/shared_resources.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/macros/src/codegen/shared_resources.rs b/macros/src/codegen/shared_resources.rs index aa50dccb6e..b27c827c23 100644 --- a/macros/src/codegen/shared_resources.rs +++ b/macros/src/codegen/shared_resources.rs @@ -69,7 +69,7 @@ pub fn codegen( let ptr = quote!( #(#cfgs)* - (&mut *#mangled_name.get_mut()).as_mut_ptr() + #mangled_name.get_mut() as *mut _ ); let ceiling = match analysis.ownerships.get(name) { From 9e24fcbbd90609a25b9d985f9292900b476fe5ea Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Wed, 3 Nov 2021 08:54:18 +0100 Subject: [PATCH 057/423] Fix CI --- examples/cfg-whole-task.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/examples/cfg-whole-task.rs b/examples/cfg-whole-task.rs index 213fe13f92..f41866db47 100644 --- a/examples/cfg-whole-task.rs +++ b/examples/cfg-whole-task.rs @@ -15,7 +15,6 @@ mod app { #[shared] struct Shared { - #[cfg(debug_assertions)] // <- `true` when using the `dev` profile count: u32, #[cfg(never)] unused: u32, @@ -31,7 +30,6 @@ mod app { ( Shared { - #[cfg(debug_assertions)] count: 0, #[cfg(never)] unused: 1, From fb092aa65a6a7935828fbf6dcb7d3e89f0355a83 Mon Sep 17 00:00:00 2001 From: Andrew Gazelka Date: Sat, 9 Oct 2021 15:27:20 -0700 Subject: [PATCH 058/423] fix #543 --- macros/src/codegen/init.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/macros/src/codegen/init.rs b/macros/src/codegen/init.rs index 2de3e73481..eaf7f612a9 100644 --- a/macros/src/codegen/init.rs +++ b/macros/src/codegen/init.rs @@ -43,8 +43,10 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> CodegenResult { .map(|(k, v)| { let ty = &v.ty; let cfgs = &v.cfgs; + let docs = &v.docs; quote!( #(#cfgs)* + #(#docs)* #k: #ty, ) }) @@ -55,8 +57,10 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> CodegenResult { .map(|(k, v)| { let ty = &v.ty; let cfgs = &v.cfgs; + let docs = &v.docs; quote!( #(#cfgs)* + #(#docs)* #k: #ty, ) }) From 03af9b19943e3b953ec8f557e5359d6b8627de5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Tj=C3=A4der?= Date: Sun, 7 Nov 2021 00:42:57 +0100 Subject: [PATCH 059/423] Match new rtic-syntax naming of shared and local --- macros/src/codegen/post_init.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/macros/src/codegen/post_init.rs b/macros/src/codegen/post_init.rs index 07fbd03c9c..8bd3a7ddcf 100644 --- a/macros/src/codegen/post_init.rs +++ b/macros/src/codegen/post_init.rs @@ -14,7 +14,7 @@ pub fn codegen(app: &App, analysis: &Analysis) -> Vec { let mangled_name = util::static_shared_resource_ident(name); // If it's live let cfgs = res.cfgs.clone(); - if analysis.shared_resource_locations.get(name).is_some() { + if analysis.shared_resources.get(name).is_some() { stmts.push(quote!( // We include the cfgs #(#cfgs)* @@ -31,7 +31,7 @@ pub fn codegen(app: &App, analysis: &Analysis) -> Vec { let mangled_name = util::static_local_resource_ident(name); // If it's live let cfgs = res.cfgs.clone(); - if analysis.local_resource_locations.get(name).is_some() { + if analysis.local_resources.get(name).is_some() { stmts.push(quote!( // We include the cfgs #(#cfgs)* From eb345b7dbb6826940c2aee3c51fd077d29284cf7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Tj=C3=A4der?= Date: Mon, 8 Nov 2021 18:39:09 +0100 Subject: [PATCH 060/423] Update CHANGELOG --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 61e12556d0..6caab7fa83 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,10 @@ This project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +### Fixed + +- Match rtic-syntax Analysis-struct updates from https://github.com/rtic-rs/rtic-syntax/pull/61 + ## [v0.6.0-rc.2] - 2021-09-28 - Fixed issue with `cortex_m` being used by the codegen instead of using the `rtic::export::...` which could make an app not compile if Systick is used and the user did not have the cortex-m crate as a dependency From 1438a5b0eaaa06747d3b3c0a89c2121de9682980 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Tj=C3=A4der?= Date: Mon, 8 Nov 2021 20:28:57 +0100 Subject: [PATCH 061/423] Update changelog from v0.5.x branch --- CHANGELOG.md | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6caab7fa83..cc7b6b3a2a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,9 @@ This project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] + +## [v0.6.0-rc.3] - 2021-11-08 + ### Fixed - Match rtic-syntax Analysis-struct updates from https://github.com/rtic-rs/rtic-syntax/pull/61 @@ -19,12 +22,26 @@ This project adheres to [Semantic Versioning](http://semver.org/). - Monotonic handlers default to maximum priority instead of minimum (to follow RTIC 0.5) - Better support for `rust-analyzer` +## [v0.5.9] - 2021-09-27 + +- Removed the `cortex-m-rt` dependency +- Docs updates + +## [v0.5.8] - 2021-08-19 + +- Feature flag was added to support `cortex-m v0.7.x` +- MSRV raised to 1.38. + ## [v0.6.0-alpha.5] - 2021-07-09 ### Changed - The new resources syntax is implemented. +## [v0.5.7] - 2021-07-05 + +- Backport: "you must enable the rt feature" compile time detection + ## [v0.6.0-alpha.4] - 2021-05-27 ### Fixed @@ -60,6 +77,10 @@ This project adheres to [Semantic Versioning](http://semver.org/). - Support for multi-locks, see `examples/multilock.rs` for syntax. - New monotonic syntax and support, see `#[monotonic]` +## [v0.5.6] - 2021-03-03 + +- **Security** Use latest security patched heapless + ## [v0.6.0-alpha.0] - 2020-11-14 ### Added @@ -423,7 +444,12 @@ Yanked due to a soundness issue in `init`; the issue has been mostly fixed in v0 - Initial release -[Unreleased]: https://github.com/rtic-rs/cortex-m-rtic/compare/v0.5.5...HEAD +[Unreleased]: https://github.com/rtic-rs/cortex-m-rtic/compare/v0.5.9...HEAD +[v0.5.x unreleased]: https://github.com/rtic-rs/cortex-m-rtic/compare/v0.5.8...v0.5.x +[v0.5.9]: https://github.com/rtic-rs/cortex-m-rtic/compare/v0.5.8...v0.5.9 +[v0.5.8]: https://github.com/rtic-rs/cortex-m-rtic/compare/v0.5.7...v0.5.8 +[v0.5.7]: https://github.com/rtic-rs/cortex-m-rtic/compare/v0.5.6...v0.5.7 +[v0.5.6]: https://github.com/rtic-rs/cortex-m-rtic/compare/v0.5.5...v0.5.6 [v0.5.5]: https://github.com/rtic-rs/cortex-m-rtic/compare/v0.5.4...v0.5.5 [v0.5.4]: https://github.com/rtic-rs/cortex-m-rtic/compare/v0.5.3...v0.5.4 [v0.5.3]: https://github.com/rtic-rs/cortex-m-rtic/compare/v0.5.2...v0.5.3 From f7d4b5dc70dac3768d128cf8fd8a8f810fdace16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Tj=C3=A4der?= Date: Mon, 8 Nov 2021 20:40:59 +0100 Subject: [PATCH 062/423] CHANGELOG: Add links to v0.6.x --- CHANGELOG.md | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cc7b6b3a2a..c86bb03e07 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -444,7 +444,17 @@ Yanked due to a soundness issue in `init`; the issue has been mostly fixed in v0 - Initial release -[Unreleased]: https://github.com/rtic-rs/cortex-m-rtic/compare/v0.5.9...HEAD +[Unreleased]: https://github.com/rtic-rs/cortex-m-rtic/compare/v0.6.0-rc.3...HEAD +[v0.6.0-rc.3]: https://github.com/rtic-rs/cortex-m-rtic/compare/v0.6.0-rc.2...v0.6.0-rc.3 +[v0.6.0-rc.2]: https://github.com/rtic-rs/cortex-m-rtic/compare/v0.6.0-rc.1...v0.6.0-rc.2 +[v0.6.0-rc.1]: https://github.com/rtic-rs/cortex-m-rtic/compare/v0.6.0-rc.0...v0.6.0-rc.1 +[v0.6.0-rc.0]: https://github.com/rtic-rs/cortex-m-rtic/compare/v0.6.0-alpha.5...v0.6.0-rc.0 +[v0.6.0-alpha.5]: https://github.com/rtic-rs/cortex-m-rtic/compare/v0.6.0-alpha.4...v0.6.0-alpha.5 +[v0.6.0-alpha.4]: https://github.com/rtic-rs/cortex-m-rtic/compare/v0.6.0-alpha.3...v0.6.0-alpha.4 +[v0.6.0-alpha.3]: https://github.com/rtic-rs/cortex-m-rtic/compare/v0.6.0-alpha.2...v0.6.0-alpha.3 +[v0.6.0-alpha.2]: https://github.com/rtic-rs/cortex-m-rtic/compare/v0.6.0-alpha.1...v0.6.0-alpha.2 +[v0.6.0-alpha.1]: https://github.com/rtic-rs/cortex-m-rtic/compare/v0.6.0-alpha.0...v0.6.0-alpha.1 +[v0.6.0-alpha.0]: https://github.com/rtic-rs/cortex-m-rtic/compare/v0.5.5...v0.6.0-alpha.0 [v0.5.x unreleased]: https://github.com/rtic-rs/cortex-m-rtic/compare/v0.5.8...v0.5.x [v0.5.9]: https://github.com/rtic-rs/cortex-m-rtic/compare/v0.5.8...v0.5.9 [v0.5.8]: https://github.com/rtic-rs/cortex-m-rtic/compare/v0.5.7...v0.5.8 From 0492d98916fb05f0ce0906b42bf7f2ee292f4b18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Tj=C3=A4der?= Date: Mon, 8 Nov 2021 20:42:04 +0100 Subject: [PATCH 063/423] Bump version to 0.6.0-rc.3 --- Cargo.toml | 4 ++-- macros/Cargo.toml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 3f32a80eee..baf287cc10 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,14 +14,14 @@ name = "cortex-m-rtic" readme = "README.md" repository = "https://github.com/rtic-rs/cortex-m-rtic" -version = "0.6.0-rc.2" +version = "0.6.0-rc.3" [lib] name = "rtic" [dependencies] cortex-m = "0.7.0" -cortex-m-rtic-macros = { path = "macros", version = "0.6.0-rc.2" } +cortex-m-rtic-macros = { path = "macros", version = "0.6.0-rc.3" } rtic-monotonic = "0.1.0-rc.1" rtic-core = "0.3.1" heapless = "0.7.7" diff --git a/macros/Cargo.toml b/macros/Cargo.toml index 986e22b038..de335bc444 100644 --- a/macros/Cargo.toml +++ b/macros/Cargo.toml @@ -12,7 +12,7 @@ license = "MIT OR Apache-2.0" name = "cortex-m-rtic-macros" readme = "../README.md" repository = "https://github.com/rtic-rs/cortex-m-rtic" -version = "0.6.0-rc.2" +version = "0.6.0-rc.3" [lib] proc-macro = true @@ -22,4 +22,4 @@ proc-macro2 = "1" proc-macro-error = "1" quote = "1" syn = "1" -rtic-syntax = "0.5.0-rc.1" +rtic-syntax = "0.5.0-rc.2" From 0dcb0c4e497b23bf68b7ac0d3d918ab3d3c209be Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Sun, 31 Oct 2021 10:09:40 +0100 Subject: [PATCH 064/423] New monotonic trait working --- Cargo.toml | 5 ++- examples/cancel-reschedule.rs | 11 +++--- examples/common.rs | 7 ++-- examples/periodic.rs | 7 ++-- examples/schedule.rs | 9 ++--- examples/t-schedule.rs | 57 ++++++++++++---------------- macros/src/codegen.rs | 15 ++------ macros/src/codegen/module.rs | 30 ++++++--------- macros/src/codegen/software_tasks.rs | 2 +- src/lib.rs | 2 +- src/tq.rs | 27 +++---------- 11 files changed, 66 insertions(+), 106 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index baf287cc10..7cdf7f94c4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,7 +22,8 @@ name = "rtic" [dependencies] cortex-m = "0.7.0" cortex-m-rtic-macros = { path = "macros", version = "0.6.0-rc.3" } -rtic-monotonic = "0.1.0-rc.1" +# rtic-monotonic = "0.1.0-rc.1" +rtic-monotonic = { git = "https://github.com/rtic-rs/rtic-monotonic.git", branch = "master" } rtic-core = "0.3.1" heapless = "0.7.7" bare-metal = "1.0.0" @@ -33,7 +34,7 @@ version_check = "0.9" [dev-dependencies] lm3s6965 = "0.1.3" cortex-m-semihosting = "0.3.3" -systick-monotonic = "0.1.0-rc.1" +systick-monotonic = { git = "https://github.com/rtic-rs/systick-monotonic.git", branch = "new-trait-and-time-lib" } [dev-dependencies.panic-semihosting] features = ["exit"] diff --git a/examples/cancel-reschedule.rs b/examples/cancel-reschedule.rs index c5ef2e739d..e0bdaae80e 100644 --- a/examples/cancel-reschedule.rs +++ b/examples/cancel-reschedule.rs @@ -10,8 +10,7 @@ use panic_semihosting as _; #[rtic::app(device = lm3s6965, dispatchers = [SSI0])] mod app { use cortex_m_semihosting::{debug, hprintln}; - use rtic::time::duration::*; - use systick_monotonic::Systick; + use systick_monotonic::*; #[monotonic(binds = SysTick, default = true)] type MyMono = Systick<100>; // 100 Hz / 10 ms granularity @@ -32,7 +31,7 @@ mod app { hprintln!("init").ok(); // Schedule `foo` to run 1 second in the future - foo::spawn_after(1.seconds()).unwrap(); + foo::spawn_after(1.secs()).unwrap(); ( Shared {}, @@ -46,8 +45,8 @@ mod app { hprintln!("foo").ok(); // Schedule `bar` to run 2 seconds in the future (1 second after foo runs) - let spawn_handle = baz::spawn_after(2.seconds()).unwrap(); - bar::spawn_after(1.seconds(), spawn_handle, false).unwrap(); // Change to true + let spawn_handle = baz::spawn_after(2.secs()).unwrap(); + bar::spawn_after(1.secs(), spawn_handle, false).unwrap(); // Change to true } #[task] @@ -57,7 +56,7 @@ mod app { if do_reschedule { // Reschedule baz 2 seconds from now, instead of the original 1 second // from now. - baz_handle.reschedule_after(2.seconds()).unwrap(); + baz_handle.reschedule_after(2.secs()).unwrap(); // Or baz_handle.reschedule_at(/* time */) } else { // Or cancel it diff --git a/examples/common.rs b/examples/common.rs index 770a0ae557..26a5c8fb33 100644 --- a/examples/common.rs +++ b/examples/common.rs @@ -10,8 +10,7 @@ use panic_semihosting as _; #[rtic::app(device = lm3s6965, dispatchers = [SSI0, QEI0])] mod app { use cortex_m_semihosting::{debug, hprintln}; - use rtic::time::duration::*; - use systick_monotonic::Systick; // Implements the `Monotonic` trait // Time helpers, such as `N.seconds()` + use systick_monotonic::*; // Implements the `Monotonic` trait // A monotonic timer to enable scheduling in RTIC #[monotonic(binds = SysTick, default = true)] @@ -41,7 +40,7 @@ mod app { // Spawn the task `bar` 1 second after `init` finishes, this is enabled // by the `#[monotonic(..)]` above - bar::spawn_after(1.seconds()).unwrap(); + bar::spawn_after(1.secs()).unwrap(); debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator @@ -83,7 +82,7 @@ mod app { hprintln!("bar").ok(); // Run `bar` once per second - bar::spawn_after(1.seconds()).unwrap(); + bar::spawn_after(1.secs()).unwrap(); } // Hardware task, bound to a hardware interrupt diff --git a/examples/periodic.rs b/examples/periodic.rs index 3066def722..495054ea42 100644 --- a/examples/periodic.rs +++ b/examples/periodic.rs @@ -10,8 +10,7 @@ use panic_semihosting as _; #[rtic::app(device = lm3s6965, dispatchers = [SSI0])] mod app { use cortex_m_semihosting::{debug, hprintln}; - use rtic::time::duration::*; - use systick_monotonic::Systick; + use systick_monotonic::*; #[monotonic(binds = SysTick, default = true)] type MyMono = Systick<100>; // 100 Hz / 10 ms granularity @@ -28,7 +27,7 @@ mod app { let mono = Systick::new(systick, 12_000_000); - foo::spawn_after(1.seconds()).unwrap(); + foo::spawn_after(1.secs()).unwrap(); (Shared {}, Local {}, init::Monotonics(mono)) } @@ -43,6 +42,6 @@ mod app { } // Periodic ever 1 seconds - foo::spawn_after(1.seconds()).unwrap(); + foo::spawn_after(1.secs()).unwrap(); } } diff --git a/examples/schedule.rs b/examples/schedule.rs index 669c67c967..446a382892 100644 --- a/examples/schedule.rs +++ b/examples/schedule.rs @@ -10,8 +10,7 @@ use panic_semihosting as _; #[rtic::app(device = lm3s6965, dispatchers = [SSI0])] mod app { use cortex_m_semihosting::{debug, hprintln}; - use rtic::time::duration::*; - use systick_monotonic::Systick; + use systick_monotonic::*; #[monotonic(binds = SysTick, default = true)] type MyMono = Systick<100>; // 100 Hz / 10 ms granularity @@ -32,7 +31,7 @@ mod app { hprintln!("init").ok(); // Schedule `foo` to run 1 second in the future - foo::spawn_after(1.seconds()).unwrap(); + foo::spawn_after(1.secs()).unwrap(); ( Shared {}, @@ -46,7 +45,7 @@ mod app { hprintln!("foo").ok(); // Schedule `bar` to run 2 seconds in the future (1 second after foo runs) - bar::spawn_after(1.seconds()).unwrap(); + bar::spawn_after(1.secs()).unwrap(); } #[task] @@ -54,7 +53,7 @@ mod app { hprintln!("bar").ok(); // Schedule `baz` to run 1 seconds from now, but with a specific time instant. - baz::spawn_at(monotonics::now() + 1.seconds()).unwrap(); + baz::spawn_at(monotonics::now() + 1.secs()).unwrap(); } #[task] diff --git a/examples/t-schedule.rs b/examples/t-schedule.rs index 5530ec6a8b..6ee08afbe9 100644 --- a/examples/t-schedule.rs +++ b/examples/t-schedule.rs @@ -10,8 +10,7 @@ use panic_semihosting as _; #[rtic::app(device = lm3s6965, dispatchers = [SSI0])] mod app { use cortex_m_semihosting::debug; - use rtic::time::duration::Seconds; - use systick_monotonic::Systick; + use systick_monotonic::*; #[monotonic(binds = SysTick, default = true)] type MyMono = Systick<100>; // 100 Hz / 10 ms granularity @@ -40,27 +39,26 @@ mod app { // Not default let _: Result = foo::MyMono::spawn_at(monotonics::MyMono::now()); - let handle: Result = foo::MyMono::spawn_after(Seconds(1_u32)); - let _: Result = - handle.unwrap().reschedule_after(Seconds(1_u32)); + let handle: Result = foo::MyMono::spawn_after(1.secs()); + let _: Result = handle.unwrap().reschedule_after(1.secs()); - let handle: Result = foo::MyMono::spawn_after(Seconds(1_u32)); + let handle: Result = foo::MyMono::spawn_after(1.secs()); let _: Result = handle.unwrap().reschedule_at(monotonics::MyMono::now()); - let handle: Result = foo::MyMono::spawn_after(Seconds(1_u32)); + let handle: Result = foo::MyMono::spawn_after(1.secs()); let _: Result<(), ()> = handle.unwrap().cancel(); // Using default let _: Result = foo::spawn_at(monotonics::now()); - let handle: Result = foo::spawn_after(Seconds(1_u32)); - let _: Result = handle.unwrap().reschedule_after(Seconds(1_u32)); + let handle: Result = foo::spawn_after(1.secs()); + let _: Result = handle.unwrap().reschedule_after(1.secs()); - let handle: Result = foo::spawn_after(Seconds(1_u32)); + let handle: Result = foo::spawn_after(1.secs()); let _: Result = handle.unwrap().reschedule_at(monotonics::MyMono::now()); - let handle: Result = foo::spawn_after(Seconds(1_u32)); + let handle: Result = foo::spawn_after(1.secs()); let _: Result<(), ()> = handle.unwrap().cancel(); // Task with single message passing @@ -68,30 +66,26 @@ mod app { // Not default let _: Result = bar::MyMono::spawn_at(monotonics::MyMono::now(), 0); - let handle: Result = - bar::MyMono::spawn_after(Seconds(1_u32), 0); - let _: Result = - handle.unwrap().reschedule_after(Seconds(1_u32)); + let handle: Result = bar::MyMono::spawn_after(1.secs(), 1); + let _: Result = handle.unwrap().reschedule_after(1.secs()); - let handle: Result = - bar::MyMono::spawn_after(Seconds(1_u32), 0); + let handle: Result = bar::MyMono::spawn_after(1.secs(), 1); let _: Result = handle.unwrap().reschedule_at(monotonics::MyMono::now()); - let handle: Result = - bar::MyMono::spawn_after(Seconds(1_u32), 0); + let handle: Result = bar::MyMono::spawn_after(1.secs(), 1); let _: Result = handle.unwrap().cancel(); // Using default let _: Result = bar::spawn_at(monotonics::MyMono::now(), 0); - let handle: Result = bar::spawn_after(Seconds(1_u32), 0); - let _: Result = handle.unwrap().reschedule_after(Seconds(1_u32)); + let handle: Result = bar::spawn_after(1.secs(), 1); + let _: Result = handle.unwrap().reschedule_after(1.secs()); - let handle: Result = bar::spawn_after(Seconds(1_u32), 0); + let handle: Result = bar::spawn_after(1.secs(), 1); let _: Result = handle.unwrap().reschedule_at(monotonics::MyMono::now()); - let handle: Result = bar::spawn_after(Seconds(1_u32), 0); + let handle: Result = bar::spawn_after(1.secs(), 1); let _: Result = handle.unwrap().cancel(); // Task with multiple message passing @@ -100,30 +94,29 @@ mod app { let _: Result = baz::MyMono::spawn_at(monotonics::MyMono::now(), 0, 1); let handle: Result = - baz::MyMono::spawn_after(Seconds(1_u32), 0, 1); - let _: Result = - handle.unwrap().reschedule_after(Seconds(1_u32)); + baz::MyMono::spawn_after(1.secs(), 1, 2); + let _: Result = handle.unwrap().reschedule_after(1.secs()); let handle: Result = - baz::MyMono::spawn_after(Seconds(1_u32), 0, 1); + baz::MyMono::spawn_after(1.secs(), 1, 2); let _: Result = handle.unwrap().reschedule_at(monotonics::MyMono::now()); let handle: Result = - baz::MyMono::spawn_after(Seconds(1_u32), 0, 1); + baz::MyMono::spawn_after(1.secs(), 1, 2); let _: Result<(u32, u32), ()> = handle.unwrap().cancel(); // Using default let _: Result = baz::spawn_at(monotonics::MyMono::now(), 0, 1); - let handle: Result = baz::spawn_after(Seconds(1_u32), 0, 1); - let _: Result = handle.unwrap().reschedule_after(Seconds(1_u32)); + let handle: Result = baz::spawn_after(1.secs(), 1, 2); + let _: Result = handle.unwrap().reschedule_after(1.secs()); - let handle: Result = baz::spawn_after(Seconds(1_u32), 0, 1); + let handle: Result = baz::spawn_after(1.secs(), 1, 2); let _: Result = handle.unwrap().reschedule_at(monotonics::MyMono::now()); - let handle: Result = baz::spawn_after(Seconds(1_u32), 0, 1); + let handle: Result = baz::spawn_after(1.secs(), 1, 2); let _: Result<(u32, u32), ()> = handle.unwrap().cancel(); loop { diff --git a/macros/src/codegen.rs b/macros/src/codegen.rs index f07326ba47..422de5f37c 100644 --- a/macros/src/codegen.rs +++ b/macros/src/codegen.rs @@ -108,10 +108,6 @@ pub fn app(app: &App, analysis: &Analysis, extra: &Extra) -> TokenStream2 { let name = &monotonic.ident; let name_str = &name.to_string(); let ident = util::monotonic_ident(&name_str); - let panic_str = &format!( - "Use of monotonic '{}' before it was passed to the runtime", - name_str - ); let doc = &format!( "This module holds the static implementation for `{}::now()`", name_str @@ -131,18 +127,13 @@ pub fn app(app: &App, analysis: &Analysis, extra: &Extra) -> TokenStream2 { pub mod #name { /// Read the current time from this monotonic - pub fn now() -> rtic::time::Instant { + pub fn now() -> ::Instant { rtic::export::interrupt::free(|_| { use rtic::Monotonic as _; - use rtic::time::Clock as _; if let Some(m) = unsafe{ &mut *super::super::#ident.get_mut() } { - if let Ok(v) = m.try_now() { - v - } else { - unreachable!("Your monotonic is not infallible!") - } + m.now() } else { - panic!(#panic_str); + ::zero() } }) } diff --git a/macros/src/codegen/module.rs b/macros/src/codegen/module.rs index c59a814c6e..996af64175 100644 --- a/macros/src/codegen/module.rs +++ b/macros/src/codegen/module.rs @@ -266,7 +266,6 @@ pub fn codegen( let tq = util::tq_ident(&monotonic.ident.to_string()); let t = util::schedule_t_ident(); let m = &monotonic.ident; - let mono_type = &monotonic.ident; let m_ident = util::monotonic_ident(&monotonic_name); let m_isr = &monotonic.args.binds; let enum_ = util::interrupt_ident(); @@ -349,15 +348,17 @@ pub fn codegen( } #[inline] - pub fn reschedule_after(self, duration: D) -> Result - where D: rtic::time::duration::Duration + rtic::time::fixed_point::FixedPoint, - D::T: Into<<#mono_type as rtic::time::Clock>::T>, - { + pub fn reschedule_after( + self, + duration: <#m as rtic::Monotonic>::Duration + ) -> Result { self.reschedule_at(monotonics::#m::now() + duration) } - pub fn reschedule_at(self, instant: rtic::time::Instant<#mono_type>) -> Result - { + pub fn reschedule_at( + self, + instant: <#m as rtic::Monotonic>::Instant + ) -> Result { rtic::export::interrupt::free(|_| unsafe { let marker = #tq_marker.get().read(); #tq_marker.get_mut().write(marker.wrapping_add(1)); @@ -375,19 +376,12 @@ pub fn codegen( /// This will use the time `Instant::new(0)` as baseline if called in `#[init]`, /// so if you use a non-resetable timer use `spawn_at` when in `#[init]` #[allow(non_snake_case)] - pub fn #internal_spawn_after_ident( - duration: D + pub fn #internal_spawn_after_ident( + duration: <#m as rtic::Monotonic>::Duration #(,#args)* ) -> Result<#name::#m::SpawnHandle, #ty> - where D: rtic::time::duration::Duration + rtic::time::fixed_point::FixedPoint, - D::T: Into<<#mono_type as rtic::time::Clock>::T>, { - - let instant = if rtic::export::interrupt::free(|_| unsafe { (&*#m_ident.get()).is_none() }) { - rtic::time::Instant::new(0) - } else { - monotonics::#m::now() - }; + let instant = monotonics::#m::now(); #internal_spawn_at_ident(instant + duration #(,#untupled)*) } @@ -396,7 +390,7 @@ pub fn codegen( /// Spawns the task at a fixed time instant #[allow(non_snake_case)] pub fn #internal_spawn_at_ident( - instant: rtic::time::Instant<#mono_type> + instant: <#m as rtic::Monotonic>::Instant #(,#args)* ) -> Result<#name::#m::SpawnHandle, #ty> { unsafe { diff --git a/macros/src/codegen/software_tasks.rs b/macros/src/codegen/software_tasks.rs index 1669a3fb1e..2008b6c98a 100644 --- a/macros/src/codegen/software_tasks.rs +++ b/macros/src/codegen/software_tasks.rs @@ -73,7 +73,7 @@ pub fn codegen( #[allow(non_upper_case_globals)] #[doc(hidden)] static #instants: - rtic::RacyCell<[core::mem::MaybeUninit>; #cap_lit]> = + rtic::RacyCell<[core::mem::MaybeUninit<<#mono_type as rtic::Monotonic>::Instant>; #cap_lit]> = rtic::RacyCell::new([#(#elems,)*]); )); } diff --git a/src/lib.rs b/src/lib.rs index 8463442acf..c96b53c7f7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -38,7 +38,7 @@ use cortex_m::{interrupt::InterruptNumber, peripheral::NVIC}; pub use cortex_m_rtic_macros::app; pub use rtic_core::{prelude as mutex_prelude, Exclusive, Mutex}; -pub use rtic_monotonic::{self, embedded_time as time, Monotonic}; +pub use rtic_monotonic::{self, Monotonic}; #[doc(hidden)] pub mod export; diff --git a/src/tq.rs b/src/tq.rs index 44f8dd4cbb..0121de5fe7 100644 --- a/src/tq.rs +++ b/src/tq.rs @@ -1,19 +1,7 @@ -use crate::{ - time::{Clock, Instant}, - Monotonic, -}; +use crate::Monotonic; use core::cmp::Ordering; use heapless::sorted_linked_list::{LinkedIndexU16, Min, SortedLinkedList}; -#[inline(always)] -fn unwrapper(val: Result) -> T { - if let Ok(v) = val { - v - } else { - unreachable!("Your monotonic is not infallible") - } -} - pub struct TimerQueue( pub SortedLinkedList, LinkedIndexU16, Min, N>, ) @@ -87,7 +75,7 @@ where &mut self, marker: u32, new_marker: u32, - instant: Instant, + instant: Mono::Instant, pend_handler: F, ) -> Result<(), ()> { if let Some(mut val) = self.0.find_mut(|nr| nr.marker == marker) { @@ -111,23 +99,20 @@ where mono.clear_compare_flag(); if let Some(instant) = self.0.peek().map(|p| p.instant) { - let now = unwrapper(Clock::try_now(mono)); - // This if statement is like this and not <= due to a bug in embedded-time - if instant < now || instant == now { + if instant <= mono.now() { // task became ready let nr = unsafe { self.0.pop_unchecked() }; Some((nr.task, nr.index)) } else { // Set compare - mono.set_compare(&instant); + mono.set_compare(instant); // Double check that the instant we set is really in the future, else // 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. - let now = unwrapper(Clock::try_now(mono)); - if instant < now || instant == now { + if instant <= mono.now() { let nr = unsafe { self.0.pop_unchecked() }; Some((nr.task, nr.index)) @@ -153,7 +138,7 @@ where Mono: Monotonic, { pub index: u8, - pub instant: Instant, + pub instant: Mono::Instant, pub task: Task, pub marker: u32, } From 5ab5112271a9dbef6d875ad89706fc726e126b95 Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Tue, 9 Nov 2021 10:35:58 +0100 Subject: [PATCH 065/423] Update versions and changelog --- CHANGELOG.md | 6 +++++- Cargo.toml | 9 ++++----- macros/Cargo.toml | 2 +- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c86bb03e07..9ee854ff4f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,9 @@ This project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +## [v0.6.0-rc.4] - 2021-11-09 + +- Updated to use the new generic `Monotonic` trait ## [v0.6.0-rc.3] - 2021-11-08 @@ -444,7 +447,8 @@ Yanked due to a soundness issue in `init`; the issue has been mostly fixed in v0 - Initial release -[Unreleased]: https://github.com/rtic-rs/cortex-m-rtic/compare/v0.6.0-rc.3...HEAD +[Unreleased]: https://github.com/rtic-rs/cortex-m-rtic/compare/v0.6.0-rc.4...HEAD +[v0.6.0-rc.4]: https://github.com/rtic-rs/cortex-m-rtic/compare/v0.6.0-rc.3...v0.6.0-rc.4 [v0.6.0-rc.3]: https://github.com/rtic-rs/cortex-m-rtic/compare/v0.6.0-rc.2...v0.6.0-rc.3 [v0.6.0-rc.2]: https://github.com/rtic-rs/cortex-m-rtic/compare/v0.6.0-rc.1...v0.6.0-rc.2 [v0.6.0-rc.1]: https://github.com/rtic-rs/cortex-m-rtic/compare/v0.6.0-rc.0...v0.6.0-rc.1 diff --git a/Cargo.toml b/Cargo.toml index 7cdf7f94c4..ed6efbaadd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,16 +14,15 @@ name = "cortex-m-rtic" readme = "README.md" repository = "https://github.com/rtic-rs/cortex-m-rtic" -version = "0.6.0-rc.3" +version = "0.6.0-rc.4" [lib] name = "rtic" [dependencies] cortex-m = "0.7.0" -cortex-m-rtic-macros = { path = "macros", version = "0.6.0-rc.3" } -# rtic-monotonic = "0.1.0-rc.1" -rtic-monotonic = { git = "https://github.com/rtic-rs/rtic-monotonic.git", branch = "master" } +cortex-m-rtic-macros = { path = "macros", version = "0.6.0-rc.4" } +rtic-monotonic = "0.1.0-rc.2" rtic-core = "0.3.1" heapless = "0.7.7" bare-metal = "1.0.0" @@ -34,7 +33,7 @@ version_check = "0.9" [dev-dependencies] lm3s6965 = "0.1.3" cortex-m-semihosting = "0.3.3" -systick-monotonic = { git = "https://github.com/rtic-rs/systick-monotonic.git", branch = "new-trait-and-time-lib" } +systick-monotonic = "0.1.0-rc.2" [dev-dependencies.panic-semihosting] features = ["exit"] diff --git a/macros/Cargo.toml b/macros/Cargo.toml index de335bc444..394c17753f 100644 --- a/macros/Cargo.toml +++ b/macros/Cargo.toml @@ -12,7 +12,7 @@ license = "MIT OR Apache-2.0" name = "cortex-m-rtic-macros" readme = "../README.md" repository = "https://github.com/rtic-rs/cortex-m-rtic" -version = "0.6.0-rc.3" +version = "0.6.0-rc.4" [lib] proc-macro = true From 3b00a2bdb8cc78d1c6572dba1defbf1576ade878 Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Wed, 10 Nov 2021 09:28:03 +0100 Subject: [PATCH 066/423] Updated the monotonic impl chapter --- book/en/src/by-example/tips_monotonic_impl.md | 40 +++++++++++-------- 1 file changed, 23 insertions(+), 17 deletions(-) diff --git a/book/en/src/by-example/tips_monotonic_impl.md b/book/en/src/by-example/tips_monotonic_impl.md index ad04ef0a69..608281a5cf 100644 --- a/book/en/src/by-example/tips_monotonic_impl.md +++ b/book/en/src/by-example/tips_monotonic_impl.md @@ -6,36 +6,42 @@ implement the [`rtic_monotonic::Monotonic`] trait. Implementing time that supports a vast range is generally **very** difficult, and in RTIC 0.5 it was a common problem how to implement time handling and not get stuck in weird special cases. Moreover -it was difficult to understand the relation between time and the timers used for scheduling. From -RTIC 0.6 we have moved to use [`embedded_time`] as the basis for all time-based operation and -abstraction of clocks. This is why from RTIC 0.6 it is almost trivial to implement the `Monotonic` -trait and use any timer in a system for scheduling. +it was difficult to understand the relation between time and the timers used for scheduling. For +RTIC 0.6 we have moved to assume the user has a time library, e.g. [`fugit`] or [`embedded_time`], +as the basis for all time-based operations when implementing `Monotonic`. This is why in RTIC 0.6 +it is almost trivial to implement the `Monotonic` trait and use any timer in a system for scheduling. The trait documents the requirements for each method, however a small PoC implementation is provided below. [`rtic_monotonic::Monotonic`]: https://docs.rs/rtic-monotonic/ +[`fugit`]: https://docs.rs/fugit/ [`embedded_time`]: https://docs.rs/embedded_time/ ```rust -use rtic_monotonic::{embedded_time::clock::Error, Clock, Fraction, Instant, Monotonic}; +pub use fugit::{self, ExtU32}; +use rtic_monotonic::Monotonic; /// Example wrapper struct for a timer -pub struct Timer { +pub struct Timer { tim: TIM2, } -impl Clock for Timer { - const SCALING_FACTOR: Fraction = Fraction::new(1, FREQ); - type T = u32; +impl Monotonic for Timer { + type Instant = fugit::TimerInstantU32; + type Duration = fugit::TimerDurationU32; - #[inline(always)] - fn try_now(&self) -> Result, Error> { - Ok(Instant::new(Self::count())) + fn now(&mut self) -> Self::Instant { + // Read the timer count + Self::Instant::from_ticks(Self::count()) + } + + fn zero() -> Self::Instant { + // This is used while the app is in `#[init]`, if the system cannot + // support time in `#[init]` this can also be a `panic!(..)` + Self::Instant::from_ticks(0) } -} -impl Monotonic for Timer { unsafe fn reset(&mut self) { // Reset timer counter self.tim.cnt.write(|_, w| w.bits(0)); @@ -45,11 +51,11 @@ impl Monotonic for Timer { self.tim.dier.modify(|_, w| w.cc1ie().set_bit()); } - // Use Compare channel 1 for Monotonic - fn set_compare(&mut self, instant: &Instant) { + fn set_compare(&mut self, instant: Instant) { + // Use Compare channel 1 for Monotonic self.tim .ccr1 - .write(|w| w.ccr().bits(instant.duration_since_epoch().integer())); + .write(|w| w.ccr().bits(instant.ticks())); } fn clear_compare_flag(&mut self) { From 1a949b5fd4d0ab95f35701a86e32769650bc1efc Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Wed, 10 Nov 2021 10:25:09 +0100 Subject: [PATCH 067/423] Remove example impl and have a list of example impls instead --- book/en/src/by-example/tips_monotonic_impl.md | 58 ++++--------------- 1 file changed, 11 insertions(+), 47 deletions(-) diff --git a/book/en/src/by-example/tips_monotonic_impl.md b/book/en/src/by-example/tips_monotonic_impl.md index 608281a5cf..fdecfc2b09 100644 --- a/book/en/src/by-example/tips_monotonic_impl.md +++ b/book/en/src/by-example/tips_monotonic_impl.md @@ -11,55 +11,19 @@ RTIC 0.6 we have moved to assume the user has a time library, e.g. [`fugit`] or as the basis for all time-based operations when implementing `Monotonic`. This is why in RTIC 0.6 it is almost trivial to implement the `Monotonic` trait and use any timer in a system for scheduling. -The trait documents the requirements for each method, however a small PoC implementation is provided -below. +The trait documents the requirements for each method, however below you can find a list of +implementations in the wild that can be used as inspiration: + +- [`STM32F411 timers`], implemented for the 32-bit timers +- [`Systick based`], runs at a fixed rate - some overhead but simple +- [`DWT and Systick based`], a more efficient `Systick` based implementation, but requires `DWT` + +If you know of more implementations feel free to add them to this list. [`rtic_monotonic::Monotonic`]: https://docs.rs/rtic-monotonic/ [`fugit`]: https://docs.rs/fugit/ [`embedded_time`]: https://docs.rs/embedded_time/ +[`STM32F411 timers`]: https://github.com/kalkyl/f411-rtic/blob/main/src/bin/mono.rs +[`Systick based`]: https://github.com/rtic-rs/systick-monotonic +[`DWT and Systick based`]: https://github.com/rtic-rs/dwt-systick-monotonic -```rust -pub use fugit::{self, ExtU32}; -use rtic_monotonic::Monotonic; - -/// Example wrapper struct for a timer -pub struct Timer { - tim: TIM2, -} - -impl Monotonic for Timer { - type Instant = fugit::TimerInstantU32; - type Duration = fugit::TimerDurationU32; - - fn now(&mut self) -> Self::Instant { - // Read the timer count - Self::Instant::from_ticks(Self::count()) - } - - fn zero() -> Self::Instant { - // This is used while the app is in `#[init]`, if the system cannot - // support time in `#[init]` this can also be a `panic!(..)` - Self::Instant::from_ticks(0) - } - - unsafe fn reset(&mut self) { - // Reset timer counter - self.tim.cnt.write(|_, w| w.bits(0)); - - // Since reset is only called once, we use it to enable - // the interrupt generation bit. - self.tim.dier.modify(|_, w| w.cc1ie().set_bit()); - } - - fn set_compare(&mut self, instant: Instant) { - // Use Compare channel 1 for Monotonic - self.tim - .ccr1 - .write(|w| w.ccr().bits(instant.ticks())); - } - - fn clear_compare_flag(&mut self) { - self.tim.sr.modify(|_, w| w.cc1if().clear_bit()); - } -} -``` From c060b5a15c1984437a67cf31f76def97b0dd9a6e Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Wed, 10 Nov 2021 13:46:34 +0100 Subject: [PATCH 068/423] Example monotonic for nRF52 --- book/en/src/by-example/tips_monotonic_impl.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/book/en/src/by-example/tips_monotonic_impl.md b/book/en/src/by-example/tips_monotonic_impl.md index fdecfc2b09..210a08e669 100644 --- a/book/en/src/by-example/tips_monotonic_impl.md +++ b/book/en/src/by-example/tips_monotonic_impl.md @@ -14,7 +14,8 @@ it is almost trivial to implement the `Monotonic` trait and use any timer in a s The trait documents the requirements for each method, however below you can find a list of implementations in the wild that can be used as inspiration: -- [`STM32F411 timers`], implemented for the 32-bit timers +- [`STM32F411 series`], implemented for the 32-bit timers +- [`Nordic nRF52 series`], implemented for the 32-bit timers - [`Systick based`], runs at a fixed rate - some overhead but simple - [`DWT and Systick based`], a more efficient `Systick` based implementation, but requires `DWT` @@ -23,7 +24,8 @@ If you know of more implementations feel free to add them to this list. [`rtic_monotonic::Monotonic`]: https://docs.rs/rtic-monotonic/ [`fugit`]: https://docs.rs/fugit/ [`embedded_time`]: https://docs.rs/embedded_time/ -[`STM32F411 timers`]: https://github.com/kalkyl/f411-rtic/blob/main/src/bin/mono.rs +[`STM32F411 series`]: https://github.com/kalkyl/f411-rtic/blob/main/src/bin/mono.rs +[`Nordic nRF52 series`]: https://github.com/kalkyl/nrf-play/blob/main/src/bin/mono.rs [`Systick based`]: https://github.com/rtic-rs/systick-monotonic [`DWT and Systick based`]: https://github.com/rtic-rs/dwt-systick-monotonic From 6f2aa08910cbe7c41ae877bdbf6eb3dc3cad5f22 Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Thu, 11 Nov 2021 14:22:47 +0100 Subject: [PATCH 069/423] Better errors on when missing to lock shared resources --- macros/src/codegen/shared_resources.rs | 10 ++++++---- macros/src/codegen/shared_resources_struct.rs | 5 +++-- macros/src/codegen/util.rs | 7 +++++++ 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/macros/src/codegen/shared_resources.rs b/macros/src/codegen/shared_resources.rs index b27c827c23..ddd3824c89 100644 --- a/macros/src/codegen/shared_resources.rs +++ b/macros/src/codegen/shared_resources.rs @@ -43,21 +43,23 @@ pub fn codegen( // For future use // let doc = format!(" RTIC internal: {}:{}", file!(), line!()); + let shared_name = util::need_to_lock_ident(name); + if !res.properties.lock_free { mod_resources.push(quote!( // #[doc = #doc] #[doc(hidden)] #[allow(non_camel_case_types)] #(#cfgs)* - pub struct #name<'a> { + pub struct #shared_name<'a> { priority: &'a Priority, } #(#cfgs)* - impl<'a> #name<'a> { + impl<'a> #shared_name<'a> { #[inline(always)] pub unsafe fn new(priority: &'a Priority) -> Self { - #name { priority } + #shared_name { priority } } #[inline(always)] @@ -86,7 +88,7 @@ pub fn codegen( extra, cfgs, true, - &name, + &shared_name, quote!(#ty), ceiling, ptr, diff --git a/macros/src/codegen/shared_resources_struct.rs b/macros/src/codegen/shared_resources_struct.rs index 9983aa4c8d..6122651795 100644 --- a/macros/src/codegen/shared_resources_struct.rs +++ b/macros/src/codegen/shared_resources_struct.rs @@ -33,6 +33,7 @@ pub fn codegen(ctxt: Context, needs_lt: &mut bool, app: &App) -> (TokenStream2, }; let ty = &res.ty; let mangled_name = util::static_shared_resource_ident(&name); + let shared_name = util::need_to_lock_ident(name); if !res.properties.lock_free { if access.is_shared() { @@ -48,12 +49,12 @@ pub fn codegen(ctxt: Context, needs_lt: &mut bool, app: &App) -> (TokenStream2, fields.push(quote!( #(#cfgs)* - pub #name: shared_resources::#name<'a> + pub #name: shared_resources::#shared_name<'a> )); values.push(quote!( #(#cfgs)* - #name: shared_resources::#name::new(priority) + #name: shared_resources::#shared_name::new(priority) )); diff --git a/macros/src/codegen/util.rs b/macros/src/codegen/util.rs index 8e40ad6129..831718aed9 100644 --- a/macros/src/codegen/util.rs +++ b/macros/src/codegen/util.rs @@ -268,6 +268,13 @@ pub fn declared_static_local_resource_ident(name: &Ident, task_name: &Ident) -> )) } +pub fn need_to_lock_ident(name: &Ident) -> Ident { + Ident::new( + &format!("{}_that_needs_to_be_locked", name.to_string()), + name.span(), + ) +} + /// The name to get better RT flag errors pub fn rt_err_ident() -> Ident { Ident::new( From 2e5c6f8e36afca64b307cde04cf33e3d068be769 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Tj=C3=A4der?= Date: Thu, 25 Nov 2021 10:04:37 +0100 Subject: [PATCH 070/423] Docs: add RTIC logo --- macros/src/lib.rs | 5 +++++ src/lib.rs | 4 ++++ 2 files changed, 9 insertions(+) diff --git a/macros/src/lib.rs b/macros/src/lib.rs index 6ac7e2a4ad..02d472d180 100644 --- a/macros/src/lib.rs +++ b/macros/src/lib.rs @@ -1,5 +1,10 @@ // #![deny(warnings)] +#![doc( + html_logo_url = "https://raw.githubusercontent.com/rtic-rs/cortex-m-rtic/master/book/en/src/RTIC.svg", + html_favicon_url = "https://raw.githubusercontent.com/rtic-rs/cortex-m-rtic/master/book/en/src/RTIC.svg" +)] + extern crate proc_macro; use proc_macro::TokenStream; diff --git a/src/lib.rs b/src/lib.rs index c96b53c7f7..71cc86b2c9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -34,6 +34,10 @@ #![deny(rust_2018_idioms)] #![deny(warnings)] #![no_std] +#![doc( + html_logo_url = "https://raw.githubusercontent.com/rtic-rs/cortex-m-rtic/master/book/en/src/RTIC.svg", + html_favicon_url = "https://raw.githubusercontent.com/rtic-rs/cortex-m-rtic/master/book/en/src/RTIC.svg" +)] use cortex_m::{interrupt::InterruptNumber, peripheral::NVIC}; pub use cortex_m_rtic_macros::app; From 3741d431bed5a4799c7fdb20de5950a0964569e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Tj=C3=A4der?= Date: Thu, 25 Nov 2021 10:46:29 +0100 Subject: [PATCH 071/423] Remove #[deny(warnings)], but deny warnings for CI --- .github/workflows/build.yml | 17 +++++++++++++++++ macros/src/lib.rs | 3 +-- src/lib.rs | 2 +- 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 438bedf4f7..d7c595e7e5 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -27,6 +27,9 @@ jobs: override: true components: rustfmt + - name: Fail on warnings + run: sed -i 's,//deny_warnings_placeholder_for_ci,#![deny(warnings)],' src/lib.rs macros/src/lib.rs + - name: cargo fmt --check uses: actions-rs/cargo@v1 with: @@ -78,6 +81,9 @@ jobs: target: ${{ matrix.target }} override: true + - name: Fail on warnings + run: sed -i 's,//deny_warnings_placeholder_for_ci,#![deny(warnings)],' src/lib.rs macros/src/lib.rs + - name: cargo check uses: actions-rs/cargo@v1 with: @@ -196,6 +202,8 @@ jobs: sudo apt update sudo apt install -y qemu-system-arm + - name: Fail on warnings + run: sed -i 's,//deny_warnings_placeholder_for_ci,#![deny(warnings)],' src/lib.rs macros/src/lib.rs - name: Run-pass tests run: @@ -244,6 +252,9 @@ jobs: target: ${{ matrix.target }} override: true + - name: Fail on warnings + run: sed -i 's,//deny_warnings_placeholder_for_ci,#![deny(warnings)],' src/lib.rs macros/src/lib.rs + - name: cargo check uses: actions-rs/cargo@v1 with: @@ -294,6 +305,9 @@ jobs: target: ${{ matrix.target }} override: true + - name: Fail on warnings + run: sed -i 's,//deny_warnings_placeholder_for_ci,#![deny(warnings)],' src/lib.rs macros/src/lib.rs + - name: cargo check uses: actions-rs/cargo@v1 with: @@ -403,6 +417,9 @@ jobs: - name: Remove cargo-config run: rm -f .cargo/config + - name: Fail on warnings + run: sed -i 's,//deny_warnings_placeholder_for_ci,#![deny(warnings)],' src/lib.rs macros/src/lib.rs + - name: Build docs run: cargo doc diff --git a/macros/src/lib.rs b/macros/src/lib.rs index 02d472d180..adcd731680 100644 --- a/macros/src/lib.rs +++ b/macros/src/lib.rs @@ -1,9 +1,8 @@ -// #![deny(warnings)] - #![doc( html_logo_url = "https://raw.githubusercontent.com/rtic-rs/cortex-m-rtic/master/book/en/src/RTIC.svg", html_favicon_url = "https://raw.githubusercontent.com/rtic-rs/cortex-m-rtic/master/book/en/src/RTIC.svg" )] +//deny_warnings_placeholder_for_ci extern crate proc_macro; diff --git a/src/lib.rs b/src/lib.rs index 71cc86b2c9..5fe35a95e2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -32,12 +32,12 @@ #![deny(missing_docs)] #![deny(rust_2018_compatibility)] #![deny(rust_2018_idioms)] -#![deny(warnings)] #![no_std] #![doc( html_logo_url = "https://raw.githubusercontent.com/rtic-rs/cortex-m-rtic/master/book/en/src/RTIC.svg", html_favicon_url = "https://raw.githubusercontent.com/rtic-rs/cortex-m-rtic/master/book/en/src/RTIC.svg" )] +//deny_warnings_placeholder_for_ci use cortex_m::{interrupt::InterruptNumber, peripheral::NVIC}; pub use cortex_m_rtic_macros::app; From 8c8f7f12c3bfc132b7fad9df80559e474ed66b66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Tj=C3=A4der?= Date: Tue, 14 Dec 2021 21:52:57 +0100 Subject: [PATCH 072/423] Idle: Switch to NOP instead of WFI Add example how to get old WFI behaviour --- book/en/src/by-example/app_idle.md | 31 ++++++++++++++++---- ci/expected/idle-wfi.run | 2 ++ examples/idle-wfi.rs | 47 ++++++++++++++++++++++++++++++ macros/src/codegen/idle.rs | 2 +- macros/src/codegen/pre_init.rs | 7 ----- src/export.rs | 1 + 6 files changed, 77 insertions(+), 13 deletions(-) create mode 100644 ci/expected/idle-wfi.run create mode 100644 examples/idle-wfi.rs diff --git a/book/en/src/by-example/app_idle.md b/book/en/src/by-example/app_idle.md index 1eb1472204..66f40497fb 100644 --- a/book/en/src/by-example/app_idle.md +++ b/book/en/src/by-example/app_idle.md @@ -8,11 +8,6 @@ When present, the runtime will execute the `idle` task after `init`. Unlike `init`, `idle` will run *with interrupts enabled* and it's not allowed to return so it must run forever. -When no `idle` function is declared, the runtime sets the [SLEEPONEXIT] bit and -then sends the microcontroller to sleep after running `init`. - -[SLEEPONEXIT]: https://developer.arm.com/docs/100737/0100/power-management/sleep-mode/sleep-on-exit-bit - Like in `init`, locally declared resources will have `'static` lifetimes that are safe to access. The example below shows that `idle` runs after `init`. @@ -25,3 +20,29 @@ The example below shows that `idle` runs after `init`. $ cargo run --target thumbv7m-none-eabi --example idle {{#include ../../../../ci/expected/idle.run}} ``` + +By default the RTIC `idle` task does not try to optimise for any specific targets. + +A common useful optimisation is to enable the [SLEEPONEXIT] and allow the MCU +to enter sleep when reaching `idle`. + +>**Caution** some hardware unless configured disables the debug unit during sleep mode. +> +>Consult your hardware specific documentation as this is outside the scope of RTIC. + +The following example shows how to enable sleep by setting the +[`SLEEPONEXIT`][SLEEPONEXIT] and providing a custom `idle` task replacing the +default [`nop()`][NOP] with [`wfi()`][WFI]. + +[SLEEPONEXIT]: https://developer.arm.com/docs/100737/0100/power-management/sleep-mode/sleep-on-exit-bit +[WFI]: https://developer.arm.com/documentation/dui0662/b/The-Cortex-M0--Instruction-Set/Miscellaneous-instructions/WFI +[NOP]: https://developer.arm.com/documentation/dui0662/b/The-Cortex-M0--Instruction-Set/Miscellaneous-instructions/NOP + +``` rust +{{#include ../../../../examples/idle-wfi.rs}} +``` + +``` console +$ cargo run --target thumbv7m-none-eabi --example idle-wfi +{{#include ../../../../ci/expected/idle-wfi.run}} +``` diff --git a/ci/expected/idle-wfi.run b/ci/expected/idle-wfi.run new file mode 100644 index 0000000000..4307776304 --- /dev/null +++ b/ci/expected/idle-wfi.run @@ -0,0 +1,2 @@ +init +idle diff --git a/examples/idle-wfi.rs b/examples/idle-wfi.rs new file mode 100644 index 0000000000..4a8a8dee2b --- /dev/null +++ b/examples/idle-wfi.rs @@ -0,0 +1,47 @@ +//! examples/idle-wfi.rs + +#![deny(unsafe_code)] +#![deny(warnings)] +#![no_main] +#![no_std] + +use panic_semihosting as _; + +#[rtic::app(device = lm3s6965)] +mod app { + use cortex_m_semihosting::{debug, hprintln}; + + #[shared] + struct Shared {} + + #[local] + struct Local {} + + #[init] + fn init(mut cx: init::Context) -> (Shared, Local, init::Monotonics) { + hprintln!("init").unwrap(); + + // Set the ARM SLEEPONEXIT bit to go to sleep after handling interrupts + // See https://developer.arm.com/docs/100737/0100/power-management/sleep-mode/sleep-on-exit-bit + cx.core.SCB.set_sleepdeep(); + + (Shared {}, Local {}, init::Monotonics()) + } + + #[idle(local = [x: u32 = 0])] + fn idle(cx: idle::Context) -> ! { + // Locals in idle have lifetime 'static + let _x: &'static mut u32 = cx.local.x; + + hprintln!("idle").unwrap(); + + debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator + + loop { + // Now Wait For Interrupt is used instead of a busy-wait loop + // to allow MCU to sleep between interrupts + // https://developer.arm.com/documentation/ddi0406/c/Application-Level-Architecture/Instruction-Details/Alphabetical-list-of-instructions/WFI + rtic::export::wfi() + } + } +} diff --git a/macros/src/codegen/idle.rs b/macros/src/codegen/idle.rs index d653931655..0dededa4e6 100644 --- a/macros/src/codegen/idle.rs +++ b/macros/src/codegen/idle.rs @@ -85,7 +85,7 @@ pub fn codegen( vec![], None, quote!(loop { - rtic::export::wfi() + rtic::export::nop() }), ) } diff --git a/macros/src/codegen/pre_init.rs b/macros/src/codegen/pre_init.rs index 42cc055298..7aaf20fc79 100644 --- a/macros/src/codegen/pre_init.rs +++ b/macros/src/codegen/pre_init.rs @@ -122,12 +122,5 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec Date: Tue, 14 Dec 2021 22:19:16 +0100 Subject: [PATCH 073/423] Remove note about SLEEPONEXIT in Russian book --- book/ru/src/by-example/app.md | 5 ----- 1 file changed, 5 deletions(-) diff --git a/book/ru/src/by-example/app.md b/book/ru/src/by-example/app.md index c2b518fd88..5259bfa08e 100644 --- a/book/ru/src/by-example/app.md +++ b/book/ru/src/by-example/app.md @@ -70,11 +70,6 @@ $ cargo run --example init `init`, `idle` будет запущена *с включенными прерываниями* и она не может вернуть результат, а значит должна работать вечно. -Если функция `idle` не определена, среда вполнения устанавливает бит [SLEEPONEXIT], а затем -отправляет микроконтроллер в сон после запуска `init`. - -[SLEEPONEXIT]: https://developer.arm.com/docs/100737/0100/Power-management/Sleep-mode/Sleep-on-exit-bit - Как и в `init`, `static mut` переменные будут трансформированы в `&'static mut` ссылки, безопасные для доступа. Обратите внимание, данная возможность может быть удалена в следующем релизе, см. `task_local` ресурсы. From d81a4da6fb759c5561eacf4d99d953526e1ee895 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Tj=C3=A4der?= Date: Tue, 14 Dec 2021 22:26:16 +0100 Subject: [PATCH 074/423] Update changelog --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9ee854ff4f..11fab90a83 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,10 @@ This project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +### Changed + +- Change default `idle` behaviour to be `NOP` instead of `WFI` + ## [v0.6.0-rc.4] - 2021-11-09 - Updated to use the new generic `Monotonic` trait From 4357d8be1511d28ed16f76439c9af60e78504b28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Tj=C3=A4der?= Date: Tue, 14 Dec 2021 22:46:15 +0100 Subject: [PATCH 075/423] Docs: By-example --- book/en/src/by-example.md | 14 ++++---- book/en/src/by-example/app.md | 14 ++++---- book/en/src/by-example/app_idle.md | 18 +++++++---- book/en/src/by-example/app_init.md | 22 +++++++++---- book/en/src/by-example/app_task.md | 15 +++++++-- book/en/src/by-example/hardware_tasks.md | 25 ++++++++------- book/en/src/by-example/resources.md | 41 +++++++++++++----------- book/en/src/preface.md | 2 +- 8 files changed, 90 insertions(+), 61 deletions(-) diff --git a/book/en/src/by-example.md b/book/en/src/by-example.md index fef6872e49..84f00193ae 100644 --- a/book/en/src/by-example.md +++ b/book/en/src/by-example.md @@ -3,15 +3,15 @@ This part of the book introduces the Real-Time Interrupt-driven Concurrency (RTIC) framework to new users by walking them through examples of increasing complexity. -All examples in this part of the book can be found in the GitHub [repository] of -the project. The examples can be run on QEMU (emulating a Cortex M3 target) so no special hardware -is required to follow along. +All examples in this part of the book are accessible at the +[GitHub repository][repoexamples]. +The examples are runnable on QEMU (emulating a Cortex M3 target), +thus no special hardware required to follow along. -[repository]: https://github.com/rtic-rs/cortex-m-rtic +[repoexamples]: https://github.com/rtic-rs/cortex-m-rtic/tree/master/examples -To run the examples on your computer you'll need the `qemu-system-arm` -program. Check [the embedded Rust book] for instructions on how to set up an +To run the examples with QEMU you will need the `qemu-system-arm` program. +Check [the embedded Rust book] for instructions on how to set up an embedded development environment that includes QEMU. [the embedded Rust book]: https://rust-embedded.github.io/book/intro/install.html - diff --git a/book/en/src/by-example/app.md b/book/en/src/by-example/app.md index 09f3371e26..2c6aca7a2b 100644 --- a/book/en/src/by-example/app.md +++ b/book/en/src/by-example/app.md @@ -3,14 +3,14 @@ ## Requirements on the `app` attribute All RTIC applications use the [`app`] attribute (`#[app(..)]`). This attribute -must be applied to a `mod`-item containing the RTIC application. The `app` -attribute has a mandatory `device` -argument that takes a *path* as a value. This must be a full path pointing to a +only applies to a `mod`-item containing the RTIC application. The `app` +attribute has a mandatory `device` argument that takes a *path* as a value. +This must be a full path pointing to a *peripheral access crate* (PAC) generated using [`svd2rust`] **v0.14.x** or newer. -The `app` attribute will expand into a suitable entry point so it's not required -to use the [`cortex_m_rt::entry`] attribute. +The `app` attribute will expand into a suitable entry point and thus replaces +the use of the [`cortex_m_rt::entry`] attribute. [`app`]: ../../../api/cortex_m_rtic_macros/attr.app.html [`svd2rust`]: https://crates.io/crates/svd2rust @@ -18,9 +18,9 @@ to use the [`cortex_m_rt::entry`] attribute. ## An RTIC application example -To give a flavor of RTIC, the following example contains commonly used features. In the following sections we will go through each feature in detail. +To give a flavour of RTIC, the following example contains commonly used features. +In the following sections we will go through each feature in detail. ``` rust {{#include ../../../../examples/common.rs}} ``` - diff --git a/book/en/src/by-example/app_idle.md b/book/en/src/by-example/app_idle.md index 66f40497fb..537902a442 100644 --- a/book/en/src/by-example/app_idle.md +++ b/book/en/src/by-example/app_idle.md @@ -1,14 +1,18 @@ # The background task `#[idle]` A function marked with the `idle` attribute can optionally appear in the -module. This function is used as the special *idle task* and must have -signature `fn(idle::Context) -> !`. +module. This becomes the special *idle task* and must have signature +`fn(idle::Context) -> !`. When present, the runtime will execute the `idle` task after `init`. Unlike -`init`, `idle` will run *with interrupts enabled* and it's not allowed to return -so it must run forever. +`init`, `idle` will run *with interrupts enabled* and must never return, +as the `-> !` function signature indicates. +[The Rust type `!` means “never”][nevertype]. -Like in `init`, locally declared resources will have `'static` lifetimes that are safe to access. +[nevertype]: https://doc.rust-lang.org/core/primitive.never.html + +Like in `init`, locally declared resources will have `'static` lifetimes that +are safe to access. The example below shows that `idle` runs after `init`. @@ -21,9 +25,9 @@ $ cargo run --target thumbv7m-none-eabi --example idle {{#include ../../../../ci/expected/idle.run}} ``` -By default the RTIC `idle` task does not try to optimise for any specific targets. +By default, the RTIC `idle` task does not try to optimize for any specific targets. -A common useful optimisation is to enable the [SLEEPONEXIT] and allow the MCU +A common useful optimization is to enable the [SLEEPONEXIT] and allow the MCU to enter sleep when reaching `idle`. >**Caution** some hardware unless configured disables the debug unit during sleep mode. diff --git a/book/en/src/by-example/app_init.md b/book/en/src/by-example/app_init.md index 3112ccf9e1..615c299102 100644 --- a/book/en/src/by-example/app_init.md +++ b/book/en/src/by-example/app_init.md @@ -1,14 +1,22 @@ # App initialization and the `#[init]` task -An RTIC application is required an `init` task setting up the system. The corresponding function must have the signature `fn(init::Context) -> (Shared, Local, init::Monotonics)`, where `Shared` and `Local` are the resource structures defined by the user. +An RTIC application requires an `init` task setting up the system. The corresponding `init` function must have the +signature `fn(init::Context) -> (Shared, Local, init::Monotonics)`, where `Shared` and `Local` are the resource +structures defined by the user. -On system reset, the `init` task is executed (after the optionally defined `pre-init` and internal RTIC initialization). The `init` task runs *with interrupts disabled* and has exclusive access to Cortex-M (the `bare_metal::CriticalSection` token is available as `cs`) while device specific peripherals are available through the `core` and `device` fields of `init::Context`. +The `init` task executes after system reset (after the optionally defined `pre-init` and internal RTIC +initialization). The `init` task runs *with interrupts disabled* and has exclusive access to Cortex-M (the +`bare_metal::CriticalSection` token is available as `cs`) while device specific peripherals are available through +the `core` and `device` fields of `init::Context`. ## Example -The example below shows the types of the `core`, `device` and `cs` fields, and showcases the use of a `local` variable with `'static` lifetime. As we will see later, such variables can later be delegated from `init` to other tasks of the RTIC application. +The example below shows the types of the `core`, `device` and `cs` fields, and showcases the use of a `local` +variable with `'static` lifetime. +Such variables can be delegated from the `init` task to other tasks of the RTIC application. -The `device` field is only available when the `peripherals` argument is set to `true` (which is the default). In the rare case you want to implement an ultra-slim application you can explicitly set `peripherals` to `false`. +The `device` field is available when the `peripherals` argument is set to the default value `true`. +In the rare case you want to implement an ultra-slim application you can explicitly set `peripherals` to `false`. ``` rust {{#include ../../../../examples/init.rs}} @@ -16,13 +24,13 @@ The `device` field is only available when the `peripherals` argument is set to ` Running the example will print `init` to the console and then exit the QEMU process. -``` console +``` console $ cargo run --target thumbv7m-none-eabi --example init {{#include ../../../../ci/expected/init.run}} ``` > **NOTE**: You can choose target device by passing a target -> triple to cargo (e.g `cargo run --example init --target thumbv7m-none-eabi`) or +> triple to cargo (e.g. `cargo run --example init --target thumbv7m-none-eabi`) or > configure a default target in `.cargo/config.toml`. > -> For running the examples, we use a Cortex M3 emulated in QEMU so the target is `thumbv7m-none-eabi`. +> For running the examples, we use a Cortex M3 emulated in QEMU, so the target is `thumbv7m-none-eabi`. diff --git a/book/en/src/by-example/app_task.md b/book/en/src/by-example/app_task.md index a5c8b171a2..97160041e3 100644 --- a/book/en/src/by-example/app_task.md +++ b/book/en/src/by-example/app_task.md @@ -1,7 +1,18 @@ # Defining tasks with `#[task]` -Tasks, defined with `#[task]`, are the main mechanism of getting work done in RTIC. Every task can be spawned, now or later, be sent messages (message passing) and be given priorities for preemptive multitasking. +Tasks, defined with `#[task]`, are the main mechanism of getting work done in RTIC. -There are two kinds of tasks, software tasks and hardware tasks, and the difference is that hardware tasks are bound to a specific interrupt vector in the MCU while software tasks are not. This means that if a hardware task is bound to the UART's RX interrupt the task will run every time a character is received. +Tasks can + +* Be spawned (now or in the future) +* Receive messages (message passing) +* Prioritized allowing preemptive multitasking +* Optionally bind to a hardware interrupt + +RTIC makes a distinction between “software tasks” and “hardware tasks”. +Hardware tasks are tasks that are bound to a specific interrupt vector in the MCU while software tasks are not. + +This means that if a hardware task is bound to an UART RX interrupt the task will run every +time this interrupt triggers, usually when a character is received. In the coming pages we will explore both tasks and the different options available. diff --git a/book/en/src/by-example/hardware_tasks.md b/book/en/src/by-example/hardware_tasks.md index d5968761dc..30b88d0df8 100644 --- a/book/en/src/by-example/hardware_tasks.md +++ b/book/en/src/by-example/hardware_tasks.md @@ -1,17 +1,21 @@ # Hardware tasks -At its core RTIC is based on using the interrupt controller in the hardware to do scheduling and -run tasks, as all tasks in the framework are run as interrupt handlers (except `#[init]` and -`#[idle]`). This also means that you can directly bind tasks to interrupt handlers. +At its core RTIC is using the hardware interrupt controller ([ARM NVIC on cortex-m][NVIC]) +to perform scheduling and executing tasks, and all tasks except `#[init]` and `#[idle]` +run as interrupt handlers. +This also means that you can manually bind tasks to interrupt handlers. -To declare interrupt handlers the `#[task]` attribute takes a `binds = InterruptName` argument whose -value is the name of the interrupt to which the handler will be bound to; the -function used with this attribute becomes the interrupt handler. Within the -framework these type of tasks are referred to as *hardware* tasks, because they -start executing in reaction to a hardware event. +To bind an interrupt use the `#[task]` attribute argument `binds = InterruptName`. +This task becomes the interrupt handler for this hardware interrupt vector. -Providing an interrupt name that does not exist will cause a compile error to help with accidental -errors. +All tasks bound to an explicit interrupt are *hardware tasks* since they +start execution in reaction to a hardware event. + +Specifying a non-existing interrupt name will cause a compilation error. The interrupt names +are commonly defined by [PAC or HAL][pacorhal] crates. + +[pacorhal]: https://docs.rust-embedded.org/book/start/registers.html +[NVIC]: https://developer.arm.com/documentation/100166/0001/Nested-Vectored-Interrupt-Controller/NVIC-functional-description/NVIC-interrupts The example below demonstrates the use of the `#[task]` attribute to declare an interrupt handler. @@ -24,4 +28,3 @@ interrupt handler. $ cargo run --target thumbv7m-none-eabi --example hardware {{#include ../../../../ci/expected/hardware.run}} ``` - diff --git a/book/en/src/by-example/resources.md b/book/en/src/by-example/resources.md index 71092b2fd2..9f2c6c577f 100644 --- a/book/en/src/by-example/resources.md +++ b/book/en/src/by-example/resources.md @@ -1,22 +1,22 @@ # Resource usage -The RTIC framework manages shared and task local resources which allows data to be persistently -stored and safely accessed without the use of unsafe code. +The RTIC framework manages shared and task local resources allowing persistent data +storage and safe accesses without the use of `unsafe` code. RTIC resources are visible only to functions declared within the `#[app]` module and the framework gives the user complete control (on a per-task basis) over resource accessibility. -System wide resources are declared as **two** `struct`'s within the `#[app]` module annotated with -the attribute `#[local]` and `#[shared]` respectively. Each field in these structures corresponds -to a different resource (identified by field name). The difference between these two sets of -resources will be covered below. +Declaration of system-wide resources are by annotating **two** `struct`s within the `#[app]` module +with the attribute `#[local]` and `#[shared]`. +Each field in these structures corresponds to a different resource (identified by field name). +The difference between these two sets of resources will be covered below. Each task must declare the resources it intends to access in its corresponding metadata attribute -using the `local` and `shared` arguments. Each argument takes a list of resource identifiers. The -listed resources are made available to the context under the `local` and `shared` fields of the +using the `local` and `shared` arguments. Each argument takes a list of resource identifiers. +The listed resources are made available to the context under the `local` and `shared` fields of the `Context` structure. -The `init` task returns the initial values for the system wide (`#[shared]` and `#[local]`) +The `init` task returns the initial values for the system-wide (`#[shared]` and `#[local]`) resources, and the set of initialized timers used by the application. The monotonic timers will be further discussed in [Monotonic & `spawn_{at/after}`](./monotonic.md). @@ -27,6 +27,9 @@ access the resource and does so without locks or critical sections. This allows commonly drivers or large objects, to be initialized in `#[init]` and then be passed to a specific task. +Thus, a task `#[local]` resource can only be accessed by one singular task. +Attempting to assign the same `#[local]` resource to more than one task is a compile-time error. + The example application shown below contains two tasks where each task has access to its own `#[local]` resource, plus that the `idle` task has its own `#[local]` as well. @@ -39,15 +42,12 @@ $ cargo run --target thumbv7m-none-eabi --example locals {{#include ../../../../ci/expected/locals.run}} ``` -A `#[local]` resource cannot be accessed from outside the task it was associated to in a `#[task]` attribute. -Assigning the same `#[local]` resource to more than one task is a compile-time error. - ### Task local initialized resources A special use-case of local resources are the ones specified directly in the resource claim, `#[task(local = [my_var: TYPE = INITIAL_VALUE, ...])]`, this allows for creating locals which do no need to be initialized in `#[init]`. -Moreover local resources in `#[init]` and `#[idle]` have `'static` lifetimes, this is safe since both are not re-entrant. +Moreover, local resources in `#[init]` and `#[idle]` have `'static` lifetimes, this is safe since both are not re-entrant. In the example below the different uses and lifetimes are shown: @@ -96,7 +96,7 @@ $ cargo run --target thumbv7m-none-eabi --example lock ## Multi-lock As an extension to `lock`, and to reduce rightward drift, locks can be taken as tuples. The -following examples shows this in use: +following examples show this in use: ``` rust {{#include ../../../../examples/multilock.rs}} @@ -109,12 +109,12 @@ $ cargo run --target thumbv7m-none-eabi --example multilock ## Only shared (`&-`) access -By default the framework assumes that all tasks require exclusive access (`&mut-`) to resources but -it is possible to specify that a task only requires shared access (`&-`) to a resource using the +By default, the framework assumes that all tasks require exclusive access (`&mut-`) to resources, +but it is possible to specify that a task only requires shared access (`&-`) to a resource using the `&resource_name` syntax in the `shared` list. The advantage of specifying shared access (`&-`) to a resource is that no locks are required to -access the resource even if the resource is contended by several tasks running at different +access the resource even if the resource is contended by more than one task running at different priorities. The downside is that the task only gets a shared reference (`&-`) to the resource, limiting the operations it can perform on it, but where a shared reference is enough this approach reduces the number of required locks. In addition to simple immutable data, this shared access can @@ -142,8 +142,11 @@ $ cargo run --target thumbv7m-none-eabi --example only-shared-access A critical section is *not* required to access a `#[shared]` resource that's only accessed by tasks running at the *same* priority. In this case, you can opt out of the `lock` API by adding the `#[lock_free]` field-level attribute to the resource declaration (see example below). Note that -this is merely a convenience: if you do use the `lock` API, at runtime the framework will -**not** produce a critical section. Also worth noting: using `#[lock_free]` on resources shared by +this is merely a convenience to reduce needless resource locking code, because even if the +`lock` API is used, at runtime the framework will **not** produce a critical section due to how +the underlying resource-ceiling preemption works. + +Also worth noting: using `#[lock_free]` on resources shared by tasks running at different priorities will result in a *compile-time* error -- not using the `lock` API would be a data race in that case. diff --git a/book/en/src/preface.md b/book/en/src/preface.md index e81542c997..7ad33e1423 100644 --- a/book/en/src/preface.md +++ b/book/en/src/preface.md @@ -8,7 +8,7 @@ # Preface This book contains user level documentation for the Real-Time Interrupt-driven Concurrency -(RTIC) framework. The API reference can be found [here](../../api/). +(RTIC) framework. The API reference available [here](../../api/). Formerly known as Real-Time For the Masses. From 2ac0e1b29ddbe4fdc4e9b67b486eeb69a106e9c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Tj=C3=A4der?= Date: Thu, 16 Dec 2021 09:46:56 +0100 Subject: [PATCH 076/423] Docs: By-example Software tasks --- book/en/src/by-example/software_tasks.md | 36 +++++++++++++++--------- 1 file changed, 23 insertions(+), 13 deletions(-) diff --git a/book/en/src/by-example/software_tasks.md b/book/en/src/by-example/software_tasks.md index f78efea9c3..370792f841 100644 --- a/book/en/src/by-example/software_tasks.md +++ b/book/en/src/by-example/software_tasks.md @@ -1,21 +1,31 @@ # Software tasks & spawn -Software tasks, as hardware tasks, are run as interrupt handlers where all software tasks at the -same priority shares a "free" interrupt handler to run from, called a dispatcher. These free -interrupts are interrupt vectors not used by hardware tasks. +Software tasks are tasks which are not directly assigned to a specific interrupt vector. -To declare tasks in the framework the `#[task]` attribute is used on a function. -By default these tasks are referred to as software tasks as they do not have a direct coupling to -an interrupt handler. Software tasks can be spawned (started) using the `task_name::spawn()` static -method which will directly run the task given that there are no higher priority tasks running. +They run as interrupt handlers where all software tasks at the +same priority level shares a "free" interrupt handler acting as a dispatcher. +Thus, what differentiates software and hardware tasks are the dispatcher versus +bound interrupt vector. -To indicate to the framework which interrupts are free for use to dispatch software tasks with the -`#[app]` attribute has a `dispatchers = [FreeInterrupt1, FreeInterrupt2, ...]` argument. You need -to provide as many dispatchers as there are priority levels used by software tasks, as an -dispatcher is assigned per interrupt level. The framework will also give a compile error if there -are not enough dispatchers provided. +These free interrupts used as dispatchers are interrupt vectors not used by hardware tasks. -This is exemplified in the following: +The `#[task]` attribute used on a function declare it as a software tasks. +The static method `task_name::spawn()` spawn (start) a software task and +given that there are no higher priority tasks running the task will start executing directly. + +A list of “free” and usable interrupts allows the framework to dispatch software tasks. +This list of dispatchers, `dispatchers = [FreeInterrupt1, FreeInterrupt2, ...]` is an +argument to the `#[app]` attribute. + +Each interrupt vector acting as dispatcher gets assigned to one priority level meaning that +the list of dispatchers need to cover all priority levels used by software tasks. + +Example: The `dispatchers =` argument needs to have at least 3 entries for an application using +three different priorities for software tasks. + +The framework will give a compilation error if there are not enough dispatchers provided. + +See the following example: ``` rust {{#include ../../../../examples/spawn.rs}} From c55016f4b237a9f25e5a0d86b42dc3ff39bf69b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Tj=C3=A4der?= Date: Thu, 16 Dec 2021 11:54:13 +0100 Subject: [PATCH 077/423] Docs: By-example App priorities and message passing --- book/en/src/by-example/app_priorities.md | 70 ++++++++++++++++------- book/en/src/by-example/message_passing.md | 12 +++- 2 files changed, 57 insertions(+), 25 deletions(-) diff --git a/book/en/src/by-example/app_priorities.md b/book/en/src/by-example/app_priorities.md index 934359d5d7..1a92ec846c 100644 --- a/book/en/src/by-example/app_priorities.md +++ b/book/en/src/by-example/app_priorities.md @@ -2,26 +2,41 @@ ## Priorities -The static priority of each handler can be declared in the `task` attribute -using the `priority` argument. For Cortex-M, tasks can have priorities in the range `1..=(1 << -NVIC_PRIO_BITS)` where `NVIC_PRIO_BITS` is a constant defined in the `device` -crate. When the `priority` argument is omitted, the priority is assumed to be -`1`. The `idle` task has a non-configurable static priority of `0`, the lowest priority. +The `priority` argument declares the static priority of each `task`. + +For Cortex-M, tasks can have priorities in the range `1..=(1 << NVIC_PRIO_BITS)` +where `NVIC_PRIO_BITS` is a constant defined in the `device` crate. + +Omitting the `priority` argument the task priority defaults to `1`. +The `idle` task has a non-configurable static priority of `0`, the lowest priority. > A higher number means a higher priority in RTIC, which is the opposite from what > Cortex-M does in the NVIC peripheral. > Explicitly, this means that number `10` has a **higher** priority than number `9`. -When several tasks are ready to be executed the one with highest static -priority will be executed first. Task prioritization can be observed in the -following scenario: during the execution of a low -priority task a higher priority task is spawned; this puts the higher priority task in the pending state. -The difference in priority results in the higher priority task preempting the -lower priority one: the execution of the lower priority task is suspended and -the higher priority task is executed to completion. Once the higher priority -task has terminated the lower priority task is resumed. +The highest static priority task takes precedence when more than one +task are ready to execute. -The following example showcases the priority based scheduling of tasks. +The following scenario demonstrates task prioritization: +Spawning a higher priority task A during execution of a lower priority task B pends +task A. Task A has higher priority thus preempting task B which gets suspended +until task A completes execution. Thus, when task A completes task B resumes execution. + +```text +Task Priority + ┌────────────────────────────────────────────────────────┐ + │ │ + │ │ +3 │ Preempts │ +2 │ A─────────► │ +1 │ B─────────► - - - - B────────► │ +0 │Idle┌─────► Resumes ┌──────────► │ + ├────┴──────────────────────────────────┴────────────────┤ + │ │ + └────────────────────────────────────────────────────────┘Time +``` + +The following example showcases the priority based scheduling of tasks: ``` rust {{#include ../../../../examples/preempt.rs}} @@ -33,13 +48,24 @@ $ cargo run --target thumbv7m-none-eabi --example preempt ``` Note that the task `bar` does *not* preempt task `baz` because its priority -is the *same* as `baz`'s. However, once `baz` returns, the execution of -task `bar` is prioritized over `foo` due to its higher priority. `foo` -is resumed only after `bar` returns. +is the *same* as `baz`'s. The higher priority task `bar` runs before `foo` +when `baz`returns. When `bar` returns `foo` can resume. One more note about priorities: choosing a priority higher than what the device -supports will result in a compile error. Due to -limitations in the language, the error message is currently far from helpful: it -will say something along the lines of "evaluation of constant value failed" and -the span of the error will *not* point out to the problematic interrupt value -- -we are sorry about this! +supports will result in a compilation error. +The error is cryptic due to limitations in the language, +if `priority = 9` for task `uart0_interrupt` in `example/common.rs` this looks like: + +```text + error[E0080]: evaluation of constant value failed + --> examples/common.rs:10:1 + | +10 | #[rtic::app(device = lm3s6965, dispatchers = [SSI0, QEI0])] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ attempt to compute `8_usize - 9_usize`, which would overflow + | + = note: this error originates in the attribute macro `rtic::app` (in Nightly builds, run with -Z macro-backtrace for more info) + +``` + +The error message incorrectly points to the starting point of the macro, but at least the +value subtracted (in this case 9) will suggest which task causes the error. diff --git a/book/en/src/by-example/message_passing.md b/book/en/src/by-example/message_passing.md index b80ae03cde..0dc8f85814 100644 --- a/book/en/src/by-example/message_passing.md +++ b/book/en/src/by-example/message_passing.md @@ -1,8 +1,14 @@ # Message passing & capacity -Software tasks have support for message passing, this means that they can be spawned with an argument -as `foo::spawn(1)` which will run the task `foo` with the argument `1`. The number of arguments is not -limited and is exemplified in the following: +Software tasks support message passing, this means that software tasks can be spawned +with an argument: `foo::spawn(1)` which will run the task `foo` with the argument `1`. + +Capacity sets the size of the spawn queue for the task, if not specified capacity defaults to 1. + +In the example below, the capacity of task `foo` is `3`, allowing three simultaneous +pending spawns of `foo`. Exceeding this capacity is an `Error`. + +The number of arguments to a task is not limited: ``` rust {{#include ../../../../examples/message_passing.rs}} From 833e22da51f65c1c9c58403c46318df68c558fce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Tj=C3=A4der?= Date: Thu, 16 Dec 2021 21:34:46 +0100 Subject: [PATCH 078/423] Docs: By-example Monotonics --- book/en/src/by-example/monotonic.md | 43 ++++++++++++++++------------- 1 file changed, 24 insertions(+), 19 deletions(-) diff --git a/book/en/src/by-example/monotonic.md b/book/en/src/by-example/monotonic.md index c2a5d86cb9..fc20dd4070 100644 --- a/book/en/src/by-example/monotonic.md +++ b/book/en/src/by-example/monotonic.md @@ -1,33 +1,38 @@ # Monotonic & spawn_{at/after} The understanding of time is an important concept in embedded systems, and to be able to run tasks -based on time is very useful. For this use-case the framework provides the static methods +based on time is useful. For this use-case the framework provides the static methods `task::spawn_after(/* duration */)` and `task::spawn_at(/* specific time instant */)`. -Mostly one uses `spawn_after`, but in cases where it's needed to have spawns happen without drift or -to a fixed baseline `spawn_at` is available. +`spawn_after` is more commonly used, but in cases where it's needed to have spawns happen +without drift or to a fixed baseline `spawn_at` is available. -To support this the `#[monotonic]` attribute exists which is applied to a type alias definition. +The `#[monotonic]` attribute, applied to a type alias definition, exists to support this. This type alias must point to a type which implements the [`rtic_monotonic::Monotonic`] trait. -This is generally some timer which handles the timing of the system. One or more monotonics can be -used in the same system, for example a slow timer that is used to wake the system from sleep and another -that is used for high granularity scheduling while the system is awake. +This is generally some timer which handles the timing of the system. +One or more monotonics can coexist in the same system, for example a slow timer that wakes the +system from sleep and another which purpose is for high granularity scheduling while the +system is awake. [`rtic_monotonic::Monotonic`]: https://docs.rs/rtic-monotonic The attribute has one required parameter and two optional parameters, `binds`, `default` and -`priority` respectively. `binds = InterruptName` defines which interrupt vector is associated to -the timer's interrupt, `default = true` enables a shorthand API when spawning and accessing the -time (`monotonics::now()` vs `monotonics::MyMono::now()`), and `priority` sets the priority the -interrupt vector has. +`priority` respectively. +The required parameter, `binds = InterruptName`, associates an interrupt vector to the timer's +interrupt, while `default = true` enables a shorthand API when spawning and accessing +time (`monotonics::now()` vs `monotonics::MyMono::now()`), and `priority` sets the priority +of the interrupt vector. -> By default `priority` is set to the **maximum priority** of the system but a lower priority -> can be selected if a high priority task cannot take the jitter introduced by the scheduling. -> This can however introduce jitter and delays into the scheduling, making it a trade-off. +> The default `priority` is the **maximum priority** of the system. +> If your system has a high priority task with tight scheduling requirements, +> it might be desirable to demote the `monotonic` task to a lower priority +> to reduce scheduling jitter for the high priority task. +> This however might introduce jitter and delays into scheduling via the `monotonic`, +> making it a trade-off. -Finally, the monotonics must be initialized in `#[init]` and returned in the `init::Monotonic( ... )` tuple. -This moves the monotonics into the active state which makes it possible to use them. +The monotonics are initialized in `#[init]` and returned within the `init::Monotonic( ... )` tuple. +This activates the monotonics making it possible to use them. -An example is provided below: +See the following example: ``` rust {{#include ../../../../examples/schedule.rs}} @@ -40,8 +45,8 @@ $ cargo run --target thumbv7m-none-eabi --example message ## Canceling or rescheduling a scheduled task -Tasks spawned using `task::spawn_after` and `task::spawn_at` has as returns a `SpawnHandle`, -where the `SpawnHandle` can be used to cancel or reschedule a task that will run in the future. +Tasks spawned using `task::spawn_after` and `task::spawn_at` returns a `SpawnHandle`, +which allows canceling or rescheduling of the task scheduled to run in the future. If `cancel` or `reschedule_at`/`reschedule_after` returns an `Err` it means that the operation was too late and that the task is already sent for execution. The following example shows this in action: From 8e68c527219477f096b3ef4ccc0ece240a98a645 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Tj=C3=A4der?= Date: Sat, 18 Dec 2021 22:35:16 +0100 Subject: [PATCH 079/423] Docs: Migration docs --- book/en/src/migration.md | 2 +- book/en/src/migration/migration_v4.md | 76 ++++++++++++++++----------- book/en/src/migration/migration_v5.md | 10 ++-- 3 files changed, 53 insertions(+), 35 deletions(-) diff --git a/book/en/src/migration.md b/book/en/src/migration.md index 08feb81e1e..f52b0a51cd 100644 --- a/book/en/src/migration.md +++ b/book/en/src/migration.md @@ -1,4 +1,4 @@ # Migration Guides -This section describes how to migrate between different version of RTIC. +This section describes how to migrate between different versions of RTIC. It also acts as a comparing reference between versions. diff --git a/book/en/src/migration/migration_v4.md b/book/en/src/migration/migration_v4.md index ac59d8c9fb..d1a7ebeb98 100644 --- a/book/en/src/migration/migration_v4.md +++ b/book/en/src/migration/migration_v4.md @@ -1,19 +1,31 @@ # Migrating from v0.4.x to v0.5.0 -This section covers how to upgrade an application written against RTIC v0.4.x to +This section covers how to upgrade an application written against RTFM v0.4.x to the version v0.5.0 of the framework. +## Project name change RTFM -> RTIC + +With release [v0.5.2][rtic0.5.2] the name was change to Real-Time Interrupt-driven Concurrency + +All occurrences of `RTFM` needs to change to `RTIC`. + +See [migration guide RTFM to RTIC](./migration_rtic.md) + +[rtic0.5.2]: https://crates.io/crates/cortex-m-rtic/0.5.2 + ## `Cargo.toml` -First, the version of the `cortex-m-rtic` dependency needs to be updated to -`"0.5.0"`. The `timer-queue` feature needs to be removed. +Change the version of `cortex-m-rtfm` to +`"0.5.0"`, change `rtfm` to `rtic`. +Remove the `timer-queue` feature. ``` toml -[dependencies.cortex-m-rtic] +[dependencies.cortex-m-rtfm] # change this version = "0.4.3" # into this +[dependencies.cortex-m-rtic] version = "0.5.0" # and remove this Cargo feature @@ -23,15 +35,15 @@ features = ["timer-queue"] ## `Context` argument -All functions inside the `#[rtic::app]` item need to take as first argument a +All functions inside the `#[rtfm::app]` item need to take as first argument a `Context` structure. This `Context` type will contain the variables that were magically injected into the scope of the function by version v0.4.x of the framework: `resources`, `spawn`, `schedule` -- these variables will become -fields of the `Context` structure. Each function within the `#[rtic::app]` item +fields of the `Context` structure. Each function within the `#[rtfm::app]` item gets a different `Context` type. ``` rust -#[rtic::app(/* .. */)] +#[rtfm::app(/* .. */)] const APP: () = { // change this #[task(resources = [x], spawn = [a], schedule = [b])] @@ -75,11 +87,11 @@ const APP: () = { ## Resources -The syntax used to declare resources has been changed from `static mut` +The syntax used to declare resources has changed from `static mut` variables to a `struct Resources`. ``` rust -#[rtic::app(/* .. */)] +#[rtfm::app(/* .. */)] const APP: () = { // change this static mut X: u32 = 0; @@ -101,13 +113,13 @@ const APP: () = { If your application was accessing the device peripherals in `#[init]` through the `device` variable then you'll need to add `peripherals = true` to the -`#[rtic::app]` attribute to continue to access the device peripherals through +`#[rtfm::app]` attribute to continue to access the device peripherals through the `device` field of the `init::Context` structure. Change this: ``` rust -#[rtic::app(/* .. */)] +#[rtfm::app(/* .. */)] const APP: () = { #[init] fn init() { @@ -121,7 +133,7 @@ const APP: () = { Into this: ``` rust -#[rtic::app(/* .. */, peripherals = true)] +#[rtfm::app(/* .. */, peripherals = true)] // ^^^^^^^^^^^^^^^^^^ const APP: () = { #[init] @@ -137,13 +149,14 @@ const APP: () = { ## `#[interrupt]` and `#[exception]` -The `#[interrupt]` and `#[exception]` attributes have been removed. To declare -hardware tasks in v0.5.x use the `#[task]` attribute with the `binds` argument. +Remove the attributes `#[interrupt]` and `#[exception]`. +To declare hardware tasks in v0.5.x use the `#[task]` +attribute with the `binds` argument instead. Change this: ``` rust -#[rtic::app(/* .. */)] +#[rtfm::app(/* .. */)] const APP: () = { // hardware tasks #[exception] @@ -163,7 +176,7 @@ const APP: () = { Into this: ``` rust -#[rtic::app(/* .. */)] +#[rtfm::app(/* .. */)] const APP: () = { #[task(binds = SVCall)] // ^^^^^^^^^^^^^^ @@ -183,25 +196,26 @@ const APP: () = { ## `schedule` -The `schedule` API no longer requires the `timer-queue` cargo feature, which has -been removed. To use the `schedule` API one must -first define the monotonic timer the runtime will use using the `monotonic` -argument of the `#[rtic::app]` attribute. To continue using the cycle counter -(CYCCNT) as the monotonic timer, and match the behavior of version v0.4.x, add -the `monotonic = rtic::cyccnt::CYCCNT` argument to the `#[rtic::app]` attribute. +The `schedule` API no longer requires the `timer-queue` cargo feature. +To use the `schedule` API one must first define the monotonic timer the +runtime will use using the `monotonic` argument of the `#[rtfm::app]` attribute. +To continue using the cycle counter (CYCCNT) as the monotonic timer, +and match the behavior of version v0.4.x, add the `monotonic = rtfm::cyccnt::CYCCNT` +argument to the `#[rtfm::app]` attribute. -Also, the `Duration` and `Instant` types and the `U32Ext` trait have been moved -into the `rtic::cyccnt` module. This module is only available on ARMv7-M+ -devices. The removal of the `timer-queue` also brings back the `DWT` peripheral -inside the core peripherals struct, this will need to be enabled by the application -inside `init`. +Also, the `Duration` and `Instant` types and the `U32Ext` trait moved +into the `rtfm::cyccnt` module. +This module is only available on ARMv7-M+ devices. +The removal of the `timer-queue` also brings back the `DWT` peripheral +inside the core peripherals struct, if `DWT` is required, +ensure it is enabled by the application inside `init`. Change this: ``` rust -use rtic::{Duration, Instant, U32Ext}; +use rtfm::{Duration, Instant, U32Ext}; -#[rtic::app(/* .. */)] +#[rtfm::app(/* .. */)] const APP: () = { #[task(schedule = [b])] fn a() { @@ -213,10 +227,10 @@ const APP: () = { Into this: ``` rust -use rtic::cyccnt::{Duration, Instant, U32Ext}; +use rtfm::cyccnt::{Duration, Instant, U32Ext}; // ^^^^^^^^ -#[rtic::app(/* .. */, monotonic = rtic::cyccnt::CYCCNT)] +#[rtfm::app(/* .. */, monotonic = rtfm::cyccnt::CYCCNT)] // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ const APP: () = { #[init] diff --git a/book/en/src/migration/migration_v5.md b/book/en/src/migration/migration_v5.md index 24353d2a31..5c0dad193e 100644 --- a/book/en/src/migration/migration_v5.md +++ b/book/en/src/migration/migration_v5.md @@ -71,7 +71,7 @@ mod app { } ``` -## Move Dispatchers from `extern "C"` to app arguments. +## Move Dispatchers from `extern "C"` to app arguments Change @@ -171,7 +171,10 @@ fn b(_: b::Context) {} ## Symmetric locks -Now RTIC utilizes symmetric locks, this means that the `lock` method need to be used for all `shared` resource access. In old code one could do the following as the high priority task has exclusive access to the resource: +Now RTIC utilizes symmetric locks, this means that the `lock` method need +to be used for all `shared` resource access. +In old code one could do the following as the high priority +task has exclusive access to the resource: ``` rust #[task(priority = 2, resources = [r])] @@ -354,6 +357,7 @@ Note that the attributes `spawn` and `schedule` are no longer needed. ### Extern tasks -Both software and hardware tasks can now be defined external to the `mod app`. Previously this was possible only by implementing a trampoline calling out the task implementation. +Both software and hardware tasks can now be defined external to the `mod app`. +Previously this was possible only by implementing a trampoline calling out the task implementation. See examples `examples/extern_binds.rs` and `examples/extern_spawn.rs`. From a2caef394ce35d4fd8e374193c1ee9705a775fbe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Tj=C3=A4der?= Date: Sat, 18 Dec 2021 22:35:39 +0100 Subject: [PATCH 080/423] Docs: Point to rtic-examples repo --- book/en/src/awesome_rtic.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/book/en/src/awesome_rtic.md b/book/en/src/awesome_rtic.md index 925cd3fd96..6d5aac2ee4 100644 --- a/book/en/src/awesome_rtic.md +++ b/book/en/src/awesome_rtic.md @@ -1 +1,8 @@ # Awesome RTIC examples + +See the [`rtic-rs/rtic-examples`][rticexamples] repository for community +provided complete examples. + +Pull-requests to this repo is welcome! + +[rticexamples]: https://github.com/rtic-rs/rtic-examples From 56822dd3b8e343a16c86960cffbaa6e48f363a16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Tj=C3=A4der?= Date: Sat, 18 Dec 2021 22:36:11 +0100 Subject: [PATCH 081/423] Docs: Tips touchup --- book/en/src/by-example/tips_destructureing.md | 5 ++-- book/en/src/by-example/tips_from_ram.md | 3 +-- book/en/src/by-example/tips_indirection.md | 5 ++-- book/en/src/by-example/tips_monotonic_impl.md | 26 ++++++++++--------- .../src/by-example/tips_static_lifetimes.md | 16 ++++++------ book/en/src/by-example/tips_view_code.md | 8 +++--- 6 files changed, 33 insertions(+), 30 deletions(-) diff --git a/book/en/src/by-example/tips_destructureing.md b/book/en/src/by-example/tips_destructureing.md index 7b864c4666..4637b48343 100644 --- a/book/en/src/by-example/tips_destructureing.md +++ b/book/en/src/by-example/tips_destructureing.md @@ -1,7 +1,8 @@ # Resource de-structure-ing -When having a task taking multiple resources it can help in readability to split -up the resource struct. Here are two examples on how this can be done: +Destructuring task resources might help readability if a task takes multiple +resources. +Here are two examples on how to split up the resource struct: ``` rust {{#include ../../../../examples/destructure.rs}} diff --git a/book/en/src/by-example/tips_from_ram.md b/book/en/src/by-example/tips_from_ram.md index 6aef2f704e..ecb5dde195 100644 --- a/book/en/src/by-example/tips_from_ram.md +++ b/book/en/src/by-example/tips_from_ram.md @@ -6,7 +6,7 @@ RTIC v0.4.0 was to allow inter-operation with other attributes. For example, the improve performance in some cases. > **IMPORTANT**: In general, the `link_section`, `export_name` and `no_mangle` -> attributes are very powerful but also easy to misuse. Incorrectly using any of +> attributes are powerful but also easy to misuse. Incorrectly using any of > these attributes can cause undefined behavior; you should always prefer to use > safe, higher level attributes around them like `cortex-m-rt`'s `interrupt` and > `exception` attributes. @@ -42,4 +42,3 @@ $ cargo nm --example ramfunc --release | grep ' foo::' $ cargo nm --example ramfunc --release | grep ' bar::' {{#include ../../../../ci/expected/ramfunc.grep.bar}} ``` - diff --git a/book/en/src/by-example/tips_indirection.md b/book/en/src/by-example/tips_indirection.md index 22c5774630..1a330c5162 100644 --- a/book/en/src/by-example/tips_indirection.md +++ b/book/en/src/by-example/tips_indirection.md @@ -3,7 +3,9 @@ Message passing always involves copying the payload from the sender into a static variable and then from the static variable into the receiver. Thus sending a large buffer, like a `[u8; 128]`, as a message involves two expensive -`memcpy`s. To minimize the message passing overhead one can use indirection: +`memcpy`s. + +Indirection can minimize message passing overhead: instead of sending the buffer by value, one can send an owning pointer into the buffer. @@ -23,4 +25,3 @@ Here's an example where `heapless::Pool` is used to "box" buffers of 128 bytes. $ cargo run --target thumbv7m-none-eabi --example pool {{#include ../../../../ci/expected/pool.run}} ``` - diff --git a/book/en/src/by-example/tips_monotonic_impl.md b/book/en/src/by-example/tips_monotonic_impl.md index 210a08e669..99e155d049 100644 --- a/book/en/src/by-example/tips_monotonic_impl.md +++ b/book/en/src/by-example/tips_monotonic_impl.md @@ -1,18 +1,21 @@ # Implementing a `Monotonic` timer for scheduling -The framework is very flexible in that it can utilize any timer which has compare-match and (optional) -overflow interrupts for scheduling. The only thing needed to make a timer usable with RTIC is to -implement the [`rtic_monotonic::Monotonic`] trait. +The framework is flexible because it can use any timer which has compare-match and optionally +supporting overflow interrupts for scheduling. +The single requirement to make a timer usable with RTIC is implementing the +[`rtic_monotonic::Monotonic`] trait. -Implementing time that supports a vast range is generally **very** difficult, and in RTIC 0.5 it was a -common problem how to implement time handling and not get stuck in weird special cases. Moreover -it was difficult to understand the relation between time and the timers used for scheduling. For -RTIC 0.6 we have moved to assume the user has a time library, e.g. [`fugit`] or [`embedded_time`], -as the basis for all time-based operations when implementing `Monotonic`. This is why in RTIC 0.6 -it is almost trivial to implement the `Monotonic` trait and use any timer in a system for scheduling. +Implementing time counting that supports large time spans is generally **difficult**, in RTIC 0.5 +implementing time handling was a common problem. +Moreover, the relation between time and timers used for scheduling was difficult to understand. -The trait documents the requirements for each method, however below you can find a list of -implementations in the wild that can be used as inspiration: +For RTIC 0.6 we have moved to assume the user has a time library, e.g. [`fugit`] or [`embedded_time`], +as the basis for all time-based operations when implementing `Monotonic`. +This makes it almost trivial to implement the `Monotonic` trait allowing the use of any timer in +the system for scheduling. + +The trait documents the requirements for each method, +and for inspiration here is a list of `Monotonic` implementations: - [`STM32F411 series`], implemented for the 32-bit timers - [`Nordic nRF52 series`], implemented for the 32-bit timers @@ -28,4 +31,3 @@ If you know of more implementations feel free to add them to this list. [`Nordic nRF52 series`]: https://github.com/kalkyl/nrf-play/blob/main/src/bin/mono.rs [`Systick based`]: https://github.com/rtic-rs/systick-monotonic [`DWT and Systick based`]: https://github.com/rtic-rs/dwt-systick-monotonic - diff --git a/book/en/src/by-example/tips_static_lifetimes.md b/book/en/src/by-example/tips_static_lifetimes.md index 3ea08166e4..8d3a832c4e 100644 --- a/book/en/src/by-example/tips_static_lifetimes.md +++ b/book/en/src/by-example/tips_static_lifetimes.md @@ -1,17 +1,17 @@ # 'static super-powers -As discussed earlier `local` resources are given `'static` lifetime in `#[init]` and `#[idle]`, -this can be used to allocate an object and then split it up or give the pre-allocated object to a -task, driver or some other object. -This is very useful when needing to allocate memory for drivers, such as USB drivers, and using -data structures that can be split such as [`heapless::spsc::Queue`]. +In `#[init]` and `#[idle]` `local` resources has `'static` lifetime. -In the following example an [`heapless::spsc::Queue`] is given to two different tasks for lock-free access -to the shared queue. +Useful when pre-allocating and/or splitting resources between tasks, drivers +or some other object. +This comes in handy when drivers, such as USB drivers, need to allocate memory and +when using splittable data structures such as [`heapless::spsc::Queue`]. + +In the following example two different tasks share a [`heapless::spsc::Queue`] +for lock-free access to the shared queue. [`heapless::spsc::Queue`]: https://docs.rs/heapless/0.7.5/heapless/spsc/struct.Queue.html - ``` rust {{#include ../../../../examples/static.rs}} ``` diff --git a/book/en/src/by-example/tips_view_code.md b/book/en/src/by-example/tips_view_code.md index 8f0d86b591..736b7ac895 100644 --- a/book/en/src/by-example/tips_view_code.md +++ b/book/en/src/by-example/tips_view_code.md @@ -7,7 +7,7 @@ options: You can inspect the file `rtic-expansion.rs` inside the `target` directory. This file contains the expansion of the `#[rtic::app]` item (not your whole program!) of the *last built* (via `cargo build` or `cargo check`) RTIC application. The -expanded code is not pretty printed by default so you'll want to run `rustfmt` +expanded code is not pretty printed by default, so you'll want to run `rustfmt` on it before you read it. ``` console @@ -15,7 +15,7 @@ $ cargo build --example foo $ rustfmt target/rtic-expansion.rs -$ tail target/rtic-expansion.rs +tail target/rtic-expansion.rs ``` ``` rust @@ -43,6 +43,6 @@ crate and print the output to the console. [`cargo-expand`]: https://crates.io/crates/cargo-expand ``` console -$ # produces the same output as before -$ cargo expand --example smallest | tail +# produces the same output as before +cargo expand --example smallest | tail ``` From e232d7e9df90227234ddc077488e9fa027d23986 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Tj=C3=A4der?= Date: Sat, 18 Dec 2021 22:37:01 +0100 Subject: [PATCH 082/423] Docs: New project touchup --- book/en/src/by-example/starting_a_project.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/book/en/src/by-example/starting_a_project.md b/book/en/src/by-example/starting_a_project.md index b50ac4a9ef..c916479aa8 100644 --- a/book/en/src/by-example/starting_a_project.md +++ b/book/en/src/by-example/starting_a_project.md @@ -1,14 +1,16 @@ # Starting a new project -When starting an RTIC project from scratch it is recommended to follow RTIC's [`defmt-app-template`]. +A recommendation when starting a RTIC project from scratch is to follow RTIC's [`defmt-app-template`]. [`defmt-app-template`]: https://github.com/rtic-rs/defmt-app-template This will give you an RTIC application with support for RTT logging with [`defmt`] and stack overflow -protection using [`flip-link`]. There are also an multitude of examples available provided by the community: +protection using [`flip-link`]. There are also a multitude of examples available provided by the community: +- [`rtic-examples`] - Multiple projects - [https://github.com/kalkyl/f411-rtic](https://github.com/kalkyl/f411-rtic) - ... More to come [`defmt`]: https://github.com/knurling-rs/defmt/ [`flip-link`]: https://github.com/knurling-rs/flip-link/ +[`rtic-examples`]: https://github.com/rtic-rs/rtic-examples From 1b1b077961421f9c174edb1ed793ee055ed489de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Tj=C3=A4der?= Date: Sun, 19 Dec 2021 10:03:18 +0100 Subject: [PATCH 083/423] Review fixup --- book/en/src/awesome_rtic.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/book/en/src/awesome_rtic.md b/book/en/src/awesome_rtic.md index 6d5aac2ee4..36d38e6252 100644 --- a/book/en/src/awesome_rtic.md +++ b/book/en/src/awesome_rtic.md @@ -3,6 +3,6 @@ See the [`rtic-rs/rtic-examples`][rticexamples] repository for community provided complete examples. -Pull-requests to this repo is welcome! +Pull-requests to this repo are welcome! [rticexamples]: https://github.com/rtic-rs/rtic-examples From 278207e2ec02b00e8719d983f499e202276e3359 Mon Sep 17 00:00:00 2001 From: perlindgren Date: Tue, 21 Dec 2021 19:43:45 +0100 Subject: [PATCH 084/423] Update monotonic.md high granularity -> fine grained --- book/en/src/by-example/monotonic.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/book/en/src/by-example/monotonic.md b/book/en/src/by-example/monotonic.md index fc20dd4070..0c8e15aed9 100644 --- a/book/en/src/by-example/monotonic.md +++ b/book/en/src/by-example/monotonic.md @@ -10,7 +10,7 @@ The `#[monotonic]` attribute, applied to a type alias definition, exists to supp This type alias must point to a type which implements the [`rtic_monotonic::Monotonic`] trait. This is generally some timer which handles the timing of the system. One or more monotonics can coexist in the same system, for example a slow timer that wakes the -system from sleep and another which purpose is for high granularity scheduling while the +system from sleep and another which purpose is for fine grained scheduling while the system is awake. [`rtic_monotonic::Monotonic`]: https://docs.rs/rtic-monotonic From d3d650b2c7e8492c96d8a37d85620ff491178632 Mon Sep 17 00:00:00 2001 From: perlindgren Date: Tue, 21 Dec 2021 19:49:09 +0100 Subject: [PATCH 085/423] Update tips_monotonic_impl.md text polishing --- book/en/src/by-example/tips_monotonic_impl.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/book/en/src/by-example/tips_monotonic_impl.md b/book/en/src/by-example/tips_monotonic_impl.md index 99e155d049..24df7712c5 100644 --- a/book/en/src/by-example/tips_monotonic_impl.md +++ b/book/en/src/by-example/tips_monotonic_impl.md @@ -9,7 +9,7 @@ Implementing time counting that supports large time spans is generally **difficu implementing time handling was a common problem. Moreover, the relation between time and timers used for scheduling was difficult to understand. -For RTIC 0.6 we have moved to assume the user has a time library, e.g. [`fugit`] or [`embedded_time`], +For RTIC 0.6 we instead assume the user has a time library, e.g. [`fugit`] or [`embedded_time`], as the basis for all time-based operations when implementing `Monotonic`. This makes it almost trivial to implement the `Monotonic` trait allowing the use of any timer in the system for scheduling. From e249813ad7a5670dd9a1a70d46b72aa02ce4dce0 Mon Sep 17 00:00:00 2001 From: perlindgren Date: Tue, 21 Dec 2021 19:55:05 +0100 Subject: [PATCH 086/423] Update preface.md polish --- book/en/src/preface.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/book/en/src/preface.md b/book/en/src/preface.md index 7ad33e1423..d9dbc04bf8 100644 --- a/book/en/src/preface.md +++ b/book/en/src/preface.md @@ -8,7 +8,7 @@ # Preface This book contains user level documentation for the Real-Time Interrupt-driven Concurrency -(RTIC) framework. The API reference available [here](../../api/). +(RTIC) framework. The API reference is available [here](../../api/). Formerly known as Real-Time For the Masses. From 93ceb6bdef9f15256ff89a1d9a333dbd04604dfb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Tj=C3=A4der?= Date: Sat, 25 Dec 2021 13:10:10 +0100 Subject: [PATCH 087/423] Edition: Bump to 2021 --- Cargo.toml | 2 +- src/lib.rs | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index ed6efbaadd..f67af6ff9e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,7 +7,7 @@ authors = [ categories = ["concurrency", "embedded", "no-std"] description = "Real-Time Interrupt-driven Concurrency (RTIC): a concurrency framework for building real-time systems" documentation = "https://rtic.rs/" -edition = "2018" +edition = "2021" keywords = ["arm", "cortex-m"] license = "MIT OR Apache-2.0" name = "cortex-m-rtic" diff --git a/src/lib.rs b/src/lib.rs index 5fe35a95e2..500993e5ec 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -30,6 +30,7 @@ //! [SemVer]: https://semver.org/spec/v2.0.0.html #![deny(missing_docs)] +#![deny(rust_2021_compatibility)] #![deny(rust_2018_compatibility)] #![deny(rust_2018_idioms)] #![no_std] From c297b4ee8d619d903b1b1673e47a8df25637d01b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Tj=C3=A4der?= Date: Sat, 25 Dec 2021 13:17:16 +0100 Subject: [PATCH 088/423] Clippy lints --- macros/src/codegen.rs | 2 +- macros/src/codegen/local_resources.rs | 2 +- macros/src/codegen/shared_resources.rs | 2 +- macros/src/codegen/shared_resources_struct.rs | 2 +- macros/src/codegen/util.rs | 6 +++--- src/lib.rs | 8 ++++++++ src/tq.rs | 2 +- 7 files changed, 16 insertions(+), 8 deletions(-) diff --git a/macros/src/codegen.rs b/macros/src/codegen.rs index 422de5f37c..100508432d 100644 --- a/macros/src/codegen.rs +++ b/macros/src/codegen.rs @@ -107,7 +107,7 @@ pub fn app(app: &App, analysis: &Analysis, extra: &Extra) -> TokenStream2 { .map(|(_, monotonic)| { let name = &monotonic.ident; let name_str = &name.to_string(); - let ident = util::monotonic_ident(&name_str); + let ident = util::monotonic_ident(name_str); let doc = &format!( "This module holds the static implementation for `{}::now()`", name_str diff --git a/macros/src/codegen/local_resources.rs b/macros/src/codegen/local_resources.rs index ff53486249..50621c32c0 100644 --- a/macros/src/codegen/local_resources.rs +++ b/macros/src/codegen/local_resources.rs @@ -51,7 +51,7 @@ pub fn codegen( let expr = &task_local.expr; let attrs = &task_local.attrs; - let mangled_name = util::declared_static_local_resource_ident(resource_name, &task_name); + let mangled_name = util::declared_static_local_resource_ident(resource_name, task_name); // For future use // let doc = format!(" RTIC internal: {}:{}", file!(), line!()); diff --git a/macros/src/codegen/shared_resources.rs b/macros/src/codegen/shared_resources.rs index ddd3824c89..a115b7c20a 100644 --- a/macros/src/codegen/shared_resources.rs +++ b/macros/src/codegen/shared_resources.rs @@ -21,7 +21,7 @@ pub fn codegen( for (name, res) in &app.shared_resources { let cfgs = &res.cfgs; let ty = &res.ty; - let mangled_name = &util::static_shared_resource_ident(&name); + let mangled_name = &util::static_shared_resource_ident(name); // late resources in `util::link_section_uninit` let section = util::link_section_uninit(); diff --git a/macros/src/codegen/shared_resources_struct.rs b/macros/src/codegen/shared_resources_struct.rs index 6122651795..7ae8d8086e 100644 --- a/macros/src/codegen/shared_resources_struct.rs +++ b/macros/src/codegen/shared_resources_struct.rs @@ -32,7 +32,7 @@ pub fn codegen(ctxt: Context, needs_lt: &mut bool, app: &App) -> (TokenStream2, None }; let ty = &res.ty; - let mangled_name = util::static_shared_resource_ident(&name); + let mangled_name = util::static_shared_resource_ident(name); let shared_name = util::need_to_lock_ident(name); if !res.properties.lock_free { diff --git a/macros/src/codegen/util.rs b/macros/src/codegen/util.rs index 831718aed9..e865434528 100644 --- a/macros/src/codegen/util.rs +++ b/macros/src/codegen/util.rs @@ -76,7 +76,7 @@ pub fn interrupt_ident() -> Ident { } pub fn timer_queue_marker_ident() -> Ident { - mark_internal_name(&"TIMER_QUEUE_MARKER") + mark_internal_name("TIMER_QUEUE_MARKER") } /// Whether `name` is an exception with configurable priority @@ -225,7 +225,7 @@ pub fn rq_ident(priority: u8) -> Ident { /// Generates an identifier for the `enum` of `schedule`-able tasks pub fn schedule_t_ident() -> Ident { - Ident::new(&"SCHED_T", Span::call_site()) + Ident::new("SCHED_T", Span::call_site()) } /// Generates an identifier for the `enum` of `spawn`-able tasks @@ -278,7 +278,7 @@ pub fn need_to_lock_ident(name: &Ident) -> Ident { /// The name to get better RT flag errors pub fn rt_err_ident() -> Ident { Ident::new( - &"you_must_enable_the_rt_feature_for_the_pac_in_your_cargo_toml", + "you_must_enable_the_rt_feature_for_the_pac_in_your_cargo_toml", Span::call_site(), ) } diff --git a/src/lib.rs b/src/lib.rs index 5fe35a95e2..a1a911aece 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -95,12 +95,20 @@ impl RacyCell { } /// Get `*mut T` + /// + /// # Safety + /// + /// See documentation notes for [`RacyCell`] #[inline(always)] pub unsafe fn get_mut(&self) -> *mut T { self.0.get() } /// Get `*const T` + /// + /// # Safety + /// + /// See documentation notes for [`RacyCell`] #[inline(always)] pub unsafe fn get(&self) -> *const T { self.0.get() diff --git a/src/tq.rs b/src/tq.rs index 0121de5fe7..26ebbd96c3 100644 --- a/src/tq.rs +++ b/src/tq.rs @@ -176,6 +176,6 @@ where Mono: Monotonic, { fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(&other)) + Some(self.cmp(other)) } } From 2b90cd34333ab044e701a8a45fb7a077a906bef9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Tj=C3=A4der?= Date: Sat, 25 Dec 2021 14:04:30 +0100 Subject: [PATCH 089/423] Bump version to 1.0.0 --- CHANGELOG.md | 7 ++++++- Cargo.toml | 11 +++++------ macros/Cargo.toml | 4 ++-- 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 11fab90a83..746f58af3f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,8 +5,12 @@ This project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +## [v1.0.0] - 2021-12-25 + ### Changed +- Bump RTIC dependencies also updated to v1.0.0 +- Edition 2021 - Change default `idle` behaviour to be `NOP` instead of `WFI` ## [v0.6.0-rc.4] - 2021-11-09 @@ -451,7 +455,8 @@ Yanked due to a soundness issue in `init`; the issue has been mostly fixed in v0 - Initial release -[Unreleased]: https://github.com/rtic-rs/cortex-m-rtic/compare/v0.6.0-rc.4...HEAD +[Unreleased]: https://github.com/rtic-rs/cortex-m-rtic/compare/v1.0.0...HEAD +[v1.0.0]: https://github.com/rtic-rs/cortex-m-rtic/compare/v0.6.0-rc.4...v1.0.0 [v0.6.0-rc.4]: https://github.com/rtic-rs/cortex-m-rtic/compare/v0.6.0-rc.3...v0.6.0-rc.4 [v0.6.0-rc.3]: https://github.com/rtic-rs/cortex-m-rtic/compare/v0.6.0-rc.2...v0.6.0-rc.3 [v0.6.0-rc.2]: https://github.com/rtic-rs/cortex-m-rtic/compare/v0.6.0-rc.1...v0.6.0-rc.2 diff --git a/Cargo.toml b/Cargo.toml index f67af6ff9e..4393533b30 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,16 +14,16 @@ name = "cortex-m-rtic" readme = "README.md" repository = "https://github.com/rtic-rs/cortex-m-rtic" -version = "0.6.0-rc.4" +version = "1.0.0" [lib] name = "rtic" [dependencies] cortex-m = "0.7.0" -cortex-m-rtic-macros = { path = "macros", version = "0.6.0-rc.4" } -rtic-monotonic = "0.1.0-rc.2" -rtic-core = "0.3.1" +cortex-m-rtic-macros = { path = "macros", version = "1.0.0" } +rtic-monotonic = "1.0.0" +rtic-core = "1.0.0" heapless = "0.7.7" bare-metal = "1.0.0" @@ -33,7 +33,7 @@ version_check = "0.9" [dev-dependencies] lm3s6965 = "0.1.3" cortex-m-semihosting = "0.3.3" -systick-monotonic = "0.1.0-rc.2" +systick-monotonic = "1.0.0" [dev-dependencies.panic-semihosting] features = ["exit"] @@ -70,4 +70,3 @@ overflow-checks = false [patch.crates-io] lm3s6965 = { git = "https://github.com/japaric/lm3s6965" } -# embedded-time = { path = "../../embedded-time" } diff --git a/macros/Cargo.toml b/macros/Cargo.toml index 394c17753f..8339549a52 100644 --- a/macros/Cargo.toml +++ b/macros/Cargo.toml @@ -12,7 +12,7 @@ license = "MIT OR Apache-2.0" name = "cortex-m-rtic-macros" readme = "../README.md" repository = "https://github.com/rtic-rs/cortex-m-rtic" -version = "0.6.0-rc.4" +version = "1.0.0" [lib] proc-macro = true @@ -22,4 +22,4 @@ proc-macro2 = "1" proc-macro-error = "1" quote = "1" syn = "1" -rtic-syntax = "0.5.0-rc.2" +rtic-syntax = "1.0.0" From 2d8252f3e5600a6246ae64d2c0be412087c8ef8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Tj=C3=A4der?= Date: Sat, 25 Dec 2021 16:18:28 +0100 Subject: [PATCH 090/423] Promote v1.0 in docs --- .github/workflows/build.yml | 2 +- book/en/src/SUMMARY.md | 2 +- book/en/src/migration/migration_v5.md | 18 +++++++++--------- book/en/src/preface.md | 2 +- book/ru/src/SUMMARY.md | 2 +- book/ru/src/migration/migration_v5.md | 14 +++++++------- book/ru/src/preface.md | 2 +- 7 files changed, 21 insertions(+), 21 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d7c595e7e5..7a324bc934 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -522,7 +522,7 @@ jobs: langs=( en ru ) devver=( dev ) # The latest stable must be the first element in the array - vers=( 0.5.x 0.4.x ) + vers=( 1.0.x 0.5.x 0.4.x ) # All releases start with "v" # followed by MAJOR.MINOR.PATCH, see semver.org diff --git a/book/en/src/SUMMARY.md b/book/en/src/SUMMARY.md index 397bb54729..045036eab5 100644 --- a/book/en/src/SUMMARY.md +++ b/book/en/src/SUMMARY.md @@ -25,7 +25,7 @@ - [Awesome RTIC examples](./awesome_rtic.md) - [Migration Guides](./migration.md) - - [v0.5.x to v0.6.x](./migration/migration_v5.md) + - [v0.5.x to v1.0.x](./migration/migration_v5.md) - [v0.4.x to v0.5.x](./migration/migration_v4.md) - [RTFM to RTIC](./migration/migration_rtic.md) - [Under the hood](./internals.md) diff --git a/book/en/src/migration/migration_v5.md b/book/en/src/migration/migration_v5.md index 5c0dad193e..e19fb62fe8 100644 --- a/book/en/src/migration/migration_v5.md +++ b/book/en/src/migration/migration_v5.md @@ -1,10 +1,10 @@ -# Migrating from v0.5.x to v0.6.0 +# Migrating from v0.5.x to v1.0.0 -This section describes how to upgrade from v0.5.x to v0.6.0 of the RTIC framework. +This section describes how to upgrade from v0.5.x to v1.0.0 of the RTIC framework. ## `Cargo.toml` - version bump -Change the version of `cortex-m-rtic` to `"0.6.0"`. +Change the version of `cortex-m-rtic` to `"1.0.0"`. ## `mod` instead of `const` @@ -112,7 +112,7 @@ struct Resources { } ``` -With RTIC v0.6.0 the resources structs are annotated similarly like +With RTIC v1.0.0 the resources structs are annotated similarly like `#[task]`, `#[init]`, `#[idle]`: with the attributes `#[shared]` and `#[local]` ``` rust @@ -131,7 +131,7 @@ These structs can be freely named by the developer. ## `shared` and `local` arguments in `#[task]`s -In v0.6.0 resources are split between `shared` resources and `local` resources. +In v1.0.0 resources are split between `shared` resources and `local` resources. `#[task]`, `#[init]` and `#[idle]` no longer have a `resources` argument; they must now use the `shared` and `local` arguments. In v0.5.x: @@ -149,7 +149,7 @@ fn a(_: a::Context) {} fn b(_: b::Context) {} ``` -In v0.6.0: +In v1.0.0: ``` rust #[shared] @@ -207,7 +207,7 @@ Note that the performance does not change thanks to LLVM's optimizations which o ## Lock-free resource access In RTIC 0.5 resources shared by tasks running at the same priority could be accessed *without* the `lock` API. -This is still possible in 0.6: the `#[shared]` resource must be annotated with the field-level `#[lock_free]` attribute. +This is still possible in 1.0: the `#[shared]` resource must be annotated with the field-level `#[lock_free]` attribute. v0.5 code: @@ -227,7 +227,7 @@ fn b(cx: b::Context) { } ``` -v0.6 code: +v1.0 code: ``` rust #[shared] @@ -262,7 +262,7 @@ fn init(_: init::Context) { } ``` -v0.6.0 code: +v1.0.0 code: ``` rust #[init(local = [ diff --git a/book/en/src/preface.md b/book/en/src/preface.md index d9dbc04bf8..f833213ed4 100644 --- a/book/en/src/preface.md +++ b/book/en/src/preface.md @@ -16,7 +16,7 @@ Formerly known as Real-Time For the Masses. -This is the documentation of v0.6.x of RTIC; for the documentation of version +This is the documentation of v1.0.x of RTIC; for the documentation of version * v0.5.x go [here](/0.5). * v0.4.x go [here](/0.4). diff --git a/book/ru/src/SUMMARY.md b/book/ru/src/SUMMARY.md index a387c9f05c..cf03cdae58 100644 --- a/book/ru/src/SUMMARY.md +++ b/book/ru/src/SUMMARY.md @@ -11,7 +11,7 @@ - [Создание нового проекта](./by-example/new.md) - [Советы и хитрости](./by-example/tips.md) - [Инструкции по миграции](./migration.md) - - [v0.5.x на v0.6.x](./migration/migration_v5.md) + - [v0.5.x на v1.0.x](./migration/migration_v5.md) - [v0.4.x на v0.5.x](./migration/migration_v4.md) - [RTFM на RTIC](./migration/migration_rtic.md) - [Под капотом](./internals.md) diff --git a/book/ru/src/migration/migration_v5.md b/book/ru/src/migration/migration_v5.md index 870c20fd62..84bd9fb847 100644 --- a/book/ru/src/migration/migration_v5.md +++ b/book/ru/src/migration/migration_v5.md @@ -1,6 +1,6 @@ -# Миграция с v0.5.x на v0.6.0 +# Миграция с v0.5.x на v1.0.0 -Этот раздел описывает как обновиться с версии v0.5.x на v0.6.0 фреймворка RTIC. +Этот раздел описывает как обновиться с версии v0.5.x на v1.0.0 фреймворка RTIC. ## `Cargo.toml` - увеличьте версию @@ -112,7 +112,7 @@ struct Resources { } ``` -Начиная с RTIC v0.6.0 структуры ресурсов аннотируются подобно +Начиная с RTIC v1.0.0 структуры ресурсов аннотируются подобно `#[task]`, `#[init]`, `#[idle]`: аттрибутами `#[shared]` и `#[local]` ``` rust @@ -131,7 +131,7 @@ struct MyLocalResources { ## `shared` и `local` аргументы в `#[task]`'ах -В v0.6.0 ресурсы разделены на `shared` ресурсы и `local` ресурсы. +В v1.0.0 ресурсы разделены на `shared` ресурсы и `local` ресурсы. `#[task]`, `#[init]` и `#[idle]` больше не имеют аргумента `resources`; они должны использовать аргументы `shared` и `local`. @@ -150,7 +150,7 @@ fn a(_: a::Context) {} fn b(_: b::Context) {} ``` -В v0.6.0: +В v1.0.0: ``` rust #[shared] @@ -229,7 +229,7 @@ fn b(cx: b::Context) { } ``` -v0.6 код: +v1.0 код: ``` rust #[shared] @@ -264,7 +264,7 @@ fn init(_: init::Context) { } ``` -v0.6.0 code: +v1.0.0 code: ``` rust #[init(local = [ diff --git a/book/ru/src/preface.md b/book/ru/src/preface.md index 700560f4aa..894b6b46ba 100644 --- a/book/ru/src/preface.md +++ b/book/ru/src/preface.md @@ -16,7 +16,7 @@ -Это документация по RTIC версии v0.6.x; за документацией по другим версиям: +Это документация по RTIC версии v1.0.x; за документацией по другим версиям: * v0.5.x [сюда](/0.5). * v0.4.x [сюда](/0.4). From 53306b1a7b470851fe3c1271133790f41d5f1d54 Mon Sep 17 00:00:00 2001 From: Per Lindgren Date: Sat, 25 Dec 2021 17:59:19 +0100 Subject: [PATCH 091/423] Docfix: remove pre-release note --- src/lib.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index ba251ef3ca..074e71ec0a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,7 +1,4 @@ -//! Real-Time Interrupt-driven Concurrency (RTIC) framework for ARM Cortex-M microcontrollers -//! -//! **HEADS UP** This is an **beta** pre-release; there may be breaking changes in the API and -//! semantics before a proper release is made. +//! Real-Time Interrupt-driven Concurrency (RTIC) framework for ARM Cortex-M microcontrollers. //! //! **IMPORTANT**: This crate is published as [`cortex-m-rtic`] on crates.io but the name of the //! library is `rtic`. From 28cf223bfd8a2a207adf341ce3dd0f97ba9699d0 Mon Sep 17 00:00:00 2001 From: Per Lindgren Date: Sat, 25 Dec 2021 18:15:41 +0100 Subject: [PATCH 092/423] Docfix: MSRV and Semantic Versioning --- src/lib.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 074e71ec0a..25f93999dd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -14,13 +14,13 @@ //! //! # Minimum Supported Rust Version (MSRV) //! -//! This crate is guaranteed to compile on stable Rust 1.36 (2018 edition) and up. It *might* -//! compile on older versions but that may change in any new patch release. +//! This crate is compiled and tested with the latest toolchain (rolling) as of the release date. +//! If you run into compilation errors, try the latest stable release of the rust toolchain. //! //! # Semantic Versioning //! //! Like the Rust project, this crate adheres to [SemVer]: breaking changes in the API and semantics -//! require a *semver bump* (a new minor version release), with the exception of breaking changes +//! require a *semver bump* (since 1.0.0 a new major version release), with the exception of breaking changes //! that fix soundness issues -- those are considered bug fixes and can be landed in a new patch //! release. //! From 887b0686117fc3976671434c6935999ad07b915e Mon Sep 17 00:00:00 2001 From: Per Lindgren Date: Sat, 25 Dec 2021 18:37:29 +0100 Subject: [PATCH 093/423] README: Remove rustc badge and Requirements --- README.md | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index f81f40409d..dc6c374d66 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,6 @@ Formerly known as Real-Time For the Masses. [![crates.io](https://img.shields.io/crates/v/cortex-m-rtic)](https://crates.io/crates/cortex-m-rtic) [![docs.rs](https://docs.rs/cortex-m-rtic/badge.svg)](https://docs.rs/cortex-m-rtic) [![book](https://img.shields.io/badge/web-rtic.rs-red.svg?style=flat&label=book&colorB=d33847)](https://rtic.rs/) -[![rustc](https://img.shields.io/badge/rustc-1.36+-lightgray.svg)](https://github.com/rust-lang/rust/releases/tag/1.36.0) [![matrix](https://img.shields.io/matrix/rtic:matrix.org)](https://matrix.to/#/#rtic:matrix.org) [![Meeting notes](https://hackmd.io/badge.svg)](https://hackmd.io/@xmis9JvZT8Gvo9lOEKyZ4Q/SkBJKsjuH) @@ -46,10 +45,6 @@ Formerly known as Real-Time For the Masses. and scheduling analysis techniques. (Though we haven't yet developed Rust friendly tooling for that.) -## Requirements - -- Applications must be written using the 2018 edition. - ### Crate `cortex-m` 0.6 vs 0.7 in RTIC 0.5.x The crate `cortex-m` 0.7 started using trait `InterruptNumber` for interrupts instead of `Nr` from `bare-metal`. In order to preserve backwards compatibility, RTIC 0.5.x will keep using `cortex-m` 0.6 by default. `cortex-m` 0.7 can be enabled using the feature `cortex-m-7` and disabling default features: @@ -58,9 +53,9 @@ The crate `cortex-m` 0.7 started using trait `InterruptNumber` for interrupts in cortex-m-rtic = { version = "0.5.8", default-features = false, features = ["cortex-m-7"] } ``` -RTIC 0.6 already uses `cortex-m` 0.7 by default. +RTIC 1.0.0 already uses `cortex-m` 0.7 by default. -## [User documentation](https://rtic.rs) - [(Development version)](https://rtic.rs/dev) +## [User documentation](https://rtic.rs) ## [API reference](https://rtic.rs/stable/api/) From bd38a4a0cc94ff15dfef2e4525151584bc193ed9 Mon Sep 17 00:00:00 2001 From: Per Lindgren Date: Sat, 25 Dec 2021 19:37:42 +0100 Subject: [PATCH 094/423] README: User documentation --- README.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index dc6c374d66..85e8b37804 100644 --- a/README.md +++ b/README.md @@ -42,8 +42,7 @@ Formerly known as Real-Time For the Masses. - **All Cortex-M devices are fully supported**. - This task model is amenable to known WCET (Worst Case Execution Time) analysis - and scheduling analysis techniques. (Though we haven't yet developed Rust - friendly tooling for that.) + and scheduling analysis techniques. ### Crate `cortex-m` 0.6 vs 0.7 in RTIC 0.5.x @@ -55,7 +54,9 @@ cortex-m-rtic = { version = "0.5.8", default-features = false, features = ["cort RTIC 1.0.0 already uses `cortex-m` 0.7 by default. -## [User documentation](https://rtic.rs) +## [User documentation](https://rtic.rs) + +Documentation for the current master branch is found in [(Development version)](https://rtic.rs/dev). ## [API reference](https://rtic.rs/stable/api/) From 19551d50e04c3733d0f40c7e351c4a50404daba4 Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Sun, 26 Dec 2021 10:14:17 +0100 Subject: [PATCH 095/423] Update README.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Henrik Tjäder --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 85e8b37804..e9d8403811 100644 --- a/README.md +++ b/README.md @@ -56,7 +56,7 @@ RTIC 1.0.0 already uses `cortex-m` 0.7 by default. ## [User documentation](https://rtic.rs) -Documentation for the current master branch is found in [(Development version)](https://rtic.rs/dev). +Documentation for the [development version](https://rtic.rs/dev). ## [API reference](https://rtic.rs/stable/api/) From 68fb811a11de9dc56ee79945106ce047392445d2 Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Sun, 26 Dec 2021 10:19:04 +0100 Subject: [PATCH 096/423] Added nRF52 RTC based monotonic to the book --- book/en/src/by-example/tips_monotonic_impl.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/book/en/src/by-example/tips_monotonic_impl.md b/book/en/src/by-example/tips_monotonic_impl.md index 24df7712c5..38f3e92d8b 100644 --- a/book/en/src/by-example/tips_monotonic_impl.md +++ b/book/en/src/by-example/tips_monotonic_impl.md @@ -18,7 +18,8 @@ The trait documents the requirements for each method, and for inspiration here is a list of `Monotonic` implementations: - [`STM32F411 series`], implemented for the 32-bit timers -- [`Nordic nRF52 series`], implemented for the 32-bit timers +- [`Nordic nRF52 series Timer`], implemented for the 32-bit timers +- [`Nordic nRF52 series RTC`], implemented for the RTCs - [`Systick based`], runs at a fixed rate - some overhead but simple - [`DWT and Systick based`], a more efficient `Systick` based implementation, but requires `DWT` @@ -28,6 +29,7 @@ If you know of more implementations feel free to add them to this list. [`fugit`]: https://docs.rs/fugit/ [`embedded_time`]: https://docs.rs/embedded_time/ [`STM32F411 series`]: https://github.com/kalkyl/f411-rtic/blob/main/src/bin/mono.rs -[`Nordic nRF52 series`]: https://github.com/kalkyl/nrf-play/blob/main/src/bin/mono.rs +[`Nordic nRF52 series Timer`]: https://github.com/kalkyl/nrf-play/blob/main/src/bin/mono.rs +[`Nordic nRF52 series RTC`]: https://gist.github.com/korken89/fe94a475726414dd1bce031c76adc3dd [`Systick based`]: https://github.com/rtic-rs/systick-monotonic [`DWT and Systick based`]: https://github.com/rtic-rs/dwt-systick-monotonic From ef4e4aaaa3d261bda0aa46f89f2e6f8edefd202e Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Sun, 26 Dec 2021 10:43:57 +0100 Subject: [PATCH 097/423] cargo xtask is now ~40x faster --- ci/expected/baseline.run | 4 - ci/expected/cfg.run | 2 - ci/expected/late.run | 1 - ci/expected/not-send.run | 0 ci/expected/ramfunc.grep.bar | 1 - ci/expected/ramfunc.grep.foo | 1 - ci/expected/resource.run | 2 - ci/expected/shared-with-init.run | 0 ci/expected/singleton.run | 2 - ci/expected/types.run | 0 xtask/src/build.rs | 42 +--------- xtask/src/command.rs | 54 +++++-------- xtask/src/main.rs | 133 ++++--------------------------- 13 files changed, 36 insertions(+), 206 deletions(-) delete mode 100644 ci/expected/baseline.run delete mode 100644 ci/expected/cfg.run delete mode 100644 ci/expected/late.run delete mode 100644 ci/expected/not-send.run delete mode 100644 ci/expected/ramfunc.grep.bar delete mode 100644 ci/expected/ramfunc.grep.foo delete mode 100644 ci/expected/resource.run delete mode 100644 ci/expected/shared-with-init.run delete mode 100644 ci/expected/singleton.run delete mode 100644 ci/expected/types.run diff --git a/ci/expected/baseline.run b/ci/expected/baseline.run deleted file mode 100644 index fa3822f696..0000000000 --- a/ci/expected/baseline.run +++ /dev/null @@ -1,4 +0,0 @@ -init(baseline = Instant(0)) -foo(baseline = Instant(0)) -UART0(baseline = Instant(904)) -foo(baseline = Instant(904)) \ No newline at end of file diff --git a/ci/expected/cfg.run b/ci/expected/cfg.run deleted file mode 100644 index b584958b38..0000000000 --- a/ci/expected/cfg.run +++ /dev/null @@ -1,2 +0,0 @@ -foo has been called 1 time -foo has been called 2 times diff --git a/ci/expected/late.run b/ci/expected/late.run deleted file mode 100644 index 6d3d3e43e5..0000000000 --- a/ci/expected/late.run +++ /dev/null @@ -1 +0,0 @@ -received message: 42 diff --git a/ci/expected/not-send.run b/ci/expected/not-send.run deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/ci/expected/ramfunc.grep.bar b/ci/expected/ramfunc.grep.bar deleted file mode 100644 index 1fa5bad1dd..0000000000 --- a/ci/expected/ramfunc.grep.bar +++ /dev/null @@ -1 +0,0 @@ -20000000 t ramfunc::bar::h9d6714fe5a3b0c89 \ No newline at end of file diff --git a/ci/expected/ramfunc.grep.foo b/ci/expected/ramfunc.grep.foo deleted file mode 100644 index 845f277f73..0000000000 --- a/ci/expected/ramfunc.grep.foo +++ /dev/null @@ -1 +0,0 @@ -00000162 t ramfunc::foo::h30e7789b08c08e19 \ No newline at end of file diff --git a/ci/expected/resource.run b/ci/expected/resource.run deleted file mode 100644 index 9b60960f44..0000000000 --- a/ci/expected/resource.run +++ /dev/null @@ -1,2 +0,0 @@ -UART1: local_to_uart1 = 1 -UART0: local_to_uart0 = 1 diff --git a/ci/expected/shared-with-init.run b/ci/expected/shared-with-init.run deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/ci/expected/singleton.run b/ci/expected/singleton.run deleted file mode 100644 index c55dc1ac76..0000000000 --- a/ci/expected/singleton.run +++ /dev/null @@ -1,2 +0,0 @@ -bar(2) -foo(1) diff --git a/ci/expected/types.run b/ci/expected/types.run deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/xtask/src/build.rs b/xtask/src/build.rs index 904e9177c0..148a9fde29 100644 --- a/xtask/src/build.rs +++ b/xtask/src/build.rs @@ -1,9 +1,4 @@ -use std::{ - fs, - path::{Path, PathBuf}, -}; - -use crate::{command::BuildMode, TestRunError}; +use std::{fs, path::Path}; const HEX_BUILD_ROOT: &str = "ci/builds"; @@ -16,38 +11,3 @@ pub fn init_build_dir() -> anyhow::Result<()> { fs::create_dir_all(HEX_BUILD_ROOT) .map_err(|_| anyhow::anyhow!("Could not create directory: {}", HEX_BUILD_ROOT)) } - -pub fn build_hexpath( - example: &str, - features: Option<&str>, - build_mode: BuildMode, - build_num: u32, -) -> anyhow::Result { - let features = match features { - Some(f) => f, - None => "", - }; - - let filename = format!("{}_{}_{}_{}.hex", example, features, build_mode, build_num); - - let mut path = PathBuf::from(HEX_BUILD_ROOT); - path.push(filename); - - path.into_os_string() - .into_string() - .map_err(|e| anyhow::Error::new(TestRunError::PathConversionError(e))) -} - -pub fn compare_builds(expected: String, got: String) -> anyhow::Result<()> { - let buf_1 = std::fs::read_to_string(expected.clone())?; - let buf_2 = std::fs::read_to_string(got.clone())?; - - if buf_1 != buf_2 { - return Err(anyhow::Error::new(TestRunError::FileCmpError { - expected, - got, - })); - } - - Ok(()) -} diff --git a/xtask/src/command.rs b/xtask/src/command.rs index d94a7ab3d0..2f719bf5c9 100644 --- a/xtask/src/command.rs +++ b/xtask/src/command.rs @@ -1,7 +1,7 @@ use crate::{RunResult, TestRunError}; use core::fmt; use os_pipe::pipe; -use std::{fs::File, io::Read, path::Path, process::Command}; +use std::{fs::File, io::Read, process::Command}; #[allow(dead_code)] #[derive(Debug, Clone, Copy, PartialEq)] @@ -18,32 +18,23 @@ pub enum CargoCommand<'a> { features: Option<&'a str>, mode: BuildMode, }, - Build { - example: &'a str, + BuildAll { target: &'a str, features: Option<&'a str>, mode: BuildMode, }, - Objcopy { - example: &'a str, - target: &'a str, - features: Option<&'a str>, - ihex: &'a str, - }, - Size { - example_paths: Vec<&'a Path>, - }, - Clean, + // Size { + // example_paths: Vec<&'a Path>, + // }, + // Clean, } impl<'a> CargoCommand<'a> { fn name(&self) -> &str { match self { CargoCommand::Run { .. } => "run", - CargoCommand::Size { example_paths: _ } => "rust-size", - CargoCommand::Clean => "clean", - CargoCommand::Build { .. } => "build", - CargoCommand::Objcopy { .. } => "objcopy", + // CargoCommand::Size { example_paths: _ } => "rust-size", + CargoCommand::BuildAll { .. } => "build", } } @@ -54,12 +45,6 @@ impl<'a> CargoCommand<'a> { target, features, mode, - } - | CargoCommand::Build { - example, - target, - features, - mode, } => { let mut args = vec![self.name(), "--example", example, "--target", target]; @@ -71,26 +56,23 @@ impl<'a> CargoCommand<'a> { } args } - CargoCommand::Size { example_paths } => { - example_paths.iter().map(|p| p.to_str().unwrap()).collect() - } - CargoCommand::Clean => vec!["clean"], - CargoCommand::Objcopy { - example, + CargoCommand::BuildAll { target, features, - ihex, + mode, } => { - let mut args = vec![self.name(), "--example", example, "--target", target]; + let mut args = vec![self.name(), "--examples", "--target", target]; if let Some(feature_name) = features { args.extend_from_slice(&["--features", feature_name]); } - - // this always needs to go at the end - args.extend_from_slice(&["--", "-O", "ihex", ihex]); + if let Some(flag) = mode.to_flag() { + args.push(flag); + } args - } + } // CargoCommand::Size { example_paths } => { + // example_paths.iter().map(|p| p.to_str().unwrap()).collect() + // } } } @@ -99,7 +81,7 @@ impl<'a> CargoCommand<'a> { // we need to cheat a little here: // `cargo size` can't be ran on multiple files, so we're using `rust-size` instead – // which isn't a command that starts wizh `cargo`. So we're sneakily swapping them out :) - CargoCommand::Size { .. } => "rust-size", + // CargoCommand::Size { .. } => "rust-size", _ => "cargo", } } diff --git a/xtask/src/main.rs b/xtask/src/main.rs index ad8719ad53..76ce04bd4c 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs @@ -14,7 +14,7 @@ use std::{ use structopt::StructOpt; use crate::{ - build::{build_hexpath, compare_builds, init_build_dir}, + build::init_build_dir, command::{run_command, run_successful, BuildMode, CargoCommand}, }; @@ -93,25 +93,6 @@ fn main() -> anyhow::Result<()> { }) .collect(); - // let examples = &[ - // "idle", - // "init", - // "hardware", - // "preempt", - // "binds", - // "lock", - // "multilock", - // "only-shared-access", - // "task", - // "message", - // "capacity", - // "not-sync", - // "generics", - // "pool", - // "ramfunc", - // "peripherals-taken", - // ]; - let opts = Options::from_args(); let target = &opts.target; @@ -120,11 +101,9 @@ fn main() -> anyhow::Result<()> { if target == "all" { for t in targets { run_test(t, &examples)?; - build_test(t, &examples)?; } } else if targets.contains(&target.as_str()) { run_test(&target, &examples)?; - build_test(&target, &examples)?; } else { eprintln!( "The target you specified is not available. Available targets are:\ @@ -139,6 +118,12 @@ fn main() -> anyhow::Result<()> { } fn run_test(target: &str, examples: &[String]) -> anyhow::Result<()> { + arm_example(&CargoCommand::BuildAll { + target, + features: None, + mode: BuildMode::Release, + })?; + for example in examples { let cmd = CargoCommand::Run { example, @@ -147,37 +132,16 @@ fn run_test(target: &str, examples: &[String]) -> anyhow::Result<()> { mode: BuildMode::Release, }; - arm_example(&cmd, 1)?; - - arm_example( - &CargoCommand::Build { - example, - target, - features: None, - mode: BuildMode::Release, - }, - 1, - )?; + arm_example(&cmd)?; } Ok(()) } // run example binary `example` -fn arm_example(command: &CargoCommand, build_num: u32) -> anyhow::Result<()> { +fn arm_example(command: &CargoCommand) -> anyhow::Result<()> { match *command { - CargoCommand::Run { - example, - target, - features, - mode, - } - | CargoCommand::Build { - example, - target, - features, - mode, - } => { + CargoCommand::Run { example, .. } => { let run_file = format!("{}.run", example); let expected_output_file = ["ci", "expected", &run_file] .iter() @@ -197,77 +161,14 @@ fn arm_example(command: &CargoCommand, build_num: u32) -> anyhow::Result<()> { _ => (), } - // now, prepare to objcopy - let hexpath = build_hexpath(example, features, mode, build_num)?; - - run_command(&CargoCommand::Objcopy { - example, - target, - features, - ihex: &hexpath, - })?; - Ok(()) } - _ => Err(anyhow::Error::new(TestRunError::IncompatibleCommand)), + CargoCommand::BuildAll { .. } => { + // command is either build or run + let cargo_run_result = run_command(&command)?; + println!("{}", cargo_run_result.output); + + Ok(()) + } // _ => Err(anyhow::Error::new(TestRunError::IncompatibleCommand)), } } - -fn build_test(target: &str, examples: &[String]) -> anyhow::Result<()> { - run_command(&CargoCommand::Clean)?; - - let mut built = vec![]; - let build_path: PathBuf = ["target", target, "release", "examples"].iter().collect(); - - for example in examples { - let no_features = None; - arm_example( - &CargoCommand::Build { - target, - example, - mode: BuildMode::Release, - features: no_features, - }, - 2, - )?; - let expected = build_hexpath(example, no_features, BuildMode::Release, 1)?; - let got = build_hexpath(example, no_features, BuildMode::Release, 2)?; - - compare_builds(expected, got)?; - - arm_example( - &CargoCommand::Build { - target, - example, - mode: BuildMode::Release, - features: no_features, - }, - 2, - )?; - let expected = build_hexpath(example, no_features, BuildMode::Release, 1)?; - let got = build_hexpath(example, no_features, BuildMode::Release, 2)?; - - compare_builds(expected, got)?; - - built.push(build_path.join(example)); - } - - let example_paths: Vec<&Path> = built.iter().map(|p| p.as_path()).collect(); - let size_run_result = run_command(&CargoCommand::Size { example_paths })?; - - if size_run_result.exit_status.success() { - println!("{}", size_run_result.output); - } - - Ok(()) -} - -// /// Check if lines in `output` contain `pattern` and print matching lines -// fn print_from_output(pattern: &str, lines: &str) { -// let lines = lines.split("\n"); -// for line in lines { -// if line.contains(pattern) { -// println!("{}", line); -// } -// } -// } From 7bec2347663d0a8c361f48adae7a2fa7e84abede Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Sun, 26 Dec 2021 11:42:14 +0100 Subject: [PATCH 098/423] Improved docs on where the 12 MHz comes from in SysTick --- examples/cancel-reschedule.rs | 2 +- examples/common.rs | 2 ++ examples/periodic.rs | 1 + examples/schedule.rs | 2 +- examples/t-schedule.rs | 1 + 5 files changed, 6 insertions(+), 2 deletions(-) diff --git a/examples/cancel-reschedule.rs b/examples/cancel-reschedule.rs index e0bdaae80e..a38a9c4eae 100644 --- a/examples/cancel-reschedule.rs +++ b/examples/cancel-reschedule.rs @@ -25,7 +25,7 @@ mod app { fn init(cx: init::Context) -> (Shared, Local, init::Monotonics) { let systick = cx.core.SYST; - // Initialize the monotonic + // Initialize the monotonic (SysTick rate in QEMU is 12 MHz) let mono = Systick::new(systick, 12_000_000); hprintln!("init").ok(); diff --git a/examples/common.rs b/examples/common.rs index 26a5c8fb33..1fe671e61a 100644 --- a/examples/common.rs +++ b/examples/common.rs @@ -33,6 +33,8 @@ mod app { #[init] fn init(cx: init::Context) -> (Shared, Local, init::Monotonics) { let systick = cx.core.SYST; + + // Initialize the monotonic (SysTick rate in QEMU is 12 MHz) let mono = Systick::new(systick, 12_000_000); // Spawn the task `foo` directly after `init` finishes diff --git a/examples/periodic.rs b/examples/periodic.rs index 495054ea42..40c69257e7 100644 --- a/examples/periodic.rs +++ b/examples/periodic.rs @@ -25,6 +25,7 @@ mod app { fn init(cx: init::Context) -> (Shared, Local, init::Monotonics) { let systick = cx.core.SYST; + // Initialize the monotonic (SysTick rate in QEMU is 12 MHz) let mono = Systick::new(systick, 12_000_000); foo::spawn_after(1.secs()).unwrap(); diff --git a/examples/schedule.rs b/examples/schedule.rs index 446a382892..5bad5a30ad 100644 --- a/examples/schedule.rs +++ b/examples/schedule.rs @@ -25,7 +25,7 @@ mod app { fn init(cx: init::Context) -> (Shared, Local, init::Monotonics) { let systick = cx.core.SYST; - // Initialize the monotonic + // Initialize the monotonic (SysTick rate in QEMU is 12 MHz) let mono = Systick::new(systick, 12_000_000); hprintln!("init").ok(); diff --git a/examples/t-schedule.rs b/examples/t-schedule.rs index 6ee08afbe9..5ec420873d 100644 --- a/examples/t-schedule.rs +++ b/examples/t-schedule.rs @@ -25,6 +25,7 @@ mod app { fn init(cx: init::Context) -> (Shared, Local, init::Monotonics) { let systick = cx.core.SYST; + // Initialize the monotonic (SysTick rate in QEMU is 12 MHz) let mono = Systick::new(systick, 12_000_000); debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator From 5d645b13114f6536a9f25570a9df59dfe66e29a6 Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Mon, 3 Jan 2022 18:37:54 +0100 Subject: [PATCH 099/423] Added changelog enforcer --- .github/workflows/changelog.yml | 28 ++++++++++++++++++++++++++++ CHANGELOG.md | 4 ++++ 2 files changed, 32 insertions(+) create mode 100644 .github/workflows/changelog.yml diff --git a/.github/workflows/changelog.yml b/.github/workflows/changelog.yml new file mode 100644 index 0000000000..ccf6eb9103 --- /dev/null +++ b/.github/workflows/changelog.yml @@ -0,0 +1,28 @@ +# Check that the changelog is updated for all changes. +# +# This is only run for PRs. + +on: + pull_request: + # opened, reopened, synchronize are the default types for pull_request. + # labeled, unlabeled ensure this check is also run if a label is added or removed. + types: [opened, reopened, labeled, unlabeled, synchronize] + +name: Changelog + +jobs: + changelog: + name: Changelog + runs-on: ubuntu-latest + steps: + - name: Checkout sources + uses: actions/checkout@v2 + + - name: Check that changelog updated + uses: dangoslen/changelog-enforcer@v3 + with: + changeLogPath: CHANGELOG.md + skipLabels: 'needs-changelog, skip-changelog' + missingUpdateErrorMessage: 'Please add a changelog entry in the CHANGELOG.md file.' + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 746f58af3f..378bd11dc7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,10 @@ This project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +### Added + +- CI changelog entry enforcer + ## [v1.0.0] - 2021-12-25 ### Changed From fcc412dfb99445f748f540228fd0b24b508a407c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Tj=C3=A4der?= Date: Tue, 4 Jan 2022 19:38:57 +0100 Subject: [PATCH 100/423] CI: Add link to HackMD to rtic.rs/meeting/ --- .github/workflows/build.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 7a324bc934..d2b45c420f 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -536,6 +536,10 @@ jobs: mkdir -p $td/$devver/book/ cp -r target/doc $td/$devver/api + # Redirect rtic.rs/meeting/index.html to hackmd + mkdir $td/meeting + sed "s|URL|https://hackmd.io/c_mFUZL-Q2C6614MlrrxOg|g" redirect.html > $td/meeting/index.html + # Redirect the main site to the stable release sed "s|URL|$stable|g" redirect.html > $td/index.html From 305e8295d5727502bcbd13a5a0b7a60759e5152b Mon Sep 17 00:00:00 2001 From: Per Lindgren Date: Tue, 4 Jan 2022 20:59:22 +0100 Subject: [PATCH 101/423] Drift free timing examples --- CHANGELOG.md | 2 ++ ci/expected/periodic-at.run | 4 +++ ci/expected/periodic-at2.run | 7 +++++ examples/periodic-at.rs | 49 +++++++++++++++++++++++++++++ examples/periodic-at2.rs | 61 ++++++++++++++++++++++++++++++++++++ 5 files changed, 123 insertions(+) create mode 100644 ci/expected/periodic-at.run create mode 100644 ci/expected/periodic-at2.run create mode 100644 examples/periodic-at.rs create mode 100644 examples/periodic-at2.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 378bd11dc7..930a338356 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,8 @@ This project adheres to [Semantic Versioning](http://semver.org/). ### Added - CI changelog entry enforcer +- `examples/periodic-at.rs`, an example of a periodic timer without accumulated drift. +- `examples/periodic-at2.rs`, an example of a periodic process with two tasks, with offset timing. Here we depict two alternative usages of the timer type, explicit and trait based. ## [v1.0.0] - 2021-12-25 diff --git a/ci/expected/periodic-at.run b/ci/expected/periodic-at.run new file mode 100644 index 0000000000..54020f9e95 --- /dev/null +++ b/ci/expected/periodic-at.run @@ -0,0 +1,4 @@ +foo Instant { ticks: 0 } +foo Instant { ticks: 100 } +foo Instant { ticks: 200 } +foo Instant { ticks: 300 } diff --git a/ci/expected/periodic-at2.run b/ci/expected/periodic-at2.run new file mode 100644 index 0000000000..47adbef486 --- /dev/null +++ b/ci/expected/periodic-at2.run @@ -0,0 +1,7 @@ +foo Instant { ticks: 0 } +bar Instant { ticks: 10 } +foo Instant { ticks: 110 } +bar Instant { ticks: 120 } +foo Instant { ticks: 220 } +bar Instant { ticks: 230 } +foo Instant { ticks: 330 } diff --git a/examples/periodic-at.rs b/examples/periodic-at.rs new file mode 100644 index 0000000000..f9fd995fb0 --- /dev/null +++ b/examples/periodic-at.rs @@ -0,0 +1,49 @@ +//! examples/periodic-at.rs + +#![deny(unsafe_code)] +#![deny(warnings)] +#![no_main] +#![no_std] + +use panic_semihosting as _; + +#[rtic::app(device = lm3s6965, dispatchers = [SSI0])] +mod app { + use cortex_m_semihosting::{debug, hprintln}; + use systick_monotonic::*; + + #[monotonic(binds = SysTick, default = true)] + type MyMono = Systick<100>; // 100 Hz / 10 ms granularity + + #[shared] + struct Shared {} + + #[local] + struct Local {} + + #[init] + fn init(cx: init::Context) -> (Shared, Local, init::Monotonics) { + let systick = cx.core.SYST; + + // Initialize the monotonic (SysTick rate in QEMU is 12 MHz) + let mut mono = Systick::new(systick, 12_000_000); + + foo::spawn_after(1.secs(), mono.now()).unwrap(); + + (Shared {}, Local {}, init::Monotonics(mono)) + } + + #[task(local = [cnt: u32 = 0])] + fn foo(cx: foo::Context, instant: fugit::TimerInstantU64<100>) { + hprintln!("foo {:?}", instant).ok(); + *cx.local.cnt += 1; + + if *cx.local.cnt == 4 { + debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator + } + + // Periodic ever 1 seconds + let next_instant = instant + 1.secs(); + foo::spawn_at(next_instant, next_instant).unwrap(); + } +} diff --git a/examples/periodic-at2.rs b/examples/periodic-at2.rs new file mode 100644 index 0000000000..879f709c65 --- /dev/null +++ b/examples/periodic-at2.rs @@ -0,0 +1,61 @@ +//! examples/periodic-at2.rs + +#![deny(unsafe_code)] +#![deny(warnings)] +#![no_main] +#![no_std] + +use panic_semihosting as _; + +#[rtic::app(device = lm3s6965, dispatchers = [SSI0])] +mod app { + use cortex_m_semihosting::{debug, hprintln}; + use systick_monotonic::*; + + #[monotonic(binds = SysTick, default = true)] + type MyMono = Systick<100>; // 100 Hz / 10 ms granularity + + #[shared] + struct Shared {} + + #[local] + struct Local {} + + #[init] + fn init(cx: init::Context) -> (Shared, Local, init::Monotonics) { + let systick = cx.core.SYST; + + // Initialize the monotonic (SysTick rate in QEMU is 12 MHz) + let mut mono = Systick::new(systick, 12_000_000); + + foo::spawn_after(1.secs(), mono.now()).unwrap(); + + (Shared {}, Local {}, init::Monotonics(mono)) + } + + // Using the explicit type of the timer implementation + #[task(local = [cnt: u32 = 0])] + fn foo(cx: foo::Context, instant: fugit::TimerInstantU64<100>) { + hprintln!("foo {:?}", instant).ok(); + *cx.local.cnt += 1; + + if *cx.local.cnt == 4 { + debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator + } + + // Spawn a new message with 100 ms offset to spawned time + let next_instant = instant + 100.millis(); + bar::spawn_at(next_instant, next_instant).unwrap(); + } + + // Using the Instant from the Monotonic trait + // This remains agnostic to the timer implementation + #[task(local = [cnt: u32 = 0])] + fn bar(_cx: bar::Context, instant: ::Instant) { + hprintln!("bar {:?}", instant).ok(); + + // Spawn a new message with 1s offset to spawned time + let next_instant = instant + 1.secs(); + foo::spawn_at(next_instant, next_instant).unwrap(); + } +} From 3da25c75cf76cd45bcff8c39a8d63ea4c0cd5544 Mon Sep 17 00:00:00 2001 From: Mareq Balint Date: Fri, 7 Jan 2022 22:31:30 +0000 Subject: [PATCH 102/423] Correct wording in 1.2 Resource usage --- book/en/src/by-example/resources.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/book/en/src/by-example/resources.md b/book/en/src/by-example/resources.md index 9f2c6c577f..0b69c4dba4 100644 --- a/book/en/src/by-example/resources.md +++ b/book/en/src/by-example/resources.md @@ -6,7 +6,7 @@ storage and safe accesses without the use of `unsafe` code. RTIC resources are visible only to functions declared within the `#[app]` module and the framework gives the user complete control (on a per-task basis) over resource accessibility. -Declaration of system-wide resources are by annotating **two** `struct`s within the `#[app]` module +Declaration of system-wide resources is done by annotating **two** `struct`s within the `#[app]` module with the attribute `#[local]` and `#[shared]`. Each field in these structures corresponds to a different resource (identified by field name). The difference between these two sets of resources will be covered below. From ce6e014cf07ba03327e9b5b5f348aa3deeb0fdbe Mon Sep 17 00:00:00 2001 From: Mareq Balint Date: Fri, 7 Jan 2022 22:33:23 +0000 Subject: [PATCH 103/423] Correct grammar in 1.5.2 Software tasks & spawn --- book/en/src/by-example/software_tasks.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/book/en/src/by-example/software_tasks.md b/book/en/src/by-example/software_tasks.md index 370792f841..f244ca68b1 100644 --- a/book/en/src/by-example/software_tasks.md +++ b/book/en/src/by-example/software_tasks.md @@ -10,7 +10,7 @@ bound interrupt vector. These free interrupts used as dispatchers are interrupt vectors not used by hardware tasks. The `#[task]` attribute used on a function declare it as a software tasks. -The static method `task_name::spawn()` spawn (start) a software task and +The static method `task_name::spawn()` spawns (starts) a software task and given that there are no higher priority tasks running the task will start executing directly. A list of “free” and usable interrupts allows the framework to dispatch software tasks. From 20f1c396d5d32d3a6b15faa31b01bee5b7855b27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20M=C5=82ynarczyk?= Date: Sat, 8 Jan 2022 16:36:59 +1100 Subject: [PATCH 104/423] Fix the locals.rs comment I believe that is a typo s/shared/local_to_bar --- examples/locals.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/locals.rs b/examples/locals.rs index eeb7fb75d1..aa5d0fee30 100644 --- a/examples/locals.rs +++ b/examples/locals.rs @@ -72,7 +72,7 @@ mod app { hprintln!("foo: local_to_foo = {}", local_to_foo).unwrap(); } - // `shared` can only be accessed from this context + // `local_to_bar` can only be accessed from this context #[task(local = [local_to_bar])] fn bar(cx: bar::Context) { let local_to_bar = cx.local.local_to_bar; From a34f0205b383a72a0c4f826657930f7ec29d6b5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Tj=C3=A4der?= Date: Fri, 21 Jan 2022 13:28:14 +0100 Subject: [PATCH 105/423] Highlight how to run examples locally --- book/en/src/by-example.md | 15 +++++++++++++++ book/en/src/by-example/resources.md | 2 ++ 2 files changed, 17 insertions(+) diff --git a/book/en/src/by-example.md b/book/en/src/by-example.md index 84f00193ae..3a523e51bb 100644 --- a/book/en/src/by-example.md +++ b/book/en/src/by-example.md @@ -15,3 +15,18 @@ Check [the embedded Rust book] for instructions on how to set up an embedded development environment that includes QEMU. [the embedded Rust book]: https://rust-embedded.github.io/book/intro/install.html + +To run the examples found in `examples/` locally, cargo needs a supported `target` and +either `--examples` (run all examples) or `--example NAME` to run a specific example. + +Assuming dependencies in place, running: + +``` console +$ cargo run --target thumbv7m-none-eabi --example locals +``` + +Yields this output: + +``` console +{{#include ../../../ci/expected/locals.run}} +``` diff --git a/book/en/src/by-example/resources.md b/book/en/src/by-example/resources.md index 0b69c4dba4..6426913289 100644 --- a/book/en/src/by-example/resources.md +++ b/book/en/src/by-example/resources.md @@ -37,6 +37,8 @@ The example application shown below contains two tasks where each task has acces {{#include ../../../../examples/locals.rs}} ``` +Running the example: + ``` console $ cargo run --target thumbv7m-none-eabi --example locals {{#include ../../../../ci/expected/locals.run}} From 2b07e3e0dcb7535c843c172aed0911bfc30fc955 Mon Sep 17 00:00:00 2001 From: Erik Date: Mon, 24 Jan 2022 12:27:42 +0100 Subject: [PATCH 106/423] Fix running command examples in chapter 1.5.5 Monotonic --- book/en/src/by-example/monotonic.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/book/en/src/by-example/monotonic.md b/book/en/src/by-example/monotonic.md index 0c8e15aed9..094bd5df02 100644 --- a/book/en/src/by-example/monotonic.md +++ b/book/en/src/by-example/monotonic.md @@ -39,7 +39,7 @@ See the following example: ``` ``` console -$ cargo run --target thumbv7m-none-eabi --example message +$ cargo run --target thumbv7m-none-eabi --example schedule {{#include ../../../../ci/expected/schedule.run}} ``` @@ -55,6 +55,6 @@ too late and that the task is already sent for execution. The following example ``` ``` console -$ cargo run --target thumbv7m-none-eabi --example message +$ cargo run --target thumbv7m-none-eabi --example cancel-reschedule {{#include ../../../../ci/expected/cancel-reschedule.run}} ``` From 9f54b4aca89d77e2a84d7d522b51e3f2bbf8ac74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Tj=C3=A4der?= Date: Thu, 27 Jan 2022 23:47:49 +0100 Subject: [PATCH 107/423] RTIC macro expansion: Try to find target-dir --- CHANGELOG.md | 1 + macros/Cargo.toml | 3 +++ macros/src/lib.rs | 66 ++++++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 67 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 930a338356..ab090a042e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). ### Changed +- Try to detect `target-dir` for rtic-expansion.rs - Bump RTIC dependencies also updated to v1.0.0 - Edition 2021 - Change default `idle` behaviour to be `NOP` instead of `WFI` diff --git a/macros/Cargo.toml b/macros/Cargo.toml index 8339549a52..0c008c88a6 100644 --- a/macros/Cargo.toml +++ b/macros/Cargo.toml @@ -23,3 +23,6 @@ proc-macro-error = "1" quote = "1" syn = "1" rtic-syntax = "1.0.0" + +[features] +debugprint = [] diff --git a/macros/src/lib.rs b/macros/src/lib.rs index adcd731680..13b6a7c213 100644 --- a/macros/src/lib.rs +++ b/macros/src/lib.rs @@ -7,7 +7,7 @@ extern crate proc_macro; use proc_macro::TokenStream; -use std::{fs, path::Path}; +use std::{env, fs, path::Path}; use rtic_syntax::Settings; @@ -42,9 +42,69 @@ pub fn app(args: TokenStream, input: TokenStream) -> TokenStream { let ts = codegen::app(&app, &analysis, &extra); + // Default output path: /target/ + let mut out_dir = Path::new("target"); + + // Get output directory from Cargo environment + // TODO don't want to break builds if OUT_DIR is not set, is this ever the case? + let out_str = env::var("OUT_DIR").unwrap_or_else(|_| "".to_string()); + + // Assuming we are building for a thumbv* target + let target_triple_prefix = "thumbv"; + + // Check for special scenario where default target/ directory is not present + // + // This is configurable in .cargo/config: + // + // [build] + // target-dir = "target" + #[cfg(feature = "debugprint")] + println!("OUT_DIR\n{:#?}", out_str); + + if !out_dir.exists() { + // Set out_dir to OUT_DIR + out_dir = Path::new(&out_str); + + // Default build path, annotated below: + // $(pwd)/target/thumbv7em-none-eabihf/debug/build/cortex-m-rtic-/out/ + // ///debug/build/cortex-m-rtic-/out/ + // + // traverse up to first occurrence of TARGET, approximated with starts_with("thumbv") + // and use the parent() of this path + // + // If no "target" directory is found, / is used + for path in out_dir.ancestors() { + if let Some(dir) = path.components().last() { + if dir + .as_os_str() + .to_str() + .unwrap() + .starts_with(target_triple_prefix) + //|| path.ends_with(&out_dir_root) + { + if let Some(out) = path.parent() { + out_dir = out; + #[cfg(feature = "debugprint")] + println!("{:#?}\n", out_dir); + break; + } else { + // If no parent, just use it + out_dir = path; + break; + } + } + } + } + } else { + #[cfg(feature = "debugprint")] + println!("\ntarget/ exists\n"); + } + // Try to write the expanded code to disk - if Path::new("target").exists() { - fs::write("target/rtic-expansion.rs", ts.to_string()).ok(); + if let Some(out_str) = out_dir.to_str() { + #[cfg(feature = "debugprint")] + println!("Write file:\n{}/rtic-expansion.rs\n", out_str); + fs::write(format!("{}/rtic-expansion.rs", out_str), ts.to_string()).ok(); } ts.into() From c7f6e924dccf577c870b81ae24ae69785acd5bcb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Thu, 3 Feb 2022 12:58:50 +0100 Subject: [PATCH 108/423] Update tips_monotonic_impl.md * There is no RTIC 0.6, only several RCs. * Timers without interrupts (like the DWT cycle counter alone) will not be useful for `Monotonic` impls. * Clarified some of the descriptions of the various implementations. --- book/en/src/by-example/tips_monotonic_impl.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/book/en/src/by-example/tips_monotonic_impl.md b/book/en/src/by-example/tips_monotonic_impl.md index 38f3e92d8b..3b50c34aef 100644 --- a/book/en/src/by-example/tips_monotonic_impl.md +++ b/book/en/src/by-example/tips_monotonic_impl.md @@ -9,10 +9,10 @@ Implementing time counting that supports large time spans is generally **difficu implementing time handling was a common problem. Moreover, the relation between time and timers used for scheduling was difficult to understand. -For RTIC 0.6 we instead assume the user has a time library, e.g. [`fugit`] or [`embedded_time`], +For RTIC 1.0 we instead assume the user has a time library, e.g. [`fugit`] or [`embedded_time`], as the basis for all time-based operations when implementing `Monotonic`. -This makes it almost trivial to implement the `Monotonic` trait allowing the use of any timer in -the system for scheduling. +This makes it much easier to correctly implement the `Monotonic` trait allowing the use of +almost any timer in the system for scheduling. The trait documents the requirements for each method, and for inspiration here is a list of `Monotonic` implementations: @@ -20,8 +20,8 @@ and for inspiration here is a list of `Monotonic` implementations: - [`STM32F411 series`], implemented for the 32-bit timers - [`Nordic nRF52 series Timer`], implemented for the 32-bit timers - [`Nordic nRF52 series RTC`], implemented for the RTCs -- [`Systick based`], runs at a fixed rate - some overhead but simple -- [`DWT and Systick based`], a more efficient `Systick` based implementation, but requires `DWT` +- [`Systick based`], runs at a fixed interrupt (tick) rate - with some overhead but simple and with support for large time spans +- [`DWT and Systick based`], a more efficient (tickless) implementation - requires both `SysTick` and `DWT`, supports both high resolution and large time spans If you know of more implementations feel free to add them to this list. From c83a69599a2db0cab87cc5525d0de2dc1309c746 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Fri, 4 Feb 2022 11:25:34 +0100 Subject: [PATCH 109/423] use permalinks for (currently broken) mono links --- book/en/src/by-example/tips_monotonic_impl.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/book/en/src/by-example/tips_monotonic_impl.md b/book/en/src/by-example/tips_monotonic_impl.md index 3b50c34aef..d97b5839b0 100644 --- a/book/en/src/by-example/tips_monotonic_impl.md +++ b/book/en/src/by-example/tips_monotonic_impl.md @@ -28,8 +28,8 @@ If you know of more implementations feel free to add them to this list. [`rtic_monotonic::Monotonic`]: https://docs.rs/rtic-monotonic/ [`fugit`]: https://docs.rs/fugit/ [`embedded_time`]: https://docs.rs/embedded_time/ -[`STM32F411 series`]: https://github.com/kalkyl/f411-rtic/blob/main/src/bin/mono.rs -[`Nordic nRF52 series Timer`]: https://github.com/kalkyl/nrf-play/blob/main/src/bin/mono.rs +[`STM32F411 series`]: https://github.com/kalkyl/f411-rtic/blob/a696fce7d6d19fda2356c37642c4d53547982cca/src/mono.rs +[`Nordic nRF52 series Timer`]: https://github.com/kalkyl/nrf-play/blob/47f4410d4e39374c18ff58dc17c25159085fb526/src/mono.rs [`Nordic nRF52 series RTC`]: https://gist.github.com/korken89/fe94a475726414dd1bce031c76adc3dd [`Systick based`]: https://github.com/rtic-rs/systick-monotonic [`DWT and Systick based`]: https://github.com/rtic-rs/dwt-systick-monotonic From b855c1bc9bb0d0c6777f5e1200af692c690f4c40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Fri, 4 Feb 2022 20:09:32 +0100 Subject: [PATCH 110/423] add changelog entry --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 930a338356..fcc1b9e69a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). - CI changelog entry enforcer - `examples/periodic-at.rs`, an example of a periodic timer without accumulated drift. - `examples/periodic-at2.rs`, an example of a periodic process with two tasks, with offset timing. Here we depict two alternative usages of the timer type, explicit and trait based. +- book: Update `Monotonic` tips. ## [v1.0.0] - 2021-12-25 From 9c559df22ed33348a25a41d1a35fa2611fdf9589 Mon Sep 17 00:00:00 2001 From: Per Lindgren Date: Wed, 27 Oct 2021 13:45:37 +0200 Subject: [PATCH 111/423] CONTRIBUTION.md now includes CI prep. --- CONTRIBUTING.md | 73 +++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 70 insertions(+), 3 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b4a8af1f62..daaba78a0f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,16 +1,83 @@ # Contributing + ## New features -New features should go through the [RFC process][rfcs] before a Pull Request is made to this repository. + +New features should go through the [RFC process][rfcs] before creating a Pull Request to this repository. [rfcs](https://github.com/rtic-rs/rfcs) ## Bugs + Report bugs by creating an issue in this repository. -## Pull Requests +## Pull Requests (PRs) + Please make pull requests against the master branch. Always use rebase instead of merge when bringing in changes from master to your feature branch. ## Writing documentation -Documentation improvements are always welcome. The source for the book is in `book/` and API documentation is generated from the source code. + +Documentation improvements are always welcome. +The source for the book is in `book/` and API documentation is generated from the source code. + +## CI test preparation + +Continuous Integration (CI) tests are run against all pull requests. + +Please make sure that tests passes locally before submitting. + +### Cargo format + +```shell +> cargo fmt +``` + +### Example check + +```shell +> cargo check --examples --target thumbv7m-none-eabi +``` + +and/or + +```shell +> cargo check --examples --target thumbv6m-none-eabi +``` + +### Run tests with xtask + +```shell +> cargo xtask --target all +``` + +Will execute `run` tests on your local `qemu` install. +(You may also pass a single target `--target thumbv6m-none-eabi/thumbv7m-none-eabi` during development). + +#### Adding tests to xtask + +If you have added further tests, you need to add the expected output in the `ci/expected` folder. + +```shell +> cargo run --example --target thumbv7m-none-eabi > ci/expected/.run +``` + +### Internal tests + +Run internal fail tests locally with: + +```shell +> cargo test --tests +``` + +#### Adding tests to internal tests + +If you have added fail tests or changed the expected behavior, the expected output needs to be updated (corresponding `.stderr` files). +Inspect the error output, when sure that `ACTUAL OUTPUT` is correct you can re-run the test as: + +```shell +> TRYBUILD=overwrite cargo test --tests +``` + +This will update the expected output to match the `ACTUAL OUTPUT`. +Please check that the updated files are indeed correct to avoid regressions. From a39d306649ebd7c6e99f5914bd5d6988a1705d4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Tj=C3=A4der?= Date: Sat, 5 Feb 2022 13:38:01 +0100 Subject: [PATCH 112/423] Docs: SW and HW tasks --- book/en/src/by-example/hardware_tasks.md | 11 +++++++++-- book/en/src/by-example/software_tasks.md | 22 ++++++++++++++-------- 2 files changed, 23 insertions(+), 10 deletions(-) diff --git a/book/en/src/by-example/hardware_tasks.md b/book/en/src/by-example/hardware_tasks.md index 30b88d0df8..7f8d3c6e14 100644 --- a/book/en/src/by-example/hardware_tasks.md +++ b/book/en/src/by-example/hardware_tasks.md @@ -14,11 +14,18 @@ start execution in reaction to a hardware event. Specifying a non-existing interrupt name will cause a compilation error. The interrupt names are commonly defined by [PAC or HAL][pacorhal] crates. +Any available interrupt vector should work, but different hardware might have +added special properties to select interrupt priority levels, such as the +[nRF “softdevice”](https://github.com/rtic-rs/cortex-m-rtic/issues/434). + +Beware of re-purposing interrupt vectors used internally by hardware features, +RTIC is unaware of such hardware specific details. + [pacorhal]: https://docs.rust-embedded.org/book/start/registers.html [NVIC]: https://developer.arm.com/documentation/100166/0001/Nested-Vectored-Interrupt-Controller/NVIC-functional-description/NVIC-interrupts -The example below demonstrates the use of the `#[task]` attribute to declare an -interrupt handler. +The example below demonstrates the use of the `#[task(binds = InterruptName)]` attribute to declare a +hardware task bound to an interrupt handler. ``` rust {{#include ../../../../examples/hardware.rs}} diff --git a/book/en/src/by-example/software_tasks.md b/book/en/src/by-example/software_tasks.md index f244ca68b1..ea40697d27 100644 --- a/book/en/src/by-example/software_tasks.md +++ b/book/en/src/by-example/software_tasks.md @@ -1,19 +1,25 @@ # Software tasks & spawn -Software tasks are tasks which are not directly assigned to a specific interrupt vector. +The RTIC concept of a software task shares a lot with that of [hardware tasks][hardware_tasks.md] +with the core difference that a software task is not explicitly bound to a specific +interrupt vector, but rather a “dispatcher” interrupt vector running +at the same priority as the software task. -They run as interrupt handlers where all software tasks at the -same priority level shares a "free" interrupt handler acting as a dispatcher. -Thus, what differentiates software and hardware tasks are the dispatcher versus -bound interrupt vector. - -These free interrupts used as dispatchers are interrupt vectors not used by hardware tasks. +Thus, software tasks are tasks which are not directly assigned to a specific interrupt vector. The `#[task]` attribute used on a function declare it as a software tasks. +Observe the absence of a `binds = InterruptName` argument to the attribute. The static method `task_name::spawn()` spawns (starts) a software task and given that there are no higher priority tasks running the task will start executing directly. -A list of “free” and usable interrupts allows the framework to dispatch software tasks. +All software tasks at the same priority level shares an interrupt handler acting as a dispatcher. +What differentiates software and hardware tasks are the dispatcher versus bound interrupt vector. + +The interrupt vectors used as dispatchers can not be used by hardware tasks. + +A list of “free” (not in use by hardware tasks) and usable interrupts allows the framework +to dispatch software tasks. + This list of dispatchers, `dispatchers = [FreeInterrupt1, FreeInterrupt2, ...]` is an argument to the `#[app]` attribute. From d161938138f27f240742f90c075ae852c8fbbd8b Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Mon, 7 Feb 2022 09:28:47 +0100 Subject: [PATCH 113/423] Make bors run --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index e9d8403811..004894fb4c 100644 --- a/README.md +++ b/README.md @@ -42,7 +42,7 @@ Formerly known as Real-Time For the Masses. - **All Cortex-M devices are fully supported**. - This task model is amenable to known WCET (Worst Case Execution Time) analysis - and scheduling analysis techniques. + and scheduling analysis techniques. ### Crate `cortex-m` 0.6 vs 0.7 in RTIC 0.5.x @@ -94,11 +94,11 @@ $ cargo xtask --target This crate is based on the [Real-Time For the Masses language][rtfm-lang] created by the Embedded Systems group at [Luleå University of Technology][ltu], -led by [Prof. Per Lindgren][per]. +led by [Prof. Per Lindgren][perl]. [rtfm-lang]: http://www.rtfm-lang.org/ [ltu]: https://www.ltu.se/?l=en -[per]: https://www.ltu.se/staff/p/pln-1.11258?l=en +[perl]: https://www.ltu.se/staff/p/pln-1.11258?l=en ## References From 7af09cb1262fa60d31164662966dcb851f0bc314 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Tj=C3=A4der?= Date: Tue, 8 Feb 2022 18:59:08 +0100 Subject: [PATCH 114/423] book: Restore accidentally removed files --- CHANGELOG.md | 4 ++++ book/en/src/by-example/tips_from_ram.md | 4 ++-- ci/expected/ramfunc.run.grep.bar | 1 + ci/expected/ramfunc.run.grep.foo | 1 + 4 files changed, 8 insertions(+), 2 deletions(-) create mode 100644 ci/expected/ramfunc.run.grep.bar create mode 100644 ci/expected/ramfunc.run.grep.foo diff --git a/CHANGELOG.md b/CHANGELOG.md index 618a00b4f3..112491dd13 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,10 @@ This project adheres to [Semantic Versioning](http://semver.org/). - `examples/periodic-at2.rs`, an example of a periodic process with two tasks, with offset timing. Here we depict two alternative usages of the timer type, explicit and trait based. - book: Update `Monotonic` tips. +### Fixed + +- Readded missing ramfunc output to book + ## [v1.0.0] - 2021-12-25 ### Changed diff --git a/book/en/src/by-example/tips_from_ram.md b/book/en/src/by-example/tips_from_ram.md index ecb5dde195..fc47803f9d 100644 --- a/book/en/src/by-example/tips_from_ram.md +++ b/book/en/src/by-example/tips_from_ram.md @@ -35,10 +35,10 @@ One can look at the output of `cargo-nm` to confirm that `bar` ended in RAM ``` console $ cargo nm --example ramfunc --release | grep ' foo::' -{{#include ../../../../ci/expected/ramfunc.grep.foo}} +{{#include ../../../../ci/expected/ramfunc.run.grep.foo}} ``` ``` console $ cargo nm --example ramfunc --release | grep ' bar::' -{{#include ../../../../ci/expected/ramfunc.grep.bar}} +{{#include ../../../../ci/expected/ramfunc.run.grep.bar}} ``` diff --git a/ci/expected/ramfunc.run.grep.bar b/ci/expected/ramfunc.run.grep.bar new file mode 100644 index 0000000000..33e002fe18 --- /dev/null +++ b/ci/expected/ramfunc.run.grep.bar @@ -0,0 +1 @@ +20000000 t ramfunc::bar::h9d6714fe5a3b0c89 diff --git a/ci/expected/ramfunc.run.grep.foo b/ci/expected/ramfunc.run.grep.foo new file mode 100644 index 0000000000..44e8822667 --- /dev/null +++ b/ci/expected/ramfunc.run.grep.foo @@ -0,0 +1 @@ +00000162 t ramfunc::foo::h30e7789b08c08e19 From 44f994dea2d825e77b4c89738cff4ab5c8c08ef2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20G=C3=B3rski?= Date: Tue, 8 Feb 2022 19:24:45 +0100 Subject: [PATCH 115/423] Add a remark about `Sync` and `Send` traits requirement for resources --- book/en/src/by-example/resources.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/book/en/src/by-example/resources.md b/book/en/src/by-example/resources.md index 6426913289..9d51d6c97a 100644 --- a/book/en/src/by-example/resources.md +++ b/book/en/src/by-example/resources.md @@ -30,6 +30,11 @@ task. Thus, a task `#[local]` resource can only be accessed by one singular task. Attempting to assign the same `#[local]` resource to more than one task is a compile-time error. +Types of `#[local]` resources must implement [`Send`] trait as they are being sent from `init` +to target task and thus crossing the thread boundary. + +[`Send`]: https://doc.rust-lang.org/stable/core/marker/trait.Send.html + The example application shown below contains two tasks where each task has access to its own `#[local]` resource, plus that the `idle` task has its own `#[local]` as well. @@ -51,6 +56,11 @@ A special use-case of local resources are the ones specified directly in the res initialized in `#[init]`. Moreover, local resources in `#[init]` and `#[idle]` have `'static` lifetimes, this is safe since both are not re-entrant. +Types of `#[task(local = [..])]` resources have to be neither [`Send`] nor [`Sync`] as they +are not crossing any thread boundary. + +[`Sync`]: https://doc.rust-lang.org/stable/core/marker/trait.Sync.html + In the example below the different uses and lifetimes are shown: ``` rust @@ -95,6 +105,8 @@ $ cargo run --target thumbv7m-none-eabi --example lock {{#include ../../../../ci/expected/lock.run}} ``` +Types of `#[shared]` resources have to be both [`Send`] and [`Sync`]. + ## Multi-lock As an extension to `lock`, and to reduce rightward drift, locks can be taken as tuples. The From 532765719fa935c8b7032ebe8b461598c9a15e8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Tj=C3=A4der?= Date: Wed, 9 Feb 2022 17:01:01 +0100 Subject: [PATCH 116/423] docs: make mdBook emit error codes --- .github/workflows/build.yml | 15 +++++++++++---- CHANGELOG.md | 1 + 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d2b45c420f..31eaa9fa5e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -458,10 +458,12 @@ jobs: mdbook-version: 'latest' - name: Build book in English - run: cd book/en && mdbook build + shell: 'script --return --quiet --command "bash {0}"' + run: cd book/en && if mdbook build |& tee /dev/tty | grep "\[ERROR\]"; then exit 1; else exit 0; fi - name: Build book in Russian - run: cd book/ru && mdbook build + shell: 'script --return --quiet --command "bash {0}"' + run: cd book/ru && if mdbook build |& tee /dev/tty | grep "\[ERROR\]"; then exit 1; else exit 0; fi - name: Check links run: | @@ -518,6 +520,7 @@ jobs: run: cargo doc - name: Build books + shell: 'script --return --quiet --command "bash {0}"' run: | langs=( en ru ) devver=( dev ) @@ -549,7 +552,9 @@ jobs: # Build books for lang in ${langs[@]}; do - ( cd book/$lang && mdbook build ) + ( cd book/$lang && + if mdbook build |& tee /dev/tty | grep "\[ERROR\]"; then exit 1; else exit 0; fi + ) cp -r book/$lang/book $td/$devver/book/$lang cp LICENSE-* $td/$devver/book/$lang/ done @@ -569,7 +574,9 @@ jobs: cp -r target/doc $td/$prefix/api sed 's|URL|rtic/index.html|g' $root/redirect.html > $td/$prefix/api/index.html for lang in ${langs[@]}; do - ( cd book/$lang && mdbook build ) + ( cd book/$lang && + if mdbook build |& tee /dev/tty | grep "\[ERROR\]"; then exit 1; else exit 0; fi + ) cp -r book/$lang/book $td/$prefix/book/$lang cp LICENSE-* $td/$prefix/book/$lang/ done diff --git a/CHANGELOG.md b/CHANGELOG.md index 112491dd13..84b42f2257 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). ### Fixed +- Force mdBook to return error codes - Readded missing ramfunc output to book ## [v1.0.0] - 2021-12-25 From 120d3109342ec34aed473d36e1d81b0039ce5ba4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Tj=C3=A4der?= Date: Wed, 9 Feb 2022 17:59:00 +0100 Subject: [PATCH 117/423] Demote Russian translation to WIP --- .github/workflows/build.yml | 2 +- book/ru/src/by-example/tips.md | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 31eaa9fa5e..fc07519fde 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -463,7 +463,7 @@ jobs: - name: Build book in Russian shell: 'script --return --quiet --command "bash {0}"' - run: cd book/ru && if mdbook build |& tee /dev/tty | grep "\[ERROR\]"; then exit 1; else exit 0; fi + run: cd book/ru && if mdbook build |& tee /dev/tty | grep "\[ERROR\]"; then echo "Russian book needs updating!"; else exit 0; fi - name: Check links run: | diff --git a/book/ru/src/by-example/tips.md b/book/ru/src/by-example/tips.md index f19cfee9d8..7d4fc2f47b 100644 --- a/book/ru/src/by-example/tips.md +++ b/book/ru/src/by-example/tips.md @@ -83,12 +83,12 @@ $ cargo run --example ramfunc ``` console $ cargo nm --example ramfunc --release | grep ' foo::' -{{#include ../../../../ci/expected/ramfunc.grep.foo}} +{{#include ../../../../ci/expected/ramfunc.run.grep.foo}} ``` ``` console $ cargo nm --example ramfunc --release | grep ' bar::' -{{#include ../../../../ci/expected/ramfunc.grep.bar}} +{{#include ../../../../ci/expected/ramfunc.run.grep.bar}} ``` ## Обходной путь для быстрой передачи сообщений From 0f903f60bd7486cbf86663d42f3abb8b67c1e04b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Tj=C3=A4der?= Date: Wed, 9 Feb 2022 18:44:57 +0100 Subject: [PATCH 118/423] GHA: Use rust-cache --- .github/workflows/build.yml | 178 +++++------------------------------- 1 file changed, 24 insertions(+), 154 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index fc07519fde..c2f635d5ef 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -17,7 +17,7 @@ jobs: runs-on: ubuntu-20.04 steps: - name: Checkout - uses: actions/checkout@v1 + uses: actions/checkout@v2 - name: Install Rust uses: actions-rs/toolchain@v1 @@ -52,28 +52,6 @@ jobs: - name: Checkout uses: actions/checkout@v2 - - name: Cache cargo dependencies - uses: actions/cache@v2 - with: - path: | - - ~/.cargo/bin/ - - ~/.cargo/registry/index/ - - ~/.cargo/registry/cache/ - - ~/.cargo/git/db/ - key: ${{ runner.OS }}-cargo-${{ hashFiles('**/Cargo.lock') }} - restore-keys: | - ${{ runner.OS }}-cargo-${{ hashFiles('**/Cargo.lock') }} - ${{ runner.OS }}-cargo- - - - name: Cache build output dependencies - uses: actions/cache@v2 - with: - path: target - key: ${{ runner.OS }}-build-${{ hashFiles('**/Cargo.lock') }} - restore-keys: | - ${{ runner.OS }}-build-${{ hashFiles('**/Cargo.lock') }} - ${{ runner.OS }}-build- - - name: Install Rust ${{ matrix.toolchain }} with target (${{ matrix.target }}) uses: actions-rs/toolchain@v1 with: @@ -84,6 +62,9 @@ jobs: - name: Fail on warnings run: sed -i 's,//deny_warnings_placeholder_for_ci,#![deny(warnings)],' src/lib.rs macros/src/lib.rs + - name: Cache Dependencies + uses: Swatinem/rust-cache@v1 + - name: cargo check uses: actions-rs/cargo@v1 with: @@ -106,28 +87,6 @@ jobs: - name: Checkout uses: actions/checkout@v2 - - name: Cache cargo dependencies - uses: actions/cache@v2 - with: - path: | - - ~/.cargo/bin/ - - ~/.cargo/registry/index/ - - ~/.cargo/registry/cache/ - - ~/.cargo/git/db/ - key: ${{ runner.OS }}-cargo-${{ hashFiles('**/Cargo.lock') }} - restore-keys: | - ${{ runner.OS }}-cargo-${{ hashFiles('**/Cargo.lock') }} - ${{ runner.OS }}-cargo- - - - name: Cache build output dependencies - uses: actions/cache@v2 - with: - path: target - key: ${{ runner.OS }}-build-${{ hashFiles('**/Cargo.lock') }} - restore-keys: | - ${{ runner.OS }}-build-${{ hashFiles('**/Cargo.lock') }} - ${{ runner.OS }}-build- - - name: Install Rust ${{ matrix.toolchain }} with target (${{ matrix.target }}) uses: actions-rs/toolchain@v1 with: @@ -136,8 +95,10 @@ jobs: override: true components: llvm-tools-preview + - name: Cache Dependencies + uses: Swatinem/rust-cache@v1 + - name: Check the examples - if: matrix.target == 'thumbv7m-none-eabi' uses: actions-rs/cargo@v1 with: use-cross: false @@ -159,28 +120,6 @@ jobs: - name: Checkout uses: actions/checkout@v2 - - name: Cache cargo dependencies - uses: actions/cache@v2 - with: - path: | - - ~/.cargo/bin/ - - ~/.cargo/registry/index/ - - ~/.cargo/registry/cache/ - - ~/.cargo/git/db/ - key: ${{ runner.OS }}-cargo-${{ hashFiles('**/Cargo.lock') }} - restore-keys: | - ${{ runner.OS }}-cargo-${{ hashFiles('**/Cargo.lock') }} - ${{ runner.OS }}-cargo- - - - name: Cache build output dependencies - uses: actions/cache@v2 - with: - path: target - key: ${{ runner.OS }}-build-${{ hashFiles('**/Cargo.lock') }} - restore-keys: | - ${{ runner.OS }}-build-${{ hashFiles('**/Cargo.lock') }} - ${{ runner.OS }}-build- - - name: Install Rust ${{ matrix.toolchain }} with target (${{ matrix.target }}) uses: actions-rs/toolchain@v1 with: @@ -197,6 +136,9 @@ jobs: version: latest use-tool-cache: true + - name: Cache Dependencies + uses: Swatinem/rust-cache@v1 + - name: Install QEMU run: | sudo apt update @@ -223,28 +165,6 @@ jobs: - name: Checkout uses: actions/checkout@v2 - - name: Cache cargo dependencies - uses: actions/cache@v2 - with: - path: | - - ~/.cargo/bin/ - - ~/.cargo/registry/index/ - - ~/.cargo/registry/cache/ - - ~/.cargo/git/db/ - key: ${{ runner.OS }}-cargo-${{ hashFiles('**/Cargo.lock') }} - restore-keys: | - ${{ runner.OS }}-cargo-${{ hashFiles('**/Cargo.lock') }} - ${{ runner.OS }}-cargo- - - - name: Cache build output dependencies - uses: actions/cache@v2 - with: - path: target - key: ${{ runner.OS }}-build-${{ hashFiles('**/Cargo.lock') }} - restore-keys: | - ${{ runner.OS }}-build-${{ hashFiles('**/Cargo.lock') }} - ${{ runner.OS }}-build- - - name: Install Rust ${{ matrix.toolchain }} with target (${{ matrix.target }}) uses: actions-rs/toolchain@v1 with: @@ -252,6 +172,9 @@ jobs: target: ${{ matrix.target }} override: true + - name: Cache Dependencies + uses: Swatinem/rust-cache@v1 + - name: Fail on warnings run: sed -i 's,//deny_warnings_placeholder_for_ci,#![deny(warnings)],' src/lib.rs macros/src/lib.rs @@ -276,28 +199,6 @@ jobs: - name: Checkout uses: actions/checkout@v2 - - name: Cache cargo dependencies - uses: actions/cache@v2 - with: - path: | - - ~/.cargo/bin/ - - ~/.cargo/registry/index/ - - ~/.cargo/registry/cache/ - - ~/.cargo/git/db/ - key: ${{ runner.OS }}-cargo-${{ hashFiles('**/Cargo.lock') }} - restore-keys: | - ${{ runner.OS }}-cargo-${{ hashFiles('**/Cargo.lock') }} - ${{ runner.OS }}-cargo- - - - name: Cache build output dependencies - uses: actions/cache@v2 - with: - path: target - key: ${{ runner.OS }}-build-${{ hashFiles('**/Cargo.lock') }} - restore-keys: | - ${{ runner.OS }}-build-${{ hashFiles('**/Cargo.lock') }} - ${{ runner.OS }}-build- - - name: Install Rust ${{ matrix.toolchain }} with target (${{ matrix.target }}) uses: actions-rs/toolchain@v1 with: @@ -305,6 +206,9 @@ jobs: target: ${{ matrix.target }} override: true + - name: Cache Dependencies + uses: Swatinem/rust-cache@v1 + - name: Fail on warnings run: sed -i 's,//deny_warnings_placeholder_for_ci,#![deny(warnings)],' src/lib.rs macros/src/lib.rs @@ -329,26 +233,6 @@ jobs: - name: Checkout uses: actions/checkout@v2 - - name: Cache cargo dependencies - uses: actions/cache@v2 - with: - path: | - - ~/.cargo/bin/ - - ~/.cargo/registry/index/ - - ~/.cargo/registry/cache/ - - ~/.cargo/git/db/ - key: ${{ runner.OS }}-cargo-${{ hashFiles('**/Cargo.lock') }} - restore-keys: | - ${{ runner.OS }}-cargo- - - - name: Cache build output dependencies - uses: actions/cache@v2 - with: - path: target - key: ${{ runner.OS }}-build-${{ hashFiles('**/Cargo.lock') }} - restore-keys: | - ${{ runner.OS }}-build- - - name: Install Rust uses: actions-rs/toolchain@v1 with: @@ -356,6 +240,12 @@ jobs: target: ${{ matrix.target }} override: true + - name: Cache Dependencies + uses: Swatinem/rust-cache@v1 + + - name: Fail on warnings + run: sed -i 's,//deny_warnings_placeholder_for_ci,#![deny(warnings)],' src/lib.rs macros/src/lib.rs + - uses: actions-rs/cargo@v1 with: use-cross: false @@ -371,26 +261,6 @@ jobs: - name: Checkout uses: actions/checkout@v2 - - name: Cache cargo dependencies - uses: actions/cache@v2 - with: - path: | - - ~/.cargo/bin/ - - ~/.cargo/registry/index/ - - ~/.cargo/registry/cache/ - - ~/.cargo/git/db/ - key: ${{ runner.OS }}-cargo-${{ hashFiles('**/Cargo.lock') }} - restore-keys: | - ${{ runner.OS }}-cargo- - - - name: Cache build output dependencies - uses: actions/cache@v2 - with: - path: target - key: ${{ runner.OS }}-build-${{ hashFiles('**/Cargo.lock') }} - restore-keys: | - ${{ runner.OS }}-build- - - name: Cache pip installed linkchecker uses: actions/cache@v2 with: @@ -453,7 +323,7 @@ jobs: run: pip install git+https://github.com/linkchecker/linkchecker.git - name: mdBook Action - uses: peaceiris/actions-mdbook@v1.1.13 + uses: peaceiris/actions-mdbook@v1 with: mdbook-version: 'latest' @@ -509,7 +379,7 @@ jobs: run: python -c "import sys; print(sys.version)" - name: mdBook Action - uses: peaceiris/actions-mdbook@v1.1.13 + uses: peaceiris/actions-mdbook@v1 with: mdbook-version: 'latest' From b42abe1b4fdba8cb3b027ea12b55e583d3147ea6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Tj=C3=A4der?= Date: Wed, 9 Feb 2022 18:45:31 +0100 Subject: [PATCH 119/423] GHA: Cleanup single target jobs --- .github/workflows/build.yml | 29 ++++++++--------------------- 1 file changed, 8 insertions(+), 21 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c2f635d5ef..1bc0a71dbd 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -189,21 +189,15 @@ jobs: testmacros: name: testmacros runs-on: ubuntu-20.04 - strategy: - matrix: - target: - - x86_64-unknown-linux-gnu - toolchain: - - stable steps: - name: Checkout uses: actions/checkout@v2 - - name: Install Rust ${{ matrix.toolchain }} with target (${{ matrix.target }}) + - name: Install Rust uses: actions-rs/toolchain@v1 with: - toolchain: ${{ matrix.toolchain }} - target: ${{ matrix.target }} + toolchain: stable + target: x86_64-unknown-linux-gnu override: true - name: Cache Dependencies @@ -217,18 +211,12 @@ jobs: with: use-cross: false command: test - args: --manifest-path macros/Cargo.toml --target=${{ matrix.target }} + args: --manifest-path macros/Cargo.toml - # Run test suite for thumbv7m + # Run test suite tests: name: tests runs-on: ubuntu-20.04 - strategy: - matrix: - target: - - x86_64-unknown-linux-gnu - toolchain: - - stable steps: - name: Checkout uses: actions/checkout@v2 @@ -236,8 +224,8 @@ jobs: - name: Install Rust uses: actions-rs/toolchain@v1 with: - toolchain: ${{ matrix.toolchain }} - target: ${{ matrix.target }} + toolchain: stable + target: x86_64-unknown-linux-gnu override: true - name: Cache Dependencies @@ -250,13 +238,12 @@ jobs: with: use-cross: false command: test - args: --test tests --target=${{ matrix.target }} + args: --test tests # Build documentation, check links docs: name: docs runs-on: ubuntu-20.04 - steps: - name: Checkout uses: actions/checkout@v2 From 10ec36b4434d3ba0426ad427c321067286ebc00c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Tj=C3=A4der?= Date: Wed, 9 Feb 2022 18:45:49 +0100 Subject: [PATCH 120/423] GHA: Add cargo clippy --- .github/workflows/build.yml | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 1bc0a71dbd..f0fac75dd5 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -72,6 +72,33 @@ jobs: command: check args: --target=${{ matrix.target }} + # Clippy + clippy: + name: Cargo clippy + runs-on: ubuntu-20.04 + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: Install Rust stable + uses: actions-rs/toolchain@v1 + with: + toolchain: stable + target: x86_64-unknown-linux-gnu + override: true + + - name: Fail on warnings + run: sed -i 's,//deny_warnings_placeholder_for_ci,#![deny(warnings)],' src/lib.rs macros/src/lib.rs + + - name: Cache Dependencies + uses: Swatinem/rust-cache@v1 + + - name: cargo clippy + uses: actions-rs/cargo@v1 + with: + use-cross: false + command: clippy + # Verify all examples, checks checkexamples: name: checkexamples @@ -467,6 +494,7 @@ jobs: needs: - style - check + - clippy - checkexamples - testexamples - checkmacros From 099544f655795aa63b87d2f0b004331a57b93dbe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Tj=C3=A4der?= Date: Wed, 9 Feb 2022 18:56:35 +0100 Subject: [PATCH 121/423] Fix/mute clippy errors --- macros/src/codegen/module.rs | 2 +- macros/src/codegen/software_tasks.rs | 1 + macros/src/codegen/util.rs | 26 +++++++------------------- src/tq.rs | 1 + 4 files changed, 10 insertions(+), 20 deletions(-) diff --git a/macros/src/codegen/module.rs b/macros/src/codegen/module.rs index 996af64175..8410b7d75c 100644 --- a/macros/src/codegen/module.rs +++ b/macros/src/codegen/module.rs @@ -269,7 +269,7 @@ pub fn codegen( let m_ident = util::monotonic_ident(&monotonic_name); let m_isr = &monotonic.args.binds; let enum_ = util::interrupt_ident(); - let spawn_handle_string = format!("{}::SpawnHandle", m.to_string()); + let spawn_handle_string = format!("{}::SpawnHandle", m); let (enable_interrupt, pend) = if &*m_isr.to_string() == "SysTick" { ( diff --git a/macros/src/codegen/software_tasks.rs b/macros/src/codegen/software_tasks.rs index 2008b6c98a..c767032f5c 100644 --- a/macros/src/codegen/software_tasks.rs +++ b/macros/src/codegen/software_tasks.rs @@ -38,6 +38,7 @@ pub fn codegen( // Create free queues and inputs / instants buffers let fq = util::fq_ident(name); + #[allow(clippy::redundant_closure)] let (fq_ty, fq_expr, mk_uninit): (_, _, Box Option<_>>) = { ( quote!(rtic::export::SCFQ<#cap_lit_p1>), diff --git a/macros/src/codegen/util.rs b/macros/src/codegen/util.rs index e865434528..46eace4c39 100644 --- a/macros/src/codegen/util.rs +++ b/macros/src/codegen/util.rs @@ -16,7 +16,7 @@ pub fn capacity_literal(capacity: usize) -> LitInt { /// Identifier for the free queue pub fn fq_ident(task: &Ident) -> Ident { - mark_internal_name(&format!("{}_FQ", task.to_string())) + mark_internal_name(&format!("{}_FQ", task)) } /// Generates a `Mutex` implementation @@ -103,17 +103,12 @@ pub fn mark_internal_name(name: &str) -> Ident { /// Generate an internal identifier for monotonics pub fn internal_monotonics_ident(task: &Ident, monotonic: &Ident, ident_name: &str) -> Ident { - mark_internal_name(&format!( - "{}_{}_{}", - task.to_string(), - monotonic.to_string(), - ident_name, - )) + mark_internal_name(&format!("{}_{}_{}", task, monotonic, ident_name,)) } /// Generate an internal identifier for tasks pub fn internal_task_ident(task: &Ident, ident_name: &str) -> Ident { - mark_internal_name(&format!("{}_{}", task.to_string(), ident_name)) + mark_internal_name(&format!("{}_{}", task, ident_name)) } fn link_section_index() -> usize { @@ -253,26 +248,19 @@ pub fn monotonic_ident(name: &str) -> Ident { } pub fn static_shared_resource_ident(name: &Ident) -> Ident { - mark_internal_name(&format!("shared_resource_{}", name.to_string())) + mark_internal_name(&format!("shared_resource_{}", name)) } pub fn static_local_resource_ident(name: &Ident) -> Ident { - mark_internal_name(&format!("local_resource_{}", name.to_string())) + mark_internal_name(&format!("local_resource_{}", name)) } pub fn declared_static_local_resource_ident(name: &Ident, task_name: &Ident) -> Ident { - mark_internal_name(&format!( - "local_{}_{}", - task_name.to_string(), - name.to_string() - )) + mark_internal_name(&format!("local_{}_{}", task_name, name)) } pub fn need_to_lock_ident(name: &Ident) -> Ident { - Ident::new( - &format!("{}_that_needs_to_be_locked", name.to_string()), - name.span(), - ) + Ident::new(&format!("{}_that_needs_to_be_locked", name), name.span()) } /// The name to get better RT flag errors diff --git a/src/tq.rs b/src/tq.rs index 26ebbd96c3..9033022bcd 100644 --- a/src/tq.rs +++ b/src/tq.rs @@ -71,6 +71,7 @@ where } /// Update the instant at an marker value to a new instant + #[allow(clippy::result_unit_err)] pub fn update_marker( &mut self, marker: u32, From e9675c9a615ac34ce71669a6a5bd8245672b8407 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Tj=C3=A4der?= Date: Wed, 9 Feb 2022 18:57:58 +0100 Subject: [PATCH 122/423] Update CHANGELOG --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 84b42f2257..e16bb75c2c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ This project adheres to [Semantic Versioning](http://semver.org/). ### Added +- Cargo clippy in CI +- Use rust-cache Github Action - CI changelog entry enforcer - `examples/periodic-at.rs`, an example of a periodic timer without accumulated drift. - `examples/periodic-at2.rs`, an example of a periodic process with two tasks, with offset timing. Here we depict two alternative usages of the timer type, explicit and trait based. From 578cc903c95aec324733df447cb7f54f41fa1581 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Tj=C3=A4der?= Date: Thu, 10 Feb 2022 09:26:46 +0100 Subject: [PATCH 123/423] Add bors/* to branches --- .github/workflows/build.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f0fac75dd5..d69740ba16 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -6,6 +6,8 @@ on: - master - staging - trying + - bors/staging + - bors/trying env: CARGO_TERM_COLOR: always From 78bdab6bf1d840ef6b072261ea33d2b8d1ad3d8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Tj=C3=A4der?= Date: Wed, 9 Feb 2022 19:23:26 +0100 Subject: [PATCH 124/423] Require clippy for deploy --- .github/workflows/build.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d69740ba16..976e698a47 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -370,6 +370,7 @@ jobs: needs: - style - check + - clippy - checkexamples - testexamples - checkmacros From 4a7951121db71ff075bf5a8eb2cb3760c65ace7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Tj=C3=A4der?= Date: Wed, 9 Feb 2022 21:03:57 +0100 Subject: [PATCH 125/423] GHA: Automatic merge to release/vX --- .github/workflows/build.yml | 61 ++++++++++++++++++++++++++++++++----- 1 file changed, 53 insertions(+), 8 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 976e698a47..9d61cc0f08 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -363,9 +363,11 @@ jobs: linkchecker $td/book/en/ linkchecker $td/book/ru/ - # Only runs when pushing to master branch - deploy: - name: deploy + # Update stable branch + # + # This needs to run before book is built + mergetostablebranch: + name: If CI passes, merge master branch into release/vX runs-on: ubuntu-20.04 needs: - style @@ -378,6 +380,39 @@ jobs: - tests - docs - mdbook + + # Only run this when pushing to master branch + if: github.ref == 'refs/heads/master' + steps: + - uses: actions/checkout@v2 + + - name: Get crate version and print output branch release/vX + id: crateversionbranch + # Parse metadata for version number, extract the Semver Major + run: | + VERSION=$(cargo metadata --format-version 1 --no-deps --offline | jq -r '.packages[] | select(.name =="cortex-m-rtic") | .version') + VERSIONMAJOR=${VERSION%.*.*} + echo "branch=release/v$VERSIONMAJOR" >> $GITHUB_ENV + echo "versionmajor=$VERSIONMAJOR" >> $GITHUB_ENV + echo "version=$VERSION" >> $GITHUB_ENV + + - uses: everlytic/branch-merge@1.1.2 + with: + github_token: ${{ github.token }} + source_ref: 'master' + target_branch: ${{ env.branch }} + commit_message_template: '[Bors] Merged {source_ref} into target {target_branch}' + + # Only runs when pushing to master branch + # Bors run CI against staging branch, + # if that succeeds Borst tries against master branch + # If all tests pass, then deploy stage is run + deploy: + name: deploy + runs-on: ubuntu-20.04 + needs: + mergetostablebranch + # Only run this when pushing to master branch if: github.ref == 'refs/heads/master' steps: @@ -400,6 +435,16 @@ jobs: with: mdbook-version: 'latest' + - name: Get crate version + id: crateversion + # Parse metadata for version number, extract the Semver Major + run: | + VERSION=$(cargo metadata --format-version 1 --no-deps --offline | jq -r '.packages[] | select(.name =="cortex-m-rtic") | .version') + VERSIONMAJOR=${VERSION%.*.*} + echo "branch=release/v$VERSIONMAJOR" >> $GITHUB_ENV + echo "versionmajor=$VERSIONMAJOR" >> $GITHUB_ENV + echo "version=$VERSION" >> $GITHUB_ENV + - name: Remove cargo-config run: rm -f .cargo/config @@ -412,12 +457,12 @@ jobs: langs=( en ru ) devver=( dev ) # The latest stable must be the first element in the array - vers=( 1.0.x 0.5.x 0.4.x ) + vers=( "1" "0.5" "0.4" ) # All releases start with "v" # followed by MAJOR.MINOR.PATCH, see semver.org - # Retain MAJOR.MINOR as $stable - stable=${vers%.*} + # Store first in array as stable + stable=${vers} echo "Stable version: $stable" @@ -449,11 +494,11 @@ jobs: # Build older versions, including stable root=$(pwd) for ver in ${vers[@]}; do - prefix=${ver%.*} + prefix=${ver} mkdir -p $td/$prefix/book src=$(mktemp -d) - curl -L https://github.com/rtic-rs/cortex-m-rtic/archive/v${ver}.tar.gz | tar xz --strip-components 1 -C $src + curl -L https://github.com/rtic-rs/cortex-m-rtic/archive/release/v${ver}.tar.gz | tar xz --strip-components 1 -C $src pushd $src rm -f .cargo/config From 780b3672ca219196a6e990b4fa6590815537e374 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Tj=C3=A4der?= Date: Wed, 9 Feb 2022 21:52:05 +0100 Subject: [PATCH 126/423] Link dev-book to stable if they are describe the same release --- .github/workflows/build.yml | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 9d61cc0f08..923c30f15a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -479,8 +479,18 @@ jobs: sed "s|URL|$stable|g" redirect.html > $td/index.html # Create the redirects for dev-version - sed 's|URL|rtic/index.html|g' redirect.html > $td/$devver/api/index.html - sed 's|URL|book/en|g' redirect.html > $td/$devver/index.html + # If the current stable and the version being built differ, + # then there is a dev-version and the links should point to it. + if [[ "$stable" != "{{ env.versionmajor }}" ]]; + then + sed 's|URL|rtic/index.html|g' redirect.html > $td/$devver/api/index.html + sed 's|URL|book/en|g' redirect.html > $td/$devver/index.html + else + # If the current stable and the "dev" version in master branch + # share the same major version, redirect dev/ to stable book + sed 's|URL|rtic.rs/$stable/api/rtic|g' redirect.html > $td/$devver/api/index.html + sed 's|URL|rtic.rs/$stable|g' redirect.html > $td/$devver/index.html + fi # Build books for lang in ${langs[@]}; do From 4d3758a6b2c391cf4c06fbafa02a1b79cc82abae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Tj=C3=A4der?= Date: Wed, 9 Feb 2022 22:18:17 +0100 Subject: [PATCH 127/423] Update CHANGELOG --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e16bb75c2c..0d30960b0d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,9 @@ This project adheres to [Semantic Versioning](http://semver.org/). ### Added +- If current $stable and master version matches, dev-book redirects to $stable book +- During deploy stage, merge master branch into current stable IFF cargo package version matches +- Rework branch structure, release/vVERSION - Cargo clippy in CI - Use rust-cache Github Action - CI changelog entry enforcer From a8a55a3913f66189b0a7e9c501245dbea64101b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Tj=C3=A4der?= Date: Thu, 10 Feb 2022 00:17:50 +0100 Subject: [PATCH 128/423] Docs: Fix dated migration docs for spawn --- CHANGELOG.md | 1 + book/en/src/migration/migration_v5.md | 19 +++++++++++++------ 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0d30960b0d..702d48923d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). ### Fixed +- Fix dated migration docs for spawn - Force mdBook to return error codes - Readded missing ramfunc output to book diff --git a/book/en/src/migration/migration_v5.md b/book/en/src/migration/migration_v5.md index e19fb62fe8..731931f013 100644 --- a/book/en/src/migration/migration_v5.md +++ b/book/en/src/migration/migration_v5.md @@ -316,11 +316,10 @@ mod app { } ``` -## Spawn/schedule from anywhere - -With the new "spawn/schedule from anywhere", old code such as: - +## Spawn from anywhere +With the new spawn/spawn_after/spawn_at interface, +old code requiring the context `cx` for spawning such as: ``` rust #[task(spawn = [bar])] @@ -344,12 +343,20 @@ fn foo(_c: foo::Context) { #[task] fn bar(_c: bar::Context) { - foo::schedule(/* ... */).unwrap(); + // Takes a Duration, relative to “now” + let spawn_handle = foo::spawn_after(/* ... */); +} + +#[task] +fn bar(_c: bar::Context) { + // Takes an Instant + let spawn_handle = foo::spawn_at(/* ... */); } ``` -Note that the attributes `spawn` and `schedule` are no longer needed. +Thus the requirement of having access to the context is dropped. +Note that the attributes `spawn`/`schedule` in the task definition are no longer needed. --- From a3aa3ace1c8801e05d050c8eb95c1218a6c3096b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Tj=C3=A4der?= Date: Thu, 10 Feb 2022 10:40:49 +0100 Subject: [PATCH 129/423] GHA: Print current crate version too --- .github/workflows/build.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 923c30f15a..2e93a79fba 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -463,8 +463,10 @@ jobs: # followed by MAJOR.MINOR.PATCH, see semver.org # Store first in array as stable stable=${vers} + crateversion={{ env.versionmajor }} - echo "Stable version: $stable" + echo "Latest stable version: $stable" + echo "Current crate version: $crateversion" # Create directories td=$(mktemp -d) @@ -481,7 +483,7 @@ jobs: # Create the redirects for dev-version # If the current stable and the version being built differ, # then there is a dev-version and the links should point to it. - if [[ "$stable" != "{{ env.versionmajor }}" ]]; + if [[ "$stable" != "$crateversion" ]]; then sed 's|URL|rtic/index.html|g' redirect.html > $td/$devver/api/index.html sed 's|URL|book/en|g' redirect.html > $td/$devver/index.html From 48d100bd1d9bf1af64ff3284ac1ef8e7449b98ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Tj=C3=A4der?= Date: Tue, 15 Feb 2022 18:30:54 +0100 Subject: [PATCH 130/423] Docs: fix link --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index daaba78a0f..9c6a861e94 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -4,7 +4,7 @@ New features should go through the [RFC process][rfcs] before creating a Pull Request to this repository. -[rfcs](https://github.com/rtic-rs/rfcs) +[rfcs]: https://github.com/rtic-rs/rfcs ## Bugs From 5f3c5f7f2c58da9c9b87c7da6b0507cd08fcb96f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Tj=C3=A4der?= Date: Tue, 15 Feb 2022 19:51:54 +0100 Subject: [PATCH 131/423] CHANGELOG merge=union --- .gitattributes | 1 + CHANGELOG.md | 1 + 2 files changed, 2 insertions(+) create mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000000..a19ade077d --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +CHANGELOG.md merge=union diff --git a/CHANGELOG.md b/CHANGELOG.md index 702d48923d..2bd1829d76 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). ### Added +- Improve how CHANGELOG.md merges are handled - If current $stable and master version matches, dev-book redirects to $stable book - During deploy stage, merge master branch into current stable IFF cargo package version matches - Rework branch structure, release/vVERSION From dfb6e3631144e8a5f3735ea2ddf7cecd9ef4ee72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Tj=C3=A4der?= Date: Thu, 10 Feb 2022 09:20:31 +0100 Subject: [PATCH 132/423] action-rs tool-cache is deprecated, always failing --- .github/workflows/build.yml | 1 - CHANGELOG.md | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 2e93a79fba..875c3f39d1 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -163,7 +163,6 @@ jobs: with: crate: cargo-binutils version: latest - use-tool-cache: true - name: Cache Dependencies uses: Swatinem/rust-cache@v1 diff --git a/CHANGELOG.md b/CHANGELOG.md index 2bd1829d76..1b140e999e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). ### Fixed - Fix dated migration docs for spawn +- Remove obsolete action-rs tool-cache - Force mdBook to return error codes - Readded missing ramfunc output to book From da25327226342bcb87d7962a36f86f31fac71ef5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Tj=C3=A4der?= Date: Thu, 10 Feb 2022 09:07:36 +0100 Subject: [PATCH 133/423] Create tiny change --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2bd1829d76..045db8ce34 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,7 +15,8 @@ This project adheres to [Semantic Versioning](http://semver.org/). - Use rust-cache Github Action - CI changelog entry enforcer - `examples/periodic-at.rs`, an example of a periodic timer without accumulated drift. -- `examples/periodic-at2.rs`, an example of a periodic process with two tasks, with offset timing. Here we depict two alternative usages of the timer type, explicit and trait based. +- `examples/periodic-at2.rs`, an example of a periodic process with two tasks, with offset timing. + Here we depict two alternative usages of the timer type, explicit and trait based. - book: Update `Monotonic` tips. ### Fixed From 2c14c9bce3b22462cce982258ddecb53df16ab49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Tj=C3=A4der?= Date: Fri, 18 Feb 2022 15:11:55 +0100 Subject: [PATCH 134/423] rtic::mutex::prelude::* fixes glob import lint rtic-core Mutex, Exclusive and multi-lock retained in old location to not be backwards breaking --- CHANGELOG.md | 1 + macros/src/codegen/hardware_tasks.rs | 2 +- macros/src/codegen/idle.rs | 2 +- macros/src/codegen/software_tasks.rs | 2 +- src/lib.rs | 5 +++++ 5 files changed, 9 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2bd1829d76..576bc6039d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). ### Fixed +- Re-export `rtic_core::prelude` as `rtic::mutex::prelude` to allow glob imports + Clippy - Fix dated migration docs for spawn - Force mdBook to return error codes - Readded missing ramfunc output to book diff --git a/macros/src/codegen/hardware_tasks.rs b/macros/src/codegen/hardware_tasks.rs index e6192e2c00..f75c71d43f 100644 --- a/macros/src/codegen/hardware_tasks.rs +++ b/macros/src/codegen/hardware_tasks.rs @@ -97,7 +97,7 @@ pub fn codegen( #[allow(non_snake_case)] fn #name(#context: #name::Context) { use rtic::Mutex as _; - use rtic::mutex_prelude::*; + use rtic::mutex::prelude::*; #(#stmts)* } diff --git a/macros/src/codegen/idle.rs b/macros/src/codegen/idle.rs index 0dededa4e6..83b85d7ba6 100644 --- a/macros/src/codegen/idle.rs +++ b/macros/src/codegen/idle.rs @@ -68,7 +68,7 @@ pub fn codegen( #[allow(non_snake_case)] fn #name(#context: #name::Context) -> ! { use rtic::Mutex as _; - use rtic::mutex_prelude::*; + use rtic::mutex::prelude::*; #(#stmts)* } diff --git a/macros/src/codegen/software_tasks.rs b/macros/src/codegen/software_tasks.rs index c767032f5c..0357003f26 100644 --- a/macros/src/codegen/software_tasks.rs +++ b/macros/src/codegen/software_tasks.rs @@ -131,7 +131,7 @@ pub fn codegen( #[allow(non_snake_case)] fn #name(#context: #name::Context #(,#inputs)*) { use rtic::Mutex as _; - use rtic::mutex_prelude::*; + use rtic::mutex::prelude::*; #(#stmts)* } diff --git a/src/lib.rs b/src/lib.rs index 25f93999dd..d3195a6346 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -42,6 +42,11 @@ pub use cortex_m_rtic_macros::app; pub use rtic_core::{prelude as mutex_prelude, Exclusive, Mutex}; pub use rtic_monotonic::{self, Monotonic}; +/// module `mutex::prelude` provides `Mutex` and multi-lock variants. Recommended over `mutex_prelude` +pub mod mutex { + pub use rtic_core::prelude; +} + #[doc(hidden)] pub mod export; #[doc(hidden)] From 7eaf732c4c2839da0d352dfc080e107828cdadd4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Tj=C3=A4der?= Date: Fri, 18 Feb 2022 19:58:21 +0100 Subject: [PATCH 135/423] Provide Mutex relative to prelude to fix doc linking issues coming from rtic-core --- src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lib.rs b/src/lib.rs index d3195a6346..871141f62c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -45,6 +45,7 @@ pub use rtic_monotonic::{self, Monotonic}; /// module `mutex::prelude` provides `Mutex` and multi-lock variants. Recommended over `mutex_prelude` pub mod mutex { pub use rtic_core::prelude; + pub use rtic_core::Mutex; } #[doc(hidden)] From 5ed93bd1bf056f1d2b8632502300d7488df4e9df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Tj=C3=A4der?= Date: Fri, 18 Feb 2022 19:38:48 +0100 Subject: [PATCH 136/423] Clippy with pedantic suggestions --- CHANGELOG.md | 1 + build.rs | 6 +- macros/src/analyze.rs | 2 +- macros/src/codegen.rs | 7 ++- macros/src/codegen/local_resources_struct.rs | 2 +- macros/src/codegen/module.rs | 13 ++--- macros/src/codegen/post_init.rs | 1 + macros/src/codegen/pre_init.rs | 8 +-- macros/src/codegen/shared_resources.rs | 7 +-- macros/src/codegen/shared_resources_struct.rs | 55 +++++++++---------- macros/src/codegen/software_tasks.rs | 2 +- macros/src/codegen/timer_queue.rs | 1 + macros/src/codegen/util.rs | 10 ++-- macros/src/lib.rs | 21 +++---- src/export.rs | 8 ++- src/lib.rs | 13 +++-- src/tq.rs | 11 ++-- 17 files changed, 84 insertions(+), 84 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 94b745b387..53406faf45 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). ### Fixed - Re-export `rtic_core::prelude` as `rtic::mutex::prelude` to allow glob imports + Clippy +- Fix all except `must_use` lints from clippy::pedantic - Fix dated migration docs for spawn - Remove obsolete action-rs tool-cache - Force mdBook to return error codes diff --git a/build.rs b/build.rs index e46837f07e..7c518c90d4 100644 --- a/build.rs +++ b/build.rs @@ -4,18 +4,18 @@ fn main() { let target = env::var("TARGET").unwrap(); if version_check::Channel::read().unwrap().is_nightly() { - println!("cargo:rustc-cfg=rustc_is_nightly") + println!("cargo:rustc-cfg=rustc_is_nightly"); } if target.starts_with("thumbv6m") { - println!("cargo:rustc-cfg=armv6m") + println!("cargo:rustc-cfg=armv6m"); } if target.starts_with("thumbv7m") | target.starts_with("thumbv7em") | target.starts_with("thumbv8m") { - println!("cargo:rustc-cfg=armv7m") + println!("cargo:rustc-cfg=armv7m"); } println!("cargo:rerun-if-changed=build.rs"); diff --git a/macros/src/analyze.rs b/macros/src/analyze.rs index 6b26138879..d255b7f5bc 100644 --- a/macros/src/analyze.rs +++ b/macros/src/analyze.rs @@ -34,7 +34,7 @@ pub fn app(analysis: P, app: &App) -> P { // map from priorities to interrupts (holding name and attributes) let interrupts: BTreeMap = priorities .iter() - .cloned() + .copied() .rev() .zip(&app.args.extern_interrupts) .map(|(p, (id, ext))| (p, (id.clone(), ext.clone()))) diff --git a/macros/src/codegen.rs b/macros/src/codegen.rs index 100508432d..f5cae34a72 100644 --- a/macros/src/codegen.rs +++ b/macros/src/codegen.rs @@ -20,6 +20,7 @@ mod software_tasks; mod timer_queue; mod util; +#[allow(clippy::too_many_lines)] pub fn app(app: &App, analysis: &Analysis, extra: &Extra) -> TokenStream2 { let mut mod_app = vec![]; let mut mains = vec![]; @@ -142,7 +143,9 @@ pub fn app(app: &App, analysis: &Analysis, extra: &Extra) -> TokenStream2 { }) .collect(); - let monotonics = if !monotonic_parts.is_empty() { + let monotonics = if monotonic_parts.is_empty() { + quote!() + } else { quote!( pub use rtic::Monotonic as _; @@ -151,8 +154,6 @@ pub fn app(app: &App, analysis: &Analysis, extra: &Extra) -> TokenStream2 { #(#monotonic_parts)* } ) - } else { - quote!() }; let rt_err = util::rt_err_ident(); diff --git a/macros/src/codegen/local_resources_struct.rs b/macros/src/codegen/local_resources_struct.rs index 10bde5fefb..b7eae3f9d3 100644 --- a/macros/src/codegen/local_resources_struct.rs +++ b/macros/src/codegen/local_resources_struct.rs @@ -78,7 +78,7 @@ pub fn codegen(ctxt: Context, needs_lt: &mut bool, app: &App) -> (TokenStream2, pub __marker__: core::marker::PhantomData<&'a ()> )); - values.push(quote!(__marker__: core::marker::PhantomData)) + values.push(quote!(__marker__: core::marker::PhantomData)); } } diff --git a/macros/src/codegen/module.rs b/macros/src/codegen/module.rs index 8410b7d75c..fd8137fad4 100644 --- a/macros/src/codegen/module.rs +++ b/macros/src/codegen/module.rs @@ -3,6 +3,7 @@ use proc_macro2::TokenStream as TokenStream2; use quote::quote; use rtic_syntax::{ast::App, Context}; +#[allow(clippy::too_many_lines)] pub fn codegen( ctxt: Context, shared_resources_tick: bool, @@ -50,11 +51,7 @@ pub fn codegen( values.push(quote!(core)); } - Context::Idle => {} - - Context::HardwareTask(_) => {} - - Context::SoftwareTask(_) => {} + Context::Idle | Context::HardwareTask(_) | Context::SoftwareTask(_) => {} } // if ctxt.has_locals(app) { @@ -438,7 +435,9 @@ pub fn codegen( } } - if !items.is_empty() { + if items.is_empty() { + quote!() + } else { quote!( #(#items)* @@ -449,7 +448,5 @@ pub fn codegen( #(#module_items)* } ) - } else { - quote!() } } diff --git a/macros/src/codegen/post_init.rs b/macros/src/codegen/post_init.rs index 8bd3a7ddcf..9531254caf 100644 --- a/macros/src/codegen/post_init.rs +++ b/macros/src/codegen/post_init.rs @@ -48,6 +48,7 @@ pub fn codegen(app: &App, analysis: &Analysis) -> Vec { // let doc = format!(" RTIC internal: {}:{}", file!(), line!()); // stmts.push(quote!(#[doc = #doc])); + #[allow(clippy::cast_possible_truncation)] let idx = Index { index: i as u32, span: Span::call_site(), diff --git a/macros/src/codegen/pre_init.rs b/macros/src/codegen/pre_init.rs index 7aaf20fc79..91c9991274 100644 --- a/macros/src/codegen/pre_init.rs +++ b/macros/src/codegen/pre_init.rs @@ -41,12 +41,12 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec *priority, - Some(Ownership::CoOwned { priority }) => *priority, + Some(Ownership::Owned { priority } | Ownership::CoOwned { priority }) => *priority, Some(Ownership::Contended { ceiling }) => *ceiling, None => 0, }; @@ -89,9 +88,9 @@ pub fn codegen( cfgs, true, &shared_name, - quote!(#ty), + "e!(#ty), ceiling, - ptr, + &ptr, )); } } diff --git a/macros/src/codegen/shared_resources_struct.rs b/macros/src/codegen/shared_resources_struct.rs index 7ae8d8086e..90cbc1beef 100644 --- a/macros/src/codegen/shared_resources_struct.rs +++ b/macros/src/codegen/shared_resources_struct.rs @@ -35,33 +35,8 @@ pub fn codegen(ctxt: Context, needs_lt: &mut bool, app: &App) -> (TokenStream2, let mangled_name = util::static_shared_resource_ident(name); let shared_name = util::need_to_lock_ident(name); - if !res.properties.lock_free { - if access.is_shared() { - lt = Some(quote!('a)); - - fields.push(quote!( - #(#cfgs)* - pub #name: &'a #ty - )); - } else { - // Resource proxy - lt = Some(quote!('a)); - - fields.push(quote!( - #(#cfgs)* - pub #name: shared_resources::#shared_name<'a> - )); - - values.push(quote!( - #(#cfgs)* - #name: shared_resources::#shared_name::new(priority) - - )); - - // continue as the value has been filled, - continue; - } - } else { + if res.properties.lock_free { + // Lock free resources of `idle` and `init` get 'static lifetime let lt = if ctxt.runs_once() { quote!('static) } else { @@ -73,6 +48,30 @@ pub fn codegen(ctxt: Context, needs_lt: &mut bool, app: &App) -> (TokenStream2, #(#cfgs)* pub #name: &#lt #mut_ #ty )); + } else if access.is_shared() { + lt = Some(quote!('a)); + + fields.push(quote!( + #(#cfgs)* + pub #name: &'a #ty + )); + } else { + // Resource proxy + lt = Some(quote!('a)); + + fields.push(quote!( + #(#cfgs)* + pub #name: shared_resources::#shared_name<'a> + )); + + values.push(quote!( + #(#cfgs)* + #name: shared_resources::#shared_name::new(priority) + + )); + + // continue as the value has been filled, + continue; } let expr = if access.is_exclusive() { @@ -97,7 +96,7 @@ pub fn codegen(ctxt: Context, needs_lt: &mut bool, app: &App) -> (TokenStream2, pub __marker__: core::marker::PhantomData<&'a ()> )); - values.push(quote!(__marker__: core::marker::PhantomData)) + values.push(quote!(__marker__: core::marker::PhantomData)); } } diff --git a/macros/src/codegen/software_tasks.rs b/macros/src/codegen/software_tasks.rs index 0357003f26..77559493bc 100644 --- a/macros/src/codegen/software_tasks.rs +++ b/macros/src/codegen/software_tasks.rs @@ -43,7 +43,7 @@ pub fn codegen( ( quote!(rtic::export::SCFQ<#cap_lit_p1>), quote!(rtic::export::Queue::new()), - Box::new(|| util::link_section_uninit()), + Box::new(|| Some(util::link_section_uninit())), ) }; mod_app.push(quote!( diff --git a/macros/src/codegen/timer_queue.rs b/macros/src/codegen/timer_queue.rs index 2a344d256b..32e288c56d 100644 --- a/macros/src/codegen/timer_queue.rs +++ b/macros/src/codegen/timer_queue.rs @@ -5,6 +5,7 @@ use rtic_syntax::ast::App; use crate::{analyze::Analysis, check::Extra, codegen::util}; /// Generates timer queues and timer queue handlers +#[allow(clippy::too_many_lines)] pub fn codegen(app: &App, analysis: &Analysis, _extra: &Extra) -> Vec { let mut items = vec![]; diff --git a/macros/src/codegen/util.rs b/macros/src/codegen/util.rs index 46eace4c39..6a07732c34 100644 --- a/macros/src/codegen/util.rs +++ b/macros/src/codegen/util.rs @@ -25,9 +25,9 @@ pub fn impl_mutex( cfgs: &[Attribute], resources_prefix: bool, name: &Ident, - ty: TokenStream2, + ty: &TokenStream2, ceiling: u8, - ptr: TokenStream2, + ptr: &TokenStream2, ) -> TokenStream2 { let (path, priority) = if resources_prefix { (quote!(shared_resources::#name), quote!(self.priority())) @@ -117,11 +117,11 @@ fn link_section_index() -> usize { INDEX.fetch_add(1, Ordering::Relaxed) } -// NOTE `None` means in shared memory -pub fn link_section_uninit() -> Option { +/// Add `link_section` attribute +pub fn link_section_uninit() -> TokenStream2 { let section = format!(".uninit.rtic{}", link_section_index()); - Some(quote!(#[link_section = #section])) + quote!(#[link_section = #section]) } // Regroups the inputs of a task diff --git a/macros/src/lib.rs b/macros/src/lib.rs index 13b6a7c213..2b52601756 100644 --- a/macros/src/lib.rs +++ b/macros/src/lib.rs @@ -20,7 +20,10 @@ mod tests; /// Attribute used to declare a RTIC application /// /// For user documentation see the [RTIC book](https://rtic.rs) - +/// +/// # Panics +/// +/// Should never panic, cargo feeds a path which is later converted to a string #[proc_macro_attribute] pub fn app(args: TokenStream, input: TokenStream) -> TokenStream { let mut settings = Settings::default(); @@ -61,7 +64,10 @@ pub fn app(args: TokenStream, input: TokenStream) -> TokenStream { #[cfg(feature = "debugprint")] println!("OUT_DIR\n{:#?}", out_str); - if !out_dir.exists() { + if out_dir.exists() { + #[cfg(feature = "debugprint")] + println!("\ntarget/ exists\n"); + } else { // Set out_dir to OUT_DIR out_dir = Path::new(&out_str); @@ -80,24 +86,19 @@ pub fn app(args: TokenStream, input: TokenStream) -> TokenStream { .to_str() .unwrap() .starts_with(target_triple_prefix) - //|| path.ends_with(&out_dir_root) { if let Some(out) = path.parent() { out_dir = out; #[cfg(feature = "debugprint")] println!("{:#?}\n", out_dir); break; - } else { - // If no parent, just use it - out_dir = path; - break; } + // If no parent, just use it + out_dir = path; + break; } } } - } else { - #[cfg(feature = "debugprint")] - println!("\ntarget/ exists\n"); } // Try to write the expanded code to disk diff --git a/src/export.rs b/src/export.rs index a124c78b3c..838ae8435e 100644 --- a/src/export.rs +++ b/src/export.rs @@ -1,3 +1,4 @@ +#![allow(clippy::inline_always)] use core::{ cell::Cell, sync::atomic::{AtomicBool, Ordering}, @@ -61,7 +62,7 @@ impl Barrier { } pub fn release(&self) { - self.inner.store(true, Ordering::Release) + self.inner.store(true, Ordering::Release); } pub fn wait(&self) { @@ -91,7 +92,7 @@ impl Priority { // These two methods are used by `lock` (see below) but can't be used from the RTIC application #[inline(always)] fn set(&self, value: u8) { - self.inner.set(value) + self.inner.set(value); } /// Get the current priority @@ -160,7 +161,7 @@ pub unsafe fn lock( } /// Lock the resource proxy by setting the PRIMASK -/// and running the closure with interrupt::free +/// and running the closure with ``interrupt::free`` /// /// # Safety /// @@ -188,6 +189,7 @@ pub unsafe fn lock( } #[inline] +#[must_use] pub fn logical2hw(logical: u8, nvic_prio_bits: u8) -> u8 { ((1 << nvic_prio_bits) - logical) << (8 - nvic_prio_bits) } diff --git a/src/lib.rs b/src/lib.rs index 871141f62c..0c0d0cc7dc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -36,6 +36,7 @@ html_favicon_url = "https://raw.githubusercontent.com/rtic-rs/cortex-m-rtic/master/book/en/src/RTIC.svg" )] //deny_warnings_placeholder_for_ci +#![allow(clippy::inline_always)] use cortex_m::{interrupt::InterruptNumber, peripheral::NVIC}; pub use cortex_m_rtic_macros::app; @@ -61,7 +62,7 @@ pub fn pend(interrupt: I) where I: InterruptNumber, { - NVIC::pend(interrupt) + NVIC::pend(interrupt); } use core::cell::UnsafeCell; @@ -72,12 +73,12 @@ use core::cell::UnsafeCell; /// /// Soundness: /// 1) Unsafe API for internal use only -/// 2) get_mut(&self) -> *mut T +/// 2) ``get_mut(&self) -> *mut T`` /// returns a raw mutable pointer to the inner T /// casting to &mut T is under control of RTIC /// RTIC ensures &mut T to be unique under Rust aliasing rules. /// -/// Implementation uses the underlying UnsafeCell +/// Implementation uses the underlying ``UnsafeCell`` /// self.0.get() -> *mut T /// /// 3) get(&self) -> *const T @@ -85,14 +86,14 @@ use core::cell::UnsafeCell; /// casting to &T is under control of RTIC /// RTIC ensures &T to be shared under Rust aliasing rules. /// -/// Implementation uses the underlying UnsafeCell +/// Implementation uses the underlying ``UnsafeCell`` /// self.0.get() -> *mut T, demoted to *const T -/// +/// #[repr(transparent)] pub struct RacyCell(UnsafeCell); impl RacyCell { - /// Create a RacyCell + /// Create a ``RacyCell`` #[inline(always)] pub const fn new(value: T) -> Self { RacyCell(UnsafeCell::new(value)) diff --git a/src/tq.rs b/src/tq.rs index 9033022bcd..0f585ba4dd 100644 --- a/src/tq.rs +++ b/src/tq.rs @@ -17,7 +17,7 @@ where /// # Safety /// /// Writing to memory with a transmute in order to enable - /// interrupts of the SysTick timer + /// interrupts of the ``SysTick`` timer /// /// Enqueue a task without checking if it is full #[inline] @@ -33,11 +33,8 @@ where { // Check if the top contains a non-empty element and if that element is // greater than nr - let if_heap_max_greater_than_nr = self - .0 - .peek() - .map(|head| nr.instant < head.instant) - .unwrap_or(true); + let if_heap_max_greater_than_nr = + self.0.peek().map_or(true, |head| nr.instant < head.instant); if if_heap_max_greater_than_nr { if Mono::DISABLE_INTERRUPT_ON_EMPTY_QUEUE && self.0.is_empty() { @@ -92,7 +89,7 @@ where } } - /// Dequeue a task from the TimerQueue + /// Dequeue a task from the ``TimerQueue`` pub fn dequeue(&mut self, disable_interrupt: F, mono: &mut Mono) -> Option<(Task, u8)> where F: FnOnce(), From 71d953f0e55a4ac5b7255af03048b49cd4370386 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Tj=C3=A4der?= Date: Tue, 22 Feb 2022 19:34:54 +0100 Subject: [PATCH 137/423] Add CHANGELOG instructions and fix incorrectly placed item --- CHANGELOG.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 94b745b387..b5e206630d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,8 @@ All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/). +For each category, *Added*, *Changed*, *Fixed* add new entries at the top! + ## [Unreleased] ### Added @@ -27,11 +29,14 @@ This project adheres to [Semantic Versioning](http://semver.org/). - Force mdBook to return error codes - Readded missing ramfunc output to book +### Changed + +- Try to detect `target-dir` for rtic-expansion.rs + ## [v1.0.0] - 2021-12-25 ### Changed -- Try to detect `target-dir` for rtic-expansion.rs - Bump RTIC dependencies also updated to v1.0.0 - Edition 2021 - Change default `idle` behaviour to be `NOP` instead of `WFI` From f86dab5ff3e9c35e9e68b798b5c4faa08d390085 Mon Sep 17 00:00:00 2001 From: Per Lindgren Date: Fri, 21 Jan 2022 21:49:45 +0100 Subject: [PATCH 138/423] Added support for SRP based scheduling for armv6m --- CHANGELOG.md | 1 + ci/expected/complex.run | 47 ++++++++ examples/complex.rs | 132 ++++++++++++++++++++++ macros/src/codegen.rs | 2 +- macros/src/codegen/assertions.rs | 32 +++++- macros/src/codegen/shared_resources.rs | 33 ++++++ macros/src/codegen/util.rs | 1 + src/export.rs | 148 ++++++++++++++++++++++--- ui/v6m-interrupt-not-enough.rs_no | 54 +++++++++ 9 files changed, 434 insertions(+), 16 deletions(-) create mode 100644 ci/expected/complex.run create mode 100644 examples/complex.rs create mode 100644 ui/v6m-interrupt-not-enough.rs_no diff --git a/CHANGELOG.md b/CHANGELOG.md index 094275735b..f05aeeaf71 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ For each category, *Added*, *Changed*, *Fixed* add new entries at the top! - Rework branch structure, release/vVERSION - Cargo clippy in CI - Use rust-cache Github Action +- Support for NVIC based SPR based scheduling for armv6m. - CI changelog entry enforcer - `examples/periodic-at.rs`, an example of a periodic timer without accumulated drift. - `examples/periodic-at2.rs`, an example of a periodic process with two tasks, with offset timing. diff --git a/ci/expected/complex.run b/ci/expected/complex.run new file mode 100644 index 0000000000..5df884dabd --- /dev/null +++ b/ci/expected/complex.run @@ -0,0 +1,47 @@ +init +idle p0 started +t2 p4 called 1 time +enter lock s4 0 +t3 p4 exit +idle enter lock s3 0 +idle pend t0 +idle pend t1 +idle pend t2 +t2 p4 called 2 times +enter lock s4 1 +t3 p4 exit +idle still in lock s3 0 +t1 p3 called 1 time +t1 enter lock s4 2 +t1 pend t0 +t1 pend t2 +t1 still in lock s4 2 +t2 p4 called 3 times +enter lock s4 2 +t3 p4 exit +t1 p3 exit +t0 p2 called 1 time +t0 p2 exit + +back in idle +enter lock s2 0 +idle pend t0 +idle pend t1 +t1 p3 called 2 times +t1 enter lock s4 3 +t1 pend t0 +t1 pend t2 +t1 still in lock s4 3 +t2 p4 called 4 times +enter lock s4 3 +t3 p4 exit +t1 p3 exit +idle pend t2 +t2 p4 called 5 times +enter lock s4 4 +t3 p4 exit +idle still in lock s2 0 +t0 p2 called 2 times +t0 p2 exit + +idle exit diff --git a/examples/complex.rs b/examples/complex.rs new file mode 100644 index 0000000000..e5cf6dbea3 --- /dev/null +++ b/examples/complex.rs @@ -0,0 +1,132 @@ +//! examples/complex.rs + +#![deny(unsafe_code)] +#![deny(warnings)] +#![no_main] +#![no_std] + +use panic_semihosting as _; + +#[rtic::app(device = lm3s6965)] +mod app { + + use cortex_m_semihosting::{debug, hprintln}; + use lm3s6965::Interrupt; + + #[shared] + struct Shared { + s2: u32, // shared with ceiling 2 + s3: u32, // shared with ceiling 3 + s4: u32, // shared with ceiling 4 + } + + #[local] + struct Local {} + + #[init] + fn init(_: init::Context) -> (Shared, Local, init::Monotonics) { + hprintln!("init").unwrap(); + + ( + Shared { + s2: 0, + s3: 0, + s4: 0, + }, + Local {}, + init::Monotonics(), + ) + } + + #[idle(shared = [s2, s3])] + fn idle(mut cx: idle::Context) -> ! { + hprintln!("idle p0 started").ok(); + rtic::pend(Interrupt::GPIOC); + cx.shared.s3.lock(|s| { + hprintln!("idle enter lock s3 {}", s).ok(); + hprintln!("idle pend t0").ok(); + rtic::pend(Interrupt::GPIOA); // t0 p2, with shared ceiling 3 + hprintln!("idle pend t1").ok(); + rtic::pend(Interrupt::GPIOB); // t1 p3, with shared ceiling 3 + hprintln!("idle pend t2").ok(); + rtic::pend(Interrupt::GPIOC); // t2 p4, no sharing + hprintln!("idle still in lock s3 {}", s).ok(); + }); + hprintln!("\nback in idle").ok(); + + cx.shared.s2.lock(|s| { + hprintln!("enter lock s2 {}", s).ok(); + hprintln!("idle pend t0").ok(); + rtic::pend(Interrupt::GPIOA); // t0 p2, with shared ceiling 2 + hprintln!("idle pend t1").ok(); + rtic::pend(Interrupt::GPIOB); // t1 p3, no sharing + hprintln!("idle pend t2").ok(); + rtic::pend(Interrupt::GPIOC); // t2 p4, no sharing + hprintln!("idle still in lock s2 {}", s).ok(); + }); + hprintln!("\nidle exit").ok(); + + debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator + + loop { + cortex_m::asm::nop(); + } + } + + #[task(binds = GPIOA, priority = 2, local = [times: u32 = 0], shared = [s2, s3])] + fn t0(cx: t0::Context) { + // Safe access to local `static mut` variable + *cx.local.times += 1; + + hprintln!( + "t0 p2 called {} time{}", + *cx.local.times, + if *cx.local.times > 1 { "s" } else { "" } + ) + .ok(); + hprintln!("t0 p2 exit").ok(); + } + + #[task(binds = GPIOB, priority = 3, local = [times: u32 = 0], shared = [s3, s4])] + fn t1(mut cx: t1::Context) { + // Safe access to local `static mut` variable + *cx.local.times += 1; + + hprintln!( + "t1 p3 called {} time{}", + *cx.local.times, + if *cx.local.times > 1 { "s" } else { "" } + ) + .ok(); + + cx.shared.s4.lock(|s| { + hprintln!("t1 enter lock s4 {}", s).ok(); + hprintln!("t1 pend t0").ok(); + rtic::pend(Interrupt::GPIOA); // t0 p2, with shared ceiling 2 + hprintln!("t1 pend t2").ok(); + rtic::pend(Interrupt::GPIOC); // t2 p4, no sharing + hprintln!("t1 still in lock s4 {}", s).ok(); + }); + + hprintln!("t1 p3 exit").ok(); + } + + #[task(binds = GPIOC, priority = 4, local = [times: u32 = 0], shared = [s4])] + fn t2(mut cx: t2::Context) { + // Safe access to local `static mut` variable + *cx.local.times += 1; + + hprintln!( + "t2 p4 called {} time{}", + *cx.local.times, + if *cx.local.times > 1 { "s" } else { "" } + ) + .unwrap(); + + cx.shared.s4.lock(|s| { + hprintln!("enter lock s4 {}", s).ok(); + *s += 1; + }); + hprintln!("t3 p4 exit").ok(); + } +} diff --git a/macros/src/codegen.rs b/macros/src/codegen.rs index f5cae34a72..01be1d5787 100644 --- a/macros/src/codegen.rs +++ b/macros/src/codegen.rs @@ -28,7 +28,7 @@ pub fn app(app: &App, analysis: &Analysis, extra: &Extra) -> TokenStream2 { let mut user = vec![]; // Generate the `main` function - let assertion_stmts = assertions::codegen(app, analysis); + let assertion_stmts = assertions::codegen(app, analysis, extra); let pre_init_stmts = pre_init::codegen(app, analysis, extra); diff --git a/macros/src/codegen/assertions.rs b/macros/src/codegen/assertions.rs index a8a4491bdf..36ab036445 100644 --- a/macros/src/codegen/assertions.rs +++ b/macros/src/codegen/assertions.rs @@ -1,11 +1,11 @@ use proc_macro2::TokenStream as TokenStream2; use quote::quote; -use crate::analyze::Analysis; +use crate::{analyze::Analysis, check::Extra, codegen::util}; use rtic_syntax::ast::App; /// Generates compile-time assertions that check that types implement the `Send` / `Sync` traits -pub fn codegen(app: &App, analysis: &Analysis) -> Vec { +pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec { let mut stmts = vec![]; for ty in &analysis.send_types { @@ -21,5 +21,33 @@ pub fn codegen(app: &App, analysis: &Analysis) -> Vec { stmts.push(quote!(rtic::export::assert_monotonic::<#ty>();)); } + let device = &extra.device; + let arm_v6_checks: Vec<_> = app + .hardware_tasks + .iter() + .filter_map(|(_, task)| { + if !util::is_exception(&task.args.binds) { + let interrupt_name = &task.args.binds; + Some(quote!(assert!((#device::Interrupt::#interrupt_name as u32) < 32);)) + } else { + None + } + }) + .collect(); + + let const_check = quote! { + const _CONST_CHECK: () = { + if rtic::export::is_armv6() { + #(#arm_v6_checks)* + } else { + // TODO: Add armv7 checks here + } + }; + + let _ = _CONST_CHECK; + }; + + stmts.push(const_check); + stmts } diff --git a/macros/src/codegen/shared_resources.rs b/macros/src/codegen/shared_resources.rs index 9e45cff973..a016e4538d 100644 --- a/macros/src/codegen/shared_resources.rs +++ b/macros/src/codegen/shared_resources.rs @@ -105,5 +105,38 @@ pub fn codegen( }) }; + // Computing mapping of used interrupts to masks + let interrupt_ids = analysis.interrupts.iter().map(|(p, (id, _))| (p, id)); + + use std::collections::HashMap; + let mut masks: HashMap = std::collections::HashMap::new(); + let device = &extra.device; + + for p in 0..3 { + masks.insert(p, quote!(0)); + } + + for (&priority, name) in interrupt_ids.chain(app.hardware_tasks.values().flat_map(|task| { + if !util::is_exception(&task.args.binds) { + Some((&task.args.priority, &task.args.binds)) + } else { + // TODO: exceptions not implemented + None + } + })) { + let name = quote!(#device::Interrupt::#name as u32); + if let Some(v) = masks.get_mut(&(priority - 1)) { + *v = quote!(#v | 1 << #name); + }; + } + + let mut mask_arr: Vec<(_, _)> = masks.iter().collect(); + mask_arr.sort_by_key(|(k, _v)| *k); + let mask_arr: Vec<_> = mask_arr.iter().map(|(_, v)| v).collect(); + + mod_app.push(quote!( + const MASKS: [u32; 3] = [#(#mask_arr),*]; + )); + (mod_app, mod_resources) } diff --git a/macros/src/codegen/util.rs b/macros/src/codegen/util.rs index 6a07732c34..4a29754bc8 100644 --- a/macros/src/codegen/util.rs +++ b/macros/src/codegen/util.rs @@ -52,6 +52,7 @@ pub fn impl_mutex( #priority, CEILING, #device::NVIC_PRIO_BITS, + &MASKS, f, ) } diff --git a/src/export.rs b/src/export.rs index 838ae8435e..ed51a9e9f7 100644 --- a/src/export.rs +++ b/src/export.rs @@ -102,6 +102,19 @@ impl Priority { } } +/// Const helper to check architecture +pub const fn is_armv6() -> bool { + #[cfg(not(armv6m))] + { + false + } + + #[cfg(armv6m)] + { + true + } +} + #[inline(always)] pub fn assert_send() where @@ -123,13 +136,40 @@ where { } -/// Lock the resource proxy by setting the BASEPRI -/// and running the closure with interrupt::free +/// Lock implementation using BASEPRI and global Critical Section (CS) /// /// # Safety /// -/// Writing to the BASEPRI -/// Dereferencing a raw pointer +/// The system ceiling is raised from current to ceiling +/// by either +/// - raising the BASEPRI to the ceiling value, or +/// - disable all interrupts in case we want to +/// mask interrupts with maximum priority +/// +/// Dereferencing a raw pointer inside CS +/// +/// The priority.set/priority.get can safely be outside the CS +/// as being a context local cell (not affected by preemptions). +/// It is merely used in order to omit masking in case current +/// priority is current priority >= ceiling. +/// +/// Lock Efficiency: +/// Experiments validate (sub)-zero cost for CS implementation +/// (Sub)-zero as: +/// - Either zero OH (lock optimized out), or +/// - Amounting to an optimal assembly implementation +/// - The BASEPRI value is folded to a constant at compile time +/// - CS entry, single assembly instruction to write BASEPRI +/// - CS exit, single assembly instruction to write BASEPRI +/// - priority.set/get optimized out (their effect not) +/// - On par or better than any handwritten implementation of SRP +/// +/// Limitations: +/// The current implementation reads/writes BASEPRI once +/// even in some edge cases where this may be omitted. +/// Total OH of per task is max 2 clock cycles, negligible in practice +/// but can in theory be fixed. +/// #[cfg(armv7m)] #[inline(always)] pub unsafe fn lock( @@ -137,6 +177,7 @@ pub unsafe fn lock( priority: &Priority, ceiling: u8, nvic_prio_bits: u8, + _mask: &[u32; 3], f: impl FnOnce(&mut T) -> R, ) -> R { let current = priority.get(); @@ -160,13 +201,50 @@ pub unsafe fn lock( } } -/// Lock the resource proxy by setting the PRIMASK -/// and running the closure with ``interrupt::free`` +/// Lock implementation using interrupt masking /// /// # Safety /// -/// Writing to the PRIMASK -/// Dereferencing a raw pointer +/// The system ceiling is raised from current to ceiling +/// by computing a 32 bit `mask` (1 bit per interrupt) +/// 1: ceiling >= priority > current +/// 0: else +/// +/// On CS entry, `clear_enable_mask(mask)` disables interrupts +/// On CS exit, `set_enable_mask(mask)` re-enables interrupts +/// +/// The priority.set/priority.get can safely be outside the CS +/// as being a context local cell (not affected by preemptions). +/// It is merely used in order to omit masking in case +/// current priority >= ceiling. +/// +/// Dereferencing a raw pointer is done safely inside the CS +/// +/// Lock Efficiency: +/// Early experiments validate (sub)-zero cost for CS implementation +/// (Sub)-zero as: +/// - Either zero OH (lock optimized out), or +/// - Amounting to an optimal assembly implementation +/// - if ceiling == (1 << nvic_prio_bits) +/// - we execute the closure in a global critical section (interrupt free) +/// - CS entry cost, single write to core register +/// - CS exit cost, single write to core register +/// else +/// - The `mask` value is folded to a constant at compile time +/// - CS entry, single write of the 32 bit `mask` to the `icer` register +/// - CS exit, single write of the 32 bit `mask` to the `iser` register +/// - priority.set/get optimized out (their effect not) +/// - On par or better than any hand written implementation of SRP +/// +/// Limitations: +/// Current implementation does not allow for tasks with shared resources +/// to be bound to exception handlers, as these cannot be masked in HW. +/// +/// Possible solutions: +/// - Mask exceptions by global critical sections (interrupt::free) +/// - Temporary lower exception priority +/// +/// These possible solutions are set goals for future work #[cfg(not(armv7m))] #[inline(always)] pub unsafe fn lock( @@ -174,20 +252,64 @@ pub unsafe fn lock( priority: &Priority, ceiling: u8, _nvic_prio_bits: u8, + masks: &[u32; 3], f: impl FnOnce(&mut T) -> R, ) -> R { let current = priority.get(); - if current < ceiling { - priority.set(u8::max_value()); - let r = interrupt::free(|_| f(&mut *ptr)); - priority.set(current); - r + if ceiling >= 4 { + // safe to manipulate outside critical section + priority.set(ceiling); + // execute closure under protection of raised system ceiling + let r = interrupt::free(|_| f(&mut *ptr)); + // safe to manipulate outside critical section + priority.set(current); + r + } else { + // safe to manipulate outside critical section + priority.set(ceiling); + let mask = compute_mask(current, ceiling, masks); + clear_enable_mask(mask); + + // execute closure under protection of raised system ceiling + let r = f(&mut *ptr); + + set_enable_mask(mask); + + // safe to manipulate outside critical section + priority.set(current); + r + } } else { + // execute closure without raising system ceiling f(&mut *ptr) } } +#[cfg(not(armv7m))] +#[inline(always)] +fn compute_mask(from_prio: u8, to_prio: u8, masks: &[u32; 3]) -> u32 { + let mut res = 0; + masks[from_prio as usize..to_prio as usize] + .iter() + .for_each(|m| res |= m); + res +} + +// enables interrupts +#[cfg(not(armv7m))] +#[inline(always)] +unsafe fn set_enable_mask(mask: u32) { + (*NVIC::ptr()).iser[0].write(mask) +} + +// disables interrupts +#[cfg(not(armv7m))] +#[inline(always)] +unsafe fn clear_enable_mask(mask: u32) { + (*NVIC::ptr()).icer[0].write(mask) +} + #[inline] #[must_use] pub fn logical2hw(logical: u8, nvic_prio_bits: u8) -> u8 { diff --git a/ui/v6m-interrupt-not-enough.rs_no b/ui/v6m-interrupt-not-enough.rs_no new file mode 100644 index 0000000000..3fbf3cf7bf --- /dev/null +++ b/ui/v6m-interrupt-not-enough.rs_no @@ -0,0 +1,54 @@ +//! v6m-interrupt-not-enough.rs_no (not run atm) +//! +//! Expected behavior: +//! should pass +//! > cargo build --example m0_perf_err --target thumbv7m-none-eabi --release +//! +//! should fail +//! > cargo build --example m0_perf_err --target thumbv6m-none-eabi --release +//! Compiling cortex-m-rtic v1.0.0 (/home/pln/rust/rtic/cortex-m-rtic) +//! error[E0308]: mismatched types +//! --> examples/m0_perf_err.rs:25:1 +//! | +//! 25 | #[rtic::app(device = lm3s6965)] +//! | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected an array with a fixed size of 4 elements, found one with 5 elements +//! | +//! = note: this error originates in the attribute macro `rtic::app` (in Nightly builds, run with -Z macro-backtrace for more info) + +#![deny(unsafe_code)] +#![deny(warnings)] +#![no_main] +#![no_std] + +use panic_semihosting as _; + +#[rtic::app(device = lm3s6965)] +mod app { + + use cortex_m_semihosting::debug; + + #[shared] + struct Shared {} + + #[local] + struct Local {} + + #[init] + fn init(_: init::Context) -> (Shared, Local, init::Monotonics) { + (Shared {}, Local {}, init::Monotonics()) + } + + #[inline(never)] + #[idle] + fn idle(_cx: idle::Context) -> ! { + debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator + + loop { + cortex_m::asm::nop(); + } + } + + // priority to high for v6m + #[task(binds = GPIOA, priority = 5)] + fn t0(_cx: t0::Context) {} +} From 750b1853ada48d81048d62ece6905b2198703f6e Mon Sep 17 00:00:00 2001 From: Riccardo Casatta Date: Mon, 7 Mar 2022 21:05:34 +0100 Subject: [PATCH 139/423] Update software_tasks.md --- book/en/src/by-example/software_tasks.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/book/en/src/by-example/software_tasks.md b/book/en/src/by-example/software_tasks.md index ea40697d27..5c03f9140b 100644 --- a/book/en/src/by-example/software_tasks.md +++ b/book/en/src/by-example/software_tasks.md @@ -1,6 +1,6 @@ # Software tasks & spawn -The RTIC concept of a software task shares a lot with that of [hardware tasks][hardware_tasks.md] +The RTIC concept of a software task shares a lot with that of [hardware tasks](./hardware_tasks.md) with the core difference that a software task is not explicitly bound to a specific interrupt vector, but rather a “dispatcher” interrupt vector running at the same priority as the software task. From a7a8f9f4eddca9476a880de6703390f28be6278b Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Sun, 20 Mar 2022 12:40:30 +0100 Subject: [PATCH 140/423] Fix error in book, shared resource need only `Send` --- book/en/src/by-example/resources.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/book/en/src/by-example/resources.md b/book/en/src/by-example/resources.md index 9d51d6c97a..6349b520b5 100644 --- a/book/en/src/by-example/resources.md +++ b/book/en/src/by-example/resources.md @@ -105,7 +105,7 @@ $ cargo run --target thumbv7m-none-eabi --example lock {{#include ../../../../ci/expected/lock.run}} ``` -Types of `#[shared]` resources have to be both [`Send`] and [`Sync`]. +Types of `#[shared]` resources have to be [`Send`]. ## Multi-lock From 4f99399e29cd103f96671f3b2455dbaae5b7b7d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Tj=C3=A4der?= Date: Wed, 13 Apr 2022 08:27:17 +0200 Subject: [PATCH 141/423] Release RTIC v1.1 Bump versions, including using using latest rtic-syntax --- CHANGELOG.md | 11 ++++++++++- Cargo.toml | 2 +- macros/Cargo.toml | 4 ++-- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f05aeeaf71..9797c82df1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,14 @@ For each category, *Added*, *Changed*, *Fixed* add new entries at the top! ### Added +### Fixed + +### Changed + +## [v1.1.0] - 2022-04-13 + +### Added + - Improve how CHANGELOG.md merges are handled - If current $stable and master version matches, dev-book redirects to $stable book - During deploy stage, merge master branch into current stable IFF cargo package version matches @@ -485,7 +493,8 @@ Yanked due to a soundness issue in `init`; the issue has been mostly fixed in v0 - Initial release -[Unreleased]: https://github.com/rtic-rs/cortex-m-rtic/compare/v1.0.0...HEAD +[Unreleased]: https://github.com/rtic-rs/cortex-m-rtic/compare/v1.1.0...HEAD +[v1.1.0]: https://github.com/rtic-rs/cortex-m-rtic/compare/v1.0.0...v1.1.0 [v1.0.0]: https://github.com/rtic-rs/cortex-m-rtic/compare/v0.6.0-rc.4...v1.0.0 [v0.6.0-rc.4]: https://github.com/rtic-rs/cortex-m-rtic/compare/v0.6.0-rc.3...v0.6.0-rc.4 [v0.6.0-rc.3]: https://github.com/rtic-rs/cortex-m-rtic/compare/v0.6.0-rc.2...v0.6.0-rc.3 diff --git a/Cargo.toml b/Cargo.toml index 4393533b30..da4372b680 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,7 +14,7 @@ name = "cortex-m-rtic" readme = "README.md" repository = "https://github.com/rtic-rs/cortex-m-rtic" -version = "1.0.0" +version = "1.1.0" [lib] name = "rtic" diff --git a/macros/Cargo.toml b/macros/Cargo.toml index 0c008c88a6..350db900b5 100644 --- a/macros/Cargo.toml +++ b/macros/Cargo.toml @@ -12,7 +12,7 @@ license = "MIT OR Apache-2.0" name = "cortex-m-rtic-macros" readme = "../README.md" repository = "https://github.com/rtic-rs/cortex-m-rtic" -version = "1.0.0" +version = "1.1.0" [lib] proc-macro = true @@ -22,7 +22,7 @@ proc-macro2 = "1" proc-macro-error = "1" quote = "1" syn = "1" -rtic-syntax = "1.0.0" +rtic-syntax = "1.0.1" [features] debugprint = [] From f247cc0b3a3b0ad8f946c13afa4429217fb09196 Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Wed, 13 Apr 2022 19:27:38 +0200 Subject: [PATCH 142/423] Fixed `macro` version --- CHANGELOG.md | 4 ++++ Cargo.toml | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9797c82df1..bd18d12269 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,10 +7,14 @@ For each category, *Added*, *Changed*, *Fixed* add new entries at the top! ## [Unreleased] +## [v1.1.1] - 2022-04-13 + ### Added ### Fixed +- Fixed `marcro` version + ### Changed ## [v1.1.0] - 2022-04-13 diff --git a/Cargo.toml b/Cargo.toml index da4372b680..605182ae47 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,14 +14,14 @@ name = "cortex-m-rtic" readme = "README.md" repository = "https://github.com/rtic-rs/cortex-m-rtic" -version = "1.1.0" +version = "1.1.1" [lib] name = "rtic" [dependencies] cortex-m = "0.7.0" -cortex-m-rtic-macros = { path = "macros", version = "1.0.0" } +cortex-m-rtic-macros = { path = "macros", version = "1.1.0" } rtic-monotonic = "1.0.0" rtic-core = "1.0.0" heapless = "0.7.7" From 9f38a39377a7ff03d0f4371feda083ba09064f5e Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Wed, 20 Apr 2022 10:46:03 +0200 Subject: [PATCH 143/423] Masks take 3 --- CHANGELOG.md | 8 ++++++ macros/src/codegen/shared_resources.rs | 40 +++++++++++++++----------- macros/src/codegen/util.rs | 7 ++++- src/export.rs | 24 ++++++++++++++++ 4 files changed, 62 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bd18d12269..b9bd7f5339 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,14 @@ For each category, *Added*, *Changed*, *Fixed* add new entries at the top! ## [Unreleased] +### Added + +### Fixed + +- Generation of masks for the source masking scheduling for thumbv6 + +### Changed + ## [v1.1.1] - 2022-04-13 ### Added diff --git a/macros/src/codegen/shared_resources.rs b/macros/src/codegen/shared_resources.rs index a016e4538d..e3109c8285 100644 --- a/macros/src/codegen/shared_resources.rs +++ b/macros/src/codegen/shared_resources.rs @@ -1,8 +1,8 @@ +use crate::{analyze::Analysis, check::Extra, codegen::util}; use proc_macro2::TokenStream as TokenStream2; use quote::quote; use rtic_syntax::{analyze::Ownership, ast::App}; - -use crate::{analyze::Analysis, check::Extra, codegen::util}; +use std::collections::HashMap; /// Generates `static` variables and shared resource proxies pub fn codegen( @@ -108,14 +108,9 @@ pub fn codegen( // Computing mapping of used interrupts to masks let interrupt_ids = analysis.interrupts.iter().map(|(p, (id, _))| (p, id)); - use std::collections::HashMap; - let mut masks: HashMap = std::collections::HashMap::new(); + let mut prio_to_masks = HashMap::new(); let device = &extra.device; - for p in 0..3 { - masks.insert(p, quote!(0)); - } - for (&priority, name) in interrupt_ids.chain(app.hardware_tasks.values().flat_map(|task| { if !util::is_exception(&task.args.binds) { Some((&task.args.priority, &task.args.binds)) @@ -124,18 +119,31 @@ pub fn codegen( None } })) { - let name = quote!(#device::Interrupt::#name as u32); - if let Some(v) = masks.get_mut(&(priority - 1)) { - *v = quote!(#v | 1 << #name); - }; + let v = prio_to_masks.entry(priority - 1).or_insert(Vec::new()); + v.push(quote!(#device::Interrupt::#name as u32)); } - let mut mask_arr: Vec<(_, _)> = masks.iter().collect(); - mask_arr.sort_by_key(|(k, _v)| *k); - let mask_arr: Vec<_> = mask_arr.iter().map(|(_, v)| v).collect(); + // Call rtic::export::create_mask([u32; N]), where the array is the list of shifts + let mut mask_arr = Vec::new(); + // NOTE: 0..3 assumes max 4 priority levels according to M0 spec + for i in 0..3 { + let v = if let Some(v) = prio_to_masks.get(&i) { + v.clone() + } else { + Vec::new() + }; + + mask_arr.push(quote!( + rtic::export::create_mask([#(#v),*]) + )); + } + + let masks_name = util::priority_masks_ident(); mod_app.push(quote!( - const MASKS: [u32; 3] = [#(#mask_arr),*]; + #[doc(hidden)] + #[allow(non_camel_case_types)] + const #masks_name: [u32; 3] = [#(#mask_arr),*]; )); (mod_app, mod_resources) diff --git a/macros/src/codegen/util.rs b/macros/src/codegen/util.rs index 4a29754bc8..0f3dca7c47 100644 --- a/macros/src/codegen/util.rs +++ b/macros/src/codegen/util.rs @@ -36,6 +36,7 @@ pub fn impl_mutex( }; let device = &extra.device; + let masks_name = priority_masks_ident(); quote!( #(#cfgs)* impl<'a> rtic::Mutex for #path<'a> { @@ -52,7 +53,7 @@ pub fn impl_mutex( #priority, CEILING, #device::NVIC_PRIO_BITS, - &MASKS, + &#masks_name, f, ) } @@ -252,6 +253,10 @@ pub fn static_shared_resource_ident(name: &Ident) -> Ident { mark_internal_name(&format!("shared_resource_{}", name)) } +pub fn priority_masks_ident() -> Ident { + mark_internal_name("MASKS") +} + pub fn static_local_resource_ident(name: &Ident) -> Ident { mark_internal_name(&format!("local_resource_{}", name)) } diff --git a/src/export.rs b/src/export.rs index ed51a9e9f7..035899180a 100644 --- a/src/export.rs +++ b/src/export.rs @@ -315,3 +315,27 @@ unsafe fn clear_enable_mask(mask: u32) { pub fn logical2hw(logical: u8, nvic_prio_bits: u8) -> u8 { ((1 << nvic_prio_bits) - logical) << (8 - nvic_prio_bits) } + +#[cfg(not(armv6m))] +pub const fn create_mask(_: [u32; N]) -> u32 { + 0 +} + +#[cfg(armv6m)] +pub const fn create_mask(list_of_shifts: [u32; N]) -> u32 { + let mut mask = 0; + let mut i = 0; + + while i < N { + let shift = list_of_shifts[i]; + i += 1; + + if shift > 31 { + panic!("Generating masks for thumbv6 failed! Are you compiling for thumbv6 on an thumbv7 MCU?"); + } + + mask |= 1 << shift; + } + + mask +} From 0f8bdbdd3f2739e37d788493cb83cf2d9c557f4e Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Wed, 20 Apr 2022 13:02:55 +0200 Subject: [PATCH 144/423] Added check for resource usage and to generate an compile error for thumbv6 exceptions --- macros/src/codegen/shared_resources.rs | 30 +++++++++++++++++++++++++- src/export.rs | 10 +++++++++ 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/macros/src/codegen/shared_resources.rs b/macros/src/codegen/shared_resources.rs index e3109c8285..f944e3e5ec 100644 --- a/macros/src/codegen/shared_resources.rs +++ b/macros/src/codegen/shared_resources.rs @@ -110,12 +110,32 @@ pub fn codegen( let mut prio_to_masks = HashMap::new(); let device = &extra.device; + let mut uses_exceptions_with_resources = false; for (&priority, name) in interrupt_ids.chain(app.hardware_tasks.values().flat_map(|task| { if !util::is_exception(&task.args.binds) { Some((&task.args.priority, &task.args.binds)) } else { - // TODO: exceptions not implemented + // If any resource to the exception uses non-lock-free or non-local resources this is + // not allwed on thumbv6. + uses_exceptions_with_resources = uses_exceptions_with_resources + || task + .args + .shared_resources + .iter() + .map(|(ident, access)| { + if access.is_exclusive() { + if let Some(r) = app.shared_resources.get(ident) { + !r.properties.lock_free + } else { + false + } + } else { + false + } + }) + .any(|v| v); + None } })) { @@ -146,5 +166,13 @@ pub fn codegen( const #masks_name: [u32; 3] = [#(#mask_arr),*]; )); + if uses_exceptions_with_resources { + mod_app.push(quote!( + #[doc(hidden)] + #[allow(non_camel_case_types)] + const __rtic_internal_V6_ERROR: () = rtic::export::v6_panic(); + )); + } + (mod_app, mod_resources) } diff --git a/src/export.rs b/src/export.rs index 035899180a..42f3fe2e34 100644 --- a/src/export.rs +++ b/src/export.rs @@ -339,3 +339,13 @@ pub const fn create_mask(list_of_shifts: [u32; N]) -> u32 { mask } + +#[cfg(not(armv6m))] +pub const fn v6_panic() { + // For non-v6 all is fine +} + +#[cfg(armv6m)] +pub const fn v6_panic() { + panic!("Exceptions with shared resources are not allowed when compiling for thumbv6. Use local resources or `#[lock_free]` shared resources"); +} From 2e5e7698b6facc1a2de1d88ed4c2c8b300178da3 Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Mon, 2 May 2022 08:32:57 +0200 Subject: [PATCH 145/423] Added matrix bot --- .github/workflows/matrix-bot.yml | 44 ++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 .github/workflows/matrix-bot.yml diff --git a/.github/workflows/matrix-bot.yml b/.github/workflows/matrix-bot.yml new file mode 100644 index 0000000000..6ef6787e5d --- /dev/null +++ b/.github/workflows/matrix-bot.yml @@ -0,0 +1,44 @@ +name: Matrix bot +on: + pull_request_target: + types: [opened, closed] + +jobs: + new-pr: + if: github.event.action == 'opened' && github.repository == 'rtic-rs/cortex-m-rtic' + runs-on: ubuntu-latest + continue-on-error: true + steps: + - name: send message + uses: s3krit/matrix-message-action@v0.0.3 + with: + room_id: ${{ secrets.MATRIX_ROOM_ID }} + access_token: ${{ secrets.MATRIX_ACCESS_TOKEN }} + message: "New PR: [${{ github.event.pull_request.title }}](${{ github.event.pull_request.html_url }})" + server: "matrix.org" + + merged-pr: + if: github.event.action == 'closed' && github.event.pull_request.merged == true && github.repository == 'rtic-rs/cortex-m-rtic' + runs-on: ubuntu-latest + continue-on-error: true + steps: + - name: send message + uses: s3krit/matrix-message-action@v0.0.3 + with: + room_id: ${{ secrets.MATRIX_ROOM_ID }} + access_token: ${{ secrets.MATRIX_ACCESS_TOKEN }} + message: "PR merged: [${{ github.event.pull_request.title }}](${{ github.event.pull_request.html_url }})" + server: "matrix.org" + + abandoned-pr: + if: github.event.action == 'closed' && github.event.pull_request.merged == false && github.repository == 'rtic-rs/cortex-m-rtic' + runs-on: ubuntu-latest + continue-on-error: true + steps: + - name: send message + uses: s3krit/matrix-message-action@v0.0.3 + with: + room_id: ${{ secrets.MATRIX_ROOM_ID }} + access_token: ${{ secrets.MATRIX_ACCESS_TOKEN }} + message: "PR closed without merging: [${{ github.event.pull_request.title }}](${{ github.event.pull_request.html_url }})" + server: "matrix.org" From 906abba71e2e21c44ef57dff0f0e0a13f242d216 Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Mon, 9 May 2022 13:32:26 +0200 Subject: [PATCH 146/423] Prepare v1.1.2 --- CHANGELOG.md | 14 ++++++++++++-- Cargo.toml | 4 ++-- macros/Cargo.toml | 2 +- 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b9bd7f5339..1cbe30fff4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,11 +11,19 @@ For each category, *Added*, *Changed*, *Fixed* add new entries at the top! ### Fixed +### Changed + +## [v1.1.2] - 2022-05-09 + +### Added + +### Fixed + - Generation of masks for the source masking scheduling for thumbv6 ### Changed -## [v1.1.1] - 2022-04-13 +## [v1.1.1] - 2022-04-13 - YANKED ### Added @@ -25,7 +33,7 @@ For each category, *Added*, *Changed*, *Fixed* add new entries at the top! ### Changed -## [v1.1.0] - 2022-04-13 +## [v1.1.0] - 2022-04-13 - YANKED ### Added @@ -506,6 +514,8 @@ Yanked due to a soundness issue in `init`; the issue has been mostly fixed in v0 - Initial release [Unreleased]: https://github.com/rtic-rs/cortex-m-rtic/compare/v1.1.0...HEAD +[v1.1.2]: https://github.com/rtic-rs/cortex-m-rtic/compare/v1.0.1...v1.1.2 +[v1.1.1]: https://github.com/rtic-rs/cortex-m-rtic/compare/v1.1.0...v1.1.1 [v1.1.0]: https://github.com/rtic-rs/cortex-m-rtic/compare/v1.0.0...v1.1.0 [v1.0.0]: https://github.com/rtic-rs/cortex-m-rtic/compare/v0.6.0-rc.4...v1.0.0 [v0.6.0-rc.4]: https://github.com/rtic-rs/cortex-m-rtic/compare/v0.6.0-rc.3...v0.6.0-rc.4 diff --git a/Cargo.toml b/Cargo.toml index 605182ae47..705eaa033a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,14 +14,14 @@ name = "cortex-m-rtic" readme = "README.md" repository = "https://github.com/rtic-rs/cortex-m-rtic" -version = "1.1.1" +version = "1.1.2" [lib] name = "rtic" [dependencies] cortex-m = "0.7.0" -cortex-m-rtic-macros = { path = "macros", version = "1.1.0" } +cortex-m-rtic-macros = { path = "macros", version = "1.1.1" } rtic-monotonic = "1.0.0" rtic-core = "1.0.0" heapless = "0.7.7" diff --git a/macros/Cargo.toml b/macros/Cargo.toml index 350db900b5..8b3bf08028 100644 --- a/macros/Cargo.toml +++ b/macros/Cargo.toml @@ -12,7 +12,7 @@ license = "MIT OR Apache-2.0" name = "cortex-m-rtic-macros" readme = "../README.md" repository = "https://github.com/rtic-rs/cortex-m-rtic" -version = "1.1.0" +version = "1.1.1" [lib] proc-macro = true From e5643ee94e91adc50e0ad82a6105eee3cba60a01 Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Tue, 10 May 2022 13:38:23 +0200 Subject: [PATCH 147/423] Fixed warning from Rust Analyzer --- macros/Cargo.toml | 2 +- macros/src/codegen/shared_resources.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/macros/Cargo.toml b/macros/Cargo.toml index 8b3bf08028..e4f5cc2379 100644 --- a/macros/Cargo.toml +++ b/macros/Cargo.toml @@ -12,7 +12,7 @@ license = "MIT OR Apache-2.0" name = "cortex-m-rtic-macros" readme = "../README.md" repository = "https://github.com/rtic-rs/cortex-m-rtic" -version = "1.1.1" +version = "1.1.2" [lib] proc-macro = true diff --git a/macros/src/codegen/shared_resources.rs b/macros/src/codegen/shared_resources.rs index f944e3e5ec..47f8faf08c 100644 --- a/macros/src/codegen/shared_resources.rs +++ b/macros/src/codegen/shared_resources.rs @@ -162,14 +162,14 @@ pub fn codegen( let masks_name = util::priority_masks_ident(); mod_app.push(quote!( #[doc(hidden)] - #[allow(non_camel_case_types)] + #[allow(non_upper_case_globals)] const #masks_name: [u32; 3] = [#(#mask_arr),*]; )); if uses_exceptions_with_resources { mod_app.push(quote!( #[doc(hidden)] - #[allow(non_camel_case_types)] + #[allow(non_upper_case_globals)] const __rtic_internal_V6_ERROR: () = rtic::export::v6_panic(); )); } From cd445165c50a46042e0cf17eb0081879f9cf4d52 Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Tue, 17 May 2022 20:12:36 +0200 Subject: [PATCH 148/423] More ergonomic error from static asserts messages --- macros/src/codegen/pre_init.rs | 25 ++++++++++++++++++++++--- ui/task-priority-too-high.stderr | 14 +++++++++++--- 2 files changed, 33 insertions(+), 6 deletions(-) diff --git a/macros/src/codegen/pre_init.rs b/macros/src/codegen/pre_init.rs index 91c9991274..ae2fd0503f 100644 --- a/macros/src/codegen/pre_init.rs +++ b/macros/src/codegen/pre_init.rs @@ -49,8 +49,14 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec= #priority as usize, #es); + )); stmts.push(quote!( core.NVIC.set_priority( @@ -72,8 +78,14 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec= #priority as usize, #es); + )); stmts.push(quote!(core.SCB.set_priority( rtic::export::SystemHandler::#name, @@ -90,8 +102,15 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec= #priority as usize, #es); + )); let mono_type = &monotonic.ty; diff --git a/ui/task-priority-too-high.stderr b/ui/task-priority-too-high.stderr index e0978f7188..a5ce918a4c 100644 --- a/ui/task-priority-too-high.stderr +++ b/ui/task-priority-too-high.stderr @@ -1,7 +1,15 @@ +warning: unused variable: `cx` + --> ui/task-priority-too-high.rs:12:13 + | +12 | fn init(cx: init::Context) -> (Shared, Local, init::Monotonics) { + | ^^ help: if this is intentional, prefix it with an underscore: `_cx` + | + = note: `#[warn(unused_variables)]` on by default + error[E0080]: evaluation of constant value failed - --> $DIR/task-priority-too-high.rs:3:1 + --> ui/task-priority-too-high.rs:3:1 | 3 | #[rtic::app(device = lm3s6965)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ attempt to compute `8_usize - 9_usize`, which would overflow + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at 'Maximum priority used by interrupt vector 'I2C0' is more than supported by hardware', $DIR/ui/task-priority-too-high.rs:3:1 | - = note: this error originates in the attribute macro `rtic::app` (in Nightly builds, run with -Z macro-backtrace for more info) + = note: this error originates in the macro `$crate::panic::panic_2015` (in Nightly builds, run with -Z macro-backtrace for more info) From b15bda2d39e708c23027264dfa1acd72deb0b59b Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Tue, 24 May 2022 05:51:44 +0200 Subject: [PATCH 149/423] Fix clash with defmt --- macros/Cargo.toml | 2 +- macros/src/codegen/assertions.rs | 6 +++++- macros/src/codegen/pre_init.rs | 6 +++--- src/export.rs | 4 +++- 4 files changed, 12 insertions(+), 6 deletions(-) diff --git a/macros/Cargo.toml b/macros/Cargo.toml index e4f5cc2379..f9d8add79c 100644 --- a/macros/Cargo.toml +++ b/macros/Cargo.toml @@ -12,7 +12,7 @@ license = "MIT OR Apache-2.0" name = "cortex-m-rtic-macros" readme = "../README.md" repository = "https://github.com/rtic-rs/cortex-m-rtic" -version = "1.1.2" +version = "1.1.3" [lib] proc-macro = true diff --git a/macros/src/codegen/assertions.rs b/macros/src/codegen/assertions.rs index 36ab036445..f6a098b5e6 100644 --- a/macros/src/codegen/assertions.rs +++ b/macros/src/codegen/assertions.rs @@ -28,7 +28,11 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec 31 { + ::core::panic!("An interrupt above value 31 is used while in armv6"); + } + )) } else { None } diff --git a/macros/src/codegen/pre_init.rs b/macros/src/codegen/pre_init.rs index ae2fd0503f..3d541a4749 100644 --- a/macros/src/codegen/pre_init.rs +++ b/macros/src/codegen/pre_init.rs @@ -55,7 +55,7 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec= #priority as usize, #es); + const _: () = if (1 << #nvic_prio_bits) < #priority as usize { ::core::panic!(#es); }; )); stmts.push(quote!( @@ -84,7 +84,7 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec= #priority as usize, #es); + const _: () = if (1 << #nvic_prio_bits) < #priority as usize { ::core::panic!(#es); }; )); stmts.push(quote!(core.SCB.set_priority( @@ -109,7 +109,7 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec= #priority as usize, #es); + const _: () = if (1 << #nvic_prio_bits) < #priority as usize { ::core::panic!(#es); }; )); let mono_type = &monotonic.ty; diff --git a/src/export.rs b/src/export.rs index 42f3fe2e34..6e1e4a6567 100644 --- a/src/export.rs +++ b/src/export.rs @@ -66,7 +66,9 @@ impl Barrier { } pub fn wait(&self) { - while !self.inner.load(Ordering::Acquire) {} + while !self.inner.load(Ordering::Acquire) { + core::hint::spin_loop() + } } } From 5c47aba1a11139742f8289ca4c47e045822ee219 Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Tue, 24 May 2022 19:38:24 +0200 Subject: [PATCH 150/423] Fix macros to Rust 2021 --- macros/Cargo.toml | 4 ++-- ui/task-priority-too-high.stderr | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/macros/Cargo.toml b/macros/Cargo.toml index f9d8add79c..2534f8abe3 100644 --- a/macros/Cargo.toml +++ b/macros/Cargo.toml @@ -6,13 +6,13 @@ authors = [ categories = ["concurrency", "embedded", "no-std"] description = "Procedural macros of the cortex-m-rtic crate" documentation = "https://rtic-rs.github.io/cortex-m-rtic/api/cortex_m_rtic" -edition = "2018" +edition = "2021" keywords = ["arm", "cortex-m"] license = "MIT OR Apache-2.0" name = "cortex-m-rtic-macros" readme = "../README.md" repository = "https://github.com/rtic-rs/cortex-m-rtic" -version = "1.1.3" +version = "1.1.4" [lib] proc-macro = true diff --git a/ui/task-priority-too-high.stderr b/ui/task-priority-too-high.stderr index a5ce918a4c..8b905c4039 100644 --- a/ui/task-priority-too-high.stderr +++ b/ui/task-priority-too-high.stderr @@ -12,4 +12,4 @@ error[E0080]: evaluation of constant value failed 3 | #[rtic::app(device = lm3s6965)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at 'Maximum priority used by interrupt vector 'I2C0' is more than supported by hardware', $DIR/ui/task-priority-too-high.rs:3:1 | - = note: this error originates in the macro `$crate::panic::panic_2015` (in Nightly builds, run with -Z macro-backtrace for more info) + = note: this error originates in the macro `$crate::panic::panic_2021` (in Nightly builds, run with -Z macro-backtrace for more info) From ab90426416c8b7cc382f4b0567a71bad9debdb69 Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Tue, 7 Jun 2022 12:36:40 +0200 Subject: [PATCH 151/423] fix ci: use SYST::PTR SYST::ptr has been deprecated in cortex-m v0.7.5 SYST::PTR is available since cortex-m v0.7.0 --- src/export.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/export.rs b/src/export.rs index 6e1e4a6567..a4d76b5357 100644 --- a/src/export.rs +++ b/src/export.rs @@ -302,14 +302,14 @@ fn compute_mask(from_prio: u8, to_prio: u8, masks: &[u32; 3]) -> u32 { #[cfg(not(armv7m))] #[inline(always)] unsafe fn set_enable_mask(mask: u32) { - (*NVIC::ptr()).iser[0].write(mask) + (*NVIC::PTR).iser[0].write(mask) } // disables interrupts #[cfg(not(armv7m))] #[inline(always)] unsafe fn clear_enable_mask(mask: u32) { - (*NVIC::ptr()).icer[0].write(mask) + (*NVIC::PTR).icer[0].write(mask) } #[inline] From 8af754fc729d73a019f1e7cba4cbf3b8a0b3adbc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Tj=C3=A4der?= Date: Thu, 23 Jun 2022 13:36:14 +0200 Subject: [PATCH 152/423] Bump rtic-syntax to v1.0.2 and fix Changelog --- CHANGELOG.md | 33 ++++++++++++++++++++++++++++++++- Cargo.toml | 2 +- macros/Cargo.toml | 4 ++-- 3 files changed, 35 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1cbe30fff4..1573a57897 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,34 @@ For each category, *Added*, *Changed*, *Fixed* add new entries at the top! ### Changed +## [v1.1.5] - 2022-06-23 + +### Added + +### Fixed +Bump rtic-syntax to 1.0.2 +fix ci: use SYST::PTR + +### Changed + +## [v1.1.4] - 2022-05-24 + +### Added + +### Fixed +Fix macros to Rust 2021 + +### Changed + +## [v1.1.3] - 2022-05-24 + +### Added + +### Fixed +Fix clash with defmt + +### Changed + ## [v1.1.2] - 2022-05-09 ### Added @@ -513,7 +541,10 @@ Yanked due to a soundness issue in `init`; the issue has been mostly fixed in v0 - Initial release -[Unreleased]: https://github.com/rtic-rs/cortex-m-rtic/compare/v1.1.0...HEAD +[Unreleased]: https://github.com/rtic-rs/cortex-m-rtic/compare/v1.1.5...HEAD +[v1.1.5]: https://github.com/rtic-rs/cortex-m-rtic/compare/v1.0.4...v1.1.5 +[v1.1.4]: https://github.com/rtic-rs/cortex-m-rtic/compare/v1.0.3...v1.1.4 +[v1.1.3]: https://github.com/rtic-rs/cortex-m-rtic/compare/v1.0.2...v1.1.3 [v1.1.2]: https://github.com/rtic-rs/cortex-m-rtic/compare/v1.0.1...v1.1.2 [v1.1.1]: https://github.com/rtic-rs/cortex-m-rtic/compare/v1.1.0...v1.1.1 [v1.1.0]: https://github.com/rtic-rs/cortex-m-rtic/compare/v1.0.0...v1.1.0 diff --git a/Cargo.toml b/Cargo.toml index 705eaa033a..fa1a5bb9ba 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,7 +21,7 @@ name = "rtic" [dependencies] cortex-m = "0.7.0" -cortex-m-rtic-macros = { path = "macros", version = "1.1.1" } +cortex-m-rtic-macros = { path = "macros", version = "1.1.5" } rtic-monotonic = "1.0.0" rtic-core = "1.0.0" heapless = "0.7.7" diff --git a/macros/Cargo.toml b/macros/Cargo.toml index 2534f8abe3..f1111e6ca1 100644 --- a/macros/Cargo.toml +++ b/macros/Cargo.toml @@ -12,7 +12,7 @@ license = "MIT OR Apache-2.0" name = "cortex-m-rtic-macros" readme = "../README.md" repository = "https://github.com/rtic-rs/cortex-m-rtic" -version = "1.1.4" +version = "1.1.5" [lib] proc-macro = true @@ -22,7 +22,7 @@ proc-macro2 = "1" proc-macro-error = "1" quote = "1" syn = "1" -rtic-syntax = "1.0.1" +rtic-syntax = "1.0.2" [features] debugprint = [] From 563a3c9d4cc04014a7c2902b72811ea58a784960 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Tj=C3=A4der?= Date: Thu, 23 Jun 2022 13:58:50 +0200 Subject: [PATCH 153/423] Release RTIC v1.1.3 --- CHANGELOG.md | 31 ++++++++++++------------------- Cargo.toml | 2 +- 2 files changed, 13 insertions(+), 20 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1573a57897..68af76937c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,31 +13,26 @@ For each category, *Added*, *Changed*, *Fixed* add new entries at the top! ### Changed -## [v1.1.5] - 2022-06-23 +## [v1.1.3] - 2022-06-23 ### Added ### Fixed -Bump rtic-syntax to 1.0.2 -fix ci: use SYST::PTR -### Changed +- Bump cortex-m-rtic-macros to 1.1.5 +- fix ci: use SYST::PTR -## [v1.1.4] - 2022-05-24 +#### cortex-m-rtic-macros v1.1.5 - 2022-06-23 -### Added +- Bump rtic-syntax to 1.0.2 -### Fixed -Fix macros to Rust 2021 +#### cortex-m-rtic-macros v1.1.4 - 2022-05-24 -### Changed +- Fix macros to Rust 2021 -## [v1.1.3] - 2022-05-24 +#### cortex-m-rtic-macros v1.1.3 - 2022-05-24 -### Added - -### Fixed -Fix clash with defmt +- Fix clash with defmt ### Changed @@ -541,11 +536,9 @@ Yanked due to a soundness issue in `init`; the issue has been mostly fixed in v0 - Initial release -[Unreleased]: https://github.com/rtic-rs/cortex-m-rtic/compare/v1.1.5...HEAD -[v1.1.5]: https://github.com/rtic-rs/cortex-m-rtic/compare/v1.0.4...v1.1.5 -[v1.1.4]: https://github.com/rtic-rs/cortex-m-rtic/compare/v1.0.3...v1.1.4 -[v1.1.3]: https://github.com/rtic-rs/cortex-m-rtic/compare/v1.0.2...v1.1.3 -[v1.1.2]: https://github.com/rtic-rs/cortex-m-rtic/compare/v1.0.1...v1.1.2 +[Unreleased]: https://github.com/rtic-rs/cortex-m-rtic/compare/v1.1.3...HEAD +[v1.1.3]: https://github.com/rtic-rs/cortex-m-rtic/compare/v1.1.2...v1.1.3 +[v1.1.2]: https://github.com/rtic-rs/cortex-m-rtic/compare/v1.1.1...v1.1.2 [v1.1.1]: https://github.com/rtic-rs/cortex-m-rtic/compare/v1.1.0...v1.1.1 [v1.1.0]: https://github.com/rtic-rs/cortex-m-rtic/compare/v1.0.0...v1.1.0 [v1.0.0]: https://github.com/rtic-rs/cortex-m-rtic/compare/v0.6.0-rc.4...v1.0.0 diff --git a/Cargo.toml b/Cargo.toml index fa1a5bb9ba..922385c943 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,7 +14,7 @@ name = "cortex-m-rtic" readme = "README.md" repository = "https://github.com/rtic-rs/cortex-m-rtic" -version = "1.1.2" +version = "1.1.3" [lib] name = "rtic" From c6fd3cdd0a4e29c1e2d77c6e9107450a1bceed92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20G=C3=B3rski?= Date: Wed, 6 Jul 2022 17:43:38 +0200 Subject: [PATCH 154/423] Allow custom `link_section` attributes for late resources This commit makes RTIC aware of user-provided `link_section` attributes, letting user override default section mapping. --- macros/src/codegen/local_resources.rs | 9 ++++++++- macros/src/codegen/shared_resources.rs | 11 +++++++++-- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/macros/src/codegen/local_resources.rs b/macros/src/codegen/local_resources.rs index 50621c32c0..087967f2a4 100644 --- a/macros/src/codegen/local_resources.rs +++ b/macros/src/codegen/local_resources.rs @@ -27,8 +27,15 @@ pub fn codegen( let mangled_name = util::static_local_resource_ident(name); let attrs = &res.attrs; + // late resources in `util::link_section_uninit` - let section = util::link_section_uninit(); + // unless user specifies custom link section + let section = if attrs.iter().any(|attr| attr.path.is_ident("link_section")) { + None + } + else { + Some(util::link_section_uninit()) + }; // For future use // let doc = format!(" RTIC internal: {}:{}", file!(), line!()); diff --git a/macros/src/codegen/shared_resources.rs b/macros/src/codegen/shared_resources.rs index 47f8faf08c..010b27aea0 100644 --- a/macros/src/codegen/shared_resources.rs +++ b/macros/src/codegen/shared_resources.rs @@ -23,10 +23,17 @@ pub fn codegen( let ty = &res.ty; let mangled_name = &util::static_shared_resource_ident(name); - // late resources in `util::link_section_uninit` - let section = util::link_section_uninit(); let attrs = &res.attrs; + // late resources in `util::link_section_uninit` + // unless user specifies custom link section + let section = if attrs.iter().any(|attr| attr.path.is_ident("link_section")) { + None + } + else { + Some(util::link_section_uninit()) + }; + // For future use // let doc = format!(" RTIC internal: {}:{}", file!(), line!()); mod_app.push(quote!( From b4cfc4db84d304f1fd6384a173d089544340b3e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20G=C3=B3rski?= Date: Wed, 27 Jul 2022 20:25:34 +0200 Subject: [PATCH 155/423] Fix missing formatting --- macros/src/codegen/local_resources.rs | 3 +-- macros/src/codegen/shared_resources.rs | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/macros/src/codegen/local_resources.rs b/macros/src/codegen/local_resources.rs index 087967f2a4..6e7c1daa72 100644 --- a/macros/src/codegen/local_resources.rs +++ b/macros/src/codegen/local_resources.rs @@ -32,8 +32,7 @@ pub fn codegen( // unless user specifies custom link section let section = if attrs.iter().any(|attr| attr.path.is_ident("link_section")) { None - } - else { + } else { Some(util::link_section_uninit()) }; diff --git a/macros/src/codegen/shared_resources.rs b/macros/src/codegen/shared_resources.rs index 010b27aea0..08d77cc326 100644 --- a/macros/src/codegen/shared_resources.rs +++ b/macros/src/codegen/shared_resources.rs @@ -29,8 +29,7 @@ pub fn codegen( // unless user specifies custom link section let section = if attrs.iter().any(|attr| attr.path.is_ident("link_section")) { None - } - else { + } else { Some(util::link_section_uninit()) }; From f15614e7cbba889462a22ae94b470e9a3c3dc817 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20G=C3=B3rski?= Date: Wed, 27 Jul 2022 20:29:14 +0200 Subject: [PATCH 156/423] Update CHANGELOG.md --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 68af76937c..b131b30729 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,8 @@ For each category, *Added*, *Changed*, *Fixed* add new entries at the top! ### Added +- Allow custom `link_section` attributes for late resources + ### Fixed ### Changed From 368ab1d4fb780386f3162c7a84502e301af7f3b0 Mon Sep 17 00:00:00 2001 From: David Watson Date: Sun, 3 Jul 2022 12:24:11 -0400 Subject: [PATCH 157/423] Remove use of basepri register on thumbv8m.base The basepri register appears to be aviable on thumbv8m.main but not thumbv8m.base. At the very least, attempting to compile against a Cortex-M23 based Microchip ATSAML10E16A generates an error: ``` error[E0432]: unresolved import `cortex_m::register::basepri` --> /Users/dwatson/.cargo/registry/src/github.com-1ecc6299db9ec823/cortex-m-rtic-1.1.3/src/export.rs:25:5 | 25 | use cortex_m::register::basepri; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ no `basepri` in `register` ``` This is an attempt to account for the fact that thumbv8m.base (M23) MCUs don't have the BASEPRI register but have more than 32 interrupts. This moves away from the architecture specific config flags and switches to a more functional flag. Make the mask size depend on the max interrupt id Rather than assuming a fixed interrupt count of 32 this code uses an array of u32 bitmasks to calculate the priority mask. The size of this array is calculated at compile time based on the size of the largest interrupt id being used in the target code. For thumbv6m this should be equivalent to the previous version that used a single u32 mask. For thumbv8m.base it will be larger depending on the interrupts used. Don't write 0s to the ISER and ICER registers Writing 0s to these registers is a no-op. Since these masks should be calculated at compile time, this conditional should result in writes being optimized out of the code. Prevent panic on non-arm targets Panicking on unknown targets was breaking things like the doc build on linux. This change should only panic when building on unknown arm targets. --- CHANGELOG.md | 2 + build.rs | 18 ++- macros/src/codegen/assertions.rs | 11 +- macros/src/codegen/shared_resources.rs | 19 +++- macros/src/codegen/util.rs | 5 + src/export.rs | 149 ++++++++++++++++++------- 6 files changed, 146 insertions(+), 58 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 68af76937c..5ebd381151 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,8 @@ For each category, *Added*, *Changed*, *Fixed* add new entries at the top! ### Fixed +- Distinguish between thumbv8m.base and thumbv8m.main for basepri usage. + ### Changed ## [v1.1.3] - 2022-06-23 diff --git a/build.rs b/build.rs index 7c518c90d4..ff9ebe35e7 100644 --- a/build.rs +++ b/build.rs @@ -7,15 +7,21 @@ fn main() { println!("cargo:rustc-cfg=rustc_is_nightly"); } - if target.starts_with("thumbv6m") { - println!("cargo:rustc-cfg=armv6m"); - } - + // These targets all have know support for the BASEPRI register. if target.starts_with("thumbv7m") | target.starts_with("thumbv7em") - | target.starts_with("thumbv8m") + | target.starts_with("thumbv8m.main") { - println!("cargo:rustc-cfg=armv7m"); + println!("cargo:rustc-cfg=have_basepri"); + + // These targets are all known to _not_ have the BASEPRI register. + } else if target.starts_with("thumb") + && !(target.starts_with("thumbv6m") | target.starts_with("thumbv8m.base")) + { + panic!( + "Unknown target '{}'. Need to update BASEPRI logic in build.rs.", + target + ); } println!("cargo:rerun-if-changed=build.rs"); diff --git a/macros/src/codegen/assertions.rs b/macros/src/codegen/assertions.rs index f6a098b5e6..66e5409504 100644 --- a/macros/src/codegen/assertions.rs +++ b/macros/src/codegen/assertions.rs @@ -22,15 +22,16 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec = app + let chunks_name = util::priority_mask_chunks_ident(); + let no_basepri_checks: Vec<_> = app .hardware_tasks .iter() .filter_map(|(_, task)| { if !util::is_exception(&task.args.binds) { let interrupt_name = &task.args.binds; Some(quote!( - if (#device::Interrupt::#interrupt_name as u32) > 31 { - ::core::panic!("An interrupt above value 31 is used while in armv6"); + if (#device::Interrupt::#interrupt_name as usize) >= (#chunks_name * 32) { + ::core::panic!("An interrupt out of range is used while in armv6 or armv8m.base"); } )) } else { @@ -41,8 +42,8 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec; 3] = [#(#mask_arr),*]; )); if uses_exceptions_with_resources { mod_app.push(quote!( #[doc(hidden)] #[allow(non_upper_case_globals)] - const __rtic_internal_V6_ERROR: () = rtic::export::v6_panic(); + const __rtic_internal_V6_ERROR: () = rtic::export::no_basepri_panic(); )); } diff --git a/macros/src/codegen/util.rs b/macros/src/codegen/util.rs index 0f3dca7c47..0a3edc20d2 100644 --- a/macros/src/codegen/util.rs +++ b/macros/src/codegen/util.rs @@ -253,6 +253,11 @@ pub fn static_shared_resource_ident(name: &Ident) -> Ident { mark_internal_name(&format!("shared_resource_{}", name)) } +/// Generates an Ident for the number of 32 bit chunks used for Mask storage. +pub fn priority_mask_chunks_ident() -> Ident { + mark_internal_name("MASK_CHUNKS") +} + pub fn priority_masks_ident() -> Ident { mark_internal_name("MASKS") } diff --git a/src/export.rs b/src/export.rs index a4d76b5357..6f2a1b63c1 100644 --- a/src/export.rs +++ b/src/export.rs @@ -21,10 +21,43 @@ pub use rtic_monotonic as monotonic; pub type SCFQ = Queue; pub type SCRQ = Queue<(T, u8), N>; -#[cfg(armv7m)] +/// Mask is used to store interrupt masks on systems without a BASEPRI register (M0, M0+, M23). +/// It needs to be large enough to cover all the relevant interrupts in use. +/// For M0/M0+ there are only 32 interrupts so we only need one u32 value. +/// For M23 there can be as many as 480 interrupts. +/// Rather than providing space for all possible interrupts, we just detect the highest interrupt in +/// use at compile time and allocate enough u32 chunks to cover them. +#[derive(Copy, Clone)] +pub struct Mask([u32; M]); + +impl core::ops::BitOrAssign for Mask { + fn bitor_assign(&mut self, rhs: Self) { + for i in 0..M { + self.0[i] |= rhs.0[i]; + } + } +} + +#[cfg(not(have_basepri))] +impl Mask { + /// Set a bit inside a Mask. + const fn set_bit(mut self, bit: u32) -> Self { + let block = bit / 32; + + if block as usize >= M { + panic!("Generating masks for thumbv6/thumbv8m.base failed! Are you compiling for thumbv6 on an thumbv7 MCU or using an unsupported thumbv8m.base MCU?"); + } + + let offset = bit - (block * 32); + self.0[block as usize] |= 1 << offset; + self + } +} + +#[cfg(have_basepri)] use cortex_m::register::basepri; -#[cfg(armv7m)] +#[cfg(have_basepri)] #[inline(always)] pub fn run(priority: u8, f: F) where @@ -41,7 +74,7 @@ where } } -#[cfg(not(armv7m))] +#[cfg(not(have_basepri))] #[inline(always)] pub fn run(_priority: u8, f: F) where @@ -105,16 +138,16 @@ impl Priority { } /// Const helper to check architecture -pub const fn is_armv6() -> bool { - #[cfg(not(armv6m))] - { - false - } - - #[cfg(armv6m)] +pub const fn have_basepri() -> bool { + #[cfg(have_basepri)] { true } + + #[cfg(not(have_basepri))] + { + false + } } #[inline(always)] @@ -172,14 +205,14 @@ where /// Total OH of per task is max 2 clock cycles, negligible in practice /// but can in theory be fixed. /// -#[cfg(armv7m)] +#[cfg(have_basepri)] #[inline(always)] -pub unsafe fn lock( +pub unsafe fn lock( ptr: *mut T, priority: &Priority, ceiling: u8, nvic_prio_bits: u8, - _mask: &[u32; 3], + _mask: &[Mask; 3], f: impl FnOnce(&mut T) -> R, ) -> R { let current = priority.get(); @@ -247,14 +280,14 @@ pub unsafe fn lock( /// - Temporary lower exception priority /// /// These possible solutions are set goals for future work -#[cfg(not(armv7m))] +#[cfg(not(have_basepri))] #[inline(always)] -pub unsafe fn lock( +pub unsafe fn lock( ptr: *mut T, priority: &Priority, ceiling: u8, _nvic_prio_bits: u8, - masks: &[u32; 3], + masks: &[Mask; 3], f: impl FnOnce(&mut T) -> R, ) -> R { let current = priority.get(); @@ -288,28 +321,38 @@ pub unsafe fn lock( } } -#[cfg(not(armv7m))] +#[cfg(not(have_basepri))] #[inline(always)] -fn compute_mask(from_prio: u8, to_prio: u8, masks: &[u32; 3]) -> u32 { - let mut res = 0; +fn compute_mask(from_prio: u8, to_prio: u8, masks: &[Mask; 3]) -> Mask { + let mut res = Mask([0; M]); masks[from_prio as usize..to_prio as usize] .iter() - .for_each(|m| res |= m); + .for_each(|m| res |= *m); res } // enables interrupts -#[cfg(not(armv7m))] +#[cfg(not(have_basepri))] #[inline(always)] -unsafe fn set_enable_mask(mask: u32) { - (*NVIC::PTR).iser[0].write(mask) +unsafe fn set_enable_mask(mask: Mask) { + for i in 0..M { + // This check should involve compile time constants and be optimized out. + if mask.0[i] != 0 { + (*NVIC::PTR).iser[i].write(mask.0[i]); + } + } } // disables interrupts -#[cfg(not(armv7m))] +#[cfg(not(have_basepri))] #[inline(always)] -unsafe fn clear_enable_mask(mask: u32) { - (*NVIC::PTR).icer[0].write(mask) +unsafe fn clear_enable_mask(mask: Mask) { + for i in 0..M { + // This check should involve compile time constants and be optimized out. + if mask.0[i] != 0 { + (*NVIC::PTR).icer[i].write(mask.0[i]); + } + } } #[inline] @@ -318,36 +361,56 @@ pub fn logical2hw(logical: u8, nvic_prio_bits: u8) -> u8 { ((1 << nvic_prio_bits) - logical) << (8 - nvic_prio_bits) } -#[cfg(not(armv6m))] -pub const fn create_mask(_: [u32; N]) -> u32 { - 0 +#[cfg(have_basepri)] +pub const fn create_mask(_: [u32; N]) -> Mask { + Mask([0; M]) } -#[cfg(armv6m)] -pub const fn create_mask(list_of_shifts: [u32; N]) -> u32 { - let mut mask = 0; +#[cfg(not(have_basepri))] +pub const fn create_mask(list_of_shifts: [u32; N]) -> Mask { + let mut mask = Mask([0; M]); let mut i = 0; while i < N { let shift = list_of_shifts[i]; i += 1; - - if shift > 31 { - panic!("Generating masks for thumbv6 failed! Are you compiling for thumbv6 on an thumbv7 MCU?"); - } - - mask |= 1 << shift; + mask = mask.set_bit(shift); } mask } -#[cfg(not(armv6m))] -pub const fn v6_panic() { +#[cfg(have_basepri)] +pub const fn compute_mask_chunks(_: [u32; L]) -> usize { + 0 +} + +/// Compute the number of u32 chunks needed to store the Mask value. +/// On M0, M0+ this should always end up being 1. +/// On M23 we will pick a number that allows us to store the highest index used by the code. +/// This means the amount of overhead will vary based on the actually interrupts used by the code. +#[cfg(not(have_basepri))] +pub const fn compute_mask_chunks(ids: [u32; L]) -> usize { + let mut max: usize = 0; + let mut i = 0; + + while i < L { + let id = ids[i] as usize; + i += 1; + + if id > max { + max = id; + } + } + (max + 32) / 32 +} + +#[cfg(have_basepri)] +pub const fn no_basepri_panic() { // For non-v6 all is fine } -#[cfg(armv6m)] -pub const fn v6_panic() { - panic!("Exceptions with shared resources are not allowed when compiling for thumbv6. Use local resources or `#[lock_free]` shared resources"); +#[cfg(not(have_basepri))] +pub const fn no_basepri_panic() { + panic!("Exceptions with shared resources are not allowed when compiling for thumbv6 or thumbv8m.base. Use local resources or `#[lock_free]` shared resources"); } From ccbaea82aa6a55f6774b6eccad975382900f86d9 Mon Sep 17 00:00:00 2001 From: Jonas Jacobsson <01joja@gmail.com> Date: Tue, 27 Sep 2022 15:29:03 +0000 Subject: [PATCH 158/423] .toml and note aboute target --- .cargo/{config => config.toml} | 0 book/en/src/by-example.md | 6 ++++++ book/en/src/by-example/app_init.md | 6 ------ 3 files changed, 6 insertions(+), 6 deletions(-) rename .cargo/{config => config.toml} (100%) diff --git a/.cargo/config b/.cargo/config.toml similarity index 100% rename from .cargo/config rename to .cargo/config.toml diff --git a/book/en/src/by-example.md b/book/en/src/by-example.md index 3a523e51bb..419a4bae64 100644 --- a/book/en/src/by-example.md +++ b/book/en/src/by-example.md @@ -30,3 +30,9 @@ Yields this output: ``` console {{#include ../../../ci/expected/locals.run}} ``` + +> **NOTE**: You can choose target device by passing a target +> triple to cargo (e.g. `cargo run --example init --target thumbv7m-none-eabi`) or +> configure a default target in `.cargo/config.toml`. +> +> For running the examples, we use a Cortex M3 emulated in QEMU, so the target is `thumbv7m-none-eabi`. \ No newline at end of file diff --git a/book/en/src/by-example/app_init.md b/book/en/src/by-example/app_init.md index 615c299102..22c4a28ade 100644 --- a/book/en/src/by-example/app_init.md +++ b/book/en/src/by-example/app_init.md @@ -28,9 +28,3 @@ Running the example will print `init` to the console and then exit the QEMU proc $ cargo run --target thumbv7m-none-eabi --example init {{#include ../../../../ci/expected/init.run}} ``` - -> **NOTE**: You can choose target device by passing a target -> triple to cargo (e.g. `cargo run --example init --target thumbv7m-none-eabi`) or -> configure a default target in `.cargo/config.toml`. -> -> For running the examples, we use a Cortex M3 emulated in QEMU, so the target is `thumbv7m-none-eabi`. From ea6f824ad2fc02a21383e86baeaa7372ee9909e0 Mon Sep 17 00:00:00 2001 From: Jonas Jacobsson <01joja@gmail.com> Date: Tue, 27 Sep 2022 15:37:54 +0000 Subject: [PATCH 159/423] added .toml to .cargo/config in book --- book/ru/src/by-example/new.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/book/ru/src/by-example/new.md b/book/ru/src/by-example/new.md index fcf523701a..0ff8d986e9 100644 --- a/book/ru/src/by-example/new.md +++ b/book/ru/src/by-example/new.md @@ -78,7 +78,7 @@ $ cargo add panic-semihosting 5. Соберите его, загрузите в микроконтроллер и запустите. ``` console -$ # ПРИМЕЧАНИЕ: Я раскомментировал опцию `runner` в `.cargo/config` +$ # ПРИМЕЧАНИЕ: Я раскомментировал опцию `runner` в `.cargo/config.toml` $ cargo run {{#include ../../../../ci/expected/init.run}} ``` From b711c036ab44da6325f773eafa0538ebab7218dc Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Wed, 28 Sep 2022 20:37:42 +0200 Subject: [PATCH 160/423] Fix new lint in the compiler --- examples/ramfunc.rs | 1 - ui/task-priority-too-high.stderr | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/examples/ramfunc.rs b/examples/ramfunc.rs index 54acd7e84c..b3b8012c38 100644 --- a/examples/ramfunc.rs +++ b/examples/ramfunc.rs @@ -1,6 +1,5 @@ //! examples/ramfunc.rs -#![deny(unsafe_code)] #![deny(warnings)] #![no_main] #![no_std] diff --git a/ui/task-priority-too-high.stderr b/ui/task-priority-too-high.stderr index 8b905c4039..a7a15ebfe5 100644 --- a/ui/task-priority-too-high.stderr +++ b/ui/task-priority-too-high.stderr @@ -12,4 +12,4 @@ error[E0080]: evaluation of constant value failed 3 | #[rtic::app(device = lm3s6965)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at 'Maximum priority used by interrupt vector 'I2C0' is more than supported by hardware', $DIR/ui/task-priority-too-high.rs:3:1 | - = note: this error originates in the macro `$crate::panic::panic_2021` (in Nightly builds, run with -Z macro-backtrace for more info) + = note: this error originates in the macro `$crate::panic::panic_2021` which comes from the expansion of the macro `::core::panic` (in Nightly builds, run with -Z macro-backtrace for more info) From 9afb1f888f272a29a0bc525809a15570f8c93dc6 Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Wed, 14 Dec 2022 20:50:51 +0100 Subject: [PATCH 161/423] Docs on RTOS --- book/en/src/preface.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/book/en/src/preface.md b/book/en/src/preface.md index f833213ed4..6041dfedea 100644 --- a/book/en/src/preface.md +++ b/book/en/src/preface.md @@ -21,6 +21,18 @@ This is the documentation of v1.0.x of RTIC; for the documentation of version * v0.5.x go [here](/0.5). * v0.4.x go [here](/0.4). +## Is RTIC an RTOS? + +A common question is whether RTIC is an RTOS or not, and depending on your background the +answer may vary. From RTIC's developers point of view; RTIC is a hardware accelerated +RTOS that utilizes the NVIC in Cortex-M MCUs to perform scheduling, rather than the more +classical software kernel. + +Another common view from the community is that RTIC is a concurrency framework as there +is no software kernel and that it relies on external HALs. + +--- + {{#include ../../../README.md:7:47}} {{#include ../../../README.md:48:}} From d6edeb6a64077df2161bf55d5abdba536799885c Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Wed, 14 Dec 2022 21:26:40 +0100 Subject: [PATCH 162/423] Fix CI error caused by `critical-section` 0.2.8 --- Cargo.toml | 7 +++++++ macros/src/codegen/shared_resources.rs | 1 + xtask/src/command.rs | 10 +++++++++- 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 922385c943..61f8e56eb2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -70,3 +70,10 @@ overflow-checks = false [patch.crates-io] lm3s6965 = { git = "https://github.com/japaric/lm3s6965" } + +[features] +test-critical-section = ["cortex-m/critical-section-single-core"] + +[[example]] +name = "pool" +required-features = ["test-critical-section"] diff --git a/macros/src/codegen/shared_resources.rs b/macros/src/codegen/shared_resources.rs index 4a75007075..b5dff09da1 100644 --- a/macros/src/codegen/shared_resources.rs +++ b/macros/src/codegen/shared_resources.rs @@ -147,6 +147,7 @@ pub fn codegen( None } })) { + #[allow(clippy::or_fun_call)] let v = prio_to_masks.entry(priority - 1).or_insert(Vec::new()); v.push(quote!(#device::Interrupt::#name as u32)); mask_ids.push(quote!(#device::Interrupt::#name as u32)); diff --git a/xtask/src/command.rs b/xtask/src/command.rs index 2f719bf5c9..100888c075 100644 --- a/xtask/src/command.rs +++ b/xtask/src/command.rs @@ -46,7 +46,15 @@ impl<'a> CargoCommand<'a> { features, mode, } => { - let mut args = vec![self.name(), "--example", example, "--target", target]; + let mut args = vec![ + self.name(), + "--example", + example, + "--target", + target, + "--features", + "test-critical-section", + ]; if let Some(feature_name) = features { args.extend_from_slice(&["--features", feature_name]); From e31ee3fa114dbdd8a52f14292106803fb8b9bb96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Tj=C3=A4der?= Date: Thu, 15 Dec 2022 22:03:55 +0100 Subject: [PATCH 163/423] CI: Update to 22.04 --- .github/workflows/build.yml | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 875c3f39d1..8205d63a4e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -16,7 +16,7 @@ jobs: # Run cargo fmt --check, includes macros/ style: name: style - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 steps: - name: Checkout uses: actions/checkout@v2 @@ -41,7 +41,7 @@ jobs: # Compilation check check: name: check - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 strategy: matrix: target: @@ -77,7 +77,7 @@ jobs: # Clippy clippy: name: Cargo clippy - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 steps: - name: Checkout uses: actions/checkout@v2 @@ -104,7 +104,7 @@ jobs: # Verify all examples, checks checkexamples: name: checkexamples - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 strategy: matrix: target: @@ -137,7 +137,7 @@ jobs: # Verify the example output with run-pass tests testexamples: name: testexamples - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 strategy: matrix: target: @@ -182,7 +182,7 @@ jobs: # Check the correctness of macros/ crate checkmacros: name: checkmacros - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 strategy: matrix: target: @@ -216,7 +216,7 @@ jobs: # Run the macros test-suite testmacros: name: testmacros - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 steps: - name: Checkout uses: actions/checkout@v2 @@ -244,7 +244,7 @@ jobs: # Run test suite tests: name: tests - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 steps: - name: Checkout uses: actions/checkout@v2 @@ -271,7 +271,7 @@ jobs: # Build documentation, check links docs: name: docs - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 steps: - name: Checkout uses: actions/checkout@v2 @@ -318,7 +318,7 @@ jobs: # Build the books mdbook: name: mdbook - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 steps: - name: Checkout uses: actions/checkout@v2 @@ -367,7 +367,7 @@ jobs: # This needs to run before book is built mergetostablebranch: name: If CI passes, merge master branch into release/vX - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 needs: - style - check @@ -408,7 +408,7 @@ jobs: # If all tests pass, then deploy stage is run deploy: name: deploy - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 needs: mergetostablebranch @@ -561,7 +561,7 @@ jobs: - tests - docs - mdbook - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 steps: - name: Mark the job as a success run: exit 0 From 10d2a5935684fdf32f06b1cc38481314fce5654e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Tj=C3=A4der?= Date: Thu, 15 Dec 2022 22:09:09 +0100 Subject: [PATCH 164/423] Clippy: Fix (clippy::needless_borrow) --- macros/src/check.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/macros/src/check.rs b/macros/src/check.rs index 374fcedd09..b0ad6f8715 100644 --- a/macros/src/check.rs +++ b/macros/src/check.rs @@ -53,7 +53,7 @@ pub fn app(app: &App, _analysis: &Analysis) -> parse::Result { // If not enough tasks and first still is None, may cause // "custom attribute panicked" due to unwrap on None - return Err(parse::Error::new(first.unwrap().span(), &s)); + return Err(parse::Error::new(first.unwrap().span(), s)); } // Check that all exceptions are valid; only exceptions with configurable priorities are From ef40dd4b0bd6f6cf8e0019353180cd403ae17ad3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Tj=C3=A4der?= Date: Thu, 15 Dec 2022 23:56:01 +0100 Subject: [PATCH 165/423] CI: Run rustup and cargo directly --- .github/workflows/build.yml | 141 ++++++++++++------------------------ 1 file changed, 47 insertions(+), 94 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 8205d63a4e..f84fe05770 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -21,22 +21,14 @@ jobs: - name: Checkout uses: actions/checkout@v2 - - name: Install Rust - uses: actions-rs/toolchain@v1 - with: - profile: minimal - toolchain: stable - override: true - components: rustfmt - - name: Fail on warnings run: sed -i 's,//deny_warnings_placeholder_for_ci,#![deny(warnings)],' src/lib.rs macros/src/lib.rs + - name: Add Rust component + run: rustup component add rustfmt + - name: cargo fmt --check - uses: actions-rs/cargo@v1 - with: - command: fmt - args: --all -- --check + run: cargo fmt --all -- --check # Compilation check check: @@ -54,12 +46,13 @@ jobs: - name: Checkout uses: actions/checkout@v2 - - name: Install Rust ${{ matrix.toolchain }} with target (${{ matrix.target }}) - uses: actions-rs/toolchain@v1 - with: - toolchain: ${{ matrix.toolchain }} - target: ${{ matrix.target }} - override: true + - name: Install Rust ${{ matrix.toolchain }} + run: | + rustup set profile minimal + rustup override set ${{ matrix.toolchain }} + + - name: Configure Rust target (${{ matrix.target }}) + run: rustup target add ${{ matrix.target }} - name: Fail on warnings run: sed -i 's,//deny_warnings_placeholder_for_ci,#![deny(warnings)],' src/lib.rs macros/src/lib.rs @@ -68,11 +61,7 @@ jobs: uses: Swatinem/rust-cache@v1 - name: cargo check - uses: actions-rs/cargo@v1 - with: - use-cross: false - command: check - args: --target=${{ matrix.target }} + run: cargo check --target=${{ matrix.target }} # Clippy clippy: @@ -82,13 +71,6 @@ jobs: - name: Checkout uses: actions/checkout@v2 - - name: Install Rust stable - uses: actions-rs/toolchain@v1 - with: - toolchain: stable - target: x86_64-unknown-linux-gnu - override: true - - name: Fail on warnings run: sed -i 's,//deny_warnings_placeholder_for_ci,#![deny(warnings)],' src/lib.rs macros/src/lib.rs @@ -96,10 +78,7 @@ jobs: uses: Swatinem/rust-cache@v1 - name: cargo clippy - uses: actions-rs/cargo@v1 - with: - use-cross: false - command: clippy + run: clippy # Verify all examples, checks checkexamples: @@ -116,23 +95,22 @@ jobs: - name: Checkout uses: actions/checkout@v2 - - name: Install Rust ${{ matrix.toolchain }} with target (${{ matrix.target }}) - uses: actions-rs/toolchain@v1 - with: - toolchain: ${{ matrix.toolchain }} - target: ${{ matrix.target }} - override: true - components: llvm-tools-preview + - name: Install Rust ${{ matrix.toolchain }} + run: | + rustup set profile minimal + rustup override set ${{ matrix.toolchain }} + + - name: Configure Rust target (${{ matrix.target }}) + run: rustup target add ${{ matrix.target }} + + - name: Add Rust component llvm-tools-preview + run: rustup component add llvm-tools-preview - name: Cache Dependencies uses: Swatinem/rust-cache@v1 - name: Check the examples - uses: actions-rs/cargo@v1 - with: - use-cross: false - command: check - args: --examples --target=${{ matrix.target }} + run: cargo check --examples --target=${{ matrix.target }} # Verify the example output with run-pass tests testexamples: @@ -149,20 +127,20 @@ jobs: - name: Checkout uses: actions/checkout@v2 - - name: Install Rust ${{ matrix.toolchain }} with target (${{ matrix.target }}) - uses: actions-rs/toolchain@v1 - with: - toolchain: ${{ matrix.toolchain }} - target: ${{ matrix.target }} - override: true - components: llvm-tools-preview + - name: Install Rust ${{ matrix.toolchain }} + run: | + rustup set profile minimal + rustup override set ${{ matrix.toolchain }} + + - name: Configure Rust target (${{ matrix.target }}) + run: rustup target add ${{ matrix.target }} + + - name: Add Rust component llvm-tools-preview + run: rustup component add llvm-tools-preview # Use precompiled binutils - name: cargo install cargo-binutils - uses: actions-rs/install@v0.1 - with: - crate: cargo-binutils - version: latest + run: cargo install cargo-binutils - name: Cache Dependencies uses: Swatinem/rust-cache@v1 @@ -176,8 +154,7 @@ jobs: run: sed -i 's,//deny_warnings_placeholder_for_ci,#![deny(warnings)],' src/lib.rs macros/src/lib.rs - name: Run-pass tests - run: - cargo xtask --target ${{ matrix.target }} + run: cargo xtask --target ${{ matrix.target }} # Check the correctness of macros/ crate checkmacros: @@ -193,12 +170,13 @@ jobs: - name: Checkout uses: actions/checkout@v2 - - name: Install Rust ${{ matrix.toolchain }} with target (${{ matrix.target }}) - uses: actions-rs/toolchain@v1 - with: - toolchain: ${{ matrix.toolchain }} - target: ${{ matrix.target }} - override: true + - name: Install Rust ${{ matrix.toolchain }} + run: | + rustup set profile minimal + rustup override set ${{ matrix.toolchain }} + + - name: Configure Rust target (${{ matrix.target }}) + run: rustup target add ${{ matrix.target }} - name: Cache Dependencies uses: Swatinem/rust-cache@v1 @@ -207,11 +185,7 @@ jobs: run: sed -i 's,//deny_warnings_placeholder_for_ci,#![deny(warnings)],' src/lib.rs macros/src/lib.rs - name: cargo check - uses: actions-rs/cargo@v1 - with: - use-cross: false - command: check - args: --manifest-path macros/Cargo.toml --target=${{ matrix.target }} + run: cargo check --manifest-path macros/Cargo.toml --target=${{ matrix.target }} # Run the macros test-suite testmacros: @@ -221,13 +195,6 @@ jobs: - name: Checkout uses: actions/checkout@v2 - - name: Install Rust - uses: actions-rs/toolchain@v1 - with: - toolchain: stable - target: x86_64-unknown-linux-gnu - override: true - - name: Cache Dependencies uses: Swatinem/rust-cache@v1 @@ -235,11 +202,7 @@ jobs: run: sed -i 's,//deny_warnings_placeholder_for_ci,#![deny(warnings)],' src/lib.rs macros/src/lib.rs - name: cargo check - uses: actions-rs/cargo@v1 - with: - use-cross: false - command: test - args: --manifest-path macros/Cargo.toml + run: cargo test --manifest-path macros/Cargo.toml # Run test suite tests: @@ -249,24 +212,14 @@ jobs: - name: Checkout uses: actions/checkout@v2 - - name: Install Rust - uses: actions-rs/toolchain@v1 - with: - toolchain: stable - target: x86_64-unknown-linux-gnu - override: true - - name: Cache Dependencies uses: Swatinem/rust-cache@v1 - name: Fail on warnings run: sed -i 's,//deny_warnings_placeholder_for_ci,#![deny(warnings)],' src/lib.rs macros/src/lib.rs - - uses: actions-rs/cargo@v1 - with: - use-cross: false - command: test - args: --test tests + - name: Run cargo test + run: cargo test --test tests # Build documentation, check links docs: From 89a1b535b7a84b1adf9c179501832d02d1d04712 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Tj=C3=A4der?= Date: Fri, 16 Dec 2022 00:25:46 +0100 Subject: [PATCH 166/423] Bump swatinem/rust-cache to v2 --- .github/workflows/build.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f84fe05770..2daad1d27a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -58,7 +58,7 @@ jobs: run: sed -i 's,//deny_warnings_placeholder_for_ci,#![deny(warnings)],' src/lib.rs macros/src/lib.rs - name: Cache Dependencies - uses: Swatinem/rust-cache@v1 + uses: Swatinem/rust-cache@v2 - name: cargo check run: cargo check --target=${{ matrix.target }} @@ -75,7 +75,7 @@ jobs: run: sed -i 's,//deny_warnings_placeholder_for_ci,#![deny(warnings)],' src/lib.rs macros/src/lib.rs - name: Cache Dependencies - uses: Swatinem/rust-cache@v1 + uses: Swatinem/rust-cache@v2 - name: cargo clippy run: clippy @@ -107,7 +107,7 @@ jobs: run: rustup component add llvm-tools-preview - name: Cache Dependencies - uses: Swatinem/rust-cache@v1 + uses: Swatinem/rust-cache@v2 - name: Check the examples run: cargo check --examples --target=${{ matrix.target }} @@ -143,7 +143,7 @@ jobs: run: cargo install cargo-binutils - name: Cache Dependencies - uses: Swatinem/rust-cache@v1 + uses: Swatinem/rust-cache@v2 - name: Install QEMU run: | @@ -179,7 +179,7 @@ jobs: run: rustup target add ${{ matrix.target }} - name: Cache Dependencies - uses: Swatinem/rust-cache@v1 + uses: Swatinem/rust-cache@v2 - name: Fail on warnings run: sed -i 's,//deny_warnings_placeholder_for_ci,#![deny(warnings)],' src/lib.rs macros/src/lib.rs @@ -196,7 +196,7 @@ jobs: uses: actions/checkout@v2 - name: Cache Dependencies - uses: Swatinem/rust-cache@v1 + uses: Swatinem/rust-cache@v2 - name: Fail on warnings run: sed -i 's,//deny_warnings_placeholder_for_ci,#![deny(warnings)],' src/lib.rs macros/src/lib.rs @@ -213,7 +213,7 @@ jobs: uses: actions/checkout@v2 - name: Cache Dependencies - uses: Swatinem/rust-cache@v1 + uses: Swatinem/rust-cache@v2 - name: Fail on warnings run: sed -i 's,//deny_warnings_placeholder_for_ci,#![deny(warnings)],' src/lib.rs macros/src/lib.rs From 49f355394366e90b75040bb7cc7c52a231503dcc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Tj=C3=A4der?= Date: Fri, 16 Dec 2022 00:28:06 +0100 Subject: [PATCH 167/423] Add clippy component --- .github/workflows/build.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 2daad1d27a..b134f038c6 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -74,6 +74,9 @@ jobs: - name: Fail on warnings run: sed -i 's,//deny_warnings_placeholder_for_ci,#![deny(warnings)],' src/lib.rs macros/src/lib.rs + - name: Add Rust component clippy + run: rustup component add clippy + - name: Cache Dependencies uses: Swatinem/rust-cache@v2 From 6dcd6a564a39eda7ebf18c42a1c814a3ea053965 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Tj=C3=A4der?= Date: Fri, 16 Dec 2022 00:40:59 +0100 Subject: [PATCH 168/423] Changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7d1a9f3d5f..e18b214419 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ For each category, *Added*, *Changed*, *Fixed* add new entries at the top! ### Fixed +- Use native GHA rustup and cargo - Distinguish between thumbv8m.base and thumbv8m.main for basepri usage. ### Changed From 51f1bce0779a009a6d2e2251403b9de3f8314dda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Tj=C3=A4der?= Date: Fri, 16 Dec 2022 00:52:46 +0100 Subject: [PATCH 169/423] Forgot cargo... --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b134f038c6..bc1750e9c1 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -81,7 +81,7 @@ jobs: uses: Swatinem/rust-cache@v2 - name: cargo clippy - run: clippy + run: cargo clippy # Verify all examples, checks checkexamples: From d0b3d8b3f21d98311e08f5b70999054b6d078de8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Tj=C3=A4der?= Date: Fri, 16 Dec 2022 11:30:01 +0100 Subject: [PATCH 170/423] Remove redundant adding of rustfmt component --- .github/workflows/build.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index bc1750e9c1..16d8a284f4 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -24,8 +24,6 @@ jobs: - name: Fail on warnings run: sed -i 's,//deny_warnings_placeholder_for_ci,#![deny(warnings)],' src/lib.rs macros/src/lib.rs - - name: Add Rust component - run: rustup component add rustfmt - name: cargo fmt --check run: cargo fmt --all -- --check From f52b5fd1c4410e972ec642e331a86850c9a75ef2 Mon Sep 17 00:00:00 2001 From: Nathan Date: Fri, 16 Dec 2022 11:38:02 -0600 Subject: [PATCH 171/423] Add documentation for different targets --- book/en/src/by-example/starting_a_project.md | 5 +- book/en/src/internals/targets.md | 59 ++++++++++++++++++++ 2 files changed, 63 insertions(+), 1 deletion(-) create mode 100644 book/en/src/internals/targets.md diff --git a/book/en/src/by-example/starting_a_project.md b/book/en/src/by-example/starting_a_project.md index c916479aa8..c4f49b6ba1 100644 --- a/book/en/src/by-example/starting_a_project.md +++ b/book/en/src/by-example/starting_a_project.md @@ -1,6 +1,9 @@ # Starting a new project -A recommendation when starting a RTIC project from scratch is to follow RTIC's [`defmt-app-template`]. +A recommendation when starting a RTIC project from scratch on an ARMv7-M or ARMv8-M-main MCU is to +follow RTIC's [`defmt-app-template`]. For ARMv6-M or ARMv8-M-base, check out Section 4.? of +this book for more information on hardware and implementation differences to be aware of before +starting with RTIC. [`defmt-app-template`]: https://github.com/rtic-rs/defmt-app-template diff --git a/book/en/src/internals/targets.md b/book/en/src/internals/targets.md new file mode 100644 index 0000000000..0104cdba8c --- /dev/null +++ b/book/en/src/internals/targets.md @@ -0,0 +1,59 @@ +# Target Architecture + +While RTIC can currently target all Cortex-m devices there are some key architecure differences that +users should be aware. Namely the absence of hardware priority ceiling (BASEPRI) support in the +ARMv6-M and ARMv8-M-base architectures requires a few tweaks from RTIC to deliver the same +features. These differences result in two flavors of critical sections: priority ceiling, and source +masking. Table 1 below shows a list of Cortex-m processors and which type of critical section they +employ. + +#### *Table 1: Critical Section Implementation by Processor Architecture* + +| Processor | Architecture | Priority Ceiling | Source Masking | +| :--------- | :----------: | :--------------: | :------------: | +| Cortex-M0 | ARMv6-M | | ઙ | +| Cortex-M0+ | ARMv6-M | | ઙ | +| Cortex-M3 | ARMv7-M | ઙ | | +| Cortex-M4 | ARMv7-M | ઙ | | +| Cortex-M23 | ARMv8-M-base | | ઙ | +| Cortex-M33 | ARMv8-M-main | ઙ | | +| Cortex-M7 | ARMv7-M | ઙ | | + +## Priority Ceiling + +This implementation is covered in depth by Chapter 4.5 of this book. + +## Source Masking + +Since there is no hardware support for a priority ceiling, RTIC must instead rely on the Nested +Vectored Interrupt Controller (NVIC) present in the core architecture. Consider Figure 1 below, +showing two tasks A and B where A has higher priority but shares a resource with B. + +#### *Figure 1: Shared Resources and Source Masking* + +```text + ┌────────────────────────────────────────────────────────────────┐ + │ │ + │ │ +3 │ Pending Preempts │ +2 │ ↑- - -A- - - - -↓A─────────► │ +1 │ B───────────────────► - - - - B────────► │ +0 │Idle┌─────► Resumes ┌────────► │ + ├────┴────────────────────────────────────────────┴──────────────┤ + │ │ + └────────────────────────────────────────────────────────────────┴──► Time + t1 t2 t3 t4 +``` + +At time *t1*, task B locks the shared resource by selectively disabling all other tasks which share +the resource using the NVIC. In effect this raisis the virtual priority ceiling. Task A is one such +task that shares resources with task B. At time *t2*, task A is either spawned by task B or becomes +pending through an interrupt condition, but does not yet preempt task B even though it's priority is +greater. This is because the NVIC is preventing it from starting due to task A's source mask being +disabled. At time *t3*, task B releases the lock by re-enabling the tasks in the NVIC. Because +task A was pending and has a higher priority than task B, it immediately preempts task B and is +free to use the shared resource without risk of data race conditions. At time *t4*, task A completes +and returns the execution context to B. + +Since source masking relies on use of the NVIC, core exception sources such as HardFault, SVCall, +PendSV, and SysTick cannot share data with other tasks. From c8d60d2910137381c9e6101b92976d47e256701e Mon Sep 17 00:00:00 2001 From: n8tlarsen <96437952+n8tlarsen@users.noreply.github.com> Date: Mon, 19 Dec 2022 17:45:53 -0600 Subject: [PATCH 172/423] Improve basepri explanation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Henrik Tjäder --- book/en/src/internals/targets.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/book/en/src/internals/targets.md b/book/en/src/internals/targets.md index 0104cdba8c..65c0712534 100644 --- a/book/en/src/internals/targets.md +++ b/book/en/src/internals/targets.md @@ -1,7 +1,7 @@ # Target Architecture While RTIC can currently target all Cortex-m devices there are some key architecure differences that -users should be aware. Namely the absence of hardware priority ceiling (BASEPRI) support in the +users should be aware of. Namely the absence of Base Priority Mask Register (`BASEPRI`) which lends itself exceptionally well to the hardware priority ceiling support used in RTIC, in the ARMv6-M and ARMv8-M-base architectures requires a few tweaks from RTIC to deliver the same features. These differences result in two flavors of critical sections: priority ceiling, and source masking. Table 1 below shows a list of Cortex-m processors and which type of critical section they From 60132495d96f99af525df0122989f08b5206e854 Mon Sep 17 00:00:00 2001 From: n8tlarsen <96437952+n8tlarsen@users.noreply.github.com> Date: Mon, 19 Dec 2022 17:48:52 -0600 Subject: [PATCH 173/423] Expand lock explanation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Henrik Tjäder --- book/en/src/internals/targets.md | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/book/en/src/internals/targets.md b/book/en/src/internals/targets.md index 65c0712534..bdfb24bb2d 100644 --- a/book/en/src/internals/targets.md +++ b/book/en/src/internals/targets.md @@ -2,10 +2,13 @@ While RTIC can currently target all Cortex-m devices there are some key architecure differences that users should be aware of. Namely the absence of Base Priority Mask Register (`BASEPRI`) which lends itself exceptionally well to the hardware priority ceiling support used in RTIC, in the -ARMv6-M and ARMv8-M-base architectures requires a few tweaks from RTIC to deliver the same -features. These differences result in two flavors of critical sections: priority ceiling, and source -masking. Table 1 below shows a list of Cortex-m processors and which type of critical section they -employ. +ARMv6-M and ARMv8-M-base architectures, which forces RTIC to use source masking instead. For each implementation of lock and a detailed commentary of pros and cons, see the implementation of [lock in src/export.rs][src_export]. + +[src_export]: https://github.com/rtic-rs/cortex-m-rtic/blob/master/src/export.rs + +These differences influence how critical sections are realized, but functionality should be the same except that ARMv6-M/ARMv8-M-base cannot have tasks with shared resources bound to exception handlers, as these cannot be masked in hardware. + +Table 1 below shows a list of Cortex-m processors and which type of critical section they employ. #### *Table 1: Critical Section Implementation by Processor Architecture* From 70ebcc409ff4e34764337a978882a25f34484c4f Mon Sep 17 00:00:00 2001 From: n8tlarsen <96437952+n8tlarsen@users.noreply.github.com> Date: Mon, 19 Dec 2022 18:16:25 -0600 Subject: [PATCH 174/423] Clarify BASEPRI and NVIC interaction MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Henrik Tjäder --- book/en/src/internals/targets.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/book/en/src/internals/targets.md b/book/en/src/internals/targets.md index bdfb24bb2d..606191d20f 100644 --- a/book/en/src/internals/targets.md +++ b/book/en/src/internals/targets.md @@ -28,8 +28,10 @@ This implementation is covered in depth by Chapter 4.5 of this book. ## Source Masking -Since there is no hardware support for a priority ceiling, RTIC must instead rely on the Nested -Vectored Interrupt Controller (NVIC) present in the core architecture. Consider Figure 1 below, +Without a `BASEPRI` register which allows for directly setting a priority ceiling in the Nested +Vectored Interrupt Controller (NVIC), RTIC must instead rely on disabling (masking) interrupts. + +Consider Figure 1 below, showing two tasks A and B where A has higher priority but shares a resource with B. #### *Figure 1: Shared Resources and Source Masking* From c688b601f1b447d51398ae3caac8ed5155cdbeb1 Mon Sep 17 00:00:00 2001 From: n8tlarsen <96437952+n8tlarsen@users.noreply.github.com> Date: Mon, 19 Dec 2022 18:17:06 -0600 Subject: [PATCH 175/423] typo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Henrik Tjäder --- book/en/src/internals/targets.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/book/en/src/internals/targets.md b/book/en/src/internals/targets.md index 606191d20f..4972e5fcb3 100644 --- a/book/en/src/internals/targets.md +++ b/book/en/src/internals/targets.md @@ -51,7 +51,7 @@ showing two tasks A and B where A has higher priority but shares a resource with ``` At time *t1*, task B locks the shared resource by selectively disabling all other tasks which share -the resource using the NVIC. In effect this raisis the virtual priority ceiling. Task A is one such +the resource using the NVIC. In effect this raises the virtual priority ceiling. Task A is one such task that shares resources with task B. At time *t2*, task A is either spawned by task B or becomes pending through an interrupt condition, but does not yet preempt task B even though it's priority is greater. This is because the NVIC is preventing it from starting due to task A's source mask being From dd1fb68f425d4bc4712b6c6bfe8313d6270017ff Mon Sep 17 00:00:00 2001 From: n8tlarsen <96437952+n8tlarsen@users.noreply.github.com> Date: Mon, 19 Dec 2022 18:18:04 -0600 Subject: [PATCH 176/423] Possessive its MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Henrik Tjäder --- book/en/src/internals/targets.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/book/en/src/internals/targets.md b/book/en/src/internals/targets.md index 4972e5fcb3..da55937157 100644 --- a/book/en/src/internals/targets.md +++ b/book/en/src/internals/targets.md @@ -53,7 +53,7 @@ showing two tasks A and B where A has higher priority but shares a resource with At time *t1*, task B locks the shared resource by selectively disabling all other tasks which share the resource using the NVIC. In effect this raises the virtual priority ceiling. Task A is one such task that shares resources with task B. At time *t2*, task A is either spawned by task B or becomes -pending through an interrupt condition, but does not yet preempt task B even though it's priority is +pending through an interrupt condition, but does not yet preempt task B even though its priority is greater. This is because the NVIC is preventing it from starting due to task A's source mask being disabled. At time *t3*, task B releases the lock by re-enabling the tasks in the NVIC. Because task A was pending and has a higher priority than task B, it immediately preempts task B and is From 5fce80b24322448ca702332a550f967b6e8a8b07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Tj=C3=A4der?= Date: Wed, 21 Dec 2022 20:26:32 +0100 Subject: [PATCH 177/423] Docs: fancier meeting redirect --- .github/workflows/build.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 16d8a284f4..3de95864bc 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -429,6 +429,8 @@ jobs: # Redirect rtic.rs/meeting/index.html to hackmd mkdir $td/meeting sed "s|URL|https://hackmd.io/c_mFUZL-Q2C6614MlrrxOg|g" redirect.html > $td/meeting/index.html + sed -i "s|Page Redirection|RTIC Meeting|" $td/meeting/index.html + sed -i "s|If you|Redirecting to RTIC HackMD. If you|" $td/meeting/index.html # Redirect the main site to the stable release sed "s|URL|$stable|g" redirect.html > $td/index.html From 171e915696ef9e065c22724e06a756f47fddc544 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Tj=C3=A4der?= Date: Wed, 21 Dec 2022 20:27:58 +0100 Subject: [PATCH 178/423] Add to changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e18b214419..6988f7ecff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,8 @@ For each category, *Added*, *Changed*, *Fixed* add new entries at the top! ### Changed +- Tuned redirect message for rtic.rs/meeting + ## [v1.1.3] - 2022-06-23 ### Added From e3808744abec9a52a979292745e38b34b4bec9d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Tj=C3=A4der?= Date: Wed, 21 Dec 2022 20:55:30 +0100 Subject: [PATCH 179/423] CI: Update checkout from v2 to v3 --- .github/workflows/build.yml | 24 ++++++++++++------------ CHANGELOG.md | 1 + 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 3de95864bc..cf621abdf6 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -19,7 +19,7 @@ jobs: runs-on: ubuntu-22.04 steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Fail on warnings run: sed -i 's,//deny_warnings_placeholder_for_ci,#![deny(warnings)],' src/lib.rs macros/src/lib.rs @@ -42,7 +42,7 @@ jobs: - stable steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Install Rust ${{ matrix.toolchain }} run: | @@ -67,7 +67,7 @@ jobs: runs-on: ubuntu-22.04 steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Fail on warnings run: sed -i 's,//deny_warnings_placeholder_for_ci,#![deny(warnings)],' src/lib.rs macros/src/lib.rs @@ -94,7 +94,7 @@ jobs: - stable steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Install Rust ${{ matrix.toolchain }} run: | @@ -126,7 +126,7 @@ jobs: - stable steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Install Rust ${{ matrix.toolchain }} run: | @@ -169,7 +169,7 @@ jobs: - stable steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Install Rust ${{ matrix.toolchain }} run: | @@ -194,7 +194,7 @@ jobs: runs-on: ubuntu-22.04 steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Cache Dependencies uses: Swatinem/rust-cache@v2 @@ -211,7 +211,7 @@ jobs: runs-on: ubuntu-22.04 steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Cache Dependencies uses: Swatinem/rust-cache@v2 @@ -228,7 +228,7 @@ jobs: runs-on: ubuntu-22.04 steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Cache pip installed linkchecker uses: actions/cache@v2 @@ -275,7 +275,7 @@ jobs: runs-on: ubuntu-22.04 steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Set up Python 3.x uses: actions/setup-python@v2 with: @@ -337,7 +337,7 @@ jobs: # Only run this when pushing to master branch if: github.ref == 'refs/heads/master' steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Get crate version and print output branch release/vX id: crateversionbranch @@ -369,7 +369,7 @@ jobs: # Only run this when pushing to master branch if: github.ref == 'refs/heads/master' steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Set up Python 3.x uses: actions/setup-python@v2 diff --git a/CHANGELOG.md b/CHANGELOG.md index 6988f7ecff..253796f3b8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ For each category, *Added*, *Changed*, *Fixed* add new entries at the top! ### Changed +- CI: Updated to checkout@v3 - Tuned redirect message for rtic.rs/meeting ## [v1.1.3] - 2022-06-23 From 9884bcf312bf0da808dac6eb76d9059cd75877bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Tj=C3=A4der?= Date: Wed, 21 Dec 2022 21:11:01 +0100 Subject: [PATCH 180/423] CI: Run example tests on thumbv8m.* --- .github/workflows/build.yml | 2 ++ CHANGELOG.md | 1 + 2 files changed, 3 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index cf621abdf6..8182e7eda5 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -90,6 +90,8 @@ jobs: target: - thumbv7m-none-eabi - thumbv6m-none-eabi + - thumbv8m.base-none-eabi + - thumbv8m.main-none-eabi toolchain: - stable steps: diff --git a/CHANGELOG.md b/CHANGELOG.md index 253796f3b8..0dc7353b13 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ For each category, *Added*, *Changed*, *Fixed* add new entries at the top! ### Added +- CI: Check examples also for thumbv8.{base,main} - Allow custom `link_section` attributes for late resources ### Fixed From 4bae1a5f4b6826fe6b9db1a07d08e84a8cbfa82c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Tj=C3=A4der?= Date: Wed, 21 Dec 2022 21:39:06 +0100 Subject: [PATCH 181/423] CI: Updated to setup-python@v4 --- .github/workflows/build.yml | 12 +++--------- CHANGELOG.md | 1 + 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 8182e7eda5..ed7c07304b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -241,12 +241,10 @@ jobs: ${{ runner.os }}-pip- - name: Set up Python 3.x - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: # Semantic version range syntax or exact version of a Python version python-version: '3.x' - # Optional - x64 or x86 architecture, defaults to x64 - architecture: 'x64' # You can test your matrix by printing the current Python version - name: Display Python version @@ -279,12 +277,10 @@ jobs: - name: Checkout uses: actions/checkout@v3 - name: Set up Python 3.x - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: # Semantic version range syntax or exact version of a Python version python-version: '3.x' - # Optional - x64 or x86 architecture, defaults to x64 - architecture: 'x64' # You can test your matrix by printing the current Python version - name: Display Python version @@ -374,12 +370,10 @@ jobs: - uses: actions/checkout@v3 - name: Set up Python 3.x - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: # Semantic version range syntax or exact version of a Python version python-version: '3.x' - # Optional - x64 or x86 architecture, defaults to x64 - architecture: 'x64' # You can test your matrix by printing the current Python version - name: Display Python version diff --git a/CHANGELOG.md b/CHANGELOG.md index 0dc7353b13..78a725380a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ For each category, *Added*, *Changed*, *Fixed* add new entries at the top! ### Changed +- CI: Updated to setup-python@v4 - CI: Updated to checkout@v3 - Tuned redirect message for rtic.rs/meeting From 27d41c1a3867c4a509e8c153bfc2e013f280ce46 Mon Sep 17 00:00:00 2001 From: n8tlarsen <96437952+n8tlarsen@users.noreply.github.com> Date: Wed, 21 Dec 2022 17:02:11 -0600 Subject: [PATCH 182/423] Revert recommended starting template MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Henrik Tjäder --- book/en/src/by-example/starting_a_project.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/book/en/src/by-example/starting_a_project.md b/book/en/src/by-example/starting_a_project.md index c4f49b6ba1..ccb0083c07 100644 --- a/book/en/src/by-example/starting_a_project.md +++ b/book/en/src/by-example/starting_a_project.md @@ -1,9 +1,9 @@ # Starting a new project -A recommendation when starting a RTIC project from scratch on an ARMv7-M or ARMv8-M-main MCU is to -follow RTIC's [`defmt-app-template`]. For ARMv6-M or ARMv8-M-base, check out Section 4.? of -this book for more information on hardware and implementation differences to be aware of before -starting with RTIC. +A recommendation when starting a RTIC project from scratch is to +follow RTIC's [`defmt-app-template`]. + +If you are targeting ARMv6-M or ARMv8-M-base architecture, check out the section [Target Architecture](../internals/targets.md) for more information on hardware limitations to be aware of. [`defmt-app-template`]: https://github.com/rtic-rs/defmt-app-template From 7848190fe6d307675e3702e9ef794bdebb948290 Mon Sep 17 00:00:00 2001 From: Ralph Ursprung Date: Wed, 28 Dec 2022 18:55:20 +0100 Subject: [PATCH 183/423] enable GitHub Dependabot this ensures that the dependencies are kept up to date. see [the docs][] for further information. [the docs]: https://docs.github.com/en/code-security/dependabot/dependabot-version-updates --- .github/dependabot.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000000..d062b443b9 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,10 @@ +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + - package-ecosystem: "cargo" + directory: "/" + schedule: + interval: "weekly" From 80b81ef122e68744744df9e866fec3263a0d7e11 Mon Sep 17 00:00:00 2001 From: Nathan Date: Thu, 29 Dec 2022 10:20:46 -0600 Subject: [PATCH 184/423] Update example with SRP priority ceiling --- book/en/src/internals/targets.md | 41 +++++++++++++++++++------------- 1 file changed, 24 insertions(+), 17 deletions(-) diff --git a/book/en/src/internals/targets.md b/book/en/src/internals/targets.md index da55937157..dfb68deaef 100644 --- a/book/en/src/internals/targets.md +++ b/book/en/src/internals/targets.md @@ -1,12 +1,17 @@ # Target Architecture While RTIC can currently target all Cortex-m devices there are some key architecure differences that -users should be aware of. Namely the absence of Base Priority Mask Register (`BASEPRI`) which lends itself exceptionally well to the hardware priority ceiling support used in RTIC, in the -ARMv6-M and ARMv8-M-base architectures, which forces RTIC to use source masking instead. For each implementation of lock and a detailed commentary of pros and cons, see the implementation of [lock in src/export.rs][src_export]. +users should be aware of. Namely the absence of Base Priority Mask Register (`BASEPRI`) which lends +itself exceptionally well to the hardware priority ceiling support used in RTIC, in the ARMv6-M and +ARMv8-M-base architectures, which forces RTIC to use source masking instead. For each implementation +of lock and a detailed commentary of pros and cons, see the implementation of +[lock in src/export.rs][src_export]. [src_export]: https://github.com/rtic-rs/cortex-m-rtic/blob/master/src/export.rs -These differences influence how critical sections are realized, but functionality should be the same except that ARMv6-M/ARMv8-M-base cannot have tasks with shared resources bound to exception handlers, as these cannot be masked in hardware. +These differences influence how critical sections are realized, but functionality should be the same +except that ARMv6-M/ARMv8-M-base cannot have tasks with shared resources bound to exception +handlers, as these cannot be masked in hardware. Table 1 below shows a list of Cortex-m processors and which type of critical section they employ. @@ -18,21 +23,20 @@ Table 1 below shows a list of Cortex-m processors and which type of critical sec | Cortex-M0+ | ARMv6-M | | ઙ | | Cortex-M3 | ARMv7-M | ઙ | | | Cortex-M4 | ARMv7-M | ઙ | | +| Cortex-M7 | ARMv7-M | ઙ | | | Cortex-M23 | ARMv8-M-base | | ઙ | | Cortex-M33 | ARMv8-M-main | ઙ | | -| Cortex-M7 | ARMv7-M | ઙ | | ## Priority Ceiling -This implementation is covered in depth by Chapter 4.5 of this book. +This implementation is covered in depth by the [Critical Sections][critical_sections] page of this book. ## Source Masking Without a `BASEPRI` register which allows for directly setting a priority ceiling in the Nested Vectored Interrupt Controller (NVIC), RTIC must instead rely on disabling (masking) interrupts. - -Consider Figure 1 below, -showing two tasks A and B where A has higher priority but shares a resource with B. +Consider Figure 1 below, showing two tasks A and B where A has higher priority but shares a resource +with B. #### *Figure 1: Shared Resources and Source Masking* @@ -50,15 +54,18 @@ showing two tasks A and B where A has higher priority but shares a resource with t1 t2 t3 t4 ``` -At time *t1*, task B locks the shared resource by selectively disabling all other tasks which share -the resource using the NVIC. In effect this raises the virtual priority ceiling. Task A is one such -task that shares resources with task B. At time *t2*, task A is either spawned by task B or becomes -pending through an interrupt condition, but does not yet preempt task B even though its priority is -greater. This is because the NVIC is preventing it from starting due to task A's source mask being -disabled. At time *t3*, task B releases the lock by re-enabling the tasks in the NVIC. Because -task A was pending and has a higher priority than task B, it immediately preempts task B and is -free to use the shared resource without risk of data race conditions. At time *t4*, task A completes -and returns the execution context to B. +At time *t1*, task B locks the shared resource by selectively disabling (using the NVIC) all other +tasks which have a priority equal to or less than any task which shares resouces with B. In effect +this creates a virtual priority ceiling, miroring the `BASEPRI` approach described in the +[Critical Sections][critical_Sections] page. Task A is one such task that shares resources with +task B. At time *t2*, task A is either spawned by task B or becomes pending through an interrupt +condition, but does not yet preempt task B even though its priority is greater. This is because the +NVIC is preventing it from starting due to task A being being disabled. At time *t3*, task B +releases the lock by re-enabling the tasks in the NVIC. Because task A was pending and has a higher +priority than task B, it immediately preempts task B and is free to use the shared resource without +risk of data race conditions. At time *t4*, task A completes and returns the execution context to B. Since source masking relies on use of the NVIC, core exception sources such as HardFault, SVCall, PendSV, and SysTick cannot share data with other tasks. + +[critical_sections]: https://github.com/rtic-rs/cortex-m-rtic/blob/master/book/en/src/internals/critical-sections.md From c5f55519ba24dc219c33f4214bdf529eae81e774 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 3 Jan 2023 21:09:02 +0000 Subject: [PATCH 185/423] Bump actions/checkout from 1 to 3 Bumps [actions/checkout](https://github.com/actions/checkout) from 1 to 3. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v1...v3) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/audit.yaml | 2 +- .github/workflows/changelog.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/audit.yaml b/.github/workflows/audit.yaml index abf0898b81..1d6069d1bc 100644 --- a/.github/workflows/audit.yaml +++ b/.github/workflows/audit.yaml @@ -6,7 +6,7 @@ jobs: audit: runs-on: ubuntu-20.04 steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v3 - uses: actions-rs/audit-check@v1 with: token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/changelog.yml b/.github/workflows/changelog.yml index ccf6eb9103..74b821dabc 100644 --- a/.github/workflows/changelog.yml +++ b/.github/workflows/changelog.yml @@ -16,7 +16,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout sources - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Check that changelog updated uses: dangoslen/changelog-enforcer@v3 From 867d010524e5f530f7c72e4426c5e67491d2a12f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 3 Jan 2023 21:09:05 +0000 Subject: [PATCH 186/423] Bump everlytic/branch-merge from 1.1.2 to 1.1.5 Bumps [everlytic/branch-merge](https://github.com/everlytic/branch-merge) from 1.1.2 to 1.1.5. - [Release notes](https://github.com/everlytic/branch-merge/releases) - [Commits](https://github.com/everlytic/branch-merge/compare/1.1.2...1.1.5) --- updated-dependencies: - dependency-name: everlytic/branch-merge dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ed7c07304b..51fd902a1e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -347,7 +347,7 @@ jobs: echo "versionmajor=$VERSIONMAJOR" >> $GITHUB_ENV echo "version=$VERSION" >> $GITHUB_ENV - - uses: everlytic/branch-merge@1.1.2 + - uses: everlytic/branch-merge@1.1.5 with: github_token: ${{ github.token }} source_ref: 'master' From f7863bd71dec1ebea9b0e038fe79633bee0b12c2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 3 Jan 2023 21:09:11 +0000 Subject: [PATCH 187/423] Bump actions/cache from 2 to 3 Bumps [actions/cache](https://github.com/actions/cache) from 2 to 3. - [Release notes](https://github.com/actions/cache/releases) - [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md) - [Commits](https://github.com/actions/cache/compare/v2...v3) --- updated-dependencies: - dependency-name: actions/cache dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ed7c07304b..c358c2c054 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -233,7 +233,7 @@ jobs: uses: actions/checkout@v3 - name: Cache pip installed linkchecker - uses: actions/cache@v2 + uses: actions/cache@v3 with: path: ~/.cache/pip key: ${{ runner.os }}-pip From 3d41b101bdb4e5865f04058349acc37588f1de71 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 3 Jan 2023 21:09:35 +0000 Subject: [PATCH 188/423] Update panic-semihosting requirement from 0.5.2 to 0.6.0 Updates the requirements on [panic-semihosting](https://github.com/rust-embedded/cortex-m) to permit the latest version. - [Release notes](https://github.com/rust-embedded/cortex-m/releases) - [Changelog](https://github.com/rust-embedded/cortex-m/blob/master/CHANGELOG.md) - [Commits](https://github.com/rust-embedded/cortex-m/compare/v0.5.2...v0.6.0) --- updated-dependencies: - dependency-name: panic-semihosting dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 61f8e56eb2..2cd3fe1f92 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -37,7 +37,7 @@ systick-monotonic = "1.0.0" [dev-dependencies.panic-semihosting] features = ["exit"] -version = "0.5.2" +version = "0.6.0" [target.x86_64-unknown-linux-gnu.dev-dependencies] trybuild = "1" From 67cccbd4819c4d874780a6b388d7f10c1c8031b2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 3 Jan 2023 21:09:44 +0000 Subject: [PATCH 189/423] Update cortex-m-semihosting requirement from 0.3.3 to 0.5.0 Updates the requirements on [cortex-m-semihosting](https://github.com/rust-embedded/cortex-m) to permit the latest version. - [Release notes](https://github.com/rust-embedded/cortex-m/releases) - [Changelog](https://github.com/rust-embedded/cortex-m/blob/master/CHANGELOG.md) - [Commits](https://github.com/rust-embedded/cortex-m/compare/c-m-sh-v0.3.5...v0.5.0) --- updated-dependencies: - dependency-name: cortex-m-semihosting dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 61f8e56eb2..b5529e2dac 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,7 +32,7 @@ version_check = "0.9" [dev-dependencies] lm3s6965 = "0.1.3" -cortex-m-semihosting = "0.3.3" +cortex-m-semihosting = "0.5.0" systick-monotonic = "1.0.0" [dev-dependencies.panic-semihosting] From 92194c680de9537d215ae3480f6d046969dc1c2a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 3 Jan 2023 21:09:50 +0000 Subject: [PATCH 190/423] Update os_pipe requirement from 0.9.2 to 1.1.2 Updates the requirements on [os_pipe](https://github.com/oconnor663/os_pipe.rs) to permit the latest version. - [Release notes](https://github.com/oconnor663/os_pipe.rs/releases) - [Commits](https://github.com/oconnor663/os_pipe.rs/compare/0.9.2...1.1.2) --- updated-dependencies: - dependency-name: os_pipe dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- xtask/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xtask/Cargo.toml b/xtask/Cargo.toml index 33e6b3ad60..f1c468ed3d 100644 --- a/xtask/Cargo.toml +++ b/xtask/Cargo.toml @@ -5,5 +5,5 @@ edition = "2018" [dependencies] anyhow = "1.0.43" -os_pipe = "0.9.2" +os_pipe = "1.1.2" structopt = "0.3.22" From 5bd109f1010715b615725a3a601387e11e8db3ce Mon Sep 17 00:00:00 2001 From: Ralph Ursprung Date: Wed, 4 Jan 2023 08:17:28 +0100 Subject: [PATCH 191/423] dependabot: set "skip-changelog" label solves #682 --- .github/dependabot.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index d062b443b9..c3fda54f80 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -4,7 +4,11 @@ updates: directory: "/" schedule: interval: "weekly" + labels: + - "skip-changelog" - package-ecosystem: "cargo" directory: "/" schedule: interval: "weekly" + labels: + - "skip-changelog" From 9c68b876f17b72cca68491bd31b47481e5f90f2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Tj=C3=A4der?= Date: Wed, 4 Jan 2023 21:20:24 +0100 Subject: [PATCH 192/423] Enable the targets chapter --- book/en/src/SUMMARY.md | 1 + 1 file changed, 1 insertion(+) diff --git a/book/en/src/SUMMARY.md b/book/en/src/SUMMARY.md index 045036eab5..853f3a5319 100644 --- a/book/en/src/SUMMARY.md +++ b/book/en/src/SUMMARY.md @@ -29,6 +29,7 @@ - [v0.4.x to v0.5.x](./migration/migration_v4.md) - [RTFM to RTIC](./migration/migration_rtic.md) - [Under the hood](./internals.md) + - [Cortex-M architectures](./internals/targets.md) From baffeaca227e36ccad828a959492b6ccb1c3207a Mon Sep 17 00:00:00 2001 From: Nathan Date: Wed, 4 Jan 2023 17:18:48 -0600 Subject: [PATCH 193/423] =?UTF-8?q?Fix=20Unicode=20=E2=9C=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- book/en/src/internals/targets.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/book/en/src/internals/targets.md b/book/en/src/internals/targets.md index dfb68deaef..04fd592203 100644 --- a/book/en/src/internals/targets.md +++ b/book/en/src/internals/targets.md @@ -19,13 +19,13 @@ Table 1 below shows a list of Cortex-m processors and which type of critical sec | Processor | Architecture | Priority Ceiling | Source Masking | | :--------- | :----------: | :--------------: | :------------: | -| Cortex-M0 | ARMv6-M | | ઙ | -| Cortex-M0+ | ARMv6-M | | ઙ | -| Cortex-M3 | ARMv7-M | ઙ | | -| Cortex-M4 | ARMv7-M | ઙ | | -| Cortex-M7 | ARMv7-M | ઙ | | -| Cortex-M23 | ARMv8-M-base | | ઙ | -| Cortex-M33 | ARMv8-M-main | ઙ | | +| Cortex-M0 | ARMv6-M | | ✓ | +| Cortex-M0+ | ARMv6-M | | ✓ | +| Cortex-M3 | ARMv7-M | ✓ | | +| Cortex-M4 | ARMv7-M | ✓ | | +| Cortex-M7 | ARMv7-M | ✓ | | +| Cortex-M23 | ARMv8-M-base | | ✓ | +| Cortex-M33 | ARMv8-M-main | ✓ | | ## Priority Ceiling From 1fe587c5160b9e99bbb67644318cb9e5c9c56b4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Tj=C3=A4der?= Date: Wed, 11 Jan 2023 21:33:44 +0100 Subject: [PATCH 194/423] Remove unwrap() from hprintln!() sd 'hprintln(.*).unwrap\(\)' 'hprintln' (fd -e rs .) --- examples/binds.rs | 4 ++-- examples/capacity.rs | 4 ++-- examples/complex.rs | 2 +- examples/destructure.rs | 4 ++-- examples/extern_binds.rs | 4 ++-- examples/extern_spawn.rs | 2 +- examples/generics.rs | 6 +++--- examples/hardware.rs | 4 ++-- examples/idle-wfi.rs | 4 ++-- examples/idle.rs | 4 ++-- examples/init.rs | 2 +- examples/locals.rs | 6 +++--- examples/lock-free.rs | 4 ++-- examples/lock.rs | 10 +++++----- examples/message.rs | 6 +++--- examples/message_passing.rs | 2 +- examples/multilock.rs | 2 +- examples/only-shared-access.rs | 4 ++-- examples/pool.rs | 4 ++-- examples/preempt.rs | 10 +++++----- examples/ramfunc.rs | 2 +- examples/resource-user-struct.rs | 4 ++-- examples/shared.rs | 2 +- examples/spawn.rs | 4 ++-- examples/static.rs | 2 +- examples/task.rs | 10 +++++----- 26 files changed, 56 insertions(+), 56 deletions(-) diff --git a/examples/binds.rs b/examples/binds.rs index 56565cbec9..04b8fad19e 100644 --- a/examples/binds.rs +++ b/examples/binds.rs @@ -23,14 +23,14 @@ mod app { fn init(_: init::Context) -> (Shared, Local, init::Monotonics) { rtic::pend(Interrupt::UART0); - hprintln!("init").unwrap(); + hprintln!("init"); (Shared {}, Local {}, init::Monotonics()) } #[idle] fn idle(_: idle::Context) -> ! { - hprintln!("idle").unwrap(); + hprintln!("idle"); rtic::pend(Interrupt::UART0); diff --git a/examples/capacity.rs b/examples/capacity.rs index a617269869..e625249af8 100644 --- a/examples/capacity.rs +++ b/examples/capacity.rs @@ -37,12 +37,12 @@ mod app { #[task(capacity = 4)] fn foo(_: foo::Context, x: u32) { - hprintln!("foo({})", x).unwrap(); + hprintln!("foo({})", x); } #[task] fn bar(_: bar::Context) { - hprintln!("bar").unwrap(); + hprintln!("bar"); debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator } diff --git a/examples/complex.rs b/examples/complex.rs index e5cf6dbea3..653198746b 100644 --- a/examples/complex.rs +++ b/examples/complex.rs @@ -25,7 +25,7 @@ mod app { #[init] fn init(_: init::Context) -> (Shared, Local, init::Monotonics) { - hprintln!("init").unwrap(); + hprintln!("init"); ( Shared { diff --git a/examples/destructure.rs b/examples/destructure.rs index 6019c225cc..fd577a6c9c 100644 --- a/examples/destructure.rs +++ b/examples/destructure.rs @@ -42,7 +42,7 @@ mod app { let b = cx.shared.b; let c = cx.shared.c; - hprintln!("foo: a = {}, b = {}, c = {}", a, b, c).unwrap(); + hprintln!("foo: a = {}, b = {}, c = {}", a, b, c); } // De-structure-ing syntax @@ -50,6 +50,6 @@ mod app { fn bar(cx: bar::Context) { let bar::SharedResources { a, b, c } = cx.shared; - hprintln!("bar: a = {}, b = {}, c = {}", a, b, c).unwrap(); + hprintln!("bar: a = {}, b = {}, c = {}", a, b, c); } } diff --git a/examples/extern_binds.rs b/examples/extern_binds.rs index 4dc6633c5d..b6df2fb257 100644 --- a/examples/extern_binds.rs +++ b/examples/extern_binds.rs @@ -29,14 +29,14 @@ mod app { fn init(_: init::Context) -> (Shared, Local, init::Monotonics) { rtic::pend(Interrupt::UART0); - hprintln!("init").unwrap(); + hprintln!("init"); (Shared {}, Local {}, init::Monotonics()) } #[idle] fn idle(_: idle::Context) -> ! { - hprintln!("idle").unwrap(); + hprintln!("idle"); rtic::pend(Interrupt::UART0); diff --git a/examples/extern_spawn.rs b/examples/extern_spawn.rs index 7f9b5a5f9b..2eec2dbc49 100644 --- a/examples/extern_spawn.rs +++ b/examples/extern_spawn.rs @@ -10,7 +10,7 @@ use panic_semihosting as _; // Free function implementing the spawnable task `foo`. fn foo(_c: app::foo::Context, x: i32, y: u32) { - hprintln!("foo {}, {}", x, y).unwrap(); + hprintln!("foo {}, {}", x, y); if x == 2 { debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator } diff --git a/examples/generics.rs b/examples/generics.rs index 72b861ba91..f9a6aacfc8 100644 --- a/examples/generics.rs +++ b/examples/generics.rs @@ -32,7 +32,7 @@ mod app { #[task(binds = UART0, shared = [shared], local = [state: u32 = 0])] fn uart0(c: uart0::Context) { - hprintln!("UART0(STATE = {})", *c.local.state).unwrap(); + hprintln!("UART0(STATE = {})", *c.local.state); // second argument has type `shared::shared` super::advance(c.local.state, c.shared.shared); @@ -44,7 +44,7 @@ mod app { #[task(binds = UART1, priority = 2, shared = [shared], local = [state: u32 = 0])] fn uart1(c: uart1::Context) { - hprintln!("UART1(STATE = {})", *c.local.state).unwrap(); + hprintln!("UART1(STATE = {})", *c.local.state); // second argument has type `shared::shared` super::advance(c.local.state, c.shared.shared); @@ -61,5 +61,5 @@ fn advance(state: &mut u32, mut shared: impl Mutex) { (old, *shared) }); - hprintln!("shared: {} -> {}", old, new).unwrap(); + hprintln!("shared: {} -> {}", old, new); } diff --git a/examples/hardware.rs b/examples/hardware.rs index 60632247fb..22cf5d928d 100644 --- a/examples/hardware.rs +++ b/examples/hardware.rs @@ -24,7 +24,7 @@ mod app { // `init` returns because interrupts are disabled rtic::pend(Interrupt::UART0); // equivalent to NVIC::pend - hprintln!("init").unwrap(); + hprintln!("init"); (Shared {}, Local {}, init::Monotonics()) } @@ -33,7 +33,7 @@ mod app { fn idle(_: idle::Context) -> ! { // interrupts are enabled again; the `UART0` handler runs at this point - hprintln!("idle").unwrap(); + hprintln!("idle"); rtic::pend(Interrupt::UART0); diff --git a/examples/idle-wfi.rs b/examples/idle-wfi.rs index 4a8a8dee2b..42fd7b49e0 100644 --- a/examples/idle-wfi.rs +++ b/examples/idle-wfi.rs @@ -19,7 +19,7 @@ mod app { #[init] fn init(mut cx: init::Context) -> (Shared, Local, init::Monotonics) { - hprintln!("init").unwrap(); + hprintln!("init"); // Set the ARM SLEEPONEXIT bit to go to sleep after handling interrupts // See https://developer.arm.com/docs/100737/0100/power-management/sleep-mode/sleep-on-exit-bit @@ -33,7 +33,7 @@ mod app { // Locals in idle have lifetime 'static let _x: &'static mut u32 = cx.local.x; - hprintln!("idle").unwrap(); + hprintln!("idle"); debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator diff --git a/examples/idle.rs b/examples/idle.rs index 55d6b15352..ad04fd68e4 100644 --- a/examples/idle.rs +++ b/examples/idle.rs @@ -19,7 +19,7 @@ mod app { #[init] fn init(_: init::Context) -> (Shared, Local, init::Monotonics) { - hprintln!("init").unwrap(); + hprintln!("init"); (Shared {}, Local {}, init::Monotonics()) } @@ -29,7 +29,7 @@ mod app { // Locals in idle have lifetime 'static let _x: &'static mut u32 = cx.local.x; - hprintln!("idle").unwrap(); + hprintln!("idle"); debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator diff --git a/examples/init.rs b/examples/init.rs index b8a5bc5b98..7a10149692 100644 --- a/examples/init.rs +++ b/examples/init.rs @@ -32,7 +32,7 @@ mod app { // to indicate that this is a critical seciton let _cs_token: bare_metal::CriticalSection = cx.cs; - hprintln!("init").unwrap(); + hprintln!("init"); debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator diff --git a/examples/locals.rs b/examples/locals.rs index aa5d0fee30..1889775316 100644 --- a/examples/locals.rs +++ b/examples/locals.rs @@ -45,7 +45,7 @@ mod app { let local_to_idle = cx.local.local_to_idle; *local_to_idle += 1; - hprintln!("idle: local_to_idle = {}", local_to_idle).unwrap(); + hprintln!("idle: local_to_idle = {}", local_to_idle); debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator @@ -69,7 +69,7 @@ mod app { // error: no `local_to_bar` field in `foo::LocalResources` // cx.local.local_to_bar += 1; - hprintln!("foo: local_to_foo = {}", local_to_foo).unwrap(); + hprintln!("foo: local_to_foo = {}", local_to_foo); } // `local_to_bar` can only be accessed from this context @@ -81,6 +81,6 @@ mod app { // error: no `local_to_foo` field in `bar::LocalResources` // cx.local.local_to_foo += 1; - hprintln!("bar: local_to_bar = {}", local_to_bar).unwrap(); + hprintln!("bar: local_to_bar = {}", local_to_bar); } } diff --git a/examples/lock-free.rs b/examples/lock-free.rs index ea6ff1bf37..32853e44ed 100644 --- a/examples/lock-free.rs +++ b/examples/lock-free.rs @@ -33,7 +33,7 @@ mod app { *c.shared.counter += 1; // <- no lock API required let counter = *c.shared.counter; - hprintln!(" foo = {}", counter).unwrap(); + hprintln!(" foo = {}", counter); } #[task(shared = [counter])] // <- same priority @@ -42,7 +42,7 @@ mod app { *c.shared.counter += 1; // <- no lock API required let counter = *c.shared.counter; - hprintln!(" bar = {}", counter).unwrap(); + hprintln!(" bar = {}", counter); debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator } diff --git a/examples/lock.rs b/examples/lock.rs index f1a16968ce..16f3b33812 100644 --- a/examples/lock.rs +++ b/examples/lock.rs @@ -29,7 +29,7 @@ mod app { // when omitted priority is assumed to be `1` #[task(shared = [shared])] fn foo(mut c: foo::Context) { - hprintln!("A").unwrap(); + hprintln!("A"); // the lower priority task requires a critical section to access the data c.shared.shared.lock(|shared| { @@ -39,7 +39,7 @@ mod app { // bar will *not* run right now due to the critical section bar::spawn().unwrap(); - hprintln!("B - shared = {}", *shared).unwrap(); + hprintln!("B - shared = {}", *shared); // baz does not contend for `shared` so it's allowed to run now baz::spawn().unwrap(); @@ -47,7 +47,7 @@ mod app { // critical section is over: bar can now start - hprintln!("E").unwrap(); + hprintln!("E"); debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator } @@ -61,11 +61,11 @@ mod app { *shared }); - hprintln!("D - shared = {}", shared).unwrap(); + hprintln!("D - shared = {}", shared); } #[task(priority = 3)] fn baz(_: baz::Context) { - hprintln!("C").unwrap(); + hprintln!("C"); } } diff --git a/examples/message.rs b/examples/message.rs index 76c5675aaa..a5c89199ee 100644 --- a/examples/message.rs +++ b/examples/message.rs @@ -26,7 +26,7 @@ mod app { #[task(local = [count: u32 = 0])] fn foo(cx: foo::Context) { - hprintln!("foo").unwrap(); + hprintln!("foo"); bar::spawn(*cx.local.count).unwrap(); *cx.local.count += 1; @@ -34,14 +34,14 @@ mod app { #[task] fn bar(_: bar::Context, x: u32) { - hprintln!("bar({})", x).unwrap(); + hprintln!("bar({})", x); baz::spawn(x + 1, x + 2).unwrap(); } #[task] fn baz(_: baz::Context, x: u32, y: u32) { - hprintln!("baz({}, {})", x, y).unwrap(); + hprintln!("baz({}, {})", x, y); if x + y > 4 { debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator diff --git a/examples/message_passing.rs b/examples/message_passing.rs index ffa9537127..13e3b98de3 100644 --- a/examples/message_passing.rs +++ b/examples/message_passing.rs @@ -29,7 +29,7 @@ mod app { #[task(capacity = 3)] fn foo(_c: foo::Context, x: i32, y: u32) { - hprintln!("foo {}, {}", x, y).unwrap(); + hprintln!("foo {}, {}", x, y); if x == 2 { debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator } diff --git a/examples/multilock.rs b/examples/multilock.rs index d99bae695e..83df8d7d4e 100644 --- a/examples/multilock.rs +++ b/examples/multilock.rs @@ -48,7 +48,7 @@ mod app { *s2 += 1; *s3 += 1; - hprintln!("Multiple locks, s1: {}, s2: {}, s3: {}", *s1, *s2, *s3).unwrap(); + hprintln!("Multiple locks, s1: {}, s2: {}, s3: {}", *s1, *s2, *s3); }); debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator diff --git a/examples/only-shared-access.rs b/examples/only-shared-access.rs index 8b0a77ef8c..c9826d09b0 100644 --- a/examples/only-shared-access.rs +++ b/examples/only-shared-access.rs @@ -30,13 +30,13 @@ mod app { #[task(shared = [&key])] fn foo(cx: foo::Context) { let key: &u32 = cx.shared.key; - hprintln!("foo(key = {:#x})", key).unwrap(); + hprintln!("foo(key = {:#x})", key); debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator } #[task(priority = 2, shared = [&key])] fn bar(cx: bar::Context) { - hprintln!("bar(key = {:#x})", cx.shared.key).unwrap(); + hprintln!("bar(key = {:#x})", cx.shared.key); } } diff --git a/examples/pool.rs b/examples/pool.rs index d59bd91607..5014e216d3 100644 --- a/examples/pool.rs +++ b/examples/pool.rs @@ -56,7 +56,7 @@ mod app { #[task] fn foo(_: foo::Context, x: Box

) { - hprintln!("foo({:?})", x.as_ptr()).unwrap(); + hprintln!("foo({:?})", x.as_ptr()); // explicitly return the block to the pool drop(x); @@ -66,7 +66,7 @@ mod app { #[task(priority = 2)] fn bar(_: bar::Context, x: Box

) { - hprintln!("bar({:?})", x.as_ptr()).unwrap(); + hprintln!("bar({:?})", x.as_ptr()); // this is done automatically so we can omit the call to `drop` // drop(x); diff --git a/examples/preempt.rs b/examples/preempt.rs index d0c8cc7d3f..3c7f242990 100644 --- a/examples/preempt.rs +++ b/examples/preempt.rs @@ -25,21 +25,21 @@ mod app { #[task(priority = 1)] fn foo(_: foo::Context) { - hprintln!("foo - start").unwrap(); + hprintln!("foo - start"); baz::spawn().unwrap(); - hprintln!("foo - end").unwrap(); + hprintln!("foo - end"); debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator } #[task(priority = 2)] fn bar(_: bar::Context) { - hprintln!(" bar").unwrap(); + hprintln!(" bar"); } #[task(priority = 2)] fn baz(_: baz::Context) { - hprintln!(" baz - start").unwrap(); + hprintln!(" baz - start"); bar::spawn().unwrap(); - hprintln!(" baz - end").unwrap(); + hprintln!(" baz - end"); } } diff --git a/examples/ramfunc.rs b/examples/ramfunc.rs index b3b8012c38..049af18fbe 100644 --- a/examples/ramfunc.rs +++ b/examples/ramfunc.rs @@ -33,7 +33,7 @@ mod app { #[inline(never)] #[task] fn foo(_: foo::Context) { - hprintln!("foo").unwrap(); + hprintln!("foo"); debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator } diff --git a/examples/resource-user-struct.rs b/examples/resource-user-struct.rs index ae1918d05d..39a5b16c8a 100644 --- a/examples/resource-user-struct.rs +++ b/examples/resource-user-struct.rs @@ -55,7 +55,7 @@ mod app { *shared }); - hprintln!("UART0: shared = {}", shared).unwrap(); + hprintln!("UART0: shared = {}", shared); } // `shared` can be accessed from this context @@ -66,6 +66,6 @@ mod app { *shared }); - hprintln!("UART1: shared = {}", shared).unwrap(); + hprintln!("UART1: shared = {}", shared); } } diff --git a/examples/shared.rs b/examples/shared.rs index d87dca5263..58d64e4191 100644 --- a/examples/shared.rs +++ b/examples/shared.rs @@ -34,7 +34,7 @@ mod app { fn idle(mut c: idle::Context) -> ! { loop { if let Some(byte) = c.shared.c.lock(|c| c.dequeue()) { - hprintln!("received message: {}", byte).unwrap(); + hprintln!("received message: {}", byte); debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator } else { diff --git a/examples/spawn.rs b/examples/spawn.rs index 2db1ab8a28..75b7f85fe8 100644 --- a/examples/spawn.rs +++ b/examples/spawn.rs @@ -19,7 +19,7 @@ mod app { #[init] fn init(_: init::Context) -> (Shared, Local, init::Monotonics) { - hprintln!("init").unwrap(); + hprintln!("init"); foo::spawn().unwrap(); (Shared {}, Local {}, init::Monotonics()) @@ -27,7 +27,7 @@ mod app { #[task] fn foo(_: foo::Context) { - hprintln!("foo").unwrap(); + hprintln!("foo"); debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator } diff --git a/examples/static.rs b/examples/static.rs index c9aa6046b5..abeb9c525a 100644 --- a/examples/static.rs +++ b/examples/static.rs @@ -37,7 +37,7 @@ mod app { loop { // Lock-free access to the same underlying queue! if let Some(data) = c.local.c.dequeue() { - hprintln!("received message: {}", data).unwrap(); + hprintln!("received message: {}", data); // Run foo until data if data == 3 { diff --git a/examples/task.rs b/examples/task.rs index 2c53aa2359..d24fca6261 100644 --- a/examples/task.rs +++ b/examples/task.rs @@ -26,31 +26,31 @@ mod app { #[task] fn foo(_: foo::Context) { - hprintln!("foo - start").unwrap(); + hprintln!("foo - start"); // spawns `bar` onto the task scheduler // `foo` and `bar` have the same priority so `bar` will not run until // after `foo` terminates bar::spawn().unwrap(); - hprintln!("foo - middle").unwrap(); + hprintln!("foo - middle"); // spawns `baz` onto the task scheduler // `baz` has higher priority than `foo` so it immediately preempts `foo` baz::spawn().unwrap(); - hprintln!("foo - end").unwrap(); + hprintln!("foo - end"); } #[task] fn bar(_: bar::Context) { - hprintln!("bar").unwrap(); + hprintln!("bar"); debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator } #[task(priority = 2)] fn baz(_: baz::Context) { - hprintln!("baz").unwrap(); + hprintln!("baz"); } } From c370c0b21f054fa224680b95df63ca3d59f33ba1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Tj=C3=A4der?= Date: Wed, 11 Jan 2023 21:40:33 +0100 Subject: [PATCH 195/423] Remove ok() from hprintln!() sd 'hprintln(.*).ok\(\)' 'hprintln' (fd -e rs .) --- examples/cancel-reschedule.rs | 8 +++---- examples/common.rs | 6 ++--- examples/complex.rs | 42 +++++++++++++++++------------------ examples/extern_binds.rs | 2 +- examples/periodic-at.rs | 2 +- examples/periodic-at2.rs | 4 ++-- examples/periodic.rs | 2 +- examples/schedule.rs | 8 +++---- 8 files changed, 37 insertions(+), 37 deletions(-) diff --git a/examples/cancel-reschedule.rs b/examples/cancel-reschedule.rs index a38a9c4eae..7ab437f272 100644 --- a/examples/cancel-reschedule.rs +++ b/examples/cancel-reschedule.rs @@ -28,7 +28,7 @@ mod app { // Initialize the monotonic (SysTick rate in QEMU is 12 MHz) let mono = Systick::new(systick, 12_000_000); - hprintln!("init").ok(); + hprintln!("init"); // Schedule `foo` to run 1 second in the future foo::spawn_after(1.secs()).unwrap(); @@ -42,7 +42,7 @@ mod app { #[task] fn foo(_: foo::Context) { - hprintln!("foo").ok(); + hprintln!("foo"); // Schedule `bar` to run 2 seconds in the future (1 second after foo runs) let spawn_handle = baz::spawn_after(2.secs()).unwrap(); @@ -51,7 +51,7 @@ mod app { #[task] fn bar(_: bar::Context, baz_handle: baz::SpawnHandle, do_reschedule: bool) { - hprintln!("bar").ok(); + hprintln!("bar"); if do_reschedule { // Reschedule baz 2 seconds from now, instead of the original 1 second @@ -67,7 +67,7 @@ mod app { #[task] fn baz(_: baz::Context) { - hprintln!("baz").ok(); + hprintln!("baz"); debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator } } diff --git a/examples/common.rs b/examples/common.rs index 1fe671e61a..7dcc5421a9 100644 --- a/examples/common.rs +++ b/examples/common.rs @@ -73,7 +73,7 @@ mod app { // This task is only spawned once in `init`, hence this task will run // only once - hprintln!("foo").ok(); + hprintln!("foo"); } // Software task, also not bound to a hardware interrupt @@ -81,7 +81,7 @@ mod app { // The resources `s1` and `s2` are shared between all other tasks. #[task(shared = [s1, s2], local = [l2])] fn bar(_: bar::Context) { - hprintln!("bar").ok(); + hprintln!("bar"); // Run `bar` once per second bar::spawn_after(1.secs()).unwrap(); @@ -97,6 +97,6 @@ mod app { // Note that RTIC does NOT clear the interrupt flag, this is up to the // user - hprintln!("UART0 interrupt!").ok(); + hprintln!("UART0 interrupt!"); } } diff --git a/examples/complex.rs b/examples/complex.rs index 653198746b..742f9c7dc4 100644 --- a/examples/complex.rs +++ b/examples/complex.rs @@ -40,31 +40,31 @@ mod app { #[idle(shared = [s2, s3])] fn idle(mut cx: idle::Context) -> ! { - hprintln!("idle p0 started").ok(); + hprintln!("idle p0 started"); rtic::pend(Interrupt::GPIOC); cx.shared.s3.lock(|s| { - hprintln!("idle enter lock s3 {}", s).ok(); - hprintln!("idle pend t0").ok(); + hprintln!("idle enter lock s3 {}", s); + hprintln!("idle pend t0"); rtic::pend(Interrupt::GPIOA); // t0 p2, with shared ceiling 3 - hprintln!("idle pend t1").ok(); + hprintln!("idle pend t1"); rtic::pend(Interrupt::GPIOB); // t1 p3, with shared ceiling 3 - hprintln!("idle pend t2").ok(); + hprintln!("idle pend t2"); rtic::pend(Interrupt::GPIOC); // t2 p4, no sharing - hprintln!("idle still in lock s3 {}", s).ok(); + hprintln!("idle still in lock s3 {}", s); }); - hprintln!("\nback in idle").ok(); + hprintln!("\nback in idle"); cx.shared.s2.lock(|s| { - hprintln!("enter lock s2 {}", s).ok(); - hprintln!("idle pend t0").ok(); + hprintln!("enter lock s2 {}", s); + hprintln!("idle pend t0"); rtic::pend(Interrupt::GPIOA); // t0 p2, with shared ceiling 2 - hprintln!("idle pend t1").ok(); + hprintln!("idle pend t1"); rtic::pend(Interrupt::GPIOB); // t1 p3, no sharing - hprintln!("idle pend t2").ok(); + hprintln!("idle pend t2"); rtic::pend(Interrupt::GPIOC); // t2 p4, no sharing - hprintln!("idle still in lock s2 {}", s).ok(); + hprintln!("idle still in lock s2 {}", s); }); - hprintln!("\nidle exit").ok(); + hprintln!("\nidle exit"); debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator @@ -84,7 +84,7 @@ mod app { if *cx.local.times > 1 { "s" } else { "" } ) .ok(); - hprintln!("t0 p2 exit").ok(); + hprintln!("t0 p2 exit"); } #[task(binds = GPIOB, priority = 3, local = [times: u32 = 0], shared = [s3, s4])] @@ -100,15 +100,15 @@ mod app { .ok(); cx.shared.s4.lock(|s| { - hprintln!("t1 enter lock s4 {}", s).ok(); - hprintln!("t1 pend t0").ok(); + hprintln!("t1 enter lock s4 {}", s); + hprintln!("t1 pend t0"); rtic::pend(Interrupt::GPIOA); // t0 p2, with shared ceiling 2 - hprintln!("t1 pend t2").ok(); + hprintln!("t1 pend t2"); rtic::pend(Interrupt::GPIOC); // t2 p4, no sharing - hprintln!("t1 still in lock s4 {}", s).ok(); + hprintln!("t1 still in lock s4 {}", s); }); - hprintln!("t1 p3 exit").ok(); + hprintln!("t1 p3 exit"); } #[task(binds = GPIOC, priority = 4, local = [times: u32 = 0], shared = [s4])] @@ -124,9 +124,9 @@ mod app { .unwrap(); cx.shared.s4.lock(|s| { - hprintln!("enter lock s4 {}", s).ok(); + hprintln!("enter lock s4 {}", s); *s += 1; }); - hprintln!("t3 p4 exit").ok(); + hprintln!("t3 p4 exit"); } } diff --git a/examples/extern_binds.rs b/examples/extern_binds.rs index b6df2fb257..e445f4ecca 100644 --- a/examples/extern_binds.rs +++ b/examples/extern_binds.rs @@ -10,7 +10,7 @@ use panic_semihosting as _; // Free function implementing the interrupt bound task `foo`. fn foo(_: app::foo::Context) { - hprintln!("foo called").ok(); + hprintln!("foo called"); } #[rtic::app(device = lm3s6965)] diff --git a/examples/periodic-at.rs b/examples/periodic-at.rs index f9fd995fb0..1116210248 100644 --- a/examples/periodic-at.rs +++ b/examples/periodic-at.rs @@ -35,7 +35,7 @@ mod app { #[task(local = [cnt: u32 = 0])] fn foo(cx: foo::Context, instant: fugit::TimerInstantU64<100>) { - hprintln!("foo {:?}", instant).ok(); + hprintln!("foo {:?}", instant); *cx.local.cnt += 1; if *cx.local.cnt == 4 { diff --git a/examples/periodic-at2.rs b/examples/periodic-at2.rs index 879f709c65..35ebb52f5b 100644 --- a/examples/periodic-at2.rs +++ b/examples/periodic-at2.rs @@ -36,7 +36,7 @@ mod app { // Using the explicit type of the timer implementation #[task(local = [cnt: u32 = 0])] fn foo(cx: foo::Context, instant: fugit::TimerInstantU64<100>) { - hprintln!("foo {:?}", instant).ok(); + hprintln!("foo {:?}", instant); *cx.local.cnt += 1; if *cx.local.cnt == 4 { @@ -52,7 +52,7 @@ mod app { // This remains agnostic to the timer implementation #[task(local = [cnt: u32 = 0])] fn bar(_cx: bar::Context, instant: ::Instant) { - hprintln!("bar {:?}", instant).ok(); + hprintln!("bar {:?}", instant); // Spawn a new message with 1s offset to spawned time let next_instant = instant + 1.secs(); diff --git a/examples/periodic.rs b/examples/periodic.rs index 40c69257e7..5d66735c28 100644 --- a/examples/periodic.rs +++ b/examples/periodic.rs @@ -35,7 +35,7 @@ mod app { #[task(local = [cnt: u32 = 0])] fn foo(cx: foo::Context) { - hprintln!("foo").ok(); + hprintln!("foo"); *cx.local.cnt += 1; if *cx.local.cnt == 4 { diff --git a/examples/schedule.rs b/examples/schedule.rs index 5bad5a30ad..58e73dad18 100644 --- a/examples/schedule.rs +++ b/examples/schedule.rs @@ -28,7 +28,7 @@ mod app { // Initialize the monotonic (SysTick rate in QEMU is 12 MHz) let mono = Systick::new(systick, 12_000_000); - hprintln!("init").ok(); + hprintln!("init"); // Schedule `foo` to run 1 second in the future foo::spawn_after(1.secs()).unwrap(); @@ -42,7 +42,7 @@ mod app { #[task] fn foo(_: foo::Context) { - hprintln!("foo").ok(); + hprintln!("foo"); // Schedule `bar` to run 2 seconds in the future (1 second after foo runs) bar::spawn_after(1.secs()).unwrap(); @@ -50,7 +50,7 @@ mod app { #[task] fn bar(_: bar::Context) { - hprintln!("bar").ok(); + hprintln!("bar"); // Schedule `baz` to run 1 seconds from now, but with a specific time instant. baz::spawn_at(monotonics::now() + 1.secs()).unwrap(); @@ -58,7 +58,7 @@ mod app { #[task] fn baz(_: baz::Context) { - hprintln!("baz").ok(); + hprintln!("baz"); debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator } } From 050313d62d84dd9f537bbc578213f18cd7640d04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Tj=C3=A4der?= Date: Wed, 11 Jan 2023 21:48:21 +0100 Subject: [PATCH 196/423] Missed hprintln with multiline --- examples/binds.rs | 3 +-- examples/cfg-whole-task.rs | 3 +-- examples/complex.rs | 9 +++------ examples/hardware.rs | 3 +-- 4 files changed, 6 insertions(+), 12 deletions(-) diff --git a/examples/binds.rs b/examples/binds.rs index 04b8fad19e..db5bd96f1a 100644 --- a/examples/binds.rs +++ b/examples/binds.rs @@ -49,7 +49,6 @@ mod app { "foo called {} time{}", *cx.local.times, if *cx.local.times > 1 { "s" } else { "" } - ) - .unwrap(); + ); } } diff --git a/examples/cfg-whole-task.rs b/examples/cfg-whole-task.rs index f41866db47..90fb9ccc9b 100644 --- a/examples/cfg-whole-task.rs +++ b/examples/cfg-whole-task.rs @@ -88,7 +88,6 @@ mod app { "foo has been called {} time{}", n, if n == 1 { "" } else { "s" } - ) - .ok(); + ); } } diff --git a/examples/complex.rs b/examples/complex.rs index 742f9c7dc4..2be71d22fb 100644 --- a/examples/complex.rs +++ b/examples/complex.rs @@ -82,8 +82,7 @@ mod app { "t0 p2 called {} time{}", *cx.local.times, if *cx.local.times > 1 { "s" } else { "" } - ) - .ok(); + ); hprintln!("t0 p2 exit"); } @@ -96,8 +95,7 @@ mod app { "t1 p3 called {} time{}", *cx.local.times, if *cx.local.times > 1 { "s" } else { "" } - ) - .ok(); + ); cx.shared.s4.lock(|s| { hprintln!("t1 enter lock s4 {}", s); @@ -120,8 +118,7 @@ mod app { "t2 p4 called {} time{}", *cx.local.times, if *cx.local.times > 1 { "s" } else { "" } - ) - .unwrap(); + ); cx.shared.s4.lock(|s| { hprintln!("enter lock s4 {}", s); diff --git a/examples/hardware.rs b/examples/hardware.rs index 22cf5d928d..8f29455969 100644 --- a/examples/hardware.rs +++ b/examples/hardware.rs @@ -53,7 +53,6 @@ mod app { "UART0 called {} time{}", *cx.local.times, if *cx.local.times > 1 { "s" } else { "" } - ) - .unwrap(); + ); } } From 9764121cc1cdd6a7c27e86fa8d65bb6d2d48dc27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Tj=C3=A4der?= Date: Sat, 14 Jan 2023 11:24:51 +0100 Subject: [PATCH 197/423] Upgrade of semihosting changed timing New semihosting 0.5 does not use error handling, returns directly and as semihosting is generally slow this led to missing print statements. Workaround is to add NOP, which seems sufficient to let it flush the buffers --- examples/binds.rs | 5 +++-- examples/extern_binds.rs | 5 +++-- examples/generics.rs | 3 +++ examples/hardware.rs | 5 +++-- 4 files changed, 12 insertions(+), 6 deletions(-) diff --git a/examples/binds.rs b/examples/binds.rs index db5bd96f1a..601f245a0c 100644 --- a/examples/binds.rs +++ b/examples/binds.rs @@ -34,10 +34,11 @@ mod app { rtic::pend(Interrupt::UART0); - debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator - loop { + // Exit moved after nop to ensure that rtic::pend gets + // to run before exiting cortex_m::asm::nop(); + debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator } } diff --git a/examples/extern_binds.rs b/examples/extern_binds.rs index e445f4ecca..c2186cb7fb 100644 --- a/examples/extern_binds.rs +++ b/examples/extern_binds.rs @@ -40,10 +40,11 @@ mod app { rtic::pend(Interrupt::UART0); - debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator - loop { cortex_m::asm::nop(); + // Exit moved after nop to ensure that rtic::pend gets + // to run before exiting + debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator } } diff --git a/examples/generics.rs b/examples/generics.rs index f9a6aacfc8..6243d562e8 100644 --- a/examples/generics.rs +++ b/examples/generics.rs @@ -39,6 +39,9 @@ mod app { rtic::pend(Interrupt::UART1); + // Exit moved after nop to ensure that rtic::pend gets + // to run before exiting + cortex_m::asm::nop(); debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator } diff --git a/examples/hardware.rs b/examples/hardware.rs index 8f29455969..590bf6ab66 100644 --- a/examples/hardware.rs +++ b/examples/hardware.rs @@ -37,10 +37,11 @@ mod app { rtic::pend(Interrupt::UART0); - debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator - loop { + // Exit moved after nop to ensure that rtic::pend gets + // to run before exiting cortex_m::asm::nop(); + debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator } } From de3056eb3dd98f0859949fd32647cc90663f43e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Tj=C3=A4der?= Date: Sat, 14 Jan 2023 22:00:14 +0100 Subject: [PATCH 198/423] Add to CHANGELOG dev-dependency cortex-m-semihosting upgraded --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 78a725380a..612b8076fa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ For each category, *Added*, *Changed*, *Fixed* add new entries at the top! ### Changed +- Updated dev-dependency cortex-m-semihosting to v0.5 - CI: Updated to setup-python@v4 - CI: Updated to checkout@v3 - Tuned redirect message for rtic.rs/meeting From 40d5ace1112e7cc7ce8fc841ae59fb90c3c310f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Tj=C3=A4der?= Date: Sat, 21 Jan 2023 23:10:43 +0100 Subject: [PATCH 199/423] Deny missing_docs for all examples --- examples/binds.rs | 1 + examples/cancel-reschedule.rs | 1 + examples/capacity.rs | 1 + examples/cfg-whole-task.rs | 1 + examples/common.rs | 1 + examples/complex.rs | 1 + examples/declared_locals.rs | 1 + examples/destructure.rs | 1 + examples/extern_binds.rs | 1 + examples/extern_spawn.rs | 1 + examples/generics.rs | 1 + examples/hardware.rs | 1 + examples/idle-wfi.rs | 1 + examples/idle.rs | 1 + examples/init.rs | 1 + examples/locals.rs | 5 +++++ examples/lock-free.rs | 1 + examples/lock.rs | 1 + examples/message.rs | 1 + examples/message_passing.rs | 1 + examples/multilock.rs | 1 + examples/not-sync.rs | 4 ++++ examples/only-shared-access.rs | 1 + examples/periodic-at.rs | 1 + examples/periodic-at2.rs | 1 + examples/periodic.rs | 1 + examples/peripherals-taken.rs | 4 +++- examples/pool.rs | 1 + examples/ramfunc.rs | 1 + examples/resource-user-struct.rs | 1 + examples/schedule.rs | 1 + examples/shared.rs | 3 +++ examples/spawn.rs | 1 + examples/static.rs | 1 + examples/t-binds.rs | 1 + examples/t-htask-main.rs | 2 ++ examples/t-idle-main.rs | 2 ++ examples/t-schedule.rs | 1 + examples/t-spawn.rs | 1 + examples/task.rs | 1 + 40 files changed, 53 insertions(+), 1 deletion(-) diff --git a/examples/binds.rs b/examples/binds.rs index 601f245a0c..1b0c8c5beb 100644 --- a/examples/binds.rs +++ b/examples/binds.rs @@ -2,6 +2,7 @@ #![deny(unsafe_code)] #![deny(warnings)] +#![deny(missing_docs)] #![no_main] #![no_std] diff --git a/examples/cancel-reschedule.rs b/examples/cancel-reschedule.rs index 7ab437f272..36c496b71a 100644 --- a/examples/cancel-reschedule.rs +++ b/examples/cancel-reschedule.rs @@ -2,6 +2,7 @@ #![deny(unsafe_code)] #![deny(warnings)] +#![deny(missing_docs)] #![no_main] #![no_std] diff --git a/examples/capacity.rs b/examples/capacity.rs index e625249af8..550829be32 100644 --- a/examples/capacity.rs +++ b/examples/capacity.rs @@ -2,6 +2,7 @@ #![deny(unsafe_code)] #![deny(warnings)] +#![deny(missing_docs)] #![no_main] #![no_std] diff --git a/examples/cfg-whole-task.rs b/examples/cfg-whole-task.rs index 90fb9ccc9b..b5b978312e 100644 --- a/examples/cfg-whole-task.rs +++ b/examples/cfg-whole-task.rs @@ -2,6 +2,7 @@ #![deny(unsafe_code)] #![deny(warnings)] +#![deny(missing_docs)] #![no_main] #![no_std] diff --git a/examples/common.rs b/examples/common.rs index 7dcc5421a9..74ee8db2c8 100644 --- a/examples/common.rs +++ b/examples/common.rs @@ -2,6 +2,7 @@ #![deny(unsafe_code)] #![deny(warnings)] +#![deny(missing_docs)] #![no_main] #![no_std] diff --git a/examples/complex.rs b/examples/complex.rs index 2be71d22fb..73df025d2f 100644 --- a/examples/complex.rs +++ b/examples/complex.rs @@ -2,6 +2,7 @@ #![deny(unsafe_code)] #![deny(warnings)] +#![deny(missing_docs)] #![no_main] #![no_std] diff --git a/examples/declared_locals.rs b/examples/declared_locals.rs index 52d354bc9a..cb6214960f 100644 --- a/examples/declared_locals.rs +++ b/examples/declared_locals.rs @@ -2,6 +2,7 @@ #![deny(unsafe_code)] #![deny(warnings)] +#![deny(missing_docs)] #![no_main] #![no_std] diff --git a/examples/destructure.rs b/examples/destructure.rs index fd577a6c9c..70b0dd7e6f 100644 --- a/examples/destructure.rs +++ b/examples/destructure.rs @@ -2,6 +2,7 @@ #![deny(unsafe_code)] #![deny(warnings)] +#![deny(missing_docs)] #![no_main] #![no_std] diff --git a/examples/extern_binds.rs b/examples/extern_binds.rs index c2186cb7fb..bfc85cfc82 100644 --- a/examples/extern_binds.rs +++ b/examples/extern_binds.rs @@ -2,6 +2,7 @@ #![deny(unsafe_code)] #![deny(warnings)] +#![deny(missing_docs)] #![no_main] #![no_std] diff --git a/examples/extern_spawn.rs b/examples/extern_spawn.rs index 2eec2dbc49..446d31a77b 100644 --- a/examples/extern_spawn.rs +++ b/examples/extern_spawn.rs @@ -2,6 +2,7 @@ #![deny(unsafe_code)] #![deny(warnings)] +#![deny(missing_docs)] #![no_main] #![no_std] diff --git a/examples/generics.rs b/examples/generics.rs index 6243d562e8..bc4959fb7b 100644 --- a/examples/generics.rs +++ b/examples/generics.rs @@ -2,6 +2,7 @@ #![deny(unsafe_code)] #![deny(warnings)] +#![deny(missing_docs)] #![no_main] #![no_std] diff --git a/examples/hardware.rs b/examples/hardware.rs index 590bf6ab66..a7fdb47a37 100644 --- a/examples/hardware.rs +++ b/examples/hardware.rs @@ -2,6 +2,7 @@ #![deny(unsafe_code)] #![deny(warnings)] +#![deny(missing_docs)] #![no_main] #![no_std] diff --git a/examples/idle-wfi.rs b/examples/idle-wfi.rs index 42fd7b49e0..5e52620d45 100644 --- a/examples/idle-wfi.rs +++ b/examples/idle-wfi.rs @@ -2,6 +2,7 @@ #![deny(unsafe_code)] #![deny(warnings)] +#![deny(missing_docs)] #![no_main] #![no_std] diff --git a/examples/idle.rs b/examples/idle.rs index ad04fd68e4..ccec9bf273 100644 --- a/examples/idle.rs +++ b/examples/idle.rs @@ -2,6 +2,7 @@ #![deny(unsafe_code)] #![deny(warnings)] +#![deny(missing_docs)] #![no_main] #![no_std] diff --git a/examples/init.rs b/examples/init.rs index 7a10149692..afd3b98ce9 100644 --- a/examples/init.rs +++ b/examples/init.rs @@ -2,6 +2,7 @@ #![deny(unsafe_code)] #![deny(warnings)] +#![deny(missing_docs)] #![no_main] #![no_std] diff --git a/examples/locals.rs b/examples/locals.rs index 1889775316..9e112be4b3 100644 --- a/examples/locals.rs +++ b/examples/locals.rs @@ -2,6 +2,8 @@ #![deny(unsafe_code)] #![deny(warnings)] +#![deny(missing_docs)] +#![deny(missing_docs)] #![no_main] #![no_std] @@ -16,8 +18,11 @@ mod app { #[local] struct Local { + /// Local foo local_to_foo: i64, + /// Local bar local_to_bar: i64, + /// Local idle local_to_idle: i64, } diff --git a/examples/lock-free.rs b/examples/lock-free.rs index 32853e44ed..6e5faadbf2 100644 --- a/examples/lock-free.rs +++ b/examples/lock-free.rs @@ -2,6 +2,7 @@ #![deny(unsafe_code)] #![deny(warnings)] +#![deny(missing_docs)] #![no_main] #![no_std] diff --git a/examples/lock.rs b/examples/lock.rs index 16f3b33812..5b3e0bcc3c 100644 --- a/examples/lock.rs +++ b/examples/lock.rs @@ -2,6 +2,7 @@ #![deny(unsafe_code)] #![deny(warnings)] +#![deny(missing_docs)] #![no_main] #![no_std] diff --git a/examples/message.rs b/examples/message.rs index a5c89199ee..8a6a12d5f4 100644 --- a/examples/message.rs +++ b/examples/message.rs @@ -2,6 +2,7 @@ #![deny(unsafe_code)] #![deny(warnings)] +#![deny(missing_docs)] #![no_main] #![no_std] diff --git a/examples/message_passing.rs b/examples/message_passing.rs index 13e3b98de3..9550a5010a 100644 --- a/examples/message_passing.rs +++ b/examples/message_passing.rs @@ -2,6 +2,7 @@ #![deny(unsafe_code)] #![deny(warnings)] +#![deny(missing_docs)] #![no_main] #![no_std] diff --git a/examples/multilock.rs b/examples/multilock.rs index 83df8d7d4e..c7085cd51c 100644 --- a/examples/multilock.rs +++ b/examples/multilock.rs @@ -2,6 +2,7 @@ #![deny(unsafe_code)] #![deny(warnings)] +#![deny(missing_docs)] #![no_main] #![no_std] diff --git a/examples/not-sync.rs b/examples/not-sync.rs index aa79ad5626..68af04a6c6 100644 --- a/examples/not-sync.rs +++ b/examples/not-sync.rs @@ -2,13 +2,16 @@ // #![deny(unsafe_code)] #![deny(warnings)] +#![deny(missing_docs)] #![no_main] #![no_std] use core::marker::PhantomData; use panic_semihosting as _; +/// Not sync pub struct NotSync { + /// Phantom action _0: PhantomData<*const ()>, } @@ -22,6 +25,7 @@ mod app { #[shared] struct Shared { + /// This resource is not Sync shared: NotSync, } diff --git a/examples/only-shared-access.rs b/examples/only-shared-access.rs index c9826d09b0..b32827abf2 100644 --- a/examples/only-shared-access.rs +++ b/examples/only-shared-access.rs @@ -2,6 +2,7 @@ #![deny(unsafe_code)] #![deny(warnings)] +#![deny(missing_docs)] #![no_main] #![no_std] diff --git a/examples/periodic-at.rs b/examples/periodic-at.rs index 1116210248..ad8a5496f2 100644 --- a/examples/periodic-at.rs +++ b/examples/periodic-at.rs @@ -2,6 +2,7 @@ #![deny(unsafe_code)] #![deny(warnings)] +#![deny(missing_docs)] #![no_main] #![no_std] diff --git a/examples/periodic-at2.rs b/examples/periodic-at2.rs index 35ebb52f5b..4719bdb7e4 100644 --- a/examples/periodic-at2.rs +++ b/examples/periodic-at2.rs @@ -2,6 +2,7 @@ #![deny(unsafe_code)] #![deny(warnings)] +#![deny(missing_docs)] #![no_main] #![no_std] diff --git a/examples/periodic.rs b/examples/periodic.rs index 5d66735c28..13ca7c852c 100644 --- a/examples/periodic.rs +++ b/examples/periodic.rs @@ -2,6 +2,7 @@ #![deny(unsafe_code)] #![deny(warnings)] +#![deny(missing_docs)] #![no_main] #![no_std] diff --git a/examples/peripherals-taken.rs b/examples/peripherals-taken.rs index d542c0e64d..cc9b9a11ce 100644 --- a/examples/peripherals-taken.rs +++ b/examples/peripherals-taken.rs @@ -1,5 +1,7 @@ -#![deny(unsafe_code)] +//! examples/peripherals-taken.rs #![deny(warnings)] +#![deny(unsafe_code)] +#![deny(missing_docs)] #![no_main] #![no_std] diff --git a/examples/pool.rs b/examples/pool.rs index 5014e216d3..4b9aa44223 100644 --- a/examples/pool.rs +++ b/examples/pool.rs @@ -2,6 +2,7 @@ #![deny(unsafe_code)] #![deny(warnings)] +#![deny(missing_docs)] #![no_main] #![no_std] diff --git a/examples/ramfunc.rs b/examples/ramfunc.rs index 049af18fbe..956a2554d8 100644 --- a/examples/ramfunc.rs +++ b/examples/ramfunc.rs @@ -1,6 +1,7 @@ //! examples/ramfunc.rs #![deny(warnings)] +#![deny(missing_docs)] #![no_main] #![no_std] diff --git a/examples/resource-user-struct.rs b/examples/resource-user-struct.rs index 39a5b16c8a..37a885609f 100644 --- a/examples/resource-user-struct.rs +++ b/examples/resource-user-struct.rs @@ -2,6 +2,7 @@ #![deny(unsafe_code)] #![deny(warnings)] +#![deny(missing_docs)] #![no_main] #![no_std] diff --git a/examples/schedule.rs b/examples/schedule.rs index 58e73dad18..9b86929d93 100644 --- a/examples/schedule.rs +++ b/examples/schedule.rs @@ -2,6 +2,7 @@ #![deny(unsafe_code)] #![deny(warnings)] +#![deny(missing_docs)] #![no_main] #![no_std] diff --git a/examples/shared.rs b/examples/shared.rs index 58d64e4191..b43a19a3c5 100644 --- a/examples/shared.rs +++ b/examples/shared.rs @@ -2,6 +2,7 @@ #![deny(unsafe_code)] #![deny(warnings)] +#![deny(missing_docs)] #![no_main] #![no_std] @@ -15,7 +16,9 @@ mod app { #[shared] struct Shared { + /// Producer p: Producer<'static, u32, 5>, + /// Consumer c: Consumer<'static, u32, 5>, } diff --git a/examples/spawn.rs b/examples/spawn.rs index 75b7f85fe8..50ae7e7a75 100644 --- a/examples/spawn.rs +++ b/examples/spawn.rs @@ -2,6 +2,7 @@ #![deny(unsafe_code)] #![deny(warnings)] +#![deny(missing_docs)] #![no_main] #![no_std] diff --git a/examples/static.rs b/examples/static.rs index abeb9c525a..efafcc7aa8 100644 --- a/examples/static.rs +++ b/examples/static.rs @@ -2,6 +2,7 @@ #![deny(unsafe_code)] #![deny(warnings)] +#![deny(missing_docs)] #![no_main] #![no_std] diff --git a/examples/t-binds.rs b/examples/t-binds.rs index 12479c0ad4..822a2eeabb 100644 --- a/examples/t-binds.rs +++ b/examples/t-binds.rs @@ -2,6 +2,7 @@ #![deny(unsafe_code)] #![deny(warnings)] +#![deny(missing_docs)] #![no_main] #![no_std] diff --git a/examples/t-htask-main.rs b/examples/t-htask-main.rs index 37189faf76..2b17b2ee02 100644 --- a/examples/t-htask-main.rs +++ b/examples/t-htask-main.rs @@ -1,5 +1,7 @@ +//! examples/t-htask-main.rs #![deny(unsafe_code)] #![deny(warnings)] +#![deny(missing_docs)] #![no_main] #![no_std] diff --git a/examples/t-idle-main.rs b/examples/t-idle-main.rs index 1adc9bf044..48635b2ab2 100644 --- a/examples/t-idle-main.rs +++ b/examples/t-idle-main.rs @@ -1,5 +1,7 @@ +//! examples/t-idle-main.rs #![deny(unsafe_code)] #![deny(warnings)] +#![deny(missing_docs)] #![no_main] #![no_std] diff --git a/examples/t-schedule.rs b/examples/t-schedule.rs index 5ec420873d..f3979dd62f 100644 --- a/examples/t-schedule.rs +++ b/examples/t-schedule.rs @@ -2,6 +2,7 @@ #![deny(unsafe_code)] #![deny(warnings)] +#![deny(missing_docs)] #![no_main] #![no_std] diff --git a/examples/t-spawn.rs b/examples/t-spawn.rs index 2bd771d7f6..7483a8494b 100644 --- a/examples/t-spawn.rs +++ b/examples/t-spawn.rs @@ -2,6 +2,7 @@ #![deny(unsafe_code)] #![deny(warnings)] +#![deny(missing_docs)] #![no_main] #![no_std] diff --git a/examples/task.rs b/examples/task.rs index d24fca6261..9757f2f559 100644 --- a/examples/task.rs +++ b/examples/task.rs @@ -2,6 +2,7 @@ #![deny(unsafe_code)] #![deny(warnings)] +#![deny(missing_docs)] #![no_main] #![no_std] From 1237f5b33b5ad61e94acb7f93255cb97bfb8ead6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Tj=C3=A4der?= Date: Sun, 22 Jan 2023 00:22:46 +0100 Subject: [PATCH 200/423] Heapless 0.7.16 pool!() generates undocumented struct --- examples/pool.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/pool.rs b/examples/pool.rs index 4b9aa44223..ab76370e71 100644 --- a/examples/pool.rs +++ b/examples/pool.rs @@ -2,7 +2,8 @@ #![deny(unsafe_code)] #![deny(warnings)] -#![deny(missing_docs)] +// pool!() generates a struct without docs +//#![deny(missing_docs)] #![no_main] #![no_std] From f6b0d18e24d49e5ecdcd18393c9c3109441a83e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Tj=C3=A4der?= Date: Sun, 22 Jan 2023 00:26:09 +0100 Subject: [PATCH 201/423] Improve RTIC doc handling Enable use of ``` #![deny(missing_docs)] ``` --- macros/src/codegen.rs | 2 +- macros/src/codegen/hardware_tasks.rs | 4 ++++ macros/src/codegen/idle.rs | 2 ++ macros/src/codegen/init.rs | 10 +++++++--- macros/src/codegen/local_resources_struct.rs | 5 ++++- macros/src/codegen/module.rs | 19 ++++++++++++++----- macros/src/codegen/shared_resources_struct.rs | 15 ++++++++++++++- macros/src/codegen/software_tasks.rs | 2 ++ 8 files changed, 48 insertions(+), 11 deletions(-) diff --git a/macros/src/codegen.rs b/macros/src/codegen.rs index 01be1d5787..9c444fed36 100644 --- a/macros/src/codegen.rs +++ b/macros/src/codegen.rs @@ -187,7 +187,7 @@ pub fn app(app: &App, analysis: &Analysis, extra: &Extra) -> TokenStream2 { #(#root_software_tasks)* - /// app module + /// App module #(#mod_app)* #(#mod_app_shared_resources)* diff --git a/macros/src/codegen/hardware_tasks.rs b/macros/src/codegen/hardware_tasks.rs index f75c71d43f..780cc7e3cc 100644 --- a/macros/src/codegen/hardware_tasks.rs +++ b/macros/src/codegen/hardware_tasks.rs @@ -33,10 +33,12 @@ pub fn codegen( let priority = task.args.priority; let cfgs = &task.cfgs; let attrs = &task.attrs; + let user_hardware_task_isr_doc = &format!(" User HW task ISR trampoline for {name}"); mod_app.push(quote!( #[allow(non_snake_case)] #[no_mangle] + #[doc = #user_hardware_task_isr_doc] #(#attrs)* #(#cfgs)* unsafe fn #symbol() { @@ -88,11 +90,13 @@ pub fn codegen( extra, )); + let user_hardware_task_doc = &format!(" User HW task: {name}"); if !task.is_extern { let attrs = &task.attrs; let context = &task.context; let stmts = &task.stmts; user_tasks.push(quote!( + #[doc = #user_hardware_task_doc] #(#attrs)* #[allow(non_snake_case)] fn #name(#context: #name::Context) { diff --git a/macros/src/codegen/idle.rs b/macros/src/codegen/idle.rs index 83b85d7ba6..55b7e6230f 100644 --- a/macros/src/codegen/idle.rs +++ b/macros/src/codegen/idle.rs @@ -59,12 +59,14 @@ pub fn codegen( analysis, extra, )); + let idle_doc = &format!(" User provided idle function"); let attrs = &idle.attrs; let context = &idle.context; let stmts = &idle.stmts; let user_idle = Some(quote!( #(#attrs)* + #[doc = #idle_doc] #[allow(non_snake_case)] fn #name(#context: #name::Context) -> ! { use rtic::Mutex as _; diff --git a/macros/src/codegen/init.rs b/macros/src/codegen/init.rs index eaf7f612a9..e8a9c8b98c 100644 --- a/macros/src/codegen/init.rs +++ b/macros/src/codegen/init.rs @@ -65,22 +65,27 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> CodegenResult { ) }) .collect(); + + let shared_resources_doc = &format!(" RTIC shared resource struct"); + let local_resources_doc = &format!(" RTIC local resource struct"); root_init.push(quote! { + #[doc = #shared_resources_doc] struct #shared { #(#shared_resources)* } + #[doc = #local_resources_doc] struct #local { #(#local_resources)* } }); - // let locals_pat = locals_pat.iter(); - let user_init_return = quote! {#shared, #local, #name::Monotonics}; + let user_init_doc = &format!(" User provided init function"); let user_init = quote!( #(#attrs)* + #[doc = #user_init_doc] #[inline(always)] #[allow(non_snake_case)] fn #name(#context: #name::Context) -> (#user_init_return) { @@ -100,7 +105,6 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> CodegenResult { mod_app = Some(constructor); } - // let locals_new = locals_new.iter(); let call_init = quote! { let (shared_resources, local_resources, mut monotonics) = #name(#name::Context::new(core.into())); }; diff --git a/macros/src/codegen/local_resources_struct.rs b/macros/src/codegen/local_resources_struct.rs index b7eae3f9d3..74bdbf8b89 100644 --- a/macros/src/codegen/local_resources_struct.rs +++ b/macros/src/codegen/local_resources_struct.rs @@ -49,7 +49,9 @@ pub fn codegen(ctxt: Context, needs_lt: &mut bool, app: &App) -> (TokenStream2, util::declared_static_local_resource_ident(name, &task_name) }; + let local_resource_doc = format!(" Local resource `{name}`"); fields.push(quote!( + #[doc = #local_resource_doc] #(#cfgs)* pub #name: &#lt mut #ty )); @@ -82,7 +84,7 @@ pub fn codegen(ctxt: Context, needs_lt: &mut bool, app: &App) -> (TokenStream2, } } - let doc = format!("Local resources `{}` has access to", ctxt.ident(app)); + let doc = format!(" Local resources `{}` has access to", ctxt.ident(app)); let ident = util::local_resources_ident(ctxt, app); let item = quote!( #[allow(non_snake_case)] @@ -96,6 +98,7 @@ pub fn codegen(ctxt: Context, needs_lt: &mut bool, app: &App) -> (TokenStream2, let constructor = quote!( impl<#lt> #ident<#lt> { #[inline(always)] + #[doc(hidden)] pub unsafe fn new() -> Self { #ident { #(#values,)* diff --git a/macros/src/codegen/module.rs b/macros/src/codegen/module.rs index fd8137fad4..d05784d18b 100644 --- a/macros/src/codegen/module.rs +++ b/macros/src/codegen/module.rs @@ -133,15 +133,16 @@ pub fn codegen( )); module_items.push(quote!( + #[doc(inline)] pub use super::#internal_monotonics_ident as Monotonics; )); } let doc = match ctxt { - Context::Idle => "Idle loop", - Context::Init => "Initialization function", - Context::HardwareTask(_) => "Hardware task", - Context::SoftwareTask(_) => "Software task", + Context::Idle => " Idle loop", + Context::Init => " Initialization function", + Context::HardwareTask(_) => " Hardware task", + Context::SoftwareTask(_) => " Software task", }; let v = Vec::new(); @@ -172,8 +173,8 @@ pub fn codegen( let internal_context_name = util::internal_task_ident(name, "Context"); items.push(quote!( - #(#cfgs)* /// Execution context + #(#cfgs)* #[allow(non_snake_case)] #[allow(non_camel_case_types)] pub struct #internal_context_name<#lt> { @@ -182,6 +183,7 @@ pub fn codegen( #(#cfgs)* impl<#lt> #internal_context_name<#lt> { + #[doc(hidden)] #[inline(always)] pub unsafe fn new(#core #priority) -> Self { #internal_context_name { @@ -192,6 +194,7 @@ pub fn codegen( )); module_items.push(quote!( + #[doc(inline)] #(#cfgs)* pub use super::#internal_context_name as Context; )); @@ -251,6 +254,7 @@ pub fn codegen( })); module_items.push(quote!( + #[doc(inline)] #(#cfgs)* pub use super::#internal_spawn_ident as spawn; )); @@ -300,6 +304,7 @@ pub fn codegen( )); } module_items.push(quote!( + #[doc(hidden)] pub mod #m { pub use super::super::#internal_spawn_after_ident as spawn_after; pub use super::super::#internal_spawn_at_ident as spawn_at; @@ -308,6 +313,7 @@ pub fn codegen( )); items.push(quote!( + #[doc(hidden)] #(#cfgs)* #[allow(non_snake_case)] #[allow(non_camel_case_types)] @@ -317,6 +323,7 @@ pub fn codegen( } impl core::fmt::Debug for #internal_spawn_handle_ident { + #[doc(hidden)] fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { f.debug_struct(#spawn_handle_string).finish() } @@ -344,6 +351,7 @@ pub fn codegen( }) } + /// Reschedule after #[inline] pub fn reschedule_after( self, @@ -352,6 +360,7 @@ pub fn codegen( self.reschedule_at(monotonics::#m::now() + duration) } + /// Reschedule at pub fn reschedule_at( self, instant: <#m as rtic::Monotonic>::Instant diff --git a/macros/src/codegen/shared_resources_struct.rs b/macros/src/codegen/shared_resources_struct.rs index 90cbc1beef..7b6cd20d5c 100644 --- a/macros/src/codegen/shared_resources_struct.rs +++ b/macros/src/codegen/shared_resources_struct.rs @@ -44,14 +44,18 @@ pub fn codegen(ctxt: Context, needs_lt: &mut bool, app: &App) -> (TokenStream2, quote!('a) }; + let lock_free_resource_doc = format!(" Lock free resource `{name}`"); fields.push(quote!( + #[doc = #lock_free_resource_doc] #(#cfgs)* pub #name: &#lt #mut_ #ty )); } else if access.is_shared() { lt = Some(quote!('a)); + let shared_resource_doc = format!(" Shared resource `{name}`"); fields.push(quote!( + #[doc = #shared_resource_doc] #(#cfgs)* pub #name: &'a #ty )); @@ -59,12 +63,16 @@ pub fn codegen(ctxt: Context, needs_lt: &mut bool, app: &App) -> (TokenStream2, // Resource proxy lt = Some(quote!('a)); + let resource_doc = + format!(" Resource proxy resource `{name}`. Use method `.lock()` to gain access"); fields.push(quote!( + #[doc = #resource_doc] #(#cfgs)* pub #name: shared_resources::#shared_name<'a> )); values.push(quote!( + #[doc(hidden)] #(#cfgs)* #name: shared_resources::#shared_name::new(priority) @@ -74,13 +82,17 @@ pub fn codegen(ctxt: Context, needs_lt: &mut bool, app: &App) -> (TokenStream2, continue; } + let resource_doc; let expr = if access.is_exclusive() { + resource_doc = format!(" Exclusive access resource `{name}`"); quote!(&mut *(&mut *#mangled_name.get_mut()).as_mut_ptr()) } else { + resource_doc = format!(" Non-exclusive access resource `{name}`"); quote!(&*(&*#mangled_name.get()).as_ptr()) }; values.push(quote!( + #[doc = #resource_doc] #(#cfgs)* #name: #expr )); @@ -100,7 +112,7 @@ pub fn codegen(ctxt: Context, needs_lt: &mut bool, app: &App) -> (TokenStream2, } } - let doc = format!("Shared resources `{}` has access to", ctxt.ident(app)); + let doc = format!(" Shared resources `{}` has access to", ctxt.ident(app)); let ident = util::shared_resources_ident(ctxt, app); let item = quote!( #[allow(non_snake_case)] @@ -118,6 +130,7 @@ pub fn codegen(ctxt: Context, needs_lt: &mut bool, app: &App) -> (TokenStream2, }; let constructor = quote!( impl<#lt> #ident<#lt> { + #[doc(hidden)] #[inline(always)] pub unsafe fn new(#arg) -> Self { #ident { diff --git a/macros/src/codegen/software_tasks.rs b/macros/src/codegen/software_tasks.rs index 77559493bc..7c3f70fb30 100644 --- a/macros/src/codegen/software_tasks.rs +++ b/macros/src/codegen/software_tasks.rs @@ -125,7 +125,9 @@ pub fn codegen( let attrs = &task.attrs; let cfgs = &task.cfgs; let stmts = &task.stmts; + let user_task_doc = format!(" User SW task {name}"); user_tasks.push(quote!( + #[doc = #user_task_doc] #(#attrs)* #(#cfgs)* #[allow(non_snake_case)] From 7ba23044e6992019d07f3f919233e505109eb77d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Tj=C3=A4der?= Date: Sun, 22 Jan 2023 00:30:40 +0100 Subject: [PATCH 202/423] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 612b8076fa..5bc38d83a3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ For each category, *Added*, *Changed*, *Fixed* add new entries at the top! ### Fixed +- Attempt to handle docs generation enabling `deny(missing_docs)` - Use native GHA rustup and cargo - Distinguish between thumbv8m.base and thumbv8m.main for basepri usage. From 3f74f3b8459fdd451707511954a6ea3d128aabe3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Tj=C3=A4der?= Date: Sun, 22 Jan 2023 00:35:03 +0100 Subject: [PATCH 203/423] Make clippy happy --- macros/src/codegen/idle.rs | 2 +- macros/src/codegen/init.rs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/macros/src/codegen/idle.rs b/macros/src/codegen/idle.rs index 55b7e6230f..77a7f9feb2 100644 --- a/macros/src/codegen/idle.rs +++ b/macros/src/codegen/idle.rs @@ -59,7 +59,7 @@ pub fn codegen( analysis, extra, )); - let idle_doc = &format!(" User provided idle function"); + let idle_doc = " User provided idle function".to_string(); let attrs = &idle.attrs; let context = &idle.context; diff --git a/macros/src/codegen/init.rs b/macros/src/codegen/init.rs index e8a9c8b98c..34f86f2721 100644 --- a/macros/src/codegen/init.rs +++ b/macros/src/codegen/init.rs @@ -66,8 +66,8 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> CodegenResult { }) .collect(); - let shared_resources_doc = &format!(" RTIC shared resource struct"); - let local_resources_doc = &format!(" RTIC local resource struct"); + let shared_resources_doc = " RTIC shared resource struct".to_string(); + let local_resources_doc = " RTIC local resource struct".to_string(); root_init.push(quote! { #[doc = #shared_resources_doc] struct #shared { @@ -81,7 +81,7 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> CodegenResult { }); let user_init_return = quote! {#shared, #local, #name::Monotonics}; - let user_init_doc = &format!(" User provided init function"); + let user_init_doc = " User provided init function".to_string(); let user_init = quote!( #(#attrs)* From c29e477aa1849dc93daf6625a09d67cfcb5fc1e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Tj=C3=A4der?= Date: Sun, 22 Jan 2023 00:55:21 +0100 Subject: [PATCH 204/423] Cleanup NVIC prio too high example --- ui/task-priority-too-high.rs | 2 +- ui/task-priority-too-high.stderr | 8 -------- 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/ui/task-priority-too-high.rs b/ui/task-priority-too-high.rs index 46ab561750..e7e0cce207 100644 --- a/ui/task-priority-too-high.rs +++ b/ui/task-priority-too-high.rs @@ -9,7 +9,7 @@ mod app { struct Local {} #[init] - fn init(cx: init::Context) -> (Shared, Local, init::Monotonics) { + fn init(_: init::Context) -> (Shared, Local, init::Monotonics) { (Shared {}, Local {}, init::Monotonics()) } diff --git a/ui/task-priority-too-high.stderr b/ui/task-priority-too-high.stderr index a7a15ebfe5..026124c8fa 100644 --- a/ui/task-priority-too-high.stderr +++ b/ui/task-priority-too-high.stderr @@ -1,11 +1,3 @@ -warning: unused variable: `cx` - --> ui/task-priority-too-high.rs:12:13 - | -12 | fn init(cx: init::Context) -> (Shared, Local, init::Monotonics) { - | ^^ help: if this is intentional, prefix it with an underscore: `_cx` - | - = note: `#[warn(unused_variables)]` on by default - error[E0080]: evaluation of constant value failed --> ui/task-priority-too-high.rs:3:1 | From be74469ab71341da9a564ce1bc3e0e3f17009688 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Tj=C3=A4der?= Date: Sun, 22 Jan 2023 03:03:24 +0100 Subject: [PATCH 205/423] Enable at least masking out a Monotonic Simplest case working, but leaves a lot to ask as shown by examples/cfg-monotonic.rs Current `rtic-syntax` is unable to validate and handle the `cfgs[]` which limits the usefulness of this. --- ci/expected/cfg-monotonic.run | 0 macros/src/codegen.rs | 7 ++++++- macros/src/codegen/module.rs | 20 ++++++++++++++++---- macros/src/codegen/post_init.rs | 15 +++++++++++---- macros/src/codegen/software_tasks.rs | 2 ++ macros/src/codegen/timer_queue.rs | 6 +++++- 6 files changed, 40 insertions(+), 10 deletions(-) create mode 100644 ci/expected/cfg-monotonic.run diff --git a/ci/expected/cfg-monotonic.run b/ci/expected/cfg-monotonic.run new file mode 100644 index 0000000000..e69de29bb2 diff --git a/macros/src/codegen.rs b/macros/src/codegen.rs index 9c444fed36..89173d450e 100644 --- a/macros/src/codegen.rs +++ b/macros/src/codegen.rs @@ -108,6 +108,7 @@ pub fn app(app: &App, analysis: &Analysis, extra: &Extra) -> TokenStream2 { .map(|(_, monotonic)| { let name = &monotonic.ident; let name_str = &name.to_string(); + let cfgs = &monotonic.cfgs; let ident = util::monotonic_ident(name_str); let doc = &format!( "This module holds the static implementation for `{}::now()`", @@ -115,7 +116,10 @@ pub fn app(app: &App, analysis: &Analysis, extra: &Extra) -> TokenStream2 { ); let default_monotonic = if monotonic.args.default { - quote!(pub use #name::now;) + quote!( + #(#cfgs)* + pub use #name::now; + ) } else { quote!() }; @@ -125,6 +129,7 @@ pub fn app(app: &App, analysis: &Analysis, extra: &Extra) -> TokenStream2 { #[doc = #doc] #[allow(non_snake_case)] + #(#cfgs)* pub mod #name { /// Read the current time from this monotonic diff --git a/macros/src/codegen/module.rs b/macros/src/codegen/module.rs index d05784d18b..71bcfa8e22 100644 --- a/macros/src/codegen/module.rs +++ b/macros/src/codegen/module.rs @@ -116,8 +116,12 @@ pub fn codegen( .monotonics .iter() .map(|(_, monotonic)| { + let cfgs = &monotonic.cfgs; let mono = &monotonic.ty; - quote! {#mono} + quote! { + #(#cfgs)* + pub #mono + } }) .collect(); @@ -128,7 +132,7 @@ pub fn codegen( #[allow(non_snake_case)] #[allow(non_camel_case_types)] pub struct #internal_monotonics_ident( - #(pub #monotonic_types),* + #(#monotonic_types),* ); )); @@ -226,8 +230,8 @@ pub fn codegen( // Spawn caller items.push(quote!( - #(#cfgs)* /// Spawns the task directly + #(#cfgs)* pub fn #internal_spawn_ident(#(#args,)*) -> Result<(), #ty> { let input = #tupled; @@ -267,6 +271,7 @@ pub fn codegen( let tq = util::tq_ident(&monotonic.ident.to_string()); let t = util::schedule_t_ident(); let m = &monotonic.ident; + let cfgs = &monotonic.cfgs; let m_ident = util::monotonic_ident(&monotonic_name); let m_isr = &monotonic.args.binds; let enum_ = util::interrupt_ident(); @@ -298,13 +303,17 @@ pub fn codegen( if monotonic.args.default { module_items.push(quote!( + #(#cfgs)* pub use #m::spawn_after; + #(#cfgs)* pub use #m::spawn_at; + #(#cfgs)* pub use #m::SpawnHandle; )); } module_items.push(quote!( #[doc(hidden)] + #(#cfgs)* pub mod #m { pub use super::super::#internal_spawn_after_ident as spawn_after; pub use super::super::#internal_spawn_at_ident as spawn_at; @@ -322,6 +331,7 @@ pub fn codegen( marker: u32, } + #(#cfgs)* impl core::fmt::Debug for #internal_spawn_handle_ident { #[doc(hidden)] fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { @@ -353,6 +363,7 @@ pub fn codegen( /// Reschedule after #[inline] + #(#cfgs)* pub fn reschedule_after( self, duration: <#m as rtic::Monotonic>::Duration @@ -361,6 +372,7 @@ pub fn codegen( } /// Reschedule at + #(#cfgs)* pub fn reschedule_at( self, instant: <#m as rtic::Monotonic>::Instant @@ -376,11 +388,11 @@ pub fn codegen( } } - #(#cfgs)* /// Spawns the task after a set duration relative to the current time /// /// This will use the time `Instant::new(0)` as baseline if called in `#[init]`, /// so if you use a non-resetable timer use `spawn_at` when in `#[init]` + #(#cfgs)* #[allow(non_snake_case)] pub fn #internal_spawn_after_ident( duration: <#m as rtic::Monotonic>::Duration diff --git a/macros/src/codegen/post_init.rs b/macros/src/codegen/post_init.rs index 9531254caf..460b4e2160 100644 --- a/macros/src/codegen/post_init.rs +++ b/macros/src/codegen/post_init.rs @@ -43,21 +43,28 @@ pub fn codegen(app: &App, analysis: &Analysis) -> Vec { } } - for (i, (monotonic, _)) in app.monotonics.iter().enumerate() { + for (i, (monotonic_ident, monotonic)) in app.monotonics.iter().enumerate() { // For future use // let doc = format!(" RTIC internal: {}:{}", file!(), line!()); // stmts.push(quote!(#[doc = #doc])); + let cfgs = &monotonic.cfgs; #[allow(clippy::cast_possible_truncation)] let idx = Index { index: i as u32, span: Span::call_site(), }; - stmts.push(quote!(monotonics.#idx.reset();)); + stmts.push(quote!( + #(#cfgs)* + monotonics.#idx.reset(); + )); // Store the monotonic - let name = util::monotonic_ident(&monotonic.to_string()); - stmts.push(quote!(#name.get_mut().write(Some(monotonics.#idx));)); + let name = util::monotonic_ident(&monotonic_ident.to_string()); + stmts.push(quote!( + #(#cfgs)* + #name.get_mut().write(Some(monotonics.#idx)); + )); } // Enable the interrupts -- this completes the `init`-ialization phase diff --git a/macros/src/codegen/software_tasks.rs b/macros/src/codegen/software_tasks.rs index 7c3f70fb30..6bd2a71f11 100644 --- a/macros/src/codegen/software_tasks.rs +++ b/macros/src/codegen/software_tasks.rs @@ -62,6 +62,7 @@ pub fn codegen( for (_, monotonic) in &app.monotonics { let instants = util::monotonic_instants_ident(name, &monotonic.ident); let mono_type = &monotonic.ty; + let cfgs = &monotonic.cfgs; let uninit = mk_uninit(); // For future use @@ -73,6 +74,7 @@ pub fn codegen( #[allow(non_camel_case_types)] #[allow(non_upper_case_globals)] #[doc(hidden)] + #(#cfgs)* static #instants: rtic::RacyCell<[core::mem::MaybeUninit<<#mono_type as rtic::Monotonic>::Instant>; #cap_lit]> = rtic::RacyCell::new([#(#elems,)*]); diff --git a/macros/src/codegen/timer_queue.rs b/macros/src/codegen/timer_queue.rs index 32e288c56d..f5867dc40e 100644 --- a/macros/src/codegen/timer_queue.rs +++ b/macros/src/codegen/timer_queue.rs @@ -13,7 +13,6 @@ pub fn codegen(app: &App, analysis: &Analysis, _extra: &Extra) -> Vec Vec Vec = rtic::RacyCell::new(rtic::export::TimerQueue(rtic::export::SortedLinkedList::new_u16())); )); @@ -88,6 +89,7 @@ pub fn codegen(app: &App, analysis: &Analysis, _extra: &Extra) -> Vec> = rtic::RacyCell::new(None); )); } @@ -126,6 +128,7 @@ pub fn codegen(app: &App, analysis: &Analysis, _extra: &Extra) -> Vec>(); + let cfgs = &monotonic.cfgs; let bound_interrupt = &monotonic.args.binds; let disable_isr = if &*bound_interrupt.to_string() == "SysTick" { quote!(core::mem::transmute::<_, rtic::export::SYST>(()).disable_interrupt()) @@ -136,6 +139,7 @@ pub fn codegen(app: &App, analysis: &Analysis, _extra: &Extra) -> Vec Date: Sun, 22 Jan 2023 11:32:37 +0100 Subject: [PATCH 206/423] Add example cfg-ing a Monotonic, showing limitations imposed by rtic-syntax --- examples/cfg-monotonic.rs | 121 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 121 insertions(+) create mode 100644 examples/cfg-monotonic.rs diff --git a/examples/cfg-monotonic.rs b/examples/cfg-monotonic.rs new file mode 100644 index 0000000000..88c0d6f009 --- /dev/null +++ b/examples/cfg-monotonic.rs @@ -0,0 +1,121 @@ +//! examples/cfg-monotonic.rs + +#![deny(unsafe_code)] +#![deny(warnings)] +#![deny(missing_docs)] +#![no_main] +#![no_std] + +use panic_semihosting as _; + +#[rtic::app(device = lm3s6965, dispatchers = [SSI0, QEI0])] +mod app { + use cortex_m_semihosting::{debug, hprintln}; + use systick_monotonic::*; // Implements the `Monotonic` trait + + // A monotonic timer to enable scheduling in RTIC + #[cfg(feature = "killmono")] + #[monotonic(binds = SysTick, default = true)] + type MyMono = Systick<100>; // 100 Hz / 10 ms granularity + + // Not allowed by current rtic-syntax: + // error: `#[monotonic(...)]` on a specific type must appear at most once + // --> examples/cfg-monotonic.rs:23:10 + // | + // 23 | type MyMono = Systick<100>; // 100 Hz / 10 ms granularity + // | ^^^^^^ + // #[monotonic(binds = SysTick, default = true)] + // type MyMono = Systick<100>; // 100 Hz / 10 ms granularity + + // Not allowed by current rtic-syntax: + // error: this interrupt is already bound + // --> examples/cfg-monotonic.rs:31:25 + // | + // 31 | #[monotonic(binds = SysTick, default = true)] + // | ^^^^^^^ + // #[monotonic(binds = SysTick, default = true)] + // type MyMono2 = DwtSystick<100>; // 100 Hz / 10 ms granularity + + // Resources shared between tasks + #[shared] + struct Shared { + s1: u32, + s2: i32, + } + + // Local resources to specific tasks (cannot be shared) + #[local] + struct Local { + l1: u8, + l2: i8, + } + + #[init] + fn init(cx: init::Context) -> (Shared, Local, init::Monotonics) { + let _systick = cx.core.SYST; + + // Initialize the monotonic (SysTick rate in QEMU is 12 MHz) + #[cfg(feature = "killmono")] + let mono = Systick::new(systick, 12_000_000); + + // Spawn the task `foo` directly after `init` finishes + foo::spawn().unwrap(); + + debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator + + ( + // Initialization of shared resources + Shared { s1: 0, s2: 1 }, + // Initialization of task local resources + Local { l1: 2, l2: 3 }, + // Move the monotonic timer to the RTIC run-time, this enables + // scheduling + #[cfg(feature = "killmono")] + init::Monotonics(mono), + init::Monotonics(), + ) + } + + // Background task, runs whenever no other tasks are running + #[idle] + fn idle(_: idle::Context) -> ! { + loop { + continue; + } + } + + // Software task, not bound to a hardware interrupt. + // This task takes the task local resource `l1` + // The resources `s1` and `s2` are shared between all other tasks. + #[task(shared = [s1, s2], local = [l1])] + fn foo(_: foo::Context) { + // This task is only spawned once in `init`, hence this task will run + // only once + + hprintln!("foo"); + } + + // Software task, also not bound to a hardware interrupt + // This task takes the task local resource `l2` + // The resources `s1` and `s2` are shared between all other tasks. + #[task(shared = [s1, s2], local = [l2])] + fn bar(_: bar::Context) { + hprintln!("bar"); + + // Run `bar` once per second + // bar::spawn_after(1.secs()).unwrap(); + } + + // Hardware task, bound to a hardware interrupt + // The resources `s1` and `s2` are shared between all other tasks. + #[task(binds = UART0, priority = 3, shared = [s1, s2])] + fn uart0_interrupt(_: uart0_interrupt::Context) { + // This task is bound to the interrupt `UART0` and will run + // whenever the interrupt fires + + // Note that RTIC does NOT clear the interrupt flag, this is up to the + // user + + hprintln!("UART0 interrupt!"); + } +} From 259be7bbf9cfa0ac24c276190515e988d98770b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Tj=C3=A4der?= Date: Sun, 22 Jan 2023 11:44:02 +0100 Subject: [PATCH 207/423] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5bc38d83a3..455c8987d6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ For each category, *Added*, *Changed*, *Fixed* add new entries at the top! ### Added +- CFG: Slightly improved support for #[cfg] on Monotonics - CI: Check examples also for thumbv8.{base,main} - Allow custom `link_section` attributes for late resources From 800904a1054639a090ef92e9b1e7c884ea9863e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Tj=C3=A4der?= Date: Sun, 22 Jan 2023 13:11:29 +0100 Subject: [PATCH 208/423] Handle more cfgs, support cfg on HW/SW tasks --- CHANGELOG.md | 1 + examples/cfg-whole-task.rs | 13 +++++++++++++ macros/src/codegen/assertions.rs | 2 ++ macros/src/codegen/hardware_tasks.rs | 2 ++ macros/src/codegen/module.rs | 7 +------ macros/src/codegen/pre_init.rs | 2 ++ macros/src/codegen/shared_resources_struct.rs | 15 +++++++++++++++ macros/src/codegen/software_tasks.rs | 3 +++ 8 files changed, 39 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 455c8987d6..c43a73d22d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ For each category, *Added*, *Changed*, *Fixed* add new entries at the top! ### Added +- CFG: Support #[cfg] on HW task, cleanup for SW tasks - CFG: Slightly improved support for #[cfg] on Monotonics - CI: Check examples also for thumbv8.{base,main} - Allow custom `link_section` attributes for late resources diff --git a/examples/cfg-whole-task.rs b/examples/cfg-whole-task.rs index b5b978312e..17f31f4ebb 100644 --- a/examples/cfg-whole-task.rs +++ b/examples/cfg-whole-task.rs @@ -82,6 +82,19 @@ mod app { // .. } + // The whole task should disappear, + // currently still present in the Tasks enum + #[cfg(never)] + #[task(binds = UART1, shared = [count])] + fn foo3(mut _cx: foo3::Context) { + #[cfg(debug_assertions)] + { + _cx.shared.count.lock(|count| *count += 10); + + log::spawn(_cx.shared.count.lock(|count| *count)).unwrap(); + } + } + #[cfg(debug_assertions)] #[task(capacity = 2)] fn log(_: log::Context, n: u32) { diff --git a/macros/src/codegen/assertions.rs b/macros/src/codegen/assertions.rs index 66e5409504..3e0ad61c9b 100644 --- a/macros/src/codegen/assertions.rs +++ b/macros/src/codegen/assertions.rs @@ -29,7 +29,9 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec= (#chunks_name * 32) { ::core::panic!("An interrupt out of range is used while in armv6 or armv8m.base"); } diff --git a/macros/src/codegen/hardware_tasks.rs b/macros/src/codegen/hardware_tasks.rs index 780cc7e3cc..b3f05d2af6 100644 --- a/macros/src/codegen/hardware_tasks.rs +++ b/macros/src/codegen/hardware_tasks.rs @@ -93,11 +93,13 @@ pub fn codegen( let user_hardware_task_doc = &format!(" User HW task: {name}"); if !task.is_extern { let attrs = &task.attrs; + let cfgs = &task.cfgs; let context = &task.context; let stmts = &task.stmts; user_tasks.push(quote!( #[doc = #user_hardware_task_doc] #(#attrs)* + #(#cfgs)* #[allow(non_snake_case)] fn #name(#context: #name::Context) { use rtic::Mutex as _; diff --git a/macros/src/codegen/module.rs b/macros/src/codegen/module.rs index 71bcfa8e22..8dcdbcf3e7 100644 --- a/macros/src/codegen/module.rs +++ b/macros/src/codegen/module.rs @@ -16,8 +16,6 @@ pub fn codegen( let mut module_items = vec![]; let mut fields = vec![]; let mut values = vec![]; - // Used to copy task cfgs to the whole module - let mut task_cfgs = vec![]; let name = ctxt.ident(app); @@ -208,8 +206,6 @@ pub fn codegen( let priority = spawnee.args.priority; let t = util::spawn_t_ident(priority); let cfgs = &spawnee.cfgs; - // Store a copy of the task cfgs - task_cfgs = cfgs.clone(); let (args, tupled, untupled, ty) = util::regroup_inputs(&spawnee.inputs); let args = &args; let tupled = &tupled; @@ -461,9 +457,8 @@ pub fn codegen( } else { quote!( #(#items)* - #[allow(non_snake_case)] - #(#task_cfgs)* + #(#cfgs)* #[doc = #doc] pub mod #name { #(#module_items)* diff --git a/macros/src/codegen/pre_init.rs b/macros/src/codegen/pre_init.rs index 3d541a4749..2362cb7466 100644 --- a/macros/src/codegen/pre_init.rs +++ b/macros/src/codegen/pre_init.rs @@ -16,9 +16,11 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec (TokenStream2, Context::SoftwareTask(name) => &app.software_tasks[name].args.shared_resources, }; + let v = Vec::new(); + let task_cfgs = match ctxt { + Context::HardwareTask(t) => { + &app.hardware_tasks[t].cfgs + // ... + } + Context::SoftwareTask(t) => { + &app.software_tasks[t].cfgs + // ... + } + _ => &v, + }; + let mut fields = vec![]; let mut values = vec![]; let mut has_cfgs = false; @@ -118,6 +131,7 @@ pub fn codegen(ctxt: Context, needs_lt: &mut bool, app: &App) -> (TokenStream2, #[allow(non_snake_case)] #[allow(non_camel_case_types)] #[doc = #doc] + #(#task_cfgs)* pub struct #ident<#lt> { #(#fields,)* } @@ -129,6 +143,7 @@ pub fn codegen(ctxt: Context, needs_lt: &mut bool, app: &App) -> (TokenStream2, Some(quote!(priority: &#lt rtic::export::Priority)) }; let constructor = quote!( + #(#task_cfgs)* impl<#lt> #ident<#lt> { #[doc(hidden)] #[inline(always)] diff --git a/macros/src/codegen/software_tasks.rs b/macros/src/codegen/software_tasks.rs index 6bd2a71f11..226121dd8c 100644 --- a/macros/src/codegen/software_tasks.rs +++ b/macros/src/codegen/software_tasks.rs @@ -29,6 +29,7 @@ pub fn codegen( for (name, task) in &app.software_tasks { let inputs = &task.inputs; + let cfgs = &task.cfgs; let (_, _, _, input_ty) = util::regroup_inputs(inputs); let cap = task.args.capacity; @@ -49,6 +50,7 @@ pub fn codegen( mod_app.push(quote!( // /// Queue version of a free-list that keeps track of empty slots in // /// the following buffers + #(#cfgs)* #[allow(non_camel_case_types)] #[allow(non_upper_case_globals)] #[doc(hidden)] @@ -89,6 +91,7 @@ pub fn codegen( #[allow(non_camel_case_types)] #[allow(non_upper_case_globals)] #[doc(hidden)] + #(#cfgs)* static #inputs_ident: rtic::RacyCell<[core::mem::MaybeUninit<#input_ty>; #cap_lit]> = rtic::RacyCell::new([#(#elems,)*]); )); From 04189cc6844d7d43305a57464713defb5a46d85c Mon Sep 17 00:00:00 2001 From: John van der Koijk <33966414+jvanderk@users.noreply.github.com> Date: Sun, 20 Feb 2022 19:21:25 +0100 Subject: [PATCH 209/423] Mostly editorial review. --- CHANGELOG.md | 1 + book/en/src/by-example/app_init.md | 17 ++++++---- book/en/src/by-example/app_priorities.md | 7 ++-- book/en/src/by-example/app_task.md | 15 ++++---- book/en/src/by-example/hardware_tasks.md | 20 ++++++----- book/en/src/by-example/monotonic.md | 6 +++- book/en/src/by-example/resources.md | 20 ++++++----- book/en/src/by-example/software_tasks.md | 34 +++++++++++-------- book/en/src/by-example/starting_a_project.md | 2 +- book/en/src/by-example/tips_indirection.md | 6 +++- book/en/src/by-example/tips_monotonic_impl.md | 2 +- .../src/by-example/tips_static_lifetimes.md | 2 +- book/en/src/migration/migration_v5.md | 2 ++ 13 files changed, 81 insertions(+), 53 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5bc38d83a3..cc84e15911 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ For each category, *Added*, *Changed*, *Fixed* add new entries at the top! ### Fixed - Attempt to handle docs generation enabling `deny(missing_docs)` +- Book: Editorial review - Use native GHA rustup and cargo - Distinguish between thumbv8m.base and thumbv8m.main for basepri usage. diff --git a/book/en/src/by-example/app_init.md b/book/en/src/by-example/app_init.md index 22c4a28ade..5bf6200e1c 100644 --- a/book/en/src/by-example/app_init.md +++ b/book/en/src/by-example/app_init.md @@ -1,13 +1,18 @@ # App initialization and the `#[init]` task An RTIC application requires an `init` task setting up the system. The corresponding `init` function must have the -signature `fn(init::Context) -> (Shared, Local, init::Monotonics)`, where `Shared` and `Local` are the resource +signature `fn(init::Context) -> (Shared, Local, init::Monotonics)`, where `Shared` and `Local` are resource structures defined by the user. -The `init` task executes after system reset (after the optionally defined `pre-init` and internal RTIC -initialization). The `init` task runs *with interrupts disabled* and has exclusive access to Cortex-M (the -`bare_metal::CriticalSection` token is available as `cs`) while device specific peripherals are available through -the `core` and `device` fields of `init::Context`. +The `init` task executes after system reset, [after an optionally defined `pre-init` code section][pre-init] and an always occurring internal RTIC +initialization. + +[pre-init]: https://docs.rs/cortex-m-rt/latest/cortex_m_rt/attr.pre_init.html + +The `init` and optional `pre-init` tasks runs *with interrupts disabled* and have exclusive access to Cortex-M (the +`bare_metal::CriticalSection` token is available as `cs`). + +Device specific peripherals are available through the `core` and `device` fields of `init::Context`. ## Example @@ -15,7 +20,7 @@ The example below shows the types of the `core`, `device` and `cs` fields, and s variable with `'static` lifetime. Such variables can be delegated from the `init` task to other tasks of the RTIC application. -The `device` field is available when the `peripherals` argument is set to the default value `true`. +The `device` field is only available when the `peripherals` argument is set to the default value `true`. In the rare case you want to implement an ultra-slim application you can explicitly set `peripherals` to `false`. ``` rust diff --git a/book/en/src/by-example/app_priorities.md b/book/en/src/by-example/app_priorities.md index 1a92ec846c..8cee7499e1 100644 --- a/book/en/src/by-example/app_priorities.md +++ b/book/en/src/by-example/app_priorities.md @@ -18,8 +18,8 @@ The highest static priority task takes precedence when more than one task are ready to execute. The following scenario demonstrates task prioritization: -Spawning a higher priority task A during execution of a lower priority task B pends -task A. Task A has higher priority thus preempting task B which gets suspended +Spawning a higher priority task A during execution of a lower priority task B suspends +task B. Task A has higher priority thus preempting task B which gets suspended until task A completes execution. Thus, when task A completes task B resumes execution. ```text @@ -53,7 +53,8 @@ when `baz`returns. When `bar` returns `foo` can resume. One more note about priorities: choosing a priority higher than what the device supports will result in a compilation error. -The error is cryptic due to limitations in the language, + +The error is cryptic due to limitations in the Rust language if `priority = 9` for task `uart0_interrupt` in `example/common.rs` this looks like: ```text diff --git a/book/en/src/by-example/app_task.md b/book/en/src/by-example/app_task.md index 97160041e3..d83f1ff15a 100644 --- a/book/en/src/by-example/app_task.md +++ b/book/en/src/by-example/app_task.md @@ -4,15 +4,18 @@ Tasks, defined with `#[task]`, are the main mechanism of getting work done in RT Tasks can -* Be spawned (now or in the future) -* Receive messages (message passing) -* Prioritized allowing preemptive multitasking +* Be spawned (now or in the future, also by themselves) +* Receive messages (passing messages between tasks) +* Be prioritized, allowing preemptive multitasking * Optionally bind to a hardware interrupt RTIC makes a distinction between “software tasks” and “hardware tasks”. -Hardware tasks are tasks that are bound to a specific interrupt vector in the MCU while software tasks are not. -This means that if a hardware task is bound to an UART RX interrupt the task will run every -time this interrupt triggers, usually when a character is received. +*Hardware tasks* are tasks that are bound to a specific interrupt vector in the MCU while software tasks are not. + +This means that if a hardware task is bound to, lets say, a UART RX interrupt, the task will be run every +time that interrupt triggers, usually when a character is received. + +*Software tasks* are explicitly spawned in a task, either immediately or using the Monotonic timer mechanism. In the coming pages we will explore both tasks and the different options available. diff --git a/book/en/src/by-example/hardware_tasks.md b/book/en/src/by-example/hardware_tasks.md index 7f8d3c6e14..2d405d324d 100644 --- a/book/en/src/by-example/hardware_tasks.md +++ b/book/en/src/by-example/hardware_tasks.md @@ -1,24 +1,26 @@ # Hardware tasks -At its core RTIC is using the hardware interrupt controller ([ARM NVIC on cortex-m][NVIC]) -to perform scheduling and executing tasks, and all tasks except `#[init]` and `#[idle]` +At its core RTIC is using a hardware interrupt controller ([ARM NVIC on cortex-m][NVIC]) +to schedule and start execution of tasks. All tasks except `pre-init`, `#[init]` and `#[idle]` run as interrupt handlers. -This also means that you can manually bind tasks to interrupt handlers. -To bind an interrupt use the `#[task]` attribute argument `binds = InterruptName`. -This task becomes the interrupt handler for this hardware interrupt vector. +Hardware tasks are explicitly bound to interrupt handlers. -All tasks bound to an explicit interrupt are *hardware tasks* since they +To bind a task to an interrupt, use the `#[task]` attribute argument `binds = InterruptName`. +This task then becomes the interrupt handler for this hardware interrupt vector. + +All tasks bound to an explicit interrupt are called *hardware tasks* since they start execution in reaction to a hardware event. Specifying a non-existing interrupt name will cause a compilation error. The interrupt names are commonly defined by [PAC or HAL][pacorhal] crates. -Any available interrupt vector should work, but different hardware might have -added special properties to select interrupt priority levels, such as the +Any available interrupt vector should work. Specific devices may bind +specific interrupt priorities to specific interrupt vectors outside +user code control. See for example the [nRF “softdevice”](https://github.com/rtic-rs/cortex-m-rtic/issues/434). -Beware of re-purposing interrupt vectors used internally by hardware features, +Beware of using interrupt vectors that are used internally by hardware features; RTIC is unaware of such hardware specific details. [pacorhal]: https://docs.rust-embedded.org/book/start/registers.html diff --git a/book/en/src/by-example/monotonic.md b/book/en/src/by-example/monotonic.md index 094bd5df02..3a23681fd9 100644 --- a/book/en/src/by-example/monotonic.md +++ b/book/en/src/by-example/monotonic.md @@ -1,7 +1,7 @@ # Monotonic & spawn_{at/after} The understanding of time is an important concept in embedded systems, and to be able to run tasks -based on time is useful. For this use-case the framework provides the static methods +based on time is essential. The framework provides the static methods `task::spawn_after(/* duration */)` and `task::spawn_at(/* specific time instant */)`. `spawn_after` is more commonly used, but in cases where it's needed to have spawns happen without drift or to a fixed baseline `spawn_at` is available. @@ -43,10 +43,14 @@ $ cargo run --target thumbv7m-none-eabi --example schedule {{#include ../../../../ci/expected/schedule.run}} ``` +A key requirement of a Monotonic is that it must deal gracefully with +hardware timer overruns. + ## Canceling or rescheduling a scheduled task Tasks spawned using `task::spawn_after` and `task::spawn_at` returns a `SpawnHandle`, which allows canceling or rescheduling of the task scheduled to run in the future. + If `cancel` or `reschedule_at`/`reschedule_after` returns an `Err` it means that the operation was too late and that the task is already sent for execution. The following example shows this in action: diff --git a/book/en/src/by-example/resources.md b/book/en/src/by-example/resources.md index 6349b520b5..30089d34a2 100644 --- a/book/en/src/by-example/resources.md +++ b/book/en/src/by-example/resources.md @@ -30,13 +30,13 @@ task. Thus, a task `#[local]` resource can only be accessed by one singular task. Attempting to assign the same `#[local]` resource to more than one task is a compile-time error. -Types of `#[local]` resources must implement [`Send`] trait as they are being sent from `init` -to target task and thus crossing the thread boundary. +Types of `#[local]` resources must implement a [`Send`] trait as they are being sent from `init` +to a target task, crossing a thread boundary. [`Send`]: https://doc.rust-lang.org/stable/core/marker/trait.Send.html The example application shown below contains two tasks where each task has access to its own -`#[local]` resource, plus that the `idle` task has its own `#[local]` as well. +`#[local]` resource; the `idle` task has its own `#[local]` as well. ``` rust {{#include ../../../../examples/locals.rs}} @@ -49,12 +49,14 @@ $ cargo run --target thumbv7m-none-eabi --example locals {{#include ../../../../ci/expected/locals.run}} ``` +Local resources in `#[init]` and `#[idle]` have `'static` +lifetimes. This is safe since both tasks are not re-entrant. + ### Task local initialized resources -A special use-case of local resources are the ones specified directly in the resource claim, -`#[task(local = [my_var: TYPE = INITIAL_VALUE, ...])]`, this allows for creating locals which do no need to be +Local resources can also be specified directly in the resource claim like so: +`#[task(local = [my_var: TYPE = INITIAL_VALUE, ...])]`; this allows for creating locals which do no need to be initialized in `#[init]`. -Moreover, local resources in `#[init]` and `#[idle]` have `'static` lifetimes, this is safe since both are not re-entrant. Types of `#[task(local = [..])]` resources have to be neither [`Send`] nor [`Sync`] as they are not crossing any thread boundary. @@ -92,9 +94,9 @@ preempting the critical section. This synchronization protocol is known as the [srp]: https://en.wikipedia.org/wiki/Stack_Resource_Policy In the example below we have three interrupt handlers with priorities ranging from one to three. -The two handlers with the lower priorities contend for the `shared` resource and need to lock the -resource for accessing the data. The highest priority handler, which do not access the `shared` -resource, is free to preempt the critical section created by the lowest priority handler. +The two handlers with the lower priorities contend for a `shared` resource and need to succeed in locking the +resource in order to access its data. The highest priority handler, which does not access the `shared` +resource, is free to preempt a critical section created by the lowest priority handler. ``` rust {{#include ../../../../examples/lock.rs}} diff --git a/book/en/src/by-example/software_tasks.md b/book/en/src/by-example/software_tasks.md index 5c03f9140b..8ee185bd15 100644 --- a/book/en/src/by-example/software_tasks.md +++ b/book/en/src/by-example/software_tasks.md @@ -2,29 +2,33 @@ The RTIC concept of a software task shares a lot with that of [hardware tasks](./hardware_tasks.md) with the core difference that a software task is not explicitly bound to a specific -interrupt vector, but rather a “dispatcher” interrupt vector running -at the same priority as the software task. +interrupt vector, but rather bound to a “dispatcher” interrupt vector running +at the intended priority of the software task (see below). -Thus, software tasks are tasks which are not directly assigned to a specific interrupt vector. +Thus, software tasks are tasks which are not *directly* bound to an interrupt vector. -The `#[task]` attribute used on a function declare it as a software tasks. -Observe the absence of a `binds = InterruptName` argument to the attribute. -The static method `task_name::spawn()` spawns (starts) a software task and -given that there are no higher priority tasks running the task will start executing directly. +The `#[task]` attributes used on a function determine if it is +software tasks, specifically the absence of a `binds = InterruptName` +argument to the attribute definition. -All software tasks at the same priority level shares an interrupt handler acting as a dispatcher. -What differentiates software and hardware tasks are the dispatcher versus bound interrupt vector. +The static method `task_name::spawn()` spawns (schedules) a software +task by registering it with a specific dispatcher. If there are no +higher priority tasks available to the scheduler (which serves a set +of dispatchers), the task will start executing directly. -The interrupt vectors used as dispatchers can not be used by hardware tasks. +All software tasks at the same priority level share an interrupt handler bound to their dispatcher. +What differentiates software and hardware tasks is the usage of either a dispatcher or a bound interrupt vector. -A list of “free” (not in use by hardware tasks) and usable interrupts allows the framework -to dispatch software tasks. +The interrupt vectors used as dispatchers cannot be used by hardware tasks. -This list of dispatchers, `dispatchers = [FreeInterrupt1, FreeInterrupt2, ...]` is an +Availability of a set of “free” (not in use by hardware tasks) and usable interrupt vectors allows the framework +to dispatch software tasks via dedicated interrupt handlers. + +This set of dispatchers, `dispatchers = [FreeInterrupt1, FreeInterrupt2, ...]` is an argument to the `#[app]` attribute. -Each interrupt vector acting as dispatcher gets assigned to one priority level meaning that -the list of dispatchers need to cover all priority levels used by software tasks. +Each interrupt vector acting as dispatcher gets assigned to a unique priority level meaning that +the list of dispatchers needs to cover all priority levels used by software tasks. Example: The `dispatchers =` argument needs to have at least 3 entries for an application using three different priorities for software tasks. diff --git a/book/en/src/by-example/starting_a_project.md b/book/en/src/by-example/starting_a_project.md index ccb0083c07..fe7be57818 100644 --- a/book/en/src/by-example/starting_a_project.md +++ b/book/en/src/by-example/starting_a_project.md @@ -8,7 +8,7 @@ If you are targeting ARMv6-M or ARMv8-M-base architecture, check out the section [`defmt-app-template`]: https://github.com/rtic-rs/defmt-app-template This will give you an RTIC application with support for RTT logging with [`defmt`] and stack overflow -protection using [`flip-link`]. There are also a multitude of examples available provided by the community: +protection using [`flip-link`]. There is also a multitude of examples provided by the community: - [`rtic-examples`] - Multiple projects - [https://github.com/kalkyl/f411-rtic](https://github.com/kalkyl/f411-rtic) diff --git a/book/en/src/by-example/tips_indirection.md b/book/en/src/by-example/tips_indirection.md index 1a330c5162..567a5e723b 100644 --- a/book/en/src/by-example/tips_indirection.md +++ b/book/en/src/by-example/tips_indirection.md @@ -9,12 +9,16 @@ Indirection can minimize message passing overhead: instead of sending the buffer by value, one can send an owning pointer into the buffer. -One can use a global allocator to achieve indirection (`alloc::Box`, +One can use a global memory allocator to achieve indirection (`alloc::Box`, `alloc::Rc`, etc.), which requires using the nightly channel as of Rust v1.37.0, or one can use a statically allocated memory pool like [`heapless::Pool`]. [`heapless::Pool`]: https://docs.rs/heapless/0.5.0/heapless/pool/index.html +As this example of approach goes completely outside of RTIC resource +model with shared and local the program would rely on the correctness +of the memory allocator, in this case `heapless::pool`. + Here's an example where `heapless::Pool` is used to "box" buffers of 128 bytes. ``` rust diff --git a/book/en/src/by-example/tips_monotonic_impl.md b/book/en/src/by-example/tips_monotonic_impl.md index d97b5839b0..7c3449b2b3 100644 --- a/book/en/src/by-example/tips_monotonic_impl.md +++ b/book/en/src/by-example/tips_monotonic_impl.md @@ -11,7 +11,7 @@ Moreover, the relation between time and timers used for scheduling was difficult For RTIC 1.0 we instead assume the user has a time library, e.g. [`fugit`] or [`embedded_time`], as the basis for all time-based operations when implementing `Monotonic`. -This makes it much easier to correctly implement the `Monotonic` trait allowing the use of +These libraries make it much easier to correctly implement the `Monotonic` trait, allowing the use of almost any timer in the system for scheduling. The trait documents the requirements for each method, diff --git a/book/en/src/by-example/tips_static_lifetimes.md b/book/en/src/by-example/tips_static_lifetimes.md index 8d3a832c4e..dadd9c9461 100644 --- a/book/en/src/by-example/tips_static_lifetimes.md +++ b/book/en/src/by-example/tips_static_lifetimes.md @@ -1,6 +1,6 @@ # 'static super-powers -In `#[init]` and `#[idle]` `local` resources has `'static` lifetime. +In `#[init]` and `#[idle]` `local` resources have `'static` lifetime. Useful when pre-allocating and/or splitting resources between tasks, drivers or some other object. diff --git a/book/en/src/migration/migration_v5.md b/book/en/src/migration/migration_v5.md index 731931f013..5a8fabce5b 100644 --- a/book/en/src/migration/migration_v5.md +++ b/book/en/src/migration/migration_v5.md @@ -368,3 +368,5 @@ Both software and hardware tasks can now be defined external to the `mod app`. Previously this was possible only by implementing a trampoline calling out the task implementation. See examples `examples/extern_binds.rs` and `examples/extern_spawn.rs`. + +This enables breaking apps into multiple files. From dbc6964f8851ab9a54b55bd2620d5e1096e30f12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Tj=C3=A4der?= Date: Sun, 26 Feb 2023 17:19:18 +0100 Subject: [PATCH 210/423] example: pool: Do not print the addr Unstable and prone to fail CI --- ci/expected/pool.run | 2 -- examples/pool.rs | 12 ++++-------- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/ci/expected/pool.run b/ci/expected/pool.run index 81f79d41e9..e69de29bb2 100644 --- a/ci/expected/pool.run +++ b/ci/expected/pool.run @@ -1,2 +0,0 @@ -bar(0x20000088) -foo(0x2000010c) diff --git a/examples/pool.rs b/examples/pool.rs index ab76370e71..4c551bef06 100644 --- a/examples/pool.rs +++ b/examples/pool.rs @@ -20,7 +20,7 @@ pool!(P: [u8; 128]); #[app(device = lm3s6965, dispatchers = [SSI0, QEI0])] mod app { use crate::{Box, Pool}; - use cortex_m_semihosting::{debug, hprintln}; + use cortex_m_semihosting::debug; use lm3s6965::Interrupt; // Import the memory pool into scope @@ -57,19 +57,15 @@ mod app { } #[task] - fn foo(_: foo::Context, x: Box

) { - hprintln!("foo({:?})", x.as_ptr()); - + fn foo(_: foo::Context, _x: Box

) { // explicitly return the block to the pool - drop(x); + drop(_x); debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator } #[task(priority = 2)] - fn bar(_: bar::Context, x: Box

) { - hprintln!("bar({:?})", x.as_ptr()); - + fn bar(_: bar::Context, _x: Box

) { // this is done automatically so we can omit the call to `drop` // drop(x); } From e1a0987dc2cff288a35359db48c3a7ca7e2807dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Tj=C3=A4der?= Date: Sun, 26 Feb 2023 16:47:24 +0100 Subject: [PATCH 211/423] Release: v1.1.4 --- CHANGELOG.md | 11 ++++++++++- Cargo.toml | 4 ++-- macros/Cargo.toml | 4 ++-- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9432cab7bd..d17227813a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,14 @@ For each category, *Added*, *Changed*, *Fixed* add new entries at the top! ### Added +### Fixed + +### Changed + +## [v1.1.4] - 2023-02-26 + +### Added + - CFG: Support #[cfg] on HW task, cleanup for SW tasks - CFG: Slightly improved support for #[cfg] on Monotonics - CI: Check examples also for thumbv8.{base,main} @@ -551,7 +559,8 @@ Yanked due to a soundness issue in `init`; the issue has been mostly fixed in v0 - Initial release -[Unreleased]: https://github.com/rtic-rs/cortex-m-rtic/compare/v1.1.3...HEAD +[Unreleased]: https://github.com/rtic-rs/cortex-m-rtic/compare/v1.1.4...HEAD +[v1.1.4]: https://github.com/rtic-rs/cortex-m-rtic/compare/v1.1.3...v1.1.4 [v1.1.3]: https://github.com/rtic-rs/cortex-m-rtic/compare/v1.1.2...v1.1.3 [v1.1.2]: https://github.com/rtic-rs/cortex-m-rtic/compare/v1.1.1...v1.1.2 [v1.1.1]: https://github.com/rtic-rs/cortex-m-rtic/compare/v1.1.0...v1.1.1 diff --git a/Cargo.toml b/Cargo.toml index 4dbc243155..68fea4cb2c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,14 +14,14 @@ name = "cortex-m-rtic" readme = "README.md" repository = "https://github.com/rtic-rs/cortex-m-rtic" -version = "1.1.3" +version = "1.1.4" [lib] name = "rtic" [dependencies] cortex-m = "0.7.0" -cortex-m-rtic-macros = { path = "macros", version = "1.1.5" } +cortex-m-rtic-macros = { path = "macros", version = "1.1.6" } rtic-monotonic = "1.0.0" rtic-core = "1.0.0" heapless = "0.7.7" diff --git a/macros/Cargo.toml b/macros/Cargo.toml index f1111e6ca1..c3f0561485 100644 --- a/macros/Cargo.toml +++ b/macros/Cargo.toml @@ -12,7 +12,7 @@ license = "MIT OR Apache-2.0" name = "cortex-m-rtic-macros" readme = "../README.md" repository = "https://github.com/rtic-rs/cortex-m-rtic" -version = "1.1.5" +version = "1.1.6" [lib] proc-macro = true @@ -22,7 +22,7 @@ proc-macro2 = "1" proc-macro-error = "1" quote = "1" syn = "1" -rtic-syntax = "1.0.2" +rtic-syntax = "1.0.3" [features] debugprint = [] From 7614b96fe45240dafe91ae549e712b560e2d4c10 Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Sat, 31 Dec 2022 14:45:13 +0100 Subject: [PATCH 212/423] RTIC v2: Initial commit rtic-syntax is now part of RTIC repository --- Cargo.toml | 14 +- macros/.gitignore | 2 + macros/Cargo.toml | 43 +- macros/src/analyze.rs | 42 +- macros/src/bindings.rs | 0 macros/src/codegen.rs | 95 +-- macros/src/codegen/assertions.rs | 10 +- macros/src/codegen/async_dispatchers.rs | 129 +++++ macros/src/codegen/dispatchers.rs | 67 ++- macros/src/codegen/hardware_tasks.rs | 16 +- macros/src/codegen/idle.rs | 15 +- macros/src/codegen/init.rs | 16 +- macros/src/codegen/local_resources.rs | 6 +- macros/src/codegen/local_resources_struct.rs | 19 +- macros/src/codegen/module.rs | 257 +++++---- macros/src/codegen/monotonic.rs | 280 +++++++++ macros/src/codegen/post_init.rs | 17 +- macros/src/codegen/pre_init.rs | 22 +- macros/src/codegen/shared_resources.rs | 18 +- macros/src/codegen/shared_resources_struct.rs | 40 +- macros/src/codegen/software_tasks.rs | 132 +++-- macros/src/codegen/timer_queue.rs | 33 +- macros/src/codegen/util.rs | 43 +- macros/src/lib.rs | 84 ++- macros/src/syntax.rs | 158 +++++ macros/src/syntax/.github/bors.toml | 3 + macros/src/syntax/.github/workflows/build.yml | 213 +++++++ .../syntax/.github/workflows/changelog.yml | 28 + .../properties/build.properties.json | 6 + macros/src/syntax/.gitignore | 4 + macros/src/syntax/.travis.yml | 31 + macros/src/syntax/accessors.rs | 113 ++++ macros/src/syntax/analyze.rs | 448 +++++++++++++++ macros/src/syntax/ast.rs | 380 ++++++++++++ macros/src/syntax/check.rs | 66 +++ macros/src/syntax/optimize.rs | 36 ++ macros/src/syntax/parse.rs | 520 +++++++++++++++++ macros/src/syntax/parse/app.rs | 539 ++++++++++++++++++ macros/src/syntax/parse/hardware_task.rs | 96 ++++ macros/src/syntax/parse/idle.rs | 45 ++ macros/src/syntax/parse/init.rs | 52 ++ macros/src/syntax/parse/monotonic.rs | 42 ++ macros/src/syntax/parse/resource.rs | 55 ++ macros/src/syntax/parse/software_task.rs | 86 +++ macros/src/syntax/parse/util.rs | 338 +++++++++++ macros/tests/ui.rs | 7 + macros/ui/async-local-resouces.rs | 28 + macros/ui/async-local-resouces.stderr | 5 + macros/ui/async-zero-prio-tasks.rs | 19 + macros/ui/async-zero-prio-tasks.stderr | 11 + macros/ui/extern-interrupt-used.rs | 16 + macros/ui/extern-interrupt-used.stderr | 5 + macros/ui/idle-double-local.rs | 9 + macros/ui/idle-double-local.stderr | 5 + macros/ui/idle-double-shared.rs | 9 + macros/ui/idle-double-shared.stderr | 5 + macros/ui/idle-input.rs | 9 + macros/ui/idle-input.stderr | 5 + macros/ui/idle-no-context.rs | 9 + macros/ui/idle-no-context.stderr | 5 + macros/ui/idle-not-divergent.rs | 7 + macros/ui/idle-not-divergent.stderr | 5 + macros/ui/idle-output.rs | 9 + macros/ui/idle-output.stderr | 5 + macros/ui/idle-pub.rs | 9 + macros/ui/idle-pub.stderr | 5 + macros/ui/idle-unsafe.rs | 9 + macros/ui/idle-unsafe.stderr | 5 + macros/ui/init-divergent.rs | 13 + macros/ui/init-divergent.stderr | 5 + macros/ui/init-double-local.rs | 7 + macros/ui/init-double-local.stderr | 5 + macros/ui/init-double-shared.rs | 7 + macros/ui/init-double-shared.stderr | 5 + macros/ui/init-input.rs | 13 + macros/ui/init-input.stderr | 5 + macros/ui/init-no-context.rs | 13 + macros/ui/init-no-context.stderr | 5 + macros/ui/init-output.rs | 9 + macros/ui/init-output.stderr | 5 + macros/ui/init-pub.rs | 13 + macros/ui/init-pub.stderr | 5 + macros/ui/init-unsafe.rs | 7 + macros/ui/init-unsafe.stderr | 5 + macros/ui/interrupt-double.rs | 10 + macros/ui/interrupt-double.stderr | 5 + macros/ui/local-collision-2.rs | 21 + macros/ui/local-collision-2.stderr | 17 + macros/ui/local-collision.rs | 21 + macros/ui/local-collision.stderr | 11 + macros/ui/local-malformed-1.rs | 16 + macros/ui/local-malformed-1.stderr | 5 + macros/ui/local-malformed-2.rs | 16 + macros/ui/local-malformed-2.stderr | 5 + macros/ui/local-malformed-3.rs | 16 + macros/ui/local-malformed-3.stderr | 5 + macros/ui/local-malformed-4.rs | 16 + macros/ui/local-malformed-4.stderr | 5 + macros/ui/local-not-declared.rs | 16 + macros/ui/local-not-declared.stderr | 5 + macros/ui/local-pub.rs | 9 + macros/ui/local-pub.stderr | 5 + macros/ui/local-shared-attribute.rs | 14 + macros/ui/local-shared-attribute.stderr | 5 + macros/ui/local-shared.rs | 28 + macros/ui/local-shared.stderr | 11 + macros/ui/monotonic-binds-collision-task.rs | 10 + .../ui/monotonic-binds-collision-task.stderr | 5 + macros/ui/monotonic-binds-collision.rs | 10 + macros/ui/monotonic-binds-collision.stderr | 5 + macros/ui/monotonic-double-binds.rs | 7 + macros/ui/monotonic-double-binds.stderr | 5 + macros/ui/monotonic-double-default.rs | 7 + macros/ui/monotonic-double-default.stderr | 5 + macros/ui/monotonic-double-prio.rs | 7 + macros/ui/monotonic-double-prio.stderr | 5 + macros/ui/monotonic-double.rs | 10 + macros/ui/monotonic-double.stderr | 5 + macros/ui/monotonic-name-collision.rs | 10 + macros/ui/monotonic-name-collision.stderr | 5 + macros/ui/monotonic-no-binds.rs | 7 + macros/ui/monotonic-no-binds.stderr | 5 + macros/ui/monotonic-no-paran.rs | 8 + macros/ui/monotonic-no-paran.stderr | 5 + macros/ui/monotonic-timer-collision.rs | 10 + macros/ui/monotonic-timer-collision.stderr | 5 + macros/ui/monotonic-with-attrs.rs | 8 + macros/ui/monotonic-with-attrs.stderr | 5 + macros/ui/pub-local.stderr | 5 + macros/ui/pub-shared.stderr | 5 + macros/ui/shared-lock-free.rs | 38 ++ macros/ui/shared-lock-free.stderr | 17 + macros/ui/shared-not-declared.rs | 16 + macros/ui/shared-not-declared.stderr | 5 + macros/ui/shared-pub.rs | 9 + macros/ui/shared-pub.stderr | 5 + macros/ui/task-bind.rs | 7 + macros/ui/task-bind.stderr | 5 + macros/ui/task-divergent.rs | 9 + macros/ui/task-divergent.stderr | 5 + macros/ui/task-double-capacity.rs | 7 + macros/ui/task-double-capacity.stderr | 5 + macros/ui/task-double-local.rs | 7 + macros/ui/task-double-local.stderr | 5 + macros/ui/task-double-priority.rs | 7 + macros/ui/task-double-priority.stderr | 5 + macros/ui/task-double-shared.rs | 7 + macros/ui/task-double-shared.stderr | 5 + macros/ui/task-idle.rs | 13 + macros/ui/task-idle.stderr | 5 + macros/ui/task-init.rs | 17 + macros/ui/task-init.stderr | 5 + macros/ui/task-interrupt-same-prio-spawn.rs | 7 + .../ui/task-interrupt-same-prio-spawn.stderr | 5 + macros/ui/task-interrupt.rs | 10 + macros/ui/task-interrupt.stderr | 5 + macros/ui/task-no-context.rs | 7 + macros/ui/task-no-context.stderr | 5 + macros/ui/task-priority-too-high.rs | 7 + macros/ui/task-priority-too-high.stderr | 5 + macros/ui/task-priority-too-low.rs | 7 + macros/ui/task-priority-too-low.stderr | 5 + macros/ui/task-pub.rs | 7 + macros/ui/task-pub.stderr | 5 + macros/ui/task-unsafe.rs | 7 + macros/ui/task-unsafe.stderr | 5 + src/lib.rs | 127 +---- 167 files changed, 5219 insertions(+), 602 deletions(-) create mode 100644 macros/.gitignore create mode 100644 macros/src/bindings.rs create mode 100644 macros/src/codegen/async_dispatchers.rs create mode 100644 macros/src/codegen/monotonic.rs create mode 100644 macros/src/syntax.rs create mode 100644 macros/src/syntax/.github/bors.toml create mode 100644 macros/src/syntax/.github/workflows/build.yml create mode 100644 macros/src/syntax/.github/workflows/changelog.yml create mode 100644 macros/src/syntax/.github/workflows/properties/build.properties.json create mode 100644 macros/src/syntax/.gitignore create mode 100644 macros/src/syntax/.travis.yml create mode 100644 macros/src/syntax/accessors.rs create mode 100644 macros/src/syntax/analyze.rs create mode 100644 macros/src/syntax/ast.rs create mode 100644 macros/src/syntax/check.rs create mode 100644 macros/src/syntax/optimize.rs create mode 100644 macros/src/syntax/parse.rs create mode 100644 macros/src/syntax/parse/app.rs create mode 100644 macros/src/syntax/parse/hardware_task.rs create mode 100644 macros/src/syntax/parse/idle.rs create mode 100644 macros/src/syntax/parse/init.rs create mode 100644 macros/src/syntax/parse/monotonic.rs create mode 100644 macros/src/syntax/parse/resource.rs create mode 100644 macros/src/syntax/parse/software_task.rs create mode 100644 macros/src/syntax/parse/util.rs create mode 100644 macros/tests/ui.rs create mode 100644 macros/ui/async-local-resouces.rs create mode 100644 macros/ui/async-local-resouces.stderr create mode 100644 macros/ui/async-zero-prio-tasks.rs create mode 100644 macros/ui/async-zero-prio-tasks.stderr create mode 100644 macros/ui/extern-interrupt-used.rs create mode 100644 macros/ui/extern-interrupt-used.stderr create mode 100644 macros/ui/idle-double-local.rs create mode 100644 macros/ui/idle-double-local.stderr create mode 100644 macros/ui/idle-double-shared.rs create mode 100644 macros/ui/idle-double-shared.stderr create mode 100644 macros/ui/idle-input.rs create mode 100644 macros/ui/idle-input.stderr create mode 100644 macros/ui/idle-no-context.rs create mode 100644 macros/ui/idle-no-context.stderr create mode 100644 macros/ui/idle-not-divergent.rs create mode 100644 macros/ui/idle-not-divergent.stderr create mode 100644 macros/ui/idle-output.rs create mode 100644 macros/ui/idle-output.stderr create mode 100644 macros/ui/idle-pub.rs create mode 100644 macros/ui/idle-pub.stderr create mode 100644 macros/ui/idle-unsafe.rs create mode 100644 macros/ui/idle-unsafe.stderr create mode 100644 macros/ui/init-divergent.rs create mode 100644 macros/ui/init-divergent.stderr create mode 100644 macros/ui/init-double-local.rs create mode 100644 macros/ui/init-double-local.stderr create mode 100644 macros/ui/init-double-shared.rs create mode 100644 macros/ui/init-double-shared.stderr create mode 100644 macros/ui/init-input.rs create mode 100644 macros/ui/init-input.stderr create mode 100644 macros/ui/init-no-context.rs create mode 100644 macros/ui/init-no-context.stderr create mode 100644 macros/ui/init-output.rs create mode 100644 macros/ui/init-output.stderr create mode 100644 macros/ui/init-pub.rs create mode 100644 macros/ui/init-pub.stderr create mode 100644 macros/ui/init-unsafe.rs create mode 100644 macros/ui/init-unsafe.stderr create mode 100644 macros/ui/interrupt-double.rs create mode 100644 macros/ui/interrupt-double.stderr create mode 100644 macros/ui/local-collision-2.rs create mode 100644 macros/ui/local-collision-2.stderr create mode 100644 macros/ui/local-collision.rs create mode 100644 macros/ui/local-collision.stderr create mode 100644 macros/ui/local-malformed-1.rs create mode 100644 macros/ui/local-malformed-1.stderr create mode 100644 macros/ui/local-malformed-2.rs create mode 100644 macros/ui/local-malformed-2.stderr create mode 100644 macros/ui/local-malformed-3.rs create mode 100644 macros/ui/local-malformed-3.stderr create mode 100644 macros/ui/local-malformed-4.rs create mode 100644 macros/ui/local-malformed-4.stderr create mode 100644 macros/ui/local-not-declared.rs create mode 100644 macros/ui/local-not-declared.stderr create mode 100644 macros/ui/local-pub.rs create mode 100644 macros/ui/local-pub.stderr create mode 100644 macros/ui/local-shared-attribute.rs create mode 100644 macros/ui/local-shared-attribute.stderr create mode 100644 macros/ui/local-shared.rs create mode 100644 macros/ui/local-shared.stderr create mode 100644 macros/ui/monotonic-binds-collision-task.rs create mode 100644 macros/ui/monotonic-binds-collision-task.stderr create mode 100644 macros/ui/monotonic-binds-collision.rs create mode 100644 macros/ui/monotonic-binds-collision.stderr create mode 100644 macros/ui/monotonic-double-binds.rs create mode 100644 macros/ui/monotonic-double-binds.stderr create mode 100644 macros/ui/monotonic-double-default.rs create mode 100644 macros/ui/monotonic-double-default.stderr create mode 100644 macros/ui/monotonic-double-prio.rs create mode 100644 macros/ui/monotonic-double-prio.stderr create mode 100644 macros/ui/monotonic-double.rs create mode 100644 macros/ui/monotonic-double.stderr create mode 100644 macros/ui/monotonic-name-collision.rs create mode 100644 macros/ui/monotonic-name-collision.stderr create mode 100644 macros/ui/monotonic-no-binds.rs create mode 100644 macros/ui/monotonic-no-binds.stderr create mode 100644 macros/ui/monotonic-no-paran.rs create mode 100644 macros/ui/monotonic-no-paran.stderr create mode 100644 macros/ui/monotonic-timer-collision.rs create mode 100644 macros/ui/monotonic-timer-collision.stderr create mode 100644 macros/ui/monotonic-with-attrs.rs create mode 100644 macros/ui/monotonic-with-attrs.stderr create mode 100644 macros/ui/pub-local.stderr create mode 100644 macros/ui/pub-shared.stderr create mode 100644 macros/ui/shared-lock-free.rs create mode 100644 macros/ui/shared-lock-free.stderr create mode 100644 macros/ui/shared-not-declared.rs create mode 100644 macros/ui/shared-not-declared.stderr create mode 100644 macros/ui/shared-pub.rs create mode 100644 macros/ui/shared-pub.stderr create mode 100644 macros/ui/task-bind.rs create mode 100644 macros/ui/task-bind.stderr create mode 100644 macros/ui/task-divergent.rs create mode 100644 macros/ui/task-divergent.stderr create mode 100644 macros/ui/task-double-capacity.rs create mode 100644 macros/ui/task-double-capacity.stderr create mode 100644 macros/ui/task-double-local.rs create mode 100644 macros/ui/task-double-local.stderr create mode 100644 macros/ui/task-double-priority.rs create mode 100644 macros/ui/task-double-priority.stderr create mode 100644 macros/ui/task-double-shared.rs create mode 100644 macros/ui/task-double-shared.stderr create mode 100644 macros/ui/task-idle.rs create mode 100644 macros/ui/task-idle.stderr create mode 100644 macros/ui/task-init.rs create mode 100644 macros/ui/task-init.stderr create mode 100644 macros/ui/task-interrupt-same-prio-spawn.rs create mode 100644 macros/ui/task-interrupt-same-prio-spawn.stderr create mode 100644 macros/ui/task-interrupt.rs create mode 100644 macros/ui/task-interrupt.stderr create mode 100644 macros/ui/task-no-context.rs create mode 100644 macros/ui/task-no-context.stderr create mode 100644 macros/ui/task-priority-too-high.rs create mode 100644 macros/ui/task-priority-too-high.stderr create mode 100644 macros/ui/task-priority-too-low.rs create mode 100644 macros/ui/task-priority-too-low.stderr create mode 100644 macros/ui/task-pub.rs create mode 100644 macros/ui/task-pub.stderr create mode 100644 macros/ui/task-unsafe.rs create mode 100644 macros/ui/task-unsafe.stderr diff --git a/Cargo.toml b/Cargo.toml index 68fea4cb2c..d995de45e4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,27 +1,29 @@ [package] authors = [ "The Real-Time Interrupt-driven Concurrency developers", + "Emil Fresk ", + "Henrik Tjäder ", "Jorge Aparicio ", "Per Lindgren ", ] -categories = ["concurrency", "embedded", "no-std"] +categories = ["concurrency", "embedded", "no-std", "asynchronous"] description = "Real-Time Interrupt-driven Concurrency (RTIC): a concurrency framework for building real-time systems" documentation = "https://rtic.rs/" edition = "2021" -keywords = ["arm", "cortex-m"] +keywords = ["arm", "cortex-m", "risc-v", "embedded", "async", "runtime", "futures", "await", "no-std", "rtos", "bare-metal"] license = "MIT OR Apache-2.0" -name = "cortex-m-rtic" +name = "rtic" readme = "README.md" -repository = "https://github.com/rtic-rs/cortex-m-rtic" +repository = "https://github.com/rtic-rs/rtic" -version = "1.1.4" +version = "2.0.0-alpha.0" [lib] name = "rtic" [dependencies] cortex-m = "0.7.0" -cortex-m-rtic-macros = { path = "macros", version = "1.1.6" } +rtic-macros = { path = "macros", version = "2.0.0-alpha.0" } rtic-monotonic = "1.0.0" rtic-core = "1.0.0" heapless = "0.7.7" diff --git a/macros/.gitignore b/macros/.gitignore new file mode 100644 index 0000000000..4fffb2f89c --- /dev/null +++ b/macros/.gitignore @@ -0,0 +1,2 @@ +/target +/Cargo.lock diff --git a/macros/Cargo.toml b/macros/Cargo.toml index c3f0561485..1cc955657b 100644 --- a/macros/Cargo.toml +++ b/macros/Cargo.toml @@ -1,28 +1,41 @@ [package] authors = [ "The Real-Time Interrupt-driven Concurrency developers", + "Emil Fresk ", + "Henrik Tjäder ", "Jorge Aparicio ", + "Per Lindgren ", ] -categories = ["concurrency", "embedded", "no-std"] -description = "Procedural macros of the cortex-m-rtic crate" -documentation = "https://rtic-rs.github.io/cortex-m-rtic/api/cortex_m_rtic" +categories = ["concurrency", "embedded", "no-std", "asynchronous"] +description = "Procedural macros, syntax parsing, and codegen of the RTIC crate" +documentation = "https://rtic-rs.github.io/rtic/api/rtic" edition = "2021" -keywords = ["arm", "cortex-m"] +keywords = ["arm", "cortex-m", "risc-v", "embedded", "async", "runtime", "futures", "await", "no-std", "rtos", "bare-metal"] license = "MIT OR Apache-2.0" -name = "cortex-m-rtic-macros" +name = "rtic-macros" readme = "../README.md" -repository = "https://github.com/rtic-rs/cortex-m-rtic" -version = "1.1.6" +repository = "https://github.com/rtic-rs/rtic" + +version = "2.0.0-alpha.0" [lib] proc-macro = true -[dependencies] -proc-macro2 = "1" -proc-macro-error = "1" -quote = "1" -syn = "1" -rtic-syntax = "1.0.3" - -[features] +[feature] +default = [] debugprint = [] +# list of supported codegen backends +thumbv6 = [] +thumbv7 = [] +# riscv-clic = [] +# riscv-ch32 = [] + +[dependencies] +indexmap = "1.9.2" +proc-macro2 = "1.0.49" +proc-macro-error = "1.0.4" +quote = "1.0.23" +syn = { version = "1.0.107", features = ["extra-traits", "full"] } + +[dev-dependencies] +trybuild = "1.0.73" diff --git a/macros/src/analyze.rs b/macros/src/analyze.rs index d255b7f5bc..ec12cfb4dc 100644 --- a/macros/src/analyze.rs +++ b/macros/src/analyze.rs @@ -1,17 +1,17 @@ use core::ops; use std::collections::{BTreeMap, BTreeSet}; -use rtic_syntax::{ +use crate::syntax::{ analyze::{self, Priority}, - ast::{App, ExternInterrupt}, - P, + ast::{App, Dispatcher}, }; use syn::Ident; /// Extend the upstream `Analysis` struct with our field pub struct Analysis { - parent: P, - pub interrupts: BTreeMap, + parent: analyze::Analysis, + pub interrupts_normal: BTreeMap, + pub interrupts_async: BTreeMap, } impl ops::Deref for Analysis { @@ -23,25 +23,43 @@ impl ops::Deref for Analysis { } // Assign an interrupt to each priority level -pub fn app(analysis: P, app: &App) -> P { +pub fn app(analysis: analyze::Analysis, app: &App) -> Analysis { + let mut available_interrupt = app.args.dispatchers.clone(); + // the set of priorities (each priority only once) let priorities = app .software_tasks .values() + .filter(|task| !task.is_async) + .map(|task| task.args.priority) + .collect::>(); + + let priorities_async = app + .software_tasks + .values() + .filter(|task| task.is_async) .map(|task| task.args.priority) .collect::>(); // map from priorities to interrupts (holding name and attributes) - let interrupts: BTreeMap = priorities + + let interrupts_normal: BTreeMap = priorities .iter() .copied() .rev() - .zip(&app.args.extern_interrupts) - .map(|(p, (id, ext))| (p, (id.clone(), ext.clone()))) + .map(|p| (p, available_interrupt.pop().expect("UNREACHABLE"))) .collect(); - P::new(Analysis { + let interrupts_async: BTreeMap = priorities_async + .iter() + .copied() + .rev() + .map(|p| (p, available_interrupt.pop().expect("UNREACHABLE"))) + .collect(); + + Analysis { parent: analysis, - interrupts, - }) + interrupts_normal, + interrupts_async, + } } diff --git a/macros/src/bindings.rs b/macros/src/bindings.rs new file mode 100644 index 0000000000..e69de29bb2 diff --git a/macros/src/codegen.rs b/macros/src/codegen.rs index 89173d450e..ef817325d8 100644 --- a/macros/src/codegen.rs +++ b/macros/src/codegen.rs @@ -1,10 +1,11 @@ use proc_macro2::TokenStream as TokenStream2; use quote::quote; -use rtic_syntax::ast::App; -use crate::{analyze::Analysis, check::Extra}; +use crate::analyze::Analysis; +use crate::syntax::ast::App; mod assertions; +mod async_dispatchers; mod dispatchers; mod hardware_tasks; mod idle; @@ -12,6 +13,7 @@ mod init; mod local_resources; mod local_resources_struct; mod module; +mod monotonic; mod post_init; mod pre_init; mod shared_resources; @@ -21,22 +23,22 @@ mod timer_queue; mod util; #[allow(clippy::too_many_lines)] -pub fn app(app: &App, analysis: &Analysis, extra: &Extra) -> TokenStream2 { +pub fn app(app: &App, analysis: &Analysis) -> TokenStream2 { let mut mod_app = vec![]; let mut mains = vec![]; let mut root = vec![]; let mut user = vec![]; // Generate the `main` function - let assertion_stmts = assertions::codegen(app, analysis, extra); + let assertion_stmts = assertions::codegen(app, analysis); - let pre_init_stmts = pre_init::codegen(app, analysis, extra); + let pre_init_stmts = pre_init::codegen(app, analysis); - let (mod_app_init, root_init, user_init, call_init) = init::codegen(app, analysis, extra); + let (mod_app_init, root_init, user_init, call_init) = init::codegen(app, analysis); let post_init_stmts = post_init::codegen(app, analysis); - let (mod_app_idle, root_idle, user_idle, call_idle) = idle::codegen(app, analysis, extra); + let (mod_app_idle, root_idle, user_idle, call_idle) = idle::codegen(app, analysis); user.push(quote!( #user_init @@ -84,82 +86,25 @@ pub fn app(app: &App, analysis: &Analysis, extra: &Extra) -> TokenStream2 { } )); - let (mod_app_shared_resources, mod_shared_resources) = - shared_resources::codegen(app, analysis, extra); - let (mod_app_local_resources, mod_local_resources) = - local_resources::codegen(app, analysis, extra); + let (mod_app_shared_resources, mod_shared_resources) = shared_resources::codegen(app, analysis); + let (mod_app_local_resources, mod_local_resources) = local_resources::codegen(app, analysis); let (mod_app_hardware_tasks, root_hardware_tasks, user_hardware_tasks) = - hardware_tasks::codegen(app, analysis, extra); + hardware_tasks::codegen(app, analysis); let (mod_app_software_tasks, root_software_tasks, user_software_tasks) = - software_tasks::codegen(app, analysis, extra); + software_tasks::codegen(app, analysis); - let mod_app_dispatchers = dispatchers::codegen(app, analysis, extra); - let mod_app_timer_queue = timer_queue::codegen(app, analysis, extra); + let monotonics = monotonic::codegen(app, analysis); + + let mod_app_dispatchers = dispatchers::codegen(app, analysis); + let mod_app_async_dispatchers = async_dispatchers::codegen(app, analysis); + let mod_app_timer_queue = timer_queue::codegen(app, analysis); let user_imports = &app.user_imports; let user_code = &app.user_code; let name = &app.name; - let device = &extra.device; + let device = &app.args.device; - let monotonic_parts: Vec<_> = app - .monotonics - .iter() - .map(|(_, monotonic)| { - let name = &monotonic.ident; - let name_str = &name.to_string(); - let cfgs = &monotonic.cfgs; - let ident = util::monotonic_ident(name_str); - let doc = &format!( - "This module holds the static implementation for `{}::now()`", - name_str - ); - - let default_monotonic = if monotonic.args.default { - quote!( - #(#cfgs)* - pub use #name::now; - ) - } else { - quote!() - }; - - quote! { - #default_monotonic - - #[doc = #doc] - #[allow(non_snake_case)] - #(#cfgs)* - pub mod #name { - - /// Read the current time from this monotonic - pub fn now() -> ::Instant { - rtic::export::interrupt::free(|_| { - use rtic::Monotonic as _; - if let Some(m) = unsafe{ &mut *super::super::#ident.get_mut() } { - m.now() - } else { - ::zero() - } - }) - } - } - } - }) - .collect(); - - let monotonics = if monotonic_parts.is_empty() { - quote!() - } else { - quote!( - pub use rtic::Monotonic as _; - - /// Holds static methods for each monotonic. - pub mod monotonics { - #(#monotonic_parts)* - } - ) - }; let rt_err = util::rt_err_ident(); quote!( @@ -205,6 +150,8 @@ pub fn app(app: &App, analysis: &Analysis, extra: &Extra) -> TokenStream2 { #(#mod_app_dispatchers)* + #(#mod_app_async_dispatchers)* + #(#mod_app_timer_queue)* #(#mains)* diff --git a/macros/src/codegen/assertions.rs b/macros/src/codegen/assertions.rs index 3e0ad61c9b..0f8326c732 100644 --- a/macros/src/codegen/assertions.rs +++ b/macros/src/codegen/assertions.rs @@ -1,11 +1,11 @@ use proc_macro2::TokenStream as TokenStream2; use quote::quote; -use crate::{analyze::Analysis, check::Extra, codegen::util}; -use rtic_syntax::ast::App; +use crate::syntax::ast::App; +use crate::{analyze::Analysis, codegen::util}; /// Generates compile-time assertions that check that types implement the `Send` / `Sync` traits -pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec { +pub fn codegen(app: &App, analysis: &Analysis) -> Vec { let mut stmts = vec![]; for ty in &analysis.send_types { @@ -21,7 +21,7 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec();)); } - let device = &extra.device; + let device = &app.args.device; let chunks_name = util::priority_mask_chunks_ident(); let no_basepri_checks: Vec<_> = app .hardware_tasks @@ -29,9 +29,7 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec= (#chunks_name * 32) { ::core::panic!("An interrupt out of range is used while in armv6 or armv8m.base"); } diff --git a/macros/src/codegen/async_dispatchers.rs b/macros/src/codegen/async_dispatchers.rs new file mode 100644 index 0000000000..8b0e928bda --- /dev/null +++ b/macros/src/codegen/async_dispatchers.rs @@ -0,0 +1,129 @@ +use crate::syntax::ast::App; +use crate::{analyze::Analysis, codegen::util}; +use proc_macro2::TokenStream as TokenStream2; +use quote::quote; + +/// Generates task dispatchers +pub fn codegen(app: &App, analysis: &Analysis) -> Vec { + let mut items = vec![]; + + let interrupts = &analysis.interrupts_async; + + // Generate executor definition and priority in global scope + for (name, task) in app.software_tasks.iter() { + if task.is_async { + let type_name = util::internal_task_ident(name, "F"); + let exec_name = util::internal_task_ident(name, "EXEC"); + let prio_name = util::internal_task_ident(name, "PRIORITY"); + + items.push(quote!( + #[allow(non_camel_case_types)] + type #type_name = impl core::future::Future + 'static; + #[allow(non_upper_case_globals)] + static #exec_name: + rtic::RacyCell> = + rtic::RacyCell::new(rtic::export::executor::AsyncTaskExecutor::new()); + + // The executors priority, this can be any value - we will overwrite it when we + // start a task + #[allow(non_upper_case_globals)] + static #prio_name: rtic::RacyCell = + unsafe { rtic::RacyCell::new(rtic::export::Priority::new(0)) }; + )); + } + } + + for (&level, channel) in &analysis.channels { + if channel + .tasks + .iter() + .map(|task_name| !app.software_tasks[task_name].is_async) + .all(|is_not_async| is_not_async) + { + // check if all tasks are not async, if so don't generate this. + continue; + } + + let mut stmts = vec![]; + let device = &app.args.device; + let enum_ = util::interrupt_ident(); + let interrupt = util::suffixed(&interrupts[&level].0.to_string()); + + for name in channel + .tasks + .iter() + .filter(|name| app.software_tasks[*name].is_async) + { + let exec_name = util::internal_task_ident(name, "EXEC"); + let prio_name = util::internal_task_ident(name, "PRIORITY"); + let task = &app.software_tasks[name]; + // let cfgs = &task.cfgs; + let (_, tupled, pats, input_types) = util::regroup_inputs(&task.inputs); + let executor_run_ident = util::executor_run_ident(name); + + let n = util::capacity_literal(channel.capacity as usize + 1); + let rq = util::rq_async_ident(name); + let (rq_ty, rq_expr) = { + ( + quote!(rtic::export::ASYNCRQ<#input_types, #n>), + quote!(rtic::export::Queue::new()), + ) + }; + + items.push(quote!( + #[doc(hidden)] + #[allow(non_camel_case_types)] + #[allow(non_upper_case_globals)] + static #rq: rtic::RacyCell<#rq_ty> = rtic::RacyCell::new(#rq_expr); + )); + + stmts.push(quote!( + if !(&*#exec_name.get()).is_running() { + if let Some(#tupled) = rtic::export::interrupt::free(|_| (&mut *#rq.get_mut()).dequeue()) { + + // The async executor needs a static priority + #prio_name.get_mut().write(rtic::export::Priority::new(PRIORITY)); + let priority: &'static _ = &*#prio_name.get(); + + (&mut *#exec_name.get_mut()).spawn(#name(#name::Context::new(priority) #(,#pats)*)); + #executor_run_ident.store(true, core::sync::atomic::Ordering::Relaxed); + } + } + + if #executor_run_ident.load(core::sync::atomic::Ordering::Relaxed) { + #executor_run_ident.store(false, core::sync::atomic::Ordering::Relaxed); + if (&mut *#exec_name.get_mut()).poll(|| { + #executor_run_ident.store(true, core::sync::atomic::Ordering::Release); + rtic::pend(#device::#enum_::#interrupt); + }) && !rtic::export::interrupt::free(|_| (&*#rq.get_mut()).is_empty()) { + // If the ready queue is not empty and the executor finished, restart this + // dispatch to check if the executor should be restarted. + rtic::pend(#device::#enum_::#interrupt); + } + } + )); + } + + let doc = format!( + "Interrupt handler to dispatch async tasks at priority {}", + level + ); + let attribute = &interrupts[&level].1.attrs; + items.push(quote!( + #[allow(non_snake_case)] + #[doc = #doc] + #[no_mangle] + #(#attribute)* + unsafe fn #interrupt() { + /// The priority of this interrupt handler + const PRIORITY: u8 = #level; + + rtic::export::run(PRIORITY, || { + #(#stmts)* + }); + } + )); + } + + items +} diff --git a/macros/src/codegen/dispatchers.rs b/macros/src/codegen/dispatchers.rs index a90a97c773..1a8b40422b 100644 --- a/macros/src/codegen/dispatchers.rs +++ b/macros/src/codegen/dispatchers.rs @@ -1,21 +1,31 @@ +use crate::syntax::ast::App; +use crate::{analyze::Analysis, codegen::util}; use proc_macro2::TokenStream as TokenStream2; use quote::quote; -use rtic_syntax::ast::App; - -use crate::{analyze::Analysis, check::Extra, codegen::util}; /// Generates task dispatchers -pub fn codegen(app: &App, analysis: &Analysis, _extra: &Extra) -> Vec { +pub fn codegen(app: &App, analysis: &Analysis) -> Vec { let mut items = vec![]; - let interrupts = &analysis.interrupts; + let interrupts = &analysis.interrupts_normal; for (&level, channel) in &analysis.channels { + if channel + .tasks + .iter() + .map(|task_name| app.software_tasks[task_name].is_async) + .all(|is_async| is_async) + { + // check if all tasks are async, if so don't generate this. + continue; + } + let mut stmts = vec![]; let variants = channel .tasks .iter() + .filter(|name| !app.software_tasks[*name].is_async) .map(|name| { let cfgs = &app.software_tasks[name].cfgs; @@ -45,6 +55,7 @@ pub fn codegen(app: &App, analysis: &Analysis, _extra: &Extra) -> Vec), @@ -64,6 +75,13 @@ pub fn codegen(app: &App, analysis: &Analysis, _extra: &Extra) -> Vec = rtic::RacyCell::new(#rq_expr); )); + let interrupt = util::suffixed( + &interrupts + .get(&level) + .expect("RTIC-ICE: Unable to get interrrupt") + .0 + .to_string(), + ); let arms = channel .tasks .iter() @@ -74,23 +92,27 @@ pub fn codegen(app: &App, analysis: &Analysis, _extra: &Extra) -> Vec { - let #tupled = - (&*#inputs - .get()) - .get_unchecked(usize::from(index)) - .as_ptr() - .read(); - (&mut *#fq.get_mut()).split().0.enqueue_unchecked(index); - let priority = &rtic::export::Priority::new(PRIORITY); - #name( - #name::Context::new(priority) - #(,#pats)* - ) - } - ) + if !task.is_async { + quote!( + #(#cfgs)* + #t::#name => { + let #tupled = + (&*#inputs + .get()) + .get_unchecked(usize::from(index)) + .as_ptr() + .read(); + (&mut *#fq.get_mut()).split().0.enqueue_unchecked(index); + let priority = &rtic::export::Priority::new(PRIORITY); + #name( + #name::Context::new(priority) + #(,#pats)* + ) + } + ) + } else { + quote!() + } }) .collect::>(); @@ -103,7 +125,6 @@ pub fn codegen(app: &App, analysis: &Analysis, _extra: &Extra) -> Vec ( // mod_app_hardware_tasks -- interrupt handlers and `${task}Resources` constructors Vec, @@ -33,12 +30,10 @@ pub fn codegen( let priority = task.args.priority; let cfgs = &task.cfgs; let attrs = &task.attrs; - let user_hardware_task_isr_doc = &format!(" User HW task ISR trampoline for {name}"); mod_app.push(quote!( #[allow(non_snake_case)] #[no_mangle] - #[doc = #user_hardware_task_isr_doc] #(#attrs)* #(#cfgs)* unsafe fn #symbol() { @@ -87,19 +82,14 @@ pub fn codegen( local_needs_lt, app, analysis, - extra, )); - let user_hardware_task_doc = &format!(" User HW task: {name}"); if !task.is_extern { let attrs = &task.attrs; - let cfgs = &task.cfgs; let context = &task.context; let stmts = &task.stmts; user_tasks.push(quote!( - #[doc = #user_hardware_task_doc] #(#attrs)* - #(#cfgs)* #[allow(non_snake_case)] fn #name(#context: #name::Context) { use rtic::Mutex as _; diff --git a/macros/src/codegen/idle.rs b/macros/src/codegen/idle.rs index 77a7f9feb2..98679399f9 100644 --- a/macros/src/codegen/idle.rs +++ b/macros/src/codegen/idle.rs @@ -1,18 +1,15 @@ -use proc_macro2::TokenStream as TokenStream2; -use quote::quote; -use rtic_syntax::{ast::App, Context}; - +use crate::syntax::{ast::App, Context}; use crate::{ analyze::Analysis, - check::Extra, codegen::{local_resources_struct, module, shared_resources_struct}, }; +use proc_macro2::TokenStream as TokenStream2; +use quote::quote; /// Generates support code for `#[idle]` functions pub fn codegen( app: &App, analysis: &Analysis, - extra: &Extra, ) -> ( // mod_app_idle -- the `${idle}Resources` constructor Vec, @@ -57,16 +54,13 @@ pub fn codegen( local_needs_lt, app, analysis, - extra, )); - let idle_doc = " User provided idle function".to_string(); let attrs = &idle.attrs; let context = &idle.context; let stmts = &idle.stmts; let user_idle = Some(quote!( #(#attrs)* - #[doc = #idle_doc] #[allow(non_snake_case)] fn #name(#context: #name::Context) -> ! { use rtic::Mutex as _; @@ -82,6 +76,9 @@ pub fn codegen( (mod_app, root_idle, user_idle, call_idle) } else { + // TODO: No idle defined, check for 0-priority tasks and generate an executor if needed + + // ( vec![], vec![], diff --git a/macros/src/codegen/init.rs b/macros/src/codegen/init.rs index 34f86f2721..9a6fe2d522 100644 --- a/macros/src/codegen/init.rs +++ b/macros/src/codegen/init.rs @@ -1,11 +1,10 @@ use proc_macro2::TokenStream as TokenStream2; use quote::quote; -use rtic_syntax::{ast::App, Context}; use crate::{ analyze::Analysis, - check::Extra, codegen::{local_resources_struct, module}, + syntax::{ast::App, Context}, }; type CodegenResult = ( @@ -24,7 +23,7 @@ type CodegenResult = ( ); /// Generates support code for `#[init]` functions -pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> CodegenResult { +pub fn codegen(app: &App, analysis: &Analysis) -> CodegenResult { let init = &app.init; let mut local_needs_lt = false; let name = &init.name; @@ -65,27 +64,22 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> CodegenResult { ) }) .collect(); - - let shared_resources_doc = " RTIC shared resource struct".to_string(); - let local_resources_doc = " RTIC local resource struct".to_string(); root_init.push(quote! { - #[doc = #shared_resources_doc] struct #shared { #(#shared_resources)* } - #[doc = #local_resources_doc] struct #local { #(#local_resources)* } }); + // let locals_pat = locals_pat.iter(); + let user_init_return = quote! {#shared, #local, #name::Monotonics}; - let user_init_doc = " User provided init function".to_string(); let user_init = quote!( #(#attrs)* - #[doc = #user_init_doc] #[inline(always)] #[allow(non_snake_case)] fn #name(#context: #name::Context) -> (#user_init_return) { @@ -105,6 +99,7 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> CodegenResult { mod_app = Some(constructor); } + // let locals_new = locals_new.iter(); let call_init = quote! { let (shared_resources, local_resources, mut monotonics) = #name(#name::Context::new(core.into())); }; @@ -115,7 +110,6 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> CodegenResult { local_needs_lt, app, analysis, - extra, )); (mod_app, root_init, user_init, call_init) diff --git a/macros/src/codegen/local_resources.rs b/macros/src/codegen/local_resources.rs index 6e7c1daa72..6fc63cd93e 100644 --- a/macros/src/codegen/local_resources.rs +++ b/macros/src/codegen/local_resources.rs @@ -1,8 +1,7 @@ +use crate::syntax::ast::App; +use crate::{analyze::Analysis, codegen::util}; use proc_macro2::TokenStream as TokenStream2; use quote::quote; -use rtic_syntax::ast::App; - -use crate::{analyze::Analysis, check::Extra, codegen::util}; /// Generates `local` variables and local resource proxies /// @@ -10,7 +9,6 @@ use crate::{analyze::Analysis, check::Extra, codegen::util}; pub fn codegen( app: &App, _analysis: &Analysis, - _extra: &Extra, ) -> ( // mod_app -- the `static` variables behind the proxies Vec, diff --git a/macros/src/codegen/local_resources_struct.rs b/macros/src/codegen/local_resources_struct.rs index 74bdbf8b89..309fd8d253 100644 --- a/macros/src/codegen/local_resources_struct.rs +++ b/macros/src/codegen/local_resources_struct.rs @@ -1,9 +1,9 @@ -use proc_macro2::TokenStream as TokenStream2; -use quote::quote; -use rtic_syntax::{ +use crate::syntax::{ ast::{App, TaskLocal}, Context, }; +use proc_macro2::TokenStream as TokenStream2; +use quote::quote; use crate::codegen::util; @@ -13,7 +13,13 @@ pub fn codegen(ctxt: Context, needs_lt: &mut bool, app: &App) -> (TokenStream2, let resources = match ctxt { Context::Init => &app.init.args.local_resources, - Context::Idle => &app.idle.as_ref().unwrap().args.local_resources, + Context::Idle => { + &app.idle + .as_ref() + .expect("RTIC-ICE: unable to get idle name") + .args + .local_resources + } Context::HardwareTask(name) => &app.hardware_tasks[name].args.local_resources, Context::SoftwareTask(name) => &app.software_tasks[name].args.local_resources, }; @@ -49,9 +55,7 @@ pub fn codegen(ctxt: Context, needs_lt: &mut bool, app: &App) -> (TokenStream2, util::declared_static_local_resource_ident(name, &task_name) }; - let local_resource_doc = format!(" Local resource `{name}`"); fields.push(quote!( - #[doc = #local_resource_doc] #(#cfgs)* pub #name: &#lt mut #ty )); @@ -84,7 +88,7 @@ pub fn codegen(ctxt: Context, needs_lt: &mut bool, app: &App) -> (TokenStream2, } } - let doc = format!(" Local resources `{}` has access to", ctxt.ident(app)); + let doc = format!("Local resources `{}` has access to", ctxt.ident(app)); let ident = util::local_resources_ident(ctxt, app); let item = quote!( #[allow(non_snake_case)] @@ -98,7 +102,6 @@ pub fn codegen(ctxt: Context, needs_lt: &mut bool, app: &App) -> (TokenStream2, let constructor = quote!( impl<#lt> #ident<#lt> { #[inline(always)] - #[doc(hidden)] pub unsafe fn new() -> Self { #ident { #(#values,)* diff --git a/macros/src/codegen/module.rs b/macros/src/codegen/module.rs index 8dcdbcf3e7..7ac06c5c05 100644 --- a/macros/src/codegen/module.rs +++ b/macros/src/codegen/module.rs @@ -1,7 +1,7 @@ -use crate::{analyze::Analysis, check::Extra, codegen::util}; +use crate::syntax::{ast::App, Context}; +use crate::{analyze::Analysis, codegen::util}; use proc_macro2::TokenStream as TokenStream2; use quote::quote; -use rtic_syntax::{ast::App, Context}; #[allow(clippy::too_many_lines)] pub fn codegen( @@ -10,12 +10,13 @@ pub fn codegen( local_resources_tick: bool, app: &App, analysis: &Analysis, - extra: &Extra, ) -> TokenStream2 { let mut items = vec![]; let mut module_items = vec![]; let mut fields = vec![]; let mut values = vec![]; + // Used to copy task cfgs to the whole module + let mut task_cfgs = vec![]; let name = ctxt.ident(app); @@ -27,8 +28,8 @@ pub fn codegen( pub core: rtic::export::Peripherals )); - if extra.peripherals { - let device = &extra.device; + if app.args.peripherals { + let device = &app.args.device; fields.push(quote!( /// Device peripherals @@ -52,14 +53,6 @@ pub fn codegen( Context::Idle | Context::HardwareTask(_) | Context::SoftwareTask(_) => {} } - // if ctxt.has_locals(app) { - // let ident = util::locals_ident(ctxt, app); - // module_items.push(quote!( - // #[doc(inline)] - // pub use super::#ident as Locals; - // )); - // } - if ctxt.has_local_resources(app) { let ident = util::local_resources_ident(ctxt, app); let lt = if local_resources_tick { @@ -114,12 +107,8 @@ pub fn codegen( .monotonics .iter() .map(|(_, monotonic)| { - let cfgs = &monotonic.cfgs; let mono = &monotonic.ty; - quote! { - #(#cfgs)* - pub #mono - } + quote! {#mono} }) .collect(); @@ -130,7 +119,7 @@ pub fn codegen( #[allow(non_snake_case)] #[allow(non_camel_case_types)] pub struct #internal_monotonics_ident( - #(#monotonic_types),* + #(pub #monotonic_types),* ); )); @@ -141,10 +130,10 @@ pub fn codegen( } let doc = match ctxt { - Context::Idle => " Idle loop", - Context::Init => " Initialization function", - Context::HardwareTask(_) => " Hardware task", - Context::SoftwareTask(_) => " Software task", + Context::Idle => "Idle loop", + Context::Init => "Initialization function", + Context::HardwareTask(_) => "Hardware task", + Context::SoftwareTask(_) => "Software task", }; let v = Vec::new(); @@ -175,8 +164,8 @@ pub fn codegen( let internal_context_name = util::internal_task_ident(name, "Context"); items.push(quote!( - /// Execution context #(#cfgs)* + /// Execution context #[allow(non_snake_case)] #[allow(non_camel_case_types)] pub struct #internal_context_name<#lt> { @@ -185,7 +174,6 @@ pub fn codegen( #(#cfgs)* impl<#lt> #internal_context_name<#lt> { - #[doc(hidden)] #[inline(always)] pub unsafe fn new(#core #priority) -> Self { #internal_context_name { @@ -196,8 +184,8 @@ pub fn codegen( )); module_items.push(quote!( - #[doc(inline)] #(#cfgs)* + #[doc(inline)] pub use super::#internal_context_name as Context; )); @@ -206,6 +194,8 @@ pub fn codegen( let priority = spawnee.args.priority; let t = util::spawn_t_ident(priority); let cfgs = &spawnee.cfgs; + // Store a copy of the task cfgs + task_cfgs = cfgs.clone(); let (args, tupled, untupled, ty) = util::regroup_inputs(&spawnee.inputs); let args = &args; let tupled = &tupled; @@ -213,112 +203,141 @@ pub fn codegen( let rq = util::rq_ident(priority); let inputs = util::inputs_ident(name); - let device = &extra.device; + let device = &app.args.device; let enum_ = util::interrupt_ident(); - let interrupt = &analysis - .interrupts - .get(&priority) - .expect("RTIC-ICE: interrupt identifer not found") - .0; + let interrupt = if spawnee.is_async { + &analysis + .interrupts_async + .get(&priority) + .expect("RTIC-ICE: interrupt identifer not found") + .0 + } else { + &analysis + .interrupts_normal + .get(&priority) + .expect("RTIC-ICE: interrupt identifer not found") + .0 + }; let internal_spawn_ident = util::internal_task_ident(name, "spawn"); // Spawn caller - items.push(quote!( + if spawnee.is_async { + let rq = util::rq_async_ident(name); + items.push(quote!( - /// Spawns the task directly - #(#cfgs)* - pub fn #internal_spawn_ident(#(#args,)*) -> Result<(), #ty> { - let input = #tupled; + #(#cfgs)* + /// Spawns the task directly + #[allow(non_snake_case)] + #[doc(hidden)] + pub fn #internal_spawn_ident(#(#args,)*) -> Result<(), #ty> { + let input = #tupled; - unsafe { - if let Some(index) = rtic::export::interrupt::free(|_| (&mut *#fq.get_mut()).dequeue()) { - (&mut *#inputs - .get_mut()) - .get_unchecked_mut(usize::from(index)) - .as_mut_ptr() - .write(input); + unsafe { + let r = rtic::export::interrupt::free(|_| (&mut *#rq.get_mut()).enqueue(input)); - rtic::export::interrupt::free(|_| { - (&mut *#rq.get_mut()).enqueue_unchecked((#t::#name, index)); - }); + if r.is_ok() { + rtic::pend(#device::#enum_::#interrupt); + } - rtic::pend(#device::#enum_::#interrupt); - - Ok(()) - } else { - Err(input) + r } - } + })); + } else { + items.push(quote!( - })); + #(#cfgs)* + /// Spawns the task directly + #[allow(non_snake_case)] + #[doc(hidden)] + pub fn #internal_spawn_ident(#(#args,)*) -> Result<(), #ty> { + let input = #tupled; + + unsafe { + if let Some(index) = rtic::export::interrupt::free(|_| (&mut *#fq.get_mut()).dequeue()) { + (&mut *#inputs + .get_mut()) + .get_unchecked_mut(usize::from(index)) + .as_mut_ptr() + .write(input); + + rtic::export::interrupt::free(|_| { + (&mut *#rq.get_mut()).enqueue_unchecked((#t::#name, index)); + }); + rtic::pend(#device::#enum_::#interrupt); + + Ok(()) + } else { + Err(input) + } + } + + })); + } module_items.push(quote!( - #[doc(inline)] #(#cfgs)* + #[doc(inline)] pub use super::#internal_spawn_ident as spawn; )); // Schedule caller - for (_, monotonic) in &app.monotonics { - let instants = util::monotonic_instants_ident(name, &monotonic.ident); - let monotonic_name = monotonic.ident.to_string(); + if !spawnee.is_async { + for (_, monotonic) in &app.monotonics { + let instants = util::monotonic_instants_ident(name, &monotonic.ident); + let monotonic_name = monotonic.ident.to_string(); - let tq = util::tq_ident(&monotonic.ident.to_string()); - let t = util::schedule_t_ident(); - let m = &monotonic.ident; - let cfgs = &monotonic.cfgs; - let m_ident = util::monotonic_ident(&monotonic_name); - let m_isr = &monotonic.args.binds; - let enum_ = util::interrupt_ident(); - let spawn_handle_string = format!("{}::SpawnHandle", m); + let tq = util::tq_ident(&monotonic.ident.to_string()); + let t = util::schedule_t_ident(); + let m = &monotonic.ident; + let m_ident = util::monotonic_ident(&monotonic_name); + let m_isr = &monotonic.args.binds; + let enum_ = util::interrupt_ident(); + let spawn_handle_string = format!("{}::SpawnHandle", m); - let (enable_interrupt, pend) = if &*m_isr.to_string() == "SysTick" { - ( - quote!(core::mem::transmute::<_, rtic::export::SYST>(()).enable_interrupt()), - quote!(rtic::export::SCB::set_pendst()), - ) - } else { - let rt_err = util::rt_err_ident(); - ( - quote!(rtic::export::NVIC::unmask(#rt_err::#enum_::#m_isr)), - quote!(rtic::pend(#rt_err::#enum_::#m_isr)), - ) - }; + let (enable_interrupt, pend) = if &*m_isr.to_string() == "SysTick" { + ( + quote!(core::mem::transmute::<_, rtic::export::SYST>(()).enable_interrupt()), + quote!(rtic::export::SCB::set_pendst()), + ) + } else { + let rt_err = util::rt_err_ident(); + ( + quote!(rtic::export::NVIC::unmask(#rt_err::#enum_::#m_isr)), + quote!(rtic::pend(#rt_err::#enum_::#m_isr)), + ) + }; - let tq_marker = &util::timer_queue_marker_ident(); + let tq_marker = &util::timer_queue_marker_ident(); - // For future use - // let doc = format!(" RTIC internal: {}:{}", file!(), line!()); - // items.push(quote!(#[doc = #doc])); - let internal_spawn_handle_ident = - util::internal_monotonics_ident(name, m, "SpawnHandle"); - let internal_spawn_at_ident = util::internal_monotonics_ident(name, m, "spawn_at"); - let internal_spawn_after_ident = - util::internal_monotonics_ident(name, m, "spawn_after"); + let internal_spawn_handle_ident = + util::internal_monotonics_ident(name, m, "SpawnHandle"); + let internal_spawn_at_ident = util::internal_monotonics_ident(name, m, "spawn_at"); + let internal_spawn_after_ident = + util::internal_monotonics_ident(name, m, "spawn_after"); - if monotonic.args.default { - module_items.push(quote!( - #(#cfgs)* - pub use #m::spawn_after; - #(#cfgs)* - pub use #m::spawn_at; - #(#cfgs)* - pub use #m::SpawnHandle; - )); - } - module_items.push(quote!( - #[doc(hidden)] - #(#cfgs)* - pub mod #m { - pub use super::super::#internal_spawn_after_ident as spawn_after; - pub use super::super::#internal_spawn_at_ident as spawn_at; - pub use super::super::#internal_spawn_handle_ident as SpawnHandle; + if monotonic.args.default { + module_items.push(quote!( + #[doc(inline)] + pub use #m::spawn_after; + #[doc(inline)] + pub use #m::spawn_at; + #[doc(inline)] + pub use #m::SpawnHandle; + )); } - )); + module_items.push(quote!( + pub mod #m { + #[doc(inline)] + pub use super::super::#internal_spawn_after_ident as spawn_after; + #[doc(inline)] + pub use super::super::#internal_spawn_at_ident as spawn_at; + #[doc(inline)] + pub use super::super::#internal_spawn_handle_ident as SpawnHandle; + } + )); - items.push(quote!( - #[doc(hidden)] + items.push(quote!( #(#cfgs)* #[allow(non_snake_case)] #[allow(non_camel_case_types)] @@ -329,7 +348,6 @@ pub fn codegen( #(#cfgs)* impl core::fmt::Debug for #internal_spawn_handle_ident { - #[doc(hidden)] fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { f.debug_struct(#spawn_handle_string).finish() } @@ -340,7 +358,7 @@ pub fn codegen( pub fn cancel(self) -> Result<#ty, ()> { rtic::export::interrupt::free(|_| unsafe { let tq = &mut *#tq.get_mut(); - if let Some((_task, index)) = tq.cancel_marker(self.marker) { + if let Some((_task, index)) = tq.cancel_task_marker(self.marker) { // Get the message let msg = (&*#inputs .get()) @@ -357,9 +375,7 @@ pub fn codegen( }) } - /// Reschedule after #[inline] - #(#cfgs)* pub fn reschedule_after( self, duration: <#m as rtic::Monotonic>::Duration @@ -367,8 +383,6 @@ pub fn codegen( self.reschedule_at(monotonics::#m::now() + duration) } - /// Reschedule at - #(#cfgs)* pub fn reschedule_at( self, instant: <#m as rtic::Monotonic>::Instant @@ -379,16 +393,17 @@ pub fn codegen( let tq = (&mut *#tq.get_mut()); - tq.update_marker(self.marker, marker, instant, || #pend).map(|_| #name::#m::SpawnHandle { marker }) + tq.update_task_marker(self.marker, marker, instant, || #pend).map(|_| #name::#m::SpawnHandle { marker }) }) } } + + #(#cfgs)* /// Spawns the task after a set duration relative to the current time /// /// This will use the time `Instant::new(0)` as baseline if called in `#[init]`, /// so if you use a non-resetable timer use `spawn_at` when in `#[init]` - #(#cfgs)* #[allow(non_snake_case)] pub fn #internal_spawn_after_ident( duration: <#m as rtic::Monotonic>::Duration @@ -424,10 +439,10 @@ pub fn codegen( rtic::export::interrupt::free(|_| { let marker = #tq_marker.get().read(); - let nr = rtic::export::NotReady { - instant, - index, + let nr = rtic::export::TaskNotReady { task: #t::#name, + index, + instant, marker, }; @@ -435,7 +450,7 @@ pub fn codegen( let tq = &mut *#tq.get_mut(); - tq.enqueue_unchecked( + tq.enqueue_task_unchecked( nr, || #enable_interrupt, || #pend, @@ -449,6 +464,7 @@ pub fn codegen( } } )); + } } } @@ -457,8 +473,9 @@ pub fn codegen( } else { quote!( #(#items)* + #[allow(non_snake_case)] - #(#cfgs)* + #(#task_cfgs)* #[doc = #doc] pub mod #name { #(#module_items)* diff --git a/macros/src/codegen/monotonic.rs b/macros/src/codegen/monotonic.rs new file mode 100644 index 0000000000..417a1d6a1c --- /dev/null +++ b/macros/src/codegen/monotonic.rs @@ -0,0 +1,280 @@ +use crate::syntax::ast::App; +use proc_macro2::TokenStream as TokenStream2; +use quote::quote; + +use crate::{analyze::Analysis, codegen::util}; + +/// Generates monotonic module dispatchers +pub fn codegen(app: &App, _analysis: &Analysis) -> TokenStream2 { + let mut monotonic_parts: Vec<_> = Vec::new(); + + let tq_marker = util::timer_queue_marker_ident(); + + for (_, monotonic) in &app.monotonics { + // let instants = util::monotonic_instants_ident(name, &monotonic.ident); + let monotonic_name = monotonic.ident.to_string(); + + let tq = util::tq_ident(&monotonic_name); + let m = &monotonic.ident; + let m_ident = util::monotonic_ident(&monotonic_name); + let m_isr = &monotonic.args.binds; + let enum_ = util::interrupt_ident(); + let name_str = &m.to_string(); + let ident = util::monotonic_ident(name_str); + let doc = &format!( + "This module holds the static implementation for `{}::now()`", + name_str + ); + + let (enable_interrupt, pend) = if &*m_isr.to_string() == "SysTick" { + ( + quote!(core::mem::transmute::<_, rtic::export::SYST>(()).enable_interrupt()), + quote!(rtic::export::SCB::set_pendst()), + ) + } else { + let rt_err = util::rt_err_ident(); + ( + quote!(rtic::export::NVIC::unmask(super::super::#rt_err::#enum_::#m_isr)), + quote!(rtic::pend(super::super::#rt_err::#enum_::#m_isr)), + ) + }; + + let default_monotonic = if monotonic.args.default { + quote!( + #[doc(inline)] + pub use #m::now; + #[doc(inline)] + pub use #m::delay; + #[doc(inline)] + pub use #m::delay_until; + #[doc(inline)] + pub use #m::timeout_at; + #[doc(inline)] + pub use #m::timeout_after; + ) + } else { + quote!() + }; + + monotonic_parts.push(quote! { + #default_monotonic + + #[doc = #doc] + #[allow(non_snake_case)] + pub mod #m { + /// Read the current time from this monotonic + pub fn now() -> ::Instant { + rtic::export::interrupt::free(|_| { + use rtic::Monotonic as _; + if let Some(m) = unsafe{ &mut *super::super::#ident.get_mut() } { + m.now() + } else { + ::zero() + } + }) + } + + /// Delay + #[inline(always)] + #[allow(non_snake_case)] + pub fn delay(duration: ::Duration) + -> DelayFuture { + let until = now() + duration; + DelayFuture { until, waker_storage: None } + } + + /// Delay until a specific time + #[inline(always)] + #[allow(non_snake_case)] + pub fn delay_until(instant: ::Instant) + -> DelayFuture { + let until = instant; + DelayFuture { until, waker_storage: None } + } + + /// Delay future. + #[allow(non_snake_case)] + #[allow(non_camel_case_types)] + pub struct DelayFuture { + until: ::Instant, + waker_storage: Option>>, + } + + impl Drop for DelayFuture { + fn drop(&mut self) { + if let Some(waker_storage) = &mut self.waker_storage { + rtic::export::interrupt::free(|_| unsafe { + let tq = &mut *super::super::#tq.get_mut(); + tq.cancel_waker_marker(waker_storage.val.marker); + }); + } + } + } + + impl core::future::Future for DelayFuture { + type Output = (); + + fn poll( + mut self: core::pin::Pin<&mut Self>, + cx: &mut core::task::Context<'_> + ) -> core::task::Poll { + let mut s = self.as_mut(); + let now = now(); + let until = s.until; + let is_ws_none = s.waker_storage.is_none(); + + if now >= until { + return core::task::Poll::Ready(()); + } else if is_ws_none { + rtic::export::interrupt::free(|_| unsafe { + let marker = super::super::#tq_marker.get().read(); + super::super::#tq_marker.get_mut().write(marker.wrapping_add(1)); + + let nr = s.waker_storage.insert(rtic::export::IntrusiveNode::new(rtic::export::WakerNotReady { + waker: cx.waker().clone(), + instant: until, + marker, + })); + + let tq = &mut *super::super::#tq.get_mut(); + + tq.enqueue_waker( + core::mem::transmute(nr), // Transmute the reference to static + || #enable_interrupt, + || #pend, + (&mut *super::super::#m_ident.get_mut()).as_mut()); + }); + } + + core::task::Poll::Pending + } + } + + /// Timeout future. + #[allow(non_snake_case)] + #[allow(non_camel_case_types)] + pub struct TimeoutFuture { + future: F, + until: ::Instant, + waker_storage: Option>>, + } + + impl Drop for TimeoutFuture { + fn drop(&mut self) { + if let Some(waker_storage) = &mut self.waker_storage { + rtic::export::interrupt::free(|_| unsafe { + let tq = &mut *super::super::#tq.get_mut(); + tq.cancel_waker_marker(waker_storage.val.marker); + }); + } + } + } + + /// Timeout after + #[allow(non_snake_case)] + #[inline(always)] + pub fn timeout_after( + future: F, + duration: ::Duration + ) -> TimeoutFuture { + let until = now() + duration; + TimeoutFuture { + future, + until, + waker_storage: None, + } + } + + /// Timeout at + #[allow(non_snake_case)] + #[inline(always)] + pub fn timeout_at( + future: F, + instant: ::Instant + ) -> TimeoutFuture { + TimeoutFuture { + future, + until: instant, + waker_storage: None, + } + } + + impl core::future::Future for TimeoutFuture + where + F: core::future::Future, + { + type Output = Result; + + fn poll( + self: core::pin::Pin<&mut Self>, + cx: &mut core::task::Context<'_> + ) -> core::task::Poll { + // SAFETY: We don't move the underlying pinned value. + let mut s = unsafe { self.get_unchecked_mut() }; + let future = unsafe { core::pin::Pin::new_unchecked(&mut s.future) }; + let now = now(); + let until = s.until; + let is_ws_none = s.waker_storage.is_none(); + + match future.poll(cx) { + core::task::Poll::Ready(r) => { + if let Some(waker_storage) = &mut s.waker_storage { + rtic::export::interrupt::free(|_| unsafe { + let tq = &mut *super::super::#tq.get_mut(); + tq.cancel_waker_marker(waker_storage.val.marker); + }); + } + + return core::task::Poll::Ready(Ok(r)); + } + core::task::Poll::Pending => { + if now >= until { + // Timeout + return core::task::Poll::Ready(Err(super::TimeoutError)); + } else if is_ws_none { + rtic::export::interrupt::free(|_| unsafe { + let marker = super::super::#tq_marker.get().read(); + super::super::#tq_marker.get_mut().write(marker.wrapping_add(1)); + + let nr = s.waker_storage.insert(rtic::export::IntrusiveNode::new(rtic::export::WakerNotReady { + waker: cx.waker().clone(), + instant: until, + marker, + })); + + let tq = &mut *super::super::#tq.get_mut(); + + tq.enqueue_waker( + core::mem::transmute(nr), // Transmute the reference to static + || #enable_interrupt, + || #pend, + (&mut *super::super::#m_ident.get_mut()).as_mut()); + }); + } + } + } + + core::task::Poll::Pending + } + } + } + }); + } + + if monotonic_parts.is_empty() { + quote!() + } else { + quote!( + pub use rtic::Monotonic as _; + + /// Holds static methods for each monotonic. + pub mod monotonics { + /// A timeout error. + #[derive(Debug)] + pub struct TimeoutError; + + #(#monotonic_parts)* + } + ) + } +} diff --git a/macros/src/codegen/post_init.rs b/macros/src/codegen/post_init.rs index 460b4e2160..df5daa1ec6 100644 --- a/macros/src/codegen/post_init.rs +++ b/macros/src/codegen/post_init.rs @@ -1,6 +1,6 @@ +use crate::syntax::ast::App; use proc_macro2::{Span, TokenStream as TokenStream2}; use quote::quote; -use rtic_syntax::ast::App; use syn::Index; use crate::{analyze::Analysis, codegen::util}; @@ -43,28 +43,21 @@ pub fn codegen(app: &App, analysis: &Analysis) -> Vec { } } - for (i, (monotonic_ident, monotonic)) in app.monotonics.iter().enumerate() { + for (i, (monotonic, _)) in app.monotonics.iter().enumerate() { // For future use // let doc = format!(" RTIC internal: {}:{}", file!(), line!()); // stmts.push(quote!(#[doc = #doc])); - let cfgs = &monotonic.cfgs; #[allow(clippy::cast_possible_truncation)] let idx = Index { index: i as u32, span: Span::call_site(), }; - stmts.push(quote!( - #(#cfgs)* - monotonics.#idx.reset(); - )); + stmts.push(quote!(monotonics.#idx.reset();)); // Store the monotonic - let name = util::monotonic_ident(&monotonic_ident.to_string()); - stmts.push(quote!( - #(#cfgs)* - #name.get_mut().write(Some(monotonics.#idx)); - )); + let name = util::monotonic_ident(&monotonic.to_string()); + stmts.push(quote!(#name.get_mut().write(Some(monotonics.#idx));)); } // Enable the interrupts -- this completes the `init`-ialization phase diff --git a/macros/src/codegen/pre_init.rs b/macros/src/codegen/pre_init.rs index 2362cb7466..ef3acba76d 100644 --- a/macros/src/codegen/pre_init.rs +++ b/macros/src/codegen/pre_init.rs @@ -1,11 +1,11 @@ +use crate::syntax::ast::App; use proc_macro2::TokenStream as TokenStream2; use quote::quote; -use rtic_syntax::ast::App; -use crate::{analyze::Analysis, check::Extra, codegen::util}; +use crate::{analyze::Analysis, codegen::util}; /// Generates code that runs before `#[init]` -pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec { +pub fn codegen(app: &App, analysis: &Analysis) -> Vec { let mut stmts = vec![]; let rt_err = util::rt_err_ident(); @@ -15,12 +15,14 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec Vec ( // mod_app -- the `static` variables behind the proxies Vec, @@ -90,7 +89,7 @@ pub fn codegen( // let doc = format!(" RTIC internal ({} resource): {}:{}", doc, file!(), line!()); mod_app.push(util::impl_mutex( - extra, + app, cfgs, true, &shared_name, @@ -112,10 +111,14 @@ pub fn codegen( }; // Computing mapping of used interrupts to masks - let interrupt_ids = analysis.interrupts.iter().map(|(p, (id, _))| (p, id)); + let interrupt_ids = analysis + .interrupts_normal + .iter() + .map(|(p, (id, _))| (p, id)) + .chain(analysis.interrupts_async.iter().map(|(p, (id, _))| (p, id))); let mut prio_to_masks = HashMap::new(); - let device = &extra.device; + let device = &app.args.device; let mut uses_exceptions_with_resources = false; let mut mask_ids = Vec::new(); @@ -147,8 +150,7 @@ pub fn codegen( None } })) { - #[allow(clippy::or_fun_call)] - let v = prio_to_masks.entry(priority - 1).or_insert(Vec::new()); + let v: &mut Vec<_> = prio_to_masks.entry(priority - 1).or_default(); v.push(quote!(#device::Interrupt::#name as u32)); mask_ids.push(quote!(#device::Interrupt::#name as u32)); } diff --git a/macros/src/codegen/shared_resources_struct.rs b/macros/src/codegen/shared_resources_struct.rs index df362719dd..1d46aa4e4c 100644 --- a/macros/src/codegen/shared_resources_struct.rs +++ b/macros/src/codegen/shared_resources_struct.rs @@ -1,6 +1,6 @@ +use crate::syntax::{ast::App, Context}; use proc_macro2::TokenStream as TokenStream2; use quote::quote; -use rtic_syntax::{ast::App, Context}; use crate::codegen::util; @@ -10,24 +10,17 @@ pub fn codegen(ctxt: Context, needs_lt: &mut bool, app: &App) -> (TokenStream2, let resources = match ctxt { Context::Init => unreachable!("Tried to generate shared resources struct for init"), - Context::Idle => &app.idle.as_ref().unwrap().args.shared_resources, + Context::Idle => { + &app.idle + .as_ref() + .expect("RTIC-ICE: unable to get idle name") + .args + .shared_resources + } Context::HardwareTask(name) => &app.hardware_tasks[name].args.shared_resources, Context::SoftwareTask(name) => &app.software_tasks[name].args.shared_resources, }; - let v = Vec::new(); - let task_cfgs = match ctxt { - Context::HardwareTask(t) => { - &app.hardware_tasks[t].cfgs - // ... - } - Context::SoftwareTask(t) => { - &app.software_tasks[t].cfgs - // ... - } - _ => &v, - }; - let mut fields = vec![]; let mut values = vec![]; let mut has_cfgs = false; @@ -57,18 +50,14 @@ pub fn codegen(ctxt: Context, needs_lt: &mut bool, app: &App) -> (TokenStream2, quote!('a) }; - let lock_free_resource_doc = format!(" Lock free resource `{name}`"); fields.push(quote!( - #[doc = #lock_free_resource_doc] #(#cfgs)* pub #name: &#lt #mut_ #ty )); } else if access.is_shared() { lt = Some(quote!('a)); - let shared_resource_doc = format!(" Shared resource `{name}`"); fields.push(quote!( - #[doc = #shared_resource_doc] #(#cfgs)* pub #name: &'a #ty )); @@ -76,16 +65,12 @@ pub fn codegen(ctxt: Context, needs_lt: &mut bool, app: &App) -> (TokenStream2, // Resource proxy lt = Some(quote!('a)); - let resource_doc = - format!(" Resource proxy resource `{name}`. Use method `.lock()` to gain access"); fields.push(quote!( - #[doc = #resource_doc] #(#cfgs)* pub #name: shared_resources::#shared_name<'a> )); values.push(quote!( - #[doc(hidden)] #(#cfgs)* #name: shared_resources::#shared_name::new(priority) @@ -95,17 +80,13 @@ pub fn codegen(ctxt: Context, needs_lt: &mut bool, app: &App) -> (TokenStream2, continue; } - let resource_doc; let expr = if access.is_exclusive() { - resource_doc = format!(" Exclusive access resource `{name}`"); quote!(&mut *(&mut *#mangled_name.get_mut()).as_mut_ptr()) } else { - resource_doc = format!(" Non-exclusive access resource `{name}`"); quote!(&*(&*#mangled_name.get()).as_ptr()) }; values.push(quote!( - #[doc = #resource_doc] #(#cfgs)* #name: #expr )); @@ -125,13 +106,12 @@ pub fn codegen(ctxt: Context, needs_lt: &mut bool, app: &App) -> (TokenStream2, } } - let doc = format!(" Shared resources `{}` has access to", ctxt.ident(app)); + let doc = format!("Shared resources `{}` has access to", ctxt.ident(app)); let ident = util::shared_resources_ident(ctxt, app); let item = quote!( #[allow(non_snake_case)] #[allow(non_camel_case_types)] #[doc = #doc] - #(#task_cfgs)* pub struct #ident<#lt> { #(#fields,)* } @@ -143,9 +123,7 @@ pub fn codegen(ctxt: Context, needs_lt: &mut bool, app: &App) -> (TokenStream2, Some(quote!(priority: &#lt rtic::export::Priority)) }; let constructor = quote!( - #(#task_cfgs)* impl<#lt> #ident<#lt> { - #[doc(hidden)] #[inline(always)] pub unsafe fn new(#arg) -> Self { #ident { diff --git a/macros/src/codegen/software_tasks.rs b/macros/src/codegen/software_tasks.rs index 226121dd8c..f9247daed2 100644 --- a/macros/src/codegen/software_tasks.rs +++ b/macros/src/codegen/software_tasks.rs @@ -1,17 +1,14 @@ -use proc_macro2::TokenStream as TokenStream2; -use quote::quote; -use rtic_syntax::{ast::App, Context}; - +use crate::syntax::{ast::App, Context}; use crate::{ analyze::Analysis, - check::Extra, codegen::{local_resources_struct, module, shared_resources_struct, util}, }; +use proc_macro2::TokenStream as TokenStream2; +use quote::quote; pub fn codegen( app: &App, analysis: &Analysis, - extra: &Extra, ) -> ( // mod_app_software_tasks -- free queues, buffers and `${task}Resources` constructors Vec, @@ -27,74 +24,87 @@ pub fn codegen( let mut root = vec![]; let mut user_tasks = vec![]; - for (name, task) in &app.software_tasks { + // Any task + for (name, task) in app.software_tasks.iter() { let inputs = &task.inputs; - let cfgs = &task.cfgs; let (_, _, _, input_ty) = util::regroup_inputs(inputs); let cap = task.args.capacity; let cap_lit = util::capacity_literal(cap as usize); let cap_lit_p1 = util::capacity_literal(cap as usize + 1); - // Create free queues and inputs / instants buffers - let fq = util::fq_ident(name); + if !task.is_async { + // Create free queues and inputs / instants buffers + let fq = util::fq_ident(name); - #[allow(clippy::redundant_closure)] - let (fq_ty, fq_expr, mk_uninit): (_, _, Box Option<_>>) = { - ( - quote!(rtic::export::SCFQ<#cap_lit_p1>), - quote!(rtic::export::Queue::new()), - Box::new(|| Some(util::link_section_uninit())), - ) - }; - mod_app.push(quote!( - // /// Queue version of a free-list that keeps track of empty slots in - // /// the following buffers - #(#cfgs)* - #[allow(non_camel_case_types)] - #[allow(non_upper_case_globals)] - #[doc(hidden)] - static #fq: rtic::RacyCell<#fq_ty> = rtic::RacyCell::new(#fq_expr); - )); + #[allow(clippy::redundant_closure)] + let (fq_ty, fq_expr, mk_uninit): (_, _, Box Option<_>>) = { + ( + quote!(rtic::export::SCFQ<#cap_lit_p1>), + quote!(rtic::export::Queue::new()), + Box::new(|| Some(util::link_section_uninit())), + ) + }; - let elems = &(0..cap) - .map(|_| quote!(core::mem::MaybeUninit::uninit())) - .collect::>(); - - for (_, monotonic) in &app.monotonics { - let instants = util::monotonic_instants_ident(name, &monotonic.ident); - let mono_type = &monotonic.ty; - let cfgs = &monotonic.cfgs; - - let uninit = mk_uninit(); - // For future use - // let doc = format!(" RTIC internal: {}:{}", file!(), line!()); mod_app.push(quote!( + // /// Queue version of a free-list that keeps track of empty slots in + // /// the following buffers + #[allow(non_camel_case_types)] + #[allow(non_upper_case_globals)] + #[doc(hidden)] + static #fq: rtic::RacyCell<#fq_ty> = rtic::RacyCell::new(#fq_expr); + )); + + let elems = &(0..cap) + .map(|_| quote!(core::mem::MaybeUninit::uninit())) + .collect::>(); + + for (_, monotonic) in &app.monotonics { + let instants = util::monotonic_instants_ident(name, &monotonic.ident); + let mono_type = &monotonic.ty; + + let uninit = mk_uninit(); + // For future use + // let doc = format!(" RTIC internal: {}:{}", file!(), line!()); + mod_app.push(quote!( #uninit // /// Buffer that holds the instants associated to the inputs of a task // #[doc = #doc] #[allow(non_camel_case_types)] #[allow(non_upper_case_globals)] #[doc(hidden)] - #(#cfgs)* static #instants: rtic::RacyCell<[core::mem::MaybeUninit<<#mono_type as rtic::Monotonic>::Instant>; #cap_lit]> = rtic::RacyCell::new([#(#elems,)*]); )); + } + + let uninit = mk_uninit(); + let inputs_ident = util::inputs_ident(name); + + // Buffer that holds the inputs of a task + mod_app.push(quote!( + #uninit + #[allow(non_camel_case_types)] + #[allow(non_upper_case_globals)] + #[doc(hidden)] + static #inputs_ident: rtic::RacyCell<[core::mem::MaybeUninit<#input_ty>; #cap_lit]> = + rtic::RacyCell::new([#(#elems,)*]); + )); } - let uninit = mk_uninit(); - let inputs_ident = util::inputs_ident(name); - mod_app.push(quote!( - #uninit - // /// Buffer that holds the inputs of a task - #[allow(non_camel_case_types)] - #[allow(non_upper_case_globals)] - #[doc(hidden)] - #(#cfgs)* - static #inputs_ident: rtic::RacyCell<[core::mem::MaybeUninit<#input_ty>; #cap_lit]> = - rtic::RacyCell::new([#(#elems,)*]); - )); + if task.is_async { + let executor_ident = util::executor_run_ident(name); + mod_app.push(quote!( + #[allow(non_camel_case_types)] + #[allow(non_upper_case_globals)] + #[doc(hidden)] + static #executor_ident: core::sync::atomic::AtomicBool = + core::sync::atomic::AtomicBool::new(false); + )); + } + + let inputs = &task.inputs; // `${task}Resources` let mut shared_needs_lt = false; @@ -130,13 +140,24 @@ pub fn codegen( let attrs = &task.attrs; let cfgs = &task.cfgs; let stmts = &task.stmts; - let user_task_doc = format!(" User SW task {name}"); + let (async_marker, context_lifetime) = if task.is_async { + ( + quote!(async), + if shared_needs_lt || local_needs_lt { + quote!(<'static>) + } else { + quote!() + }, + ) + } else { + (quote!(), quote!()) + }; + user_tasks.push(quote!( - #[doc = #user_task_doc] #(#attrs)* #(#cfgs)* #[allow(non_snake_case)] - fn #name(#context: #name::Context #(,#inputs)*) { + #async_marker fn #name(#context: #name::Context #context_lifetime #(,#inputs)*) { use rtic::Mutex as _; use rtic::mutex::prelude::*; @@ -151,7 +172,6 @@ pub fn codegen( local_needs_lt, app, analysis, - extra, )); } diff --git a/macros/src/codegen/timer_queue.rs b/macros/src/codegen/timer_queue.rs index f5867dc40e..281148d9b4 100644 --- a/macros/src/codegen/timer_queue.rs +++ b/macros/src/codegen/timer_queue.rs @@ -1,18 +1,18 @@ +use crate::syntax::ast::App; +use crate::{analyze::Analysis, codegen::util}; use proc_macro2::TokenStream as TokenStream2; use quote::quote; -use rtic_syntax::ast::App; - -use crate::{analyze::Analysis, check::Extra, codegen::util}; /// Generates timer queues and timer queue handlers #[allow(clippy::too_many_lines)] -pub fn codegen(app: &App, analysis: &Analysis, _extra: &Extra) -> Vec { +pub fn codegen(app: &App, analysis: &Analysis) -> Vec { let mut items = vec![]; if !app.monotonics.is_empty() { // Generate the marker counter used to track for `cancel` and `reschedule` let tq_marker = util::timer_queue_marker_ident(); items.push(quote!( + // #[doc = #doc] #[doc(hidden)] #[allow(non_camel_case_types)] #[allow(non_upper_case_globals)] @@ -26,6 +26,7 @@ pub fn codegen(app: &App, analysis: &Analysis, _extra: &Extra) -> Vec Vec Vec); + let n_task = util::capacity_literal(cap); + let tq_ty = quote!(rtic::export::TimerQueue<#mono_type, #t, #n_task>); // For future use // let doc = format!(" RTIC internal: {}:{}", file!(), line!()); @@ -76,9 +76,12 @@ pub fn codegen(app: &App, analysis: &Analysis, _extra: &Extra) -> Vec = - rtic::RacyCell::new(rtic::export::TimerQueue(rtic::export::SortedLinkedList::new_u16())); + static #tq: rtic::RacyCell<#tq_ty> = rtic::RacyCell::new( + rtic::export::TimerQueue { + task_queue: rtic::export::SortedLinkedList::new_u16(), + waker_queue: rtic::export::IntrusiveSortedLinkedList::new(), + } + ); )); let mono = util::monotonic_ident(&monotonic_name); @@ -89,7 +92,6 @@ pub fn codegen(app: &App, analysis: &Analysis, _extra: &Extra) -> Vec> = rtic::RacyCell::new(None); )); } @@ -102,6 +104,7 @@ pub fn codegen(app: &App, analysis: &Analysis, _extra: &Extra) -> Vec Vec Vec { - rtic::export::interrupt::free(|_| (&mut *#rq.get_mut()).split().0.enqueue_unchecked((#rqt::#name, index))); + rtic::export::interrupt::free(|_| + (&mut *#rq.get_mut()).split().0.enqueue_unchecked((#rqt::#name, index)) + ); #pend } @@ -128,7 +133,6 @@ pub fn codegen(app: &App, analysis: &Analysis, _extra: &Extra) -> Vec>(); - let cfgs = &monotonic.cfgs; let bound_interrupt = &monotonic.args.binds; let disable_isr = if &*bound_interrupt.to_string() == "SysTick" { quote!(core::mem::transmute::<_, rtic::export::SYST>(()).disable_interrupt()) @@ -139,7 +143,6 @@ pub fn codegen(app: &App, analysis: &Analysis, _extra: &Extra) -> Vec Ident { /// Generates a `Mutex` implementation pub fn impl_mutex( - extra: &Extra, + app: &App, cfgs: &[Attribute], resources_prefix: bool, name: &Ident, @@ -35,7 +33,7 @@ pub fn impl_mutex( (quote!(#name), quote!(self.priority)) }; - let device = &extra.device; + let device = &app.args.device; let masks_name = priority_masks_ident(); quote!( #(#cfgs)* @@ -67,6 +65,11 @@ pub fn inputs_ident(task: &Ident) -> Ident { mark_internal_name(&format!("{}_INPUTS", task)) } +/// Generates an identifier for the `EXECUTOR_RUN` atomics (`async` API) +pub fn executor_run_ident(task: &Ident) -> Ident { + mark_internal_name(&format!("{}_EXECUTOR_RUN", task)) +} + /// Generates an identifier for the `INSTANTS` buffer (`schedule` API) pub fn monotonic_instants_ident(task: &Ident, monotonic: &Ident) -> Ident { mark_internal_name(&format!("{}_{}_INSTANTS", task, monotonic)) @@ -179,7 +182,12 @@ pub fn regroup_inputs( pub fn get_task_name(ctxt: Context, app: &App) -> Ident { let s = match ctxt { Context::Init => app.init.name.to_string(), - Context::Idle => app.idle.as_ref().unwrap().name.to_string(), + Context::Idle => app + .idle + .as_ref() + .expect("RTIC-ICE: unable to find idle name") + .name + .to_string(), Context::HardwareTask(ident) | Context::SoftwareTask(ident) => ident.to_string(), }; @@ -190,7 +198,12 @@ pub fn get_task_name(ctxt: Context, app: &App) -> Ident { pub fn shared_resources_ident(ctxt: Context, app: &App) -> Ident { let mut s = match ctxt { Context::Init => app.init.name.to_string(), - Context::Idle => app.idle.as_ref().unwrap().name.to_string(), + Context::Idle => app + .idle + .as_ref() + .expect("RTIC-ICE: unable to find idle name") + .name + .to_string(), Context::HardwareTask(ident) | Context::SoftwareTask(ident) => ident.to_string(), }; @@ -203,7 +216,12 @@ pub fn shared_resources_ident(ctxt: Context, app: &App) -> Ident { pub fn local_resources_ident(ctxt: Context, app: &App) -> Ident { let mut s = match ctxt { Context::Init => app.init.name.to_string(), - Context::Idle => app.idle.as_ref().unwrap().name.to_string(), + Context::Idle => app + .idle + .as_ref() + .expect("RTIC-ICE: unable to find idle name") + .name + .to_string(), Context::HardwareTask(ident) | Context::SoftwareTask(ident) => ident.to_string(), }; @@ -220,9 +238,14 @@ pub fn rq_ident(priority: u8) -> Ident { mark_internal_name(&format!("P{}_RQ", priority)) } +/// Generates an identifier for a ready queue, async task version +pub fn rq_async_ident(async_task_name: &Ident) -> Ident { + mark_internal_name(&format!("ASYNC_TACK_{}_RQ", async_task_name)) +} + /// Generates an identifier for the `enum` of `schedule`-able tasks pub fn schedule_t_ident() -> Ident { - Ident::new("SCHED_T", Span::call_site()) + mark_internal_name("SCHED_T") } /// Generates an identifier for the `enum` of `spawn`-able tasks @@ -230,7 +253,7 @@ pub fn schedule_t_ident() -> Ident { /// This identifier needs the same structure as the `RQ` identifier because there's one ready queue /// for each of these `T` enums pub fn spawn_t_ident(priority: u8) -> Ident { - Ident::new(&format!("P{}_T", priority), Span::call_site()) + mark_internal_name(&format!("P{}_T", priority)) } /// Suffixed identifier diff --git a/macros/src/lib.rs b/macros/src/lib.rs index 2b52601756..7729dcbed0 100644 --- a/macros/src/lib.rs +++ b/macros/src/lib.rs @@ -1,21 +1,46 @@ #![doc( - html_logo_url = "https://raw.githubusercontent.com/rtic-rs/cortex-m-rtic/master/book/en/src/RTIC.svg", - html_favicon_url = "https://raw.githubusercontent.com/rtic-rs/cortex-m-rtic/master/book/en/src/RTIC.svg" + html_logo_url = "https://raw.githubusercontent.com/rtic-rs/rtic/master/book/en/src/RTIC.svg", + html_favicon_url = "https://raw.githubusercontent.com/rtic-rs/rtic/master/book/en/src/RTIC.svg" )] -//deny_warnings_placeholder_for_ci -extern crate proc_macro; +//deny_warnings_placeholder_for_ci use proc_macro::TokenStream; use std::{env, fs, path::Path}; -use rtic_syntax::Settings; - mod analyze; -mod check; +mod bindings; mod codegen; -#[cfg(test)] -mod tests; +mod syntax; + +// Used for mocking the API in testing +#[doc(hidden)] +#[proc_macro_attribute] +pub fn mock_app(args: TokenStream, input: TokenStream) -> TokenStream { + let mut settings = syntax::Settings::default(); + let mut rtic_args = vec![]; + for arg in args.to_string().split(',') { + if arg.trim() == "parse_binds" { + settings.parse_binds = true; + } else if arg.trim() == "parse_extern_interrupt" { + settings.parse_extern_interrupt = true; + } else { + rtic_args.push(arg.to_string()); + } + } + + // rtic_args.push("device = mock".into()); + + let args = rtic_args.join(", ").parse(); + + println!("args: {:?}", args); + + if let Err(e) = syntax::parse(args.unwrap(), input, settings) { + e.to_compile_error().into() + } else { + "fn main() {}".parse().unwrap() + } +} /// Attribute used to declare a RTIC application /// @@ -26,24 +51,19 @@ mod tests; /// Should never panic, cargo feeds a path which is later converted to a string #[proc_macro_attribute] pub fn app(args: TokenStream, input: TokenStream) -> TokenStream { - let mut settings = Settings::default(); + let mut settings = syntax::Settings::default(); settings.optimize_priorities = false; settings.parse_binds = true; settings.parse_extern_interrupt = true; - let (app, analysis) = match rtic_syntax::parse(args, input, settings) { - Err(e) => return e.to_compile_error().into(), - Ok(x) => x, - }; - - let extra = match check::app(&app, &analysis) { + let (app, analysis) = match syntax::parse(args, input, settings) { Err(e) => return e.to_compile_error().into(), Ok(x) => x, }; let analysis = analyze::app(analysis, &app); - let ts = codegen::app(&app, &analysis, &extra); + let ts = codegen::app(&app, &analysis); // Default output path: /target/ let mut out_dir = Path::new("target"); @@ -52,22 +72,7 @@ pub fn app(args: TokenStream, input: TokenStream) -> TokenStream { // TODO don't want to break builds if OUT_DIR is not set, is this ever the case? let out_str = env::var("OUT_DIR").unwrap_or_else(|_| "".to_string()); - // Assuming we are building for a thumbv* target - let target_triple_prefix = "thumbv"; - - // Check for special scenario where default target/ directory is not present - // - // This is configurable in .cargo/config: - // - // [build] - // target-dir = "target" - #[cfg(feature = "debugprint")] - println!("OUT_DIR\n{:#?}", out_str); - - if out_dir.exists() { - #[cfg(feature = "debugprint")] - println!("\ntarget/ exists\n"); - } else { + if !out_dir.exists() { // Set out_dir to OUT_DIR out_dir = Path::new(&out_str); @@ -81,16 +86,11 @@ pub fn app(args: TokenStream, input: TokenStream) -> TokenStream { // If no "target" directory is found, / is used for path in out_dir.ancestors() { if let Some(dir) = path.components().last() { - if dir - .as_os_str() - .to_str() - .unwrap() - .starts_with(target_triple_prefix) - { + let dir = dir.as_os_str().to_str().unwrap(); + + if dir.starts_with("thumbv") || dir.starts_with("riscv") { if let Some(out) = path.parent() { out_dir = out; - #[cfg(feature = "debugprint")] - println!("{:#?}\n", out_dir); break; } // If no parent, just use it @@ -103,8 +103,6 @@ pub fn app(args: TokenStream, input: TokenStream) -> TokenStream { // Try to write the expanded code to disk if let Some(out_str) = out_dir.to_str() { - #[cfg(feature = "debugprint")] - println!("Write file:\n{}/rtic-expansion.rs\n", out_str); fs::write(format!("{}/rtic-expansion.rs", out_str), ts.to_string()).ok(); } diff --git a/macros/src/syntax.rs b/macros/src/syntax.rs new file mode 100644 index 0000000000..11b92c1b9d --- /dev/null +++ b/macros/src/syntax.rs @@ -0,0 +1,158 @@ +#[allow(unused_extern_crates)] +extern crate proc_macro; + +use core::ops; +use proc_macro::TokenStream; + +use indexmap::{IndexMap, IndexSet}; +use proc_macro2::TokenStream as TokenStream2; +use syn::Ident; + +use crate::syntax::ast::App; + +mod accessors; +pub mod analyze; +pub mod ast; +mod check; +mod optimize; +mod parse; + +/// An ordered map keyed by identifier +pub type Map = IndexMap; + +/// An order set +pub type Set = IndexSet; + +/// Immutable pointer +pub struct P { + ptr: Box, +} + +impl P { + /// Boxes `x` making the value immutable + pub fn new(x: T) -> P { + P { ptr: Box::new(x) } + } +} + +impl ops::Deref for P { + type Target = T; + + fn deref(&self) -> &T { + &self.ptr + } +} + +/// Execution context +#[derive(Clone, Copy)] +pub enum Context<'a> { + /// The `idle` context + Idle, + + /// The `init`-ialization function + Init, + + /// A software task: `#[task]` + SoftwareTask(&'a Ident), + + /// A hardware task: `#[exception]` or `#[interrupt]` + HardwareTask(&'a Ident), +} + +impl<'a> Context<'a> { + /// The identifier of this context + pub fn ident(&self, app: &'a App) -> &'a Ident { + match self { + Context::HardwareTask(ident) => ident, + Context::Idle => &app.idle.as_ref().unwrap().name, + Context::Init => &app.init.name, + Context::SoftwareTask(ident) => ident, + } + } + + /// Is this the `idle` context? + pub fn is_idle(&self) -> bool { + matches!(self, Context::Idle) + } + + /// Is this the `init`-ialization context? + pub fn is_init(&self) -> bool { + matches!(self, Context::Init) + } + + /// Whether this context runs only once + pub fn runs_once(&self) -> bool { + self.is_init() || self.is_idle() + } + + /// Whether this context has shared resources + pub fn has_shared_resources(&self, app: &App) -> bool { + match *self { + Context::HardwareTask(name) => { + !app.hardware_tasks[name].args.shared_resources.is_empty() + } + Context::Idle => !app.idle.as_ref().unwrap().args.shared_resources.is_empty(), + Context::Init => false, + Context::SoftwareTask(name) => { + !app.software_tasks[name].args.shared_resources.is_empty() + } + } + } + + /// Whether this context has local resources + pub fn has_local_resources(&self, app: &App) -> bool { + match *self { + Context::HardwareTask(name) => { + !app.hardware_tasks[name].args.local_resources.is_empty() + } + Context::Idle => !app.idle.as_ref().unwrap().args.local_resources.is_empty(), + Context::Init => !app.init.args.local_resources.is_empty(), + Context::SoftwareTask(name) => { + !app.software_tasks[name].args.local_resources.is_empty() + } + } + } +} + +/// Parser and optimizer configuration +#[derive(Default)] +#[non_exhaustive] +pub struct Settings { + /// Whether to accept the `binds` argument in `#[task]` or not + pub parse_binds: bool, + /// Whether to parse `extern` interrupts (functions) or not + pub parse_extern_interrupt: bool, + /// Whether to "compress" priorities or not + pub optimize_priorities: bool, +} + +/// Parses the input of the `#[app]` attribute +pub fn parse( + args: TokenStream, + input: TokenStream, + settings: Settings, +) -> Result<(ast::App, analyze::Analysis), syn::parse::Error> { + parse2(args.into(), input.into(), settings) +} + +/// `proc_macro2::TokenStream` version of `parse` +pub fn parse2( + args: TokenStream2, + input: TokenStream2, + settings: Settings, +) -> Result<(ast::App, analyze::Analysis), syn::parse::Error> { + let mut app = parse::app(args, input, &settings)?; + check::app(&app)?; + optimize::app(&mut app, &settings); + + match analyze::app(&app) { + Err(e) => Err(e), + // If no errors, return the app and analysis results + Ok(analysis) => Ok((app, analysis)), + } +} + +enum Either { + Left(A), + Right(B), +} diff --git a/macros/src/syntax/.github/bors.toml b/macros/src/syntax/.github/bors.toml new file mode 100644 index 0000000000..aee6042f81 --- /dev/null +++ b/macros/src/syntax/.github/bors.toml @@ -0,0 +1,3 @@ +block_labels = ["S-blocked"] +delete_merged_branches = true +status = ["ci"] diff --git a/macros/src/syntax/.github/workflows/build.yml b/macros/src/syntax/.github/workflows/build.yml new file mode 100644 index 0000000000..29971b1021 --- /dev/null +++ b/macros/src/syntax/.github/workflows/build.yml @@ -0,0 +1,213 @@ +name: Build +on: + pull_request: + push: + branches: + - master + - staging + - trying + - bors/staging + - bors/trying + +env: + CARGO_TERM_COLOR: always + +jobs: + # Run cargo fmt --check + style: + name: style + runs-on: ubuntu-20.04 + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: Install Rust + uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: stable + override: true + components: rustfmt + + - name: Fail on warnings + run: sed -i 's,//deny_warnings_placeholder_for_ci,#![deny(warnings)],' src/lib.rs + + - name: cargo fmt --check + uses: actions-rs/cargo@v1 + with: + command: fmt + args: --all -- --check + + # Compilation check + check: + name: check + runs-on: ubuntu-20.04 + strategy: + matrix: + target: + - x86_64-unknown-linux-gnu + toolchain: + - stable + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: Install Rust ${{ matrix.toolchain }} with target (${{ matrix.target }}) + uses: actions-rs/toolchain@v1 + with: + toolchain: ${{ matrix.toolchain }} + target: ${{ matrix.target }} + override: true + + - name: Fail on warnings + run: sed -i 's,//deny_warnings_placeholder_for_ci,#![deny(warnings)],' src/lib.rs + + - name: Cache Dependencies + uses: Swatinem/rust-cache@v1 + + - name: cargo check + uses: actions-rs/cargo@v1 + with: + use-cross: false + command: check + args: --target=${{ matrix.target }} + + # Clippy + clippy: + name: Cargo clippy + runs-on: ubuntu-20.04 + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: Install Rust stable + uses: actions-rs/toolchain@v1 + with: + toolchain: stable + target: x86_64-unknown-linux-gnu + override: true + + - name: Fail on warnings + run: sed -i 's,//deny_warnings_placeholder_for_ci,#![deny(warnings)],' src/lib.rs + + - name: Cache Dependencies + uses: Swatinem/rust-cache@v1 + + - name: cargo clippy + uses: actions-rs/cargo@v1 + with: + use-cross: false + command: clippy + + # Verify all examples + testexamples: + name: testexamples + runs-on: ubuntu-20.04 + strategy: + matrix: + target: + - x86_64-unknown-linux-gnu + toolchain: + - stable + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: Install Rust ${{ matrix.toolchain }} with target (${{ matrix.target }}) + uses: actions-rs/toolchain@v1 + with: + toolchain: ${{ matrix.toolchain }} + target: ${{ matrix.target }} + override: true + + - name: Cache Dependencies + uses: Swatinem/rust-cache@v1 + + - name: Fail on warnings + run: sed -i 's,//deny_warnings_placeholder_for_ci,#![deny(warnings)],' src/lib.rs + + - uses: actions-rs/cargo@v1 + with: + use-cross: false + command: test + args: --examples + + # Run test suite for UI + testui: + name: testui + runs-on: ubuntu-20.04 + strategy: + matrix: + target: + - x86_64-unknown-linux-gnu + toolchain: + - stable + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: Install Rust ${{ matrix.toolchain }} with target (${{ matrix.target }}) + uses: actions-rs/toolchain@v1 + with: + toolchain: ${{ matrix.toolchain }} + target: ${{ matrix.target }} + override: true + + - name: Cache Dependencies + uses: Swatinem/rust-cache@v1 + + - name: Fail on warnings + run: sed -i 's,//deny_warnings_placeholder_for_ci,#![deny(warnings)],' src/lib.rs + + + - uses: actions-rs/cargo@v1 + with: + use-cross: false + command: test + args: --test ui + + # Run test suite + test: + name: test + runs-on: ubuntu-20.04 + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: Install Rust + uses: actions-rs/toolchain@v1 + with: + toolchain: stable + target: thumbv7m-none-eabi + override: true + + - name: Cache Dependencies + uses: Swatinem/rust-cache@v1 + + - name: Fail on warnings + run: sed -i 's,//deny_warnings_placeholder_for_ci,#![deny(warnings)],' src/lib.rs + + - uses: actions-rs/cargo@v1 + with: + use-cross: false + command: test + args: --lib + + # Refs: https://github.com/rust-lang/crater/blob/9ab6f9697c901c4a44025cf0a39b73ad5b37d198/.github/workflows/bors.yml#L125-L149 + # + # ALL THE PREVIOUS JOBS NEEDS TO BE ADDED TO THE `needs` SECTION OF THIS JOB! + + ci-success: + name: ci + if: github.event_name == 'push' && success() + needs: + - style + - check + - clippy + - testexamples + - test + - testui + runs-on: ubuntu-20.04 + steps: + - name: Mark the job as a success + run: exit 0 diff --git a/macros/src/syntax/.github/workflows/changelog.yml b/macros/src/syntax/.github/workflows/changelog.yml new file mode 100644 index 0000000000..ccf6eb9103 --- /dev/null +++ b/macros/src/syntax/.github/workflows/changelog.yml @@ -0,0 +1,28 @@ +# Check that the changelog is updated for all changes. +# +# This is only run for PRs. + +on: + pull_request: + # opened, reopened, synchronize are the default types for pull_request. + # labeled, unlabeled ensure this check is also run if a label is added or removed. + types: [opened, reopened, labeled, unlabeled, synchronize] + +name: Changelog + +jobs: + changelog: + name: Changelog + runs-on: ubuntu-latest + steps: + - name: Checkout sources + uses: actions/checkout@v2 + + - name: Check that changelog updated + uses: dangoslen/changelog-enforcer@v3 + with: + changeLogPath: CHANGELOG.md + skipLabels: 'needs-changelog, skip-changelog' + missingUpdateErrorMessage: 'Please add a changelog entry in the CHANGELOG.md file.' + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file diff --git a/macros/src/syntax/.github/workflows/properties/build.properties.json b/macros/src/syntax/.github/workflows/properties/build.properties.json new file mode 100644 index 0000000000..fd3eed37a1 --- /dev/null +++ b/macros/src/syntax/.github/workflows/properties/build.properties.json @@ -0,0 +1,6 @@ +{ + "name": "Build", + "description": "RTIC Test Suite", + "iconName": "rust", + "categories": ["Rust"] +} diff --git a/macros/src/syntax/.gitignore b/macros/src/syntax/.gitignore new file mode 100644 index 0000000000..f8d7c8b493 --- /dev/null +++ b/macros/src/syntax/.gitignore @@ -0,0 +1,4 @@ +**/*.rs.bk +.#* +/target/ +Cargo.lock diff --git a/macros/src/syntax/.travis.yml b/macros/src/syntax/.travis.yml new file mode 100644 index 0000000000..52d1ffdd61 --- /dev/null +++ b/macros/src/syntax/.travis.yml @@ -0,0 +1,31 @@ +language: rust + +matrix: + include: + # MSRV + - env: TARGET=x86_64-unknown-linux-gnu + rust: 1.36.0 + + - env: TARGET=x86_64-unknown-linux-gnu + rust: stable + +before_install: set -e + +script: + - bash ci/script.sh + +after_script: set +e + +cache: cargo + +before_cache: + - chmod -R a+r $HOME/.cargo; + +branches: + only: + - staging + - trying + +notifications: + email: + on_success: never diff --git a/macros/src/syntax/accessors.rs b/macros/src/syntax/accessors.rs new file mode 100644 index 0000000000..e75dde6c3b --- /dev/null +++ b/macros/src/syntax/accessors.rs @@ -0,0 +1,113 @@ +use syn::Ident; + +use crate::syntax::{ + analyze::Priority, + ast::{Access, App, Local, TaskLocal}, +}; + +impl App { + pub(crate) fn shared_resource_accesses( + &self, + ) -> impl Iterator, &Ident, Access)> { + self.idle + .iter() + .flat_map(|idle| { + idle.args + .shared_resources + .iter() + .map(move |(name, access)| (Some(0), name, *access)) + }) + .chain(self.hardware_tasks.values().flat_map(|task| { + task.args + .shared_resources + .iter() + .map(move |(name, access)| (Some(task.args.priority), name, *access)) + })) + .chain(self.software_tasks.values().flat_map(|task| { + task.args + .shared_resources + .iter() + .map(move |(name, access)| (Some(task.args.priority), name, *access)) + })) + } + + fn is_external(task_local: &TaskLocal) -> bool { + matches!(task_local, TaskLocal::External) + } + + pub(crate) fn local_resource_accesses(&self) -> impl Iterator { + self.init + .args + .local_resources + .iter() + .filter(|(_, task_local)| Self::is_external(task_local)) // Only check the resources declared in `#[local]` + .map(move |(name, _)| name) + .chain(self.idle.iter().flat_map(|idle| { + idle.args + .local_resources + .iter() + .filter(|(_, task_local)| Self::is_external(task_local)) // Only check the resources declared in `#[local]` + .map(move |(name, _)| name) + })) + .chain(self.hardware_tasks.values().flat_map(|task| { + task.args + .local_resources + .iter() + .filter(|(_, task_local)| Self::is_external(task_local)) // Only check the resources declared in `#[local]` + .map(move |(name, _)| name) + })) + .chain(self.software_tasks.values().flat_map(|task| { + task.args + .local_resources + .iter() + .filter(|(_, task_local)| Self::is_external(task_local)) // Only check the resources declared in `#[local]` + .map(move |(name, _)| name) + })) + } + + fn get_declared_local(tl: &TaskLocal) -> Option<&Local> { + match tl { + TaskLocal::External => None, + TaskLocal::Declared(l) => Some(l), + } + } + + /// Get all declared local resources, i.e. `local = [NAME: TYPE = EXPR]`. + /// + /// Returns a vector of (task name, resource name, `Local` struct) + pub fn declared_local_resources(&self) -> Vec<(&Ident, &Ident, &Local)> { + self.init + .args + .local_resources + .iter() + .filter_map(move |(name, tl)| { + Self::get_declared_local(tl).map(|l| (&self.init.name, name, l)) + }) + .chain(self.idle.iter().flat_map(|idle| { + idle.args + .local_resources + .iter() + .filter_map(move |(name, tl)| { + Self::get_declared_local(tl) + .map(|l| (&self.idle.as_ref().unwrap().name, name, l)) + }) + })) + .chain(self.hardware_tasks.iter().flat_map(|(task_name, task)| { + task.args + .local_resources + .iter() + .filter_map(move |(name, tl)| { + Self::get_declared_local(tl).map(|l| (task_name, name, l)) + }) + })) + .chain(self.software_tasks.iter().flat_map(|(task_name, task)| { + task.args + .local_resources + .iter() + .filter_map(move |(name, tl)| { + Self::get_declared_local(tl).map(|l| (task_name, name, l)) + }) + })) + .collect() + } +} diff --git a/macros/src/syntax/analyze.rs b/macros/src/syntax/analyze.rs new file mode 100644 index 0000000000..06b23f4685 --- /dev/null +++ b/macros/src/syntax/analyze.rs @@ -0,0 +1,448 @@ +//! RTIC application analysis + +use core::cmp; +use std::collections::{BTreeMap, BTreeSet, HashMap}; + +use indexmap::{IndexMap, IndexSet}; +use syn::{Ident, Type}; + +use crate::syntax::{ + ast::{App, LocalResources, TaskLocal}, + Set, +}; + +pub(crate) fn app(app: &App) -> Result { + // Collect all tasks into a vector + type TaskName = String; + type Priority = u8; + + // The task list is a Tuple (Name, Shared Resources, Local Resources, Priority, IsAsync) + let task_resources_list: Vec<(TaskName, Vec<&Ident>, &LocalResources, Priority, bool)> = + Some(&app.init) + .iter() + .map(|ht| { + ( + "init".to_string(), + Vec::new(), + &ht.args.local_resources, + 0, + false, + ) + }) + .chain(app.idle.iter().map(|ht| { + ( + "idle".to_string(), + ht.args + .shared_resources + .iter() + .map(|(v, _)| v) + .collect::>(), + &ht.args.local_resources, + 0, + false, + ) + })) + .chain(app.software_tasks.iter().map(|(name, ht)| { + ( + name.to_string(), + ht.args + .shared_resources + .iter() + .map(|(v, _)| v) + .collect::>(), + &ht.args.local_resources, + ht.args.priority, + ht.is_async, + ) + })) + .chain(app.hardware_tasks.iter().map(|(name, ht)| { + ( + name.to_string(), + ht.args + .shared_resources + .iter() + .map(|(v, _)| v) + .collect::>(), + &ht.args.local_resources, + ht.args.priority, + false, + ) + })) + .collect(); + + let mut error = vec![]; + let mut lf_res_with_error = vec![]; + let mut lf_hash = HashMap::new(); + + // Collect lock free resources + let lock_free: Vec<&Ident> = app + .shared_resources + .iter() + .filter(|(_, r)| r.properties.lock_free) + .map(|(i, _)| i) + .collect(); + + // Check that lock_free resources are correct + for lf_res in lock_free.iter() { + for (task, tr, _, priority, is_async) in task_resources_list.iter() { + for r in tr { + // Get all uses of resources annotated lock_free + if lf_res == r { + // lock_free resources are not allowed in async tasks + if *is_async { + error.push(syn::Error::new( + r.span(), + format!( + "Lock free shared resource {:?} is used by an async tasks, which is forbidden", + r.to_string(), + ), + )); + } + + // HashMap returns the previous existing object if old.key == new.key + if let Some(lf_res) = lf_hash.insert(r.to_string(), (task, r, priority)) { + // Check if priority differ, if it does, append to + // list of resources which will be annotated with errors + if priority != lf_res.2 { + lf_res_with_error.push(lf_res.1); + lf_res_with_error.push(r); + } + // If the resource already violates lock free properties + if lf_res_with_error.contains(&r) { + lf_res_with_error.push(lf_res.1); + lf_res_with_error.push(r); + } + } + } + } + } + } + + // Add error message in the resource struct + for r in lock_free { + if lf_res_with_error.contains(&&r) { + error.push(syn::Error::new( + r.span(), + format!( + "Lock free shared resource {:?} is used by tasks at different priorities", + r.to_string(), + ), + )); + } + } + + // Add error message for each use of the shared resource + for resource in lf_res_with_error.clone() { + error.push(syn::Error::new( + resource.span(), + format!( + "Shared resource {:?} is declared lock free but used by tasks at different priorities", + resource.to_string(), + ), + )); + } + + // Collect local resources + let local: Vec<&Ident> = app.local_resources.iter().map(|(i, _)| i).collect(); + + let mut lr_with_error = vec![]; + let mut lr_hash = HashMap::new(); + + // Check that local resources are not shared + for lr in local { + for (task, _, local_resources, _, _) in task_resources_list.iter() { + for (name, res) in local_resources.iter() { + // Get all uses of resources annotated lock_free + if lr == name { + match res { + TaskLocal::External => { + // HashMap returns the previous existing object if old.key == new.key + if let Some(lr) = lr_hash.insert(name.to_string(), (task, name)) { + lr_with_error.push(lr.1); + lr_with_error.push(name); + } + } + // If a declared local has the same name as the `#[local]` struct, it's an + // direct error + TaskLocal::Declared(_) => { + lr_with_error.push(lr); + lr_with_error.push(name); + } + } + } + } + } + } + + // Add error message for each use of the local resource + for resource in lr_with_error.clone() { + error.push(syn::Error::new( + resource.span(), + format!( + "Local resource {:?} is used by multiple tasks or collides with multiple definitions", + resource.to_string(), + ), + )); + } + + // Check 0-priority async software tasks and idle dependency + for (name, task) in &app.software_tasks { + if task.args.priority == 0 { + // If there is a 0-priority task, there must be no idle + if app.idle.is_some() { + error.push(syn::Error::new( + name.span(), + format!( + "Software task {:?} has priority 0, but `#[idle]` is defined. 0-priority software tasks are only allowed if there is no `#[idle]`.", + name.to_string(), + ) + )); + } + + // 0-priority tasks must be async + if !task.is_async { + error.push(syn::Error::new( + name.span(), + format!( + "Software task {:?} has priority 0, but is not `async`. 0-priority software tasks must be `async`.", + name.to_string(), + ) + )); + } + } + } + + // Collect errors if any and return/halt + if !error.is_empty() { + let mut err = error.get(0).unwrap().clone(); + error.iter().for_each(|e| err.combine(e.clone())); + return Err(err); + } + + // e. Location of resources + let mut used_shared_resource = IndexSet::new(); + let mut ownerships = Ownerships::new(); + let mut sync_types = SyncTypes::new(); + for (prio, name, access) in app.shared_resource_accesses() { + let res = app.shared_resources.get(name).expect("UNREACHABLE"); + + // (e) + // This shared resource is used + used_shared_resource.insert(name.clone()); + + // (c) + if let Some(priority) = prio { + if let Some(ownership) = ownerships.get_mut(name) { + match *ownership { + Ownership::Owned { priority: ceiling } + | Ownership::CoOwned { priority: ceiling } + | Ownership::Contended { ceiling } + if priority != ceiling => + { + *ownership = Ownership::Contended { + ceiling: cmp::max(ceiling, priority), + }; + + if access.is_shared() { + sync_types.insert(res.ty.clone()); + } + } + + Ownership::Owned { priority: ceil } if ceil == priority => { + *ownership = Ownership::CoOwned { priority }; + } + + _ => {} + } + } else { + ownerships.insert(name.clone(), Ownership::Owned { priority }); + } + } + } + + // Create the list of used local resource Idents + let mut used_local_resource = IndexSet::new(); + + for (_, _, locals, _, _) in task_resources_list { + for (local, _) in locals { + used_local_resource.insert(local.clone()); + } + } + + // Most shared resources need to be `Send` + let mut send_types = SendTypes::new(); + let owned_by_idle = Ownership::Owned { priority: 0 }; + for (name, res) in app.shared_resources.iter() { + // Handle not owned by idle + if ownerships + .get(name) + .map(|ownership| *ownership != owned_by_idle) + .unwrap_or(false) + { + send_types.insert(res.ty.clone()); + } + } + + // Most local resources need to be `Send` as well + for (name, res) in app.local_resources.iter() { + if let Some(idle) = &app.idle { + // Only Send if not in idle or not at idle prio + if idle.args.local_resources.get(name).is_none() + && !ownerships + .get(name) + .map(|ownership| *ownership != owned_by_idle) + .unwrap_or(false) + { + send_types.insert(res.ty.clone()); + } + } else { + send_types.insert(res.ty.clone()); + } + } + + let mut channels = Channels::new(); + + for (name, spawnee) in &app.software_tasks { + let spawnee_prio = spawnee.args.priority; + + let channel = channels.entry(spawnee_prio).or_default(); + channel.tasks.insert(name.clone()); + + if !spawnee.args.only_same_priority_spawn { + // Require `Send` if the task can be spawned from other priorities + spawnee.inputs.iter().for_each(|input| { + send_types.insert(input.ty.clone()); + }); + } + } + + // No channel should ever be empty + debug_assert!(channels.values().all(|channel| !channel.tasks.is_empty())); + + // Compute channel capacities + for channel in channels.values_mut() { + channel.capacity = channel + .tasks + .iter() + .map(|name| app.software_tasks[name].args.capacity) + .sum(); + } + + Ok(Analysis { + channels, + shared_resources: used_shared_resource, + local_resources: used_local_resource, + ownerships, + send_types, + sync_types, + }) +} + +/// Priority ceiling +pub type Ceiling = Option; + +/// Task priority +pub type Priority = u8; + +/// Resource name +pub type Resource = Ident; + +/// Task name +pub type Task = Ident; + +/// The result of analyzing an RTIC application +pub struct Analysis { + /// SPSC message channels + pub channels: Channels, + + /// Shared resources + /// + /// If a resource is not listed here it means that's a "dead" (never + /// accessed) resource and the backend should not generate code for it + pub shared_resources: UsedSharedResource, + + /// Local resources + /// + /// If a resource is not listed here it means that's a "dead" (never + /// accessed) resource and the backend should not generate code for it + pub local_resources: UsedLocalResource, + + /// Resource ownership + pub ownerships: Ownerships, + + /// These types must implement the `Send` trait + pub send_types: SendTypes, + + /// These types must implement the `Sync` trait + pub sync_types: SyncTypes, +} + +/// All channels, keyed by dispatch priority +pub type Channels = BTreeMap; + +/// Location of all *used* shared resources +pub type UsedSharedResource = IndexSet; + +/// Location of all *used* local resources +pub type UsedLocalResource = IndexSet; + +/// Resource ownership +pub type Ownerships = IndexMap; + +/// These types must implement the `Send` trait +pub type SendTypes = Set>; + +/// These types must implement the `Sync` trait +pub type SyncTypes = Set>; + +/// A channel used to send messages +#[derive(Debug, Default)] +pub struct Channel { + /// The channel capacity + pub capacity: u8, + + /// Tasks that can be spawned on this channel + pub tasks: BTreeSet, +} + +/// Resource ownership +#[derive(Clone, Copy, Debug, PartialEq)] +pub enum Ownership { + /// Owned by a single task + Owned { + /// Priority of the task that owns this resource + priority: u8, + }, + + /// "Co-owned" by more than one task; all of them have the same priority + CoOwned { + /// Priority of the tasks that co-own this resource + priority: u8, + }, + + /// Contended by more than one task; the tasks have different priorities + Contended { + /// Priority ceiling + ceiling: u8, + }, +} + +impl Ownership { + /// Whether this resource needs to a lock at this priority level + pub fn needs_lock(&self, priority: u8) -> bool { + match self { + Ownership::Owned { .. } | Ownership::CoOwned { .. } => false, + + Ownership::Contended { ceiling } => { + debug_assert!(*ceiling >= priority); + + priority < *ceiling + } + } + } + + /// Whether this resource is exclusively owned + pub fn is_owned(&self) -> bool { + matches!(self, Ownership::Owned { .. }) + } +} diff --git a/macros/src/syntax/ast.rs b/macros/src/syntax/ast.rs new file mode 100644 index 0000000000..0f2e36f44c --- /dev/null +++ b/macros/src/syntax/ast.rs @@ -0,0 +1,380 @@ +//! Abstract Syntax Tree + +use syn::{Attribute, Expr, Ident, Item, ItemUse, Pat, PatType, Path, Stmt, Type}; + +use crate::syntax::Map; + +/// The `#[app]` attribute +#[derive(Debug)] +#[non_exhaustive] +pub struct App { + /// The arguments to the `#[app]` attribute + pub args: AppArgs, + + /// The name of the `const` item on which the `#[app]` attribute has been placed + pub name: Ident, + + /// The `#[init]` function + pub init: Init, + + /// The `#[idle]` function + pub idle: Option, + + /// Monotonic clocks + pub monotonics: Map, + + /// Resources shared between tasks defined in `#[shared]` + pub shared_resources: Map, + + /// Task local resources defined in `#[local]` + pub local_resources: Map, + + /// User imports + pub user_imports: Vec, + + /// User code + pub user_code: Vec, + + /// Hardware tasks: `#[task(binds = ..)]`s + pub hardware_tasks: Map, + + /// Software tasks: `#[task]` + pub software_tasks: Map, +} + +/// Interrupts used to dispatch software tasks +pub type Dispatchers = Map; + +/// Interrupt that could be used to dispatch software tasks +#[derive(Debug, Clone)] +#[non_exhaustive] +pub struct Dispatcher { + /// Attributes that will apply to this interrupt handler + pub attrs: Vec, +} + +/// The arguments of the `#[app]` attribute +#[derive(Debug)] +pub struct AppArgs { + /// Device + pub device: Path, + + /// Peripherals + pub peripherals: bool, + + /// Interrupts used to dispatch software tasks + pub dispatchers: Dispatchers, +} + +/// The `init`-ialization function +#[derive(Debug)] +#[non_exhaustive] +pub struct Init { + /// `init` context metadata + pub args: InitArgs, + + /// Attributes that will apply to this `init` function + pub attrs: Vec, + + /// The name of the `#[init]` function + pub name: Ident, + + /// The context argument + pub context: Box, + + /// The statements that make up this `init` function + pub stmts: Vec, + + /// The name of the user provided shared resources struct + pub user_shared_struct: Ident, + + /// The name of the user provided local resources struct + pub user_local_struct: Ident, +} + +/// `init` context metadata +#[derive(Debug)] +#[non_exhaustive] +pub struct InitArgs { + /// Local resources that can be accessed from this context + pub local_resources: LocalResources, +} + +impl Default for InitArgs { + fn default() -> Self { + Self { + local_resources: LocalResources::new(), + } + } +} + +/// The `idle` context +#[derive(Debug)] +#[non_exhaustive] +pub struct Idle { + /// `idle` context metadata + pub args: IdleArgs, + + /// Attributes that will apply to this `idle` function + pub attrs: Vec, + + /// The name of the `#[idle]` function + pub name: Ident, + + /// The context argument + pub context: Box, + + /// The statements that make up this `idle` function + pub stmts: Vec, +} + +/// `idle` context metadata +#[derive(Debug)] +#[non_exhaustive] +pub struct IdleArgs { + /// Local resources that can be accessed from this context + pub local_resources: LocalResources, + + /// Shared resources that can be accessed from this context + pub shared_resources: SharedResources, +} + +impl Default for IdleArgs { + fn default() -> Self { + Self { + local_resources: LocalResources::new(), + shared_resources: SharedResources::new(), + } + } +} + +/// Shared resource properties +#[derive(Debug)] +pub struct SharedResourceProperties { + /// A lock free (exclusive resource) + pub lock_free: bool, +} + +/// A shared resource, defined in `#[shared]` +#[derive(Debug)] +#[non_exhaustive] +pub struct SharedResource { + /// `#[cfg]` attributes like `#[cfg(debug_assertions)]` + pub cfgs: Vec, + + /// `#[doc]` attributes like `/// this is a docstring` + pub docs: Vec, + + /// Attributes that will apply to this resource + pub attrs: Vec, + + /// The type of this resource + pub ty: Box, + + /// Shared resource properties + pub properties: SharedResourceProperties, +} + +/// A local resource, defined in `#[local]` +#[derive(Debug)] +#[non_exhaustive] +pub struct LocalResource { + /// `#[cfg]` attributes like `#[cfg(debug_assertions)]` + pub cfgs: Vec, + + /// `#[doc]` attributes like `/// this is a docstring` + pub docs: Vec, + + /// Attributes that will apply to this resource + pub attrs: Vec, + + /// The type of this resource + pub ty: Box, +} + +/// Monotonic +#[derive(Debug)] +#[non_exhaustive] +pub struct Monotonic { + /// `#[cfg]` attributes like `#[cfg(debug_assertions)]` + pub cfgs: Vec, + + /// The identifier of the monotonic + pub ident: Ident, + + /// The type of this monotonic + pub ty: Box, + + /// Monotonic args + pub args: MonotonicArgs, +} + +/// Monotonic metadata +#[derive(Debug)] +#[non_exhaustive] +pub struct MonotonicArgs { + /// The interrupt or exception that this monotonic is bound to + pub binds: Ident, + + /// The priority of this monotonic + pub priority: Option, + + /// If this is the default monotonic + pub default: bool, +} + +/// A software task +#[derive(Debug)] +#[non_exhaustive] +pub struct SoftwareTask { + /// Software task metadata + pub args: SoftwareTaskArgs, + + /// `#[cfg]` attributes like `#[cfg(debug_assertions)]` + pub cfgs: Vec, + + /// Attributes that will apply to this interrupt handler + pub attrs: Vec, + + /// The context argument + pub context: Box, + + /// The inputs of this software task + pub inputs: Vec, + + /// The statements that make up the task handler + pub stmts: Vec, + + /// The task is declared externally + pub is_extern: bool, + + /// If the task is marked as `async` + pub is_async: bool, +} + +/// Software task metadata +#[derive(Debug)] +#[non_exhaustive] +pub struct SoftwareTaskArgs { + /// The task capacity: the maximum number of pending messages that can be queued + pub capacity: u8, + + /// The priority of this task + pub priority: u8, + + /// Local resources that can be accessed from this context + pub local_resources: LocalResources, + + /// Shared resources that can be accessed from this context + pub shared_resources: SharedResources, + + /// Only same priority tasks can spawn this task + pub only_same_priority_spawn: bool, +} + +impl Default for SoftwareTaskArgs { + fn default() -> Self { + Self { + capacity: 1, + priority: 1, + local_resources: LocalResources::new(), + shared_resources: SharedResources::new(), + only_same_priority_spawn: false, + } + } +} + +/// A hardware task +#[derive(Debug)] +#[non_exhaustive] +pub struct HardwareTask { + /// Hardware task metadata + pub args: HardwareTaskArgs, + + /// `#[cfg]` attributes like `#[cfg(debug_assertions)]` + pub cfgs: Vec, + + /// Attributes that will apply to this interrupt handler + pub attrs: Vec, + + /// The context argument + pub context: Box, + + /// The statements that make up the task handler + pub stmts: Vec, + + /// The task is declared externally + pub is_extern: bool, +} + +/// Hardware task metadata +#[derive(Debug)] +#[non_exhaustive] +pub struct HardwareTaskArgs { + /// The interrupt or exception that this task is bound to + pub binds: Ident, + + /// The priority of this task + pub priority: u8, + + /// Local resources that can be accessed from this context + pub local_resources: LocalResources, + + /// Shared resources that can be accessed from this context + pub shared_resources: SharedResources, +} + +/// A `static mut` variable local to and owned by a context +#[derive(Debug)] +#[non_exhaustive] +pub struct Local { + /// Attributes like `#[link_section]` + pub attrs: Vec, + + /// `#[cfg]` attributes like `#[cfg(debug_assertions)]` + pub cfgs: Vec, + + /// Type + pub ty: Box, + + /// Initial value + pub expr: Box, +} + +/// A wrapper of the 2 kinds of locals that tasks can have +#[derive(Debug)] +#[non_exhaustive] +pub enum TaskLocal { + /// The local is declared externally (i.e. `#[local]` struct) + External, + /// The local is declared in the task + Declared(Local), +} + +/// Resource access +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum Access { + /// `[x]`, a mutable resource + Exclusive, + + /// `[&x]`, a static non-mutable resource + Shared, +} + +impl Access { + /// Is this enum in the `Exclusive` variant? + pub fn is_exclusive(&self) -> bool { + *self == Access::Exclusive + } + + /// Is this enum in the `Shared` variant? + pub fn is_shared(&self) -> bool { + *self == Access::Shared + } +} + +/// Shared resource access list in task attribute +pub type SharedResources = Map; + +/// Local resource access/declaration list in task attribute +pub type LocalResources = Map; diff --git a/macros/src/syntax/check.rs b/macros/src/syntax/check.rs new file mode 100644 index 0000000000..989d41804c --- /dev/null +++ b/macros/src/syntax/check.rs @@ -0,0 +1,66 @@ +use std::collections::HashSet; + +use syn::parse; + +use crate::syntax::ast::App; + +pub fn app(app: &App) -> parse::Result<()> { + // Check that all referenced resources have been declared + // Check that resources are NOT `Exclusive`-ly shared + let mut owners = HashSet::new(); + for (_, name, access) in app.shared_resource_accesses() { + if app.shared_resources.get(name).is_none() { + return Err(parse::Error::new( + name.span(), + "this shared resource has NOT been declared", + )); + } + + if access.is_exclusive() { + owners.insert(name); + } + } + + for name in app.local_resource_accesses() { + if app.local_resources.get(name).is_none() { + return Err(parse::Error::new( + name.span(), + "this local resource has NOT been declared", + )); + } + } + + // Check that no resource has both types of access (`Exclusive` & `Shared`) + let exclusive_accesses = app + .shared_resource_accesses() + .filter_map(|(priority, name, access)| { + if priority.is_some() && access.is_exclusive() { + Some(name) + } else { + None + } + }) + .collect::>(); + for (_, name, access) in app.shared_resource_accesses() { + if access.is_shared() && exclusive_accesses.contains(name) { + return Err(parse::Error::new( + name.span(), + "this implementation doesn't support shared (`&-`) - exclusive (`&mut-`) locks; use `x` instead of `&x`", + )); + } + } + + // check that dispatchers are not used as hardware tasks + for task in app.hardware_tasks.values() { + let binds = &task.args.binds; + + if app.args.dispatchers.contains_key(binds) { + return Err(parse::Error::new( + binds.span(), + "dispatcher interrupts can't be used as hardware tasks", + )); + } + } + + Ok(()) +} diff --git a/macros/src/syntax/optimize.rs b/macros/src/syntax/optimize.rs new file mode 100644 index 0000000000..87a6258d21 --- /dev/null +++ b/macros/src/syntax/optimize.rs @@ -0,0 +1,36 @@ +use std::collections::{BTreeSet, HashMap}; + +use crate::syntax::{ast::App, Settings}; + +pub fn app(app: &mut App, settings: &Settings) { + // "compress" priorities + // If the user specified, for example, task priorities of "1, 3, 6", + // compress them into "1, 2, 3" as to leave no gaps + if settings.optimize_priorities { + // all task priorities ordered in ascending order + let priorities = app + .hardware_tasks + .values() + .map(|task| Some(task.args.priority)) + .chain( + app.software_tasks + .values() + .map(|task| Some(task.args.priority)), + ) + .collect::>(); + + let map = priorities + .iter() + .cloned() + .zip(1..) + .collect::>(); + + for task in app.hardware_tasks.values_mut() { + task.args.priority = map[&Some(task.args.priority)]; + } + + for task in app.software_tasks.values_mut() { + task.args.priority = map[&Some(task.args.priority)]; + } + } +} diff --git a/macros/src/syntax/parse.rs b/macros/src/syntax/parse.rs new file mode 100644 index 0000000000..74f94f2be7 --- /dev/null +++ b/macros/src/syntax/parse.rs @@ -0,0 +1,520 @@ +mod app; +mod hardware_task; +mod idle; +mod init; +mod monotonic; +mod resource; +mod software_task; +mod util; + +use proc_macro2::TokenStream as TokenStream2; +use syn::{ + braced, parenthesized, + parse::{self, Parse, ParseStream, Parser}, + token::{self, Brace}, + Ident, Item, LitBool, LitInt, Path, Token, +}; + +use crate::syntax::{ + ast::{ + App, AppArgs, HardwareTaskArgs, IdleArgs, InitArgs, MonotonicArgs, SoftwareTaskArgs, + TaskLocal, + }, + Either, Settings, +}; + +// Parse the app, both app arguments and body (input) +pub fn app(args: TokenStream2, input: TokenStream2, settings: &Settings) -> parse::Result { + let args = AppArgs::parse(args)?; + let input: Input = syn::parse2(input)?; + + App::parse(args, input, settings) +} + +pub(crate) struct Input { + _mod_token: Token![mod], + pub ident: Ident, + _brace_token: Brace, + pub items: Vec, +} + +impl Parse for Input { + fn parse(input: ParseStream<'_>) -> parse::Result { + fn parse_items(input: ParseStream<'_>) -> parse::Result> { + let mut items = vec![]; + + while !input.is_empty() { + items.push(input.parse()?); + } + + Ok(items) + } + + let content; + + let _mod_token = input.parse()?; + let ident = input.parse()?; + let _brace_token = braced!(content in input); + let items = content.call(parse_items)?; + + Ok(Input { + _mod_token, + ident, + _brace_token, + items, + }) + } +} + +fn init_args(tokens: TokenStream2) -> parse::Result { + (|input: ParseStream<'_>| -> parse::Result { + if input.is_empty() { + return Ok(InitArgs::default()); + } + + let mut local_resources = None; + + let content; + parenthesized!(content in input); + + if !content.is_empty() { + loop { + // Parse identifier name + let ident: Ident = content.parse()?; + // Handle equal sign + let _: Token![=] = content.parse()?; + + match &*ident.to_string() { + "local" => { + if local_resources.is_some() { + return Err(parse::Error::new( + ident.span(), + "argument appears more than once", + )); + } + + local_resources = Some(util::parse_local_resources(&content)?); + } + _ => { + return Err(parse::Error::new(ident.span(), "unexpected argument")); + } + } + + if content.is_empty() { + break; + } + // Handle comma: , + let _: Token![,] = content.parse()?; + } + } + + if let Some(locals) = &local_resources { + for (ident, task_local) in locals { + if let TaskLocal::External = task_local { + return Err(parse::Error::new( + ident.span(), + "only declared local resources are allowed in init", + )); + } + } + } + + Ok(InitArgs { + local_resources: local_resources.unwrap_or_default(), + }) + }) + .parse2(tokens) +} + +fn idle_args(tokens: TokenStream2) -> parse::Result { + (|input: ParseStream<'_>| -> parse::Result { + if input.is_empty() { + return Ok(IdleArgs::default()); + } + + let mut shared_resources = None; + let mut local_resources = None; + + let content; + parenthesized!(content in input); + if !content.is_empty() { + loop { + // Parse identifier name + let ident: Ident = content.parse()?; + // Handle equal sign + let _: Token![=] = content.parse()?; + + match &*ident.to_string() { + "shared" => { + if shared_resources.is_some() { + return Err(parse::Error::new( + ident.span(), + "argument appears more than once", + )); + } + + shared_resources = Some(util::parse_shared_resources(&content)?); + } + + "local" => { + if local_resources.is_some() { + return Err(parse::Error::new( + ident.span(), + "argument appears more than once", + )); + } + + local_resources = Some(util::parse_local_resources(&content)?); + } + + _ => { + return Err(parse::Error::new(ident.span(), "unexpected argument")); + } + } + if content.is_empty() { + break; + } + + // Handle comma: , + let _: Token![,] = content.parse()?; + } + } + + Ok(IdleArgs { + shared_resources: shared_resources.unwrap_or_default(), + local_resources: local_resources.unwrap_or_default(), + }) + }) + .parse2(tokens) +} + +fn task_args( + tokens: TokenStream2, + settings: &Settings, +) -> parse::Result> { + (|input: ParseStream<'_>| -> parse::Result> { + if input.is_empty() { + return Ok(Either::Right(SoftwareTaskArgs::default())); + } + + let mut binds = None; + let mut capacity = None; + let mut priority = None; + let mut shared_resources = None; + let mut local_resources = None; + let mut prio_span = None; + let mut only_same_priority_spawn = false; + let mut only_same_prio_span = None; + + let content; + parenthesized!(content in input); + loop { + if content.is_empty() { + break; + } + + // Parse identifier name + let ident: Ident = content.parse()?; + let ident_s = ident.to_string(); + + if ident_s == "only_same_priority_spawn_please_fix_me" { + if only_same_priority_spawn { + return Err(parse::Error::new( + ident.span(), + "argument appears more than once", + )); + } + + only_same_priority_spawn = true; + only_same_prio_span = Some(ident.span()); + + if content.is_empty() { + break; + } + + // Handle comma: , + let _: Token![,] = content.parse()?; + + continue; + } + + // Handle equal sign + let _: Token![=] = content.parse()?; + + match &*ident_s { + "binds" if !settings.parse_binds => { + return Err(parse::Error::new( + ident.span(), + "Unexpected bind in task argument. Binds are only parsed if Settings::parse_binds is set.", + )); + } + + "binds" if settings.parse_binds => { + if binds.is_some() { + return Err(parse::Error::new( + ident.span(), + "argument appears more than once", + )); + } + + if capacity.is_some() { + return Err(parse::Error::new( + ident.span(), + "hardware tasks can't use the `capacity` argument", + )); + } + + // Parse identifier name + let ident = content.parse()?; + + binds = Some(ident); + } + + "capacity" => { + if capacity.is_some() { + return Err(parse::Error::new( + ident.span(), + "argument appears more than once", + )); + } + + if binds.is_some() { + return Err(parse::Error::new( + ident.span(), + "hardware tasks can't use the `capacity` argument", + )); + } + + // #lit + let lit: LitInt = content.parse()?; + + if !lit.suffix().is_empty() { + return Err(parse::Error::new( + lit.span(), + "this literal must be unsuffixed", + )); + } + + let value = lit.base10_parse::().ok(); + if value.is_none() || value == Some(0) { + return Err(parse::Error::new( + lit.span(), + "this literal must be in the range 1...255", + )); + } + + capacity = Some(value.unwrap()); + } + + "priority" => { + if priority.is_some() { + return Err(parse::Error::new( + ident.span(), + "argument appears more than once", + )); + } + + // #lit + let lit: LitInt = content.parse()?; + + if !lit.suffix().is_empty() { + return Err(parse::Error::new( + lit.span(), + "this literal must be unsuffixed", + )); + } + + let value = lit.base10_parse::().ok(); + if value.is_none() { + return Err(parse::Error::new( + lit.span(), + "this literal must be in the range 0...255", + )); + } + + prio_span = Some(lit.span()); + priority = Some(value.unwrap()); + } + + "shared" => { + if shared_resources.is_some() { + return Err(parse::Error::new( + ident.span(), + "argument appears more than once", + )); + } + + shared_resources = Some(util::parse_shared_resources(&content)?); + } + + "local" => { + if local_resources.is_some() { + return Err(parse::Error::new( + ident.span(), + "argument appears more than once", + )); + } + + local_resources = Some(util::parse_local_resources(&content)?); + } + + + _ => { + return Err(parse::Error::new(ident.span(), "unexpected argument")); + } + } + + if content.is_empty() { + break; + } + + // Handle comma: , + let _: Token![,] = content.parse()?; + } + let priority = priority.unwrap_or(1); + let shared_resources = shared_resources.unwrap_or_default(); + let local_resources = local_resources.unwrap_or_default(); + + Ok(if let Some(binds) = binds { + if priority == 0 { + return Err(parse::Error::new( + prio_span.unwrap(), + "hardware tasks are not allowed to be at priority 0", + )); + } + + if only_same_priority_spawn { + return Err(parse::Error::new( + only_same_prio_span.unwrap(), + "hardware tasks are not allowed to be spawned, `only_same_priority_spawn_please_fix_me` is only for software tasks", + )); + } + + Either::Left(HardwareTaskArgs { + binds, + priority, + shared_resources, + local_resources, + }) + } else { + Either::Right(SoftwareTaskArgs { + capacity: capacity.unwrap_or(1), + priority, + shared_resources, + local_resources, + only_same_priority_spawn, + }) + }) + }) + .parse2(tokens) +} + +fn monotonic_args(path: Path, tokens: TokenStream2) -> parse::Result { + (|input: ParseStream<'_>| -> parse::Result { + let mut binds = None; + let mut priority = None; + let mut default = None; + + if !input.peek(token::Paren) { + return Err(parse::Error::new( + path.segments.first().unwrap().ident.span(), + "expected opening ( in #[monotonic( ... )]", + )); + } + + let content; + parenthesized!(content in input); + + if !content.is_empty() { + loop { + // Parse identifier name + let ident: Ident = content.parse()?; + // Handle equal sign + let _: Token![=] = content.parse()?; + + match &*ident.to_string() { + "binds" => { + if binds.is_some() { + return Err(parse::Error::new( + ident.span(), + "argument appears more than once", + )); + } + // Parse identifier name + let ident = content.parse()?; + + binds = Some(ident); + } + + "priority" => { + if priority.is_some() { + return Err(parse::Error::new( + ident.span(), + "argument appears more than once", + )); + } + + // #lit + let lit: LitInt = content.parse()?; + + if !lit.suffix().is_empty() { + return Err(parse::Error::new( + lit.span(), + "this literal must be unsuffixed", + )); + } + + let value = lit.base10_parse::().ok(); + if value.is_none() || value == Some(0) { + return Err(parse::Error::new( + lit.span(), + "this literal must be in the range 1...255", + )); + } + + priority = Some(value.unwrap()); + } + + "default" => { + if default.is_some() { + return Err(parse::Error::new( + ident.span(), + "argument appears more than once", + )); + } + + let lit: LitBool = content.parse()?; + default = Some(lit.value); + } + + _ => { + return Err(parse::Error::new(ident.span(), "unexpected argument")); + } + } + if content.is_empty() { + break; + } + + // Handle comma: , + let _: Token![,] = content.parse()?; + } + } + + let binds = if let Some(r) = binds { + r + } else { + return Err(parse::Error::new( + content.span(), + "`binds = ...` is missing", + )); + }; + let default = default.unwrap_or(false); + + Ok(MonotonicArgs { + binds, + priority, + default, + }) + }) + .parse2(tokens) +} diff --git a/macros/src/syntax/parse/app.rs b/macros/src/syntax/parse/app.rs new file mode 100644 index 0000000000..7eb415d337 --- /dev/null +++ b/macros/src/syntax/parse/app.rs @@ -0,0 +1,539 @@ +use std::collections::HashSet; + +// use indexmap::map::Entry; +use proc_macro2::TokenStream as TokenStream2; +use syn::{ + parse::{self, ParseStream, Parser}, + spanned::Spanned, + Expr, ExprArray, Fields, ForeignItem, Ident, Item, LitBool, Path, Token, Type, Visibility, +}; + +use super::Input; +use crate::syntax::{ + ast::{ + App, AppArgs, Dispatcher, Dispatchers, HardwareTask, Idle, IdleArgs, Init, InitArgs, + LocalResource, Monotonic, MonotonicArgs, SharedResource, SoftwareTask, + }, + parse::{self as syntax_parse, util}, + Either, Map, Set, Settings, +}; + +impl AppArgs { + pub(crate) fn parse(tokens: TokenStream2) -> parse::Result { + (|input: ParseStream<'_>| -> parse::Result { + let mut custom = Set::new(); + let mut device = None; + let mut peripherals = true; + let mut dispatchers = Dispatchers::new(); + + loop { + if input.is_empty() { + break; + } + + // #ident = .. + let ident: Ident = input.parse()?; + let _eq_token: Token![=] = input.parse()?; + + if custom.contains(&ident) { + return Err(parse::Error::new( + ident.span(), + "argument appears more than once", + )); + } + + custom.insert(ident.clone()); + + let ks = ident.to_string(); + + match &*ks { + "device" => { + if let Ok(p) = input.parse::() { + device = Some(p); + } else { + return Err(parse::Error::new( + ident.span(), + "unexpected argument value; this should be a path", + )); + } + } + + "peripherals" => { + if let Ok(p) = input.parse::() { + peripherals = p.value; + } else { + return Err(parse::Error::new( + ident.span(), + "unexpected argument value; this should be a boolean", + )); + } + } + + "dispatchers" => { + if let Ok(p) = input.parse::() { + for e in p.elems { + match e { + Expr::Path(ep) => { + let path = ep.path; + let ident = if path.leading_colon.is_some() + || path.segments.len() != 1 + { + return Err(parse::Error::new( + path.span(), + "interrupt must be an identifier, not a path", + )); + } else { + path.segments[0].ident.clone() + }; + let span = ident.span(); + if dispatchers.contains_key(&ident) { + return Err(parse::Error::new( + span, + "this extern interrupt is listed more than once", + )); + } else { + dispatchers + .insert(ident, Dispatcher { attrs: ep.attrs }); + } + } + _ => { + return Err(parse::Error::new( + e.span(), + "interrupt must be an identifier", + )); + } + } + } + } else { + return Err(parse::Error::new( + ident.span(), + // increasing the length of the error message will break rustfmt + "unexpected argument value; expected an array", + )); + } + } + _ => { + return Err(parse::Error::new(ident.span(), "unexpected argument")); + } + } + + if input.is_empty() { + break; + } + + // , + let _: Token![,] = input.parse()?; + } + + let device = if let Some(device) = device { + device + } else { + return Err(parse::Error::new(input.span(), "missing `device = ...`")); + }; + + Ok(AppArgs { + device, + peripherals, + dispatchers, + }) + }) + .parse2(tokens) + } +} + +impl App { + pub(crate) fn parse(args: AppArgs, input: Input, settings: &Settings) -> parse::Result { + let mut init = None; + let mut idle = None; + + let mut shared_resources_ident = None; + let mut shared_resources = Map::new(); + let mut local_resources_ident = None; + let mut local_resources = Map::new(); + let mut monotonics = Map::new(); + let mut hardware_tasks = Map::new(); + let mut software_tasks = Map::new(); + let mut user_imports = vec![]; + let mut user_code = vec![]; + + let mut seen_idents = HashSet::::new(); + let mut bindings = HashSet::::new(); + let mut monotonic_types = HashSet::::new(); + + let mut check_binding = |ident: &Ident| { + if bindings.contains(ident) { + return Err(parse::Error::new( + ident.span(), + "this interrupt is already bound", + )); + } else { + bindings.insert(ident.clone()); + } + + Ok(()) + }; + + let mut check_ident = |ident: &Ident| { + if seen_idents.contains(ident) { + return Err(parse::Error::new( + ident.span(), + "this identifier has already been used", + )); + } else { + seen_idents.insert(ident.clone()); + } + + Ok(()) + }; + + let mut check_monotonic = |ty: &Type| { + if monotonic_types.contains(ty) { + return Err(parse::Error::new( + ty.span(), + "this type is already used by another monotonic", + )); + } else { + monotonic_types.insert(ty.clone()); + } + + Ok(()) + }; + + for mut item in input.items { + match item { + Item::Fn(mut item) => { + let span = item.sig.ident.span(); + if let Some(pos) = item + .attrs + .iter() + .position(|attr| util::attr_eq(attr, "init")) + { + let args = InitArgs::parse(item.attrs.remove(pos).tokens)?; + + // If an init function already exists, error + if init.is_some() { + return Err(parse::Error::new( + span, + "`#[init]` function must appear at most once", + )); + } + + check_ident(&item.sig.ident)?; + + init = Some(Init::parse(args, item)?); + } else if let Some(pos) = item + .attrs + .iter() + .position(|attr| util::attr_eq(attr, "idle")) + { + let args = IdleArgs::parse(item.attrs.remove(pos).tokens)?; + + // If an idle function already exists, error + if idle.is_some() { + return Err(parse::Error::new( + span, + "`#[idle]` function must appear at most once", + )); + } + + check_ident(&item.sig.ident)?; + + idle = Some(Idle::parse(args, item)?); + } else if let Some(pos) = item + .attrs + .iter() + .position(|attr| util::attr_eq(attr, "task")) + { + if hardware_tasks.contains_key(&item.sig.ident) + || software_tasks.contains_key(&item.sig.ident) + { + return Err(parse::Error::new( + span, + "this task is defined multiple times", + )); + } + + match syntax_parse::task_args(item.attrs.remove(pos).tokens, settings)? { + Either::Left(args) => { + check_binding(&args.binds)?; + check_ident(&item.sig.ident)?; + + hardware_tasks.insert( + item.sig.ident.clone(), + HardwareTask::parse(args, item)?, + ); + } + + Either::Right(args) => { + check_ident(&item.sig.ident)?; + + software_tasks.insert( + item.sig.ident.clone(), + SoftwareTask::parse(args, item)?, + ); + } + } + } else { + // Forward normal functions + user_code.push(Item::Fn(item.clone())); + } + } + + Item::Struct(ref mut struct_item) => { + // Match structures with the attribute #[shared], name of structure is not + // important + if let Some(_pos) = struct_item + .attrs + .iter() + .position(|attr| util::attr_eq(attr, "shared")) + { + let span = struct_item.ident.span(); + + shared_resources_ident = Some(struct_item.ident.clone()); + + if !shared_resources.is_empty() { + return Err(parse::Error::new( + span, + "`#[shared]` struct must appear at most once", + )); + } + + if struct_item.vis != Visibility::Inherited { + return Err(parse::Error::new( + struct_item.span(), + "this item must have inherited / private visibility", + )); + } + + if let Fields::Named(fields) = &mut struct_item.fields { + for field in &mut fields.named { + let ident = field.ident.as_ref().expect("UNREACHABLE"); + + if shared_resources.contains_key(ident) { + return Err(parse::Error::new( + ident.span(), + "this resource is listed more than once", + )); + } + + shared_resources.insert( + ident.clone(), + SharedResource::parse(field, ident.span())?, + ); + } + } else { + return Err(parse::Error::new( + struct_item.span(), + "this `struct` must have named fields", + )); + } + } else if let Some(_pos) = struct_item + .attrs + .iter() + .position(|attr| util::attr_eq(attr, "local")) + { + let span = struct_item.ident.span(); + + local_resources_ident = Some(struct_item.ident.clone()); + + if !local_resources.is_empty() { + return Err(parse::Error::new( + span, + "`#[local]` struct must appear at most once", + )); + } + + if struct_item.vis != Visibility::Inherited { + return Err(parse::Error::new( + struct_item.span(), + "this item must have inherited / private visibility", + )); + } + + if let Fields::Named(fields) = &mut struct_item.fields { + for field in &mut fields.named { + let ident = field.ident.as_ref().expect("UNREACHABLE"); + + if local_resources.contains_key(ident) { + return Err(parse::Error::new( + ident.span(), + "this resource is listed more than once", + )); + } + + local_resources.insert( + ident.clone(), + LocalResource::parse(field, ident.span())?, + ); + } + } else { + return Err(parse::Error::new( + struct_item.span(), + "this `struct` must have named fields", + )); + } + } else { + // Structure without the #[resources] attribute should just be passed along + user_code.push(item.clone()); + } + } + + Item::ForeignMod(mod_) => { + if !util::abi_is_rust(&mod_.abi) { + return Err(parse::Error::new( + mod_.abi.extern_token.span(), + "this `extern` block must use the \"Rust\" ABI", + )); + } + + for item in mod_.items { + if let ForeignItem::Fn(mut item) = item { + let span = item.sig.ident.span(); + if let Some(pos) = item + .attrs + .iter() + .position(|attr| util::attr_eq(attr, "task")) + { + if hardware_tasks.contains_key(&item.sig.ident) + || software_tasks.contains_key(&item.sig.ident) + { + return Err(parse::Error::new( + span, + "this task is defined multiple times", + )); + } + + if item.attrs.len() != 1 { + return Err(parse::Error::new( + span, + "`extern` task required `#[task(..)]` attribute", + )); + } + + match syntax_parse::task_args( + item.attrs.remove(pos).tokens, + settings, + )? { + Either::Left(args) => { + check_binding(&args.binds)?; + check_ident(&item.sig.ident)?; + + hardware_tasks.insert( + item.sig.ident.clone(), + HardwareTask::parse_foreign(args, item)?, + ); + } + + Either::Right(args) => { + check_ident(&item.sig.ident)?; + + software_tasks.insert( + item.sig.ident.clone(), + SoftwareTask::parse_foreign(args, item)?, + ); + } + } + } else { + return Err(parse::Error::new( + span, + "`extern` task required `#[task(..)]` attribute", + )); + } + } else { + return Err(parse::Error::new( + item.span(), + "this item must live outside the `#[app]` module", + )); + } + } + } + Item::Use(itemuse_) => { + // Store the user provided use-statements + user_imports.push(itemuse_.clone()); + } + Item::Type(ref mut type_item) => { + // Match types with the attribute #[monotonic] + if let Some(pos) = type_item + .attrs + .iter() + .position(|attr| util::attr_eq(attr, "monotonic")) + { + let span = type_item.ident.span(); + + if monotonics.contains_key(&type_item.ident) { + return Err(parse::Error::new( + span, + "`#[monotonic(...)]` on a specific type must appear at most once", + )); + } + + if type_item.vis != Visibility::Inherited { + return Err(parse::Error::new( + type_item.span(), + "this item must have inherited / private visibility", + )); + } + + check_monotonic(&*type_item.ty)?; + + let m = type_item.attrs.remove(pos); + let args = MonotonicArgs::parse(m)?; + + check_binding(&args.binds)?; + + let monotonic = Monotonic::parse(args, type_item, span)?; + + monotonics.insert(type_item.ident.clone(), monotonic); + } + + // All types are passed on + user_code.push(item.clone()); + } + _ => { + // Anything else within the module should not make any difference + user_code.push(item.clone()); + } + } + } + + let shared_resources_ident = + shared_resources_ident.expect("No `#[shared]` resource struct defined"); + let local_resources_ident = + local_resources_ident.expect("No `#[local]` resource struct defined"); + let init = init.expect("No `#[init]` function defined"); + + if shared_resources_ident != init.user_shared_struct { + return Err(parse::Error::new( + init.user_shared_struct.span(), + format!( + "This name and the one defined on `#[shared]` are not the same. Should this be `{}`?", + shared_resources_ident + ), + )); + } + + if local_resources_ident != init.user_local_struct { + return Err(parse::Error::new( + init.user_local_struct.span(), + format!( + "This name and the one defined on `#[local]` are not the same. Should this be `{}`?", + local_resources_ident + ), + )); + } + + Ok(App { + args, + name: input.ident, + init, + idle, + monotonics, + shared_resources, + local_resources, + user_imports, + user_code, + hardware_tasks, + software_tasks, + }) + } +} diff --git a/macros/src/syntax/parse/hardware_task.rs b/macros/src/syntax/parse/hardware_task.rs new file mode 100644 index 0000000000..304bfcd3f0 --- /dev/null +++ b/macros/src/syntax/parse/hardware_task.rs @@ -0,0 +1,96 @@ +use syn::{parse, ForeignItemFn, ItemFn, Stmt}; + +use crate::syntax::parse::util::FilterAttrs; +use crate::syntax::{ + ast::{HardwareTask, HardwareTaskArgs}, + parse::util, +}; + +impl HardwareTask { + pub(crate) fn parse(args: HardwareTaskArgs, item: ItemFn) -> parse::Result { + let span = item.sig.ident.span(); + let valid_signature = util::check_fn_signature(&item, false) + && item.sig.inputs.len() == 1 + && util::type_is_unit(&item.sig.output); + + let name = item.sig.ident.to_string(); + + if name == "init" || name == "idle" { + return Err(parse::Error::new( + span, + "tasks cannot be named `init` or `idle`", + )); + } + + if valid_signature { + if let Some((context, Ok(rest))) = util::parse_inputs(item.sig.inputs, &name) { + if rest.is_empty() { + let FilterAttrs { cfgs, attrs, .. } = util::filter_attributes(item.attrs); + + return Ok(HardwareTask { + args, + cfgs, + attrs, + context, + stmts: item.block.stmts, + is_extern: false, + }); + } + } + } + + Err(parse::Error::new( + span, + &format!( + "this task handler must have type signature `fn({}::Context)`", + name + ), + )) + } +} + +impl HardwareTask { + pub(crate) fn parse_foreign( + args: HardwareTaskArgs, + item: ForeignItemFn, + ) -> parse::Result { + let span = item.sig.ident.span(); + let valid_signature = util::check_foreign_fn_signature(&item, false) + && item.sig.inputs.len() == 1 + && util::type_is_unit(&item.sig.output); + + let name = item.sig.ident.to_string(); + + if name == "init" || name == "idle" { + return Err(parse::Error::new( + span, + "tasks cannot be named `init` or `idle`", + )); + } + + if valid_signature { + if let Some((context, Ok(rest))) = util::parse_inputs(item.sig.inputs, &name) { + if rest.is_empty() { + let FilterAttrs { cfgs, attrs, .. } = util::filter_attributes(item.attrs); + + return Ok(HardwareTask { + args, + cfgs, + attrs, + context, + stmts: Vec::::new(), + is_extern: true, + }); + } + } + } + + Err(parse::Error::new( + span, + &format!( + "this task handler must have type signature `fn({}::Context)`", + name + ), + )) + } +} diff --git a/macros/src/syntax/parse/idle.rs b/macros/src/syntax/parse/idle.rs new file mode 100644 index 0000000000..d9f3a99e6f --- /dev/null +++ b/macros/src/syntax/parse/idle.rs @@ -0,0 +1,45 @@ +use proc_macro2::TokenStream as TokenStream2; +use syn::{parse, ItemFn}; + +use crate::syntax::{ + ast::{Idle, IdleArgs}, + parse::util, +}; + +impl IdleArgs { + pub(crate) fn parse(tokens: TokenStream2) -> parse::Result { + crate::syntax::parse::idle_args(tokens) + } +} + +impl Idle { + pub(crate) fn parse(args: IdleArgs, item: ItemFn) -> parse::Result { + let valid_signature = util::check_fn_signature(&item, false) + && item.sig.inputs.len() == 1 + && util::type_is_bottom(&item.sig.output); + + let name = item.sig.ident.to_string(); + + if valid_signature { + if let Some((context, Ok(rest))) = util::parse_inputs(item.sig.inputs, &name) { + if rest.is_empty() { + return Ok(Idle { + args, + attrs: item.attrs, + context, + name: item.sig.ident, + stmts: item.block.stmts, + }); + } + } + } + + Err(parse::Error::new( + item.sig.ident.span(), + &format!( + "this `#[idle]` function must have signature `fn({}::Context) -> !`", + name + ), + )) + } +} diff --git a/macros/src/syntax/parse/init.rs b/macros/src/syntax/parse/init.rs new file mode 100644 index 0000000000..727ee20508 --- /dev/null +++ b/macros/src/syntax/parse/init.rs @@ -0,0 +1,52 @@ +use proc_macro2::TokenStream as TokenStream2; + +use syn::{parse, ItemFn}; + +use crate::syntax::{ + ast::{Init, InitArgs}, + parse::{self as syntax_parse, util}, +}; + +impl InitArgs { + pub(crate) fn parse(tokens: TokenStream2) -> parse::Result { + syntax_parse::init_args(tokens) + } +} + +impl Init { + pub(crate) fn parse(args: InitArgs, item: ItemFn) -> parse::Result { + let valid_signature = util::check_fn_signature(&item, false) && item.sig.inputs.len() == 1; + + let span = item.sig.ident.span(); + + let name = item.sig.ident.to_string(); + + if valid_signature { + if let Ok((user_shared_struct, user_local_struct)) = + util::type_is_init_return(&item.sig.output, &name) + { + if let Some((context, Ok(rest))) = util::parse_inputs(item.sig.inputs, &name) { + if rest.is_empty() { + return Ok(Init { + args, + attrs: item.attrs, + context, + name: item.sig.ident, + stmts: item.block.stmts, + user_shared_struct, + user_local_struct, + }); + } + } + } + } + + Err(parse::Error::new( + span, + &format!( + "the `#[init]` function must have signature `fn({}::Context) -> (Shared resources struct, Local resources struct, {0}::Monotonics)`", + name + ), + )) + } +} diff --git a/macros/src/syntax/parse/monotonic.rs b/macros/src/syntax/parse/monotonic.rs new file mode 100644 index 0000000000..05832339b7 --- /dev/null +++ b/macros/src/syntax/parse/monotonic.rs @@ -0,0 +1,42 @@ +use proc_macro2::Span; +use syn::Attribute; +use syn::{parse, spanned::Spanned, ItemType, Visibility}; + +use crate::syntax::parse::util::FilterAttrs; +use crate::syntax::{ + ast::{Monotonic, MonotonicArgs}, + parse::util, +}; + +impl MonotonicArgs { + pub(crate) fn parse(attr: Attribute) -> parse::Result { + crate::syntax::parse::monotonic_args(attr.path, attr.tokens) + } +} + +impl Monotonic { + pub(crate) fn parse(args: MonotonicArgs, item: &ItemType, span: Span) -> parse::Result { + if item.vis != Visibility::Inherited { + return Err(parse::Error::new( + span, + "this field must have inherited / private visibility", + )); + } + + let FilterAttrs { cfgs, attrs, .. } = util::filter_attributes(item.attrs.clone()); + + if !attrs.is_empty() { + return Err(parse::Error::new( + attrs[0].path.span(), + "Monotonic does not support attributes other than `#[cfg]`", + )); + } + + Ok(Monotonic { + cfgs, + ident: item.ident.clone(), + ty: item.ty.clone(), + args, + }) + } +} diff --git a/macros/src/syntax/parse/resource.rs b/macros/src/syntax/parse/resource.rs new file mode 100644 index 0000000000..ff1005763e --- /dev/null +++ b/macros/src/syntax/parse/resource.rs @@ -0,0 +1,55 @@ +use proc_macro2::Span; +use syn::{parse, Field, Visibility}; + +use crate::syntax::parse::util::FilterAttrs; +use crate::syntax::{ + ast::{LocalResource, SharedResource, SharedResourceProperties}, + parse::util, +}; + +impl SharedResource { + pub(crate) fn parse(item: &Field, span: Span) -> parse::Result { + if item.vis != Visibility::Inherited { + return Err(parse::Error::new( + span, + "this field must have inherited / private visibility", + )); + } + + let FilterAttrs { + cfgs, + mut attrs, + docs, + } = util::filter_attributes(item.attrs.clone()); + + let lock_free = util::extract_lock_free(&mut attrs)?; + + Ok(SharedResource { + cfgs, + attrs, + docs, + ty: Box::new(item.ty.clone()), + properties: SharedResourceProperties { lock_free }, + }) + } +} + +impl LocalResource { + pub(crate) fn parse(item: &Field, span: Span) -> parse::Result { + if item.vis != Visibility::Inherited { + return Err(parse::Error::new( + span, + "this field must have inherited / private visibility", + )); + } + + let FilterAttrs { cfgs, attrs, docs } = util::filter_attributes(item.attrs.clone()); + + Ok(LocalResource { + cfgs, + attrs, + docs, + ty: Box::new(item.ty.clone()), + }) + } +} diff --git a/macros/src/syntax/parse/software_task.rs b/macros/src/syntax/parse/software_task.rs new file mode 100644 index 0000000000..2b1ac4a5b4 --- /dev/null +++ b/macros/src/syntax/parse/software_task.rs @@ -0,0 +1,86 @@ +use syn::{parse, ForeignItemFn, ItemFn, Stmt}; + +use crate::syntax::parse::util::FilterAttrs; +use crate::syntax::{ + ast::{SoftwareTask, SoftwareTaskArgs}, + parse::util, +}; + +impl SoftwareTask { + pub(crate) fn parse(args: SoftwareTaskArgs, item: ItemFn) -> parse::Result { + let valid_signature = + util::check_fn_signature(&item, true) && util::type_is_unit(&item.sig.output); + + let span = item.sig.ident.span(); + + let name = item.sig.ident.to_string(); + + let is_async = item.sig.asyncness.is_some(); + + if valid_signature { + if let Some((context, Ok(inputs))) = util::parse_inputs(item.sig.inputs, &name) { + let FilterAttrs { cfgs, attrs, .. } = util::filter_attributes(item.attrs); + + return Ok(SoftwareTask { + args, + attrs, + cfgs, + context, + inputs, + stmts: item.block.stmts, + is_extern: false, + is_async, + }); + } + } + + Err(parse::Error::new( + span, + &format!( + "this task handler must have type signature `(async) fn({}::Context, ..)`", + name + ), + )) + } +} + +impl SoftwareTask { + pub(crate) fn parse_foreign( + args: SoftwareTaskArgs, + item: ForeignItemFn, + ) -> parse::Result { + let valid_signature = + util::check_foreign_fn_signature(&item, true) && util::type_is_unit(&item.sig.output); + + let span = item.sig.ident.span(); + + let name = item.sig.ident.to_string(); + + let is_async = item.sig.asyncness.is_some(); + + if valid_signature { + if let Some((context, Ok(inputs))) = util::parse_inputs(item.sig.inputs, &name) { + let FilterAttrs { cfgs, attrs, .. } = util::filter_attributes(item.attrs); + + return Ok(SoftwareTask { + args, + attrs, + cfgs, + context, + inputs, + stmts: Vec::::new(), + is_extern: true, + is_async, + }); + } + } + + Err(parse::Error::new( + span, + &format!( + "this task handler must have type signature `(async) fn({}::Context, ..)`", + name + ), + )) + } +} diff --git a/macros/src/syntax/parse/util.rs b/macros/src/syntax/parse/util.rs new file mode 100644 index 0000000000..3fa51ef803 --- /dev/null +++ b/macros/src/syntax/parse/util.rs @@ -0,0 +1,338 @@ +use syn::{ + bracketed, + parse::{self, ParseStream}, + punctuated::Punctuated, + spanned::Spanned, + Abi, AttrStyle, Attribute, Expr, FnArg, ForeignItemFn, Ident, ItemFn, Pat, PatType, Path, + PathArguments, ReturnType, Token, Type, Visibility, +}; + +use crate::syntax::{ + ast::{Access, Local, LocalResources, SharedResources, TaskLocal}, + Map, +}; + +pub fn abi_is_rust(abi: &Abi) -> bool { + match &abi.name { + None => true, + Some(s) => s.value() == "Rust", + } +} + +pub fn attr_eq(attr: &Attribute, name: &str) -> bool { + attr.style == AttrStyle::Outer && attr.path.segments.len() == 1 && { + let segment = attr.path.segments.first().unwrap(); + segment.arguments == PathArguments::None && *segment.ident.to_string() == *name + } +} + +/// checks that a function signature +/// +/// - has no bounds (like where clauses) +/// - is not `async` +/// - is not `const` +/// - is not `unsafe` +/// - is not generic (has no type parameters) +/// - is not variadic +/// - uses the Rust ABI (and not e.g. "C") +pub fn check_fn_signature(item: &ItemFn, allow_async: bool) -> bool { + item.vis == Visibility::Inherited + && item.sig.constness.is_none() + && (item.sig.asyncness.is_none() || allow_async) + && item.sig.abi.is_none() + && item.sig.unsafety.is_none() + && item.sig.generics.params.is_empty() + && item.sig.generics.where_clause.is_none() + && item.sig.variadic.is_none() +} + +#[allow(dead_code)] +pub fn check_foreign_fn_signature(item: &ForeignItemFn, allow_async: bool) -> bool { + item.vis == Visibility::Inherited + && item.sig.constness.is_none() + && (item.sig.asyncness.is_none() || allow_async) + && item.sig.abi.is_none() + && item.sig.unsafety.is_none() + && item.sig.generics.params.is_empty() + && item.sig.generics.where_clause.is_none() + && item.sig.variadic.is_none() +} + +pub struct FilterAttrs { + pub cfgs: Vec, + pub docs: Vec, + pub attrs: Vec, +} + +pub fn filter_attributes(input_attrs: Vec) -> FilterAttrs { + let mut cfgs = vec![]; + let mut docs = vec![]; + let mut attrs = vec![]; + + for attr in input_attrs { + if attr_eq(&attr, "cfg") { + cfgs.push(attr); + } else if attr_eq(&attr, "doc") { + docs.push(attr); + } else { + attrs.push(attr); + } + } + + FilterAttrs { cfgs, docs, attrs } +} + +pub fn extract_lock_free(attrs: &mut Vec) -> parse::Result { + if let Some(pos) = attrs.iter().position(|attr| attr_eq(attr, "lock_free")) { + attrs.remove(pos); + Ok(true) + } else { + Ok(false) + } +} + +pub fn parse_shared_resources(content: ParseStream<'_>) -> parse::Result { + let inner; + bracketed!(inner in content); + + let mut resources = Map::new(); + for e in inner.call(Punctuated::::parse_terminated)? { + let err = Err(parse::Error::new( + e.span(), + "identifier appears more than once in list", + )); + let (access, path) = match e { + Expr::Path(e) => (Access::Exclusive, e.path), + + Expr::Reference(ref r) if r.mutability.is_none() => match &*r.expr { + Expr::Path(e) => (Access::Shared, e.path.clone()), + + _ => return err, + }, + + _ => return err, + }; + + let ident = extract_resource_name_ident(path)?; + + if resources.contains_key(&ident) { + return Err(parse::Error::new( + ident.span(), + "resource appears more than once in list", + )); + } + + resources.insert(ident, access); + } + + Ok(resources) +} + +fn extract_resource_name_ident(path: Path) -> parse::Result { + if path.leading_colon.is_some() + || path.segments.len() != 1 + || path.segments[0].arguments != PathArguments::None + { + Err(parse::Error::new( + path.span(), + "resource must be an identifier, not a path", + )) + } else { + Ok(path.segments[0].ident.clone()) + } +} + +pub fn parse_local_resources(content: ParseStream<'_>) -> parse::Result { + let inner; + bracketed!(inner in content); + + let mut resources = Map::new(); + + for e in inner.call(Punctuated::::parse_terminated)? { + let err = Err(parse::Error::new( + e.span(), + "identifier appears more than once in list", + )); + + let (name, local) = match e { + // local = [IDENT], + Expr::Path(path) => { + if !path.attrs.is_empty() { + return Err(parse::Error::new( + path.span(), + "attributes are not supported here", + )); + } + + let ident = extract_resource_name_ident(path.path)?; + // let (cfgs, attrs) = extract_cfgs(path.attrs); + + (ident, TaskLocal::External) + } + + // local = [IDENT: TYPE = EXPR] + Expr::Assign(e) => { + let (name, ty, cfgs, attrs) = match *e.left { + Expr::Type(t) => { + // Extract name and attributes + let (name, cfgs, attrs) = match *t.expr { + Expr::Path(path) => { + let name = extract_resource_name_ident(path.path)?; + let FilterAttrs { cfgs, attrs, .. } = filter_attributes(path.attrs); + + (name, cfgs, attrs) + } + _ => return err, + }; + + let ty = t.ty; + + // Error check + match &*ty { + Type::Array(_) => {} + Type::Path(_) => {} + Type::Ptr(_) => {} + Type::Tuple(_) => {} + _ => return Err(parse::Error::new( + ty.span(), + "unsupported type, must be an array, tuple, pointer or type path", + )), + }; + + (name, ty, cfgs, attrs) + } + e => return Err(parse::Error::new(e.span(), "malformed, expected a type")), + }; + + let expr = e.right; // Expr + + ( + name, + TaskLocal::Declared(Local { + attrs, + cfgs, + ty, + expr, + }), + ) + } + + expr => { + return Err(parse::Error::new( + expr.span(), + "malformed, expected 'IDENT: TYPE = EXPR'", + )) + } + }; + + resources.insert(name, local); + } + + Ok(resources) +} + +type ParseInputResult = Option<(Box, Result, FnArg>)>; + +pub fn parse_inputs(inputs: Punctuated, name: &str) -> ParseInputResult { + let mut inputs = inputs.into_iter(); + + match inputs.next() { + Some(FnArg::Typed(first)) => { + if type_is_path(&first.ty, &[name, "Context"]) { + let rest = inputs + .map(|arg| match arg { + FnArg::Typed(arg) => Ok(arg), + _ => Err(arg), + }) + .collect::, _>>(); + + Some((first.pat, rest)) + } else { + None + } + } + + _ => None, + } +} + +pub fn type_is_bottom(ty: &ReturnType) -> bool { + if let ReturnType::Type(_, ty) = ty { + matches!(**ty, Type::Never(_)) + } else { + false + } +} + +fn extract_init_resource_name_ident(ty: Type) -> Result { + match ty { + Type::Path(path) => { + let path = path.path; + + if path.leading_colon.is_some() + || path.segments.len() != 1 + || path.segments[0].arguments != PathArguments::None + { + Err(()) + } else { + Ok(path.segments[0].ident.clone()) + } + } + _ => Err(()), + } +} + +/// Checks Init's return type, return the user provided types for analysis +pub fn type_is_init_return(ty: &ReturnType, name: &str) -> Result<(Ident, Ident), ()> { + match ty { + ReturnType::Default => Err(()), + + ReturnType::Type(_, ty) => match &**ty { + Type::Tuple(t) => { + // return should be: + // fn -> (User's #[shared] struct, User's #[local] struct, {name}::Monotonics) + // + // We check the length and the last one here, analysis checks that the user + // provided structs are correct. + if t.elems.len() == 3 && type_is_path(&t.elems[2], &[name, "Monotonics"]) { + return Ok(( + extract_init_resource_name_ident(t.elems[0].clone())?, + extract_init_resource_name_ident(t.elems[1].clone())?, + )); + } + + Err(()) + } + + _ => Err(()), + }, + } +} + +pub fn type_is_path(ty: &Type, segments: &[&str]) -> bool { + match ty { + Type::Path(tpath) if tpath.qself.is_none() => { + tpath.path.segments.len() == segments.len() + && tpath + .path + .segments + .iter() + .zip(segments) + .all(|(lhs, rhs)| lhs.ident == **rhs) + } + + _ => false, + } +} + +pub fn type_is_unit(ty: &ReturnType) -> bool { + if let ReturnType::Type(_, ty) = ty { + if let Type::Tuple(ref tuple) = **ty { + tuple.elems.is_empty() + } else { + false + } + } else { + true + } +} diff --git a/macros/tests/ui.rs b/macros/tests/ui.rs new file mode 100644 index 0000000000..9fb88a1bba --- /dev/null +++ b/macros/tests/ui.rs @@ -0,0 +1,7 @@ +use trybuild::TestCases; + +#[test] +fn ui() { + let t = TestCases::new(); + t.compile_fail("ui/*.rs"); +} diff --git a/macros/ui/async-local-resouces.rs b/macros/ui/async-local-resouces.rs new file mode 100644 index 0000000000..1ba58652da --- /dev/null +++ b/macros/ui/async-local-resouces.rs @@ -0,0 +1,28 @@ +#![no_main] + +#[rtic_macros::mock_app(device = mock)] +mod app { + #[shared] + struct Shared { + #[lock_free] + e: u32, + } + + #[local] + struct Local {} + + #[init] + fn init(_: init::Context) -> (Shared, Local, init::Monotonics) {} + + // e ok + #[task(priority = 1, shared = [e])] + fn uart0(cx: uart0::Context) {} + + // e ok + #[task(priority = 1, shared = [e])] + fn uart1(cx: uart1::Context) {} + + // e not ok + #[task(priority = 1, shared = [e])] + async fn async_task(cx: async_task::Context) {} +} diff --git a/macros/ui/async-local-resouces.stderr b/macros/ui/async-local-resouces.stderr new file mode 100644 index 0000000000..7ce7517d99 --- /dev/null +++ b/macros/ui/async-local-resouces.stderr @@ -0,0 +1,5 @@ +error: Lock free shared resource "e" is used by an async tasks, which is forbidden + --> ui/async-local-resouces.rs:26:36 + | +26 | #[task(priority = 1, shared = [e])] + | ^ diff --git a/macros/ui/async-zero-prio-tasks.rs b/macros/ui/async-zero-prio-tasks.rs new file mode 100644 index 0000000000..91e0990a8b --- /dev/null +++ b/macros/ui/async-zero-prio-tasks.rs @@ -0,0 +1,19 @@ +#![no_main] + +#[rtic_macros::mock_app(device = mock)] +mod app { + #[shared] + struct Shared {} + + #[local] + struct Local {} + + #[init] + fn init(_: init::Context) -> (Shared, Local, init::Monotonics) {} + + #[task(priority = 0)] + fn foo(_: foo::Context) {} + + #[idle] + fn idle(_: idle::Context) -> ! {} +} diff --git a/macros/ui/async-zero-prio-tasks.stderr b/macros/ui/async-zero-prio-tasks.stderr new file mode 100644 index 0000000000..d617feb275 --- /dev/null +++ b/macros/ui/async-zero-prio-tasks.stderr @@ -0,0 +1,11 @@ +error: Software task "foo" has priority 0, but `#[idle]` is defined. 0-priority software tasks are only allowed if there is no `#[idle]`. + --> ui/async-zero-prio-tasks.rs:15:8 + | +15 | fn foo(_: foo::Context) {} + | ^^^ + +error: Software task "foo" has priority 0, but is not `async`. 0-priority software tasks must be `async`. + --> ui/async-zero-prio-tasks.rs:15:8 + | +15 | fn foo(_: foo::Context) {} + | ^^^ diff --git a/macros/ui/extern-interrupt-used.rs b/macros/ui/extern-interrupt-used.rs new file mode 100644 index 0000000000..71dc50f397 --- /dev/null +++ b/macros/ui/extern-interrupt-used.rs @@ -0,0 +1,16 @@ +#![no_main] + +#[rtic_macros::mock_app(parse_extern_interrupt, parse_binds, device = mock, dispatchers = [EXTI0])] +mod app { + #[shared] + struct Shared {} + + #[local] + struct Local {} + + #[init] + fn init(_: init::Context) -> (Shared, Local, init::Monotonics) {} + + #[task(binds = EXTI0)] + fn foo(_: foo::Context) {} +} diff --git a/macros/ui/extern-interrupt-used.stderr b/macros/ui/extern-interrupt-used.stderr new file mode 100644 index 0000000000..f9510d7037 --- /dev/null +++ b/macros/ui/extern-interrupt-used.stderr @@ -0,0 +1,5 @@ +error: dispatcher interrupts can't be used as hardware tasks + --> $DIR/extern-interrupt-used.rs:14:20 + | +14 | #[task(binds = EXTI0)] + | ^^^^^ diff --git a/macros/ui/idle-double-local.rs b/macros/ui/idle-double-local.rs new file mode 100644 index 0000000000..54e67d3433 --- /dev/null +++ b/macros/ui/idle-double-local.rs @@ -0,0 +1,9 @@ +#![no_main] + +#[rtic_macros::mock_app(device = mock)] +mod app { + #[idle(local = [A], local = [B])] + fn idle(_: idle::Context) -> ! { + loop {} + } +} diff --git a/macros/ui/idle-double-local.stderr b/macros/ui/idle-double-local.stderr new file mode 100644 index 0000000000..d3ba4ec9f7 --- /dev/null +++ b/macros/ui/idle-double-local.stderr @@ -0,0 +1,5 @@ +error: argument appears more than once + --> $DIR/idle-double-local.rs:5:25 + | +5 | #[idle(local = [A], local = [B])] + | ^^^^^ diff --git a/macros/ui/idle-double-shared.rs b/macros/ui/idle-double-shared.rs new file mode 100644 index 0000000000..f66cb935bf --- /dev/null +++ b/macros/ui/idle-double-shared.rs @@ -0,0 +1,9 @@ +#![no_main] + +#[rtic_macros::mock_app(device = mock)] +mod app { + #[idle(shared = [A], shared = [B])] + fn idle(_: idle::Context) -> ! { + loop {} + } +} diff --git a/macros/ui/idle-double-shared.stderr b/macros/ui/idle-double-shared.stderr new file mode 100644 index 0000000000..84864a1560 --- /dev/null +++ b/macros/ui/idle-double-shared.stderr @@ -0,0 +1,5 @@ +error: argument appears more than once + --> $DIR/idle-double-shared.rs:5:26 + | +5 | #[idle(shared = [A], shared = [B])] + | ^^^^^^ diff --git a/macros/ui/idle-input.rs b/macros/ui/idle-input.rs new file mode 100644 index 0000000000..c896b1ced9 --- /dev/null +++ b/macros/ui/idle-input.rs @@ -0,0 +1,9 @@ +#![no_main] + +#[rtic_macros::mock_app(device = mock)] +mod app { + #[idle] + fn idle(_: idle::Context, _undef: u32) -> ! { + loop {} + } +} diff --git a/macros/ui/idle-input.stderr b/macros/ui/idle-input.stderr new file mode 100644 index 0000000000..34c38fc0f7 --- /dev/null +++ b/macros/ui/idle-input.stderr @@ -0,0 +1,5 @@ +error: this `#[idle]` function must have signature `fn(idle::Context) -> !` + --> ui/idle-input.rs:6:8 + | +6 | fn idle(_: idle::Context, _undef: u32) -> ! { + | ^^^^ diff --git a/macros/ui/idle-no-context.rs b/macros/ui/idle-no-context.rs new file mode 100644 index 0000000000..bab4680bd3 --- /dev/null +++ b/macros/ui/idle-no-context.rs @@ -0,0 +1,9 @@ +#![no_main] + +#[rtic_macros::mock_app(device = mock)] +mod app { + #[idle] + fn idle() -> ! { + loop {} + } +} diff --git a/macros/ui/idle-no-context.stderr b/macros/ui/idle-no-context.stderr new file mode 100644 index 0000000000..c9f4b3df7c --- /dev/null +++ b/macros/ui/idle-no-context.stderr @@ -0,0 +1,5 @@ +error: this `#[idle]` function must have signature `fn(idle::Context) -> !` + --> ui/idle-no-context.rs:6:8 + | +6 | fn idle() -> ! { + | ^^^^ diff --git a/macros/ui/idle-not-divergent.rs b/macros/ui/idle-not-divergent.rs new file mode 100644 index 0000000000..d1ae8b1da8 --- /dev/null +++ b/macros/ui/idle-not-divergent.rs @@ -0,0 +1,7 @@ +#![no_main] + +#[rtic_macros::mock_app(device = mock)] +mod app { + #[idle] + fn idle(_: idle::Context) {} +} diff --git a/macros/ui/idle-not-divergent.stderr b/macros/ui/idle-not-divergent.stderr new file mode 100644 index 0000000000..e318f58a6a --- /dev/null +++ b/macros/ui/idle-not-divergent.stderr @@ -0,0 +1,5 @@ +error: this `#[idle]` function must have signature `fn(idle::Context) -> !` + --> ui/idle-not-divergent.rs:6:8 + | +6 | fn idle(_: idle::Context) {} + | ^^^^ diff --git a/macros/ui/idle-output.rs b/macros/ui/idle-output.rs new file mode 100644 index 0000000000..16621572e8 --- /dev/null +++ b/macros/ui/idle-output.rs @@ -0,0 +1,9 @@ +#![no_main] + +#[rtic_macros::mock_app(device = mock)] +mod app { + #[idle] + fn idle(_: idle::Context) -> u32 { + 0 + } +} diff --git a/macros/ui/idle-output.stderr b/macros/ui/idle-output.stderr new file mode 100644 index 0000000000..7070e25fe1 --- /dev/null +++ b/macros/ui/idle-output.stderr @@ -0,0 +1,5 @@ +error: this `#[idle]` function must have signature `fn(idle::Context) -> !` + --> ui/idle-output.rs:6:8 + | +6 | fn idle(_: idle::Context) -> u32 { + | ^^^^ diff --git a/macros/ui/idle-pub.rs b/macros/ui/idle-pub.rs new file mode 100644 index 0000000000..0d8dd01a4e --- /dev/null +++ b/macros/ui/idle-pub.rs @@ -0,0 +1,9 @@ +#![no_main] + +#[rtic_macros::mock_app(device = mock)] +mod app { + #[idle] + pub fn idle(_: idle::Context) -> ! { + loop {} + } +} diff --git a/macros/ui/idle-pub.stderr b/macros/ui/idle-pub.stderr new file mode 100644 index 0000000000..aa46ac3974 --- /dev/null +++ b/macros/ui/idle-pub.stderr @@ -0,0 +1,5 @@ +error: this `#[idle]` function must have signature `fn(idle::Context) -> !` + --> ui/idle-pub.rs:6:12 + | +6 | pub fn idle(_: idle::Context) -> ! { + | ^^^^ diff --git a/macros/ui/idle-unsafe.rs b/macros/ui/idle-unsafe.rs new file mode 100644 index 0000000000..3422ef2c98 --- /dev/null +++ b/macros/ui/idle-unsafe.rs @@ -0,0 +1,9 @@ +#![no_main] + +#[rtic_macros::mock_app(device = mock)] +mod app { + #[idle] + unsafe fn idle(_: idle::Context) -> ! { + loop {} + } +} diff --git a/macros/ui/idle-unsafe.stderr b/macros/ui/idle-unsafe.stderr new file mode 100644 index 0000000000..a416800f4f --- /dev/null +++ b/macros/ui/idle-unsafe.stderr @@ -0,0 +1,5 @@ +error: this `#[idle]` function must have signature `fn(idle::Context) -> !` + --> ui/idle-unsafe.rs:6:15 + | +6 | unsafe fn idle(_: idle::Context) -> ! { + | ^^^^ diff --git a/macros/ui/init-divergent.rs b/macros/ui/init-divergent.rs new file mode 100644 index 0000000000..5e4e96a3db --- /dev/null +++ b/macros/ui/init-divergent.rs @@ -0,0 +1,13 @@ +#![no_main] + +#[rtic_macros::mock_app(device = mock)] +mod app { + #[shared] + struct Shared {} + + #[local] + struct Local {} + + #[init] + fn init(_: init::Context) -> ! {} +} diff --git a/macros/ui/init-divergent.stderr b/macros/ui/init-divergent.stderr new file mode 100644 index 0000000000..2d5cc39cf0 --- /dev/null +++ b/macros/ui/init-divergent.stderr @@ -0,0 +1,5 @@ +error: the `#[init]` function must have signature `fn(init::Context) -> (Shared resources struct, Local resources struct, init::Monotonics)` + --> $DIR/init-divergent.rs:12:8 + | +12 | fn init(_: init::Context) -> ! {} + | ^^^^ diff --git a/macros/ui/init-double-local.rs b/macros/ui/init-double-local.rs new file mode 100644 index 0000000000..5f6d7ac091 --- /dev/null +++ b/macros/ui/init-double-local.rs @@ -0,0 +1,7 @@ +#![no_main] + +#[rtic_macros::mock_app(device = mock)] +mod app { + #[init(local = [A], local = [B])] + fn init(_: init::Context) {} +} diff --git a/macros/ui/init-double-local.stderr b/macros/ui/init-double-local.stderr new file mode 100644 index 0000000000..5ffd2c153a --- /dev/null +++ b/macros/ui/init-double-local.stderr @@ -0,0 +1,5 @@ +error: argument appears more than once + --> $DIR/init-double-local.rs:5:25 + | +5 | #[init(local = [A], local = [B])] + | ^^^^^ diff --git a/macros/ui/init-double-shared.rs b/macros/ui/init-double-shared.rs new file mode 100644 index 0000000000..4503c87ea1 --- /dev/null +++ b/macros/ui/init-double-shared.rs @@ -0,0 +1,7 @@ +#![no_main] + +#[rtic_macros::mock_app(device = mock)] +mod app { + #[init(shared = [A], shared = [B])] + fn init(_: init::Context) {} +} diff --git a/macros/ui/init-double-shared.stderr b/macros/ui/init-double-shared.stderr new file mode 100644 index 0000000000..b6b1f6da1e --- /dev/null +++ b/macros/ui/init-double-shared.stderr @@ -0,0 +1,5 @@ +error: unexpected argument + --> $DIR/init-double-shared.rs:5:12 + | +5 | #[init(shared = [A], shared = [B])] + | ^^^^^^ diff --git a/macros/ui/init-input.rs b/macros/ui/init-input.rs new file mode 100644 index 0000000000..ac2a1bdae7 --- /dev/null +++ b/macros/ui/init-input.rs @@ -0,0 +1,13 @@ +#![no_main] + +#[rtic_macros::mock_app(device = mock)] +mod app { + #[shared] + struct Shared {} + + #[local] + struct Local {} + + #[init] + fn init(_: init::Context, _undef: u32) -> (Shared, Local, init::Monotonics) {} +} diff --git a/macros/ui/init-input.stderr b/macros/ui/init-input.stderr new file mode 100644 index 0000000000..983c46926a --- /dev/null +++ b/macros/ui/init-input.stderr @@ -0,0 +1,5 @@ +error: the `#[init]` function must have signature `fn(init::Context) -> (Shared resources struct, Local resources struct, init::Monotonics)` + --> $DIR/init-input.rs:12:8 + | +12 | fn init(_: init::Context, _undef: u32) -> (Shared, Local, init::Monotonics) {} + | ^^^^ diff --git a/macros/ui/init-no-context.rs b/macros/ui/init-no-context.rs new file mode 100644 index 0000000000..a74093ae3c --- /dev/null +++ b/macros/ui/init-no-context.rs @@ -0,0 +1,13 @@ +#![no_main] + +#[rtic_macros::mock_app(device = mock)] +mod app { + #[shared] + struct Shared {} + + #[local] + struct Local {} + + #[init] + fn init() -> (Shared, Local, init::Monotonics) {} +} diff --git a/macros/ui/init-no-context.stderr b/macros/ui/init-no-context.stderr new file mode 100644 index 0000000000..742e2ab18e --- /dev/null +++ b/macros/ui/init-no-context.stderr @@ -0,0 +1,5 @@ +error: the `#[init]` function must have signature `fn(init::Context) -> (Shared resources struct, Local resources struct, init::Monotonics)` + --> $DIR/init-no-context.rs:12:8 + | +12 | fn init() -> (Shared, Local, init::Monotonics) {} + | ^^^^ diff --git a/macros/ui/init-output.rs b/macros/ui/init-output.rs new file mode 100644 index 0000000000..7057c95478 --- /dev/null +++ b/macros/ui/init-output.rs @@ -0,0 +1,9 @@ +#![no_main] + +#[rtic_macros::mock_app(device = mock)] +mod app { + #[init] + fn init(_: init::Context) -> u32 { + 0 + } +} diff --git a/macros/ui/init-output.stderr b/macros/ui/init-output.stderr new file mode 100644 index 0000000000..03e982c607 --- /dev/null +++ b/macros/ui/init-output.stderr @@ -0,0 +1,5 @@ +error: the `#[init]` function must have signature `fn(init::Context) -> (Shared resources struct, Local resources struct, init::Monotonics)` + --> $DIR/init-output.rs:6:8 + | +6 | fn init(_: init::Context) -> u32 { + | ^^^^ diff --git a/macros/ui/init-pub.rs b/macros/ui/init-pub.rs new file mode 100644 index 0000000000..43375e4e54 --- /dev/null +++ b/macros/ui/init-pub.rs @@ -0,0 +1,13 @@ +#![no_main] + +#[rtic_macros::mock_app(device = mock)] +mod app { + #[shared] + struct Shared {} + + #[local] + struct Local {} + + #[init] + pub fn init(_: init::Context) -> (Shared, Local, init::Monotonics) {} +} diff --git a/macros/ui/init-pub.stderr b/macros/ui/init-pub.stderr new file mode 100644 index 0000000000..eb68e1ef21 --- /dev/null +++ b/macros/ui/init-pub.stderr @@ -0,0 +1,5 @@ +error: the `#[init]` function must have signature `fn(init::Context) -> (Shared resources struct, Local resources struct, init::Monotonics)` + --> $DIR/init-pub.rs:12:12 + | +12 | pub fn init(_: init::Context) -> (Shared, Local, init::Monotonics) {} + | ^^^^ diff --git a/macros/ui/init-unsafe.rs b/macros/ui/init-unsafe.rs new file mode 100644 index 0000000000..b5d391dd9d --- /dev/null +++ b/macros/ui/init-unsafe.rs @@ -0,0 +1,7 @@ +#![no_main] + +#[rtic_macros::mock_app(device = mock)] +mod app { + #[init] + unsafe fn init(_: init::Context) -> (Shared, Local, init::Monotonics) {} +} diff --git a/macros/ui/init-unsafe.stderr b/macros/ui/init-unsafe.stderr new file mode 100644 index 0000000000..2a48533914 --- /dev/null +++ b/macros/ui/init-unsafe.stderr @@ -0,0 +1,5 @@ +error: the `#[init]` function must have signature `fn(init::Context) -> (Shared resources struct, Local resources struct, init::Monotonics)` + --> $DIR/init-unsafe.rs:6:15 + | +6 | unsafe fn init(_: init::Context) -> (Shared, Local, init::Monotonics) {} + | ^^^^ diff --git a/macros/ui/interrupt-double.rs b/macros/ui/interrupt-double.rs new file mode 100644 index 0000000000..1133c5cfd9 --- /dev/null +++ b/macros/ui/interrupt-double.rs @@ -0,0 +1,10 @@ +#![no_main] + +#[rtic_macros::mock_app(parse_binds, device = mock)] +mod app { + #[task(binds = UART0)] + fn foo(_: foo::Context) {} + + #[task(binds = UART0)] + fn bar(_: bar::Context) {} +} diff --git a/macros/ui/interrupt-double.stderr b/macros/ui/interrupt-double.stderr new file mode 100644 index 0000000000..62b979bbfa --- /dev/null +++ b/macros/ui/interrupt-double.stderr @@ -0,0 +1,5 @@ +error: this interrupt is already bound + --> $DIR/interrupt-double.rs:8:20 + | +8 | #[task(binds = UART0)] + | ^^^^^ diff --git a/macros/ui/local-collision-2.rs b/macros/ui/local-collision-2.rs new file mode 100644 index 0000000000..7bd092c845 --- /dev/null +++ b/macros/ui/local-collision-2.rs @@ -0,0 +1,21 @@ +#![no_main] + +#[rtic_macros::mock_app(device = mock)] +mod app { + #[shared] + struct Shared {} + + #[local] + struct Local { + a: u32, + } + + fn bar(_: bar::Context) {} + + #[task(local = [a: u8 = 3])] + fn bar(_: bar::Context) {} + + #[init(local = [a: u16 = 2])] + fn init(_: init::Context) -> (Shared, Local, init::Monotonics) {} +} + diff --git a/macros/ui/local-collision-2.stderr b/macros/ui/local-collision-2.stderr new file mode 100644 index 0000000000..1e4c5fa629 --- /dev/null +++ b/macros/ui/local-collision-2.stderr @@ -0,0 +1,17 @@ +error: Local resource "a" is used by multiple tasks or collides with multiple definitions + --> $DIR/local-collision-2.rs:10:9 + | +10 | a: u32, + | ^ + +error: Local resource "a" is used by multiple tasks or collides with multiple definitions + --> $DIR/local-collision-2.rs:18:21 + | +18 | #[init(local = [a: u16 = 2])] + | ^ + +error: Local resource "a" is used by multiple tasks or collides with multiple definitions + --> $DIR/local-collision-2.rs:15:21 + | +15 | #[task(local = [a: u8 = 3])] + | ^ diff --git a/macros/ui/local-collision.rs b/macros/ui/local-collision.rs new file mode 100644 index 0000000000..7dbe97640f --- /dev/null +++ b/macros/ui/local-collision.rs @@ -0,0 +1,21 @@ +#![no_main] + +#[rtic_macros::mock_app(device = mock)] +mod app { + #[shared] + struct Shared {} + + #[local] + struct Local { + a: u32, + } + + #[task(local = [a])] + fn foo(_: foo::Context) {} + + #[task(local = [a: u8 = 3])] + fn bar(_: bar::Context) {} + + #[init] + fn init(_: init::Context) -> (Shared, Local, init::Monotonics) {} +} diff --git a/macros/ui/local-collision.stderr b/macros/ui/local-collision.stderr new file mode 100644 index 0000000000..1ba1da922d --- /dev/null +++ b/macros/ui/local-collision.stderr @@ -0,0 +1,11 @@ +error: Local resource "a" is used by multiple tasks or collides with multiple definitions + --> $DIR/local-collision.rs:10:9 + | +10 | a: u32, + | ^ + +error: Local resource "a" is used by multiple tasks or collides with multiple definitions + --> $DIR/local-collision.rs:16:21 + | +16 | #[task(local = [a: u8 = 3])] + | ^ diff --git a/macros/ui/local-malformed-1.rs b/macros/ui/local-malformed-1.rs new file mode 100644 index 0000000000..7efcd9c14a --- /dev/null +++ b/macros/ui/local-malformed-1.rs @@ -0,0 +1,16 @@ +#![no_main] + +#[rtic_macros::mock_app(device = mock)] +mod app { + #[shared] + struct Shared {} + + #[local] + struct Local {} + + #[task(local = [a:])] + fn foo(_: foo::Context) {} + + #[init] + fn init(_: init::Context) -> (Shared, Local, init::Monotonics) {} +} diff --git a/macros/ui/local-malformed-1.stderr b/macros/ui/local-malformed-1.stderr new file mode 100644 index 0000000000..d15c324bc3 --- /dev/null +++ b/macros/ui/local-malformed-1.stderr @@ -0,0 +1,5 @@ +error: unexpected end of input, expected one of: `for`, parentheses, `fn`, `unsafe`, `extern`, identifier, `::`, `<`, square brackets, `*`, `&`, `!`, `impl`, `_`, lifetime + --> ui/local-malformed-1.rs:11:23 + | +11 | #[task(local = [a:])] + | ^ diff --git a/macros/ui/local-malformed-2.rs b/macros/ui/local-malformed-2.rs new file mode 100644 index 0000000000..ce5f891a39 --- /dev/null +++ b/macros/ui/local-malformed-2.rs @@ -0,0 +1,16 @@ +#![no_main] + +#[rtic_macros::mock_app(device = mock)] +mod app { + #[shared] + struct Shared {} + + #[local] + struct Local {} + + #[task(local = [a: u32])] + fn foo(_: foo::Context) {} + + #[init] + fn init(_: init::Context) -> (Shared, Local, init::Monotonics) {} +} diff --git a/macros/ui/local-malformed-2.stderr b/macros/ui/local-malformed-2.stderr new file mode 100644 index 0000000000..ceb04058fe --- /dev/null +++ b/macros/ui/local-malformed-2.stderr @@ -0,0 +1,5 @@ +error: malformed, expected 'IDENT: TYPE = EXPR' + --> ui/local-malformed-2.rs:11:21 + | +11 | #[task(local = [a: u32])] + | ^ diff --git a/macros/ui/local-malformed-3.rs b/macros/ui/local-malformed-3.rs new file mode 100644 index 0000000000..935dc2c65d --- /dev/null +++ b/macros/ui/local-malformed-3.rs @@ -0,0 +1,16 @@ +#![no_main] + +#[rtic_macros::mock_app(device = mock)] +mod app { + #[shared] + struct Shared {} + + #[local] + struct Local {} + + #[task(local = [a: u32 =])] + fn foo(_: foo::Context) {} + + #[init] + fn init(_: init::Context) -> (Shared, Local, init::Monotonics) {} +} diff --git a/macros/ui/local-malformed-3.stderr b/macros/ui/local-malformed-3.stderr new file mode 100644 index 0000000000..61af4f38d5 --- /dev/null +++ b/macros/ui/local-malformed-3.stderr @@ -0,0 +1,5 @@ +error: unexpected end of input, expected expression + --> ui/local-malformed-3.rs:11:29 + | +11 | #[task(local = [a: u32 =])] + | ^ diff --git a/macros/ui/local-malformed-4.rs b/macros/ui/local-malformed-4.rs new file mode 100644 index 0000000000..49661b5e8c --- /dev/null +++ b/macros/ui/local-malformed-4.rs @@ -0,0 +1,16 @@ +#![no_main] + +#[rtic_macros::mock_app(device = mock)] +mod app { + #[shared] + struct Shared {} + + #[local] + struct Local {} + + #[task(local = [a = u32])] + fn foo(_: foo::Context) {} + + #[init] + fn init(_: init::Context) -> (Shared, Local, init::Monotonics) {} +} diff --git a/macros/ui/local-malformed-4.stderr b/macros/ui/local-malformed-4.stderr new file mode 100644 index 0000000000..0f7d9e7202 --- /dev/null +++ b/macros/ui/local-malformed-4.stderr @@ -0,0 +1,5 @@ +error: malformed, expected a type + --> ui/local-malformed-4.rs:11:21 + | +11 | #[task(local = [a = u32])] + | ^ diff --git a/macros/ui/local-not-declared.rs b/macros/ui/local-not-declared.rs new file mode 100644 index 0000000000..5a38b3d746 --- /dev/null +++ b/macros/ui/local-not-declared.rs @@ -0,0 +1,16 @@ +#![no_main] + +#[rtic_macros::mock_app(device = mock)] +mod app { + #[shared] + struct Shared {} + + #[local] + struct Local {} + + #[task(local = [A])] + fn foo(_: foo::Context) {} + + #[init] + fn init(_: init::Context) -> (Shared, Local, init::Monotonics) {} +} diff --git a/macros/ui/local-not-declared.stderr b/macros/ui/local-not-declared.stderr new file mode 100644 index 0000000000..540b4bb772 --- /dev/null +++ b/macros/ui/local-not-declared.stderr @@ -0,0 +1,5 @@ +error: this local resource has NOT been declared + --> $DIR/local-not-declared.rs:11:21 + | +11 | #[task(local = [A])] + | ^ diff --git a/macros/ui/local-pub.rs b/macros/ui/local-pub.rs new file mode 100644 index 0000000000..8c51754688 --- /dev/null +++ b/macros/ui/local-pub.rs @@ -0,0 +1,9 @@ +#![no_main] + +#[rtic_macros::mock_app(device = mock)] +mod app { + #[local] + struct Local { + pub x: u32, + } +} diff --git a/macros/ui/local-pub.stderr b/macros/ui/local-pub.stderr new file mode 100644 index 0000000000..041bc598b6 --- /dev/null +++ b/macros/ui/local-pub.stderr @@ -0,0 +1,5 @@ +error: this field must have inherited / private visibility + --> $DIR/local-pub.rs:7:13 + | +7 | pub x: u32, + | ^ diff --git a/macros/ui/local-shared-attribute.rs b/macros/ui/local-shared-attribute.rs new file mode 100644 index 0000000000..1ccce4ad1b --- /dev/null +++ b/macros/ui/local-shared-attribute.rs @@ -0,0 +1,14 @@ +#![no_main] + +#[rtic_macros::mock_app(device = mock)] +mod app { + #[task(local = [ + #[test] + a: u32 = 0, // Ok + #[test] + b, // Error + ])] + fn foo(_: foo::Context) { + + } +} diff --git a/macros/ui/local-shared-attribute.stderr b/macros/ui/local-shared-attribute.stderr new file mode 100644 index 0000000000..5c15fb5bd6 --- /dev/null +++ b/macros/ui/local-shared-attribute.stderr @@ -0,0 +1,5 @@ +error: attributes are not supported here + --> $DIR/local-shared-attribute.rs:8:9 + | +8 | #[test] + | ^ diff --git a/macros/ui/local-shared.rs b/macros/ui/local-shared.rs new file mode 100644 index 0000000000..f6fb491ae6 --- /dev/null +++ b/macros/ui/local-shared.rs @@ -0,0 +1,28 @@ +#![no_main] + +#[rtic_macros::mock_app(device = mock)] +mod app { + #[shared] + struct Shared {} + + #[local] + struct Local { + l1: u32, + l2: u32, + } + + #[init] + fn init(_: init::Context) -> (Shared, Local, init::Monotonics) {} + + // l2 ok + #[idle(local = [l2])] + fn idle(cx: idle::Context) -> ! {} + + // l1 rejected (not local) + #[task(priority = 1, local = [l1])] + fn uart0(cx: uart0::Context) {} + + // l1 rejected (not lock_free) + #[task(priority = 2, local = [l1])] + fn uart1(cx: uart1::Context) {} +} diff --git a/macros/ui/local-shared.stderr b/macros/ui/local-shared.stderr new file mode 100644 index 0000000000..0d22db30e3 --- /dev/null +++ b/macros/ui/local-shared.stderr @@ -0,0 +1,11 @@ +error: Local resource "l1" is used by multiple tasks or collides with multiple definitions + --> $DIR/local-shared.rs:22:35 + | +22 | #[task(priority = 1, local = [l1])] + | ^^ + +error: Local resource "l1" is used by multiple tasks or collides with multiple definitions + --> $DIR/local-shared.rs:26:35 + | +26 | #[task(priority = 2, local = [l1])] + | ^^ diff --git a/macros/ui/monotonic-binds-collision-task.rs b/macros/ui/monotonic-binds-collision-task.rs new file mode 100644 index 0000000000..57c59c1b01 --- /dev/null +++ b/macros/ui/monotonic-binds-collision-task.rs @@ -0,0 +1,10 @@ +#![no_main] + +#[rtic_macros::mock_app(parse_extern_interrupt, parse_binds, device = mock)] +mod app { + #[monotonic(binds = Tim1)] + type Fast1 = hal::Tim1Monotonic; + + #[task(binds = Tim1)] + fn foo(_: foo::Context) {} +} diff --git a/macros/ui/monotonic-binds-collision-task.stderr b/macros/ui/monotonic-binds-collision-task.stderr new file mode 100644 index 0000000000..8f84986ae1 --- /dev/null +++ b/macros/ui/monotonic-binds-collision-task.stderr @@ -0,0 +1,5 @@ +error: this interrupt is already bound + --> $DIR/monotonic-binds-collision-task.rs:8:20 + | +8 | #[task(binds = Tim1)] + | ^^^^ diff --git a/macros/ui/monotonic-binds-collision.rs b/macros/ui/monotonic-binds-collision.rs new file mode 100644 index 0000000000..4e54814588 --- /dev/null +++ b/macros/ui/monotonic-binds-collision.rs @@ -0,0 +1,10 @@ +#![no_main] + +#[rtic_macros::mock_app(device = mock)] +mod app { + #[monotonic(binds = Tim1)] + type Fast1 = hal::Tim1Monotonic; + + #[monotonic(binds = Tim1)] + type Fast2 = hal::Tim2Monotonic; +} diff --git a/macros/ui/monotonic-binds-collision.stderr b/macros/ui/monotonic-binds-collision.stderr new file mode 100644 index 0000000000..62b764b254 --- /dev/null +++ b/macros/ui/monotonic-binds-collision.stderr @@ -0,0 +1,5 @@ +error: this interrupt is already bound + --> $DIR/monotonic-binds-collision.rs:8:25 + | +8 | #[monotonic(binds = Tim1)] + | ^^^^ diff --git a/macros/ui/monotonic-double-binds.rs b/macros/ui/monotonic-double-binds.rs new file mode 100644 index 0000000000..1705dc4950 --- /dev/null +++ b/macros/ui/monotonic-double-binds.rs @@ -0,0 +1,7 @@ +#![no_main] + +#[rtic_macros::mock_app(device = mock)] +mod app { + #[monotonic(binds = Tim1, binds = Tim2)] + type Fast = hal::Tim1Monotonic; +} diff --git a/macros/ui/monotonic-double-binds.stderr b/macros/ui/monotonic-double-binds.stderr new file mode 100644 index 0000000000..c7313dfac6 --- /dev/null +++ b/macros/ui/monotonic-double-binds.stderr @@ -0,0 +1,5 @@ +error: argument appears more than once + --> $DIR/monotonic-double-binds.rs:5:31 + | +5 | #[monotonic(binds = Tim1, binds = Tim2)] + | ^^^^^ diff --git a/macros/ui/monotonic-double-default.rs b/macros/ui/monotonic-double-default.rs new file mode 100644 index 0000000000..dc4eac62a6 --- /dev/null +++ b/macros/ui/monotonic-double-default.rs @@ -0,0 +1,7 @@ +#![no_main] + +#[rtic_macros::mock_app(device = mock)] +mod app { + #[monotonic(binds = Tim1, default = true, default = false)] + type Fast = hal::Tim1Monotonic; +} diff --git a/macros/ui/monotonic-double-default.stderr b/macros/ui/monotonic-double-default.stderr new file mode 100644 index 0000000000..9819d04afe --- /dev/null +++ b/macros/ui/monotonic-double-default.stderr @@ -0,0 +1,5 @@ +error: argument appears more than once + --> $DIR/monotonic-double-default.rs:5:47 + | +5 | #[monotonic(binds = Tim1, default = true, default = false)] + | ^^^^^^^ diff --git a/macros/ui/monotonic-double-prio.rs b/macros/ui/monotonic-double-prio.rs new file mode 100644 index 0000000000..4330ddb4f3 --- /dev/null +++ b/macros/ui/monotonic-double-prio.rs @@ -0,0 +1,7 @@ +#![no_main] + +#[rtic_macros::mock_app(device = mock)] +mod app { + #[monotonic(binds = Tim1, priority = 1, priority = 2)] + type Fast = hal::Tim1Monotonic; +} diff --git a/macros/ui/monotonic-double-prio.stderr b/macros/ui/monotonic-double-prio.stderr new file mode 100644 index 0000000000..fa888e264e --- /dev/null +++ b/macros/ui/monotonic-double-prio.stderr @@ -0,0 +1,5 @@ +error: argument appears more than once + --> $DIR/monotonic-double-prio.rs:5:45 + | +5 | #[monotonic(binds = Tim1, priority = 1, priority = 2)] + | ^^^^^^^^ diff --git a/macros/ui/monotonic-double.rs b/macros/ui/monotonic-double.rs new file mode 100644 index 0000000000..3c43fae8f5 --- /dev/null +++ b/macros/ui/monotonic-double.rs @@ -0,0 +1,10 @@ +#![no_main] + +#[rtic_macros::mock_app(device = mock)] +mod app { + #[monotonic(binds = Tim1)] + type Fast = hal::Tim1Monotonic; + + #[monotonic(binds = Tim1)] + type Fast = hal::Tim1Monotonic; +} diff --git a/macros/ui/monotonic-double.stderr b/macros/ui/monotonic-double.stderr new file mode 100644 index 0000000000..9fab84c84e --- /dev/null +++ b/macros/ui/monotonic-double.stderr @@ -0,0 +1,5 @@ +error: `#[monotonic(...)]` on a specific type must appear at most once + --> ui/monotonic-double.rs:9:10 + | +9 | type Fast = hal::Tim1Monotonic; + | ^^^^ diff --git a/macros/ui/monotonic-name-collision.rs b/macros/ui/monotonic-name-collision.rs new file mode 100644 index 0000000000..d8d44310de --- /dev/null +++ b/macros/ui/monotonic-name-collision.rs @@ -0,0 +1,10 @@ +#![no_main] + +#[rtic_macros::mock_app(device = mock)] +mod app { + #[monotonic(binds = Tim1)] + type Fast1 = hal::Tim1Monotonic; + + #[monotonic(binds = Tim2)] + type Fast1 = hal::Tim2Monotonic; +} diff --git a/macros/ui/monotonic-name-collision.stderr b/macros/ui/monotonic-name-collision.stderr new file mode 100644 index 0000000000..6557ee5b2d --- /dev/null +++ b/macros/ui/monotonic-name-collision.stderr @@ -0,0 +1,5 @@ +error: `#[monotonic(...)]` on a specific type must appear at most once + --> ui/monotonic-name-collision.rs:9:10 + | +9 | type Fast1 = hal::Tim2Monotonic; + | ^^^^^ diff --git a/macros/ui/monotonic-no-binds.rs b/macros/ui/monotonic-no-binds.rs new file mode 100644 index 0000000000..462d73e1d7 --- /dev/null +++ b/macros/ui/monotonic-no-binds.rs @@ -0,0 +1,7 @@ +#![no_main] + +#[rtic_macros::mock_app(device = mock)] +mod app { + #[monotonic()] + type Fast = hal::Tim1Monotonic; +} diff --git a/macros/ui/monotonic-no-binds.stderr b/macros/ui/monotonic-no-binds.stderr new file mode 100644 index 0000000000..0ef7b60522 --- /dev/null +++ b/macros/ui/monotonic-no-binds.stderr @@ -0,0 +1,5 @@ +error: `binds = ...` is missing + --> $DIR/monotonic-no-binds.rs:5:17 + | +5 | #[monotonic()] + | ^ diff --git a/macros/ui/monotonic-no-paran.rs b/macros/ui/monotonic-no-paran.rs new file mode 100644 index 0000000000..e294bc8595 --- /dev/null +++ b/macros/ui/monotonic-no-paran.rs @@ -0,0 +1,8 @@ +#![no_main] + +#[rtic_macros::mock_app(device = mock)] +mod app { + #[monotonic] + type Fast = hal::Tim1Monotonic; +} + diff --git a/macros/ui/monotonic-no-paran.stderr b/macros/ui/monotonic-no-paran.stderr new file mode 100644 index 0000000000..c2b32c5c87 --- /dev/null +++ b/macros/ui/monotonic-no-paran.stderr @@ -0,0 +1,5 @@ +error: expected opening ( in #[monotonic( ... )] + --> ui/monotonic-no-paran.rs:5:7 + | +5 | #[monotonic] + | ^^^^^^^^^ diff --git a/macros/ui/monotonic-timer-collision.rs b/macros/ui/monotonic-timer-collision.rs new file mode 100644 index 0000000000..5663ad7743 --- /dev/null +++ b/macros/ui/monotonic-timer-collision.rs @@ -0,0 +1,10 @@ +#![no_main] + +#[rtic_macros::mock_app(device = mock)] +mod app { + #[monotonic(binds = Tim1)] + type Fast1 = hal::Tim1Monotonic; + + #[monotonic(binds = Tim2)] + type Fast2 = hal::Tim1Monotonic; +} diff --git a/macros/ui/monotonic-timer-collision.stderr b/macros/ui/monotonic-timer-collision.stderr new file mode 100644 index 0000000000..239b96b6ef --- /dev/null +++ b/macros/ui/monotonic-timer-collision.stderr @@ -0,0 +1,5 @@ +error: this type is already used by another monotonic + --> $DIR/monotonic-timer-collision.rs:9:18 + | +9 | type Fast2 = hal::Tim1Monotonic; + | ^^^ diff --git a/macros/ui/monotonic-with-attrs.rs b/macros/ui/monotonic-with-attrs.rs new file mode 100644 index 0000000000..7c63fbbf6d --- /dev/null +++ b/macros/ui/monotonic-with-attrs.rs @@ -0,0 +1,8 @@ +#![no_main] + +#[rtic_macros::mock_app(device = mock)] +mod app { + #[no_mangle] + #[monotonic(binds = Tim1)] + type Fast = hal::Tim1Monotonic; +} diff --git a/macros/ui/monotonic-with-attrs.stderr b/macros/ui/monotonic-with-attrs.stderr new file mode 100644 index 0000000000..62655d872c --- /dev/null +++ b/macros/ui/monotonic-with-attrs.stderr @@ -0,0 +1,5 @@ +error: Monotonic does not support attributes other than `#[cfg]` + --> $DIR/monotonic-with-attrs.rs:5:7 + | +5 | #[no_mangle] + | ^^^^^^^^^ diff --git a/macros/ui/pub-local.stderr b/macros/ui/pub-local.stderr new file mode 100644 index 0000000000..dee818ccab --- /dev/null +++ b/macros/ui/pub-local.stderr @@ -0,0 +1,5 @@ +error: this field must have inherited / private visibility + --> $DIR/pub-local.rs:7:13 + | +7 | pub x: u32, + | ^ diff --git a/macros/ui/pub-shared.stderr b/macros/ui/pub-shared.stderr new file mode 100644 index 0000000000..0fdb1ff517 --- /dev/null +++ b/macros/ui/pub-shared.stderr @@ -0,0 +1,5 @@ +error: this field must have inherited / private visibility + --> $DIR/pub-shared.rs:7:13 + | +7 | pub x: u32, + | ^ diff --git a/macros/ui/shared-lock-free.rs b/macros/ui/shared-lock-free.rs new file mode 100644 index 0000000000..c7f8a16d5e --- /dev/null +++ b/macros/ui/shared-lock-free.rs @@ -0,0 +1,38 @@ +#![no_main] + +#[rtic_macros::mock_app(device = mock)] +mod app { + #[shared] + struct Shared { + // An exclusive, early resource + #[lock_free] + e1: u32, + + // An exclusive, late resource + #[lock_free] + e2: u32, + } + + #[local] + struct Local {} + + #[init] + fn init(_: init::Context) -> (Shared, Local, init::Monotonics) {} + + // e2 ok + #[idle(shared = [e2])] + fn idle(cx: idle::Context) -> ! { + debug::exit(debug::EXIT_SUCCESS); + loop {} + } + + // e1 rejected (not lock_free) + #[task(priority = 1, shared = [e1])] + fn uart0(cx: uart0::Context) { + *cx.resources.e1 += 10; + } + + // e1 rejected (not lock_free) + #[task(priority = 2, shared = [e1])] + fn uart1(cx: uart1::Context) {} +} diff --git a/macros/ui/shared-lock-free.stderr b/macros/ui/shared-lock-free.stderr new file mode 100644 index 0000000000..c6820e8729 --- /dev/null +++ b/macros/ui/shared-lock-free.stderr @@ -0,0 +1,17 @@ +error: Lock free shared resource "e1" is used by tasks at different priorities + --> $DIR/shared-lock-free.rs:9:9 + | +9 | e1: u32, + | ^^ + +error: Shared resource "e1" is declared lock free but used by tasks at different priorities + --> $DIR/shared-lock-free.rs:30:36 + | +30 | #[task(priority = 1, shared = [e1])] + | ^^ + +error: Shared resource "e1" is declared lock free but used by tasks at different priorities + --> $DIR/shared-lock-free.rs:36:36 + | +36 | #[task(priority = 2, shared = [e1])] + | ^^ diff --git a/macros/ui/shared-not-declared.rs b/macros/ui/shared-not-declared.rs new file mode 100644 index 0000000000..aca4178761 --- /dev/null +++ b/macros/ui/shared-not-declared.rs @@ -0,0 +1,16 @@ +#![no_main] + +#[rtic_macros::mock_app(device = mock)] +mod app { + #[shared] + struct Shared {} + + #[local] + struct Local {} + + #[task(shared = [A])] + fn foo(_: foo::Context) {} + + #[init] + fn init(_: init::Context) -> (Shared, Local, init::Monotonics) {} +} diff --git a/macros/ui/shared-not-declared.stderr b/macros/ui/shared-not-declared.stderr new file mode 100644 index 0000000000..c17425191d --- /dev/null +++ b/macros/ui/shared-not-declared.stderr @@ -0,0 +1,5 @@ +error: this shared resource has NOT been declared + --> $DIR/shared-not-declared.rs:11:22 + | +11 | #[task(shared = [A])] + | ^ diff --git a/macros/ui/shared-pub.rs b/macros/ui/shared-pub.rs new file mode 100644 index 0000000000..10351fd562 --- /dev/null +++ b/macros/ui/shared-pub.rs @@ -0,0 +1,9 @@ +#![no_main] + +#[rtic_macros::mock_app(device = mock)] +mod app { + #[shared] + struct Shared { + pub x: u32, + } +} diff --git a/macros/ui/shared-pub.stderr b/macros/ui/shared-pub.stderr new file mode 100644 index 0000000000..8f761c6be8 --- /dev/null +++ b/macros/ui/shared-pub.stderr @@ -0,0 +1,5 @@ +error: this field must have inherited / private visibility + --> $DIR/shared-pub.rs:7:13 + | +7 | pub x: u32, + | ^ diff --git a/macros/ui/task-bind.rs b/macros/ui/task-bind.rs new file mode 100644 index 0000000000..de60524314 --- /dev/null +++ b/macros/ui/task-bind.rs @@ -0,0 +1,7 @@ +#![no_main] + +#[rtic_macros::mock_app(device = mock)] +mod app { + #[task(binds = UART0)] + fn foo(_: foo::Context) {} +} diff --git a/macros/ui/task-bind.stderr b/macros/ui/task-bind.stderr new file mode 100644 index 0000000000..60cfdc8b86 --- /dev/null +++ b/macros/ui/task-bind.stderr @@ -0,0 +1,5 @@ +error: Unexpected bind in task argument. Binds are only parsed if Settings::parse_binds is set. + --> $DIR/task-bind.rs:5:12 + | +5 | #[task(binds = UART0)] + | ^^^^^ diff --git a/macros/ui/task-divergent.rs b/macros/ui/task-divergent.rs new file mode 100644 index 0000000000..5a471f3cca --- /dev/null +++ b/macros/ui/task-divergent.rs @@ -0,0 +1,9 @@ +#![no_main] + +#[rtic_macros::mock_app(device = mock)] +mod app { + #[task] + fn foo(_: foo::Context) -> ! { + loop {} + } +} diff --git a/macros/ui/task-divergent.stderr b/macros/ui/task-divergent.stderr new file mode 100644 index 0000000000..b25ca5d798 --- /dev/null +++ b/macros/ui/task-divergent.stderr @@ -0,0 +1,5 @@ +error: this task handler must have type signature `(async) fn(foo::Context, ..)` + --> ui/task-divergent.rs:6:8 + | +6 | fn foo(_: foo::Context) -> ! { + | ^^^ diff --git a/macros/ui/task-double-capacity.rs b/macros/ui/task-double-capacity.rs new file mode 100644 index 0000000000..806d973146 --- /dev/null +++ b/macros/ui/task-double-capacity.rs @@ -0,0 +1,7 @@ +#![no_main] + +#[rtic_macros::mock_app(device = mock)] +mod app { + #[task(capacity = 1, capacity = 2)] + fn foo(_: foo::Context) {} +} diff --git a/macros/ui/task-double-capacity.stderr b/macros/ui/task-double-capacity.stderr new file mode 100644 index 0000000000..f73bca5fbf --- /dev/null +++ b/macros/ui/task-double-capacity.stderr @@ -0,0 +1,5 @@ +error: argument appears more than once + --> $DIR/task-double-capacity.rs:5:26 + | +5 | #[task(capacity = 1, capacity = 2)] + | ^^^^^^^^ diff --git a/macros/ui/task-double-local.rs b/macros/ui/task-double-local.rs new file mode 100644 index 0000000000..2e465d7c0e --- /dev/null +++ b/macros/ui/task-double-local.rs @@ -0,0 +1,7 @@ +#![no_main] + +#[rtic_macros::mock_app(device = mock)] +mod app { + #[task(local = [A], local = [B])] + fn foo(_: foo::Context) {} +} diff --git a/macros/ui/task-double-local.stderr b/macros/ui/task-double-local.stderr new file mode 100644 index 0000000000..654ed33328 --- /dev/null +++ b/macros/ui/task-double-local.stderr @@ -0,0 +1,5 @@ +error: argument appears more than once + --> $DIR/task-double-local.rs:5:25 + | +5 | #[task(local = [A], local = [B])] + | ^^^^^ diff --git a/macros/ui/task-double-priority.rs b/macros/ui/task-double-priority.rs new file mode 100644 index 0000000000..0a2ef0ddfc --- /dev/null +++ b/macros/ui/task-double-priority.rs @@ -0,0 +1,7 @@ +#![no_main] + +#[rtic_macros::mock_app(device = mock)] +mod app { + #[task(priority = 1, priority = 2)] + fn foo(_: foo::Context) {} +} diff --git a/macros/ui/task-double-priority.stderr b/macros/ui/task-double-priority.stderr new file mode 100644 index 0000000000..3d06dc6643 --- /dev/null +++ b/macros/ui/task-double-priority.stderr @@ -0,0 +1,5 @@ +error: argument appears more than once + --> $DIR/task-double-priority.rs:5:26 + | +5 | #[task(priority = 1, priority = 2)] + | ^^^^^^^^ diff --git a/macros/ui/task-double-shared.rs b/macros/ui/task-double-shared.rs new file mode 100644 index 0000000000..3b4d411584 --- /dev/null +++ b/macros/ui/task-double-shared.rs @@ -0,0 +1,7 @@ +#![no_main] + +#[rtic_macros::mock_app(device = mock)] +mod app { + #[task(shared = [A], shared = [B])] + fn foo(_: foo::Context) {} +} diff --git a/macros/ui/task-double-shared.stderr b/macros/ui/task-double-shared.stderr new file mode 100644 index 0000000000..6952f06c17 --- /dev/null +++ b/macros/ui/task-double-shared.stderr @@ -0,0 +1,5 @@ +error: argument appears more than once + --> $DIR/task-double-shared.rs:5:26 + | +5 | #[task(shared = [A], shared = [B])] + | ^^^^^^ diff --git a/macros/ui/task-idle.rs b/macros/ui/task-idle.rs new file mode 100644 index 0000000000..3be6e282b1 --- /dev/null +++ b/macros/ui/task-idle.rs @@ -0,0 +1,13 @@ +#![no_main] + +#[rtic_macros::mock_app(device = mock)] +mod app { + #[idle] + fn foo(_: foo::Context) -> ! { + loop {} + } + + // name collides with `#[idle]` function + #[task] + fn foo(_: foo::Context) {} +} diff --git a/macros/ui/task-idle.stderr b/macros/ui/task-idle.stderr new file mode 100644 index 0000000000..ba4fc94c23 --- /dev/null +++ b/macros/ui/task-idle.stderr @@ -0,0 +1,5 @@ +error: this identifier has already been used + --> $DIR/task-idle.rs:12:8 + | +12 | fn foo(_: foo::Context) {} + | ^^^ diff --git a/macros/ui/task-init.rs b/macros/ui/task-init.rs new file mode 100644 index 0000000000..bab3805019 --- /dev/null +++ b/macros/ui/task-init.rs @@ -0,0 +1,17 @@ +#![no_main] + +#[rtic_macros::mock_app(device = mock)] +mod app { + #[shared] + struct Shared {} + + #[local] + struct Local {} + + #[init] + fn foo(_: foo::Context) -> (Shared, Local, foo::Monotonics) {} + + // name collides with `#[idle]` function + #[task] + fn foo(_: foo::Context) {} +} diff --git a/macros/ui/task-init.stderr b/macros/ui/task-init.stderr new file mode 100644 index 0000000000..911af37f12 --- /dev/null +++ b/macros/ui/task-init.stderr @@ -0,0 +1,5 @@ +error: this identifier has already been used + --> $DIR/task-init.rs:16:8 + | +16 | fn foo(_: foo::Context) {} + | ^^^ diff --git a/macros/ui/task-interrupt-same-prio-spawn.rs b/macros/ui/task-interrupt-same-prio-spawn.rs new file mode 100644 index 0000000000..741e60e462 --- /dev/null +++ b/macros/ui/task-interrupt-same-prio-spawn.rs @@ -0,0 +1,7 @@ +#![no_main] + +#[rtic_macros::mock_app(parse_binds, device = mock)] +mod app { + #[task(binds = SysTick, only_same_priority_spawn_please_fix_me)] + fn foo(_: foo::Context) {} +} diff --git a/macros/ui/task-interrupt-same-prio-spawn.stderr b/macros/ui/task-interrupt-same-prio-spawn.stderr new file mode 100644 index 0000000000..171b85064f --- /dev/null +++ b/macros/ui/task-interrupt-same-prio-spawn.stderr @@ -0,0 +1,5 @@ +error: hardware tasks are not allowed to be spawned, `only_same_priority_spawn_please_fix_me` is only for software tasks + --> ui/task-interrupt-same-prio-spawn.rs:5:29 + | +5 | #[task(binds = SysTick, only_same_priority_spawn_please_fix_me)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/macros/ui/task-interrupt.rs b/macros/ui/task-interrupt.rs new file mode 100644 index 0000000000..71fef9aba0 --- /dev/null +++ b/macros/ui/task-interrupt.rs @@ -0,0 +1,10 @@ +#![no_main] + +#[rtic_macros::mock_app(parse_binds, device = mock)] +mod app { + #[task(binds = SysTick)] + fn foo(_: foo::Context) {} + + #[task] + fn foo(_: foo::Context) {} +} diff --git a/macros/ui/task-interrupt.stderr b/macros/ui/task-interrupt.stderr new file mode 100644 index 0000000000..6efb0f9966 --- /dev/null +++ b/macros/ui/task-interrupt.stderr @@ -0,0 +1,5 @@ +error: this task is defined multiple times + --> $DIR/task-interrupt.rs:9:8 + | +9 | fn foo(_: foo::Context) {} + | ^^^ diff --git a/macros/ui/task-no-context.rs b/macros/ui/task-no-context.rs new file mode 100644 index 0000000000..e2da625417 --- /dev/null +++ b/macros/ui/task-no-context.rs @@ -0,0 +1,7 @@ +#![no_main] + +#[rtic_macros::mock_app(device = mock)] +mod app { + #[task] + fn foo() {} +} diff --git a/macros/ui/task-no-context.stderr b/macros/ui/task-no-context.stderr new file mode 100644 index 0000000000..8bf3438b52 --- /dev/null +++ b/macros/ui/task-no-context.stderr @@ -0,0 +1,5 @@ +error: this task handler must have type signature `(async) fn(foo::Context, ..)` + --> ui/task-no-context.rs:6:8 + | +6 | fn foo() {} + | ^^^ diff --git a/macros/ui/task-priority-too-high.rs b/macros/ui/task-priority-too-high.rs new file mode 100644 index 0000000000..8c32bebaa3 --- /dev/null +++ b/macros/ui/task-priority-too-high.rs @@ -0,0 +1,7 @@ +#![no_main] + +#[rtic_macros::mock_app(device = mock)] +mod app { + #[task(priority = 256)] + fn foo(_: foo::Context) {} +} diff --git a/macros/ui/task-priority-too-high.stderr b/macros/ui/task-priority-too-high.stderr new file mode 100644 index 0000000000..5790c8833d --- /dev/null +++ b/macros/ui/task-priority-too-high.stderr @@ -0,0 +1,5 @@ +error: this literal must be in the range 0...255 + --> ui/task-priority-too-high.rs:5:23 + | +5 | #[task(priority = 256)] + | ^^^ diff --git a/macros/ui/task-priority-too-low.rs b/macros/ui/task-priority-too-low.rs new file mode 100644 index 0000000000..beed4de1e1 --- /dev/null +++ b/macros/ui/task-priority-too-low.rs @@ -0,0 +1,7 @@ +#![no_main] + +#[rtic_macros::mock_app(parse_binds, device = mock)] +mod app { + #[task(binds = UART0, priority = 0)] + fn foo(_: foo::Context) {} +} diff --git a/macros/ui/task-priority-too-low.stderr b/macros/ui/task-priority-too-low.stderr new file mode 100644 index 0000000000..85c86604ee --- /dev/null +++ b/macros/ui/task-priority-too-low.stderr @@ -0,0 +1,5 @@ +error: hardware tasks are not allowed to be at priority 0 + --> ui/task-priority-too-low.rs:5:38 + | +5 | #[task(binds = UART0, priority = 0)] + | ^ diff --git a/macros/ui/task-pub.rs b/macros/ui/task-pub.rs new file mode 100644 index 0000000000..3cbd523413 --- /dev/null +++ b/macros/ui/task-pub.rs @@ -0,0 +1,7 @@ +#![no_main] + +#[rtic_macros::mock_app(device = mock)] +mod app { + #[task] + pub fn foo(_: foo::Context) {} +} diff --git a/macros/ui/task-pub.stderr b/macros/ui/task-pub.stderr new file mode 100644 index 0000000000..56e09b11d9 --- /dev/null +++ b/macros/ui/task-pub.stderr @@ -0,0 +1,5 @@ +error: this task handler must have type signature `(async) fn(foo::Context, ..)` + --> ui/task-pub.rs:6:12 + | +6 | pub fn foo(_: foo::Context) {} + | ^^^ diff --git a/macros/ui/task-unsafe.rs b/macros/ui/task-unsafe.rs new file mode 100644 index 0000000000..44255f02b5 --- /dev/null +++ b/macros/ui/task-unsafe.rs @@ -0,0 +1,7 @@ +#![no_main] + +#[rtic_macros::mock_app(device = mock)] +mod app { + #[task] + unsafe fn foo(_: foo::Context) {} +} diff --git a/macros/ui/task-unsafe.stderr b/macros/ui/task-unsafe.stderr new file mode 100644 index 0000000000..424c5af323 --- /dev/null +++ b/macros/ui/task-unsafe.stderr @@ -0,0 +1,5 @@ +error: this task handler must have type signature `(async) fn(foo::Context, ..)` + --> ui/task-unsafe.rs:6:15 + | +6 | unsafe fn foo(_: foo::Context) {} + | ^^^ diff --git a/src/lib.rs b/src/lib.rs index 0c0d0cc7dc..7d12d9af81 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,123 +1,14 @@ -//! Real-Time Interrupt-driven Concurrency (RTIC) framework for ARM Cortex-M microcontrollers. -//! -//! **IMPORTANT**: This crate is published as [`cortex-m-rtic`] on crates.io but the name of the -//! library is `rtic`. -//! -//! [`cortex-m-rtic`]: https://crates.io/crates/cortex-m-rtic -//! -//! The user level documentation can be found [here]. -//! -//! [here]: https://rtic.rs -//! -//! Don't forget to check the documentation of the `#[app]` attribute (listed under the reexports -//! section), which is the main component of the framework. -//! -//! # Minimum Supported Rust Version (MSRV) -//! -//! This crate is compiled and tested with the latest toolchain (rolling) as of the release date. -//! If you run into compilation errors, try the latest stable release of the rust toolchain. -//! -//! # Semantic Versioning -//! -//! Like the Rust project, this crate adheres to [SemVer]: breaking changes in the API and semantics -//! require a *semver bump* (since 1.0.0 a new major version release), with the exception of breaking changes -//! that fix soundness issues -- those are considered bug fixes and can be landed in a new patch -//! release. -//! -//! [SemVer]: https://semver.org/spec/v2.0.0.html - -#![deny(missing_docs)] -#![deny(rust_2021_compatibility)] -#![deny(rust_2018_compatibility)] -#![deny(rust_2018_idioms)] -#![no_std] -#![doc( - html_logo_url = "https://raw.githubusercontent.com/rtic-rs/cortex-m-rtic/master/book/en/src/RTIC.svg", - html_favicon_url = "https://raw.githubusercontent.com/rtic-rs/cortex-m-rtic/master/book/en/src/RTIC.svg" -)] -//deny_warnings_placeholder_for_ci -#![allow(clippy::inline_always)] - -use cortex_m::{interrupt::InterruptNumber, peripheral::NVIC}; -pub use cortex_m_rtic_macros::app; -pub use rtic_core::{prelude as mutex_prelude, Exclusive, Mutex}; -pub use rtic_monotonic::{self, Monotonic}; - -/// module `mutex::prelude` provides `Mutex` and multi-lock variants. Recommended over `mutex_prelude` -pub mod mutex { - pub use rtic_core::prelude; - pub use rtic_core::Mutex; +pub fn add(left: usize, right: usize) -> usize { + left + right } -#[doc(hidden)] -pub mod export; -#[doc(hidden)] -mod tq; +#[cfg(test)] +mod tests { + use super::*; -/// Sets the given `interrupt` as pending -/// -/// This is a convenience function around -/// [`NVIC::pend`](../cortex_m/peripheral/struct.NVIC.html#method.pend) -pub fn pend(interrupt: I) -where - I: InterruptNumber, -{ - NVIC::pend(interrupt); -} - -use core::cell::UnsafeCell; - -/// Internal replacement for `static mut T` -/// -/// Used to represent RTIC Resources -/// -/// Soundness: -/// 1) Unsafe API for internal use only -/// 2) ``get_mut(&self) -> *mut T`` -/// returns a raw mutable pointer to the inner T -/// casting to &mut T is under control of RTIC -/// RTIC ensures &mut T to be unique under Rust aliasing rules. -/// -/// Implementation uses the underlying ``UnsafeCell`` -/// self.0.get() -> *mut T -/// -/// 3) get(&self) -> *const T -/// returns a raw immutable (const) pointer to the inner T -/// casting to &T is under control of RTIC -/// RTIC ensures &T to be shared under Rust aliasing rules. -/// -/// Implementation uses the underlying ``UnsafeCell`` -/// self.0.get() -> *mut T, demoted to *const T -/// -#[repr(transparent)] -pub struct RacyCell(UnsafeCell); - -impl RacyCell { - /// Create a ``RacyCell`` - #[inline(always)] - pub const fn new(value: T) -> Self { - RacyCell(UnsafeCell::new(value)) - } - - /// Get `*mut T` - /// - /// # Safety - /// - /// See documentation notes for [`RacyCell`] - #[inline(always)] - pub unsafe fn get_mut(&self) -> *mut T { - self.0.get() - } - - /// Get `*const T` - /// - /// # Safety - /// - /// See documentation notes for [`RacyCell`] - #[inline(always)] - pub unsafe fn get(&self) -> *const T { - self.0.get() + #[test] + fn it_works() { + let result = add(2, 2); + assert_eq!(result, 4); } } - -unsafe impl Sync for RacyCell {} From 582c602912592ec7ebea3096aefa02aea99c2143 Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Mon, 2 Jan 2023 14:34:05 +0100 Subject: [PATCH 213/423] Old xtask test pass --- ci/expected/async-delay.run | 7 + ci/expected/async-infinite-loop.run | 6 + ci/expected/async-task-multiple-prios.run | 5 + ci/expected/async-task.run | 3 + ci/expected/async-timeout.run | 5 + ci/expected/periodic-at.run | 6 +- ci/expected/periodic-at2.run | 10 +- examples/async-delay.rs | 67 +++ examples/async-infinite-loop.rs | 57 +++ examples/async-task-multiple-prios.rs | 76 ++++ examples/async-task.rs | 61 +++ examples/async-timeout.rs | 87 ++++ examples/binds.rs | 13 +- examples/cancel-reschedule.rs | 9 +- examples/capacity.rs | 5 +- examples/cfg-whole-task.rs | 17 +- examples/common.rs | 7 +- examples/complex.rs | 54 +-- examples/declared_locals.rs | 1 - examples/destructure.rs | 5 +- examples/extern_binds.rs | 12 +- examples/extern_spawn.rs | 3 +- examples/generics.rs | 10 +- examples/hardware.rs | 13 +- examples/idle-wfi.rs | 5 +- examples/idle.rs | 5 +- examples/init.rs | 3 +- examples/locals.rs | 11 +- examples/lock-free.rs | 5 +- examples/lock.rs | 11 +- examples/message.rs | 7 +- examples/message_passing.rs | 3 +- examples/multilock.rs | 3 +- examples/not-sync.rs | 4 - examples/only-shared-access.rs | 5 +- examples/periodic-at.rs | 7 +- examples/periodic-at2.rs | 11 +- examples/periodic.rs | 9 +- examples/peripherals-taken.rs | 4 +- examples/pool.rs | 2 - examples/preempt.rs | 10 +- examples/ramfunc.rs | 3 +- examples/resource-user-struct.rs | 5 +- examples/schedule.rs | 9 +- examples/shared.rs | 5 +- examples/spawn.rs | 5 +- examples/static.rs | 3 +- examples/t-binds.rs | 1 - examples/t-htask-main.rs | 2 - examples/t-idle-main.rs | 2 - examples/t-schedule.rs | 1 - examples/t-spawn.rs | 1 - examples/task.rs | 11 +- macros/src/codegen/local_resources_struct.rs | 1 - macros/src/syntax.rs | 21 - macros/src/syntax/analyze.rs | 42 +- src/export.rs | 134 +++++- src/lib.rs | 129 +++++- src/sll.rs | 421 +++++++++++++++++++ src/tq.rs | 277 +++++++++--- ui/extern-interrupt-not-enough.stderr | 4 +- ui/task-priority-too-high.rs | 2 +- ui/task-priority-too-high.stderr | 8 + xtask/src/command.rs | 3 +- 64 files changed, 1418 insertions(+), 316 deletions(-) create mode 100644 ci/expected/async-delay.run create mode 100644 ci/expected/async-infinite-loop.run create mode 100644 ci/expected/async-task-multiple-prios.run create mode 100644 ci/expected/async-task.run create mode 100644 ci/expected/async-timeout.run create mode 100644 examples/async-delay.rs create mode 100644 examples/async-infinite-loop.rs create mode 100644 examples/async-task-multiple-prios.rs create mode 100644 examples/async-task.rs create mode 100644 examples/async-timeout.rs create mode 100644 src/sll.rs diff --git a/ci/expected/async-delay.run b/ci/expected/async-delay.run new file mode 100644 index 0000000000..61852abfd9 --- /dev/null +++ b/ci/expected/async-delay.run @@ -0,0 +1,7 @@ +init +hello from bar +hello from baz +hello from foo +bye from foo +bye from bar +bye from baz diff --git a/ci/expected/async-infinite-loop.run b/ci/expected/async-infinite-loop.run new file mode 100644 index 0000000000..f9fd4e494c --- /dev/null +++ b/ci/expected/async-infinite-loop.run @@ -0,0 +1,6 @@ +init +hello from async 0 +hello from async 1 +hello from async 2 +hello from async 3 +hello from async 4 diff --git a/ci/expected/async-task-multiple-prios.run b/ci/expected/async-task-multiple-prios.run new file mode 100644 index 0000000000..9b0f53365b --- /dev/null +++ b/ci/expected/async-task-multiple-prios.run @@ -0,0 +1,5 @@ +init +hello from normal 2 +hello from async 2 +hello from normal 1 +hello from async 1 diff --git a/ci/expected/async-task.run b/ci/expected/async-task.run new file mode 100644 index 0000000000..f7ce3a6065 --- /dev/null +++ b/ci/expected/async-task.run @@ -0,0 +1,3 @@ +init +hello from normal +hello from async diff --git a/ci/expected/async-timeout.run b/ci/expected/async-timeout.run new file mode 100644 index 0000000000..a8074230ee --- /dev/null +++ b/ci/expected/async-timeout.run @@ -0,0 +1,5 @@ +init +hello from bar +hello from foo +foo no timeout +bar timeout diff --git a/ci/expected/periodic-at.run b/ci/expected/periodic-at.run index 54020f9e95..bf5bb0631b 100644 --- a/ci/expected/periodic-at.run +++ b/ci/expected/periodic-at.run @@ -1,4 +1,4 @@ foo Instant { ticks: 0 } -foo Instant { ticks: 100 } -foo Instant { ticks: 200 } -foo Instant { ticks: 300 } +foo Instant { ticks: 10 } +foo Instant { ticks: 20 } +foo Instant { ticks: 30 } diff --git a/ci/expected/periodic-at2.run b/ci/expected/periodic-at2.run index 47adbef486..6e56421a30 100644 --- a/ci/expected/periodic-at2.run +++ b/ci/expected/periodic-at2.run @@ -1,7 +1,7 @@ foo Instant { ticks: 0 } bar Instant { ticks: 10 } -foo Instant { ticks: 110 } -bar Instant { ticks: 120 } -foo Instant { ticks: 220 } -bar Instant { ticks: 230 } -foo Instant { ticks: 330 } +foo Instant { ticks: 30 } +bar Instant { ticks: 40 } +foo Instant { ticks: 60 } +bar Instant { ticks: 70 } +foo Instant { ticks: 90 } diff --git a/examples/async-delay.rs b/examples/async-delay.rs new file mode 100644 index 0000000000..7802bda4d4 --- /dev/null +++ b/examples/async-delay.rs @@ -0,0 +1,67 @@ +#![no_main] +#![no_std] +#![feature(type_alias_impl_trait)] + +use panic_semihosting as _; + +#[rtic::app(device = lm3s6965, dispatchers = [SSI0, UART0], peripherals = true)] +mod app { + use cortex_m_semihosting::{debug, hprintln}; + use systick_monotonic::*; + + #[shared] + struct Shared {} + + #[local] + struct Local {} + + #[monotonic(binds = SysTick, default = true)] + type MyMono = Systick<100>; + + #[init] + fn init(cx: init::Context) -> (Shared, Local, init::Monotonics) { + hprintln!("init").unwrap(); + + foo::spawn().ok(); + bar::spawn().ok(); + baz::spawn().ok(); + + ( + Shared {}, + Local {}, + init::Monotonics(Systick::new(cx.core.SYST, 12_000_000)), + ) + } + + #[idle] + fn idle(_: idle::Context) -> ! { + // debug::exit(debug::EXIT_SUCCESS); + loop { + // hprintln!("idle"); + cortex_m::asm::wfi(); // put the MCU in sleep mode until interrupt occurs + } + } + + #[task] + async fn foo(_cx: foo::Context) { + hprintln!("hello from foo").ok(); + monotonics::delay(100.millis()).await; + hprintln!("bye from foo").ok(); + } + + #[task] + async fn bar(_cx: bar::Context) { + hprintln!("hello from bar").ok(); + monotonics::delay(200.millis()).await; + hprintln!("bye from bar").ok(); + } + + #[task] + async fn baz(_cx: baz::Context) { + hprintln!("hello from baz").ok(); + monotonics::delay(300.millis()).await; + hprintln!("bye from baz").ok(); + + debug::exit(debug::EXIT_SUCCESS); + } +} diff --git a/examples/async-infinite-loop.rs b/examples/async-infinite-loop.rs new file mode 100644 index 0000000000..7615818d3c --- /dev/null +++ b/examples/async-infinite-loop.rs @@ -0,0 +1,57 @@ +#![no_main] +#![no_std] +#![feature(type_alias_impl_trait)] + +use panic_semihosting as _; + +#[rtic::app(device = lm3s6965, dispatchers = [SSI0, UART0], peripherals = true)] +mod app { + use cortex_m_semihosting::{debug, hprintln}; + use systick_monotonic::*; + + #[shared] + struct Shared {} + + #[local] + struct Local {} + + #[monotonic(binds = SysTick, default = true)] + type MyMono = Systick<100>; + + #[init] + fn init(cx: init::Context) -> (Shared, Local, init::Monotonics) { + hprintln!("init").unwrap(); + + foo::spawn().ok(); + + ( + Shared {}, + Local {}, + init::Monotonics(Systick::new(cx.core.SYST, 12_000_000)), + ) + } + + #[idle] + fn idle(_: idle::Context) -> ! { + loop { + cortex_m::asm::wfi(); // put the MCU in sleep mode until interrupt occurs + } + } + + // Infinite loops are not allowed in RTIC, however in async tasks they are - if there is an + // await inside the loop. + #[task] + async fn foo(_cx: foo::Context) { + let mut i = 0; + loop { + if i == 5 { + debug::exit(debug::EXIT_SUCCESS); + } + + hprintln!("hello from async {}", i).ok(); + monotonics::delay(100.millis()).await; // This makes it okey! + + i += 1; + } + } +} diff --git a/examples/async-task-multiple-prios.rs b/examples/async-task-multiple-prios.rs new file mode 100644 index 0000000000..3e197987a2 --- /dev/null +++ b/examples/async-task-multiple-prios.rs @@ -0,0 +1,76 @@ +#![no_main] +#![no_std] +#![feature(type_alias_impl_trait)] + +use panic_semihosting as _; + +// NOTES: +// +// - Async tasks cannot have `#[lock_free]` resources, as they can interleve and each async +// task can have a mutable reference stored. +// - Spawning an async task equates to it being polled once. + +#[rtic::app(device = lm3s6965, dispatchers = [SSI0, QEI0, UART0, UART1], peripherals = true)] +mod app { + use cortex_m_semihosting::{debug, hprintln}; + use systick_monotonic::*; + + #[shared] + struct Shared { + a: u32, + b: u32, + } + + #[local] + struct Local {} + + #[monotonic(binds = SysTick, default = true)] + type MyMono = Systick<100>; + + #[init] + fn init(cx: init::Context) -> (Shared, Local, init::Monotonics) { + hprintln!("init").unwrap(); + + normal_task::spawn().ok(); + async_task::spawn().ok(); + normal_task2::spawn().ok(); + async_task2::spawn().ok(); + + ( + Shared { a: 0, b: 0 }, + Local {}, + init::Monotonics(Systick::new(cx.core.SYST, 12_000_000)), + ) + } + + #[idle] + fn idle(_: idle::Context) -> ! { + // debug::exit(debug::EXIT_SUCCESS); + loop { + // hprintln!("idle"); + cortex_m::asm::wfi(); // put the MCU in sleep mode until interrupt occurs + } + } + + #[task(priority = 1, shared = [a, b])] + fn normal_task(_cx: normal_task::Context) { + hprintln!("hello from normal 1").ok(); + } + + #[task(priority = 1, shared = [a, b])] + async fn async_task(_cx: async_task::Context) { + hprintln!("hello from async 1").ok(); + + debug::exit(debug::EXIT_SUCCESS); + } + + #[task(priority = 2, shared = [a, b])] + fn normal_task2(_cx: normal_task2::Context) { + hprintln!("hello from normal 2").ok(); + } + + #[task(priority = 2, shared = [a, b])] + async fn async_task2(_cx: async_task2::Context) { + hprintln!("hello from async 2").ok(); + } +} diff --git a/examples/async-task.rs b/examples/async-task.rs new file mode 100644 index 0000000000..4d25ec4401 --- /dev/null +++ b/examples/async-task.rs @@ -0,0 +1,61 @@ +#![no_main] +#![no_std] +#![feature(type_alias_impl_trait)] + +use panic_semihosting as _; + +// NOTES: +// +// - Async tasks cannot have `#[lock_free]` resources, as they can interleve and each async +// task can have a mutable reference stored. +// - Spawning an async task equates to it being polled once. + +#[rtic::app(device = lm3s6965, dispatchers = [SSI0, UART0], peripherals = true)] +mod app { + use cortex_m_semihosting::{debug, hprintln}; + use systick_monotonic::*; + + #[shared] + struct Shared {} + + #[local] + struct Local {} + + #[monotonic(binds = SysTick, default = true)] + type MyMono = Systick<100>; + + #[init] + fn init(cx: init::Context) -> (Shared, Local, init::Monotonics) { + hprintln!("init").unwrap(); + + normal_task::spawn().ok(); + async_task::spawn().ok(); + + ( + Shared {}, + Local {}, + init::Monotonics(Systick::new(cx.core.SYST, 12_000_000)), + ) + } + + #[idle] + fn idle(_: idle::Context) -> ! { + // debug::exit(debug::EXIT_SUCCESS); + loop { + // hprintln!("idle"); + cortex_m::asm::wfi(); // put the MCU in sleep mode until interrupt occurs + } + } + + #[task] + fn normal_task(_cx: normal_task::Context) { + hprintln!("hello from normal").ok(); + } + + #[task] + async fn async_task(_cx: async_task::Context) { + hprintln!("hello from async").ok(); + + debug::exit(debug::EXIT_SUCCESS); + } +} diff --git a/examples/async-timeout.rs b/examples/async-timeout.rs new file mode 100644 index 0000000000..3f68df744d --- /dev/null +++ b/examples/async-timeout.rs @@ -0,0 +1,87 @@ +#![no_main] +#![no_std] +#![feature(type_alias_impl_trait)] + +use panic_semihosting as _; + +// NOTES: +// +// - Async tasks cannot have `#[lock_free]` resources, as they can interleve and each async +// task can have a mutable reference stored. +// - Spawning an async task equates to it being polled once. + +#[rtic::app(device = lm3s6965, dispatchers = [SSI0, UART0], peripherals = true)] +mod app { + use core::{ + future::Future, + pin::Pin, + task::{Context, Poll}, + }; + use cortex_m_semihosting::{debug, hprintln}; + use systick_monotonic::*; + + #[shared] + struct Shared {} + + #[local] + struct Local {} + + #[monotonic(binds = SysTick, default = true)] + type MyMono = Systick<100>; + + #[init] + fn init(cx: init::Context) -> (Shared, Local, init::Monotonics) { + hprintln!("init").unwrap(); + + foo::spawn().ok(); + bar::spawn().ok(); + + ( + Shared {}, + Local {}, + init::Monotonics(Systick::new(cx.core.SYST, 12_000_000)), + ) + } + + #[idle] + fn idle(_: idle::Context) -> ! { + loop { + cortex_m::asm::wfi(); // put the MCU in sleep mode until interrupt occurs + } + } + + #[task] + async fn foo(_cx: foo::Context) { + hprintln!("hello from foo").ok(); + + // This will not timeout + match monotonics::timeout_after(monotonics::delay(100.millis()), 200.millis()).await { + Ok(_) => hprintln!("foo no timeout").ok(), + Err(_) => hprintln!("foo timeout").ok(), + }; + } + + #[task] + async fn bar(_cx: bar::Context) { + hprintln!("hello from bar").ok(); + + // This will timeout + match monotonics::timeout_after(NeverEndingFuture {}, 300.millis()).await { + Ok(_) => hprintln!("bar no timeout").ok(), + Err(_) => hprintln!("bar timeout").ok(), + }; + + debug::exit(debug::EXIT_SUCCESS); + } + + pub struct NeverEndingFuture {} + + impl Future for NeverEndingFuture { + type Output = (); + + fn poll(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll { + // Never finish + Poll::Pending + } + } +} diff --git a/examples/binds.rs b/examples/binds.rs index 1b0c8c5beb..56565cbec9 100644 --- a/examples/binds.rs +++ b/examples/binds.rs @@ -2,7 +2,6 @@ #![deny(unsafe_code)] #![deny(warnings)] -#![deny(missing_docs)] #![no_main] #![no_std] @@ -24,22 +23,21 @@ mod app { fn init(_: init::Context) -> (Shared, Local, init::Monotonics) { rtic::pend(Interrupt::UART0); - hprintln!("init"); + hprintln!("init").unwrap(); (Shared {}, Local {}, init::Monotonics()) } #[idle] fn idle(_: idle::Context) -> ! { - hprintln!("idle"); + hprintln!("idle").unwrap(); rtic::pend(Interrupt::UART0); + debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator + loop { - // Exit moved after nop to ensure that rtic::pend gets - // to run before exiting cortex_m::asm::nop(); - debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator } } @@ -51,6 +49,7 @@ mod app { "foo called {} time{}", *cx.local.times, if *cx.local.times > 1 { "s" } else { "" } - ); + ) + .unwrap(); } } diff --git a/examples/cancel-reschedule.rs b/examples/cancel-reschedule.rs index 36c496b71a..a38a9c4eae 100644 --- a/examples/cancel-reschedule.rs +++ b/examples/cancel-reschedule.rs @@ -2,7 +2,6 @@ #![deny(unsafe_code)] #![deny(warnings)] -#![deny(missing_docs)] #![no_main] #![no_std] @@ -29,7 +28,7 @@ mod app { // Initialize the monotonic (SysTick rate in QEMU is 12 MHz) let mono = Systick::new(systick, 12_000_000); - hprintln!("init"); + hprintln!("init").ok(); // Schedule `foo` to run 1 second in the future foo::spawn_after(1.secs()).unwrap(); @@ -43,7 +42,7 @@ mod app { #[task] fn foo(_: foo::Context) { - hprintln!("foo"); + hprintln!("foo").ok(); // Schedule `bar` to run 2 seconds in the future (1 second after foo runs) let spawn_handle = baz::spawn_after(2.secs()).unwrap(); @@ -52,7 +51,7 @@ mod app { #[task] fn bar(_: bar::Context, baz_handle: baz::SpawnHandle, do_reschedule: bool) { - hprintln!("bar"); + hprintln!("bar").ok(); if do_reschedule { // Reschedule baz 2 seconds from now, instead of the original 1 second @@ -68,7 +67,7 @@ mod app { #[task] fn baz(_: baz::Context) { - hprintln!("baz"); + hprintln!("baz").ok(); debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator } } diff --git a/examples/capacity.rs b/examples/capacity.rs index 550829be32..a617269869 100644 --- a/examples/capacity.rs +++ b/examples/capacity.rs @@ -2,7 +2,6 @@ #![deny(unsafe_code)] #![deny(warnings)] -#![deny(missing_docs)] #![no_main] #![no_std] @@ -38,12 +37,12 @@ mod app { #[task(capacity = 4)] fn foo(_: foo::Context, x: u32) { - hprintln!("foo({})", x); + hprintln!("foo({})", x).unwrap(); } #[task] fn bar(_: bar::Context) { - hprintln!("bar"); + hprintln!("bar").unwrap(); debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator } diff --git a/examples/cfg-whole-task.rs b/examples/cfg-whole-task.rs index 17f31f4ebb..f41866db47 100644 --- a/examples/cfg-whole-task.rs +++ b/examples/cfg-whole-task.rs @@ -2,7 +2,6 @@ #![deny(unsafe_code)] #![deny(warnings)] -#![deny(missing_docs)] #![no_main] #![no_std] @@ -82,19 +81,6 @@ mod app { // .. } - // The whole task should disappear, - // currently still present in the Tasks enum - #[cfg(never)] - #[task(binds = UART1, shared = [count])] - fn foo3(mut _cx: foo3::Context) { - #[cfg(debug_assertions)] - { - _cx.shared.count.lock(|count| *count += 10); - - log::spawn(_cx.shared.count.lock(|count| *count)).unwrap(); - } - } - #[cfg(debug_assertions)] #[task(capacity = 2)] fn log(_: log::Context, n: u32) { @@ -102,6 +88,7 @@ mod app { "foo has been called {} time{}", n, if n == 1 { "" } else { "s" } - ); + ) + .ok(); } } diff --git a/examples/common.rs b/examples/common.rs index 74ee8db2c8..1fe671e61a 100644 --- a/examples/common.rs +++ b/examples/common.rs @@ -2,7 +2,6 @@ #![deny(unsafe_code)] #![deny(warnings)] -#![deny(missing_docs)] #![no_main] #![no_std] @@ -74,7 +73,7 @@ mod app { // This task is only spawned once in `init`, hence this task will run // only once - hprintln!("foo"); + hprintln!("foo").ok(); } // Software task, also not bound to a hardware interrupt @@ -82,7 +81,7 @@ mod app { // The resources `s1` and `s2` are shared between all other tasks. #[task(shared = [s1, s2], local = [l2])] fn bar(_: bar::Context) { - hprintln!("bar"); + hprintln!("bar").ok(); // Run `bar` once per second bar::spawn_after(1.secs()).unwrap(); @@ -98,6 +97,6 @@ mod app { // Note that RTIC does NOT clear the interrupt flag, this is up to the // user - hprintln!("UART0 interrupt!"); + hprintln!("UART0 interrupt!").ok(); } } diff --git a/examples/complex.rs b/examples/complex.rs index 73df025d2f..e5cf6dbea3 100644 --- a/examples/complex.rs +++ b/examples/complex.rs @@ -2,7 +2,6 @@ #![deny(unsafe_code)] #![deny(warnings)] -#![deny(missing_docs)] #![no_main] #![no_std] @@ -26,7 +25,7 @@ mod app { #[init] fn init(_: init::Context) -> (Shared, Local, init::Monotonics) { - hprintln!("init"); + hprintln!("init").unwrap(); ( Shared { @@ -41,31 +40,31 @@ mod app { #[idle(shared = [s2, s3])] fn idle(mut cx: idle::Context) -> ! { - hprintln!("idle p0 started"); + hprintln!("idle p0 started").ok(); rtic::pend(Interrupt::GPIOC); cx.shared.s3.lock(|s| { - hprintln!("idle enter lock s3 {}", s); - hprintln!("idle pend t0"); + hprintln!("idle enter lock s3 {}", s).ok(); + hprintln!("idle pend t0").ok(); rtic::pend(Interrupt::GPIOA); // t0 p2, with shared ceiling 3 - hprintln!("idle pend t1"); + hprintln!("idle pend t1").ok(); rtic::pend(Interrupt::GPIOB); // t1 p3, with shared ceiling 3 - hprintln!("idle pend t2"); + hprintln!("idle pend t2").ok(); rtic::pend(Interrupt::GPIOC); // t2 p4, no sharing - hprintln!("idle still in lock s3 {}", s); + hprintln!("idle still in lock s3 {}", s).ok(); }); - hprintln!("\nback in idle"); + hprintln!("\nback in idle").ok(); cx.shared.s2.lock(|s| { - hprintln!("enter lock s2 {}", s); - hprintln!("idle pend t0"); + hprintln!("enter lock s2 {}", s).ok(); + hprintln!("idle pend t0").ok(); rtic::pend(Interrupt::GPIOA); // t0 p2, with shared ceiling 2 - hprintln!("idle pend t1"); + hprintln!("idle pend t1").ok(); rtic::pend(Interrupt::GPIOB); // t1 p3, no sharing - hprintln!("idle pend t2"); + hprintln!("idle pend t2").ok(); rtic::pend(Interrupt::GPIOC); // t2 p4, no sharing - hprintln!("idle still in lock s2 {}", s); + hprintln!("idle still in lock s2 {}", s).ok(); }); - hprintln!("\nidle exit"); + hprintln!("\nidle exit").ok(); debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator @@ -83,8 +82,9 @@ mod app { "t0 p2 called {} time{}", *cx.local.times, if *cx.local.times > 1 { "s" } else { "" } - ); - hprintln!("t0 p2 exit"); + ) + .ok(); + hprintln!("t0 p2 exit").ok(); } #[task(binds = GPIOB, priority = 3, local = [times: u32 = 0], shared = [s3, s4])] @@ -96,18 +96,19 @@ mod app { "t1 p3 called {} time{}", *cx.local.times, if *cx.local.times > 1 { "s" } else { "" } - ); + ) + .ok(); cx.shared.s4.lock(|s| { - hprintln!("t1 enter lock s4 {}", s); - hprintln!("t1 pend t0"); + hprintln!("t1 enter lock s4 {}", s).ok(); + hprintln!("t1 pend t0").ok(); rtic::pend(Interrupt::GPIOA); // t0 p2, with shared ceiling 2 - hprintln!("t1 pend t2"); + hprintln!("t1 pend t2").ok(); rtic::pend(Interrupt::GPIOC); // t2 p4, no sharing - hprintln!("t1 still in lock s4 {}", s); + hprintln!("t1 still in lock s4 {}", s).ok(); }); - hprintln!("t1 p3 exit"); + hprintln!("t1 p3 exit").ok(); } #[task(binds = GPIOC, priority = 4, local = [times: u32 = 0], shared = [s4])] @@ -119,12 +120,13 @@ mod app { "t2 p4 called {} time{}", *cx.local.times, if *cx.local.times > 1 { "s" } else { "" } - ); + ) + .unwrap(); cx.shared.s4.lock(|s| { - hprintln!("enter lock s4 {}", s); + hprintln!("enter lock s4 {}", s).ok(); *s += 1; }); - hprintln!("t3 p4 exit"); + hprintln!("t3 p4 exit").ok(); } } diff --git a/examples/declared_locals.rs b/examples/declared_locals.rs index cb6214960f..52d354bc9a 100644 --- a/examples/declared_locals.rs +++ b/examples/declared_locals.rs @@ -2,7 +2,6 @@ #![deny(unsafe_code)] #![deny(warnings)] -#![deny(missing_docs)] #![no_main] #![no_std] diff --git a/examples/destructure.rs b/examples/destructure.rs index 70b0dd7e6f..6019c225cc 100644 --- a/examples/destructure.rs +++ b/examples/destructure.rs @@ -2,7 +2,6 @@ #![deny(unsafe_code)] #![deny(warnings)] -#![deny(missing_docs)] #![no_main] #![no_std] @@ -43,7 +42,7 @@ mod app { let b = cx.shared.b; let c = cx.shared.c; - hprintln!("foo: a = {}, b = {}, c = {}", a, b, c); + hprintln!("foo: a = {}, b = {}, c = {}", a, b, c).unwrap(); } // De-structure-ing syntax @@ -51,6 +50,6 @@ mod app { fn bar(cx: bar::Context) { let bar::SharedResources { a, b, c } = cx.shared; - hprintln!("bar: a = {}, b = {}, c = {}", a, b, c); + hprintln!("bar: a = {}, b = {}, c = {}", a, b, c).unwrap(); } } diff --git a/examples/extern_binds.rs b/examples/extern_binds.rs index bfc85cfc82..4dc6633c5d 100644 --- a/examples/extern_binds.rs +++ b/examples/extern_binds.rs @@ -2,7 +2,6 @@ #![deny(unsafe_code)] #![deny(warnings)] -#![deny(missing_docs)] #![no_main] #![no_std] @@ -11,7 +10,7 @@ use panic_semihosting as _; // Free function implementing the interrupt bound task `foo`. fn foo(_: app::foo::Context) { - hprintln!("foo called"); + hprintln!("foo called").ok(); } #[rtic::app(device = lm3s6965)] @@ -30,22 +29,21 @@ mod app { fn init(_: init::Context) -> (Shared, Local, init::Monotonics) { rtic::pend(Interrupt::UART0); - hprintln!("init"); + hprintln!("init").unwrap(); (Shared {}, Local {}, init::Monotonics()) } #[idle] fn idle(_: idle::Context) -> ! { - hprintln!("idle"); + hprintln!("idle").unwrap(); rtic::pend(Interrupt::UART0); + debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator + loop { cortex_m::asm::nop(); - // Exit moved after nop to ensure that rtic::pend gets - // to run before exiting - debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator } } diff --git a/examples/extern_spawn.rs b/examples/extern_spawn.rs index 446d31a77b..7f9b5a5f9b 100644 --- a/examples/extern_spawn.rs +++ b/examples/extern_spawn.rs @@ -2,7 +2,6 @@ #![deny(unsafe_code)] #![deny(warnings)] -#![deny(missing_docs)] #![no_main] #![no_std] @@ -11,7 +10,7 @@ use panic_semihosting as _; // Free function implementing the spawnable task `foo`. fn foo(_c: app::foo::Context, x: i32, y: u32) { - hprintln!("foo {}, {}", x, y); + hprintln!("foo {}, {}", x, y).unwrap(); if x == 2 { debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator } diff --git a/examples/generics.rs b/examples/generics.rs index bc4959fb7b..72b861ba91 100644 --- a/examples/generics.rs +++ b/examples/generics.rs @@ -2,7 +2,6 @@ #![deny(unsafe_code)] #![deny(warnings)] -#![deny(missing_docs)] #![no_main] #![no_std] @@ -33,22 +32,19 @@ mod app { #[task(binds = UART0, shared = [shared], local = [state: u32 = 0])] fn uart0(c: uart0::Context) { - hprintln!("UART0(STATE = {})", *c.local.state); + hprintln!("UART0(STATE = {})", *c.local.state).unwrap(); // second argument has type `shared::shared` super::advance(c.local.state, c.shared.shared); rtic::pend(Interrupt::UART1); - // Exit moved after nop to ensure that rtic::pend gets - // to run before exiting - cortex_m::asm::nop(); debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator } #[task(binds = UART1, priority = 2, shared = [shared], local = [state: u32 = 0])] fn uart1(c: uart1::Context) { - hprintln!("UART1(STATE = {})", *c.local.state); + hprintln!("UART1(STATE = {})", *c.local.state).unwrap(); // second argument has type `shared::shared` super::advance(c.local.state, c.shared.shared); @@ -65,5 +61,5 @@ fn advance(state: &mut u32, mut shared: impl Mutex) { (old, *shared) }); - hprintln!("shared: {} -> {}", old, new); + hprintln!("shared: {} -> {}", old, new).unwrap(); } diff --git a/examples/hardware.rs b/examples/hardware.rs index a7fdb47a37..60632247fb 100644 --- a/examples/hardware.rs +++ b/examples/hardware.rs @@ -2,7 +2,6 @@ #![deny(unsafe_code)] #![deny(warnings)] -#![deny(missing_docs)] #![no_main] #![no_std] @@ -25,7 +24,7 @@ mod app { // `init` returns because interrupts are disabled rtic::pend(Interrupt::UART0); // equivalent to NVIC::pend - hprintln!("init"); + hprintln!("init").unwrap(); (Shared {}, Local {}, init::Monotonics()) } @@ -34,15 +33,14 @@ mod app { fn idle(_: idle::Context) -> ! { // interrupts are enabled again; the `UART0` handler runs at this point - hprintln!("idle"); + hprintln!("idle").unwrap(); rtic::pend(Interrupt::UART0); + debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator + loop { - // Exit moved after nop to ensure that rtic::pend gets - // to run before exiting cortex_m::asm::nop(); - debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator } } @@ -55,6 +53,7 @@ mod app { "UART0 called {} time{}", *cx.local.times, if *cx.local.times > 1 { "s" } else { "" } - ); + ) + .unwrap(); } } diff --git a/examples/idle-wfi.rs b/examples/idle-wfi.rs index 5e52620d45..4a8a8dee2b 100644 --- a/examples/idle-wfi.rs +++ b/examples/idle-wfi.rs @@ -2,7 +2,6 @@ #![deny(unsafe_code)] #![deny(warnings)] -#![deny(missing_docs)] #![no_main] #![no_std] @@ -20,7 +19,7 @@ mod app { #[init] fn init(mut cx: init::Context) -> (Shared, Local, init::Monotonics) { - hprintln!("init"); + hprintln!("init").unwrap(); // Set the ARM SLEEPONEXIT bit to go to sleep after handling interrupts // See https://developer.arm.com/docs/100737/0100/power-management/sleep-mode/sleep-on-exit-bit @@ -34,7 +33,7 @@ mod app { // Locals in idle have lifetime 'static let _x: &'static mut u32 = cx.local.x; - hprintln!("idle"); + hprintln!("idle").unwrap(); debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator diff --git a/examples/idle.rs b/examples/idle.rs index ccec9bf273..55d6b15352 100644 --- a/examples/idle.rs +++ b/examples/idle.rs @@ -2,7 +2,6 @@ #![deny(unsafe_code)] #![deny(warnings)] -#![deny(missing_docs)] #![no_main] #![no_std] @@ -20,7 +19,7 @@ mod app { #[init] fn init(_: init::Context) -> (Shared, Local, init::Monotonics) { - hprintln!("init"); + hprintln!("init").unwrap(); (Shared {}, Local {}, init::Monotonics()) } @@ -30,7 +29,7 @@ mod app { // Locals in idle have lifetime 'static let _x: &'static mut u32 = cx.local.x; - hprintln!("idle"); + hprintln!("idle").unwrap(); debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator diff --git a/examples/init.rs b/examples/init.rs index afd3b98ce9..b8a5bc5b98 100644 --- a/examples/init.rs +++ b/examples/init.rs @@ -2,7 +2,6 @@ #![deny(unsafe_code)] #![deny(warnings)] -#![deny(missing_docs)] #![no_main] #![no_std] @@ -33,7 +32,7 @@ mod app { // to indicate that this is a critical seciton let _cs_token: bare_metal::CriticalSection = cx.cs; - hprintln!("init"); + hprintln!("init").unwrap(); debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator diff --git a/examples/locals.rs b/examples/locals.rs index 9e112be4b3..aa5d0fee30 100644 --- a/examples/locals.rs +++ b/examples/locals.rs @@ -2,8 +2,6 @@ #![deny(unsafe_code)] #![deny(warnings)] -#![deny(missing_docs)] -#![deny(missing_docs)] #![no_main] #![no_std] @@ -18,11 +16,8 @@ mod app { #[local] struct Local { - /// Local foo local_to_foo: i64, - /// Local bar local_to_bar: i64, - /// Local idle local_to_idle: i64, } @@ -50,7 +45,7 @@ mod app { let local_to_idle = cx.local.local_to_idle; *local_to_idle += 1; - hprintln!("idle: local_to_idle = {}", local_to_idle); + hprintln!("idle: local_to_idle = {}", local_to_idle).unwrap(); debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator @@ -74,7 +69,7 @@ mod app { // error: no `local_to_bar` field in `foo::LocalResources` // cx.local.local_to_bar += 1; - hprintln!("foo: local_to_foo = {}", local_to_foo); + hprintln!("foo: local_to_foo = {}", local_to_foo).unwrap(); } // `local_to_bar` can only be accessed from this context @@ -86,6 +81,6 @@ mod app { // error: no `local_to_foo` field in `bar::LocalResources` // cx.local.local_to_foo += 1; - hprintln!("bar: local_to_bar = {}", local_to_bar); + hprintln!("bar: local_to_bar = {}", local_to_bar).unwrap(); } } diff --git a/examples/lock-free.rs b/examples/lock-free.rs index 6e5faadbf2..ea6ff1bf37 100644 --- a/examples/lock-free.rs +++ b/examples/lock-free.rs @@ -2,7 +2,6 @@ #![deny(unsafe_code)] #![deny(warnings)] -#![deny(missing_docs)] #![no_main] #![no_std] @@ -34,7 +33,7 @@ mod app { *c.shared.counter += 1; // <- no lock API required let counter = *c.shared.counter; - hprintln!(" foo = {}", counter); + hprintln!(" foo = {}", counter).unwrap(); } #[task(shared = [counter])] // <- same priority @@ -43,7 +42,7 @@ mod app { *c.shared.counter += 1; // <- no lock API required let counter = *c.shared.counter; - hprintln!(" bar = {}", counter); + hprintln!(" bar = {}", counter).unwrap(); debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator } diff --git a/examples/lock.rs b/examples/lock.rs index 5b3e0bcc3c..f1a16968ce 100644 --- a/examples/lock.rs +++ b/examples/lock.rs @@ -2,7 +2,6 @@ #![deny(unsafe_code)] #![deny(warnings)] -#![deny(missing_docs)] #![no_main] #![no_std] @@ -30,7 +29,7 @@ mod app { // when omitted priority is assumed to be `1` #[task(shared = [shared])] fn foo(mut c: foo::Context) { - hprintln!("A"); + hprintln!("A").unwrap(); // the lower priority task requires a critical section to access the data c.shared.shared.lock(|shared| { @@ -40,7 +39,7 @@ mod app { // bar will *not* run right now due to the critical section bar::spawn().unwrap(); - hprintln!("B - shared = {}", *shared); + hprintln!("B - shared = {}", *shared).unwrap(); // baz does not contend for `shared` so it's allowed to run now baz::spawn().unwrap(); @@ -48,7 +47,7 @@ mod app { // critical section is over: bar can now start - hprintln!("E"); + hprintln!("E").unwrap(); debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator } @@ -62,11 +61,11 @@ mod app { *shared }); - hprintln!("D - shared = {}", shared); + hprintln!("D - shared = {}", shared).unwrap(); } #[task(priority = 3)] fn baz(_: baz::Context) { - hprintln!("C"); + hprintln!("C").unwrap(); } } diff --git a/examples/message.rs b/examples/message.rs index 8a6a12d5f4..76c5675aaa 100644 --- a/examples/message.rs +++ b/examples/message.rs @@ -2,7 +2,6 @@ #![deny(unsafe_code)] #![deny(warnings)] -#![deny(missing_docs)] #![no_main] #![no_std] @@ -27,7 +26,7 @@ mod app { #[task(local = [count: u32 = 0])] fn foo(cx: foo::Context) { - hprintln!("foo"); + hprintln!("foo").unwrap(); bar::spawn(*cx.local.count).unwrap(); *cx.local.count += 1; @@ -35,14 +34,14 @@ mod app { #[task] fn bar(_: bar::Context, x: u32) { - hprintln!("bar({})", x); + hprintln!("bar({})", x).unwrap(); baz::spawn(x + 1, x + 2).unwrap(); } #[task] fn baz(_: baz::Context, x: u32, y: u32) { - hprintln!("baz({}, {})", x, y); + hprintln!("baz({}, {})", x, y).unwrap(); if x + y > 4 { debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator diff --git a/examples/message_passing.rs b/examples/message_passing.rs index 9550a5010a..ffa9537127 100644 --- a/examples/message_passing.rs +++ b/examples/message_passing.rs @@ -2,7 +2,6 @@ #![deny(unsafe_code)] #![deny(warnings)] -#![deny(missing_docs)] #![no_main] #![no_std] @@ -30,7 +29,7 @@ mod app { #[task(capacity = 3)] fn foo(_c: foo::Context, x: i32, y: u32) { - hprintln!("foo {}, {}", x, y); + hprintln!("foo {}, {}", x, y).unwrap(); if x == 2 { debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator } diff --git a/examples/multilock.rs b/examples/multilock.rs index c7085cd51c..d99bae695e 100644 --- a/examples/multilock.rs +++ b/examples/multilock.rs @@ -2,7 +2,6 @@ #![deny(unsafe_code)] #![deny(warnings)] -#![deny(missing_docs)] #![no_main] #![no_std] @@ -49,7 +48,7 @@ mod app { *s2 += 1; *s3 += 1; - hprintln!("Multiple locks, s1: {}, s2: {}, s3: {}", *s1, *s2, *s3); + hprintln!("Multiple locks, s1: {}, s2: {}, s3: {}", *s1, *s2, *s3).unwrap(); }); debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator diff --git a/examples/not-sync.rs b/examples/not-sync.rs index 68af04a6c6..aa79ad5626 100644 --- a/examples/not-sync.rs +++ b/examples/not-sync.rs @@ -2,16 +2,13 @@ // #![deny(unsafe_code)] #![deny(warnings)] -#![deny(missing_docs)] #![no_main] #![no_std] use core::marker::PhantomData; use panic_semihosting as _; -/// Not sync pub struct NotSync { - /// Phantom action _0: PhantomData<*const ()>, } @@ -25,7 +22,6 @@ mod app { #[shared] struct Shared { - /// This resource is not Sync shared: NotSync, } diff --git a/examples/only-shared-access.rs b/examples/only-shared-access.rs index b32827abf2..8b0a77ef8c 100644 --- a/examples/only-shared-access.rs +++ b/examples/only-shared-access.rs @@ -2,7 +2,6 @@ #![deny(unsafe_code)] #![deny(warnings)] -#![deny(missing_docs)] #![no_main] #![no_std] @@ -31,13 +30,13 @@ mod app { #[task(shared = [&key])] fn foo(cx: foo::Context) { let key: &u32 = cx.shared.key; - hprintln!("foo(key = {:#x})", key); + hprintln!("foo(key = {:#x})", key).unwrap(); debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator } #[task(priority = 2, shared = [&key])] fn bar(cx: bar::Context) { - hprintln!("bar(key = {:#x})", cx.shared.key); + hprintln!("bar(key = {:#x})", cx.shared.key).unwrap(); } } diff --git a/examples/periodic-at.rs b/examples/periodic-at.rs index ad8a5496f2..ca68ed5eb9 100644 --- a/examples/periodic-at.rs +++ b/examples/periodic-at.rs @@ -2,7 +2,6 @@ #![deny(unsafe_code)] #![deny(warnings)] -#![deny(missing_docs)] #![no_main] #![no_std] @@ -36,15 +35,15 @@ mod app { #[task(local = [cnt: u32 = 0])] fn foo(cx: foo::Context, instant: fugit::TimerInstantU64<100>) { - hprintln!("foo {:?}", instant); + hprintln!("foo {:?}", instant).ok(); *cx.local.cnt += 1; if *cx.local.cnt == 4 { debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator } - // Periodic ever 1 seconds - let next_instant = instant + 1.secs(); + // Periodic every 100 milliseconds + let next_instant = instant + 100.millis(); foo::spawn_at(next_instant, next_instant).unwrap(); } } diff --git a/examples/periodic-at2.rs b/examples/periodic-at2.rs index 4719bdb7e4..ec9adcc50c 100644 --- a/examples/periodic-at2.rs +++ b/examples/periodic-at2.rs @@ -2,7 +2,6 @@ #![deny(unsafe_code)] #![deny(warnings)] -#![deny(missing_docs)] #![no_main] #![no_std] @@ -29,7 +28,7 @@ mod app { // Initialize the monotonic (SysTick rate in QEMU is 12 MHz) let mut mono = Systick::new(systick, 12_000_000); - foo::spawn_after(1.secs(), mono.now()).unwrap(); + foo::spawn_after(200.millis(), mono.now()).unwrap(); (Shared {}, Local {}, init::Monotonics(mono)) } @@ -37,7 +36,7 @@ mod app { // Using the explicit type of the timer implementation #[task(local = [cnt: u32 = 0])] fn foo(cx: foo::Context, instant: fugit::TimerInstantU64<100>) { - hprintln!("foo {:?}", instant); + hprintln!("foo {:?}", instant).ok(); *cx.local.cnt += 1; if *cx.local.cnt == 4 { @@ -53,10 +52,10 @@ mod app { // This remains agnostic to the timer implementation #[task(local = [cnt: u32 = 0])] fn bar(_cx: bar::Context, instant: ::Instant) { - hprintln!("bar {:?}", instant); + hprintln!("bar {:?}", instant).ok(); - // Spawn a new message with 1s offset to spawned time - let next_instant = instant + 1.secs(); + // Spawn a new message with 200ms offset to spawned time + let next_instant = instant + 200.millis(); foo::spawn_at(next_instant, next_instant).unwrap(); } } diff --git a/examples/periodic.rs b/examples/periodic.rs index 13ca7c852c..2f9e8e6a64 100644 --- a/examples/periodic.rs +++ b/examples/periodic.rs @@ -2,7 +2,6 @@ #![deny(unsafe_code)] #![deny(warnings)] -#![deny(missing_docs)] #![no_main] #![no_std] @@ -29,21 +28,21 @@ mod app { // Initialize the monotonic (SysTick rate in QEMU is 12 MHz) let mono = Systick::new(systick, 12_000_000); - foo::spawn_after(1.secs()).unwrap(); + foo::spawn_after(100.millis()).unwrap(); (Shared {}, Local {}, init::Monotonics(mono)) } #[task(local = [cnt: u32 = 0])] fn foo(cx: foo::Context) { - hprintln!("foo"); + hprintln!("foo").ok(); *cx.local.cnt += 1; if *cx.local.cnt == 4 { debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator } - // Periodic ever 1 seconds - foo::spawn_after(1.secs()).unwrap(); + // Periodic every 100ms + foo::spawn_after(100.millis()).unwrap(); } } diff --git a/examples/peripherals-taken.rs b/examples/peripherals-taken.rs index cc9b9a11ce..d542c0e64d 100644 --- a/examples/peripherals-taken.rs +++ b/examples/peripherals-taken.rs @@ -1,7 +1,5 @@ -//! examples/peripherals-taken.rs -#![deny(warnings)] #![deny(unsafe_code)] -#![deny(missing_docs)] +#![deny(warnings)] #![no_main] #![no_std] diff --git a/examples/pool.rs b/examples/pool.rs index 4c551bef06..5aadd24cd4 100644 --- a/examples/pool.rs +++ b/examples/pool.rs @@ -2,8 +2,6 @@ #![deny(unsafe_code)] #![deny(warnings)] -// pool!() generates a struct without docs -//#![deny(missing_docs)] #![no_main] #![no_std] diff --git a/examples/preempt.rs b/examples/preempt.rs index 3c7f242990..d0c8cc7d3f 100644 --- a/examples/preempt.rs +++ b/examples/preempt.rs @@ -25,21 +25,21 @@ mod app { #[task(priority = 1)] fn foo(_: foo::Context) { - hprintln!("foo - start"); + hprintln!("foo - start").unwrap(); baz::spawn().unwrap(); - hprintln!("foo - end"); + hprintln!("foo - end").unwrap(); debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator } #[task(priority = 2)] fn bar(_: bar::Context) { - hprintln!(" bar"); + hprintln!(" bar").unwrap(); } #[task(priority = 2)] fn baz(_: baz::Context) { - hprintln!(" baz - start"); + hprintln!(" baz - start").unwrap(); bar::spawn().unwrap(); - hprintln!(" baz - end"); + hprintln!(" baz - end").unwrap(); } } diff --git a/examples/ramfunc.rs b/examples/ramfunc.rs index 956a2554d8..b3b8012c38 100644 --- a/examples/ramfunc.rs +++ b/examples/ramfunc.rs @@ -1,7 +1,6 @@ //! examples/ramfunc.rs #![deny(warnings)] -#![deny(missing_docs)] #![no_main] #![no_std] @@ -34,7 +33,7 @@ mod app { #[inline(never)] #[task] fn foo(_: foo::Context) { - hprintln!("foo"); + hprintln!("foo").unwrap(); debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator } diff --git a/examples/resource-user-struct.rs b/examples/resource-user-struct.rs index 37a885609f..ae1918d05d 100644 --- a/examples/resource-user-struct.rs +++ b/examples/resource-user-struct.rs @@ -2,7 +2,6 @@ #![deny(unsafe_code)] #![deny(warnings)] -#![deny(missing_docs)] #![no_main] #![no_std] @@ -56,7 +55,7 @@ mod app { *shared }); - hprintln!("UART0: shared = {}", shared); + hprintln!("UART0: shared = {}", shared).unwrap(); } // `shared` can be accessed from this context @@ -67,6 +66,6 @@ mod app { *shared }); - hprintln!("UART1: shared = {}", shared); + hprintln!("UART1: shared = {}", shared).unwrap(); } } diff --git a/examples/schedule.rs b/examples/schedule.rs index 9b86929d93..5bad5a30ad 100644 --- a/examples/schedule.rs +++ b/examples/schedule.rs @@ -2,7 +2,6 @@ #![deny(unsafe_code)] #![deny(warnings)] -#![deny(missing_docs)] #![no_main] #![no_std] @@ -29,7 +28,7 @@ mod app { // Initialize the monotonic (SysTick rate in QEMU is 12 MHz) let mono = Systick::new(systick, 12_000_000); - hprintln!("init"); + hprintln!("init").ok(); // Schedule `foo` to run 1 second in the future foo::spawn_after(1.secs()).unwrap(); @@ -43,7 +42,7 @@ mod app { #[task] fn foo(_: foo::Context) { - hprintln!("foo"); + hprintln!("foo").ok(); // Schedule `bar` to run 2 seconds in the future (1 second after foo runs) bar::spawn_after(1.secs()).unwrap(); @@ -51,7 +50,7 @@ mod app { #[task] fn bar(_: bar::Context) { - hprintln!("bar"); + hprintln!("bar").ok(); // Schedule `baz` to run 1 seconds from now, but with a specific time instant. baz::spawn_at(monotonics::now() + 1.secs()).unwrap(); @@ -59,7 +58,7 @@ mod app { #[task] fn baz(_: baz::Context) { - hprintln!("baz"); + hprintln!("baz").ok(); debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator } } diff --git a/examples/shared.rs b/examples/shared.rs index b43a19a3c5..d87dca5263 100644 --- a/examples/shared.rs +++ b/examples/shared.rs @@ -2,7 +2,6 @@ #![deny(unsafe_code)] #![deny(warnings)] -#![deny(missing_docs)] #![no_main] #![no_std] @@ -16,9 +15,7 @@ mod app { #[shared] struct Shared { - /// Producer p: Producer<'static, u32, 5>, - /// Consumer c: Consumer<'static, u32, 5>, } @@ -37,7 +34,7 @@ mod app { fn idle(mut c: idle::Context) -> ! { loop { if let Some(byte) = c.shared.c.lock(|c| c.dequeue()) { - hprintln!("received message: {}", byte); + hprintln!("received message: {}", byte).unwrap(); debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator } else { diff --git a/examples/spawn.rs b/examples/spawn.rs index 50ae7e7a75..2db1ab8a28 100644 --- a/examples/spawn.rs +++ b/examples/spawn.rs @@ -2,7 +2,6 @@ #![deny(unsafe_code)] #![deny(warnings)] -#![deny(missing_docs)] #![no_main] #![no_std] @@ -20,7 +19,7 @@ mod app { #[init] fn init(_: init::Context) -> (Shared, Local, init::Monotonics) { - hprintln!("init"); + hprintln!("init").unwrap(); foo::spawn().unwrap(); (Shared {}, Local {}, init::Monotonics()) @@ -28,7 +27,7 @@ mod app { #[task] fn foo(_: foo::Context) { - hprintln!("foo"); + hprintln!("foo").unwrap(); debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator } diff --git a/examples/static.rs b/examples/static.rs index efafcc7aa8..c9aa6046b5 100644 --- a/examples/static.rs +++ b/examples/static.rs @@ -2,7 +2,6 @@ #![deny(unsafe_code)] #![deny(warnings)] -#![deny(missing_docs)] #![no_main] #![no_std] @@ -38,7 +37,7 @@ mod app { loop { // Lock-free access to the same underlying queue! if let Some(data) = c.local.c.dequeue() { - hprintln!("received message: {}", data); + hprintln!("received message: {}", data).unwrap(); // Run foo until data if data == 3 { diff --git a/examples/t-binds.rs b/examples/t-binds.rs index 822a2eeabb..12479c0ad4 100644 --- a/examples/t-binds.rs +++ b/examples/t-binds.rs @@ -2,7 +2,6 @@ #![deny(unsafe_code)] #![deny(warnings)] -#![deny(missing_docs)] #![no_main] #![no_std] diff --git a/examples/t-htask-main.rs b/examples/t-htask-main.rs index 2b17b2ee02..37189faf76 100644 --- a/examples/t-htask-main.rs +++ b/examples/t-htask-main.rs @@ -1,7 +1,5 @@ -//! examples/t-htask-main.rs #![deny(unsafe_code)] #![deny(warnings)] -#![deny(missing_docs)] #![no_main] #![no_std] diff --git a/examples/t-idle-main.rs b/examples/t-idle-main.rs index 48635b2ab2..1adc9bf044 100644 --- a/examples/t-idle-main.rs +++ b/examples/t-idle-main.rs @@ -1,7 +1,5 @@ -//! examples/t-idle-main.rs #![deny(unsafe_code)] #![deny(warnings)] -#![deny(missing_docs)] #![no_main] #![no_std] diff --git a/examples/t-schedule.rs b/examples/t-schedule.rs index f3979dd62f..5ec420873d 100644 --- a/examples/t-schedule.rs +++ b/examples/t-schedule.rs @@ -2,7 +2,6 @@ #![deny(unsafe_code)] #![deny(warnings)] -#![deny(missing_docs)] #![no_main] #![no_std] diff --git a/examples/t-spawn.rs b/examples/t-spawn.rs index 7483a8494b..2bd771d7f6 100644 --- a/examples/t-spawn.rs +++ b/examples/t-spawn.rs @@ -2,7 +2,6 @@ #![deny(unsafe_code)] #![deny(warnings)] -#![deny(missing_docs)] #![no_main] #![no_std] diff --git a/examples/task.rs b/examples/task.rs index 9757f2f559..2c53aa2359 100644 --- a/examples/task.rs +++ b/examples/task.rs @@ -2,7 +2,6 @@ #![deny(unsafe_code)] #![deny(warnings)] -#![deny(missing_docs)] #![no_main] #![no_std] @@ -27,31 +26,31 @@ mod app { #[task] fn foo(_: foo::Context) { - hprintln!("foo - start"); + hprintln!("foo - start").unwrap(); // spawns `bar` onto the task scheduler // `foo` and `bar` have the same priority so `bar` will not run until // after `foo` terminates bar::spawn().unwrap(); - hprintln!("foo - middle"); + hprintln!("foo - middle").unwrap(); // spawns `baz` onto the task scheduler // `baz` has higher priority than `foo` so it immediately preempts `foo` baz::spawn().unwrap(); - hprintln!("foo - end"); + hprintln!("foo - end").unwrap(); } #[task] fn bar(_: bar::Context) { - hprintln!("bar"); + hprintln!("bar").unwrap(); debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator } #[task(priority = 2)] fn baz(_: baz::Context) { - hprintln!("baz"); + hprintln!("baz").unwrap(); } } diff --git a/macros/src/codegen/local_resources_struct.rs b/macros/src/codegen/local_resources_struct.rs index 309fd8d253..6bcf4fadc8 100644 --- a/macros/src/codegen/local_resources_struct.rs +++ b/macros/src/codegen/local_resources_struct.rs @@ -37,7 +37,6 @@ pub fn codegen(ctxt: Context, needs_lt: &mut bool, app: &App) -> (TokenStream2, (&r.cfgs, &r.ty, false) } TaskLocal::Declared(r) => (&r.cfgs, &r.ty, true), - _ => unreachable!(), }; has_cfgs |= !cfgs.is_empty(); diff --git a/macros/src/syntax.rs b/macros/src/syntax.rs index 11b92c1b9d..09b2ab3d2c 100644 --- a/macros/src/syntax.rs +++ b/macros/src/syntax.rs @@ -1,7 +1,6 @@ #[allow(unused_extern_crates)] extern crate proc_macro; -use core::ops; use proc_macro::TokenStream; use indexmap::{IndexMap, IndexSet}; @@ -23,26 +22,6 @@ pub type Map = IndexMap; /// An order set pub type Set = IndexSet; -/// Immutable pointer -pub struct P { - ptr: Box, -} - -impl P { - /// Boxes `x` making the value immutable - pub fn new(x: T) -> P { - P { ptr: Box::new(x) } - } -} - -impl ops::Deref for P { - type Target = T; - - fn deref(&self) -> &T { - &self.ptr - } -} - /// Execution context #[derive(Clone, Copy)] pub enum Context<'a> { diff --git a/macros/src/syntax/analyze.rs b/macros/src/syntax/analyze.rs index 06b23f4685..44960b9e8a 100644 --- a/macros/src/syntax/analyze.rs +++ b/macros/src/syntax/analyze.rs @@ -338,8 +338,8 @@ pub(crate) fn app(app: &App) -> Result { }) } -/// Priority ceiling -pub type Ceiling = Option; +// /// Priority ceiling +// pub type Ceiling = Option; /// Task priority pub type Priority = u8; @@ -427,22 +427,22 @@ pub enum Ownership { }, } -impl Ownership { - /// Whether this resource needs to a lock at this priority level - pub fn needs_lock(&self, priority: u8) -> bool { - match self { - Ownership::Owned { .. } | Ownership::CoOwned { .. } => false, - - Ownership::Contended { ceiling } => { - debug_assert!(*ceiling >= priority); - - priority < *ceiling - } - } - } - - /// Whether this resource is exclusively owned - pub fn is_owned(&self) -> bool { - matches!(self, Ownership::Owned { .. }) - } -} +// impl Ownership { +// /// Whether this resource needs to a lock at this priority level +// pub fn needs_lock(&self, priority: u8) -> bool { +// match self { +// Ownership::Owned { .. } | Ownership::CoOwned { .. } => false, +// +// Ownership::Contended { ceiling } => { +// debug_assert!(*ceiling >= priority); +// +// priority < *ceiling +// } +// } +// } +// +// /// Whether this resource is exclusively owned +// pub fn is_owned(&self) -> bool { +// matches!(self, Ownership::Owned { .. }) +// } +// } diff --git a/src/export.rs b/src/export.rs index 6f2a1b63c1..da4a6917b4 100644 --- a/src/export.rs +++ b/src/export.rs @@ -1,11 +1,13 @@ #![allow(clippy::inline_always)] +pub use crate::{ + sll::{IntrusiveSortedLinkedList, Node as IntrusiveNode}, + tq::{TaskNotReady, TimerQueue, WakerNotReady}, +}; +pub use bare_metal::CriticalSection; use core::{ cell::Cell, sync::atomic::{AtomicBool, Ordering}, }; - -pub use crate::tq::{NotReady, TimerQueue}; -pub use bare_metal::CriticalSection; pub use cortex_m::{ asm::nop, asm::wfi, @@ -16,10 +18,134 @@ pub use cortex_m::{ pub use heapless::sorted_linked_list::SortedLinkedList; pub use heapless::spsc::Queue; pub use heapless::BinaryHeap; +pub use heapless::Vec; pub use rtic_monotonic as monotonic; +pub mod idle_executor { + use core::{ + future::Future, + pin::Pin, + task::{Context, Poll, RawWaker, RawWakerVTable, Waker}, + }; + + fn no_op(_: *const ()) {} + fn no_op_clone(_: *const ()) -> RawWaker { + noop_raw_waker() + } + + static IDLE_WAKER_TABLE: RawWakerVTable = RawWakerVTable::new(no_op_clone, no_op, no_op, no_op); + + #[inline] + fn noop_raw_waker() -> RawWaker { + RawWaker::new(core::ptr::null(), &IDLE_WAKER_TABLE) + } + + pub struct IdleExecutor + where + T: Future, + { + idle: T, + } + + impl IdleExecutor + where + T: Future, + { + #[inline(always)] + pub fn new(idle: T) -> Self { + Self { idle } + } + + #[inline(always)] + pub fn run(&mut self) -> ! { + let w = unsafe { Waker::from_raw(noop_raw_waker()) }; + let mut ctxt = Context::from_waker(&w); + loop { + match unsafe { Pin::new_unchecked(&mut self.idle) }.poll(&mut ctxt) { + Poll::Pending => { + // All ok! + } + Poll::Ready(_) => { + // The idle executor will never return + unreachable!() + } + } + } + } + } +} + +pub mod executor { + use core::{ + future::Future, + mem, + pin::Pin, + task::{Context, Poll, RawWaker, RawWakerVTable, Waker}, + }; + + static WAKER_VTABLE: RawWakerVTable = + RawWakerVTable::new(waker_clone, waker_wake, waker_wake, waker_drop); + + unsafe fn waker_clone(p: *const ()) -> RawWaker { + RawWaker::new(p, &WAKER_VTABLE) + } + + unsafe fn waker_wake(p: *const ()) { + // The only thing we need from a waker is the function to call to pend the async + // dispatcher. + let f: fn() = mem::transmute(p); + f(); + } + + unsafe fn waker_drop(_: *const ()) { + // nop + } + + //============ + // AsyncTaskExecutor + + pub struct AsyncTaskExecutor { + task: Option, + } + + impl AsyncTaskExecutor { + pub const fn new() -> Self { + Self { task: None } + } + + pub fn is_running(&self) -> bool { + self.task.is_some() + } + + pub fn spawn(&mut self, future: F) { + self.task = Some(future); + } + + pub fn poll(&mut self, wake: fn()) -> bool { + if let Some(future) = &mut self.task { + unsafe { + let waker = Waker::from_raw(RawWaker::new(wake as *const (), &WAKER_VTABLE)); + let mut cx = Context::from_waker(&waker); + let future = Pin::new_unchecked(future); + + match future.poll(&mut cx) { + Poll::Ready(_) => { + self.task = None; + true // Only true if we finished now + } + Poll::Pending => false, + } + } + } else { + false + } + } + } +} + pub type SCFQ = Queue; pub type SCRQ = Queue<(T, u8), N>; +pub type ASYNCRQ = Queue; /// Mask is used to store interrupt masks on systems without a BASEPRI register (M0, M0+, M23). /// It needs to be large enough to cover all the relevant interrupts in use. @@ -117,7 +243,7 @@ impl Priority { /// /// Will overwrite the current Priority #[inline(always)] - pub unsafe fn new(value: u8) -> Self { + pub const unsafe fn new(value: u8) -> Self { Priority { inner: Cell::new(value), } diff --git a/src/lib.rs b/src/lib.rs index 7d12d9af81..da556a5c49 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,14 +1,125 @@ -pub fn add(left: usize, right: usize) -> usize { - left + right +//! Real-Time Interrupt-driven Concurrency (RTIC) framework for ARM Cortex-M microcontrollers. +//! +//! **IMPORTANT**: This crate is published as [`cortex-m-rtic`] on crates.io but the name of the +//! library is `rtic`. +//! +//! [`cortex-m-rtic`]: https://crates.io/crates/cortex-m-rtic +//! +//! The user level documentation can be found [here]. +//! +//! [here]: https://rtic.rs +//! +//! Don't forget to check the documentation of the `#[app]` attribute (listed under the reexports +//! section), which is the main component of the framework. +//! +//! # Minimum Supported Rust Version (MSRV) +//! +//! This crate is compiled and tested with the latest toolchain (rolling) as of the release date. +//! If you run into compilation errors, try the latest stable release of the rust toolchain. +//! +//! # Semantic Versioning +//! +//! Like the Rust project, this crate adheres to [SemVer]: breaking changes in the API and semantics +//! require a *semver bump* (since 1.0.0 a new major version release), with the exception of breaking changes +//! that fix soundness issues -- those are considered bug fixes and can be landed in a new patch +//! release. +//! +//! [SemVer]: https://semver.org/spec/v2.0.0.html + +#![deny(missing_docs)] +#![deny(rust_2021_compatibility)] +#![deny(rust_2018_compatibility)] +#![deny(rust_2018_idioms)] +#![no_std] +#![doc( + html_logo_url = "https://raw.githubusercontent.com/rtic-rs/cortex-m-rtic/master/book/en/src/RTIC.svg", + html_favicon_url = "https://raw.githubusercontent.com/rtic-rs/cortex-m-rtic/master/book/en/src/RTIC.svg" +)] +//deny_warnings_placeholder_for_ci +#![allow(clippy::inline_always)] + +use cortex_m::{interrupt::InterruptNumber, peripheral::NVIC}; +pub use rtic_core::{prelude as mutex_prelude, Exclusive, Mutex}; +pub use rtic_macros::app; +pub use rtic_monotonic::{self, Monotonic}; + +/// module `mutex::prelude` provides `Mutex` and multi-lock variants. Recommended over `mutex_prelude` +pub mod mutex { + pub use rtic_core::prelude; + pub use rtic_core::Mutex; } -#[cfg(test)] -mod tests { - use super::*; +#[doc(hidden)] +pub mod export; +#[doc(hidden)] +pub mod sll; +#[doc(hidden)] +mod tq; - #[test] - fn it_works() { - let result = add(2, 2); - assert_eq!(result, 4); +/// Sets the given `interrupt` as pending +/// +/// This is a convenience function around +/// [`NVIC::pend`](../cortex_m/peripheral/struct.NVIC.html#method.pend) +pub fn pend(interrupt: I) +where + I: InterruptNumber, +{ + NVIC::pend(interrupt); +} + +use core::cell::UnsafeCell; + +/// Internal replacement for `static mut T` +/// +/// Used to represent RTIC Resources +/// +/// Soundness: +/// 1) Unsafe API for internal use only +/// 2) ``get_mut(&self) -> *mut T`` +/// returns a raw mutable pointer to the inner T +/// casting to &mut T is under control of RTIC +/// RTIC ensures &mut T to be unique under Rust aliasing rules. +/// +/// Implementation uses the underlying ``UnsafeCell`` +/// self.0.get() -> *mut T +/// +/// 3) get(&self) -> *const T +/// returns a raw immutable (const) pointer to the inner T +/// casting to &T is under control of RTIC +/// RTIC ensures &T to be shared under Rust aliasing rules. +/// +/// Implementation uses the underlying ``UnsafeCell`` +/// self.0.get() -> *mut T, demoted to *const T +/// +#[repr(transparent)] +pub struct RacyCell(UnsafeCell); + +impl RacyCell { + /// Create a ``RacyCell`` + #[inline(always)] + pub const fn new(value: T) -> Self { + RacyCell(UnsafeCell::new(value)) + } + + /// Get `*mut T` + /// + /// # Safety + /// + /// See documentation notes for [`RacyCell`] + #[inline(always)] + pub unsafe fn get_mut(&self) -> *mut T { + self.0.get() + } + + /// Get `*const T` + /// + /// # Safety + /// + /// See documentation notes for [`RacyCell`] + #[inline(always)] + pub unsafe fn get(&self) -> *const T { + self.0.get() } } + +unsafe impl Sync for RacyCell {} diff --git a/src/sll.rs b/src/sll.rs new file mode 100644 index 0000000000..43b53c1749 --- /dev/null +++ b/src/sll.rs @@ -0,0 +1,421 @@ +//! An intrusive sorted priority linked list, designed for use in `Future`s in RTIC. +use core::cmp::Ordering; +use core::fmt; +use core::marker::PhantomData; +use core::ops::{Deref, DerefMut}; +use core::ptr::NonNull; + +/// Marker for Min sorted [`IntrusiveSortedLinkedList`]. +pub struct Min; + +/// Marker for Max sorted [`IntrusiveSortedLinkedList`]. +pub struct Max; + +/// The linked list kind: min-list or max-list +pub trait Kind: private::Sealed { + #[doc(hidden)] + fn ordering() -> Ordering; +} + +impl Kind for Min { + fn ordering() -> Ordering { + Ordering::Less + } +} + +impl Kind for Max { + fn ordering() -> Ordering { + Ordering::Greater + } +} + +/// Sealed traits +mod private { + pub trait Sealed {} +} + +impl private::Sealed for Max {} +impl private::Sealed for Min {} + +/// A node in the [`IntrusiveSortedLinkedList`]. +pub struct Node { + pub val: T, + next: Option>>, +} + +impl Node { + pub fn new(val: T) -> Self { + Self { val, next: None } + } +} + +/// The linked list. +pub struct IntrusiveSortedLinkedList<'a, T, K> { + head: Option>>, + _kind: PhantomData, + _lt: PhantomData<&'a ()>, +} + +impl<'a, T, K> fmt::Debug for IntrusiveSortedLinkedList<'a, T, K> +where + T: Ord + core::fmt::Debug, + K: Kind, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut l = f.debug_list(); + let mut current = self.head; + + while let Some(head) = current { + let head = unsafe { head.as_ref() }; + current = head.next; + + l.entry(&head.val); + } + + l.finish() + } +} + +impl<'a, T, K> IntrusiveSortedLinkedList<'a, T, K> +where + T: Ord, + K: Kind, +{ + pub const fn new() -> Self { + Self { + head: None, + _kind: PhantomData, + _lt: PhantomData, + } + } + + // Push to the list. + pub fn push(&mut self, new: &'a mut Node) { + unsafe { + if let Some(head) = self.head { + if head.as_ref().val.cmp(&new.val) != K::ordering() { + // This is newer than head, replace head + new.next = self.head; + self.head = Some(NonNull::new_unchecked(new)); + } else { + // It's not head, search the list for the correct placement + let mut current = head; + + while let Some(next) = current.as_ref().next { + if next.as_ref().val.cmp(&new.val) != K::ordering() { + break; + } + + current = next; + } + + new.next = current.as_ref().next; + current.as_mut().next = Some(NonNull::new_unchecked(new)); + } + } else { + // List is empty, place at head + self.head = Some(NonNull::new_unchecked(new)) + } + } + } + + /// Get an iterator over the sorted list. + pub fn iter(&self) -> Iter<'_, T, K> { + Iter { + _list: self, + index: self.head, + } + } + + /// Find an element in the list that can be changed and resorted. + pub fn find_mut(&mut self, mut f: F) -> Option> + where + F: FnMut(&T) -> bool, + { + let head = self.head?; + + // Special-case, first element + if f(&unsafe { head.as_ref() }.val) { + return Some(FindMut { + is_head: true, + prev_index: None, + index: self.head, + list: self, + maybe_changed: false, + }); + } + + let mut current = head; + + while let Some(next) = unsafe { current.as_ref() }.next { + if f(&unsafe { next.as_ref() }.val) { + return Some(FindMut { + is_head: false, + prev_index: Some(current), + index: Some(next), + list: self, + maybe_changed: false, + }); + } + + current = next; + } + + None + } + + /// Peek at the first element. + pub fn peek(&self) -> Option<&T> { + self.head.map(|head| unsafe { &head.as_ref().val }) + } + + /// Pops the first element in the list. + /// + /// Complexity is worst-case `O(1)`. + pub fn pop(&mut self) -> Option<&'a Node> { + if let Some(head) = self.head { + let v = unsafe { head.as_ref() }; + self.head = v.next; + Some(v) + } else { + None + } + } + + /// Checks if the linked list is empty. + #[inline] + pub fn is_empty(&self) -> bool { + self.head.is_none() + } +} + +/// Iterator for the linked list. +pub struct Iter<'a, T, K> +where + T: Ord, + K: Kind, +{ + _list: &'a IntrusiveSortedLinkedList<'a, T, K>, + index: Option>>, +} + +impl<'a, T, K> Iterator for Iter<'a, T, K> +where + T: Ord, + K: Kind, +{ + type Item = &'a T; + + fn next(&mut self) -> Option { + let index = self.index?; + + let node = unsafe { index.as_ref() }; + self.index = node.next; + + Some(&node.val) + } +} + +/// Comes from [`IntrusiveSortedLinkedList::find_mut`]. +pub struct FindMut<'a, 'b, T, K> +where + T: Ord + 'b, + K: Kind, +{ + list: &'a mut IntrusiveSortedLinkedList<'b, T, K>, + is_head: bool, + prev_index: Option>>, + index: Option>>, + maybe_changed: bool, +} + +impl<'a, 'b, T, K> FindMut<'a, 'b, T, K> +where + T: Ord, + K: Kind, +{ + unsafe fn pop_internal(&mut self) -> &'b mut Node { + if self.is_head { + // If it is the head element, we can do a normal pop + let mut head = self.list.head.unwrap_unchecked(); + let v = head.as_mut(); + self.list.head = v.next; + v + } else { + // Somewhere in the list + let mut prev = self.prev_index.unwrap_unchecked(); + let mut curr = self.index.unwrap_unchecked(); + + // Re-point the previous index + prev.as_mut().next = curr.as_ref().next; + + curr.as_mut() + } + } + + /// This will pop the element from the list. + /// + /// Complexity is worst-case `O(1)`. + #[inline] + pub fn pop(mut self) -> &'b mut Node { + unsafe { self.pop_internal() } + } + + /// This will resort the element into the correct position in the list if needed. The resorting + /// will only happen if the element has been accessed mutably. + /// + /// Same as calling `drop`. + /// + /// Complexity is worst-case `O(N)`. + #[inline] + pub fn finish(self) { + drop(self) + } +} + +impl<'b, T, K> Drop for FindMut<'_, 'b, T, K> +where + T: Ord + 'b, + K: Kind, +{ + fn drop(&mut self) { + // Only resort the list if the element has changed + if self.maybe_changed { + unsafe { + let val = self.pop_internal(); + self.list.push(val); + } + } + } +} + +impl Deref for FindMut<'_, '_, T, K> +where + T: Ord, + K: Kind, +{ + type Target = T; + + fn deref(&self) -> &Self::Target { + unsafe { &self.index.unwrap_unchecked().as_ref().val } + } +} + +impl DerefMut for FindMut<'_, '_, T, K> +where + T: Ord, + K: Kind, +{ + fn deref_mut(&mut self) -> &mut Self::Target { + self.maybe_changed = true; + unsafe { &mut self.index.unwrap_unchecked().as_mut().val } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn const_new() { + static mut _V1: IntrusiveSortedLinkedList = IntrusiveSortedLinkedList::new(); + } + + #[test] + fn test_peek() { + let mut ll: IntrusiveSortedLinkedList = IntrusiveSortedLinkedList::new(); + + let mut a = Node { val: 1, next: None }; + ll.push(&mut a); + assert_eq!(ll.peek().unwrap(), &1); + + let mut a = Node { val: 2, next: None }; + ll.push(&mut a); + assert_eq!(ll.peek().unwrap(), &2); + + let mut a = Node { val: 3, next: None }; + ll.push(&mut a); + assert_eq!(ll.peek().unwrap(), &3); + + let mut ll: IntrusiveSortedLinkedList = IntrusiveSortedLinkedList::new(); + + let mut a = Node { val: 2, next: None }; + ll.push(&mut a); + assert_eq!(ll.peek().unwrap(), &2); + + let mut a = Node { val: 1, next: None }; + ll.push(&mut a); + assert_eq!(ll.peek().unwrap(), &1); + + let mut a = Node { val: 3, next: None }; + ll.push(&mut a); + assert_eq!(ll.peek().unwrap(), &1); + } + + #[test] + fn test_empty() { + let ll: IntrusiveSortedLinkedList = IntrusiveSortedLinkedList::new(); + + assert!(ll.is_empty()) + } + + #[test] + fn test_updating() { + let mut ll: IntrusiveSortedLinkedList = IntrusiveSortedLinkedList::new(); + + let mut a = Node { val: 1, next: None }; + ll.push(&mut a); + + let mut a = Node { val: 2, next: None }; + ll.push(&mut a); + + let mut a = Node { val: 3, next: None }; + ll.push(&mut a); + + let mut find = ll.find_mut(|v| *v == 2).unwrap(); + + *find += 1000; + find.finish(); + + assert_eq!(ll.peek().unwrap(), &1002); + + let mut find = ll.find_mut(|v| *v == 3).unwrap(); + + *find += 1000; + find.finish(); + + assert_eq!(ll.peek().unwrap(), &1003); + + // Remove largest element + ll.find_mut(|v| *v == 1003).unwrap().pop(); + + assert_eq!(ll.peek().unwrap(), &1002); + } + + #[test] + fn test_updating_1() { + let mut ll: IntrusiveSortedLinkedList = IntrusiveSortedLinkedList::new(); + + let mut a = Node { val: 1, next: None }; + ll.push(&mut a); + + let v = ll.pop().unwrap(); + + assert_eq!(v.val, 1); + } + + #[test] + fn test_updating_2() { + let mut ll: IntrusiveSortedLinkedList = IntrusiveSortedLinkedList::new(); + + let mut a = Node { val: 1, next: None }; + ll.push(&mut a); + + let mut find = ll.find_mut(|v| *v == 1).unwrap(); + + *find += 1000; + find.finish(); + + assert_eq!(ll.peek().unwrap(), &1001); + } +} diff --git a/src/tq.rs b/src/tq.rs index 0f585ba4dd..daa91c8d48 100644 --- a/src/tq.rs +++ b/src/tq.rs @@ -1,29 +1,28 @@ -use crate::Monotonic; +use crate::{ + sll::{IntrusiveSortedLinkedList, Min as IsslMin, Node as IntrusiveNode}, + Monotonic, +}; use core::cmp::Ordering; -use heapless::sorted_linked_list::{LinkedIndexU16, Min, SortedLinkedList}; +use core::task::Waker; +use heapless::sorted_linked_list::{LinkedIndexU16, Min as SllMin, SortedLinkedList}; -pub struct TimerQueue( - pub SortedLinkedList, LinkedIndexU16, Min, N>, -) -where - Mono: Monotonic, - Task: Copy; - -impl TimerQueue +pub struct TimerQueue<'a, Mono, Task, const N_TASK: usize> where Mono: Monotonic, Task: Copy, { - /// # Safety - /// - /// Writing to memory with a transmute in order to enable - /// interrupts of the ``SysTick`` timer - /// - /// Enqueue a task without checking if it is full - #[inline] - pub unsafe fn enqueue_unchecked( - &mut self, - nr: NotReady, + pub task_queue: SortedLinkedList, LinkedIndexU16, SllMin, N_TASK>, + pub waker_queue: IntrusiveSortedLinkedList<'a, WakerNotReady, IsslMin>, +} + +impl<'a, Mono, Task, const N_TASK: usize> TimerQueue<'a, Mono, Task, N_TASK> +where + Mono: Monotonic + 'a, + Task: Copy, +{ + fn check_if_enable( + &self, + instant: Mono::Instant, enable_interrupt: F1, pend_handler: F2, mono: Option<&mut Mono>, @@ -33,11 +32,17 @@ where { // Check if the top contains a non-empty element and if that element is // greater than nr - let if_heap_max_greater_than_nr = - self.0.peek().map_or(true, |head| nr.instant < head.instant); + let if_task_heap_max_greater_than_nr = self + .task_queue + .peek() + .map_or(true, |head| instant < head.instant); + let if_waker_heap_max_greater_than_nr = self + .waker_queue + .peek() + .map_or(true, |head| instant < head.instant); - if if_heap_max_greater_than_nr { - if Mono::DISABLE_INTERRUPT_ON_EMPTY_QUEUE && self.0.is_empty() { + if if_task_heap_max_greater_than_nr || if_waker_heap_max_greater_than_nr { + if Mono::DISABLE_INTERRUPT_ON_EMPTY_QUEUE && self.is_empty() { if let Some(mono) = mono { mono.enable_timer(); } @@ -46,19 +51,49 @@ where pend_handler(); } - - self.0.push_unchecked(nr); } - /// Check if the timer queue is empty. + /// Enqueue a task without checking if it is full + #[inline] + pub unsafe fn enqueue_task_unchecked( + &mut self, + nr: TaskNotReady, + enable_interrupt: F1, + pend_handler: F2, + mono: Option<&mut Mono>, + ) where + F1: FnOnce(), + F2: FnOnce(), + { + self.check_if_enable(nr.instant, enable_interrupt, pend_handler, mono); + self.task_queue.push_unchecked(nr); + } + + /// Enqueue a waker + #[inline] + pub fn enqueue_waker( + &mut self, + nr: &'a mut IntrusiveNode>, + enable_interrupt: F1, + pend_handler: F2, + mono: Option<&mut Mono>, + ) where + F1: FnOnce(), + F2: FnOnce(), + { + self.check_if_enable(nr.val.instant, enable_interrupt, pend_handler, mono); + self.waker_queue.push(nr); + } + + /// Check if all the timer queue is empty. #[inline] pub fn is_empty(&self) -> bool { - self.0.is_empty() + self.task_queue.is_empty() && self.waker_queue.is_empty() } - /// Cancel the marker value - pub fn cancel_marker(&mut self, marker: u32) -> Option<(Task, u8)> { - if let Some(val) = self.0.find_mut(|nr| nr.marker == marker) { + /// Cancel the marker value for a task + pub fn cancel_task_marker(&mut self, marker: u32) -> Option<(Task, u8)> { + if let Some(val) = self.task_queue.find_mut(|nr| nr.marker == marker) { let nr = val.pop(); Some((nr.task, nr.index)) @@ -67,16 +102,23 @@ where } } - /// Update the instant at an marker value to a new instant + /// Cancel the marker value for a waker + pub fn cancel_waker_marker(&mut self, marker: u32) { + if let Some(val) = self.waker_queue.find_mut(|nr| nr.marker == marker) { + let _ = val.pop(); + } + } + + /// Update the instant at an marker value for a task to a new instant #[allow(clippy::result_unit_err)] - pub fn update_marker( + pub fn update_task_marker( &mut self, marker: u32, new_marker: u32, instant: Mono::Instant, pend_handler: F, ) -> Result<(), ()> { - if let Some(mut val) = self.0.find_mut(|nr| nr.marker == marker) { + if let Some(mut val) = self.task_queue.find_mut(|nr| nr.marker == marker) { val.instant = instant; val.marker = new_marker; @@ -89,6 +131,62 @@ where } } + fn dequeue_task_queue( + &mut self, + instant: Mono::Instant, + mono: &mut Mono, + ) -> Option<(Task, u8)> { + if instant <= mono.now() { + // task became ready + let nr = unsafe { self.task_queue.pop_unchecked() }; + Some((nr.task, nr.index)) + } else { + // Set compare + mono.set_compare(instant); + + // Double check that the instant we set is really in the future, else + // 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 <= mono.now() { + let nr = unsafe { self.task_queue.pop_unchecked() }; + Some((nr.task, nr.index)) + } else { + None + } + } + } + + fn dequeue_waker_queue(&mut self, instant: Mono::Instant, mono: &mut Mono) -> bool { + let mut did_wake = false; + + if instant <= mono.now() { + // Task became ready, wake the waker + if let Some(v) = self.waker_queue.pop() { + v.val.waker.wake_by_ref(); + + did_wake = true; + } + } else { + // Set compare + mono.set_compare(instant); + + // Double check that the instant we set is really in the future, else + // 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 <= mono.now() { + if let Some(v) = self.waker_queue.pop() { + v.val.waker.wake_by_ref(); + + did_wake = true; + } + } + } + + did_wake + } + /// Dequeue a task from the ``TimerQueue`` pub fn dequeue(&mut self, disable_interrupt: F, mono: &mut Mono) -> Option<(Task, u8)> where @@ -96,59 +194,72 @@ where { mono.clear_compare_flag(); - if let Some(instant) = self.0.peek().map(|p| p.instant) { - if instant <= mono.now() { - // task became ready - let nr = unsafe { self.0.pop_unchecked() }; + loop { + let tq = self.task_queue.peek().map(|p| p.instant); + let wq = self.waker_queue.peek().map(|p| p.instant); - Some((nr.task, nr.index)) - } else { - // Set compare - mono.set_compare(instant); + let dequeue_task; + let instant; - // Double check that the instant we set is really in the future, else - // 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 <= mono.now() { - let nr = unsafe { self.0.pop_unchecked() }; + match (tq, wq) { + (Some(tq_instant), Some(wq_instant)) => { + if tq_instant <= wq_instant { + dequeue_task = true; + instant = tq_instant; + } else { + dequeue_task = false; + instant = wq_instant; + } + } + (Some(tq_instant), None) => { + dequeue_task = true; + instant = tq_instant; + } + (None, Some(wq_instant)) => { + dequeue_task = false; + instant = wq_instant; + } + (None, None) => { + // The queue is empty, disable the interrupt. + if Mono::DISABLE_INTERRUPT_ON_EMPTY_QUEUE { + disable_interrupt(); + mono.disable_timer(); + } - Some((nr.task, nr.index)) - } else { - None + return None; } } - } else { - // The queue is empty, disable the interrupt. - if Mono::DISABLE_INTERRUPT_ON_EMPTY_QUEUE { - disable_interrupt(); - mono.disable_timer(); - } - None + if dequeue_task { + return self.dequeue_task_queue(instant, mono); + } else if !self.dequeue_waker_queue(instant, mono) { + return None; + } else { + // Run the dequeue again + } } } } -pub struct NotReady +pub struct TaskNotReady where Task: Copy, Mono: Monotonic, { + pub task: Task, pub index: u8, pub instant: Mono::Instant, - pub task: Task, pub marker: u32, } -impl Eq for NotReady +impl Eq for TaskNotReady where Task: Copy, Mono: Monotonic, { } -impl Ord for NotReady +impl Ord for TaskNotReady where Task: Copy, Mono: Monotonic, @@ -158,7 +269,7 @@ where } } -impl PartialEq for NotReady +impl PartialEq for TaskNotReady where Task: Copy, Mono: Monotonic, @@ -168,7 +279,7 @@ where } } -impl PartialOrd for NotReady +impl PartialOrd for TaskNotReady where Task: Copy, Mono: Monotonic, @@ -177,3 +288,41 @@ where Some(self.cmp(other)) } } + +pub struct WakerNotReady +where + Mono: Monotonic, +{ + pub waker: Waker, + pub instant: Mono::Instant, + pub marker: u32, +} + +impl Eq for WakerNotReady where Mono: Monotonic {} + +impl Ord for WakerNotReady +where + Mono: Monotonic, +{ + fn cmp(&self, other: &Self) -> Ordering { + self.instant.cmp(&other.instant) + } +} + +impl PartialEq for WakerNotReady +where + Mono: Monotonic, +{ + fn eq(&self, other: &Self) -> bool { + self.instant == other.instant + } +} + +impl PartialOrd for WakerNotReady +where + Mono: Monotonic, +{ + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} diff --git a/ui/extern-interrupt-not-enough.stderr b/ui/extern-interrupt-not-enough.stderr index a667c58824..d8c01b9a1a 100644 --- a/ui/extern-interrupt-not-enough.stderr +++ b/ui/extern-interrupt-not-enough.stderr @@ -1,5 +1,5 @@ -error: not enough interrupts to dispatch all software tasks (need: 1; given: 0) - --> $DIR/extern-interrupt-not-enough.rs:17:8 +error: not enough interrupts to dispatch all software and async tasks (need: 1; given: 0) - one interrupt is needed per priority and sync/async task + --> ui/extern-interrupt-not-enough.rs:17:8 | 17 | fn a(_: a::Context) {} | ^ diff --git a/ui/task-priority-too-high.rs b/ui/task-priority-too-high.rs index e7e0cce207..46ab561750 100644 --- a/ui/task-priority-too-high.rs +++ b/ui/task-priority-too-high.rs @@ -9,7 +9,7 @@ mod app { struct Local {} #[init] - fn init(_: init::Context) -> (Shared, Local, init::Monotonics) { + fn init(cx: init::Context) -> (Shared, Local, init::Monotonics) { (Shared {}, Local {}, init::Monotonics()) } diff --git a/ui/task-priority-too-high.stderr b/ui/task-priority-too-high.stderr index 026124c8fa..a7a15ebfe5 100644 --- a/ui/task-priority-too-high.stderr +++ b/ui/task-priority-too-high.stderr @@ -1,3 +1,11 @@ +warning: unused variable: `cx` + --> ui/task-priority-too-high.rs:12:13 + | +12 | fn init(cx: init::Context) -> (Shared, Local, init::Monotonics) { + | ^^ help: if this is intentional, prefix it with an underscore: `_cx` + | + = note: `#[warn(unused_variables)]` on by default + error[E0080]: evaluation of constant value failed --> ui/task-priority-too-high.rs:3:1 | diff --git a/xtask/src/command.rs b/xtask/src/command.rs index 100888c075..889540c529 100644 --- a/xtask/src/command.rs +++ b/xtask/src/command.rs @@ -47,6 +47,7 @@ impl<'a> CargoCommand<'a> { mode, } => { let mut args = vec![ + "+nightly", self.name(), "--example", example, @@ -69,7 +70,7 @@ impl<'a> CargoCommand<'a> { features, mode, } => { - let mut args = vec![self.name(), "--examples", "--target", target]; + let mut args = vec!["+nightly", self.name(), "--examples", "--target", target]; if let Some(feature_name) = features { args.extend_from_slice(&["--features", feature_name]); From 9829d0ac07180967208403610bc9a25249b9fe85 Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Mon, 2 Jan 2023 14:58:37 +0100 Subject: [PATCH 214/423] Add check again --- macros/src/check.rs | 28 ++++++--------------------- macros/src/lib.rs | 6 ++++++ ui/extern-interrupt-not-enough.stderr | 2 +- 3 files changed, 13 insertions(+), 23 deletions(-) diff --git a/macros/src/check.rs b/macros/src/check.rs index b0ad6f8715..312b84d5f0 100644 --- a/macros/src/check.rs +++ b/macros/src/check.rs @@ -1,18 +1,12 @@ use std::collections::HashSet; -use proc_macro2::Span; -use rtic_syntax::{analyze::Analysis, ast::App}; -use syn::{parse, Path}; +use crate::syntax::ast::App; +use syn::parse; -pub struct Extra { - pub device: Path, - pub peripherals: bool, -} - -pub fn app(app: &App, _analysis: &Analysis) -> parse::Result { +pub fn app(app: &App) -> parse::Result<()> { // Check that external (device-specific) interrupts are not named after known (Cortex-M) // exceptions - for name in app.args.extern_interrupts.keys() { + for name in app.args.dispatchers.keys() { let name_s = name.to_string(); match &*name_s { @@ -41,7 +35,7 @@ pub fn app(app: &App, _analysis: &Analysis) -> parse::Result { .collect::>(); let need = priorities.len(); - let given = app.args.extern_interrupts.len(); + let given = app.args.dispatchers.len(); if need > given { let s = { format!( @@ -72,15 +66,5 @@ pub fn app(app: &App, _analysis: &Analysis) -> parse::Result { } } - if let Some(device) = app.args.device.clone() { - Ok(Extra { - device, - peripherals: app.args.peripherals, - }) - } else { - Err(parse::Error::new( - Span::call_site(), - "a `device` argument must be specified in `#[rtic::app]`", - )) - } + Ok(()) } diff --git a/macros/src/lib.rs b/macros/src/lib.rs index 7729dcbed0..1bda8d2f78 100644 --- a/macros/src/lib.rs +++ b/macros/src/lib.rs @@ -10,6 +10,7 @@ use std::{env, fs, path::Path}; mod analyze; mod bindings; +mod check; mod codegen; mod syntax; @@ -61,6 +62,11 @@ pub fn app(args: TokenStream, input: TokenStream) -> TokenStream { Ok(x) => x, }; + match check::app(&app) { + Err(e) => return e.to_compile_error().into(), + _ => {} + } + let analysis = analyze::app(analysis, &app); let ts = codegen::app(&app, &analysis); diff --git a/ui/extern-interrupt-not-enough.stderr b/ui/extern-interrupt-not-enough.stderr index d8c01b9a1a..6f28b7ad00 100644 --- a/ui/extern-interrupt-not-enough.stderr +++ b/ui/extern-interrupt-not-enough.stderr @@ -1,4 +1,4 @@ -error: not enough interrupts to dispatch all software and async tasks (need: 1; given: 0) - one interrupt is needed per priority and sync/async task +error: not enough interrupts to dispatch all software tasks (need: 1; given: 0) --> ui/extern-interrupt-not-enough.rs:17:8 | 17 | fn a(_: a::Context) {} From d7ed7a8b9f78344f6855fa1c2655ae0d85e44068 Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Tue, 3 Jan 2023 07:51:35 +0100 Subject: [PATCH 215/423] syntax: Remove parse settings struct --- macros/src/lib.rs | 27 ++------------------- macros/src/syntax.rs | 24 +++--------------- macros/src/syntax/optimize.rs | 2 +- macros/src/syntax/parse.rs | 20 ++++----------- macros/src/syntax/parse/app.rs | 11 +++------ macros/ui/extern-interrupt-used.rs | 2 +- macros/ui/interrupt-double.rs | 2 +- macros/ui/monotonic-binds-collision-task.rs | 2 +- macros/ui/task-bind.rs | 7 ------ macros/ui/task-bind.stderr | 5 ---- macros/ui/task-interrupt-same-prio-spawn.rs | 2 +- macros/ui/task-interrupt.rs | 2 +- macros/ui/task-priority-too-low.rs | 2 +- 13 files changed, 22 insertions(+), 86 deletions(-) delete mode 100644 macros/ui/task-bind.rs delete mode 100644 macros/ui/task-bind.stderr diff --git a/macros/src/lib.rs b/macros/src/lib.rs index 1bda8d2f78..34f2bb619b 100644 --- a/macros/src/lib.rs +++ b/macros/src/lib.rs @@ -18,25 +18,7 @@ mod syntax; #[doc(hidden)] #[proc_macro_attribute] pub fn mock_app(args: TokenStream, input: TokenStream) -> TokenStream { - let mut settings = syntax::Settings::default(); - let mut rtic_args = vec![]; - for arg in args.to_string().split(',') { - if arg.trim() == "parse_binds" { - settings.parse_binds = true; - } else if arg.trim() == "parse_extern_interrupt" { - settings.parse_extern_interrupt = true; - } else { - rtic_args.push(arg.to_string()); - } - } - - // rtic_args.push("device = mock".into()); - - let args = rtic_args.join(", ").parse(); - - println!("args: {:?}", args); - - if let Err(e) = syntax::parse(args.unwrap(), input, settings) { + if let Err(e) = syntax::parse(args, input) { e.to_compile_error().into() } else { "fn main() {}".parse().unwrap() @@ -52,12 +34,7 @@ pub fn mock_app(args: TokenStream, input: TokenStream) -> TokenStream { /// Should never panic, cargo feeds a path which is later converted to a string #[proc_macro_attribute] pub fn app(args: TokenStream, input: TokenStream) -> TokenStream { - let mut settings = syntax::Settings::default(); - settings.optimize_priorities = false; - settings.parse_binds = true; - settings.parse_extern_interrupt = true; - - let (app, analysis) = match syntax::parse(args, input, settings) { + let (app, analysis) = match syntax::parse(args, input) { Err(e) => return e.to_compile_error().into(), Ok(x) => x, }; diff --git a/macros/src/syntax.rs b/macros/src/syntax.rs index 09b2ab3d2c..d6f5a476b5 100644 --- a/macros/src/syntax.rs +++ b/macros/src/syntax.rs @@ -13,7 +13,6 @@ mod accessors; pub mod analyze; pub mod ast; mod check; -mod optimize; mod parse; /// An ordered map keyed by identifier @@ -31,10 +30,10 @@ pub enum Context<'a> { /// The `init`-ialization function Init, - /// A software task: `#[task]` + /// A async software task SoftwareTask(&'a Ident), - /// A hardware task: `#[exception]` or `#[interrupt]` + /// A hardware task HardwareTask(&'a Ident), } @@ -93,36 +92,21 @@ impl<'a> Context<'a> { } } -/// Parser and optimizer configuration -#[derive(Default)] -#[non_exhaustive] -pub struct Settings { - /// Whether to accept the `binds` argument in `#[task]` or not - pub parse_binds: bool, - /// Whether to parse `extern` interrupts (functions) or not - pub parse_extern_interrupt: bool, - /// Whether to "compress" priorities or not - pub optimize_priorities: bool, -} - /// Parses the input of the `#[app]` attribute pub fn parse( args: TokenStream, input: TokenStream, - settings: Settings, ) -> Result<(ast::App, analyze::Analysis), syn::parse::Error> { - parse2(args.into(), input.into(), settings) + parse2(args.into(), input.into()) } /// `proc_macro2::TokenStream` version of `parse` pub fn parse2( args: TokenStream2, input: TokenStream2, - settings: Settings, ) -> Result<(ast::App, analyze::Analysis), syn::parse::Error> { - let mut app = parse::app(args, input, &settings)?; + let app = parse::app(args, input)?; check::app(&app)?; - optimize::app(&mut app, &settings); match analyze::app(&app) { Err(e) => Err(e), diff --git a/macros/src/syntax/optimize.rs b/macros/src/syntax/optimize.rs index 87a6258d21..e83ba31bd1 100644 --- a/macros/src/syntax/optimize.rs +++ b/macros/src/syntax/optimize.rs @@ -1,6 +1,6 @@ use std::collections::{BTreeSet, HashMap}; -use crate::syntax::{ast::App, Settings}; +use crate::syntax::ast::App; pub fn app(app: &mut App, settings: &Settings) { // "compress" priorities diff --git a/macros/src/syntax/parse.rs b/macros/src/syntax/parse.rs index 74f94f2be7..ceedaa9891 100644 --- a/macros/src/syntax/parse.rs +++ b/macros/src/syntax/parse.rs @@ -20,15 +20,15 @@ use crate::syntax::{ App, AppArgs, HardwareTaskArgs, IdleArgs, InitArgs, MonotonicArgs, SoftwareTaskArgs, TaskLocal, }, - Either, Settings, + Either, }; // Parse the app, both app arguments and body (input) -pub fn app(args: TokenStream2, input: TokenStream2, settings: &Settings) -> parse::Result { +pub fn app(args: TokenStream2, input: TokenStream2) -> parse::Result { let args = AppArgs::parse(args)?; let input: Input = syn::parse2(input)?; - App::parse(args, input, settings) + App::parse(args, input) } pub(crate) struct Input { @@ -188,10 +188,7 @@ fn idle_args(tokens: TokenStream2) -> parse::Result { .parse2(tokens) } -fn task_args( - tokens: TokenStream2, - settings: &Settings, -) -> parse::Result> { +fn task_args(tokens: TokenStream2) -> parse::Result> { (|input: ParseStream<'_>| -> parse::Result> { if input.is_empty() { return Ok(Either::Right(SoftwareTaskArgs::default())); @@ -242,14 +239,7 @@ fn task_args( let _: Token![=] = content.parse()?; match &*ident_s { - "binds" if !settings.parse_binds => { - return Err(parse::Error::new( - ident.span(), - "Unexpected bind in task argument. Binds are only parsed if Settings::parse_binds is set.", - )); - } - - "binds" if settings.parse_binds => { + "binds" => { if binds.is_some() { return Err(parse::Error::new( ident.span(), diff --git a/macros/src/syntax/parse/app.rs b/macros/src/syntax/parse/app.rs index 7eb415d337..dd7c399908 100644 --- a/macros/src/syntax/parse/app.rs +++ b/macros/src/syntax/parse/app.rs @@ -15,7 +15,7 @@ use crate::syntax::{ LocalResource, Monotonic, MonotonicArgs, SharedResource, SoftwareTask, }, parse::{self as syntax_parse, util}, - Either, Map, Set, Settings, + Either, Map, Set, }; impl AppArgs { @@ -142,7 +142,7 @@ impl AppArgs { } impl App { - pub(crate) fn parse(args: AppArgs, input: Input, settings: &Settings) -> parse::Result { + pub(crate) fn parse(args: AppArgs, input: Input) -> parse::Result { let mut init = None; let mut idle = None; @@ -253,7 +253,7 @@ impl App { )); } - match syntax_parse::task_args(item.attrs.remove(pos).tokens, settings)? { + match syntax_parse::task_args(item.attrs.remove(pos).tokens)? { Either::Left(args) => { check_binding(&args.binds)?; check_ident(&item.sig.ident)?; @@ -410,10 +410,7 @@ impl App { )); } - match syntax_parse::task_args( - item.attrs.remove(pos).tokens, - settings, - )? { + match syntax_parse::task_args(item.attrs.remove(pos).tokens)? { Either::Left(args) => { check_binding(&args.binds)?; check_ident(&item.sig.ident)?; diff --git a/macros/ui/extern-interrupt-used.rs b/macros/ui/extern-interrupt-used.rs index 71dc50f397..a6e0b3b29a 100644 --- a/macros/ui/extern-interrupt-used.rs +++ b/macros/ui/extern-interrupt-used.rs @@ -1,6 +1,6 @@ #![no_main] -#[rtic_macros::mock_app(parse_extern_interrupt, parse_binds, device = mock, dispatchers = [EXTI0])] +#[rtic_macros::mock_app(device = mock, dispatchers = [EXTI0])] mod app { #[shared] struct Shared {} diff --git a/macros/ui/interrupt-double.rs b/macros/ui/interrupt-double.rs index 1133c5cfd9..e2addc7ce6 100644 --- a/macros/ui/interrupt-double.rs +++ b/macros/ui/interrupt-double.rs @@ -1,6 +1,6 @@ #![no_main] -#[rtic_macros::mock_app(parse_binds, device = mock)] +#[rtic_macros::mock_app(device = mock)] mod app { #[task(binds = UART0)] fn foo(_: foo::Context) {} diff --git a/macros/ui/monotonic-binds-collision-task.rs b/macros/ui/monotonic-binds-collision-task.rs index 57c59c1b01..1c58f9ddaf 100644 --- a/macros/ui/monotonic-binds-collision-task.rs +++ b/macros/ui/monotonic-binds-collision-task.rs @@ -1,6 +1,6 @@ #![no_main] -#[rtic_macros::mock_app(parse_extern_interrupt, parse_binds, device = mock)] +#[rtic_macros::mock_app(device = mock)] mod app { #[monotonic(binds = Tim1)] type Fast1 = hal::Tim1Monotonic; diff --git a/macros/ui/task-bind.rs b/macros/ui/task-bind.rs deleted file mode 100644 index de60524314..0000000000 --- a/macros/ui/task-bind.rs +++ /dev/null @@ -1,7 +0,0 @@ -#![no_main] - -#[rtic_macros::mock_app(device = mock)] -mod app { - #[task(binds = UART0)] - fn foo(_: foo::Context) {} -} diff --git a/macros/ui/task-bind.stderr b/macros/ui/task-bind.stderr deleted file mode 100644 index 60cfdc8b86..0000000000 --- a/macros/ui/task-bind.stderr +++ /dev/null @@ -1,5 +0,0 @@ -error: Unexpected bind in task argument. Binds are only parsed if Settings::parse_binds is set. - --> $DIR/task-bind.rs:5:12 - | -5 | #[task(binds = UART0)] - | ^^^^^ diff --git a/macros/ui/task-interrupt-same-prio-spawn.rs b/macros/ui/task-interrupt-same-prio-spawn.rs index 741e60e462..1d5f1f8429 100644 --- a/macros/ui/task-interrupt-same-prio-spawn.rs +++ b/macros/ui/task-interrupt-same-prio-spawn.rs @@ -1,6 +1,6 @@ #![no_main] -#[rtic_macros::mock_app(parse_binds, device = mock)] +#[rtic_macros::mock_app(device = mock)] mod app { #[task(binds = SysTick, only_same_priority_spawn_please_fix_me)] fn foo(_: foo::Context) {} diff --git a/macros/ui/task-interrupt.rs b/macros/ui/task-interrupt.rs index 71fef9aba0..5e063def3b 100644 --- a/macros/ui/task-interrupt.rs +++ b/macros/ui/task-interrupt.rs @@ -1,6 +1,6 @@ #![no_main] -#[rtic_macros::mock_app(parse_binds, device = mock)] +#[rtic_macros::mock_app(device = mock)] mod app { #[task(binds = SysTick)] fn foo(_: foo::Context) {} diff --git a/macros/ui/task-priority-too-low.rs b/macros/ui/task-priority-too-low.rs index beed4de1e1..16e05577ce 100644 --- a/macros/ui/task-priority-too-low.rs +++ b/macros/ui/task-priority-too-low.rs @@ -1,6 +1,6 @@ #![no_main] -#[rtic_macros::mock_app(parse_binds, device = mock)] +#[rtic_macros::mock_app(device = mock)] mod app { #[task(binds = UART0, priority = 0)] fn foo(_: foo::Context) {} From f8352122a301c30db7c7851ebf50ad1608ebdad3 Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Tue, 3 Jan 2023 15:10:59 +0100 Subject: [PATCH 216/423] Min codegen --- macros/src/analyze.rs | 23 +- macros/src/codegen.rs | 29 +-- macros/src/codegen/assertions.rs | 5 - macros/src/codegen/async_dispatchers.rs | 64 ++--- macros/src/codegen/dispatchers.rs | 146 ----------- macros/src/codegen/module.rs | 300 ++--------------------- macros/src/codegen/monotonic.rs | 280 --------------------- macros/src/codegen/post_init.rs | 20 +- macros/src/codegen/pre_init.rs | 68 +---- macros/src/codegen/shared_resources.rs | 6 +- macros/src/codegen/software_tasks.rs | 179 -------------- macros/src/codegen/timer_queue.rs | 170 ------------- macros/src/codegen/util.rs | 111 +-------- macros/src/syntax/analyze.rs | 57 +---- macros/src/syntax/ast.rs | 50 +--- macros/src/syntax/parse.rs | 121 +-------- macros/src/syntax/parse/app.rs | 58 +---- macros/src/syntax/parse/hardware_task.rs | 44 ++-- macros/src/syntax/parse/idle.rs | 18 +- macros/src/syntax/parse/init.rs | 22 +- macros/src/syntax/parse/software_task.rs | 26 +- macros/src/syntax/parse/util.rs | 26 +- 22 files changed, 129 insertions(+), 1694 deletions(-) delete mode 100644 macros/src/codegen/dispatchers.rs delete mode 100644 macros/src/codegen/monotonic.rs delete mode 100644 macros/src/codegen/software_tasks.rs delete mode 100644 macros/src/codegen/timer_queue.rs diff --git a/macros/src/analyze.rs b/macros/src/analyze.rs index ec12cfb4dc..cb42ad6f2a 100644 --- a/macros/src/analyze.rs +++ b/macros/src/analyze.rs @@ -10,8 +10,7 @@ use syn::Ident; /// Extend the upstream `Analysis` struct with our field pub struct Analysis { parent: analyze::Analysis, - pub interrupts_normal: BTreeMap, - pub interrupts_async: BTreeMap, + pub interrupts: BTreeMap, } impl ops::Deref for Analysis { @@ -30,27 +29,12 @@ pub fn app(analysis: analyze::Analysis, app: &App) -> Analysis { let priorities = app .software_tasks .values() - .filter(|task| !task.is_async) - .map(|task| task.args.priority) - .collect::>(); - - let priorities_async = app - .software_tasks - .values() - .filter(|task| task.is_async) .map(|task| task.args.priority) .collect::>(); // map from priorities to interrupts (holding name and attributes) - let interrupts_normal: BTreeMap = priorities - .iter() - .copied() - .rev() - .map(|p| (p, available_interrupt.pop().expect("UNREACHABLE"))) - .collect(); - - let interrupts_async: BTreeMap = priorities_async + let interrupts: BTreeMap = priorities .iter() .copied() .rev() @@ -59,7 +43,6 @@ pub fn app(analysis: analyze::Analysis, app: &App) -> Analysis { Analysis { parent: analysis, - interrupts_normal, - interrupts_async, + interrupts, } } diff --git a/macros/src/codegen.rs b/macros/src/codegen.rs index ef817325d8..618d9f3a68 100644 --- a/macros/src/codegen.rs +++ b/macros/src/codegen.rs @@ -6,20 +6,20 @@ use crate::syntax::ast::App; mod assertions; mod async_dispatchers; -mod dispatchers; +// mod dispatchers; mod hardware_tasks; mod idle; mod init; mod local_resources; mod local_resources_struct; mod module; -mod monotonic; +// mod monotonic; mod post_init; mod pre_init; mod shared_resources; mod shared_resources_struct; -mod software_tasks; -mod timer_queue; +// mod software_tasks; +// mod timer_queue; mod util; #[allow(clippy::too_many_lines)] @@ -92,14 +92,7 @@ pub fn app(app: &App, analysis: &Analysis) -> TokenStream2 { let (mod_app_hardware_tasks, root_hardware_tasks, user_hardware_tasks) = hardware_tasks::codegen(app, analysis); - let (mod_app_software_tasks, root_software_tasks, user_software_tasks) = - software_tasks::codegen(app, analysis); - - let monotonics = monotonic::codegen(app, analysis); - - let mod_app_dispatchers = dispatchers::codegen(app, analysis); let mod_app_async_dispatchers = async_dispatchers::codegen(app, analysis); - let mod_app_timer_queue = timer_queue::codegen(app, analysis); let user_imports = &app.user_imports; let user_code = &app.user_code; let name = &app.name; @@ -113,8 +106,6 @@ pub fn app(app: &App, analysis: &Analysis) -> TokenStream2 { /// Always include the device crate which contains the vector table use #device as #rt_err; - #monotonics - #(#user_imports)* /// User code from within the module @@ -125,8 +116,6 @@ pub fn app(app: &App, analysis: &Analysis) -> TokenStream2 { #(#user_hardware_tasks)* - #(#user_software_tasks)* - #(#root)* #mod_shared_resources @@ -135,9 +124,7 @@ pub fn app(app: &App, analysis: &Analysis) -> TokenStream2 { #(#root_hardware_tasks)* - #(#root_software_tasks)* - - /// App module + /// app module #(#mod_app)* #(#mod_app_shared_resources)* @@ -146,14 +133,8 @@ pub fn app(app: &App, analysis: &Analysis) -> TokenStream2 { #(#mod_app_hardware_tasks)* - #(#mod_app_software_tasks)* - - #(#mod_app_dispatchers)* - #(#mod_app_async_dispatchers)* - #(#mod_app_timer_queue)* - #(#mains)* } ) diff --git a/macros/src/codegen/assertions.rs b/macros/src/codegen/assertions.rs index 0f8326c732..dd94aa6d8c 100644 --- a/macros/src/codegen/assertions.rs +++ b/macros/src/codegen/assertions.rs @@ -16,11 +16,6 @@ pub fn codegen(app: &App, analysis: &Analysis) -> Vec { stmts.push(quote!(rtic::export::assert_sync::<#ty>();)); } - for (_, monotonic) in &app.monotonics { - let ty = &monotonic.ty; - stmts.push(quote!(rtic::export::assert_monotonic::<#ty>();)); - } - let device = &app.args.device; let chunks_name = util::priority_mask_chunks_ident(); let no_basepri_checks: Vec<_> = app diff --git a/macros/src/codegen/async_dispatchers.rs b/macros/src/codegen/async_dispatchers.rs index 8b0e928bda..aa854d7f8e 100644 --- a/macros/src/codegen/async_dispatchers.rs +++ b/macros/src/codegen/async_dispatchers.rs @@ -7,65 +7,47 @@ use quote::quote; pub fn codegen(app: &App, analysis: &Analysis) -> Vec { let mut items = vec![]; - let interrupts = &analysis.interrupts_async; + let interrupts = &analysis.interrupts; // Generate executor definition and priority in global scope - for (name, task) in app.software_tasks.iter() { - if task.is_async { - let type_name = util::internal_task_ident(name, "F"); - let exec_name = util::internal_task_ident(name, "EXEC"); - let prio_name = util::internal_task_ident(name, "PRIORITY"); + for (name, _) in app.software_tasks.iter() { + let type_name = util::internal_task_ident(name, "F"); + let exec_name = util::internal_task_ident(name, "EXEC"); + let prio_name = util::internal_task_ident(name, "PRIORITY"); - items.push(quote!( - #[allow(non_camel_case_types)] - type #type_name = impl core::future::Future + 'static; - #[allow(non_upper_case_globals)] - static #exec_name: - rtic::RacyCell> = - rtic::RacyCell::new(rtic::export::executor::AsyncTaskExecutor::new()); + items.push(quote!( + #[allow(non_camel_case_types)] + type #type_name = impl core::future::Future + 'static; + #[allow(non_upper_case_globals)] + static #exec_name: + rtic::RacyCell> = + rtic::RacyCell::new(rtic::export::executor::AsyncTaskExecutor::new()); - // The executors priority, this can be any value - we will overwrite it when we - // start a task - #[allow(non_upper_case_globals)] - static #prio_name: rtic::RacyCell = - unsafe { rtic::RacyCell::new(rtic::export::Priority::new(0)) }; - )); - } + // The executors priority, this can be any value - we will overwrite it when we + // start a task + #[allow(non_upper_case_globals)] + static #prio_name: rtic::RacyCell = + unsafe { rtic::RacyCell::new(rtic::export::Priority::new(0)) }; + )); } for (&level, channel) in &analysis.channels { - if channel - .tasks - .iter() - .map(|task_name| !app.software_tasks[task_name].is_async) - .all(|is_not_async| is_not_async) - { - // check if all tasks are not async, if so don't generate this. - continue; - } - let mut stmts = vec![]; let device = &app.args.device; let enum_ = util::interrupt_ident(); let interrupt = util::suffixed(&interrupts[&level].0.to_string()); - for name in channel - .tasks - .iter() - .filter(|name| app.software_tasks[*name].is_async) - { + for name in channel.tasks.iter() { let exec_name = util::internal_task_ident(name, "EXEC"); let prio_name = util::internal_task_ident(name, "PRIORITY"); - let task = &app.software_tasks[name]; + // let task = &app.software_tasks[name]; // let cfgs = &task.cfgs; - let (_, tupled, pats, input_types) = util::regroup_inputs(&task.inputs); let executor_run_ident = util::executor_run_ident(name); - let n = util::capacity_literal(channel.capacity as usize + 1); let rq = util::rq_async_ident(name); let (rq_ty, rq_expr) = { ( - quote!(rtic::export::ASYNCRQ<#input_types, #n>), + quote!(rtic::export::ASYNCRQ<(), 2>), // TODO: This needs updating to a counter instead of a queue quote!(rtic::export::Queue::new()), ) }; @@ -79,13 +61,13 @@ pub fn codegen(app: &App, analysis: &Analysis) -> Vec { stmts.push(quote!( if !(&*#exec_name.get()).is_running() { - if let Some(#tupled) = rtic::export::interrupt::free(|_| (&mut *#rq.get_mut()).dequeue()) { + if let Some(()) = rtic::export::interrupt::free(|_| (&mut *#rq.get_mut()).dequeue()) { // The async executor needs a static priority #prio_name.get_mut().write(rtic::export::Priority::new(PRIORITY)); let priority: &'static _ = &*#prio_name.get(); - (&mut *#exec_name.get_mut()).spawn(#name(#name::Context::new(priority) #(,#pats)*)); + (&mut *#exec_name.get_mut()).spawn(#name(#name::Context::new(priority))); #executor_run_ident.store(true, core::sync::atomic::Ordering::Relaxed); } } diff --git a/macros/src/codegen/dispatchers.rs b/macros/src/codegen/dispatchers.rs deleted file mode 100644 index 1a8b40422b..0000000000 --- a/macros/src/codegen/dispatchers.rs +++ /dev/null @@ -1,146 +0,0 @@ -use crate::syntax::ast::App; -use crate::{analyze::Analysis, codegen::util}; -use proc_macro2::TokenStream as TokenStream2; -use quote::quote; - -/// Generates task dispatchers -pub fn codegen(app: &App, analysis: &Analysis) -> Vec { - let mut items = vec![]; - - let interrupts = &analysis.interrupts_normal; - - for (&level, channel) in &analysis.channels { - if channel - .tasks - .iter() - .map(|task_name| app.software_tasks[task_name].is_async) - .all(|is_async| is_async) - { - // check if all tasks are async, if so don't generate this. - continue; - } - - let mut stmts = vec![]; - - let variants = channel - .tasks - .iter() - .filter(|name| !app.software_tasks[*name].is_async) - .map(|name| { - let cfgs = &app.software_tasks[name].cfgs; - - quote!( - #(#cfgs)* - #name - ) - }) - .collect::>(); - - // For future use - // let doc = format!( - // "Software tasks to be dispatched at priority level {}", - // level, - // ); - let t = util::spawn_t_ident(level); - items.push(quote!( - #[allow(non_snake_case)] - #[allow(non_camel_case_types)] - #[derive(Clone, Copy)] - // #[doc = #doc] - #[doc(hidden)] - pub enum #t { - #(#variants,)* - } - )); - - let n = util::capacity_literal(channel.capacity as usize + 1); - let rq = util::rq_ident(level); - // let (_, _, _, input_ty) = util::regroup_inputs(inputs); - let (rq_ty, rq_expr) = { - ( - quote!(rtic::export::SCRQ<#t, #n>), - quote!(rtic::export::Queue::new()), - ) - }; - - // For future use - // let doc = format!( - // "Queue of tasks ready to be dispatched at priority level {}", - // level - // ); - items.push(quote!( - #[doc(hidden)] - #[allow(non_camel_case_types)] - #[allow(non_upper_case_globals)] - static #rq: rtic::RacyCell<#rq_ty> = rtic::RacyCell::new(#rq_expr); - )); - - let interrupt = util::suffixed( - &interrupts - .get(&level) - .expect("RTIC-ICE: Unable to get interrrupt") - .0 - .to_string(), - ); - let arms = channel - .tasks - .iter() - .map(|name| { - let task = &app.software_tasks[name]; - let cfgs = &task.cfgs; - let fq = util::fq_ident(name); - let inputs = util::inputs_ident(name); - let (_, tupled, pats, _) = util::regroup_inputs(&task.inputs); - - if !task.is_async { - quote!( - #(#cfgs)* - #t::#name => { - let #tupled = - (&*#inputs - .get()) - .get_unchecked(usize::from(index)) - .as_ptr() - .read(); - (&mut *#fq.get_mut()).split().0.enqueue_unchecked(index); - let priority = &rtic::export::Priority::new(PRIORITY); - #name( - #name::Context::new(priority) - #(,#pats)* - ) - } - ) - } else { - quote!() - } - }) - .collect::>(); - - stmts.push(quote!( - while let Some((task, index)) = (&mut *#rq.get_mut()).split().1.dequeue() { - match task { - #(#arms)* - } - } - )); - - let doc = format!("Interrupt handler to dispatch tasks at priority {}", level); - let attribute = &interrupts[&level].1.attrs; - items.push(quote!( - #[allow(non_snake_case)] - #[doc = #doc] - #[no_mangle] - #(#attribute)* - unsafe fn #interrupt() { - /// The priority of this interrupt handler - const PRIORITY: u8 = #level; - - rtic::export::run(PRIORITY, || { - #(#stmts)* - }); - } - )); - } - - items -} diff --git a/macros/src/codegen/module.rs b/macros/src/codegen/module.rs index 7ac06c5c05..eb0cb65ba0 100644 --- a/macros/src/codegen/module.rs +++ b/macros/src/codegen/module.rs @@ -102,33 +102,6 @@ pub fn codegen( values.push(quote!(shared: #name::SharedResources::new(#priority))); } - if let Context::Init = ctxt { - let monotonic_types: Vec<_> = app - .monotonics - .iter() - .map(|(_, monotonic)| { - let mono = &monotonic.ty; - quote! {#mono} - }) - .collect(); - - let internal_monotonics_ident = util::mark_internal_name("Monotonics"); - - items.push(quote!( - /// Monotonics used by the system - #[allow(non_snake_case)] - #[allow(non_camel_case_types)] - pub struct #internal_monotonics_ident( - #(pub #monotonic_types),* - ); - )); - - module_items.push(quote!( - #[doc(inline)] - pub use super::#internal_monotonics_ident as Monotonics; - )); - } - let doc = match ctxt { Context::Idle => "Idle loop", Context::Init => "Initialization function", @@ -192,280 +165,45 @@ pub fn codegen( if let Context::SoftwareTask(..) = ctxt { let spawnee = &app.software_tasks[name]; let priority = spawnee.args.priority; - let t = util::spawn_t_ident(priority); let cfgs = &spawnee.cfgs; // Store a copy of the task cfgs task_cfgs = cfgs.clone(); - let (args, tupled, untupled, ty) = util::regroup_inputs(&spawnee.inputs); - let args = &args; - let tupled = &tupled; - let fq = util::fq_ident(name); - let rq = util::rq_ident(priority); - let inputs = util::inputs_ident(name); let device = &app.args.device; let enum_ = util::interrupt_ident(); - let interrupt = if spawnee.is_async { - &analysis - .interrupts_async - .get(&priority) - .expect("RTIC-ICE: interrupt identifer not found") - .0 - } else { - &analysis - .interrupts_normal - .get(&priority) - .expect("RTIC-ICE: interrupt identifer not found") - .0 - }; + let interrupt = &analysis + .interrupts + .get(&priority) + .expect("RTIC-ICE: interrupt identifer not found") + .0; let internal_spawn_ident = util::internal_task_ident(name, "spawn"); // Spawn caller - if spawnee.is_async { - let rq = util::rq_async_ident(name); - items.push(quote!( + let rq = util::rq_async_ident(name); + items.push(quote!( - #(#cfgs)* - /// Spawns the task directly - #[allow(non_snake_case)] - #[doc(hidden)] - pub fn #internal_spawn_ident(#(#args,)*) -> Result<(), #ty> { - let input = #tupled; + #(#cfgs)* + /// Spawns the task directly + #[allow(non_snake_case)] + #[doc(hidden)] + pub fn #internal_spawn_ident() -> Result<(), ()> { + unsafe { + let r = rtic::export::interrupt::free(|_| (&mut *#rq.get_mut()).enqueue(())); - unsafe { - let r = rtic::export::interrupt::free(|_| (&mut *#rq.get_mut()).enqueue(input)); - - if r.is_ok() { - rtic::pend(#device::#enum_::#interrupt); - } - - r - } - })); - } else { - items.push(quote!( - - #(#cfgs)* - /// Spawns the task directly - #[allow(non_snake_case)] - #[doc(hidden)] - pub fn #internal_spawn_ident(#(#args,)*) -> Result<(), #ty> { - let input = #tupled; - - unsafe { - if let Some(index) = rtic::export::interrupt::free(|_| (&mut *#fq.get_mut()).dequeue()) { - (&mut *#inputs - .get_mut()) - .get_unchecked_mut(usize::from(index)) - .as_mut_ptr() - .write(input); - - rtic::export::interrupt::free(|_| { - (&mut *#rq.get_mut()).enqueue_unchecked((#t::#name, index)); - }); - rtic::pend(#device::#enum_::#interrupt); - - Ok(()) - } else { - Err(input) - } + if r.is_ok() { + rtic::pend(#device::#enum_::#interrupt); } - })); - } + r + } + })); module_items.push(quote!( #(#cfgs)* #[doc(inline)] pub use super::#internal_spawn_ident as spawn; )); - - // Schedule caller - if !spawnee.is_async { - for (_, monotonic) in &app.monotonics { - let instants = util::monotonic_instants_ident(name, &monotonic.ident); - let monotonic_name = monotonic.ident.to_string(); - - let tq = util::tq_ident(&monotonic.ident.to_string()); - let t = util::schedule_t_ident(); - let m = &monotonic.ident; - let m_ident = util::monotonic_ident(&monotonic_name); - let m_isr = &monotonic.args.binds; - let enum_ = util::interrupt_ident(); - let spawn_handle_string = format!("{}::SpawnHandle", m); - - let (enable_interrupt, pend) = if &*m_isr.to_string() == "SysTick" { - ( - quote!(core::mem::transmute::<_, rtic::export::SYST>(()).enable_interrupt()), - quote!(rtic::export::SCB::set_pendst()), - ) - } else { - let rt_err = util::rt_err_ident(); - ( - quote!(rtic::export::NVIC::unmask(#rt_err::#enum_::#m_isr)), - quote!(rtic::pend(#rt_err::#enum_::#m_isr)), - ) - }; - - let tq_marker = &util::timer_queue_marker_ident(); - - let internal_spawn_handle_ident = - util::internal_monotonics_ident(name, m, "SpawnHandle"); - let internal_spawn_at_ident = util::internal_monotonics_ident(name, m, "spawn_at"); - let internal_spawn_after_ident = - util::internal_monotonics_ident(name, m, "spawn_after"); - - if monotonic.args.default { - module_items.push(quote!( - #[doc(inline)] - pub use #m::spawn_after; - #[doc(inline)] - pub use #m::spawn_at; - #[doc(inline)] - pub use #m::SpawnHandle; - )); - } - module_items.push(quote!( - pub mod #m { - #[doc(inline)] - pub use super::super::#internal_spawn_after_ident as spawn_after; - #[doc(inline)] - pub use super::super::#internal_spawn_at_ident as spawn_at; - #[doc(inline)] - pub use super::super::#internal_spawn_handle_ident as SpawnHandle; - } - )); - - items.push(quote!( - #(#cfgs)* - #[allow(non_snake_case)] - #[allow(non_camel_case_types)] - pub struct #internal_spawn_handle_ident { - #[doc(hidden)] - marker: u32, - } - - #(#cfgs)* - impl core::fmt::Debug for #internal_spawn_handle_ident { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - f.debug_struct(#spawn_handle_string).finish() - } - } - - #(#cfgs)* - impl #internal_spawn_handle_ident { - pub fn cancel(self) -> Result<#ty, ()> { - rtic::export::interrupt::free(|_| unsafe { - let tq = &mut *#tq.get_mut(); - if let Some((_task, index)) = tq.cancel_task_marker(self.marker) { - // Get the message - let msg = (&*#inputs - .get()) - .get_unchecked(usize::from(index)) - .as_ptr() - .read(); - // Return the index to the free queue - (&mut *#fq.get_mut()).split().0.enqueue_unchecked(index); - - Ok(msg) - } else { - Err(()) - } - }) - } - - #[inline] - pub fn reschedule_after( - self, - duration: <#m as rtic::Monotonic>::Duration - ) -> Result { - self.reschedule_at(monotonics::#m::now() + duration) - } - - pub fn reschedule_at( - self, - instant: <#m as rtic::Monotonic>::Instant - ) -> Result { - rtic::export::interrupt::free(|_| unsafe { - let marker = #tq_marker.get().read(); - #tq_marker.get_mut().write(marker.wrapping_add(1)); - - let tq = (&mut *#tq.get_mut()); - - tq.update_task_marker(self.marker, marker, instant, || #pend).map(|_| #name::#m::SpawnHandle { marker }) - }) - } - } - - - #(#cfgs)* - /// Spawns the task after a set duration relative to the current time - /// - /// This will use the time `Instant::new(0)` as baseline if called in `#[init]`, - /// so if you use a non-resetable timer use `spawn_at` when in `#[init]` - #[allow(non_snake_case)] - pub fn #internal_spawn_after_ident( - duration: <#m as rtic::Monotonic>::Duration - #(,#args)* - ) -> Result<#name::#m::SpawnHandle, #ty> - { - let instant = monotonics::#m::now(); - - #internal_spawn_at_ident(instant + duration #(,#untupled)*) - } - - #(#cfgs)* - /// Spawns the task at a fixed time instant - #[allow(non_snake_case)] - pub fn #internal_spawn_at_ident( - instant: <#m as rtic::Monotonic>::Instant - #(,#args)* - ) -> Result<#name::#m::SpawnHandle, #ty> { - unsafe { - let input = #tupled; - if let Some(index) = rtic::export::interrupt::free(|_| (&mut *#fq.get_mut()).dequeue()) { - (&mut *#inputs - .get_mut()) - .get_unchecked_mut(usize::from(index)) - .as_mut_ptr() - .write(input); - - (&mut *#instants - .get_mut()) - .get_unchecked_mut(usize::from(index)) - .as_mut_ptr() - .write(instant); - - rtic::export::interrupt::free(|_| { - let marker = #tq_marker.get().read(); - let nr = rtic::export::TaskNotReady { - task: #t::#name, - index, - instant, - marker, - }; - - #tq_marker.get_mut().write(#tq_marker.get().read().wrapping_add(1)); - - let tq = &mut *#tq.get_mut(); - - tq.enqueue_task_unchecked( - nr, - || #enable_interrupt, - || #pend, - (&mut *#m_ident.get_mut()).as_mut()); - - Ok(#name::#m::SpawnHandle { marker }) - }) - } else { - Err(input) - } - } - } - )); - } - } } if items.is_empty() { diff --git a/macros/src/codegen/monotonic.rs b/macros/src/codegen/monotonic.rs deleted file mode 100644 index 417a1d6a1c..0000000000 --- a/macros/src/codegen/monotonic.rs +++ /dev/null @@ -1,280 +0,0 @@ -use crate::syntax::ast::App; -use proc_macro2::TokenStream as TokenStream2; -use quote::quote; - -use crate::{analyze::Analysis, codegen::util}; - -/// Generates monotonic module dispatchers -pub fn codegen(app: &App, _analysis: &Analysis) -> TokenStream2 { - let mut monotonic_parts: Vec<_> = Vec::new(); - - let tq_marker = util::timer_queue_marker_ident(); - - for (_, monotonic) in &app.monotonics { - // let instants = util::monotonic_instants_ident(name, &monotonic.ident); - let monotonic_name = monotonic.ident.to_string(); - - let tq = util::tq_ident(&monotonic_name); - let m = &monotonic.ident; - let m_ident = util::monotonic_ident(&monotonic_name); - let m_isr = &monotonic.args.binds; - let enum_ = util::interrupt_ident(); - let name_str = &m.to_string(); - let ident = util::monotonic_ident(name_str); - let doc = &format!( - "This module holds the static implementation for `{}::now()`", - name_str - ); - - let (enable_interrupt, pend) = if &*m_isr.to_string() == "SysTick" { - ( - quote!(core::mem::transmute::<_, rtic::export::SYST>(()).enable_interrupt()), - quote!(rtic::export::SCB::set_pendst()), - ) - } else { - let rt_err = util::rt_err_ident(); - ( - quote!(rtic::export::NVIC::unmask(super::super::#rt_err::#enum_::#m_isr)), - quote!(rtic::pend(super::super::#rt_err::#enum_::#m_isr)), - ) - }; - - let default_monotonic = if monotonic.args.default { - quote!( - #[doc(inline)] - pub use #m::now; - #[doc(inline)] - pub use #m::delay; - #[doc(inline)] - pub use #m::delay_until; - #[doc(inline)] - pub use #m::timeout_at; - #[doc(inline)] - pub use #m::timeout_after; - ) - } else { - quote!() - }; - - monotonic_parts.push(quote! { - #default_monotonic - - #[doc = #doc] - #[allow(non_snake_case)] - pub mod #m { - /// Read the current time from this monotonic - pub fn now() -> ::Instant { - rtic::export::interrupt::free(|_| { - use rtic::Monotonic as _; - if let Some(m) = unsafe{ &mut *super::super::#ident.get_mut() } { - m.now() - } else { - ::zero() - } - }) - } - - /// Delay - #[inline(always)] - #[allow(non_snake_case)] - pub fn delay(duration: ::Duration) - -> DelayFuture { - let until = now() + duration; - DelayFuture { until, waker_storage: None } - } - - /// Delay until a specific time - #[inline(always)] - #[allow(non_snake_case)] - pub fn delay_until(instant: ::Instant) - -> DelayFuture { - let until = instant; - DelayFuture { until, waker_storage: None } - } - - /// Delay future. - #[allow(non_snake_case)] - #[allow(non_camel_case_types)] - pub struct DelayFuture { - until: ::Instant, - waker_storage: Option>>, - } - - impl Drop for DelayFuture { - fn drop(&mut self) { - if let Some(waker_storage) = &mut self.waker_storage { - rtic::export::interrupt::free(|_| unsafe { - let tq = &mut *super::super::#tq.get_mut(); - tq.cancel_waker_marker(waker_storage.val.marker); - }); - } - } - } - - impl core::future::Future for DelayFuture { - type Output = (); - - fn poll( - mut self: core::pin::Pin<&mut Self>, - cx: &mut core::task::Context<'_> - ) -> core::task::Poll { - let mut s = self.as_mut(); - let now = now(); - let until = s.until; - let is_ws_none = s.waker_storage.is_none(); - - if now >= until { - return core::task::Poll::Ready(()); - } else if is_ws_none { - rtic::export::interrupt::free(|_| unsafe { - let marker = super::super::#tq_marker.get().read(); - super::super::#tq_marker.get_mut().write(marker.wrapping_add(1)); - - let nr = s.waker_storage.insert(rtic::export::IntrusiveNode::new(rtic::export::WakerNotReady { - waker: cx.waker().clone(), - instant: until, - marker, - })); - - let tq = &mut *super::super::#tq.get_mut(); - - tq.enqueue_waker( - core::mem::transmute(nr), // Transmute the reference to static - || #enable_interrupt, - || #pend, - (&mut *super::super::#m_ident.get_mut()).as_mut()); - }); - } - - core::task::Poll::Pending - } - } - - /// Timeout future. - #[allow(non_snake_case)] - #[allow(non_camel_case_types)] - pub struct TimeoutFuture { - future: F, - until: ::Instant, - waker_storage: Option>>, - } - - impl Drop for TimeoutFuture { - fn drop(&mut self) { - if let Some(waker_storage) = &mut self.waker_storage { - rtic::export::interrupt::free(|_| unsafe { - let tq = &mut *super::super::#tq.get_mut(); - tq.cancel_waker_marker(waker_storage.val.marker); - }); - } - } - } - - /// Timeout after - #[allow(non_snake_case)] - #[inline(always)] - pub fn timeout_after( - future: F, - duration: ::Duration - ) -> TimeoutFuture { - let until = now() + duration; - TimeoutFuture { - future, - until, - waker_storage: None, - } - } - - /// Timeout at - #[allow(non_snake_case)] - #[inline(always)] - pub fn timeout_at( - future: F, - instant: ::Instant - ) -> TimeoutFuture { - TimeoutFuture { - future, - until: instant, - waker_storage: None, - } - } - - impl core::future::Future for TimeoutFuture - where - F: core::future::Future, - { - type Output = Result; - - fn poll( - self: core::pin::Pin<&mut Self>, - cx: &mut core::task::Context<'_> - ) -> core::task::Poll { - // SAFETY: We don't move the underlying pinned value. - let mut s = unsafe { self.get_unchecked_mut() }; - let future = unsafe { core::pin::Pin::new_unchecked(&mut s.future) }; - let now = now(); - let until = s.until; - let is_ws_none = s.waker_storage.is_none(); - - match future.poll(cx) { - core::task::Poll::Ready(r) => { - if let Some(waker_storage) = &mut s.waker_storage { - rtic::export::interrupt::free(|_| unsafe { - let tq = &mut *super::super::#tq.get_mut(); - tq.cancel_waker_marker(waker_storage.val.marker); - }); - } - - return core::task::Poll::Ready(Ok(r)); - } - core::task::Poll::Pending => { - if now >= until { - // Timeout - return core::task::Poll::Ready(Err(super::TimeoutError)); - } else if is_ws_none { - rtic::export::interrupt::free(|_| unsafe { - let marker = super::super::#tq_marker.get().read(); - super::super::#tq_marker.get_mut().write(marker.wrapping_add(1)); - - let nr = s.waker_storage.insert(rtic::export::IntrusiveNode::new(rtic::export::WakerNotReady { - waker: cx.waker().clone(), - instant: until, - marker, - })); - - let tq = &mut *super::super::#tq.get_mut(); - - tq.enqueue_waker( - core::mem::transmute(nr), // Transmute the reference to static - || #enable_interrupt, - || #pend, - (&mut *super::super::#m_ident.get_mut()).as_mut()); - }); - } - } - } - - core::task::Poll::Pending - } - } - } - }); - } - - if monotonic_parts.is_empty() { - quote!() - } else { - quote!( - pub use rtic::Monotonic as _; - - /// Holds static methods for each monotonic. - pub mod monotonics { - /// A timeout error. - #[derive(Debug)] - pub struct TimeoutError; - - #(#monotonic_parts)* - } - ) - } -} diff --git a/macros/src/codegen/post_init.rs b/macros/src/codegen/post_init.rs index df5daa1ec6..e8183b9368 100644 --- a/macros/src/codegen/post_init.rs +++ b/macros/src/codegen/post_init.rs @@ -1,7 +1,6 @@ use crate::syntax::ast::App; -use proc_macro2::{Span, TokenStream as TokenStream2}; +use proc_macro2::TokenStream as TokenStream2; use quote::quote; -use syn::Index; use crate::{analyze::Analysis, codegen::util}; @@ -43,23 +42,6 @@ pub fn codegen(app: &App, analysis: &Analysis) -> Vec { } } - for (i, (monotonic, _)) in app.monotonics.iter().enumerate() { - // For future use - // let doc = format!(" RTIC internal: {}:{}", file!(), line!()); - // stmts.push(quote!(#[doc = #doc])); - - #[allow(clippy::cast_possible_truncation)] - let idx = Index { - index: i as u32, - span: Span::call_site(), - }; - stmts.push(quote!(monotonics.#idx.reset();)); - - // Store the monotonic - let name = util::monotonic_ident(&monotonic.to_string()); - stmts.push(quote!(#name.get_mut().write(Some(monotonics.#idx));)); - } - // Enable the interrupts -- this completes the `init`-ialization phase stmts.push(quote!(rtic::export::interrupt::enable();)); diff --git a/macros/src/codegen/pre_init.rs b/macros/src/codegen/pre_init.rs index ef3acba76d..14926888ab 100644 --- a/macros/src/codegen/pre_init.rs +++ b/macros/src/codegen/pre_init.rs @@ -13,20 +13,6 @@ pub fn codegen(app: &App, analysis: &Analysis) -> Vec { // Disable interrupts -- `init` must run with interrupts disabled stmts.push(quote!(rtic::export::interrupt::disable();)); - // Populate the FreeQueue - for (name, task) in &app.software_tasks { - if task.is_async { - continue; - } - - let cap = task.args.capacity; - let fq_ident = util::fq_ident(name); - - stmts.push(quote!( - (0..#cap).for_each(|i| (&mut *#fq_ident.get_mut()).enqueue_unchecked(i)); - )); - } - stmts.push(quote!( // To set the variable in cortex_m so the peripherals cannot be taken multiple times let mut core: rtic::export::Peripherals = rtic::export::Peripherals::steal().into(); @@ -42,11 +28,7 @@ pub fn codegen(app: &App, analysis: &Analysis) -> Vec { stmts.push(quote!(let _ = #rt_err::#interrupt::#name;)); } - let interrupt_ids = analysis - .interrupts_normal - .iter() - .map(|(p, (id, _))| (p, id)) - .chain(analysis.interrupts_async.iter().map(|(p, (id, _))| (p, id))); + let interrupt_ids = analysis.interrupts.iter().map(|(p, (id, _))| (p, id)); // Unmask interrupts and set their priorities for (&priority, name) in interrupt_ids.chain(app.hardware_tasks.values().filter_map(|task| { @@ -101,53 +83,5 @@ pub fn codegen(app: &App, analysis: &Analysis) -> Vec { );)); } - // Initialize monotonic's interrupts - for (_, monotonic) in &app.monotonics { - let priority = if let Some(prio) = monotonic.args.priority { - quote! { #prio } - } else { - quote! { (1 << #nvic_prio_bits) } - }; - let binds = &monotonic.args.binds; - - let name = &monotonic.ident; - let es = format!( - "Maximum priority used by monotonic '{}' is more than supported by hardware", - name - ); - // Compile time assert that this priority is supported by the device - stmts.push(quote!( - const _: () = if (1 << #nvic_prio_bits) < #priority as usize { ::core::panic!(#es); }; - )); - - let mono_type = &monotonic.ty; - - if &*binds.to_string() == "SysTick" { - stmts.push(quote!( - core.SCB.set_priority( - rtic::export::SystemHandler::SysTick, - rtic::export::logical2hw(#priority, #nvic_prio_bits), - ); - - // Always enable monotonic interrupts if they should never be off - if !<#mono_type as rtic::Monotonic>::DISABLE_INTERRUPT_ON_EMPTY_QUEUE { - core::mem::transmute::<_, rtic::export::SYST>(()) - .enable_interrupt(); - } - )); - } else { - stmts.push(quote!( - core.NVIC.set_priority( - #rt_err::#interrupt::#binds, - rtic::export::logical2hw(#priority, #nvic_prio_bits), - ); - - // Always enable monotonic interrupts if they should never be off - if !<#mono_type as rtic::Monotonic>::DISABLE_INTERRUPT_ON_EMPTY_QUEUE { - rtic::export::NVIC::unmask(#rt_err::#interrupt::#binds); - } - )); - } - } stmts } diff --git a/macros/src/codegen/shared_resources.rs b/macros/src/codegen/shared_resources.rs index 66f3800234..b63e7432d6 100644 --- a/macros/src/codegen/shared_resources.rs +++ b/macros/src/codegen/shared_resources.rs @@ -111,11 +111,7 @@ pub fn codegen( }; // Computing mapping of used interrupts to masks - let interrupt_ids = analysis - .interrupts_normal - .iter() - .map(|(p, (id, _))| (p, id)) - .chain(analysis.interrupts_async.iter().map(|(p, (id, _))| (p, id))); + let interrupt_ids = analysis.interrupts.iter().map(|(p, (id, _))| (p, id)); let mut prio_to_masks = HashMap::new(); let device = &app.args.device; diff --git a/macros/src/codegen/software_tasks.rs b/macros/src/codegen/software_tasks.rs deleted file mode 100644 index f9247daed2..0000000000 --- a/macros/src/codegen/software_tasks.rs +++ /dev/null @@ -1,179 +0,0 @@ -use crate::syntax::{ast::App, Context}; -use crate::{ - analyze::Analysis, - codegen::{local_resources_struct, module, shared_resources_struct, util}, -}; -use proc_macro2::TokenStream as TokenStream2; -use quote::quote; - -pub fn codegen( - app: &App, - analysis: &Analysis, -) -> ( - // mod_app_software_tasks -- free queues, buffers and `${task}Resources` constructors - Vec, - // root_software_tasks -- items that must be placed in the root of the crate: - // - `${task}Locals` structs - // - `${task}Resources` structs - // - `${task}` modules - Vec, - // user_software_tasks -- the `#[task]` functions written by the user - Vec, -) { - let mut mod_app = vec![]; - let mut root = vec![]; - let mut user_tasks = vec![]; - - // Any task - for (name, task) in app.software_tasks.iter() { - let inputs = &task.inputs; - let (_, _, _, input_ty) = util::regroup_inputs(inputs); - - let cap = task.args.capacity; - let cap_lit = util::capacity_literal(cap as usize); - let cap_lit_p1 = util::capacity_literal(cap as usize + 1); - - if !task.is_async { - // Create free queues and inputs / instants buffers - let fq = util::fq_ident(name); - - #[allow(clippy::redundant_closure)] - let (fq_ty, fq_expr, mk_uninit): (_, _, Box Option<_>>) = { - ( - quote!(rtic::export::SCFQ<#cap_lit_p1>), - quote!(rtic::export::Queue::new()), - Box::new(|| Some(util::link_section_uninit())), - ) - }; - - mod_app.push(quote!( - // /// Queue version of a free-list that keeps track of empty slots in - // /// the following buffers - #[allow(non_camel_case_types)] - #[allow(non_upper_case_globals)] - #[doc(hidden)] - static #fq: rtic::RacyCell<#fq_ty> = rtic::RacyCell::new(#fq_expr); - )); - - let elems = &(0..cap) - .map(|_| quote!(core::mem::MaybeUninit::uninit())) - .collect::>(); - - for (_, monotonic) in &app.monotonics { - let instants = util::monotonic_instants_ident(name, &monotonic.ident); - let mono_type = &monotonic.ty; - - let uninit = mk_uninit(); - // For future use - // let doc = format!(" RTIC internal: {}:{}", file!(), line!()); - mod_app.push(quote!( - #uninit - // /// Buffer that holds the instants associated to the inputs of a task - // #[doc = #doc] - #[allow(non_camel_case_types)] - #[allow(non_upper_case_globals)] - #[doc(hidden)] - static #instants: - rtic::RacyCell<[core::mem::MaybeUninit<<#mono_type as rtic::Monotonic>::Instant>; #cap_lit]> = - rtic::RacyCell::new([#(#elems,)*]); - )); - } - - let uninit = mk_uninit(); - let inputs_ident = util::inputs_ident(name); - - // Buffer that holds the inputs of a task - mod_app.push(quote!( - #uninit - #[allow(non_camel_case_types)] - #[allow(non_upper_case_globals)] - #[doc(hidden)] - static #inputs_ident: rtic::RacyCell<[core::mem::MaybeUninit<#input_ty>; #cap_lit]> = - rtic::RacyCell::new([#(#elems,)*]); - )); - } - - if task.is_async { - let executor_ident = util::executor_run_ident(name); - mod_app.push(quote!( - #[allow(non_camel_case_types)] - #[allow(non_upper_case_globals)] - #[doc(hidden)] - static #executor_ident: core::sync::atomic::AtomicBool = - core::sync::atomic::AtomicBool::new(false); - )); - } - - let inputs = &task.inputs; - - // `${task}Resources` - let mut shared_needs_lt = false; - let mut local_needs_lt = false; - - // `${task}Locals` - if !task.args.local_resources.is_empty() { - let (item, constructor) = local_resources_struct::codegen( - Context::SoftwareTask(name), - &mut local_needs_lt, - app, - ); - - root.push(item); - - mod_app.push(constructor); - } - - if !task.args.shared_resources.is_empty() { - let (item, constructor) = shared_resources_struct::codegen( - Context::SoftwareTask(name), - &mut shared_needs_lt, - app, - ); - - root.push(item); - - mod_app.push(constructor); - } - - if !&task.is_extern { - let context = &task.context; - let attrs = &task.attrs; - let cfgs = &task.cfgs; - let stmts = &task.stmts; - let (async_marker, context_lifetime) = if task.is_async { - ( - quote!(async), - if shared_needs_lt || local_needs_lt { - quote!(<'static>) - } else { - quote!() - }, - ) - } else { - (quote!(), quote!()) - }; - - user_tasks.push(quote!( - #(#attrs)* - #(#cfgs)* - #[allow(non_snake_case)] - #async_marker fn #name(#context: #name::Context #context_lifetime #(,#inputs)*) { - use rtic::Mutex as _; - use rtic::mutex::prelude::*; - - #(#stmts)* - } - )); - } - - root.push(module::codegen( - Context::SoftwareTask(name), - shared_needs_lt, - local_needs_lt, - app, - analysis, - )); - } - - (mod_app, root, user_tasks) -} diff --git a/macros/src/codegen/timer_queue.rs b/macros/src/codegen/timer_queue.rs deleted file mode 100644 index 281148d9b4..0000000000 --- a/macros/src/codegen/timer_queue.rs +++ /dev/null @@ -1,170 +0,0 @@ -use crate::syntax::ast::App; -use crate::{analyze::Analysis, codegen::util}; -use proc_macro2::TokenStream as TokenStream2; -use quote::quote; - -/// Generates timer queues and timer queue handlers -#[allow(clippy::too_many_lines)] -pub fn codegen(app: &App, analysis: &Analysis) -> Vec { - let mut items = vec![]; - - if !app.monotonics.is_empty() { - // Generate the marker counter used to track for `cancel` and `reschedule` - let tq_marker = util::timer_queue_marker_ident(); - items.push(quote!( - // #[doc = #doc] - #[doc(hidden)] - #[allow(non_camel_case_types)] - #[allow(non_upper_case_globals)] - static #tq_marker: rtic::RacyCell = rtic::RacyCell::new(0); - )); - - let t = util::schedule_t_ident(); - - // Enumeration of `schedule`-able tasks - { - let variants = app - .software_tasks - .iter() - .filter(|(_, task)| !task.is_async) - .map(|(name, task)| { - let cfgs = &task.cfgs; - - quote!( - #(#cfgs)* - #name - ) - }) - .collect::>(); - - // For future use - // let doc = "Tasks that can be scheduled".to_string(); - items.push(quote!( - // #[doc = #doc] - #[doc(hidden)] - #[allow(non_camel_case_types)] - #[derive(Clone, Copy)] - pub enum #t { - #(#variants,)* - } - )); - } - } - - for (_, monotonic) in &app.monotonics { - let monotonic_name = monotonic.ident.to_string(); - let tq = util::tq_ident(&monotonic_name); - let t = util::schedule_t_ident(); - let mono_type = &monotonic.ty; - let m_ident = util::monotonic_ident(&monotonic_name); - - // Static variables and resource proxy - { - // For future use - // let doc = &format!("Timer queue for {}", monotonic_name); - let cap: usize = app - .software_tasks - .iter() - .map(|(_name, task)| task.args.capacity as usize) - .sum(); - let n_task = util::capacity_literal(cap); - let tq_ty = quote!(rtic::export::TimerQueue<#mono_type, #t, #n_task>); - - // For future use - // let doc = format!(" RTIC internal: {}:{}", file!(), line!()); - items.push(quote!( - #[doc(hidden)] - #[allow(non_camel_case_types)] - #[allow(non_upper_case_globals)] - static #tq: rtic::RacyCell<#tq_ty> = rtic::RacyCell::new( - rtic::export::TimerQueue { - task_queue: rtic::export::SortedLinkedList::new_u16(), - waker_queue: rtic::export::IntrusiveSortedLinkedList::new(), - } - ); - )); - - let mono = util::monotonic_ident(&monotonic_name); - // For future use - // let doc = &format!("Storage for {}", monotonic_name); - - items.push(quote!( - #[doc(hidden)] - #[allow(non_camel_case_types)] - #[allow(non_upper_case_globals)] - static #mono: rtic::RacyCell> = rtic::RacyCell::new(None); - )); - } - - // Timer queue handler - { - let enum_ = util::interrupt_ident(); - let rt_err = util::rt_err_ident(); - - let arms = app - .software_tasks - .iter() - .filter(|(_, task)| !task.is_async) - .map(|(name, task)| { - let cfgs = &task.cfgs; - let priority = task.args.priority; - let rq = util::rq_ident(priority); - let rqt = util::spawn_t_ident(priority); - - // The interrupt that runs the task dispatcher - let interrupt = &analysis.interrupts_normal.get(&priority).expect("RTIC-ICE: interrupt not found").0; - - let pend = { - quote!( - rtic::pend(#rt_err::#enum_::#interrupt); - ) - }; - - quote!( - #(#cfgs)* - #t::#name => { - rtic::export::interrupt::free(|_| - (&mut *#rq.get_mut()).split().0.enqueue_unchecked((#rqt::#name, index)) - ); - - #pend - } - ) - }) - .collect::>(); - - let bound_interrupt = &monotonic.args.binds; - let disable_isr = if &*bound_interrupt.to_string() == "SysTick" { - quote!(core::mem::transmute::<_, rtic::export::SYST>(()).disable_interrupt()) - } else { - quote!(rtic::export::NVIC::mask(#rt_err::#enum_::#bound_interrupt)) - }; - - items.push(quote!( - #[no_mangle] - #[allow(non_snake_case)] - unsafe fn #bound_interrupt() { - while let Some((task, index)) = rtic::export::interrupt::free(|_| - if let Some(mono) = (&mut *#m_ident.get_mut()).as_mut() { - (&mut *#tq.get_mut()).dequeue(|| #disable_isr, mono) - } else { - // 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() - }) - { - match task { - #(#arms)* - } - } - - rtic::export::interrupt::free(|_| if let Some(mono) = (&mut *#m_ident.get_mut()).as_mut() { - mono.on_interrupt(); - }); - } - )); - } - } - - items -} diff --git a/macros/src/codegen/util.rs b/macros/src/codegen/util.rs index 151906da5f..61bde98fa5 100644 --- a/macros/src/codegen/util.rs +++ b/macros/src/codegen/util.rs @@ -3,20 +3,10 @@ use core::sync::atomic::{AtomicUsize, Ordering}; use crate::syntax::{ast::App, Context}; use proc_macro2::{Span, TokenStream as TokenStream2}; use quote::quote; -use syn::{Attribute, Ident, LitInt, PatType}; +use syn::{Attribute, Ident}; const RTIC_INTERNAL: &str = "__rtic_internal"; -/// Turns `capacity` into an unsuffixed integer literal -pub fn capacity_literal(capacity: usize) -> LitInt { - LitInt::new(&capacity.to_string(), Span::call_site()) -} - -/// Identifier for the free queue -pub fn fq_ident(task: &Ident) -> Ident { - mark_internal_name(&format!("{}_FQ", task)) -} - /// Generates a `Mutex` implementation pub fn impl_mutex( app: &App, @@ -60,30 +50,16 @@ pub fn impl_mutex( ) } -/// Generates an identifier for the `INPUTS` buffer (`spawn` & `schedule` API) -pub fn inputs_ident(task: &Ident) -> Ident { - mark_internal_name(&format!("{}_INPUTS", task)) -} - /// Generates an identifier for the `EXECUTOR_RUN` atomics (`async` API) pub fn executor_run_ident(task: &Ident) -> Ident { mark_internal_name(&format!("{}_EXECUTOR_RUN", task)) } -/// Generates an identifier for the `INSTANTS` buffer (`schedule` API) -pub fn monotonic_instants_ident(task: &Ident, monotonic: &Ident) -> Ident { - mark_internal_name(&format!("{}_{}_INSTANTS", task, monotonic)) -} - pub fn interrupt_ident() -> Ident { let span = Span::call_site(); Ident::new("interrupt", span) } -pub fn timer_queue_marker_ident() -> Ident { - mark_internal_name("TIMER_QUEUE_MARKER") -} - /// Whether `name` is an exception with configurable priority pub fn is_exception(name: &Ident) -> bool { let s = name.to_string(); @@ -106,11 +82,6 @@ pub fn mark_internal_name(name: &str) -> Ident { Ident::new(&format!("{}_{}", RTIC_INTERNAL, name), Span::call_site()) } -/// Generate an internal identifier for monotonics -pub fn internal_monotonics_ident(task: &Ident, monotonic: &Ident, ident_name: &str) -> Ident { - mark_internal_name(&format!("{}_{}_{}", task, monotonic, ident_name,)) -} - /// Generate an internal identifier for tasks pub fn internal_task_ident(task: &Ident, ident_name: &str) -> Ident { mark_internal_name(&format!("{}_{}", task, ident_name)) @@ -129,55 +100,6 @@ pub fn link_section_uninit() -> TokenStream2 { quote!(#[link_section = #section]) } -// Regroups the inputs of a task -// -// `inputs` could be &[`input: Foo`] OR &[`mut x: i32`, `ref y: i64`] -pub fn regroup_inputs( - inputs: &[PatType], -) -> ( - // args e.g. &[`_0`], &[`_0: i32`, `_1: i64`] - Vec, - // tupled e.g. `_0`, `(_0, _1)` - TokenStream2, - // untupled e.g. &[`_0`], &[`_0`, `_1`] - Vec, - // ty e.g. `Foo`, `(i32, i64)` - TokenStream2, -) { - if inputs.len() == 1 { - let ty = &inputs[0].ty; - - ( - vec![quote!(_0: #ty)], - quote!(_0), - vec![quote!(_0)], - quote!(#ty), - ) - } else { - let mut args = vec![]; - let mut pats = vec![]; - let mut tys = vec![]; - - for (i, input) in inputs.iter().enumerate() { - let i = Ident::new(&format!("_{}", i), Span::call_site()); - let ty = &input.ty; - - args.push(quote!(#i: #ty)); - - pats.push(quote!(#i)); - - tys.push(quote!(#ty)); - } - - let tupled = { - let pats = pats.clone(); - quote!((#(#pats,)*)) - }; - let ty = quote!((#(#tys,)*)); - (args, tupled, pats, ty) - } -} - /// Get the ident for the name of the task pub fn get_task_name(ctxt: Context, app: &App) -> Ident { let s = match ctxt { @@ -230,48 +152,17 @@ pub fn local_resources_ident(ctxt: Context, app: &App) -> Ident { mark_internal_name(&s) } -/// Generates an identifier for a ready queue -/// -/// There may be several task dispatchers, one for each priority level. -/// The ready queues are SPSC queues -pub fn rq_ident(priority: u8) -> Ident { - mark_internal_name(&format!("P{}_RQ", priority)) -} - /// Generates an identifier for a ready queue, async task version pub fn rq_async_ident(async_task_name: &Ident) -> Ident { mark_internal_name(&format!("ASYNC_TACK_{}_RQ", async_task_name)) } -/// Generates an identifier for the `enum` of `schedule`-able tasks -pub fn schedule_t_ident() -> Ident { - mark_internal_name("SCHED_T") -} - -/// Generates an identifier for the `enum` of `spawn`-able tasks -/// -/// This identifier needs the same structure as the `RQ` identifier because there's one ready queue -/// for each of these `T` enums -pub fn spawn_t_ident(priority: u8) -> Ident { - mark_internal_name(&format!("P{}_T", priority)) -} - /// Suffixed identifier pub fn suffixed(name: &str) -> Ident { let span = Span::call_site(); Ident::new(name, span) } -/// Generates an identifier for a timer queue -pub fn tq_ident(name: &str) -> Ident { - mark_internal_name(&format!("TQ_{}", name)) -} - -/// Generates an identifier for monotonic timer storage -pub fn monotonic_ident(name: &str) -> Ident { - mark_internal_name(&format!("MONOTONIC_STORAGE_{}", name)) -} - pub fn static_shared_resource_ident(name: &Ident) -> Ident { mark_internal_name(&format!("shared_resource_{}", name)) } diff --git a/macros/src/syntax/analyze.rs b/macros/src/syntax/analyze.rs index 44960b9e8a..ff0577dacc 100644 --- a/macros/src/syntax/analyze.rs +++ b/macros/src/syntax/analyze.rs @@ -16,19 +16,11 @@ pub(crate) fn app(app: &App) -> Result { type TaskName = String; type Priority = u8; - // The task list is a Tuple (Name, Shared Resources, Local Resources, Priority, IsAsync) - let task_resources_list: Vec<(TaskName, Vec<&Ident>, &LocalResources, Priority, bool)> = + // The task list is a Tuple (Name, Shared Resources, Local Resources, Priority) + let task_resources_list: Vec<(TaskName, Vec<&Ident>, &LocalResources, Priority)> = Some(&app.init) .iter() - .map(|ht| { - ( - "init".to_string(), - Vec::new(), - &ht.args.local_resources, - 0, - false, - ) - }) + .map(|ht| ("init".to_string(), Vec::new(), &ht.args.local_resources, 0)) .chain(app.idle.iter().map(|ht| { ( "idle".to_string(), @@ -39,7 +31,6 @@ pub(crate) fn app(app: &App) -> Result { .collect::>(), &ht.args.local_resources, 0, - false, ) })) .chain(app.software_tasks.iter().map(|(name, ht)| { @@ -52,7 +43,6 @@ pub(crate) fn app(app: &App) -> Result { .collect::>(), &ht.args.local_resources, ht.args.priority, - ht.is_async, ) })) .chain(app.hardware_tasks.iter().map(|(name, ht)| { @@ -65,7 +55,6 @@ pub(crate) fn app(app: &App) -> Result { .collect::>(), &ht.args.local_resources, ht.args.priority, - false, ) })) .collect(); @@ -84,21 +73,20 @@ pub(crate) fn app(app: &App) -> Result { // Check that lock_free resources are correct for lf_res in lock_free.iter() { - for (task, tr, _, priority, is_async) in task_resources_list.iter() { + for (task, tr, _, priority) in task_resources_list.iter() { for r in tr { // Get all uses of resources annotated lock_free if lf_res == r { // lock_free resources are not allowed in async tasks - if *is_async { - error.push(syn::Error::new( + error.push(syn::Error::new( r.span(), format!( "Lock free shared resource {:?} is used by an async tasks, which is forbidden", r.to_string(), ), )); - } + // TODO: Should this be removed? // HashMap returns the previous existing object if old.key == new.key if let Some(lf_res) = lf_hash.insert(r.to_string(), (task, r, priority)) { // Check if priority differ, if it does, append to @@ -150,7 +138,7 @@ pub(crate) fn app(app: &App) -> Result { // Check that local resources are not shared for lr in local { - for (task, _, local_resources, _, _) in task_resources_list.iter() { + for (task, _, local_resources, _) in task_resources_list.iter() { for (name, res) in local_resources.iter() { // Get all uses of resources annotated lock_free if lr == name { @@ -193,18 +181,7 @@ pub(crate) fn app(app: &App) -> Result { error.push(syn::Error::new( name.span(), format!( - "Software task {:?} has priority 0, but `#[idle]` is defined. 0-priority software tasks are only allowed if there is no `#[idle]`.", - name.to_string(), - ) - )); - } - - // 0-priority tasks must be async - if !task.is_async { - error.push(syn::Error::new( - name.span(), - format!( - "Software task {:?} has priority 0, but is not `async`. 0-priority software tasks must be `async`.", + "Async task {:?} has priority 0, but `#[idle]` is defined. 0-priority async tasks are only allowed if there is no `#[idle]`.", name.to_string(), ) )); @@ -263,7 +240,7 @@ pub(crate) fn app(app: &App) -> Result { // Create the list of used local resource Idents let mut used_local_resource = IndexSet::new(); - for (_, _, locals, _, _) in task_resources_list { + for (_, _, locals, _) in task_resources_list { for (local, _) in locals { used_local_resource.insert(local.clone()); } @@ -307,27 +284,11 @@ pub(crate) fn app(app: &App) -> Result { let channel = channels.entry(spawnee_prio).or_default(); channel.tasks.insert(name.clone()); - - if !spawnee.args.only_same_priority_spawn { - // Require `Send` if the task can be spawned from other priorities - spawnee.inputs.iter().for_each(|input| { - send_types.insert(input.ty.clone()); - }); - } } // No channel should ever be empty debug_assert!(channels.values().all(|channel| !channel.tasks.is_empty())); - // Compute channel capacities - for channel in channels.values_mut() { - channel.capacity = channel - .tasks - .iter() - .map(|name| app.software_tasks[name].args.capacity) - .sum(); - } - Ok(Analysis { channels, shared_resources: used_shared_resource, diff --git a/macros/src/syntax/ast.rs b/macros/src/syntax/ast.rs index 0f2e36f44c..ea6e402c9a 100644 --- a/macros/src/syntax/ast.rs +++ b/macros/src/syntax/ast.rs @@ -1,6 +1,6 @@ //! Abstract Syntax Tree -use syn::{Attribute, Expr, Ident, Item, ItemUse, Pat, PatType, Path, Stmt, Type}; +use syn::{Attribute, Expr, Ident, Item, ItemUse, Pat, Path, Stmt, Type}; use crate::syntax::Map; @@ -20,9 +20,6 @@ pub struct App { /// The `#[idle]` function pub idle: Option, - /// Monotonic clocks - pub monotonics: Map, - /// Resources shared between tasks defined in `#[shared]` pub shared_resources: Map, @@ -38,7 +35,7 @@ pub struct App { /// Hardware tasks: `#[task(binds = ..)]`s pub hardware_tasks: Map, - /// Software tasks: `#[task]` + /// Async software tasks: `#[task]` pub software_tasks: Map, } @@ -192,38 +189,7 @@ pub struct LocalResource { pub ty: Box, } -/// Monotonic -#[derive(Debug)] -#[non_exhaustive] -pub struct Monotonic { - /// `#[cfg]` attributes like `#[cfg(debug_assertions)]` - pub cfgs: Vec, - - /// The identifier of the monotonic - pub ident: Ident, - - /// The type of this monotonic - pub ty: Box, - - /// Monotonic args - pub args: MonotonicArgs, -} - -/// Monotonic metadata -#[derive(Debug)] -#[non_exhaustive] -pub struct MonotonicArgs { - /// The interrupt or exception that this monotonic is bound to - pub binds: Ident, - - /// The priority of this monotonic - pub priority: Option, - - /// If this is the default monotonic - pub default: bool, -} - -/// A software task +/// An async software task #[derive(Debug)] #[non_exhaustive] pub struct SoftwareTask { @@ -239,26 +205,17 @@ pub struct SoftwareTask { /// The context argument pub context: Box, - /// The inputs of this software task - pub inputs: Vec, - /// The statements that make up the task handler pub stmts: Vec, /// The task is declared externally pub is_extern: bool, - - /// If the task is marked as `async` - pub is_async: bool, } /// Software task metadata #[derive(Debug)] #[non_exhaustive] pub struct SoftwareTaskArgs { - /// The task capacity: the maximum number of pending messages that can be queued - pub capacity: u8, - /// The priority of this task pub priority: u8, @@ -275,7 +232,6 @@ pub struct SoftwareTaskArgs { impl Default for SoftwareTaskArgs { fn default() -> Self { Self { - capacity: 1, priority: 1, local_resources: LocalResources::new(), shared_resources: SharedResources::new(), diff --git a/macros/src/syntax/parse.rs b/macros/src/syntax/parse.rs index ceedaa9891..abdd677ab8 100644 --- a/macros/src/syntax/parse.rs +++ b/macros/src/syntax/parse.rs @@ -2,7 +2,6 @@ mod app; mod hardware_task; mod idle; mod init; -mod monotonic; mod resource; mod software_task; mod util; @@ -11,15 +10,12 @@ use proc_macro2::TokenStream as TokenStream2; use syn::{ braced, parenthesized, parse::{self, Parse, ParseStream, Parser}, - token::{self, Brace}, - Ident, Item, LitBool, LitInt, Path, Token, + token::Brace, + Ident, Item, LitInt, Token, }; use crate::syntax::{ - ast::{ - App, AppArgs, HardwareTaskArgs, IdleArgs, InitArgs, MonotonicArgs, SoftwareTaskArgs, - TaskLocal, - }, + ast::{App, AppArgs, HardwareTaskArgs, IdleArgs, InitArgs, SoftwareTaskArgs, TaskLocal}, Either, }; @@ -388,7 +384,6 @@ fn task_args(tokens: TokenStream2) -> parse::Result parse::Result parse::Result { - (|input: ParseStream<'_>| -> parse::Result { - let mut binds = None; - let mut priority = None; - let mut default = None; - - if !input.peek(token::Paren) { - return Err(parse::Error::new( - path.segments.first().unwrap().ident.span(), - "expected opening ( in #[monotonic( ... )]", - )); - } - - let content; - parenthesized!(content in input); - - if !content.is_empty() { - loop { - // Parse identifier name - let ident: Ident = content.parse()?; - // Handle equal sign - let _: Token![=] = content.parse()?; - - match &*ident.to_string() { - "binds" => { - if binds.is_some() { - return Err(parse::Error::new( - ident.span(), - "argument appears more than once", - )); - } - // Parse identifier name - let ident = content.parse()?; - - binds = Some(ident); - } - - "priority" => { - if priority.is_some() { - return Err(parse::Error::new( - ident.span(), - "argument appears more than once", - )); - } - - // #lit - let lit: LitInt = content.parse()?; - - if !lit.suffix().is_empty() { - return Err(parse::Error::new( - lit.span(), - "this literal must be unsuffixed", - )); - } - - let value = lit.base10_parse::().ok(); - if value.is_none() || value == Some(0) { - return Err(parse::Error::new( - lit.span(), - "this literal must be in the range 1...255", - )); - } - - priority = Some(value.unwrap()); - } - - "default" => { - if default.is_some() { - return Err(parse::Error::new( - ident.span(), - "argument appears more than once", - )); - } - - let lit: LitBool = content.parse()?; - default = Some(lit.value); - } - - _ => { - return Err(parse::Error::new(ident.span(), "unexpected argument")); - } - } - if content.is_empty() { - break; - } - - // Handle comma: , - let _: Token![,] = content.parse()?; - } - } - - let binds = if let Some(r) = binds { - r - } else { - return Err(parse::Error::new( - content.span(), - "`binds = ...` is missing", - )); - }; - let default = default.unwrap_or(false); - - Ok(MonotonicArgs { - binds, - priority, - default, - }) - }) - .parse2(tokens) -} diff --git a/macros/src/syntax/parse/app.rs b/macros/src/syntax/parse/app.rs index dd7c399908..8a9242e91d 100644 --- a/macros/src/syntax/parse/app.rs +++ b/macros/src/syntax/parse/app.rs @@ -5,14 +5,14 @@ use proc_macro2::TokenStream as TokenStream2; use syn::{ parse::{self, ParseStream, Parser}, spanned::Spanned, - Expr, ExprArray, Fields, ForeignItem, Ident, Item, LitBool, Path, Token, Type, Visibility, + Expr, ExprArray, Fields, ForeignItem, Ident, Item, LitBool, Path, Token, Visibility, }; use super::Input; use crate::syntax::{ ast::{ App, AppArgs, Dispatcher, Dispatchers, HardwareTask, Idle, IdleArgs, Init, InitArgs, - LocalResource, Monotonic, MonotonicArgs, SharedResource, SoftwareTask, + LocalResource, SharedResource, SoftwareTask, }, parse::{self as syntax_parse, util}, Either, Map, Set, @@ -150,7 +150,6 @@ impl App { let mut shared_resources = Map::new(); let mut local_resources_ident = None; let mut local_resources = Map::new(); - let mut monotonics = Map::new(); let mut hardware_tasks = Map::new(); let mut software_tasks = Map::new(); let mut user_imports = vec![]; @@ -158,7 +157,6 @@ impl App { let mut seen_idents = HashSet::::new(); let mut bindings = HashSet::::new(); - let mut monotonic_types = HashSet::::new(); let mut check_binding = |ident: &Ident| { if bindings.contains(ident) { @@ -186,19 +184,6 @@ impl App { Ok(()) }; - let mut check_monotonic = |ty: &Type| { - if monotonic_types.contains(ty) { - return Err(parse::Error::new( - ty.span(), - "this type is already used by another monotonic", - )); - } else { - monotonic_types.insert(ty.clone()); - } - - Ok(()) - }; - for mut item in input.items { match item { Item::Fn(mut item) => { @@ -448,44 +433,6 @@ impl App { // Store the user provided use-statements user_imports.push(itemuse_.clone()); } - Item::Type(ref mut type_item) => { - // Match types with the attribute #[monotonic] - if let Some(pos) = type_item - .attrs - .iter() - .position(|attr| util::attr_eq(attr, "monotonic")) - { - let span = type_item.ident.span(); - - if monotonics.contains_key(&type_item.ident) { - return Err(parse::Error::new( - span, - "`#[monotonic(...)]` on a specific type must appear at most once", - )); - } - - if type_item.vis != Visibility::Inherited { - return Err(parse::Error::new( - type_item.span(), - "this item must have inherited / private visibility", - )); - } - - check_monotonic(&*type_item.ty)?; - - let m = type_item.attrs.remove(pos); - let args = MonotonicArgs::parse(m)?; - - check_binding(&args.binds)?; - - let monotonic = Monotonic::parse(args, type_item, span)?; - - monotonics.insert(type_item.ident.clone(), monotonic); - } - - // All types are passed on - user_code.push(item.clone()); - } _ => { // Anything else within the module should not make any difference user_code.push(item.clone()); @@ -524,7 +471,6 @@ impl App { name: input.ident, init, idle, - monotonics, shared_resources, local_resources, user_imports, diff --git a/macros/src/syntax/parse/hardware_task.rs b/macros/src/syntax/parse/hardware_task.rs index 304bfcd3f0..ff94bc5190 100644 --- a/macros/src/syntax/parse/hardware_task.rs +++ b/macros/src/syntax/parse/hardware_task.rs @@ -23,19 +23,17 @@ impl HardwareTask { } if valid_signature { - if let Some((context, Ok(rest))) = util::parse_inputs(item.sig.inputs, &name) { - if rest.is_empty() { - let FilterAttrs { cfgs, attrs, .. } = util::filter_attributes(item.attrs); + if let Some(context) = util::parse_inputs(item.sig.inputs, &name) { + let FilterAttrs { cfgs, attrs, .. } = util::filter_attributes(item.attrs); - return Ok(HardwareTask { - args, - cfgs, - attrs, - context, - stmts: item.block.stmts, - is_extern: false, - }); - } + return Ok(HardwareTask { + args, + cfgs, + attrs, + context, + stmts: item.block.stmts, + is_extern: false, + }); } } @@ -69,19 +67,17 @@ impl HardwareTask { } if valid_signature { - if let Some((context, Ok(rest))) = util::parse_inputs(item.sig.inputs, &name) { - if rest.is_empty() { - let FilterAttrs { cfgs, attrs, .. } = util::filter_attributes(item.attrs); + if let Some(context) = util::parse_inputs(item.sig.inputs, &name) { + let FilterAttrs { cfgs, attrs, .. } = util::filter_attributes(item.attrs); - return Ok(HardwareTask { - args, - cfgs, - attrs, - context, - stmts: Vec::::new(), - is_extern: true, - }); - } + return Ok(HardwareTask { + args, + cfgs, + attrs, + context, + stmts: Vec::::new(), + is_extern: true, + }); } } diff --git a/macros/src/syntax/parse/idle.rs b/macros/src/syntax/parse/idle.rs index d9f3a99e6f..ffec358fc4 100644 --- a/macros/src/syntax/parse/idle.rs +++ b/macros/src/syntax/parse/idle.rs @@ -21,16 +21,14 @@ impl Idle { let name = item.sig.ident.to_string(); if valid_signature { - if let Some((context, Ok(rest))) = util::parse_inputs(item.sig.inputs, &name) { - if rest.is_empty() { - return Ok(Idle { - args, - attrs: item.attrs, - context, - name: item.sig.ident, - stmts: item.block.stmts, - }); - } + if let Some(context) = util::parse_inputs(item.sig.inputs, &name) { + return Ok(Idle { + args, + attrs: item.attrs, + context, + name: item.sig.ident, + stmts: item.block.stmts, + }); } } diff --git a/macros/src/syntax/parse/init.rs b/macros/src/syntax/parse/init.rs index 727ee20508..5ec1abaf46 100644 --- a/macros/src/syntax/parse/init.rs +++ b/macros/src/syntax/parse/init.rs @@ -25,18 +25,16 @@ impl Init { if let Ok((user_shared_struct, user_local_struct)) = util::type_is_init_return(&item.sig.output, &name) { - if let Some((context, Ok(rest))) = util::parse_inputs(item.sig.inputs, &name) { - if rest.is_empty() { - return Ok(Init { - args, - attrs: item.attrs, - context, - name: item.sig.ident, - stmts: item.block.stmts, - user_shared_struct, - user_local_struct, - }); - } + if let Some(context) = util::parse_inputs(item.sig.inputs, &name) { + return Ok(Init { + args, + attrs: item.attrs, + context, + name: item.sig.ident, + stmts: item.block.stmts, + user_shared_struct, + user_local_struct, + }); } } } diff --git a/macros/src/syntax/parse/software_task.rs b/macros/src/syntax/parse/software_task.rs index 2b1ac4a5b4..6be597e8fb 100644 --- a/macros/src/syntax/parse/software_task.rs +++ b/macros/src/syntax/parse/software_task.rs @@ -8,17 +8,16 @@ use crate::syntax::{ impl SoftwareTask { pub(crate) fn parse(args: SoftwareTaskArgs, item: ItemFn) -> parse::Result { - let valid_signature = - util::check_fn_signature(&item, true) && util::type_is_unit(&item.sig.output); + let valid_signature = util::check_fn_signature(&item, true) + && util::type_is_unit(&item.sig.output) + && item.sig.asyncness.is_some(); let span = item.sig.ident.span(); let name = item.sig.ident.to_string(); - let is_async = item.sig.asyncness.is_some(); - if valid_signature { - if let Some((context, Ok(inputs))) = util::parse_inputs(item.sig.inputs, &name) { + if let Some(context) = util::parse_inputs(item.sig.inputs, &name) { let FilterAttrs { cfgs, attrs, .. } = util::filter_attributes(item.attrs); return Ok(SoftwareTask { @@ -26,10 +25,8 @@ impl SoftwareTask { attrs, cfgs, context, - inputs, stmts: item.block.stmts, is_extern: false, - is_async, }); } } @@ -37,7 +34,7 @@ impl SoftwareTask { Err(parse::Error::new( span, &format!( - "this task handler must have type signature `(async) fn({}::Context, ..)`", + "this task handler must have type signature `async fn({}::Context)`", name ), )) @@ -49,17 +46,16 @@ impl SoftwareTask { args: SoftwareTaskArgs, item: ForeignItemFn, ) -> parse::Result { - let valid_signature = - util::check_foreign_fn_signature(&item, true) && util::type_is_unit(&item.sig.output); + let valid_signature = util::check_foreign_fn_signature(&item, true) + && util::type_is_unit(&item.sig.output) + && item.sig.asyncness.is_some(); let span = item.sig.ident.span(); let name = item.sig.ident.to_string(); - let is_async = item.sig.asyncness.is_some(); - if valid_signature { - if let Some((context, Ok(inputs))) = util::parse_inputs(item.sig.inputs, &name) { + if let Some(context) = util::parse_inputs(item.sig.inputs, &name) { let FilterAttrs { cfgs, attrs, .. } = util::filter_attributes(item.attrs); return Ok(SoftwareTask { @@ -67,10 +63,8 @@ impl SoftwareTask { attrs, cfgs, context, - inputs, stmts: Vec::::new(), is_extern: true, - is_async, }); } } @@ -78,7 +72,7 @@ impl SoftwareTask { Err(parse::Error::new( span, &format!( - "this task handler must have type signature `(async) fn({}::Context, ..)`", + "this task handler must have type signature `async fn({}::Context)`", name ), )) diff --git a/macros/src/syntax/parse/util.rs b/macros/src/syntax/parse/util.rs index 3fa51ef803..119129c0ae 100644 --- a/macros/src/syntax/parse/util.rs +++ b/macros/src/syntax/parse/util.rs @@ -3,8 +3,8 @@ use syn::{ parse::{self, ParseStream}, punctuated::Punctuated, spanned::Spanned, - Abi, AttrStyle, Attribute, Expr, FnArg, ForeignItemFn, Ident, ItemFn, Pat, PatType, Path, - PathArguments, ReturnType, Token, Type, Visibility, + Abi, AttrStyle, Attribute, Expr, FnArg, ForeignItemFn, Ident, ItemFn, Pat, Path, PathArguments, + ReturnType, Token, Type, Visibility, }; use crate::syntax::{ @@ -231,29 +231,23 @@ pub fn parse_local_resources(content: ParseStream<'_>) -> parse::Result, Result, FnArg>)>; - -pub fn parse_inputs(inputs: Punctuated, name: &str) -> ParseInputResult { +pub fn parse_inputs(inputs: Punctuated, name: &str) -> Option> { let mut inputs = inputs.into_iter(); match inputs.next() { Some(FnArg::Typed(first)) => { if type_is_path(&first.ty, &[name, "Context"]) { - let rest = inputs - .map(|arg| match arg { - FnArg::Typed(arg) => Ok(arg), - _ => Err(arg), - }) - .collect::, _>>(); - - Some((first.pat, rest)) - } else { - None + // No more inputs + if inputs.next().is_none() { + return Some(first.pat); + } } } - _ => None, + _ => {} } + + None } pub fn type_is_bottom(ty: &ReturnType) -> bool { From d27d0fe33fdb54e6a11a1e9d09a7916f19e5c9ec Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Wed, 4 Jan 2023 20:01:05 +0100 Subject: [PATCH 217/423] Added software task codegen back --- macros/src/codegen.rs | 11 ++- macros/src/codegen/software_tasks.rs | 101 +++++++++++++++++++++++++++ 2 files changed, 111 insertions(+), 1 deletion(-) create mode 100644 macros/src/codegen/software_tasks.rs diff --git a/macros/src/codegen.rs b/macros/src/codegen.rs index 618d9f3a68..6460afec1d 100644 --- a/macros/src/codegen.rs +++ b/macros/src/codegen.rs @@ -18,7 +18,7 @@ mod post_init; mod pre_init; mod shared_resources; mod shared_resources_struct; -// mod software_tasks; +mod software_tasks; // mod timer_queue; mod util; @@ -92,6 +92,9 @@ pub fn app(app: &App, analysis: &Analysis) -> TokenStream2 { let (mod_app_hardware_tasks, root_hardware_tasks, user_hardware_tasks) = hardware_tasks::codegen(app, analysis); + let (mod_app_software_tasks, root_software_tasks, user_software_tasks) = + software_tasks::codegen(app, analysis); + let mod_app_async_dispatchers = async_dispatchers::codegen(app, analysis); let user_imports = &app.user_imports; let user_code = &app.user_code; @@ -116,6 +119,8 @@ pub fn app(app: &App, analysis: &Analysis) -> TokenStream2 { #(#user_hardware_tasks)* + #(#user_software_tasks)* + #(#root)* #mod_shared_resources @@ -124,6 +129,8 @@ pub fn app(app: &App, analysis: &Analysis) -> TokenStream2 { #(#root_hardware_tasks)* + #(#root_software_tasks)* + /// app module #(#mod_app)* @@ -133,6 +140,8 @@ pub fn app(app: &App, analysis: &Analysis) -> TokenStream2 { #(#mod_app_hardware_tasks)* + #(#mod_app_software_tasks)* + #(#mod_app_async_dispatchers)* #(#mains)* diff --git a/macros/src/codegen/software_tasks.rs b/macros/src/codegen/software_tasks.rs new file mode 100644 index 0000000000..b2b468ca9a --- /dev/null +++ b/macros/src/codegen/software_tasks.rs @@ -0,0 +1,101 @@ +use crate::syntax::{ast::App, Context}; +use crate::{ + analyze::Analysis, + codegen::{local_resources_struct, module, shared_resources_struct, util}, +}; +use proc_macro2::TokenStream as TokenStream2; +use quote::quote; + +pub fn codegen( + app: &App, + analysis: &Analysis, +) -> ( + // mod_app_software_tasks -- free queues, buffers and `${task}Resources` constructors + Vec, + // root_software_tasks -- items that must be placed in the root of the crate: + // - `${task}Locals` structs + // - `${task}Resources` structs + // - `${task}` modules + Vec, + // user_software_tasks -- the `#[task]` functions written by the user + Vec, +) { + let mut mod_app = vec![]; + let mut root = vec![]; + let mut user_tasks = vec![]; + + // Any task + for (name, task) in app.software_tasks.iter() { + let executor_ident = util::executor_run_ident(name); + mod_app.push(quote!( + #[allow(non_camel_case_types)] + #[allow(non_upper_case_globals)] + #[doc(hidden)] + static #executor_ident: core::sync::atomic::AtomicBool = + core::sync::atomic::AtomicBool::new(false); + )); + + // `${task}Resources` + let mut shared_needs_lt = false; + let mut local_needs_lt = false; + + // `${task}Locals` + if !task.args.local_resources.is_empty() { + let (item, constructor) = local_resources_struct::codegen( + Context::SoftwareTask(name), + &mut local_needs_lt, + app, + ); + + root.push(item); + + mod_app.push(constructor); + } + + if !task.args.shared_resources.is_empty() { + let (item, constructor) = shared_resources_struct::codegen( + Context::SoftwareTask(name), + &mut shared_needs_lt, + app, + ); + + root.push(item); + + mod_app.push(constructor); + } + + if !&task.is_extern { + let context = &task.context; + let attrs = &task.attrs; + let cfgs = &task.cfgs; + let stmts = &task.stmts; + let context_lifetime = if shared_needs_lt || local_needs_lt { + quote!(<'static>) + } else { + quote!() + }; + + user_tasks.push(quote!( + #(#attrs)* + #(#cfgs)* + #[allow(non_snake_case)] + async fn #name(#context: #name::Context #context_lifetime) { + use rtic::Mutex as _; + use rtic::mutex::prelude::*; + + #(#stmts)* + } + )); + } + + root.push(module::codegen( + Context::SoftwareTask(name), + shared_needs_lt, + local_needs_lt, + app, + analysis, + )); + } + + (mod_app, root, user_tasks) +} From 5c3cedf69ad07cb8522a0b040bdbf1f9ca4cf37b Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Wed, 4 Jan 2023 20:29:16 +0100 Subject: [PATCH 218/423] Fix fences --- macros/src/codegen/async_dispatchers.rs | 5 +++++ macros/src/codegen/module.rs | 10 ++-------- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/macros/src/codegen/async_dispatchers.rs b/macros/src/codegen/async_dispatchers.rs index aa854d7f8e..c8116654e1 100644 --- a/macros/src/codegen/async_dispatchers.rs +++ b/macros/src/codegen/async_dispatchers.rs @@ -101,7 +101,12 @@ pub fn codegen(app: &App, analysis: &Analysis) -> Vec { const PRIORITY: u8 = #level; rtic::export::run(PRIORITY, || { + // Have the acquire/release semantics outside the checks to no overdo it + core::sync::atomic::fence(core::sync::atomic::Ordering::Acquire); + #(#stmts)* + + core::sync::atomic::fence(core::sync::atomic::Ordering::Release); }); } )); diff --git a/macros/src/codegen/module.rs b/macros/src/codegen/module.rs index eb0cb65ba0..b4ad68aad8 100644 --- a/macros/src/codegen/module.rs +++ b/macros/src/codegen/module.rs @@ -111,14 +111,8 @@ pub fn codegen( let v = Vec::new(); let cfgs = match ctxt { - Context::HardwareTask(t) => { - &app.hardware_tasks[t].cfgs - // ... - } - Context::SoftwareTask(t) => { - &app.software_tasks[t].cfgs - // ... - } + Context::HardwareTask(t) => &app.hardware_tasks[t].cfgs, + Context::SoftwareTask(t) => &app.software_tasks[t].cfgs, _ => &v, }; From 858320cbfc391a74bff6b9c8a0b3c7696a232b76 Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Wed, 4 Jan 2023 21:08:44 +0100 Subject: [PATCH 219/423] Even more cleanup --- macros/src/codegen.rs | 3 -- macros/src/codegen/init.rs | 5 +- macros/src/syntax/parse/init.rs | 4 +- macros/src/syntax/parse/monotonic.rs | 42 ----------------- macros/src/syntax/parse/util.rs | 6 +-- src/export.rs | 70 ---------------------------- 6 files changed, 7 insertions(+), 123 deletions(-) delete mode 100644 macros/src/syntax/parse/monotonic.rs diff --git a/macros/src/codegen.rs b/macros/src/codegen.rs index 6460afec1d..0f68c34731 100644 --- a/macros/src/codegen.rs +++ b/macros/src/codegen.rs @@ -6,20 +6,17 @@ use crate::syntax::ast::App; mod assertions; mod async_dispatchers; -// mod dispatchers; mod hardware_tasks; mod idle; mod init; mod local_resources; mod local_resources_struct; mod module; -// mod monotonic; mod post_init; mod pre_init; mod shared_resources; mod shared_resources_struct; mod software_tasks; -// mod timer_queue; mod util; #[allow(clippy::too_many_lines)] diff --git a/macros/src/codegen/init.rs b/macros/src/codegen/init.rs index 9a6fe2d522..c7b871234e 100644 --- a/macros/src/codegen/init.rs +++ b/macros/src/codegen/init.rs @@ -76,7 +76,7 @@ pub fn codegen(app: &App, analysis: &Analysis) -> CodegenResult { // let locals_pat = locals_pat.iter(); - let user_init_return = quote! {#shared, #local, #name::Monotonics}; + let user_init_return = quote! {#shared, #local}; let user_init = quote!( #(#attrs)* @@ -99,9 +99,8 @@ pub fn codegen(app: &App, analysis: &Analysis) -> CodegenResult { mod_app = Some(constructor); } - // let locals_new = locals_new.iter(); let call_init = quote! { - let (shared_resources, local_resources, mut monotonics) = #name(#name::Context::new(core.into())); + let (shared_resources, local_resources) = #name(#name::Context::new(core.into())); }; root_init.push(module::codegen( diff --git a/macros/src/syntax/parse/init.rs b/macros/src/syntax/parse/init.rs index 5ec1abaf46..61d35391fb 100644 --- a/macros/src/syntax/parse/init.rs +++ b/macros/src/syntax/parse/init.rs @@ -23,7 +23,7 @@ impl Init { if valid_signature { if let Ok((user_shared_struct, user_local_struct)) = - util::type_is_init_return(&item.sig.output, &name) + util::type_is_init_return(&item.sig.output) { if let Some(context) = util::parse_inputs(item.sig.inputs, &name) { return Ok(Init { @@ -42,7 +42,7 @@ impl Init { Err(parse::Error::new( span, &format!( - "the `#[init]` function must have signature `fn({}::Context) -> (Shared resources struct, Local resources struct, {0}::Monotonics)`", + "the `#[init]` function must have signature `fn({}::Context) -> (Shared resources struct, Local resources struct)`", name ), )) diff --git a/macros/src/syntax/parse/monotonic.rs b/macros/src/syntax/parse/monotonic.rs deleted file mode 100644 index 05832339b7..0000000000 --- a/macros/src/syntax/parse/monotonic.rs +++ /dev/null @@ -1,42 +0,0 @@ -use proc_macro2::Span; -use syn::Attribute; -use syn::{parse, spanned::Spanned, ItemType, Visibility}; - -use crate::syntax::parse::util::FilterAttrs; -use crate::syntax::{ - ast::{Monotonic, MonotonicArgs}, - parse::util, -}; - -impl MonotonicArgs { - pub(crate) fn parse(attr: Attribute) -> parse::Result { - crate::syntax::parse::monotonic_args(attr.path, attr.tokens) - } -} - -impl Monotonic { - pub(crate) fn parse(args: MonotonicArgs, item: &ItemType, span: Span) -> parse::Result { - if item.vis != Visibility::Inherited { - return Err(parse::Error::new( - span, - "this field must have inherited / private visibility", - )); - } - - let FilterAttrs { cfgs, attrs, .. } = util::filter_attributes(item.attrs.clone()); - - if !attrs.is_empty() { - return Err(parse::Error::new( - attrs[0].path.span(), - "Monotonic does not support attributes other than `#[cfg]`", - )); - } - - Ok(Monotonic { - cfgs, - ident: item.ident.clone(), - ty: item.ty.clone(), - args, - }) - } -} diff --git a/macros/src/syntax/parse/util.rs b/macros/src/syntax/parse/util.rs index 119129c0ae..28c3eac6b6 100644 --- a/macros/src/syntax/parse/util.rs +++ b/macros/src/syntax/parse/util.rs @@ -277,18 +277,18 @@ fn extract_init_resource_name_ident(ty: Type) -> Result { } /// Checks Init's return type, return the user provided types for analysis -pub fn type_is_init_return(ty: &ReturnType, name: &str) -> Result<(Ident, Ident), ()> { +pub fn type_is_init_return(ty: &ReturnType) -> Result<(Ident, Ident), ()> { match ty { ReturnType::Default => Err(()), ReturnType::Type(_, ty) => match &**ty { Type::Tuple(t) => { // return should be: - // fn -> (User's #[shared] struct, User's #[local] struct, {name}::Monotonics) + // fn -> (User's #[shared] struct, User's #[local] struct) // // We check the length and the last one here, analysis checks that the user // provided structs are correct. - if t.elems.len() == 3 && type_is_path(&t.elems[2], &[name, "Monotonics"]) { + if t.elems.len() == 2 { return Ok(( extract_init_resource_name_ident(t.elems[0].clone())?, extract_init_resource_name_ident(t.elems[1].clone())?, diff --git a/src/export.rs b/src/export.rs index da4a6917b4..82320fbbcb 100644 --- a/src/export.rs +++ b/src/export.rs @@ -15,65 +15,6 @@ pub use cortex_m::{ peripheral::{scb::SystemHandler, DWT, NVIC, SCB, SYST}, Peripherals, }; -pub use heapless::sorted_linked_list::SortedLinkedList; -pub use heapless::spsc::Queue; -pub use heapless::BinaryHeap; -pub use heapless::Vec; -pub use rtic_monotonic as monotonic; - -pub mod idle_executor { - use core::{ - future::Future, - pin::Pin, - task::{Context, Poll, RawWaker, RawWakerVTable, Waker}, - }; - - fn no_op(_: *const ()) {} - fn no_op_clone(_: *const ()) -> RawWaker { - noop_raw_waker() - } - - static IDLE_WAKER_TABLE: RawWakerVTable = RawWakerVTable::new(no_op_clone, no_op, no_op, no_op); - - #[inline] - fn noop_raw_waker() -> RawWaker { - RawWaker::new(core::ptr::null(), &IDLE_WAKER_TABLE) - } - - pub struct IdleExecutor - where - T: Future, - { - idle: T, - } - - impl IdleExecutor - where - T: Future, - { - #[inline(always)] - pub fn new(idle: T) -> Self { - Self { idle } - } - - #[inline(always)] - pub fn run(&mut self) -> ! { - let w = unsafe { Waker::from_raw(noop_raw_waker()) }; - let mut ctxt = Context::from_waker(&w); - loop { - match unsafe { Pin::new_unchecked(&mut self.idle) }.poll(&mut ctxt) { - Poll::Pending => { - // All ok! - } - Poll::Ready(_) => { - // The idle executor will never return - unreachable!() - } - } - } - } - } -} pub mod executor { use core::{ @@ -143,10 +84,6 @@ pub mod executor { } } -pub type SCFQ = Queue; -pub type SCRQ = Queue<(T, u8), N>; -pub type ASYNCRQ = Queue; - /// Mask is used to store interrupt masks on systems without a BASEPRI register (M0, M0+, M23). /// It needs to be large enough to cover all the relevant interrupts in use. /// For M0/M0+ there are only 32 interrupts so we only need one u32 value. @@ -290,13 +227,6 @@ where { } -#[inline(always)] -pub fn assert_monotonic() -where - T: monotonic::Monotonic, -{ -} - /// Lock implementation using BASEPRI and global Critical Section (CS) /// /// # Safety From 3b97531a5c40293e265999db543acec365c629df Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Wed, 4 Jan 2023 21:33:41 +0100 Subject: [PATCH 220/423] First example builds again --- examples/async-task.rs | 20 +++----------------- macros/src/codegen/async_dispatchers.rs | 15 ++++++--------- macros/src/codegen/module.rs | 11 ++++++----- macros/src/codegen/util.rs | 2 +- rust-toolchain.toml | 4 ++++ 5 files changed, 20 insertions(+), 32 deletions(-) create mode 100644 rust-toolchain.toml diff --git a/examples/async-task.rs b/examples/async-task.rs index 4d25ec4401..7d0ee86e03 100644 --- a/examples/async-task.rs +++ b/examples/async-task.rs @@ -13,7 +13,6 @@ use panic_semihosting as _; #[rtic::app(device = lm3s6965, dispatchers = [SSI0, UART0], peripherals = true)] mod app { use cortex_m_semihosting::{debug, hprintln}; - use systick_monotonic::*; #[shared] struct Shared {} @@ -21,21 +20,13 @@ mod app { #[local] struct Local {} - #[monotonic(binds = SysTick, default = true)] - type MyMono = Systick<100>; - #[init] - fn init(cx: init::Context) -> (Shared, Local, init::Monotonics) { + fn init(cx: init::Context) -> (Shared, Local) { hprintln!("init").unwrap(); - normal_task::spawn().ok(); - async_task::spawn().ok(); + async_task::spawn().unwrap(); - ( - Shared {}, - Local {}, - init::Monotonics(Systick::new(cx.core.SYST, 12_000_000)), - ) + (Shared {}, Local {}) } #[idle] @@ -47,11 +38,6 @@ mod app { } } - #[task] - fn normal_task(_cx: normal_task::Context) { - hprintln!("hello from normal").ok(); - } - #[task] async fn async_task(_cx: async_task::Context) { hprintln!("hello from async").ok(); diff --git a/macros/src/codegen/async_dispatchers.rs b/macros/src/codegen/async_dispatchers.rs index c8116654e1..f428cef04f 100644 --- a/macros/src/codegen/async_dispatchers.rs +++ b/macros/src/codegen/async_dispatchers.rs @@ -45,23 +45,20 @@ pub fn codegen(app: &App, analysis: &Analysis) -> Vec { let executor_run_ident = util::executor_run_ident(name); let rq = util::rq_async_ident(name); - let (rq_ty, rq_expr) = { - ( - quote!(rtic::export::ASYNCRQ<(), 2>), // TODO: This needs updating to a counter instead of a queue - quote!(rtic::export::Queue::new()), - ) - }; items.push(quote!( #[doc(hidden)] #[allow(non_camel_case_types)] #[allow(non_upper_case_globals)] - static #rq: rtic::RacyCell<#rq_ty> = rtic::RacyCell::new(#rq_expr); + static #rq: core::sync::atomic::AtomicBool = core::sync::atomic::AtomicBool::new(false); )); stmts.push(quote!( if !(&*#exec_name.get()).is_running() { - if let Some(()) = rtic::export::interrupt::free(|_| (&mut *#rq.get_mut()).dequeue()) { + // TODO Fix this to be compare and swap + if #rq.load(core::sync::atomic::Ordering::Relaxed) { + #rq.store(false, core::sync::atomic::Ordering::Relaxed); + // The async executor needs a static priority #prio_name.get_mut().write(rtic::export::Priority::new(PRIORITY)); @@ -77,7 +74,7 @@ pub fn codegen(app: &App, analysis: &Analysis) -> Vec { if (&mut *#exec_name.get_mut()).poll(|| { #executor_run_ident.store(true, core::sync::atomic::Ordering::Release); rtic::pend(#device::#enum_::#interrupt); - }) && !rtic::export::interrupt::free(|_| (&*#rq.get_mut()).is_empty()) { + }) && #rq.load(core::sync::atomic::Ordering::Relaxed) { // If the ready queue is not empty and the executor finished, restart this // dispatch to check if the executor should be restarted. rtic::pend(#device::#enum_::#interrupt); diff --git a/macros/src/codegen/module.rs b/macros/src/codegen/module.rs index b4ad68aad8..7bbfdf37c2 100644 --- a/macros/src/codegen/module.rs +++ b/macros/src/codegen/module.rs @@ -183,13 +183,14 @@ pub fn codegen( #[doc(hidden)] pub fn #internal_spawn_ident() -> Result<(), ()> { unsafe { - let r = rtic::export::interrupt::free(|_| (&mut *#rq.get_mut()).enqueue(())); - - if r.is_ok() { + // TODO: Fix this to be compare and swap + if #rq.load(core::sync::atomic::Ordering::Acquire) { + Err(()) + } else { + #rq.store(true, core::sync::atomic::Ordering::Release); rtic::pend(#device::#enum_::#interrupt); + Ok(()) } - - r } })); diff --git a/macros/src/codegen/util.rs b/macros/src/codegen/util.rs index 61bde98fa5..aa720c0e5f 100644 --- a/macros/src/codegen/util.rs +++ b/macros/src/codegen/util.rs @@ -154,7 +154,7 @@ pub fn local_resources_ident(ctxt: Context, app: &App) -> Ident { /// Generates an identifier for a ready queue, async task version pub fn rq_async_ident(async_task_name: &Ident) -> Ident { - mark_internal_name(&format!("ASYNC_TACK_{}_RQ", async_task_name)) + mark_internal_name(&format!("ASYNC_TASK_{}_RQ", async_task_name)) } /// Suffixed identifier diff --git a/rust-toolchain.toml b/rust-toolchain.toml new file mode 100644 index 0000000000..bbd57bcb24 --- /dev/null +++ b/rust-toolchain.toml @@ -0,0 +1,4 @@ +[toolchain] +channel = "nightly" +components = [ "rust-src", "rustfmt", "llvm-tools-preview" ] +targets = [ "thumbv7em-none-eabihf" ] From 53f3d397e76383deabbe9579a3522174c422a958 Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Wed, 4 Jan 2023 21:36:43 +0100 Subject: [PATCH 221/423] More removal --- src/export.rs | 5 - src/lib.rs | 4 - src/sll.rs | 421 -------------------------------------------------- src/tq.rs | 328 --------------------------------------- 4 files changed, 758 deletions(-) delete mode 100644 src/sll.rs delete mode 100644 src/tq.rs diff --git a/src/export.rs b/src/export.rs index 82320fbbcb..2cc031e9e5 100644 --- a/src/export.rs +++ b/src/export.rs @@ -1,8 +1,3 @@ -#![allow(clippy::inline_always)] -pub use crate::{ - sll::{IntrusiveSortedLinkedList, Node as IntrusiveNode}, - tq::{TaskNotReady, TimerQueue, WakerNotReady}, -}; pub use bare_metal::CriticalSection; use core::{ cell::Cell, diff --git a/src/lib.rs b/src/lib.rs index da556a5c49..e8b8140a79 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -51,10 +51,6 @@ pub mod mutex { #[doc(hidden)] pub mod export; -#[doc(hidden)] -pub mod sll; -#[doc(hidden)] -mod tq; /// Sets the given `interrupt` as pending /// diff --git a/src/sll.rs b/src/sll.rs deleted file mode 100644 index 43b53c1749..0000000000 --- a/src/sll.rs +++ /dev/null @@ -1,421 +0,0 @@ -//! An intrusive sorted priority linked list, designed for use in `Future`s in RTIC. -use core::cmp::Ordering; -use core::fmt; -use core::marker::PhantomData; -use core::ops::{Deref, DerefMut}; -use core::ptr::NonNull; - -/// Marker for Min sorted [`IntrusiveSortedLinkedList`]. -pub struct Min; - -/// Marker for Max sorted [`IntrusiveSortedLinkedList`]. -pub struct Max; - -/// The linked list kind: min-list or max-list -pub trait Kind: private::Sealed { - #[doc(hidden)] - fn ordering() -> Ordering; -} - -impl Kind for Min { - fn ordering() -> Ordering { - Ordering::Less - } -} - -impl Kind for Max { - fn ordering() -> Ordering { - Ordering::Greater - } -} - -/// Sealed traits -mod private { - pub trait Sealed {} -} - -impl private::Sealed for Max {} -impl private::Sealed for Min {} - -/// A node in the [`IntrusiveSortedLinkedList`]. -pub struct Node { - pub val: T, - next: Option>>, -} - -impl Node { - pub fn new(val: T) -> Self { - Self { val, next: None } - } -} - -/// The linked list. -pub struct IntrusiveSortedLinkedList<'a, T, K> { - head: Option>>, - _kind: PhantomData, - _lt: PhantomData<&'a ()>, -} - -impl<'a, T, K> fmt::Debug for IntrusiveSortedLinkedList<'a, T, K> -where - T: Ord + core::fmt::Debug, - K: Kind, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let mut l = f.debug_list(); - let mut current = self.head; - - while let Some(head) = current { - let head = unsafe { head.as_ref() }; - current = head.next; - - l.entry(&head.val); - } - - l.finish() - } -} - -impl<'a, T, K> IntrusiveSortedLinkedList<'a, T, K> -where - T: Ord, - K: Kind, -{ - pub const fn new() -> Self { - Self { - head: None, - _kind: PhantomData, - _lt: PhantomData, - } - } - - // Push to the list. - pub fn push(&mut self, new: &'a mut Node) { - unsafe { - if let Some(head) = self.head { - if head.as_ref().val.cmp(&new.val) != K::ordering() { - // This is newer than head, replace head - new.next = self.head; - self.head = Some(NonNull::new_unchecked(new)); - } else { - // It's not head, search the list for the correct placement - let mut current = head; - - while let Some(next) = current.as_ref().next { - if next.as_ref().val.cmp(&new.val) != K::ordering() { - break; - } - - current = next; - } - - new.next = current.as_ref().next; - current.as_mut().next = Some(NonNull::new_unchecked(new)); - } - } else { - // List is empty, place at head - self.head = Some(NonNull::new_unchecked(new)) - } - } - } - - /// Get an iterator over the sorted list. - pub fn iter(&self) -> Iter<'_, T, K> { - Iter { - _list: self, - index: self.head, - } - } - - /// Find an element in the list that can be changed and resorted. - pub fn find_mut(&mut self, mut f: F) -> Option> - where - F: FnMut(&T) -> bool, - { - let head = self.head?; - - // Special-case, first element - if f(&unsafe { head.as_ref() }.val) { - return Some(FindMut { - is_head: true, - prev_index: None, - index: self.head, - list: self, - maybe_changed: false, - }); - } - - let mut current = head; - - while let Some(next) = unsafe { current.as_ref() }.next { - if f(&unsafe { next.as_ref() }.val) { - return Some(FindMut { - is_head: false, - prev_index: Some(current), - index: Some(next), - list: self, - maybe_changed: false, - }); - } - - current = next; - } - - None - } - - /// Peek at the first element. - pub fn peek(&self) -> Option<&T> { - self.head.map(|head| unsafe { &head.as_ref().val }) - } - - /// Pops the first element in the list. - /// - /// Complexity is worst-case `O(1)`. - pub fn pop(&mut self) -> Option<&'a Node> { - if let Some(head) = self.head { - let v = unsafe { head.as_ref() }; - self.head = v.next; - Some(v) - } else { - None - } - } - - /// Checks if the linked list is empty. - #[inline] - pub fn is_empty(&self) -> bool { - self.head.is_none() - } -} - -/// Iterator for the linked list. -pub struct Iter<'a, T, K> -where - T: Ord, - K: Kind, -{ - _list: &'a IntrusiveSortedLinkedList<'a, T, K>, - index: Option>>, -} - -impl<'a, T, K> Iterator for Iter<'a, T, K> -where - T: Ord, - K: Kind, -{ - type Item = &'a T; - - fn next(&mut self) -> Option { - let index = self.index?; - - let node = unsafe { index.as_ref() }; - self.index = node.next; - - Some(&node.val) - } -} - -/// Comes from [`IntrusiveSortedLinkedList::find_mut`]. -pub struct FindMut<'a, 'b, T, K> -where - T: Ord + 'b, - K: Kind, -{ - list: &'a mut IntrusiveSortedLinkedList<'b, T, K>, - is_head: bool, - prev_index: Option>>, - index: Option>>, - maybe_changed: bool, -} - -impl<'a, 'b, T, K> FindMut<'a, 'b, T, K> -where - T: Ord, - K: Kind, -{ - unsafe fn pop_internal(&mut self) -> &'b mut Node { - if self.is_head { - // If it is the head element, we can do a normal pop - let mut head = self.list.head.unwrap_unchecked(); - let v = head.as_mut(); - self.list.head = v.next; - v - } else { - // Somewhere in the list - let mut prev = self.prev_index.unwrap_unchecked(); - let mut curr = self.index.unwrap_unchecked(); - - // Re-point the previous index - prev.as_mut().next = curr.as_ref().next; - - curr.as_mut() - } - } - - /// This will pop the element from the list. - /// - /// Complexity is worst-case `O(1)`. - #[inline] - pub fn pop(mut self) -> &'b mut Node { - unsafe { self.pop_internal() } - } - - /// This will resort the element into the correct position in the list if needed. The resorting - /// will only happen if the element has been accessed mutably. - /// - /// Same as calling `drop`. - /// - /// Complexity is worst-case `O(N)`. - #[inline] - pub fn finish(self) { - drop(self) - } -} - -impl<'b, T, K> Drop for FindMut<'_, 'b, T, K> -where - T: Ord + 'b, - K: Kind, -{ - fn drop(&mut self) { - // Only resort the list if the element has changed - if self.maybe_changed { - unsafe { - let val = self.pop_internal(); - self.list.push(val); - } - } - } -} - -impl Deref for FindMut<'_, '_, T, K> -where - T: Ord, - K: Kind, -{ - type Target = T; - - fn deref(&self) -> &Self::Target { - unsafe { &self.index.unwrap_unchecked().as_ref().val } - } -} - -impl DerefMut for FindMut<'_, '_, T, K> -where - T: Ord, - K: Kind, -{ - fn deref_mut(&mut self) -> &mut Self::Target { - self.maybe_changed = true; - unsafe { &mut self.index.unwrap_unchecked().as_mut().val } - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn const_new() { - static mut _V1: IntrusiveSortedLinkedList = IntrusiveSortedLinkedList::new(); - } - - #[test] - fn test_peek() { - let mut ll: IntrusiveSortedLinkedList = IntrusiveSortedLinkedList::new(); - - let mut a = Node { val: 1, next: None }; - ll.push(&mut a); - assert_eq!(ll.peek().unwrap(), &1); - - let mut a = Node { val: 2, next: None }; - ll.push(&mut a); - assert_eq!(ll.peek().unwrap(), &2); - - let mut a = Node { val: 3, next: None }; - ll.push(&mut a); - assert_eq!(ll.peek().unwrap(), &3); - - let mut ll: IntrusiveSortedLinkedList = IntrusiveSortedLinkedList::new(); - - let mut a = Node { val: 2, next: None }; - ll.push(&mut a); - assert_eq!(ll.peek().unwrap(), &2); - - let mut a = Node { val: 1, next: None }; - ll.push(&mut a); - assert_eq!(ll.peek().unwrap(), &1); - - let mut a = Node { val: 3, next: None }; - ll.push(&mut a); - assert_eq!(ll.peek().unwrap(), &1); - } - - #[test] - fn test_empty() { - let ll: IntrusiveSortedLinkedList = IntrusiveSortedLinkedList::new(); - - assert!(ll.is_empty()) - } - - #[test] - fn test_updating() { - let mut ll: IntrusiveSortedLinkedList = IntrusiveSortedLinkedList::new(); - - let mut a = Node { val: 1, next: None }; - ll.push(&mut a); - - let mut a = Node { val: 2, next: None }; - ll.push(&mut a); - - let mut a = Node { val: 3, next: None }; - ll.push(&mut a); - - let mut find = ll.find_mut(|v| *v == 2).unwrap(); - - *find += 1000; - find.finish(); - - assert_eq!(ll.peek().unwrap(), &1002); - - let mut find = ll.find_mut(|v| *v == 3).unwrap(); - - *find += 1000; - find.finish(); - - assert_eq!(ll.peek().unwrap(), &1003); - - // Remove largest element - ll.find_mut(|v| *v == 1003).unwrap().pop(); - - assert_eq!(ll.peek().unwrap(), &1002); - } - - #[test] - fn test_updating_1() { - let mut ll: IntrusiveSortedLinkedList = IntrusiveSortedLinkedList::new(); - - let mut a = Node { val: 1, next: None }; - ll.push(&mut a); - - let v = ll.pop().unwrap(); - - assert_eq!(v.val, 1); - } - - #[test] - fn test_updating_2() { - let mut ll: IntrusiveSortedLinkedList = IntrusiveSortedLinkedList::new(); - - let mut a = Node { val: 1, next: None }; - ll.push(&mut a); - - let mut find = ll.find_mut(|v| *v == 1).unwrap(); - - *find += 1000; - find.finish(); - - assert_eq!(ll.peek().unwrap(), &1001); - } -} diff --git a/src/tq.rs b/src/tq.rs deleted file mode 100644 index daa91c8d48..0000000000 --- a/src/tq.rs +++ /dev/null @@ -1,328 +0,0 @@ -use crate::{ - sll::{IntrusiveSortedLinkedList, Min as IsslMin, Node as IntrusiveNode}, - Monotonic, -}; -use core::cmp::Ordering; -use core::task::Waker; -use heapless::sorted_linked_list::{LinkedIndexU16, Min as SllMin, SortedLinkedList}; - -pub struct TimerQueue<'a, Mono, Task, const N_TASK: usize> -where - Mono: Monotonic, - Task: Copy, -{ - pub task_queue: SortedLinkedList, LinkedIndexU16, SllMin, N_TASK>, - pub waker_queue: IntrusiveSortedLinkedList<'a, WakerNotReady, IsslMin>, -} - -impl<'a, Mono, Task, const N_TASK: usize> TimerQueue<'a, Mono, Task, N_TASK> -where - Mono: Monotonic + 'a, - Task: Copy, -{ - fn check_if_enable( - &self, - instant: Mono::Instant, - enable_interrupt: F1, - pend_handler: F2, - mono: Option<&mut Mono>, - ) where - F1: FnOnce(), - F2: FnOnce(), - { - // Check if the top contains a non-empty element and if that element is - // greater than nr - let if_task_heap_max_greater_than_nr = self - .task_queue - .peek() - .map_or(true, |head| instant < head.instant); - let if_waker_heap_max_greater_than_nr = self - .waker_queue - .peek() - .map_or(true, |head| instant < head.instant); - - if if_task_heap_max_greater_than_nr || if_waker_heap_max_greater_than_nr { - if Mono::DISABLE_INTERRUPT_ON_EMPTY_QUEUE && self.is_empty() { - if let Some(mono) = mono { - mono.enable_timer(); - } - enable_interrupt(); - } - - pend_handler(); - } - } - - /// Enqueue a task without checking if it is full - #[inline] - pub unsafe fn enqueue_task_unchecked( - &mut self, - nr: TaskNotReady, - enable_interrupt: F1, - pend_handler: F2, - mono: Option<&mut Mono>, - ) where - F1: FnOnce(), - F2: FnOnce(), - { - self.check_if_enable(nr.instant, enable_interrupt, pend_handler, mono); - self.task_queue.push_unchecked(nr); - } - - /// Enqueue a waker - #[inline] - pub fn enqueue_waker( - &mut self, - nr: &'a mut IntrusiveNode>, - enable_interrupt: F1, - pend_handler: F2, - mono: Option<&mut Mono>, - ) where - F1: FnOnce(), - F2: FnOnce(), - { - self.check_if_enable(nr.val.instant, enable_interrupt, pend_handler, mono); - self.waker_queue.push(nr); - } - - /// Check if all the timer queue is empty. - #[inline] - pub fn is_empty(&self) -> bool { - self.task_queue.is_empty() && self.waker_queue.is_empty() - } - - /// Cancel the marker value for a task - pub fn cancel_task_marker(&mut self, marker: u32) -> Option<(Task, u8)> { - if let Some(val) = self.task_queue.find_mut(|nr| nr.marker == marker) { - let nr = val.pop(); - - Some((nr.task, nr.index)) - } else { - None - } - } - - /// Cancel the marker value for a waker - pub fn cancel_waker_marker(&mut self, marker: u32) { - if let Some(val) = self.waker_queue.find_mut(|nr| nr.marker == marker) { - let _ = val.pop(); - } - } - - /// Update the instant at an marker value for a task to a new instant - #[allow(clippy::result_unit_err)] - pub fn update_task_marker( - &mut self, - marker: u32, - new_marker: u32, - instant: Mono::Instant, - pend_handler: F, - ) -> Result<(), ()> { - if let Some(mut val) = self.task_queue.find_mut(|nr| nr.marker == marker) { - val.instant = instant; - val.marker = new_marker; - - // On update pend the handler to reconfigure the next compare match - pend_handler(); - - Ok(()) - } else { - Err(()) - } - } - - fn dequeue_task_queue( - &mut self, - instant: Mono::Instant, - mono: &mut Mono, - ) -> Option<(Task, u8)> { - if instant <= mono.now() { - // task became ready - let nr = unsafe { self.task_queue.pop_unchecked() }; - Some((nr.task, nr.index)) - } else { - // Set compare - mono.set_compare(instant); - - // Double check that the instant we set is really in the future, else - // 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 <= mono.now() { - let nr = unsafe { self.task_queue.pop_unchecked() }; - Some((nr.task, nr.index)) - } else { - None - } - } - } - - fn dequeue_waker_queue(&mut self, instant: Mono::Instant, mono: &mut Mono) -> bool { - let mut did_wake = false; - - if instant <= mono.now() { - // Task became ready, wake the waker - if let Some(v) = self.waker_queue.pop() { - v.val.waker.wake_by_ref(); - - did_wake = true; - } - } else { - // Set compare - mono.set_compare(instant); - - // Double check that the instant we set is really in the future, else - // 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 <= mono.now() { - if let Some(v) = self.waker_queue.pop() { - v.val.waker.wake_by_ref(); - - did_wake = true; - } - } - } - - did_wake - } - - /// Dequeue a task from the ``TimerQueue`` - pub fn dequeue(&mut self, disable_interrupt: F, mono: &mut Mono) -> Option<(Task, u8)> - where - F: FnOnce(), - { - mono.clear_compare_flag(); - - loop { - let tq = self.task_queue.peek().map(|p| p.instant); - let wq = self.waker_queue.peek().map(|p| p.instant); - - let dequeue_task; - let instant; - - match (tq, wq) { - (Some(tq_instant), Some(wq_instant)) => { - if tq_instant <= wq_instant { - dequeue_task = true; - instant = tq_instant; - } else { - dequeue_task = false; - instant = wq_instant; - } - } - (Some(tq_instant), None) => { - dequeue_task = true; - instant = tq_instant; - } - (None, Some(wq_instant)) => { - dequeue_task = false; - instant = wq_instant; - } - (None, None) => { - // The queue is empty, disable the interrupt. - if Mono::DISABLE_INTERRUPT_ON_EMPTY_QUEUE { - disable_interrupt(); - mono.disable_timer(); - } - - return None; - } - } - - if dequeue_task { - return self.dequeue_task_queue(instant, mono); - } else if !self.dequeue_waker_queue(instant, mono) { - return None; - } else { - // Run the dequeue again - } - } - } -} - -pub struct TaskNotReady -where - Task: Copy, - Mono: Monotonic, -{ - pub task: Task, - pub index: u8, - pub instant: Mono::Instant, - pub marker: u32, -} - -impl Eq for TaskNotReady -where - Task: Copy, - Mono: Monotonic, -{ -} - -impl Ord for TaskNotReady -where - Task: Copy, - Mono: Monotonic, -{ - fn cmp(&self, other: &Self) -> Ordering { - self.instant.cmp(&other.instant) - } -} - -impl PartialEq for TaskNotReady -where - Task: Copy, - Mono: Monotonic, -{ - fn eq(&self, other: &Self) -> bool { - self.instant == other.instant - } -} - -impl PartialOrd for TaskNotReady -where - Task: Copy, - Mono: Monotonic, -{ - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -pub struct WakerNotReady -where - Mono: Monotonic, -{ - pub waker: Waker, - pub instant: Mono::Instant, - pub marker: u32, -} - -impl Eq for WakerNotReady where Mono: Monotonic {} - -impl Ord for WakerNotReady -where - Mono: Monotonic, -{ - fn cmp(&self, other: &Self) -> Ordering { - self.instant.cmp(&other.instant) - } -} - -impl PartialEq for WakerNotReady -where - Mono: Monotonic, -{ - fn eq(&self, other: &Self) -> bool { - self.instant == other.instant - } -} - -impl PartialOrd for WakerNotReady -where - Mono: Monotonic, -{ - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} From 714020a624ca93c42d5da7ebe612e7fc668e1471 Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Sat, 7 Jan 2023 11:24:13 +0100 Subject: [PATCH 222/423] Removed Priority, simplified lifetime handling --- examples/async-task.rs | 26 ++++- macros/src/bindings.rs | 1 + macros/src/codegen.rs | 4 + macros/src/codegen/async_dispatchers.rs | 17 +-- macros/src/codegen/hardware_tasks.rs | 31 ++---- macros/src/codegen/idle.rs | 18 +-- macros/src/codegen/init.rs | 11 +- macros/src/codegen/local_resources_struct.rs | 8 +- macros/src/codegen/module.rs | 50 ++------- macros/src/codegen/shared_resources.rs | 13 +-- macros/src/codegen/shared_resources_struct.rs | 13 +-- macros/src/codegen/software_tasks.rs | 31 +----- macros/src/codegen/util.rs | 7 +- rust-toolchain.toml | 2 +- src/export.rs | 103 +++++------------- 15 files changed, 99 insertions(+), 236 deletions(-) diff --git a/examples/async-task.rs b/examples/async-task.rs index 7d0ee86e03..d058fe54d5 100644 --- a/examples/async-task.rs +++ b/examples/async-task.rs @@ -15,7 +15,9 @@ mod app { use cortex_m_semihosting::{debug, hprintln}; #[shared] - struct Shared {} + struct Shared { + a: u32, + } #[local] struct Local {} @@ -25,11 +27,12 @@ mod app { hprintln!("init").unwrap(); async_task::spawn().unwrap(); + async_task2::spawn().unwrap(); - (Shared {}, Local {}) + (Shared { a: 0 }, Local {}) } - #[idle] + #[idle(shared = [a])] fn idle(_: idle::Context) -> ! { // debug::exit(debug::EXIT_SUCCESS); loop { @@ -38,10 +41,23 @@ mod app { } } - #[task] - async fn async_task(_cx: async_task::Context) { + #[task(binds = UART1, shared = [a])] + fn hw_task(cx: hw_task::Context) { + let hw_task::SharedResources { a } = cx.shared; + hprintln!("hello from hw").ok(); + } + + #[task(shared = [a])] + async fn async_task(cx: async_task::Context) { + let async_task::SharedResources { a } = cx.shared; hprintln!("hello from async").ok(); debug::exit(debug::EXIT_SUCCESS); } + + #[task(priority = 2, shared = [a])] + async fn async_task2(cx: async_task2::Context) { + let async_task2::SharedResources { a } = cx.shared; + hprintln!("hello from async2").ok(); + } } diff --git a/macros/src/bindings.rs b/macros/src/bindings.rs index e69de29bb2..8b13789179 100644 --- a/macros/src/bindings.rs +++ b/macros/src/bindings.rs @@ -0,0 +1 @@ + diff --git a/macros/src/codegen.rs b/macros/src/codegen.rs index 0f68c34731..b490d7a501 100644 --- a/macros/src/codegen.rs +++ b/macros/src/codegen.rs @@ -19,6 +19,10 @@ mod shared_resources_struct; mod software_tasks; mod util; +// TODO: organize codegen to actual parts of code +// so `main::codegen` generates ALL the code for `fn main`, +// `software_tasks::codegen` generates ALL the code for software tasks etc... + #[allow(clippy::too_many_lines)] pub fn app(app: &App, analysis: &Analysis) -> TokenStream2 { let mut mod_app = vec![]; diff --git a/macros/src/codegen/async_dispatchers.rs b/macros/src/codegen/async_dispatchers.rs index f428cef04f..d53d7b5e0c 100644 --- a/macros/src/codegen/async_dispatchers.rs +++ b/macros/src/codegen/async_dispatchers.rs @@ -13,7 +13,6 @@ pub fn codegen(app: &App, analysis: &Analysis) -> Vec { for (name, _) in app.software_tasks.iter() { let type_name = util::internal_task_ident(name, "F"); let exec_name = util::internal_task_ident(name, "EXEC"); - let prio_name = util::internal_task_ident(name, "PRIORITY"); items.push(quote!( #[allow(non_camel_case_types)] @@ -22,12 +21,6 @@ pub fn codegen(app: &App, analysis: &Analysis) -> Vec { static #exec_name: rtic::RacyCell> = rtic::RacyCell::new(rtic::export::executor::AsyncTaskExecutor::new()); - - // The executors priority, this can be any value - we will overwrite it when we - // start a task - #[allow(non_upper_case_globals)] - static #prio_name: rtic::RacyCell = - unsafe { rtic::RacyCell::new(rtic::export::Priority::new(0)) }; )); } @@ -39,7 +32,6 @@ pub fn codegen(app: &App, analysis: &Analysis) -> Vec { for name in channel.tasks.iter() { let exec_name = util::internal_task_ident(name, "EXEC"); - let prio_name = util::internal_task_ident(name, "PRIORITY"); // let task = &app.software_tasks[name]; // let cfgs = &task.cfgs; let executor_run_ident = util::executor_run_ident(name); @@ -57,14 +49,9 @@ pub fn codegen(app: &App, analysis: &Analysis) -> Vec { if !(&*#exec_name.get()).is_running() { // TODO Fix this to be compare and swap if #rq.load(core::sync::atomic::Ordering::Relaxed) { - #rq.store(false, core::sync::atomic::Ordering::Relaxed); + #rq.store(false, core::sync::atomic::Ordering::Relaxed); - - // The async executor needs a static priority - #prio_name.get_mut().write(rtic::export::Priority::new(PRIORITY)); - let priority: &'static _ = &*#prio_name.get(); - - (&mut *#exec_name.get_mut()).spawn(#name(#name::Context::new(priority))); + (&mut *#exec_name.get_mut()).spawn(#name(#name::Context::new())); #executor_run_ident.store(true, core::sync::atomic::Ordering::Relaxed); } } diff --git a/macros/src/codegen/hardware_tasks.rs b/macros/src/codegen/hardware_tasks.rs index 2a81d9a051..9ea5825b3d 100644 --- a/macros/src/codegen/hardware_tasks.rs +++ b/macros/src/codegen/hardware_tasks.rs @@ -41,22 +41,16 @@ pub fn codegen( rtic::export::run(PRIORITY, || { #name( - #name::Context::new(&rtic::export::Priority::new(PRIORITY)) + #name::Context::new() ) }); } )); - let mut shared_needs_lt = false; - let mut local_needs_lt = false; - // `${task}Locals` if !task.args.local_resources.is_empty() { - let (item, constructor) = local_resources_struct::codegen( - Context::HardwareTask(name), - &mut local_needs_lt, - app, - ); + let (item, constructor) = + local_resources_struct::codegen(Context::HardwareTask(name), app); root.push(item); @@ -65,24 +59,19 @@ pub fn codegen( // `${task}Resources` if !task.args.shared_resources.is_empty() { - let (item, constructor) = shared_resources_struct::codegen( - Context::HardwareTask(name), - &mut shared_needs_lt, - app, - ); + let (item, constructor) = + shared_resources_struct::codegen(Context::HardwareTask(name), app); root.push(item); mod_app.push(constructor); } - root.push(module::codegen( - Context::HardwareTask(name), - shared_needs_lt, - local_needs_lt, - app, - analysis, - )); + // Module generation... + + root.push(module::codegen(Context::HardwareTask(name), app, analysis)); + + // End module generation if !task.is_extern { let attrs = &task.attrs; diff --git a/macros/src/codegen/idle.rs b/macros/src/codegen/idle.rs index 98679399f9..a4f6325420 100644 --- a/macros/src/codegen/idle.rs +++ b/macros/src/codegen/idle.rs @@ -24,37 +24,27 @@ pub fn codegen( TokenStream2, ) { if let Some(idle) = &app.idle { - let mut shared_needs_lt = false; - let mut local_needs_lt = false; let mut mod_app = vec![]; let mut root_idle = vec![]; let name = &idle.name; if !idle.args.shared_resources.is_empty() { - let (item, constructor) = - shared_resources_struct::codegen(Context::Idle, &mut shared_needs_lt, app); + let (item, constructor) = shared_resources_struct::codegen(Context::Idle, app); root_idle.push(item); mod_app.push(constructor); } if !idle.args.local_resources.is_empty() { - let (item, constructor) = - local_resources_struct::codegen(Context::Idle, &mut local_needs_lt, app); + let (item, constructor) = local_resources_struct::codegen(Context::Idle, app); root_idle.push(item); mod_app.push(constructor); } - root_idle.push(module::codegen( - Context::Idle, - shared_needs_lt, - local_needs_lt, - app, - analysis, - )); + root_idle.push(module::codegen(Context::Idle, app, analysis)); let attrs = &idle.attrs; let context = &idle.context; @@ -71,7 +61,7 @@ pub fn codegen( )); let call_idle = quote!(#name( - #name::Context::new(&rtic::export::Priority::new(0)) + #name::Context::new() )); (mod_app, root_idle, user_idle, call_idle) diff --git a/macros/src/codegen/init.rs b/macros/src/codegen/init.rs index c7b871234e..bbde4f275f 100644 --- a/macros/src/codegen/init.rs +++ b/macros/src/codegen/init.rs @@ -91,8 +91,7 @@ pub fn codegen(app: &App, analysis: &Analysis) -> CodegenResult { // `${task}Locals` if !init.args.local_resources.is_empty() { - let (item, constructor) = - local_resources_struct::codegen(Context::Init, &mut local_needs_lt, app); + let (item, constructor) = local_resources_struct::codegen(Context::Init, app); root_init.push(item); @@ -103,13 +102,7 @@ pub fn codegen(app: &App, analysis: &Analysis) -> CodegenResult { let (shared_resources, local_resources) = #name(#name::Context::new(core.into())); }; - root_init.push(module::codegen( - Context::Init, - false, - local_needs_lt, - app, - analysis, - )); + root_init.push(module::codegen(Context::Init, app, analysis)); (mod_app, root_init, user_init, call_init) } diff --git a/macros/src/codegen/local_resources_struct.rs b/macros/src/codegen/local_resources_struct.rs index 6bcf4fadc8..a0413f9cfa 100644 --- a/macros/src/codegen/local_resources_struct.rs +++ b/macros/src/codegen/local_resources_struct.rs @@ -8,7 +8,7 @@ use quote::quote; use crate::codegen::util; /// Generates local resources structs -pub fn codegen(ctxt: Context, needs_lt: &mut bool, app: &App) -> (TokenStream2, TokenStream2) { +pub fn codegen(ctxt: Context, app: &App) -> (TokenStream2, TokenStream2) { let mut lt = None; let resources = match ctxt { @@ -74,16 +74,14 @@ pub fn codegen(ctxt: Context, needs_lt: &mut bool, app: &App) -> (TokenStream2, } if lt.is_some() { - *needs_lt = true; - // The struct could end up empty due to `cfg`s leading to an error due to `'a` being unused if has_cfgs { fields.push(quote!( #[doc(hidden)] - pub __marker__: core::marker::PhantomData<&'a ()> + pub __rtic_internal_marker: ::core::marker::PhantomData<&'a ()> )); - values.push(quote!(__marker__: core::marker::PhantomData)); + values.push(quote!(__rtic_internal_marker: ::core::marker::PhantomData)); } } diff --git a/macros/src/codegen/module.rs b/macros/src/codegen/module.rs index 7bbfdf37c2..a64abd8a74 100644 --- a/macros/src/codegen/module.rs +++ b/macros/src/codegen/module.rs @@ -4,13 +4,7 @@ use proc_macro2::TokenStream as TokenStream2; use quote::quote; #[allow(clippy::too_many_lines)] -pub fn codegen( - ctxt: Context, - shared_resources_tick: bool, - local_resources_tick: bool, - app: &App, - analysis: &Analysis, -) -> TokenStream2 { +pub fn codegen(ctxt: Context, app: &App, analysis: &Analysis) -> TokenStream2 { let mut items = vec![]; let mut module_items = vec![]; let mut fields = vec![]; @@ -20,7 +14,6 @@ pub fn codegen( let name = ctxt.ident(app); - let mut lt = None; match ctxt { Context::Init => { fields.push(quote!( @@ -39,10 +32,9 @@ pub fn codegen( values.push(quote!(device: #device::Peripherals::steal())); } - lt = Some(quote!('a)); fields.push(quote!( /// Critical section token for init - pub cs: rtic::export::CriticalSection<#lt> + pub cs: rtic::export::CriticalSection<'a> )); values.push(quote!(cs: rtic::export::CriticalSection::new())); @@ -55,12 +47,6 @@ pub fn codegen( if ctxt.has_local_resources(app) { let ident = util::local_resources_ident(ctxt, app); - let lt = if local_resources_tick { - lt = Some(quote!('a)); - Some(quote!('a)) - } else { - None - }; module_items.push(quote!( #[doc(inline)] @@ -69,7 +55,7 @@ pub fn codegen( fields.push(quote!( /// Local Resources this task has access to - pub local: #name::LocalResources<#lt> + pub local: #name::LocalResources<'a> )); values.push(quote!(local: #name::LocalResources::new())); @@ -77,12 +63,6 @@ pub fn codegen( if ctxt.has_shared_resources(app) { let ident = util::shared_resources_ident(ctxt, app); - let lt = if shared_resources_tick { - lt = Some(quote!('a)); - Some(quote!('a)) - } else { - None - }; module_items.push(quote!( #[doc(inline)] @@ -91,15 +71,10 @@ pub fn codegen( fields.push(quote!( /// Shared Resources this task has access to - pub shared: #name::SharedResources<#lt> + pub shared: #name::SharedResources<'a> )); - let priority = if ctxt.is_init() { - None - } else { - Some(quote!(priority)) - }; - values.push(quote!(shared: #name::SharedResources::new(#priority))); + values.push(quote!(shared: #name::SharedResources::new())); } let doc = match ctxt { @@ -122,12 +97,6 @@ pub fn codegen( None }; - let priority = if ctxt.is_init() { - None - } else { - Some(quote!(priority: &#lt rtic::export::Priority)) - }; - let internal_context_name = util::internal_task_ident(name, "Context"); items.push(quote!( @@ -135,15 +104,18 @@ pub fn codegen( /// Execution context #[allow(non_snake_case)] #[allow(non_camel_case_types)] - pub struct #internal_context_name<#lt> { + pub struct #internal_context_name<'a> { + #[doc(hidden)] + __rtic_internal_p: ::core::marker::PhantomData<&'a ()>, #(#fields,)* } #(#cfgs)* - impl<#lt> #internal_context_name<#lt> { + impl<'a> #internal_context_name<'a> { #[inline(always)] - pub unsafe fn new(#core #priority) -> Self { + pub unsafe fn new(#core) -> Self { #internal_context_name { + __rtic_internal_p: ::core::marker::PhantomData, #(#values,)* } } diff --git a/macros/src/codegen/shared_resources.rs b/macros/src/codegen/shared_resources.rs index b63e7432d6..5c54fb99b2 100644 --- a/macros/src/codegen/shared_resources.rs +++ b/macros/src/codegen/shared_resources.rs @@ -57,19 +57,14 @@ pub fn codegen( #[allow(non_camel_case_types)] #(#cfgs)* pub struct #shared_name<'a> { - priority: &'a Priority, + __rtic_internal_p: ::core::marker::PhantomData<&'a ()>, } #(#cfgs)* impl<'a> #shared_name<'a> { #[inline(always)] - pub unsafe fn new(priority: &'a Priority) -> Self { - #shared_name { priority } - } - - #[inline(always)] - pub unsafe fn priority(&self) -> &Priority { - self.priority + pub unsafe fn new() -> Self { + #shared_name { __rtic_internal_p: ::core::marker::PhantomData } } } )); @@ -104,8 +99,6 @@ pub fn codegen( quote!() } else { quote!(mod shared_resources { - use rtic::export::Priority; - #(#mod_resources)* }) }; diff --git a/macros/src/codegen/shared_resources_struct.rs b/macros/src/codegen/shared_resources_struct.rs index 1d46aa4e4c..de597cab16 100644 --- a/macros/src/codegen/shared_resources_struct.rs +++ b/macros/src/codegen/shared_resources_struct.rs @@ -5,7 +5,7 @@ use quote::quote; use crate::codegen::util; /// Generate shared resources structs -pub fn codegen(ctxt: Context, needs_lt: &mut bool, app: &App) -> (TokenStream2, TokenStream2) { +pub fn codegen(ctxt: Context, app: &App) -> (TokenStream2, TokenStream2) { let mut lt = None; let resources = match ctxt { @@ -72,7 +72,7 @@ pub fn codegen(ctxt: Context, needs_lt: &mut bool, app: &App) -> (TokenStream2, values.push(quote!( #(#cfgs)* - #name: shared_resources::#shared_name::new(priority) + #name: shared_resources::#shared_name::new() )); @@ -93,8 +93,6 @@ pub fn codegen(ctxt: Context, needs_lt: &mut bool, app: &App) -> (TokenStream2, } if lt.is_some() { - *needs_lt = true; - // The struct could end up empty due to `cfg`s leading to an error due to `'a` being unused if has_cfgs { fields.push(quote!( @@ -117,15 +115,10 @@ pub fn codegen(ctxt: Context, needs_lt: &mut bool, app: &App) -> (TokenStream2, } ); - let arg = if ctxt.is_init() { - None - } else { - Some(quote!(priority: &#lt rtic::export::Priority)) - }; let constructor = quote!( impl<#lt> #ident<#lt> { #[inline(always)] - pub unsafe fn new(#arg) -> Self { + pub unsafe fn new() -> Self { #ident { #(#values,)* } diff --git a/macros/src/codegen/software_tasks.rs b/macros/src/codegen/software_tasks.rs index b2b468ca9a..350c1e6009 100644 --- a/macros/src/codegen/software_tasks.rs +++ b/macros/src/codegen/software_tasks.rs @@ -36,16 +36,11 @@ pub fn codegen( )); // `${task}Resources` - let mut shared_needs_lt = false; - let mut local_needs_lt = false; // `${task}Locals` if !task.args.local_resources.is_empty() { - let (item, constructor) = local_resources_struct::codegen( - Context::SoftwareTask(name), - &mut local_needs_lt, - app, - ); + let (item, constructor) = + local_resources_struct::codegen(Context::SoftwareTask(name), app); root.push(item); @@ -53,11 +48,8 @@ pub fn codegen( } if !task.args.shared_resources.is_empty() { - let (item, constructor) = shared_resources_struct::codegen( - Context::SoftwareTask(name), - &mut shared_needs_lt, - app, - ); + let (item, constructor) = + shared_resources_struct::codegen(Context::SoftwareTask(name), app); root.push(item); @@ -69,17 +61,12 @@ pub fn codegen( let attrs = &task.attrs; let cfgs = &task.cfgs; let stmts = &task.stmts; - let context_lifetime = if shared_needs_lt || local_needs_lt { - quote!(<'static>) - } else { - quote!() - }; user_tasks.push(quote!( #(#attrs)* #(#cfgs)* #[allow(non_snake_case)] - async fn #name(#context: #name::Context #context_lifetime) { + async fn #name(#context: #name::Context<'static>) { use rtic::Mutex as _; use rtic::mutex::prelude::*; @@ -88,13 +75,7 @@ pub fn codegen( )); } - root.push(module::codegen( - Context::SoftwareTask(name), - shared_needs_lt, - local_needs_lt, - app, - analysis, - )); + root.push(module::codegen(Context::SoftwareTask(name), app, analysis)); } (mod_app, root, user_tasks) diff --git a/macros/src/codegen/util.rs b/macros/src/codegen/util.rs index aa720c0e5f..a071ca279d 100644 --- a/macros/src/codegen/util.rs +++ b/macros/src/codegen/util.rs @@ -17,10 +17,10 @@ pub fn impl_mutex( ceiling: u8, ptr: &TokenStream2, ) -> TokenStream2 { - let (path, priority) = if resources_prefix { - (quote!(shared_resources::#name), quote!(self.priority())) + let path = if resources_prefix { + quote!(shared_resources::#name) } else { - (quote!(#name), quote!(self.priority)) + quote!(#name) }; let device = &app.args.device; @@ -38,7 +38,6 @@ pub fn impl_mutex( unsafe { rtic::export::lock( #ptr, - #priority, CEILING, #device::NVIC_PRIO_BITS, &#masks_name, diff --git a/rust-toolchain.toml b/rust-toolchain.toml index bbd57bcb24..e28b55de64 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,4 +1,4 @@ [toolchain] channel = "nightly" components = [ "rust-src", "rustfmt", "llvm-tools-preview" ] -targets = [ "thumbv7em-none-eabihf" ] +targets = [ "thumbv6m-none-eabi", "thumbv7m-none-eabi" ] diff --git a/src/export.rs b/src/export.rs index 2cc031e9e5..49ebd878ef 100644 --- a/src/export.rs +++ b/src/export.rs @@ -163,38 +163,6 @@ impl Barrier { } } -// Newtype over `Cell` that forbids mutation through a shared reference -pub struct Priority { - inner: Cell, -} - -impl Priority { - /// Create a new Priority - /// - /// # Safety - /// - /// Will overwrite the current Priority - #[inline(always)] - pub const unsafe fn new(value: u8) -> Self { - Priority { - inner: Cell::new(value), - } - } - - /// Change the current priority to `value` - // These two methods are used by `lock` (see below) but can't be used from the RTIC application - #[inline(always)] - fn set(&self, value: u8) { - self.inner.set(value); - } - - /// Get the current priority - #[inline(always)] - fn get(&self) -> u8 { - self.inner.get() - } -} - /// Const helper to check architecture pub const fn have_basepri() -> bool { #[cfg(have_basepri)] @@ -260,30 +228,20 @@ where #[inline(always)] pub unsafe fn lock( ptr: *mut T, - priority: &Priority, ceiling: u8, nvic_prio_bits: u8, _mask: &[Mask; 3], f: impl FnOnce(&mut T) -> R, ) -> R { - let current = priority.get(); - - if current < ceiling { - if ceiling == (1 << nvic_prio_bits) { - priority.set(u8::max_value()); - let r = interrupt::free(|_| f(&mut *ptr)); - priority.set(current); - r - } else { - priority.set(ceiling); - basepri::write(logical2hw(ceiling, nvic_prio_bits)); - let r = f(&mut *ptr); - basepri::write(logical2hw(current, nvic_prio_bits)); - priority.set(current); - r - } + if ceiling == (1 << nvic_prio_bits) { + let r = interrupt::free(|_| f(&mut *ptr)); + r } else { - f(&mut *ptr) + let current = basepri::read(); + basepri::write(logical2hw(ceiling, nvic_prio_bits)); + let r = f(&mut *ptr); + basepri::write(logical2hw(current, nvic_prio_bits)); + r } } @@ -335,40 +293,29 @@ pub unsafe fn lock( #[inline(always)] pub unsafe fn lock( ptr: *mut T, - priority: &Priority, ceiling: u8, _nvic_prio_bits: u8, masks: &[Mask; 3], f: impl FnOnce(&mut T) -> R, ) -> R { - let current = priority.get(); - if current < ceiling { - if ceiling >= 4 { - // safe to manipulate outside critical section - priority.set(ceiling); - // execute closure under protection of raised system ceiling - let r = interrupt::free(|_| f(&mut *ptr)); - // safe to manipulate outside critical section - priority.set(current); - r - } else { - // safe to manipulate outside critical section - priority.set(ceiling); - let mask = compute_mask(current, ceiling, masks); - clear_enable_mask(mask); - - // execute closure under protection of raised system ceiling - let r = f(&mut *ptr); - - set_enable_mask(mask); - - // safe to manipulate outside critical section - priority.set(current); - r - } + if ceiling >= 4 { + // safe to manipulate outside critical section + // execute closure under protection of raised system ceiling + let r = interrupt::free(|_| f(&mut *ptr)); + // safe to manipulate outside critical section + r } else { - // execute closure without raising system ceiling - f(&mut *ptr) + // safe to manipulate outside critical section + let mask = compute_mask(0, ceiling, masks); + clear_enable_mask(mask); + + // execute closure under protection of raised system ceiling + let r = f(&mut *ptr); + + set_enable_mask(mask); + + // safe to manipulate outside critical section + r } } From 511ff675b558812d65048ae737b6f1c53133e211 Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Sat, 7 Jan 2023 11:36:32 +0100 Subject: [PATCH 223/423] Break codegen for 0-prio async --- macros/src/codegen/idle.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/macros/src/codegen/idle.rs b/macros/src/codegen/idle.rs index a4f6325420..c0eecbcabb 100644 --- a/macros/src/codegen/idle.rs +++ b/macros/src/codegen/idle.rs @@ -67,15 +67,15 @@ pub fn codegen( (mod_app, root_idle, user_idle, call_idle) } else { // TODO: No idle defined, check for 0-priority tasks and generate an executor if needed + unimplemented!(); - // - ( - vec![], - vec![], - None, - quote!(loop { - rtic::export::nop() - }), - ) + // ( + // vec![], + // vec![], + // None, + // quote!(loop { + // rtic::export::nop() + // }), + // ) } } From 2ad36a6efed5028e0e6bd991b82a50c045f825a8 Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Sat, 7 Jan 2023 13:18:43 +0100 Subject: [PATCH 224/423] Lifetime cleanup --- examples/async-task.rs | 6 ++-- macros/src/codegen/local_resources_struct.rs | 25 +++++----------- macros/src/codegen/shared_resources_struct.rs | 29 +++++-------------- 3 files changed, 17 insertions(+), 43 deletions(-) diff --git a/examples/async-task.rs b/examples/async-task.rs index d058fe54d5..45d44fd583 100644 --- a/examples/async-task.rs +++ b/examples/async-task.rs @@ -43,13 +43,13 @@ mod app { #[task(binds = UART1, shared = [a])] fn hw_task(cx: hw_task::Context) { - let hw_task::SharedResources { a } = cx.shared; + let hw_task::SharedResources { a, .. } = cx.shared; hprintln!("hello from hw").ok(); } #[task(shared = [a])] async fn async_task(cx: async_task::Context) { - let async_task::SharedResources { a } = cx.shared; + let async_task::SharedResources { a, .. } = cx.shared; hprintln!("hello from async").ok(); debug::exit(debug::EXIT_SUCCESS); @@ -57,7 +57,7 @@ mod app { #[task(priority = 2, shared = [a])] async fn async_task2(cx: async_task2::Context) { - let async_task2::SharedResources { a } = cx.shared; + let async_task2::SharedResources { a, .. } = cx.shared; hprintln!("hello from async2").ok(); } } diff --git a/macros/src/codegen/local_resources_struct.rs b/macros/src/codegen/local_resources_struct.rs index a0413f9cfa..e268508bc4 100644 --- a/macros/src/codegen/local_resources_struct.rs +++ b/macros/src/codegen/local_resources_struct.rs @@ -9,8 +9,6 @@ use crate::codegen::util; /// Generates local resources structs pub fn codegen(ctxt: Context, app: &App) -> (TokenStream2, TokenStream2) { - let mut lt = None; - let resources = match ctxt { Context::Init => &app.init.args.local_resources, Context::Idle => { @@ -28,7 +26,6 @@ pub fn codegen(ctxt: Context, app: &App) -> (TokenStream2, TokenStream2) { let mut fields = vec![]; let mut values = vec![]; - let mut has_cfgs = false; for (name, task_local) in resources { let (cfgs, ty, is_declared) = match task_local { @@ -39,12 +36,9 @@ pub fn codegen(ctxt: Context, app: &App) -> (TokenStream2, TokenStream2) { TaskLocal::Declared(r) => (&r.cfgs, &r.ty, true), }; - has_cfgs |= !cfgs.is_empty(); - let lt = if ctxt.runs_once() { quote!('static) } else { - lt = Some(quote!('a)); quote!('a) }; @@ -73,17 +67,12 @@ pub fn codegen(ctxt: Context, app: &App) -> (TokenStream2, TokenStream2) { )); } - if lt.is_some() { - // The struct could end up empty due to `cfg`s leading to an error due to `'a` being unused - if has_cfgs { - fields.push(quote!( - #[doc(hidden)] - pub __rtic_internal_marker: ::core::marker::PhantomData<&'a ()> - )); + fields.push(quote!( + #[doc(hidden)] + pub __rtic_internal_marker: ::core::marker::PhantomData<&'a ()> + )); - values.push(quote!(__rtic_internal_marker: ::core::marker::PhantomData)); - } - } + values.push(quote!(__rtic_internal_marker: ::core::marker::PhantomData)); let doc = format!("Local resources `{}` has access to", ctxt.ident(app)); let ident = util::local_resources_ident(ctxt, app); @@ -91,13 +80,13 @@ pub fn codegen(ctxt: Context, app: &App) -> (TokenStream2, TokenStream2) { #[allow(non_snake_case)] #[allow(non_camel_case_types)] #[doc = #doc] - pub struct #ident<#lt> { + pub struct #ident<'a> { #(#fields,)* } ); let constructor = quote!( - impl<#lt> #ident<#lt> { + impl<'a> #ident<'a> { #[inline(always)] pub unsafe fn new() -> Self { #ident { diff --git a/macros/src/codegen/shared_resources_struct.rs b/macros/src/codegen/shared_resources_struct.rs index de597cab16..24c93de6c1 100644 --- a/macros/src/codegen/shared_resources_struct.rs +++ b/macros/src/codegen/shared_resources_struct.rs @@ -6,8 +6,6 @@ use crate::codegen::util; /// Generate shared resources structs pub fn codegen(ctxt: Context, app: &App) -> (TokenStream2, TokenStream2) { - let mut lt = None; - let resources = match ctxt { Context::Init => unreachable!("Tried to generate shared resources struct for init"), Context::Idle => { @@ -23,13 +21,11 @@ pub fn codegen(ctxt: Context, app: &App) -> (TokenStream2, TokenStream2) { let mut fields = vec![]; let mut values = vec![]; - let mut has_cfgs = false; for (name, access) in resources { let res = app.shared_resources.get(name).expect("UNREACHABLE"); let cfgs = &res.cfgs; - has_cfgs |= !cfgs.is_empty(); // access hold if the resource is [x] (exclusive) or [&x] (shared) let mut_ = if access.is_exclusive() { @@ -46,7 +42,6 @@ pub fn codegen(ctxt: Context, app: &App) -> (TokenStream2, TokenStream2) { let lt = if ctxt.runs_once() { quote!('static) } else { - lt = Some(quote!('a)); quote!('a) }; @@ -55,16 +50,11 @@ pub fn codegen(ctxt: Context, app: &App) -> (TokenStream2, TokenStream2) { pub #name: &#lt #mut_ #ty )); } else if access.is_shared() { - lt = Some(quote!('a)); - fields.push(quote!( #(#cfgs)* pub #name: &'a #ty )); } else { - // Resource proxy - lt = Some(quote!('a)); - fields.push(quote!( #(#cfgs)* pub #name: shared_resources::#shared_name<'a> @@ -92,17 +82,12 @@ pub fn codegen(ctxt: Context, app: &App) -> (TokenStream2, TokenStream2) { )); } - if lt.is_some() { - // The struct could end up empty due to `cfg`s leading to an error due to `'a` being unused - if has_cfgs { - fields.push(quote!( - #[doc(hidden)] - pub __marker__: core::marker::PhantomData<&'a ()> - )); + fields.push(quote!( + #[doc(hidden)] + pub __rtic_internal_marker: core::marker::PhantomData<&'a ()> + )); - values.push(quote!(__marker__: core::marker::PhantomData)); - } - } + values.push(quote!(__rtic_internal_marker: core::marker::PhantomData)); let doc = format!("Shared resources `{}` has access to", ctxt.ident(app)); let ident = util::shared_resources_ident(ctxt, app); @@ -110,13 +95,13 @@ pub fn codegen(ctxt: Context, app: &App) -> (TokenStream2, TokenStream2) { #[allow(non_snake_case)] #[allow(non_camel_case_types)] #[doc = #doc] - pub struct #ident<#lt> { + pub struct #ident<'a> { #(#fields,)* } ); let constructor = quote!( - impl<#lt> #ident<#lt> { + impl<'a> #ident<'a> { #[inline(always)] pub unsafe fn new() -> Self { #ident { From fe2b5cc52ee634346bc8aecf5041b6af9fdea529 Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Sat, 7 Jan 2023 13:23:20 +0100 Subject: [PATCH 225/423] Removed same prio spawn --- macros/src/codegen/init.rs | 1 - macros/src/syntax/ast.rs | 4 --- macros/src/syntax/parse.rs | 32 ------------------- macros/ui/task-interrupt-same-prio-spawn.rs | 7 ---- .../ui/task-interrupt-same-prio-spawn.stderr | 5 --- 5 files changed, 49 deletions(-) delete mode 100644 macros/ui/task-interrupt-same-prio-spawn.rs delete mode 100644 macros/ui/task-interrupt-same-prio-spawn.stderr diff --git a/macros/src/codegen/init.rs b/macros/src/codegen/init.rs index bbde4f275f..2aa8fb31fb 100644 --- a/macros/src/codegen/init.rs +++ b/macros/src/codegen/init.rs @@ -25,7 +25,6 @@ type CodegenResult = ( /// Generates support code for `#[init]` functions pub fn codegen(app: &App, analysis: &Analysis) -> CodegenResult { let init = &app.init; - let mut local_needs_lt = false; let name = &init.name; let mut root_init = vec![]; diff --git a/macros/src/syntax/ast.rs b/macros/src/syntax/ast.rs index ea6e402c9a..da6016add8 100644 --- a/macros/src/syntax/ast.rs +++ b/macros/src/syntax/ast.rs @@ -224,9 +224,6 @@ pub struct SoftwareTaskArgs { /// Shared resources that can be accessed from this context pub shared_resources: SharedResources, - - /// Only same priority tasks can spawn this task - pub only_same_priority_spawn: bool, } impl Default for SoftwareTaskArgs { @@ -235,7 +232,6 @@ impl Default for SoftwareTaskArgs { priority: 1, local_resources: LocalResources::new(), shared_resources: SharedResources::new(), - only_same_priority_spawn: false, } } } diff --git a/macros/src/syntax/parse.rs b/macros/src/syntax/parse.rs index abdd677ab8..c78453a437 100644 --- a/macros/src/syntax/parse.rs +++ b/macros/src/syntax/parse.rs @@ -196,8 +196,6 @@ fn task_args(tokens: TokenStream2) -> parse::Result parse::Result parse::Result { return Err(parse::Error::new(ident.span(), "unexpected argument")); } @@ -369,13 +345,6 @@ fn task_args(tokens: TokenStream2) -> parse::Result parse::Result ui/task-interrupt-same-prio-spawn.rs:5:29 - | -5 | #[task(binds = SysTick, only_same_priority_spawn_please_fix_me)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ From 9247252cc74fab4a421f8e8370b29c37ca2fa48a Mon Sep 17 00:00:00 2001 From: Per Lindgren Date: Sat, 7 Jan 2023 13:58:15 +0100 Subject: [PATCH 226/423] examples/async-task fixup --- examples/async-task.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/examples/async-task.rs b/examples/async-task.rs index 45d44fd583..012e95eec2 100644 --- a/examples/async-task.rs +++ b/examples/async-task.rs @@ -6,7 +6,7 @@ use panic_semihosting as _; // NOTES: // -// - Async tasks cannot have `#[lock_free]` resources, as they can interleve and each async +// - Async tasks cannot have `#[lock_free]` resources, as they can interleave and each async // task can have a mutable reference stored. // - Spawning an async task equates to it being polled once. @@ -23,7 +23,7 @@ mod app { struct Local {} #[init] - fn init(cx: init::Context) -> (Shared, Local) { + fn init(_cx: init::Context) -> (Shared, Local) { hprintln!("init").unwrap(); async_task::spawn().unwrap(); @@ -43,13 +43,13 @@ mod app { #[task(binds = UART1, shared = [a])] fn hw_task(cx: hw_task::Context) { - let hw_task::SharedResources { a, .. } = cx.shared; + let hw_task::SharedResources { a: _, .. } = cx.shared; hprintln!("hello from hw").ok(); } #[task(shared = [a])] async fn async_task(cx: async_task::Context) { - let async_task::SharedResources { a, .. } = cx.shared; + let async_task::SharedResources { a: _, .. } = cx.shared; hprintln!("hello from async").ok(); debug::exit(debug::EXIT_SUCCESS); @@ -57,7 +57,7 @@ mod app { #[task(priority = 2, shared = [a])] async fn async_task2(cx: async_task2::Context) { - let async_task2::SharedResources { a, .. } = cx.shared; + let async_task2::SharedResources { a: _, .. } = cx.shared; hprintln!("hello from async2").ok(); } } From 29228c47239f12794feb91ae5a81d748530c40dc Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Sat, 7 Jan 2023 14:06:11 +0100 Subject: [PATCH 227/423] Main in main codegen --- macros/src/codegen.rs | 43 ++++----------------------- macros/src/codegen/idle.rs | 18 ++---------- macros/src/codegen/init.rs | 9 ++---- macros/src/codegen/main.rs | 51 +++++++++++++++++++++++++++++++++ macros/src/codegen/post_init.rs | 4 +-- 5 files changed, 62 insertions(+), 63 deletions(-) create mode 100644 macros/src/codegen/main.rs diff --git a/macros/src/codegen.rs b/macros/src/codegen.rs index b490d7a501..839b1cd401 100644 --- a/macros/src/codegen.rs +++ b/macros/src/codegen.rs @@ -19,6 +19,8 @@ mod shared_resources_struct; mod software_tasks; mod util; +mod main; + // TODO: organize codegen to actual parts of code // so `main::codegen` generates ALL the code for `fn main`, // `software_tasks::codegen` generates ALL the code for software tasks etc... @@ -26,20 +28,15 @@ mod util; #[allow(clippy::too_many_lines)] pub fn app(app: &App, analysis: &Analysis) -> TokenStream2 { let mut mod_app = vec![]; - let mut mains = vec![]; let mut root = vec![]; let mut user = vec![]; // Generate the `main` function - let assertion_stmts = assertions::codegen(app, analysis); + let main = main::codegen(app, analysis); - let pre_init_stmts = pre_init::codegen(app, analysis); + let (mod_app_init, root_init, user_init) = init::codegen(app, analysis); - let (mod_app_init, root_init, user_init, call_init) = init::codegen(app, analysis); - - let post_init_stmts = post_init::codegen(app, analysis); - - let (mod_app_idle, root_idle, user_idle, call_idle) = idle::codegen(app, analysis); + let (mod_app_idle, root_idle, user_idle) = idle::codegen(app, analysis); user.push(quote!( #user_init @@ -59,34 +56,6 @@ pub fn app(app: &App, analysis: &Analysis) -> TokenStream2 { #(#mod_app_idle)* )); - let main = util::suffixed("main"); - mains.push(quote!( - #[doc(hidden)] - mod rtic_ext { - use super::*; - #[no_mangle] - unsafe extern "C" fn #main() -> ! { - #(#assertion_stmts)* - - #(#pre_init_stmts)* - - #[inline(never)] - fn __rtic_init_resources(f: F) where F: FnOnce() { - f(); - } - - // Wrap late_init_stmts in a function to ensure that stack space is reclaimed. - __rtic_init_resources(||{ - #call_init - - #(#post_init_stmts)* - }); - - #call_idle - } - } - )); - let (mod_app_shared_resources, mod_shared_resources) = shared_resources::codegen(app, analysis); let (mod_app_local_resources, mod_local_resources) = local_resources::codegen(app, analysis); @@ -145,7 +114,7 @@ pub fn app(app: &App, analysis: &Analysis) -> TokenStream2 { #(#mod_app_async_dispatchers)* - #(#mains)* + #main } ) } diff --git a/macros/src/codegen/idle.rs b/macros/src/codegen/idle.rs index c0eecbcabb..1f05d12901 100644 --- a/macros/src/codegen/idle.rs +++ b/macros/src/codegen/idle.rs @@ -20,8 +20,6 @@ pub fn codegen( Vec, // user_idle Option, - // call_idle - TokenStream2, ) { if let Some(idle) = &app.idle { let mut mod_app = vec![]; @@ -60,22 +58,10 @@ pub fn codegen( } )); - let call_idle = quote!(#name( - #name::Context::new() - )); - - (mod_app, root_idle, user_idle, call_idle) + (mod_app, root_idle, user_idle) } else { // TODO: No idle defined, check for 0-priority tasks and generate an executor if needed - unimplemented!(); - // ( - // vec![], - // vec![], - // None, - // quote!(loop { - // rtic::export::nop() - // }), - // ) + (vec![], vec![], None) } } diff --git a/macros/src/codegen/init.rs b/macros/src/codegen/init.rs index 2aa8fb31fb..3b2bcd471b 100644 --- a/macros/src/codegen/init.rs +++ b/macros/src/codegen/init.rs @@ -18,8 +18,6 @@ type CodegenResult = ( Vec, // user_init -- the `#[init]` function written by the user TokenStream2, - // call_init -- the call to the user `#[init]` - TokenStream2, ); /// Generates support code for `#[init]` functions @@ -63,6 +61,7 @@ pub fn codegen(app: &App, analysis: &Analysis) -> CodegenResult { ) }) .collect(); + root_init.push(quote! { struct #shared { #(#shared_resources)* @@ -97,11 +96,7 @@ pub fn codegen(app: &App, analysis: &Analysis) -> CodegenResult { mod_app = Some(constructor); } - let call_init = quote! { - let (shared_resources, local_resources) = #name(#name::Context::new(core.into())); - }; - root_init.push(module::codegen(Context::Init, app, analysis)); - (mod_app, root_init, user_init, call_init) + (mod_app, root_init, user_init) } diff --git a/macros/src/codegen/main.rs b/macros/src/codegen/main.rs new file mode 100644 index 0000000000..90f09ae0d8 --- /dev/null +++ b/macros/src/codegen/main.rs @@ -0,0 +1,51 @@ +use crate::{analyze::Analysis, codegen::util, syntax::ast::App}; +use proc_macro2::TokenStream as TokenStream2; +use quote::quote; + +use super::{assertions, post_init, pre_init}; + +/// Generates code for `fn main` +pub fn codegen(app: &App, analysis: &Analysis) -> TokenStream2 { + let assertion_stmts = assertions::codegen(app, analysis); + + let pre_init_stmts = pre_init::codegen(app, analysis); + + let post_init_stmts = post_init::codegen(app, analysis); + + let call_idle = if let Some(idle) = &app.idle { + let name = &idle.name; + quote!(#name(#name::Context::new())) + } else { + // TODO: No idle defined, check for 0-priority tasks and generate an executor if needed + + quote!(loop { + rtic::export::nop() + }) + }; + + let main = util::suffixed("main"); + let init_name = &app.init.name; + quote!( + #[doc(hidden)] + #[no_mangle] + unsafe extern "C" fn #main() -> ! { + #(#assertion_stmts)* + + #(#pre_init_stmts)* + + #[inline(never)] + fn __rtic_init_resources(f: F) where F: FnOnce() { + f(); + } + + // Wrap late_init_stmts in a function to ensure that stack space is reclaimed. + __rtic_init_resources(||{ + let (shared_resources, local_resources) = #init_name(#init_name::Context::new(core.into())); + + #(#post_init_stmts)* + }); + + #call_idle + } + ) +} diff --git a/macros/src/codegen/post_init.rs b/macros/src/codegen/post_init.rs index e8183b9368..c4e5383718 100644 --- a/macros/src/codegen/post_init.rs +++ b/macros/src/codegen/post_init.rs @@ -1,9 +1,7 @@ -use crate::syntax::ast::App; +use crate::{analyze::Analysis, codegen::util, syntax::ast::App}; use proc_macro2::TokenStream as TokenStream2; use quote::quote; -use crate::{analyze::Analysis, codegen::util}; - /// Generates code that runs after `#[init]` returns pub fn codegen(app: &App, analysis: &Analysis) -> Vec { let mut stmts = vec![]; From 6dc2d29cd994a27fa59e23f9fb0bece677c83ffa Mon Sep 17 00:00:00 2001 From: Per Lindgren Date: Sat, 7 Jan 2023 14:07:50 +0100 Subject: [PATCH 228/423] export Cell removed, expmples updated --- examples/idle.rs | 4 ++-- examples/init.rs | 4 ++-- src/export.rs | 5 +---- 3 files changed, 5 insertions(+), 8 deletions(-) diff --git a/examples/idle.rs b/examples/idle.rs index 55d6b15352..9a7a7275a9 100644 --- a/examples/idle.rs +++ b/examples/idle.rs @@ -18,10 +18,10 @@ mod app { struct Local {} #[init] - fn init(_: init::Context) -> (Shared, Local, init::Monotonics) { + fn init(_: init::Context) -> (Shared, Local) { hprintln!("init").unwrap(); - (Shared {}, Local {}, init::Monotonics()) + (Shared {}, Local {}) } #[idle(local = [x: u32 = 0])] diff --git a/examples/init.rs b/examples/init.rs index b8a5bc5b98..c807a3c10b 100644 --- a/examples/init.rs +++ b/examples/init.rs @@ -18,7 +18,7 @@ mod app { struct Local {} #[init(local = [x: u32 = 0])] - fn init(cx: init::Context) -> (Shared, Local, init::Monotonics) { + fn init(cx: init::Context) -> (Shared, Local) { // Cortex-M peripherals let _core: cortex_m::Peripherals = cx.core; @@ -36,6 +36,6 @@ mod app { debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator - (Shared {}, Local {}, init::Monotonics()) + (Shared {}, Local {}) } } diff --git a/src/export.rs b/src/export.rs index 49ebd878ef..d6b2fc0406 100644 --- a/src/export.rs +++ b/src/export.rs @@ -1,8 +1,5 @@ pub use bare_metal::CriticalSection; -use core::{ - cell::Cell, - sync::atomic::{AtomicBool, Ordering}, -}; +use core::sync::atomic::{AtomicBool, Ordering}; pub use cortex_m::{ asm::nop, asm::wfi, From 4337e3980c52116e1606c60ff12eaea4a9971ece Mon Sep 17 00:00:00 2001 From: Per Lindgren Date: Sat, 7 Jan 2023 14:09:31 +0100 Subject: [PATCH 229/423] examples/idle-wfi fixed --- examples/idle-wfi.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/idle-wfi.rs b/examples/idle-wfi.rs index 4a8a8dee2b..dd2d43f7c7 100644 --- a/examples/idle-wfi.rs +++ b/examples/idle-wfi.rs @@ -18,14 +18,14 @@ mod app { struct Local {} #[init] - fn init(mut cx: init::Context) -> (Shared, Local, init::Monotonics) { + fn init(mut cx: init::Context) -> (Shared, Local) { hprintln!("init").unwrap(); // Set the ARM SLEEPONEXIT bit to go to sleep after handling interrupts // See https://developer.arm.com/docs/100737/0100/power-management/sleep-mode/sleep-on-exit-bit cx.core.SCB.set_sleepdeep(); - (Shared {}, Local {}, init::Monotonics()) + (Shared {}, Local {}) } #[idle(local = [x: u32 = 0])] From b9b3ded5e21c40256163cf85f4fba2991c03a45c Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Sat, 7 Jan 2023 14:13:18 +0100 Subject: [PATCH 230/423] Cleanup weird locals in codegen --- macros/src/codegen.rs | 39 +++++++++++---------------------------- 1 file changed, 11 insertions(+), 28 deletions(-) diff --git a/macros/src/codegen.rs b/macros/src/codegen.rs index 839b1cd401..bb1028f764 100644 --- a/macros/src/codegen.rs +++ b/macros/src/codegen.rs @@ -27,10 +27,6 @@ mod main; #[allow(clippy::too_many_lines)] pub fn app(app: &App, analysis: &Analysis) -> TokenStream2 { - let mut mod_app = vec![]; - let mut root = vec![]; - let mut user = vec![]; - // Generate the `main` function let main = main::codegen(app, analysis); @@ -38,24 +34,6 @@ pub fn app(app: &App, analysis: &Analysis) -> TokenStream2 { let (mod_app_idle, root_idle, user_idle) = idle::codegen(app, analysis); - user.push(quote!( - #user_init - - #user_idle - )); - - root.push(quote!( - #(#root_init)* - - #(#root_idle)* - )); - - mod_app.push(quote!( - #mod_app_init - - #(#mod_app_idle)* - )); - let (mod_app_shared_resources, mod_shared_resources) = shared_resources::codegen(app, analysis); let (mod_app_local_resources, mod_local_resources) = local_resources::codegen(app, analysis); @@ -85,13 +63,21 @@ pub fn app(app: &App, analysis: &Analysis) -> TokenStream2 { #(#user_code)* /// User code end - #(#user)* - #(#user_hardware_tasks)* #(#user_software_tasks)* - #(#root)* + #mod_app_init + + #(#root_init)* + + #user_init + + #(#mod_app_idle)* + + #(#root_idle)* + + #user_idle #mod_shared_resources @@ -101,9 +87,6 @@ pub fn app(app: &App, analysis: &Analysis) -> TokenStream2 { #(#root_software_tasks)* - /// app module - #(#mod_app)* - #(#mod_app_shared_resources)* #(#mod_app_local_resources)* From bd20d0d89e3ea477c9970f06f0ec750cee71690b Mon Sep 17 00:00:00 2001 From: Per Lindgren Date: Sat, 7 Jan 2023 14:16:24 +0100 Subject: [PATCH 231/423] examples/locals fixed --- examples/locals.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/locals.rs b/examples/locals.rs index aa5d0fee30..a35b4c9275 100644 --- a/examples/locals.rs +++ b/examples/locals.rs @@ -1,5 +1,6 @@ //! examples/locals.rs +#![feature(type_alias_impl_trait)] #![deny(unsafe_code)] #![deny(warnings)] #![no_main] @@ -23,7 +24,7 @@ mod app { // `#[init]` cannot access locals from the `#[local]` struct as they are initialized here. #[init] - fn init(_: init::Context) -> (Shared, Local, init::Monotonics) { + fn init(_: init::Context) -> (Shared, Local) { foo::spawn().unwrap(); bar::spawn().unwrap(); @@ -35,7 +36,6 @@ mod app { local_to_bar: 0, local_to_idle: 0, }, - init::Monotonics(), ) } @@ -62,7 +62,7 @@ mod app { // `local_to_foo` can only be accessed from this context #[task(local = [local_to_foo])] - fn foo(cx: foo::Context) { + async fn foo(cx: foo::Context) { let local_to_foo = cx.local.local_to_foo; *local_to_foo += 1; @@ -74,7 +74,7 @@ mod app { // `local_to_bar` can only be accessed from this context #[task(local = [local_to_bar])] - fn bar(cx: bar::Context) { + async fn bar(cx: bar::Context) { let local_to_bar = cx.local.local_to_bar; *local_to_bar += 1; From b054e871d486e8eb35e3c98a73652640238c5e7d Mon Sep 17 00:00:00 2001 From: Per Lindgren Date: Sat, 7 Jan 2023 14:23:32 +0100 Subject: [PATCH 232/423] examples/lock fixed --- examples/{lock-free.rs => lock-free.no_rs} | 9 +++++---- examples/lock.rs | 11 ++++++----- 2 files changed, 11 insertions(+), 9 deletions(-) rename examples/{lock-free.rs => lock-free.no_rs} (83%) diff --git a/examples/lock-free.rs b/examples/lock-free.no_rs similarity index 83% rename from examples/lock-free.rs rename to examples/lock-free.no_rs index ea6ff1bf37..053307c16d 100644 --- a/examples/lock-free.rs +++ b/examples/lock-free.no_rs @@ -4,6 +4,7 @@ #![deny(warnings)] #![no_main] #![no_std] +#![feature(type_alias_impl_trait)] use panic_semihosting as _; @@ -21,14 +22,14 @@ mod app { struct Local {} #[init] - fn init(_: init::Context) -> (Shared, Local, init::Monotonics) { + fn init(_: init::Context) -> (Shared, Local) { foo::spawn().unwrap(); - (Shared { counter: 0 }, Local {}, init::Monotonics()) + (Shared { counter: 0 }, Local {}) } #[task(shared = [counter])] // <- same priority - fn foo(c: foo::Context) { + async fn foo(c: foo::Context) { bar::spawn().unwrap(); *c.shared.counter += 1; // <- no lock API required @@ -37,7 +38,7 @@ mod app { } #[task(shared = [counter])] // <- same priority - fn bar(c: bar::Context) { + async fn bar(c: bar::Context) { foo::spawn().unwrap(); *c.shared.counter += 1; // <- no lock API required diff --git a/examples/lock.rs b/examples/lock.rs index f1a16968ce..50b6aaaefd 100644 --- a/examples/lock.rs +++ b/examples/lock.rs @@ -4,6 +4,7 @@ #![deny(warnings)] #![no_main] #![no_std] +#![feature(type_alias_impl_trait)] use panic_semihosting as _; @@ -20,15 +21,15 @@ mod app { struct Local {} #[init] - fn init(_: init::Context) -> (Shared, Local, init::Monotonics) { + fn init(_: init::Context) -> (Shared, Local) { foo::spawn().unwrap(); - (Shared { shared: 0 }, Local {}, init::Monotonics()) + (Shared { shared: 0 }, Local {}) } // when omitted priority is assumed to be `1` #[task(shared = [shared])] - fn foo(mut c: foo::Context) { + async fn foo(mut c: foo::Context) { hprintln!("A").unwrap(); // the lower priority task requires a critical section to access the data @@ -53,7 +54,7 @@ mod app { } #[task(priority = 2, shared = [shared])] - fn bar(mut c: bar::Context) { + async fn bar(mut c: bar::Context) { // the higher priority task does still need a critical section let shared = c.shared.shared.lock(|shared| { *shared += 1; @@ -65,7 +66,7 @@ mod app { } #[task(priority = 3)] - fn baz(_: baz::Context) { + async fn baz(_: baz::Context) { hprintln!("C").unwrap(); } } From 76595b7aedd2a14aea8569b75fabe62120f93230 Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Sat, 7 Jan 2023 14:26:55 +0100 Subject: [PATCH 233/423] All codegen is now explicit --- macros/src/codegen.rs | 56 +++++++------------------ macros/src/codegen/async_dispatchers.rs | 4 +- macros/src/codegen/hardware_tasks.rs | 23 ++++------ macros/src/codegen/idle.rs | 27 ++++-------- macros/src/codegen/init.rs | 23 ++++------ macros/src/codegen/local_resources.rs | 13 +----- macros/src/codegen/shared_resources.rs | 16 +++---- macros/src/codegen/software_tasks.rs | 23 ++++------ 8 files changed, 57 insertions(+), 128 deletions(-) diff --git a/macros/src/codegen.rs b/macros/src/codegen.rs index bb1028f764..24e98ce90a 100644 --- a/macros/src/codegen.rs +++ b/macros/src/codegen.rs @@ -29,21 +29,14 @@ mod main; pub fn app(app: &App, analysis: &Analysis) -> TokenStream2 { // Generate the `main` function let main = main::codegen(app, analysis); + let init_codegen = init::codegen(app, analysis); + let idle_codegen = idle::codegen(app, analysis); + let shared_resources_codegen = shared_resources::codegen(app, analysis); + let local_resources_codegen = local_resources::codegen(app, analysis); + let hardware_tasks_codegen = hardware_tasks::codegen(app, analysis); + let software_tasks_codegen = software_tasks::codegen(app, analysis); + let async_dispatchers_codegen = async_dispatchers::codegen(app, analysis); - let (mod_app_init, root_init, user_init) = init::codegen(app, analysis); - - let (mod_app_idle, root_idle, user_idle) = idle::codegen(app, analysis); - - let (mod_app_shared_resources, mod_shared_resources) = shared_resources::codegen(app, analysis); - let (mod_app_local_resources, mod_local_resources) = local_resources::codegen(app, analysis); - - let (mod_app_hardware_tasks, root_hardware_tasks, user_hardware_tasks) = - hardware_tasks::codegen(app, analysis); - - let (mod_app_software_tasks, root_software_tasks, user_software_tasks) = - software_tasks::codegen(app, analysis); - - let mod_app_async_dispatchers = async_dispatchers::codegen(app, analysis); let user_imports = &app.user_imports; let user_code = &app.user_code; let name = &app.name; @@ -59,43 +52,22 @@ pub fn app(app: &App, analysis: &Analysis) -> TokenStream2 { #(#user_imports)* - /// User code from within the module #(#user_code)* /// User code end - #(#user_hardware_tasks)* + #init_codegen - #(#user_software_tasks)* + #idle_codegen - #mod_app_init + #hardware_tasks_codegen - #(#root_init)* + #software_tasks_codegen - #user_init + #shared_resources_codegen - #(#mod_app_idle)* + #local_resources_codegen - #(#root_idle)* - - #user_idle - - #mod_shared_resources - - #mod_local_resources - - #(#root_hardware_tasks)* - - #(#root_software_tasks)* - - #(#mod_app_shared_resources)* - - #(#mod_app_local_resources)* - - #(#mod_app_hardware_tasks)* - - #(#mod_app_software_tasks)* - - #(#mod_app_async_dispatchers)* + #async_dispatchers_codegen #main } diff --git a/macros/src/codegen/async_dispatchers.rs b/macros/src/codegen/async_dispatchers.rs index d53d7b5e0c..62b17fee4a 100644 --- a/macros/src/codegen/async_dispatchers.rs +++ b/macros/src/codegen/async_dispatchers.rs @@ -4,7 +4,7 @@ use proc_macro2::TokenStream as TokenStream2; use quote::quote; /// Generates task dispatchers -pub fn codegen(app: &App, analysis: &Analysis) -> Vec { +pub fn codegen(app: &App, analysis: &Analysis) -> TokenStream2 { let mut items = vec![]; let interrupts = &analysis.interrupts; @@ -96,5 +96,5 @@ pub fn codegen(app: &App, analysis: &Analysis) -> Vec { )); } - items + quote!(#(#items)*) } diff --git a/macros/src/codegen/hardware_tasks.rs b/macros/src/codegen/hardware_tasks.rs index 9ea5825b3d..8a5a8f6cf4 100644 --- a/macros/src/codegen/hardware_tasks.rs +++ b/macros/src/codegen/hardware_tasks.rs @@ -7,20 +7,7 @@ use proc_macro2::TokenStream as TokenStream2; use quote::quote; /// Generate support code for hardware tasks (`#[exception]`s and `#[interrupt]`s) -pub fn codegen( - app: &App, - analysis: &Analysis, -) -> ( - // mod_app_hardware_tasks -- interrupt handlers and `${task}Resources` constructors - Vec, - // root_hardware_tasks -- items that must be placed in the root of the crate: - // - `${task}Locals` structs - // - `${task}Resources` structs - // - `${task}` modules - Vec, - // user_hardware_tasks -- the `#[task]` functions written by the user - Vec, -) { +pub fn codegen(app: &App, analysis: &Analysis) -> TokenStream2 { let mut mod_app = vec![]; let mut root = vec![]; let mut user_tasks = vec![]; @@ -90,5 +77,11 @@ pub fn codegen( } } - (mod_app, root, user_tasks) + quote!( + #(#mod_app)* + + #(#root)* + + #(#user_tasks)* + ) } diff --git a/macros/src/codegen/idle.rs b/macros/src/codegen/idle.rs index 1f05d12901..0c833ef391 100644 --- a/macros/src/codegen/idle.rs +++ b/macros/src/codegen/idle.rs @@ -7,20 +7,7 @@ use proc_macro2::TokenStream as TokenStream2; use quote::quote; /// Generates support code for `#[idle]` functions -pub fn codegen( - app: &App, - analysis: &Analysis, -) -> ( - // mod_app_idle -- the `${idle}Resources` constructor - Vec, - // root_idle -- items that must be placed in the root of the crate: - // - the `${idle}Locals` struct - // - the `${idle}Resources` struct - // - the `${idle}` module, which contains types like `${idle}::Context` - Vec, - // user_idle - Option, -) { +pub fn codegen(app: &App, analysis: &Analysis) -> TokenStream2 { if let Some(idle) = &app.idle { let mut mod_app = vec![]; let mut root_idle = vec![]; @@ -58,10 +45,14 @@ pub fn codegen( } )); - (mod_app, root_idle, user_idle) - } else { - // TODO: No idle defined, check for 0-priority tasks and generate an executor if needed + quote!( + #(#mod_app)* - (vec![], vec![], None) + #(#root_idle)* + + #user_idle + ) + } else { + quote!() } } diff --git a/macros/src/codegen/init.rs b/macros/src/codegen/init.rs index 3b2bcd471b..6e1059f7eb 100644 --- a/macros/src/codegen/init.rs +++ b/macros/src/codegen/init.rs @@ -7,21 +7,8 @@ use crate::{ syntax::{ast::App, Context}, }; -type CodegenResult = ( - // mod_app_idle -- the `${init}Resources` constructor - Option, - // root_init -- items that must be placed in the root of the crate: - // - the `${init}Locals` struct - // - the `${init}Resources` struct - // - the `${init}LateResources` struct - // - the `${init}` module, which contains types like `${init}::Context` - Vec, - // user_init -- the `#[init]` function written by the user - TokenStream2, -); - /// Generates support code for `#[init]` functions -pub fn codegen(app: &App, analysis: &Analysis) -> CodegenResult { +pub fn codegen(app: &App, analysis: &Analysis) -> TokenStream2 { let init = &app.init; let name = &init.name; @@ -98,5 +85,11 @@ pub fn codegen(app: &App, analysis: &Analysis) -> CodegenResult { root_init.push(module::codegen(Context::Init, app, analysis)); - (mod_app, root_init, user_init) + quote!( + #mod_app + + #(#root_init)* + + #user_init + ) } diff --git a/macros/src/codegen/local_resources.rs b/macros/src/codegen/local_resources.rs index 6fc63cd93e..e6d15533b8 100644 --- a/macros/src/codegen/local_resources.rs +++ b/macros/src/codegen/local_resources.rs @@ -6,17 +6,8 @@ use quote::quote; /// Generates `local` variables and local resource proxies /// /// I.e. the `static` variables and theirs proxies. -pub fn codegen( - app: &App, - _analysis: &Analysis, -) -> ( - // mod_app -- the `static` variables behind the proxies - Vec, - // mod_resources -- the `resources` module - TokenStream2, -) { +pub fn codegen(app: &App, _analysis: &Analysis) -> TokenStream2 { let mut mod_app = vec![]; - // let mut mod_resources: _ = vec![]; // All local resources declared in the `#[local]' struct for (name, res) in &app.local_resources { @@ -70,5 +61,5 @@ pub fn codegen( )); } - (mod_app, TokenStream2::new()) + quote!(#(#mod_app)*) } diff --git a/macros/src/codegen/shared_resources.rs b/macros/src/codegen/shared_resources.rs index 5c54fb99b2..19fd13fecc 100644 --- a/macros/src/codegen/shared_resources.rs +++ b/macros/src/codegen/shared_resources.rs @@ -5,15 +5,7 @@ use quote::quote; use std::collections::HashMap; /// Generates `static` variables and shared resource proxies -pub fn codegen( - app: &App, - analysis: &Analysis, -) -> ( - // mod_app -- the `static` variables behind the proxies - Vec, - // mod_resources -- the `resources` module - TokenStream2, -) { +pub fn codegen(app: &App, analysis: &Analysis) -> TokenStream2 { let mut mod_app = vec![]; let mut mod_resources = vec![]; @@ -183,5 +175,9 @@ pub fn codegen( )); } - (mod_app, mod_resources) + quote!( + #(#mod_app)* + + #mod_resources + ) } diff --git a/macros/src/codegen/software_tasks.rs b/macros/src/codegen/software_tasks.rs index 350c1e6009..4cb1fa95f6 100644 --- a/macros/src/codegen/software_tasks.rs +++ b/macros/src/codegen/software_tasks.rs @@ -6,20 +6,7 @@ use crate::{ use proc_macro2::TokenStream as TokenStream2; use quote::quote; -pub fn codegen( - app: &App, - analysis: &Analysis, -) -> ( - // mod_app_software_tasks -- free queues, buffers and `${task}Resources` constructors - Vec, - // root_software_tasks -- items that must be placed in the root of the crate: - // - `${task}Locals` structs - // - `${task}Resources` structs - // - `${task}` modules - Vec, - // user_software_tasks -- the `#[task]` functions written by the user - Vec, -) { +pub fn codegen(app: &App, analysis: &Analysis) -> TokenStream2 { let mut mod_app = vec![]; let mut root = vec![]; let mut user_tasks = vec![]; @@ -78,5 +65,11 @@ pub fn codegen( root.push(module::codegen(Context::SoftwareTask(name), app, analysis)); } - (mod_app, root, user_tasks) + quote!( + #(#mod_app)* + + #(#root)* + + #(#user_tasks)* + ) } From 569a761122f15a92671b299603a5dc7fe6e5324b Mon Sep 17 00:00:00 2001 From: Per Lindgren Date: Sat, 7 Jan 2023 14:27:57 +0100 Subject: [PATCH 234/423] examples/multiloc fixed --- examples/{message.rs => message.no_rs} | 0 examples/{message_passing.rs => message_passing.no_rs} | 0 examples/multilock.rs | 6 +++--- 3 files changed, 3 insertions(+), 3 deletions(-) rename examples/{message.rs => message.no_rs} (100%) rename examples/{message_passing.rs => message_passing.no_rs} (100%) diff --git a/examples/message.rs b/examples/message.no_rs similarity index 100% rename from examples/message.rs rename to examples/message.no_rs diff --git a/examples/message_passing.rs b/examples/message_passing.no_rs similarity index 100% rename from examples/message_passing.rs rename to examples/message_passing.no_rs diff --git a/examples/multilock.rs b/examples/multilock.rs index d99bae695e..7bea2d37c4 100644 --- a/examples/multilock.rs +++ b/examples/multilock.rs @@ -4,6 +4,7 @@ #![deny(warnings)] #![no_main] #![no_std] +#![feature(type_alias_impl_trait)] use panic_semihosting as _; @@ -22,7 +23,7 @@ mod app { struct Local {} #[init] - fn init(_: init::Context) -> (Shared, Local, init::Monotonics) { + fn init(_: init::Context) -> (Shared, Local) { locks::spawn().unwrap(); ( @@ -32,13 +33,12 @@ mod app { shared3: 0, }, Local {}, - init::Monotonics(), ) } // when omitted priority is assumed to be `1` #[task(shared = [shared1, shared2, shared3])] - fn locks(c: locks::Context) { + async fn locks(c: locks::Context) { let s1 = c.shared.shared1; let s2 = c.shared.shared2; let s3 = c.shared.shared3; From 5606ba3cf38c80be5d3e9c88ad4da9982b114851 Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Sat, 7 Jan 2023 14:38:04 +0100 Subject: [PATCH 235/423] Fix locks, basepri writeback error --- src/export.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/export.rs b/src/export.rs index d6b2fc0406..bfd0f6dda1 100644 --- a/src/export.rs +++ b/src/export.rs @@ -237,7 +237,7 @@ pub unsafe fn lock( let current = basepri::read(); basepri::write(logical2hw(ceiling, nvic_prio_bits)); let r = f(&mut *ptr); - basepri::write(logical2hw(current, nvic_prio_bits)); + basepri::write(current); r } } From 9a4f97ca5ebf19e6612115db5c763d0d61dd28a1 Mon Sep 17 00:00:00 2001 From: Per Lindgren Date: Sat, 7 Jan 2023 17:59:39 +0100 Subject: [PATCH 236/423] more examples --- Cargo.toml | 11 +-- .../{async-delay.rs => async-delay.no_rs} | 8 +- ...nite-loop.rs => async-infinite-loop.no_rs} | 8 +- examples/async-task-multiple-prios.rs | 73 ++++++++++++------- examples/async-task.rs | 6 +- .../{async-timeout.rs => async-timeout.no_rs} | 0 examples/big-struct-opt.rs | 34 +++++++-- examples/binds.rs | 4 +- ...-reschedule.rs => cancel-reschedule.no_rs} | 0 examples/{capacity.rs => capacity.no_rs} | 0 ...cfg-whole-task.rs => cfg-whole-task.no_rs} | 0 examples/{common.rs => common.no_rs} | 0 examples/complex.rs | 3 +- examples/declared_locals.rs | 8 +- examples/destructure.rs | 11 +-- examples/extern_binds.rs | 4 +- examples/extern_spawn.rs | 19 +++-- examples/generics.rs | 4 +- examples/hardware.rs | 4 +- examples/not-sync.rs | 34 ++++++--- examples/only-shared-access.rs | 9 ++- .../{periodic-at.rs => periodic-at.no_rs} | 0 .../{periodic-at2.rs => periodic-at2.no_rs} | 0 examples/{periodic.rs => periodic.no_rs} | 0 examples/peripherals-taken.rs | 4 +- examples/{pool.rs => pool.no_rs} | 12 +-- examples/preempt.rs | 11 +-- examples/ramfunc.rs | 10 +-- examples/resource-user-struct.rs | 4 +- examples/{schedule.rs => schedule.no_rs} | 0 examples/shared.rs | 4 +- examples/smallest.rs | 4 +- examples/spawn.rs | 7 +- examples/static.rs | 7 +- examples/t-binds.rs | 4 +- examples/t-cfg-resources.rs | 3 +- examples/t-htask-main.rs | 4 +- examples/t-idle-main.rs | 4 +- examples/t-late-not-send.rs | 3 +- examples/{t-schedule.rs => t-schedule.no_rs} | 0 examples/{t-spawn.rs => t-spawn.no_rs} | 11 +-- examples/task.rs | 11 +-- 42 files changed, 192 insertions(+), 151 deletions(-) rename examples/{async-delay.rs => async-delay.no_rs} (87%) rename examples/{async-infinite-loop.rs => async-infinite-loop.no_rs} (84%) rename examples/{async-timeout.rs => async-timeout.no_rs} (100%) rename examples/{cancel-reschedule.rs => cancel-reschedule.no_rs} (100%) rename examples/{capacity.rs => capacity.no_rs} (100%) rename examples/{cfg-whole-task.rs => cfg-whole-task.no_rs} (100%) rename examples/{common.rs => common.no_rs} (100%) rename examples/{periodic-at.rs => periodic-at.no_rs} (100%) rename examples/{periodic-at2.rs => periodic-at2.no_rs} (100%) rename examples/{periodic.rs => periodic.no_rs} (100%) rename examples/{pool.rs => pool.no_rs} (84%) rename examples/{schedule.rs => schedule.no_rs} (100%) rename examples/{t-schedule.rs => t-schedule.no_rs} (100%) rename examples/{t-spawn.rs => t-spawn.no_rs} (85%) diff --git a/Cargo.toml b/Cargo.toml index d995de45e4..cad929196c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -49,10 +49,7 @@ codegen-units = 1 lto = true [workspace] -members = [ - "macros", - "xtask", -] +members = ["macros", "xtask"] # do not optimize proc-macro deps or build scripts [profile.dev.build-override] @@ -76,6 +73,6 @@ lm3s6965 = { git = "https://github.com/japaric/lm3s6965" } [features] test-critical-section = ["cortex-m/critical-section-single-core"] -[[example]] -name = "pool" -required-features = ["test-critical-section"] +# [[example]] +# name = "pool" +# required-features = ["test-critical-section"] diff --git a/examples/async-delay.rs b/examples/async-delay.no_rs similarity index 87% rename from examples/async-delay.rs rename to examples/async-delay.no_rs index 7802bda4d4..fb478c3fbd 100644 --- a/examples/async-delay.rs +++ b/examples/async-delay.no_rs @@ -19,18 +19,14 @@ mod app { type MyMono = Systick<100>; #[init] - fn init(cx: init::Context) -> (Shared, Local, init::Monotonics) { + fn init(cx: init::Context) -> (Shared, Local) { hprintln!("init").unwrap(); foo::spawn().ok(); bar::spawn().ok(); baz::spawn().ok(); - ( - Shared {}, - Local {}, - init::Monotonics(Systick::new(cx.core.SYST, 12_000_000)), - ) + (Shared {}, Local {}) } #[idle] diff --git a/examples/async-infinite-loop.rs b/examples/async-infinite-loop.no_rs similarity index 84% rename from examples/async-infinite-loop.rs rename to examples/async-infinite-loop.no_rs index 7615818d3c..a95f9986f8 100644 --- a/examples/async-infinite-loop.rs +++ b/examples/async-infinite-loop.no_rs @@ -19,16 +19,12 @@ mod app { type MyMono = Systick<100>; #[init] - fn init(cx: init::Context) -> (Shared, Local, init::Monotonics) { + fn init(cx: init::Context) -> (Shared, Local) { hprintln!("init").unwrap(); foo::spawn().ok(); - ( - Shared {}, - Local {}, - init::Monotonics(Systick::new(cx.core.SYST, 12_000_000)), - ) + (Shared {}, Local {}) } #[idle] diff --git a/examples/async-task-multiple-prios.rs b/examples/async-task-multiple-prios.rs index 3e197987a2..2f3a0f7b27 100644 --- a/examples/async-task-multiple-prios.rs +++ b/examples/async-task-multiple-prios.rs @@ -6,14 +6,13 @@ use panic_semihosting as _; // NOTES: // -// - Async tasks cannot have `#[lock_free]` resources, as they can interleve and each async +// - Async tasks cannot have `#[lock_free]` resources, as they can interleave and each async // task can have a mutable reference stored. // - Spawning an async task equates to it being polled once. -#[rtic::app(device = lm3s6965, dispatchers = [SSI0, QEI0, UART0, UART1], peripherals = true)] +#[rtic::app(device = lm3s6965, dispatchers = [SSI0, QEI0])] mod app { use cortex_m_semihosting::{debug, hprintln}; - use systick_monotonic::*; #[shared] struct Shared { @@ -24,53 +23,71 @@ mod app { #[local] struct Local {} - #[monotonic(binds = SysTick, default = true)] - type MyMono = Systick<100>; - #[init] - fn init(cx: init::Context) -> (Shared, Local, init::Monotonics) { + fn init(cx: init::Context) -> (Shared, Local) { hprintln!("init").unwrap(); - normal_task::spawn().ok(); - async_task::spawn().ok(); - normal_task2::spawn().ok(); + async_task1::spawn().ok(); async_task2::spawn().ok(); + async_task3::spawn().ok(); + async_task4::spawn().ok(); - ( - Shared { a: 0, b: 0 }, - Local {}, - init::Monotonics(Systick::new(cx.core.SYST, 12_000_000)), - ) + (Shared { a: 0, b: 0 }, Local {}) } #[idle] fn idle(_: idle::Context) -> ! { - // debug::exit(debug::EXIT_SUCCESS); loop { - // hprintln!("idle"); - cortex_m::asm::wfi(); // put the MCU in sleep mode until interrupt occurs + hprintln!("idle"); + debug::exit(debug::EXIT_SUCCESS); } } #[task(priority = 1, shared = [a, b])] - fn normal_task(_cx: normal_task::Context) { - hprintln!("hello from normal 1").ok(); + async fn async_task1(mut cx: async_task1::Context) { + hprintln!( + "hello from async 1 a {}", + cx.shared.a.lock(|a| { + *a += 1; + *a + }) + ) + .ok(); } #[task(priority = 1, shared = [a, b])] - async fn async_task(_cx: async_task::Context) { - hprintln!("hello from async 1").ok(); - - debug::exit(debug::EXIT_SUCCESS); + async fn async_task2(mut cx: async_task2::Context) { + hprintln!( + "hello from async 2 a {}", + cx.shared.a.lock(|a| { + *a += 1; + *a + }) + ) + .ok(); } #[task(priority = 2, shared = [a, b])] - fn normal_task2(_cx: normal_task2::Context) { - hprintln!("hello from normal 2").ok(); + async fn async_task3(mut cx: async_task3::Context) { + hprintln!( + "hello from async 3 a {}", + cx.shared.a.lock(|a| { + *a += 1; + *a + }) + ) + .ok(); } #[task(priority = 2, shared = [a, b])] - async fn async_task2(_cx: async_task2::Context) { - hprintln!("hello from async 2").ok(); + async fn async_task4(mut cx: async_task4::Context) { + hprintln!( + "hello from async 4 a {}", + cx.shared.a.lock(|a| { + *a += 1; + *a + }) + ) + .ok(); } } diff --git a/examples/async-task.rs b/examples/async-task.rs index 012e95eec2..210a86510d 100644 --- a/examples/async-task.rs +++ b/examples/async-task.rs @@ -34,9 +34,9 @@ mod app { #[idle(shared = [a])] fn idle(_: idle::Context) -> ! { - // debug::exit(debug::EXIT_SUCCESS); loop { - // hprintln!("idle"); + hprintln!("idle"); + debug::exit(debug::EXIT_SUCCESS); cortex_m::asm::wfi(); // put the MCU in sleep mode until interrupt occurs } } @@ -51,8 +51,6 @@ mod app { async fn async_task(cx: async_task::Context) { let async_task::SharedResources { a: _, .. } = cx.shared; hprintln!("hello from async").ok(); - - debug::exit(debug::EXIT_SUCCESS); } #[task(priority = 2, shared = [a])] diff --git a/examples/async-timeout.rs b/examples/async-timeout.no_rs similarity index 100% rename from examples/async-timeout.rs rename to examples/async-timeout.no_rs diff --git a/examples/big-struct-opt.rs b/examples/big-struct-opt.rs index bbc2535a39..4bf93b2af5 100644 --- a/examples/big-struct-opt.rs +++ b/examples/big-struct-opt.rs @@ -5,6 +5,7 @@ #![no_main] #![no_std] +#![feature(type_alias_impl_trait)] use panic_semihosting as _; @@ -20,11 +21,12 @@ impl BigStruct { } } -#[rtic::app(device = lm3s6965)] +#[rtic::app(device = lm3s6965, dispatchers = [SSI0])] mod app { use super::BigStruct; use core::mem::MaybeUninit; - use cortex_m_semihosting::debug; + use cortex_m_semihosting::{debug, hprintln}; + use lm3s6965::Interrupt; #[shared] struct Shared { @@ -35,25 +37,43 @@ mod app { struct Local {} #[init(local = [bs: MaybeUninit = MaybeUninit::uninit()])] - fn init(cx: init::Context) -> (Shared, Local, init::Monotonics) { + fn init(cx: init::Context) -> (Shared, Local) { let big_struct = unsafe { // write directly into the static storage cx.local.bs.as_mut_ptr().write(BigStruct::new()); &mut *cx.local.bs.as_mut_ptr() }; - debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator - + rtic::pend(Interrupt::UART0); + async_task::spawn().unwrap(); ( Shared { // assign the reference so we can use the resource big_struct, }, Local {}, - init::Monotonics(), ) } + #[idle] + fn idle(_: idle::Context) -> ! { + loop { + hprintln!("idle"); + debug::exit(debug::EXIT_SUCCESS); + } + } + #[task(binds = UART0, shared = [big_struct])] - fn task(_: task::Context) {} + fn uart0(mut cx: uart0::Context) { + cx.shared + .big_struct + .lock(|b| hprintln!("uart0 data:{:?}", &b.data[0..5]).unwrap()); + } + + #[task(shared = [big_struct], priority = 2)] + async fn async_task(mut cx: async_task::Context) { + cx.shared + .big_struct + .lock(|b| hprintln!("async_task data:{:?}", &b.data[0..5]).unwrap()); + } } diff --git a/examples/binds.rs b/examples/binds.rs index 56565cbec9..ec25ccca88 100644 --- a/examples/binds.rs +++ b/examples/binds.rs @@ -20,12 +20,12 @@ mod app { struct Local {} #[init] - fn init(_: init::Context) -> (Shared, Local, init::Monotonics) { + fn init(_: init::Context) -> (Shared, Local) { rtic::pend(Interrupt::UART0); hprintln!("init").unwrap(); - (Shared {}, Local {}, init::Monotonics()) + (Shared {}, Local {}) } #[idle] diff --git a/examples/cancel-reschedule.rs b/examples/cancel-reschedule.no_rs similarity index 100% rename from examples/cancel-reschedule.rs rename to examples/cancel-reschedule.no_rs diff --git a/examples/capacity.rs b/examples/capacity.no_rs similarity index 100% rename from examples/capacity.rs rename to examples/capacity.no_rs diff --git a/examples/cfg-whole-task.rs b/examples/cfg-whole-task.no_rs similarity index 100% rename from examples/cfg-whole-task.rs rename to examples/cfg-whole-task.no_rs diff --git a/examples/common.rs b/examples/common.no_rs similarity index 100% rename from examples/common.rs rename to examples/common.no_rs diff --git a/examples/complex.rs b/examples/complex.rs index e5cf6dbea3..df9c862214 100644 --- a/examples/complex.rs +++ b/examples/complex.rs @@ -24,7 +24,7 @@ mod app { struct Local {} #[init] - fn init(_: init::Context) -> (Shared, Local, init::Monotonics) { + fn init(_: init::Context) -> (Shared, Local) { hprintln!("init").unwrap(); ( @@ -34,7 +34,6 @@ mod app { s4: 0, }, Local {}, - init::Monotonics(), ) } diff --git a/examples/declared_locals.rs b/examples/declared_locals.rs index 52d354bc9a..79001aa5e5 100644 --- a/examples/declared_locals.rs +++ b/examples/declared_locals.rs @@ -7,7 +7,7 @@ use panic_semihosting as _; -#[rtic::app(device = lm3s6965, dispatchers = [UART0])] +#[rtic::app(device = lm3s6965)] mod app { use cortex_m_semihosting::debug; @@ -18,13 +18,13 @@ mod app { struct Local {} #[init(local = [a: u32 = 0])] - fn init(cx: init::Context) -> (Shared, Local, init::Monotonics) { + fn init(cx: init::Context) -> (Shared, Local) { // Locals in `#[init]` have 'static lifetime let _a: &'static mut u32 = cx.local.a; debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator - (Shared {}, Local {}, init::Monotonics()) + (Shared {}, Local {}) } #[idle(local = [a: u32 = 0])] @@ -35,7 +35,7 @@ mod app { loop {} } - #[task(local = [a: u32 = 0])] + #[task(binds = UART0, local = [a: u32 = 0])] fn foo(cx: foo::Context) { // Locals in `#[task]`s have a local lifetime let _a: &mut u32 = cx.local.a; diff --git a/examples/destructure.rs b/examples/destructure.rs index 6019c225cc..89336bfd74 100644 --- a/examples/destructure.rs +++ b/examples/destructure.rs @@ -4,6 +4,7 @@ #![deny(warnings)] #![no_main] #![no_std] +#![feature(type_alias_impl_trait)] use panic_semihosting as _; @@ -22,11 +23,11 @@ mod app { struct Local {} #[init] - fn init(_: init::Context) -> (Shared, Local, init::Monotonics) { + fn init(_: init::Context) -> (Shared, Local) { foo::spawn().unwrap(); bar::spawn().unwrap(); - (Shared { a: 0, b: 0, c: 0 }, Local {}, init::Monotonics()) + (Shared { a: 0, b: 1, c: 2 }, Local {}) } #[idle] @@ -37,7 +38,7 @@ mod app { // Direct destructure #[task(shared = [&a, &b, &c])] - fn foo(cx: foo::Context) { + async fn foo(cx: foo::Context) { let a = cx.shared.a; let b = cx.shared.b; let c = cx.shared.c; @@ -47,8 +48,8 @@ mod app { // De-structure-ing syntax #[task(shared = [&a, &b, &c])] - fn bar(cx: bar::Context) { - let bar::SharedResources { a, b, c } = cx.shared; + async fn bar(cx: bar::Context) { + let bar::SharedResources { a, b, c, .. } = cx.shared; hprintln!("bar: a = {}, b = {}, c = {}", a, b, c).unwrap(); } diff --git a/examples/extern_binds.rs b/examples/extern_binds.rs index 4dc6633c5d..c9fc108670 100644 --- a/examples/extern_binds.rs +++ b/examples/extern_binds.rs @@ -26,12 +26,12 @@ mod app { struct Local {} #[init] - fn init(_: init::Context) -> (Shared, Local, init::Monotonics) { + fn init(_: init::Context) -> (Shared, Local) { rtic::pend(Interrupt::UART0); hprintln!("init").unwrap(); - (Shared {}, Local {}, init::Monotonics()) + (Shared {}, Local {}) } #[idle] diff --git a/examples/extern_spawn.rs b/examples/extern_spawn.rs index 7f9b5a5f9b..2d9d7b636a 100644 --- a/examples/extern_spawn.rs +++ b/examples/extern_spawn.rs @@ -4,17 +4,16 @@ #![deny(warnings)] #![no_main] #![no_std] +#![feature(type_alias_impl_trait)] use cortex_m_semihosting::{debug, hprintln}; use panic_semihosting as _; // Free function implementing the spawnable task `foo`. -fn foo(_c: app::foo::Context, x: i32, y: u32) { - hprintln!("foo {}, {}", x, y).unwrap(); - if x == 2 { - debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator - } - app::foo::spawn(2, 3).unwrap(); +// Notice, you need to indicate an anonymous lifetime <'a_> +async fn foo(_c: app::foo::Context<'_>) { + hprintln!("foo").unwrap(); + debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator } #[rtic::app(device = lm3s6965, dispatchers = [SSI0])] @@ -28,14 +27,14 @@ mod app { struct Local {} #[init] - fn init(_: init::Context) -> (Shared, Local, init::Monotonics) { - foo::spawn(1, 2).unwrap(); + fn init(_: init::Context) -> (Shared, Local) { + foo::spawn().unwrap(); - (Shared {}, Local {}, init::Monotonics()) + (Shared {}, Local {}) } extern "Rust" { #[task()] - fn foo(_c: foo::Context, _x: i32, _y: u32); + async fn foo(_c: foo::Context); } } diff --git a/examples/generics.rs b/examples/generics.rs index 72b861ba91..a73b00fb22 100644 --- a/examples/generics.rs +++ b/examples/generics.rs @@ -23,11 +23,11 @@ mod app { struct Local {} #[init] - fn init(_: init::Context) -> (Shared, Local, init::Monotonics) { + fn init(_: init::Context) -> (Shared, Local) { rtic::pend(Interrupt::UART0); rtic::pend(Interrupt::UART1); - (Shared { shared: 0 }, Local {}, init::Monotonics()) + (Shared { shared: 0 }, Local {}) } #[task(binds = UART0, shared = [shared], local = [state: u32 = 0])] diff --git a/examples/hardware.rs b/examples/hardware.rs index 60632247fb..8e4f423b7a 100644 --- a/examples/hardware.rs +++ b/examples/hardware.rs @@ -19,14 +19,14 @@ mod app { struct Local {} #[init] - fn init(_: init::Context) -> (Shared, Local, init::Monotonics) { + fn init(_: init::Context) -> (Shared, Local) { // Pends the UART0 interrupt but its handler won't run until *after* // `init` returns because interrupts are disabled rtic::pend(Interrupt::UART0); // equivalent to NVIC::pend hprintln!("init").unwrap(); - (Shared {}, Local {}, init::Monotonics()) + (Shared {}, Local {}) } #[idle] diff --git a/examples/not-sync.rs b/examples/not-sync.rs index aa79ad5626..eb5c9f8fab 100644 --- a/examples/not-sync.rs +++ b/examples/not-sync.rs @@ -4,12 +4,14 @@ #![deny(warnings)] #![no_main] #![no_std] +#![feature(type_alias_impl_trait)] use core::marker::PhantomData; use panic_semihosting as _; pub struct NotSync { _0: PhantomData<*const ()>, + data: u32, } unsafe impl Send for NotSync {} @@ -18,7 +20,7 @@ unsafe impl Send for NotSync {} mod app { use super::NotSync; use core::marker::PhantomData; - use cortex_m_semihosting::debug; + use cortex_m_semihosting::{debug, hprintln}; #[shared] struct Shared { @@ -29,25 +31,37 @@ mod app { struct Local {} #[init] - fn init(_: init::Context) -> (Shared, Local, init::Monotonics) { - debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator + fn init(_: init::Context) -> (Shared, Local) { + hprintln!("init").unwrap(); + foo::spawn().unwrap(); + bar::spawn().unwrap(); ( Shared { - shared: NotSync { _0: PhantomData }, + shared: NotSync { + _0: PhantomData, + data: 13, + }, }, Local {}, - init::Monotonics(), ) } - #[task(shared = [&shared])] - fn foo(c: foo::Context) { - let _: &NotSync = c.shared.shared; + #[idle] + fn idle(_: idle::Context) -> ! { + debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator + loop {} } #[task(shared = [&shared])] - fn bar(c: bar::Context) { - let _: &NotSync = c.shared.shared; + async fn foo(c: foo::Context) { + let shared: &NotSync = c.shared.shared; + hprintln!("foo a {}", shared.data).unwrap(); + } + + #[task(shared = [&shared])] + async fn bar(c: bar::Context) { + let shared: &NotSync = c.shared.shared; + hprintln!("foo a {}", shared.data).unwrap(); } } diff --git a/examples/only-shared-access.rs b/examples/only-shared-access.rs index 8b0a77ef8c..b506e44130 100644 --- a/examples/only-shared-access.rs +++ b/examples/only-shared-access.rs @@ -4,6 +4,7 @@ #![deny(warnings)] #![no_main] #![no_std] +#![feature(type_alias_impl_trait)] use panic_semihosting as _; @@ -20,15 +21,15 @@ mod app { struct Local {} #[init] - fn init(_: init::Context) -> (Shared, Local, init::Monotonics) { + fn init(_: init::Context) -> (Shared, Local) { foo::spawn().unwrap(); bar::spawn().unwrap(); - (Shared { key: 0xdeadbeef }, Local {}, init::Monotonics()) + (Shared { key: 0xdeadbeef }, Local {}) } #[task(shared = [&key])] - fn foo(cx: foo::Context) { + async fn foo(cx: foo::Context) { let key: &u32 = cx.shared.key; hprintln!("foo(key = {:#x})", key).unwrap(); @@ -36,7 +37,7 @@ mod app { } #[task(priority = 2, shared = [&key])] - fn bar(cx: bar::Context) { + async fn bar(cx: bar::Context) { hprintln!("bar(key = {:#x})", cx.shared.key).unwrap(); } } diff --git a/examples/periodic-at.rs b/examples/periodic-at.no_rs similarity index 100% rename from examples/periodic-at.rs rename to examples/periodic-at.no_rs diff --git a/examples/periodic-at2.rs b/examples/periodic-at2.no_rs similarity index 100% rename from examples/periodic-at2.rs rename to examples/periodic-at2.no_rs diff --git a/examples/periodic.rs b/examples/periodic.no_rs similarity index 100% rename from examples/periodic.rs rename to examples/periodic.no_rs diff --git a/examples/peripherals-taken.rs b/examples/peripherals-taken.rs index d542c0e64d..9b014667cb 100644 --- a/examples/peripherals-taken.rs +++ b/examples/peripherals-taken.rs @@ -16,10 +16,10 @@ mod app { struct Local {} #[init] - fn init(_: init::Context) -> (Shared, Local, init::Monotonics) { + fn init(_: init::Context) -> (Shared, Local) { assert!(cortex_m::Peripherals::take().is_none()); debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator - (Shared {}, Local {}, init::Monotonics()) + (Shared {}, Local {}) } } diff --git a/examples/pool.rs b/examples/pool.no_rs similarity index 84% rename from examples/pool.rs rename to examples/pool.no_rs index 5aadd24cd4..fb8589ad46 100644 --- a/examples/pool.rs +++ b/examples/pool.no_rs @@ -31,17 +31,17 @@ mod app { struct Local {} #[init(local = [memory: [u8; 512] = [0; 512]])] - fn init(cx: init::Context) -> (Shared, Local, init::Monotonics) { + fn init(cx: init::Context) -> (Shared, Local) { // Increase the capacity of the memory pool by ~4 P::grow(cx.local.memory); rtic::pend(Interrupt::I2C0); - (Shared {}, Local {}, init::Monotonics()) + (Shared {}, Local {}) } #[task(binds = I2C0, priority = 2)] - fn i2c0(_: i2c0::Context) { + async fn i2c0(_: i2c0::Context) { // claim a memory block, initialize it and .. let x = P::alloc().unwrap().init([0u8; 128]); @@ -55,7 +55,7 @@ mod app { } #[task] - fn foo(_: foo::Context, _x: Box

) { + async fn foo(_: foo::Context, _x: Box

) { // explicitly return the block to the pool drop(_x); @@ -63,8 +63,8 @@ mod app { } #[task(priority = 2)] - fn bar(_: bar::Context, _x: Box

) { + async fn bar(_: bar::Context, _x: Box

) { // this is done automatically so we can omit the call to `drop` - // drop(x); + // drop(_x); } } diff --git a/examples/preempt.rs b/examples/preempt.rs index d0c8cc7d3f..aad9125301 100644 --- a/examples/preempt.rs +++ b/examples/preempt.rs @@ -2,6 +2,7 @@ #![no_main] #![no_std] +#![feature(type_alias_impl_trait)] use panic_semihosting as _; use rtic::app; @@ -17,14 +18,14 @@ mod app { struct Local {} #[init] - fn init(_: init::Context) -> (Shared, Local, init::Monotonics) { + fn init(_: init::Context) -> (Shared, Local) { foo::spawn().unwrap(); - (Shared {}, Local {}, init::Monotonics()) + (Shared {}, Local {}) } #[task(priority = 1)] - fn foo(_: foo::Context) { + async fn foo(_: foo::Context) { hprintln!("foo - start").unwrap(); baz::spawn().unwrap(); hprintln!("foo - end").unwrap(); @@ -32,12 +33,12 @@ mod app { } #[task(priority = 2)] - fn bar(_: bar::Context) { + async fn bar(_: bar::Context) { hprintln!(" bar").unwrap(); } #[task(priority = 2)] - fn baz(_: baz::Context) { + async fn baz(_: baz::Context) { hprintln!(" baz - start").unwrap(); bar::spawn().unwrap(); hprintln!(" baz - end").unwrap(); diff --git a/examples/ramfunc.rs b/examples/ramfunc.rs index b3b8012c38..dd1f76e5a7 100644 --- a/examples/ramfunc.rs +++ b/examples/ramfunc.rs @@ -3,7 +3,7 @@ #![deny(warnings)] #![no_main] #![no_std] - +#![feature(type_alias_impl_trait)] use panic_semihosting as _; #[rtic::app( @@ -24,15 +24,15 @@ mod app { struct Local {} #[init] - fn init(_: init::Context) -> (Shared, Local, init::Monotonics) { + fn init(_: init::Context) -> (Shared, Local) { foo::spawn().unwrap(); - (Shared {}, Local {}, init::Monotonics()) + (Shared {}, Local {}) } #[inline(never)] #[task] - fn foo(_: foo::Context) { + async fn foo(_: foo::Context) { hprintln!("foo").unwrap(); debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator @@ -42,7 +42,7 @@ mod app { #[inline(never)] #[link_section = ".data.bar"] #[task(priority = 2)] - fn bar(_: bar::Context) { + async fn bar(_: bar::Context) { foo::spawn().unwrap(); } } diff --git a/examples/resource-user-struct.rs b/examples/resource-user-struct.rs index ae1918d05d..a4478ce6b4 100644 --- a/examples/resource-user-struct.rs +++ b/examples/resource-user-struct.rs @@ -29,11 +29,11 @@ mod app { struct Local {} #[init] - fn init(_: init::Context) -> (Shared, Local, init::Monotonics) { + fn init(_: init::Context) -> (Shared, Local) { rtic::pend(Interrupt::UART0); rtic::pend(Interrupt::UART1); - (Shared { shared: 0 }, Local {}, init::Monotonics()) + (Shared { shared: 0 }, Local {}) } // `shared` cannot be accessed from this context diff --git a/examples/schedule.rs b/examples/schedule.no_rs similarity index 100% rename from examples/schedule.rs rename to examples/schedule.no_rs diff --git a/examples/shared.rs b/examples/shared.rs index d87dca5263..fdc1b1c5c7 100644 --- a/examples/shared.rs +++ b/examples/shared.rs @@ -23,11 +23,11 @@ mod app { struct Local {} #[init(local = [q: Queue = Queue::new()])] - fn init(cx: init::Context) -> (Shared, Local, init::Monotonics) { + fn init(cx: init::Context) -> (Shared, Local) { let (p, c) = cx.local.q.split(); // Initialization of shared resources - (Shared { p, c }, Local {}, init::Monotonics()) + (Shared { p, c }, Local {}) } #[idle(shared = [c])] diff --git a/examples/smallest.rs b/examples/smallest.rs index b121fcff88..5071392db6 100644 --- a/examples/smallest.rs +++ b/examples/smallest.rs @@ -17,8 +17,8 @@ mod app { struct Local {} #[init] - fn init(_: init::Context) -> (Shared, Local, init::Monotonics) { + fn init(_: init::Context) -> (Shared, Local) { debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator - (Shared {}, Local {}, init::Monotonics()) + (Shared {}, Local {}) } } diff --git a/examples/spawn.rs b/examples/spawn.rs index 2db1ab8a28..266ace8630 100644 --- a/examples/spawn.rs +++ b/examples/spawn.rs @@ -4,6 +4,7 @@ #![deny(warnings)] #![no_main] #![no_std] +#![feature(type_alias_impl_trait)] use panic_semihosting as _; @@ -18,15 +19,15 @@ mod app { struct Local {} #[init] - fn init(_: init::Context) -> (Shared, Local, init::Monotonics) { + fn init(_: init::Context) -> (Shared, Local) { hprintln!("init").unwrap(); foo::spawn().unwrap(); - (Shared {}, Local {}, init::Monotonics()) + (Shared {}, Local {}) } #[task] - fn foo(_: foo::Context) { + async fn foo(_: foo::Context) { hprintln!("foo").unwrap(); debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator diff --git a/examples/static.rs b/examples/static.rs index c9aa6046b5..9c981db3e9 100644 --- a/examples/static.rs +++ b/examples/static.rs @@ -4,6 +4,7 @@ #![deny(warnings)] #![no_main] #![no_std] +#![feature(type_alias_impl_trait)] use panic_semihosting as _; @@ -22,14 +23,14 @@ mod app { } #[init(local = [q: Queue = Queue::new()])] - fn init(cx: init::Context) -> (Shared, Local, init::Monotonics) { + fn init(cx: init::Context) -> (Shared, Local) { // q has 'static life-time so after the split and return of `init` // it will continue to exist and be allocated let (p, c) = cx.local.q.split(); foo::spawn().unwrap(); - (Shared {}, Local { p, c }, init::Monotonics()) + (Shared {}, Local { p, c }) } #[idle(local = [c])] @@ -50,7 +51,7 @@ mod app { } #[task(local = [p, state: u32 = 0])] - fn foo(c: foo::Context) { + async fn foo(c: foo::Context) { *c.local.state += 1; // Lock-free access to the same underlying queue! diff --git a/examples/t-binds.rs b/examples/t-binds.rs index 12479c0ad4..785348bc96 100644 --- a/examples/t-binds.rs +++ b/examples/t-binds.rs @@ -18,10 +18,10 @@ mod app { struct Local {} #[init] - fn init(_: init::Context) -> (Shared, Local, init::Monotonics) { + fn init(_: init::Context) -> (Shared, Local) { debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator - (Shared {}, Local {}, init::Monotonics()) + (Shared {}, Local {}) } // Cortex-M exception diff --git a/examples/t-cfg-resources.rs b/examples/t-cfg-resources.rs index 99c97ba5e1..0174f33e3b 100644 --- a/examples/t-cfg-resources.rs +++ b/examples/t-cfg-resources.rs @@ -20,7 +20,7 @@ mod app { struct Local {} #[init] - fn init(_: init::Context) -> (Shared, Local, init::Monotonics) { + fn init(_: init::Context) -> (Shared, Local) { debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator ( @@ -29,7 +29,6 @@ mod app { x: 0, }, Local {}, - init::Monotonics(), ) } diff --git a/examples/t-htask-main.rs b/examples/t-htask-main.rs index 37189faf76..0595e9fc18 100644 --- a/examples/t-htask-main.rs +++ b/examples/t-htask-main.rs @@ -16,10 +16,10 @@ mod app { struct Local {} #[init] - fn init(_: init::Context) -> (Shared, Local, init::Monotonics) { + fn init(_: init::Context) -> (Shared, Local) { rtic::pend(lm3s6965::Interrupt::UART0); - (Shared {}, Local {}, init::Monotonics()) + (Shared {}, Local {}) } #[task(binds = UART0)] diff --git a/examples/t-idle-main.rs b/examples/t-idle-main.rs index 1adc9bf044..307ccb20f8 100644 --- a/examples/t-idle-main.rs +++ b/examples/t-idle-main.rs @@ -16,8 +16,8 @@ mod app { struct Local {} #[init] - fn init(_: init::Context) -> (Shared, Local, init::Monotonics) { - (Shared {}, Local {}, init::Monotonics()) + fn init(_: init::Context) -> (Shared, Local) { + (Shared {}, Local {}) } #[idle] diff --git a/examples/t-late-not-send.rs b/examples/t-late-not-send.rs index 06aedaa2ee..0fbf237b77 100644 --- a/examples/t-late-not-send.rs +++ b/examples/t-late-not-send.rs @@ -27,14 +27,13 @@ mod app { struct Local {} #[init] - fn init(_: init::Context) -> (Shared, Local, init::Monotonics) { + fn init(_: init::Context) -> (Shared, Local) { ( Shared { x: NotSend { _0: PhantomData }, y: None, }, Local {}, - init::Monotonics(), ) } diff --git a/examples/t-schedule.rs b/examples/t-schedule.no_rs similarity index 100% rename from examples/t-schedule.rs rename to examples/t-schedule.no_rs diff --git a/examples/t-spawn.rs b/examples/t-spawn.no_rs similarity index 85% rename from examples/t-spawn.rs rename to examples/t-spawn.no_rs index 2bd771d7f6..dad0c83ac2 100644 --- a/examples/t-spawn.rs +++ b/examples/t-spawn.no_rs @@ -4,6 +4,7 @@ #![deny(warnings)] #![no_main] #![no_std] +#![feature(type_alias_impl_trait)] use panic_semihosting as _; @@ -18,14 +19,14 @@ mod app { struct Local {} #[init] - fn init(_: init::Context) -> (Shared, Local, init::Monotonics) { + fn init(_: init::Context) -> (Shared, Local) { let _: Result<(), ()> = foo::spawn(); let _: Result<(), u32> = bar::spawn(0); let _: Result<(), (u32, u32)> = baz::spawn(0, 1); debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator - (Shared {}, Local {}, init::Monotonics()) + (Shared {}, Local {}) } #[idle] @@ -54,15 +55,15 @@ mod app { } #[task] - fn foo(_: foo::Context) { + async fn foo(_: foo::Context) { let _: Result<(), ()> = foo::spawn(); let _: Result<(), u32> = bar::spawn(0); let _: Result<(), (u32, u32)> = baz::spawn(0, 1); } #[task] - fn bar(_: bar::Context, _x: u32) {} + async fn bar(_: bar::Context, _x: u32) {} #[task] - fn baz(_: baz::Context, _x: u32, _y: u32) {} + async fn baz(_: baz::Context, _x: u32, _y: u32) {} } diff --git a/examples/task.rs b/examples/task.rs index 2c53aa2359..fe1408b4ac 100644 --- a/examples/task.rs +++ b/examples/task.rs @@ -4,6 +4,7 @@ #![deny(warnings)] #![no_main] #![no_std] +#![feature(type_alias_impl_trait)] use panic_semihosting as _; @@ -18,14 +19,14 @@ mod app { struct Local {} #[init] - fn init(_: init::Context) -> (Shared, Local, init::Monotonics) { + fn init(_: init::Context) -> (Shared, Local) { foo::spawn().unwrap(); - (Shared {}, Local {}, init::Monotonics()) + (Shared {}, Local {}) } #[task] - fn foo(_: foo::Context) { + async fn foo(_: foo::Context) { hprintln!("foo - start").unwrap(); // spawns `bar` onto the task scheduler @@ -43,14 +44,14 @@ mod app { } #[task] - fn bar(_: bar::Context) { + async fn bar(_: bar::Context) { hprintln!("bar").unwrap(); debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator } #[task(priority = 2)] - fn baz(_: baz::Context) { + async fn baz(_: baz::Context) { hprintln!("baz").unwrap(); } } From 584ac7e1b335411e3d923aeef92466efdc92ae50 Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Sun, 8 Jan 2023 16:25:46 +0100 Subject: [PATCH 237/423] Update UI tests, 1 failing that needs fixing --- macros/ui/async-local-resouces.rs | 28 ------------------- macros/ui/async-local-resouces.stderr | 5 ---- macros/ui/async-zero-prio-tasks.stderr | 11 -------- macros/ui/extern-interrupt-used.rs | 2 +- macros/ui/extern-interrupt-used.stderr | 2 +- macros/ui/idle-double-local.stderr | 2 +- macros/ui/idle-double-shared.stderr | 2 +- macros/ui/init-divergent.stderr | 4 +-- macros/ui/init-double-local.stderr | 2 +- macros/ui/init-double-shared.stderr | 2 +- macros/ui/init-input.rs | 2 +- macros/ui/init-input.stderr | 6 ++-- macros/ui/init-no-context.rs | 2 +- macros/ui/init-no-context.stderr | 6 ++-- macros/ui/init-output.stderr | 4 +-- macros/ui/init-pub.rs | 2 +- macros/ui/init-pub.stderr | 6 ++-- macros/ui/init-unsafe.rs | 2 +- macros/ui/init-unsafe.stderr | 6 ++-- macros/ui/interrupt-double.stderr | 2 +- macros/ui/local-collision-2.rs | 7 ++--- macros/ui/local-collision-2.stderr | 10 +++---- macros/ui/local-collision.rs | 6 ++-- macros/ui/local-collision.stderr | 4 +-- macros/ui/local-malformed-1.rs | 4 +-- macros/ui/local-malformed-2.rs | 4 +-- macros/ui/local-malformed-2.stderr | 2 +- macros/ui/local-malformed-3.rs | 4 +-- macros/ui/local-malformed-4.rs | 4 +-- macros/ui/local-not-declared.rs | 4 +-- macros/ui/local-not-declared.stderr | 2 +- macros/ui/local-pub.rs | 6 ++++ macros/ui/local-pub.stderr | 8 +++--- macros/ui/local-shared-attribute.rs | 13 +++++++-- macros/ui/local-shared-attribute.stderr | 9 +++--- macros/ui/local-shared.rs | 6 ++-- macros/ui/local-shared.stderr | 4 +-- macros/ui/monotonic-binds-collision-task.rs | 10 ------- .../ui/monotonic-binds-collision-task.stderr | 5 ---- macros/ui/monotonic-binds-collision.rs | 10 ------- macros/ui/monotonic-binds-collision.stderr | 5 ---- macros/ui/monotonic-double-binds.rs | 7 ----- macros/ui/monotonic-double-binds.stderr | 5 ---- macros/ui/monotonic-double-default.rs | 7 ----- macros/ui/monotonic-double-default.stderr | 5 ---- macros/ui/monotonic-double-prio.rs | 7 ----- macros/ui/monotonic-double-prio.stderr | 5 ---- macros/ui/monotonic-double.rs | 10 ------- macros/ui/monotonic-double.stderr | 5 ---- macros/ui/monotonic-name-collision.rs | 10 ------- macros/ui/monotonic-name-collision.stderr | 5 ---- macros/ui/monotonic-no-binds.rs | 7 ----- macros/ui/monotonic-no-binds.stderr | 5 ---- macros/ui/monotonic-no-paran.rs | 8 ------ macros/ui/monotonic-no-paran.stderr | 5 ---- macros/ui/monotonic-timer-collision.rs | 10 ------- macros/ui/monotonic-timer-collision.stderr | 5 ---- macros/ui/monotonic-with-attrs.rs | 8 ------ macros/ui/monotonic-with-attrs.stderr | 5 ---- macros/ui/pub-local.stderr | 5 ---- macros/ui/pub-shared.stderr | 5 ---- macros/ui/shared-lock-free.rs | 6 ++-- macros/ui/shared-lock-free.stderr | 17 ----------- macros/ui/shared-not-declared.rs | 4 +-- macros/ui/shared-not-declared.stderr | 2 +- macros/ui/shared-pub.stderr | 2 +- macros/ui/task-divergent.rs | 2 +- macros/ui/task-divergent.stderr | 8 +++--- macros/ui/task-double-capacity.rs | 7 ----- macros/ui/task-double-capacity.stderr | 5 ---- macros/ui/task-double-local.rs | 2 +- macros/ui/task-double-local.stderr | 2 +- macros/ui/task-double-priority.rs | 2 +- macros/ui/task-double-priority.stderr | 2 +- macros/ui/task-double-shared.rs | 2 +- macros/ui/task-double-shared.stderr | 2 +- macros/ui/task-idle.rs | 2 +- macros/ui/task-idle.stderr | 6 ++-- macros/ui/task-init.rs | 4 +-- macros/ui/task-init.stderr | 6 ++-- macros/ui/task-interrupt.rs | 2 +- macros/ui/task-interrupt.stderr | 6 ++-- macros/ui/task-no-context.rs | 2 +- macros/ui/task-no-context.stderr | 8 +++--- macros/ui/task-priority-too-high.rs | 2 +- macros/ui/task-pub.rs | 2 +- macros/ui/task-pub.stderr | 8 +++--- macros/ui/task-unsafe.rs | 2 +- macros/ui/task-unsafe.stderr | 8 +++--- ...c-zero-prio-tasks.rs => task-zero-prio.rs} | 2 +- macros/ui/task-zero-prio.stderr | 5 ++++ 91 files changed, 134 insertions(+), 350 deletions(-) delete mode 100644 macros/ui/async-local-resouces.rs delete mode 100644 macros/ui/async-local-resouces.stderr delete mode 100644 macros/ui/async-zero-prio-tasks.stderr delete mode 100644 macros/ui/monotonic-binds-collision-task.rs delete mode 100644 macros/ui/monotonic-binds-collision-task.stderr delete mode 100644 macros/ui/monotonic-binds-collision.rs delete mode 100644 macros/ui/monotonic-binds-collision.stderr delete mode 100644 macros/ui/monotonic-double-binds.rs delete mode 100644 macros/ui/monotonic-double-binds.stderr delete mode 100644 macros/ui/monotonic-double-default.rs delete mode 100644 macros/ui/monotonic-double-default.stderr delete mode 100644 macros/ui/monotonic-double-prio.rs delete mode 100644 macros/ui/monotonic-double-prio.stderr delete mode 100644 macros/ui/monotonic-double.rs delete mode 100644 macros/ui/monotonic-double.stderr delete mode 100644 macros/ui/monotonic-name-collision.rs delete mode 100644 macros/ui/monotonic-name-collision.stderr delete mode 100644 macros/ui/monotonic-no-binds.rs delete mode 100644 macros/ui/monotonic-no-binds.stderr delete mode 100644 macros/ui/monotonic-no-paran.rs delete mode 100644 macros/ui/monotonic-no-paran.stderr delete mode 100644 macros/ui/monotonic-timer-collision.rs delete mode 100644 macros/ui/monotonic-timer-collision.stderr delete mode 100644 macros/ui/monotonic-with-attrs.rs delete mode 100644 macros/ui/monotonic-with-attrs.stderr delete mode 100644 macros/ui/pub-local.stderr delete mode 100644 macros/ui/pub-shared.stderr delete mode 100644 macros/ui/shared-lock-free.stderr delete mode 100644 macros/ui/task-double-capacity.rs delete mode 100644 macros/ui/task-double-capacity.stderr rename macros/ui/{async-zero-prio-tasks.rs => task-zero-prio.rs} (78%) create mode 100644 macros/ui/task-zero-prio.stderr diff --git a/macros/ui/async-local-resouces.rs b/macros/ui/async-local-resouces.rs deleted file mode 100644 index 1ba58652da..0000000000 --- a/macros/ui/async-local-resouces.rs +++ /dev/null @@ -1,28 +0,0 @@ -#![no_main] - -#[rtic_macros::mock_app(device = mock)] -mod app { - #[shared] - struct Shared { - #[lock_free] - e: u32, - } - - #[local] - struct Local {} - - #[init] - fn init(_: init::Context) -> (Shared, Local, init::Monotonics) {} - - // e ok - #[task(priority = 1, shared = [e])] - fn uart0(cx: uart0::Context) {} - - // e ok - #[task(priority = 1, shared = [e])] - fn uart1(cx: uart1::Context) {} - - // e not ok - #[task(priority = 1, shared = [e])] - async fn async_task(cx: async_task::Context) {} -} diff --git a/macros/ui/async-local-resouces.stderr b/macros/ui/async-local-resouces.stderr deleted file mode 100644 index 7ce7517d99..0000000000 --- a/macros/ui/async-local-resouces.stderr +++ /dev/null @@ -1,5 +0,0 @@ -error: Lock free shared resource "e" is used by an async tasks, which is forbidden - --> ui/async-local-resouces.rs:26:36 - | -26 | #[task(priority = 1, shared = [e])] - | ^ diff --git a/macros/ui/async-zero-prio-tasks.stderr b/macros/ui/async-zero-prio-tasks.stderr deleted file mode 100644 index d617feb275..0000000000 --- a/macros/ui/async-zero-prio-tasks.stderr +++ /dev/null @@ -1,11 +0,0 @@ -error: Software task "foo" has priority 0, but `#[idle]` is defined. 0-priority software tasks are only allowed if there is no `#[idle]`. - --> ui/async-zero-prio-tasks.rs:15:8 - | -15 | fn foo(_: foo::Context) {} - | ^^^ - -error: Software task "foo" has priority 0, but is not `async`. 0-priority software tasks must be `async`. - --> ui/async-zero-prio-tasks.rs:15:8 - | -15 | fn foo(_: foo::Context) {} - | ^^^ diff --git a/macros/ui/extern-interrupt-used.rs b/macros/ui/extern-interrupt-used.rs index a6e0b3b29a..6346a7d76e 100644 --- a/macros/ui/extern-interrupt-used.rs +++ b/macros/ui/extern-interrupt-used.rs @@ -9,7 +9,7 @@ mod app { struct Local {} #[init] - fn init(_: init::Context) -> (Shared, Local, init::Monotonics) {} + fn init(_: init::Context) -> (Shared, Local) {} #[task(binds = EXTI0)] fn foo(_: foo::Context) {} diff --git a/macros/ui/extern-interrupt-used.stderr b/macros/ui/extern-interrupt-used.stderr index f9510d7037..970d39be77 100644 --- a/macros/ui/extern-interrupt-used.stderr +++ b/macros/ui/extern-interrupt-used.stderr @@ -1,5 +1,5 @@ error: dispatcher interrupts can't be used as hardware tasks - --> $DIR/extern-interrupt-used.rs:14:20 + --> ui/extern-interrupt-used.rs:14:20 | 14 | #[task(binds = EXTI0)] | ^^^^^ diff --git a/macros/ui/idle-double-local.stderr b/macros/ui/idle-double-local.stderr index d3ba4ec9f7..b558136a0d 100644 --- a/macros/ui/idle-double-local.stderr +++ b/macros/ui/idle-double-local.stderr @@ -1,5 +1,5 @@ error: argument appears more than once - --> $DIR/idle-double-local.rs:5:25 + --> ui/idle-double-local.rs:5:25 | 5 | #[idle(local = [A], local = [B])] | ^^^^^ diff --git a/macros/ui/idle-double-shared.stderr b/macros/ui/idle-double-shared.stderr index 84864a1560..6f62ad2802 100644 --- a/macros/ui/idle-double-shared.stderr +++ b/macros/ui/idle-double-shared.stderr @@ -1,5 +1,5 @@ error: argument appears more than once - --> $DIR/idle-double-shared.rs:5:26 + --> ui/idle-double-shared.rs:5:26 | 5 | #[idle(shared = [A], shared = [B])] | ^^^^^^ diff --git a/macros/ui/init-divergent.stderr b/macros/ui/init-divergent.stderr index 2d5cc39cf0..9f6acf6742 100644 --- a/macros/ui/init-divergent.stderr +++ b/macros/ui/init-divergent.stderr @@ -1,5 +1,5 @@ -error: the `#[init]` function must have signature `fn(init::Context) -> (Shared resources struct, Local resources struct, init::Monotonics)` - --> $DIR/init-divergent.rs:12:8 +error: the `#[init]` function must have signature `fn(init::Context) -> (Shared resources struct, Local resources struct)` + --> ui/init-divergent.rs:12:8 | 12 | fn init(_: init::Context) -> ! {} | ^^^^ diff --git a/macros/ui/init-double-local.stderr b/macros/ui/init-double-local.stderr index 5ffd2c153a..07c3b50eda 100644 --- a/macros/ui/init-double-local.stderr +++ b/macros/ui/init-double-local.stderr @@ -1,5 +1,5 @@ error: argument appears more than once - --> $DIR/init-double-local.rs:5:25 + --> ui/init-double-local.rs:5:25 | 5 | #[init(local = [A], local = [B])] | ^^^^^ diff --git a/macros/ui/init-double-shared.stderr b/macros/ui/init-double-shared.stderr index b6b1f6da1e..af2a97bc60 100644 --- a/macros/ui/init-double-shared.stderr +++ b/macros/ui/init-double-shared.stderr @@ -1,5 +1,5 @@ error: unexpected argument - --> $DIR/init-double-shared.rs:5:12 + --> ui/init-double-shared.rs:5:12 | 5 | #[init(shared = [A], shared = [B])] | ^^^^^^ diff --git a/macros/ui/init-input.rs b/macros/ui/init-input.rs index ac2a1bdae7..d41a503f51 100644 --- a/macros/ui/init-input.rs +++ b/macros/ui/init-input.rs @@ -9,5 +9,5 @@ mod app { struct Local {} #[init] - fn init(_: init::Context, _undef: u32) -> (Shared, Local, init::Monotonics) {} + fn init(_: init::Context, _undef: u32) -> (Shared, Local) {} } diff --git a/macros/ui/init-input.stderr b/macros/ui/init-input.stderr index 983c46926a..e2360435b4 100644 --- a/macros/ui/init-input.stderr +++ b/macros/ui/init-input.stderr @@ -1,5 +1,5 @@ -error: the `#[init]` function must have signature `fn(init::Context) -> (Shared resources struct, Local resources struct, init::Monotonics)` - --> $DIR/init-input.rs:12:8 +error: the `#[init]` function must have signature `fn(init::Context) -> (Shared resources struct, Local resources struct)` + --> ui/init-input.rs:12:8 | -12 | fn init(_: init::Context, _undef: u32) -> (Shared, Local, init::Monotonics) {} +12 | fn init(_: init::Context, _undef: u32) -> (Shared, Local) {} | ^^^^ diff --git a/macros/ui/init-no-context.rs b/macros/ui/init-no-context.rs index a74093ae3c..cdce4c5566 100644 --- a/macros/ui/init-no-context.rs +++ b/macros/ui/init-no-context.rs @@ -9,5 +9,5 @@ mod app { struct Local {} #[init] - fn init() -> (Shared, Local, init::Monotonics) {} + fn init() -> (Shared, Local) {} } diff --git a/macros/ui/init-no-context.stderr b/macros/ui/init-no-context.stderr index 742e2ab18e..28e1fd4f2d 100644 --- a/macros/ui/init-no-context.stderr +++ b/macros/ui/init-no-context.stderr @@ -1,5 +1,5 @@ -error: the `#[init]` function must have signature `fn(init::Context) -> (Shared resources struct, Local resources struct, init::Monotonics)` - --> $DIR/init-no-context.rs:12:8 +error: the `#[init]` function must have signature `fn(init::Context) -> (Shared resources struct, Local resources struct)` + --> ui/init-no-context.rs:12:8 | -12 | fn init() -> (Shared, Local, init::Monotonics) {} +12 | fn init() -> (Shared, Local) {} | ^^^^ diff --git a/macros/ui/init-output.stderr b/macros/ui/init-output.stderr index 03e982c607..8bc3c83037 100644 --- a/macros/ui/init-output.stderr +++ b/macros/ui/init-output.stderr @@ -1,5 +1,5 @@ -error: the `#[init]` function must have signature `fn(init::Context) -> (Shared resources struct, Local resources struct, init::Monotonics)` - --> $DIR/init-output.rs:6:8 +error: the `#[init]` function must have signature `fn(init::Context) -> (Shared resources struct, Local resources struct)` + --> ui/init-output.rs:6:8 | 6 | fn init(_: init::Context) -> u32 { | ^^^^ diff --git a/macros/ui/init-pub.rs b/macros/ui/init-pub.rs index 43375e4e54..dd59aa1983 100644 --- a/macros/ui/init-pub.rs +++ b/macros/ui/init-pub.rs @@ -9,5 +9,5 @@ mod app { struct Local {} #[init] - pub fn init(_: init::Context) -> (Shared, Local, init::Monotonics) {} + pub fn init(_: init::Context) -> (Shared, Local) {} } diff --git a/macros/ui/init-pub.stderr b/macros/ui/init-pub.stderr index eb68e1ef21..b1610ed74d 100644 --- a/macros/ui/init-pub.stderr +++ b/macros/ui/init-pub.stderr @@ -1,5 +1,5 @@ -error: the `#[init]` function must have signature `fn(init::Context) -> (Shared resources struct, Local resources struct, init::Monotonics)` - --> $DIR/init-pub.rs:12:12 +error: the `#[init]` function must have signature `fn(init::Context) -> (Shared resources struct, Local resources struct)` + --> ui/init-pub.rs:12:12 | -12 | pub fn init(_: init::Context) -> (Shared, Local, init::Monotonics) {} +12 | pub fn init(_: init::Context) -> (Shared, Local) {} | ^^^^ diff --git a/macros/ui/init-unsafe.rs b/macros/ui/init-unsafe.rs index b5d391dd9d..4f89bafa71 100644 --- a/macros/ui/init-unsafe.rs +++ b/macros/ui/init-unsafe.rs @@ -3,5 +3,5 @@ #[rtic_macros::mock_app(device = mock)] mod app { #[init] - unsafe fn init(_: init::Context) -> (Shared, Local, init::Monotonics) {} + unsafe fn init(_: init::Context) -> (Shared, Local) {} } diff --git a/macros/ui/init-unsafe.stderr b/macros/ui/init-unsafe.stderr index 2a48533914..fd0b8f36ff 100644 --- a/macros/ui/init-unsafe.stderr +++ b/macros/ui/init-unsafe.stderr @@ -1,5 +1,5 @@ -error: the `#[init]` function must have signature `fn(init::Context) -> (Shared resources struct, Local resources struct, init::Monotonics)` - --> $DIR/init-unsafe.rs:6:15 +error: the `#[init]` function must have signature `fn(init::Context) -> (Shared resources struct, Local resources struct)` + --> ui/init-unsafe.rs:6:15 | -6 | unsafe fn init(_: init::Context) -> (Shared, Local, init::Monotonics) {} +6 | unsafe fn init(_: init::Context) -> (Shared, Local) {} | ^^^^ diff --git a/macros/ui/interrupt-double.stderr b/macros/ui/interrupt-double.stderr index 62b979bbfa..8db34e2601 100644 --- a/macros/ui/interrupt-double.stderr +++ b/macros/ui/interrupt-double.stderr @@ -1,5 +1,5 @@ error: this interrupt is already bound - --> $DIR/interrupt-double.rs:8:20 + --> ui/interrupt-double.rs:8:20 | 8 | #[task(binds = UART0)] | ^^^^^ diff --git a/macros/ui/local-collision-2.rs b/macros/ui/local-collision-2.rs index 7bd092c845..08bc8e5538 100644 --- a/macros/ui/local-collision-2.rs +++ b/macros/ui/local-collision-2.rs @@ -10,12 +10,9 @@ mod app { a: u32, } - fn bar(_: bar::Context) {} - #[task(local = [a: u8 = 3])] - fn bar(_: bar::Context) {} + async fn bar(_: bar::Context) {} #[init(local = [a: u16 = 2])] - fn init(_: init::Context) -> (Shared, Local, init::Monotonics) {} + fn init(_: init::Context) -> (Shared, Local) {} } - diff --git a/macros/ui/local-collision-2.stderr b/macros/ui/local-collision-2.stderr index 1e4c5fa629..47dbbe336f 100644 --- a/macros/ui/local-collision-2.stderr +++ b/macros/ui/local-collision-2.stderr @@ -1,17 +1,17 @@ error: Local resource "a" is used by multiple tasks or collides with multiple definitions - --> $DIR/local-collision-2.rs:10:9 + --> ui/local-collision-2.rs:10:9 | 10 | a: u32, | ^ error: Local resource "a" is used by multiple tasks or collides with multiple definitions - --> $DIR/local-collision-2.rs:18:21 + --> ui/local-collision-2.rs:16:21 | -18 | #[init(local = [a: u16 = 2])] +16 | #[init(local = [a: u16 = 2])] | ^ error: Local resource "a" is used by multiple tasks or collides with multiple definitions - --> $DIR/local-collision-2.rs:15:21 + --> ui/local-collision-2.rs:13:21 | -15 | #[task(local = [a: u8 = 3])] +13 | #[task(local = [a: u8 = 3])] | ^ diff --git a/macros/ui/local-collision.rs b/macros/ui/local-collision.rs index 7dbe97640f..0e4eef720c 100644 --- a/macros/ui/local-collision.rs +++ b/macros/ui/local-collision.rs @@ -11,11 +11,11 @@ mod app { } #[task(local = [a])] - fn foo(_: foo::Context) {} + async fn foo(_: foo::Context) {} #[task(local = [a: u8 = 3])] - fn bar(_: bar::Context) {} + async fn bar(_: bar::Context) {} #[init] - fn init(_: init::Context) -> (Shared, Local, init::Monotonics) {} + fn init(_: init::Context) -> (Shared, Local) {} } diff --git a/macros/ui/local-collision.stderr b/macros/ui/local-collision.stderr index 1ba1da922d..47fbb6ec23 100644 --- a/macros/ui/local-collision.stderr +++ b/macros/ui/local-collision.stderr @@ -1,11 +1,11 @@ error: Local resource "a" is used by multiple tasks or collides with multiple definitions - --> $DIR/local-collision.rs:10:9 + --> ui/local-collision.rs:10:9 | 10 | a: u32, | ^ error: Local resource "a" is used by multiple tasks or collides with multiple definitions - --> $DIR/local-collision.rs:16:21 + --> ui/local-collision.rs:16:21 | 16 | #[task(local = [a: u8 = 3])] | ^ diff --git a/macros/ui/local-malformed-1.rs b/macros/ui/local-malformed-1.rs index 7efcd9c14a..219eef543c 100644 --- a/macros/ui/local-malformed-1.rs +++ b/macros/ui/local-malformed-1.rs @@ -9,8 +9,8 @@ mod app { struct Local {} #[task(local = [a:])] - fn foo(_: foo::Context) {} + async fn foo(_: foo::Context) {} #[init] - fn init(_: init::Context) -> (Shared, Local, init::Monotonics) {} + fn init(_: init::Context) -> (Shared, Local) {} } diff --git a/macros/ui/local-malformed-2.rs b/macros/ui/local-malformed-2.rs index ce5f891a39..d6914536fd 100644 --- a/macros/ui/local-malformed-2.rs +++ b/macros/ui/local-malformed-2.rs @@ -9,8 +9,8 @@ mod app { struct Local {} #[task(local = [a: u32])] - fn foo(_: foo::Context) {} + async fn foo(_: foo::Context) {} #[init] - fn init(_: init::Context) -> (Shared, Local, init::Monotonics) {} + fn init(_: init::Context) -> (Shared, Local) {} } diff --git a/macros/ui/local-malformed-2.stderr b/macros/ui/local-malformed-2.stderr index ceb04058fe..0b448f01ba 100644 --- a/macros/ui/local-malformed-2.stderr +++ b/macros/ui/local-malformed-2.stderr @@ -2,4 +2,4 @@ error: malformed, expected 'IDENT: TYPE = EXPR' --> ui/local-malformed-2.rs:11:21 | 11 | #[task(local = [a: u32])] - | ^ + | ^^^^^^ diff --git a/macros/ui/local-malformed-3.rs b/macros/ui/local-malformed-3.rs index 935dc2c65d..7eddfa41e0 100644 --- a/macros/ui/local-malformed-3.rs +++ b/macros/ui/local-malformed-3.rs @@ -9,8 +9,8 @@ mod app { struct Local {} #[task(local = [a: u32 =])] - fn foo(_: foo::Context) {} + async fn foo(_: foo::Context) {} #[init] - fn init(_: init::Context) -> (Shared, Local, init::Monotonics) {} + fn init(_: init::Context) -> (Shared, Local) {} } diff --git a/macros/ui/local-malformed-4.rs b/macros/ui/local-malformed-4.rs index 49661b5e8c..b91394763c 100644 --- a/macros/ui/local-malformed-4.rs +++ b/macros/ui/local-malformed-4.rs @@ -9,8 +9,8 @@ mod app { struct Local {} #[task(local = [a = u32])] - fn foo(_: foo::Context) {} + async fn foo(_: foo::Context) {} #[init] - fn init(_: init::Context) -> (Shared, Local, init::Monotonics) {} + fn init(_: init::Context) -> (Shared, Local) {} } diff --git a/macros/ui/local-not-declared.rs b/macros/ui/local-not-declared.rs index 5a38b3d746..7c087e47bb 100644 --- a/macros/ui/local-not-declared.rs +++ b/macros/ui/local-not-declared.rs @@ -9,8 +9,8 @@ mod app { struct Local {} #[task(local = [A])] - fn foo(_: foo::Context) {} + async fn foo(_: foo::Context) {} #[init] - fn init(_: init::Context) -> (Shared, Local, init::Monotonics) {} + fn init(_: init::Context) -> (Shared, Local) {} } diff --git a/macros/ui/local-not-declared.stderr b/macros/ui/local-not-declared.stderr index 540b4bb772..10d4b04bb8 100644 --- a/macros/ui/local-not-declared.stderr +++ b/macros/ui/local-not-declared.stderr @@ -1,5 +1,5 @@ error: this local resource has NOT been declared - --> $DIR/local-not-declared.rs:11:21 + --> ui/local-not-declared.rs:11:21 | 11 | #[task(local = [A])] | ^ diff --git a/macros/ui/local-pub.rs b/macros/ui/local-pub.rs index 8c51754688..42da4f4746 100644 --- a/macros/ui/local-pub.rs +++ b/macros/ui/local-pub.rs @@ -2,8 +2,14 @@ #[rtic_macros::mock_app(device = mock)] mod app { + #[shared] + struct Shared {} + #[local] struct Local { pub x: u32, } + + #[init] + fn init(_: init::Context) -> (Shared, Local) {} } diff --git a/macros/ui/local-pub.stderr b/macros/ui/local-pub.stderr index 041bc598b6..e4814ca080 100644 --- a/macros/ui/local-pub.stderr +++ b/macros/ui/local-pub.stderr @@ -1,5 +1,5 @@ error: this field must have inherited / private visibility - --> $DIR/local-pub.rs:7:13 - | -7 | pub x: u32, - | ^ + --> ui/local-pub.rs:10:13 + | +10 | pub x: u32, + | ^ diff --git a/macros/ui/local-shared-attribute.rs b/macros/ui/local-shared-attribute.rs index 1ccce4ad1b..c594b5f692 100644 --- a/macros/ui/local-shared-attribute.rs +++ b/macros/ui/local-shared-attribute.rs @@ -2,13 +2,20 @@ #[rtic_macros::mock_app(device = mock)] mod app { + #[shared] + struct Shared {} + + #[local] + struct Local {} + + #[init] + fn init(_: init::Context) -> (Shared, Local) {} + #[task(local = [ #[test] a: u32 = 0, // Ok #[test] b, // Error ])] - fn foo(_: foo::Context) { - - } + fn foo(_: foo::Context) {} } diff --git a/macros/ui/local-shared-attribute.stderr b/macros/ui/local-shared-attribute.stderr index 5c15fb5bd6..a8130e8890 100644 --- a/macros/ui/local-shared-attribute.stderr +++ b/macros/ui/local-shared-attribute.stderr @@ -1,5 +1,6 @@ error: attributes are not supported here - --> $DIR/local-shared-attribute.rs:8:9 - | -8 | #[test] - | ^ + --> ui/local-shared-attribute.rs:17:9 + | +17 | / #[test] +18 | | b, // Error + | |_________^ diff --git a/macros/ui/local-shared.rs b/macros/ui/local-shared.rs index f6fb491ae6..4e8f9f4802 100644 --- a/macros/ui/local-shared.rs +++ b/macros/ui/local-shared.rs @@ -12,7 +12,7 @@ mod app { } #[init] - fn init(_: init::Context) -> (Shared, Local, init::Monotonics) {} + fn init(_: init::Context) -> (Shared, Local) {} // l2 ok #[idle(local = [l2])] @@ -20,9 +20,9 @@ mod app { // l1 rejected (not local) #[task(priority = 1, local = [l1])] - fn uart0(cx: uart0::Context) {} + async fn uart0(cx: uart0::Context) {} // l1 rejected (not lock_free) #[task(priority = 2, local = [l1])] - fn uart1(cx: uart1::Context) {} + async fn uart1(cx: uart1::Context) {} } diff --git a/macros/ui/local-shared.stderr b/macros/ui/local-shared.stderr index 0d22db30e3..fceb7630f3 100644 --- a/macros/ui/local-shared.stderr +++ b/macros/ui/local-shared.stderr @@ -1,11 +1,11 @@ error: Local resource "l1" is used by multiple tasks or collides with multiple definitions - --> $DIR/local-shared.rs:22:35 + --> ui/local-shared.rs:22:35 | 22 | #[task(priority = 1, local = [l1])] | ^^ error: Local resource "l1" is used by multiple tasks or collides with multiple definitions - --> $DIR/local-shared.rs:26:35 + --> ui/local-shared.rs:26:35 | 26 | #[task(priority = 2, local = [l1])] | ^^ diff --git a/macros/ui/monotonic-binds-collision-task.rs b/macros/ui/monotonic-binds-collision-task.rs deleted file mode 100644 index 1c58f9ddaf..0000000000 --- a/macros/ui/monotonic-binds-collision-task.rs +++ /dev/null @@ -1,10 +0,0 @@ -#![no_main] - -#[rtic_macros::mock_app(device = mock)] -mod app { - #[monotonic(binds = Tim1)] - type Fast1 = hal::Tim1Monotonic; - - #[task(binds = Tim1)] - fn foo(_: foo::Context) {} -} diff --git a/macros/ui/monotonic-binds-collision-task.stderr b/macros/ui/monotonic-binds-collision-task.stderr deleted file mode 100644 index 8f84986ae1..0000000000 --- a/macros/ui/monotonic-binds-collision-task.stderr +++ /dev/null @@ -1,5 +0,0 @@ -error: this interrupt is already bound - --> $DIR/monotonic-binds-collision-task.rs:8:20 - | -8 | #[task(binds = Tim1)] - | ^^^^ diff --git a/macros/ui/monotonic-binds-collision.rs b/macros/ui/monotonic-binds-collision.rs deleted file mode 100644 index 4e54814588..0000000000 --- a/macros/ui/monotonic-binds-collision.rs +++ /dev/null @@ -1,10 +0,0 @@ -#![no_main] - -#[rtic_macros::mock_app(device = mock)] -mod app { - #[monotonic(binds = Tim1)] - type Fast1 = hal::Tim1Monotonic; - - #[monotonic(binds = Tim1)] - type Fast2 = hal::Tim2Monotonic; -} diff --git a/macros/ui/monotonic-binds-collision.stderr b/macros/ui/monotonic-binds-collision.stderr deleted file mode 100644 index 62b764b254..0000000000 --- a/macros/ui/monotonic-binds-collision.stderr +++ /dev/null @@ -1,5 +0,0 @@ -error: this interrupt is already bound - --> $DIR/monotonic-binds-collision.rs:8:25 - | -8 | #[monotonic(binds = Tim1)] - | ^^^^ diff --git a/macros/ui/monotonic-double-binds.rs b/macros/ui/monotonic-double-binds.rs deleted file mode 100644 index 1705dc4950..0000000000 --- a/macros/ui/monotonic-double-binds.rs +++ /dev/null @@ -1,7 +0,0 @@ -#![no_main] - -#[rtic_macros::mock_app(device = mock)] -mod app { - #[monotonic(binds = Tim1, binds = Tim2)] - type Fast = hal::Tim1Monotonic; -} diff --git a/macros/ui/monotonic-double-binds.stderr b/macros/ui/monotonic-double-binds.stderr deleted file mode 100644 index c7313dfac6..0000000000 --- a/macros/ui/monotonic-double-binds.stderr +++ /dev/null @@ -1,5 +0,0 @@ -error: argument appears more than once - --> $DIR/monotonic-double-binds.rs:5:31 - | -5 | #[monotonic(binds = Tim1, binds = Tim2)] - | ^^^^^ diff --git a/macros/ui/monotonic-double-default.rs b/macros/ui/monotonic-double-default.rs deleted file mode 100644 index dc4eac62a6..0000000000 --- a/macros/ui/monotonic-double-default.rs +++ /dev/null @@ -1,7 +0,0 @@ -#![no_main] - -#[rtic_macros::mock_app(device = mock)] -mod app { - #[monotonic(binds = Tim1, default = true, default = false)] - type Fast = hal::Tim1Monotonic; -} diff --git a/macros/ui/monotonic-double-default.stderr b/macros/ui/monotonic-double-default.stderr deleted file mode 100644 index 9819d04afe..0000000000 --- a/macros/ui/monotonic-double-default.stderr +++ /dev/null @@ -1,5 +0,0 @@ -error: argument appears more than once - --> $DIR/monotonic-double-default.rs:5:47 - | -5 | #[monotonic(binds = Tim1, default = true, default = false)] - | ^^^^^^^ diff --git a/macros/ui/monotonic-double-prio.rs b/macros/ui/monotonic-double-prio.rs deleted file mode 100644 index 4330ddb4f3..0000000000 --- a/macros/ui/monotonic-double-prio.rs +++ /dev/null @@ -1,7 +0,0 @@ -#![no_main] - -#[rtic_macros::mock_app(device = mock)] -mod app { - #[monotonic(binds = Tim1, priority = 1, priority = 2)] - type Fast = hal::Tim1Monotonic; -} diff --git a/macros/ui/monotonic-double-prio.stderr b/macros/ui/monotonic-double-prio.stderr deleted file mode 100644 index fa888e264e..0000000000 --- a/macros/ui/monotonic-double-prio.stderr +++ /dev/null @@ -1,5 +0,0 @@ -error: argument appears more than once - --> $DIR/monotonic-double-prio.rs:5:45 - | -5 | #[monotonic(binds = Tim1, priority = 1, priority = 2)] - | ^^^^^^^^ diff --git a/macros/ui/monotonic-double.rs b/macros/ui/monotonic-double.rs deleted file mode 100644 index 3c43fae8f5..0000000000 --- a/macros/ui/monotonic-double.rs +++ /dev/null @@ -1,10 +0,0 @@ -#![no_main] - -#[rtic_macros::mock_app(device = mock)] -mod app { - #[monotonic(binds = Tim1)] - type Fast = hal::Tim1Monotonic; - - #[monotonic(binds = Tim1)] - type Fast = hal::Tim1Monotonic; -} diff --git a/macros/ui/monotonic-double.stderr b/macros/ui/monotonic-double.stderr deleted file mode 100644 index 9fab84c84e..0000000000 --- a/macros/ui/monotonic-double.stderr +++ /dev/null @@ -1,5 +0,0 @@ -error: `#[monotonic(...)]` on a specific type must appear at most once - --> ui/monotonic-double.rs:9:10 - | -9 | type Fast = hal::Tim1Monotonic; - | ^^^^ diff --git a/macros/ui/monotonic-name-collision.rs b/macros/ui/monotonic-name-collision.rs deleted file mode 100644 index d8d44310de..0000000000 --- a/macros/ui/monotonic-name-collision.rs +++ /dev/null @@ -1,10 +0,0 @@ -#![no_main] - -#[rtic_macros::mock_app(device = mock)] -mod app { - #[monotonic(binds = Tim1)] - type Fast1 = hal::Tim1Monotonic; - - #[monotonic(binds = Tim2)] - type Fast1 = hal::Tim2Monotonic; -} diff --git a/macros/ui/monotonic-name-collision.stderr b/macros/ui/monotonic-name-collision.stderr deleted file mode 100644 index 6557ee5b2d..0000000000 --- a/macros/ui/monotonic-name-collision.stderr +++ /dev/null @@ -1,5 +0,0 @@ -error: `#[monotonic(...)]` on a specific type must appear at most once - --> ui/monotonic-name-collision.rs:9:10 - | -9 | type Fast1 = hal::Tim2Monotonic; - | ^^^^^ diff --git a/macros/ui/monotonic-no-binds.rs b/macros/ui/monotonic-no-binds.rs deleted file mode 100644 index 462d73e1d7..0000000000 --- a/macros/ui/monotonic-no-binds.rs +++ /dev/null @@ -1,7 +0,0 @@ -#![no_main] - -#[rtic_macros::mock_app(device = mock)] -mod app { - #[monotonic()] - type Fast = hal::Tim1Monotonic; -} diff --git a/macros/ui/monotonic-no-binds.stderr b/macros/ui/monotonic-no-binds.stderr deleted file mode 100644 index 0ef7b60522..0000000000 --- a/macros/ui/monotonic-no-binds.stderr +++ /dev/null @@ -1,5 +0,0 @@ -error: `binds = ...` is missing - --> $DIR/monotonic-no-binds.rs:5:17 - | -5 | #[monotonic()] - | ^ diff --git a/macros/ui/monotonic-no-paran.rs b/macros/ui/monotonic-no-paran.rs deleted file mode 100644 index e294bc8595..0000000000 --- a/macros/ui/monotonic-no-paran.rs +++ /dev/null @@ -1,8 +0,0 @@ -#![no_main] - -#[rtic_macros::mock_app(device = mock)] -mod app { - #[monotonic] - type Fast = hal::Tim1Monotonic; -} - diff --git a/macros/ui/monotonic-no-paran.stderr b/macros/ui/monotonic-no-paran.stderr deleted file mode 100644 index c2b32c5c87..0000000000 --- a/macros/ui/monotonic-no-paran.stderr +++ /dev/null @@ -1,5 +0,0 @@ -error: expected opening ( in #[monotonic( ... )] - --> ui/monotonic-no-paran.rs:5:7 - | -5 | #[monotonic] - | ^^^^^^^^^ diff --git a/macros/ui/monotonic-timer-collision.rs b/macros/ui/monotonic-timer-collision.rs deleted file mode 100644 index 5663ad7743..0000000000 --- a/macros/ui/monotonic-timer-collision.rs +++ /dev/null @@ -1,10 +0,0 @@ -#![no_main] - -#[rtic_macros::mock_app(device = mock)] -mod app { - #[monotonic(binds = Tim1)] - type Fast1 = hal::Tim1Monotonic; - - #[monotonic(binds = Tim2)] - type Fast2 = hal::Tim1Monotonic; -} diff --git a/macros/ui/monotonic-timer-collision.stderr b/macros/ui/monotonic-timer-collision.stderr deleted file mode 100644 index 239b96b6ef..0000000000 --- a/macros/ui/monotonic-timer-collision.stderr +++ /dev/null @@ -1,5 +0,0 @@ -error: this type is already used by another monotonic - --> $DIR/monotonic-timer-collision.rs:9:18 - | -9 | type Fast2 = hal::Tim1Monotonic; - | ^^^ diff --git a/macros/ui/monotonic-with-attrs.rs b/macros/ui/monotonic-with-attrs.rs deleted file mode 100644 index 7c63fbbf6d..0000000000 --- a/macros/ui/monotonic-with-attrs.rs +++ /dev/null @@ -1,8 +0,0 @@ -#![no_main] - -#[rtic_macros::mock_app(device = mock)] -mod app { - #[no_mangle] - #[monotonic(binds = Tim1)] - type Fast = hal::Tim1Monotonic; -} diff --git a/macros/ui/monotonic-with-attrs.stderr b/macros/ui/monotonic-with-attrs.stderr deleted file mode 100644 index 62655d872c..0000000000 --- a/macros/ui/monotonic-with-attrs.stderr +++ /dev/null @@ -1,5 +0,0 @@ -error: Monotonic does not support attributes other than `#[cfg]` - --> $DIR/monotonic-with-attrs.rs:5:7 - | -5 | #[no_mangle] - | ^^^^^^^^^ diff --git a/macros/ui/pub-local.stderr b/macros/ui/pub-local.stderr deleted file mode 100644 index dee818ccab..0000000000 --- a/macros/ui/pub-local.stderr +++ /dev/null @@ -1,5 +0,0 @@ -error: this field must have inherited / private visibility - --> $DIR/pub-local.rs:7:13 - | -7 | pub x: u32, - | ^ diff --git a/macros/ui/pub-shared.stderr b/macros/ui/pub-shared.stderr deleted file mode 100644 index 0fdb1ff517..0000000000 --- a/macros/ui/pub-shared.stderr +++ /dev/null @@ -1,5 +0,0 @@ -error: this field must have inherited / private visibility - --> $DIR/pub-shared.rs:7:13 - | -7 | pub x: u32, - | ^ diff --git a/macros/ui/shared-lock-free.rs b/macros/ui/shared-lock-free.rs index c7f8a16d5e..b3a4b9c7e2 100644 --- a/macros/ui/shared-lock-free.rs +++ b/macros/ui/shared-lock-free.rs @@ -17,7 +17,7 @@ mod app { struct Local {} #[init] - fn init(_: init::Context) -> (Shared, Local, init::Monotonics) {} + fn init(_: init::Context) -> (Shared, Local) {} // e2 ok #[idle(shared = [e2])] @@ -27,12 +27,12 @@ mod app { } // e1 rejected (not lock_free) - #[task(priority = 1, shared = [e1])] + #[task(binds = UART0, priority = 1, shared = [e1])] fn uart0(cx: uart0::Context) { *cx.resources.e1 += 10; } // e1 rejected (not lock_free) - #[task(priority = 2, shared = [e1])] + #[task(binds = UART1, priority = 2, shared = [e1])] fn uart1(cx: uart1::Context) {} } diff --git a/macros/ui/shared-lock-free.stderr b/macros/ui/shared-lock-free.stderr deleted file mode 100644 index c6820e8729..0000000000 --- a/macros/ui/shared-lock-free.stderr +++ /dev/null @@ -1,17 +0,0 @@ -error: Lock free shared resource "e1" is used by tasks at different priorities - --> $DIR/shared-lock-free.rs:9:9 - | -9 | e1: u32, - | ^^ - -error: Shared resource "e1" is declared lock free but used by tasks at different priorities - --> $DIR/shared-lock-free.rs:30:36 - | -30 | #[task(priority = 1, shared = [e1])] - | ^^ - -error: Shared resource "e1" is declared lock free but used by tasks at different priorities - --> $DIR/shared-lock-free.rs:36:36 - | -36 | #[task(priority = 2, shared = [e1])] - | ^^ diff --git a/macros/ui/shared-not-declared.rs b/macros/ui/shared-not-declared.rs index aca4178761..5fef5347cb 100644 --- a/macros/ui/shared-not-declared.rs +++ b/macros/ui/shared-not-declared.rs @@ -9,8 +9,8 @@ mod app { struct Local {} #[task(shared = [A])] - fn foo(_: foo::Context) {} + async fn foo(_: foo::Context) {} #[init] - fn init(_: init::Context) -> (Shared, Local, init::Monotonics) {} + fn init(_: init::Context) -> (Shared, Local) {} } diff --git a/macros/ui/shared-not-declared.stderr b/macros/ui/shared-not-declared.stderr index c17425191d..7c5fb32e95 100644 --- a/macros/ui/shared-not-declared.stderr +++ b/macros/ui/shared-not-declared.stderr @@ -1,5 +1,5 @@ error: this shared resource has NOT been declared - --> $DIR/shared-not-declared.rs:11:22 + --> ui/shared-not-declared.rs:11:22 | 11 | #[task(shared = [A])] | ^ diff --git a/macros/ui/shared-pub.stderr b/macros/ui/shared-pub.stderr index 8f761c6be8..71488933d1 100644 --- a/macros/ui/shared-pub.stderr +++ b/macros/ui/shared-pub.stderr @@ -1,5 +1,5 @@ error: this field must have inherited / private visibility - --> $DIR/shared-pub.rs:7:13 + --> ui/shared-pub.rs:7:13 | 7 | pub x: u32, | ^ diff --git a/macros/ui/task-divergent.rs b/macros/ui/task-divergent.rs index 5a471f3cca..ffe2dc0f1c 100644 --- a/macros/ui/task-divergent.rs +++ b/macros/ui/task-divergent.rs @@ -3,7 +3,7 @@ #[rtic_macros::mock_app(device = mock)] mod app { #[task] - fn foo(_: foo::Context) -> ! { + async fn foo(_: foo::Context) -> ! { loop {} } } diff --git a/macros/ui/task-divergent.stderr b/macros/ui/task-divergent.stderr index b25ca5d798..bd22bd3358 100644 --- a/macros/ui/task-divergent.stderr +++ b/macros/ui/task-divergent.stderr @@ -1,5 +1,5 @@ -error: this task handler must have type signature `(async) fn(foo::Context, ..)` - --> ui/task-divergent.rs:6:8 +error: this task handler must have type signature `async fn(foo::Context)` + --> ui/task-divergent.rs:6:14 | -6 | fn foo(_: foo::Context) -> ! { - | ^^^ +6 | async fn foo(_: foo::Context) -> ! { + | ^^^ diff --git a/macros/ui/task-double-capacity.rs b/macros/ui/task-double-capacity.rs deleted file mode 100644 index 806d973146..0000000000 --- a/macros/ui/task-double-capacity.rs +++ /dev/null @@ -1,7 +0,0 @@ -#![no_main] - -#[rtic_macros::mock_app(device = mock)] -mod app { - #[task(capacity = 1, capacity = 2)] - fn foo(_: foo::Context) {} -} diff --git a/macros/ui/task-double-capacity.stderr b/macros/ui/task-double-capacity.stderr deleted file mode 100644 index f73bca5fbf..0000000000 --- a/macros/ui/task-double-capacity.stderr +++ /dev/null @@ -1,5 +0,0 @@ -error: argument appears more than once - --> $DIR/task-double-capacity.rs:5:26 - | -5 | #[task(capacity = 1, capacity = 2)] - | ^^^^^^^^ diff --git a/macros/ui/task-double-local.rs b/macros/ui/task-double-local.rs index 2e465d7c0e..c5277e2bd5 100644 --- a/macros/ui/task-double-local.rs +++ b/macros/ui/task-double-local.rs @@ -3,5 +3,5 @@ #[rtic_macros::mock_app(device = mock)] mod app { #[task(local = [A], local = [B])] - fn foo(_: foo::Context) {} + async fn foo(_: foo::Context) {} } diff --git a/macros/ui/task-double-local.stderr b/macros/ui/task-double-local.stderr index 654ed33328..91ed844647 100644 --- a/macros/ui/task-double-local.stderr +++ b/macros/ui/task-double-local.stderr @@ -1,5 +1,5 @@ error: argument appears more than once - --> $DIR/task-double-local.rs:5:25 + --> ui/task-double-local.rs:5:25 | 5 | #[task(local = [A], local = [B])] | ^^^^^ diff --git a/macros/ui/task-double-priority.rs b/macros/ui/task-double-priority.rs index 0a2ef0ddfc..5c8bd5b1f3 100644 --- a/macros/ui/task-double-priority.rs +++ b/macros/ui/task-double-priority.rs @@ -3,5 +3,5 @@ #[rtic_macros::mock_app(device = mock)] mod app { #[task(priority = 1, priority = 2)] - fn foo(_: foo::Context) {} + async fn foo(_: foo::Context) {} } diff --git a/macros/ui/task-double-priority.stderr b/macros/ui/task-double-priority.stderr index 3d06dc6643..b3c814a997 100644 --- a/macros/ui/task-double-priority.stderr +++ b/macros/ui/task-double-priority.stderr @@ -1,5 +1,5 @@ error: argument appears more than once - --> $DIR/task-double-priority.rs:5:26 + --> ui/task-double-priority.rs:5:26 | 5 | #[task(priority = 1, priority = 2)] | ^^^^^^^^ diff --git a/macros/ui/task-double-shared.rs b/macros/ui/task-double-shared.rs index 3b4d411584..f9812d394a 100644 --- a/macros/ui/task-double-shared.rs +++ b/macros/ui/task-double-shared.rs @@ -3,5 +3,5 @@ #[rtic_macros::mock_app(device = mock)] mod app { #[task(shared = [A], shared = [B])] - fn foo(_: foo::Context) {} + async fn foo(_: foo::Context) {} } diff --git a/macros/ui/task-double-shared.stderr b/macros/ui/task-double-shared.stderr index 6952f06c17..bb90212625 100644 --- a/macros/ui/task-double-shared.stderr +++ b/macros/ui/task-double-shared.stderr @@ -1,5 +1,5 @@ error: argument appears more than once - --> $DIR/task-double-shared.rs:5:26 + --> ui/task-double-shared.rs:5:26 | 5 | #[task(shared = [A], shared = [B])] | ^^^^^^ diff --git a/macros/ui/task-idle.rs b/macros/ui/task-idle.rs index 3be6e282b1..353c7826da 100644 --- a/macros/ui/task-idle.rs +++ b/macros/ui/task-idle.rs @@ -9,5 +9,5 @@ mod app { // name collides with `#[idle]` function #[task] - fn foo(_: foo::Context) {} + async fn foo(_: foo::Context) {} } diff --git a/macros/ui/task-idle.stderr b/macros/ui/task-idle.stderr index ba4fc94c23..4ccc113570 100644 --- a/macros/ui/task-idle.stderr +++ b/macros/ui/task-idle.stderr @@ -1,5 +1,5 @@ error: this identifier has already been used - --> $DIR/task-idle.rs:12:8 + --> ui/task-idle.rs:12:14 | -12 | fn foo(_: foo::Context) {} - | ^^^ +12 | async fn foo(_: foo::Context) {} + | ^^^ diff --git a/macros/ui/task-init.rs b/macros/ui/task-init.rs index bab3805019..e58fdcebe0 100644 --- a/macros/ui/task-init.rs +++ b/macros/ui/task-init.rs @@ -9,9 +9,9 @@ mod app { struct Local {} #[init] - fn foo(_: foo::Context) -> (Shared, Local, foo::Monotonics) {} + fn foo(_: foo::Context) -> (Shared, Local) {} // name collides with `#[idle]` function #[task] - fn foo(_: foo::Context) {} + async fn foo(_: foo::Context) {} } diff --git a/macros/ui/task-init.stderr b/macros/ui/task-init.stderr index 911af37f12..161e1943cb 100644 --- a/macros/ui/task-init.stderr +++ b/macros/ui/task-init.stderr @@ -1,5 +1,5 @@ error: this identifier has already been used - --> $DIR/task-init.rs:16:8 + --> ui/task-init.rs:16:14 | -16 | fn foo(_: foo::Context) {} - | ^^^ +16 | async fn foo(_: foo::Context) {} + | ^^^ diff --git a/macros/ui/task-interrupt.rs b/macros/ui/task-interrupt.rs index 5e063def3b..3d50bd8340 100644 --- a/macros/ui/task-interrupt.rs +++ b/macros/ui/task-interrupt.rs @@ -6,5 +6,5 @@ mod app { fn foo(_: foo::Context) {} #[task] - fn foo(_: foo::Context) {} + async fn foo(_: foo::Context) {} } diff --git a/macros/ui/task-interrupt.stderr b/macros/ui/task-interrupt.stderr index 6efb0f9966..087b6c6eb5 100644 --- a/macros/ui/task-interrupt.stderr +++ b/macros/ui/task-interrupt.stderr @@ -1,5 +1,5 @@ error: this task is defined multiple times - --> $DIR/task-interrupt.rs:9:8 + --> ui/task-interrupt.rs:9:14 | -9 | fn foo(_: foo::Context) {} - | ^^^ +9 | async fn foo(_: foo::Context) {} + | ^^^ diff --git a/macros/ui/task-no-context.rs b/macros/ui/task-no-context.rs index e2da625417..55e8c3b48e 100644 --- a/macros/ui/task-no-context.rs +++ b/macros/ui/task-no-context.rs @@ -3,5 +3,5 @@ #[rtic_macros::mock_app(device = mock)] mod app { #[task] - fn foo() {} + async fn foo() {} } diff --git a/macros/ui/task-no-context.stderr b/macros/ui/task-no-context.stderr index 8bf3438b52..91239a17b0 100644 --- a/macros/ui/task-no-context.stderr +++ b/macros/ui/task-no-context.stderr @@ -1,5 +1,5 @@ -error: this task handler must have type signature `(async) fn(foo::Context, ..)` - --> ui/task-no-context.rs:6:8 +error: this task handler must have type signature `async fn(foo::Context)` + --> ui/task-no-context.rs:6:14 | -6 | fn foo() {} - | ^^^ +6 | async fn foo() {} + | ^^^ diff --git a/macros/ui/task-priority-too-high.rs b/macros/ui/task-priority-too-high.rs index 8c32bebaa3..f33ba5699d 100644 --- a/macros/ui/task-priority-too-high.rs +++ b/macros/ui/task-priority-too-high.rs @@ -3,5 +3,5 @@ #[rtic_macros::mock_app(device = mock)] mod app { #[task(priority = 256)] - fn foo(_: foo::Context) {} + async fn foo(_: foo::Context) {} } diff --git a/macros/ui/task-pub.rs b/macros/ui/task-pub.rs index 3cbd523413..1ae533f6c0 100644 --- a/macros/ui/task-pub.rs +++ b/macros/ui/task-pub.rs @@ -3,5 +3,5 @@ #[rtic_macros::mock_app(device = mock)] mod app { #[task] - pub fn foo(_: foo::Context) {} + pub async fn foo(_: foo::Context) {} } diff --git a/macros/ui/task-pub.stderr b/macros/ui/task-pub.stderr index 56e09b11d9..72c4e637d1 100644 --- a/macros/ui/task-pub.stderr +++ b/macros/ui/task-pub.stderr @@ -1,5 +1,5 @@ -error: this task handler must have type signature `(async) fn(foo::Context, ..)` - --> ui/task-pub.rs:6:12 +error: this task handler must have type signature `async fn(foo::Context)` + --> ui/task-pub.rs:6:18 | -6 | pub fn foo(_: foo::Context) {} - | ^^^ +6 | pub async fn foo(_: foo::Context) {} + | ^^^ diff --git a/macros/ui/task-unsafe.rs b/macros/ui/task-unsafe.rs index 44255f02b5..a8383ef425 100644 --- a/macros/ui/task-unsafe.rs +++ b/macros/ui/task-unsafe.rs @@ -3,5 +3,5 @@ #[rtic_macros::mock_app(device = mock)] mod app { #[task] - unsafe fn foo(_: foo::Context) {} + async unsafe fn foo(_: foo::Context) {} } diff --git a/macros/ui/task-unsafe.stderr b/macros/ui/task-unsafe.stderr index 424c5af323..4908481311 100644 --- a/macros/ui/task-unsafe.stderr +++ b/macros/ui/task-unsafe.stderr @@ -1,5 +1,5 @@ -error: this task handler must have type signature `(async) fn(foo::Context, ..)` - --> ui/task-unsafe.rs:6:15 +error: this task handler must have type signature `async fn(foo::Context)` + --> ui/task-unsafe.rs:6:21 | -6 | unsafe fn foo(_: foo::Context) {} - | ^^^ +6 | async unsafe fn foo(_: foo::Context) {} + | ^^^ diff --git a/macros/ui/async-zero-prio-tasks.rs b/macros/ui/task-zero-prio.rs similarity index 78% rename from macros/ui/async-zero-prio-tasks.rs rename to macros/ui/task-zero-prio.rs index 91e0990a8b..de3c86fc8e 100644 --- a/macros/ui/async-zero-prio-tasks.rs +++ b/macros/ui/task-zero-prio.rs @@ -9,7 +9,7 @@ mod app { struct Local {} #[init] - fn init(_: init::Context) -> (Shared, Local, init::Monotonics) {} + fn init(_: init::Context) -> (Shared, Local) {} #[task(priority = 0)] fn foo(_: foo::Context) {} diff --git a/macros/ui/task-zero-prio.stderr b/macros/ui/task-zero-prio.stderr new file mode 100644 index 0000000000..f2d82231e3 --- /dev/null +++ b/macros/ui/task-zero-prio.stderr @@ -0,0 +1,5 @@ +error: this task handler must have type signature `async fn(foo::Context)` + --> ui/task-zero-prio.rs:15:8 + | +15 | fn foo(_: foo::Context) {} + | ^^^ From cbe592688047e41ebfd0f15e7bf5799f81bfcd4a Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Sun, 8 Jan 2023 16:36:48 +0100 Subject: [PATCH 238/423] Fix failing UI test --- macros/src/syntax/analyze.rs | 18 ++++++++++-------- macros/ui/shared-lock-free.stderr | 17 +++++++++++++++++ 2 files changed, 27 insertions(+), 8 deletions(-) create mode 100644 macros/ui/shared-lock-free.stderr diff --git a/macros/src/syntax/analyze.rs b/macros/src/syntax/analyze.rs index ff0577dacc..dd5a9b40d2 100644 --- a/macros/src/syntax/analyze.rs +++ b/macros/src/syntax/analyze.rs @@ -13,17 +13,17 @@ use crate::syntax::{ pub(crate) fn app(app: &App) -> Result { // Collect all tasks into a vector - type TaskName = String; + type TaskName = Ident; type Priority = u8; // The task list is a Tuple (Name, Shared Resources, Local Resources, Priority) let task_resources_list: Vec<(TaskName, Vec<&Ident>, &LocalResources, Priority)> = Some(&app.init) .iter() - .map(|ht| ("init".to_string(), Vec::new(), &ht.args.local_resources, 0)) + .map(|ht| (ht.name.clone(), Vec::new(), &ht.args.local_resources, 0)) .chain(app.idle.iter().map(|ht| { ( - "idle".to_string(), + ht.name.clone(), ht.args .shared_resources .iter() @@ -35,7 +35,7 @@ pub(crate) fn app(app: &App) -> Result { })) .chain(app.software_tasks.iter().map(|(name, ht)| { ( - name.to_string(), + name.clone(), ht.args .shared_resources .iter() @@ -47,7 +47,7 @@ pub(crate) fn app(app: &App) -> Result { })) .chain(app.hardware_tasks.iter().map(|(name, ht)| { ( - name.to_string(), + name.clone(), ht.args .shared_resources .iter() @@ -77,16 +77,17 @@ pub(crate) fn app(app: &App) -> Result { for r in tr { // Get all uses of resources annotated lock_free if lf_res == r { - // lock_free resources are not allowed in async tasks - error.push(syn::Error::new( + // Check so async tasks do not use lock free resources + if app.software_tasks.get(task).is_some() { + error.push(syn::Error::new( r.span(), format!( "Lock free shared resource {:?} is used by an async tasks, which is forbidden", r.to_string(), ), )); + } - // TODO: Should this be removed? // HashMap returns the previous existing object if old.key == new.key if let Some(lf_res) = lf_hash.insert(r.to_string(), (task, r, priority)) { // Check if priority differ, if it does, append to @@ -95,6 +96,7 @@ pub(crate) fn app(app: &App) -> Result { lf_res_with_error.push(lf_res.1); lf_res_with_error.push(r); } + // If the resource already violates lock free properties if lf_res_with_error.contains(&r) { lf_res_with_error.push(lf_res.1); diff --git a/macros/ui/shared-lock-free.stderr b/macros/ui/shared-lock-free.stderr new file mode 100644 index 0000000000..51e99a0c43 --- /dev/null +++ b/macros/ui/shared-lock-free.stderr @@ -0,0 +1,17 @@ +error: Lock free shared resource "e1" is used by tasks at different priorities + --> ui/shared-lock-free.rs:9:9 + | +9 | e1: u32, + | ^^ + +error: Shared resource "e1" is declared lock free but used by tasks at different priorities + --> ui/shared-lock-free.rs:30:51 + | +30 | #[task(binds = UART0, priority = 1, shared = [e1])] + | ^^ + +error: Shared resource "e1" is declared lock free but used by tasks at different priorities + --> ui/shared-lock-free.rs:36:51 + | +36 | #[task(binds = UART1, priority = 2, shared = [e1])] + | ^^ From 9a67f00a30f14df3b9635913f728afd0b40c138d Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Sun, 8 Jan 2023 19:16:36 +0100 Subject: [PATCH 239/423] Fix typos --- examples/init.rs | 2 +- macros/src/codegen/module.rs | 2 +- xtask/src/command.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/init.rs b/examples/init.rs index c807a3c10b..d37903ea50 100644 --- a/examples/init.rs +++ b/examples/init.rs @@ -29,7 +29,7 @@ mod app { let _x: &'static mut u32 = cx.local.x; // Access to the critical section token, - // to indicate that this is a critical seciton + // to indicate that this is a critical section let _cs_token: bare_metal::CriticalSection = cx.cs; hprintln!("init").unwrap(); diff --git a/macros/src/codegen/module.rs b/macros/src/codegen/module.rs index a64abd8a74..c6f7690fc3 100644 --- a/macros/src/codegen/module.rs +++ b/macros/src/codegen/module.rs @@ -140,7 +140,7 @@ pub fn codegen(ctxt: Context, app: &App, analysis: &Analysis) -> TokenStream2 { let interrupt = &analysis .interrupts .get(&priority) - .expect("RTIC-ICE: interrupt identifer not found") + .expect("RTIC-ICE: interrupt identifier not found") .0; let internal_spawn_ident = util::internal_task_ident(name, "spawn"); diff --git a/xtask/src/command.rs b/xtask/src/command.rs index 889540c529..418f440cb0 100644 --- a/xtask/src/command.rs +++ b/xtask/src/command.rs @@ -136,7 +136,7 @@ pub fn run_command(command: &CargoCommand) -> anyhow::Result { }) } -/// Check if `run` was sucessful. +/// Check if `run` was successful. /// returns Ok in case the run went as expected, /// Err otherwise pub fn run_successful(run: &RunResult, expected_output_file: String) -> Result<(), TestRunError> { From ceaf3613d3256f60b139a4f93220e3c298603b83 Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Sun, 8 Jan 2023 19:40:31 +0100 Subject: [PATCH 240/423] Update semihosting --- examples/async-task-multiple-prios.rs | 16 +++----- examples/async-task.rs | 8 ++-- examples/big-struct-opt.rs | 4 +- examples/binds.rs | 7 ++-- examples/complex.rs | 53 +++++++++++++-------------- examples/destructure.rs | 4 +- examples/extern_binds.rs | 6 +-- examples/extern_spawn.rs | 2 +- examples/generics.rs | 6 +-- examples/hardware.rs | 7 ++-- examples/idle-wfi.rs | 4 +- examples/idle.rs | 4 +- examples/init.rs | 2 +- examples/locals.rs | 6 +-- examples/lock.rs | 10 ++--- examples/multilock.rs | 2 +- examples/not-sync.rs | 6 +-- examples/only-shared-access.rs | 4 +- examples/preempt.rs | 10 ++--- examples/ramfunc.rs | 2 +- examples/resource-user-struct.rs | 4 +- examples/shared.rs | 2 +- examples/spawn.rs | 4 +- examples/static.rs | 2 +- examples/task.rs | 10 ++--- 25 files changed, 88 insertions(+), 97 deletions(-) diff --git a/examples/async-task-multiple-prios.rs b/examples/async-task-multiple-prios.rs index 2f3a0f7b27..f614820cb5 100644 --- a/examples/async-task-multiple-prios.rs +++ b/examples/async-task-multiple-prios.rs @@ -24,8 +24,8 @@ mod app { struct Local {} #[init] - fn init(cx: init::Context) -> (Shared, Local) { - hprintln!("init").unwrap(); + fn init(_: init::Context) -> (Shared, Local) { + hprintln!("init"); async_task1::spawn().ok(); async_task2::spawn().ok(); @@ -51,8 +51,7 @@ mod app { *a += 1; *a }) - ) - .ok(); + ); } #[task(priority = 1, shared = [a, b])] @@ -63,8 +62,7 @@ mod app { *a += 1; *a }) - ) - .ok(); + ); } #[task(priority = 2, shared = [a, b])] @@ -75,8 +73,7 @@ mod app { *a += 1; *a }) - ) - .ok(); + ); } #[task(priority = 2, shared = [a, b])] @@ -87,7 +84,6 @@ mod app { *a += 1; *a }) - ) - .ok(); + ); } } diff --git a/examples/async-task.rs b/examples/async-task.rs index 210a86510d..780bc08157 100644 --- a/examples/async-task.rs +++ b/examples/async-task.rs @@ -24,7 +24,7 @@ mod app { #[init] fn init(_cx: init::Context) -> (Shared, Local) { - hprintln!("init").unwrap(); + hprintln!("init"); async_task::spawn().unwrap(); async_task2::spawn().unwrap(); @@ -44,18 +44,18 @@ mod app { #[task(binds = UART1, shared = [a])] fn hw_task(cx: hw_task::Context) { let hw_task::SharedResources { a: _, .. } = cx.shared; - hprintln!("hello from hw").ok(); + hprintln!("hello from hw"); } #[task(shared = [a])] async fn async_task(cx: async_task::Context) { let async_task::SharedResources { a: _, .. } = cx.shared; - hprintln!("hello from async").ok(); + hprintln!("hello from async"); } #[task(priority = 2, shared = [a])] async fn async_task2(cx: async_task2::Context) { let async_task2::SharedResources { a: _, .. } = cx.shared; - hprintln!("hello from async2").ok(); + hprintln!("hello from async2"); } } diff --git a/examples/big-struct-opt.rs b/examples/big-struct-opt.rs index 4bf93b2af5..3100a0e22c 100644 --- a/examples/big-struct-opt.rs +++ b/examples/big-struct-opt.rs @@ -67,13 +67,13 @@ mod app { fn uart0(mut cx: uart0::Context) { cx.shared .big_struct - .lock(|b| hprintln!("uart0 data:{:?}", &b.data[0..5]).unwrap()); + .lock(|b| hprintln!("uart0 data:{:?}", &b.data[0..5])); } #[task(shared = [big_struct], priority = 2)] async fn async_task(mut cx: async_task::Context) { cx.shared .big_struct - .lock(|b| hprintln!("async_task data:{:?}", &b.data[0..5]).unwrap()); + .lock(|b| hprintln!("async_task data:{:?}", &b.data[0..5])); } } diff --git a/examples/binds.rs b/examples/binds.rs index ec25ccca88..d78dffbf5f 100644 --- a/examples/binds.rs +++ b/examples/binds.rs @@ -23,14 +23,14 @@ mod app { fn init(_: init::Context) -> (Shared, Local) { rtic::pend(Interrupt::UART0); - hprintln!("init").unwrap(); + hprintln!("init"); (Shared {}, Local {}) } #[idle] fn idle(_: idle::Context) -> ! { - hprintln!("idle").unwrap(); + hprintln!("idle"); rtic::pend(Interrupt::UART0); @@ -49,7 +49,6 @@ mod app { "foo called {} time{}", *cx.local.times, if *cx.local.times > 1 { "s" } else { "" } - ) - .unwrap(); + ); } } diff --git a/examples/complex.rs b/examples/complex.rs index df9c862214..ab3979244c 100644 --- a/examples/complex.rs +++ b/examples/complex.rs @@ -25,7 +25,7 @@ mod app { #[init] fn init(_: init::Context) -> (Shared, Local) { - hprintln!("init").unwrap(); + hprintln!("init"); ( Shared { @@ -39,31 +39,31 @@ mod app { #[idle(shared = [s2, s3])] fn idle(mut cx: idle::Context) -> ! { - hprintln!("idle p0 started").ok(); + hprintln!("idle p0 started"); rtic::pend(Interrupt::GPIOC); cx.shared.s3.lock(|s| { - hprintln!("idle enter lock s3 {}", s).ok(); - hprintln!("idle pend t0").ok(); + hprintln!("idle enter lock s3 {}", s); + hprintln!("idle pend t0"); rtic::pend(Interrupt::GPIOA); // t0 p2, with shared ceiling 3 - hprintln!("idle pend t1").ok(); + hprintln!("idle pend t1"); rtic::pend(Interrupt::GPIOB); // t1 p3, with shared ceiling 3 - hprintln!("idle pend t2").ok(); + hprintln!("idle pend t2"); rtic::pend(Interrupt::GPIOC); // t2 p4, no sharing - hprintln!("idle still in lock s3 {}", s).ok(); + hprintln!("idle still in lock s3 {}", s); }); - hprintln!("\nback in idle").ok(); + hprintln!("\nback in idle"); cx.shared.s2.lock(|s| { - hprintln!("enter lock s2 {}", s).ok(); - hprintln!("idle pend t0").ok(); + hprintln!("enter lock s2 {}", s); + hprintln!("idle pend t0"); rtic::pend(Interrupt::GPIOA); // t0 p2, with shared ceiling 2 - hprintln!("idle pend t1").ok(); + hprintln!("idle pend t1"); rtic::pend(Interrupt::GPIOB); // t1 p3, no sharing - hprintln!("idle pend t2").ok(); + hprintln!("idle pend t2"); rtic::pend(Interrupt::GPIOC); // t2 p4, no sharing - hprintln!("idle still in lock s2 {}", s).ok(); + hprintln!("idle still in lock s2 {}", s); }); - hprintln!("\nidle exit").ok(); + hprintln!("\nidle exit"); debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator @@ -81,9 +81,8 @@ mod app { "t0 p2 called {} time{}", *cx.local.times, if *cx.local.times > 1 { "s" } else { "" } - ) - .ok(); - hprintln!("t0 p2 exit").ok(); + ); + hprintln!("t0 p2 exit"); } #[task(binds = GPIOB, priority = 3, local = [times: u32 = 0], shared = [s3, s4])] @@ -95,19 +94,18 @@ mod app { "t1 p3 called {} time{}", *cx.local.times, if *cx.local.times > 1 { "s" } else { "" } - ) - .ok(); + ); cx.shared.s4.lock(|s| { - hprintln!("t1 enter lock s4 {}", s).ok(); - hprintln!("t1 pend t0").ok(); + hprintln!("t1 enter lock s4 {}", s); + hprintln!("t1 pend t0"); rtic::pend(Interrupt::GPIOA); // t0 p2, with shared ceiling 2 - hprintln!("t1 pend t2").ok(); + hprintln!("t1 pend t2"); rtic::pend(Interrupt::GPIOC); // t2 p4, no sharing - hprintln!("t1 still in lock s4 {}", s).ok(); + hprintln!("t1 still in lock s4 {}", s); }); - hprintln!("t1 p3 exit").ok(); + hprintln!("t1 p3 exit"); } #[task(binds = GPIOC, priority = 4, local = [times: u32 = 0], shared = [s4])] @@ -119,13 +117,12 @@ mod app { "t2 p4 called {} time{}", *cx.local.times, if *cx.local.times > 1 { "s" } else { "" } - ) - .unwrap(); + ); cx.shared.s4.lock(|s| { - hprintln!("enter lock s4 {}", s).ok(); + hprintln!("enter lock s4 {}", s); *s += 1; }); - hprintln!("t3 p4 exit").ok(); + hprintln!("t3 p4 exit"); } } diff --git a/examples/destructure.rs b/examples/destructure.rs index 89336bfd74..dc5d8ef8ea 100644 --- a/examples/destructure.rs +++ b/examples/destructure.rs @@ -43,7 +43,7 @@ mod app { let b = cx.shared.b; let c = cx.shared.c; - hprintln!("foo: a = {}, b = {}, c = {}", a, b, c).unwrap(); + hprintln!("foo: a = {}, b = {}, c = {}", a, b, c); } // De-structure-ing syntax @@ -51,6 +51,6 @@ mod app { async fn bar(cx: bar::Context) { let bar::SharedResources { a, b, c, .. } = cx.shared; - hprintln!("bar: a = {}, b = {}, c = {}", a, b, c).unwrap(); + hprintln!("bar: a = {}, b = {}, c = {}", a, b, c); } } diff --git a/examples/extern_binds.rs b/examples/extern_binds.rs index c9fc108670..23b99d5ad5 100644 --- a/examples/extern_binds.rs +++ b/examples/extern_binds.rs @@ -10,7 +10,7 @@ use panic_semihosting as _; // Free function implementing the interrupt bound task `foo`. fn foo(_: app::foo::Context) { - hprintln!("foo called").ok(); + hprintln!("foo called"); } #[rtic::app(device = lm3s6965)] @@ -29,14 +29,14 @@ mod app { fn init(_: init::Context) -> (Shared, Local) { rtic::pend(Interrupt::UART0); - hprintln!("init").unwrap(); + hprintln!("init"); (Shared {}, Local {}) } #[idle] fn idle(_: idle::Context) -> ! { - hprintln!("idle").unwrap(); + hprintln!("idle"); rtic::pend(Interrupt::UART0); diff --git a/examples/extern_spawn.rs b/examples/extern_spawn.rs index 2d9d7b636a..8a3928d5b0 100644 --- a/examples/extern_spawn.rs +++ b/examples/extern_spawn.rs @@ -12,7 +12,7 @@ use panic_semihosting as _; // Free function implementing the spawnable task `foo`. // Notice, you need to indicate an anonymous lifetime <'a_> async fn foo(_c: app::foo::Context<'_>) { - hprintln!("foo").unwrap(); + hprintln!("foo"); debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator } diff --git a/examples/generics.rs b/examples/generics.rs index a73b00fb22..7117349a1e 100644 --- a/examples/generics.rs +++ b/examples/generics.rs @@ -32,7 +32,7 @@ mod app { #[task(binds = UART0, shared = [shared], local = [state: u32 = 0])] fn uart0(c: uart0::Context) { - hprintln!("UART0(STATE = {})", *c.local.state).unwrap(); + hprintln!("UART0(STATE = {})", *c.local.state); // second argument has type `shared::shared` super::advance(c.local.state, c.shared.shared); @@ -44,7 +44,7 @@ mod app { #[task(binds = UART1, priority = 2, shared = [shared], local = [state: u32 = 0])] fn uart1(c: uart1::Context) { - hprintln!("UART1(STATE = {})", *c.local.state).unwrap(); + hprintln!("UART1(STATE = {})", *c.local.state); // second argument has type `shared::shared` super::advance(c.local.state, c.shared.shared); @@ -61,5 +61,5 @@ fn advance(state: &mut u32, mut shared: impl Mutex) { (old, *shared) }); - hprintln!("shared: {} -> {}", old, new).unwrap(); + hprintln!("shared: {} -> {}", old, new); } diff --git a/examples/hardware.rs b/examples/hardware.rs index 8e4f423b7a..d3ceab9ed6 100644 --- a/examples/hardware.rs +++ b/examples/hardware.rs @@ -24,7 +24,7 @@ mod app { // `init` returns because interrupts are disabled rtic::pend(Interrupt::UART0); // equivalent to NVIC::pend - hprintln!("init").unwrap(); + hprintln!("init"); (Shared {}, Local {}) } @@ -33,7 +33,7 @@ mod app { fn idle(_: idle::Context) -> ! { // interrupts are enabled again; the `UART0` handler runs at this point - hprintln!("idle").unwrap(); + hprintln!("idle"); rtic::pend(Interrupt::UART0); @@ -53,7 +53,6 @@ mod app { "UART0 called {} time{}", *cx.local.times, if *cx.local.times > 1 { "s" } else { "" } - ) - .unwrap(); + ); } } diff --git a/examples/idle-wfi.rs b/examples/idle-wfi.rs index dd2d43f7c7..a68fe84560 100644 --- a/examples/idle-wfi.rs +++ b/examples/idle-wfi.rs @@ -19,7 +19,7 @@ mod app { #[init] fn init(mut cx: init::Context) -> (Shared, Local) { - hprintln!("init").unwrap(); + hprintln!("init"); // Set the ARM SLEEPONEXIT bit to go to sleep after handling interrupts // See https://developer.arm.com/docs/100737/0100/power-management/sleep-mode/sleep-on-exit-bit @@ -33,7 +33,7 @@ mod app { // Locals in idle have lifetime 'static let _x: &'static mut u32 = cx.local.x; - hprintln!("idle").unwrap(); + hprintln!("idle"); debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator diff --git a/examples/idle.rs b/examples/idle.rs index 9a7a7275a9..78f169777a 100644 --- a/examples/idle.rs +++ b/examples/idle.rs @@ -19,7 +19,7 @@ mod app { #[init] fn init(_: init::Context) -> (Shared, Local) { - hprintln!("init").unwrap(); + hprintln!("init"); (Shared {}, Local {}) } @@ -29,7 +29,7 @@ mod app { // Locals in idle have lifetime 'static let _x: &'static mut u32 = cx.local.x; - hprintln!("idle").unwrap(); + hprintln!("idle"); debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator diff --git a/examples/init.rs b/examples/init.rs index d37903ea50..1e362be702 100644 --- a/examples/init.rs +++ b/examples/init.rs @@ -32,7 +32,7 @@ mod app { // to indicate that this is a critical section let _cs_token: bare_metal::CriticalSection = cx.cs; - hprintln!("init").unwrap(); + hprintln!("init"); debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator diff --git a/examples/locals.rs b/examples/locals.rs index a35b4c9275..4e3b98bc86 100644 --- a/examples/locals.rs +++ b/examples/locals.rs @@ -45,7 +45,7 @@ mod app { let local_to_idle = cx.local.local_to_idle; *local_to_idle += 1; - hprintln!("idle: local_to_idle = {}", local_to_idle).unwrap(); + hprintln!("idle: local_to_idle = {}", local_to_idle); debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator @@ -69,7 +69,7 @@ mod app { // error: no `local_to_bar` field in `foo::LocalResources` // cx.local.local_to_bar += 1; - hprintln!("foo: local_to_foo = {}", local_to_foo).unwrap(); + hprintln!("foo: local_to_foo = {}", local_to_foo); } // `local_to_bar` can only be accessed from this context @@ -81,6 +81,6 @@ mod app { // error: no `local_to_foo` field in `bar::LocalResources` // cx.local.local_to_foo += 1; - hprintln!("bar: local_to_bar = {}", local_to_bar).unwrap(); + hprintln!("bar: local_to_bar = {}", local_to_bar); } } diff --git a/examples/lock.rs b/examples/lock.rs index 50b6aaaefd..3c1a5142a1 100644 --- a/examples/lock.rs +++ b/examples/lock.rs @@ -30,7 +30,7 @@ mod app { // when omitted priority is assumed to be `1` #[task(shared = [shared])] async fn foo(mut c: foo::Context) { - hprintln!("A").unwrap(); + hprintln!("A"); // the lower priority task requires a critical section to access the data c.shared.shared.lock(|shared| { @@ -40,7 +40,7 @@ mod app { // bar will *not* run right now due to the critical section bar::spawn().unwrap(); - hprintln!("B - shared = {}", *shared).unwrap(); + hprintln!("B - shared = {}", *shared); // baz does not contend for `shared` so it's allowed to run now baz::spawn().unwrap(); @@ -48,7 +48,7 @@ mod app { // critical section is over: bar can now start - hprintln!("E").unwrap(); + hprintln!("E"); debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator } @@ -62,11 +62,11 @@ mod app { *shared }); - hprintln!("D - shared = {}", shared).unwrap(); + hprintln!("D - shared = {}", shared); } #[task(priority = 3)] async fn baz(_: baz::Context) { - hprintln!("C").unwrap(); + hprintln!("C"); } } diff --git a/examples/multilock.rs b/examples/multilock.rs index 7bea2d37c4..2eb285ea9b 100644 --- a/examples/multilock.rs +++ b/examples/multilock.rs @@ -48,7 +48,7 @@ mod app { *s2 += 1; *s3 += 1; - hprintln!("Multiple locks, s1: {}, s2: {}, s3: {}", *s1, *s2, *s3).unwrap(); + hprintln!("Multiple locks, s1: {}, s2: {}, s3: {}", *s1, *s2, *s3); }); debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator diff --git a/examples/not-sync.rs b/examples/not-sync.rs index eb5c9f8fab..28a48f2321 100644 --- a/examples/not-sync.rs +++ b/examples/not-sync.rs @@ -32,7 +32,7 @@ mod app { #[init] fn init(_: init::Context) -> (Shared, Local) { - hprintln!("init").unwrap(); + hprintln!("init"); foo::spawn().unwrap(); bar::spawn().unwrap(); @@ -56,12 +56,12 @@ mod app { #[task(shared = [&shared])] async fn foo(c: foo::Context) { let shared: &NotSync = c.shared.shared; - hprintln!("foo a {}", shared.data).unwrap(); + hprintln!("foo a {}", shared.data); } #[task(shared = [&shared])] async fn bar(c: bar::Context) { let shared: &NotSync = c.shared.shared; - hprintln!("foo a {}", shared.data).unwrap(); + hprintln!("foo a {}", shared.data); } } diff --git a/examples/only-shared-access.rs b/examples/only-shared-access.rs index b506e44130..09cb23a594 100644 --- a/examples/only-shared-access.rs +++ b/examples/only-shared-access.rs @@ -31,13 +31,13 @@ mod app { #[task(shared = [&key])] async fn foo(cx: foo::Context) { let key: &u32 = cx.shared.key; - hprintln!("foo(key = {:#x})", key).unwrap(); + hprintln!("foo(key = {:#x})", key); debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator } #[task(priority = 2, shared = [&key])] async fn bar(cx: bar::Context) { - hprintln!("bar(key = {:#x})", cx.shared.key).unwrap(); + hprintln!("bar(key = {:#x})", cx.shared.key); } } diff --git a/examples/preempt.rs b/examples/preempt.rs index aad9125301..960fc57111 100644 --- a/examples/preempt.rs +++ b/examples/preempt.rs @@ -26,21 +26,21 @@ mod app { #[task(priority = 1)] async fn foo(_: foo::Context) { - hprintln!("foo - start").unwrap(); + hprintln!("foo - start"); baz::spawn().unwrap(); - hprintln!("foo - end").unwrap(); + hprintln!("foo - end"); debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator } #[task(priority = 2)] async fn bar(_: bar::Context) { - hprintln!(" bar").unwrap(); + hprintln!(" bar"); } #[task(priority = 2)] async fn baz(_: baz::Context) { - hprintln!(" baz - start").unwrap(); + hprintln!(" baz - start"); bar::spawn().unwrap(); - hprintln!(" baz - end").unwrap(); + hprintln!(" baz - end"); } } diff --git a/examples/ramfunc.rs b/examples/ramfunc.rs index dd1f76e5a7..316f6d8c53 100644 --- a/examples/ramfunc.rs +++ b/examples/ramfunc.rs @@ -33,7 +33,7 @@ mod app { #[inline(never)] #[task] async fn foo(_: foo::Context) { - hprintln!("foo").unwrap(); + hprintln!("foo"); debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator } diff --git a/examples/resource-user-struct.rs b/examples/resource-user-struct.rs index a4478ce6b4..2acbbc36fc 100644 --- a/examples/resource-user-struct.rs +++ b/examples/resource-user-struct.rs @@ -55,7 +55,7 @@ mod app { *shared }); - hprintln!("UART0: shared = {}", shared).unwrap(); + hprintln!("UART0: shared = {}", shared); } // `shared` can be accessed from this context @@ -66,6 +66,6 @@ mod app { *shared }); - hprintln!("UART1: shared = {}", shared).unwrap(); + hprintln!("UART1: shared = {}", shared); } } diff --git a/examples/shared.rs b/examples/shared.rs index fdc1b1c5c7..fd31cfb28a 100644 --- a/examples/shared.rs +++ b/examples/shared.rs @@ -34,7 +34,7 @@ mod app { fn idle(mut c: idle::Context) -> ! { loop { if let Some(byte) = c.shared.c.lock(|c| c.dequeue()) { - hprintln!("received message: {}", byte).unwrap(); + hprintln!("received message: {}", byte); debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator } else { diff --git a/examples/spawn.rs b/examples/spawn.rs index 266ace8630..384f0a0057 100644 --- a/examples/spawn.rs +++ b/examples/spawn.rs @@ -20,7 +20,7 @@ mod app { #[init] fn init(_: init::Context) -> (Shared, Local) { - hprintln!("init").unwrap(); + hprintln!("init"); foo::spawn().unwrap(); (Shared {}, Local {}) @@ -28,7 +28,7 @@ mod app { #[task] async fn foo(_: foo::Context) { - hprintln!("foo").unwrap(); + hprintln!("foo"); debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator } diff --git a/examples/static.rs b/examples/static.rs index 9c981db3e9..822224e373 100644 --- a/examples/static.rs +++ b/examples/static.rs @@ -38,7 +38,7 @@ mod app { loop { // Lock-free access to the same underlying queue! if let Some(data) = c.local.c.dequeue() { - hprintln!("received message: {}", data).unwrap(); + hprintln!("received message: {}", data); // Run foo until data if data == 3 { diff --git a/examples/task.rs b/examples/task.rs index fe1408b4ac..50287edd30 100644 --- a/examples/task.rs +++ b/examples/task.rs @@ -27,31 +27,31 @@ mod app { #[task] async fn foo(_: foo::Context) { - hprintln!("foo - start").unwrap(); + hprintln!("foo - start"); // spawns `bar` onto the task scheduler // `foo` and `bar` have the same priority so `bar` will not run until // after `foo` terminates bar::spawn().unwrap(); - hprintln!("foo - middle").unwrap(); + hprintln!("foo - middle"); // spawns `baz` onto the task scheduler // `baz` has higher priority than `foo` so it immediately preempts `foo` baz::spawn().unwrap(); - hprintln!("foo - end").unwrap(); + hprintln!("foo - end"); } #[task] async fn bar(_: bar::Context) { - hprintln!("bar").unwrap(); + hprintln!("bar"); debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator } #[task(priority = 2)] async fn baz(_: baz::Context) { - hprintln!("baz").unwrap(); + hprintln!("baz"); } } From 35c97b61c17a30de675eb1c7f852a100b200a0c2 Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Sun, 8 Jan 2023 19:56:47 +0100 Subject: [PATCH 241/423] All examples pass with `cargo xtask --target all` --- ci/expected/async-task-multiple-prios.run | 9 +++++---- ci/expected/async-task.run | 3 ++- ci/expected/big-struct-opt.run | 3 +++ ci/expected/destructure.run | 4 ++-- ci/expected/extern_spawn.run | 3 +-- ci/expected/locals.run | 2 +- ci/expected/not-sync.run | 3 +++ examples/binds.rs | 3 +-- examples/extern_binds.rs | 3 +-- examples/generics.rs | 1 + examples/hardware.rs | 3 +-- examples/not-sync.rs | 2 +- xtask/src/main.rs | 10 ++++++---- 13 files changed, 28 insertions(+), 21 deletions(-) diff --git a/ci/expected/async-task-multiple-prios.run b/ci/expected/async-task-multiple-prios.run index 9b0f53365b..0b42df0afe 100644 --- a/ci/expected/async-task-multiple-prios.run +++ b/ci/expected/async-task-multiple-prios.run @@ -1,5 +1,6 @@ init -hello from normal 2 -hello from async 2 -hello from normal 1 -hello from async 1 +hello from async 3 a 1 +hello from async 4 a 2 +hello from async 1 a 3 +hello from async 2 a 4 +idle diff --git a/ci/expected/async-task.run b/ci/expected/async-task.run index f7ce3a6065..6787fa8fae 100644 --- a/ci/expected/async-task.run +++ b/ci/expected/async-task.run @@ -1,3 +1,4 @@ init -hello from normal +hello from async2 hello from async +idle diff --git a/ci/expected/big-struct-opt.run b/ci/expected/big-struct-opt.run index e69de29bb2..7fdef35dcc 100644 --- a/ci/expected/big-struct-opt.run +++ b/ci/expected/big-struct-opt.run @@ -0,0 +1,3 @@ +async_task data:[22, 22, 22, 22, 22] +uart0 data:[22, 22, 22, 22, 22] +idle diff --git a/ci/expected/destructure.run b/ci/expected/destructure.run index b9b7cc90cc..25a4b1bded 100644 --- a/ci/expected/destructure.run +++ b/ci/expected/destructure.run @@ -1,2 +1,2 @@ -foo: a = 0, b = 0, c = 0 -bar: a = 0, b = 0, c = 0 +bar: a = 0, b = 1, c = 2 +foo: a = 0, b = 1, c = 2 diff --git a/ci/expected/extern_spawn.run b/ci/expected/extern_spawn.run index 2f8c74f6a4..257cc5642c 100644 --- a/ci/expected/extern_spawn.run +++ b/ci/expected/extern_spawn.run @@ -1,2 +1 @@ -foo 1, 2 -foo 2, 3 +foo diff --git a/ci/expected/locals.run b/ci/expected/locals.run index bf1d207698..4f1d3509c2 100644 --- a/ci/expected/locals.run +++ b/ci/expected/locals.run @@ -1,3 +1,3 @@ -foo: local_to_foo = 1 bar: local_to_bar = 1 +foo: local_to_foo = 1 idle: local_to_idle = 1 diff --git a/ci/expected/not-sync.run b/ci/expected/not-sync.run index e69de29bb2..cd91476aac 100644 --- a/ci/expected/not-sync.run +++ b/ci/expected/not-sync.run @@ -0,0 +1,3 @@ +init +bar a 13 +foo a 13 diff --git a/examples/binds.rs b/examples/binds.rs index d78dffbf5f..0c1ed971bd 100644 --- a/examples/binds.rs +++ b/examples/binds.rs @@ -34,10 +34,9 @@ mod app { rtic::pend(Interrupt::UART0); - debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator - loop { cortex_m::asm::nop(); + debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator } } diff --git a/examples/extern_binds.rs b/examples/extern_binds.rs index 23b99d5ad5..b24e7a1929 100644 --- a/examples/extern_binds.rs +++ b/examples/extern_binds.rs @@ -40,10 +40,9 @@ mod app { rtic::pend(Interrupt::UART0); - debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator - loop { cortex_m::asm::nop(); + debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator } } diff --git a/examples/generics.rs b/examples/generics.rs index 7117349a1e..dfd47adfb1 100644 --- a/examples/generics.rs +++ b/examples/generics.rs @@ -39,6 +39,7 @@ mod app { rtic::pend(Interrupt::UART1); + cortex_m::asm::nop(); debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator } diff --git a/examples/hardware.rs b/examples/hardware.rs index d3ceab9ed6..61eb6357aa 100644 --- a/examples/hardware.rs +++ b/examples/hardware.rs @@ -37,10 +37,9 @@ mod app { rtic::pend(Interrupt::UART0); - debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator - loop { cortex_m::asm::nop(); + debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator } } diff --git a/examples/not-sync.rs b/examples/not-sync.rs index 28a48f2321..5d868dfb52 100644 --- a/examples/not-sync.rs +++ b/examples/not-sync.rs @@ -62,6 +62,6 @@ mod app { #[task(shared = [&shared])] async fn bar(c: bar::Context) { let shared: &NotSync = c.shared.shared; - hprintln!("foo a {}", shared.data); + hprintln!("bar a {}", shared.data); } } diff --git a/xtask/src/main.rs b/xtask/src/main.rs index 76ce04bd4c..7eada91732 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs @@ -87,12 +87,14 @@ fn main() -> anyhow::Result<()> { let targets = [ARMV7M, ARMV6M]; let examples: Vec<_> = std::fs::read_dir("./examples")? - .filter_map(|path| { - path.map(|p| p.path().file_stem().unwrap().to_str().unwrap().to_string()) - .ok() - }) + .filter_map(|p| p.ok()) + .map(|p| p.path()) + .filter(|p| p.display().to_string().ends_with(".rs")) + .map(|path| path.file_stem().unwrap().to_str().unwrap().to_string()) .collect(); + println!("examples: {examples:?}"); + let opts = Options::from_args(); let target = &opts.target; From 6d252785e83218eeb5d080836281c90b86ca0e03 Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Sun, 8 Jan 2023 21:10:06 +0100 Subject: [PATCH 242/423] Support 0 prio tasks --- ci/expected/zero-prio-task.run | 3 + examples/zero-prio-task.rs | 56 +++++++++++++++++ macros/src/analyze.rs | 1 + macros/src/check.rs | 1 + macros/src/codegen/async_dispatchers.rs | 81 +++++++++++++++++-------- macros/src/codegen/main.rs | 13 ++-- macros/src/codegen/module.rs | 17 +++--- macros/src/codegen/util.rs | 4 ++ macros/src/syntax/analyze.rs | 33 +++++----- 9 files changed, 154 insertions(+), 55 deletions(-) create mode 100644 ci/expected/zero-prio-task.run create mode 100644 examples/zero-prio-task.rs diff --git a/ci/expected/zero-prio-task.run b/ci/expected/zero-prio-task.run new file mode 100644 index 0000000000..123b0f2687 --- /dev/null +++ b/ci/expected/zero-prio-task.run @@ -0,0 +1,3 @@ +init +hello from async +hello from async2 diff --git a/examples/zero-prio-task.rs b/examples/zero-prio-task.rs new file mode 100644 index 0000000000..fc385092c4 --- /dev/null +++ b/examples/zero-prio-task.rs @@ -0,0 +1,56 @@ +#![no_main] +#![no_std] +#![feature(type_alias_impl_trait)] + +use core::marker::PhantomData; +use panic_semihosting as _; + +pub struct NotSend { + _0: PhantomData<*const ()>, +} + +#[rtic::app(device = lm3s6965, peripherals = true)] +mod app { + use super::NotSend; + use core::marker::PhantomData; + use cortex_m_semihosting::{debug, hprintln}; + + #[shared] + struct Shared { + x: NotSend, + } + + #[local] + struct Local { + y: NotSend, + } + + #[init] + fn init(_cx: init::Context) -> (Shared, Local) { + hprintln!("init"); + + async_task::spawn().unwrap(); + async_task2::spawn().unwrap(); + + ( + Shared { + x: NotSend { _0: PhantomData }, + }, + Local { + y: NotSend { _0: PhantomData }, + }, + ) + } + + #[task(priority = 0, shared = [x], local = [y])] + async fn async_task(_: async_task::Context) { + hprintln!("hello from async"); + } + + #[task(priority = 0, shared = [x])] + async fn async_task2(_: async_task2::Context) { + hprintln!("hello from async2"); + + debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator + } +} diff --git a/macros/src/analyze.rs b/macros/src/analyze.rs index cb42ad6f2a..65774f6c4d 100644 --- a/macros/src/analyze.rs +++ b/macros/src/analyze.rs @@ -36,6 +36,7 @@ pub fn app(analysis: analyze::Analysis, app: &App) -> Analysis { let interrupts: BTreeMap = priorities .iter() + .filter(|prio| **prio > 0) // 0 prio tasks are run in main .copied() .rev() .map(|p| (p, available_interrupt.pop().expect("UNREACHABLE"))) diff --git a/macros/src/check.rs b/macros/src/check.rs index 312b84d5f0..72d0a27024 100644 --- a/macros/src/check.rs +++ b/macros/src/check.rs @@ -32,6 +32,7 @@ pub fn app(app: &App) -> parse::Result<()> { first = Some(name); task.args.priority }) + .filter(|prio| *prio > 0) .collect::>(); let need = priorities.len(); diff --git a/macros/src/codegen/async_dispatchers.rs b/macros/src/codegen/async_dispatchers.rs index 62b17fee4a..f6408e1edf 100644 --- a/macros/src/codegen/async_dispatchers.rs +++ b/macros/src/codegen/async_dispatchers.rs @@ -26,9 +26,22 @@ pub fn codegen(app: &App, analysis: &Analysis) -> TokenStream2 { for (&level, channel) in &analysis.channels { let mut stmts = vec![]; - let device = &app.args.device; - let enum_ = util::interrupt_ident(); - let interrupt = util::suffixed(&interrupts[&level].0.to_string()); + + let dispatcher_name = if level > 0 { + util::suffixed(&interrupts.get(&level).expect("UNREACHABLE").0.to_string()) + } else { + util::zero_prio_dispatcher_ident() + }; + + let pend_interrupt = if level > 0 { + let device = &app.args.device; + let enum_ = util::interrupt_ident(); + + quote!(rtic::pend(#device::#enum_::#dispatcher_name);) + } else { + // For 0 priority tasks we don't need to pend anything + quote!() + }; for name in channel.tasks.iter() { let exec_name = util::internal_task_ident(name, "EXEC"); @@ -60,40 +73,56 @@ pub fn codegen(app: &App, analysis: &Analysis) -> TokenStream2 { #executor_run_ident.store(false, core::sync::atomic::Ordering::Relaxed); if (&mut *#exec_name.get_mut()).poll(|| { #executor_run_ident.store(true, core::sync::atomic::Ordering::Release); - rtic::pend(#device::#enum_::#interrupt); + #pend_interrupt }) && #rq.load(core::sync::atomic::Ordering::Relaxed) { // If the ready queue is not empty and the executor finished, restart this // dispatch to check if the executor should be restarted. - rtic::pend(#device::#enum_::#interrupt); + #pend_interrupt } } )); } - let doc = format!( - "Interrupt handler to dispatch async tasks at priority {}", - level - ); - let attribute = &interrupts[&level].1.attrs; - items.push(quote!( - #[allow(non_snake_case)] - #[doc = #doc] - #[no_mangle] - #(#attribute)* - unsafe fn #interrupt() { - /// The priority of this interrupt handler - const PRIORITY: u8 = #level; + if level > 0 { + let doc = format!( + "Interrupt handler to dispatch async tasks at priority {}", + level + ); + let attribute = &interrupts.get(&level).expect("UNREACHABLE").1.attrs; + items.push(quote!( + #[allow(non_snake_case)] + #[doc = #doc] + #[no_mangle] + #(#attribute)* + unsafe fn #dispatcher_name() { + /// The priority of this interrupt handler + const PRIORITY: u8 = #level; - rtic::export::run(PRIORITY, || { - // Have the acquire/release semantics outside the checks to no overdo it - core::sync::atomic::fence(core::sync::atomic::Ordering::Acquire); + rtic::export::run(PRIORITY, || { + // Have the acquire/release semantics outside the checks to no overdo it + core::sync::atomic::fence(core::sync::atomic::Ordering::Acquire); - #(#stmts)* + #(#stmts)* - core::sync::atomic::fence(core::sync::atomic::Ordering::Release); - }); - } - )); + core::sync::atomic::fence(core::sync::atomic::Ordering::Release); + }); + } + )); + } else { + items.push(quote!( + #[allow(non_snake_case)] + unsafe fn #dispatcher_name() -> ! { + loop { + // Have the acquire/release semantics outside the checks to no overdo it + core::sync::atomic::fence(core::sync::atomic::Ordering::Acquire); + + #(#stmts)* + + core::sync::atomic::fence(core::sync::atomic::Ordering::Release); + } + } + )); + } } quote!(#(#items)*) diff --git a/macros/src/codegen/main.rs b/macros/src/codegen/main.rs index 90f09ae0d8..8e7138f438 100644 --- a/macros/src/codegen/main.rs +++ b/macros/src/codegen/main.rs @@ -16,11 +16,14 @@ pub fn codegen(app: &App, analysis: &Analysis) -> TokenStream2 { let name = &idle.name; quote!(#name(#name::Context::new())) } else { - // TODO: No idle defined, check for 0-priority tasks and generate an executor if needed - - quote!(loop { - rtic::export::nop() - }) + if analysis.channels.get(&0).is_some() { + let dispatcher = util::zero_prio_dispatcher_ident(); + quote!(#dispatcher();) + } else { + quote!(loop { + rtic::export::nop() + }) + } }; let main = util::suffixed("main"); diff --git a/macros/src/codegen/module.rs b/macros/src/codegen/module.rs index c6f7690fc3..70fbb5e651 100644 --- a/macros/src/codegen/module.rs +++ b/macros/src/codegen/module.rs @@ -135,13 +135,14 @@ pub fn codegen(ctxt: Context, app: &App, analysis: &Analysis) -> TokenStream2 { // Store a copy of the task cfgs task_cfgs = cfgs.clone(); - let device = &app.args.device; - let enum_ = util::interrupt_ident(); - let interrupt = &analysis - .interrupts - .get(&priority) - .expect("RTIC-ICE: interrupt identifier not found") - .0; + let pend_interrupt = if priority > 0 { + let device = &app.args.device; + let enum_ = util::interrupt_ident(); + let interrupt = &analysis.interrupts.get(&priority).expect("UREACHABLE").0; + quote!(rtic::pend(#device::#enum_::#interrupt);) + } else { + quote!() + }; let internal_spawn_ident = util::internal_task_ident(name, "spawn"); @@ -160,7 +161,7 @@ pub fn codegen(ctxt: Context, app: &App, analysis: &Analysis) -> TokenStream2 { Err(()) } else { #rq.store(true, core::sync::atomic::Ordering::Release); - rtic::pend(#device::#enum_::#interrupt); + #pend_interrupt Ok(()) } } diff --git a/macros/src/codegen/util.rs b/macros/src/codegen/util.rs index a071ca279d..6552839f76 100644 --- a/macros/src/codegen/util.rs +++ b/macros/src/codegen/util.rs @@ -187,6 +187,10 @@ pub fn need_to_lock_ident(name: &Ident) -> Ident { Ident::new(&format!("{}_that_needs_to_be_locked", name), name.span()) } +pub fn zero_prio_dispatcher_ident() -> Ident { + Ident::new("__rtic_internal_async_0_prio_dispatcher", Span::call_site()) +} + /// The name to get better RT flag errors pub fn rt_err_ident() -> Ident { Ident::new( diff --git a/macros/src/syntax/analyze.rs b/macros/src/syntax/analyze.rs index dd5a9b40d2..b70ceb8b38 100644 --- a/macros/src/syntax/analyze.rs +++ b/macros/src/syntax/analyze.rs @@ -248,33 +248,34 @@ pub(crate) fn app(app: &App) -> Result { } } - // Most shared resources need to be `Send` + // Most shared resources need to be `Send`, only 0 prio does not need it let mut send_types = SendTypes::new(); - let owned_by_idle = Ownership::Owned { priority: 0 }; + for (name, res) in app.shared_resources.iter() { - // Handle not owned by idle if ownerships .get(name) - .map(|ownership| *ownership != owned_by_idle) + .map(|ownership| match *ownership { + Ownership::Owned { priority: ceiling } + | Ownership::CoOwned { priority: ceiling } + | Ownership::Contended { ceiling } => ceiling != 0, + }) .unwrap_or(false) { send_types.insert(res.ty.clone()); } } - // Most local resources need to be `Send` as well + // Most local resources need to be `Send` as well, only 0 prio does not need it for (name, res) in app.local_resources.iter() { - if let Some(idle) = &app.idle { - // Only Send if not in idle or not at idle prio - if idle.args.local_resources.get(name).is_none() - && !ownerships - .get(name) - .map(|ownership| *ownership != owned_by_idle) - .unwrap_or(false) - { - send_types.insert(res.ty.clone()); - } - } else { + if ownerships + .get(name) + .map(|ownership| match *ownership { + Ownership::Owned { priority: ceiling } + | Ownership::CoOwned { priority: ceiling } + | Ownership::Contended { ceiling } => ceiling != 0, + }) + .unwrap_or(false) + { send_types.insert(res.ty.clone()); } } From c40c89bb4edc22c4a60d8677c660a9ab7eb47e92 Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Sun, 8 Jan 2023 21:30:53 +0100 Subject: [PATCH 243/423] Clippy fixes --- build.rs | 3 +-- macros/src/check.rs | 3 +-- macros/src/codegen/async_dispatchers.rs | 3 +-- macros/src/codegen/main.rs | 14 ++++++-------- macros/src/codegen/pre_init.rs | 6 ++---- macros/src/codegen/util.rs | 16 ++++++++-------- macros/src/lib.rs | 7 +++---- macros/src/syntax/parse/app.rs | 6 ++---- macros/src/syntax/parse/hardware_task.rs | 10 ++++------ macros/src/syntax/parse/idle.rs | 5 ++--- macros/src/syntax/parse/init.rs | 5 ++--- macros/src/syntax/parse/software_task.rs | 10 ++++------ macros/src/syntax/parse/util.rs | 14 +++++--------- src/export.rs | 4 ++-- 14 files changed, 43 insertions(+), 63 deletions(-) diff --git a/build.rs b/build.rs index ff9ebe35e7..38d2c1866d 100644 --- a/build.rs +++ b/build.rs @@ -19,8 +19,7 @@ fn main() { && !(target.starts_with("thumbv6m") | target.starts_with("thumbv8m.base")) { panic!( - "Unknown target '{}'. Need to update BASEPRI logic in build.rs.", - target + "Unknown target '{target}'. Need to update BASEPRI logic in build.rs." ); } diff --git a/macros/src/check.rs b/macros/src/check.rs index 72d0a27024..a05c82e8a4 100644 --- a/macros/src/check.rs +++ b/macros/src/check.rs @@ -41,8 +41,7 @@ pub fn app(app: &App) -> parse::Result<()> { let s = { format!( "not enough interrupts to dispatch \ - all software tasks (need: {}; given: {})", - need, given + all software tasks (need: {need}; given: {given})" ) }; diff --git a/macros/src/codegen/async_dispatchers.rs b/macros/src/codegen/async_dispatchers.rs index f6408e1edf..be02ad09a1 100644 --- a/macros/src/codegen/async_dispatchers.rs +++ b/macros/src/codegen/async_dispatchers.rs @@ -85,8 +85,7 @@ pub fn codegen(app: &App, analysis: &Analysis) -> TokenStream2 { if level > 0 { let doc = format!( - "Interrupt handler to dispatch async tasks at priority {}", - level + "Interrupt handler to dispatch async tasks at priority {level}" ); let attribute = &interrupts.get(&level).expect("UNREACHABLE").1.attrs; items.push(quote!( diff --git a/macros/src/codegen/main.rs b/macros/src/codegen/main.rs index 8e7138f438..2775d259cf 100644 --- a/macros/src/codegen/main.rs +++ b/macros/src/codegen/main.rs @@ -15,15 +15,13 @@ pub fn codegen(app: &App, analysis: &Analysis) -> TokenStream2 { let call_idle = if let Some(idle) = &app.idle { let name = &idle.name; quote!(#name(#name::Context::new())) + } else if analysis.channels.get(&0).is_some() { + let dispatcher = util::zero_prio_dispatcher_ident(); + quote!(#dispatcher();) } else { - if analysis.channels.get(&0).is_some() { - let dispatcher = util::zero_prio_dispatcher_ident(); - quote!(#dispatcher();) - } else { - quote!(loop { - rtic::export::nop() - }) - } + quote!(loop { + rtic::export::nop() + }) }; let main = util::suffixed("main"); diff --git a/macros/src/codegen/pre_init.rs b/macros/src/codegen/pre_init.rs index 14926888ab..28ba29c0e6 100644 --- a/macros/src/codegen/pre_init.rs +++ b/macros/src/codegen/pre_init.rs @@ -40,8 +40,7 @@ pub fn codegen(app: &App, analysis: &Analysis) -> Vec { } })) { let es = format!( - "Maximum priority used by interrupt vector '{}' is more than supported by hardware", - name + "Maximum priority used by interrupt vector '{name}' is more than supported by hardware" ); // Compile time assert that this priority is supported by the device stmts.push(quote!( @@ -69,8 +68,7 @@ pub fn codegen(app: &App, analysis: &Analysis) -> Vec { } }) { let es = format!( - "Maximum priority used by interrupt vector '{}' is more than supported by hardware", - name + "Maximum priority used by interrupt vector '{name}' is more than supported by hardware" ); // Compile time assert that this priority is supported by the device stmts.push(quote!( diff --git a/macros/src/codegen/util.rs b/macros/src/codegen/util.rs index 6552839f76..a0caf0aeef 100644 --- a/macros/src/codegen/util.rs +++ b/macros/src/codegen/util.rs @@ -51,7 +51,7 @@ pub fn impl_mutex( /// Generates an identifier for the `EXECUTOR_RUN` atomics (`async` API) pub fn executor_run_ident(task: &Ident) -> Ident { - mark_internal_name(&format!("{}_EXECUTOR_RUN", task)) + mark_internal_name(&format!("{task}_EXECUTOR_RUN")) } pub fn interrupt_ident() -> Ident { @@ -78,12 +78,12 @@ pub fn is_exception(name: &Ident) -> bool { /// Mark a name as internal pub fn mark_internal_name(name: &str) -> Ident { - Ident::new(&format!("{}_{}", RTIC_INTERNAL, name), Span::call_site()) + Ident::new(&format!("{RTIC_INTERNAL}_{name}"), Span::call_site()) } /// Generate an internal identifier for tasks pub fn internal_task_ident(task: &Ident, ident_name: &str) -> Ident { - mark_internal_name(&format!("{}_{}", task, ident_name)) + mark_internal_name(&format!("{task}_{ident_name}")) } fn link_section_index() -> usize { @@ -153,7 +153,7 @@ pub fn local_resources_ident(ctxt: Context, app: &App) -> Ident { /// Generates an identifier for a ready queue, async task version pub fn rq_async_ident(async_task_name: &Ident) -> Ident { - mark_internal_name(&format!("ASYNC_TASK_{}_RQ", async_task_name)) + mark_internal_name(&format!("ASYNC_TASK_{async_task_name}_RQ")) } /// Suffixed identifier @@ -163,7 +163,7 @@ pub fn suffixed(name: &str) -> Ident { } pub fn static_shared_resource_ident(name: &Ident) -> Ident { - mark_internal_name(&format!("shared_resource_{}", name)) + mark_internal_name(&format!("shared_resource_{name}")) } /// Generates an Ident for the number of 32 bit chunks used for Mask storage. @@ -176,15 +176,15 @@ pub fn priority_masks_ident() -> Ident { } pub fn static_local_resource_ident(name: &Ident) -> Ident { - mark_internal_name(&format!("local_resource_{}", name)) + mark_internal_name(&format!("local_resource_{name}")) } pub fn declared_static_local_resource_ident(name: &Ident, task_name: &Ident) -> Ident { - mark_internal_name(&format!("local_{}_{}", task_name, name)) + mark_internal_name(&format!("local_{task_name}_{name}")) } pub fn need_to_lock_ident(name: &Ident) -> Ident { - Ident::new(&format!("{}_that_needs_to_be_locked", name), name.span()) + Ident::new(&format!("{name}_that_needs_to_be_locked"), name.span()) } pub fn zero_prio_dispatcher_ident() -> Ident { diff --git a/macros/src/lib.rs b/macros/src/lib.rs index 34f2bb619b..a8422d0927 100644 --- a/macros/src/lib.rs +++ b/macros/src/lib.rs @@ -39,9 +39,8 @@ pub fn app(args: TokenStream, input: TokenStream) -> TokenStream { Ok(x) => x, }; - match check::app(&app) { - Err(e) => return e.to_compile_error().into(), - _ => {} + if let Err(e) = check::app(&app) { + return e.to_compile_error().into(); } let analysis = analyze::app(analysis, &app); @@ -86,7 +85,7 @@ pub fn app(args: TokenStream, input: TokenStream) -> TokenStream { // Try to write the expanded code to disk if let Some(out_str) = out_dir.to_str() { - fs::write(format!("{}/rtic-expansion.rs", out_str), ts.to_string()).ok(); + fs::write(format!("{out_str}/rtic-expansion.rs"), ts.to_string()).ok(); } ts.into() diff --git a/macros/src/syntax/parse/app.rs b/macros/src/syntax/parse/app.rs index 8a9242e91d..e797f75e37 100644 --- a/macros/src/syntax/parse/app.rs +++ b/macros/src/syntax/parse/app.rs @@ -450,8 +450,7 @@ impl App { return Err(parse::Error::new( init.user_shared_struct.span(), format!( - "This name and the one defined on `#[shared]` are not the same. Should this be `{}`?", - shared_resources_ident + "This name and the one defined on `#[shared]` are not the same. Should this be `{shared_resources_ident}`?" ), )); } @@ -460,8 +459,7 @@ impl App { return Err(parse::Error::new( init.user_local_struct.span(), format!( - "This name and the one defined on `#[local]` are not the same. Should this be `{}`?", - local_resources_ident + "This name and the one defined on `#[local]` are not the same. Should this be `{local_resources_ident}`?" ), )); } diff --git a/macros/src/syntax/parse/hardware_task.rs b/macros/src/syntax/parse/hardware_task.rs index ff94bc5190..6207e564f5 100644 --- a/macros/src/syntax/parse/hardware_task.rs +++ b/macros/src/syntax/parse/hardware_task.rs @@ -39,9 +39,8 @@ impl HardwareTask { Err(parse::Error::new( span, - &format!( - "this task handler must have type signature `fn({}::Context)`", - name + format!( + "this task handler must have type signature `fn({name}::Context)`" ), )) } @@ -83,9 +82,8 @@ impl HardwareTask { Err(parse::Error::new( span, - &format!( - "this task handler must have type signature `fn({}::Context)`", - name + format!( + "this task handler must have type signature `fn({name}::Context)`" ), )) } diff --git a/macros/src/syntax/parse/idle.rs b/macros/src/syntax/parse/idle.rs index ffec358fc4..aa2ef5e910 100644 --- a/macros/src/syntax/parse/idle.rs +++ b/macros/src/syntax/parse/idle.rs @@ -34,9 +34,8 @@ impl Idle { Err(parse::Error::new( item.sig.ident.span(), - &format!( - "this `#[idle]` function must have signature `fn({}::Context) -> !`", - name + format!( + "this `#[idle]` function must have signature `fn({name}::Context) -> !`" ), )) } diff --git a/macros/src/syntax/parse/init.rs b/macros/src/syntax/parse/init.rs index 61d35391fb..23130c85ac 100644 --- a/macros/src/syntax/parse/init.rs +++ b/macros/src/syntax/parse/init.rs @@ -41,9 +41,8 @@ impl Init { Err(parse::Error::new( span, - &format!( - "the `#[init]` function must have signature `fn({}::Context) -> (Shared resources struct, Local resources struct)`", - name + format!( + "the `#[init]` function must have signature `fn({name}::Context) -> (Shared resources struct, Local resources struct)`" ), )) } diff --git a/macros/src/syntax/parse/software_task.rs b/macros/src/syntax/parse/software_task.rs index 6be597e8fb..319620a4b0 100644 --- a/macros/src/syntax/parse/software_task.rs +++ b/macros/src/syntax/parse/software_task.rs @@ -33,9 +33,8 @@ impl SoftwareTask { Err(parse::Error::new( span, - &format!( - "this task handler must have type signature `async fn({}::Context)`", - name + format!( + "this task handler must have type signature `async fn({name}::Context)`" ), )) } @@ -71,9 +70,8 @@ impl SoftwareTask { Err(parse::Error::new( span, - &format!( - "this task handler must have type signature `async fn({}::Context)`", - name + format!( + "this task handler must have type signature `async fn({name}::Context)`" ), )) } diff --git a/macros/src/syntax/parse/util.rs b/macros/src/syntax/parse/util.rs index 28c3eac6b6..900ef9d66f 100644 --- a/macros/src/syntax/parse/util.rs +++ b/macros/src/syntax/parse/util.rs @@ -234,17 +234,13 @@ pub fn parse_local_resources(content: ParseStream<'_>) -> parse::Result, name: &str) -> Option> { let mut inputs = inputs.into_iter(); - match inputs.next() { - Some(FnArg::Typed(first)) => { - if type_is_path(&first.ty, &[name, "Context"]) { - // No more inputs - if inputs.next().is_none() { - return Some(first.pat); - } + if let Some(FnArg::Typed(first)) = inputs.next() { + if type_is_path(&first.ty, &[name, "Context"]) { + // No more inputs + if inputs.next().is_none() { + return Some(first.pat); } } - - _ => {} } None diff --git a/src/export.rs b/src/export.rs index bfd0f6dda1..091cfb87ce 100644 --- a/src/export.rs +++ b/src/export.rs @@ -298,9 +298,9 @@ pub unsafe fn lock( if ceiling >= 4 { // safe to manipulate outside critical section // execute closure under protection of raised system ceiling - let r = interrupt::free(|_| f(&mut *ptr)); + // safe to manipulate outside critical section - r + interrupt::free(|_| f(&mut *ptr)) } else { // safe to manipulate outside critical section let mask = compute_mask(0, ceiling, masks); From 95e494968053a17ac05a0c1cec9d8b2c7d450296 Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Sun, 8 Jan 2023 21:33:44 +0100 Subject: [PATCH 244/423] Start CI, disable docs building --- .github/workflows/build.yml | 552 +++++++++++------------ build.rs | 4 +- macros/src/codegen/async_dispatchers.rs | 4 +- macros/src/syntax/parse/hardware_task.rs | 8 +- macros/src/syntax/parse/idle.rs | 4 +- macros/src/syntax/parse/software_task.rs | 8 +- src/export.rs | 2 +- ui/exception-invalid.rs | 4 +- ui/extern-interrupt-not-enough.rs | 6 +- ui/extern-interrupt-not-enough.stderr | 6 +- ui/extern-interrupt-used.rs | 4 +- ui/task-priority-too-high.rs | 4 +- ui/task-priority-too-high.stderr | 2 +- ui/unknown-interrupt.rs | 4 +- 14 files changed, 299 insertions(+), 313 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 5e1467ca05..35c0bffa4e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -39,7 +39,7 @@ jobs: - thumbv6m-none-eabi - x86_64-unknown-linux-gnu toolchain: - - stable + - nightly steps: - name: Checkout uses: actions/checkout@v3 @@ -93,7 +93,7 @@ jobs: - thumbv8m.base-none-eabi - thumbv8m.main-none-eabi toolchain: - - stable + - nightly steps: - name: Checkout uses: actions/checkout@v3 @@ -125,7 +125,7 @@ jobs: - thumbv7m-none-eabi - thumbv6m-none-eabi toolchain: - - stable + - nightly steps: - name: Checkout uses: actions/checkout@v3 @@ -168,7 +168,7 @@ jobs: target: - x86_64-unknown-linux-gnu toolchain: - - stable + - nightly steps: - name: Checkout uses: actions/checkout@v3 @@ -224,276 +224,276 @@ jobs: - name: Run cargo test run: cargo test --test tests - # Build documentation, check links - docs: - name: docs - runs-on: ubuntu-22.04 - steps: - - name: Checkout - uses: actions/checkout@v3 - - - name: Cache pip installed linkchecker - uses: actions/cache@v3 - with: - path: ~/.cache/pip - key: ${{ runner.os }}-pip - restore-keys: | - ${{ runner.os }}-pip- - - - name: Set up Python 3.x - uses: actions/setup-python@v4 - with: - # Semantic version range syntax or exact version of a Python version - python-version: '3.x' - - # You can test your matrix by printing the current Python version - - name: Display Python version - run: python -c "import sys; print(sys.version)" - - - name: Install dependencies - run: pip install git+https://github.com/linkchecker/linkchecker.git - - - name: Remove cargo-config - run: rm -f .cargo/config - - - name: Fail on warnings - run: sed -i 's,//deny_warnings_placeholder_for_ci,#![deny(warnings)],' src/lib.rs macros/src/lib.rs - - - name: Build docs - run: cargo doc - - - name: Check links - run: | - td=$(mktemp -d) - cp -r target/doc $td/api - linkchecker $td/api/rtic/ - linkchecker $td/api/cortex_m_rtic_macros/ - - # Build the books - mdbook: - name: mdbook - runs-on: ubuntu-22.04 - steps: - - name: Checkout - uses: actions/checkout@v3 - - name: Set up Python 3.x - uses: actions/setup-python@v4 - with: - # Semantic version range syntax or exact version of a Python version - python-version: '3.x' - - # You can test your matrix by printing the current Python version - - name: Display Python version - run: python -c "import sys; print(sys.version)" - - - name: Install dependencies - run: pip install git+https://github.com/linkchecker/linkchecker.git - - - name: mdBook Action - uses: peaceiris/actions-mdbook@v1 - with: - mdbook-version: 'latest' - - - name: Build book in English - shell: 'script --return --quiet --command "bash {0}"' - run: cd book/en && if mdbook build |& tee /dev/tty | grep "\[ERROR\]"; then exit 1; else exit 0; fi - - - name: Build book in Russian - shell: 'script --return --quiet --command "bash {0}"' - run: cd book/ru && if mdbook build |& tee /dev/tty | grep "\[ERROR\]"; then echo "Russian book needs updating!"; else exit 0; fi - - - name: Check links - run: | - td=$(mktemp -d) - mkdir $td/book - cp -r book/en/book $td/book/en - cp -r book/ru/book $td/book/ru - cp LICENSE-* $td/book/en - cp LICENSE-* $td/book/ru - - linkchecker $td/book/en/ - linkchecker $td/book/ru/ - - # Update stable branch - # - # This needs to run before book is built - mergetostablebranch: - name: If CI passes, merge master branch into release/vX - runs-on: ubuntu-22.04 - needs: - - style - - check - - clippy - - checkexamples - - testexamples - - checkmacros - - testmacros - - tests - - docs - - mdbook - - # Only run this when pushing to master branch - if: github.ref == 'refs/heads/master' - steps: - - uses: actions/checkout@v3 - - - name: Get crate version and print output branch release/vX - id: crateversionbranch - # Parse metadata for version number, extract the Semver Major - run: | - VERSION=$(cargo metadata --format-version 1 --no-deps --offline | jq -r '.packages[] | select(.name =="cortex-m-rtic") | .version') - VERSIONMAJOR=${VERSION%.*.*} - echo "branch=release/v$VERSIONMAJOR" >> $GITHUB_ENV - echo "versionmajor=$VERSIONMAJOR" >> $GITHUB_ENV - echo "version=$VERSION" >> $GITHUB_ENV - - - uses: everlytic/branch-merge@1.1.5 - with: - github_token: ${{ github.token }} - source_ref: 'master' - target_branch: ${{ env.branch }} - commit_message_template: '[Bors] Merged {source_ref} into target {target_branch}' - - # Only runs when pushing to master branch - # Bors run CI against staging branch, - # if that succeeds Borst tries against master branch - # If all tests pass, then deploy stage is run - deploy: - name: deploy - runs-on: ubuntu-22.04 - needs: - mergetostablebranch - - # Only run this when pushing to master branch - if: github.ref == 'refs/heads/master' - steps: - - uses: actions/checkout@v3 - - - name: Set up Python 3.x - uses: actions/setup-python@v4 - with: - # Semantic version range syntax or exact version of a Python version - python-version: '3.x' - - # You can test your matrix by printing the current Python version - - name: Display Python version - run: python -c "import sys; print(sys.version)" - - - name: mdBook Action - uses: peaceiris/actions-mdbook@v1 - with: - mdbook-version: 'latest' - - - name: Get crate version - id: crateversion - # Parse metadata for version number, extract the Semver Major - run: | - VERSION=$(cargo metadata --format-version 1 --no-deps --offline | jq -r '.packages[] | select(.name =="cortex-m-rtic") | .version') - VERSIONMAJOR=${VERSION%.*.*} - echo "branch=release/v$VERSIONMAJOR" >> $GITHUB_ENV - echo "versionmajor=$VERSIONMAJOR" >> $GITHUB_ENV - echo "version=$VERSION" >> $GITHUB_ENV - - - name: Remove cargo-config - run: rm -f .cargo/config - - - name: Build docs - run: cargo doc - - - name: Build books - shell: 'script --return --quiet --command "bash {0}"' - run: | - langs=( en ru ) - devver=( dev ) - # The latest stable must be the first element in the array - vers=( "1" "0.5" "0.4" ) - - # All releases start with "v" - # followed by MAJOR.MINOR.PATCH, see semver.org - # Store first in array as stable - stable=${vers} - crateversion={{ env.versionmajor }} - - echo "Latest stable version: $stable" - echo "Current crate version: $crateversion" - - # Create directories - td=$(mktemp -d) - mkdir -p $td/$devver/book/ - cp -r target/doc $td/$devver/api - - # Redirect rtic.rs/meeting/index.html to hackmd - mkdir $td/meeting - sed "s|URL|https://hackmd.io/c_mFUZL-Q2C6614MlrrxOg|g" redirect.html > $td/meeting/index.html - sed -i "s|Page Redirection|RTIC Meeting|" $td/meeting/index.html - sed -i "s|If you|Redirecting to RTIC HackMD. If you|" $td/meeting/index.html - - # Redirect the main site to the stable release - sed "s|URL|$stable|g" redirect.html > $td/index.html - - # Create the redirects for dev-version - # If the current stable and the version being built differ, - # then there is a dev-version and the links should point to it. - if [[ "$stable" != "$crateversion" ]]; - then - sed 's|URL|rtic/index.html|g' redirect.html > $td/$devver/api/index.html - sed 's|URL|book/en|g' redirect.html > $td/$devver/index.html - else - # If the current stable and the "dev" version in master branch - # share the same major version, redirect dev/ to stable book - sed 's|URL|rtic.rs/$stable/api/rtic|g' redirect.html > $td/$devver/api/index.html - sed 's|URL|rtic.rs/$stable|g' redirect.html > $td/$devver/index.html - fi - - # Build books - for lang in ${langs[@]}; do - ( cd book/$lang && - if mdbook build |& tee /dev/tty | grep "\[ERROR\]"; then exit 1; else exit 0; fi - ) - cp -r book/$lang/book $td/$devver/book/$lang - cp LICENSE-* $td/$devver/book/$lang/ - done - - # Build older versions, including stable - root=$(pwd) - for ver in ${vers[@]}; do - prefix=${ver} - - mkdir -p $td/$prefix/book - src=$(mktemp -d) - curl -L https://github.com/rtic-rs/cortex-m-rtic/archive/release/v${ver}.tar.gz | tar xz --strip-components 1 -C $src - - pushd $src - rm -f .cargo/config - cargo doc || cargo doc --features timer-queue - cp -r target/doc $td/$prefix/api - sed 's|URL|rtic/index.html|g' $root/redirect.html > $td/$prefix/api/index.html - for lang in ${langs[@]}; do - ( cd book/$lang && - if mdbook build |& tee /dev/tty | grep "\[ERROR\]"; then exit 1; else exit 0; fi - ) - cp -r book/$lang/book $td/$prefix/book/$lang - cp LICENSE-* $td/$prefix/book/$lang/ - done - sed 's|URL|book/en|g' $root/redirect.html > $td/$prefix/index.html - popd - - rm -rf $src - done - - # Copy the stable book to the stable alias - cp -r $td/$stable $td/stable - - # Forward CNAME file - cp CNAME $td/ - mv $td/ bookstodeploy - - - name: Deploy to GH-pages - uses: peaceiris/actions-gh-pages@v3 - with: - github_token: ${{ secrets.GITHUB_TOKEN }} - publish_dir: ./bookstodeploy - force_orphan: true +# # Build documentation, check links +# docs: +# name: docs +# runs-on: ubuntu-22.04 +# steps: +# - name: Checkout +# uses: actions/checkout@v3 +# +# - name: Cache pip installed linkchecker +# uses: actions/cache@v3 +# with: +# path: ~/.cache/pip +# key: ${{ runner.os }}-pip +# restore-keys: | +# ${{ runner.os }}-pip- +# +# - name: Set up Python 3.x +# uses: actions/setup-python@v4 +# with: +# # Semantic version range syntax or exact version of a Python version +# python-version: '3.x' +# +# # You can test your matrix by printing the current Python version +# - name: Display Python version +# run: python -c "import sys; print(sys.version)" +# +# - name: Install dependencies +# run: pip install git+https://github.com/linkchecker/linkchecker.git +# +# - name: Remove cargo-config +# run: rm -f .cargo/config +# +# - name: Fail on warnings +# run: sed -i 's,//deny_warnings_placeholder_for_ci,#![deny(warnings)],' src/lib.rs macros/src/lib.rs +# +# - name: Build docs +# run: cargo doc +# +# - name: Check links +# run: | +# td=$(mktemp -d) +# cp -r target/doc $td/api +# linkchecker $td/api/rtic/ +# linkchecker $td/api/cortex_m_rtic_macros/ +# +# # Build the books +# mdbook: +# name: mdbook +# runs-on: ubuntu-22.04 +# steps: +# - name: Checkout +# uses: actions/checkout@v3 +# - name: Set up Python 3.x +# uses: actions/setup-python@v4 +# with: +# # Semantic version range syntax or exact version of a Python version +# python-version: '3.x' +# +# # You can test your matrix by printing the current Python version +# - name: Display Python version +# run: python -c "import sys; print(sys.version)" +# +# - name: Install dependencies +# run: pip install git+https://github.com/linkchecker/linkchecker.git +# +# - name: mdBook Action +# uses: peaceiris/actions-mdbook@v1 +# with: +# mdbook-version: 'latest' +# +# - name: Build book in English +# shell: 'script --return --quiet --command "bash {0}"' +# run: cd book/en && if mdbook build |& tee /dev/tty | grep "\[ERROR\]"; then exit 1; else exit 0; fi +# +# - name: Build book in Russian +# shell: 'script --return --quiet --command "bash {0}"' +# run: cd book/ru && if mdbook build |& tee /dev/tty | grep "\[ERROR\]"; then echo "Russian book needs updating!"; else exit 0; fi +# +# - name: Check links +# run: | +# td=$(mktemp -d) +# mkdir $td/book +# cp -r book/en/book $td/book/en +# cp -r book/ru/book $td/book/ru +# cp LICENSE-* $td/book/en +# cp LICENSE-* $td/book/ru +# +# linkchecker $td/book/en/ +# linkchecker $td/book/ru/ +# +# # Update stable branch +# # +# # This needs to run before book is built +# mergetostablebranch: +# name: If CI passes, merge master branch into release/vX +# runs-on: ubuntu-22.04 +# needs: +# - style +# - check +# - clippy +# - checkexamples +# - testexamples +# - checkmacros +# - testmacros +# - tests +# - docs +# - mdbook +# +# # Only run this when pushing to master branch +# if: github.ref == 'refs/heads/master' +# steps: +# - uses: actions/checkout@v3 +# +# - name: Get crate version and print output branch release/vX +# id: crateversionbranch +# # Parse metadata for version number, extract the Semver Major +# run: | +# VERSION=$(cargo metadata --format-version 1 --no-deps --offline | jq -r '.packages[] | select(.name =="cortex-m-rtic") | .version') +# VERSIONMAJOR=${VERSION%.*.*} +# echo "branch=release/v$VERSIONMAJOR" >> $GITHUB_ENV +# echo "versionmajor=$VERSIONMAJOR" >> $GITHUB_ENV +# echo "version=$VERSION" >> $GITHUB_ENV +# +# - uses: everlytic/branch-merge@1.1.5 +# with: +# github_token: ${{ github.token }} +# source_ref: 'master' +# target_branch: ${{ env.branch }} +# commit_message_template: '[Bors] Merged {source_ref} into target {target_branch}' +# +# # Only runs when pushing to master branch +# # Bors run CI against staging branch, +# # if that succeeds Borst tries against master branch +# # If all tests pass, then deploy stage is run +# deploy: +# name: deploy +# runs-on: ubuntu-22.04 +# needs: +# mergetostablebranch +# +# # Only run this when pushing to master branch +# if: github.ref == 'refs/heads/master' +# steps: +# - uses: actions/checkout@v3 +# +# - name: Set up Python 3.x +# uses: actions/setup-python@v4 +# with: +# # Semantic version range syntax or exact version of a Python version +# python-version: '3.x' +# +# # You can test your matrix by printing the current Python version +# - name: Display Python version +# run: python -c "import sys; print(sys.version)" +# +# - name: mdBook Action +# uses: peaceiris/actions-mdbook@v1 +# with: +# mdbook-version: 'latest' +# +# - name: Get crate version +# id: crateversion +# # Parse metadata for version number, extract the Semver Major +# run: | +# VERSION=$(cargo metadata --format-version 1 --no-deps --offline | jq -r '.packages[] | select(.name =="cortex-m-rtic") | .version') +# VERSIONMAJOR=${VERSION%.*.*} +# echo "branch=release/v$VERSIONMAJOR" >> $GITHUB_ENV +# echo "versionmajor=$VERSIONMAJOR" >> $GITHUB_ENV +# echo "version=$VERSION" >> $GITHUB_ENV +# +# - name: Remove cargo-config +# run: rm -f .cargo/config +# +# - name: Build docs +# run: cargo doc +# +# - name: Build books +# shell: 'script --return --quiet --command "bash {0}"' +# run: | +# langs=( en ru ) +# devver=( dev ) +# # The latest stable must be the first element in the array +# vers=( "1" "0.5" "0.4" ) +# +# # All releases start with "v" +# # followed by MAJOR.MINOR.PATCH, see semver.org +# # Store first in array as stable +# stable=${vers} +# crateversion={{ env.versionmajor }} +# +# echo "Latest stable version: $stable" +# echo "Current crate version: $crateversion" +# +# # Create directories +# td=$(mktemp -d) +# mkdir -p $td/$devver/book/ +# cp -r target/doc $td/$devver/api +# +# # Redirect rtic.rs/meeting/index.html to hackmd +# mkdir $td/meeting +# sed "s|URL|https://hackmd.io/c_mFUZL-Q2C6614MlrrxOg|g" redirect.html > $td/meeting/index.html +# sed -i "s|Page Redirection|RTIC Meeting|" $td/meeting/index.html +# sed -i "s|If you|Redirecting to RTIC HackMD. If you|" $td/meeting/index.html +# +# # Redirect the main site to the stable release +# sed "s|URL|$stable|g" redirect.html > $td/index.html +# +# # Create the redirects for dev-version +# # If the current stable and the version being built differ, +# # then there is a dev-version and the links should point to it. +# if [[ "$stable" != "$crateversion" ]]; +# then +# sed 's|URL|rtic/index.html|g' redirect.html > $td/$devver/api/index.html +# sed 's|URL|book/en|g' redirect.html > $td/$devver/index.html +# else +# # If the current stable and the "dev" version in master branch +# # share the same major version, redirect dev/ to stable book +# sed 's|URL|rtic.rs/$stable/api/rtic|g' redirect.html > $td/$devver/api/index.html +# sed 's|URL|rtic.rs/$stable|g' redirect.html > $td/$devver/index.html +# fi +# +# # Build books +# for lang in ${langs[@]}; do +# ( cd book/$lang && +# if mdbook build |& tee /dev/tty | grep "\[ERROR\]"; then exit 1; else exit 0; fi +# ) +# cp -r book/$lang/book $td/$devver/book/$lang +# cp LICENSE-* $td/$devver/book/$lang/ +# done +# +# # Build older versions, including stable +# root=$(pwd) +# for ver in ${vers[@]}; do +# prefix=${ver} +# +# mkdir -p $td/$prefix/book +# src=$(mktemp -d) +# curl -L https://github.com/rtic-rs/cortex-m-rtic/archive/release/v${ver}.tar.gz | tar xz --strip-components 1 -C $src +# +# pushd $src +# rm -f .cargo/config +# cargo doc || cargo doc --features timer-queue +# cp -r target/doc $td/$prefix/api +# sed 's|URL|rtic/index.html|g' $root/redirect.html > $td/$prefix/api/index.html +# for lang in ${langs[@]}; do +# ( cd book/$lang && +# if mdbook build |& tee /dev/tty | grep "\[ERROR\]"; then exit 1; else exit 0; fi +# ) +# cp -r book/$lang/book $td/$prefix/book/$lang +# cp LICENSE-* $td/$prefix/book/$lang/ +# done +# sed 's|URL|book/en|g' $root/redirect.html > $td/$prefix/index.html +# popd +# +# rm -rf $src +# done +# +# # Copy the stable book to the stable alias +# cp -r $td/$stable $td/stable +# +# # Forward CNAME file +# cp CNAME $td/ +# mv $td/ bookstodeploy +# +# - name: Deploy to GH-pages +# uses: peaceiris/actions-gh-pages@v3 +# with: +# github_token: ${{ secrets.GITHUB_TOKEN }} +# publish_dir: ./bookstodeploy +# force_orphan: true # Refs: https://github.com/rust-lang/crater/blob/9ab6f9697c901c4a44025cf0a39b73ad5b37d198/.github/workflows/bors.yml#L125-L149 # @@ -511,8 +511,8 @@ jobs: - checkmacros - testmacros - tests - - docs - - mdbook +# - docs +# - mdbook runs-on: ubuntu-22.04 steps: - name: Mark the job as a success diff --git a/build.rs b/build.rs index 38d2c1866d..35f8303f5c 100644 --- a/build.rs +++ b/build.rs @@ -18,9 +18,7 @@ fn main() { } else if target.starts_with("thumb") && !(target.starts_with("thumbv6m") | target.starts_with("thumbv8m.base")) { - panic!( - "Unknown target '{target}'. Need to update BASEPRI logic in build.rs." - ); + panic!("Unknown target '{target}'. Need to update BASEPRI logic in build.rs."); } println!("cargo:rerun-if-changed=build.rs"); diff --git a/macros/src/codegen/async_dispatchers.rs b/macros/src/codegen/async_dispatchers.rs index be02ad09a1..341f76ff6f 100644 --- a/macros/src/codegen/async_dispatchers.rs +++ b/macros/src/codegen/async_dispatchers.rs @@ -84,9 +84,7 @@ pub fn codegen(app: &App, analysis: &Analysis) -> TokenStream2 { } if level > 0 { - let doc = format!( - "Interrupt handler to dispatch async tasks at priority {level}" - ); + let doc = format!("Interrupt handler to dispatch async tasks at priority {level}"); let attribute = &interrupts.get(&level).expect("UNREACHABLE").1.attrs; items.push(quote!( #[allow(non_snake_case)] diff --git a/macros/src/syntax/parse/hardware_task.rs b/macros/src/syntax/parse/hardware_task.rs index 6207e564f5..c13426f4a3 100644 --- a/macros/src/syntax/parse/hardware_task.rs +++ b/macros/src/syntax/parse/hardware_task.rs @@ -39,9 +39,7 @@ impl HardwareTask { Err(parse::Error::new( span, - format!( - "this task handler must have type signature `fn({name}::Context)`" - ), + format!("this task handler must have type signature `fn({name}::Context)`"), )) } } @@ -82,9 +80,7 @@ impl HardwareTask { Err(parse::Error::new( span, - format!( - "this task handler must have type signature `fn({name}::Context)`" - ), + format!("this task handler must have type signature `fn({name}::Context)`"), )) } } diff --git a/macros/src/syntax/parse/idle.rs b/macros/src/syntax/parse/idle.rs index aa2ef5e910..f049cca85e 100644 --- a/macros/src/syntax/parse/idle.rs +++ b/macros/src/syntax/parse/idle.rs @@ -34,9 +34,7 @@ impl Idle { Err(parse::Error::new( item.sig.ident.span(), - format!( - "this `#[idle]` function must have signature `fn({name}::Context) -> !`" - ), + format!("this `#[idle]` function must have signature `fn({name}::Context) -> !`"), )) } } diff --git a/macros/src/syntax/parse/software_task.rs b/macros/src/syntax/parse/software_task.rs index 319620a4b0..fb9b37c442 100644 --- a/macros/src/syntax/parse/software_task.rs +++ b/macros/src/syntax/parse/software_task.rs @@ -33,9 +33,7 @@ impl SoftwareTask { Err(parse::Error::new( span, - format!( - "this task handler must have type signature `async fn({name}::Context)`" - ), + format!("this task handler must have type signature `async fn({name}::Context)`"), )) } } @@ -70,9 +68,7 @@ impl SoftwareTask { Err(parse::Error::new( span, - format!( - "this task handler must have type signature `async fn({name}::Context)`" - ), + format!("this task handler must have type signature `async fn({name}::Context)`"), )) } } diff --git a/src/export.rs b/src/export.rs index 091cfb87ce..7beaf1631d 100644 --- a/src/export.rs +++ b/src/export.rs @@ -298,7 +298,7 @@ pub unsafe fn lock( if ceiling >= 4 { // safe to manipulate outside critical section // execute closure under protection of raised system ceiling - + // safe to manipulate outside critical section interrupt::free(|_| f(&mut *ptr)) } else { diff --git a/ui/exception-invalid.rs b/ui/exception-invalid.rs index 07d3c21f54..4f8e943ec2 100644 --- a/ui/exception-invalid.rs +++ b/ui/exception-invalid.rs @@ -9,8 +9,8 @@ mod app { struct Local {} #[init] - fn init(cx: init::Context) -> (Shared, Local, init::Monotonics) { - (Shared {}, Local {}, init::Monotonics()) + fn init(cx: init::Context) -> (Shared, Local) { + (Shared {}, Local {}) } #[task(binds = NonMaskableInt)] diff --git a/ui/extern-interrupt-not-enough.rs b/ui/extern-interrupt-not-enough.rs index 1dbe923c98..94c8ee116a 100644 --- a/ui/extern-interrupt-not-enough.rs +++ b/ui/extern-interrupt-not-enough.rs @@ -9,10 +9,10 @@ mod app { struct Local {} #[init] - fn init(cx: init::Context) -> (Shared, Local, init::Monotonics) { - (Shared {}, Local {}, init::Monotonics()) + fn init(cx: init::Context) -> (Shared, Local) { + (Shared {}, Local {}) } #[task] - fn a(_: a::Context) {} + async fn a(_: a::Context) {} } diff --git a/ui/extern-interrupt-not-enough.stderr b/ui/extern-interrupt-not-enough.stderr index 6f28b7ad00..e6c01b99e4 100644 --- a/ui/extern-interrupt-not-enough.stderr +++ b/ui/extern-interrupt-not-enough.stderr @@ -1,5 +1,5 @@ error: not enough interrupts to dispatch all software tasks (need: 1; given: 0) - --> ui/extern-interrupt-not-enough.rs:17:8 + --> ui/extern-interrupt-not-enough.rs:17:14 | -17 | fn a(_: a::Context) {} - | ^ +17 | async fn a(_: a::Context) {} + | ^ diff --git a/ui/extern-interrupt-used.rs b/ui/extern-interrupt-used.rs index 882d5e3ab0..42de4c080f 100644 --- a/ui/extern-interrupt-used.rs +++ b/ui/extern-interrupt-used.rs @@ -9,8 +9,8 @@ mod app { struct Local {} #[init] - fn init(cx: init::Context) -> (Shared, Local, init::Monotonics) { - (Shared {}, Local {}, init::Monotonics()) + fn init(cx: init::Context) -> (Shared, Local) { + (Shared {}, Local {}) } #[task(binds = UART0)] diff --git a/ui/task-priority-too-high.rs b/ui/task-priority-too-high.rs index 46ab561750..44e4a25dbd 100644 --- a/ui/task-priority-too-high.rs +++ b/ui/task-priority-too-high.rs @@ -9,8 +9,8 @@ mod app { struct Local {} #[init] - fn init(cx: init::Context) -> (Shared, Local, init::Monotonics) { - (Shared {}, Local {}, init::Monotonics()) + fn init(cx: init::Context) -> (Shared, Local) { + (Shared {}, Local {}) } #[task(binds = GPIOA, priority = 1)] diff --git a/ui/task-priority-too-high.stderr b/ui/task-priority-too-high.stderr index a7a15ebfe5..125637773f 100644 --- a/ui/task-priority-too-high.stderr +++ b/ui/task-priority-too-high.stderr @@ -1,7 +1,7 @@ warning: unused variable: `cx` --> ui/task-priority-too-high.rs:12:13 | -12 | fn init(cx: init::Context) -> (Shared, Local, init::Monotonics) { +12 | fn init(cx: init::Context) -> (Shared, Local) { | ^^ help: if this is intentional, prefix it with an underscore: `_cx` | = note: `#[warn(unused_variables)]` on by default diff --git a/ui/unknown-interrupt.rs b/ui/unknown-interrupt.rs index f2bc629589..3c6c69f8f3 100644 --- a/ui/unknown-interrupt.rs +++ b/ui/unknown-interrupt.rs @@ -9,7 +9,7 @@ mod app { struct Local {} #[init] - fn init(cx: init::Context) -> (Shared, Local, init::Monotonics) { - (Shared {}, Local {}, init::Monotonics()) + fn init(cx: init::Context) -> (Shared, Local) { + (Shared {}, Local {}) } } From 1eabb94f0424d7ff85786ad05615da69a379f01d Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Mon, 9 Jan 2023 09:48:39 +0100 Subject: [PATCH 245/423] New executor design --- src/export.rs | 68 +--------------------------- src/export/executor.rs | 100 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 101 insertions(+), 67 deletions(-) create mode 100644 src/export/executor.rs diff --git a/src/export.rs b/src/export.rs index 7beaf1631d..6017dcf78f 100644 --- a/src/export.rs +++ b/src/export.rs @@ -8,73 +8,7 @@ pub use cortex_m::{ Peripherals, }; -pub mod executor { - use core::{ - future::Future, - mem, - pin::Pin, - task::{Context, Poll, RawWaker, RawWakerVTable, Waker}, - }; - - static WAKER_VTABLE: RawWakerVTable = - RawWakerVTable::new(waker_clone, waker_wake, waker_wake, waker_drop); - - unsafe fn waker_clone(p: *const ()) -> RawWaker { - RawWaker::new(p, &WAKER_VTABLE) - } - - unsafe fn waker_wake(p: *const ()) { - // The only thing we need from a waker is the function to call to pend the async - // dispatcher. - let f: fn() = mem::transmute(p); - f(); - } - - unsafe fn waker_drop(_: *const ()) { - // nop - } - - //============ - // AsyncTaskExecutor - - pub struct AsyncTaskExecutor { - task: Option, - } - - impl AsyncTaskExecutor { - pub const fn new() -> Self { - Self { task: None } - } - - pub fn is_running(&self) -> bool { - self.task.is_some() - } - - pub fn spawn(&mut self, future: F) { - self.task = Some(future); - } - - pub fn poll(&mut self, wake: fn()) -> bool { - if let Some(future) = &mut self.task { - unsafe { - let waker = Waker::from_raw(RawWaker::new(wake as *const (), &WAKER_VTABLE)); - let mut cx = Context::from_waker(&waker); - let future = Pin::new_unchecked(future); - - match future.poll(&mut cx) { - Poll::Ready(_) => { - self.task = None; - true // Only true if we finished now - } - Poll::Pending => false, - } - } - } else { - false - } - } - } -} +pub mod executor; /// Mask is used to store interrupt masks on systems without a BASEPRI register (M0, M0+, M23). /// It needs to be large enough to cover all the relevant interrupts in use. diff --git a/src/export/executor.rs b/src/export/executor.rs new file mode 100644 index 0000000000..874ee192be --- /dev/null +++ b/src/export/executor.rs @@ -0,0 +1,100 @@ +use core::{ + cell::UnsafeCell, + future::Future, + mem::{self, MaybeUninit}, + pin::Pin, + sync::atomic::{AtomicBool, Ordering}, + task::{Context, Poll, RawWaker, RawWakerVTable, Waker}, +}; + +static WAKER_VTABLE: RawWakerVTable = + RawWakerVTable::new(waker_clone, waker_wake, waker_wake, waker_drop); + +unsafe fn waker_clone(p: *const ()) -> RawWaker { + RawWaker::new(p, &WAKER_VTABLE) +} + +unsafe fn waker_wake(p: *const ()) { + // The only thing we need from a waker is the function to call to pend the async + // dispatcher. + let f: fn() = mem::transmute(p); + f(); +} + +unsafe fn waker_drop(_: *const ()) { + // nop +} + +//============ +// AsyncTaskExecutor + +/// Executor for an async task. +pub struct AsyncTaskExecutor { + // `task` is proteced by the `running` flag. + task: UnsafeCell>, + running: AtomicBool, + pending: AtomicBool, +} + +unsafe impl Sync for AsyncTaskExecutor {} + +impl AsyncTaskExecutor { + /// Create a new executor. + pub const fn new() -> Self { + Self { + task: UnsafeCell::new(MaybeUninit::uninit()), + running: AtomicBool::new(false), + pending: AtomicBool::new(false), + } + } + + /// Check if there is an active task in the executor. + pub fn is_running(&self) -> bool { + self.running.load(Ordering::Relaxed) + } + + /// Checks if a waker has pended the executor. + pub fn is_pending(&self) -> bool { + self.pending.load(Ordering::Relaxed) + } + + // Used by wakers to indicate that the executor needs to run. + pub fn set_pending(&self) { + self.pending.store(true, Ordering::Release); + } + + /// Try to reserve the executor for a future. + /// Used in conjunction with `spawn_unchecked` to reserve the executor before spawning. + /// + /// This could have been joined with `spawn_unchecked` for a complete safe API, however the + /// codegen needs to see if the reserve fails so it can give back input parameters. If spawning + /// was done within the same call the input parameters would be lost and could not be returned. + pub fn try_reserve(&self) -> bool { + self.running + .compare_exchange(false, true, Ordering::AcqRel, Ordering::Relaxed) + .is_ok() + } + + /// Spawn a future, only valid to do after `try_reserve` succeeds. + pub unsafe fn spawn_unchecked(&self, future: F) { + debug_assert!(self.running.load(Ordering::Relaxed)); + + self.task.get().write(MaybeUninit::new(future)); + } + + /// Poll the future in the executor. + pub fn poll(&self, wake: fn()) { + if self.is_running() { + let waker = unsafe { Waker::from_raw(RawWaker::new(wake as *const (), &WAKER_VTABLE)) }; + let mut cx = Context::from_waker(&waker); + let future = unsafe { Pin::new_unchecked(&mut *(self.task.get() as *mut F)) }; + + match future.poll(&mut cx) { + Poll::Ready(_) => { + self.running.store(false, Ordering::Release); + } + Poll::Pending => {} + } + } + } +} From cd790a94286cdc307d399b7f7a43e305e90de5bf Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Mon, 9 Jan 2023 21:02:53 +0100 Subject: [PATCH 246/423] More work on new spawn/executor --- Cargo.toml | 2 + macros/src/codegen/async_dispatchers.rs | 50 ++++--------------------- macros/src/codegen/module.rs | 29 +++++++------- macros/src/codegen/software_tasks.rs | 14 +------ macros/src/codegen/util.rs | 10 ----- src/export.rs | 25 +------------ src/export/executor.rs | 11 ++++-- xtask/src/command.rs | 10 ++++- 8 files changed, 43 insertions(+), 108 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index cad929196c..6eb691df6e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,6 +28,8 @@ rtic-monotonic = "1.0.0" rtic-core = "1.0.0" heapless = "0.7.7" bare-metal = "1.0.0" +#portable-atomic = { version = "0.3.19" } +atomic-polyfill = "1" [build-dependencies] version_check = "0.9" diff --git a/macros/src/codegen/async_dispatchers.rs b/macros/src/codegen/async_dispatchers.rs index 341f76ff6f..012bd61a36 100644 --- a/macros/src/codegen/async_dispatchers.rs +++ b/macros/src/codegen/async_dispatchers.rs @@ -16,11 +16,10 @@ pub fn codegen(app: &App, analysis: &Analysis) -> TokenStream2 { items.push(quote!( #[allow(non_camel_case_types)] - type #type_name = impl core::future::Future + 'static; + type #type_name = impl core::future::Future; #[allow(non_upper_case_globals)] - static #exec_name: - rtic::RacyCell> = - rtic::RacyCell::new(rtic::export::executor::AsyncTaskExecutor::new()); + static #exec_name: rtic::export::executor::AsyncTaskExecutor<#type_name> = + rtic::export::executor::AsyncTaskExecutor::new(); )); } @@ -47,38 +46,13 @@ pub fn codegen(app: &App, analysis: &Analysis) -> TokenStream2 { let exec_name = util::internal_task_ident(name, "EXEC"); // let task = &app.software_tasks[name]; // let cfgs = &task.cfgs; - let executor_run_ident = util::executor_run_ident(name); - - let rq = util::rq_async_ident(name); - - items.push(quote!( - #[doc(hidden)] - #[allow(non_camel_case_types)] - #[allow(non_upper_case_globals)] - static #rq: core::sync::atomic::AtomicBool = core::sync::atomic::AtomicBool::new(false); - )); stmts.push(quote!( - if !(&*#exec_name.get()).is_running() { - // TODO Fix this to be compare and swap - if #rq.load(core::sync::atomic::Ordering::Relaxed) { - #rq.store(false, core::sync::atomic::Ordering::Relaxed); - - (&mut *#exec_name.get_mut()).spawn(#name(#name::Context::new())); - #executor_run_ident.store(true, core::sync::atomic::Ordering::Relaxed); - } - } - - if #executor_run_ident.load(core::sync::atomic::Ordering::Relaxed) { - #executor_run_ident.store(false, core::sync::atomic::Ordering::Relaxed); - if (&mut *#exec_name.get_mut()).poll(|| { - #executor_run_ident.store(true, core::sync::atomic::Ordering::Release); + if #exec_name.check_and_clear_pending() { + #exec_name.poll(|| { + #exec_name.set_pending(); #pend_interrupt - }) && #rq.load(core::sync::atomic::Ordering::Relaxed) { - // If the ready queue is not empty and the executor finished, restart this - // dispatch to check if the executor should be restarted. - #pend_interrupt - } + }); } )); } @@ -96,12 +70,7 @@ pub fn codegen(app: &App, analysis: &Analysis) -> TokenStream2 { const PRIORITY: u8 = #level; rtic::export::run(PRIORITY, || { - // Have the acquire/release semantics outside the checks to no overdo it - core::sync::atomic::fence(core::sync::atomic::Ordering::Acquire); - #(#stmts)* - - core::sync::atomic::fence(core::sync::atomic::Ordering::Release); }); } )); @@ -110,12 +79,7 @@ pub fn codegen(app: &App, analysis: &Analysis) -> TokenStream2 { #[allow(non_snake_case)] unsafe fn #dispatcher_name() -> ! { loop { - // Have the acquire/release semantics outside the checks to no overdo it - core::sync::atomic::fence(core::sync::atomic::Ordering::Acquire); - #(#stmts)* - - core::sync::atomic::fence(core::sync::atomic::Ordering::Release); } } )); diff --git a/macros/src/codegen/module.rs b/macros/src/codegen/module.rs index 70fbb5e651..19cf2417bb 100644 --- a/macros/src/codegen/module.rs +++ b/macros/src/codegen/module.rs @@ -98,6 +98,7 @@ pub fn codegen(ctxt: Context, app: &App, analysis: &Analysis) -> TokenStream2 { }; let internal_context_name = util::internal_task_ident(name, "Context"); + let exec_name = util::internal_task_ident(name, "EXEC"); items.push(quote!( #(#cfgs)* @@ -147,25 +148,25 @@ pub fn codegen(ctxt: Context, app: &App, analysis: &Analysis) -> TokenStream2 { let internal_spawn_ident = util::internal_task_ident(name, "spawn"); // Spawn caller - let rq = util::rq_async_ident(name); items.push(quote!( - - #(#cfgs)* - /// Spawns the task directly - #[allow(non_snake_case)] - #[doc(hidden)] - pub fn #internal_spawn_ident() -> Result<(), ()> { - unsafe { - // TODO: Fix this to be compare and swap - if #rq.load(core::sync::atomic::Ordering::Acquire) { - Err(()) - } else { - #rq.store(true, core::sync::atomic::Ordering::Release); + #(#cfgs)* + /// Spawns the task directly + #[allow(non_snake_case)] + #[doc(hidden)] + pub fn #internal_spawn_ident() -> Result<(), ()> { + if #exec_name.try_reserve() { + unsafe { + // TODO: Add args here + #exec_name.spawn_unchecked(#name(#name::Context::new())); + } #pend_interrupt + Ok(()) + } else { + Err(()) } } - })); + )); module_items.push(quote!( #(#cfgs)* diff --git a/macros/src/codegen/software_tasks.rs b/macros/src/codegen/software_tasks.rs index 4cb1fa95f6..b923283269 100644 --- a/macros/src/codegen/software_tasks.rs +++ b/macros/src/codegen/software_tasks.rs @@ -1,7 +1,7 @@ use crate::syntax::{ast::App, Context}; use crate::{ analyze::Analysis, - codegen::{local_resources_struct, module, shared_resources_struct, util}, + codegen::{local_resources_struct, module, shared_resources_struct}, }; use proc_macro2::TokenStream as TokenStream2; use quote::quote; @@ -13,18 +13,6 @@ pub fn codegen(app: &App, analysis: &Analysis) -> TokenStream2 { // Any task for (name, task) in app.software_tasks.iter() { - let executor_ident = util::executor_run_ident(name); - mod_app.push(quote!( - #[allow(non_camel_case_types)] - #[allow(non_upper_case_globals)] - #[doc(hidden)] - static #executor_ident: core::sync::atomic::AtomicBool = - core::sync::atomic::AtomicBool::new(false); - )); - - // `${task}Resources` - - // `${task}Locals` if !task.args.local_resources.is_empty() { let (item, constructor) = local_resources_struct::codegen(Context::SoftwareTask(name), app); diff --git a/macros/src/codegen/util.rs b/macros/src/codegen/util.rs index a0caf0aeef..0558d9d102 100644 --- a/macros/src/codegen/util.rs +++ b/macros/src/codegen/util.rs @@ -49,11 +49,6 @@ pub fn impl_mutex( ) } -/// Generates an identifier for the `EXECUTOR_RUN` atomics (`async` API) -pub fn executor_run_ident(task: &Ident) -> Ident { - mark_internal_name(&format!("{task}_EXECUTOR_RUN")) -} - pub fn interrupt_ident() -> Ident { let span = Span::call_site(); Ident::new("interrupt", span) @@ -151,11 +146,6 @@ pub fn local_resources_ident(ctxt: Context, app: &App) -> Ident { mark_internal_name(&s) } -/// Generates an identifier for a ready queue, async task version -pub fn rq_async_ident(async_task_name: &Ident) -> Ident { - mark_internal_name(&format!("ASYNC_TASK_{async_task_name}_RQ")) -} - /// Suffixed identifier pub fn suffixed(name: &str) -> Ident { let span = Span::call_site(); diff --git a/src/export.rs b/src/export.rs index 6017dcf78f..cdca972785 100644 --- a/src/export.rs +++ b/src/export.rs @@ -1,5 +1,4 @@ pub use bare_metal::CriticalSection; -use core::sync::atomic::{AtomicBool, Ordering}; pub use cortex_m::{ asm::nop, asm::wfi, @@ -7,6 +6,8 @@ pub use cortex_m::{ peripheral::{scb::SystemHandler, DWT, NVIC, SCB, SYST}, Peripherals, }; +//pub use portable_atomic as atomic; +pub use atomic_polyfill as atomic; pub mod executor; @@ -72,28 +73,6 @@ where f(); } -pub struct Barrier { - inner: AtomicBool, -} - -impl Barrier { - pub const fn new() -> Self { - Barrier { - inner: AtomicBool::new(false), - } - } - - pub fn release(&self) { - self.inner.store(true, Ordering::Release); - } - - pub fn wait(&self) { - while !self.inner.load(Ordering::Acquire) { - core::hint::spin_loop() - } - } -} - /// Const helper to check architecture pub const fn have_basepri() -> bool { #[cfg(have_basepri)] diff --git a/src/export/executor.rs b/src/export/executor.rs index 874ee192be..2f88eff968 100644 --- a/src/export/executor.rs +++ b/src/export/executor.rs @@ -1,9 +1,9 @@ +use super::atomic::{AtomicBool, Ordering}; use core::{ cell::UnsafeCell, future::Future, mem::{self, MaybeUninit}, pin::Pin, - sync::atomic::{AtomicBool, Ordering}, task::{Context, Poll, RawWaker, RawWakerVTable, Waker}, }; @@ -53,9 +53,11 @@ impl AsyncTaskExecutor { self.running.load(Ordering::Relaxed) } - /// Checks if a waker has pended the executor. - pub fn is_pending(&self) -> bool { - self.pending.load(Ordering::Relaxed) + /// Checks if a waker has pended the executor and simultaneously clears the flag. + pub fn check_and_clear_pending(&self) -> bool { + self.pending + .compare_exchange(true, false, Ordering::Relaxed, Ordering::Relaxed) + .is_ok() } // Used by wakers to indicate that the executor needs to run. @@ -80,6 +82,7 @@ impl AsyncTaskExecutor { debug_assert!(self.running.load(Ordering::Relaxed)); self.task.get().write(MaybeUninit::new(future)); + self.set_pending(); } /// Poll the future in the executor. diff --git a/xtask/src/command.rs b/xtask/src/command.rs index 418f440cb0..4e903691a2 100644 --- a/xtask/src/command.rs +++ b/xtask/src/command.rs @@ -70,7 +70,15 @@ impl<'a> CargoCommand<'a> { features, mode, } => { - let mut args = vec!["+nightly", self.name(), "--examples", "--target", target]; + let mut args = vec![ + "+nightly", + self.name(), + "--examples", + "--target", + target, + "--features", + "test-critical-section", + ]; if let Some(feature_name) = features { args.extend_from_slice(&["--features", feature_name]); From d6d58b0eb88242cf63724e1420bd29f8a4489916 Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Tue, 10 Jan 2023 21:03:10 +0100 Subject: [PATCH 247/423] Async tasks can now take arguments at spawn again --- ci/expected/async-task.run | 1 + examples/async-task.rs | 6 +++ macros/src/codegen/module.rs | 11 +++-- macros/src/codegen/software_tasks.rs | 3 +- macros/src/codegen/util.rs | 54 ++++++++++++++++++++-- macros/src/syntax/analyze.rs | 5 ++ macros/src/syntax/ast.rs | 5 +- macros/src/syntax/parse/hardware_task.rs | 58 ++++++++++-------------- macros/src/syntax/parse/idle.rs | 18 ++++---- macros/src/syntax/parse/init.rs | 22 +++++---- macros/src/syntax/parse/software_task.rs | 10 ++-- macros/src/syntax/parse/util.rs | 30 ++++++++---- macros/ui/task-divergent.stderr | 2 +- macros/ui/task-no-context.stderr | 2 +- macros/ui/task-pub.stderr | 2 +- macros/ui/task-unsafe.stderr | 2 +- macros/ui/task-zero-prio.stderr | 2 +- 17 files changed, 153 insertions(+), 80 deletions(-) diff --git a/ci/expected/async-task.run b/ci/expected/async-task.run index 6787fa8fae..1f93a4c766 100644 --- a/ci/expected/async-task.run +++ b/ci/expected/async-task.run @@ -1,4 +1,5 @@ init hello from async2 hello from async +hello from async with args a: 1, b: 2 idle diff --git a/examples/async-task.rs b/examples/async-task.rs index 780bc08157..e1ab143258 100644 --- a/examples/async-task.rs +++ b/examples/async-task.rs @@ -27,6 +27,7 @@ mod app { hprintln!("init"); async_task::spawn().unwrap(); + async_task_args::spawn(1, 2).unwrap(); async_task2::spawn().unwrap(); (Shared { a: 0 }, Local {}) @@ -53,6 +54,11 @@ mod app { hprintln!("hello from async"); } + #[task] + async fn async_task_args(_cx: async_task_args::Context, a: u32, b: i32) { + hprintln!("hello from async with args a: {}, b: {}", a, b); + } + #[task(priority = 2, shared = [a])] async fn async_task2(cx: async_task2::Context) { let async_task2::SharedResources { a: _, .. } = cx.shared; diff --git a/macros/src/codegen/module.rs b/macros/src/codegen/module.rs index 19cf2417bb..666bd0420a 100644 --- a/macros/src/codegen/module.rs +++ b/macros/src/codegen/module.rs @@ -146,6 +146,8 @@ pub fn codegen(ctxt: Context, app: &App, analysis: &Analysis) -> TokenStream2 { }; let internal_spawn_ident = util::internal_task_ident(name, "spawn"); + let (input_args, input_tupled, input_untupled, input_ty) = + util::regroup_inputs(&spawnee.inputs); // Spawn caller items.push(quote!( @@ -153,17 +155,18 @@ pub fn codegen(ctxt: Context, app: &App, analysis: &Analysis) -> TokenStream2 { /// Spawns the task directly #[allow(non_snake_case)] #[doc(hidden)] - pub fn #internal_spawn_ident() -> Result<(), ()> { + pub fn #internal_spawn_ident(#(#input_args,)*) -> Result<(), #input_ty> { if #exec_name.try_reserve() { + // This unsafe is protected by `try_reserve`, see its documentation for details unsafe { - // TODO: Add args here - #exec_name.spawn_unchecked(#name(#name::Context::new())); + #exec_name.spawn_unchecked(#name(#name::Context::new() #(,#input_untupled)*)); } + #pend_interrupt Ok(()) } else { - Err(()) + Err(#input_tupled) } } )); diff --git a/macros/src/codegen/software_tasks.rs b/macros/src/codegen/software_tasks.rs index b923283269..34fc851a8c 100644 --- a/macros/src/codegen/software_tasks.rs +++ b/macros/src/codegen/software_tasks.rs @@ -36,12 +36,13 @@ pub fn codegen(app: &App, analysis: &Analysis) -> TokenStream2 { let attrs = &task.attrs; let cfgs = &task.cfgs; let stmts = &task.stmts; + let inputs = &task.inputs; user_tasks.push(quote!( #(#attrs)* #(#cfgs)* #[allow(non_snake_case)] - async fn #name(#context: #name::Context<'static>) { + async fn #name<'a>(#context: #name::Context<'a> #(,#inputs)*) { use rtic::Mutex as _; use rtic::mutex::prelude::*; diff --git a/macros/src/codegen/util.rs b/macros/src/codegen/util.rs index 0558d9d102..e121487c70 100644 --- a/macros/src/codegen/util.rs +++ b/macros/src/codegen/util.rs @@ -1,9 +1,8 @@ -use core::sync::atomic::{AtomicUsize, Ordering}; - use crate::syntax::{ast::App, Context}; +use core::sync::atomic::{AtomicUsize, Ordering}; use proc_macro2::{Span, TokenStream as TokenStream2}; use quote::quote; -use syn::{Attribute, Ident}; +use syn::{Attribute, Ident, PatType}; const RTIC_INTERNAL: &str = "__rtic_internal"; @@ -94,6 +93,55 @@ pub fn link_section_uninit() -> TokenStream2 { quote!(#[link_section = #section]) } +/// Regroups the inputs of a task +/// +/// `inputs` could be &[`input: Foo`] OR &[`mut x: i32`, `ref y: i64`] +pub fn regroup_inputs( + inputs: &[PatType], +) -> ( + // args e.g. &[`_0`], &[`_0: i32`, `_1: i64`] + Vec, + // tupled e.g. `_0`, `(_0, _1)` + TokenStream2, + // untupled e.g. &[`_0`], &[`_0`, `_1`] + Vec, + // ty e.g. `Foo`, `(i32, i64)` + TokenStream2, +) { + if inputs.len() == 1 { + let ty = &inputs[0].ty; + + ( + vec![quote!(_0: #ty)], + quote!(_0), + vec![quote!(_0)], + quote!(#ty), + ) + } else { + let mut args = vec![]; + let mut pats = vec![]; + let mut tys = vec![]; + + for (i, input) in inputs.iter().enumerate() { + let i = Ident::new(&format!("_{}", i), Span::call_site()); + let ty = &input.ty; + + args.push(quote!(#i: #ty)); + + pats.push(quote!(#i)); + + tys.push(quote!(#ty)); + } + + let tupled = { + let pats = pats.clone(); + quote!((#(#pats,)*)) + }; + let ty = quote!((#(#tys,)*)); + (args, tupled, pats, ty) + } +} + /// Get the ident for the name of the task pub fn get_task_name(ctxt: Context, app: &App) -> Ident { let s = match ctxt { diff --git a/macros/src/syntax/analyze.rs b/macros/src/syntax/analyze.rs index b70ceb8b38..3ed1487741 100644 --- a/macros/src/syntax/analyze.rs +++ b/macros/src/syntax/analyze.rs @@ -287,6 +287,11 @@ pub(crate) fn app(app: &App) -> Result { let channel = channels.entry(spawnee_prio).or_default(); channel.tasks.insert(name.clone()); + + // All inputs are send as we do not know from where they may be spawned. + spawnee.inputs.iter().for_each(|input| { + send_types.insert(input.ty.clone()); + }); } // No channel should ever be empty diff --git a/macros/src/syntax/ast.rs b/macros/src/syntax/ast.rs index da6016add8..27e6773f7f 100644 --- a/macros/src/syntax/ast.rs +++ b/macros/src/syntax/ast.rs @@ -1,6 +1,6 @@ //! Abstract Syntax Tree -use syn::{Attribute, Expr, Ident, Item, ItemUse, Pat, Path, Stmt, Type}; +use syn::{Attribute, Expr, Ident, Item, ItemUse, Pat, PatType, Path, Stmt, Type}; use crate::syntax::Map; @@ -205,6 +205,9 @@ pub struct SoftwareTask { /// The context argument pub context: Box, + /// The inputs of this software task + pub inputs: Vec, + /// The statements that make up the task handler pub stmts: Vec, diff --git a/macros/src/syntax/parse/hardware_task.rs b/macros/src/syntax/parse/hardware_task.rs index c13426f4a3..7f6dfbe4c3 100644 --- a/macros/src/syntax/parse/hardware_task.rs +++ b/macros/src/syntax/parse/hardware_task.rs @@ -15,25 +15,20 @@ impl HardwareTask { let name = item.sig.ident.to_string(); - if name == "init" || name == "idle" { - return Err(parse::Error::new( - span, - "tasks cannot be named `init` or `idle`", - )); - } - if valid_signature { - if let Some(context) = util::parse_inputs(item.sig.inputs, &name) { - let FilterAttrs { cfgs, attrs, .. } = util::filter_attributes(item.attrs); + if let Some((context, Ok(rest))) = util::parse_inputs(item.sig.inputs, &name) { + if rest.is_empty() { + let FilterAttrs { cfgs, attrs, .. } = util::filter_attributes(item.attrs); - return Ok(HardwareTask { - args, - cfgs, - attrs, - context, - stmts: item.block.stmts, - is_extern: false, - }); + return Ok(HardwareTask { + args, + cfgs, + attrs, + context, + stmts: item.block.stmts, + is_extern: false, + }); + } } } @@ -56,25 +51,20 @@ impl HardwareTask { let name = item.sig.ident.to_string(); - if name == "init" || name == "idle" { - return Err(parse::Error::new( - span, - "tasks cannot be named `init` or `idle`", - )); - } - if valid_signature { - if let Some(context) = util::parse_inputs(item.sig.inputs, &name) { - let FilterAttrs { cfgs, attrs, .. } = util::filter_attributes(item.attrs); + if let Some((context, Ok(rest))) = util::parse_inputs(item.sig.inputs, &name) { + if rest.is_empty() { + let FilterAttrs { cfgs, attrs, .. } = util::filter_attributes(item.attrs); - return Ok(HardwareTask { - args, - cfgs, - attrs, - context, - stmts: Vec::::new(), - is_extern: true, - }); + return Ok(HardwareTask { + args, + cfgs, + attrs, + context, + stmts: Vec::::new(), + is_extern: true, + }); + } } } diff --git a/macros/src/syntax/parse/idle.rs b/macros/src/syntax/parse/idle.rs index f049cca85e..124c136684 100644 --- a/macros/src/syntax/parse/idle.rs +++ b/macros/src/syntax/parse/idle.rs @@ -21,14 +21,16 @@ impl Idle { let name = item.sig.ident.to_string(); if valid_signature { - if let Some(context) = util::parse_inputs(item.sig.inputs, &name) { - return Ok(Idle { - args, - attrs: item.attrs, - context, - name: item.sig.ident, - stmts: item.block.stmts, - }); + if let Some((context, Ok(rest))) = util::parse_inputs(item.sig.inputs, &name) { + if rest.is_empty() { + return Ok(Idle { + args, + attrs: item.attrs, + context, + name: item.sig.ident, + stmts: item.block.stmts, + }); + } } } diff --git a/macros/src/syntax/parse/init.rs b/macros/src/syntax/parse/init.rs index 23130c85ac..0aea20bd32 100644 --- a/macros/src/syntax/parse/init.rs +++ b/macros/src/syntax/parse/init.rs @@ -25,16 +25,18 @@ impl Init { if let Ok((user_shared_struct, user_local_struct)) = util::type_is_init_return(&item.sig.output) { - if let Some(context) = util::parse_inputs(item.sig.inputs, &name) { - return Ok(Init { - args, - attrs: item.attrs, - context, - name: item.sig.ident, - stmts: item.block.stmts, - user_shared_struct, - user_local_struct, - }); + if let Some((context, Ok(rest))) = util::parse_inputs(item.sig.inputs, &name) { + if rest.is_empty() { + return Ok(Init { + args, + attrs: item.attrs, + context, + name: item.sig.ident, + stmts: item.block.stmts, + user_shared_struct, + user_local_struct, + }); + } } } } diff --git a/macros/src/syntax/parse/software_task.rs b/macros/src/syntax/parse/software_task.rs index fb9b37c442..769aa653da 100644 --- a/macros/src/syntax/parse/software_task.rs +++ b/macros/src/syntax/parse/software_task.rs @@ -17,7 +17,7 @@ impl SoftwareTask { let name = item.sig.ident.to_string(); if valid_signature { - if let Some(context) = util::parse_inputs(item.sig.inputs, &name) { + if let Some((context, Ok(inputs))) = util::parse_inputs(item.sig.inputs, &name) { let FilterAttrs { cfgs, attrs, .. } = util::filter_attributes(item.attrs); return Ok(SoftwareTask { @@ -25,6 +25,7 @@ impl SoftwareTask { attrs, cfgs, context, + inputs, stmts: item.block.stmts, is_extern: false, }); @@ -33,7 +34,7 @@ impl SoftwareTask { Err(parse::Error::new( span, - format!("this task handler must have type signature `async fn({name}::Context)`"), + format!("this task handler must have type signature `async fn({name}::Context, ..)`"), )) } } @@ -52,7 +53,7 @@ impl SoftwareTask { let name = item.sig.ident.to_string(); if valid_signature { - if let Some(context) = util::parse_inputs(item.sig.inputs, &name) { + if let Some((context, Ok(inputs))) = util::parse_inputs(item.sig.inputs, &name) { let FilterAttrs { cfgs, attrs, .. } = util::filter_attributes(item.attrs); return Ok(SoftwareTask { @@ -60,6 +61,7 @@ impl SoftwareTask { attrs, cfgs, context, + inputs, stmts: Vec::::new(), is_extern: true, }); @@ -68,7 +70,7 @@ impl SoftwareTask { Err(parse::Error::new( span, - format!("this task handler must have type signature `async fn({name}::Context)`"), + format!("this task handler must have type signature `async fn({name}::Context, ..)`"), )) } } diff --git a/macros/src/syntax/parse/util.rs b/macros/src/syntax/parse/util.rs index 900ef9d66f..5a5e0c0ef2 100644 --- a/macros/src/syntax/parse/util.rs +++ b/macros/src/syntax/parse/util.rs @@ -3,8 +3,8 @@ use syn::{ parse::{self, ParseStream}, punctuated::Punctuated, spanned::Spanned, - Abi, AttrStyle, Attribute, Expr, FnArg, ForeignItemFn, Ident, ItemFn, Pat, Path, PathArguments, - ReturnType, Token, Type, Visibility, + Abi, AttrStyle, Attribute, Expr, FnArg, ForeignItemFn, Ident, ItemFn, Pat, PatType, Path, + PathArguments, ReturnType, Token, Type, Visibility, }; use crate::syntax::{ @@ -231,19 +231,29 @@ pub fn parse_local_resources(content: ParseStream<'_>) -> parse::Result, name: &str) -> Option> { +type ParseInputResult = Option<(Box, Result, FnArg>)>; + +pub fn parse_inputs(inputs: Punctuated, name: &str) -> ParseInputResult { let mut inputs = inputs.into_iter(); - if let Some(FnArg::Typed(first)) = inputs.next() { - if type_is_path(&first.ty, &[name, "Context"]) { - // No more inputs - if inputs.next().is_none() { - return Some(first.pat); + match inputs.next() { + Some(FnArg::Typed(first)) => { + if type_is_path(&first.ty, &[name, "Context"]) { + let rest = inputs + .map(|arg| match arg { + FnArg::Typed(arg) => Ok(arg), + _ => Err(arg), + }) + .collect::, _>>(); + + Some((first.pat, rest)) + } else { + None } } - } - None + _ => None, + } } pub fn type_is_bottom(ty: &ReturnType) -> bool { diff --git a/macros/ui/task-divergent.stderr b/macros/ui/task-divergent.stderr index bd22bd3358..dd002080b7 100644 --- a/macros/ui/task-divergent.stderr +++ b/macros/ui/task-divergent.stderr @@ -1,4 +1,4 @@ -error: this task handler must have type signature `async fn(foo::Context)` +error: this task handler must have type signature `async fn(foo::Context, ..)` --> ui/task-divergent.rs:6:14 | 6 | async fn foo(_: foo::Context) -> ! { diff --git a/macros/ui/task-no-context.stderr b/macros/ui/task-no-context.stderr index 91239a17b0..62147aab95 100644 --- a/macros/ui/task-no-context.stderr +++ b/macros/ui/task-no-context.stderr @@ -1,4 +1,4 @@ -error: this task handler must have type signature `async fn(foo::Context)` +error: this task handler must have type signature `async fn(foo::Context, ..)` --> ui/task-no-context.rs:6:14 | 6 | async fn foo() {} diff --git a/macros/ui/task-pub.stderr b/macros/ui/task-pub.stderr index 72c4e637d1..7b9813d848 100644 --- a/macros/ui/task-pub.stderr +++ b/macros/ui/task-pub.stderr @@ -1,4 +1,4 @@ -error: this task handler must have type signature `async fn(foo::Context)` +error: this task handler must have type signature `async fn(foo::Context, ..)` --> ui/task-pub.rs:6:18 | 6 | pub async fn foo(_: foo::Context) {} diff --git a/macros/ui/task-unsafe.stderr b/macros/ui/task-unsafe.stderr index 4908481311..90ac76fe01 100644 --- a/macros/ui/task-unsafe.stderr +++ b/macros/ui/task-unsafe.stderr @@ -1,4 +1,4 @@ -error: this task handler must have type signature `async fn(foo::Context)` +error: this task handler must have type signature `async fn(foo::Context, ..)` --> ui/task-unsafe.rs:6:21 | 6 | async unsafe fn foo(_: foo::Context) {} diff --git a/macros/ui/task-zero-prio.stderr b/macros/ui/task-zero-prio.stderr index f2d82231e3..1ab9aab690 100644 --- a/macros/ui/task-zero-prio.stderr +++ b/macros/ui/task-zero-prio.stderr @@ -1,4 +1,4 @@ -error: this task handler must have type signature `async fn(foo::Context)` +error: this task handler must have type signature `async fn(foo::Context, ..)` --> ui/task-zero-prio.rs:15:8 | 15 | fn foo(_: foo::Context) {} From 5688a5d332cdaffaca64ade5b138a3676ac7cd32 Mon Sep 17 00:00:00 2001 From: Per Lindgren Date: Thu, 12 Jan 2023 08:50:12 +0100 Subject: [PATCH 248/423] executor update for less unsafe and more clear --- macros/src/codegen/async_dispatchers.rs | 11 +++--- macros/src/codegen/module.rs | 8 ++--- src/export/executor.rs | 45 ++++++++++++++----------- 3 files changed, 34 insertions(+), 30 deletions(-) diff --git a/macros/src/codegen/async_dispatchers.rs b/macros/src/codegen/async_dispatchers.rs index 012bd61a36..a12ad325fc 100644 --- a/macros/src/codegen/async_dispatchers.rs +++ b/macros/src/codegen/async_dispatchers.rs @@ -44,16 +44,15 @@ pub fn codegen(app: &App, analysis: &Analysis) -> TokenStream2 { for name in channel.tasks.iter() { let exec_name = util::internal_task_ident(name, "EXEC"); + // TODO: Fix cfg // let task = &app.software_tasks[name]; // let cfgs = &task.cfgs; stmts.push(quote!( - if #exec_name.check_and_clear_pending() { - #exec_name.poll(|| { - #exec_name.set_pending(); - #pend_interrupt - }); - } + #exec_name.poll(|| { + #exec_name.set_pending(); + #pend_interrupt + }); )); } diff --git a/macros/src/codegen/module.rs b/macros/src/codegen/module.rs index 666bd0420a..f4c188a466 100644 --- a/macros/src/codegen/module.rs +++ b/macros/src/codegen/module.rs @@ -156,11 +156,8 @@ pub fn codegen(ctxt: Context, app: &App, analysis: &Analysis) -> TokenStream2 { #[allow(non_snake_case)] #[doc(hidden)] pub fn #internal_spawn_ident(#(#input_args,)*) -> Result<(), #input_ty> { - if #exec_name.try_reserve() { - // This unsafe is protected by `try_reserve`, see its documentation for details - unsafe { - #exec_name.spawn_unchecked(#name(#name::Context::new() #(,#input_untupled)*)); - } + + if #exec_name.spawn(|| #name(unsafe { #name::Context::new() } #(,#input_untupled)*) ) { #pend_interrupt @@ -168,6 +165,7 @@ pub fn codegen(ctxt: Context, app: &App, analysis: &Analysis) -> TokenStream2 { } else { Err(#input_tupled) } + } )); diff --git a/src/export/executor.rs b/src/export/executor.rs index 2f88eff968..e64cc43ec1 100644 --- a/src/export/executor.rs +++ b/src/export/executor.rs @@ -30,7 +30,7 @@ unsafe fn waker_drop(_: *const ()) { /// Executor for an async task. pub struct AsyncTaskExecutor { - // `task` is proteced by the `running` flag. + // `task` is protected by the `running` flag. task: UnsafeCell>, running: AtomicBool, pending: AtomicBool, @@ -40,6 +40,7 @@ unsafe impl Sync for AsyncTaskExecutor {} impl AsyncTaskExecutor { /// Create a new executor. + #[inline(always)] pub const fn new() -> Self { Self { task: UnsafeCell::new(MaybeUninit::uninit()), @@ -49,45 +50,51 @@ impl AsyncTaskExecutor { } /// Check if there is an active task in the executor. + #[inline(always)] pub fn is_running(&self) -> bool { self.running.load(Ordering::Relaxed) } /// Checks if a waker has pended the executor and simultaneously clears the flag. - pub fn check_and_clear_pending(&self) -> bool { + #[inline(always)] + fn check_and_clear_pending(&self) -> bool { + // Ordering::Acquire to enforce that update of task is visible to poll self.pending - .compare_exchange(true, false, Ordering::Relaxed, Ordering::Relaxed) + .compare_exchange(true, false, Ordering::Acquire, Ordering::Relaxed) .is_ok() } // Used by wakers to indicate that the executor needs to run. + #[inline(always)] pub fn set_pending(&self) { self.pending.store(true, Ordering::Release); } - /// Try to reserve the executor for a future. - /// Used in conjunction with `spawn_unchecked` to reserve the executor before spawning. - /// - /// This could have been joined with `spawn_unchecked` for a complete safe API, however the - /// codegen needs to see if the reserve fails so it can give back input parameters. If spawning - /// was done within the same call the input parameters would be lost and could not be returned. - pub fn try_reserve(&self) -> bool { - self.running + /// Spawn a future + #[inline(always)] + pub fn spawn(&self, future: impl Fn() -> F) -> bool { + // Try to reserve the executor for a future. + if self + .running .compare_exchange(false, true, Ordering::AcqRel, Ordering::Relaxed) .is_ok() - } + { + // This unsafe is protected by `running` being false and the atomic setting it to true. + unsafe { + self.task.get().write(MaybeUninit::new(future())); + } + self.set_pending(); - /// Spawn a future, only valid to do after `try_reserve` succeeds. - pub unsafe fn spawn_unchecked(&self, future: F) { - debug_assert!(self.running.load(Ordering::Relaxed)); - - self.task.get().write(MaybeUninit::new(future)); - self.set_pending(); + true + } else { + false + } } /// Poll the future in the executor. + #[inline(always)] pub fn poll(&self, wake: fn()) { - if self.is_running() { + if self.is_running() && self.check_and_clear_pending() { let waker = unsafe { Waker::from_raw(RawWaker::new(wake as *const (), &WAKER_VTABLE)) }; let mut cx = Context::from_waker(&waker); let future = unsafe { Pin::new_unchecked(&mut *(self.task.get() as *mut F)) }; From 4601782466c518d313ba79d9437bf7a3f8dbbf76 Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Sat, 14 Jan 2023 21:11:55 +0100 Subject: [PATCH 249/423] monotonic experiments --- Cargo.toml | 2 +- rtic-timer/Cargo.toml | 11 ++ rtic-timer/src/lib.rs | 138 ++++++++++++++ rtic-timer/src/sll.rs | 421 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 571 insertions(+), 1 deletion(-) create mode 100644 rtic-timer/Cargo.toml create mode 100644 rtic-timer/src/lib.rs create mode 100644 rtic-timer/src/sll.rs diff --git a/Cargo.toml b/Cargo.toml index 6eb691df6e..c22d02372f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -51,7 +51,7 @@ codegen-units = 1 lto = true [workspace] -members = ["macros", "xtask"] +members = ["macros", "xtask", "rtic-timer"] # do not optimize proc-macro deps or build scripts [profile.dev.build-override] diff --git a/rtic-timer/Cargo.toml b/rtic-timer/Cargo.toml new file mode 100644 index 0000000000..8e2e2ad602 --- /dev/null +++ b/rtic-timer/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "rtic-timer" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +cortex-m = "0.7.6" +rtic-monotonic = "1.0.0" +fugit = "0.3.6" \ No newline at end of file diff --git a/rtic-timer/src/lib.rs b/rtic-timer/src/lib.rs new file mode 100644 index 0000000000..e7051d2779 --- /dev/null +++ b/rtic-timer/src/lib.rs @@ -0,0 +1,138 @@ +#![no_std] + +use core::sync::atomic::{AtomicU32, Ordering}; +use core::{cmp::Ordering, task::Waker}; +use cortex_m::peripheral::{syst::SystClkSource, SYST}; +pub use fugit::{self, ExtU64}; +pub use rtic_monotonic::Monotonic; + +mod sll; +use sll::{IntrusiveSortedLinkedList, Min as IsslMin, Node as IntrusiveNode}; + +pub struct Timer { + cnt: AtomicU32, + // queue: IntrusiveSortedLinkedList<'static, WakerNotReady, IsslMin>, +} + +#[allow(non_snake_case)] +#[no_mangle] +fn SysTick() { + // .. + let cnt = unsafe { + static mut CNT: u32 = 0; + &mut CNT + }; + + *cnt = cnt.wrapping_add(1); +} + +/// Systick implementing `rtic_monotonic::Monotonic` which runs at a +/// settable rate using the `TIMER_HZ` parameter. +pub struct Systick { + systick: SYST, + cnt: u64, +} + +impl Systick { + /// Provide a new `Monotonic` based on SysTick. + /// + /// The `sysclk` parameter is the speed at which SysTick runs at. This value should come from + /// the clock generation function of the used HAL. + /// + /// Notice that the actual rate of the timer is a best approximation based on the given + /// `sysclk` and `TIMER_HZ`. + pub fn new(mut systick: SYST, sysclk: u32) -> Self { + // + TIMER_HZ / 2 provides round to nearest instead of round to 0. + // - 1 as the counter range is inclusive [0, reload] + let reload = (sysclk + TIMER_HZ / 2) / TIMER_HZ - 1; + + assert!(reload <= 0x00ff_ffff); + assert!(reload > 0); + + systick.disable_counter(); + systick.set_clock_source(SystClkSource::Core); + systick.set_reload(reload); + + Systick { systick, cnt: 0 } + } +} + +impl Monotonic for Systick { + const DISABLE_INTERRUPT_ON_EMPTY_QUEUE: bool = false; + + type Instant = fugit::TimerInstantU64; + type Duration = fugit::TimerDurationU64; + + fn now(&mut self) -> Self::Instant { + if self.systick.has_wrapped() { + self.cnt = self.cnt.wrapping_add(1); + } + + Self::Instant::from_ticks(self.cnt) + } + + unsafe fn reset(&mut self) { + self.systick.clear_current(); + self.systick.enable_counter(); + } + + #[inline(always)] + fn set_compare(&mut self, _val: Self::Instant) { + // No need to do something here, we get interrupts anyway. + } + + #[inline(always)] + fn clear_compare_flag(&mut self) { + // NOOP with SysTick interrupt + } + + #[inline(always)] + fn zero() -> Self::Instant { + Self::Instant::from_ticks(0) + } + + #[inline(always)] + fn on_interrupt(&mut self) { + if self.systick.has_wrapped() { + self.cnt = self.cnt.wrapping_add(1); + } + } +} + +struct WakerNotReady +where + Mono: Monotonic, +{ + pub waker: Waker, + pub instant: Mono::Instant, + pub marker: u32, +} + +impl Eq for WakerNotReady where Mono: Monotonic {} + +impl Ord for WakerNotReady +where + Mono: Monotonic, +{ + fn cmp(&self, other: &Self) -> Ordering { + self.instant.cmp(&other.instant) + } +} + +impl PartialEq for WakerNotReady +where + Mono: Monotonic, +{ + fn eq(&self, other: &Self) -> bool { + self.instant == other.instant + } +} + +impl PartialOrd for WakerNotReady +where + Mono: Monotonic, +{ + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} diff --git a/rtic-timer/src/sll.rs b/rtic-timer/src/sll.rs new file mode 100644 index 0000000000..43b53c1749 --- /dev/null +++ b/rtic-timer/src/sll.rs @@ -0,0 +1,421 @@ +//! An intrusive sorted priority linked list, designed for use in `Future`s in RTIC. +use core::cmp::Ordering; +use core::fmt; +use core::marker::PhantomData; +use core::ops::{Deref, DerefMut}; +use core::ptr::NonNull; + +/// Marker for Min sorted [`IntrusiveSortedLinkedList`]. +pub struct Min; + +/// Marker for Max sorted [`IntrusiveSortedLinkedList`]. +pub struct Max; + +/// The linked list kind: min-list or max-list +pub trait Kind: private::Sealed { + #[doc(hidden)] + fn ordering() -> Ordering; +} + +impl Kind for Min { + fn ordering() -> Ordering { + Ordering::Less + } +} + +impl Kind for Max { + fn ordering() -> Ordering { + Ordering::Greater + } +} + +/// Sealed traits +mod private { + pub trait Sealed {} +} + +impl private::Sealed for Max {} +impl private::Sealed for Min {} + +/// A node in the [`IntrusiveSortedLinkedList`]. +pub struct Node { + pub val: T, + next: Option>>, +} + +impl Node { + pub fn new(val: T) -> Self { + Self { val, next: None } + } +} + +/// The linked list. +pub struct IntrusiveSortedLinkedList<'a, T, K> { + head: Option>>, + _kind: PhantomData, + _lt: PhantomData<&'a ()>, +} + +impl<'a, T, K> fmt::Debug for IntrusiveSortedLinkedList<'a, T, K> +where + T: Ord + core::fmt::Debug, + K: Kind, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut l = f.debug_list(); + let mut current = self.head; + + while let Some(head) = current { + let head = unsafe { head.as_ref() }; + current = head.next; + + l.entry(&head.val); + } + + l.finish() + } +} + +impl<'a, T, K> IntrusiveSortedLinkedList<'a, T, K> +where + T: Ord, + K: Kind, +{ + pub const fn new() -> Self { + Self { + head: None, + _kind: PhantomData, + _lt: PhantomData, + } + } + + // Push to the list. + pub fn push(&mut self, new: &'a mut Node) { + unsafe { + if let Some(head) = self.head { + if head.as_ref().val.cmp(&new.val) != K::ordering() { + // This is newer than head, replace head + new.next = self.head; + self.head = Some(NonNull::new_unchecked(new)); + } else { + // It's not head, search the list for the correct placement + let mut current = head; + + while let Some(next) = current.as_ref().next { + if next.as_ref().val.cmp(&new.val) != K::ordering() { + break; + } + + current = next; + } + + new.next = current.as_ref().next; + current.as_mut().next = Some(NonNull::new_unchecked(new)); + } + } else { + // List is empty, place at head + self.head = Some(NonNull::new_unchecked(new)) + } + } + } + + /// Get an iterator over the sorted list. + pub fn iter(&self) -> Iter<'_, T, K> { + Iter { + _list: self, + index: self.head, + } + } + + /// Find an element in the list that can be changed and resorted. + pub fn find_mut(&mut self, mut f: F) -> Option> + where + F: FnMut(&T) -> bool, + { + let head = self.head?; + + // Special-case, first element + if f(&unsafe { head.as_ref() }.val) { + return Some(FindMut { + is_head: true, + prev_index: None, + index: self.head, + list: self, + maybe_changed: false, + }); + } + + let mut current = head; + + while let Some(next) = unsafe { current.as_ref() }.next { + if f(&unsafe { next.as_ref() }.val) { + return Some(FindMut { + is_head: false, + prev_index: Some(current), + index: Some(next), + list: self, + maybe_changed: false, + }); + } + + current = next; + } + + None + } + + /// Peek at the first element. + pub fn peek(&self) -> Option<&T> { + self.head.map(|head| unsafe { &head.as_ref().val }) + } + + /// Pops the first element in the list. + /// + /// Complexity is worst-case `O(1)`. + pub fn pop(&mut self) -> Option<&'a Node> { + if let Some(head) = self.head { + let v = unsafe { head.as_ref() }; + self.head = v.next; + Some(v) + } else { + None + } + } + + /// Checks if the linked list is empty. + #[inline] + pub fn is_empty(&self) -> bool { + self.head.is_none() + } +} + +/// Iterator for the linked list. +pub struct Iter<'a, T, K> +where + T: Ord, + K: Kind, +{ + _list: &'a IntrusiveSortedLinkedList<'a, T, K>, + index: Option>>, +} + +impl<'a, T, K> Iterator for Iter<'a, T, K> +where + T: Ord, + K: Kind, +{ + type Item = &'a T; + + fn next(&mut self) -> Option { + let index = self.index?; + + let node = unsafe { index.as_ref() }; + self.index = node.next; + + Some(&node.val) + } +} + +/// Comes from [`IntrusiveSortedLinkedList::find_mut`]. +pub struct FindMut<'a, 'b, T, K> +where + T: Ord + 'b, + K: Kind, +{ + list: &'a mut IntrusiveSortedLinkedList<'b, T, K>, + is_head: bool, + prev_index: Option>>, + index: Option>>, + maybe_changed: bool, +} + +impl<'a, 'b, T, K> FindMut<'a, 'b, T, K> +where + T: Ord, + K: Kind, +{ + unsafe fn pop_internal(&mut self) -> &'b mut Node { + if self.is_head { + // If it is the head element, we can do a normal pop + let mut head = self.list.head.unwrap_unchecked(); + let v = head.as_mut(); + self.list.head = v.next; + v + } else { + // Somewhere in the list + let mut prev = self.prev_index.unwrap_unchecked(); + let mut curr = self.index.unwrap_unchecked(); + + // Re-point the previous index + prev.as_mut().next = curr.as_ref().next; + + curr.as_mut() + } + } + + /// This will pop the element from the list. + /// + /// Complexity is worst-case `O(1)`. + #[inline] + pub fn pop(mut self) -> &'b mut Node { + unsafe { self.pop_internal() } + } + + /// This will resort the element into the correct position in the list if needed. The resorting + /// will only happen if the element has been accessed mutably. + /// + /// Same as calling `drop`. + /// + /// Complexity is worst-case `O(N)`. + #[inline] + pub fn finish(self) { + drop(self) + } +} + +impl<'b, T, K> Drop for FindMut<'_, 'b, T, K> +where + T: Ord + 'b, + K: Kind, +{ + fn drop(&mut self) { + // Only resort the list if the element has changed + if self.maybe_changed { + unsafe { + let val = self.pop_internal(); + self.list.push(val); + } + } + } +} + +impl Deref for FindMut<'_, '_, T, K> +where + T: Ord, + K: Kind, +{ + type Target = T; + + fn deref(&self) -> &Self::Target { + unsafe { &self.index.unwrap_unchecked().as_ref().val } + } +} + +impl DerefMut for FindMut<'_, '_, T, K> +where + T: Ord, + K: Kind, +{ + fn deref_mut(&mut self) -> &mut Self::Target { + self.maybe_changed = true; + unsafe { &mut self.index.unwrap_unchecked().as_mut().val } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn const_new() { + static mut _V1: IntrusiveSortedLinkedList = IntrusiveSortedLinkedList::new(); + } + + #[test] + fn test_peek() { + let mut ll: IntrusiveSortedLinkedList = IntrusiveSortedLinkedList::new(); + + let mut a = Node { val: 1, next: None }; + ll.push(&mut a); + assert_eq!(ll.peek().unwrap(), &1); + + let mut a = Node { val: 2, next: None }; + ll.push(&mut a); + assert_eq!(ll.peek().unwrap(), &2); + + let mut a = Node { val: 3, next: None }; + ll.push(&mut a); + assert_eq!(ll.peek().unwrap(), &3); + + let mut ll: IntrusiveSortedLinkedList = IntrusiveSortedLinkedList::new(); + + let mut a = Node { val: 2, next: None }; + ll.push(&mut a); + assert_eq!(ll.peek().unwrap(), &2); + + let mut a = Node { val: 1, next: None }; + ll.push(&mut a); + assert_eq!(ll.peek().unwrap(), &1); + + let mut a = Node { val: 3, next: None }; + ll.push(&mut a); + assert_eq!(ll.peek().unwrap(), &1); + } + + #[test] + fn test_empty() { + let ll: IntrusiveSortedLinkedList = IntrusiveSortedLinkedList::new(); + + assert!(ll.is_empty()) + } + + #[test] + fn test_updating() { + let mut ll: IntrusiveSortedLinkedList = IntrusiveSortedLinkedList::new(); + + let mut a = Node { val: 1, next: None }; + ll.push(&mut a); + + let mut a = Node { val: 2, next: None }; + ll.push(&mut a); + + let mut a = Node { val: 3, next: None }; + ll.push(&mut a); + + let mut find = ll.find_mut(|v| *v == 2).unwrap(); + + *find += 1000; + find.finish(); + + assert_eq!(ll.peek().unwrap(), &1002); + + let mut find = ll.find_mut(|v| *v == 3).unwrap(); + + *find += 1000; + find.finish(); + + assert_eq!(ll.peek().unwrap(), &1003); + + // Remove largest element + ll.find_mut(|v| *v == 1003).unwrap().pop(); + + assert_eq!(ll.peek().unwrap(), &1002); + } + + #[test] + fn test_updating_1() { + let mut ll: IntrusiveSortedLinkedList = IntrusiveSortedLinkedList::new(); + + let mut a = Node { val: 1, next: None }; + ll.push(&mut a); + + let v = ll.pop().unwrap(); + + assert_eq!(v.val, 1); + } + + #[test] + fn test_updating_2() { + let mut ll: IntrusiveSortedLinkedList = IntrusiveSortedLinkedList::new(); + + let mut a = Node { val: 1, next: None }; + ll.push(&mut a); + + let mut find = ll.find_mut(|v| *v == 1).unwrap(); + + *find += 1000; + find.finish(); + + assert_eq!(ll.peek().unwrap(), &1001); + } +} From b8b881f446a226d6f3c4a7db7c9174590b47dbf6 Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Thu, 19 Jan 2023 13:56:59 +0100 Subject: [PATCH 250/423] Fix so deny(missing_docs) work --- examples/async-task-multiple-prios.rs | 3 +++ examples/async-task.rs | 3 +++ examples/big-struct-opt.rs | 1 + examples/binds.rs | 1 + examples/complex.rs | 1 + examples/declared_locals.rs | 1 + examples/destructure.rs | 1 + examples/extern_binds.rs | 1 + examples/extern_spawn.rs | 1 + examples/generics.rs | 1 + examples/hardware.rs | 1 + examples/idle-wfi.rs | 1 + examples/idle.rs | 1 + examples/init.rs | 1 + examples/locals.rs | 1 + examples/lock.rs | 1 + examples/multilock.rs | 1 + examples/not-sync.rs | 2 ++ examples/only-shared-access.rs | 1 + examples/peripherals-taken.rs | 3 +++ examples/preempt.rs | 1 + examples/ramfunc.rs | 2 ++ examples/resource-user-struct.rs | 1 + examples/shared.rs | 1 + examples/smallest.rs | 1 + examples/spawn.rs | 1 + examples/static.rs | 1 + examples/t-binds.rs | 1 + examples/t-cfg-resources.rs | 3 ++- examples/t-htask-main.rs | 3 +++ examples/t-idle-main.rs | 3 +++ examples/t-late-not-send.rs | 3 ++- examples/task.rs | 1 + examples/zero-prio-task.rs | 4 ++++ macros/src/codegen/local_resources_struct.rs | 2 ++ macros/src/codegen/module.rs | 1 + macros/src/codegen/shared_resources_struct.rs | 4 ++++ 37 files changed, 58 insertions(+), 2 deletions(-) diff --git a/examples/async-task-multiple-prios.rs b/examples/async-task-multiple-prios.rs index f614820cb5..5c9674d768 100644 --- a/examples/async-task-multiple-prios.rs +++ b/examples/async-task-multiple-prios.rs @@ -1,6 +1,9 @@ +//! examples/async-task-multiple-prios.rs + #![no_main] #![no_std] #![feature(type_alias_impl_trait)] +#![deny(missing_docs)] use panic_semihosting as _; diff --git a/examples/async-task.rs b/examples/async-task.rs index e1ab143258..7730c54d59 100644 --- a/examples/async-task.rs +++ b/examples/async-task.rs @@ -1,6 +1,9 @@ +//! examples/async-task.rs + #![no_main] #![no_std] #![feature(type_alias_impl_trait)] +#![deny(missing_docs)] use panic_semihosting as _; diff --git a/examples/big-struct-opt.rs b/examples/big-struct-opt.rs index 3100a0e22c..408a2dec92 100644 --- a/examples/big-struct-opt.rs +++ b/examples/big-struct-opt.rs @@ -6,6 +6,7 @@ #![no_main] #![no_std] #![feature(type_alias_impl_trait)] +#![deny(missing_docs)] use panic_semihosting as _; diff --git a/examples/binds.rs b/examples/binds.rs index 0c1ed971bd..cf078ffe9e 100644 --- a/examples/binds.rs +++ b/examples/binds.rs @@ -4,6 +4,7 @@ #![deny(warnings)] #![no_main] #![no_std] +#![deny(missing_docs)] use panic_semihosting as _; diff --git a/examples/complex.rs b/examples/complex.rs index ab3979244c..c1e9c6c6e0 100644 --- a/examples/complex.rs +++ b/examples/complex.rs @@ -2,6 +2,7 @@ #![deny(unsafe_code)] #![deny(warnings)] +#![deny(missing_docs)] #![no_main] #![no_std] diff --git a/examples/declared_locals.rs b/examples/declared_locals.rs index 79001aa5e5..c845191024 100644 --- a/examples/declared_locals.rs +++ b/examples/declared_locals.rs @@ -2,6 +2,7 @@ #![deny(unsafe_code)] #![deny(warnings)] +#![deny(missing_docs)] #![no_main] #![no_std] diff --git a/examples/destructure.rs b/examples/destructure.rs index dc5d8ef8ea..81eff3b412 100644 --- a/examples/destructure.rs +++ b/examples/destructure.rs @@ -2,6 +2,7 @@ #![deny(unsafe_code)] #![deny(warnings)] +#![deny(missing_docs)] #![no_main] #![no_std] #![feature(type_alias_impl_trait)] diff --git a/examples/extern_binds.rs b/examples/extern_binds.rs index b24e7a1929..142a11d0b1 100644 --- a/examples/extern_binds.rs +++ b/examples/extern_binds.rs @@ -2,6 +2,7 @@ #![deny(unsafe_code)] #![deny(warnings)] +#![deny(missing_docs)] #![no_main] #![no_std] diff --git a/examples/extern_spawn.rs b/examples/extern_spawn.rs index 8a3928d5b0..b2b95b9da4 100644 --- a/examples/extern_spawn.rs +++ b/examples/extern_spawn.rs @@ -2,6 +2,7 @@ #![deny(unsafe_code)] #![deny(warnings)] +#![deny(missing_docs)] #![no_main] #![no_std] #![feature(type_alias_impl_trait)] diff --git a/examples/generics.rs b/examples/generics.rs index dfd47adfb1..2f23cce94f 100644 --- a/examples/generics.rs +++ b/examples/generics.rs @@ -2,6 +2,7 @@ #![deny(unsafe_code)] #![deny(warnings)] +#![deny(missing_docs)] #![no_main] #![no_std] diff --git a/examples/hardware.rs b/examples/hardware.rs index 61eb6357aa..62ae0d66b6 100644 --- a/examples/hardware.rs +++ b/examples/hardware.rs @@ -2,6 +2,7 @@ #![deny(unsafe_code)] #![deny(warnings)] +#![deny(missing_docs)] #![no_main] #![no_std] diff --git a/examples/idle-wfi.rs b/examples/idle-wfi.rs index a68fe84560..8134ce3e0f 100644 --- a/examples/idle-wfi.rs +++ b/examples/idle-wfi.rs @@ -2,6 +2,7 @@ #![deny(unsafe_code)] #![deny(warnings)] +#![deny(missing_docs)] #![no_main] #![no_std] diff --git a/examples/idle.rs b/examples/idle.rs index 78f169777a..0c4bd044d0 100644 --- a/examples/idle.rs +++ b/examples/idle.rs @@ -2,6 +2,7 @@ #![deny(unsafe_code)] #![deny(warnings)] +#![deny(missing_docs)] #![no_main] #![no_std] diff --git a/examples/init.rs b/examples/init.rs index 1e362be702..c3081bf8ed 100644 --- a/examples/init.rs +++ b/examples/init.rs @@ -2,6 +2,7 @@ #![deny(unsafe_code)] #![deny(warnings)] +#![deny(missing_docs)] #![no_main] #![no_std] diff --git a/examples/locals.rs b/examples/locals.rs index 4e3b98bc86..ec3d59d8db 100644 --- a/examples/locals.rs +++ b/examples/locals.rs @@ -2,6 +2,7 @@ #![feature(type_alias_impl_trait)] #![deny(unsafe_code)] +#![deny(missing_docs)] #![deny(warnings)] #![no_main] #![no_std] diff --git a/examples/lock.rs b/examples/lock.rs index 3c1a5142a1..203ae6f47d 100644 --- a/examples/lock.rs +++ b/examples/lock.rs @@ -2,6 +2,7 @@ #![deny(unsafe_code)] #![deny(warnings)] +#![deny(missing_docs)] #![no_main] #![no_std] #![feature(type_alias_impl_trait)] diff --git a/examples/multilock.rs b/examples/multilock.rs index 2eb285ea9b..6208caccd4 100644 --- a/examples/multilock.rs +++ b/examples/multilock.rs @@ -2,6 +2,7 @@ #![deny(unsafe_code)] #![deny(warnings)] +#![deny(missing_docs)] #![no_main] #![no_std] #![feature(type_alias_impl_trait)] diff --git a/examples/not-sync.rs b/examples/not-sync.rs index 5d868dfb52..6d1ddaea58 100644 --- a/examples/not-sync.rs +++ b/examples/not-sync.rs @@ -2,6 +2,7 @@ // #![deny(unsafe_code)] #![deny(warnings)] +#![deny(missing_docs)] #![no_main] #![no_std] #![feature(type_alias_impl_trait)] @@ -9,6 +10,7 @@ use core::marker::PhantomData; use panic_semihosting as _; +/// Not sync pub struct NotSync { _0: PhantomData<*const ()>, data: u32, diff --git a/examples/only-shared-access.rs b/examples/only-shared-access.rs index 09cb23a594..1d006e636b 100644 --- a/examples/only-shared-access.rs +++ b/examples/only-shared-access.rs @@ -2,6 +2,7 @@ #![deny(unsafe_code)] #![deny(warnings)] +#![deny(missing_docs)] #![no_main] #![no_std] #![feature(type_alias_impl_trait)] diff --git a/examples/peripherals-taken.rs b/examples/peripherals-taken.rs index 9b014667cb..2f710e90c3 100644 --- a/examples/peripherals-taken.rs +++ b/examples/peripherals-taken.rs @@ -1,5 +1,8 @@ +//! examples/peripherals-taken.rs + #![deny(unsafe_code)] #![deny(warnings)] +#![deny(missing_docs)] #![no_main] #![no_std] diff --git a/examples/preempt.rs b/examples/preempt.rs index 960fc57111..4b11907c87 100644 --- a/examples/preempt.rs +++ b/examples/preempt.rs @@ -3,6 +3,7 @@ #![no_main] #![no_std] #![feature(type_alias_impl_trait)] +#![deny(missing_docs)] use panic_semihosting as _; use rtic::app; diff --git a/examples/ramfunc.rs b/examples/ramfunc.rs index 316f6d8c53..e2e7f67bb0 100644 --- a/examples/ramfunc.rs +++ b/examples/ramfunc.rs @@ -1,9 +1,11 @@ //! examples/ramfunc.rs #![deny(warnings)] +#![deny(missing_docs)] #![no_main] #![no_std] #![feature(type_alias_impl_trait)] + use panic_semihosting as _; #[rtic::app( diff --git a/examples/resource-user-struct.rs b/examples/resource-user-struct.rs index 2acbbc36fc..fcbacaea8c 100644 --- a/examples/resource-user-struct.rs +++ b/examples/resource-user-struct.rs @@ -2,6 +2,7 @@ #![deny(unsafe_code)] #![deny(warnings)] +#![deny(missing_docs)] #![no_main] #![no_std] diff --git a/examples/shared.rs b/examples/shared.rs index fd31cfb28a..d0633fbd0e 100644 --- a/examples/shared.rs +++ b/examples/shared.rs @@ -2,6 +2,7 @@ #![deny(unsafe_code)] #![deny(warnings)] +#![deny(missing_docs)] #![no_main] #![no_std] diff --git a/examples/smallest.rs b/examples/smallest.rs index 5071392db6..e54ae44813 100644 --- a/examples/smallest.rs +++ b/examples/smallest.rs @@ -2,6 +2,7 @@ #![no_main] #![no_std] +#![deny(missing_docs)] use panic_semihosting as _; // panic handler use rtic::app; diff --git a/examples/spawn.rs b/examples/spawn.rs index 384f0a0057..d30ecf1bb6 100644 --- a/examples/spawn.rs +++ b/examples/spawn.rs @@ -2,6 +2,7 @@ #![deny(unsafe_code)] #![deny(warnings)] +#![deny(missing_docs)] #![no_main] #![no_std] #![feature(type_alias_impl_trait)] diff --git a/examples/static.rs b/examples/static.rs index 822224e373..7f656f4529 100644 --- a/examples/static.rs +++ b/examples/static.rs @@ -2,6 +2,7 @@ #![deny(unsafe_code)] #![deny(warnings)] +#![deny(missing_docs)] #![no_main] #![no_std] #![feature(type_alias_impl_trait)] diff --git a/examples/t-binds.rs b/examples/t-binds.rs index 785348bc96..bdeb3917dd 100644 --- a/examples/t-binds.rs +++ b/examples/t-binds.rs @@ -2,6 +2,7 @@ #![deny(unsafe_code)] #![deny(warnings)] +#![deny(missing_docs)] #![no_main] #![no_std] diff --git a/examples/t-cfg-resources.rs b/examples/t-cfg-resources.rs index 0174f33e3b..0328700957 100644 --- a/examples/t-cfg-resources.rs +++ b/examples/t-cfg-resources.rs @@ -1,7 +1,8 @@ //! [compile-pass] check that `#[cfg]` attributes applied on resources work -//! + #![no_main] #![no_std] +#![deny(missing_docs)] use panic_semihosting as _; diff --git a/examples/t-htask-main.rs b/examples/t-htask-main.rs index 0595e9fc18..8f885bc173 100644 --- a/examples/t-htask-main.rs +++ b/examples/t-htask-main.rs @@ -1,5 +1,8 @@ +//! examples/h-task-main.rs + #![deny(unsafe_code)] #![deny(warnings)] +#![deny(missing_docs)] #![no_main] #![no_std] diff --git a/examples/t-idle-main.rs b/examples/t-idle-main.rs index 307ccb20f8..43215cf7c5 100644 --- a/examples/t-idle-main.rs +++ b/examples/t-idle-main.rs @@ -1,5 +1,8 @@ +//! examples/t-idle-main.rs + #![deny(unsafe_code)] #![deny(warnings)] +#![deny(missing_docs)] #![no_main] #![no_std] diff --git a/examples/t-late-not-send.rs b/examples/t-late-not-send.rs index 0fbf237b77..44d1d855b4 100644 --- a/examples/t-late-not-send.rs +++ b/examples/t-late-not-send.rs @@ -2,11 +2,12 @@ #![no_main] #![no_std] +#![deny(missing_docs)] use core::marker::PhantomData; - use panic_semihosting as _; +/// Not send pub struct NotSend { _0: PhantomData<*const ()>, } diff --git a/examples/task.rs b/examples/task.rs index 50287edd30..ab6a1e0e51 100644 --- a/examples/task.rs +++ b/examples/task.rs @@ -2,6 +2,7 @@ #![deny(unsafe_code)] #![deny(warnings)] +#![deny(missing_docs)] #![no_main] #![no_std] #![feature(type_alias_impl_trait)] diff --git a/examples/zero-prio-task.rs b/examples/zero-prio-task.rs index fc385092c4..c810e8fa87 100644 --- a/examples/zero-prio-task.rs +++ b/examples/zero-prio-task.rs @@ -1,10 +1,14 @@ +//! examples/zero-prio-task.rs + #![no_main] #![no_std] #![feature(type_alias_impl_trait)] +#![deny(missing_docs)] use core::marker::PhantomData; use panic_semihosting as _; +/// Does not impl send pub struct NotSend { _0: PhantomData<*const ()>, } diff --git a/macros/src/codegen/local_resources_struct.rs b/macros/src/codegen/local_resources_struct.rs index e268508bc4..100c3eb5dc 100644 --- a/macros/src/codegen/local_resources_struct.rs +++ b/macros/src/codegen/local_resources_struct.rs @@ -50,6 +50,7 @@ pub fn codegen(ctxt: Context, app: &App) -> (TokenStream2, TokenStream2) { fields.push(quote!( #(#cfgs)* + #[allow(missing_docs)] pub #name: &#lt mut #ty )); @@ -88,6 +89,7 @@ pub fn codegen(ctxt: Context, app: &App) -> (TokenStream2, TokenStream2) { let constructor = quote!( impl<'a> #ident<'a> { #[inline(always)] + #[allow(missing_docs)] pub unsafe fn new() -> Self { #ident { #(#values,)* diff --git a/macros/src/codegen/module.rs b/macros/src/codegen/module.rs index f4c188a466..4725b9a993 100644 --- a/macros/src/codegen/module.rs +++ b/macros/src/codegen/module.rs @@ -114,6 +114,7 @@ pub fn codegen(ctxt: Context, app: &App, analysis: &Analysis) -> TokenStream2 { #(#cfgs)* impl<'a> #internal_context_name<'a> { #[inline(always)] + #[allow(missing_docs)] pub unsafe fn new(#core) -> Self { #internal_context_name { __rtic_internal_p: ::core::marker::PhantomData, diff --git a/macros/src/codegen/shared_resources_struct.rs b/macros/src/codegen/shared_resources_struct.rs index 24c93de6c1..fa6f0fcb5e 100644 --- a/macros/src/codegen/shared_resources_struct.rs +++ b/macros/src/codegen/shared_resources_struct.rs @@ -47,16 +47,19 @@ pub fn codegen(ctxt: Context, app: &App) -> (TokenStream2, TokenStream2) { fields.push(quote!( #(#cfgs)* + #[allow(missing_docs)] pub #name: &#lt #mut_ #ty )); } else if access.is_shared() { fields.push(quote!( #(#cfgs)* + #[allow(missing_docs)] pub #name: &'a #ty )); } else { fields.push(quote!( #(#cfgs)* + #[allow(missing_docs)] pub #name: shared_resources::#shared_name<'a> )); @@ -103,6 +106,7 @@ pub fn codegen(ctxt: Context, app: &App) -> (TokenStream2, TokenStream2) { let constructor = quote!( impl<'a> #ident<'a> { #[inline(always)] + #[allow(missing_docs)] pub unsafe fn new() -> Self { #ident { #(#values,)* From 306aa47170fd59369b7a184924e287dc3706d64d Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Mon, 23 Jan 2023 20:05:47 +0100 Subject: [PATCH 251/423] Add rtic-timer (timerqueue + monotonic) and rtic-monotonics (systick-monotonic) --- ci/expected/cfg-monotonic.run | 0 examples/cfg-monotonic.rs | 121 ----- macros/src/tests.rs | 4 - macros/src/tests/single.rs | 40 -- rtic-monotonics/.gitignore | 6 + rtic-monotonics/Cargo.toml | 12 + .../rust-toolchain.toml | 0 rtic-monotonics/src/lib.rs | 11 + rtic-monotonics/src/systick_monotonic.rs | 1 + rtic-timer/.gitignore | 6 + rtic-timer/Cargo.toml | 7 +- rtic-timer/rust-toolchain.toml | 4 + rtic-timer/src/lib.rs | 442 +++++++++++++----- rtic-timer/src/linked_list.rs | 173 +++++++ rtic-timer/src/monotonic.rs | 60 +++ rtic-timer/src/sll.rs | 421 ----------------- {.cargo => rtic/.cargo}/config.toml | 0 rtic/.gitignore | 6 + CHANGELOG.md => rtic/CHANGELOG.md | 0 Cargo.toml => rtic/Cargo.toml | 0 build.rs => rtic/build.rs | 0 {ci => rtic/ci}/expected/async-delay.run | 0 .../ci}/expected/async-infinite-loop.run | 0 .../expected/async-task-multiple-prios.run | 0 {ci => rtic/ci}/expected/async-task.run | 0 {ci => rtic/ci}/expected/async-timeout.run | 0 {ci => rtic/ci}/expected/big-struct-opt.run | 0 {ci => rtic/ci}/expected/binds.run | 0 .../ci}/expected/cancel-reschedule.run | 0 {ci => rtic/ci}/expected/capacity.run | 0 {ci => rtic/ci}/expected/cfg-whole-task.run | 0 {ci => rtic/ci}/expected/common.run | 0 {ci => rtic/ci}/expected/complex.run | 0 {ci => rtic/ci}/expected/declared_locals.run | 0 {ci => rtic/ci}/expected/destructure.run | 0 {ci => rtic/ci}/expected/extern_binds.run | 0 {ci => rtic/ci}/expected/extern_spawn.run | 0 {ci => rtic/ci}/expected/generics.run | 0 {ci => rtic/ci}/expected/hardware.run | 0 {ci => rtic/ci}/expected/idle-wfi.run | 0 {ci => rtic/ci}/expected/idle.run | 0 {ci => rtic/ci}/expected/init.run | 0 {ci => rtic/ci}/expected/locals.run | 0 {ci => rtic/ci}/expected/lock-free.run | 0 {ci => rtic/ci}/expected/lock.run | 0 {ci => rtic/ci}/expected/message.run | 0 {ci => rtic/ci}/expected/message_passing.run | 0 {ci => rtic/ci}/expected/multilock.run | 0 {ci => rtic/ci}/expected/not-sync.run | 0 .../ci}/expected/only-shared-access.run | 0 {ci => rtic/ci}/expected/periodic-at.run | 0 {ci => rtic/ci}/expected/periodic-at2.run | 0 {ci => rtic/ci}/expected/periodic.run | 0 .../ci}/expected/peripherals-taken.run | 0 {ci => rtic/ci}/expected/pool.run | 0 {ci => rtic/ci}/expected/preempt.run | 0 {ci => rtic/ci}/expected/ramfunc.run | 0 {ci => rtic/ci}/expected/ramfunc.run.grep.bar | 0 {ci => rtic/ci}/expected/ramfunc.run.grep.foo | 0 .../ci}/expected/resource-user-struct.run | 0 {ci => rtic/ci}/expected/schedule.run | 0 {ci => rtic/ci}/expected/shared.run | 0 {ci => rtic/ci}/expected/smallest.run | 0 {ci => rtic/ci}/expected/spawn.run | 0 {ci => rtic/ci}/expected/static.run | 0 {ci => rtic/ci}/expected/t-binds.run | 0 {ci => rtic/ci}/expected/t-cfg-resources.run | 0 {ci => rtic/ci}/expected/t-htask-main.run | 0 {ci => rtic/ci}/expected/t-idle-main.run | 0 {ci => rtic/ci}/expected/t-late-not-send.run | 0 {ci => rtic/ci}/expected/t-schedule.run | 0 {ci => rtic/ci}/expected/t-spawn.run | 0 {ci => rtic/ci}/expected/task.run | 0 {ci => rtic/ci}/expected/zero-prio-task.run | 0 {examples => rtic/examples}/async-delay.no_rs | 0 .../examples}/async-infinite-loop.no_rs | 0 .../examples}/async-task-multiple-prios.rs | 0 {examples => rtic/examples}/async-task.rs | 0 .../examples}/async-timeout.no_rs | 0 {examples => rtic/examples}/big-struct-opt.rs | 0 {examples => rtic/examples}/binds.rs | 0 .../examples}/cancel-reschedule.no_rs | 0 {examples => rtic/examples}/capacity.no_rs | 0 .../examples}/cfg-whole-task.no_rs | 0 {examples => rtic/examples}/common.no_rs | 0 {examples => rtic/examples}/complex.rs | 0 .../examples}/declared_locals.rs | 0 {examples => rtic/examples}/destructure.rs | 0 {examples => rtic/examples}/extern_binds.rs | 0 {examples => rtic/examples}/extern_spawn.rs | 0 {examples => rtic/examples}/generics.rs | 0 {examples => rtic/examples}/hardware.rs | 0 {examples => rtic/examples}/idle-wfi.rs | 0 {examples => rtic/examples}/idle.rs | 0 {examples => rtic/examples}/init.rs | 0 {examples => rtic/examples}/locals.rs | 0 {examples => rtic/examples}/lock-free.no_rs | 0 {examples => rtic/examples}/lock.rs | 0 {examples => rtic/examples}/message.no_rs | 0 .../examples}/message_passing.no_rs | 0 {examples => rtic/examples}/multilock.rs | 0 {examples => rtic/examples}/not-sync.rs | 0 .../examples}/only-shared-access.rs | 0 {examples => rtic/examples}/periodic-at.no_rs | 0 .../examples}/periodic-at2.no_rs | 0 {examples => rtic/examples}/periodic.no_rs | 0 .../examples}/peripherals-taken.rs | 0 {examples => rtic/examples}/pool.no_rs | 0 {examples => rtic/examples}/preempt.rs | 0 {examples => rtic/examples}/ramfunc.rs | 0 .../examples}/resource-user-struct.rs | 0 {examples => rtic/examples}/schedule.no_rs | 0 {examples => rtic/examples}/shared.rs | 0 {examples => rtic/examples}/smallest.rs | 0 {examples => rtic/examples}/spawn.rs | 0 {examples => rtic/examples}/static.rs | 0 {examples => rtic/examples}/t-binds.rs | 0 .../examples}/t-cfg-resources.rs | 0 {examples => rtic/examples}/t-htask-main.rs | 0 {examples => rtic/examples}/t-idle-main.rs | 0 .../examples}/t-late-not-send.rs | 0 {examples => rtic/examples}/t-schedule.no_rs | 0 {examples => rtic/examples}/t-spawn.no_rs | 0 {examples => rtic/examples}/task.rs | 0 {examples => rtic/examples}/zero-prio-task.rs | 0 {macros => rtic/macros}/.gitignore | 0 {macros => rtic/macros}/Cargo.toml | 2 +- {macros => rtic/macros}/src/analyze.rs | 0 {macros => rtic/macros}/src/bindings.rs | 0 {macros => rtic/macros}/src/check.rs | 0 {macros => rtic/macros}/src/codegen.rs | 0 .../macros}/src/codegen/assertions.rs | 0 .../macros}/src/codegen/async_dispatchers.rs | 0 .../macros}/src/codegen/hardware_tasks.rs | 0 {macros => rtic/macros}/src/codegen/idle.rs | 0 {macros => rtic/macros}/src/codegen/init.rs | 0 .../macros}/src/codegen/local_resources.rs | 0 .../src/codegen/local_resources_struct.rs | 0 {macros => rtic/macros}/src/codegen/main.rs | 0 {macros => rtic/macros}/src/codegen/module.rs | 0 .../macros}/src/codegen/post_init.rs | 0 .../macros}/src/codegen/pre_init.rs | 0 .../macros}/src/codegen/shared_resources.rs | 0 .../src/codegen/shared_resources_struct.rs | 0 .../macros}/src/codegen/software_tasks.rs | 0 {macros => rtic/macros}/src/codegen/util.rs | 0 {macros => rtic/macros}/src/lib.rs | 0 {macros => rtic/macros}/src/syntax.rs | 0 .../macros}/src/syntax/.github/bors.toml | 0 .../src/syntax/.github/workflows/build.yml | 0 .../syntax/.github/workflows/changelog.yml | 0 .../properties/build.properties.json | 0 {macros => rtic/macros}/src/syntax/.gitignore | 0 .../macros}/src/syntax/.travis.yml | 0 .../macros}/src/syntax/accessors.rs | 0 {macros => rtic/macros}/src/syntax/analyze.rs | 0 {macros => rtic/macros}/src/syntax/ast.rs | 0 {macros => rtic/macros}/src/syntax/check.rs | 0 .../macros}/src/syntax/optimize.rs | 0 {macros => rtic/macros}/src/syntax/parse.rs | 0 .../macros}/src/syntax/parse/app.rs | 0 .../macros}/src/syntax/parse/hardware_task.rs | 0 .../macros}/src/syntax/parse/idle.rs | 0 .../macros}/src/syntax/parse/init.rs | 0 .../macros}/src/syntax/parse/resource.rs | 0 .../macros}/src/syntax/parse/software_task.rs | 0 .../macros}/src/syntax/parse/util.rs | 0 {macros => rtic/macros}/tests/ui.rs | 0 .../macros}/ui/extern-interrupt-used.rs | 0 .../macros}/ui/extern-interrupt-used.stderr | 0 .../macros}/ui/idle-double-local.rs | 0 .../macros}/ui/idle-double-local.stderr | 0 .../macros}/ui/idle-double-shared.rs | 0 .../macros}/ui/idle-double-shared.stderr | 0 {macros => rtic/macros}/ui/idle-input.rs | 0 {macros => rtic/macros}/ui/idle-input.stderr | 0 {macros => rtic/macros}/ui/idle-no-context.rs | 0 .../macros}/ui/idle-no-context.stderr | 0 .../macros}/ui/idle-not-divergent.rs | 0 .../macros}/ui/idle-not-divergent.stderr | 0 {macros => rtic/macros}/ui/idle-output.rs | 0 {macros => rtic/macros}/ui/idle-output.stderr | 0 {macros => rtic/macros}/ui/idle-pub.rs | 0 {macros => rtic/macros}/ui/idle-pub.stderr | 0 {macros => rtic/macros}/ui/idle-unsafe.rs | 0 {macros => rtic/macros}/ui/idle-unsafe.stderr | 0 {macros => rtic/macros}/ui/init-divergent.rs | 0 .../macros}/ui/init-divergent.stderr | 0 .../macros}/ui/init-double-local.rs | 0 .../macros}/ui/init-double-local.stderr | 0 .../macros}/ui/init-double-shared.rs | 0 .../macros}/ui/init-double-shared.stderr | 0 {macros => rtic/macros}/ui/init-input.rs | 0 {macros => rtic/macros}/ui/init-input.stderr | 0 {macros => rtic/macros}/ui/init-no-context.rs | 0 .../macros}/ui/init-no-context.stderr | 0 {macros => rtic/macros}/ui/init-output.rs | 0 {macros => rtic/macros}/ui/init-output.stderr | 0 {macros => rtic/macros}/ui/init-pub.rs | 0 {macros => rtic/macros}/ui/init-pub.stderr | 0 {macros => rtic/macros}/ui/init-unsafe.rs | 0 {macros => rtic/macros}/ui/init-unsafe.stderr | 0 .../macros}/ui/interrupt-double.rs | 0 .../macros}/ui/interrupt-double.stderr | 0 .../macros}/ui/local-collision-2.rs | 0 .../macros}/ui/local-collision-2.stderr | 0 {macros => rtic/macros}/ui/local-collision.rs | 0 .../macros}/ui/local-collision.stderr | 0 .../macros}/ui/local-malformed-1.rs | 0 .../macros}/ui/local-malformed-1.stderr | 0 .../macros}/ui/local-malformed-2.rs | 0 .../macros}/ui/local-malformed-2.stderr | 0 .../macros}/ui/local-malformed-3.rs | 0 .../macros}/ui/local-malformed-3.stderr | 0 .../macros}/ui/local-malformed-4.rs | 0 .../macros}/ui/local-malformed-4.stderr | 0 .../macros}/ui/local-not-declared.rs | 0 .../macros}/ui/local-not-declared.stderr | 0 {macros => rtic/macros}/ui/local-pub.rs | 0 {macros => rtic/macros}/ui/local-pub.stderr | 0 .../macros}/ui/local-shared-attribute.rs | 0 .../macros}/ui/local-shared-attribute.stderr | 0 {macros => rtic/macros}/ui/local-shared.rs | 0 .../macros}/ui/local-shared.stderr | 0 .../macros}/ui/shared-lock-free.rs | 0 .../macros}/ui/shared-lock-free.stderr | 0 .../macros}/ui/shared-not-declared.rs | 0 .../macros}/ui/shared-not-declared.stderr | 0 {macros => rtic/macros}/ui/shared-pub.rs | 0 {macros => rtic/macros}/ui/shared-pub.stderr | 0 {macros => rtic/macros}/ui/task-divergent.rs | 0 .../macros}/ui/task-divergent.stderr | 0 .../macros}/ui/task-double-local.rs | 0 .../macros}/ui/task-double-local.stderr | 0 .../macros}/ui/task-double-priority.rs | 0 .../macros}/ui/task-double-priority.stderr | 0 .../macros}/ui/task-double-shared.rs | 0 .../macros}/ui/task-double-shared.stderr | 0 {macros => rtic/macros}/ui/task-idle.rs | 0 {macros => rtic/macros}/ui/task-idle.stderr | 0 {macros => rtic/macros}/ui/task-init.rs | 0 {macros => rtic/macros}/ui/task-init.stderr | 0 {macros => rtic/macros}/ui/task-interrupt.rs | 0 .../macros}/ui/task-interrupt.stderr | 0 {macros => rtic/macros}/ui/task-no-context.rs | 0 .../macros}/ui/task-no-context.stderr | 0 .../macros}/ui/task-priority-too-high.rs | 0 .../macros}/ui/task-priority-too-high.stderr | 0 .../macros}/ui/task-priority-too-low.rs | 0 .../macros}/ui/task-priority-too-low.stderr | 0 {macros => rtic/macros}/ui/task-pub.rs | 0 {macros => rtic/macros}/ui/task-pub.stderr | 0 {macros => rtic/macros}/ui/task-unsafe.rs | 0 {macros => rtic/macros}/ui/task-unsafe.stderr | 0 {macros => rtic/macros}/ui/task-zero-prio.rs | 0 .../macros}/ui/task-zero-prio.stderr | 0 rtic/rust-toolchain.toml | 4 + {src => rtic/src}/export.rs | 0 {src => rtic/src}/export/executor.rs | 0 {src => rtic/src}/lib.rs | 0 {tests => rtic/tests}/tests.rs | 0 {ui => rtic/ui}/exception-invalid.rs | 0 {ui => rtic/ui}/exception-invalid.stderr | 0 .../ui}/extern-interrupt-not-enough.rs | 0 .../ui}/extern-interrupt-not-enough.stderr | 0 {ui => rtic/ui}/extern-interrupt-used.rs | 0 {ui => rtic/ui}/extern-interrupt-used.stderr | 0 {ui => rtic/ui}/task-priority-too-high.rs | 0 {ui => rtic/ui}/task-priority-too-high.stderr | 0 {ui => rtic/ui}/unknown-interrupt.rs | 0 {ui => rtic/ui}/unknown-interrupt.stderr | 0 .../ui}/v6m-interrupt-not-enough.rs_no | 0 {xtask => rtic/xtask}/Cargo.toml | 0 {xtask => rtic/xtask}/src/build.rs | 0 {xtask => rtic/xtask}/src/command.rs | 0 {xtask => rtic/xtask}/src/main.rs | 0 276 files changed, 607 insertions(+), 713 deletions(-) delete mode 100644 ci/expected/cfg-monotonic.run delete mode 100644 examples/cfg-monotonic.rs delete mode 100644 macros/src/tests.rs delete mode 100644 macros/src/tests/single.rs create mode 100644 rtic-monotonics/.gitignore create mode 100644 rtic-monotonics/Cargo.toml rename rust-toolchain.toml => rtic-monotonics/rust-toolchain.toml (100%) create mode 100644 rtic-monotonics/src/lib.rs create mode 100644 rtic-monotonics/src/systick_monotonic.rs create mode 100644 rtic-timer/.gitignore create mode 100644 rtic-timer/rust-toolchain.toml create mode 100644 rtic-timer/src/linked_list.rs create mode 100644 rtic-timer/src/monotonic.rs delete mode 100644 rtic-timer/src/sll.rs rename {.cargo => rtic/.cargo}/config.toml (100%) create mode 100644 rtic/.gitignore rename CHANGELOG.md => rtic/CHANGELOG.md (100%) rename Cargo.toml => rtic/Cargo.toml (100%) rename build.rs => rtic/build.rs (100%) rename {ci => rtic/ci}/expected/async-delay.run (100%) rename {ci => rtic/ci}/expected/async-infinite-loop.run (100%) rename {ci => rtic/ci}/expected/async-task-multiple-prios.run (100%) rename {ci => rtic/ci}/expected/async-task.run (100%) rename {ci => rtic/ci}/expected/async-timeout.run (100%) rename {ci => rtic/ci}/expected/big-struct-opt.run (100%) rename {ci => rtic/ci}/expected/binds.run (100%) rename {ci => rtic/ci}/expected/cancel-reschedule.run (100%) rename {ci => rtic/ci}/expected/capacity.run (100%) rename {ci => rtic/ci}/expected/cfg-whole-task.run (100%) rename {ci => rtic/ci}/expected/common.run (100%) rename {ci => rtic/ci}/expected/complex.run (100%) rename {ci => rtic/ci}/expected/declared_locals.run (100%) rename {ci => rtic/ci}/expected/destructure.run (100%) rename {ci => rtic/ci}/expected/extern_binds.run (100%) rename {ci => rtic/ci}/expected/extern_spawn.run (100%) rename {ci => rtic/ci}/expected/generics.run (100%) rename {ci => rtic/ci}/expected/hardware.run (100%) rename {ci => rtic/ci}/expected/idle-wfi.run (100%) rename {ci => rtic/ci}/expected/idle.run (100%) rename {ci => rtic/ci}/expected/init.run (100%) rename {ci => rtic/ci}/expected/locals.run (100%) rename {ci => rtic/ci}/expected/lock-free.run (100%) rename {ci => rtic/ci}/expected/lock.run (100%) rename {ci => rtic/ci}/expected/message.run (100%) rename {ci => rtic/ci}/expected/message_passing.run (100%) rename {ci => rtic/ci}/expected/multilock.run (100%) rename {ci => rtic/ci}/expected/not-sync.run (100%) rename {ci => rtic/ci}/expected/only-shared-access.run (100%) rename {ci => rtic/ci}/expected/periodic-at.run (100%) rename {ci => rtic/ci}/expected/periodic-at2.run (100%) rename {ci => rtic/ci}/expected/periodic.run (100%) rename {ci => rtic/ci}/expected/peripherals-taken.run (100%) rename {ci => rtic/ci}/expected/pool.run (100%) rename {ci => rtic/ci}/expected/preempt.run (100%) rename {ci => rtic/ci}/expected/ramfunc.run (100%) rename {ci => rtic/ci}/expected/ramfunc.run.grep.bar (100%) rename {ci => rtic/ci}/expected/ramfunc.run.grep.foo (100%) rename {ci => rtic/ci}/expected/resource-user-struct.run (100%) rename {ci => rtic/ci}/expected/schedule.run (100%) rename {ci => rtic/ci}/expected/shared.run (100%) rename {ci => rtic/ci}/expected/smallest.run (100%) rename {ci => rtic/ci}/expected/spawn.run (100%) rename {ci => rtic/ci}/expected/static.run (100%) rename {ci => rtic/ci}/expected/t-binds.run (100%) rename {ci => rtic/ci}/expected/t-cfg-resources.run (100%) rename {ci => rtic/ci}/expected/t-htask-main.run (100%) rename {ci => rtic/ci}/expected/t-idle-main.run (100%) rename {ci => rtic/ci}/expected/t-late-not-send.run (100%) rename {ci => rtic/ci}/expected/t-schedule.run (100%) rename {ci => rtic/ci}/expected/t-spawn.run (100%) rename {ci => rtic/ci}/expected/task.run (100%) rename {ci => rtic/ci}/expected/zero-prio-task.run (100%) rename {examples => rtic/examples}/async-delay.no_rs (100%) rename {examples => rtic/examples}/async-infinite-loop.no_rs (100%) rename {examples => rtic/examples}/async-task-multiple-prios.rs (100%) rename {examples => rtic/examples}/async-task.rs (100%) rename {examples => rtic/examples}/async-timeout.no_rs (100%) rename {examples => rtic/examples}/big-struct-opt.rs (100%) rename {examples => rtic/examples}/binds.rs (100%) rename {examples => rtic/examples}/cancel-reschedule.no_rs (100%) rename {examples => rtic/examples}/capacity.no_rs (100%) rename {examples => rtic/examples}/cfg-whole-task.no_rs (100%) rename {examples => rtic/examples}/common.no_rs (100%) rename {examples => rtic/examples}/complex.rs (100%) rename {examples => rtic/examples}/declared_locals.rs (100%) rename {examples => rtic/examples}/destructure.rs (100%) rename {examples => rtic/examples}/extern_binds.rs (100%) rename {examples => rtic/examples}/extern_spawn.rs (100%) rename {examples => rtic/examples}/generics.rs (100%) rename {examples => rtic/examples}/hardware.rs (100%) rename {examples => rtic/examples}/idle-wfi.rs (100%) rename {examples => rtic/examples}/idle.rs (100%) rename {examples => rtic/examples}/init.rs (100%) rename {examples => rtic/examples}/locals.rs (100%) rename {examples => rtic/examples}/lock-free.no_rs (100%) rename {examples => rtic/examples}/lock.rs (100%) rename {examples => rtic/examples}/message.no_rs (100%) rename {examples => rtic/examples}/message_passing.no_rs (100%) rename {examples => rtic/examples}/multilock.rs (100%) rename {examples => rtic/examples}/not-sync.rs (100%) rename {examples => rtic/examples}/only-shared-access.rs (100%) rename {examples => rtic/examples}/periodic-at.no_rs (100%) rename {examples => rtic/examples}/periodic-at2.no_rs (100%) rename {examples => rtic/examples}/periodic.no_rs (100%) rename {examples => rtic/examples}/peripherals-taken.rs (100%) rename {examples => rtic/examples}/pool.no_rs (100%) rename {examples => rtic/examples}/preempt.rs (100%) rename {examples => rtic/examples}/ramfunc.rs (100%) rename {examples => rtic/examples}/resource-user-struct.rs (100%) rename {examples => rtic/examples}/schedule.no_rs (100%) rename {examples => rtic/examples}/shared.rs (100%) rename {examples => rtic/examples}/smallest.rs (100%) rename {examples => rtic/examples}/spawn.rs (100%) rename {examples => rtic/examples}/static.rs (100%) rename {examples => rtic/examples}/t-binds.rs (100%) rename {examples => rtic/examples}/t-cfg-resources.rs (100%) rename {examples => rtic/examples}/t-htask-main.rs (100%) rename {examples => rtic/examples}/t-idle-main.rs (100%) rename {examples => rtic/examples}/t-late-not-send.rs (100%) rename {examples => rtic/examples}/t-schedule.no_rs (100%) rename {examples => rtic/examples}/t-spawn.no_rs (100%) rename {examples => rtic/examples}/task.rs (100%) rename {examples => rtic/examples}/zero-prio-task.rs (100%) rename {macros => rtic/macros}/.gitignore (100%) rename {macros => rtic/macros}/Cargo.toml (99%) rename {macros => rtic/macros}/src/analyze.rs (100%) rename {macros => rtic/macros}/src/bindings.rs (100%) rename {macros => rtic/macros}/src/check.rs (100%) rename {macros => rtic/macros}/src/codegen.rs (100%) rename {macros => rtic/macros}/src/codegen/assertions.rs (100%) rename {macros => rtic/macros}/src/codegen/async_dispatchers.rs (100%) rename {macros => rtic/macros}/src/codegen/hardware_tasks.rs (100%) rename {macros => rtic/macros}/src/codegen/idle.rs (100%) rename {macros => rtic/macros}/src/codegen/init.rs (100%) rename {macros => rtic/macros}/src/codegen/local_resources.rs (100%) rename {macros => rtic/macros}/src/codegen/local_resources_struct.rs (100%) rename {macros => rtic/macros}/src/codegen/main.rs (100%) rename {macros => rtic/macros}/src/codegen/module.rs (100%) rename {macros => rtic/macros}/src/codegen/post_init.rs (100%) rename {macros => rtic/macros}/src/codegen/pre_init.rs (100%) rename {macros => rtic/macros}/src/codegen/shared_resources.rs (100%) rename {macros => rtic/macros}/src/codegen/shared_resources_struct.rs (100%) rename {macros => rtic/macros}/src/codegen/software_tasks.rs (100%) rename {macros => rtic/macros}/src/codegen/util.rs (100%) rename {macros => rtic/macros}/src/lib.rs (100%) rename {macros => rtic/macros}/src/syntax.rs (100%) rename {macros => rtic/macros}/src/syntax/.github/bors.toml (100%) rename {macros => rtic/macros}/src/syntax/.github/workflows/build.yml (100%) rename {macros => rtic/macros}/src/syntax/.github/workflows/changelog.yml (100%) rename {macros => rtic/macros}/src/syntax/.github/workflows/properties/build.properties.json (100%) rename {macros => rtic/macros}/src/syntax/.gitignore (100%) rename {macros => rtic/macros}/src/syntax/.travis.yml (100%) rename {macros => rtic/macros}/src/syntax/accessors.rs (100%) rename {macros => rtic/macros}/src/syntax/analyze.rs (100%) rename {macros => rtic/macros}/src/syntax/ast.rs (100%) rename {macros => rtic/macros}/src/syntax/check.rs (100%) rename {macros => rtic/macros}/src/syntax/optimize.rs (100%) rename {macros => rtic/macros}/src/syntax/parse.rs (100%) rename {macros => rtic/macros}/src/syntax/parse/app.rs (100%) rename {macros => rtic/macros}/src/syntax/parse/hardware_task.rs (100%) rename {macros => rtic/macros}/src/syntax/parse/idle.rs (100%) rename {macros => rtic/macros}/src/syntax/parse/init.rs (100%) rename {macros => rtic/macros}/src/syntax/parse/resource.rs (100%) rename {macros => rtic/macros}/src/syntax/parse/software_task.rs (100%) rename {macros => rtic/macros}/src/syntax/parse/util.rs (100%) rename {macros => rtic/macros}/tests/ui.rs (100%) rename {macros => rtic/macros}/ui/extern-interrupt-used.rs (100%) rename {macros => rtic/macros}/ui/extern-interrupt-used.stderr (100%) rename {macros => rtic/macros}/ui/idle-double-local.rs (100%) rename {macros => rtic/macros}/ui/idle-double-local.stderr (100%) rename {macros => rtic/macros}/ui/idle-double-shared.rs (100%) rename {macros => rtic/macros}/ui/idle-double-shared.stderr (100%) rename {macros => rtic/macros}/ui/idle-input.rs (100%) rename {macros => rtic/macros}/ui/idle-input.stderr (100%) rename {macros => rtic/macros}/ui/idle-no-context.rs (100%) rename {macros => rtic/macros}/ui/idle-no-context.stderr (100%) rename {macros => rtic/macros}/ui/idle-not-divergent.rs (100%) rename {macros => rtic/macros}/ui/idle-not-divergent.stderr (100%) rename {macros => rtic/macros}/ui/idle-output.rs (100%) rename {macros => rtic/macros}/ui/idle-output.stderr (100%) rename {macros => rtic/macros}/ui/idle-pub.rs (100%) rename {macros => rtic/macros}/ui/idle-pub.stderr (100%) rename {macros => rtic/macros}/ui/idle-unsafe.rs (100%) rename {macros => rtic/macros}/ui/idle-unsafe.stderr (100%) rename {macros => rtic/macros}/ui/init-divergent.rs (100%) rename {macros => rtic/macros}/ui/init-divergent.stderr (100%) rename {macros => rtic/macros}/ui/init-double-local.rs (100%) rename {macros => rtic/macros}/ui/init-double-local.stderr (100%) rename {macros => rtic/macros}/ui/init-double-shared.rs (100%) rename {macros => rtic/macros}/ui/init-double-shared.stderr (100%) rename {macros => rtic/macros}/ui/init-input.rs (100%) rename {macros => rtic/macros}/ui/init-input.stderr (100%) rename {macros => rtic/macros}/ui/init-no-context.rs (100%) rename {macros => rtic/macros}/ui/init-no-context.stderr (100%) rename {macros => rtic/macros}/ui/init-output.rs (100%) rename {macros => rtic/macros}/ui/init-output.stderr (100%) rename {macros => rtic/macros}/ui/init-pub.rs (100%) rename {macros => rtic/macros}/ui/init-pub.stderr (100%) rename {macros => rtic/macros}/ui/init-unsafe.rs (100%) rename {macros => rtic/macros}/ui/init-unsafe.stderr (100%) rename {macros => rtic/macros}/ui/interrupt-double.rs (100%) rename {macros => rtic/macros}/ui/interrupt-double.stderr (100%) rename {macros => rtic/macros}/ui/local-collision-2.rs (100%) rename {macros => rtic/macros}/ui/local-collision-2.stderr (100%) rename {macros => rtic/macros}/ui/local-collision.rs (100%) rename {macros => rtic/macros}/ui/local-collision.stderr (100%) rename {macros => rtic/macros}/ui/local-malformed-1.rs (100%) rename {macros => rtic/macros}/ui/local-malformed-1.stderr (100%) rename {macros => rtic/macros}/ui/local-malformed-2.rs (100%) rename {macros => rtic/macros}/ui/local-malformed-2.stderr (100%) rename {macros => rtic/macros}/ui/local-malformed-3.rs (100%) rename {macros => rtic/macros}/ui/local-malformed-3.stderr (100%) rename {macros => rtic/macros}/ui/local-malformed-4.rs (100%) rename {macros => rtic/macros}/ui/local-malformed-4.stderr (100%) rename {macros => rtic/macros}/ui/local-not-declared.rs (100%) rename {macros => rtic/macros}/ui/local-not-declared.stderr (100%) rename {macros => rtic/macros}/ui/local-pub.rs (100%) rename {macros => rtic/macros}/ui/local-pub.stderr (100%) rename {macros => rtic/macros}/ui/local-shared-attribute.rs (100%) rename {macros => rtic/macros}/ui/local-shared-attribute.stderr (100%) rename {macros => rtic/macros}/ui/local-shared.rs (100%) rename {macros => rtic/macros}/ui/local-shared.stderr (100%) rename {macros => rtic/macros}/ui/shared-lock-free.rs (100%) rename {macros => rtic/macros}/ui/shared-lock-free.stderr (100%) rename {macros => rtic/macros}/ui/shared-not-declared.rs (100%) rename {macros => rtic/macros}/ui/shared-not-declared.stderr (100%) rename {macros => rtic/macros}/ui/shared-pub.rs (100%) rename {macros => rtic/macros}/ui/shared-pub.stderr (100%) rename {macros => rtic/macros}/ui/task-divergent.rs (100%) rename {macros => rtic/macros}/ui/task-divergent.stderr (100%) rename {macros => rtic/macros}/ui/task-double-local.rs (100%) rename {macros => rtic/macros}/ui/task-double-local.stderr (100%) rename {macros => rtic/macros}/ui/task-double-priority.rs (100%) rename {macros => rtic/macros}/ui/task-double-priority.stderr (100%) rename {macros => rtic/macros}/ui/task-double-shared.rs (100%) rename {macros => rtic/macros}/ui/task-double-shared.stderr (100%) rename {macros => rtic/macros}/ui/task-idle.rs (100%) rename {macros => rtic/macros}/ui/task-idle.stderr (100%) rename {macros => rtic/macros}/ui/task-init.rs (100%) rename {macros => rtic/macros}/ui/task-init.stderr (100%) rename {macros => rtic/macros}/ui/task-interrupt.rs (100%) rename {macros => rtic/macros}/ui/task-interrupt.stderr (100%) rename {macros => rtic/macros}/ui/task-no-context.rs (100%) rename {macros => rtic/macros}/ui/task-no-context.stderr (100%) rename {macros => rtic/macros}/ui/task-priority-too-high.rs (100%) rename {macros => rtic/macros}/ui/task-priority-too-high.stderr (100%) rename {macros => rtic/macros}/ui/task-priority-too-low.rs (100%) rename {macros => rtic/macros}/ui/task-priority-too-low.stderr (100%) rename {macros => rtic/macros}/ui/task-pub.rs (100%) rename {macros => rtic/macros}/ui/task-pub.stderr (100%) rename {macros => rtic/macros}/ui/task-unsafe.rs (100%) rename {macros => rtic/macros}/ui/task-unsafe.stderr (100%) rename {macros => rtic/macros}/ui/task-zero-prio.rs (100%) rename {macros => rtic/macros}/ui/task-zero-prio.stderr (100%) create mode 100644 rtic/rust-toolchain.toml rename {src => rtic/src}/export.rs (100%) rename {src => rtic/src}/export/executor.rs (100%) rename {src => rtic/src}/lib.rs (100%) rename {tests => rtic/tests}/tests.rs (100%) rename {ui => rtic/ui}/exception-invalid.rs (100%) rename {ui => rtic/ui}/exception-invalid.stderr (100%) rename {ui => rtic/ui}/extern-interrupt-not-enough.rs (100%) rename {ui => rtic/ui}/extern-interrupt-not-enough.stderr (100%) rename {ui => rtic/ui}/extern-interrupt-used.rs (100%) rename {ui => rtic/ui}/extern-interrupt-used.stderr (100%) rename {ui => rtic/ui}/task-priority-too-high.rs (100%) rename {ui => rtic/ui}/task-priority-too-high.stderr (100%) rename {ui => rtic/ui}/unknown-interrupt.rs (100%) rename {ui => rtic/ui}/unknown-interrupt.stderr (100%) rename {ui => rtic/ui}/v6m-interrupt-not-enough.rs_no (100%) rename {xtask => rtic/xtask}/Cargo.toml (100%) rename {xtask => rtic/xtask}/src/build.rs (100%) rename {xtask => rtic/xtask}/src/command.rs (100%) rename {xtask => rtic/xtask}/src/main.rs (100%) diff --git a/ci/expected/cfg-monotonic.run b/ci/expected/cfg-monotonic.run deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/examples/cfg-monotonic.rs b/examples/cfg-monotonic.rs deleted file mode 100644 index 88c0d6f009..0000000000 --- a/examples/cfg-monotonic.rs +++ /dev/null @@ -1,121 +0,0 @@ -//! examples/cfg-monotonic.rs - -#![deny(unsafe_code)] -#![deny(warnings)] -#![deny(missing_docs)] -#![no_main] -#![no_std] - -use panic_semihosting as _; - -#[rtic::app(device = lm3s6965, dispatchers = [SSI0, QEI0])] -mod app { - use cortex_m_semihosting::{debug, hprintln}; - use systick_monotonic::*; // Implements the `Monotonic` trait - - // A monotonic timer to enable scheduling in RTIC - #[cfg(feature = "killmono")] - #[monotonic(binds = SysTick, default = true)] - type MyMono = Systick<100>; // 100 Hz / 10 ms granularity - - // Not allowed by current rtic-syntax: - // error: `#[monotonic(...)]` on a specific type must appear at most once - // --> examples/cfg-monotonic.rs:23:10 - // | - // 23 | type MyMono = Systick<100>; // 100 Hz / 10 ms granularity - // | ^^^^^^ - // #[monotonic(binds = SysTick, default = true)] - // type MyMono = Systick<100>; // 100 Hz / 10 ms granularity - - // Not allowed by current rtic-syntax: - // error: this interrupt is already bound - // --> examples/cfg-monotonic.rs:31:25 - // | - // 31 | #[monotonic(binds = SysTick, default = true)] - // | ^^^^^^^ - // #[monotonic(binds = SysTick, default = true)] - // type MyMono2 = DwtSystick<100>; // 100 Hz / 10 ms granularity - - // Resources shared between tasks - #[shared] - struct Shared { - s1: u32, - s2: i32, - } - - // Local resources to specific tasks (cannot be shared) - #[local] - struct Local { - l1: u8, - l2: i8, - } - - #[init] - fn init(cx: init::Context) -> (Shared, Local, init::Monotonics) { - let _systick = cx.core.SYST; - - // Initialize the monotonic (SysTick rate in QEMU is 12 MHz) - #[cfg(feature = "killmono")] - let mono = Systick::new(systick, 12_000_000); - - // Spawn the task `foo` directly after `init` finishes - foo::spawn().unwrap(); - - debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator - - ( - // Initialization of shared resources - Shared { s1: 0, s2: 1 }, - // Initialization of task local resources - Local { l1: 2, l2: 3 }, - // Move the monotonic timer to the RTIC run-time, this enables - // scheduling - #[cfg(feature = "killmono")] - init::Monotonics(mono), - init::Monotonics(), - ) - } - - // Background task, runs whenever no other tasks are running - #[idle] - fn idle(_: idle::Context) -> ! { - loop { - continue; - } - } - - // Software task, not bound to a hardware interrupt. - // This task takes the task local resource `l1` - // The resources `s1` and `s2` are shared between all other tasks. - #[task(shared = [s1, s2], local = [l1])] - fn foo(_: foo::Context) { - // This task is only spawned once in `init`, hence this task will run - // only once - - hprintln!("foo"); - } - - // Software task, also not bound to a hardware interrupt - // This task takes the task local resource `l2` - // The resources `s1` and `s2` are shared between all other tasks. - #[task(shared = [s1, s2], local = [l2])] - fn bar(_: bar::Context) { - hprintln!("bar"); - - // Run `bar` once per second - // bar::spawn_after(1.secs()).unwrap(); - } - - // Hardware task, bound to a hardware interrupt - // The resources `s1` and `s2` are shared between all other tasks. - #[task(binds = UART0, priority = 3, shared = [s1, s2])] - fn uart0_interrupt(_: uart0_interrupt::Context) { - // This task is bound to the interrupt `UART0` and will run - // whenever the interrupt fires - - // Note that RTIC does NOT clear the interrupt flag, this is up to the - // user - - hprintln!("UART0 interrupt!"); - } -} diff --git a/macros/src/tests.rs b/macros/src/tests.rs deleted file mode 100644 index e9e3326ee9..0000000000 --- a/macros/src/tests.rs +++ /dev/null @@ -1,4 +0,0 @@ -// NOTE these tests are specific to the Cortex-M port; `rtic-syntax` has a more extensive test suite -// that tests functionality common to all the RTIC ports - -mod single; diff --git a/macros/src/tests/single.rs b/macros/src/tests/single.rs deleted file mode 100644 index f20c9ccbb3..0000000000 --- a/macros/src/tests/single.rs +++ /dev/null @@ -1,40 +0,0 @@ -use quote::quote; -use rtic_syntax::Settings; - -#[test] -fn analyze() { - let mut settings = Settings::default(); - settings.parse_extern_interrupt = true; - let (app, analysis) = rtic_syntax::parse2( - // First interrupt is assigned to the highest priority dispatcher - quote!(device = pac, dispatchers = [B, A]), - quote!( - mod app { - #[shared] - struct Shared {} - - #[local] - struct Local {} - - #[init] - fn init(_: init::Context) -> (Shared, Local, init::Monotonics) { - (Shared {}, Local {}, init::Monotonics()) - } - - #[task(priority = 1)] - fn a(_: a::Context) {} - - #[task(priority = 2)] - fn b(_: b::Context) {} - } - ), - settings, - ) - .unwrap(); - - let analysis = crate::analyze::app(analysis, &app); - let interrupts = &analysis.interrupts; - assert_eq!(interrupts.len(), 2); - assert_eq!(interrupts[&2].0.to_string(), "B"); - assert_eq!(interrupts[&1].0.to_string(), "A"); -} diff --git a/rtic-monotonics/.gitignore b/rtic-monotonics/.gitignore new file mode 100644 index 0000000000..c4002562d3 --- /dev/null +++ b/rtic-monotonics/.gitignore @@ -0,0 +1,6 @@ +**/*.rs.bk +.#* +.gdb_history +/target +Cargo.lock +*.hex diff --git a/rtic-monotonics/Cargo.toml b/rtic-monotonics/Cargo.toml new file mode 100644 index 0000000000..24448fb29e --- /dev/null +++ b/rtic-monotonics/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "rtic-timer" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +cortex-m = { version = "0.7.6" } +embedded-hal-async = "0.2.0-alpha.0" +fugit = { version = "0.3.6", features = ["defmt"] } +rtic-timer = { version = "1.0.0", path = "../rtic-timer" } diff --git a/rust-toolchain.toml b/rtic-monotonics/rust-toolchain.toml similarity index 100% rename from rust-toolchain.toml rename to rtic-monotonics/rust-toolchain.toml diff --git a/rtic-monotonics/src/lib.rs b/rtic-monotonics/src/lib.rs new file mode 100644 index 0000000000..88398cad3a --- /dev/null +++ b/rtic-monotonics/src/lib.rs @@ -0,0 +1,11 @@ +//! Crate + +#![no_std] +#![no_main] +#![deny(missing_docs)] +#![allow(incomplete_features)] +#![feature(async_fn_in_trait)] + +pub use rtic_timer::{Monotonic, TimeoutError, TimerQueue}; + +pub mod systick_monotonic; diff --git a/rtic-monotonics/src/systick_monotonic.rs b/rtic-monotonics/src/systick_monotonic.rs new file mode 100644 index 0000000000..491cf81c58 --- /dev/null +++ b/rtic-monotonics/src/systick_monotonic.rs @@ -0,0 +1 @@ +//! ... diff --git a/rtic-timer/.gitignore b/rtic-timer/.gitignore new file mode 100644 index 0000000000..c4002562d3 --- /dev/null +++ b/rtic-timer/.gitignore @@ -0,0 +1,6 @@ +**/*.rs.bk +.#* +.gdb_history +/target +Cargo.lock +*.hex diff --git a/rtic-timer/Cargo.toml b/rtic-timer/Cargo.toml index 8e2e2ad602..b7b3a5fba3 100644 --- a/rtic-timer/Cargo.toml +++ b/rtic-timer/Cargo.toml @@ -1,11 +1,10 @@ [package] name = "rtic-timer" -version = "0.1.0" +version = "1.0.0" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -cortex-m = "0.7.6" -rtic-monotonic = "1.0.0" -fugit = "0.3.6" \ No newline at end of file +critical-section = "1" +futures-util = { version = "0.3.25", default-features = false } diff --git a/rtic-timer/rust-toolchain.toml b/rtic-timer/rust-toolchain.toml new file mode 100644 index 0000000000..e28b55de64 --- /dev/null +++ b/rtic-timer/rust-toolchain.toml @@ -0,0 +1,4 @@ +[toolchain] +channel = "nightly" +components = [ "rust-src", "rustfmt", "llvm-tools-preview" ] +targets = [ "thumbv6m-none-eabi", "thumbv7m-none-eabi" ] diff --git a/rtic-timer/src/lib.rs b/rtic-timer/src/lib.rs index e7051d2779..d7faa07ff7 100644 --- a/rtic-timer/src/lib.rs +++ b/rtic-timer/src/lib.rs @@ -1,138 +1,336 @@ +//! Crate + #![no_std] +#![no_main] +#![deny(missing_docs)] +#![allow(incomplete_features)] +#![feature(async_fn_in_trait)] -use core::sync::atomic::{AtomicU32, Ordering}; -use core::{cmp::Ordering, task::Waker}; -use cortex_m::peripheral::{syst::SystClkSource, SYST}; -pub use fugit::{self, ExtU64}; -pub use rtic_monotonic::Monotonic; +pub mod monotonic; -mod sll; -use sll::{IntrusiveSortedLinkedList, Min as IsslMin, Node as IntrusiveNode}; +use core::future::{poll_fn, Future}; +use core::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; +use core::task::{Poll, Waker}; +use futures_util::{ + future::{select, Either}, + pin_mut, +}; +pub use monotonic::Monotonic; -pub struct Timer { - cnt: AtomicU32, - // queue: IntrusiveSortedLinkedList<'static, WakerNotReady, IsslMin>, +mod linked_list; + +use linked_list::{Link, LinkedList}; + +/// Holds a waker and at which time instant this waker shall be awoken. +struct WaitingWaker { + waker: Waker, + release_at: Mono::Instant, } -#[allow(non_snake_case)] -#[no_mangle] -fn SysTick() { - // .. - let cnt = unsafe { - static mut CNT: u32 = 0; - &mut CNT - }; - - *cnt = cnt.wrapping_add(1); -} - -/// Systick implementing `rtic_monotonic::Monotonic` which runs at a -/// settable rate using the `TIMER_HZ` parameter. -pub struct Systick { - systick: SYST, - cnt: u64, -} - -impl Systick { - /// Provide a new `Monotonic` based on SysTick. - /// - /// The `sysclk` parameter is the speed at which SysTick runs at. This value should come from - /// the clock generation function of the used HAL. - /// - /// Notice that the actual rate of the timer is a best approximation based on the given - /// `sysclk` and `TIMER_HZ`. - pub fn new(mut systick: SYST, sysclk: u32) -> Self { - // + TIMER_HZ / 2 provides round to nearest instead of round to 0. - // - 1 as the counter range is inclusive [0, reload] - let reload = (sysclk + TIMER_HZ / 2) / TIMER_HZ - 1; - - assert!(reload <= 0x00ff_ffff); - assert!(reload > 0); - - systick.disable_counter(); - systick.set_clock_source(SystClkSource::Core); - systick.set_reload(reload); - - Systick { systick, cnt: 0 } - } -} - -impl Monotonic for Systick { - const DISABLE_INTERRUPT_ON_EMPTY_QUEUE: bool = false; - - type Instant = fugit::TimerInstantU64; - type Duration = fugit::TimerDurationU64; - - fn now(&mut self) -> Self::Instant { - if self.systick.has_wrapped() { - self.cnt = self.cnt.wrapping_add(1); - } - - Self::Instant::from_ticks(self.cnt) - } - - unsafe fn reset(&mut self) { - self.systick.clear_current(); - self.systick.enable_counter(); - } - - #[inline(always)] - fn set_compare(&mut self, _val: Self::Instant) { - // No need to do something here, we get interrupts anyway. - } - - #[inline(always)] - fn clear_compare_flag(&mut self) { - // NOOP with SysTick interrupt - } - - #[inline(always)] - fn zero() -> Self::Instant { - Self::Instant::from_ticks(0) - } - - #[inline(always)] - fn on_interrupt(&mut self) { - if self.systick.has_wrapped() { - self.cnt = self.cnt.wrapping_add(1); +impl Clone for WaitingWaker { + fn clone(&self) -> Self { + Self { + waker: self.waker.clone(), + release_at: self.release_at, } } } -struct WakerNotReady -where - Mono: Monotonic, -{ - pub waker: Waker, - pub instant: Mono::Instant, - pub marker: u32, -} - -impl Eq for WakerNotReady where Mono: Monotonic {} - -impl Ord for WakerNotReady -where - Mono: Monotonic, -{ - fn cmp(&self, other: &Self) -> Ordering { - self.instant.cmp(&other.instant) - } -} - -impl PartialEq for WakerNotReady -where - Mono: Monotonic, -{ +impl PartialEq for WaitingWaker { fn eq(&self, other: &Self) -> bool { - self.instant == other.instant + self.release_at == other.release_at } } -impl PartialOrd for WakerNotReady -where - Mono: Monotonic, -{ - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) +impl PartialOrd for WaitingWaker { + fn partial_cmp(&self, other: &Self) -> Option { + self.release_at.partial_cmp(&other.release_at) } } + +/// A generic timer queue for async executors. +/// +/// # Blocking +/// +/// The internal priority queue uses global critical sections to manage access. This means that +/// `await`ing a delay will cause a lock of the entire system for O(n) time. In practice the lock +/// duration is ~10 clock cycles per element in the queue. +/// +/// # Safety +/// +/// This timer queue is based on an intrusive linked list, and by extension the links are strored +/// on the async stacks of callers. The links are deallocated on `drop` or when the wait is +/// complete. +/// +/// Do not call `mem::forget` on an awaited future, or there will be dragons! +pub struct TimerQueue { + queue: LinkedList>, + initialized: AtomicBool, +} + +/// This indicates that there was a timeout. +pub struct TimeoutError; + +impl TimerQueue { + /// Make a new queue. + pub const fn new() -> Self { + Self { + queue: LinkedList::new(), + initialized: AtomicBool::new(false), + } + } + + /// Forwards the `Monotonic::now()` method. + #[inline(always)] + pub fn now(&self) -> Mono::Instant { + Mono::now() + } + + /// Takes the initialized monotonic to initialize the TimerQueue. + pub fn initialize(&self, monotonic: Mono) { + self.initialized.store(true, Ordering::SeqCst); + + // Don't run drop on `Mono` + core::mem::forget(monotonic); + } + + /// Call this in the interrupt handler of the hardware timer supporting the `Monotonic` + /// + /// # Safety + /// + /// It's always safe to call, but it must only be called from the interrupt of the + /// monotonic timer for correct operation. + pub unsafe fn on_monotonic_interrupt(&self) { + Mono::clear_compare_flag(); + Mono::on_interrupt(); + + loop { + let mut release_at = None; + let head = self.queue.pop_if(|head| { + release_at = Some(head.release_at); + + Mono::now() >= head.release_at + }); + + match (head, release_at) { + (Some(link), _) => { + link.waker.wake(); + } + (None, Some(instant)) => { + Mono::enable_timer(); + Mono::set_compare(instant); + + if Mono::now() >= instant { + // The time for the next instant passed while handling it, + // continue dequeueing + continue; + } + + break; + } + (None, None) => { + // Queue is empty + Mono::disable_timer(); + + break; + } + } + } + } + + /// Timeout at a specific time. + pub async fn timeout_at( + &self, + instant: Mono::Instant, + future: F, + ) -> Result { + let delay = self.delay_until(instant); + + pin_mut!(future); + pin_mut!(delay); + + match select(future, delay).await { + Either::Left((r, _)) => Ok(r), + Either::Right(_) => Err(TimeoutError), + } + } + + /// Timeout after a specific duration. + #[inline] + pub async fn timeout_after( + &self, + duration: Mono::Duration, + future: F, + ) -> Result { + self.timeout_at(Mono::now() + duration, future).await + } + + /// Delay for some duration of time. + #[inline] + pub async fn delay(&self, duration: Mono::Duration) { + let now = Mono::now(); + + self.delay_until(now + duration).await; + } + + /// Delay to some specific time instant. + pub async fn delay_until(&self, instant: Mono::Instant) { + if !self.initialized.load(Ordering::Relaxed) { + panic!( + "The timer queue is not initialized with a monotonic, you need to run `initialize`" + ); + } + + let mut first_run = true; + let queue = &self.queue; + let mut link = Link::new(WaitingWaker { + waker: poll_fn(|cx| Poll::Ready(cx.waker().clone())).await, + release_at: instant, + }); + + let marker = &AtomicUsize::new(0); + + let dropper = OnDrop::new(|| { + queue.delete(marker.load(Ordering::Relaxed)); + }); + + poll_fn(|_| { + if Mono::now() >= instant { + return Poll::Ready(()); + } + + if first_run { + first_run = false; + let (was_empty, addr) = queue.insert(&mut link); + marker.store(addr, Ordering::Relaxed); + + if was_empty { + // Pend the monotonic handler if the queue was empty to setup the timer. + Mono::pend_interrupt(); + } + } + + Poll::Pending + }) + .await; + + // Make sure that our link is deleted from the list before we drop this stack + drop(dropper); + } +} + +struct OnDrop { + f: core::mem::MaybeUninit, +} + +impl OnDrop { + pub fn new(f: F) -> Self { + Self { + f: core::mem::MaybeUninit::new(f), + } + } + + #[allow(unused)] + pub fn defuse(self) { + core::mem::forget(self) + } +} + +impl Drop for OnDrop { + fn drop(&mut self) { + unsafe { self.f.as_ptr().read()() } + } +} + +// -------- Test program --------- +// +// +// use systick_monotonic::{Systick, TimerQueue}; +// +// // same panicking *behavior* as `panic-probe` but doesn't print a panic message +// // this prevents the panic message being printed *twice* when `defmt::panic` is invoked +// #[defmt::panic_handler] +// fn panic() -> ! { +// cortex_m::asm::udf() +// } +// +// /// Terminates the application and makes `probe-run` exit with exit-code = 0 +// pub fn exit() -> ! { +// loop { +// cortex_m::asm::bkpt(); +// } +// } +// +// defmt::timestamp!("{=u64:us}", { +// let time_us: fugit::MicrosDurationU32 = MONO.now().duration_since_epoch().convert(); +// +// time_us.ticks() as u64 +// }); +// +// make_systick_timer_queue!(MONO, Systick<1_000>); +// +// #[rtic::app( +// device = nrf52832_hal::pac, +// dispatchers = [SWI0_EGU0, SWI1_EGU1, SWI2_EGU2, SWI3_EGU3, SWI4_EGU4, SWI5_EGU5], +// )] +// mod app { +// use super::{Systick, MONO}; +// use fugit::ExtU32; +// +// #[shared] +// struct Shared {} +// +// #[local] +// struct Local {} +// +// #[init] +// fn init(cx: init::Context) -> (Shared, Local) { +// defmt::println!("init"); +// +// let systick = Systick::start(cx.core.SYST, 64_000_000); +// +// defmt::println!("initializing monotonic"); +// +// MONO.initialize(systick); +// +// async_task::spawn().ok(); +// async_task2::spawn().ok(); +// async_task3::spawn().ok(); +// +// (Shared {}, Local {}) +// } +// +// #[idle] +// fn idle(_: idle::Context) -> ! { +// defmt::println!("idle"); +// +// loop { +// core::hint::spin_loop(); +// } +// } +// +// #[task] +// async fn async_task(_: async_task::Context) { +// loop { +// defmt::println!("async task waiting for 1 second"); +// MONO.delay(1.secs()).await; +// } +// } +// +// #[task] +// async fn async_task2(_: async_task2::Context) { +// loop { +// defmt::println!(" async task 2 waiting for 0.5 second"); +// MONO.delay(500.millis()).await; +// } +// } +// +// #[task] +// async fn async_task3(_: async_task3::Context) { +// loop { +// defmt::println!(" async task 3 waiting for 0.2 second"); +// MONO.delay(200.millis()).await; +// } +// } +// } +// diff --git a/rtic-timer/src/linked_list.rs b/rtic-timer/src/linked_list.rs new file mode 100644 index 0000000000..42ff8cb6f9 --- /dev/null +++ b/rtic-timer/src/linked_list.rs @@ -0,0 +1,173 @@ +//! ... + +use core::marker::PhantomPinned; +use core::sync::atomic::{AtomicPtr, Ordering}; +use critical_section as cs; + +/// A sorted linked list for the timer queue. +pub struct LinkedList { + head: AtomicPtr>, +} + +impl LinkedList { + /// Create a new linked list. + pub const fn new() -> Self { + Self { + head: AtomicPtr::new(core::ptr::null_mut()), + } + } +} + +impl LinkedList { + /// Pop the first element in the queue if the closure returns true. + pub fn pop_if bool>(&self, f: F) -> Option { + cs::with(|_| { + // Make sure all previous writes are visible + core::sync::atomic::fence(Ordering::SeqCst); + + let head = self.head.load(Ordering::Relaxed); + + // SAFETY: `as_ref` is safe as `insert` requires a valid reference to a link + if let Some(head) = unsafe { head.as_ref() } { + if f(&head.val) { + // Move head to the next element + self.head + .store(head.next.load(Ordering::Relaxed), Ordering::Relaxed); + + // We read the value at head + let head_val = head.val.clone(); + + return Some(head_val); + } + } + None + }) + } + + /// Delete a link at an address. + pub fn delete(&self, addr: usize) { + cs::with(|_| { + // Make sure all previous writes are visible + core::sync::atomic::fence(Ordering::SeqCst); + + let head = self.head.load(Ordering::Relaxed); + + // SAFETY: `as_ref` is safe as `insert` requires a valid reference to a link + let head_ref = if let Some(head_ref) = unsafe { head.as_ref() } { + head_ref + } else { + // 1. List is empty, do nothing + return; + }; + + if head as *const _ as usize == addr { + // 2. Replace head with head.next + self.head + .store(head_ref.next.load(Ordering::Relaxed), Ordering::Relaxed); + + return; + } + + // 3. search list for correct node + let mut curr = head_ref; + let mut next = head_ref.next.load(Ordering::Relaxed); + + // SAFETY: `as_ref` is safe as `insert` requires a valid reference to a link + while let Some(next_link) = unsafe { next.as_ref() } { + // Next is not null + + if next as *const _ as usize == addr { + curr.next + .store(next_link.next.load(Ordering::Relaxed), Ordering::Relaxed); + + return; + } + + // Continue searching + curr = next_link; + next = next_link.next.load(Ordering::Relaxed); + } + }) + } + + /// Insert a new link into the linked list. + /// The return is (was_empty, address), where the address of the link is for use with `delete`. + pub fn insert(&self, val: &mut Link) -> (bool, usize) { + cs::with(|_| { + let addr = val as *const _ as usize; + + // Make sure all previous writes are visible + core::sync::atomic::fence(Ordering::SeqCst); + + let head = self.head.load(Ordering::Relaxed); + + // 3 cases to handle + + // 1. List is empty, write to head + // SAFETY: `as_ref` is safe as `insert` requires a valid reference to a link + let head_ref = if let Some(head_ref) = unsafe { head.as_ref() } { + head_ref + } else { + self.head.store(val, Ordering::Relaxed); + return (true, addr); + }; + + // 2. val needs to go in first + if val.val < head_ref.val { + // Set current head as next of `val` + val.next.store(head, Ordering::Relaxed); + + // `val` is now first in the queue + self.head.store(val, Ordering::Relaxed); + + return (false, addr); + } + + // 3. search list for correct place + let mut curr = head_ref; + let mut next = head_ref.next.load(Ordering::Relaxed); + + // SAFETY: `as_ref` is safe as `insert` requires a valid reference to a link + while let Some(next_link) = unsafe { next.as_ref() } { + // Next is not null + + if val.val < next_link.val { + // Replace next with `val` + val.next.store(next, Ordering::Relaxed); + + // Insert `val` + curr.next.store(val, Ordering::Relaxed); + + return (false, addr); + } + + // Continue searching + curr = next_link; + next = next_link.next.load(Ordering::Relaxed); + } + + // No next, write link to last position in list + curr.next.store(val, Ordering::Relaxed); + + (false, addr) + }) + } +} + +/// A link in the linked list. +pub struct Link { + val: T, + next: AtomicPtr>, + _up: PhantomPinned, +} + +impl Link { + /// Create a new link. + pub const fn new(val: T) -> Self { + Self { + val, + next: AtomicPtr::new(core::ptr::null_mut()), + _up: PhantomPinned, + } + } +} diff --git a/rtic-timer/src/monotonic.rs b/rtic-timer/src/monotonic.rs new file mode 100644 index 0000000000..9b3742fa87 --- /dev/null +++ b/rtic-timer/src/monotonic.rs @@ -0,0 +1,60 @@ +//! ... + +/// # A monotonic clock / counter definition. +/// +/// ## Correctness +/// +/// The trait enforces that proper time-math is implemented between `Instant` and `Duration`. This +/// is a requirement on the time library that the user chooses to use. +pub trait Monotonic { + /// The time at time zero. + const ZERO: Self::Instant; + + /// The type for instant, defining an instant in time. + /// + /// **Note:** In all APIs in RTIC that use instants from this monotonic, this type will be used. + type Instant: Ord + + Copy + + core::ops::Add + + core::ops::Sub + + core::ops::Sub; + + /// The type for duration, defining an duration of time. + /// + /// **Note:** In all APIs in RTIC that use duration from this monotonic, this type will be used. + type Duration; + + /// Get the current time. + fn now() -> Self::Instant; + + /// Set the compare value of the timer interrupt. + /// + /// **Note:** This method does not need to handle race conditions of the monotonic, the timer + /// queue in RTIC checks this. + fn set_compare(instant: Self::Instant); + + /// Clear the compare interrupt flag. + fn clear_compare_flag(); + + /// Pend the timer's interrupt. + fn pend_interrupt(); + + /// Optional. Runs on interrupt before any timer queue handling. + fn on_interrupt() {} + + /// Optional. This is used to save power, this is called when the timer queue is not empty. + /// + /// Enabling and disabling the monotonic needs to propagate to `now` so that an instant + /// based of `now()` is still valid. + /// + /// NOTE: This may be called more than once. + fn enable_timer() {} + + /// Optional. This is used to save power, this is called when the timer queue is empty. + /// + /// Enabling and disabling the monotonic needs to propagate to `now` so that an instant + /// based of `now()` is still valid. + /// + /// NOTE: This may be called more than once. + fn disable_timer() {} +} diff --git a/rtic-timer/src/sll.rs b/rtic-timer/src/sll.rs deleted file mode 100644 index 43b53c1749..0000000000 --- a/rtic-timer/src/sll.rs +++ /dev/null @@ -1,421 +0,0 @@ -//! An intrusive sorted priority linked list, designed for use in `Future`s in RTIC. -use core::cmp::Ordering; -use core::fmt; -use core::marker::PhantomData; -use core::ops::{Deref, DerefMut}; -use core::ptr::NonNull; - -/// Marker for Min sorted [`IntrusiveSortedLinkedList`]. -pub struct Min; - -/// Marker for Max sorted [`IntrusiveSortedLinkedList`]. -pub struct Max; - -/// The linked list kind: min-list or max-list -pub trait Kind: private::Sealed { - #[doc(hidden)] - fn ordering() -> Ordering; -} - -impl Kind for Min { - fn ordering() -> Ordering { - Ordering::Less - } -} - -impl Kind for Max { - fn ordering() -> Ordering { - Ordering::Greater - } -} - -/// Sealed traits -mod private { - pub trait Sealed {} -} - -impl private::Sealed for Max {} -impl private::Sealed for Min {} - -/// A node in the [`IntrusiveSortedLinkedList`]. -pub struct Node { - pub val: T, - next: Option>>, -} - -impl Node { - pub fn new(val: T) -> Self { - Self { val, next: None } - } -} - -/// The linked list. -pub struct IntrusiveSortedLinkedList<'a, T, K> { - head: Option>>, - _kind: PhantomData, - _lt: PhantomData<&'a ()>, -} - -impl<'a, T, K> fmt::Debug for IntrusiveSortedLinkedList<'a, T, K> -where - T: Ord + core::fmt::Debug, - K: Kind, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let mut l = f.debug_list(); - let mut current = self.head; - - while let Some(head) = current { - let head = unsafe { head.as_ref() }; - current = head.next; - - l.entry(&head.val); - } - - l.finish() - } -} - -impl<'a, T, K> IntrusiveSortedLinkedList<'a, T, K> -where - T: Ord, - K: Kind, -{ - pub const fn new() -> Self { - Self { - head: None, - _kind: PhantomData, - _lt: PhantomData, - } - } - - // Push to the list. - pub fn push(&mut self, new: &'a mut Node) { - unsafe { - if let Some(head) = self.head { - if head.as_ref().val.cmp(&new.val) != K::ordering() { - // This is newer than head, replace head - new.next = self.head; - self.head = Some(NonNull::new_unchecked(new)); - } else { - // It's not head, search the list for the correct placement - let mut current = head; - - while let Some(next) = current.as_ref().next { - if next.as_ref().val.cmp(&new.val) != K::ordering() { - break; - } - - current = next; - } - - new.next = current.as_ref().next; - current.as_mut().next = Some(NonNull::new_unchecked(new)); - } - } else { - // List is empty, place at head - self.head = Some(NonNull::new_unchecked(new)) - } - } - } - - /// Get an iterator over the sorted list. - pub fn iter(&self) -> Iter<'_, T, K> { - Iter { - _list: self, - index: self.head, - } - } - - /// Find an element in the list that can be changed and resorted. - pub fn find_mut(&mut self, mut f: F) -> Option> - where - F: FnMut(&T) -> bool, - { - let head = self.head?; - - // Special-case, first element - if f(&unsafe { head.as_ref() }.val) { - return Some(FindMut { - is_head: true, - prev_index: None, - index: self.head, - list: self, - maybe_changed: false, - }); - } - - let mut current = head; - - while let Some(next) = unsafe { current.as_ref() }.next { - if f(&unsafe { next.as_ref() }.val) { - return Some(FindMut { - is_head: false, - prev_index: Some(current), - index: Some(next), - list: self, - maybe_changed: false, - }); - } - - current = next; - } - - None - } - - /// Peek at the first element. - pub fn peek(&self) -> Option<&T> { - self.head.map(|head| unsafe { &head.as_ref().val }) - } - - /// Pops the first element in the list. - /// - /// Complexity is worst-case `O(1)`. - pub fn pop(&mut self) -> Option<&'a Node> { - if let Some(head) = self.head { - let v = unsafe { head.as_ref() }; - self.head = v.next; - Some(v) - } else { - None - } - } - - /// Checks if the linked list is empty. - #[inline] - pub fn is_empty(&self) -> bool { - self.head.is_none() - } -} - -/// Iterator for the linked list. -pub struct Iter<'a, T, K> -where - T: Ord, - K: Kind, -{ - _list: &'a IntrusiveSortedLinkedList<'a, T, K>, - index: Option>>, -} - -impl<'a, T, K> Iterator for Iter<'a, T, K> -where - T: Ord, - K: Kind, -{ - type Item = &'a T; - - fn next(&mut self) -> Option { - let index = self.index?; - - let node = unsafe { index.as_ref() }; - self.index = node.next; - - Some(&node.val) - } -} - -/// Comes from [`IntrusiveSortedLinkedList::find_mut`]. -pub struct FindMut<'a, 'b, T, K> -where - T: Ord + 'b, - K: Kind, -{ - list: &'a mut IntrusiveSortedLinkedList<'b, T, K>, - is_head: bool, - prev_index: Option>>, - index: Option>>, - maybe_changed: bool, -} - -impl<'a, 'b, T, K> FindMut<'a, 'b, T, K> -where - T: Ord, - K: Kind, -{ - unsafe fn pop_internal(&mut self) -> &'b mut Node { - if self.is_head { - // If it is the head element, we can do a normal pop - let mut head = self.list.head.unwrap_unchecked(); - let v = head.as_mut(); - self.list.head = v.next; - v - } else { - // Somewhere in the list - let mut prev = self.prev_index.unwrap_unchecked(); - let mut curr = self.index.unwrap_unchecked(); - - // Re-point the previous index - prev.as_mut().next = curr.as_ref().next; - - curr.as_mut() - } - } - - /// This will pop the element from the list. - /// - /// Complexity is worst-case `O(1)`. - #[inline] - pub fn pop(mut self) -> &'b mut Node { - unsafe { self.pop_internal() } - } - - /// This will resort the element into the correct position in the list if needed. The resorting - /// will only happen if the element has been accessed mutably. - /// - /// Same as calling `drop`. - /// - /// Complexity is worst-case `O(N)`. - #[inline] - pub fn finish(self) { - drop(self) - } -} - -impl<'b, T, K> Drop for FindMut<'_, 'b, T, K> -where - T: Ord + 'b, - K: Kind, -{ - fn drop(&mut self) { - // Only resort the list if the element has changed - if self.maybe_changed { - unsafe { - let val = self.pop_internal(); - self.list.push(val); - } - } - } -} - -impl Deref for FindMut<'_, '_, T, K> -where - T: Ord, - K: Kind, -{ - type Target = T; - - fn deref(&self) -> &Self::Target { - unsafe { &self.index.unwrap_unchecked().as_ref().val } - } -} - -impl DerefMut for FindMut<'_, '_, T, K> -where - T: Ord, - K: Kind, -{ - fn deref_mut(&mut self) -> &mut Self::Target { - self.maybe_changed = true; - unsafe { &mut self.index.unwrap_unchecked().as_mut().val } - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn const_new() { - static mut _V1: IntrusiveSortedLinkedList = IntrusiveSortedLinkedList::new(); - } - - #[test] - fn test_peek() { - let mut ll: IntrusiveSortedLinkedList = IntrusiveSortedLinkedList::new(); - - let mut a = Node { val: 1, next: None }; - ll.push(&mut a); - assert_eq!(ll.peek().unwrap(), &1); - - let mut a = Node { val: 2, next: None }; - ll.push(&mut a); - assert_eq!(ll.peek().unwrap(), &2); - - let mut a = Node { val: 3, next: None }; - ll.push(&mut a); - assert_eq!(ll.peek().unwrap(), &3); - - let mut ll: IntrusiveSortedLinkedList = IntrusiveSortedLinkedList::new(); - - let mut a = Node { val: 2, next: None }; - ll.push(&mut a); - assert_eq!(ll.peek().unwrap(), &2); - - let mut a = Node { val: 1, next: None }; - ll.push(&mut a); - assert_eq!(ll.peek().unwrap(), &1); - - let mut a = Node { val: 3, next: None }; - ll.push(&mut a); - assert_eq!(ll.peek().unwrap(), &1); - } - - #[test] - fn test_empty() { - let ll: IntrusiveSortedLinkedList = IntrusiveSortedLinkedList::new(); - - assert!(ll.is_empty()) - } - - #[test] - fn test_updating() { - let mut ll: IntrusiveSortedLinkedList = IntrusiveSortedLinkedList::new(); - - let mut a = Node { val: 1, next: None }; - ll.push(&mut a); - - let mut a = Node { val: 2, next: None }; - ll.push(&mut a); - - let mut a = Node { val: 3, next: None }; - ll.push(&mut a); - - let mut find = ll.find_mut(|v| *v == 2).unwrap(); - - *find += 1000; - find.finish(); - - assert_eq!(ll.peek().unwrap(), &1002); - - let mut find = ll.find_mut(|v| *v == 3).unwrap(); - - *find += 1000; - find.finish(); - - assert_eq!(ll.peek().unwrap(), &1003); - - // Remove largest element - ll.find_mut(|v| *v == 1003).unwrap().pop(); - - assert_eq!(ll.peek().unwrap(), &1002); - } - - #[test] - fn test_updating_1() { - let mut ll: IntrusiveSortedLinkedList = IntrusiveSortedLinkedList::new(); - - let mut a = Node { val: 1, next: None }; - ll.push(&mut a); - - let v = ll.pop().unwrap(); - - assert_eq!(v.val, 1); - } - - #[test] - fn test_updating_2() { - let mut ll: IntrusiveSortedLinkedList = IntrusiveSortedLinkedList::new(); - - let mut a = Node { val: 1, next: None }; - ll.push(&mut a); - - let mut find = ll.find_mut(|v| *v == 1).unwrap(); - - *find += 1000; - find.finish(); - - assert_eq!(ll.peek().unwrap(), &1001); - } -} diff --git a/.cargo/config.toml b/rtic/.cargo/config.toml similarity index 100% rename from .cargo/config.toml rename to rtic/.cargo/config.toml diff --git a/rtic/.gitignore b/rtic/.gitignore new file mode 100644 index 0000000000..c4002562d3 --- /dev/null +++ b/rtic/.gitignore @@ -0,0 +1,6 @@ +**/*.rs.bk +.#* +.gdb_history +/target +Cargo.lock +*.hex diff --git a/CHANGELOG.md b/rtic/CHANGELOG.md similarity index 100% rename from CHANGELOG.md rename to rtic/CHANGELOG.md diff --git a/Cargo.toml b/rtic/Cargo.toml similarity index 100% rename from Cargo.toml rename to rtic/Cargo.toml diff --git a/build.rs b/rtic/build.rs similarity index 100% rename from build.rs rename to rtic/build.rs diff --git a/ci/expected/async-delay.run b/rtic/ci/expected/async-delay.run similarity index 100% rename from ci/expected/async-delay.run rename to rtic/ci/expected/async-delay.run diff --git a/ci/expected/async-infinite-loop.run b/rtic/ci/expected/async-infinite-loop.run similarity index 100% rename from ci/expected/async-infinite-loop.run rename to rtic/ci/expected/async-infinite-loop.run diff --git a/ci/expected/async-task-multiple-prios.run b/rtic/ci/expected/async-task-multiple-prios.run similarity index 100% rename from ci/expected/async-task-multiple-prios.run rename to rtic/ci/expected/async-task-multiple-prios.run diff --git a/ci/expected/async-task.run b/rtic/ci/expected/async-task.run similarity index 100% rename from ci/expected/async-task.run rename to rtic/ci/expected/async-task.run diff --git a/ci/expected/async-timeout.run b/rtic/ci/expected/async-timeout.run similarity index 100% rename from ci/expected/async-timeout.run rename to rtic/ci/expected/async-timeout.run diff --git a/ci/expected/big-struct-opt.run b/rtic/ci/expected/big-struct-opt.run similarity index 100% rename from ci/expected/big-struct-opt.run rename to rtic/ci/expected/big-struct-opt.run diff --git a/ci/expected/binds.run b/rtic/ci/expected/binds.run similarity index 100% rename from ci/expected/binds.run rename to rtic/ci/expected/binds.run diff --git a/ci/expected/cancel-reschedule.run b/rtic/ci/expected/cancel-reschedule.run similarity index 100% rename from ci/expected/cancel-reschedule.run rename to rtic/ci/expected/cancel-reschedule.run diff --git a/ci/expected/capacity.run b/rtic/ci/expected/capacity.run similarity index 100% rename from ci/expected/capacity.run rename to rtic/ci/expected/capacity.run diff --git a/ci/expected/cfg-whole-task.run b/rtic/ci/expected/cfg-whole-task.run similarity index 100% rename from ci/expected/cfg-whole-task.run rename to rtic/ci/expected/cfg-whole-task.run diff --git a/ci/expected/common.run b/rtic/ci/expected/common.run similarity index 100% rename from ci/expected/common.run rename to rtic/ci/expected/common.run diff --git a/ci/expected/complex.run b/rtic/ci/expected/complex.run similarity index 100% rename from ci/expected/complex.run rename to rtic/ci/expected/complex.run diff --git a/ci/expected/declared_locals.run b/rtic/ci/expected/declared_locals.run similarity index 100% rename from ci/expected/declared_locals.run rename to rtic/ci/expected/declared_locals.run diff --git a/ci/expected/destructure.run b/rtic/ci/expected/destructure.run similarity index 100% rename from ci/expected/destructure.run rename to rtic/ci/expected/destructure.run diff --git a/ci/expected/extern_binds.run b/rtic/ci/expected/extern_binds.run similarity index 100% rename from ci/expected/extern_binds.run rename to rtic/ci/expected/extern_binds.run diff --git a/ci/expected/extern_spawn.run b/rtic/ci/expected/extern_spawn.run similarity index 100% rename from ci/expected/extern_spawn.run rename to rtic/ci/expected/extern_spawn.run diff --git a/ci/expected/generics.run b/rtic/ci/expected/generics.run similarity index 100% rename from ci/expected/generics.run rename to rtic/ci/expected/generics.run diff --git a/ci/expected/hardware.run b/rtic/ci/expected/hardware.run similarity index 100% rename from ci/expected/hardware.run rename to rtic/ci/expected/hardware.run diff --git a/ci/expected/idle-wfi.run b/rtic/ci/expected/idle-wfi.run similarity index 100% rename from ci/expected/idle-wfi.run rename to rtic/ci/expected/idle-wfi.run diff --git a/ci/expected/idle.run b/rtic/ci/expected/idle.run similarity index 100% rename from ci/expected/idle.run rename to rtic/ci/expected/idle.run diff --git a/ci/expected/init.run b/rtic/ci/expected/init.run similarity index 100% rename from ci/expected/init.run rename to rtic/ci/expected/init.run diff --git a/ci/expected/locals.run b/rtic/ci/expected/locals.run similarity index 100% rename from ci/expected/locals.run rename to rtic/ci/expected/locals.run diff --git a/ci/expected/lock-free.run b/rtic/ci/expected/lock-free.run similarity index 100% rename from ci/expected/lock-free.run rename to rtic/ci/expected/lock-free.run diff --git a/ci/expected/lock.run b/rtic/ci/expected/lock.run similarity index 100% rename from ci/expected/lock.run rename to rtic/ci/expected/lock.run diff --git a/ci/expected/message.run b/rtic/ci/expected/message.run similarity index 100% rename from ci/expected/message.run rename to rtic/ci/expected/message.run diff --git a/ci/expected/message_passing.run b/rtic/ci/expected/message_passing.run similarity index 100% rename from ci/expected/message_passing.run rename to rtic/ci/expected/message_passing.run diff --git a/ci/expected/multilock.run b/rtic/ci/expected/multilock.run similarity index 100% rename from ci/expected/multilock.run rename to rtic/ci/expected/multilock.run diff --git a/ci/expected/not-sync.run b/rtic/ci/expected/not-sync.run similarity index 100% rename from ci/expected/not-sync.run rename to rtic/ci/expected/not-sync.run diff --git a/ci/expected/only-shared-access.run b/rtic/ci/expected/only-shared-access.run similarity index 100% rename from ci/expected/only-shared-access.run rename to rtic/ci/expected/only-shared-access.run diff --git a/ci/expected/periodic-at.run b/rtic/ci/expected/periodic-at.run similarity index 100% rename from ci/expected/periodic-at.run rename to rtic/ci/expected/periodic-at.run diff --git a/ci/expected/periodic-at2.run b/rtic/ci/expected/periodic-at2.run similarity index 100% rename from ci/expected/periodic-at2.run rename to rtic/ci/expected/periodic-at2.run diff --git a/ci/expected/periodic.run b/rtic/ci/expected/periodic.run similarity index 100% rename from ci/expected/periodic.run rename to rtic/ci/expected/periodic.run diff --git a/ci/expected/peripherals-taken.run b/rtic/ci/expected/peripherals-taken.run similarity index 100% rename from ci/expected/peripherals-taken.run rename to rtic/ci/expected/peripherals-taken.run diff --git a/ci/expected/pool.run b/rtic/ci/expected/pool.run similarity index 100% rename from ci/expected/pool.run rename to rtic/ci/expected/pool.run diff --git a/ci/expected/preempt.run b/rtic/ci/expected/preempt.run similarity index 100% rename from ci/expected/preempt.run rename to rtic/ci/expected/preempt.run diff --git a/ci/expected/ramfunc.run b/rtic/ci/expected/ramfunc.run similarity index 100% rename from ci/expected/ramfunc.run rename to rtic/ci/expected/ramfunc.run diff --git a/ci/expected/ramfunc.run.grep.bar b/rtic/ci/expected/ramfunc.run.grep.bar similarity index 100% rename from ci/expected/ramfunc.run.grep.bar rename to rtic/ci/expected/ramfunc.run.grep.bar diff --git a/ci/expected/ramfunc.run.grep.foo b/rtic/ci/expected/ramfunc.run.grep.foo similarity index 100% rename from ci/expected/ramfunc.run.grep.foo rename to rtic/ci/expected/ramfunc.run.grep.foo diff --git a/ci/expected/resource-user-struct.run b/rtic/ci/expected/resource-user-struct.run similarity index 100% rename from ci/expected/resource-user-struct.run rename to rtic/ci/expected/resource-user-struct.run diff --git a/ci/expected/schedule.run b/rtic/ci/expected/schedule.run similarity index 100% rename from ci/expected/schedule.run rename to rtic/ci/expected/schedule.run diff --git a/ci/expected/shared.run b/rtic/ci/expected/shared.run similarity index 100% rename from ci/expected/shared.run rename to rtic/ci/expected/shared.run diff --git a/ci/expected/smallest.run b/rtic/ci/expected/smallest.run similarity index 100% rename from ci/expected/smallest.run rename to rtic/ci/expected/smallest.run diff --git a/ci/expected/spawn.run b/rtic/ci/expected/spawn.run similarity index 100% rename from ci/expected/spawn.run rename to rtic/ci/expected/spawn.run diff --git a/ci/expected/static.run b/rtic/ci/expected/static.run similarity index 100% rename from ci/expected/static.run rename to rtic/ci/expected/static.run diff --git a/ci/expected/t-binds.run b/rtic/ci/expected/t-binds.run similarity index 100% rename from ci/expected/t-binds.run rename to rtic/ci/expected/t-binds.run diff --git a/ci/expected/t-cfg-resources.run b/rtic/ci/expected/t-cfg-resources.run similarity index 100% rename from ci/expected/t-cfg-resources.run rename to rtic/ci/expected/t-cfg-resources.run diff --git a/ci/expected/t-htask-main.run b/rtic/ci/expected/t-htask-main.run similarity index 100% rename from ci/expected/t-htask-main.run rename to rtic/ci/expected/t-htask-main.run diff --git a/ci/expected/t-idle-main.run b/rtic/ci/expected/t-idle-main.run similarity index 100% rename from ci/expected/t-idle-main.run rename to rtic/ci/expected/t-idle-main.run diff --git a/ci/expected/t-late-not-send.run b/rtic/ci/expected/t-late-not-send.run similarity index 100% rename from ci/expected/t-late-not-send.run rename to rtic/ci/expected/t-late-not-send.run diff --git a/ci/expected/t-schedule.run b/rtic/ci/expected/t-schedule.run similarity index 100% rename from ci/expected/t-schedule.run rename to rtic/ci/expected/t-schedule.run diff --git a/ci/expected/t-spawn.run b/rtic/ci/expected/t-spawn.run similarity index 100% rename from ci/expected/t-spawn.run rename to rtic/ci/expected/t-spawn.run diff --git a/ci/expected/task.run b/rtic/ci/expected/task.run similarity index 100% rename from ci/expected/task.run rename to rtic/ci/expected/task.run diff --git a/ci/expected/zero-prio-task.run b/rtic/ci/expected/zero-prio-task.run similarity index 100% rename from ci/expected/zero-prio-task.run rename to rtic/ci/expected/zero-prio-task.run diff --git a/examples/async-delay.no_rs b/rtic/examples/async-delay.no_rs similarity index 100% rename from examples/async-delay.no_rs rename to rtic/examples/async-delay.no_rs diff --git a/examples/async-infinite-loop.no_rs b/rtic/examples/async-infinite-loop.no_rs similarity index 100% rename from examples/async-infinite-loop.no_rs rename to rtic/examples/async-infinite-loop.no_rs diff --git a/examples/async-task-multiple-prios.rs b/rtic/examples/async-task-multiple-prios.rs similarity index 100% rename from examples/async-task-multiple-prios.rs rename to rtic/examples/async-task-multiple-prios.rs diff --git a/examples/async-task.rs b/rtic/examples/async-task.rs similarity index 100% rename from examples/async-task.rs rename to rtic/examples/async-task.rs diff --git a/examples/async-timeout.no_rs b/rtic/examples/async-timeout.no_rs similarity index 100% rename from examples/async-timeout.no_rs rename to rtic/examples/async-timeout.no_rs diff --git a/examples/big-struct-opt.rs b/rtic/examples/big-struct-opt.rs similarity index 100% rename from examples/big-struct-opt.rs rename to rtic/examples/big-struct-opt.rs diff --git a/examples/binds.rs b/rtic/examples/binds.rs similarity index 100% rename from examples/binds.rs rename to rtic/examples/binds.rs diff --git a/examples/cancel-reschedule.no_rs b/rtic/examples/cancel-reschedule.no_rs similarity index 100% rename from examples/cancel-reschedule.no_rs rename to rtic/examples/cancel-reschedule.no_rs diff --git a/examples/capacity.no_rs b/rtic/examples/capacity.no_rs similarity index 100% rename from examples/capacity.no_rs rename to rtic/examples/capacity.no_rs diff --git a/examples/cfg-whole-task.no_rs b/rtic/examples/cfg-whole-task.no_rs similarity index 100% rename from examples/cfg-whole-task.no_rs rename to rtic/examples/cfg-whole-task.no_rs diff --git a/examples/common.no_rs b/rtic/examples/common.no_rs similarity index 100% rename from examples/common.no_rs rename to rtic/examples/common.no_rs diff --git a/examples/complex.rs b/rtic/examples/complex.rs similarity index 100% rename from examples/complex.rs rename to rtic/examples/complex.rs diff --git a/examples/declared_locals.rs b/rtic/examples/declared_locals.rs similarity index 100% rename from examples/declared_locals.rs rename to rtic/examples/declared_locals.rs diff --git a/examples/destructure.rs b/rtic/examples/destructure.rs similarity index 100% rename from examples/destructure.rs rename to rtic/examples/destructure.rs diff --git a/examples/extern_binds.rs b/rtic/examples/extern_binds.rs similarity index 100% rename from examples/extern_binds.rs rename to rtic/examples/extern_binds.rs diff --git a/examples/extern_spawn.rs b/rtic/examples/extern_spawn.rs similarity index 100% rename from examples/extern_spawn.rs rename to rtic/examples/extern_spawn.rs diff --git a/examples/generics.rs b/rtic/examples/generics.rs similarity index 100% rename from examples/generics.rs rename to rtic/examples/generics.rs diff --git a/examples/hardware.rs b/rtic/examples/hardware.rs similarity index 100% rename from examples/hardware.rs rename to rtic/examples/hardware.rs diff --git a/examples/idle-wfi.rs b/rtic/examples/idle-wfi.rs similarity index 100% rename from examples/idle-wfi.rs rename to rtic/examples/idle-wfi.rs diff --git a/examples/idle.rs b/rtic/examples/idle.rs similarity index 100% rename from examples/idle.rs rename to rtic/examples/idle.rs diff --git a/examples/init.rs b/rtic/examples/init.rs similarity index 100% rename from examples/init.rs rename to rtic/examples/init.rs diff --git a/examples/locals.rs b/rtic/examples/locals.rs similarity index 100% rename from examples/locals.rs rename to rtic/examples/locals.rs diff --git a/examples/lock-free.no_rs b/rtic/examples/lock-free.no_rs similarity index 100% rename from examples/lock-free.no_rs rename to rtic/examples/lock-free.no_rs diff --git a/examples/lock.rs b/rtic/examples/lock.rs similarity index 100% rename from examples/lock.rs rename to rtic/examples/lock.rs diff --git a/examples/message.no_rs b/rtic/examples/message.no_rs similarity index 100% rename from examples/message.no_rs rename to rtic/examples/message.no_rs diff --git a/examples/message_passing.no_rs b/rtic/examples/message_passing.no_rs similarity index 100% rename from examples/message_passing.no_rs rename to rtic/examples/message_passing.no_rs diff --git a/examples/multilock.rs b/rtic/examples/multilock.rs similarity index 100% rename from examples/multilock.rs rename to rtic/examples/multilock.rs diff --git a/examples/not-sync.rs b/rtic/examples/not-sync.rs similarity index 100% rename from examples/not-sync.rs rename to rtic/examples/not-sync.rs diff --git a/examples/only-shared-access.rs b/rtic/examples/only-shared-access.rs similarity index 100% rename from examples/only-shared-access.rs rename to rtic/examples/only-shared-access.rs diff --git a/examples/periodic-at.no_rs b/rtic/examples/periodic-at.no_rs similarity index 100% rename from examples/periodic-at.no_rs rename to rtic/examples/periodic-at.no_rs diff --git a/examples/periodic-at2.no_rs b/rtic/examples/periodic-at2.no_rs similarity index 100% rename from examples/periodic-at2.no_rs rename to rtic/examples/periodic-at2.no_rs diff --git a/examples/periodic.no_rs b/rtic/examples/periodic.no_rs similarity index 100% rename from examples/periodic.no_rs rename to rtic/examples/periodic.no_rs diff --git a/examples/peripherals-taken.rs b/rtic/examples/peripherals-taken.rs similarity index 100% rename from examples/peripherals-taken.rs rename to rtic/examples/peripherals-taken.rs diff --git a/examples/pool.no_rs b/rtic/examples/pool.no_rs similarity index 100% rename from examples/pool.no_rs rename to rtic/examples/pool.no_rs diff --git a/examples/preempt.rs b/rtic/examples/preempt.rs similarity index 100% rename from examples/preempt.rs rename to rtic/examples/preempt.rs diff --git a/examples/ramfunc.rs b/rtic/examples/ramfunc.rs similarity index 100% rename from examples/ramfunc.rs rename to rtic/examples/ramfunc.rs diff --git a/examples/resource-user-struct.rs b/rtic/examples/resource-user-struct.rs similarity index 100% rename from examples/resource-user-struct.rs rename to rtic/examples/resource-user-struct.rs diff --git a/examples/schedule.no_rs b/rtic/examples/schedule.no_rs similarity index 100% rename from examples/schedule.no_rs rename to rtic/examples/schedule.no_rs diff --git a/examples/shared.rs b/rtic/examples/shared.rs similarity index 100% rename from examples/shared.rs rename to rtic/examples/shared.rs diff --git a/examples/smallest.rs b/rtic/examples/smallest.rs similarity index 100% rename from examples/smallest.rs rename to rtic/examples/smallest.rs diff --git a/examples/spawn.rs b/rtic/examples/spawn.rs similarity index 100% rename from examples/spawn.rs rename to rtic/examples/spawn.rs diff --git a/examples/static.rs b/rtic/examples/static.rs similarity index 100% rename from examples/static.rs rename to rtic/examples/static.rs diff --git a/examples/t-binds.rs b/rtic/examples/t-binds.rs similarity index 100% rename from examples/t-binds.rs rename to rtic/examples/t-binds.rs diff --git a/examples/t-cfg-resources.rs b/rtic/examples/t-cfg-resources.rs similarity index 100% rename from examples/t-cfg-resources.rs rename to rtic/examples/t-cfg-resources.rs diff --git a/examples/t-htask-main.rs b/rtic/examples/t-htask-main.rs similarity index 100% rename from examples/t-htask-main.rs rename to rtic/examples/t-htask-main.rs diff --git a/examples/t-idle-main.rs b/rtic/examples/t-idle-main.rs similarity index 100% rename from examples/t-idle-main.rs rename to rtic/examples/t-idle-main.rs diff --git a/examples/t-late-not-send.rs b/rtic/examples/t-late-not-send.rs similarity index 100% rename from examples/t-late-not-send.rs rename to rtic/examples/t-late-not-send.rs diff --git a/examples/t-schedule.no_rs b/rtic/examples/t-schedule.no_rs similarity index 100% rename from examples/t-schedule.no_rs rename to rtic/examples/t-schedule.no_rs diff --git a/examples/t-spawn.no_rs b/rtic/examples/t-spawn.no_rs similarity index 100% rename from examples/t-spawn.no_rs rename to rtic/examples/t-spawn.no_rs diff --git a/examples/task.rs b/rtic/examples/task.rs similarity index 100% rename from examples/task.rs rename to rtic/examples/task.rs diff --git a/examples/zero-prio-task.rs b/rtic/examples/zero-prio-task.rs similarity index 100% rename from examples/zero-prio-task.rs rename to rtic/examples/zero-prio-task.rs diff --git a/macros/.gitignore b/rtic/macros/.gitignore similarity index 100% rename from macros/.gitignore rename to rtic/macros/.gitignore diff --git a/macros/Cargo.toml b/rtic/macros/Cargo.toml similarity index 99% rename from macros/Cargo.toml rename to rtic/macros/Cargo.toml index 1cc955657b..2041d37c8a 100644 --- a/macros/Cargo.toml +++ b/rtic/macros/Cargo.toml @@ -21,7 +21,7 @@ version = "2.0.0-alpha.0" [lib] proc-macro = true -[feature] +[features] default = [] debugprint = [] # list of supported codegen backends diff --git a/macros/src/analyze.rs b/rtic/macros/src/analyze.rs similarity index 100% rename from macros/src/analyze.rs rename to rtic/macros/src/analyze.rs diff --git a/macros/src/bindings.rs b/rtic/macros/src/bindings.rs similarity index 100% rename from macros/src/bindings.rs rename to rtic/macros/src/bindings.rs diff --git a/macros/src/check.rs b/rtic/macros/src/check.rs similarity index 100% rename from macros/src/check.rs rename to rtic/macros/src/check.rs diff --git a/macros/src/codegen.rs b/rtic/macros/src/codegen.rs similarity index 100% rename from macros/src/codegen.rs rename to rtic/macros/src/codegen.rs diff --git a/macros/src/codegen/assertions.rs b/rtic/macros/src/codegen/assertions.rs similarity index 100% rename from macros/src/codegen/assertions.rs rename to rtic/macros/src/codegen/assertions.rs diff --git a/macros/src/codegen/async_dispatchers.rs b/rtic/macros/src/codegen/async_dispatchers.rs similarity index 100% rename from macros/src/codegen/async_dispatchers.rs rename to rtic/macros/src/codegen/async_dispatchers.rs diff --git a/macros/src/codegen/hardware_tasks.rs b/rtic/macros/src/codegen/hardware_tasks.rs similarity index 100% rename from macros/src/codegen/hardware_tasks.rs rename to rtic/macros/src/codegen/hardware_tasks.rs diff --git a/macros/src/codegen/idle.rs b/rtic/macros/src/codegen/idle.rs similarity index 100% rename from macros/src/codegen/idle.rs rename to rtic/macros/src/codegen/idle.rs diff --git a/macros/src/codegen/init.rs b/rtic/macros/src/codegen/init.rs similarity index 100% rename from macros/src/codegen/init.rs rename to rtic/macros/src/codegen/init.rs diff --git a/macros/src/codegen/local_resources.rs b/rtic/macros/src/codegen/local_resources.rs similarity index 100% rename from macros/src/codegen/local_resources.rs rename to rtic/macros/src/codegen/local_resources.rs diff --git a/macros/src/codegen/local_resources_struct.rs b/rtic/macros/src/codegen/local_resources_struct.rs similarity index 100% rename from macros/src/codegen/local_resources_struct.rs rename to rtic/macros/src/codegen/local_resources_struct.rs diff --git a/macros/src/codegen/main.rs b/rtic/macros/src/codegen/main.rs similarity index 100% rename from macros/src/codegen/main.rs rename to rtic/macros/src/codegen/main.rs diff --git a/macros/src/codegen/module.rs b/rtic/macros/src/codegen/module.rs similarity index 100% rename from macros/src/codegen/module.rs rename to rtic/macros/src/codegen/module.rs diff --git a/macros/src/codegen/post_init.rs b/rtic/macros/src/codegen/post_init.rs similarity index 100% rename from macros/src/codegen/post_init.rs rename to rtic/macros/src/codegen/post_init.rs diff --git a/macros/src/codegen/pre_init.rs b/rtic/macros/src/codegen/pre_init.rs similarity index 100% rename from macros/src/codegen/pre_init.rs rename to rtic/macros/src/codegen/pre_init.rs diff --git a/macros/src/codegen/shared_resources.rs b/rtic/macros/src/codegen/shared_resources.rs similarity index 100% rename from macros/src/codegen/shared_resources.rs rename to rtic/macros/src/codegen/shared_resources.rs diff --git a/macros/src/codegen/shared_resources_struct.rs b/rtic/macros/src/codegen/shared_resources_struct.rs similarity index 100% rename from macros/src/codegen/shared_resources_struct.rs rename to rtic/macros/src/codegen/shared_resources_struct.rs diff --git a/macros/src/codegen/software_tasks.rs b/rtic/macros/src/codegen/software_tasks.rs similarity index 100% rename from macros/src/codegen/software_tasks.rs rename to rtic/macros/src/codegen/software_tasks.rs diff --git a/macros/src/codegen/util.rs b/rtic/macros/src/codegen/util.rs similarity index 100% rename from macros/src/codegen/util.rs rename to rtic/macros/src/codegen/util.rs diff --git a/macros/src/lib.rs b/rtic/macros/src/lib.rs similarity index 100% rename from macros/src/lib.rs rename to rtic/macros/src/lib.rs diff --git a/macros/src/syntax.rs b/rtic/macros/src/syntax.rs similarity index 100% rename from macros/src/syntax.rs rename to rtic/macros/src/syntax.rs diff --git a/macros/src/syntax/.github/bors.toml b/rtic/macros/src/syntax/.github/bors.toml similarity index 100% rename from macros/src/syntax/.github/bors.toml rename to rtic/macros/src/syntax/.github/bors.toml diff --git a/macros/src/syntax/.github/workflows/build.yml b/rtic/macros/src/syntax/.github/workflows/build.yml similarity index 100% rename from macros/src/syntax/.github/workflows/build.yml rename to rtic/macros/src/syntax/.github/workflows/build.yml diff --git a/macros/src/syntax/.github/workflows/changelog.yml b/rtic/macros/src/syntax/.github/workflows/changelog.yml similarity index 100% rename from macros/src/syntax/.github/workflows/changelog.yml rename to rtic/macros/src/syntax/.github/workflows/changelog.yml diff --git a/macros/src/syntax/.github/workflows/properties/build.properties.json b/rtic/macros/src/syntax/.github/workflows/properties/build.properties.json similarity index 100% rename from macros/src/syntax/.github/workflows/properties/build.properties.json rename to rtic/macros/src/syntax/.github/workflows/properties/build.properties.json diff --git a/macros/src/syntax/.gitignore b/rtic/macros/src/syntax/.gitignore similarity index 100% rename from macros/src/syntax/.gitignore rename to rtic/macros/src/syntax/.gitignore diff --git a/macros/src/syntax/.travis.yml b/rtic/macros/src/syntax/.travis.yml similarity index 100% rename from macros/src/syntax/.travis.yml rename to rtic/macros/src/syntax/.travis.yml diff --git a/macros/src/syntax/accessors.rs b/rtic/macros/src/syntax/accessors.rs similarity index 100% rename from macros/src/syntax/accessors.rs rename to rtic/macros/src/syntax/accessors.rs diff --git a/macros/src/syntax/analyze.rs b/rtic/macros/src/syntax/analyze.rs similarity index 100% rename from macros/src/syntax/analyze.rs rename to rtic/macros/src/syntax/analyze.rs diff --git a/macros/src/syntax/ast.rs b/rtic/macros/src/syntax/ast.rs similarity index 100% rename from macros/src/syntax/ast.rs rename to rtic/macros/src/syntax/ast.rs diff --git a/macros/src/syntax/check.rs b/rtic/macros/src/syntax/check.rs similarity index 100% rename from macros/src/syntax/check.rs rename to rtic/macros/src/syntax/check.rs diff --git a/macros/src/syntax/optimize.rs b/rtic/macros/src/syntax/optimize.rs similarity index 100% rename from macros/src/syntax/optimize.rs rename to rtic/macros/src/syntax/optimize.rs diff --git a/macros/src/syntax/parse.rs b/rtic/macros/src/syntax/parse.rs similarity index 100% rename from macros/src/syntax/parse.rs rename to rtic/macros/src/syntax/parse.rs diff --git a/macros/src/syntax/parse/app.rs b/rtic/macros/src/syntax/parse/app.rs similarity index 100% rename from macros/src/syntax/parse/app.rs rename to rtic/macros/src/syntax/parse/app.rs diff --git a/macros/src/syntax/parse/hardware_task.rs b/rtic/macros/src/syntax/parse/hardware_task.rs similarity index 100% rename from macros/src/syntax/parse/hardware_task.rs rename to rtic/macros/src/syntax/parse/hardware_task.rs diff --git a/macros/src/syntax/parse/idle.rs b/rtic/macros/src/syntax/parse/idle.rs similarity index 100% rename from macros/src/syntax/parse/idle.rs rename to rtic/macros/src/syntax/parse/idle.rs diff --git a/macros/src/syntax/parse/init.rs b/rtic/macros/src/syntax/parse/init.rs similarity index 100% rename from macros/src/syntax/parse/init.rs rename to rtic/macros/src/syntax/parse/init.rs diff --git a/macros/src/syntax/parse/resource.rs b/rtic/macros/src/syntax/parse/resource.rs similarity index 100% rename from macros/src/syntax/parse/resource.rs rename to rtic/macros/src/syntax/parse/resource.rs diff --git a/macros/src/syntax/parse/software_task.rs b/rtic/macros/src/syntax/parse/software_task.rs similarity index 100% rename from macros/src/syntax/parse/software_task.rs rename to rtic/macros/src/syntax/parse/software_task.rs diff --git a/macros/src/syntax/parse/util.rs b/rtic/macros/src/syntax/parse/util.rs similarity index 100% rename from macros/src/syntax/parse/util.rs rename to rtic/macros/src/syntax/parse/util.rs diff --git a/macros/tests/ui.rs b/rtic/macros/tests/ui.rs similarity index 100% rename from macros/tests/ui.rs rename to rtic/macros/tests/ui.rs diff --git a/macros/ui/extern-interrupt-used.rs b/rtic/macros/ui/extern-interrupt-used.rs similarity index 100% rename from macros/ui/extern-interrupt-used.rs rename to rtic/macros/ui/extern-interrupt-used.rs diff --git a/macros/ui/extern-interrupt-used.stderr b/rtic/macros/ui/extern-interrupt-used.stderr similarity index 100% rename from macros/ui/extern-interrupt-used.stderr rename to rtic/macros/ui/extern-interrupt-used.stderr diff --git a/macros/ui/idle-double-local.rs b/rtic/macros/ui/idle-double-local.rs similarity index 100% rename from macros/ui/idle-double-local.rs rename to rtic/macros/ui/idle-double-local.rs diff --git a/macros/ui/idle-double-local.stderr b/rtic/macros/ui/idle-double-local.stderr similarity index 100% rename from macros/ui/idle-double-local.stderr rename to rtic/macros/ui/idle-double-local.stderr diff --git a/macros/ui/idle-double-shared.rs b/rtic/macros/ui/idle-double-shared.rs similarity index 100% rename from macros/ui/idle-double-shared.rs rename to rtic/macros/ui/idle-double-shared.rs diff --git a/macros/ui/idle-double-shared.stderr b/rtic/macros/ui/idle-double-shared.stderr similarity index 100% rename from macros/ui/idle-double-shared.stderr rename to rtic/macros/ui/idle-double-shared.stderr diff --git a/macros/ui/idle-input.rs b/rtic/macros/ui/idle-input.rs similarity index 100% rename from macros/ui/idle-input.rs rename to rtic/macros/ui/idle-input.rs diff --git a/macros/ui/idle-input.stderr b/rtic/macros/ui/idle-input.stderr similarity index 100% rename from macros/ui/idle-input.stderr rename to rtic/macros/ui/idle-input.stderr diff --git a/macros/ui/idle-no-context.rs b/rtic/macros/ui/idle-no-context.rs similarity index 100% rename from macros/ui/idle-no-context.rs rename to rtic/macros/ui/idle-no-context.rs diff --git a/macros/ui/idle-no-context.stderr b/rtic/macros/ui/idle-no-context.stderr similarity index 100% rename from macros/ui/idle-no-context.stderr rename to rtic/macros/ui/idle-no-context.stderr diff --git a/macros/ui/idle-not-divergent.rs b/rtic/macros/ui/idle-not-divergent.rs similarity index 100% rename from macros/ui/idle-not-divergent.rs rename to rtic/macros/ui/idle-not-divergent.rs diff --git a/macros/ui/idle-not-divergent.stderr b/rtic/macros/ui/idle-not-divergent.stderr similarity index 100% rename from macros/ui/idle-not-divergent.stderr rename to rtic/macros/ui/idle-not-divergent.stderr diff --git a/macros/ui/idle-output.rs b/rtic/macros/ui/idle-output.rs similarity index 100% rename from macros/ui/idle-output.rs rename to rtic/macros/ui/idle-output.rs diff --git a/macros/ui/idle-output.stderr b/rtic/macros/ui/idle-output.stderr similarity index 100% rename from macros/ui/idle-output.stderr rename to rtic/macros/ui/idle-output.stderr diff --git a/macros/ui/idle-pub.rs b/rtic/macros/ui/idle-pub.rs similarity index 100% rename from macros/ui/idle-pub.rs rename to rtic/macros/ui/idle-pub.rs diff --git a/macros/ui/idle-pub.stderr b/rtic/macros/ui/idle-pub.stderr similarity index 100% rename from macros/ui/idle-pub.stderr rename to rtic/macros/ui/idle-pub.stderr diff --git a/macros/ui/idle-unsafe.rs b/rtic/macros/ui/idle-unsafe.rs similarity index 100% rename from macros/ui/idle-unsafe.rs rename to rtic/macros/ui/idle-unsafe.rs diff --git a/macros/ui/idle-unsafe.stderr b/rtic/macros/ui/idle-unsafe.stderr similarity index 100% rename from macros/ui/idle-unsafe.stderr rename to rtic/macros/ui/idle-unsafe.stderr diff --git a/macros/ui/init-divergent.rs b/rtic/macros/ui/init-divergent.rs similarity index 100% rename from macros/ui/init-divergent.rs rename to rtic/macros/ui/init-divergent.rs diff --git a/macros/ui/init-divergent.stderr b/rtic/macros/ui/init-divergent.stderr similarity index 100% rename from macros/ui/init-divergent.stderr rename to rtic/macros/ui/init-divergent.stderr diff --git a/macros/ui/init-double-local.rs b/rtic/macros/ui/init-double-local.rs similarity index 100% rename from macros/ui/init-double-local.rs rename to rtic/macros/ui/init-double-local.rs diff --git a/macros/ui/init-double-local.stderr b/rtic/macros/ui/init-double-local.stderr similarity index 100% rename from macros/ui/init-double-local.stderr rename to rtic/macros/ui/init-double-local.stderr diff --git a/macros/ui/init-double-shared.rs b/rtic/macros/ui/init-double-shared.rs similarity index 100% rename from macros/ui/init-double-shared.rs rename to rtic/macros/ui/init-double-shared.rs diff --git a/macros/ui/init-double-shared.stderr b/rtic/macros/ui/init-double-shared.stderr similarity index 100% rename from macros/ui/init-double-shared.stderr rename to rtic/macros/ui/init-double-shared.stderr diff --git a/macros/ui/init-input.rs b/rtic/macros/ui/init-input.rs similarity index 100% rename from macros/ui/init-input.rs rename to rtic/macros/ui/init-input.rs diff --git a/macros/ui/init-input.stderr b/rtic/macros/ui/init-input.stderr similarity index 100% rename from macros/ui/init-input.stderr rename to rtic/macros/ui/init-input.stderr diff --git a/macros/ui/init-no-context.rs b/rtic/macros/ui/init-no-context.rs similarity index 100% rename from macros/ui/init-no-context.rs rename to rtic/macros/ui/init-no-context.rs diff --git a/macros/ui/init-no-context.stderr b/rtic/macros/ui/init-no-context.stderr similarity index 100% rename from macros/ui/init-no-context.stderr rename to rtic/macros/ui/init-no-context.stderr diff --git a/macros/ui/init-output.rs b/rtic/macros/ui/init-output.rs similarity index 100% rename from macros/ui/init-output.rs rename to rtic/macros/ui/init-output.rs diff --git a/macros/ui/init-output.stderr b/rtic/macros/ui/init-output.stderr similarity index 100% rename from macros/ui/init-output.stderr rename to rtic/macros/ui/init-output.stderr diff --git a/macros/ui/init-pub.rs b/rtic/macros/ui/init-pub.rs similarity index 100% rename from macros/ui/init-pub.rs rename to rtic/macros/ui/init-pub.rs diff --git a/macros/ui/init-pub.stderr b/rtic/macros/ui/init-pub.stderr similarity index 100% rename from macros/ui/init-pub.stderr rename to rtic/macros/ui/init-pub.stderr diff --git a/macros/ui/init-unsafe.rs b/rtic/macros/ui/init-unsafe.rs similarity index 100% rename from macros/ui/init-unsafe.rs rename to rtic/macros/ui/init-unsafe.rs diff --git a/macros/ui/init-unsafe.stderr b/rtic/macros/ui/init-unsafe.stderr similarity index 100% rename from macros/ui/init-unsafe.stderr rename to rtic/macros/ui/init-unsafe.stderr diff --git a/macros/ui/interrupt-double.rs b/rtic/macros/ui/interrupt-double.rs similarity index 100% rename from macros/ui/interrupt-double.rs rename to rtic/macros/ui/interrupt-double.rs diff --git a/macros/ui/interrupt-double.stderr b/rtic/macros/ui/interrupt-double.stderr similarity index 100% rename from macros/ui/interrupt-double.stderr rename to rtic/macros/ui/interrupt-double.stderr diff --git a/macros/ui/local-collision-2.rs b/rtic/macros/ui/local-collision-2.rs similarity index 100% rename from macros/ui/local-collision-2.rs rename to rtic/macros/ui/local-collision-2.rs diff --git a/macros/ui/local-collision-2.stderr b/rtic/macros/ui/local-collision-2.stderr similarity index 100% rename from macros/ui/local-collision-2.stderr rename to rtic/macros/ui/local-collision-2.stderr diff --git a/macros/ui/local-collision.rs b/rtic/macros/ui/local-collision.rs similarity index 100% rename from macros/ui/local-collision.rs rename to rtic/macros/ui/local-collision.rs diff --git a/macros/ui/local-collision.stderr b/rtic/macros/ui/local-collision.stderr similarity index 100% rename from macros/ui/local-collision.stderr rename to rtic/macros/ui/local-collision.stderr diff --git a/macros/ui/local-malformed-1.rs b/rtic/macros/ui/local-malformed-1.rs similarity index 100% rename from macros/ui/local-malformed-1.rs rename to rtic/macros/ui/local-malformed-1.rs diff --git a/macros/ui/local-malformed-1.stderr b/rtic/macros/ui/local-malformed-1.stderr similarity index 100% rename from macros/ui/local-malformed-1.stderr rename to rtic/macros/ui/local-malformed-1.stderr diff --git a/macros/ui/local-malformed-2.rs b/rtic/macros/ui/local-malformed-2.rs similarity index 100% rename from macros/ui/local-malformed-2.rs rename to rtic/macros/ui/local-malformed-2.rs diff --git a/macros/ui/local-malformed-2.stderr b/rtic/macros/ui/local-malformed-2.stderr similarity index 100% rename from macros/ui/local-malformed-2.stderr rename to rtic/macros/ui/local-malformed-2.stderr diff --git a/macros/ui/local-malformed-3.rs b/rtic/macros/ui/local-malformed-3.rs similarity index 100% rename from macros/ui/local-malformed-3.rs rename to rtic/macros/ui/local-malformed-3.rs diff --git a/macros/ui/local-malformed-3.stderr b/rtic/macros/ui/local-malformed-3.stderr similarity index 100% rename from macros/ui/local-malformed-3.stderr rename to rtic/macros/ui/local-malformed-3.stderr diff --git a/macros/ui/local-malformed-4.rs b/rtic/macros/ui/local-malformed-4.rs similarity index 100% rename from macros/ui/local-malformed-4.rs rename to rtic/macros/ui/local-malformed-4.rs diff --git a/macros/ui/local-malformed-4.stderr b/rtic/macros/ui/local-malformed-4.stderr similarity index 100% rename from macros/ui/local-malformed-4.stderr rename to rtic/macros/ui/local-malformed-4.stderr diff --git a/macros/ui/local-not-declared.rs b/rtic/macros/ui/local-not-declared.rs similarity index 100% rename from macros/ui/local-not-declared.rs rename to rtic/macros/ui/local-not-declared.rs diff --git a/macros/ui/local-not-declared.stderr b/rtic/macros/ui/local-not-declared.stderr similarity index 100% rename from macros/ui/local-not-declared.stderr rename to rtic/macros/ui/local-not-declared.stderr diff --git a/macros/ui/local-pub.rs b/rtic/macros/ui/local-pub.rs similarity index 100% rename from macros/ui/local-pub.rs rename to rtic/macros/ui/local-pub.rs diff --git a/macros/ui/local-pub.stderr b/rtic/macros/ui/local-pub.stderr similarity index 100% rename from macros/ui/local-pub.stderr rename to rtic/macros/ui/local-pub.stderr diff --git a/macros/ui/local-shared-attribute.rs b/rtic/macros/ui/local-shared-attribute.rs similarity index 100% rename from macros/ui/local-shared-attribute.rs rename to rtic/macros/ui/local-shared-attribute.rs diff --git a/macros/ui/local-shared-attribute.stderr b/rtic/macros/ui/local-shared-attribute.stderr similarity index 100% rename from macros/ui/local-shared-attribute.stderr rename to rtic/macros/ui/local-shared-attribute.stderr diff --git a/macros/ui/local-shared.rs b/rtic/macros/ui/local-shared.rs similarity index 100% rename from macros/ui/local-shared.rs rename to rtic/macros/ui/local-shared.rs diff --git a/macros/ui/local-shared.stderr b/rtic/macros/ui/local-shared.stderr similarity index 100% rename from macros/ui/local-shared.stderr rename to rtic/macros/ui/local-shared.stderr diff --git a/macros/ui/shared-lock-free.rs b/rtic/macros/ui/shared-lock-free.rs similarity index 100% rename from macros/ui/shared-lock-free.rs rename to rtic/macros/ui/shared-lock-free.rs diff --git a/macros/ui/shared-lock-free.stderr b/rtic/macros/ui/shared-lock-free.stderr similarity index 100% rename from macros/ui/shared-lock-free.stderr rename to rtic/macros/ui/shared-lock-free.stderr diff --git a/macros/ui/shared-not-declared.rs b/rtic/macros/ui/shared-not-declared.rs similarity index 100% rename from macros/ui/shared-not-declared.rs rename to rtic/macros/ui/shared-not-declared.rs diff --git a/macros/ui/shared-not-declared.stderr b/rtic/macros/ui/shared-not-declared.stderr similarity index 100% rename from macros/ui/shared-not-declared.stderr rename to rtic/macros/ui/shared-not-declared.stderr diff --git a/macros/ui/shared-pub.rs b/rtic/macros/ui/shared-pub.rs similarity index 100% rename from macros/ui/shared-pub.rs rename to rtic/macros/ui/shared-pub.rs diff --git a/macros/ui/shared-pub.stderr b/rtic/macros/ui/shared-pub.stderr similarity index 100% rename from macros/ui/shared-pub.stderr rename to rtic/macros/ui/shared-pub.stderr diff --git a/macros/ui/task-divergent.rs b/rtic/macros/ui/task-divergent.rs similarity index 100% rename from macros/ui/task-divergent.rs rename to rtic/macros/ui/task-divergent.rs diff --git a/macros/ui/task-divergent.stderr b/rtic/macros/ui/task-divergent.stderr similarity index 100% rename from macros/ui/task-divergent.stderr rename to rtic/macros/ui/task-divergent.stderr diff --git a/macros/ui/task-double-local.rs b/rtic/macros/ui/task-double-local.rs similarity index 100% rename from macros/ui/task-double-local.rs rename to rtic/macros/ui/task-double-local.rs diff --git a/macros/ui/task-double-local.stderr b/rtic/macros/ui/task-double-local.stderr similarity index 100% rename from macros/ui/task-double-local.stderr rename to rtic/macros/ui/task-double-local.stderr diff --git a/macros/ui/task-double-priority.rs b/rtic/macros/ui/task-double-priority.rs similarity index 100% rename from macros/ui/task-double-priority.rs rename to rtic/macros/ui/task-double-priority.rs diff --git a/macros/ui/task-double-priority.stderr b/rtic/macros/ui/task-double-priority.stderr similarity index 100% rename from macros/ui/task-double-priority.stderr rename to rtic/macros/ui/task-double-priority.stderr diff --git a/macros/ui/task-double-shared.rs b/rtic/macros/ui/task-double-shared.rs similarity index 100% rename from macros/ui/task-double-shared.rs rename to rtic/macros/ui/task-double-shared.rs diff --git a/macros/ui/task-double-shared.stderr b/rtic/macros/ui/task-double-shared.stderr similarity index 100% rename from macros/ui/task-double-shared.stderr rename to rtic/macros/ui/task-double-shared.stderr diff --git a/macros/ui/task-idle.rs b/rtic/macros/ui/task-idle.rs similarity index 100% rename from macros/ui/task-idle.rs rename to rtic/macros/ui/task-idle.rs diff --git a/macros/ui/task-idle.stderr b/rtic/macros/ui/task-idle.stderr similarity index 100% rename from macros/ui/task-idle.stderr rename to rtic/macros/ui/task-idle.stderr diff --git a/macros/ui/task-init.rs b/rtic/macros/ui/task-init.rs similarity index 100% rename from macros/ui/task-init.rs rename to rtic/macros/ui/task-init.rs diff --git a/macros/ui/task-init.stderr b/rtic/macros/ui/task-init.stderr similarity index 100% rename from macros/ui/task-init.stderr rename to rtic/macros/ui/task-init.stderr diff --git a/macros/ui/task-interrupt.rs b/rtic/macros/ui/task-interrupt.rs similarity index 100% rename from macros/ui/task-interrupt.rs rename to rtic/macros/ui/task-interrupt.rs diff --git a/macros/ui/task-interrupt.stderr b/rtic/macros/ui/task-interrupt.stderr similarity index 100% rename from macros/ui/task-interrupt.stderr rename to rtic/macros/ui/task-interrupt.stderr diff --git a/macros/ui/task-no-context.rs b/rtic/macros/ui/task-no-context.rs similarity index 100% rename from macros/ui/task-no-context.rs rename to rtic/macros/ui/task-no-context.rs diff --git a/macros/ui/task-no-context.stderr b/rtic/macros/ui/task-no-context.stderr similarity index 100% rename from macros/ui/task-no-context.stderr rename to rtic/macros/ui/task-no-context.stderr diff --git a/macros/ui/task-priority-too-high.rs b/rtic/macros/ui/task-priority-too-high.rs similarity index 100% rename from macros/ui/task-priority-too-high.rs rename to rtic/macros/ui/task-priority-too-high.rs diff --git a/macros/ui/task-priority-too-high.stderr b/rtic/macros/ui/task-priority-too-high.stderr similarity index 100% rename from macros/ui/task-priority-too-high.stderr rename to rtic/macros/ui/task-priority-too-high.stderr diff --git a/macros/ui/task-priority-too-low.rs b/rtic/macros/ui/task-priority-too-low.rs similarity index 100% rename from macros/ui/task-priority-too-low.rs rename to rtic/macros/ui/task-priority-too-low.rs diff --git a/macros/ui/task-priority-too-low.stderr b/rtic/macros/ui/task-priority-too-low.stderr similarity index 100% rename from macros/ui/task-priority-too-low.stderr rename to rtic/macros/ui/task-priority-too-low.stderr diff --git a/macros/ui/task-pub.rs b/rtic/macros/ui/task-pub.rs similarity index 100% rename from macros/ui/task-pub.rs rename to rtic/macros/ui/task-pub.rs diff --git a/macros/ui/task-pub.stderr b/rtic/macros/ui/task-pub.stderr similarity index 100% rename from macros/ui/task-pub.stderr rename to rtic/macros/ui/task-pub.stderr diff --git a/macros/ui/task-unsafe.rs b/rtic/macros/ui/task-unsafe.rs similarity index 100% rename from macros/ui/task-unsafe.rs rename to rtic/macros/ui/task-unsafe.rs diff --git a/macros/ui/task-unsafe.stderr b/rtic/macros/ui/task-unsafe.stderr similarity index 100% rename from macros/ui/task-unsafe.stderr rename to rtic/macros/ui/task-unsafe.stderr diff --git a/macros/ui/task-zero-prio.rs b/rtic/macros/ui/task-zero-prio.rs similarity index 100% rename from macros/ui/task-zero-prio.rs rename to rtic/macros/ui/task-zero-prio.rs diff --git a/macros/ui/task-zero-prio.stderr b/rtic/macros/ui/task-zero-prio.stderr similarity index 100% rename from macros/ui/task-zero-prio.stderr rename to rtic/macros/ui/task-zero-prio.stderr diff --git a/rtic/rust-toolchain.toml b/rtic/rust-toolchain.toml new file mode 100644 index 0000000000..e28b55de64 --- /dev/null +++ b/rtic/rust-toolchain.toml @@ -0,0 +1,4 @@ +[toolchain] +channel = "nightly" +components = [ "rust-src", "rustfmt", "llvm-tools-preview" ] +targets = [ "thumbv6m-none-eabi", "thumbv7m-none-eabi" ] diff --git a/src/export.rs b/rtic/src/export.rs similarity index 100% rename from src/export.rs rename to rtic/src/export.rs diff --git a/src/export/executor.rs b/rtic/src/export/executor.rs similarity index 100% rename from src/export/executor.rs rename to rtic/src/export/executor.rs diff --git a/src/lib.rs b/rtic/src/lib.rs similarity index 100% rename from src/lib.rs rename to rtic/src/lib.rs diff --git a/tests/tests.rs b/rtic/tests/tests.rs similarity index 100% rename from tests/tests.rs rename to rtic/tests/tests.rs diff --git a/ui/exception-invalid.rs b/rtic/ui/exception-invalid.rs similarity index 100% rename from ui/exception-invalid.rs rename to rtic/ui/exception-invalid.rs diff --git a/ui/exception-invalid.stderr b/rtic/ui/exception-invalid.stderr similarity index 100% rename from ui/exception-invalid.stderr rename to rtic/ui/exception-invalid.stderr diff --git a/ui/extern-interrupt-not-enough.rs b/rtic/ui/extern-interrupt-not-enough.rs similarity index 100% rename from ui/extern-interrupt-not-enough.rs rename to rtic/ui/extern-interrupt-not-enough.rs diff --git a/ui/extern-interrupt-not-enough.stderr b/rtic/ui/extern-interrupt-not-enough.stderr similarity index 100% rename from ui/extern-interrupt-not-enough.stderr rename to rtic/ui/extern-interrupt-not-enough.stderr diff --git a/ui/extern-interrupt-used.rs b/rtic/ui/extern-interrupt-used.rs similarity index 100% rename from ui/extern-interrupt-used.rs rename to rtic/ui/extern-interrupt-used.rs diff --git a/ui/extern-interrupt-used.stderr b/rtic/ui/extern-interrupt-used.stderr similarity index 100% rename from ui/extern-interrupt-used.stderr rename to rtic/ui/extern-interrupt-used.stderr diff --git a/ui/task-priority-too-high.rs b/rtic/ui/task-priority-too-high.rs similarity index 100% rename from ui/task-priority-too-high.rs rename to rtic/ui/task-priority-too-high.rs diff --git a/ui/task-priority-too-high.stderr b/rtic/ui/task-priority-too-high.stderr similarity index 100% rename from ui/task-priority-too-high.stderr rename to rtic/ui/task-priority-too-high.stderr diff --git a/ui/unknown-interrupt.rs b/rtic/ui/unknown-interrupt.rs similarity index 100% rename from ui/unknown-interrupt.rs rename to rtic/ui/unknown-interrupt.rs diff --git a/ui/unknown-interrupt.stderr b/rtic/ui/unknown-interrupt.stderr similarity index 100% rename from ui/unknown-interrupt.stderr rename to rtic/ui/unknown-interrupt.stderr diff --git a/ui/v6m-interrupt-not-enough.rs_no b/rtic/ui/v6m-interrupt-not-enough.rs_no similarity index 100% rename from ui/v6m-interrupt-not-enough.rs_no rename to rtic/ui/v6m-interrupt-not-enough.rs_no diff --git a/xtask/Cargo.toml b/rtic/xtask/Cargo.toml similarity index 100% rename from xtask/Cargo.toml rename to rtic/xtask/Cargo.toml diff --git a/xtask/src/build.rs b/rtic/xtask/src/build.rs similarity index 100% rename from xtask/src/build.rs rename to rtic/xtask/src/build.rs diff --git a/xtask/src/command.rs b/rtic/xtask/src/command.rs similarity index 100% rename from xtask/src/command.rs rename to rtic/xtask/src/command.rs diff --git a/xtask/src/main.rs b/rtic/xtask/src/main.rs similarity index 100% rename from xtask/src/main.rs rename to rtic/xtask/src/main.rs From a3f48a524b94107a6e250f41f87f29c1c0d65821 Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Mon, 23 Jan 2023 20:14:50 +0100 Subject: [PATCH 252/423] Does CI work again? --- .github/workflows/build.yml | 19 ++++++++++++++++++- .github/workflows/changelog.yml | 22 ++++++++++++++++++++-- rtic-monotonics/CHANGELOG.md | 0 rtic-timer/CHANGELOG.md | 0 rtic/Cargo.toml | 2 +- 5 files changed, 39 insertions(+), 4 deletions(-) create mode 100644 rtic-monotonics/CHANGELOG.md create mode 100644 rtic-timer/CHANGELOG.md diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 35c0bffa4e..1493c3f66a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -22,10 +22,11 @@ jobs: uses: actions/checkout@v3 - name: Fail on warnings + working-directory: ./rtic run: sed -i 's,//deny_warnings_placeholder_for_ci,#![deny(warnings)],' src/lib.rs macros/src/lib.rs - - name: cargo fmt --check + working-directory: ./rtic run: cargo fmt --all -- --check # Compilation check @@ -45,20 +46,24 @@ jobs: uses: actions/checkout@v3 - name: Install Rust ${{ matrix.toolchain }} + working-directory: ./rtic run: | rustup set profile minimal rustup override set ${{ matrix.toolchain }} - name: Configure Rust target (${{ matrix.target }}) + working-directory: ./rtic run: rustup target add ${{ matrix.target }} - name: Fail on warnings + working-directory: ./rtic run: sed -i 's,//deny_warnings_placeholder_for_ci,#![deny(warnings)],' src/lib.rs macros/src/lib.rs - name: Cache Dependencies uses: Swatinem/rust-cache@v2 - name: cargo check + working-directory: ./rtic run: cargo check --target=${{ matrix.target }} # Clippy @@ -70,15 +75,18 @@ jobs: uses: actions/checkout@v3 - name: Fail on warnings + working-directory: ./rtic run: sed -i 's,//deny_warnings_placeholder_for_ci,#![deny(warnings)],' src/lib.rs macros/src/lib.rs - name: Add Rust component clippy + working-directory: ./rtic run: rustup component add clippy - name: Cache Dependencies uses: Swatinem/rust-cache@v2 - name: cargo clippy + working-directory: ./rtic run: cargo clippy # Verify all examples, checks @@ -113,6 +121,7 @@ jobs: uses: Swatinem/rust-cache@v2 - name: Check the examples + working-directory: ./rtic run: cargo check --examples --target=${{ matrix.target }} # Verify the example output with run-pass tests @@ -154,9 +163,11 @@ jobs: sudo apt install -y qemu-system-arm - name: Fail on warnings + working-directory: ./rtic run: sed -i 's,//deny_warnings_placeholder_for_ci,#![deny(warnings)],' src/lib.rs macros/src/lib.rs - name: Run-pass tests + working-directory: ./rtic run: cargo xtask --target ${{ matrix.target }} # Check the correctness of macros/ crate @@ -185,9 +196,11 @@ jobs: uses: Swatinem/rust-cache@v2 - name: Fail on warnings + working-directory: ./rtic run: sed -i 's,//deny_warnings_placeholder_for_ci,#![deny(warnings)],' src/lib.rs macros/src/lib.rs - name: cargo check + working-directory: ./rtic run: cargo check --manifest-path macros/Cargo.toml --target=${{ matrix.target }} # Run the macros test-suite @@ -202,9 +215,11 @@ jobs: uses: Swatinem/rust-cache@v2 - name: Fail on warnings + working-directory: ./rtic run: sed -i 's,//deny_warnings_placeholder_for_ci,#![deny(warnings)],' src/lib.rs macros/src/lib.rs - name: cargo check + working-directory: ./rtic run: cargo test --manifest-path macros/Cargo.toml # Run test suite @@ -219,9 +234,11 @@ jobs: uses: Swatinem/rust-cache@v2 - name: Fail on warnings + working-directory: ./rtic run: sed -i 's,//deny_warnings_placeholder_for_ci,#![deny(warnings)],' src/lib.rs macros/src/lib.rs - name: Run cargo test + working-directory: ./rtic run: cargo test --test tests # # Build documentation, check links diff --git a/.github/workflows/changelog.yml b/.github/workflows/changelog.yml index 74b821dabc..6e23a7a051 100644 --- a/.github/workflows/changelog.yml +++ b/.github/workflows/changelog.yml @@ -18,10 +18,28 @@ jobs: - name: Checkout sources uses: actions/checkout@v3 - - name: Check that changelog updated + - name: Check that changelog updated (rtic) uses: dangoslen/changelog-enforcer@v3 with: - changeLogPath: CHANGELOG.md + changeLogPath: ./rtic/CHANGELOG.md + skipLabels: 'needs-changelog, skip-changelog' + missingUpdateErrorMessage: 'Please add a changelog entry in the CHANGELOG.md file.' + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Check that changelog updated (rtic-timer) + uses: dangoslen/changelog-enforcer@v3 + with: + changeLogPath: ./rtic-timer/CHANGELOG.md + skipLabels: 'needs-changelog, skip-changelog' + missingUpdateErrorMessage: 'Please add a changelog entry in the CHANGELOG.md file.' + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Check that changelog updated (rtic-monotonics) + uses: dangoslen/changelog-enforcer@v3 + with: + changeLogPath: ./rtic-monotonics/CHANGELOG.md skipLabels: 'needs-changelog, skip-changelog' missingUpdateErrorMessage: 'Please add a changelog entry in the CHANGELOG.md file.' env: diff --git a/rtic-monotonics/CHANGELOG.md b/rtic-monotonics/CHANGELOG.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/rtic-timer/CHANGELOG.md b/rtic-timer/CHANGELOG.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/rtic/Cargo.toml b/rtic/Cargo.toml index c22d02372f..6eb691df6e 100644 --- a/rtic/Cargo.toml +++ b/rtic/Cargo.toml @@ -51,7 +51,7 @@ codegen-units = 1 lto = true [workspace] -members = ["macros", "xtask", "rtic-timer"] +members = ["macros", "xtask"] # do not optimize proc-macro deps or build scripts [profile.dev.build-override] From 0f6ae7c1dd0ce10a83682a922bf68aca9121df1c Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Mon, 23 Jan 2023 20:28:28 +0100 Subject: [PATCH 253/423] Add gitignore for book --- book/.gitignore | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 book/.gitignore diff --git a/book/.gitignore b/book/.gitignore new file mode 100644 index 0000000000..ee70b5e5f5 --- /dev/null +++ b/book/.gitignore @@ -0,0 +1,7 @@ +**/*.rs.bk +.#* +.gdb_history +/*/book +/target +Cargo.lock +*.hex From 71b5f9438e1beb5fe12b90415d9d6307e79c0cdf Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Mon, 23 Jan 2023 20:57:56 +0100 Subject: [PATCH 254/423] Fixed systick monotonic --- rtic-monotonics/Cargo.toml | 4 +- rtic-monotonics/src/lib.rs | 2 +- rtic-monotonics/src/systick_monotonic.rs | 133 ++++++++++++++++++ {rtic-timer => rtic-time}/.gitignore | 0 {rtic-timer => rtic-time}/CHANGELOG.md | 0 {rtic-timer => rtic-time}/Cargo.toml | 2 +- {rtic-timer => rtic-time}/rust-toolchain.toml | 0 {rtic-timer => rtic-time}/src/lib.rs | 0 {rtic-timer => rtic-time}/src/linked_list.rs | 0 {rtic-timer => rtic-time}/src/monotonic.rs | 0 10 files changed, 137 insertions(+), 4 deletions(-) rename {rtic-timer => rtic-time}/.gitignore (100%) rename {rtic-timer => rtic-time}/CHANGELOG.md (100%) rename {rtic-timer => rtic-time}/Cargo.toml (92%) rename {rtic-timer => rtic-time}/rust-toolchain.toml (100%) rename {rtic-timer => rtic-time}/src/lib.rs (100%) rename {rtic-timer => rtic-time}/src/linked_list.rs (100%) rename {rtic-timer => rtic-time}/src/monotonic.rs (100%) diff --git a/rtic-monotonics/Cargo.toml b/rtic-monotonics/Cargo.toml index 24448fb29e..68daba4041 100644 --- a/rtic-monotonics/Cargo.toml +++ b/rtic-monotonics/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "rtic-timer" +name = "rtic-monotonics" version = "0.1.0" edition = "2021" @@ -9,4 +9,4 @@ edition = "2021" cortex-m = { version = "0.7.6" } embedded-hal-async = "0.2.0-alpha.0" fugit = { version = "0.3.6", features = ["defmt"] } -rtic-timer = { version = "1.0.0", path = "../rtic-timer" } +rtic-time = { version = "1.0.0", path = "../rtic-time" } diff --git a/rtic-monotonics/src/lib.rs b/rtic-monotonics/src/lib.rs index 88398cad3a..ce30c36b19 100644 --- a/rtic-monotonics/src/lib.rs +++ b/rtic-monotonics/src/lib.rs @@ -6,6 +6,6 @@ #![allow(incomplete_features)] #![feature(async_fn_in_trait)] -pub use rtic_timer::{Monotonic, TimeoutError, TimerQueue}; +pub use rtic_time::{Monotonic, TimeoutError, TimerQueue}; pub mod systick_monotonic; diff --git a/rtic-monotonics/src/systick_monotonic.rs b/rtic-monotonics/src/systick_monotonic.rs index 491cf81c58..7c713b6108 100644 --- a/rtic-monotonics/src/systick_monotonic.rs +++ b/rtic-monotonics/src/systick_monotonic.rs @@ -1 +1,134 @@ //! ... + +use super::Monotonic; +pub use super::{TimeoutError, TimerQueue}; +use core::{ + ops::Deref, + sync::atomic::{AtomicU32, Ordering}, +}; +use cortex_m::peripheral::SYST; +use embedded_hal_async::delay::DelayUs; +use fugit::ExtU32; + +/// Systick implementing `rtic_monotonic::Monotonic` which runs at a +/// settable rate using the `TIMER_HZ` parameter. +pub struct Systick; + +impl Systick { + /// Start a `Monotonic` based on SysTick. + /// + /// The `sysclk` parameter is the speed at which SysTick runs at. This value should come from + /// the clock generation function of the used HAL. + /// + /// Notice that the actual rate of the timer is a best approximation based on the given + /// `sysclk` and `TIMER_HZ`. + /// + /// Note: Give the return value to `TimerQueue::initialize()` to initialize the timer queue. + #[must_use] + pub fn start(mut systick: cortex_m::peripheral::SYST, sysclk: u32) -> Self { + // + TIMER_HZ / 2 provides round to nearest instead of round to 0. + // - 1 as the counter range is inclusive [0, reload] + let reload = (sysclk + TIMER_HZ / 2) / TIMER_HZ - 1; + + assert!(reload <= 0x00ff_ffff); + assert!(reload > 0); + + systick.disable_counter(); + systick.set_clock_source(cortex_m::peripheral::syst::SystClkSource::Core); + systick.set_reload(reload); + systick.enable_interrupt(); + systick.enable_counter(); + + Systick {} + } + + fn systick() -> SYST { + unsafe { core::mem::transmute::<(), SYST>(()) } + } +} + +static SYSTICK_CNT: AtomicU32 = AtomicU32::new(0); + +impl Monotonic for Systick { + type Instant = fugit::TimerInstantU32; + type Duration = fugit::TimerDurationU32; + + const ZERO: Self::Instant = Self::Instant::from_ticks(0); + + fn now() -> Self::Instant { + if Self::systick().has_wrapped() { + SYSTICK_CNT.fetch_add(1, Ordering::AcqRel); + } + + Self::Instant::from_ticks(SYSTICK_CNT.load(Ordering::Relaxed)) + } + + fn set_compare(_: Self::Instant) { + // No need to do something here, we get interrupts anyway. + } + + fn clear_compare_flag() { + // NOOP with SysTick interrupt + } + + fn pend_interrupt() { + cortex_m::peripheral::SCB::set_pendst(); + } + + fn on_interrupt() { + if Self::systick().has_wrapped() { + SYSTICK_CNT.fetch_add(1, Ordering::AcqRel); + } + } + + fn enable_timer() {} + + fn disable_timer() {} +} + +/// Timer queue wrapper to implement traits on +pub struct SystickTimerQueue(TimerQueue>); + +impl SystickTimerQueue { + /// Create a new timer queue. + pub const fn new() -> Self { + Self(TimerQueue::new()) + } +} + +impl Deref for SystickTimerQueue { + type Target = TimerQueue>; + + #[inline(always)] + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DelayUs for SystickTimerQueue { + type Error = core::convert::Infallible; + + async fn delay_us(&mut self, us: u32) -> Result<(), Self::Error> { + self.delay(us.micros()).await; + Ok(()) + } + + async fn delay_ms(&mut self, ms: u32) -> Result<(), Self::Error> { + self.delay(ms.millis()).await; + Ok(()) + } +} + +/// Register the Systick interrupt and crate a timer queue with a specific name and speed. +#[macro_export] +macro_rules! make_systick_timer_queue { + ($timer_queue_name:ident, $systick_speed:expr) => { + static $timer_queue_name: SystickTimerQueue<$systick_speed> = SystickTimerQueue::new(); + + #[no_mangle] + #[allow(non_snake_case)] + unsafe extern "C" fn SysTick() { + $timer_queue_name.on_monotonic_interrupt(); + } + }; +} diff --git a/rtic-timer/.gitignore b/rtic-time/.gitignore similarity index 100% rename from rtic-timer/.gitignore rename to rtic-time/.gitignore diff --git a/rtic-timer/CHANGELOG.md b/rtic-time/CHANGELOG.md similarity index 100% rename from rtic-timer/CHANGELOG.md rename to rtic-time/CHANGELOG.md diff --git a/rtic-timer/Cargo.toml b/rtic-time/Cargo.toml similarity index 92% rename from rtic-timer/Cargo.toml rename to rtic-time/Cargo.toml index b7b3a5fba3..ea0593968b 100644 --- a/rtic-timer/Cargo.toml +++ b/rtic-time/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "rtic-timer" +name = "rtic-time" version = "1.0.0" edition = "2021" diff --git a/rtic-timer/rust-toolchain.toml b/rtic-time/rust-toolchain.toml similarity index 100% rename from rtic-timer/rust-toolchain.toml rename to rtic-time/rust-toolchain.toml diff --git a/rtic-timer/src/lib.rs b/rtic-time/src/lib.rs similarity index 100% rename from rtic-timer/src/lib.rs rename to rtic-time/src/lib.rs diff --git a/rtic-timer/src/linked_list.rs b/rtic-time/src/linked_list.rs similarity index 100% rename from rtic-timer/src/linked_list.rs rename to rtic-time/src/linked_list.rs diff --git a/rtic-timer/src/monotonic.rs b/rtic-time/src/monotonic.rs similarity index 100% rename from rtic-timer/src/monotonic.rs rename to rtic-time/src/monotonic.rs From 143cd136eeeb2856d06a1b83e3ef5682f720c251 Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Tue, 24 Jan 2023 11:55:48 +0100 Subject: [PATCH 255/423] Optimize linked list popping so delete is not run everytime --- rtic-time/src/lib.rs | 131 +++++++---------------------------- rtic-time/src/linked_list.rs | 4 +- 2 files changed, 28 insertions(+), 107 deletions(-) diff --git a/rtic-time/src/lib.rs b/rtic-time/src/lib.rs index d7faa07ff7..850885b78d 100644 --- a/rtic-time/src/lib.rs +++ b/rtic-time/src/lib.rs @@ -6,9 +6,8 @@ #![allow(incomplete_features)] #![feature(async_fn_in_trait)] -pub mod monotonic; - use core::future::{poll_fn, Future}; +use core::mem::MaybeUninit; use core::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; use core::task::{Poll, Waker}; use futures_util::{ @@ -18,20 +17,25 @@ use futures_util::{ pub use monotonic::Monotonic; mod linked_list; +mod monotonic; use linked_list::{Link, LinkedList}; /// Holds a waker and at which time instant this waker shall be awoken. struct WaitingWaker { - waker: Waker, + // This is alway initialized when used, we create this struct on the async stack and then + // initialize the waker field in the `poll_fn` closure (we then know the waker) + waker: MaybeUninit, release_at: Mono::Instant, + was_poped: AtomicBool, } impl Clone for WaitingWaker { fn clone(&self) -> Self { Self { - waker: self.waker.clone(), + waker: MaybeUninit::new(unsafe { self.waker.assume_init_ref() }.clone()), release_at: self.release_at, + was_poped: AtomicBool::new(self.was_poped.load(Ordering::Relaxed)), } } } @@ -109,12 +113,15 @@ impl TimerQueue { let head = self.queue.pop_if(|head| { release_at = Some(head.release_at); - Mono::now() >= head.release_at + let should_pop = Mono::now() >= head.release_at; + head.was_poped.store(should_pop, Ordering::Relaxed); + + should_pop }); match (head, release_at) { (Some(link), _) => { - link.waker.wake(); + link.waker.assume_init().wake(); } (None, Some(instant)) => { Mono::enable_timer(); @@ -181,26 +188,28 @@ impl TimerQueue { ); } - let mut first_run = true; - let queue = &self.queue; let mut link = Link::new(WaitingWaker { - waker: poll_fn(|cx| Poll::Ready(cx.waker().clone())).await, + waker: MaybeUninit::uninit(), release_at: instant, + was_poped: AtomicBool::new(false), }); + let mut first_run = true; + let queue = &self.queue; let marker = &AtomicUsize::new(0); let dropper = OnDrop::new(|| { queue.delete(marker.load(Ordering::Relaxed)); }); - poll_fn(|_| { + poll_fn(|cx| { if Mono::now() >= instant { return Poll::Ready(()); } if first_run { first_run = false; + link.val.waker.write(cx.waker().clone()); let (was_empty, addr) = queue.insert(&mut link); marker.store(addr, Ordering::Relaxed); @@ -214,8 +223,13 @@ impl TimerQueue { }) .await; - // Make sure that our link is deleted from the list before we drop this stack - drop(dropper); + if link.val.was_poped.load(Ordering::Relaxed) { + // If it was poped from the queue there is no need to run delete + dropper.defuse(); + } else { + // Make sure that our link is deleted from the list before we drop this stack + drop(dropper); + } } } @@ -241,96 +255,3 @@ impl Drop for OnDrop { unsafe { self.f.as_ptr().read()() } } } - -// -------- Test program --------- -// -// -// use systick_monotonic::{Systick, TimerQueue}; -// -// // same panicking *behavior* as `panic-probe` but doesn't print a panic message -// // this prevents the panic message being printed *twice* when `defmt::panic` is invoked -// #[defmt::panic_handler] -// fn panic() -> ! { -// cortex_m::asm::udf() -// } -// -// /// Terminates the application and makes `probe-run` exit with exit-code = 0 -// pub fn exit() -> ! { -// loop { -// cortex_m::asm::bkpt(); -// } -// } -// -// defmt::timestamp!("{=u64:us}", { -// let time_us: fugit::MicrosDurationU32 = MONO.now().duration_since_epoch().convert(); -// -// time_us.ticks() as u64 -// }); -// -// make_systick_timer_queue!(MONO, Systick<1_000>); -// -// #[rtic::app( -// device = nrf52832_hal::pac, -// dispatchers = [SWI0_EGU0, SWI1_EGU1, SWI2_EGU2, SWI3_EGU3, SWI4_EGU4, SWI5_EGU5], -// )] -// mod app { -// use super::{Systick, MONO}; -// use fugit::ExtU32; -// -// #[shared] -// struct Shared {} -// -// #[local] -// struct Local {} -// -// #[init] -// fn init(cx: init::Context) -> (Shared, Local) { -// defmt::println!("init"); -// -// let systick = Systick::start(cx.core.SYST, 64_000_000); -// -// defmt::println!("initializing monotonic"); -// -// MONO.initialize(systick); -// -// async_task::spawn().ok(); -// async_task2::spawn().ok(); -// async_task3::spawn().ok(); -// -// (Shared {}, Local {}) -// } -// -// #[idle] -// fn idle(_: idle::Context) -> ! { -// defmt::println!("idle"); -// -// loop { -// core::hint::spin_loop(); -// } -// } -// -// #[task] -// async fn async_task(_: async_task::Context) { -// loop { -// defmt::println!("async task waiting for 1 second"); -// MONO.delay(1.secs()).await; -// } -// } -// -// #[task] -// async fn async_task2(_: async_task2::Context) { -// loop { -// defmt::println!(" async task 2 waiting for 0.5 second"); -// MONO.delay(500.millis()).await; -// } -// } -// -// #[task] -// async fn async_task3(_: async_task3::Context) { -// loop { -// defmt::println!(" async task 3 waiting for 0.2 second"); -// MONO.delay(200.millis()).await; -// } -// } -// } -// diff --git a/rtic-time/src/linked_list.rs b/rtic-time/src/linked_list.rs index 42ff8cb6f9..52a955bd46 100644 --- a/rtic-time/src/linked_list.rs +++ b/rtic-time/src/linked_list.rs @@ -5,7 +5,7 @@ use core::sync::atomic::{AtomicPtr, Ordering}; use critical_section as cs; /// A sorted linked list for the timer queue. -pub struct LinkedList { +pub(crate) struct LinkedList { head: AtomicPtr>, } @@ -156,7 +156,7 @@ impl LinkedList { /// A link in the linked list. pub struct Link { - val: T, + pub(crate) val: T, next: AtomicPtr>, _up: PhantomPinned, } From bdf577c30800aedb0ab6b27768e228c057e18fb5 Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Tue, 24 Jan 2023 12:34:11 +0100 Subject: [PATCH 256/423] Systick runs at 1 kHz --- rtic-monotonics/src/systick_monotonic.rs | 25 ++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/rtic-monotonics/src/systick_monotonic.rs b/rtic-monotonics/src/systick_monotonic.rs index 7c713b6108..af17f937fe 100644 --- a/rtic-monotonics/src/systick_monotonic.rs +++ b/rtic-monotonics/src/systick_monotonic.rs @@ -10,11 +10,12 @@ use cortex_m::peripheral::SYST; use embedded_hal_async::delay::DelayUs; use fugit::ExtU32; -/// Systick implementing `rtic_monotonic::Monotonic` which runs at a -/// settable rate using the `TIMER_HZ` parameter. -pub struct Systick; +const TIMER_HZ: u32 = 1_000; -impl Systick { +/// Systick implementing `rtic_monotonic::Monotonic` which runs at 1 kHz. +pub struct Systick; + +impl Systick { /// Start a `Monotonic` based on SysTick. /// /// The `sysclk` parameter is the speed at which SysTick runs at. This value should come from @@ -49,7 +50,7 @@ impl Systick { static SYSTICK_CNT: AtomicU32 = AtomicU32::new(0); -impl Monotonic for Systick { +impl Monotonic for Systick { type Instant = fugit::TimerInstantU32; type Duration = fugit::TimerDurationU32; @@ -87,17 +88,17 @@ impl Monotonic for Systick { } /// Timer queue wrapper to implement traits on -pub struct SystickTimerQueue(TimerQueue>); +pub struct SystickTimerQueue(TimerQueue); -impl SystickTimerQueue { +impl SystickTimerQueue { /// Create a new timer queue. pub const fn new() -> Self { Self(TimerQueue::new()) } } -impl Deref for SystickTimerQueue { - type Target = TimerQueue>; +impl Deref for SystickTimerQueue { + type Target = TimerQueue; #[inline(always)] fn deref(&self) -> &Self::Target { @@ -105,7 +106,7 @@ impl Deref for SystickTimerQueue { } } -impl DelayUs for SystickTimerQueue { +impl DelayUs for SystickTimerQueue { type Error = core::convert::Infallible; async fn delay_us(&mut self, us: u32) -> Result<(), Self::Error> { @@ -122,8 +123,8 @@ impl DelayUs for SystickTimerQueue { /// Register the Systick interrupt and crate a timer queue with a specific name and speed. #[macro_export] macro_rules! make_systick_timer_queue { - ($timer_queue_name:ident, $systick_speed:expr) => { - static $timer_queue_name: SystickTimerQueue<$systick_speed> = SystickTimerQueue::new(); + ($timer_queue_name:ident) => { + static $timer_queue_name: SystickTimerQueue = SystickTimerQueue::new(); #[no_mangle] #[allow(non_snake_case)] From 2e96229c912076829d4c2be83a8b5ce27d81ebaa Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Wed, 25 Jan 2023 15:24:32 +0100 Subject: [PATCH 257/423] Remove unnecessary MaybeUninit --- rtic-time/src/lib.rs | 36 +++++++++++++++++------------------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/rtic-time/src/lib.rs b/rtic-time/src/lib.rs index 850885b78d..34f93622aa 100644 --- a/rtic-time/src/lib.rs +++ b/rtic-time/src/lib.rs @@ -7,7 +7,6 @@ #![feature(async_fn_in_trait)] use core::future::{poll_fn, Future}; -use core::mem::MaybeUninit; use core::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; use core::task::{Poll, Waker}; use futures_util::{ @@ -23,9 +22,7 @@ use linked_list::{Link, LinkedList}; /// Holds a waker and at which time instant this waker shall be awoken. struct WaitingWaker { - // This is alway initialized when used, we create this struct on the async stack and then - // initialize the waker field in the `poll_fn` closure (we then know the waker) - waker: MaybeUninit, + waker: Waker, release_at: Mono::Instant, was_poped: AtomicBool, } @@ -33,7 +30,7 @@ struct WaitingWaker { impl Clone for WaitingWaker { fn clone(&self) -> Self { Self { - waker: MaybeUninit::new(unsafe { self.waker.assume_init_ref() }.clone()), + waker: self.waker.clone(), release_at: self.release_at, was_poped: AtomicBool::new(self.was_poped.load(Ordering::Relaxed)), } @@ -121,7 +118,7 @@ impl TimerQueue { match (head, release_at) { (Some(link), _) => { - link.waker.assume_init().wake(); + link.waker.wake(); } (None, Some(instant)) => { Mono::enable_timer(); @@ -188,13 +185,8 @@ impl TimerQueue { ); } - let mut link = Link::new(WaitingWaker { - waker: MaybeUninit::uninit(), - release_at: instant, - was_poped: AtomicBool::new(false), - }); + let mut link = None; - let mut first_run = true; let queue = &self.queue; let marker = &AtomicUsize::new(0); @@ -207,10 +199,14 @@ impl TimerQueue { return Poll::Ready(()); } - if first_run { - first_run = false; - link.val.waker.write(cx.waker().clone()); - let (was_empty, addr) = queue.insert(&mut link); + if link.is_none() { + let mut link_ref = link.insert(Link::new(WaitingWaker { + waker: cx.waker().clone(), + release_at: instant, + was_poped: AtomicBool::new(false), + })); + + let (was_empty, addr) = queue.insert(&mut link_ref); marker.store(addr, Ordering::Relaxed); if was_empty { @@ -223,9 +219,11 @@ impl TimerQueue { }) .await; - if link.val.was_poped.load(Ordering::Relaxed) { - // If it was poped from the queue there is no need to run delete - dropper.defuse(); + if let Some(link) = link { + if link.val.was_poped.load(Ordering::Relaxed) { + // If it was poped from the queue there is no need to run delete + dropper.defuse(); + } } else { // Make sure that our link is deleted from the list before we drop this stack drop(dropper); From 51d4eccc726d4dc7950aac6f8d74a34ddf669f67 Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Thu, 26 Jan 2023 21:29:52 +0100 Subject: [PATCH 258/423] Fixes in MPSC linked list and dropper handling --- rtic-channel/Cargo.toml | 15 ++ rtic-channel/src/lib.rs | 380 +++++++++++++++++++++++++++++++++ rtic-channel/src/wait_queue.rs | 278 ++++++++++++++++++++++++ rtic-time/src/lib.rs | 1 - 4 files changed, 673 insertions(+), 1 deletion(-) create mode 100644 rtic-channel/Cargo.toml create mode 100644 rtic-channel/src/lib.rs create mode 100644 rtic-channel/src/wait_queue.rs diff --git a/rtic-channel/Cargo.toml b/rtic-channel/Cargo.toml new file mode 100644 index 0000000000..89623524e5 --- /dev/null +++ b/rtic-channel/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "rtic-channel" +version = "1.0.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +heapless = "0.7" +critical-section = "1" + + +[features] +default = [] +testing = ["critical-section/std"] diff --git a/rtic-channel/src/lib.rs b/rtic-channel/src/lib.rs new file mode 100644 index 0000000000..a7098ee245 --- /dev/null +++ b/rtic-channel/src/lib.rs @@ -0,0 +1,380 @@ +//! Crate + +#![no_std] +#![deny(missing_docs)] + +use core::{ + cell::UnsafeCell, + future::poll_fn, + mem::MaybeUninit, + ptr, + task::{Poll, Waker}, +}; +use heapless::Deque; +use wait_queue::WaitQueue; +use waker_registration::CriticalSectionWakerRegistration as WakerRegistration; + +mod wait_queue; +mod waker_registration; + +/// An MPSC channel for use in no-alloc systems. `N` sets the size of the queue. +/// +/// This channel uses critical sections, however there are extremely small and all `memcpy` +/// operations of `T` are done without critical sections. +pub struct Channel { + // Here are all indexes that are not used in `slots` and ready to be allocated. + freeq: UnsafeCell>, + // Here are wakers and indexes to slots that are ready to be dequeued by the receiver. + readyq: UnsafeCell>, + // Waker for the receiver. + receiver_waker: WakerRegistration, + // Storage for N `T`s, so we don't memcpy around a lot of `T`s. + slots: [UnsafeCell>; N], + // If there is no room in the queue a `Sender`s can wait for there to be place in the queue. + wait_queue: WaitQueue, + // Keep track of the receiver. + receiver_dropped: UnsafeCell, + // Keep track of the number of senders. + num_senders: UnsafeCell, +} + +struct UnsafeAccess<'a, const N: usize> { + freeq: &'a mut Deque, + readyq: &'a mut Deque, + receiver_dropped: &'a mut bool, + num_senders: &'a mut usize, +} + +impl Channel { + const _CHECK: () = assert!(N < 256, "This queue support a maximum of 255 entries"); + + const INIT_SLOTS: UnsafeCell> = UnsafeCell::new(MaybeUninit::uninit()); + + /// Create a new channel. + pub const fn new() -> Self { + Self { + freeq: UnsafeCell::new(Deque::new()), + readyq: UnsafeCell::new(Deque::new()), + receiver_waker: WakerRegistration::new(), + slots: [Self::INIT_SLOTS; N], + wait_queue: WaitQueue::new(), + receiver_dropped: UnsafeCell::new(false), + num_senders: UnsafeCell::new(0), + } + } + + /// Split the queue into a `Sender`/`Receiver` pair. + pub fn split<'a>(&'a mut self) -> (Sender<'a, T, N>, Receiver<'a, T, N>) { + // Fill free queue + for idx in 0..(N - 1) as u8 { + debug_assert!(!self.freeq.get_mut().is_full()); + + // SAFETY: This safe as the loop goes from 0 to the capacity of the underlying queue. + unsafe { + self.freeq.get_mut().push_back_unchecked(idx); + } + } + + debug_assert!(self.freeq.get_mut().is_full()); + + // There is now 1 sender + *self.num_senders.get_mut() = 1; + + (Sender(self), Receiver(self)) + } + + fn access<'a>(&'a self, _cs: critical_section::CriticalSection) -> UnsafeAccess<'a, N> { + // SAFETY: This is safe as are in a critical section. + unsafe { + UnsafeAccess { + freeq: &mut *self.freeq.get(), + readyq: &mut *self.readyq.get(), + receiver_dropped: &mut *self.receiver_dropped.get(), + num_senders: &mut *self.num_senders.get(), + } + } + } +} + +/// Creates a split channel with `'static` lifetime. +#[macro_export] +macro_rules! make_channel { + ($type:path, $size:expr) => {{ + static mut CHANNEL: Channel<$type, $size> = Channel::new(); + + // SAFETY: This is safe as we hide the static mut from others to access it. + // Only this point is where the mutable access happens. + unsafe { CHANNEL.split() } + }}; +} + +// -------- Sender + +/// Error state for when the receiver has been dropped. +pub struct NoReceiver(pub T); + +/// A `Sender` can send to the channel and can be cloned. +pub struct Sender<'a, T, const N: usize>(&'a Channel); + +unsafe impl<'a, T, const N: usize> Send for Sender<'a, T, N> {} + +impl<'a, T, const N: usize> Sender<'a, T, N> { + #[inline(always)] + fn send_footer(&mut self, idx: u8, val: T) { + // Write the value to the slots, note; this memcpy is not under a critical section. + unsafe { + ptr::write( + self.0.slots.get_unchecked(idx as usize).get() as *mut T, + val, + ) + } + + // Write the value into the ready queue. + critical_section::with(|cs| unsafe { self.0.access(cs).readyq.push_back_unchecked(idx) }); + + // If there is a receiver waker, wake it. + self.0.receiver_waker.wake(); + } + + /// Try to send a value, non-blocking. If the channel is full this will return an error. + /// Note; this does not check if the channel is closed. + pub fn try_send(&mut self, val: T) -> Result<(), T> { + // If the wait queue is not empty, we can't try to push into the queue. + if !self.0.wait_queue.is_empty() { + return Err(val); + } + + let idx = + if let Some(idx) = critical_section::with(|cs| self.0.access(cs).freeq.pop_front()) { + idx + } else { + return Err(val); + }; + + self.send_footer(idx, val); + + Ok(()) + } + + /// Send a value. If there is no place left in the queue this will wait until there is. + /// If the receiver does not exist this will return an error. + pub async fn send(&mut self, val: T) -> Result<(), NoReceiver> { + if self.is_closed() {} + + let mut __hidden_link: Option> = None; + + // Make this future `Drop`-safe + let link_ptr = &mut __hidden_link as *mut Option>; + let dropper = OnDrop::new(|| { + // SAFETY: We only run this closure and dereference the pointer if we have + // exited the `poll_fn` below in the `drop(dropper)` call. The other dereference + // of this pointer is in the `poll_fn`. + if let Some(link) = unsafe { &mut *link_ptr } { + link.remove_from_list(&self.0.wait_queue); + } + }); + + let idx = poll_fn(|cx| { + if self.is_closed() { + return Poll::Ready(Err(())); + } + + // Do all this in one critical section, else there can be race conditions + let queue_idx = critical_section::with(|cs| { + if !self.0.wait_queue.is_empty() || self.0.access(cs).freeq.is_empty() { + // SAFETY: This pointer is only dereferenced here and on drop of the future. + let link = unsafe { &mut *link_ptr }; + if link.is_none() { + // Place the link in the wait queue on first run. + let link_ref = link.insert(wait_queue::Link::new(cx.waker().clone())); + self.0.wait_queue.push(link_ref); + } + + return None; + } + + // Get index as the queue is guaranteed not empty and the wait queue is empty + let idx = unsafe { self.0.access(cs).freeq.pop_front_unchecked() }; + + Some(idx) + }); + + if let Some(idx) = queue_idx { + // Return the index + Poll::Ready(Ok(idx)) + } else { + return Poll::Pending; + } + }) + .await; + + // Make sure the link is removed from the queue. + drop(dropper); + + if let Ok(idx) = idx { + self.send_footer(idx, val); + + Ok(()) + } else { + Err(NoReceiver(val)) + } + } + + /// Returns true if there is no `Receiver`s. + pub fn is_closed(&self) -> bool { + critical_section::with(|cs| *self.0.access(cs).receiver_dropped) + } + + /// Is the queue full. + pub fn is_full(&self) -> bool { + critical_section::with(|cs| self.0.access(cs).freeq.is_empty()) + } + + /// Is the queue empty. + pub fn is_empty(&self) -> bool { + critical_section::with(|cs| self.0.access(cs).freeq.is_full()) + } +} + +impl<'a, T, const N: usize> Drop for Sender<'a, T, N> { + fn drop(&mut self) { + // Count down the reference counter + let num_senders = critical_section::with(|cs| { + *self.0.access(cs).num_senders -= 1; + + *self.0.access(cs).num_senders + }); + + // If there are no senders, wake the receiver to do error handling. + if num_senders == 0 { + self.0.receiver_waker.wake(); + } + } +} + +impl<'a, T, const N: usize> Clone for Sender<'a, T, N> { + fn clone(&self) -> Self { + // Count up the reference counter + critical_section::with(|cs| *self.0.access(cs).num_senders += 1); + + Self(self.0) + } +} + +// -------- Receiver + +/// A receiver of the channel. There can only be one receiver at any time. +pub struct Receiver<'a, T, const N: usize>(&'a Channel); + +/// Error state for when all senders has been dropped. +pub struct NoSender; + +impl<'a, T, const N: usize> Receiver<'a, T, N> { + /// Receives a value if there is one in the channel, non-blocking. + /// Note; this does not check if the channel is closed. + pub fn try_recv(&mut self) -> Option { + // Try to get a ready slot. + let ready_slot = + critical_section::with(|cs| self.0.access(cs).readyq.pop_front().map(|rs| rs)); + + if let Some(rs) = ready_slot { + // Read the value from the slots, note; this memcpy is not under a critical section. + let r = unsafe { ptr::read(self.0.slots.get_unchecked(rs as usize).get() as *const T) }; + + // Return the index to the free queue after we've read the value. + critical_section::with(|cs| unsafe { self.0.access(cs).freeq.push_back_unchecked(rs) }); + + // If someone is waiting in the WaiterQueue, wake the first one up. + if let Some(wait_head) = self.0.wait_queue.pop() { + wait_head.wake(); + } + + Some(r) + } else { + None + } + } + + /// Receives a value, waiting if the queue is empty. + /// If all senders are dropped this will error with `NoSender`. + pub async fn recv(&mut self) -> Result { + // There was nothing in the queue, setup the waiting. + poll_fn(|cx| { + // Register waker. + // TODO: Should it happen here or after the if? This might cause a spurious wake. + self.0.receiver_waker.register(cx.waker()); + + // Try to dequeue. + if let Some(val) = self.try_recv() { + return Poll::Ready(Ok(val)); + } + + // If the queue is empty and there is no sender, return the error. + if self.is_closed() { + return Poll::Ready(Err(NoSender)); + } + + Poll::Pending + }) + .await + } + + /// Returns true if there are no `Sender`s. + pub fn is_closed(&self) -> bool { + critical_section::with(|cs| *self.0.access(cs).num_senders == 0) + } + + /// Is the queue full. + pub fn is_full(&self) -> bool { + critical_section::with(|cs| self.0.access(cs).readyq.is_empty()) + } + + /// Is the queue empty. + pub fn is_empty(&self) -> bool { + critical_section::with(|cs| self.0.access(cs).readyq.is_empty()) + } +} + +impl<'a, T, const N: usize> Drop for Receiver<'a, T, N> { + fn drop(&mut self) { + // Mark the receiver as dropped and wake all waiters + critical_section::with(|cs| *self.0.access(cs).receiver_dropped = true); + + while let Some(waker) = self.0.wait_queue.pop() { + waker.wake(); + } + } +} + +struct OnDrop { + f: core::mem::MaybeUninit, +} + +impl OnDrop { + pub fn new(f: F) -> Self { + Self { + f: core::mem::MaybeUninit::new(f), + } + } + + #[allow(unused)] + pub fn defuse(self) { + core::mem::forget(self) + } +} + +impl Drop for OnDrop { + fn drop(&mut self) { + unsafe { self.f.as_ptr().read()() } + } +} + +#[cfg(test)] +#[macro_use] +extern crate std; + +#[cfg(test)] +mod tests { + #[test] + fn channel() {} +} diff --git a/rtic-channel/src/wait_queue.rs b/rtic-channel/src/wait_queue.rs new file mode 100644 index 0000000000..90d762bdf3 --- /dev/null +++ b/rtic-channel/src/wait_queue.rs @@ -0,0 +1,278 @@ +//! ... + +use core::cell::UnsafeCell; +use core::marker::PhantomPinned; +use core::ptr::null_mut; +use core::sync::atomic::{AtomicPtr, Ordering}; +use core::task::Waker; +use critical_section as cs; + +pub type WaitQueue = LinkedList; + +struct MyLinkPtr(UnsafeCell<*mut Link>); + +impl MyLinkPtr { + #[inline(always)] + fn new(val: *mut Link) -> Self { + Self(UnsafeCell::new(val)) + } + + /// SAFETY: Only use this in a critical section, and don't forget them barriers. + #[inline(always)] + unsafe fn load_relaxed(&self) -> *mut Link { + unsafe { *self.0.get() } + } + + /// SAFETY: Only use this in a critical section, and don't forget them barriers. + #[inline(always)] + unsafe fn store_relaxed(&self, val: *mut Link) { + unsafe { self.0.get().write(val) } + } +} + +/// A FIFO linked list for a wait queue. +pub struct LinkedList { + head: AtomicPtr>, // UnsafeCell<*mut Link> + tail: AtomicPtr>, +} + +impl LinkedList { + /// Create a new linked list. + pub const fn new() -> Self { + Self { + head: AtomicPtr::new(null_mut()), + tail: AtomicPtr::new(null_mut()), + } + } +} + +impl LinkedList { + const R: Ordering = Ordering::Relaxed; + + /// Pop the first element in the queue. + pub fn pop(&self) -> Option { + cs::with(|_| { + // Make sure all previous writes are visible + core::sync::atomic::fence(Ordering::SeqCst); + + let head = self.head.load(Self::R); + + // SAFETY: `as_ref` is safe as `insert` requires a valid reference to a link + if let Some(head_ref) = unsafe { head.as_ref() } { + // Move head to the next element + self.head.store(head_ref.next.load(Self::R), Self::R); + + // We read the value at head + let head_val = head_ref.val.clone(); + + let tail = self.tail.load(Self::R); + if head == tail { + // The queue is empty + self.tail.store(null_mut(), Self::R); + } + + if let Some(next_ref) = unsafe { head_ref.next.load(Self::R).as_ref() } { + next_ref.prev.store(null_mut(), Self::R); + } + + // Clear the pointers in the node. + head_ref.next.store(null_mut(), Self::R); + head_ref.prev.store(null_mut(), Self::R); + + return Some(head_val); + } + + None + }) + } + + /// Put an element at the back of the queue. + pub fn push(&self, link: &mut Link) { + cs::with(|_| { + // Make sure all previous writes are visible + core::sync::atomic::fence(Ordering::SeqCst); + + let tail = self.tail.load(Self::R); + + if let Some(tail_ref) = unsafe { tail.as_ref() } { + // Queue is not empty + link.prev.store(tail, Self::R); + self.tail.store(link, Self::R); + tail_ref.next.store(link, Self::R); + } else { + // Queue is empty + self.tail.store(link, Self::R); + self.head.store(link, Self::R); + } + }); + } + + /// Check if the queue is empty. + pub fn is_empty(&self) -> bool { + self.head.load(Self::R).is_null() + } +} + +/// A link in the linked list. +pub struct Link { + pub(crate) val: T, + next: AtomicPtr>, + prev: AtomicPtr>, + _up: PhantomPinned, +} + +impl Link { + const R: Ordering = Ordering::Relaxed; + + /// Create a new link. + pub const fn new(val: T) -> Self { + Self { + val, + next: AtomicPtr::new(null_mut()), + prev: AtomicPtr::new(null_mut()), + _up: PhantomPinned, + } + } + + pub fn remove_from_list(&mut self, list: &LinkedList) { + cs::with(|_| { + // Make sure all previous writes are visible + core::sync::atomic::fence(Ordering::SeqCst); + + let prev = self.prev.load(Self::R); + let next = self.next.load(Self::R); + + match unsafe { (prev.as_ref(), next.as_ref()) } { + (None, None) => { + // Not in the list or alone in the list, check if list head == node address + let sp = self as *const _; + + if sp == list.head.load(Ordering::Relaxed) { + list.head.store(null_mut(), Self::R); + list.tail.store(null_mut(), Self::R); + } + } + (None, Some(next_ref)) => { + // First in the list + next_ref.prev.store(null_mut(), Self::R); + list.head.store(next, Self::R); + } + (Some(prev_ref), None) => { + // Last in the list + prev_ref.next.store(null_mut(), Self::R); + list.tail.store(prev, Self::R); + } + (Some(prev_ref), Some(next_ref)) => { + // Somewhere in the list + + // Connect the `prev.next` and `next.prev` with each other to remove the node + prev_ref.next.store(next, Self::R); + next_ref.prev.store(prev, Self::R); + } + } + }) + } +} + +#[cfg(test)] +impl LinkedList { + fn print(&self) { + cs::with(|_| { + // Make sure all previous writes are visible + core::sync::atomic::fence(Ordering::SeqCst); + + let mut head = self.head.load(Self::R); + let tail = self.tail.load(Self::R); + + println!( + "List - h = 0x{:x}, t = 0x{:x}", + head as usize, tail as usize + ); + + let mut i = 0; + + // SAFETY: `as_ref` is safe as `insert` requires a valid reference to a link + while let Some(head_ref) = unsafe { head.as_ref() } { + println!( + " {}: {:?}, s = 0x{:x}, n = 0x{:x}, p = 0x{:x}", + i, + head_ref.val, + head as usize, + head_ref.next.load(Ordering::Relaxed) as usize, + head_ref.prev.load(Ordering::Relaxed) as usize + ); + + head = head_ref.next.load(Self::R); + + i += 1; + } + }); + } +} + +#[cfg(test)] +impl Link { + fn print(&self) { + cs::with(|_| { + // Make sure all previous writes are visible + core::sync::atomic::fence(Ordering::SeqCst); + + println!("Link:"); + + println!( + " val = {:?}, n = 0x{:x}, p = 0x{:x}", + self.val, + self.next.load(Ordering::Relaxed) as usize, + self.prev.load(Ordering::Relaxed) as usize + ); + }); + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn linked_list() { + let mut wq = LinkedList::::new(); + + let mut i1 = Link::new(10); + let mut i2 = Link::new(11); + let mut i3 = Link::new(12); + let mut i4 = Link::new(13); + let mut i5 = Link::new(14); + + wq.push(&mut i1); + wq.push(&mut i2); + wq.push(&mut i3); + wq.push(&mut i4); + wq.push(&mut i5); + + wq.print(); + + wq.pop(); + i1.print(); + + wq.print(); + + i4.remove_from_list(&wq); + + wq.print(); + + // i1.remove_from_list(&wq); + // wq.print(); + + println!("i2"); + i2.remove_from_list(&wq); + wq.print(); + + println!("i3"); + i3.remove_from_list(&wq); + wq.print(); + + println!("i5"); + i5.remove_from_list(&wq); + wq.print(); + } +} diff --git a/rtic-time/src/lib.rs b/rtic-time/src/lib.rs index 34f93622aa..78ece1df20 100644 --- a/rtic-time/src/lib.rs +++ b/rtic-time/src/lib.rs @@ -1,7 +1,6 @@ //! Crate #![no_std] -#![no_main] #![deny(missing_docs)] #![allow(incomplete_features)] #![feature(async_fn_in_trait)] From 1974f1f00ac950c68a99b6c83fe448093aca5011 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Tj=C3=A4der?= Date: Fri, 27 Jan 2023 11:24:43 +0100 Subject: [PATCH 259/423] Make clippy and fmt happy --- rtic/macros/src/codegen/util.rs | 2 +- rtic/macros/src/lib.rs | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/rtic/macros/src/codegen/util.rs b/rtic/macros/src/codegen/util.rs index e121487c70..d0c8cc0e44 100644 --- a/rtic/macros/src/codegen/util.rs +++ b/rtic/macros/src/codegen/util.rs @@ -123,7 +123,7 @@ pub fn regroup_inputs( let mut tys = vec![]; for (i, input) in inputs.iter().enumerate() { - let i = Ident::new(&format!("_{}", i), Span::call_site()); + let i = Ident::new(&format!("_{i}"), Span::call_site()); let ty = &input.ty; args.push(quote!(#i: #ty)); diff --git a/rtic/macros/src/lib.rs b/rtic/macros/src/lib.rs index a8422d0927..92d7cddd99 100644 --- a/rtic/macros/src/lib.rs +++ b/rtic/macros/src/lib.rs @@ -2,7 +2,6 @@ html_logo_url = "https://raw.githubusercontent.com/rtic-rs/rtic/master/book/en/src/RTIC.svg", html_favicon_url = "https://raw.githubusercontent.com/rtic-rs/rtic/master/book/en/src/RTIC.svg" )] - //deny_warnings_placeholder_for_ci use proc_macro::TokenStream; From 2af2cbf637fdc9f9b8d533ad0cc00b928a209659 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Tj=C3=A4der?= Date: Fri, 27 Jan 2023 12:09:39 +0100 Subject: [PATCH 260/423] Experiment with changelog enforcer per path --- .github/workflows/changelog.yml | 66 +++++++++++++++++++++------------ 1 file changed, 43 insertions(+), 23 deletions(-) diff --git a/.github/workflows/changelog.yml b/.github/workflows/changelog.yml index 6e23a7a051..56161801ac 100644 --- a/.github/workflows/changelog.yml +++ b/.github/workflows/changelog.yml @@ -18,29 +18,49 @@ jobs: - name: Checkout sources uses: actions/checkout@v3 - - name: Check that changelog updated (rtic) - uses: dangoslen/changelog-enforcer@v3 + - name: Check which component is modified + uses: dorny/paths-filter@v2 + id: changes with: - changeLogPath: ./rtic/CHANGELOG.md - skipLabels: 'needs-changelog, skip-changelog' - missingUpdateErrorMessage: 'Please add a changelog entry in the CHANGELOG.md file.' - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + filters: | + rtic: + - 'rtic/**' + rtic-timer: + - 'rtic-timer/**' + rtic-monotonics: + - 'rtic-monotonics/**' - - name: Check that changelog updated (rtic-timer) - uses: dangoslen/changelog-enforcer@v3 - with: - changeLogPath: ./rtic-timer/CHANGELOG.md - skipLabels: 'needs-changelog, skip-changelog' - missingUpdateErrorMessage: 'Please add a changelog entry in the CHANGELOG.md file.' - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + # run only if some file in matching folder was changed + - if: steps.changes.outputs.rtic == 'true' + steps: - - name: Check that changelog updated (rtic-monotonics) - uses: dangoslen/changelog-enforcer@v3 - with: - changeLogPath: ./rtic-monotonics/CHANGELOG.md - skipLabels: 'needs-changelog, skip-changelog' - missingUpdateErrorMessage: 'Please add a changelog entry in the CHANGELOG.md file.' - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file + - name: Check that changelog updated (rtic) + uses: dangoslen/changelog-enforcer@v3 + with: + changeLogPath: ./rtic/CHANGELOG.md + skipLabels: 'needs-changelog, skip-changelog' + missingUpdateErrorMessage: 'Please add a changelog entry in the rtic/CHANGELOG.md file.' + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - if: steps.changes.outputs.rtic-timer == 'true' + steps: + - name: Check that changelog updated (rtic-timer) + uses: dangoslen/changelog-enforcer@v3 + with: + changeLogPath: ./rtic-timer/CHANGELOG.md + skipLabels: 'needs-changelog, skip-changelog' + missingUpdateErrorMessage: 'Please add a changelog entry in the rtic-timer/CHANGELOG.md file.' + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - if: steps.changes.outputs.rtic-monotonics == 'true' + steps: + - name: Check that changelog updated (rtic-monotonics) + uses: dangoslen/changelog-enforcer@v3 + with: + changeLogPath: ./rtic-monotonics/CHANGELOG.md + skipLabels: 'needs-changelog, skip-changelog' + missingUpdateErrorMessage: 'Please add a changelog entry in the rtic-monotonics/CHANGELOG.md file.' + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file From 67b16594bfc35ad6fd5ed170c61384b7bdcee406 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Tj=C3=A4der?= Date: Fri, 27 Jan 2023 12:28:32 +0100 Subject: [PATCH 261/423] CI: Changelog fix syntax --- .github/workflows/changelog.yml | 59 +++++++++++++++------------------ 1 file changed, 27 insertions(+), 32 deletions(-) diff --git a/.github/workflows/changelog.yml b/.github/workflows/changelog.yml index 56161801ac..eb715727b4 100644 --- a/.github/workflows/changelog.yml +++ b/.github/workflows/changelog.yml @@ -30,37 +30,32 @@ jobs: rtic-monotonics: - 'rtic-monotonics/**' - # run only if some file in matching folder was changed - - if: steps.changes.outputs.rtic == 'true' - steps: + - name: Check that changelog updated (rtic) + if: steps.changes.outputs.rtic == 'true' + uses: dangoslen/changelog-enforcer@v3 + with: + changeLogPath: ./rtic/CHANGELOG.md + skipLabels: 'needs-changelog, skip-changelog' + missingUpdateErrorMessage: 'Please add a changelog entry in the rtic/CHANGELOG.md file.' + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - name: Check that changelog updated (rtic) - uses: dangoslen/changelog-enforcer@v3 - with: - changeLogPath: ./rtic/CHANGELOG.md - skipLabels: 'needs-changelog, skip-changelog' - missingUpdateErrorMessage: 'Please add a changelog entry in the rtic/CHANGELOG.md file.' - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Check that changelog updated (rtic-timer) + if: steps.changes.outputs.rtic-timer == 'true' + uses: dangoslen/changelog-enforcer@v3 + with: + changeLogPath: ./rtic-timer/CHANGELOG.md + skipLabels: 'needs-changelog, skip-changelog' + missingUpdateErrorMessage: 'Please add a changelog entry in the rtic-timer/CHANGELOG.md file.' + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - if: steps.changes.outputs.rtic-timer == 'true' - steps: - - name: Check that changelog updated (rtic-timer) - uses: dangoslen/changelog-enforcer@v3 - with: - changeLogPath: ./rtic-timer/CHANGELOG.md - skipLabels: 'needs-changelog, skip-changelog' - missingUpdateErrorMessage: 'Please add a changelog entry in the rtic-timer/CHANGELOG.md file.' - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - if: steps.changes.outputs.rtic-monotonics == 'true' - steps: - - name: Check that changelog updated (rtic-monotonics) - uses: dangoslen/changelog-enforcer@v3 - with: - changeLogPath: ./rtic-monotonics/CHANGELOG.md - skipLabels: 'needs-changelog, skip-changelog' - missingUpdateErrorMessage: 'Please add a changelog entry in the rtic-monotonics/CHANGELOG.md file.' - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file + - name: Check that changelog updated (rtic-monotonics) + if: steps.changes.outputs.rtic-monotonics == 'true' + uses: dangoslen/changelog-enforcer@v3 + with: + changeLogPath: ./rtic-monotonics/CHANGELOG.md + skipLabels: 'needs-changelog, skip-changelog' + missingUpdateErrorMessage: 'Please add a changelog entry in the rtic-monotonics/CHANGELOG.md file.' + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file From 9d3c3a89aa22ba4287e1f989222a8a31b83f97fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Tj=C3=A4der?= Date: Fri, 27 Jan 2023 12:53:08 +0100 Subject: [PATCH 262/423] CI: Changelog: s/timer/time/ --- .github/workflows/changelog.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/changelog.yml b/.github/workflows/changelog.yml index eb715727b4..5a3df0a33e 100644 --- a/.github/workflows/changelog.yml +++ b/.github/workflows/changelog.yml @@ -25,8 +25,8 @@ jobs: filters: | rtic: - 'rtic/**' - rtic-timer: - - 'rtic-timer/**' + rtic-time: + - 'rtic-time/**' rtic-monotonics: - 'rtic-monotonics/**' @@ -40,13 +40,13 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - name: Check that changelog updated (rtic-timer) - if: steps.changes.outputs.rtic-timer == 'true' + - name: Check that changelog updated (rtic-time) + if: steps.changes.outputs.rtic-time == 'true' uses: dangoslen/changelog-enforcer@v3 with: - changeLogPath: ./rtic-timer/CHANGELOG.md + changeLogPath: ./rtic-time/CHANGELOG.md skipLabels: 'needs-changelog, skip-changelog' - missingUpdateErrorMessage: 'Please add a changelog entry in the rtic-timer/CHANGELOG.md file.' + missingUpdateErrorMessage: 'Please add a changelog entry in the rtic-time/CHANGELOG.md file.' env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From f62d0d17b2a1fb05635fc7468a80f9151b514d6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Tj=C3=A4der?= Date: Fri, 27 Jan 2023 12:55:26 +0100 Subject: [PATCH 263/423] CI: Clippy for time, monotonics, channel --- .github/workflows/build.yml | 69 +++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 1493c3f66a..6c51d894af 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -89,6 +89,72 @@ jobs: working-directory: ./rtic run: cargo clippy + clippytime: + name: Cargo clippy rtic-time + runs-on: ubuntu-22.04 + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Fail on warnings + working-directory: ./rtic-time + run: sed -i 's,//deny_warnings_placeholder_for_ci,#![deny(warnings)],' src/lib.rs + + - name: Add Rust component clippy + working-directory: ./rtic-time + run: rustup component add clippy + + - name: Cache Dependencies + uses: Swatinem/rust-cache@v2 + + - name: cargo clippy + working-directory: ./rtic-time + run: cargo clippy + + clippymonotonics: + name: Cargo clippy rtic-monotonics + runs-on: ubuntu-22.04 + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Fail on warnings + working-directory: ./rtic-monotonics + run: sed -i 's,//deny_warnings_placeholder_for_ci,#![deny(warnings)],' src/lib.rs + + - name: Add Rust component clippy + working-directory: ./rtic-monotonics + run: rustup component add clippy + + - name: Cache Dependencies + uses: Swatinem/rust-cache@v2 + + - name: cargo clippy + working-directory: ./rtic-monotonics + run: cargo clippy + + clippychannel: + name: Cargo clippy rtic-channel + runs-on: ubuntu-22.04 + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Fail on warnings + working-directory: ./rtic-channel + run: sed -i 's,//deny_warnings_placeholder_for_ci,#![deny(warnings)],' src/lib.rs + + - name: Add Rust component clippy + working-directory: ./rtic-channel + run: rustup component add clippy + + - name: Cache Dependencies + uses: Swatinem/rust-cache@v2 + + - name: cargo clippy + working-directory: ./rtic-channel + run: cargo clippy + # Verify all examples, checks checkexamples: name: checkexamples @@ -523,6 +589,9 @@ jobs: - style - check - clippy + - clippytime + - clippymonotonics + - clippychannel - checkexamples - testexamples - checkmacros From 3f60522d63ae18dbda5c6d5daf38d5e7ed96f858 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Tj=C3=A4der?= Date: Fri, 27 Jan 2023 13:00:16 +0100 Subject: [PATCH 264/423] waker registration somehow lost, back again --- rtic-channel/src/waker_registration.rs | 64 ++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 rtic-channel/src/waker_registration.rs diff --git a/rtic-channel/src/waker_registration.rs b/rtic-channel/src/waker_registration.rs new file mode 100644 index 0000000000..c30df7fe07 --- /dev/null +++ b/rtic-channel/src/waker_registration.rs @@ -0,0 +1,64 @@ +use core::cell::UnsafeCell; +use core::task::Waker; + +/// A critical section based waker handler. +pub struct CriticalSectionWakerRegistration { + waker: UnsafeCell>, +} + +unsafe impl Send for CriticalSectionWakerRegistration {} +unsafe impl Sync for CriticalSectionWakerRegistration {} + +impl CriticalSectionWakerRegistration { + /// Create a new waker registration. + pub const fn new() -> Self { + Self { + waker: UnsafeCell::new(None), + } + } + + /// Register a waker. + /// This will overwrite the previous waker if there was one. + pub fn register(&self, new_waker: &Waker) { + critical_section::with(|_| { + // SAFETY: This access is protected by the critical section. + let self_waker = unsafe { &mut *self.waker.get() }; + + // From embassy + // https://github.com/embassy-rs/embassy/blob/b99533607ceed225dd12ae73aaa9a0d969a7365e/embassy-sync/src/waitqueue/waker.rs#L59-L61 + match self_waker { + // Optimization: If both the old and new Wakers wake the same task, we can simply + // keep the old waker, skipping the clone. (In most executor implementations, + // cloning a waker is somewhat expensive, comparable to cloning an Arc). + Some(ref w2) if (w2.will_wake(new_waker)) => {} + _ => { + // clone the new waker and store it + if let Some(old_waker) = core::mem::replace(self_waker, Some(new_waker.clone())) + { + // We had a waker registered for another task. Wake it, so the other task can + // reregister itself if it's still interested. + // + // If two tasks are waiting on the same thing concurrently, this will cause them + // to wake each other in a loop fighting over this WakerRegistration. This wastes + // CPU but things will still work. + // + // If the user wants to have two tasks waiting on the same thing they should use + // a more appropriate primitive that can store multiple wakers. + old_waker.wake() + } + } + } + }); + } + + /// Wake the waker. + pub fn wake(&self) { + critical_section::with(|_| { + // SAFETY: This access is protected by the critical section. + let self_waker = unsafe { &mut *self.waker.get() }; + if let Some(waker) = self_waker.take() { + waker.wake() + } + }); + } +} From 1baa4a4228cae4576e194174618bf35f5c206959 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Tj=C3=A4der?= Date: Fri, 27 Jan 2023 13:18:29 +0100 Subject: [PATCH 265/423] CI: Don't let warnings get away --- rtic-channel/src/lib.rs | 1 + rtic-monotonics/src/lib.rs | 1 + rtic-time/src/lib.rs | 1 + 3 files changed, 3 insertions(+) diff --git a/rtic-channel/src/lib.rs b/rtic-channel/src/lib.rs index a7098ee245..166015fca5 100644 --- a/rtic-channel/src/lib.rs +++ b/rtic-channel/src/lib.rs @@ -2,6 +2,7 @@ #![no_std] #![deny(missing_docs)] +//deny_warnings_placeholder_for_ci use core::{ cell::UnsafeCell, diff --git a/rtic-monotonics/src/lib.rs b/rtic-monotonics/src/lib.rs index ce30c36b19..1e23088b48 100644 --- a/rtic-monotonics/src/lib.rs +++ b/rtic-monotonics/src/lib.rs @@ -3,6 +3,7 @@ #![no_std] #![no_main] #![deny(missing_docs)] +//deny_warnings_placeholder_for_ci #![allow(incomplete_features)] #![feature(async_fn_in_trait)] diff --git a/rtic-time/src/lib.rs b/rtic-time/src/lib.rs index 78ece1df20..eeecd86c2d 100644 --- a/rtic-time/src/lib.rs +++ b/rtic-time/src/lib.rs @@ -2,6 +2,7 @@ #![no_std] #![deny(missing_docs)] +//deny_warnings_placeholder_for_ci #![allow(incomplete_features)] #![feature(async_fn_in_trait)] From d23de6282365bbe83099e3d10877bdd435559016 Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Fri, 27 Jan 2023 19:33:25 +0100 Subject: [PATCH 266/423] Remove parsing on `capacity` --- rtic/macros/src/syntax/analyze.rs | 3 --- rtic/macros/src/syntax/parse.rs | 44 ------------------------------- 2 files changed, 47 deletions(-) diff --git a/rtic/macros/src/syntax/analyze.rs b/rtic/macros/src/syntax/analyze.rs index 3ed1487741..57f9f2cdaf 100644 --- a/rtic/macros/src/syntax/analyze.rs +++ b/rtic/macros/src/syntax/analyze.rs @@ -367,9 +367,6 @@ pub type SyncTypes = Set>; /// A channel used to send messages #[derive(Debug, Default)] pub struct Channel { - /// The channel capacity - pub capacity: u8, - /// Tasks that can be spawned on this channel pub tasks: BTreeSet, } diff --git a/rtic/macros/src/syntax/parse.rs b/rtic/macros/src/syntax/parse.rs index c78453a437..72eeeaf69c 100644 --- a/rtic/macros/src/syntax/parse.rs +++ b/rtic/macros/src/syntax/parse.rs @@ -191,7 +191,6 @@ fn task_args(tokens: TokenStream2) -> parse::Result parse::Result { - if capacity.is_some() { - return Err(parse::Error::new( - ident.span(), - "argument appears more than once", - )); - } - - if binds.is_some() { - return Err(parse::Error::new( - ident.span(), - "hardware tasks can't use the `capacity` argument", - )); - } - - // #lit - let lit: LitInt = content.parse()?; - - if !lit.suffix().is_empty() { - return Err(parse::Error::new( - lit.span(), - "this literal must be unsuffixed", - )); - } - - let value = lit.base10_parse::().ok(); - if value.is_none() || value == Some(0) { - return Err(parse::Error::new( - lit.span(), - "this literal must be in the range 1...255", - )); - } - - capacity = Some(value.unwrap()); - } - "priority" => { if priority.is_some() { return Err(parse::Error::new( From 922f1ad0eb56d362e527ff28e456b6fa133e212b Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Fri, 27 Jan 2023 20:20:14 +0100 Subject: [PATCH 267/423] Added examples for async crates + fixed codegen for non-Copy arguments --- rtic-channel/.gitignore | 2 + rtic-channel/src/lib.rs | 36 +++++++++-- rtic-channel/src/wait_queue.rs | 22 ------- rtic-monotonics/Cargo.toml | 1 + rtic-monotonics/src/systick_monotonic.rs | 8 +-- rtic/Cargo.toml | 3 + rtic/ci/expected/async-channel.run | 6 ++ rtic/ci/expected/message_passing.run | 2 - rtic/examples/async-channel.rs | 61 +++++++++++++++++++ .../{async-delay.no_rs => async-delay.rs} | 30 ++++----- ...ssage_passing.no_rs => message_passing.rs} | 17 +++--- rtic/macros/src/codegen/module.rs | 17 +++--- rtic/src/export/executor.rs | 24 ++++---- 13 files changed, 152 insertions(+), 77 deletions(-) create mode 100644 rtic-channel/.gitignore create mode 100644 rtic/ci/expected/async-channel.run create mode 100644 rtic/examples/async-channel.rs rename rtic/examples/{async-delay.no_rs => async-delay.rs} (62%) rename rtic/examples/{message_passing.no_rs => message_passing.rs} (52%) diff --git a/rtic-channel/.gitignore b/rtic-channel/.gitignore new file mode 100644 index 0000000000..1e7caa9ea8 --- /dev/null +++ b/rtic-channel/.gitignore @@ -0,0 +1,2 @@ +Cargo.lock +target/ diff --git a/rtic-channel/src/lib.rs b/rtic-channel/src/lib.rs index 166015fca5..5ee2c710a9 100644 --- a/rtic-channel/src/lib.rs +++ b/rtic-channel/src/lib.rs @@ -67,7 +67,7 @@ impl Channel { /// Split the queue into a `Sender`/`Receiver` pair. pub fn split<'a>(&'a mut self) -> (Sender<'a, T, N>, Receiver<'a, T, N>) { // Fill free queue - for idx in 0..(N - 1) as u8 { + for idx in 0..N as u8 { debug_assert!(!self.freeq.get_mut().is_full()); // SAFETY: This safe as the loop goes from 0 to the capacity of the underlying queue. @@ -114,11 +114,26 @@ macro_rules! make_channel { /// Error state for when the receiver has been dropped. pub struct NoReceiver(pub T); +impl core::fmt::Debug for NoReceiver +where + T: core::fmt::Debug, +{ + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "NoReceiver({:?})", self.0) + } +} + /// A `Sender` can send to the channel and can be cloned. pub struct Sender<'a, T, const N: usize>(&'a Channel); unsafe impl<'a, T, const N: usize> Send for Sender<'a, T, N> {} +impl<'a, T, const N: usize> core::fmt::Debug for Sender<'a, T, N> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "Sender") + } +} + impl<'a, T, const N: usize> Sender<'a, T, N> { #[inline(always)] fn send_footer(&mut self, idx: u8, val: T) { @@ -204,7 +219,7 @@ impl<'a, T, const N: usize> Sender<'a, T, N> { // Return the index Poll::Ready(Ok(idx)) } else { - return Poll::Pending; + Poll::Pending } }) .await; @@ -267,16 +282,29 @@ impl<'a, T, const N: usize> Clone for Sender<'a, T, N> { /// A receiver of the channel. There can only be one receiver at any time. pub struct Receiver<'a, T, const N: usize>(&'a Channel); +unsafe impl<'a, T, const N: usize> Send for Receiver<'a, T, N> {} + +impl<'a, T, const N: usize> core::fmt::Debug for Receiver<'a, T, N> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "Receiver") + } +} + /// Error state for when all senders has been dropped. pub struct NoSender; +impl core::fmt::Debug for NoSender { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "NoSender") + } +} + impl<'a, T, const N: usize> Receiver<'a, T, N> { /// Receives a value if there is one in the channel, non-blocking. /// Note; this does not check if the channel is closed. pub fn try_recv(&mut self) -> Option { // Try to get a ready slot. - let ready_slot = - critical_section::with(|cs| self.0.access(cs).readyq.pop_front().map(|rs| rs)); + let ready_slot = critical_section::with(|cs| self.0.access(cs).readyq.pop_front()); if let Some(rs) = ready_slot { // Read the value from the slots, note; this memcpy is not under a critical section. diff --git a/rtic-channel/src/wait_queue.rs b/rtic-channel/src/wait_queue.rs index 90d762bdf3..5b59983261 100644 --- a/rtic-channel/src/wait_queue.rs +++ b/rtic-channel/src/wait_queue.rs @@ -1,6 +1,5 @@ //! ... -use core::cell::UnsafeCell; use core::marker::PhantomPinned; use core::ptr::null_mut; use core::sync::atomic::{AtomicPtr, Ordering}; @@ -9,27 +8,6 @@ use critical_section as cs; pub type WaitQueue = LinkedList; -struct MyLinkPtr(UnsafeCell<*mut Link>); - -impl MyLinkPtr { - #[inline(always)] - fn new(val: *mut Link) -> Self { - Self(UnsafeCell::new(val)) - } - - /// SAFETY: Only use this in a critical section, and don't forget them barriers. - #[inline(always)] - unsafe fn load_relaxed(&self) -> *mut Link { - unsafe { *self.0.get() } - } - - /// SAFETY: Only use this in a critical section, and don't forget them barriers. - #[inline(always)] - unsafe fn store_relaxed(&self, val: *mut Link) { - unsafe { self.0.get().write(val) } - } -} - /// A FIFO linked list for a wait queue. pub struct LinkedList { head: AtomicPtr>, // UnsafeCell<*mut Link> diff --git a/rtic-monotonics/Cargo.toml b/rtic-monotonics/Cargo.toml index 68daba4041..9d364c80be 100644 --- a/rtic-monotonics/Cargo.toml +++ b/rtic-monotonics/Cargo.toml @@ -10,3 +10,4 @@ cortex-m = { version = "0.7.6" } embedded-hal-async = "0.2.0-alpha.0" fugit = { version = "0.3.6", features = ["defmt"] } rtic-time = { version = "1.0.0", path = "../rtic-time" } +atomic-polyfill = "1" diff --git a/rtic-monotonics/src/systick_monotonic.rs b/rtic-monotonics/src/systick_monotonic.rs index af17f937fe..fec97f2e86 100644 --- a/rtic-monotonics/src/systick_monotonic.rs +++ b/rtic-monotonics/src/systick_monotonic.rs @@ -2,13 +2,11 @@ use super::Monotonic; pub use super::{TimeoutError, TimerQueue}; -use core::{ - ops::Deref, - sync::atomic::{AtomicU32, Ordering}, -}; +use atomic_polyfill::{AtomicU32, Ordering}; +use core::ops::Deref; use cortex_m::peripheral::SYST; use embedded_hal_async::delay::DelayUs; -use fugit::ExtU32; +pub use fugit::ExtU32; const TIMER_HZ: u32 = 1_000; diff --git a/rtic/Cargo.toml b/rtic/Cargo.toml index 6eb691df6e..12a34da1b8 100644 --- a/rtic/Cargo.toml +++ b/rtic/Cargo.toml @@ -38,6 +38,9 @@ version_check = "0.9" lm3s6965 = "0.1.3" cortex-m-semihosting = "0.5.0" systick-monotonic = "1.0.0" +rtic-time = { path = "../rtic-time" } +rtic-channel = { path = "../rtic-channel" } +rtic-monotonics = { path = "../rtic-monotonics" } [dev-dependencies.panic-semihosting] features = ["exit"] diff --git a/rtic/ci/expected/async-channel.run b/rtic/ci/expected/async-channel.run new file mode 100644 index 0000000000..3e3c232427 --- /dev/null +++ b/rtic/ci/expected/async-channel.run @@ -0,0 +1,6 @@ +Sender 1 sending: 1 +Sender 2 sending: 2 +Sender 3 sending: 3 +Receiver got: 1 +Receiver got: 2 +Receiver got: 5 diff --git a/rtic/ci/expected/message_passing.run b/rtic/ci/expected/message_passing.run index a1448d8da5..9085e13db0 100644 --- a/rtic/ci/expected/message_passing.run +++ b/rtic/ci/expected/message_passing.run @@ -1,3 +1 @@ foo 1, 1 -foo 1, 2 -foo 2, 3 diff --git a/rtic/examples/async-channel.rs b/rtic/examples/async-channel.rs new file mode 100644 index 0000000000..3a2e491181 --- /dev/null +++ b/rtic/examples/async-channel.rs @@ -0,0 +1,61 @@ +//! examples/async-channel.rs + +#![deny(unsafe_code)] +#![deny(warnings)] +#![no_main] +#![no_std] +#![feature(type_alias_impl_trait)] + +use panic_semihosting as _; + +#[rtic::app(device = lm3s6965, dispatchers = [SSI0])] +mod app { + use cortex_m_semihosting::{debug, hprintln}; + use rtic_channel::*; + + #[shared] + struct Shared {} + + #[local] + struct Local {} + + #[init] + fn init(_: init::Context) -> (Shared, Local) { + let (s, r) = make_channel!(u32, 5); + + receiver::spawn(r).unwrap(); + sender1::spawn(s.clone()).unwrap(); + sender2::spawn(s.clone()).unwrap(); + sender3::spawn(s).unwrap(); + + (Shared {}, Local {}) + } + + #[task] + async fn receiver(_c: receiver::Context, mut receiver: Receiver<'static, u32, 5>) { + while let Ok(val) = receiver.recv().await { + hprintln!("Receiver got: {}", val); + if val == 5 { + debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator + } + } + } + + #[task] + async fn sender1(_c: sender1::Context, mut sender: Sender<'static, u32, 5>) { + hprintln!("Sender 1 sending: 1"); + sender.send(1).await.unwrap(); + } + + #[task] + async fn sender2(_c: sender2::Context, mut sender: Sender<'static, u32, 5>) { + hprintln!("Sender 2 sending: 2"); + sender.send(2).await.unwrap(); + } + + #[task] + async fn sender3(_c: sender3::Context, mut sender: Sender<'static, u32, 5>) { + hprintln!("Sender 3 sending: 3"); + sender.send(5).await.unwrap(); + } +} diff --git a/rtic/examples/async-delay.no_rs b/rtic/examples/async-delay.rs similarity index 62% rename from rtic/examples/async-delay.no_rs rename to rtic/examples/async-delay.rs index fb478c3fbd..0440f774ad 100644 --- a/rtic/examples/async-delay.no_rs +++ b/rtic/examples/async-delay.rs @@ -7,7 +7,9 @@ use panic_semihosting as _; #[rtic::app(device = lm3s6965, dispatchers = [SSI0, UART0], peripherals = true)] mod app { use cortex_m_semihosting::{debug, hprintln}; - use systick_monotonic::*; + use rtic_monotonics::systick_monotonic::*; + + rtic_monotonics::make_systick_timer_queue!(TIMER); #[shared] struct Shared {} @@ -15,12 +17,12 @@ mod app { #[local] struct Local {} - #[monotonic(binds = SysTick, default = true)] - type MyMono = Systick<100>; - #[init] fn init(cx: init::Context) -> (Shared, Local) { - hprintln!("init").unwrap(); + hprintln!("init"); + + let systick = Systick::start(cx.core.SYST, 12_000_000); + TIMER.initialize(systick); foo::spawn().ok(); bar::spawn().ok(); @@ -40,23 +42,23 @@ mod app { #[task] async fn foo(_cx: foo::Context) { - hprintln!("hello from foo").ok(); - monotonics::delay(100.millis()).await; - hprintln!("bye from foo").ok(); + hprintln!("hello from foo"); + TIMER.delay(100.millis()).await; + hprintln!("bye from foo"); } #[task] async fn bar(_cx: bar::Context) { - hprintln!("hello from bar").ok(); - monotonics::delay(200.millis()).await; - hprintln!("bye from bar").ok(); + hprintln!("hello from bar"); + TIMER.delay(200.millis()).await; + hprintln!("bye from bar"); } #[task] async fn baz(_cx: baz::Context) { - hprintln!("hello from baz").ok(); - monotonics::delay(300.millis()).await; - hprintln!("bye from baz").ok(); + hprintln!("hello from baz"); + TIMER.delay(300.millis()).await; + hprintln!("bye from baz"); debug::exit(debug::EXIT_SUCCESS); } diff --git a/rtic/examples/message_passing.no_rs b/rtic/examples/message_passing.rs similarity index 52% rename from rtic/examples/message_passing.no_rs rename to rtic/examples/message_passing.rs index ffa9537127..01f1a65499 100644 --- a/rtic/examples/message_passing.no_rs +++ b/rtic/examples/message_passing.rs @@ -4,6 +4,7 @@ #![deny(warnings)] #![no_main] #![no_std] +#![feature(type_alias_impl_trait)] use panic_semihosting as _; @@ -18,20 +19,16 @@ mod app { struct Local {} #[init] - fn init(_: init::Context) -> (Shared, Local, init::Monotonics) { + fn init(_: init::Context) -> (Shared, Local) { foo::spawn(1, 1).unwrap(); - foo::spawn(1, 2).unwrap(); - foo::spawn(2, 3).unwrap(); assert!(foo::spawn(1, 4).is_err()); // The capacity of `foo` is reached - (Shared {}, Local {}, init::Monotonics()) + (Shared {}, Local {}) } - #[task(capacity = 3)] - fn foo(_c: foo::Context, x: i32, y: u32) { - hprintln!("foo {}, {}", x, y).unwrap(); - if x == 2 { - debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator - } + #[task] + async fn foo(_c: foo::Context, x: i32, y: u32) { + hprintln!("foo {}, {}", x, y); + debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator } } diff --git a/rtic/macros/src/codegen/module.rs b/rtic/macros/src/codegen/module.rs index 4725b9a993..8b3fca2319 100644 --- a/rtic/macros/src/codegen/module.rs +++ b/rtic/macros/src/codegen/module.rs @@ -157,14 +157,17 @@ pub fn codegen(ctxt: Context, app: &App, analysis: &Analysis) -> TokenStream2 { #[allow(non_snake_case)] #[doc(hidden)] pub fn #internal_spawn_ident(#(#input_args,)*) -> Result<(), #input_ty> { + // SAFETY: If `try_allocate` suceeds one must call `spawn`, which we do. + unsafe { + if #exec_name.try_allocate() { + let f = #name(unsafe { #name::Context::new() } #(,#input_untupled)*); + #exec_name.spawn(f); + #pend_interrupt - if #exec_name.spawn(|| #name(unsafe { #name::Context::new() } #(,#input_untupled)*) ) { - - #pend_interrupt - - Ok(()) - } else { - Err(#input_tupled) + Ok(()) + } else { + Err(#input_tupled) + } } } diff --git a/rtic/src/export/executor.rs b/rtic/src/export/executor.rs index e64cc43ec1..36e47d7e43 100644 --- a/rtic/src/export/executor.rs +++ b/rtic/src/export/executor.rs @@ -70,25 +70,23 @@ impl AsyncTaskExecutor { self.pending.store(true, Ordering::Release); } - /// Spawn a future + /// Allocate the executor. To use with `spawn`. #[inline(always)] - pub fn spawn(&self, future: impl Fn() -> F) -> bool { + pub unsafe fn try_allocate(&self) -> bool { // Try to reserve the executor for a future. - if self - .running + self.running .compare_exchange(false, true, Ordering::AcqRel, Ordering::Relaxed) .is_ok() - { - // This unsafe is protected by `running` being false and the atomic setting it to true. - unsafe { - self.task.get().write(MaybeUninit::new(future())); - } - self.set_pending(); + } - true - } else { - false + /// Spawn a future + #[inline(always)] + pub unsafe fn spawn(&self, future: F) { + // This unsafe is protected by `running` being false and the atomic setting it to true. + unsafe { + self.task.get().write(MaybeUninit::new(future)); } + self.set_pending(); } /// Poll the future in the executor. From 9c6e2c1c99448304fb54354297c284c1a8810cd6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Tj=C3=A4der?= Date: Sat, 28 Jan 2023 11:15:59 +0100 Subject: [PATCH 268/423] Add changelog templates --- rtic-channel/CHANGELOG.md | 16 ++++++++++++++++ rtic-monotonics/CHANGELOG.md | 16 ++++++++++++++++ rtic-time/CHANGELOG.md | 16 ++++++++++++++++ 3 files changed, 48 insertions(+) create mode 100644 rtic-channel/CHANGELOG.md diff --git a/rtic-channel/CHANGELOG.md b/rtic-channel/CHANGELOG.md new file mode 100644 index 0000000000..d3a9d846ee --- /dev/null +++ b/rtic-channel/CHANGELOG.md @@ -0,0 +1,16 @@ +# Change Log + +All notable changes to this project will be documented in this file. +This project adheres to [Semantic Versioning](http://semver.org/). + +For each category, *Added*, *Changed*, *Fixed* add new entries at the top! + +## [Unreleased] + +### Added + +### Changed + +### Fixed + +## [v1.0.0] - 2023-xx-xx diff --git a/rtic-monotonics/CHANGELOG.md b/rtic-monotonics/CHANGELOG.md index e69de29bb2..e99022372b 100644 --- a/rtic-monotonics/CHANGELOG.md +++ b/rtic-monotonics/CHANGELOG.md @@ -0,0 +1,16 @@ +# Change Log + +All notable changes to this project will be documented in this file. +This project adheres to [Semantic Versioning](http://semver.org/). + +For each category, *Added*, *Changed*, *Fixed* add new entries at the top! + +## [Unreleased] + +### Added + +### Changed + +### Fixed + +## [v0.1.0] - 2023-xx-xx diff --git a/rtic-time/CHANGELOG.md b/rtic-time/CHANGELOG.md index e69de29bb2..d3a9d846ee 100644 --- a/rtic-time/CHANGELOG.md +++ b/rtic-time/CHANGELOG.md @@ -0,0 +1,16 @@ +# Change Log + +All notable changes to this project will be documented in this file. +This project adheres to [Semantic Versioning](http://semver.org/). + +For each category, *Added*, *Changed*, *Fixed* add new entries at the top! + +## [Unreleased] + +### Added + +### Changed + +### Fixed + +## [v1.0.0] - 2023-xx-xx From ff12a02d020965956ae8f9bda75ef243b3d1dd3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Tj=C3=A4der?= Date: Sat, 28 Jan 2023 11:28:59 +0100 Subject: [PATCH 269/423] CI: Add rtic-channel to Changelog, remove defunct changelog --- .github/workflows/changelog.yml | 12 ++++++++ .../syntax/.github/workflows/changelog.yml | 28 ------------------- 2 files changed, 12 insertions(+), 28 deletions(-) delete mode 100644 rtic/macros/src/syntax/.github/workflows/changelog.yml diff --git a/.github/workflows/changelog.yml b/.github/workflows/changelog.yml index 5a3df0a33e..0eb4cf6b53 100644 --- a/.github/workflows/changelog.yml +++ b/.github/workflows/changelog.yml @@ -25,6 +25,8 @@ jobs: filters: | rtic: - 'rtic/**' + rtic-channel: + - 'rtic-channel/**' rtic-time: - 'rtic-time/**' rtic-monotonics: @@ -40,6 +42,16 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Check that changelog updated (rtic-channel) + if: steps.changes.outputs.rtic-channel == 'true' + uses: dangoslen/changelog-enforcer@v3 + with: + changeLogPath: ./rtic-channel/CHANGELOG.md + skipLabels: 'needs-changelog, skip-changelog' + missingUpdateErrorMessage: 'Please add a changelog entry in the rtic-channel/CHANGELOG.md file.' + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Check that changelog updated (rtic-time) if: steps.changes.outputs.rtic-time == 'true' uses: dangoslen/changelog-enforcer@v3 diff --git a/rtic/macros/src/syntax/.github/workflows/changelog.yml b/rtic/macros/src/syntax/.github/workflows/changelog.yml deleted file mode 100644 index ccf6eb9103..0000000000 --- a/rtic/macros/src/syntax/.github/workflows/changelog.yml +++ /dev/null @@ -1,28 +0,0 @@ -# Check that the changelog is updated for all changes. -# -# This is only run for PRs. - -on: - pull_request: - # opened, reopened, synchronize are the default types for pull_request. - # labeled, unlabeled ensure this check is also run if a label is added or removed. - types: [opened, reopened, labeled, unlabeled, synchronize] - -name: Changelog - -jobs: - changelog: - name: Changelog - runs-on: ubuntu-latest - steps: - - name: Checkout sources - uses: actions/checkout@v2 - - - name: Check that changelog updated - uses: dangoslen/changelog-enforcer@v3 - with: - changeLogPath: CHANGELOG.md - skipLabels: 'needs-changelog, skip-changelog' - missingUpdateErrorMessage: 'Please add a changelog entry in the CHANGELOG.md file.' - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file From 07c11b071d048ef9f31e76584484719587e3ed71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Tj=C3=A4der?= Date: Sat, 28 Jan 2023 11:42:24 +0100 Subject: [PATCH 270/423] CI: Cargo fmt for channel, mono., time --- .github/workflows/build.yml | 48 ++++++++++++++++++++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 6c51d894af..a709fb9c49 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -15,7 +15,7 @@ env: jobs: # Run cargo fmt --check, includes macros/ style: - name: style + name: cargo fmt runs-on: ubuntu-22.04 steps: - name: Checkout @@ -29,6 +29,52 @@ jobs: working-directory: ./rtic run: cargo fmt --all -- --check + stylechannel: + name: cargo fmt rtic-channel + runs-on: ubuntu-22.04 + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Fail on warnings + working-directory: ./rtic-channel + run: sed -i 's,//deny_warnings_placeholder_for_ci,#![deny(warnings)],' src/lib.rs + + - name: cargo fmt --check + working-directory: ./rtic-channel + run: cargo fmt --all -- --check + + stylemonotonics: + name: cargo fmt rtic-monotonics + runs-on: ubuntu-22.04 + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Fail on warnings + working-directory: ./rtic-monotonics + run: sed -i 's,//deny_warnings_placeholder_for_ci,#![deny(warnings)],' src/lib.rs + + - name: cargo fmt --check + working-directory: ./rtic-monotonics + run: cargo fmt --all -- --check + + styletime: + name: cargo fmt rtic-time + runs-on: ubuntu-22.04 + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Fail on warnings + working-directory: ./rtic-time + run: sed -i 's,//deny_warnings_placeholder_for_ci,#![deny(warnings)],' src/lib.rs + + - name: cargo fmt --check + working-directory: ./rtic-time + run: cargo fmt --all -- --check + + # Compilation check check: name: check From 8cb05049bea710dce441a7221ed3a541e5f4f7b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Tj=C3=A4der?= Date: Sat, 28 Jan 2023 11:42:56 +0100 Subject: [PATCH 271/423] CI: Alphabetical sort of clippy jobs --- .github/workflows/build.yml | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a709fb9c49..9f7d221975 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -135,28 +135,29 @@ jobs: working-directory: ./rtic run: cargo clippy - clippytime: - name: Cargo clippy rtic-time + clippychannel: + name: Cargo clippy rtic-channel runs-on: ubuntu-22.04 steps: - name: Checkout uses: actions/checkout@v3 - name: Fail on warnings - working-directory: ./rtic-time + working-directory: ./rtic-channel run: sed -i 's,//deny_warnings_placeholder_for_ci,#![deny(warnings)],' src/lib.rs - name: Add Rust component clippy - working-directory: ./rtic-time + working-directory: ./rtic-channel run: rustup component add clippy - name: Cache Dependencies uses: Swatinem/rust-cache@v2 - name: cargo clippy - working-directory: ./rtic-time + working-directory: ./rtic-channel run: cargo clippy + clippymonotonics: name: Cargo clippy rtic-monotonics runs-on: ubuntu-22.04 @@ -178,27 +179,27 @@ jobs: - name: cargo clippy working-directory: ./rtic-monotonics run: cargo clippy - - clippychannel: - name: Cargo clippy rtic-channel + + clippytime: + name: Cargo clippy rtic-time runs-on: ubuntu-22.04 steps: - name: Checkout uses: actions/checkout@v3 - name: Fail on warnings - working-directory: ./rtic-channel + working-directory: ./rtic-time run: sed -i 's,//deny_warnings_placeholder_for_ci,#![deny(warnings)],' src/lib.rs - name: Add Rust component clippy - working-directory: ./rtic-channel + working-directory: ./rtic-time run: rustup component add clippy - name: Cache Dependencies uses: Swatinem/rust-cache@v2 - name: cargo clippy - working-directory: ./rtic-channel + working-directory: ./rtic-time run: cargo clippy # Verify all examples, checks From 5908d5bdbc3a3daf741d58b925fa280a3a81bf6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Tj=C3=A4der?= Date: Sat, 28 Jan 2023 12:07:05 +0100 Subject: [PATCH 272/423] CI: Cleanup old syntax CI --- rtic/macros/src/syntax/.github/bors.toml | 3 - .../src/syntax/.github/workflows/build.yml | 213 ------------------ .../properties/build.properties.json | 6 - rtic/macros/src/syntax/.gitignore | 4 - 4 files changed, 226 deletions(-) delete mode 100644 rtic/macros/src/syntax/.github/bors.toml delete mode 100644 rtic/macros/src/syntax/.github/workflows/build.yml delete mode 100644 rtic/macros/src/syntax/.github/workflows/properties/build.properties.json delete mode 100644 rtic/macros/src/syntax/.gitignore diff --git a/rtic/macros/src/syntax/.github/bors.toml b/rtic/macros/src/syntax/.github/bors.toml deleted file mode 100644 index aee6042f81..0000000000 --- a/rtic/macros/src/syntax/.github/bors.toml +++ /dev/null @@ -1,3 +0,0 @@ -block_labels = ["S-blocked"] -delete_merged_branches = true -status = ["ci"] diff --git a/rtic/macros/src/syntax/.github/workflows/build.yml b/rtic/macros/src/syntax/.github/workflows/build.yml deleted file mode 100644 index 29971b1021..0000000000 --- a/rtic/macros/src/syntax/.github/workflows/build.yml +++ /dev/null @@ -1,213 +0,0 @@ -name: Build -on: - pull_request: - push: - branches: - - master - - staging - - trying - - bors/staging - - bors/trying - -env: - CARGO_TERM_COLOR: always - -jobs: - # Run cargo fmt --check - style: - name: style - runs-on: ubuntu-20.04 - steps: - - name: Checkout - uses: actions/checkout@v2 - - - name: Install Rust - uses: actions-rs/toolchain@v1 - with: - profile: minimal - toolchain: stable - override: true - components: rustfmt - - - name: Fail on warnings - run: sed -i 's,//deny_warnings_placeholder_for_ci,#![deny(warnings)],' src/lib.rs - - - name: cargo fmt --check - uses: actions-rs/cargo@v1 - with: - command: fmt - args: --all -- --check - - # Compilation check - check: - name: check - runs-on: ubuntu-20.04 - strategy: - matrix: - target: - - x86_64-unknown-linux-gnu - toolchain: - - stable - steps: - - name: Checkout - uses: actions/checkout@v2 - - - name: Install Rust ${{ matrix.toolchain }} with target (${{ matrix.target }}) - uses: actions-rs/toolchain@v1 - with: - toolchain: ${{ matrix.toolchain }} - target: ${{ matrix.target }} - override: true - - - name: Fail on warnings - run: sed -i 's,//deny_warnings_placeholder_for_ci,#![deny(warnings)],' src/lib.rs - - - name: Cache Dependencies - uses: Swatinem/rust-cache@v1 - - - name: cargo check - uses: actions-rs/cargo@v1 - with: - use-cross: false - command: check - args: --target=${{ matrix.target }} - - # Clippy - clippy: - name: Cargo clippy - runs-on: ubuntu-20.04 - steps: - - name: Checkout - uses: actions/checkout@v2 - - - name: Install Rust stable - uses: actions-rs/toolchain@v1 - with: - toolchain: stable - target: x86_64-unknown-linux-gnu - override: true - - - name: Fail on warnings - run: sed -i 's,//deny_warnings_placeholder_for_ci,#![deny(warnings)],' src/lib.rs - - - name: Cache Dependencies - uses: Swatinem/rust-cache@v1 - - - name: cargo clippy - uses: actions-rs/cargo@v1 - with: - use-cross: false - command: clippy - - # Verify all examples - testexamples: - name: testexamples - runs-on: ubuntu-20.04 - strategy: - matrix: - target: - - x86_64-unknown-linux-gnu - toolchain: - - stable - steps: - - name: Checkout - uses: actions/checkout@v2 - - - name: Install Rust ${{ matrix.toolchain }} with target (${{ matrix.target }}) - uses: actions-rs/toolchain@v1 - with: - toolchain: ${{ matrix.toolchain }} - target: ${{ matrix.target }} - override: true - - - name: Cache Dependencies - uses: Swatinem/rust-cache@v1 - - - name: Fail on warnings - run: sed -i 's,//deny_warnings_placeholder_for_ci,#![deny(warnings)],' src/lib.rs - - - uses: actions-rs/cargo@v1 - with: - use-cross: false - command: test - args: --examples - - # Run test suite for UI - testui: - name: testui - runs-on: ubuntu-20.04 - strategy: - matrix: - target: - - x86_64-unknown-linux-gnu - toolchain: - - stable - steps: - - name: Checkout - uses: actions/checkout@v2 - - - name: Install Rust ${{ matrix.toolchain }} with target (${{ matrix.target }}) - uses: actions-rs/toolchain@v1 - with: - toolchain: ${{ matrix.toolchain }} - target: ${{ matrix.target }} - override: true - - - name: Cache Dependencies - uses: Swatinem/rust-cache@v1 - - - name: Fail on warnings - run: sed -i 's,//deny_warnings_placeholder_for_ci,#![deny(warnings)],' src/lib.rs - - - - uses: actions-rs/cargo@v1 - with: - use-cross: false - command: test - args: --test ui - - # Run test suite - test: - name: test - runs-on: ubuntu-20.04 - steps: - - name: Checkout - uses: actions/checkout@v2 - - - name: Install Rust - uses: actions-rs/toolchain@v1 - with: - toolchain: stable - target: thumbv7m-none-eabi - override: true - - - name: Cache Dependencies - uses: Swatinem/rust-cache@v1 - - - name: Fail on warnings - run: sed -i 's,//deny_warnings_placeholder_for_ci,#![deny(warnings)],' src/lib.rs - - - uses: actions-rs/cargo@v1 - with: - use-cross: false - command: test - args: --lib - - # Refs: https://github.com/rust-lang/crater/blob/9ab6f9697c901c4a44025cf0a39b73ad5b37d198/.github/workflows/bors.yml#L125-L149 - # - # ALL THE PREVIOUS JOBS NEEDS TO BE ADDED TO THE `needs` SECTION OF THIS JOB! - - ci-success: - name: ci - if: github.event_name == 'push' && success() - needs: - - style - - check - - clippy - - testexamples - - test - - testui - runs-on: ubuntu-20.04 - steps: - - name: Mark the job as a success - run: exit 0 diff --git a/rtic/macros/src/syntax/.github/workflows/properties/build.properties.json b/rtic/macros/src/syntax/.github/workflows/properties/build.properties.json deleted file mode 100644 index fd3eed37a1..0000000000 --- a/rtic/macros/src/syntax/.github/workflows/properties/build.properties.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "name": "Build", - "description": "RTIC Test Suite", - "iconName": "rust", - "categories": ["Rust"] -} diff --git a/rtic/macros/src/syntax/.gitignore b/rtic/macros/src/syntax/.gitignore deleted file mode 100644 index f8d7c8b493..0000000000 --- a/rtic/macros/src/syntax/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -**/*.rs.bk -.#* -/target/ -Cargo.lock From 3050fc0591f087a4fbe08840c69633e89d3f58a7 Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Sat, 28 Jan 2023 13:21:44 +0100 Subject: [PATCH 273/423] Use `Pin` in the linked lists --- rtic-channel/src/lib.rs | 18 +++++++++++++----- rtic-channel/src/wait_queue.rs | 16 ++++++++++------ rtic-time/src/lib.rs | 18 +++++++++++++++--- rtic-time/src/linked_list.rs | 5 ++++- 4 files changed, 42 insertions(+), 15 deletions(-) diff --git a/rtic-channel/src/lib.rs b/rtic-channel/src/lib.rs index 5ee2c710a9..20086ac7fb 100644 --- a/rtic-channel/src/lib.rs +++ b/rtic-channel/src/lib.rs @@ -8,6 +8,7 @@ use core::{ cell::UnsafeCell, future::poll_fn, mem::MaybeUninit, + pin::Pin, ptr, task::{Poll, Waker}, }; @@ -177,10 +178,11 @@ impl<'a, T, const N: usize> Sender<'a, T, N> { pub async fn send(&mut self, val: T) -> Result<(), NoReceiver> { if self.is_closed() {} - let mut __hidden_link: Option> = None; + let mut link_ptr: Option> = None; + + // Make this future `Drop`-safe, also shadow the original definition so we can't abuse it. + let link_ptr = &mut link_ptr as *mut Option>; - // Make this future `Drop`-safe - let link_ptr = &mut __hidden_link as *mut Option>; let dropper = OnDrop::new(|| { // SAFETY: We only run this closure and dereference the pointer if we have // exited the `poll_fn` below in the `drop(dropper)` call. The other dereference @@ -198,12 +200,18 @@ impl<'a, T, const N: usize> Sender<'a, T, N> { // Do all this in one critical section, else there can be race conditions let queue_idx = critical_section::with(|cs| { if !self.0.wait_queue.is_empty() || self.0.access(cs).freeq.is_empty() { - // SAFETY: This pointer is only dereferenced here and on drop of the future. + // SAFETY: This pointer is only dereferenced here and on drop of the future + // which happens outside this `poll_fn`'s stack frame. let link = unsafe { &mut *link_ptr }; if link.is_none() { // Place the link in the wait queue on first run. let link_ref = link.insert(wait_queue::Link::new(cx.waker().clone())); - self.0.wait_queue.push(link_ref); + + // SAFETY: The address to the link is stable as it is hidden behind + // `link_ptr`, and `link_ptr` shadows the original making it unmovable. + self.0 + .wait_queue + .push(unsafe { Pin::new_unchecked(link_ref) }); } return None; diff --git a/rtic-channel/src/wait_queue.rs b/rtic-channel/src/wait_queue.rs index 5b59983261..ba05e6bb75 100644 --- a/rtic-channel/src/wait_queue.rs +++ b/rtic-channel/src/wait_queue.rs @@ -1,6 +1,7 @@ //! ... use core::marker::PhantomPinned; +use core::pin::Pin; use core::ptr::null_mut; use core::sync::atomic::{AtomicPtr, Ordering}; use core::task::Waker; @@ -65,13 +66,16 @@ impl LinkedList { } /// Put an element at the back of the queue. - pub fn push(&self, link: &mut Link) { + pub fn push(&self, link: Pin<&mut Link>) { cs::with(|_| { // Make sure all previous writes are visible core::sync::atomic::fence(Ordering::SeqCst); let tail = self.tail.load(Self::R); + // SAFETY: This datastructure does not move the underlying value. + let link = unsafe { link.get_unchecked_mut() }; + if let Some(tail_ref) = unsafe { tail.as_ref() } { // Queue is not empty link.prev.store(tail, Self::R); @@ -221,11 +225,11 @@ mod tests { let mut i4 = Link::new(13); let mut i5 = Link::new(14); - wq.push(&mut i1); - wq.push(&mut i2); - wq.push(&mut i3); - wq.push(&mut i4); - wq.push(&mut i5); + wq.push(unsafe { Pin::new_unchecked(&mut i1) }); + wq.push(unsafe { Pin::new_unchecked(&mut i2) }); + wq.push(unsafe { Pin::new_unchecked(&mut i3) }); + wq.push(unsafe { Pin::new_unchecked(&mut i4) }); + wq.push(unsafe { Pin::new_unchecked(&mut i5) }); wq.print(); diff --git a/rtic-time/src/lib.rs b/rtic-time/src/lib.rs index eeecd86c2d..6b23f7653d 100644 --- a/rtic-time/src/lib.rs +++ b/rtic-time/src/lib.rs @@ -7,6 +7,7 @@ #![feature(async_fn_in_trait)] use core::future::{poll_fn, Future}; +use core::pin::Pin; use core::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; use core::task::{Poll, Waker}; use futures_util::{ @@ -185,7 +186,10 @@ impl TimerQueue { ); } - let mut link = None; + let mut link_ptr: Option>> = None; + + // Make this future `Drop`-safe, also shadow the original definition so we can't abuse it. + let link_ptr = &mut link_ptr as *mut Option>>; let queue = &self.queue; let marker = &AtomicUsize::new(0); @@ -199,6 +203,9 @@ impl TimerQueue { return Poll::Ready(()); } + // SAFETY: This pointer is only dereferenced here and on drop of the future + // which happens outside this `poll_fn`'s stack frame. + let link = unsafe { &mut *link_ptr }; if link.is_none() { let mut link_ref = link.insert(Link::new(WaitingWaker { waker: cx.waker().clone(), @@ -206,7 +213,9 @@ impl TimerQueue { was_poped: AtomicBool::new(false), })); - let (was_empty, addr) = queue.insert(&mut link_ref); + // SAFETY: The address to the link is stable as it is defined outside this stack + // frame. + let (was_empty, addr) = queue.insert(unsafe { Pin::new_unchecked(&mut link_ref) }); marker.store(addr, Ordering::Relaxed); if was_empty { @@ -219,7 +228,10 @@ impl TimerQueue { }) .await; - if let Some(link) = link { + // SAFETY: We only run this and dereference the pointer if we have + // exited the `poll_fn` below in the `drop(dropper)` call. The other dereference + // of this pointer is in the `poll_fn`. + if let Some(link) = unsafe { &mut *link_ptr } { if link.val.was_poped.load(Ordering::Relaxed) { // If it was poped from the queue there is no need to run delete dropper.defuse(); diff --git a/rtic-time/src/linked_list.rs b/rtic-time/src/linked_list.rs index 52a955bd46..de5ea2a48a 100644 --- a/rtic-time/src/linked_list.rs +++ b/rtic-time/src/linked_list.rs @@ -1,6 +1,7 @@ //! ... use core::marker::PhantomPinned; +use core::pin::Pin; use core::sync::atomic::{AtomicPtr, Ordering}; use critical_section as cs; @@ -92,8 +93,10 @@ impl LinkedList { /// Insert a new link into the linked list. /// The return is (was_empty, address), where the address of the link is for use with `delete`. - pub fn insert(&self, val: &mut Link) -> (bool, usize) { + pub fn insert(&self, val: Pin<&mut Link>) -> (bool, usize) { cs::with(|_| { + // SAFETY: This datastructure does not move the underlying value. + let val = unsafe { val.get_unchecked_mut() }; let addr = val as *const _ as usize; // Make sure all previous writes are visible From b2c53824303dddd092ba4d9d7928635d1f89f789 Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Sat, 28 Jan 2023 13:35:37 +0100 Subject: [PATCH 274/423] rtic-channel: Fix clippy lint --- rtic-channel/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rtic-channel/src/lib.rs b/rtic-channel/src/lib.rs index 20086ac7fb..b6a317fb84 100644 --- a/rtic-channel/src/lib.rs +++ b/rtic-channel/src/lib.rs @@ -66,7 +66,7 @@ impl Channel { } /// Split the queue into a `Sender`/`Receiver` pair. - pub fn split<'a>(&'a mut self) -> (Sender<'a, T, N>, Receiver<'a, T, N>) { + pub fn split(&mut self) -> (Sender<'_, T, N>, Receiver<'_, T, N>) { // Fill free queue for idx in 0..N as u8 { debug_assert!(!self.freeq.get_mut().is_full()); From 4cf8bbbf192064aad2e55e29f6494c1bf1010437 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Tj=C3=A4der?= Date: Sat, 28 Jan 2023 12:21:48 +0100 Subject: [PATCH 275/423] Book: Fix gitignore to exclude mdbook output --- book/.gitignore | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/book/.gitignore b/book/.gitignore index ee70b5e5f5..79339e2bf0 100644 --- a/book/.gitignore +++ b/book/.gitignore @@ -1,7 +1,2 @@ -**/*.rs.bk -.#* -.gdb_history -/*/book -/target -Cargo.lock -*.hex +# Make sure that mdbook output in repo-root/book//book is ignored +*/book From 6021aa2df8cbcf74910ea4b19ad24036f529710f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Tj=C3=A4der?= Date: Sat, 28 Jan 2023 14:02:54 +0100 Subject: [PATCH 276/423] CI: Check and tests for all crates --- .github/workflows/build.yml | 176 +++++++++++++++++++++++++++++++++++- 1 file changed, 172 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 9f7d221975..2e3f2b7614 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -15,7 +15,7 @@ env: jobs: # Run cargo fmt --check, includes macros/ style: - name: cargo fmt + name: cargo fmt rtic runs-on: ubuntu-22.04 steps: - name: Checkout @@ -77,7 +77,7 @@ jobs: # Compilation check check: - name: check + name: check rtic runs-on: ubuntu-22.04 strategy: matrix: @@ -112,9 +112,120 @@ jobs: working-directory: ./rtic run: cargo check --target=${{ matrix.target }} + # Compilation check + checkchannel: + name: check rtic-channel + runs-on: ubuntu-22.04 + strategy: + matrix: + target: + - thumbv7m-none-eabi + - thumbv6m-none-eabi + - x86_64-unknown-linux-gnu + toolchain: + - nightly + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Install Rust ${{ matrix.toolchain }} + working-directory: ./rtic-channel + run: | + rustup set profile minimal + rustup override set ${{ matrix.toolchain }} + + - name: Configure Rust target (${{ matrix.target }}) + working-directory: ./rtic-channel + run: rustup target add ${{ matrix.target }} + + - name: Fail on warnings + working-directory: ./rtic-channel + run: sed -i 's,//deny_warnings_placeholder_for_ci,#![deny(warnings)],' src/lib.rs + + - name: Cache Dependencies + uses: Swatinem/rust-cache@v2 + + - name: cargo check + working-directory: ./rtic-channel + run: cargo check --target=${{ matrix.target }} + + # Compilation check + checkmonotonics: + name: check rtic-monotonics + runs-on: ubuntu-22.04 + strategy: + matrix: + target: + - thumbv7m-none-eabi + - thumbv6m-none-eabi + - x86_64-unknown-linux-gnu + toolchain: + - nightly + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Install Rust ${{ matrix.toolchain }} + working-directory: ./rtic-monotonics + run: | + rustup set profile minimal + rustup override set ${{ matrix.toolchain }} + + - name: Configure Rust target (${{ matrix.target }}) + working-directory: ./rtic-monotonics + run: rustup target add ${{ matrix.target }} + + - name: Fail on warnings + working-directory: ./rtic-monotonics + run: sed -i 's,//deny_warnings_placeholder_for_ci,#![deny(warnings)],' src/lib.rs + + - name: Cache Dependencies + uses: Swatinem/rust-cache@v2 + + - name: cargo check + working-directory: ./rtic-monotonics + run: cargo check --target=${{ matrix.target }} + + # Compilation check + checktime: + name: check rtic-time + runs-on: ubuntu-22.04 + strategy: + matrix: + target: + - thumbv7m-none-eabi + - thumbv6m-none-eabi + - x86_64-unknown-linux-gnu + toolchain: + - nightly + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Install Rust ${{ matrix.toolchain }} + working-directory: ./rtic-time + run: | + rustup set profile minimal + rustup override set ${{ matrix.toolchain }} + + - name: Configure Rust target (${{ matrix.target }}) + working-directory: ./rtic-time + run: rustup target add ${{ matrix.target }} + + - name: Fail on warnings + working-directory: ./rtic-time + run: sed -i 's,//deny_warnings_placeholder_for_ci,#![deny(warnings)],' src/lib.rs + + - name: Cache Dependencies + uses: Swatinem/rust-cache@v2 + + - name: cargo check + working-directory: ./rtic-time + run: cargo check --target=${{ matrix.target }} + # Clippy clippy: - name: Cargo clippy + name: Cargo clippy rtic runs-on: ubuntu-22.04 steps: - name: Checkout @@ -337,7 +448,7 @@ jobs: # Run test suite tests: - name: tests + name: tests rtic runs-on: ubuntu-22.04 steps: - name: Checkout @@ -354,6 +465,63 @@ jobs: working-directory: ./rtic run: cargo test --test tests + # Run test suite + testschannel: + name: tests rtic-channel + runs-on: ubuntu-22.04 + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Cache Dependencies + uses: Swatinem/rust-cache@v2 + + - name: Fail on warnings + working-directory: ./rtic-channel + run: sed -i 's,//deny_warnings_placeholder_for_ci,#![deny(warnings)],' src/lib.rs + + - name: Run cargo test + working-directory: ./rtic-channel + run: cargo test --test tests + + # Run test suite + testsmonotonics: + name: tests rtic-monotonics + runs-on: ubuntu-22.04 + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Cache Dependencies + uses: Swatinem/rust-cache@v2 + + - name: Fail on warnings + working-directory: ./rtic-monotonics + run: sed -i 's,//deny_warnings_placeholder_for_ci,#![deny(warnings)],' src/lib.rs + + - name: Run cargo test + working-directory: ./rtic-monotonics + run: cargo test --test tests + + # Run test suite + teststime: + name: tests rtic-time + runs-on: ubuntu-22.04 + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Cache Dependencies + uses: Swatinem/rust-cache@v2 + + - name: Fail on warnings + working-directory: ./rtic-time + run: sed -i 's,//deny_warnings_placeholder_for_ci,#![deny(warnings)],' src/lib.rs + + - name: Run cargo test + working-directory: ./rtic-time + run: cargo test --test tests + # # Build documentation, check links # docs: # name: docs From 48ac310036bb0053d36d9cfce191351028808651 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Tj=C3=A4der?= Date: Sat, 28 Jan 2023 14:12:32 +0100 Subject: [PATCH 277/423] CI: Check/build the docs Still no publish or further steps --- .github/workflows/build.yml | 184 ++++++++++++++++++------------------ 1 file changed, 94 insertions(+), 90 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 2e3f2b7614..004a5ecc25 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -522,96 +522,100 @@ jobs: working-directory: ./rtic-time run: cargo test --test tests -# # Build documentation, check links -# docs: -# name: docs -# runs-on: ubuntu-22.04 -# steps: -# - name: Checkout -# uses: actions/checkout@v3 -# -# - name: Cache pip installed linkchecker -# uses: actions/cache@v3 -# with: -# path: ~/.cache/pip -# key: ${{ runner.os }}-pip -# restore-keys: | -# ${{ runner.os }}-pip- -# -# - name: Set up Python 3.x -# uses: actions/setup-python@v4 -# with: -# # Semantic version range syntax or exact version of a Python version -# python-version: '3.x' -# -# # You can test your matrix by printing the current Python version -# - name: Display Python version -# run: python -c "import sys; print(sys.version)" -# -# - name: Install dependencies -# run: pip install git+https://github.com/linkchecker/linkchecker.git -# -# - name: Remove cargo-config -# run: rm -f .cargo/config -# -# - name: Fail on warnings -# run: sed -i 's,//deny_warnings_placeholder_for_ci,#![deny(warnings)],' src/lib.rs macros/src/lib.rs -# -# - name: Build docs -# run: cargo doc -# -# - name: Check links -# run: | -# td=$(mktemp -d) -# cp -r target/doc $td/api -# linkchecker $td/api/rtic/ -# linkchecker $td/api/cortex_m_rtic_macros/ -# -# # Build the books -# mdbook: -# name: mdbook -# runs-on: ubuntu-22.04 -# steps: -# - name: Checkout -# uses: actions/checkout@v3 -# - name: Set up Python 3.x -# uses: actions/setup-python@v4 -# with: -# # Semantic version range syntax or exact version of a Python version -# python-version: '3.x' -# -# # You can test your matrix by printing the current Python version -# - name: Display Python version -# run: python -c "import sys; print(sys.version)" -# -# - name: Install dependencies -# run: pip install git+https://github.com/linkchecker/linkchecker.git -# -# - name: mdBook Action -# uses: peaceiris/actions-mdbook@v1 -# with: -# mdbook-version: 'latest' -# -# - name: Build book in English -# shell: 'script --return --quiet --command "bash {0}"' -# run: cd book/en && if mdbook build |& tee /dev/tty | grep "\[ERROR\]"; then exit 1; else exit 0; fi -# -# - name: Build book in Russian -# shell: 'script --return --quiet --command "bash {0}"' -# run: cd book/ru && if mdbook build |& tee /dev/tty | grep "\[ERROR\]"; then echo "Russian book needs updating!"; else exit 0; fi -# -# - name: Check links -# run: | -# td=$(mktemp -d) -# mkdir $td/book -# cp -r book/en/book $td/book/en -# cp -r book/ru/book $td/book/ru -# cp LICENSE-* $td/book/en -# cp LICENSE-* $td/book/ru -# -# linkchecker $td/book/en/ -# linkchecker $td/book/ru/ -# + # Build documentation, check links + docs: + name: docs + runs-on: ubuntu-22.04 + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Cache pip installed linkchecker + uses: actions/cache@v3 + with: + path: ~/.cache/pip + key: ${{ runner.os }}-pip + restore-keys: | + ${{ runner.os }}-pip- + + - name: Set up Python 3.x + uses: actions/setup-python@v4 + with: + # Semantic version range syntax or exact version of a Python version + python-version: '3.x' + + # You can test your matrix by printing the current Python version + - name: Display Python version + run: python -c "import sys; print(sys.version)" + + - name: Install dependencies + run: pip install git+https://github.com/linkchecker/linkchecker.git + + - name: Remove cargo-config + working-directory: ./rtic + run: rm -f .cargo/config + + - name: Fail on warnings + working-directory: ./rtic + run: sed -i 's,//deny_warnings_placeholder_for_ci,#![deny(warnings)],' src/lib.rs macros/src/lib.rs + + - name: Build docs + working-directory: ./rtic + run: cargo doc + + - name: Check links + working-directory: ./rtic + run: | + td=$(mktemp -d) + cp -r target/doc $td/api + linkchecker $td/api/rtic/ + linkchecker $td/api/rtic_macros/ + + # Build the books + mdbook: + name: mdbook + runs-on: ubuntu-22.04 + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Set up Python 3.x + uses: actions/setup-python@v4 + with: + # Semantic version range syntax or exact version of a Python version + python-version: '3.x' + + # You can test your matrix by printing the current Python version + - name: Display Python version + run: python -c "import sys; print(sys.version)" + + - name: Install dependencies + run: pip install git+https://github.com/linkchecker/linkchecker.git + + - name: mdBook Action + uses: peaceiris/actions-mdbook@v1 + with: + mdbook-version: 'latest' + + - name: Build book in English + shell: 'script --return --quiet --command "bash {0}"' + run: cd book/en && if mdbook build |& tee /dev/tty | grep "\[ERROR\]"; then exit 1; else exit 0; fi + + - name: Build book in Russian + shell: 'script --return --quiet --command "bash {0}"' + run: cd book/ru && if mdbook build |& tee /dev/tty | grep "\[ERROR\]"; then echo "Russian book needs updating!"; else exit 0; fi + + - name: Check links + run: | + td=$(mktemp -d) + mkdir $td/book + cp -r book/en/book $td/book/en + cp -r book/ru/book $td/book/ru + cp LICENSE-* $td/book/en + cp LICENSE-* $td/book/ru + + linkchecker $td/book/en/ + linkchecker $td/book/ru/ + # # Update stable branch # # # # This needs to run before book is built From 94b00df2c7e82efb9003945bb90675e73ed0f5e0 Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Sat, 28 Jan 2023 20:47:21 +0100 Subject: [PATCH 278/423] rtic-channel: Add testing, fix bugs --- rtic-channel/Cargo.toml | 3 + rtic-channel/src/lib.rs | 170 ++++++++++++++++++++++++++++++--- rtic-channel/src/wait_queue.rs | 14 ++- 3 files changed, 172 insertions(+), 15 deletions(-) diff --git a/rtic-channel/Cargo.toml b/rtic-channel/Cargo.toml index 89623524e5..5d4cbd0e08 100644 --- a/rtic-channel/Cargo.toml +++ b/rtic-channel/Cargo.toml @@ -9,6 +9,9 @@ edition = "2021" heapless = "0.7" critical-section = "1" +[dev-dependencies] +tokio = { version = "1", features = ["rt", "macros", "time"] } + [features] default = [] diff --git a/rtic-channel/src/lib.rs b/rtic-channel/src/lib.rs index b6a317fb84..1077b5a6e2 100644 --- a/rtic-channel/src/lib.rs +++ b/rtic-channel/src/lib.rs @@ -10,6 +10,7 @@ use core::{ mem::MaybeUninit, pin::Pin, ptr, + sync::atomic::{fence, Ordering}, task::{Poll, Waker}, }; use heapless::Deque; @@ -40,6 +41,9 @@ pub struct Channel { num_senders: UnsafeCell, } +unsafe impl Send for Channel {} +unsafe impl Sync for Channel {} + struct UnsafeAccess<'a, const N: usize> { freeq: &'a mut Deque, readyq: &'a mut Deque, @@ -129,6 +133,21 @@ pub struct Sender<'a, T, const N: usize>(&'a Channel); unsafe impl<'a, T, const N: usize> Send for Sender<'a, T, N> {} +/// This is needed to make the async closure in `send` accept that we "share" +/// the link possible between threads. +#[derive(Clone)] +struct LinkPtr(*mut Option>); + +impl LinkPtr { + /// This will dereference the pointer stored within and give out an `&mut`. + unsafe fn get(&self) -> &mut Option> { + &mut *self.0 + } +} + +unsafe impl Send for LinkPtr {} +unsafe impl Sync for LinkPtr {} + impl<'a, T, const N: usize> core::fmt::Debug for Sender<'a, T, N> { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { write!(f, "Sender") @@ -147,7 +166,12 @@ impl<'a, T, const N: usize> Sender<'a, T, N> { } // Write the value into the ready queue. - critical_section::with(|cs| unsafe { self.0.access(cs).readyq.push_back_unchecked(idx) }); + critical_section::with(|cs| { + debug_assert!(!self.0.access(cs).readyq.is_full()); + unsafe { self.0.access(cs).readyq.push_back_unchecked(idx) } + }); + + fence(Ordering::SeqCst); // If there is a receiver waker, wake it. self.0.receiver_waker.wake(); @@ -176,18 +200,17 @@ impl<'a, T, const N: usize> Sender<'a, T, N> { /// Send a value. If there is no place left in the queue this will wait until there is. /// If the receiver does not exist this will return an error. pub async fn send(&mut self, val: T) -> Result<(), NoReceiver> { - if self.is_closed() {} - let mut link_ptr: Option> = None; // Make this future `Drop`-safe, also shadow the original definition so we can't abuse it. - let link_ptr = &mut link_ptr as *mut Option>; + let link_ptr = LinkPtr(&mut link_ptr as *mut Option>); + let link_ptr2 = link_ptr.clone(); let dropper = OnDrop::new(|| { // SAFETY: We only run this closure and dereference the pointer if we have // exited the `poll_fn` below in the `drop(dropper)` call. The other dereference // of this pointer is in the `poll_fn`. - if let Some(link) = unsafe { &mut *link_ptr } { + if let Some(link) = unsafe { link_ptr2.get() } { link.remove_from_list(&self.0.wait_queue); } }); @@ -199,11 +222,19 @@ impl<'a, T, const N: usize> Sender<'a, T, N> { // Do all this in one critical section, else there can be race conditions let queue_idx = critical_section::with(|cs| { - if !self.0.wait_queue.is_empty() || self.0.access(cs).freeq.is_empty() { + let wq_empty = self.0.wait_queue.is_empty(); + let fq_empty = self.0.access(cs).freeq.is_empty(); + if !wq_empty || fq_empty { // SAFETY: This pointer is only dereferenced here and on drop of the future // which happens outside this `poll_fn`'s stack frame. - let link = unsafe { &mut *link_ptr }; - if link.is_none() { + let link = unsafe { link_ptr.get() }; + if let Some(link) = link { + if !link.is_poped() { + return None; + } else { + // Fall through to dequeue + } + } else { // Place the link in the wait queue on first run. let link_ref = link.insert(wait_queue::Link::new(cx.waker().clone())); @@ -212,11 +243,12 @@ impl<'a, T, const N: usize> Sender<'a, T, N> { self.0 .wait_queue .push(unsafe { Pin::new_unchecked(link_ref) }); - } - return None; + return None; + } } + debug_assert!(!self.0.access(cs).freeq.is_empty()); // Get index as the queue is guaranteed not empty and the wait queue is empty let idx = unsafe { self.0.access(cs).freeq.pop_front_unchecked() }; @@ -319,7 +351,12 @@ impl<'a, T, const N: usize> Receiver<'a, T, N> { let r = unsafe { ptr::read(self.0.slots.get_unchecked(rs as usize).get() as *const T) }; // Return the index to the free queue after we've read the value. - critical_section::with(|cs| unsafe { self.0.access(cs).freeq.push_back_unchecked(rs) }); + critical_section::with(|cs| { + debug_assert!(!self.0.access(cs).freeq.is_full()); + unsafe { self.0.access(cs).freeq.push_back_unchecked(rs) } + }); + + fence(Ordering::SeqCst); // If someone is waiting in the WaiterQueue, wake the first one up. if let Some(wait_head) = self.0.wait_queue.pop() { @@ -363,7 +400,7 @@ impl<'a, T, const N: usize> Receiver<'a, T, N> { /// Is the queue full. pub fn is_full(&self) -> bool { - critical_section::with(|cs| self.0.access(cs).readyq.is_empty()) + critical_section::with(|cs| self.0.access(cs).readyq.is_full()) } /// Is the queue empty. @@ -412,6 +449,113 @@ extern crate std; #[cfg(test)] mod tests { + use super::*; + #[test] - fn channel() {} + fn empty() { + let (mut s, mut r) = make_channel!(u32, 10); + + assert!(s.is_empty()); + assert!(r.is_empty()); + + s.try_send(1).unwrap(); + + assert!(!s.is_empty()); + assert!(!r.is_empty()); + + r.try_recv().unwrap(); + + assert!(s.is_empty()); + assert!(r.is_empty()); + } + + #[test] + fn full() { + let (mut s, mut r) = make_channel!(u32, 3); + + for _ in 0..3 { + assert!(!s.is_full()); + assert!(!r.is_full()); + + s.try_send(1).unwrap(); + } + + assert!(s.is_full()); + assert!(r.is_full()); + + for _ in 0..3 { + r.try_recv().unwrap(); + + assert!(!s.is_full()); + assert!(!r.is_full()); + } + } + + #[test] + fn send_recieve() { + let (mut s, mut r) = make_channel!(u32, 10); + + for i in 0..10 { + s.try_send(i).unwrap(); + } + + assert_eq!(s.try_send(11), Err(11)); + + for i in 0..10 { + assert_eq!(r.try_recv().unwrap(), i); + } + + assert_eq!(r.try_recv(), None); + } + + #[test] + fn closed_recv() { + let (s, mut r) = make_channel!(u32, 10); + + drop(s); + + assert!(r.is_closed()); + + assert_eq!(r.try_recv(), None); + } + + #[test] + fn closed_sender() { + let (mut s, r) = make_channel!(u32, 10); + + drop(r); + + assert!(s.is_closed()); + + assert_eq!(s.try_send(11), Ok(())); + } + + #[tokio::test] + async fn stress_channel() { + const NUM_RUNS: usize = 1_000; + const QUEUE_SIZE: usize = 10; + + let (s, mut r) = make_channel!(u32, QUEUE_SIZE); + let mut v = std::vec::Vec::new(); + + for i in 0..NUM_RUNS { + let mut s = s.clone(); + + v.push(tokio::spawn(async move { + s.send(i as _).await.unwrap(); + })); + } + + let mut map = std::collections::BTreeSet::new(); + + for _ in 0..NUM_RUNS { + map.insert(r.recv().await.unwrap()); + } + + assert_eq!(map.len(), NUM_RUNS); + + for v in v { + v.await.unwrap(); + } + } } diff --git a/rtic-channel/src/wait_queue.rs b/rtic-channel/src/wait_queue.rs index ba05e6bb75..e6d5a8b97e 100644 --- a/rtic-channel/src/wait_queue.rs +++ b/rtic-channel/src/wait_queue.rs @@ -3,7 +3,7 @@ use core::marker::PhantomPinned; use core::pin::Pin; use core::ptr::null_mut; -use core::sync::atomic::{AtomicPtr, Ordering}; +use core::sync::atomic::{AtomicBool, AtomicPtr, Ordering}; use core::task::Waker; use critical_section as cs; @@ -57,6 +57,7 @@ impl LinkedList { // Clear the pointers in the node. head_ref.next.store(null_mut(), Self::R); head_ref.prev.store(null_mut(), Self::R); + head_ref.is_poped.store(true, Self::R); return Some(head_val); } @@ -100,9 +101,12 @@ pub struct Link { pub(crate) val: T, next: AtomicPtr>, prev: AtomicPtr>, + is_poped: AtomicBool, _up: PhantomPinned, } +unsafe impl Send for Link {} + impl Link { const R: Ordering = Ordering::Relaxed; @@ -112,10 +116,15 @@ impl Link { val, next: AtomicPtr::new(null_mut()), prev: AtomicPtr::new(null_mut()), + is_poped: AtomicBool::new(false), _up: PhantomPinned, } } + pub fn is_poped(&self) -> bool { + self.is_poped.load(Self::R) + } + pub fn remove_from_list(&mut self, list: &LinkedList) { cs::with(|_| { // Make sure all previous writes are visible @@ -123,6 +132,7 @@ impl Link { let prev = self.prev.load(Self::R); let next = self.next.load(Self::R); + self.is_poped.store(true, Self::R); match unsafe { (prev.as_ref(), next.as_ref()) } { (None, None) => { @@ -217,7 +227,7 @@ mod tests { #[test] fn linked_list() { - let mut wq = LinkedList::::new(); + let wq = LinkedList::::new(); let mut i1 = Link::new(10); let mut i2 = Link::new(11); From bef6c359a0802cb93c7bf0963d0fca7db540f64b Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Sat, 28 Jan 2023 20:54:34 +0100 Subject: [PATCH 279/423] Fix CI for rtic-channel --- .github/workflows/build.yml | 2 +- rtic-channel/src/lib.rs | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 004a5ecc25..650fc533da 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -482,7 +482,7 @@ jobs: - name: Run cargo test working-directory: ./rtic-channel - run: cargo test --test tests + run: cargo test --features testing # Run test suite testsmonotonics: diff --git a/rtic-channel/src/lib.rs b/rtic-channel/src/lib.rs index 1077b5a6e2..eafa25c3bf 100644 --- a/rtic-channel/src/lib.rs +++ b/rtic-channel/src/lib.rs @@ -140,7 +140,7 @@ struct LinkPtr(*mut Option>); impl LinkPtr { /// This will dereference the pointer stored within and give out an `&mut`. - unsafe fn get(&self) -> &mut Option> { + unsafe fn get(&mut self) -> &mut Option> { &mut *self.0 } } @@ -203,9 +203,9 @@ impl<'a, T, const N: usize> Sender<'a, T, N> { let mut link_ptr: Option> = None; // Make this future `Drop`-safe, also shadow the original definition so we can't abuse it. - let link_ptr = LinkPtr(&mut link_ptr as *mut Option>); + let mut link_ptr = LinkPtr(&mut link_ptr as *mut Option>); - let link_ptr2 = link_ptr.clone(); + let mut link_ptr2 = link_ptr.clone(); let dropper = OnDrop::new(|| { // SAFETY: We only run this closure and dereference the pointer if we have // exited the `poll_fn` below in the `drop(dropper)` call. The other dereference @@ -532,7 +532,7 @@ mod tests { #[tokio::test] async fn stress_channel() { - const NUM_RUNS: usize = 1_000; + const NUM_RUNS: usize = 1_000000; const QUEUE_SIZE: usize = 10; let (s, mut r) = make_channel!(u32, QUEUE_SIZE); From 2bd70baeb9362050196d431f2801551066e27e59 Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Sat, 28 Jan 2023 21:11:18 +0100 Subject: [PATCH 280/423] rtic-time: Make Send happy --- rtic-channel/src/lib.rs | 2 +- rtic-channel/src/wait_queue.rs | 2 -- rtic-time/src/lib.rs | 28 +++++++++++++++++++++++++--- 3 files changed, 26 insertions(+), 6 deletions(-) diff --git a/rtic-channel/src/lib.rs b/rtic-channel/src/lib.rs index eafa25c3bf..acfa801315 100644 --- a/rtic-channel/src/lib.rs +++ b/rtic-channel/src/lib.rs @@ -532,7 +532,7 @@ mod tests { #[tokio::test] async fn stress_channel() { - const NUM_RUNS: usize = 1_000000; + const NUM_RUNS: usize = 1_000; const QUEUE_SIZE: usize = 10; let (s, mut r) = make_channel!(u32, QUEUE_SIZE); diff --git a/rtic-channel/src/wait_queue.rs b/rtic-channel/src/wait_queue.rs index e6d5a8b97e..2de6311d17 100644 --- a/rtic-channel/src/wait_queue.rs +++ b/rtic-channel/src/wait_queue.rs @@ -105,8 +105,6 @@ pub struct Link { _up: PhantomPinned, } -unsafe impl Send for Link {} - impl Link { const R: Ordering = Ordering::Relaxed; diff --git a/rtic-time/src/lib.rs b/rtic-time/src/lib.rs index 6b23f7653d..44fdbcecfc 100644 --- a/rtic-time/src/lib.rs +++ b/rtic-time/src/lib.rs @@ -73,6 +73,26 @@ pub struct TimerQueue { /// This indicates that there was a timeout. pub struct TimeoutError; +/// This is needed to make the async closure in `delay_until` accept that we "share" +/// the link possible between threads. +struct LinkPtr(*mut Option>>); + +impl Clone for LinkPtr { + fn clone(&self) -> Self { + LinkPtr(self.0) + } +} + +impl LinkPtr { + /// This will dereference the pointer stored within and give out an `&mut`. + unsafe fn get(&mut self) -> &mut Option>> { + &mut *self.0 + } +} + +unsafe impl Send for LinkPtr {} +unsafe impl Sync for LinkPtr {} + impl TimerQueue { /// Make a new queue. pub const fn new() -> Self { @@ -189,7 +209,9 @@ impl TimerQueue { let mut link_ptr: Option>> = None; // Make this future `Drop`-safe, also shadow the original definition so we can't abuse it. - let link_ptr = &mut link_ptr as *mut Option>>; + let mut link_ptr = + LinkPtr(&mut link_ptr as *mut Option>>); + let mut link_ptr2 = link_ptr.clone(); let queue = &self.queue; let marker = &AtomicUsize::new(0); @@ -205,7 +227,7 @@ impl TimerQueue { // SAFETY: This pointer is only dereferenced here and on drop of the future // which happens outside this `poll_fn`'s stack frame. - let link = unsafe { &mut *link_ptr }; + let link = unsafe { link_ptr2.get() }; if link.is_none() { let mut link_ref = link.insert(Link::new(WaitingWaker { waker: cx.waker().clone(), @@ -231,7 +253,7 @@ impl TimerQueue { // SAFETY: We only run this and dereference the pointer if we have // exited the `poll_fn` below in the `drop(dropper)` call. The other dereference // of this pointer is in the `poll_fn`. - if let Some(link) = unsafe { &mut *link_ptr } { + if let Some(link) = unsafe { link_ptr.get() } { if link.val.was_poped.load(Ordering::Relaxed) { // If it was poped from the queue there is no need to run delete dropper.defuse(); From 58692a35e87ddc8b8faca5bb262070d343ceb869 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Tj=C3=A4der?= Date: Sat, 28 Jan 2023 20:26:50 +0100 Subject: [PATCH 281/423] Fix some references to cortex-m-rtic --- rtic/macros/src/lib.rs | 4 ++-- rtic/src/lib.rs | 9 ++------- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/rtic/macros/src/lib.rs b/rtic/macros/src/lib.rs index 92d7cddd99..3ac27017db 100644 --- a/rtic/macros/src/lib.rs +++ b/rtic/macros/src/lib.rs @@ -58,8 +58,8 @@ pub fn app(args: TokenStream, input: TokenStream) -> TokenStream { out_dir = Path::new(&out_str); // Default build path, annotated below: - // $(pwd)/target/thumbv7em-none-eabihf/debug/build/cortex-m-rtic-/out/ - // ///debug/build/cortex-m-rtic-/out/ + // $(pwd)/target/thumbv7em-none-eabihf/debug/build/rtic-/out/ + // ///debug/build/rtic-/out/ // // traverse up to first occurrence of TARGET, approximated with starts_with("thumbv") // and use the parent() of this path diff --git a/rtic/src/lib.rs b/rtic/src/lib.rs index e8b8140a79..860e7436ff 100644 --- a/rtic/src/lib.rs +++ b/rtic/src/lib.rs @@ -1,10 +1,5 @@ //! Real-Time Interrupt-driven Concurrency (RTIC) framework for ARM Cortex-M microcontrollers. //! -//! **IMPORTANT**: This crate is published as [`cortex-m-rtic`] on crates.io but the name of the -//! library is `rtic`. -//! -//! [`cortex-m-rtic`]: https://crates.io/crates/cortex-m-rtic -//! //! The user level documentation can be found [here]. //! //! [here]: https://rtic.rs @@ -32,8 +27,8 @@ #![deny(rust_2018_idioms)] #![no_std] #![doc( - html_logo_url = "https://raw.githubusercontent.com/rtic-rs/cortex-m-rtic/master/book/en/src/RTIC.svg", - html_favicon_url = "https://raw.githubusercontent.com/rtic-rs/cortex-m-rtic/master/book/en/src/RTIC.svg" + html_logo_url = "https://raw.githubusercontent.com/rtic-rs/rtic/master/book/en/src/RTIC.svg", + html_favicon_url = "https://raw.githubusercontent.com/rtic-rs/rtic/master/book/en/src/RTIC.svg" )] //deny_warnings_placeholder_for_ci #![allow(clippy::inline_always)] From e65e532c2a342f77080ac6fc8e5be11aa7d82575 Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Sun, 29 Jan 2023 20:16:23 +0100 Subject: [PATCH 282/423] Move common data structures to `rtic-common` --- rtic-channel/Cargo.toml | 3 ++- rtic-channel/src/lib.rs | 17 +++++++---------- rtic-common/.gitignore | 2 ++ rtic-common/CHANGELOG.md | 16 ++++++++++++++++ rtic-common/Cargo.toml | 13 +++++++++++++ rtic-common/src/lib.rs | 8 ++++++++ {rtic-channel => rtic-common}/src/wait_queue.rs | 3 +++ .../src/waker_registration.rs | 2 ++ 8 files changed, 53 insertions(+), 11 deletions(-) create mode 100644 rtic-common/.gitignore create mode 100644 rtic-common/CHANGELOG.md create mode 100644 rtic-common/Cargo.toml create mode 100644 rtic-common/src/lib.rs rename {rtic-channel => rtic-common}/src/wait_queue.rs (98%) rename {rtic-channel => rtic-common}/src/waker_registration.rs (98%) diff --git a/rtic-channel/Cargo.toml b/rtic-channel/Cargo.toml index 5d4cbd0e08..a0955bc405 100644 --- a/rtic-channel/Cargo.toml +++ b/rtic-channel/Cargo.toml @@ -8,6 +8,7 @@ edition = "2021" [dependencies] heapless = "0.7" critical-section = "1" +rtic-common = { version = "1.0.0", path = "../rtic-common" } [dev-dependencies] tokio = { version = "1", features = ["rt", "macros", "time"] } @@ -15,4 +16,4 @@ tokio = { version = "1", features = ["rt", "macros", "time"] } [features] default = [] -testing = ["critical-section/std"] +testing = ["critical-section/std", "rtic-common/testing"] diff --git a/rtic-channel/src/lib.rs b/rtic-channel/src/lib.rs index acfa801315..2b237f669f 100644 --- a/rtic-channel/src/lib.rs +++ b/rtic-channel/src/lib.rs @@ -14,11 +14,8 @@ use core::{ task::{Poll, Waker}, }; use heapless::Deque; -use wait_queue::WaitQueue; -use waker_registration::CriticalSectionWakerRegistration as WakerRegistration; - -mod wait_queue; -mod waker_registration; +use rtic_common::wait_queue::{Link, WaitQueue}; +use rtic_common::waker_registration::CriticalSectionWakerRegistration as WakerRegistration; /// An MPSC channel for use in no-alloc systems. `N` sets the size of the queue. /// @@ -136,11 +133,11 @@ unsafe impl<'a, T, const N: usize> Send for Sender<'a, T, N> {} /// This is needed to make the async closure in `send` accept that we "share" /// the link possible between threads. #[derive(Clone)] -struct LinkPtr(*mut Option>); +struct LinkPtr(*mut Option>); impl LinkPtr { /// This will dereference the pointer stored within and give out an `&mut`. - unsafe fn get(&mut self) -> &mut Option> { + unsafe fn get(&mut self) -> &mut Option> { &mut *self.0 } } @@ -200,10 +197,10 @@ impl<'a, T, const N: usize> Sender<'a, T, N> { /// Send a value. If there is no place left in the queue this will wait until there is. /// If the receiver does not exist this will return an error. pub async fn send(&mut self, val: T) -> Result<(), NoReceiver> { - let mut link_ptr: Option> = None; + let mut link_ptr: Option> = None; // Make this future `Drop`-safe, also shadow the original definition so we can't abuse it. - let mut link_ptr = LinkPtr(&mut link_ptr as *mut Option>); + let mut link_ptr = LinkPtr(&mut link_ptr as *mut Option>); let mut link_ptr2 = link_ptr.clone(); let dropper = OnDrop::new(|| { @@ -236,7 +233,7 @@ impl<'a, T, const N: usize> Sender<'a, T, N> { } } else { // Place the link in the wait queue on first run. - let link_ref = link.insert(wait_queue::Link::new(cx.waker().clone())); + let link_ref = link.insert(Link::new(cx.waker().clone())); // SAFETY: The address to the link is stable as it is hidden behind // `link_ptr`, and `link_ptr` shadows the original making it unmovable. diff --git a/rtic-common/.gitignore b/rtic-common/.gitignore new file mode 100644 index 0000000000..1e7caa9ea8 --- /dev/null +++ b/rtic-common/.gitignore @@ -0,0 +1,2 @@ +Cargo.lock +target/ diff --git a/rtic-common/CHANGELOG.md b/rtic-common/CHANGELOG.md new file mode 100644 index 0000000000..d3a9d846ee --- /dev/null +++ b/rtic-common/CHANGELOG.md @@ -0,0 +1,16 @@ +# Change Log + +All notable changes to this project will be documented in this file. +This project adheres to [Semantic Versioning](http://semver.org/). + +For each category, *Added*, *Changed*, *Fixed* add new entries at the top! + +## [Unreleased] + +### Added + +### Changed + +### Fixed + +## [v1.0.0] - 2023-xx-xx diff --git a/rtic-common/Cargo.toml b/rtic-common/Cargo.toml new file mode 100644 index 0000000000..258caae06f --- /dev/null +++ b/rtic-common/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "rtic-common" +version = "1.0.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +critical-section = "1" + +[features] +default = [] +testing = ["critical-section/std"] diff --git a/rtic-common/src/lib.rs b/rtic-common/src/lib.rs new file mode 100644 index 0000000000..3c75856c8a --- /dev/null +++ b/rtic-common/src/lib.rs @@ -0,0 +1,8 @@ +//! Crate + +#![no_std] +#![deny(missing_docs)] +//deny_warnings_placeholder_for_ci + +pub mod wait_queue; +pub mod waker_registration; diff --git a/rtic-channel/src/wait_queue.rs b/rtic-common/src/wait_queue.rs similarity index 98% rename from rtic-channel/src/wait_queue.rs rename to rtic-common/src/wait_queue.rs index 2de6311d17..ba8ff5471a 100644 --- a/rtic-channel/src/wait_queue.rs +++ b/rtic-common/src/wait_queue.rs @@ -7,6 +7,7 @@ use core::sync::atomic::{AtomicBool, AtomicPtr, Ordering}; use core::task::Waker; use critical_section as cs; +/// A helper definition of a wait queue. pub type WaitQueue = LinkedList; /// A FIFO linked list for a wait queue. @@ -119,10 +120,12 @@ impl Link { } } + /// Return true if this link has been poped from the list. pub fn is_poped(&self) -> bool { self.is_poped.load(Self::R) } + /// Remove this link from a linked list. pub fn remove_from_list(&mut self, list: &LinkedList) { cs::with(|_| { // Make sure all previous writes are visible diff --git a/rtic-channel/src/waker_registration.rs b/rtic-common/src/waker_registration.rs similarity index 98% rename from rtic-channel/src/waker_registration.rs rename to rtic-common/src/waker_registration.rs index c30df7fe07..174765c2ca 100644 --- a/rtic-channel/src/waker_registration.rs +++ b/rtic-common/src/waker_registration.rs @@ -1,3 +1,5 @@ +//! Waker registration utility. + use core::cell::UnsafeCell; use core::task::Waker; From 5c1cefbf4e249c38467e3f6eb4e061e5b8073d6c Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Mon, 30 Jan 2023 14:49:24 +0100 Subject: [PATCH 283/423] Add `rtic-arbiter` --- rtic-arbiter/.gitignore | 2 + rtic-arbiter/CHANGELOG.md | 16 ++++ rtic-arbiter/Cargo.toml | 18 ++++ rtic-arbiter/src/lib.rs | 175 +++++++++++++++++++++++++++++++++++++ rtic-channel/src/lib.rs | 28 +----- rtic-common/src/dropper.rs | 26 ++++++ rtic-common/src/lib.rs | 1 + 7 files changed, 242 insertions(+), 24 deletions(-) create mode 100644 rtic-arbiter/.gitignore create mode 100644 rtic-arbiter/CHANGELOG.md create mode 100644 rtic-arbiter/Cargo.toml create mode 100644 rtic-arbiter/src/lib.rs create mode 100644 rtic-common/src/dropper.rs diff --git a/rtic-arbiter/.gitignore b/rtic-arbiter/.gitignore new file mode 100644 index 0000000000..1e7caa9ea8 --- /dev/null +++ b/rtic-arbiter/.gitignore @@ -0,0 +1,2 @@ +Cargo.lock +target/ diff --git a/rtic-arbiter/CHANGELOG.md b/rtic-arbiter/CHANGELOG.md new file mode 100644 index 0000000000..d3a9d846ee --- /dev/null +++ b/rtic-arbiter/CHANGELOG.md @@ -0,0 +1,16 @@ +# Change Log + +All notable changes to this project will be documented in this file. +This project adheres to [Semantic Versioning](http://semver.org/). + +For each category, *Added*, *Changed*, *Fixed* add new entries at the top! + +## [Unreleased] + +### Added + +### Changed + +### Fixed + +## [v1.0.0] - 2023-xx-xx diff --git a/rtic-arbiter/Cargo.toml b/rtic-arbiter/Cargo.toml new file mode 100644 index 0000000000..b1afaf4516 --- /dev/null +++ b/rtic-arbiter/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "rtic-arbiter" +version = "1.0.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +critical-section = "1" +rtic-common = { version = "1.0.0", path = "../rtic-common" } + +[dev-dependencies] +tokio = { version = "1", features = ["rt", "macros", "time"] } + + +[features] +default = [] +testing = ["critical-section/std", "rtic-common/testing"] diff --git a/rtic-arbiter/src/lib.rs b/rtic-arbiter/src/lib.rs new file mode 100644 index 0000000000..487c64cecd --- /dev/null +++ b/rtic-arbiter/src/lib.rs @@ -0,0 +1,175 @@ +//! Crate + +#![no_std] +#![deny(missing_docs)] +//deny_warnings_placeholder_for_ci + +use core::cell::UnsafeCell; +use core::future::poll_fn; +use core::ops::{Deref, DerefMut}; +use core::pin::Pin; +use core::sync::atomic::{fence, AtomicBool, Ordering}; +use core::task::{Poll, Waker}; + +use rtic_common::dropper::OnDrop; +use rtic_common::wait_queue::{Link, WaitQueue}; + +/// This is needed to make the async closure in `send` accept that we "share" +/// the link possible between threads. +#[derive(Clone)] +struct LinkPtr(*mut Option>); + +impl LinkPtr { + /// This will dereference the pointer stored within and give out an `&mut`. + unsafe fn get(&mut self) -> &mut Option> { + &mut *self.0 + } +} + +unsafe impl Send for LinkPtr {} +unsafe impl Sync for LinkPtr {} + +/// An FIFO waitqueue for use in shared bus usecases. +pub struct Arbiter { + wait_queue: WaitQueue, + inner: UnsafeCell, + taken: AtomicBool, +} + +impl Arbiter { + /// Create a new arbiter. + pub const fn new(inner: T) -> Self { + Self { + wait_queue: WaitQueue::new(), + inner: UnsafeCell::new(inner), + taken: AtomicBool::new(false), + } + } + + /// Get access to the inner value in the `Arbiter`. This will wait until access is granted, + /// for non-blocking access use `try_access`. + pub async fn access(&self) -> ExclusiveAccess<'_, T> { + let mut link_ptr: Option> = None; + + // Make this future `Drop`-safe, also shadow the original definition so we can't abuse it. + let mut link_ptr = LinkPtr(&mut link_ptr as *mut Option>); + + let mut link_ptr2 = link_ptr.clone(); + let dropper = OnDrop::new(|| { + // SAFETY: We only run this closure and dereference the pointer if we have + // exited the `poll_fn` below in the `drop(dropper)` call. The other dereference + // of this pointer is in the `poll_fn`. + if let Some(link) = unsafe { link_ptr2.get() } { + link.remove_from_list(&self.wait_queue); + } + }); + + poll_fn(|cx| { + critical_section::with(|_| { + fence(Ordering::SeqCst); + + // The queue is empty and noone has taken the value. + if self.wait_queue.is_empty() && !self.taken.load(Ordering::Relaxed) { + self.taken.store(true, Ordering::Relaxed); + + return Poll::Ready(()); + } + + // SAFETY: This pointer is only dereferenced here and on drop of the future + // which happens outside this `poll_fn`'s stack frame. + let link = unsafe { link_ptr.get() }; + if let Some(link) = link { + if link.is_poped() { + return Poll::Ready(()); + } + } else { + // Place the link in the wait queue on first run. + let link_ref = link.insert(Link::new(cx.waker().clone())); + + // SAFETY: The address to the link is stable as it is hidden behind + // `link_ptr`, and `link_ptr` shadows the original making it unmovable. + self.wait_queue + .push(unsafe { Pin::new_unchecked(link_ref) }); + } + + Poll::Pending + }) + }) + .await; + + // Make sure the link is removed from the queue. + drop(dropper); + + // SAFETY: One only gets here if there is exlusive access. + ExclusiveAccess { + arbiter: self, + inner: unsafe { &mut *self.inner.get() }, + } + } + + /// Non-blockingly tries to access the underlying value. + /// If someone is in queue to get it, this will return `None`. + pub fn try_access(&self) -> Option> { + critical_section::with(|_| { + fence(Ordering::SeqCst); + + // The queue is empty and noone has taken the value. + if self.wait_queue.is_empty() && !self.taken.load(Ordering::Relaxed) { + self.taken.store(true, Ordering::Relaxed); + + // SAFETY: One only gets here if there is exlusive access. + Some(ExclusiveAccess { + arbiter: self, + inner: unsafe { &mut *self.inner.get() }, + }) + } else { + None + } + }) + } +} + +/// This token represents exclusive access to the value protected by the `Arbiter`. +pub struct ExclusiveAccess<'a, T> { + arbiter: &'a Arbiter, + inner: &'a mut T, +} + +impl<'a, T> Drop for ExclusiveAccess<'a, T> { + fn drop(&mut self) { + critical_section::with(|_| { + fence(Ordering::SeqCst); + + if self.arbiter.wait_queue.is_empty() { + // If noone is in queue and we release exclusive access, reset `taken`. + self.arbiter.taken.store(false, Ordering::Relaxed); + } else if let Some(next) = self.arbiter.wait_queue.pop() { + // Wake the next one in queue. + next.wake(); + } + }) + } +} + +impl<'a, T> Deref for ExclusiveAccess<'a, T> { + type Target = T; + + fn deref(&self) -> &Self::Target { + self.inner + } +} + +impl<'a, T> DerefMut for ExclusiveAccess<'a, T> { + fn deref_mut(&mut self) -> &mut Self::Target { + self.inner + } +} + +#[cfg(test)] +#[macro_use] +extern crate std; + +#[cfg(test)] +mod tests { + // use super::*; +} diff --git a/rtic-channel/src/lib.rs b/rtic-channel/src/lib.rs index 2b237f669f..6f816b5755 100644 --- a/rtic-channel/src/lib.rs +++ b/rtic-channel/src/lib.rs @@ -14,8 +14,11 @@ use core::{ task::{Poll, Waker}, }; use heapless::Deque; -use rtic_common::wait_queue::{Link, WaitQueue}; use rtic_common::waker_registration::CriticalSectionWakerRegistration as WakerRegistration; +use rtic_common::{ + dropper::OnDrop, + wait_queue::{Link, WaitQueue}, +}; /// An MPSC channel for use in no-alloc systems. `N` sets the size of the queue. /// @@ -417,29 +420,6 @@ impl<'a, T, const N: usize> Drop for Receiver<'a, T, N> { } } -struct OnDrop { - f: core::mem::MaybeUninit, -} - -impl OnDrop { - pub fn new(f: F) -> Self { - Self { - f: core::mem::MaybeUninit::new(f), - } - } - - #[allow(unused)] - pub fn defuse(self) { - core::mem::forget(self) - } -} - -impl Drop for OnDrop { - fn drop(&mut self) { - unsafe { self.f.as_ptr().read()() } - } -} - #[cfg(test)] #[macro_use] extern crate std; diff --git a/rtic-common/src/dropper.rs b/rtic-common/src/dropper.rs new file mode 100644 index 0000000000..a4b4d15927 --- /dev/null +++ b/rtic-common/src/dropper.rs @@ -0,0 +1,26 @@ +//! A drop implementation runner. + +/// Runs a closure on drop. +pub struct OnDrop { + f: core::mem::MaybeUninit, +} + +impl OnDrop { + /// Make a new droppper given a closure. + pub fn new(f: F) -> Self { + Self { + f: core::mem::MaybeUninit::new(f), + } + } + + /// Make it not run drop. + pub fn defuse(self) { + core::mem::forget(self) + } +} + +impl Drop for OnDrop { + fn drop(&mut self) { + unsafe { self.f.as_ptr().read()() } + } +} diff --git a/rtic-common/src/lib.rs b/rtic-common/src/lib.rs index 3c75856c8a..b8b5e0d931 100644 --- a/rtic-common/src/lib.rs +++ b/rtic-common/src/lib.rs @@ -4,5 +4,6 @@ #![deny(missing_docs)] //deny_warnings_placeholder_for_ci +pub mod dropper; pub mod wait_queue; pub mod waker_registration; From f2e0cd342ee11ab1a2e480b67a1a91d3b277932b Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Mon, 30 Jan 2023 21:24:12 +0100 Subject: [PATCH 284/423] Added testing to rtic-arbiter --- .github/workflows/build.yml | 93 +++++++++++++++++++++++++++++++++++++ rtic-arbiter/src/lib.rs | 25 +++++++++- 2 files changed, 117 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 650fc533da..3ef7d52e18 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -29,6 +29,21 @@ jobs: working-directory: ./rtic run: cargo fmt --all -- --check + stylearbiter: + name: cargo fmt rtic-arbiter + runs-on: ubuntu-22.04 + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Fail on warnings + working-directory: ./rtic-arbiter + run: sed -i 's,//deny_warnings_placeholder_for_ci,#![deny(warnings)],' src/lib.rs + + - name: cargo fmt --check + working-directory: ./rtic-arbiter + run: cargo fmt --all -- --check + stylechannel: name: cargo fmt rtic-channel runs-on: ubuntu-22.04 @@ -112,6 +127,43 @@ jobs: working-directory: ./rtic run: cargo check --target=${{ matrix.target }} + # Compilation check + checkarbiter: + name: check rtic-arbiter + runs-on: ubuntu-22.04 + strategy: + matrix: + target: + - thumbv7m-none-eabi + - thumbv6m-none-eabi + - x86_64-unknown-linux-gnu + toolchain: + - nightly + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Install Rust ${{ matrix.toolchain }} + working-directory: ./rtic-arbiter + run: | + rustup set profile minimal + rustup override set ${{ matrix.toolchain }} + + - name: Configure Rust target (${{ matrix.target }}) + working-directory: ./rtic-arbiter + run: rustup target add ${{ matrix.target }} + + - name: Fail on warnings + working-directory: ./rtic-arbiter + run: sed -i 's,//deny_warnings_placeholder_for_ci,#![deny(warnings)],' src/lib.rs + + - name: Cache Dependencies + uses: Swatinem/rust-cache@v2 + + - name: cargo check + working-directory: ./rtic-arbiter + run: cargo check --target=${{ matrix.target }} + # Compilation check checkchannel: name: check rtic-channel @@ -246,6 +298,28 @@ jobs: working-directory: ./rtic run: cargo clippy + clippyarbiter: + name: Cargo clippy rtic-arbiter + runs-on: ubuntu-22.04 + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Fail on warnings + working-directory: ./rtic-arbiter + run: sed -i 's,//deny_warnings_placeholder_for_ci,#![deny(warnings)],' src/lib.rs + + - name: Add Rust component clippy + working-directory: ./rtic-arbiter + run: rustup component add clippy + + - name: Cache Dependencies + uses: Swatinem/rust-cache@v2 + + - name: cargo clippy + working-directory: ./rtic-arbiter + run: cargo clippy + clippychannel: name: Cargo clippy rtic-channel runs-on: ubuntu-22.04 @@ -465,6 +539,25 @@ jobs: working-directory: ./rtic run: cargo test --test tests + # Run test suite + testsarbiter: + name: tests rtic-arbiter + runs-on: ubuntu-22.04 + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Cache Dependencies + uses: Swatinem/rust-cache@v2 + + - name: Fail on warnings + working-directory: ./rtic-arbiter + run: sed -i 's,//deny_warnings_placeholder_for_ci,#![deny(warnings)],' src/lib.rs + + - name: Run cargo test + working-directory: ./rtic-arbiter + run: cargo test --features testing + # Run test suite testschannel: name: tests rtic-channel diff --git a/rtic-arbiter/src/lib.rs b/rtic-arbiter/src/lib.rs index 487c64cecd..519ce2cd34 100644 --- a/rtic-arbiter/src/lib.rs +++ b/rtic-arbiter/src/lib.rs @@ -36,6 +36,9 @@ pub struct Arbiter { taken: AtomicBool, } +unsafe impl Send for Arbiter {} +unsafe impl Sync for Arbiter {} + impl Arbiter { /// Create a new arbiter. pub const fn new(inner: T) -> Self { @@ -171,5 +174,25 @@ extern crate std; #[cfg(test)] mod tests { - // use super::*; + use super::*; + + #[tokio::test] + async fn stress_channel() { + const NUM_RUNS: usize = 100_000; + + static ARB: Arbiter = Arbiter::new(0); + let mut v = std::vec::Vec::new(); + + for _ in 0..NUM_RUNS { + v.push(tokio::spawn(async move { + *ARB.access().await += 1; + })); + } + + for v in v { + v.await.unwrap(); + } + + assert_eq!(*ARB.access().await, NUM_RUNS) + } } From 8f38470a44407dd40e64e3f1cd193ff4f6d769c0 Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Tue, 31 Jan 2023 14:54:31 +0100 Subject: [PATCH 285/423] rtic-channel: try_* APIs now error if Sender/Receiver does not exist --- rtic-channel/src/lib.rs | 96 +++++++++++++++++++++++++++++------------ 1 file changed, 69 insertions(+), 27 deletions(-) diff --git a/rtic-channel/src/lib.rs b/rtic-channel/src/lib.rs index 6f816b5755..3cee78be5c 100644 --- a/rtic-channel/src/lib.rs +++ b/rtic-channel/src/lib.rs @@ -119,6 +119,14 @@ macro_rules! make_channel { /// Error state for when the receiver has been dropped. pub struct NoReceiver(pub T); +/// Errors that 'try_send` can have. +pub enum TrySendError { + /// Error state for when the receiver has been dropped. + NoReceiver(T), + /// Error state when the queue is full. + Full(T), +} + impl core::fmt::Debug for NoReceiver where T: core::fmt::Debug, @@ -128,6 +136,32 @@ where } } +impl core::fmt::Debug for TrySendError +where + T: core::fmt::Debug, +{ + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + TrySendError::NoReceiver(v) => write!(f, "NoReceiver({:?})", v), + TrySendError::Full(v) => write!(f, "Full({:?})", v), + } + } +} + +impl PartialEq for TrySendError +where + T: PartialEq, +{ + fn eq(&self, other: &Self) -> bool { + match (self, other) { + (TrySendError::NoReceiver(v1), TrySendError::NoReceiver(v2)) => v1.eq(v2), + (TrySendError::NoReceiver(_), TrySendError::Full(_)) => false, + (TrySendError::Full(_), TrySendError::NoReceiver(_)) => false, + (TrySendError::Full(v1), TrySendError::Full(v2)) => v1.eq(v2), + } + } +} + /// A `Sender` can send to the channel and can be cloned. pub struct Sender<'a, T, const N: usize>(&'a Channel); @@ -178,18 +212,22 @@ impl<'a, T, const N: usize> Sender<'a, T, N> { } /// Try to send a value, non-blocking. If the channel is full this will return an error. - /// Note; this does not check if the channel is closed. - pub fn try_send(&mut self, val: T) -> Result<(), T> { + pub fn try_send(&mut self, val: T) -> Result<(), TrySendError> { // If the wait queue is not empty, we can't try to push into the queue. if !self.0.wait_queue.is_empty() { - return Err(val); + return Err(TrySendError::Full(val)); + } + + // No receiver available. + if self.is_closed() { + return Err(TrySendError::NoReceiver(val)); } let idx = if let Some(idx) = critical_section::with(|cs| self.0.access(cs).freeq.pop_front()) { idx } else { - return Err(val); + return Err(TrySendError::Full(val)); }; self.send_footer(idx, val); @@ -330,19 +368,18 @@ impl<'a, T, const N: usize> core::fmt::Debug for Receiver<'a, T, N> { } } -/// Error state for when all senders has been dropped. -pub struct NoSender; - -impl core::fmt::Debug for NoSender { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - write!(f, "NoSender") - } +/// Possible receive errors. +#[derive(Debug, PartialEq, Eq)] +pub enum ReceiveError { + /// Error state for when all senders has been dropped. + NoSender, + /// Error state for when the queue is empty. + Empty, } impl<'a, T, const N: usize> Receiver<'a, T, N> { /// Receives a value if there is one in the channel, non-blocking. - /// Note; this does not check if the channel is closed. - pub fn try_recv(&mut self) -> Option { + pub fn try_recv(&mut self) -> Result { // Try to get a ready slot. let ready_slot = critical_section::with(|cs| self.0.access(cs).readyq.pop_front()); @@ -363,15 +400,19 @@ impl<'a, T, const N: usize> Receiver<'a, T, N> { wait_head.wake(); } - Some(r) + Ok(r) } else { - None + if self.is_closed() { + Err(ReceiveError::NoSender) + } else { + Err(ReceiveError::Empty) + } } } /// Receives a value, waiting if the queue is empty. /// If all senders are dropped this will error with `NoSender`. - pub async fn recv(&mut self) -> Result { + pub async fn recv(&mut self) -> Result { // There was nothing in the queue, setup the waiting. poll_fn(|cx| { // Register waker. @@ -379,13 +420,14 @@ impl<'a, T, const N: usize> Receiver<'a, T, N> { self.0.receiver_waker.register(cx.waker()); // Try to dequeue. - if let Some(val) = self.try_recv() { - return Poll::Ready(Ok(val)); - } - - // If the queue is empty and there is no sender, return the error. - if self.is_closed() { - return Poll::Ready(Err(NoSender)); + match self.try_recv() { + Ok(val) => { + return Poll::Ready(Ok(val)); + } + Err(ReceiveError::NoSender) => { + return Poll::Ready(Err(ReceiveError::NoSender)); + } + _ => {} } Poll::Pending @@ -476,13 +518,13 @@ mod tests { s.try_send(i).unwrap(); } - assert_eq!(s.try_send(11), Err(11)); + assert_eq!(s.try_send(11), Err(TrySendError::Full(11))); for i in 0..10 { assert_eq!(r.try_recv().unwrap(), i); } - assert_eq!(r.try_recv(), None); + assert_eq!(r.try_recv(), Err(ReceiveError::Empty)); } #[test] @@ -493,7 +535,7 @@ mod tests { assert!(r.is_closed()); - assert_eq!(r.try_recv(), None); + assert_eq!(r.try_recv(), Err(ReceiveError::NoSender)); } #[test] @@ -504,7 +546,7 @@ mod tests { assert!(s.is_closed()); - assert_eq!(s.try_send(11), Ok(())); + assert_eq!(s.try_send(11), Err(TrySendError::NoReceiver(11))); } #[tokio::test] From 15d788b7fad0254ad3323962b8dd6753cf95796d Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Tue, 31 Jan 2023 15:55:14 +0100 Subject: [PATCH 286/423] Fix spelling error --- rtic-arbiter/src/lib.rs | 2 +- rtic-channel/src/lib.rs | 2 +- rtic-common/src/wait_queue.rs | 12 ++++++------ 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/rtic-arbiter/src/lib.rs b/rtic-arbiter/src/lib.rs index 519ce2cd34..09d1b2eea3 100644 --- a/rtic-arbiter/src/lib.rs +++ b/rtic-arbiter/src/lib.rs @@ -82,7 +82,7 @@ impl Arbiter { // which happens outside this `poll_fn`'s stack frame. let link = unsafe { link_ptr.get() }; if let Some(link) = link { - if link.is_poped() { + if link.is_popped() { return Poll::Ready(()); } } else { diff --git a/rtic-channel/src/lib.rs b/rtic-channel/src/lib.rs index 3cee78be5c..fef546b357 100644 --- a/rtic-channel/src/lib.rs +++ b/rtic-channel/src/lib.rs @@ -267,7 +267,7 @@ impl<'a, T, const N: usize> Sender<'a, T, N> { // which happens outside this `poll_fn`'s stack frame. let link = unsafe { link_ptr.get() }; if let Some(link) = link { - if !link.is_poped() { + if !link.is_popped() { return None; } else { // Fall through to dequeue diff --git a/rtic-common/src/wait_queue.rs b/rtic-common/src/wait_queue.rs index ba8ff5471a..4ced6ab9ae 100644 --- a/rtic-common/src/wait_queue.rs +++ b/rtic-common/src/wait_queue.rs @@ -58,7 +58,7 @@ impl LinkedList { // Clear the pointers in the node. head_ref.next.store(null_mut(), Self::R); head_ref.prev.store(null_mut(), Self::R); - head_ref.is_poped.store(true, Self::R); + head_ref.is_popped.store(true, Self::R); return Some(head_val); } @@ -102,7 +102,7 @@ pub struct Link { pub(crate) val: T, next: AtomicPtr>, prev: AtomicPtr>, - is_poped: AtomicBool, + is_popped: AtomicBool, _up: PhantomPinned, } @@ -115,14 +115,14 @@ impl Link { val, next: AtomicPtr::new(null_mut()), prev: AtomicPtr::new(null_mut()), - is_poped: AtomicBool::new(false), + is_popped: AtomicBool::new(false), _up: PhantomPinned, } } /// Return true if this link has been poped from the list. - pub fn is_poped(&self) -> bool { - self.is_poped.load(Self::R) + pub fn is_popped(&self) -> bool { + self.is_popped.load(Self::R) } /// Remove this link from a linked list. @@ -133,7 +133,7 @@ impl Link { let prev = self.prev.load(Self::R); let next = self.next.load(Self::R); - self.is_poped.store(true, Self::R); + self.is_popped.store(true, Self::R); match unsafe { (prev.as_ref(), next.as_ref()) } { (None, None) => { From d0c51269608c18a105fd010f070bd9af6f443c60 Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Tue, 31 Jan 2023 22:05:43 +0100 Subject: [PATCH 287/423] Cleanup common code and clippy fixes --- rtic-channel/src/lib.rs | 12 +++++------- rtic-time/Cargo.toml | 1 + rtic-time/src/lib.rs | 27 ++------------------------- 3 files changed, 8 insertions(+), 32 deletions(-) diff --git a/rtic-channel/src/lib.rs b/rtic-channel/src/lib.rs index fef546b357..47e4a77d02 100644 --- a/rtic-channel/src/lib.rs +++ b/rtic-channel/src/lib.rs @@ -142,8 +142,8 @@ where { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { match self { - TrySendError::NoReceiver(v) => write!(f, "NoReceiver({:?})", v), - TrySendError::Full(v) => write!(f, "Full({:?})", v), + TrySendError::NoReceiver(v) => write!(f, "NoReceiver({v:?})"), + TrySendError::Full(v) => write!(f, "Full({v:?})"), } } } @@ -401,12 +401,10 @@ impl<'a, T, const N: usize> Receiver<'a, T, N> { } Ok(r) + } else if self.is_closed() { + Err(ReceiveError::NoSender) } else { - if self.is_closed() { - Err(ReceiveError::NoSender) - } else { - Err(ReceiveError::Empty) - } + Err(ReceiveError::Empty) } } diff --git a/rtic-time/Cargo.toml b/rtic-time/Cargo.toml index ea0593968b..dbcf45444a 100644 --- a/rtic-time/Cargo.toml +++ b/rtic-time/Cargo.toml @@ -8,3 +8,4 @@ edition = "2021" [dependencies] critical-section = "1" futures-util = { version = "0.3.25", default-features = false } +rtic-common = { version = "1.0.0", path = "../rtic-common" } diff --git a/rtic-time/src/lib.rs b/rtic-time/src/lib.rs index 44fdbcecfc..5e4457cc0e 100644 --- a/rtic-time/src/lib.rs +++ b/rtic-time/src/lib.rs @@ -14,13 +14,13 @@ use futures_util::{ future::{select, Either}, pin_mut, }; +use linked_list::{Link, LinkedList}; pub use monotonic::Monotonic; +use rtic_common::dropper::OnDrop; mod linked_list; mod monotonic; -use linked_list::{Link, LinkedList}; - /// Holds a waker and at which time instant this waker shall be awoken. struct WaitingWaker { waker: Waker, @@ -264,26 +264,3 @@ impl TimerQueue { } } } - -struct OnDrop { - f: core::mem::MaybeUninit, -} - -impl OnDrop { - pub fn new(f: F) -> Self { - Self { - f: core::mem::MaybeUninit::new(f), - } - } - - #[allow(unused)] - pub fn defuse(self) { - core::mem::forget(self) - } -} - -impl Drop for OnDrop { - fn drop(&mut self) { - unsafe { self.f.as_ptr().read()() } - } -} From 1f51b10297e9cbb4797aa1ed8be6a2b84c9f2e07 Mon Sep 17 00:00:00 2001 From: Per Lindgren Date: Sat, 28 Jan 2023 21:57:43 +0100 Subject: [PATCH 288/423] Book: Major rework for RTIC v2 --- book/en/src/SUMMARY.md | 19 +- book/en/src/by-example.md | 12 +- book/en/src/by-example/app.md | 24 ++- book/en/src/by-example/app_idle.md | 29 ++- book/en/src/by-example/app_init.md | 25 +-- book/en/src/by-example/app_minimal.md | 16 +- book/en/src/by-example/app_priorities.md | 30 +--- book/en/src/by-example/app_task.md | 17 +- book/en/src/by-example/channel.md | 112 ++++++++++++ book/en/src/by-example/delay.md | 116 ++++++++++++ book/en/src/by-example/hardware_tasks.md | 30 +--- book/en/src/by-example/resources.md | 136 ++++++--------- book/en/src/by-example/software_tasks.md | 106 ++++++++--- book/en/src/by-example/starting_a_project.md | 2 + book/en/src/preface.md | 165 ++++++++++++++++-- book/en/src/rtic_vs.md | 31 ++++ rtic/Cargo.toml | 20 ++- rtic/ci/expected/async-channel-done.run | 9 + .../ci/expected/async-channel-no-receiver.run | 1 + rtic/ci/expected/async-channel-no-sender.run | 1 + rtic/ci/expected/async-channel-try.run | 2 + rtic/ci/expected/async-channel.run | 2 +- rtic/ci/expected/async-timeout.run | 19 +- rtic/ci/expected/common.run | 3 + ...essage_passing.run => spawn_arguments.run} | 0 rtic/ci/expected/spawn_err.run | 3 + rtic/ci/expected/spawn_loop.run | 7 + rtic/examples/async-channel-done.rs | 65 +++++++ rtic/examples/async-channel-no-receiver.rs | 40 +++++ rtic/examples/async-channel-no-sender.rs | 40 +++++ rtic/examples/async-channel-try.rs | 48 +++++ rtic/examples/async-channel.rs | 15 +- rtic/examples/async-delay.rs | 2 + rtic/examples/async-timeout.rs | 87 +++++++++ rtic/examples/common.rs | 86 +++++++++ rtic/examples/lock-free.rs | 50 ++++++ ...{message_passing.rs => spawn_arguments.rs} | 0 rtic/examples/spawn_err.rs | 40 +++++ rtic/examples/spawn_loop.rs | 42 +++++ rtic/examples/zero-prio-task_err.no_rs | 66 +++++++ 40 files changed, 1267 insertions(+), 251 deletions(-) create mode 100644 book/en/src/by-example/channel.md create mode 100644 book/en/src/by-example/delay.md create mode 100644 book/en/src/rtic_vs.md create mode 100644 rtic/ci/expected/async-channel-done.run create mode 100644 rtic/ci/expected/async-channel-no-receiver.run create mode 100644 rtic/ci/expected/async-channel-no-sender.run create mode 100644 rtic/ci/expected/async-channel-try.run rename rtic/ci/expected/{message_passing.run => spawn_arguments.run} (100%) create mode 100644 rtic/ci/expected/spawn_err.run create mode 100644 rtic/ci/expected/spawn_loop.run create mode 100644 rtic/examples/async-channel-done.rs create mode 100644 rtic/examples/async-channel-no-receiver.rs create mode 100644 rtic/examples/async-channel-no-sender.rs create mode 100644 rtic/examples/async-channel-try.rs create mode 100644 rtic/examples/async-timeout.rs create mode 100644 rtic/examples/common.rs create mode 100644 rtic/examples/lock-free.rs rename rtic/examples/{message_passing.rs => spawn_arguments.rs} (100%) create mode 100644 rtic/examples/spawn_err.rs create mode 100644 rtic/examples/spawn_loop.rs create mode 100644 rtic/examples/zero-prio-task_err.no_rs diff --git a/book/en/src/SUMMARY.md b/book/en/src/SUMMARY.md index 853f3a5319..407be6d5b8 100644 --- a/book/en/src/SUMMARY.md +++ b/book/en/src/SUMMARY.md @@ -4,15 +4,13 @@ - [RTIC by example](./by-example.md) - [The `app`](./by-example/app.md) + - [Hardware tasks & `pend`](./by-example/hardware_tasks.md) + - [Software tasks & `spawn`](./by-example/software_tasks.md) - [Resources](./by-example/resources.md) - [The init task](./by-example/app_init.md) - [The idle task](./by-example/app_idle.md) - - [Defining tasks](./by-example/app_task.md) - - [Hardware tasks](./by-example/hardware_tasks.md) - - [Software tasks & `spawn`](./by-example/software_tasks.md) - - [Message passing & `capacity`](./by-example/message_passing.md) - - [Task priorities](./by-example/app_priorities.md) - - [Monotonic & `spawn_{at/after}`](./by-example/monotonic.md) + - [Channel based communication](./by-example/channel.md) + - [Tasks with delay](./by-example/delay.md) - [Starting a new project](./by-example/starting_a_project.md) - [The minimal app](./by-example/app_minimal.md) - [Tips & Tricks](./by-example/tips.md) @@ -23,13 +21,13 @@ - [Inspecting generated code](./by-example/tips_view_code.md) - [Running tasks from RAM](./by-example/tips_from_ram.md) +- [RTIC vs. the world](./rtic_vs.md) - [Awesome RTIC examples](./awesome_rtic.md) - [Migration Guides](./migration.md) - [v0.5.x to v1.0.x](./migration/migration_v5.md) - [v0.4.x to v0.5.x](./migration/migration_v4.md) - [RTFM to RTIC](./migration/migration_rtic.md) - [Under the hood](./internals.md) - - [Cortex-M architectures](./internals/targets.md) @@ -38,3 +36,10 @@ + + + \ No newline at end of file diff --git a/book/en/src/by-example.md b/book/en/src/by-example.md index 419a4bae64..a2e5b27887 100644 --- a/book/en/src/by-example.md +++ b/book/en/src/by-example.md @@ -1,14 +1,15 @@ # RTIC by example -This part of the book introduces the Real-Time Interrupt-driven Concurrency (RTIC) framework -to new users by walking them through examples of increasing complexity. +This part of the book introduces the RTIC framework to new users by walking them through examples of increasing complexity. All examples in this part of the book are accessible at the [GitHub repository][repoexamples]. The examples are runnable on QEMU (emulating a Cortex M3 target), thus no special hardware required to follow along. -[repoexamples]: https://github.com/rtic-rs/cortex-m-rtic/tree/master/examples +[repoexamples]: https://github.com/rtic-rs/rtic/tree/master/examples + +## Running an example To run the examples with QEMU you will need the `qemu-system-arm` program. Check [the embedded Rust book] for instructions on how to set up an @@ -28,11 +29,12 @@ $ cargo run --target thumbv7m-none-eabi --example locals Yields this output: ``` console -{{#include ../../../ci/expected/locals.run}} +{{#include ../../../rtic/ci/expected/locals.run}} ``` > **NOTE**: You can choose target device by passing a target > triple to cargo (e.g. `cargo run --example init --target thumbv7m-none-eabi`) or > configure a default target in `.cargo/config.toml`. > -> For running the examples, we use a Cortex M3 emulated in QEMU, so the target is `thumbv7m-none-eabi`. \ No newline at end of file +> For running the examples, we (typically) use a Cortex M3 emulated in QEMU, so the target is `thumbv7m-none-eabi`. +> Since the M3 architecture is backwards compatible to the M0/M0+ architecture, you may also use the `thumbv6m-none-eabi`, in case you want to inspect generated assembly code for the M0/M0+ architecture. diff --git a/book/en/src/by-example/app.md b/book/en/src/by-example/app.md index 2c6aca7a2b..cef8288523 100644 --- a/book/en/src/by-example/app.md +++ b/book/en/src/by-example/app.md @@ -2,25 +2,31 @@ ## Requirements on the `app` attribute -All RTIC applications use the [`app`] attribute (`#[app(..)]`). This attribute -only applies to a `mod`-item containing the RTIC application. The `app` -attribute has a mandatory `device` argument that takes a *path* as a value. -This must be a full path pointing to a -*peripheral access crate* (PAC) generated using [`svd2rust`] **v0.14.x** or -newer. +All RTIC applications use the [`app`] attribute (`#[app(..)]`). This attribute only applies to a `mod`-item containing the RTIC application. The `app` attribute has a mandatory `device` argument that takes a *path* as a value. This must be a full path pointing to a *peripheral access crate* (PAC) generated using [`svd2rust`] **v0.14.x** or newer. -The `app` attribute will expand into a suitable entry point and thus replaces -the use of the [`cortex_m_rt::entry`] attribute. +The `app` attribute will expand into a suitable entry point and thus replaces the use of the [`cortex_m_rt::entry`] attribute. [`app`]: ../../../api/cortex_m_rtic_macros/attr.app.html [`svd2rust`]: https://crates.io/crates/svd2rust [`cortex_m_rt::entry`]: ../../../api/cortex_m_rt_macros/attr.entry.html +## Structure and zero-cost concurrency + +An RTIC `app` is an executable system model for since-core applications, declaring a set of `local` and `shared` resources operated on by a set of `init`, `idle`, *hardware* and *software* tasks. In short the `init` task runs before any other task returning the set of `local` and `shared` resources. Tasks run preemptively based on their associated static priority, `idle` has the lowest priority (and can be used for background work, and/or to put the system to sleep until woken by some event). Hardware tasks are bound to underlying hardware interrupts, while software tasks are scheduled by asynchronous executors (one for each software task priority). + +At compile time the task/resource model is analyzed under the Stack Resource Policy (SRP) and executable code generated with the following outstanding properties: + +- guaranteed race-free resource access and deadlock-free execution on a single-shared stack + - hardware task scheduling is performed directly by the hardware, and + - software task scheduling is performed by auto generated async executors tailored to the application. + +Overall, the generated code infers no additional overhead in comparison to a hand-written implementation, thus in Rust terms RTIC offers a zero-cost abstraction to concurrency. + ## An RTIC application example To give a flavour of RTIC, the following example contains commonly used features. In the following sections we will go through each feature in detail. ``` rust -{{#include ../../../../examples/common.rs}} +{{#include ../../../../rtic/examples/common.rs}} ``` diff --git a/book/en/src/by-example/app_idle.md b/book/en/src/by-example/app_idle.md index 537902a442..4856ee19a6 100644 --- a/book/en/src/by-example/app_idle.md +++ b/book/en/src/by-example/app_idle.md @@ -1,52 +1,47 @@ # The background task `#[idle]` -A function marked with the `idle` attribute can optionally appear in the -module. This becomes the special *idle task* and must have signature -`fn(idle::Context) -> !`. +A function marked with the `idle` attribute can optionally appear in the module. This becomes the special *idle task* and must have signature `fn(idle::Context) -> !`. -When present, the runtime will execute the `idle` task after `init`. Unlike -`init`, `idle` will run *with interrupts enabled* and must never return, -as the `-> !` function signature indicates. +When present, the runtime will execute the `idle` task after `init`. Unlike `init`, `idle` will run *with interrupts enabled* and must never return, as the `-> !` function signature indicates. [The Rust type `!` means “never”][nevertype]. [nevertype]: https://doc.rust-lang.org/core/primitive.never.html -Like in `init`, locally declared resources will have `'static` lifetimes that -are safe to access. +Like in `init`, locally declared resources will have `'static` lifetimes that are safe to access. The example below shows that `idle` runs after `init`. ``` rust -{{#include ../../../../examples/idle.rs}} +{{#include ../../../../rtic/examples/idle.rs}} ``` ``` console $ cargo run --target thumbv7m-none-eabi --example idle -{{#include ../../../../ci/expected/idle.run}} +{{#include ../../../../rtic/ci/expected/idle.run}} ``` By default, the RTIC `idle` task does not try to optimize for any specific targets. -A common useful optimization is to enable the [SLEEPONEXIT] and allow the MCU -to enter sleep when reaching `idle`. +A common useful optimization is to enable the [SLEEPONEXIT] and allow the MCU to enter sleep when reaching `idle`. ->**Caution** some hardware unless configured disables the debug unit during sleep mode. +>**Caution**: some hardware unless configured disables the debug unit during sleep mode. > >Consult your hardware specific documentation as this is outside the scope of RTIC. The following example shows how to enable sleep by setting the -[`SLEEPONEXIT`][SLEEPONEXIT] and providing a custom `idle` task replacing the -default [`nop()`][NOP] with [`wfi()`][WFI]. +[`SLEEPONEXIT`][SLEEPONEXIT] and providing a custom `idle` task replacing the default [`nop()`][NOP] with [`wfi()`][WFI]. [SLEEPONEXIT]: https://developer.arm.com/docs/100737/0100/power-management/sleep-mode/sleep-on-exit-bit [WFI]: https://developer.arm.com/documentation/dui0662/b/The-Cortex-M0--Instruction-Set/Miscellaneous-instructions/WFI [NOP]: https://developer.arm.com/documentation/dui0662/b/The-Cortex-M0--Instruction-Set/Miscellaneous-instructions/NOP ``` rust -{{#include ../../../../examples/idle-wfi.rs}} +{{#include ../../../../rtic/examples/idle-wfi.rs}} ``` ``` console $ cargo run --target thumbv7m-none-eabi --example idle-wfi -{{#include ../../../../ci/expected/idle-wfi.run}} +{{#include ../../../../rtic/ci/expected/idle-wfi.run}} ``` + +> **Notice**: The `idle` task cannot be used together with *software* tasks running at priority zero. The reason is that `idle` is running as a non-returning Rust function at priority zero. Thus there would be no way for an executor at priority zero to give control to *software* tasks at the same priority. diff --git a/book/en/src/by-example/app_init.md b/book/en/src/by-example/app_init.md index 5bf6200e1c..62fb55b837 100644 --- a/book/en/src/by-example/app_init.md +++ b/book/en/src/by-example/app_init.md @@ -1,35 +1,28 @@ # App initialization and the `#[init]` task An RTIC application requires an `init` task setting up the system. The corresponding `init` function must have the -signature `fn(init::Context) -> (Shared, Local, init::Monotonics)`, where `Shared` and `Local` are resource +signature `fn(init::Context) -> (Shared, Local)`, where `Shared` and `Local` are the resource structures defined by the user. -The `init` task executes after system reset, [after an optionally defined `pre-init` code section][pre-init] and an always occurring internal RTIC -initialization. - +The `init` task executes after system reset (after the optionally defined [pre-init] and internal RTIC +initialization). The `init` task runs *with interrupts disabled* and has exclusive access to Cortex-M (the +`bare_metal::CriticalSection` token is available as `cs`) while device specific peripherals are available through +the `core` and `device` fields of `init::Context`. [pre-init]: https://docs.rs/cortex-m-rt/latest/cortex_m_rt/attr.pre_init.html - -The `init` and optional `pre-init` tasks runs *with interrupts disabled* and have exclusive access to Cortex-M (the -`bare_metal::CriticalSection` token is available as `cs`). - -Device specific peripherals are available through the `core` and `device` fields of `init::Context`. - ## Example -The example below shows the types of the `core`, `device` and `cs` fields, and showcases the use of a `local` -variable with `'static` lifetime. -Such variables can be delegated from the `init` task to other tasks of the RTIC application. +The example below shows the types of the `core`, `device` and `cs` fields, and showcases the use of a `local` variable with `'static` lifetime. Such variables can be delegated from the `init` task to other tasks of the RTIC application. -The `device` field is only available when the `peripherals` argument is set to the default value `true`. +The `device` field is available when the `peripherals` argument is set to the default value `true`. In the rare case you want to implement an ultra-slim application you can explicitly set `peripherals` to `false`. ``` rust -{{#include ../../../../examples/init.rs}} +{{#include ../../../../rtic/examples/init.rs}} ``` Running the example will print `init` to the console and then exit the QEMU process. ``` console $ cargo run --target thumbv7m-none-eabi --example init -{{#include ../../../../ci/expected/init.run}} +{{#include ../../../../rtic/ci/expected/init.run}} ``` diff --git a/book/en/src/by-example/app_minimal.md b/book/en/src/by-example/app_minimal.md index d0ff40a303..f241089061 100644 --- a/book/en/src/by-example/app_minimal.md +++ b/book/en/src/by-example/app_minimal.md @@ -3,5 +3,19 @@ This is the smallest possible RTIC application: ``` rust -{{#include ../../../../examples/smallest.rs}} +{{#include ../../../../rtic/examples/smallest.rs}} ``` + +RTIC is designed with resource efficiency in mind. RTIC itself does not rely on any dynamic memory allocation, thus RAM requirement is dependent only on the application. The flash memory footprint is below 1kB including the interrupt vector table. + +For a minimal example you can expect something like: +``` console +$ cargo size --example smallest --target thumbv7m-none-eabi --release +Finished release [optimized] target(s) in 0.07s + text data bss dec hex filename + 924 0 0 924 39c smallest +``` + + diff --git a/book/en/src/by-example/app_priorities.md b/book/en/src/by-example/app_priorities.md index 8cee7499e1..f03ebf7390 100644 --- a/book/en/src/by-example/app_priorities.md +++ b/book/en/src/by-example/app_priorities.md @@ -4,23 +4,18 @@ The `priority` argument declares the static priority of each `task`. -For Cortex-M, tasks can have priorities in the range `1..=(1 << NVIC_PRIO_BITS)` -where `NVIC_PRIO_BITS` is a constant defined in the `device` crate. +For Cortex-M, tasks can have priorities in the range `1..=(1 << NVIC_PRIO_BITS)` where `NVIC_PRIO_BITS` is a constant defined in the `device` crate. -Omitting the `priority` argument the task priority defaults to `1`. -The `idle` task has a non-configurable static priority of `0`, the lowest priority. +Omitting the `priority` argument the task priority defaults to `1`. The `idle` task has a non-configurable static priority of `0`, the lowest priority. > A higher number means a higher priority in RTIC, which is the opposite from what > Cortex-M does in the NVIC peripheral. > Explicitly, this means that number `10` has a **higher** priority than number `9`. -The highest static priority task takes precedence when more than one -task are ready to execute. +The highest static priority task takes precedence when more than one task are ready to execute. The following scenario demonstrates task prioritization: -Spawning a higher priority task A during execution of a lower priority task B suspends -task B. Task A has higher priority thus preempting task B which gets suspended -until task A completes execution. Thus, when task A completes task B resumes execution. +Spawning a higher priority task A during execution of a lower priority task B pends task A. Task A has higher priority thus preempting task B which gets suspended until task A completes execution. Thus, when task A completes task B resumes execution. ```text Task Priority @@ -39,23 +34,17 @@ Task Priority The following example showcases the priority based scheduling of tasks: ``` rust -{{#include ../../../../examples/preempt.rs}} +{{#include ../../../../rtic/examples/preempt.rs}} ``` ``` console $ cargo run --target thumbv7m-none-eabi --example preempt -{{#include ../../../../ci/expected/preempt.run}} +{{#include ../../../../rtic/ci/expected/preempt.run}} ``` -Note that the task `bar` does *not* preempt task `baz` because its priority -is the *same* as `baz`'s. The higher priority task `bar` runs before `foo` -when `baz`returns. When `bar` returns `foo` can resume. +Note that the task `bar` does *not* preempt task `baz` because its priority is the *same* as `baz`'s. The higher priority task `bar` runs before `foo` when `baz`returns. When `bar` returns `foo` can resume. -One more note about priorities: choosing a priority higher than what the device -supports will result in a compilation error. - -The error is cryptic due to limitations in the Rust language -if `priority = 9` for task `uart0_interrupt` in `example/common.rs` this looks like: +One more note about priorities: choosing a priority higher than what the device supports will result in a compilation error. The error is cryptic due to limitations in the Rust language, if `priority = 9` for task `uart0_interrupt` in `example/common.rs` this looks like: ```text error[E0080]: evaluation of constant value failed @@ -68,5 +57,4 @@ if `priority = 9` for task `uart0_interrupt` in `example/common.rs` this looks l ``` -The error message incorrectly points to the starting point of the macro, but at least the -value subtracted (in this case 9) will suggest which task causes the error. +The error message incorrectly points to the starting point of the macro, but at least the value subtracted (in this case 9) will suggest which task causes the error. diff --git a/book/en/src/by-example/app_task.md b/book/en/src/by-example/app_task.md index d83f1ff15a..e0c67ad2c2 100644 --- a/book/en/src/by-example/app_task.md +++ b/book/en/src/by-example/app_task.md @@ -1,21 +1,18 @@ + + # Defining tasks with `#[task]` Tasks, defined with `#[task]`, are the main mechanism of getting work done in RTIC. Tasks can -* Be spawned (now or in the future, also by themselves) -* Receive messages (passing messages between tasks) -* Be prioritized, allowing preemptive multitasking +* Be spawned (now or in the future) +* Receive messages (message passing) +* Prioritized allowing preemptive multitasking * Optionally bind to a hardware interrupt -RTIC makes a distinction between “software tasks” and “hardware tasks”. +RTIC makes a distinction between “software tasks” and “hardware tasks”. Hardware tasks are tasks that are bound to a specific interrupt vector in the MCU while software tasks are not. -*Hardware tasks* are tasks that are bound to a specific interrupt vector in the MCU while software tasks are not. - -This means that if a hardware task is bound to, lets say, a UART RX interrupt, the task will be run every -time that interrupt triggers, usually when a character is received. - -*Software tasks* are explicitly spawned in a task, either immediately or using the Monotonic timer mechanism. +This means that if a hardware task is bound to an UART RX interrupt the task will run every time this interrupt triggers, usually when a character is received. In the coming pages we will explore both tasks and the different options available. diff --git a/book/en/src/by-example/channel.md b/book/en/src/by-example/channel.md new file mode 100644 index 0000000000..99bfedda3c --- /dev/null +++ b/book/en/src/by-example/channel.md @@ -0,0 +1,112 @@ +# Communication over channels. + +Channels can be used to communicate data between running *software* tasks. The channel is essentially a wait queue, allowing tasks with multiple producers and a single receiver. A channel is constructed in the `init` task and backed by statically allocated memory. Send and receive endpoints are distributed to *software* tasks: + +```rust +... +const CAPACITY: usize = 5; +#[init] + fn init(_: init::Context) -> (Shared, Local) { + let (s, r) = make_channel!(u32, CAPACITY); + receiver::spawn(r).unwrap(); + sender1::spawn(s.clone()).unwrap(); + sender2::spawn(s.clone()).unwrap(); + ... +``` + +In this case the channel holds data of `u32` type with a capacity of 5 elements. + +## Sending data + +The `send` method post a message on the channel as shown below: + +```rust +#[task] +async fn sender1(_c: sender1::Context, mut sender: Sender<'static, u32, CAPACITY>) { + hprintln!("Sender 1 sending: 1"); + sender.send(1).await.unwrap(); +} +``` + +## Receiving data + +The receiver can `await` incoming messages: + +```rust +#[task] +async fn receiver(_c: receiver::Context, mut receiver: Receiver<'static, u32, CAPACITY>) { + while let Ok(val) = receiver.recv().await { + hprintln!("Receiver got: {}", val); + ... + } +} +``` + +For a complete example: + +``` rust +{{#include ../../../../rtic/examples/async-channel.rs}} +``` + +``` console +$ cargo run --target thumbv7m-none-eabi --example async-channel --features test-critical-section +{{#include ../../../../rtic/ci/expected/async-channel.run}} +``` + +Also sender endpoint can be awaited. In case there the channel capacity has not been reached, `await` the sender can progress immediately, while in the case the capacity is reached, the sender is blocked until there is free space in the queue. In this way data is never lost. + +In the below example the `CAPACITY` has been reduced to 1, forcing sender tasks to wait until the data in the channel has been received. + +``` rust +{{#include ../../../../rtic/examples/async-channel-done.rs}} +``` + +Looking at the output, we find that `Sender 2` will wait until the data sent by `Sender 1` as been received. + +> **NOTICE** *Software* tasks at the same priority are executed asynchronously to each other, thus **NO** strict order can be assumed. (The presented order here applies only to the current implementation, and may change between RTIC framework releases.) + +``` console +$ cargo run --target thumbv7m-none-eabi --example async-channel-done --features test-critical-section +{{#include ../../../../rtic/ci/expected/async-channel-done.run}} +``` + +## Error handling + +In case all senders have been dropped `await` on an empty receiver channel results in an error. This allows to gracefully implement different types of shutdown operations. + +``` rust +{{#include ../../../../rtic/examples/async-channel-no-sender.rs}} +``` + +``` console +$ cargo run --target thumbv7m-none-eabi --example async-channel-no-sender --features test-critical-section +{{#include ../../../../rtic/ci/expected/async-channel-no-sender.run}} +``` + +Similarly, `await` on a send channel results in an error in case the receiver has been dropped. This allows to gracefully implement application level error handling. + +The resulting error returns the data back to the sender, allowing the sender to take appropriate action (e.g., storing the data to later retry sending it). + +``` rust +{{#include ../../../../rtic/examples/async-channel-no-receiver.rs}} +``` + +``` console +$ cargo run --target thumbv7m-none-eabi --example async-channel-no-receiver --features test-critical-section +{{#include ../../../../rtic/ci/expected/async-channel-no-receiver.run}} +``` + + + +## Try API + +In cases you wish the sender to proceed even in case the channel is full. To that end, a `try_send` API is provided. + +``` rust +{{#include ../../../../rtic/examples/async-channel-try.rs}} +``` + +``` console +$ cargo run --target thumbv7m-none-eabi --example async-channel-try --features test-critical-section +{{#include ../../../../rtic/ci/expected/async-channel-try.run}} +``` \ No newline at end of file diff --git a/book/en/src/by-example/delay.md b/book/en/src/by-example/delay.md new file mode 100644 index 0000000000..d35d9da453 --- /dev/null +++ b/book/en/src/by-example/delay.md @@ -0,0 +1,116 @@ +# Tasks with delay + +A convenient way to express *miniminal* timing requirements is by means of delaying progression. + +This can be achieved by instantiating a monotonic timer: + +```rust +... +rtic_monotonics::make_systick_timer_queue!(TIMER); + +#[init] +fn init(cx: init::Context) -> (Shared, Local) { + let systick = Systick::start(cx.core.SYST, 12_000_000); + TIMER.initialize(systick); + ... +``` + +A *software* task can `await` the delay to expire: + +```rust +#[task] +async fn foo(_cx: foo::Context) { + ... + TIMER.delay(100.millis()).await; + ... +``` + +Technically, the timer queue is implemented as a list based priority queue, where list-nodes are statically allocated as part of the underlying task `Future`. Thus, the timer queue is infallible at run-time (its size and allocation is determined at compile time). + +For a complete example: + +``` rust +{{#include ../../../../rtic/examples/async-delay.rs}} +``` + +``` console +$ cargo run --target thumbv7m-none-eabi --example async-delay --features test-critical-section +{{#include ../../../../rtic/ci/expected/async-delay.run}} +``` + +## Timeout + +Rust `Futures` (underlying Rust `async`/`await`) are composable. This makes it possible to `select` in between `Futures` that have completed. + +A common use case is transactions with associated timeout. In the examples shown below, we introduce a fake HAL device which performs some transaction. We have modelled the time it takes based on the input parameter (`n`) as `350ms + n * 100ms)`. + +Using the `select_biased` macro from the `futures` crate it may look like this: + +```rust +// Call hal with short relative timeout using `select_biased` +select_biased! { + v = hal_get(&TIMER, 1).fuse() => hprintln!("hal returned {}", v), + _ = TIMER.delay(200.millis()).fuse() => hprintln!("timeout", ), // this will finish first +} +``` + +Assuming the `hal_get` will take 450ms to finish, a short timeout of 200ms will expire. + +```rust +// Call hal with long relative timeout using `select_biased` +select_biased! { + v = hal_get(&TIMER, 1).fuse() => hprintln!("hal returned {}", v), // hal finish first + _ = TIMER.delay(1000.millis()).fuse() => hprintln!("timeout", ), +} +``` + +By extending the timeout to 1000ms, the `hal_get` will finish first. + +Using `select_biased` any number of futures can be combined, so its very powerful. However, as the timeout pattern is frequently used, it is directly supported by the RTIC [rtc-monotonics] and [rtic-time] crates. The second example from above using `timeout_after`: + +```rust +// Call hal with long relative timeout using monotonic `timeout_after` +match TIMER.timeout_after(1000.millis(), hal_get(&TIMER, 1)).await { + Ok(v) => hprintln!("hal returned {}", v), + _ => hprintln!("timeout"), +} +``` + +In cases you want exact control over time without drift. For this purpose we can use exact points in time using `Instance`, and spans of time using `Duration`. Operations on the `Instance` and `Duration` types are given by the [fugit] crate. + +[fugit]: https://crates.io/crates/fugit + +```rust +// get the current time instance +let mut instant = TIMER.now(); + +// do this 3 times +for n in 0..3 { + // exact point in time without drift + instant += 1000.millis(); + TIMER.delay_until(instant).await; + + // exact point it time for timeout + let timeout = instant + 500.millis(); + hprintln!("now is {:?}, timeout at {:?}", TIMER.now(), timeout); + + match TIMER.timeout_at(timeout, hal_get(&TIMER, n)).await { + Ok(v) => hprintln!("hal returned {} at time {:?}", v, TIMER.now()), + _ => hprintln!("timeout"), + } +} +``` + +`instant = TIMER.now()` gives the baseline (i.e., the exact current point in time). We want to call `hal_get` after 1000ms relative to this exact point in time. This can be accomplished by `TIMER.delay_until(instant).await;`. We define the absolute point in time for the `timeout`, and call `TIMER.timeout_at(timeout, hal_get(&TIMER, n)).await`. For the first loop iteration `n == 0`, and the `hal_get` will take 350ms (and finishes before the timeout). For the second iteration `n == 1`, and `hal_get` will take 450ms (and again succeeds to finish before the timeout). For the third iteration `n == 2` (`hal_get` will take 5500ms to finish). In this case we will run into a timeout. + + +The complete example: + +``` rust +{{#include ../../../../rtic/examples/async-timeout.rs}} +``` + +``` console +$ cargo run --target thumbv7m-none-eabi --example async-timeout --features test-critical-section +{{#include ../../../../rtic/ci/expected/async-timeout.run}} +``` diff --git a/book/en/src/by-example/hardware_tasks.md b/book/en/src/by-example/hardware_tasks.md index 2d405d324d..e3e51acc59 100644 --- a/book/en/src/by-example/hardware_tasks.md +++ b/book/en/src/by-example/hardware_tasks.md @@ -1,39 +1,27 @@ # Hardware tasks -At its core RTIC is using a hardware interrupt controller ([ARM NVIC on cortex-m][NVIC]) -to schedule and start execution of tasks. All tasks except `pre-init`, `#[init]` and `#[idle]` -run as interrupt handlers. +At its core RTIC is using the hardware interrupt controller ([ARM NVIC on cortex-m][NVIC]) to perform scheduling and executing tasks, and all (*hardware*) tasks except `#[init]` and `#[idle]` run as interrupt handlers. This also means that you can manually bind tasks to interrupt handlers. -Hardware tasks are explicitly bound to interrupt handlers. +To bind an interrupt use the `#[task]` attribute argument `binds = InterruptName`. This task becomes the interrupt handler for this hardware interrupt vector. -To bind a task to an interrupt, use the `#[task]` attribute argument `binds = InterruptName`. -This task then becomes the interrupt handler for this hardware interrupt vector. +All tasks bound to an explicit interrupt are *hardware tasks* since they start execution in reaction to a hardware event (interrupt). -All tasks bound to an explicit interrupt are called *hardware tasks* since they -start execution in reaction to a hardware event. +Specifying a non-existing interrupt name will cause a compilation error. The interrupt names are commonly defined by [PAC or HAL][pacorhal] crates. -Specifying a non-existing interrupt name will cause a compilation error. The interrupt names -are commonly defined by [PAC or HAL][pacorhal] crates. +Any available interrupt vector should work, but different hardware might have added special properties to select interrupt priority levels, such as the [nRF “softdevice”](https://github.com/rtic-rs/cortex-m-rtic/issues/434). -Any available interrupt vector should work. Specific devices may bind -specific interrupt priorities to specific interrupt vectors outside -user code control. See for example the -[nRF “softdevice”](https://github.com/rtic-rs/cortex-m-rtic/issues/434). - -Beware of using interrupt vectors that are used internally by hardware features; -RTIC is unaware of such hardware specific details. +Beware of re-purposing interrupt vectors used internally by hardware features, RTIC is unaware of such hardware specific details. [pacorhal]: https://docs.rust-embedded.org/book/start/registers.html [NVIC]: https://developer.arm.com/documentation/100166/0001/Nested-Vectored-Interrupt-Controller/NVIC-functional-description/NVIC-interrupts -The example below demonstrates the use of the `#[task(binds = InterruptName)]` attribute to declare a -hardware task bound to an interrupt handler. +The example below demonstrates the use of the `#[task(binds = InterruptName)]` attribute to declare a hardware task bound to an interrupt handler. In the example the interrupt triggering task execution is manually pended (`rtic::pend(Interrupt::UART0)`). However, in the typical case, interrupts are pended by the hardware peripheral. RTIC does not interfere with mechanisms for clearing peripheral interrupts, so any hardware specific implementation is completely up to the implementer. ``` rust -{{#include ../../../../examples/hardware.rs}} +{{#include ../../../../rtic/examples/hardware.rs}} ``` ``` console $ cargo run --target thumbv7m-none-eabi --example hardware -{{#include ../../../../ci/expected/hardware.run}} +{{#include ../../../../rtic/ci/expected/hardware.run}} ``` diff --git a/book/en/src/by-example/resources.md b/book/en/src/by-example/resources.md index 30089d34a2..ea67b2661b 100644 --- a/book/en/src/by-example/resources.md +++ b/book/en/src/by-example/resources.md @@ -1,176 +1,138 @@ # Resource usage -The RTIC framework manages shared and task local resources allowing persistent data -storage and safe accesses without the use of `unsafe` code. +The RTIC framework manages shared and task local resources allowing persistent data storage and safe accesses without the use of `unsafe` code. -RTIC resources are visible only to functions declared within the `#[app]` module and the framework -gives the user complete control (on a per-task basis) over resource accessibility. +RTIC resources are visible only to functions declared within the `#[app]` module and the framework gives the user complete control (on a per-task basis) over resource accessibility. -Declaration of system-wide resources is done by annotating **two** `struct`s within the `#[app]` module -with the attribute `#[local]` and `#[shared]`. -Each field in these structures corresponds to a different resource (identified by field name). -The difference between these two sets of resources will be covered below. +Declaration of system-wide resources is done by annotating **two** `struct`s within the `#[app]` module with the attribute `#[local]` and `#[shared]`. Each field in these structures corresponds to a different resource (identified by field name). The difference between these two sets of resources will be covered below. -Each task must declare the resources it intends to access in its corresponding metadata attribute -using the `local` and `shared` arguments. Each argument takes a list of resource identifiers. -The listed resources are made available to the context under the `local` and `shared` fields of the -`Context` structure. +Each task must declare the resources it intends to access in its corresponding metadata attribute using the `local` and `shared` arguments. Each argument takes a list of resource identifiers. The listed resources are made available to the context under the `local` and `shared` fields of the `Context` structure. -The `init` task returns the initial values for the system-wide (`#[shared]` and `#[local]`) -resources, and the set of initialized timers used by the application. The monotonic timers will be -further discussed in [Monotonic & `spawn_{at/after}`](./monotonic.md). +The `init` task returns the initial values for the system-wide (`#[shared]` and `#[local]`) resources. + + ## `#[local]` resources -`#[local]` resources are locally accessible to a specific task, meaning that only that task can -access the resource and does so without locks or critical sections. This allows for the resources, -commonly drivers or large objects, to be initialized in `#[init]` and then be passed to a specific -task. +`#[local]` resources accessible only to a single task. This task is given unique access to the resource without the use of locks or critical sections. -Thus, a task `#[local]` resource can only be accessed by one singular task. -Attempting to assign the same `#[local]` resource to more than one task is a compile-time error. +This allows for the resources, commonly drivers or large objects, to be initialized in `#[init]` and then be passed to a specific task. (Thus, a task `#[local]` resource can only be accessed by one single task.) Attempting to assign the same `#[local]` resource to more than one task is a compile-time error. -Types of `#[local]` resources must implement a [`Send`] trait as they are being sent from `init` -to a target task, crossing a thread boundary. +Types of `#[local]` resources must implement [`Send`] trait as they are being sent from `init` to the target task and thus crossing the *thread* boundary. [`Send`]: https://doc.rust-lang.org/stable/core/marker/trait.Send.html -The example application shown below contains two tasks where each task has access to its own -`#[local]` resource; the `idle` task has its own `#[local]` as well. +The example application shown below contains three tasks `foo`, `bar` and `idle`, each having access to its own `#[local]` resource. ``` rust -{{#include ../../../../examples/locals.rs}} +{{#include ../../../../rtic/examples/locals.rs}} ``` Running the example: ``` console $ cargo run --target thumbv7m-none-eabi --example locals -{{#include ../../../../ci/expected/locals.run}} +{{#include ../../../../rtic/ci/expected/locals.run}} ``` -Local resources in `#[init]` and `#[idle]` have `'static` -lifetimes. This is safe since both tasks are not re-entrant. - ### Task local initialized resources -Local resources can also be specified directly in the resource claim like so: -`#[task(local = [my_var: TYPE = INITIAL_VALUE, ...])]`; this allows for creating locals which do no need to be -initialized in `#[init]`. +A special use-case of local resources are the ones specified directly in the task declaration, `#[task(local = [my_var: TYPE = INITIAL_VALUE, ...])]`. This allows for creating locals which do no need to be initialized in `#[init]`. Moreover, local resources in `#[init]` and `#[idle]` have `'static` lifetimes, this is safe since both are not re-entrant. -Types of `#[task(local = [..])]` resources have to be neither [`Send`] nor [`Sync`] as they -are not crossing any thread boundary. +Types of `#[task(local = [..])]` resources have to be neither [`Send`] nor [`Sync`] as they are not crossing any thread boundary. [`Sync`]: https://doc.rust-lang.org/stable/core/marker/trait.Sync.html In the example below the different uses and lifetimes are shown: ``` rust -{{#include ../../../../examples/declared_locals.rs}} +{{#include ../../../../rtic/examples/declared_locals.rs}} ``` - +You can run the application, but as the example is designed merely to showcase the lifetime properties there is no output (it suffices to build the application). + +``` console +$ cargo build --target thumbv7m-none-eabi --example declared_locals +``` + ## `#[shared]` resources and `lock` -Critical sections are required to access `#[shared]` resources in a data race-free manner and to -achieve this the `shared` field of the passed `Context` implements the [`Mutex`] trait for each -shared resource accessible to the task. This trait has only one method, [`lock`], which runs its -closure argument in a critical section. +Critical sections are required to access `#[shared]` resources in a data race-free manner and to achieve this the `shared` field of the passed `Context` implements the [`Mutex`] trait for each shared resource accessible to the task. This trait has only one method, [`lock`], which runs its closure argument in a critical section. [`Mutex`]: ../../../api/rtic/trait.Mutex.html [`lock`]: ../../../api/rtic/trait.Mutex.html#method.lock -The critical section created by the `lock` API is based on dynamic priorities: it temporarily -raises the dynamic priority of the context to a *ceiling* priority that prevents other tasks from -preempting the critical section. This synchronization protocol is known as the -[Immediate Ceiling Priority Protocol (ICPP)][icpp], and complies with -[Stack Resource Policy (SRP)][srp] based scheduling of RTIC. +The critical section created by the `lock` API is based on dynamic priorities: it temporarily raises the dynamic priority of the context to a *ceiling* priority that prevents other tasks from preempting the critical section. This synchronization protocol is known as the [Immediate Ceiling Priority Protocol (ICPP)][icpp], and complies with [Stack Resource Policy (SRP)][srp] based scheduling of RTIC. [icpp]: https://en.wikipedia.org/wiki/Priority_ceiling_protocol [srp]: https://en.wikipedia.org/wiki/Stack_Resource_Policy -In the example below we have three interrupt handlers with priorities ranging from one to three. -The two handlers with the lower priorities contend for a `shared` resource and need to succeed in locking the -resource in order to access its data. The highest priority handler, which does not access the `shared` -resource, is free to preempt a critical section created by the lowest priority handler. +In the example below we have three interrupt handlers with priorities ranging from one to three. The two handlers with the lower priorities contend for the `shared` resource and need to lock the resource for accessing the data. The highest priority handler, which do not access the `shared` resource, is free to preempt the critical section created by the lowest priority handler. ``` rust -{{#include ../../../../examples/lock.rs}} +{{#include ../../../../rtic/examples/lock.rs}} ``` ``` console $ cargo run --target thumbv7m-none-eabi --example lock -{{#include ../../../../ci/expected/lock.run}} +{{#include ../../../../rtic/ci/expected/lock.run}} ``` Types of `#[shared]` resources have to be [`Send`]. ## Multi-lock -As an extension to `lock`, and to reduce rightward drift, locks can be taken as tuples. The -following examples show this in use: +As an extension to `lock`, and to reduce rightward drift, locks can be taken as tuples. The following examples show this in use: ``` rust -{{#include ../../../../examples/multilock.rs}} +{{#include ../../../../rtic/examples/multilock.rs}} ``` ``` console $ cargo run --target thumbv7m-none-eabi --example multilock -{{#include ../../../../ci/expected/multilock.run}} +{{#include ../../../../rtic/ci/expected/multilock.run}} ``` ## Only shared (`&-`) access -By default, the framework assumes that all tasks require exclusive access (`&mut-`) to resources, -but it is possible to specify that a task only requires shared access (`&-`) to a resource using the -`&resource_name` syntax in the `shared` list. +By default, the framework assumes that all tasks require exclusive mutable access (`&mut-`) to resources, but it is possible to specify that a task only requires shared access (`&-`) to a resource using the `&resource_name` syntax in the `shared` list. -The advantage of specifying shared access (`&-`) to a resource is that no locks are required to -access the resource even if the resource is contended by more than one task running at different -priorities. The downside is that the task only gets a shared reference (`&-`) to the resource, -limiting the operations it can perform on it, but where a shared reference is enough this approach -reduces the number of required locks. In addition to simple immutable data, this shared access can -be useful where the resource type safely implements interior mutability, with appropriate locking -or atomic operations of its own. +The advantage of specifying shared access (`&-`) to a resource is that no locks are required to access the resource even if the resource is contended by more than one task running at different priorities. The downside is that the task only gets a shared reference (`&-`) to the resource, limiting the operations it can perform on it, but where a shared reference is enough this approach reduces the number of required locks. In addition to simple immutable data, this shared access can be useful where the resource type safely implements interior mutability, with appropriate locking or atomic operations of its own. -Note that in this release of RTIC it is not possible to request both exclusive access (`&mut-`) -and shared access (`&-`) to the *same* resource from different tasks. Attempting to do so will -result in a compile error. +Note that in this release of RTIC it is not possible to request both exclusive access (`&mut-`) and shared access (`&-`) to the *same* resource from different tasks. Attempting to do so will result in a compile error. -In the example below a key (e.g. a cryptographic key) is loaded (or created) at runtime and then -used from two tasks that run at different priorities without any kind of lock. +In the example below a key (e.g. a cryptographic key) is loaded (or created) at runtime (returned by `init`) and then used from two tasks that run at different priorities without any kind of lock. ``` rust -{{#include ../../../../examples/only-shared-access.rs}} +{{#include ../../../../rtic/examples/only-shared-access.rs}} ``` ``` console $ cargo run --target thumbv7m-none-eabi --example only-shared-access -{{#include ../../../../ci/expected/only-shared-access.run}} +{{#include ../../../../rtic/ci/expected/only-shared-access.run}} ``` -## Lock-free resource access of shared resources +## Lock-free access of shared resources -A critical section is *not* required to access a `#[shared]` resource that's only accessed by tasks -running at the *same* priority. In this case, you can opt out of the `lock` API by adding the -`#[lock_free]` field-level attribute to the resource declaration (see example below). Note that -this is merely a convenience to reduce needless resource locking code, because even if the +A critical section is *not* required to access a `#[shared]` resource that's only accessed by tasks running at the *same* priority. In this case, you can opt out of the `lock` API by adding the `#[lock_free]` field-level attribute to the resource declaration (see example below). + + -Also worth noting: using `#[lock_free]` on resources shared by -tasks running at different priorities will result in a *compile-time* error -- not using the `lock` -API would be a data race in that case. +To adhere to the Rust [aliasing] rule, a resource may be either accessed through multiple immutable references or a singe mutable reference (but not both at the same time). + +[aliasing]: https://doc.rust-lang.org/nomicon/aliasing.html + +Using `#[lock_free]` on resources shared by tasks running at different priorities will result in a *compile-time* error -- not using the `lock` API would violate the aforementioned alias rule. Similarly, for each priority there can be only a single *software* task accessing a shared resource (as an `async` task may yield execution to other *software* or *hardware* tasks running at the same priority). However, under this single-task restriction, we make the observation that the resource is in effect no longer `shared` but rather `local`. Thus, using a `#[lock_free]` shared resource will result in a *compile-time* error -- where applicable, use a `#[local]` resource instead. ``` rust -{{#include ../../../../examples/lock-free.rs}} +{{#include ../../../../rtic/examples/lock-free.rs}} ``` ``` console $ cargo run --target thumbv7m-none-eabi --example lock-free -{{#include ../../../../ci/expected/lock-free.run}} +{{#include ../../../../rtic/ci/expected/lock-free.run}} ``` diff --git a/book/en/src/by-example/software_tasks.md b/book/en/src/by-example/software_tasks.md index 8ee185bd15..27527078d3 100644 --- a/book/en/src/by-example/software_tasks.md +++ b/book/en/src/by-example/software_tasks.md @@ -1,47 +1,99 @@ # Software tasks & spawn -The RTIC concept of a software task shares a lot with that of [hardware tasks](./hardware_tasks.md) -with the core difference that a software task is not explicitly bound to a specific -interrupt vector, but rather bound to a “dispatcher” interrupt vector running -at the intended priority of the software task (see below). +The RTIC concept of a *software* task shares a lot with that of [hardware tasks](./hardware_tasks.md) with the core difference that a software task is not explicitly bound to a specific +interrupt vector, but rather to a “dispatcher” interrupt vector running at the same priority as the software task. -Thus, software tasks are tasks which are not *directly* bound to an interrupt vector. +Similarly to *hardware* tasks, the `#[task]` attribute used on a function declare it as a task. The absence of a `binds = InterruptName` argument to the attribute declares the function as a *software task*. -The `#[task]` attributes used on a function determine if it is -software tasks, specifically the absence of a `binds = InterruptName` -argument to the attribute definition. +The static method `task_name::spawn()` spawns (starts) a software task and given that there are no higher priority tasks running the task will start executing directly. -The static method `task_name::spawn()` spawns (schedules) a software -task by registering it with a specific dispatcher. If there are no -higher priority tasks available to the scheduler (which serves a set -of dispatchers), the task will start executing directly. +The *software* task itself is given as an `async` Rust function, which allows the user to optionally `await` future events. This allows to blend reactive programming (by means of *hardware* tasks) with sequential programming (by means of *software* tasks). -All software tasks at the same priority level share an interrupt handler bound to their dispatcher. -What differentiates software and hardware tasks is the usage of either a dispatcher or a bound interrupt vector. +Whereas, *hardware* tasks are assumed to run-to-completion (and return), *software* tasks may be started (`spawned`) once and run forever, with the side condition that any loop (execution path) is broken by at least one `await` (yielding operation). -The interrupt vectors used as dispatchers cannot be used by hardware tasks. +All *software* tasks at the same priority level shares an interrupt handler acting as an async executor dispatching the software tasks. -Availability of a set of “free” (not in use by hardware tasks) and usable interrupt vectors allows the framework -to dispatch software tasks via dedicated interrupt handlers. +This list of dispatchers, `dispatchers = [FreeInterrupt1, FreeInterrupt2, ...]` is an argument to the `#[app]` attribute, where you define the set of free and usable interrupts. -This set of dispatchers, `dispatchers = [FreeInterrupt1, FreeInterrupt2, ...]` is an -argument to the `#[app]` attribute. +Each interrupt vector acting as dispatcher gets assigned to one priority level meaning that the list of dispatchers need to cover all priority levels used by software tasks. -Each interrupt vector acting as dispatcher gets assigned to a unique priority level meaning that -the list of dispatchers needs to cover all priority levels used by software tasks. +Example: The `dispatchers =` argument needs to have at least 3 entries for an application using three different priorities for software tasks. -Example: The `dispatchers =` argument needs to have at least 3 entries for an application using -three different priorities for software tasks. - -The framework will give a compilation error if there are not enough dispatchers provided. +The framework will give a compilation error if there are not enough dispatchers provided, or if a clash occurs between the list of dispatchers and interrupts bound to *hardware* tasks. See the following example: ``` rust -{{#include ../../../../examples/spawn.rs}} +{{#include ../../../../rtic/examples/spawn.rs}} ``` ``` console $ cargo run --target thumbv7m-none-eabi --example spawn -{{#include ../../../../ci/expected/spawn.run}} +{{#include ../../../../rtic/ci/expected/spawn.run}} ``` +You may `spawn` a *software* task again, given that it has run-to-completion (returned). + +In the below example, we `spawn` the *software* task `foo` from the `idle` task. Since the default priority of the *software* task is 1 (higher than `idle`), the dispatcher will execute `foo` (preempting `idle`). Since `foo` runs-to-completion. It is ok to `spawn` the `foo` task again. + +Technically the async executor will `poll` the `foo` *future* which in this case leaves the *future* in a *completed* state. + +``` rust +{{#include ../../../../rtic/examples/spawn_loop.rs}} +``` + +``` console +$ cargo run --target thumbv7m-none-eabi --example spawn_loop +{{#include ../../../../rtic/ci/expected/spawn_loop.run}} +``` + +An attempt to `spawn` an already spawned task (running) task will result in an error. Notice, the that the error is reported before the `foo` task is actually run. This is since, the actual execution of the *software* task is handled by the dispatcher interrupt (`SSIO`), which is not enabled until we exit the `init` task. (Remember, `init` runs in a critical section, i.e. all interrupts being disabled.) + +Technically, a `spawn` to a *future* that is not in *completed* state is considered an error. + +``` rust +{{#include ../../../../rtic/examples/spawn_err.rs}} +``` + +``` console +$ cargo run --target thumbv7m-none-eabi --example spawn_err +{{#include ../../../../rtic/ci/expected/spawn_err.run}} +``` + +## Passing arguments +You can also pass arguments at spawn as follows. + +``` rust +{{#include ../../../../rtic/examples/spawn_arguments.rs}} +``` + +``` console +$ cargo run --target thumbv7m-none-eabi --example spawn_arguments +{{#include ../../../../rtic/ci/expected/spawn_arguments.run}} +``` + +## Priority zero tasks + +In RTIC tasks run preemptively to each other, with priority zero (0) the lowest priority. You can use priority zero tasks for background work, without any strict real-time requirements. + +Conceptually, one can see such tasks as running in the `main` thread of the application, thus the resources associated are not required the [Send] bound. + +[Send]: https://doc.rust-lang.org/nomicon/send-and-sync.html + + +``` rust +{{#include ../../../../rtic/examples/zero-prio-task.rs}} +``` + +``` console +$ cargo run --target thumbv7m-none-eabi --example zero-prio-task +{{#include ../../../../rtic/ci/expected/zero-prio-task.run}} +``` + +> **Notice**: *software* task at zero priority cannot co-exist with the [idle] task. The reason is that `idle` is running as a non-returning Rust function at priority zero. Thus there would be no way for an executor at priority zero to give control to *software* tasks at the same priority. + +--- + +Application side safety: Technically, the RTIC framework ensures that `poll` is never executed on any *software* task with *completed* future, thus adhering to the soundness rules of async Rust. + + + diff --git a/book/en/src/by-example/starting_a_project.md b/book/en/src/by-example/starting_a_project.md index fe7be57818..8638f9064d 100644 --- a/book/en/src/by-example/starting_a_project.md +++ b/book/en/src/by-example/starting_a_project.md @@ -10,6 +10,8 @@ If you are targeting ARMv6-M or ARMv8-M-base architecture, check out the section This will give you an RTIC application with support for RTT logging with [`defmt`] and stack overflow protection using [`flip-link`]. There is also a multitude of examples provided by the community: +For inspiration you may look at the below resources. For now they cover RTIC 1.0.x, but will be updated with RTIC 2.0.x examples over time. + - [`rtic-examples`] - Multiple projects - [https://github.com/kalkyl/f411-rtic](https://github.com/kalkyl/f411-rtic) - ... More to come diff --git a/book/en/src/preface.md b/book/en/src/preface.md index 6041dfedea..3f47cb3d1c 100644 --- a/book/en/src/preface.md +++ b/book/en/src/preface.md @@ -1,7 +1,7 @@

RTIC
-

Real-Time Interrupt-driven Concurrency

+

The Embedded Rust RTOS

A concurrency framework for building real-time systems

@@ -10,29 +10,160 @@ This book contains user level documentation for the Real-Time Interrupt-driven Concurrency (RTIC) framework. The API reference is available [here](../../api/). -Formerly known as Real-Time For the Masses. + -This is the documentation of v1.0.x of RTIC; for the documentation of version +This is the documentation of v2.0.x (pre-release) of RTIC 2. +## RTIC - The Past, current and Future + +This section gives a background to the RTIC model. Feel free to skip to section [RTIC the model](preface.md#rtic-the-model) for a TL;DR. + +The RTIC framework takes the outset from real-time systems research at Luleå University of Technology (LTU) Sweden. RTIC is inspired by the concurrency model of the [Timber] language, the [RTFM-SRP] based scheduler, the [RTFM-core] language and [Abstract Timer] implementation. For a full list of related research see [TODO]. + +[Timber]: https://timber-lang.org/ +[RTFM-SRP]: https://www.diva-portal.org/smash/get/diva2:1005680/FULLTEXT01.pdf +[RTFM-core]: https://ltu.diva-portal.org/smash/get/diva2:1013248/FULLTEXT01.pdf +[AbstractTimer]: https://ltu.diva-portal.org/smash/get/diva2:1013030/FULLTEXT01.pdf + +## Stack Resource Policy based Scheduling + +Stack Resource Policy (SRP) based concurrency and resource management is at heart of the RTIC framework. The [SRP] model itself extends on [Priority Inheritance Protocols], and provides a set of outstanding properties for single core scheduling. To name a few: + +- preemptive deadlock and race-free scheduling +- resource efficiency + - tasks execute on a single shared stack + - tasks run-to-completion with wait free access to shared resources +- predictable scheduling, with bounded priority inversion by a single (named) critical section +- theoretical underpinning amenable to static analysis (e.g., for task response times and overall schedulability) + +SRP comes with a set of system wide requirements: +- each task is associated a static priority, +- tasks execute on a single-core, +- tasks must be run-to-completion, and +- resources must be claimed/locked in LIFO order. + +[SRP]: https://link.springer.com/article/10.1007/BF00365393 +[Priority Inheritance Protocols]: https://ieeexplore.ieee.org/document/57058 + +## SRP analysis + +SRP based scheduling requires the set of static priority tasks and their access to shared resources to be known in order to compute a static *ceiling* (𝝅) for each resource. The static resource *ceiling* 𝝅(r) reflects the maximum static priority of any task that accesses the resource `r`. + +### Example + +Assume two tasks `A` (with priority `p(A) = 2`) and `B` (with priority `p(B) = 4`) both accessing the shared resource `R`. The static ceiling of `R` is 4 (computed from `𝝅(R) = max(p(A) = 2, p(B) = 4) = 4`). + +A graph representation of the example: + +```mermaid +graph LR + A["p(A) = 2"] --> R + B["p(A) = 4"] --> R + R["𝝅(R) = 4"] +``` + +## RTIC the hardware accelerated real-time scheduler + +SRP itself is compatible both to dynamic and static priority scheduling. For the implementation of RTIC we leverage on the underlying hardware for accelerated static priority scheduling. + +In the case of the `ARM Cortex-M` architecture, each interrupt vector entry `v[i]` is associated a function pointer (`v[i].fn`), and a static priority (`v[i].priority`), an enabled- (`v[i].enabled`) and a pending-bit (`v[i].pending`). + +An interrupt `i` is scheduled (run) by the hardware under the conditions: +1. is `pended` and `enabled` and has a priority higher than the (optional `BASEPRI`) register, and +1. has the highest priority among interrupts meeting 1. + +The first condition (1) can be seen a filter allowing RTIC to take control over which tasks should be allowed to start (and which should be prevented from starting). + +The SPR model for single-core static scheduling on the other hand states that a task should be scheduled (run) under the conditions: +1. it is `requested` to run and has a static priority higher than the current system ceiling (𝜫) +1. it has the highest static priority among tasks meeting 1. + +The similarities are striking and it is not by chance/luck/coincidence. The hardware was cleverly designed with real-time scheduling in mind. + +In order to map the SRP scheduling onto the hardware we need to have a closer look on the system ceiling (𝜫). Under SRP 𝜫 is computed as the maximum priority ceiling of the currently held resources, and will thus change dynamically during the system operation. + +## Example + +Assume the task model above. Starting from an idle system, 𝜫 is 0, (no task is holding any resource). Assume that `A` is requested for execution, it will immediately be scheduled. Assume that `A` claims (locks) the resource `R`. During the claim (lock of `R`) any request `B` will be blocked from starting (by 𝜫 = `max(𝝅(R) = 4) = 4`, `p(B) = 4`, thus SRP scheduling condition 1 is not met). + +## Mapping + +The mapping of static priority SRP based scheduling to the Cortex M hardware is straightforward: + +- each task `t` are mapped to an interrupt vector index `i` with a corresponding function `v[i].fn = t` and given the static priority `v[i].priority = p(t)`. +- the current system ceiling is mapped to the `BASEPRI` register or implemented through masking the interrupt enable bits accordingly. + +## Example + +For the running example, a snapshot of the ARM Cortex M [NVIC] may have the following configuration (after task `A` has been pended for execution.) + +| Index | Fn | Priority | Enabled | Pended | +| ----- | --- | -------- | ------- | ------ | +| 0 | A | 2 | true | true | +| 1 | B | 4 | true | false | + +[NVIC]: https://developer.arm.com/documentation/ddi0337/h/nested-vectored-interrupt-controller/about-the-nvic + +(As discussed later, the assignment of interrupt and exception vectors is up to the user.) + + +A claim (lock(r)) will change the current system ceiling (𝜫) and can be implemented as a *named* critical section: + - old_ceiling = 𝜫, 𝜫 = 𝝅(r) + - execute code within critical section + - old_ceiling = 𝜫 + +This amounts to a resource protection mechanism requiring only two machine instructions on enter and one on exit the critical section for managing the `BASEPRI` register. For architectures lacking `BASEPRI`, we can implement the system ceiling through a set of machine instructions for disabling/enabling interrupts on entry/exit for the named critical section. The number of machine instructions vary depending on the number of mask registers that needs to be updated (a single machine operation can operate on up to 32 interrupts, so for the M0/M0+ architecture a single instruction suffice). RTIC will determine the ceiling values and masking constants at compile time, thus all operations is in Rust terms zero-cost. + +In this way RTIC fuses SRP based preemptive scheduling with a zero-cost hardware accelerated implementation, resulting in "best in class" guarantees and performance. + +Given that the approach is dead simple, how come SRP and hardware accelerated scheduling is not adopted by any other mainstream RTOS? + +The answer is simple, the commonly adopted threading model does not lend itself well to static analysis - there is no known way to extract the task/resource dependencies from the source code at compile time (thus ceilings cannot be efficiently computed and the LIFO resource locking requirement cannot be ensured). Thus SRP based scheduling is in the general case out of reach for any thread based RTOS. + +## RTIC into the Future + +Asynchronous programming in various forms are getting increased popularity and language support. Rust natively provides an `async`/`await` API for cooperative multitasking and the compiler generates the necessary boilerplate for storing and retrieving execution contexts (i.e., managing the set of local variables that spans each `await`). + +The Rust standard library provides collections for dynamically allocated data-structures (useful to manage execution contexts at run-time. However, in the setting of resource constrained real-time systems, dynamic allocations are problematic (both regarding performance and reliability - Rust runs into a *panic* on an out-of-memory condition). Thus, static allocation is king! + +RTIC provides a mechanism for `async`/`await` that relies solely on static allocations. However, the implementation relies on the `#![feature(type_alias_impl_trait)]` (TAIT) which is undergoing stabilization (thus RTIC 2.0.x currently requires a *nightly* toolchain). Technically, using TAIT, the compiler determines the size of each execution context allowing static allocation. + +From a modelling perspective `async/await` lifts the run-to-completion requirement of SRP, and each section of code between two yield points (`await`s) can be seen as an individual task. The compiler will reject any attempt to `await` while holding a resource (not doing so would break the strict LIFO requirement on resource usage under SRP). + +So with the technical stuff out of the way, what does `async/await` bring to the RTIC table? + +The answer is - improved ergonomics! In cases you want a task to perform a sequence of requests (and await their results in order to progress). Without `async`/`await` the programmer would be forced to split the task into individual sub-tasks and maintain some sort of state encoding (and manually progress by selecting sub-task). Using `async/await` each yield point (`await`) essentially represents a state, and the progression mechanism is built automatically for you at compile time by means of `Futures`. + +Rust `async`/`await` support is still incomplete and/or under development (e.g., there are no stable way to express `async` closures, precluding use in iterator patterns). Nevertheless, Rust `async`/`await` is production ready and covers most common use cases. + +An important property is that futures are composable, thus you can await either, all, or any combination of possible futures (allowing e.g., timeouts and/or asynchronous errors to be promptly handled). For more details and examples see Section [todo]. + +## RTIC the model + +An RTIC `app` is a declarative and executable system model for single-core applications, defining a set of (`local` and `shared`) resources operated on by a set of (`init`, `idle`, *hardware* and *software*) tasks. In short the `init` task runs before any other task returning a set of resources (`local` and `shared`). Tasks run preemptively based on their associated static priority, `idle` has the lowest priority (and can be used for background work, and/or to put the system to sleep until woken by some event). Hardware tasks are bound to underlying hardware interrupts, while software tasks are scheduled by asynchronous executors (one for each software task priority). + +At compile time the task/resource model is analyzed under SRP and executable code generated with the following outstanding properties: + +- guaranteed race-free resource access and deadlock-free execution on a single-shared stack (thanks to SRP) + - hardware task scheduling is performed directly by the hardware, and + - software task scheduling is performed by auto generated async executors tailored to the application. + +The RTIC API design ensures that both SRP requirements and Rust soundness rules are upheld at all times, thus the executable model is correct by construction. Overall, the generated code infers no additional overhead in comparison to a hand-written implementation, thus in Rust terms RTIC offers a zero-cost abstraction to concurrency. + + + diff --git a/book/en/src/rtic_vs.md b/book/en/src/rtic_vs.md new file mode 100644 index 0000000000..2f8c8d5856 --- /dev/null +++ b/book/en/src/rtic_vs.md @@ -0,0 +1,31 @@ +# RTIC vs. the world + +RTIC aims to provide the lowest level of abstraction needed for developing robust and reliable embedded software. + +It provides a minimal set of required mechanisms for safe sharing of mutable resources among interrupts and asynchronously executing tasks. The scheduling primitives leverages on the underlying hardware for unparalleled performance and predictability, in effect RTIC provides in Rust terms a zero-cost abstraction to concurrent real-time programming. + + + +## Comparison regarding safety and security + +Comparing RTIC to traditional a Real-Time Operating System (RTOS) is hard. Firstly, a traditional RTOS typically comes with no guarantees regarding system safety, even the most hardened kernels like the formally verified [seL4] kernel. Their claims to integrity, confidentiality, and availability regards only the kernel itself (under additional assumptions its configuration and environment). They even state: + +"An OS kernel, verified or not, does not automatically make a system secure. In fact, any system, no matter how secure, can be used in insecure ways." + +[seL4]: https://sel4.systems/ + +### Security by design + +In the world of information security we commonly find: + +- confidentiality, protecting the information from being exposed to an unauthorized party, +- integrity, referring to accuracy and completeness of data, and +- availability, referring to data being accessible to authorized users. + +Obviously, a traditional OS can guarantee neither confidentiality nor integrity, as both requires the security critical code to be trusted. Regarding availability, this typically boils down to the usage of system resources. Any OS that allows for dynamic allocation of resources, relies on that the application correctly handles allocations/de-allocations, and cases of allocation failures. + +Thus their claim is correct, security is completely out of hands for the OS, the best we can hope for is that it does not add further vulnerabilities. + +RTIC on the other hand holds your back. The declarative system wide model gives you a static set of tasks and resources, with precise control over what data is shared and between which parties. Moreover, Rust as a programming language comes with strong properties regarding integrity (compile time aliasing, mutability and lifetime guarantees, together with ensured data validity). + +Using RTIC these properties propagate to the system wide model, without interference of other applications running. The RTIC kernel is internally infallible without any need of dynamically allocated data. \ No newline at end of file diff --git a/rtic/Cargo.toml b/rtic/Cargo.toml index 12a34da1b8..9b8a2916c3 100644 --- a/rtic/Cargo.toml +++ b/rtic/Cargo.toml @@ -10,7 +10,19 @@ categories = ["concurrency", "embedded", "no-std", "asynchronous"] description = "Real-Time Interrupt-driven Concurrency (RTIC): a concurrency framework for building real-time systems" documentation = "https://rtic.rs/" edition = "2021" -keywords = ["arm", "cortex-m", "risc-v", "embedded", "async", "runtime", "futures", "await", "no-std", "rtos", "bare-metal"] +keywords = [ + "arm", + "cortex-m", + "risc-v", + "embedded", + "async", + "runtime", + "futures", + "await", + "no-std", + "rtos", + "bare-metal", +] license = "MIT OR Apache-2.0" name = "rtic" readme = "README.md" @@ -31,6 +43,7 @@ bare-metal = "1.0.0" #portable-atomic = { version = "0.3.19" } atomic-polyfill = "1" + [build-dependencies] version_check = "0.9" @@ -42,6 +55,11 @@ rtic-time = { path = "../rtic-time" } rtic-channel = { path = "../rtic-channel" } rtic-monotonics = { path = "../rtic-monotonics" } +[dev-dependencies.futures] +version = "0.3.26" +default-features = false +features = ["async-await"] + [dev-dependencies.panic-semihosting] features = ["exit"] version = "0.6.0" diff --git a/rtic/ci/expected/async-channel-done.run b/rtic/ci/expected/async-channel-done.run new file mode 100644 index 0000000000..525962a2a0 --- /dev/null +++ b/rtic/ci/expected/async-channel-done.run @@ -0,0 +1,9 @@ +Sender 1 sending: 1 +Sender 1 done +Sender 2 sending: 2 +Sender 3 sending: 3 +Receiver got: 1 +Sender 2 done +Receiver got: 2 +Sender 3 done +Receiver got: 3 diff --git a/rtic/ci/expected/async-channel-no-receiver.run b/rtic/ci/expected/async-channel-no-receiver.run new file mode 100644 index 0000000000..34624e16ce --- /dev/null +++ b/rtic/ci/expected/async-channel-no-receiver.run @@ -0,0 +1 @@ +Sender 1 sending: 1 Err(NoReceiver(1)) diff --git a/rtic/ci/expected/async-channel-no-sender.run b/rtic/ci/expected/async-channel-no-sender.run new file mode 100644 index 0000000000..237f2f145a --- /dev/null +++ b/rtic/ci/expected/async-channel-no-sender.run @@ -0,0 +1 @@ +Receiver got: Err(NoSender) diff --git a/rtic/ci/expected/async-channel-try.run b/rtic/ci/expected/async-channel-try.run new file mode 100644 index 0000000000..c3a4092f84 --- /dev/null +++ b/rtic/ci/expected/async-channel-try.run @@ -0,0 +1,2 @@ +Sender 1 sending: 1 +Sender 1 try sending: 2 Err(Full(2)) diff --git a/rtic/ci/expected/async-channel.run b/rtic/ci/expected/async-channel.run index 3e3c232427..4e313a12e7 100644 --- a/rtic/ci/expected/async-channel.run +++ b/rtic/ci/expected/async-channel.run @@ -3,4 +3,4 @@ Sender 2 sending: 2 Sender 3 sending: 3 Receiver got: 1 Receiver got: 2 -Receiver got: 5 +Receiver got: 3 diff --git a/rtic/ci/expected/async-timeout.run b/rtic/ci/expected/async-timeout.run index a8074230ee..ca929c7b4c 100644 --- a/rtic/ci/expected/async-timeout.run +++ b/rtic/ci/expected/async-timeout.run @@ -1,5 +1,16 @@ init -hello from bar -hello from foo -foo no timeout -bar timeout +the hal takes a duration of Duration { ticks: 450 } +timeout +the hal takes a duration of Duration { ticks: 450 } +hal returned 5 +the hal takes a duration of Duration { ticks: 450 } +hal returned 5 +now is Instant { ticks: 2102 }, timeout at Instant { ticks: 2602 } +the hal takes a duration of Duration { ticks: 350 } +hal returned 5 at time Instant { ticks: 2452 } +now is Instant { ticks: 3102 }, timeout at Instant { ticks: 3602 } +the hal takes a duration of Duration { ticks: 450 } +hal returned 5 at time Instant { ticks: 3552 } +now is Instant { ticks: 4102 }, timeout at Instant { ticks: 4602 } +the hal takes a duration of Duration { ticks: 550 } +timeout diff --git a/rtic/ci/expected/common.run b/rtic/ci/expected/common.run index e69de29bb2..4f1d3509c2 100644 --- a/rtic/ci/expected/common.run +++ b/rtic/ci/expected/common.run @@ -0,0 +1,3 @@ +bar: local_to_bar = 1 +foo: local_to_foo = 1 +idle: local_to_idle = 1 diff --git a/rtic/ci/expected/message_passing.run b/rtic/ci/expected/spawn_arguments.run similarity index 100% rename from rtic/ci/expected/message_passing.run rename to rtic/ci/expected/spawn_arguments.run diff --git a/rtic/ci/expected/spawn_err.run b/rtic/ci/expected/spawn_err.run new file mode 100644 index 0000000000..97c4112c03 --- /dev/null +++ b/rtic/ci/expected/spawn_err.run @@ -0,0 +1,3 @@ +init +Cannot spawn a spawned (running) task! +foo diff --git a/rtic/ci/expected/spawn_loop.run b/rtic/ci/expected/spawn_loop.run new file mode 100644 index 0000000000..ee6e1e30a2 --- /dev/null +++ b/rtic/ci/expected/spawn_loop.run @@ -0,0 +1,7 @@ +init +foo +idle +foo +idle +foo +idle diff --git a/rtic/examples/async-channel-done.rs b/rtic/examples/async-channel-done.rs new file mode 100644 index 0000000000..1c32d32b9e --- /dev/null +++ b/rtic/examples/async-channel-done.rs @@ -0,0 +1,65 @@ +//! examples/async-channel-done.rs + +#![deny(unsafe_code)] +#![deny(warnings)] +#![no_main] +#![no_std] +#![feature(type_alias_impl_trait)] + +use panic_semihosting as _; + +#[rtic::app(device = lm3s6965, dispatchers = [SSI0])] +mod app { + use cortex_m_semihosting::{debug, hprintln}; + use rtic_channel::*; + + #[shared] + struct Shared {} + + #[local] + struct Local {} + + const CAPACITY: usize = 1; + #[init] + fn init(_: init::Context) -> (Shared, Local) { + let (s, r) = make_channel!(u32, CAPACITY); + + receiver::spawn(r).unwrap(); + sender1::spawn(s.clone()).unwrap(); + sender2::spawn(s.clone()).unwrap(); + sender3::spawn(s).unwrap(); + + (Shared {}, Local {}) + } + + #[task] + async fn receiver(_c: receiver::Context, mut receiver: Receiver<'static, u32, CAPACITY>) { + while let Ok(val) = receiver.recv().await { + hprintln!("Receiver got: {}", val); + if val == 3 { + debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator + } + } + } + + #[task] + async fn sender1(_c: sender1::Context, mut sender: Sender<'static, u32, CAPACITY>) { + hprintln!("Sender 1 sending: 1"); + sender.send(1).await.unwrap(); + hprintln!("Sender 1 done"); + } + + #[task] + async fn sender2(_c: sender2::Context, mut sender: Sender<'static, u32, CAPACITY>) { + hprintln!("Sender 2 sending: 2"); + sender.send(2).await.unwrap(); + hprintln!("Sender 2 done"); + } + + #[task] + async fn sender3(_c: sender3::Context, mut sender: Sender<'static, u32, CAPACITY>) { + hprintln!("Sender 3 sending: 3"); + sender.send(3).await.unwrap(); + hprintln!("Sender 3 done"); + } +} diff --git a/rtic/examples/async-channel-no-receiver.rs b/rtic/examples/async-channel-no-receiver.rs new file mode 100644 index 0000000000..ffb78e4e03 --- /dev/null +++ b/rtic/examples/async-channel-no-receiver.rs @@ -0,0 +1,40 @@ +//! examples/async-channel-no-receiver.rs + +#![deny(unsafe_code)] +#![deny(warnings)] +#![no_main] +#![no_std] +#![feature(type_alias_impl_trait)] + +use panic_semihosting as _; + +#[rtic::app(device = lm3s6965, dispatchers = [SSI0])] +mod app { + use cortex_m_semihosting::{debug, hprintln}; + use rtic_channel::*; + + #[shared] + struct Shared {} + + #[local] + struct Local {} + + const CAPACITY: usize = 1; + #[init] + fn init(_: init::Context) -> (Shared, Local) { + let (s, _r) = make_channel!(u32, CAPACITY); + + sender1::spawn(s.clone()).unwrap(); + + (Shared {}, Local {}) + } + + + #[task] + async fn sender1(_c: sender1::Context, mut sender: Sender<'static, u32, CAPACITY>) { + + hprintln!("Sender 1 sending: 1 {:?}", sender.send(1).await); + debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator + } + +} diff --git a/rtic/examples/async-channel-no-sender.rs b/rtic/examples/async-channel-no-sender.rs new file mode 100644 index 0000000000..58e010d585 --- /dev/null +++ b/rtic/examples/async-channel-no-sender.rs @@ -0,0 +1,40 @@ +//! examples/async-channel-no-sender.rs + +#![deny(unsafe_code)] +#![deny(warnings)] +#![no_main] +#![no_std] +#![feature(type_alias_impl_trait)] + +use panic_semihosting as _; + +#[rtic::app(device = lm3s6965, dispatchers = [SSI0])] +mod app { + use cortex_m_semihosting::{debug, hprintln}; + use rtic_channel::*; + + #[shared] + struct Shared {} + + #[local] + struct Local {} + + const CAPACITY: usize = 1; + #[init] + fn init(_: init::Context) -> (Shared, Local) { + let (_s, r) = make_channel!(u32, CAPACITY); + + receiver::spawn(r).unwrap(); + + (Shared {}, Local {}) + } + + #[task] + async fn receiver(_c: receiver::Context, mut receiver: Receiver<'static, u32, CAPACITY>) { + hprintln!("Receiver got: {:?}", receiver.recv().await); + + debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator + } + + +} diff --git a/rtic/examples/async-channel-try.rs b/rtic/examples/async-channel-try.rs new file mode 100644 index 0000000000..4a79935e82 --- /dev/null +++ b/rtic/examples/async-channel-try.rs @@ -0,0 +1,48 @@ +//! examples/async-channel-try.rs + +#![deny(unsafe_code)] +#![deny(warnings)] +#![no_main] +#![no_std] +#![feature(type_alias_impl_trait)] + +use panic_semihosting as _; + +#[rtic::app(device = lm3s6965, dispatchers = [SSI0])] +mod app { + use cortex_m_semihosting::{debug, hprintln}; + use rtic_channel::*; + + #[shared] + struct Shared {} + + #[local] + struct Local {} + + const CAPACITY: usize = 1; + #[init] + fn init(_: init::Context) -> (Shared, Local) { + let (s, r) = make_channel!(u32, CAPACITY); + + receiver::spawn(r).unwrap(); + sender1::spawn(s.clone()).unwrap(); + + (Shared {}, Local {}) + } + + #[task] + async fn receiver(_c: receiver::Context, mut receiver: Receiver<'static, u32, CAPACITY>) { + while let Ok(val) = receiver.recv().await { + hprintln!("Receiver got: {}", val); + } + } + + #[task] + async fn sender1(_c: sender1::Context, mut sender: Sender<'static, u32, CAPACITY>) { + hprintln!("Sender 1 sending: 1"); + sender.send(1).await.unwrap(); + hprintln!("Sender 1 try sending: 2 {:?}", sender.try_send(2)); + debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator + } + +} diff --git a/rtic/examples/async-channel.rs b/rtic/examples/async-channel.rs index 3a2e491181..58eeee87a7 100644 --- a/rtic/examples/async-channel.rs +++ b/rtic/examples/async-channel.rs @@ -19,9 +19,10 @@ mod app { #[local] struct Local {} + const CAPACITY: usize = 5; #[init] fn init(_: init::Context) -> (Shared, Local) { - let (s, r) = make_channel!(u32, 5); + let (s, r) = make_channel!(u32, CAPACITY); receiver::spawn(r).unwrap(); sender1::spawn(s.clone()).unwrap(); @@ -32,30 +33,30 @@ mod app { } #[task] - async fn receiver(_c: receiver::Context, mut receiver: Receiver<'static, u32, 5>) { + async fn receiver(_c: receiver::Context, mut receiver: Receiver<'static, u32, CAPACITY>) { while let Ok(val) = receiver.recv().await { hprintln!("Receiver got: {}", val); - if val == 5 { + if val == 3 { debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator } } } #[task] - async fn sender1(_c: sender1::Context, mut sender: Sender<'static, u32, 5>) { + async fn sender1(_c: sender1::Context, mut sender: Sender<'static, u32, CAPACITY>) { hprintln!("Sender 1 sending: 1"); sender.send(1).await.unwrap(); } #[task] - async fn sender2(_c: sender2::Context, mut sender: Sender<'static, u32, 5>) { + async fn sender2(_c: sender2::Context, mut sender: Sender<'static, u32, CAPACITY>) { hprintln!("Sender 2 sending: 2"); sender.send(2).await.unwrap(); } #[task] - async fn sender3(_c: sender3::Context, mut sender: Sender<'static, u32, 5>) { + async fn sender3(_c: sender3::Context, mut sender: Sender<'static, u32, CAPACITY>) { hprintln!("Sender 3 sending: 3"); - sender.send(5).await.unwrap(); + sender.send(3).await.unwrap(); } } diff --git a/rtic/examples/async-delay.rs b/rtic/examples/async-delay.rs index 0440f774ad..6c79cf54ca 100644 --- a/rtic/examples/async-delay.rs +++ b/rtic/examples/async-delay.rs @@ -1,3 +1,5 @@ +// examples/async-delay.rs +// #![no_main] #![no_std] #![feature(type_alias_impl_trait)] diff --git a/rtic/examples/async-timeout.rs b/rtic/examples/async-timeout.rs new file mode 100644 index 0000000000..30fee43e2c --- /dev/null +++ b/rtic/examples/async-timeout.rs @@ -0,0 +1,87 @@ +// examples/async-timeout.rs +// +#![no_main] +#![no_std] +#![feature(type_alias_impl_trait)] + +use cortex_m_semihosting::{debug, hprintln}; +use panic_semihosting as _; +use rtic_monotonics::systick_monotonic::*; + +#[rtic::app(device = lm3s6965, dispatchers = [SSI0, UART0], peripherals = true)] +mod app { + use super::*; + use futures::{future::FutureExt, select_biased}; + + rtic_monotonics::make_systick_timer_queue!(TIMER); + + #[shared] + struct Shared {} + + #[local] + struct Local {} + + #[init] + fn init(cx: init::Context) -> (Shared, Local) { + hprintln!("init"); + + let systick = Systick::start(cx.core.SYST, 12_000_000); + TIMER.initialize(systick); + + foo::spawn().ok(); + + (Shared {}, Local {}) + } + + #[task] + async fn foo(_cx: foo::Context) { + // Call hal with short relative timeout using `select_biased` + select_biased! { + v = hal_get(&TIMER, 1).fuse() => hprintln!("hal returned {}", v), + _ = TIMER.delay(200.millis()).fuse() => hprintln!("timeout", ), // this will finish first + } + + // Call hal with long relative timeout using `select_biased` + select_biased! { + v = hal_get(&TIMER, 1).fuse() => hprintln!("hal returned {}", v), // hal finish first + _ = TIMER.delay(1000.millis()).fuse() => hprintln!("timeout", ), + } + + // Call hal with long relative timeout using monotonic `timeout_after` + match TIMER.timeout_after(1000.millis(), hal_get(&TIMER, 1)).await { + Ok(v) => hprintln!("hal returned {}", v), + _ => hprintln!("timeout"), + } + + // get the current time instance + let mut instant = TIMER.now(); + + // do this 3 times + for n in 0..3 { + // exact point in time without drift + instant += 1000.millis(); + TIMER.delay_until(instant).await; + + // exact point it time for timeout + let timeout = instant + 500.millis(); + hprintln!("now is {:?}, timeout at {:?}", TIMER.now(), timeout); + + match TIMER.timeout_at(timeout, hal_get(&TIMER, n)).await { + Ok(v) => hprintln!("hal returned {} at time {:?}", v, TIMER.now()), + _ => hprintln!("timeout"), + } + } + + debug::exit(debug::EXIT_SUCCESS); + } +} + +// Emulate some hal +async fn hal_get(timer: &'static SystickTimerQueue, n: u32) -> u32 { + // emulate some delay time dependent on n + let d = 350.millis() + n * 100.millis(); + hprintln!("the hal takes a duration of {:?}", d); + timer.delay(d).await; + // emulate some return value + 5 +} diff --git a/rtic/examples/common.rs b/rtic/examples/common.rs new file mode 100644 index 0000000000..f07b69c54d --- /dev/null +++ b/rtic/examples/common.rs @@ -0,0 +1,86 @@ +//! examples/common.rs +#![feature(type_alias_impl_trait)] +#![deny(unsafe_code)] +#![deny(missing_docs)] +#![deny(warnings)] +#![no_main] +#![no_std] + +use panic_semihosting as _; + +#[rtic::app(device = lm3s6965, dispatchers = [UART0, UART1])] +mod app { + use cortex_m_semihosting::{debug, hprintln}; + + #[shared] + struct Shared {} + + #[local] + struct Local { + local_to_foo: i64, + local_to_bar: i64, + local_to_idle: i64, + } + + // `#[init]` cannot access locals from the `#[local]` struct as they are initialized here. + #[init] + fn init(_: init::Context) -> (Shared, Local) { + foo::spawn().unwrap(); + bar::spawn().unwrap(); + + ( + Shared {}, + // initial values for the `#[local]` resources + Local { + local_to_foo: 0, + local_to_bar: 0, + local_to_idle: 0, + }, + ) + } + + // `local_to_idle` can only be accessed from this context + #[idle(local = [local_to_idle])] + fn idle(cx: idle::Context) -> ! { + let local_to_idle = cx.local.local_to_idle; + *local_to_idle += 1; + + hprintln!("idle: local_to_idle = {}", local_to_idle); + + debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator + + // error: no `local_to_foo` field in `idle::LocalResources` + // _cx.local.local_to_foo += 1; + + // error: no `local_to_bar` field in `idle::LocalResources` + // _cx.local.local_to_bar += 1; + + loop { + cortex_m::asm::nop(); + } + } + + // `local_to_foo` can only be accessed from this context + #[task(local = [local_to_foo])] + async fn foo(cx: foo::Context) { + let local_to_foo = cx.local.local_to_foo; + *local_to_foo += 1; + + // error: no `local_to_bar` field in `foo::LocalResources` + // cx.local.local_to_bar += 1; + + hprintln!("foo: local_to_foo = {}", local_to_foo); + } + + // `local_to_bar` can only be accessed from this context + #[task(local = [local_to_bar])] + async fn bar(cx: bar::Context) { + let local_to_bar = cx.local.local_to_bar; + *local_to_bar += 1; + + // error: no `local_to_foo` field in `bar::LocalResources` + // cx.local.local_to_foo += 1; + + hprintln!("bar: local_to_bar = {}", local_to_bar); + } +} diff --git a/rtic/examples/lock-free.rs b/rtic/examples/lock-free.rs new file mode 100644 index 0000000000..3e0960408e --- /dev/null +++ b/rtic/examples/lock-free.rs @@ -0,0 +1,50 @@ +//! examples/lock-free.rs + +#![deny(unsafe_code)] +#![deny(warnings)] +#![no_main] +#![no_std] +#![feature(type_alias_impl_trait)] + +use panic_semihosting as _; + +#[rtic::app(device = lm3s6965)] +mod app { + use cortex_m_semihosting::{debug, hprintln}; + use lm3s6965::Interrupt; + + #[shared] + struct Shared { + #[lock_free] // <- lock-free shared resource + counter: u64, + } + + #[local] + struct Local {} + + #[init] + fn init(_: init::Context) -> (Shared, Local) { + rtic::pend(Interrupt::UART0); + + (Shared { counter: 0 }, Local {}) + } + + #[task(binds = UART0, shared = [counter])] // <- same priority + fn foo(c: foo::Context) { + rtic::pend(Interrupt::UART1); + + *c.shared.counter += 1; // <- no lock API required + let counter = *c.shared.counter; + hprintln!(" foo = {}", counter); + } + + #[task(binds = UART1, shared = [counter])] // <- same priority + fn bar(c: bar::Context) { + rtic::pend(Interrupt::UART0); + *c.shared.counter += 1; // <- no lock API required + let counter = *c.shared.counter; + hprintln!(" bar = {}", counter); + + debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator + } +} diff --git a/rtic/examples/message_passing.rs b/rtic/examples/spawn_arguments.rs similarity index 100% rename from rtic/examples/message_passing.rs rename to rtic/examples/spawn_arguments.rs diff --git a/rtic/examples/spawn_err.rs b/rtic/examples/spawn_err.rs new file mode 100644 index 0000000000..ee9904ae19 --- /dev/null +++ b/rtic/examples/spawn_err.rs @@ -0,0 +1,40 @@ +//! examples/spawn.rs + +#![deny(unsafe_code)] +#![deny(warnings)] +#![deny(missing_docs)] +#![no_main] +#![no_std] +#![feature(type_alias_impl_trait)] + +use panic_semihosting as _; + +#[rtic::app(device = lm3s6965, dispatchers = [SSI0])] +mod app { + use cortex_m_semihosting::{debug, hprintln}; + + #[shared] + struct Shared {} + + #[local] + struct Local {} + + #[init] + fn init(_: init::Context) -> (Shared, Local) { + hprintln!("init"); + foo::spawn().unwrap(); + match foo::spawn() { + Ok(_) => {} + Err(()) => hprintln!("Cannot spawn a spawned (running) task!"), + } + + (Shared {}, Local {}) + } + + #[task] + async fn foo(_: foo::Context) { + hprintln!("foo"); + + debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator + } +} diff --git a/rtic/examples/spawn_loop.rs b/rtic/examples/spawn_loop.rs new file mode 100644 index 0000000000..c1ad04e36c --- /dev/null +++ b/rtic/examples/spawn_loop.rs @@ -0,0 +1,42 @@ +//! examples/spawn.rs + +#![deny(unsafe_code)] +#![deny(warnings)] +#![deny(missing_docs)] +#![no_main] +#![no_std] +#![feature(type_alias_impl_trait)] + +use panic_semihosting as _; + +#[rtic::app(device = lm3s6965, dispatchers = [SSI0])] +mod app { + use cortex_m_semihosting::{debug, hprintln}; + + #[shared] + struct Shared {} + + #[local] + struct Local {} + + #[init] + fn init(_: init::Context) -> (Shared, Local) { + hprintln!("init"); + + (Shared {}, Local {}) + } + #[idle] + fn idle(_: idle::Context) -> ! { + for _ in 0..3 { + foo::spawn().unwrap(); + hprintln!("idle"); + } + debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator + loop {} + } + + #[task] + async fn foo(_: foo::Context) { + hprintln!("foo"); + } +} diff --git a/rtic/examples/zero-prio-task_err.no_rs b/rtic/examples/zero-prio-task_err.no_rs new file mode 100644 index 0000000000..ab67d82e3f --- /dev/null +++ b/rtic/examples/zero-prio-task_err.no_rs @@ -0,0 +1,66 @@ +//! examples/zero-prio-task.rs + +#![no_main] +#![no_std] +#![feature(type_alias_impl_trait)] +#![deny(missing_docs)] + +use core::marker::PhantomData; +use panic_semihosting as _; + +/// Does not impl send +pub struct NotSend { + _0: PhantomData<*const ()>, +} + +#[rtic::app(device = lm3s6965, peripherals = true)] +mod app { + use super::NotSend; + use core::marker::PhantomData; + use cortex_m_semihosting::{debug, hprintln}; + + #[shared] + struct Shared { + x: NotSend, + } + + #[local] + struct Local { + y: NotSend, + } + + #[init] + fn init(_cx: init::Context) -> (Shared, Local) { + hprintln!("init"); + + async_task::spawn().unwrap(); + async_task2::spawn().unwrap(); + + ( + Shared { + x: NotSend { _0: PhantomData }, + }, + Local { + y: NotSend { _0: PhantomData }, + }, + ) + } + + #[task(priority = 0, shared = [x], local = [y])] + async fn async_task(_: async_task::Context) { + hprintln!("hello from async"); + } + + #[task(priority = 0, shared = [x])] + async fn async_task2(_: async_task2::Context) { + hprintln!("hello from async2"); + } + + #[idle(shared = [x])] + fn idle(_: idle::Context) -> ! { + hprintln!("hello from idle"); + + debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator + loop {} + } +} From 274de31a78a47b4391987e9951e6d7cef56fb0d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Tj=C3=A4der?= Date: Mon, 30 Jan 2023 22:15:43 +0100 Subject: [PATCH 289/423] CI: Add mdbook-mermaid --- .github/workflows/build.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 3ef7d52e18..7df817cce8 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -684,6 +684,9 @@ jobs: - name: Install dependencies run: pip install git+https://github.com/linkchecker/linkchecker.git + - name: Install mdbook-mermaid + run: cargo install mdbook-mermaid + - name: mdBook Action uses: peaceiris/actions-mdbook@v1 with: @@ -774,6 +777,9 @@ jobs: # - name: Display Python version # run: python -c "import sys; print(sys.version)" # +# - name: Install mdbook-mermaid +# run: cargo install mdbook-mermaid +# # - name: mdBook Action # uses: peaceiris/actions-mdbook@v1 # with: From d3cadac90b109510ced86c585b57c35493d3fa2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Tj=C3=A4der?= Date: Mon, 30 Jan 2023 22:18:36 +0100 Subject: [PATCH 290/423] Book: Enable mermaid for mdbook --- book/en/book.toml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/book/en/book.toml b/book/en/book.toml index 98c5bf3f72..e134c291b9 100644 --- a/book/en/book.toml +++ b/book/en/book.toml @@ -4,6 +4,10 @@ multilingual = false src = "src" title = "Real-Time Interrupt-driven Concurrency" +[preprocessor.mermaid] +command = "mdbook-mermaid" + [output.html] git-repository-url = "https://github.com/rtic-rs/cortex-m-rtic" git-repository-icon = "fa-github" +additional-js = ["mermaid.min.js", "mermaid-init.js"] From 8d46fb9cf9f2b9cdd14844672ad840d777739cb2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Tj=C3=A4der?= Date: Mon, 30 Jan 2023 22:19:53 +0100 Subject: [PATCH 291/423] Book: Add mermaid files --- book/en/mermaid-init.js | 1 + book/en/mermaid.min.js | 1282 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 1283 insertions(+) create mode 100644 book/en/mermaid-init.js create mode 100644 book/en/mermaid.min.js diff --git a/book/en/mermaid-init.js b/book/en/mermaid-init.js new file mode 100644 index 0000000000..313a6e8bc8 --- /dev/null +++ b/book/en/mermaid-init.js @@ -0,0 +1 @@ +mermaid.initialize({startOnLoad:true}); diff --git a/book/en/mermaid.min.js b/book/en/mermaid.min.js new file mode 100644 index 0000000000..8c7ea4e4e4 --- /dev/null +++ b/book/en/mermaid.min.js @@ -0,0 +1,1282 @@ +/* MIT Licensed. Copyright (c) 2014 - 2022 Knut Sveidqvist */ +/* For license information please see https://github.com/mermaid-js/mermaid/blob/v9.2.2/LICENSE */ +(function(jr,wn){typeof exports=="object"&&typeof module<"u"?module.exports=wn():typeof define=="function"&&define.amd?define(wn):(jr=typeof globalThis<"u"?globalThis:jr||self,jr.mermaid=wn())})(this,function(){"use strict";var Ost=Object.defineProperty;var Fst=(jr,wn,fn)=>wn in jr?Ost(jr,wn,{enumerable:!0,configurable:!0,writable:!0,value:fn}):jr[wn]=fn;var vl=(jr,wn,fn)=>(Fst(jr,typeof wn!="symbol"?wn+"":wn,fn),fn);var jr=typeof globalThis<"u"?globalThis:typeof window<"u"?window:typeof global<"u"?global:typeof self<"u"?self:{};function wn(t){var e=t.default;if(typeof e=="function"){var r=function(){return e.apply(this,arguments)};r.prototype=e.prototype}else r={};return Object.defineProperty(r,"__esModule",{value:!0}),Object.keys(t).forEach(function(n){var i=Object.getOwnPropertyDescriptor(t,n);Object.defineProperty(r,n,i.get?i:{enumerable:!0,get:function(){return t[n]}})}),r}function fn(t){throw new Error('Could not dynamically require "'+t+'". Please configure the dynamicRequireTargets or/and ignoreDynamicRequires option of @rollup/plugin-commonjs appropriately for this require call to work.')}var y_={exports:{}};(function(t,e){(function(r,n){t.exports=n()})(jr,function(){var r;function n(){return r.apply(null,arguments)}function i(g){return g instanceof Array||Object.prototype.toString.call(g)==="[object Array]"}function a(g){return g!=null&&Object.prototype.toString.call(g)==="[object Object]"}function s(g,E){return Object.prototype.hasOwnProperty.call(g,E)}function o(g){if(Object.getOwnPropertyNames)return Object.getOwnPropertyNames(g).length===0;for(var E in g)if(s(g,E))return;return 1}function l(g){return g===void 0}function u(g){return typeof g=="number"||Object.prototype.toString.call(g)==="[object Number]"}function h(g){return g instanceof Date||Object.prototype.toString.call(g)==="[object Date]"}function d(g,E){for(var I=[],O=g.length,G=0;G>>0,O=0;Oue(g)?(ht=g+1,xt-ue(g)):(ht=g,xt);return{year:ht,dayOfYear:Mt}}function Ke(g,E,I){var O,G,ht=Ie(g.year(),E,I),ht=Math.floor((g.dayOfYear()-ht-1)/7)+1;return ht<1?O=ht+wr(G=g.year()-1,E,I):ht>wr(g.year(),E,I)?(O=ht-wr(g.year(),E,I),G=g.year()+1):(G=g.year(),O=ht),{week:O,year:G}}function wr(g,G,I){var O=Ie(g,G,I),G=Ie(g+1,G,I);return(ue(g)-O+G)/7}Y("w",["ww",2],"wo","week"),Y("W",["WW",2],"Wo","isoWeek"),W("week","w"),W("isoWeek","W"),Z("week",5),Z("isoWeek",5),ft("w",at),ft("ww",at,fe),ft("W",at),ft("WW",at,fe),we(["w","ww","W","WW"],function(g,E,I,O){E[O.substr(0,1)]=q(g)});function Ge(g,E){return g.slice(E,7).concat(g.slice(0,E))}Y("d",0,"do","day"),Y("dd",0,0,function(g){return this.localeData().weekdaysMin(this,g)}),Y("ddd",0,0,function(g){return this.localeData().weekdaysShort(this,g)}),Y("dddd",0,0,function(g){return this.localeData().weekdays(this,g)}),Y("e",0,0,"weekday"),Y("E",0,0,"isoWeekday"),W("day","d"),W("weekday","e"),W("isoWeekday","E"),Z("day",11),Z("weekday",11),Z("isoWeekday",11),ft("d",at),ft("e",at),ft("E",at),ft("dd",function(g,E){return E.weekdaysMinRegex(g)}),ft("ddd",function(g,E){return E.weekdaysShortRegex(g)}),ft("dddd",function(g,E){return E.weekdaysRegex(g)}),we(["dd","ddd","dddd"],function(g,E,I,O){O=I._locale.weekdaysParse(g,O,I._strict),O!=null?E.d=O:m(I).invalidWeekday=g}),we(["d","e","E"],function(g,E,I,O){E[O]=q(g)});var Ze="Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),qt="Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),st="Su_Mo_Tu_We_Th_Fr_Sa".split("_"),At=Tt,Nt=Tt,Jt=Tt;function ze(){function g(Ot,de){return de.length-Ot.length}for(var E,I,O,G=[],ht=[],xt=[],Mt=[],Vt=0;Vt<7;Vt++)O=p([2e3,1]).day(Vt),E=Dt(this.weekdaysMin(O,"")),I=Dt(this.weekdaysShort(O,"")),O=Dt(this.weekdays(O,"")),G.push(E),ht.push(I),xt.push(O),Mt.push(E),Mt.push(I),Mt.push(O);G.sort(g),ht.sort(g),xt.sort(g),Mt.sort(g),this._weekdaysRegex=new RegExp("^("+Mt.join("|")+")","i"),this._weekdaysShortRegex=this._weekdaysRegex,this._weekdaysMinRegex=this._weekdaysRegex,this._weekdaysStrictRegex=new RegExp("^("+xt.join("|")+")","i"),this._weekdaysShortStrictRegex=new RegExp("^("+ht.join("|")+")","i"),this._weekdaysMinStrictRegex=new RegExp("^("+G.join("|")+")","i")}function Pe(){return this.hours()%12||12}function qe(g,E){Y(g,0,0,function(){return this.localeData().meridiem(this.hours(),this.minutes(),E)})}function Tr(g,E){return E._meridiemParse}Y("H",["HH",2],0,"hour"),Y("h",["hh",2],0,Pe),Y("k",["kk",2],0,function(){return this.hours()||24}),Y("hmm",0,0,function(){return""+Pe.apply(this)+N(this.minutes(),2)}),Y("hmmss",0,0,function(){return""+Pe.apply(this)+N(this.minutes(),2)+N(this.seconds(),2)}),Y("Hmm",0,0,function(){return""+this.hours()+N(this.minutes(),2)}),Y("Hmmss",0,0,function(){return""+this.hours()+N(this.minutes(),2)+N(this.seconds(),2)}),qe("a",!0),qe("A",!1),W("hour","h"),Z("hour",13),ft("a",Tr),ft("A",Tr),ft("H",at),ft("h",at),ft("k",at),ft("HH",at,fe),ft("hh",at,fe),ft("kk",at,fe),ft("hmm",It),ft("hmmss",Lt),ft("Hmm",It),ft("Hmmss",Lt),Qt(["H","HH"],bt),Qt(["k","kk"],function(g,E,I){g=q(g),E[bt]=g===24?0:g}),Qt(["a","A"],function(g,E,I){I._isPm=I._locale.isPM(g),I._meridiem=g}),Qt(["h","hh"],function(g,E,I){E[bt]=q(g),m(I).bigHour=!0}),Qt("hmm",function(g,E,I){var O=g.length-2;E[bt]=q(g.substr(0,O)),E[Et]=q(g.substr(O)),m(I).bigHour=!0}),Qt("hmmss",function(g,E,I){var O=g.length-4,G=g.length-2;E[bt]=q(g.substr(0,O)),E[Et]=q(g.substr(O,2)),E[kt]=q(g.substr(G)),m(I).bigHour=!0}),Qt("Hmm",function(g,E,I){var O=g.length-2;E[bt]=q(g.substr(0,O)),E[Et]=q(g.substr(O))}),Qt("Hmmss",function(g,E,I){var O=g.length-4,G=g.length-2;E[bt]=q(g.substr(0,O)),E[Et]=q(g.substr(O,2)),E[kt]=q(g.substr(G))}),Tt=U("Hours",!0);var Ve,va={calendar:{sameDay:"[Today at] LT",nextDay:"[Tomorrow at] LT",nextWeek:"dddd [at] LT",lastDay:"[Yesterday at] LT",lastWeek:"[Last] dddd [at] LT",sameElse:"L"},longDateFormat:{LTS:"h:mm:ss A",LT:"h:mm A",L:"MM/DD/YYYY",LL:"MMMM D, YYYY",LLL:"MMMM D, YYYY h:mm A",LLLL:"dddd, MMMM D, YYYY h:mm A"},invalidDate:"Invalid date",ordinal:"%d",dayOfMonthOrdinalParse:/\d{1,2}/,relativeTime:{future:"in %s",past:"%s ago",s:"a few seconds",ss:"%d seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",w:"a week",ww:"%d weeks",M:"a month",MM:"%d months",y:"a year",yy:"%d years"},months:ne,monthsShort:ve,week:{dow:0,doy:6},weekdays:Ze,weekdaysMin:st,weekdaysShort:qt,meridiemParse:/[ap]\.?m?\.?/i},Ce={},Wi={};function E0(g){return g&&g.toLowerCase().replace("_","-")}function _u(g){for(var E,I,O,G,ht=0;ht=E&&function(xt,Mt){for(var Vt=Math.min(xt.length,Mt.length),Ot=0;Ot=E-1)break;E--}ht++}return Ve}function Ln(g){var E;if(Ce[g]===void 0&&!0&&t&&t.exports&&g.match("^[^/\\\\]*$")!=null)try{E=Ve._abbr,fn("./locale/"+g),Xt(E)}catch{Ce[g]=null}return Ce[g]}function Xt(g,E){return g&&((E=l(E)?ce(g):ee(g,E))?Ve=E:typeof console<"u"&&console.warn&&console.warn("Locale "+g+" not found. Did you forget to load it?")),Ve._abbr}function ee(g,E){if(E===null)return delete Ce[g],null;var I,O=va;if(E.abbr=g,Ce[g]!=null)L("defineLocaleOverride","use moment.updateLocale(localeName, config) to change an existing locale. moment.defineLocale(localeName, config) should only be used for creating a new locale See http://momentjs.com/guides/#/warnings/define-locale/ for more info."),O=Ce[g]._config;else if(E.parentLocale!=null)if(Ce[E.parentLocale]!=null)O=Ce[E.parentLocale]._config;else{if((I=Ln(E.parentLocale))==null)return Wi[E.parentLocale]||(Wi[E.parentLocale]=[]),Wi[E.parentLocale].push({name:g,config:E}),null;O=I._config}return Ce[g]=new w(B(O,E)),Wi[g]&&Wi[g].forEach(function(G){ee(G.name,G.config)}),Xt(g),Ce[g]}function ce(g){var E;if(!(g=g&&g._locale&&g._locale._abbr?g._locale._abbr:g))return Ve;if(!i(g)){if(E=Ln(g))return E;g=[g]}return _u(g)}function Pt(g){var E=g._a;return E&&m(g).overflow===-2&&(E=E[zt]<0||11yt(E[Ft],E[zt])?wt:E[bt]<0||24wr(ht,Vt,Ot)?m(O)._overflowWeeks=!0:de!=null?m(O)._overflowWeekday=!0:(ie=oe(ht,xt,Mt,Vt,Ot),O._a[Ft]=ie.year,O._dayOfYear=ie.dayOfYear)),g._dayOfYear!=null&&(G=Gi(g._a[Ft],I[Ft]),(g._dayOfYear>ue(G)||g._dayOfYear===0)&&(m(g)._overflowDayOfYear=!0),de=Hr(G,0,g._dayOfYear),g._a[zt]=de.getUTCMonth(),g._a[wt]=de.getUTCDate()),E=0;E<3&&g._a[E]==null;++E)g._a[E]=er[E]=I[E];for(;E<7;E++)g._a[E]=er[E]=g._a[E]==null?E===2?1:0:g._a[E];g._a[bt]===24&&g._a[Et]===0&&g._a[kt]===0&&g._a[Ut]===0&&(g._nextDay=!0,g._a[bt]=0),g._d=(g._useUTC?Hr:_a).apply(null,er),ht=g._useUTC?g._d.getUTCDay():g._d.getDay(),g._tzm!=null&&g._d.setUTCMinutes(g._d.getUTCMinutes()-g._tzm),g._nextDay&&(g._a[bt]=24),g._w&&g._w.d!==void 0&&g._w.d!==ht&&(m(g).weekdayMismatch=!0)}}function vu(g){if(g._f===n.ISO_8601)A0(g);else if(g._f===n.RFC_2822)Hi(g);else{g._a=[],m(g).empty=!0;for(var E,I,O,G,ht,xt=""+g._i,Mt=xt.length,Vt=0,Ot=lt(g._f,g._locale).match(z)||[],de=Ot.length,ie=0;ieg.valueOf():g.valueOf()"}),P.toJSON=function(){return this.isValid()?this.toISOString():null},P.toString=function(){return this.clone().locale("en").format("ddd MMM DD YYYY HH:mm:ss [GMT]ZZ")},P.unix=function(){return Math.floor(this.valueOf()/1e3)},P.valueOf=function(){return this._d.valueOf()-6e4*(this._offset||0)},P.creationData=function(){return{input:this._i,format:this._f,locale:this._locale,isUTC:this._isUTC,strict:this._strict}},P.eraName=function(){for(var g,E=this.localeData().eras(),I=0,O=E.length;Ithis.clone().month(0).utcOffset()||this.utcOffset()>this.clone().month(5).utcOffset()},P.isLocal=function(){return!!this.isValid()&&!this._isUTC},P.isUtcOffset=function(){return!!this.isValid()&&this._isUTC},P.isUtc=rR,P.isUTC=rR,P.zoneAbbr=function(){return this._isUTC?"UTC":""},P.zoneName=function(){return this._isUTC?"Coordinated Universal Time":""},P.dates=R("dates accessor is deprecated. Use date instead.",ls),P.months=R("months accessor is deprecated. Use month instead",se),P.years=R("years accessor is deprecated. Use year instead",N0),P.zone=R("moment().zone is deprecated, use moment().utcOffset instead. http://momentjs.com/guides/#/warnings/zone/",function(g,E){return g!=null?(this.utcOffset(g=typeof g!="string"?-g:g,E),this):-this.utcOffset()}),P.isDSTShifted=R("isDSTShifted is deprecated. See http://momentjs.com/guides/#/warnings/dst-shifted/ for more information",function(){if(!l(this._isDSTShifted))return this._isDSTShifted;var g,E={};return T(E,this),(E=M0(E))._a?(g=(E._isUTC?p:De)(E._a),this._isDSTShifted=this.isValid()&&0{},debug:(...t)=>{},info:(...t)=>{},warn:(...t)=>{},error:(...t)=>{},fatal:(...t)=>{}},D0=function(t="fatal"){let e=ji.fatal;typeof t=="string"?(t=t.toLowerCase(),t in ji&&(e=ji[t])):typeof t=="number"&&(e=t),H.trace=()=>{},H.debug=()=>{},H.info=()=>{},H.warn=()=>{},H.error=()=>{},H.fatal=()=>{},e<=ji.fatal&&(H.fatal=console.error?console.error.bind(console,Nn("FATAL"),"color: orange"):console.log.bind(console,"\x1B[35m",Nn("FATAL"))),e<=ji.error&&(H.error=console.error?console.error.bind(console,Nn("ERROR"),"color: orange"):console.log.bind(console,"\x1B[31m",Nn("ERROR"))),e<=ji.warn&&(H.warn=console.warn?console.warn.bind(console,Nn("WARN"),"color: orange"):console.log.bind(console,"\x1B[33m",Nn("WARN"))),e<=ji.info&&(H.info=console.info?console.info.bind(console,Nn("INFO"),"color: lightblue"):console.log.bind(console,"\x1B[34m",Nn("INFO"))),e<=ji.debug&&(H.debug=console.debug?console.debug.bind(console,Nn("DEBUG"),"color: lightgreen"):console.log.bind(console,"\x1B[32m",Nn("DEBUG"))),e<=ji.trace&&(H.trace=console.debug?console.debug.bind(console,Nn("TRACE"),"color: lightgreen"):console.log.bind(console,"\x1B[32m",Nn("TRACE")))},Nn=t=>`%c${Xn().format("ss.SSS")} : ${t} : `;var O0={};Object.defineProperty(O0,"__esModule",{value:!0});var ki=O0.sanitizeUrl=void 0,bR=/^([^\w]*)(javascript|data|vbscript)/im,_R=/&#(\w+)(^\w|;)?/g,vR=/[\u0000-\u001F\u007F-\u009F\u2000-\u200D\uFEFF]/gim,xR=/^([^:]+):/gm,kR=[".","/"];function wR(t){return kR.indexOf(t[0])>-1}function TR(t){return t.replace(_R,function(e,r){return String.fromCharCode(r)})}function ER(t){var e=TR(t||"").replace(vR,"").trim();if(!e)return"about:blank";if(wR(e))return e;var r=e.match(xR);if(!r)return e;var n=r[0];return bR.test(n)?"about:blank":e}ki=O0.sanitizeUrl=ER;function Qe(t,e){return t==null||e==null?NaN:te?1:t>=e?0:NaN}function m_(t,e){return t==null||e==null?NaN:et?1:e>=t?0:NaN}function ku(t){let e,r,n;t.length!==2?(e=Qe,r=(o,l)=>Qe(t(o),l),n=(o,l)=>t(o)-l):(e=t===Qe||t===m_?t:CR,r=t,n=t);function i(o,l,u=0,h=o.length){if(u>>1;r(o[d],l)<0?u=d+1:h=d}while(u>>1;r(o[d],l)<=0?u=d+1:h=d}while(uu&&n(o[d-1],l)>-n(o[d],l)?d-1:d}return{left:i,center:s,right:a}}function CR(){return 0}function b_(t){return t===null?NaN:+t}function*__(t,e){if(e===void 0)for(let r of t)r!=null&&(r=+r)>=r&&(yield r);else{let r=-1;for(let n of t)(n=e(n,++r,t))!=null&&(n=+n)>=n&&(yield n)}}const v_=ku(Qe),x_=v_.right,SR=v_.left,AR=ku(b_).center,cs=x_;function MR(t,e){if(!((e=+e)>=0))throw new RangeError("invalid r");let r=t.length;if(!((r=Math.floor(r))>=0))throw new RangeError("invalid length");if(!r||!e)return t;const n=F0(e),i=t.slice();return n(t,i,0,r,1),n(i,t,0,r,1),n(t,i,0,r,1),t}const k_=w_(F0),LR=w_(RR);function w_(t){return function(e,r,n=r){if(!((r=+r)>=0))throw new RangeError("invalid rx");if(!((n=+n)>=0))throw new RangeError("invalid ry");let{data:i,width:a,height:s}=e;if(!((a=Math.floor(a))>=0))throw new RangeError("invalid width");if(!((s=Math.floor(s!==void 0?s:i.length/a))>=0))throw new RangeError("invalid height");if(!a||!s||!r&&!n)return e;const o=r&&t(r),l=n&&t(n),u=i.slice();return o&&l?(ro(o,u,i,a,s),ro(o,i,u,a,s),ro(o,u,i,a,s),no(l,i,u,a,s),no(l,u,i,a,s),no(l,i,u,a,s)):o?(ro(o,i,u,a,s),ro(o,u,i,a,s),ro(o,i,u,a,s)):l&&(no(l,i,u,a,s),no(l,u,i,a,s),no(l,i,u,a,s)),e}}function ro(t,e,r,n,i){for(let a=0,s=n*i;a{i<<=2,a<<=2,s<<=2,e(r,n,i+0,a+0,s),e(r,n,i+1,a+1,s),e(r,n,i+2,a+2,s),e(r,n,i+3,a+3,s)}}function F0(t){const e=Math.floor(t);if(e===t)return IR(t);const r=t-e,n=2*t+1;return(i,a,s,o,l)=>{if(!((o-=l)>=s))return;let u=e*a[s];const h=l*e,d=h+l;for(let f=s,p=s+h;f{if(!((a-=s)>=i))return;let o=t*n[i];const l=s*t;for(let u=i,h=i+l;u=n&&++r;else{let n=-1;for(let i of t)(i=e(i,++n,t))!=null&&(i=+i)>=i&&++r}return r}function NR(t){return t.length|0}function BR(t){return!(t>0)}function DR(t){return typeof t!="object"||"length"in t?t:Array.from(t)}function OR(t){return e=>t(...e)}function FR(...t){const e=typeof t[t.length-1]=="function"&&OR(t.pop());t=t.map(DR);const r=t.map(NR),n=t.length-1,i=new Array(n+1).fill(0),a=[];if(n<0||r.some(BR))return a;for(;;){a.push(i.map((o,l)=>t[l][o]));let s=n;for(;++i[s]===r[s];){if(s===0)return e?a.map(e):a;i[s--]=0}}}function PR(t,e){var r=0,n=0;return Float64Array.from(t,e===void 0?i=>r+=+i||0:i=>r+=+e(i,n++,t)||0)}function T_(t,e){let r=0,n,i=0,a=0;if(e===void 0)for(let s of t)s!=null&&(s=+s)>=s&&(n=s-i,i+=n/++r,a+=n*(s-i));else{let s=-1;for(let o of t)(o=e(o,++s,t))!=null&&(o=+o)>=o&&(n=o-i,i+=n/++r,a+=n*(o-i))}if(r>1)return a/(r-1)}function E_(t,e){const r=T_(t,e);return r&&Math.sqrt(r)}function xl(t,e){let r,n;if(e===void 0)for(const i of t)i!=null&&(r===void 0?i>=i&&(r=n=i):(r>i&&(r=i),n=a&&(r=n=a):(r>a&&(r=a),n0){for(s=e[--r];r>0&&(n=s,i=e[--r],s=n+i,a=i-(s-n),!a););r>0&&(a<0&&e[r-1]<0||a>0&&e[r-1]>0)&&(i=a*2,n=s+i,i==n-s&&(s=n))}return s}}function qR(t,e){const r=new _r;if(e===void 0)for(let n of t)(n=+n)&&r.add(n);else{let n=-1;for(let i of t)(i=+e(i,++n,t))&&r.add(i)}return+r}function VR(t,e){const r=new _r;let n=-1;return Float64Array.from(t,e===void 0?i=>r.add(+i||0):i=>r.add(+e(i,++n,t)||0))}class kl extends Map{constructor(e,r=A_){if(super(),Object.defineProperties(this,{_intern:{value:new Map},_key:{value:r}}),e!=null)for(const[n,i]of e)this.set(n,i)}get(e){return super.get(P0(this,e))}has(e){return super.has(P0(this,e))}set(e,r){return super.set(C_(this,e),r)}delete(e){return super.delete(S_(this,e))}}class us extends Set{constructor(e,r=A_){if(super(),Object.defineProperties(this,{_intern:{value:new Map},_key:{value:r}}),e!=null)for(const n of e)this.add(n)}has(e){return super.has(P0(this,e))}add(e){return super.add(C_(this,e))}delete(e){return super.delete(S_(this,e))}}function P0({_intern:t,_key:e},r){const n=e(r);return t.has(n)?t.get(n):r}function C_({_intern:t,_key:e},r){const n=e(r);return t.has(n)?t.get(n):(t.set(n,r),r)}function S_({_intern:t,_key:e},r){const n=e(r);return t.has(n)&&(r=t.get(n),t.delete(n)),r}function A_(t){return t!==null&&typeof t=="object"?t.valueOf():t}function io(t){return t}function M_(t,...e){return ao(t,io,io,e)}function L_(t,...e){return ao(t,Array.from,io,e)}function R_(t,e){for(let r=1,n=e.length;ri.pop().map(([a,s])=>[...i,a,s]));return t}function zR(t,...e){return R_(L_(t,...e),e)}function YR(t,e,...r){return R_(N_(t,e,...r),r)}function I_(t,e,...r){return ao(t,io,e,r)}function N_(t,e,...r){return ao(t,Array.from,e,r)}function UR(t,...e){return ao(t,io,B_,e)}function WR(t,...e){return ao(t,Array.from,B_,e)}function B_(t){if(t.length!==1)throw new Error("duplicate key");return t[0]}function ao(t,e,r,n){return function i(a,s){if(s>=n.length)return r(a);const o=new kl,l=n[s++];let u=-1;for(const h of a){const d=l(h,++u,a),f=o.get(d);f?f.push(h):o.set(d,[h])}for(const[h,d]of o)o.set(h,i(d,s));return e(o)}(t,0)}function D_(t,e){return Array.from(e,r=>t[r])}function q0(t,...e){if(typeof t[Symbol.iterator]!="function")throw new TypeError("values is not iterable");t=Array.from(t);let[r]=e;if(r&&r.length!==2||e.length>1){const n=Uint32Array.from(t,(i,a)=>a);return e.length>1?(e=e.map(i=>t.map(i)),n.sort((i,a)=>{for(const s of e){const o=so(s[i],s[a]);if(o)return o}})):(r=t.map(r),n.sort((i,a)=>so(r[i],r[a]))),D_(t,n)}return t.sort(V0(r))}function V0(t=Qe){if(t===Qe)return so;if(typeof t!="function")throw new TypeError("compare is not a function");return(e,r)=>{const n=t(e,r);return n||n===0?n:(t(r,r)===0)-(t(e,e)===0)}}function so(t,e){return(t==null||!(t>=t))-(e==null||!(e>=e))||(te?1:0)}function HR(t,e,r){return(e.length!==2?q0(I_(t,e,r),([n,i],[a,s])=>Qe(i,s)||Qe(n,a)):q0(M_(t,r),([n,i],[a,s])=>e(i,s)||Qe(n,a))).map(([n])=>n)}var GR=Array.prototype,jR=GR.slice;function Tu(t){return()=>t}var z0=Math.sqrt(50),Y0=Math.sqrt(10),U0=Math.sqrt(2);function hs(t,e,r){var n,i=-1,a,s,o;if(e=+e,t=+t,r=+r,t===e&&r>0)return[t];if((n=e0){let l=Math.round(t/o),u=Math.round(e/o);for(l*oe&&--u,s=new Array(a=u-l+1);++ie&&--u,s=new Array(a=u-l+1);++i=0?(a>=z0?10:a>=Y0?5:a>=U0?2:1)*Math.pow(10,i):-Math.pow(10,-i)/(a>=z0?10:a>=Y0?5:a>=U0?2:1)}function wl(t,e,r){var n=Math.abs(e-t)/Math.max(0,r),i=Math.pow(10,Math.floor(Math.log(n)/Math.LN10)),a=n/i;return a>=z0?i*=10:a>=Y0?i*=5:a>=U0&&(i*=2),e0?(t=Math.floor(t/i)*i,e=Math.ceil(e/i)*i):i<0&&(t=Math.ceil(t*i)/i,e=Math.floor(e*i)/i),n=i}}function W0(t){return Math.ceil(Math.log(wu(t))/Math.LN2)+1}function F_(){var t=io,e=xl,r=W0;function n(i){Array.isArray(i)||(i=Array.from(i));var a,s=i.length,o,l,u=new Array(s);for(a=0;a=f)if(b>=f&&e===xl){const k=oo(d,f,x);isFinite(k)&&(k>0?f=(Math.floor(f/k)+1)*k:k<0&&(f=(Math.ceil(f*-k)+1)/-k))}else p.pop()}for(var m=p.length;p[0]<=d;)p.shift(),--m;for(;p[m-1]>f;)p.pop(),--m;var _=new Array(m+1),y;for(a=0;a<=m;++a)y=_[a]=[],y.x0=a>0?p[a-1]:d,y.x1=a0)for(a=0;a=n)&&(r=n);else{let n=-1;for(let i of t)(i=e(i,++n,t))!=null&&(r=i)&&(r=i)}return r}function H0(t,e){let r,n=-1,i=-1;if(e===void 0)for(const a of t)++i,a!=null&&(r=a)&&(r=a,n=i);else for(let a of t)(a=e(a,++i,t))!=null&&(r=a)&&(r=a,n=i);return n}function Tl(t,e){let r;if(e===void 0)for(const n of t)n!=null&&(r>n||r===void 0&&n>=n)&&(r=n);else{let n=-1;for(let i of t)(i=e(i,++n,t))!=null&&(r>i||r===void 0&&i>=i)&&(r=i)}return r}function G0(t,e){let r,n=-1,i=-1;if(e===void 0)for(const a of t)++i,a!=null&&(r>a||r===void 0&&a>=a)&&(r=a,n=i);else for(let a of t)(a=e(a,++i,t))!=null&&(r>a||r===void 0&&a>=a)&&(r=a,n=i);return n}function Eu(t,e,r=0,n=t.length-1,i){for(i=i===void 0?so:V0(i);n>r;){if(n-r>600){const l=n-r+1,u=e-r+1,h=Math.log(l),d=.5*Math.exp(2*h/3),f=.5*Math.sqrt(h*d*(l-d)/l)*(u-l/2<0?-1:1),p=Math.max(r,Math.floor(e-u*d/l+f)),m=Math.min(n,Math.floor(e+(l-u)*d/l+f));Eu(t,e,p,m,i)}const a=t[e];let s=r,o=n;for(El(t,r,e),i(t[n],a)>0&&El(t,r,n);s0;)--o}i(t[r],a)===0?El(t,r,o):(++o,El(t,o,n)),o<=e&&(r=o+1),e<=o&&(n=o-1)}return t}function El(t,e,r){const n=t[e];t[e]=t[r],t[r]=n}function P_(t,e=Qe){let r,n=!1;if(e.length===1){let i;for(const a of t){const s=e(a);(n?Qe(s,i)>0:Qe(s,s)===0)&&(r=a,i=s,n=!0)}}else for(const i of t)(n?e(i,r)>0:e(i,i)===0)&&(r=i,n=!0);return r}function Cl(t,e,r){if(t=Float64Array.from(__(t,r)),!!(n=t.length)){if((e=+e)<=0||n<2)return Tl(t);if(e>=1)return lo(t);var n,i=(n-1)*e,a=Math.floor(i),s=lo(Eu(t,a).subarray(0,a+1)),o=Tl(t.subarray(a+1));return s+(o-s)*(i-a)}}function q_(t,e,r=b_){if(!!(n=t.length)){if((e=+e)<=0||n<2)return+r(t[0],0,t);if(e>=1)return+r(t[n-1],n-1,t);var n,i=(n-1)*e,a=Math.floor(i),s=+r(t[a],a,t),o=+r(t[a+1],a+1,t);return s+(o-s)*(i-a)}}function V_(t,e,r){if(t=Float64Array.from(__(t,r)),!!(n=t.length)){if((e=+e)<=0||n<2)return G0(t);if(e>=1)return H0(t);var n,i=Math.floor((n-1)*e),a=(o,l)=>so(t[o],t[l]),s=Eu(Uint32Array.from(t,(o,l)=>l),i,0,n-1,a);return P_(s.subarray(0,i+1),o=>t[o])}}function $R(t,e,r){return Math.ceil((r-e)/(2*(Cl(t,.75)-Cl(t,.25))*Math.pow(wu(t),-1/3)))}function XR(t,e,r){return Math.ceil((r-e)*Math.cbrt(wu(t))/(3.49*E_(t)))}function KR(t,e){let r=0,n=0;if(e===void 0)for(let i of t)i!=null&&(i=+i)>=i&&(++r,n+=i);else{let i=-1;for(let a of t)(a=e(a,++i,t))!=null&&(a=+a)>=a&&(++r,n+=a)}if(r)return n/r}function ZR(t,e){return Cl(t,.5,e)}function QR(t,e){return V_(t,.5,e)}function*JR(t){for(const e of t)yield*e}function j0(t){return Array.from(JR(t))}function tI(t,e){const r=new kl;if(e===void 0)for(let a of t)a!=null&&a>=a&&r.set(a,(r.get(a)||0)+1);else{let a=-1;for(let s of t)(s=e(s,++a,t))!=null&&s>=s&&r.set(s,(r.get(s)||0)+1)}let n,i=0;for(const[a,s]of r)s>i&&(i=s,n=a);return n}function eI(t,e=rI){const r=[];let n,i=!1;for(const a of t)i&&r.push(e(n,a)),n=a,i=!0;return r}function rI(t,e){return[t,e]}function Ca(t,e,r){t=+t,e=+e,r=(i=arguments.length)<2?(e=t,t=0,1):i<3?1:+r;for(var n=-1,i=Math.max(0,Math.ceil((e-t)/r))|0,a=new Array(i);++ne(r[o],r[l]);let a,s;return Uint32Array.from(r,(o,l)=>l).sort(e===Qe?(o,l)=>so(r[o],r[l]):V0(i)).forEach((o,l)=>{const u=i(o,a===void 0?o:a);u>=0?((a===void 0||u>0)&&(a=o,s=l),n[o]=s):n[o]=NaN}),n}function iI(t,e=Qe){let r,n=!1;if(e.length===1){let i;for(const a of t){const s=e(a);(n?Qe(s,i)<0:Qe(s,s)===0)&&(r=a,i=s,n=!0)}}else for(const i of t)(n?e(i,r)<0:e(i,i)===0)&&(r=i,n=!0);return r}function z_(t,e=Qe){if(e.length===1)return G0(t,e);let r,n=-1,i=-1;for(const a of t)++i,(n<0?e(a,a)===0:e(a,r)<0)&&(r=a,n=i);return n}function aI(t,e=Qe){if(e.length===1)return H0(t,e);let r,n=-1,i=-1;for(const a of t)++i,(n<0?e(a,a)===0:e(a,r)>0)&&(r=a,n=i);return n}function sI(t,e){const r=z_(t,e);return r<0?void 0:r}const oI=Y_(Math.random);function Y_(t){return function(r,n=0,i=r.length){let a=i-(n=+n);for(;a;){const s=t()*a--|0,o=r[a+n];r[a+n]=r[s+n],r[s+n]=o}return r}}function lI(t,e){let r=0;if(e===void 0)for(let n of t)(n=+n)&&(r+=n);else{let n=-1;for(let i of t)(i=+e(i,++n,t))&&(r+=i)}return r}function U_(t){if(!(a=t.length))return[];for(var e=-1,r=Tl(t,cI),n=new Array(r);++ee(r,n,t))}function gI(t,e,r){if(typeof e!="function")throw new TypeError("reducer is not a function");const n=t[Symbol.iterator]();let i,a,s=-1;if(arguments.length<3){if({done:i,value:r}=n.next(),i)return;++s}for(;{done:i,value:a}=n.next(),!i;)r=e(r,a,++s,t);return r}function yI(t){if(typeof t[Symbol.iterator]!="function")throw new TypeError("values is not iterable");return Array.from(t).reverse()}function mI(t,...e){t=new us(t);for(const r of e)for(const n of r)t.delete(n);return t}function bI(t,e){const r=e[Symbol.iterator](),n=new us;for(const i of t){if(n.has(i))return!1;let a,s;for(;({value:a,done:s}=r.next())&&!s;){if(Object.is(i,a))return!1;n.add(a)}}return!0}function _I(t,...e){t=new us(t),e=e.map(vI);t:for(const r of t)for(const n of e)if(!n.has(r)){t.delete(r);continue t}return t}function vI(t){return t instanceof us?t:new us(t)}function W_(t,e){const r=t[Symbol.iterator](),n=new Set;for(const i of e){const a=H_(i);if(n.has(a))continue;let s,o;for(;{value:s,done:o}=r.next();){if(o)return!1;const l=H_(s);if(n.add(l),Object.is(a,l))break}}return!0}function H_(t){return t!==null&&typeof t=="object"?t.valueOf():t}function xI(t,e){return W_(e,t)}function kI(...t){const e=new us;for(const r of t)for(const n of r)e.add(n);return e}function wI(t){return t}var Cu=1,Su=2,$0=3,Sl=4,G_=1e-6;function TI(t){return"translate("+t+",0)"}function EI(t){return"translate(0,"+t+")"}function CI(t){return e=>+t(e)}function SI(t,e){return e=Math.max(0,t.bandwidth()-e*2)/2,t.round()&&(e=Math.round(e)),r=>+t(r)+e}function AI(){return!this.__axis}function Au(t,e){var r=[],n=null,i=null,a=6,s=6,o=3,l=typeof window<"u"&&window.devicePixelRatio>1?0:.5,u=t===Cu||t===Sl?-1:1,h=t===Sl||t===Su?"x":"y",d=t===Cu||t===$0?TI:EI;function f(p){var m=n==null?e.ticks?e.ticks.apply(e,r):e.domain():n,_=i==null?e.tickFormat?e.tickFormat.apply(e,r):wI:i,y=Math.max(a,0)+o,b=e.range(),x=+b[0]+l,k=+b[b.length-1]+l,T=(e.bandwidth?SI:CI)(e.copy(),l),C=p.selection?p.selection():p,M=C.selectAll(".domain").data([null]),S=C.selectAll(".tick").data(m,e).order(),R=S.exit(),A=S.enter().append("g").attr("class","tick"),L=S.select("line"),v=S.select("text");M=M.merge(M.enter().insert("path",".tick").attr("class","domain").attr("stroke","currentColor")),S=S.merge(A),L=L.merge(A.append("line").attr("stroke","currentColor").attr(h+"2",u*a)),v=v.merge(A.append("text").attr("fill","currentColor").attr(h,u*y).attr("dy",t===Cu?"0em":t===$0?"0.71em":"0.32em")),p!==C&&(M=M.transition(p),S=S.transition(p),L=L.transition(p),v=v.transition(p),R=R.transition(p).attr("opacity",G_).attr("transform",function(B){return isFinite(B=T(B))?d(B+l):this.getAttribute("transform")}),A.attr("opacity",G_).attr("transform",function(B){var w=this.parentNode.__axis;return d((w&&isFinite(w=w(B))?w:T(B))+l)})),R.remove(),M.attr("d",t===Sl||t===Su?s?"M"+u*s+","+x+"H"+l+"V"+k+"H"+u*s:"M"+l+","+x+"V"+k:s?"M"+x+","+u*s+"V"+l+"H"+k+"V"+u*s:"M"+x+","+l+"H"+k),S.attr("opacity",1).attr("transform",function(B){return d(T(B)+l)}),L.attr(h+"2",u*a),v.attr(h,u*y).text(_),C.filter(AI).attr("fill","none").attr("font-size",10).attr("font-family","sans-serif").attr("text-anchor",t===Su?"start":t===Sl?"end":"middle"),C.each(function(){this.__axis=T})}return f.scale=function(p){return arguments.length?(e=p,f):e},f.ticks=function(){return r=Array.from(arguments),f},f.tickArguments=function(p){return arguments.length?(r=p==null?[]:Array.from(p),f):r.slice()},f.tickValues=function(p){return arguments.length?(n=p==null?null:Array.from(p),f):n&&n.slice()},f.tickFormat=function(p){return arguments.length?(i=p,f):i},f.tickSize=function(p){return arguments.length?(a=s=+p,f):a},f.tickSizeInner=function(p){return arguments.length?(a=+p,f):a},f.tickSizeOuter=function(p){return arguments.length?(s=+p,f):s},f.tickPadding=function(p){return arguments.length?(o=+p,f):o},f.offset=function(p){return arguments.length?(l=+p,f):l},f}function j_(t){return Au(Cu,t)}function MI(t){return Au(Su,t)}function $_(t){return Au($0,t)}function LI(t){return Au(Sl,t)}var RI={value:()=>{}};function fs(){for(var t=0,e=arguments.length,r={},n;t=0&&(n=r.slice(i+1),r=r.slice(0,i)),r&&!e.hasOwnProperty(r))throw new Error("unknown type: "+r);return{type:r,name:n}})}Mu.prototype=fs.prototype={constructor:Mu,on:function(t,e){var r=this._,n=II(t+"",r),i,a=-1,s=n.length;if(arguments.length<2){for(;++a0)for(var r=new Array(i),n=0,i,a;n=0&&(e=t.slice(0,r))!=="xmlns"&&(t=t.slice(r+1)),K0.hasOwnProperty(e)?{space:K0[e],local:t}:t}function BI(t){return function(){var e=this.ownerDocument,r=this.namespaceURI;return r===X0&&e.documentElement.namespaceURI===X0?e.createElement(t):e.createElementNS(r,t)}}function DI(t){return function(){return this.ownerDocument.createElementNS(t.space,t.local)}}function Lu(t){var e=Al(t);return(e.local?DI:BI)(e)}function OI(){}function Ru(t){return t==null?OI:function(){return this.querySelector(t)}}function FI(t){typeof t!="function"&&(t=Ru(t));for(var e=this._groups,r=e.length,n=new Array(r),i=0;i=k&&(k=x+1);!(C=y[k])&&++k=0;)(s=n[i])&&(a&&s.compareDocumentPosition(a)^4&&a.parentNode.insertBefore(s,a),a=s);return this}function oN(t){t||(t=lN);function e(d,f){return d&&f?t(d.__data__,f.__data__):!d-!f}for(var r=this._groups,n=r.length,i=new Array(n),a=0;ae?1:t>=e?0:NaN}function cN(){var t=arguments[0];return arguments[0]=this,t.apply(null,arguments),this}function uN(){return Array.from(this)}function hN(){for(var t=this._groups,e=0,r=t.length;e1?this.each((e==null?kN:typeof e=="function"?TN:wN)(t,e,r==null?"":r)):ds(this.node(),t)}function ds(t,e){return t.style.getPropertyValue(e)||J0(t).getComputedStyle(t,null).getPropertyValue(e)}function CN(t){return function(){delete this[t]}}function SN(t,e){return function(){this[t]=e}}function AN(t,e){return function(){var r=e.apply(this,arguments);r==null?delete this[t]:this[t]=r}}function MN(t,e){return arguments.length>1?this.each((e==null?CN:typeof e=="function"?AN:SN)(t,e)):this.node()[t]}function J_(t){return t.trim().split(/^|\s+/)}function td(t){return t.classList||new t5(t)}function t5(t){this._node=t,this._names=J_(t.getAttribute("class")||"")}t5.prototype={add:function(t){var e=this._names.indexOf(t);e<0&&(this._names.push(t),this._node.setAttribute("class",this._names.join(" ")))},remove:function(t){var e=this._names.indexOf(t);e>=0&&(this._names.splice(e,1),this._node.setAttribute("class",this._names.join(" ")))},contains:function(t){return this._names.indexOf(t)>=0}};function e5(t,e){for(var r=td(t),n=-1,i=e.length;++n=0&&(r=e.slice(n+1),e=e.slice(0,n)),{type:e,name:r}})}function nB(t){return function(){var e=this.__on;if(!!e){for(var r=0,n=-1,i=e.length,a;rTn(r,e))}function Nu(t){return typeof t=="string"?new $r([document.querySelectorAll(t)],[document.documentElement]):new $r([K_(t)],ed)}const pB={passive:!1},Ml={capture:!0,passive:!1};function nd(t){t.stopImmediatePropagation()}function co(t){t.preventDefault(),t.stopImmediatePropagation()}function Bu(t){var e=t.document.documentElement,r=St(t).on("dragstart.drag",co,Ml);"onselectstart"in e?r.on("selectstart.drag",co,Ml):(e.__noselect=e.style.MozUserSelect,e.style.MozUserSelect="none")}function Du(t,e){var r=t.document.documentElement,n=St(t).on("dragstart.drag",null);e&&(n.on("click.drag",co,Ml),setTimeout(function(){n.on("click.drag",null)},0)),"onselectstart"in r?n.on("selectstart.drag",null):(r.style.MozUserSelect=r.__noselect,delete r.__noselect)}const Ou=t=>()=>t;function id(t,{sourceEvent:e,subject:r,target:n,identifier:i,active:a,x:s,y:o,dx:l,dy:u,dispatch:h}){Object.defineProperties(this,{type:{value:t,enumerable:!0,configurable:!0},sourceEvent:{value:e,enumerable:!0,configurable:!0},subject:{value:r,enumerable:!0,configurable:!0},target:{value:n,enumerable:!0,configurable:!0},identifier:{value:i,enumerable:!0,configurable:!0},active:{value:a,enumerable:!0,configurable:!0},x:{value:s,enumerable:!0,configurable:!0},y:{value:o,enumerable:!0,configurable:!0},dx:{value:l,enumerable:!0,configurable:!0},dy:{value:u,enumerable:!0,configurable:!0},_:{value:h}})}id.prototype.on=function(){var t=this._.on.apply(this._,arguments);return t===this._?this:t};function gB(t){return!t.ctrlKey&&!t.button}function yB(){return this.parentNode}function mB(t,e){return e==null?{x:t.x,y:t.y}:e}function bB(){return navigator.maxTouchPoints||"ontouchstart"in this}function _B(){var t=gB,e=yB,r=mB,n=bB,i={},a=fs("start","drag","end"),s=0,o,l,u,h,d=0;function f(T){T.on("mousedown.drag",p).filter(n).on("touchstart.drag",y).on("touchmove.drag",b,pB).on("touchend.drag touchcancel.drag",x).style("touch-action","none").style("-webkit-tap-highlight-color","rgba(0,0,0,0)")}function p(T,C){if(!(h||!t.call(this,T,C))){var M=k(this,e.call(this,T,C),T,C,"mouse");!M||(St(T.view).on("mousemove.drag",m,Ml).on("mouseup.drag",_,Ml),Bu(T.view),nd(T),u=!1,o=T.clientX,l=T.clientY,M("start",T))}}function m(T){if(co(T),!u){var C=T.clientX-o,M=T.clientY-l;u=C*C+M*M>d}i.mouse("drag",T)}function _(T){St(T.view).on("mousemove.drag mouseup.drag",null),Du(T.view,u),co(T),i.mouse("end",T)}function y(T,C){if(!!t.call(this,T,C)){var M=T.changedTouches,S=e.call(this,T,C),R=M.length,A,L;for(A=0;A>8&15|e>>4&240,e>>4&15|e&240,(e&15)<<4|e&15,1):r===8?Fu(e>>24&255,e>>16&255,e>>8&255,(e&255)/255):r===4?Fu(e>>12&15|e>>8&240,e>>8&15|e>>4&240,e>>4&15|e&240,((e&15)<<4|e&15)/255):null):(e=xB.exec(t))?new Er(e[1],e[2],e[3],1):(e=kB.exec(t))?new Er(e[1]*255/100,e[2]*255/100,e[3]*255/100,1):(e=wB.exec(t))?Fu(e[1],e[2],e[3],e[4]):(e=TB.exec(t))?Fu(e[1]*255/100,e[2]*255/100,e[3]*255/100,e[4]):(e=EB.exec(t))?f5(e[1],e[2]/100,e[3]/100,1):(e=CB.exec(t))?f5(e[1],e[2]/100,e[3]/100,e[4]):s5.hasOwnProperty(t)?c5(s5[t]):t==="transparent"?new Er(NaN,NaN,NaN,0):null}function c5(t){return new Er(t>>16&255,t>>8&255,t&255,1)}function Fu(t,e,r,n){return n<=0&&(t=e=r=NaN),new Er(t,e,r,n)}function ad(t){return t instanceof Sa||(t=Aa(t)),t?(t=t.rgb(),new Er(t.r,t.g,t.b,t.opacity)):new Er}function po(t,e,r,n){return arguments.length===1?ad(t):new Er(t,e,r,n==null?1:n)}function Er(t,e,r,n){this.r=+t,this.g=+e,this.b=+r,this.opacity=+n}uo(Er,po,Ll(Sa,{brighter(t){return t=t==null?ho:Math.pow(ho,t),new Er(this.r*t,this.g*t,this.b*t,this.opacity)},darker(t){return t=t==null?gs:Math.pow(gs,t),new Er(this.r*t,this.g*t,this.b*t,this.opacity)},rgb(){return this},clamp(){return new Er(ys(this.r),ys(this.g),ys(this.b),Pu(this.opacity))},displayable(){return-.5<=this.r&&this.r<255.5&&-.5<=this.g&&this.g<255.5&&-.5<=this.b&&this.b<255.5&&0<=this.opacity&&this.opacity<=1},hex:u5,formatHex:u5,formatHex8:MB,formatRgb:h5,toString:h5}));function u5(){return`#${ms(this.r)}${ms(this.g)}${ms(this.b)}`}function MB(){return`#${ms(this.r)}${ms(this.g)}${ms(this.b)}${ms((isNaN(this.opacity)?1:this.opacity)*255)}`}function h5(){const t=Pu(this.opacity);return`${t===1?"rgb(":"rgba("}${ys(this.r)}, ${ys(this.g)}, ${ys(this.b)}${t===1?")":`, ${t})`}`}function Pu(t){return isNaN(t)?1:Math.max(0,Math.min(1,t))}function ys(t){return Math.max(0,Math.min(255,Math.round(t)||0))}function ms(t){return t=ys(t),(t<16?"0":"")+t.toString(16)}function f5(t,e,r,n){return n<=0?t=e=r=NaN:r<=0||r>=1?t=e=NaN:e<=0&&(t=NaN),new Kn(t,e,r,n)}function d5(t){if(t instanceof Kn)return new Kn(t.h,t.s,t.l,t.opacity);if(t instanceof Sa||(t=Aa(t)),!t)return new Kn;if(t instanceof Kn)return t;t=t.rgb();var e=t.r/255,r=t.g/255,n=t.b/255,i=Math.min(e,r,n),a=Math.max(e,r,n),s=NaN,o=a-i,l=(a+i)/2;return o?(e===a?s=(r-n)/o+(r0&&l<1?0:s,new Kn(s,o,l,t.opacity)}function qu(t,e,r,n){return arguments.length===1?d5(t):new Kn(t,e,r,n==null?1:n)}function Kn(t,e,r,n){this.h=+t,this.s=+e,this.l=+r,this.opacity=+n}uo(Kn,qu,Ll(Sa,{brighter(t){return t=t==null?ho:Math.pow(ho,t),new Kn(this.h,this.s,this.l*t,this.opacity)},darker(t){return t=t==null?gs:Math.pow(gs,t),new Kn(this.h,this.s,this.l*t,this.opacity)},rgb(){var t=this.h%360+(this.h<0)*360,e=isNaN(t)||isNaN(this.s)?0:this.s,r=this.l,n=r+(r<.5?r:1-r)*e,i=2*r-n;return new Er(sd(t>=240?t-240:t+120,i,n),sd(t,i,n),sd(t<120?t+240:t-120,i,n),this.opacity)},clamp(){return new Kn(p5(this.h),Vu(this.s),Vu(this.l),Pu(this.opacity))},displayable(){return(0<=this.s&&this.s<=1||isNaN(this.s))&&0<=this.l&&this.l<=1&&0<=this.opacity&&this.opacity<=1},formatHsl(){const t=Pu(this.opacity);return`${t===1?"hsl(":"hsla("}${p5(this.h)}, ${Vu(this.s)*100}%, ${Vu(this.l)*100}%${t===1?")":`, ${t})`}`}}));function p5(t){return t=(t||0)%360,t<0?t+360:t}function Vu(t){return Math.max(0,Math.min(1,t||0))}function sd(t,e,r){return(t<60?e+(r-e)*t/60:t<180?r:t<240?e+(r-e)*(240-t)/60:e)*255}const g5=Math.PI/180,y5=180/Math.PI,zu=18,m5=.96422,b5=1,_5=.82521,v5=4/29,go=6/29,x5=3*go*go,LB=go*go*go;function k5(t){if(t instanceof Zn)return new Zn(t.l,t.a,t.b,t.opacity);if(t instanceof Ti)return T5(t);t instanceof Er||(t=ad(t));var e=ud(t.r),r=ud(t.g),n=ud(t.b),i=od((.2225045*e+.7168786*r+.0606169*n)/b5),a,s;return e===r&&r===n?a=s=i:(a=od((.4360747*e+.3850649*r+.1430804*n)/m5),s=od((.0139322*e+.0971045*r+.7141733*n)/_5)),new Zn(116*i-16,500*(a-i),200*(i-s),t.opacity)}function RB(t,e){return new Zn(t,0,0,e==null?1:e)}function Yu(t,e,r,n){return arguments.length===1?k5(t):new Zn(t,e,r,n==null?1:n)}function Zn(t,e,r,n){this.l=+t,this.a=+e,this.b=+r,this.opacity=+n}uo(Zn,Yu,Ll(Sa,{brighter(t){return new Zn(this.l+zu*(t==null?1:t),this.a,this.b,this.opacity)},darker(t){return new Zn(this.l-zu*(t==null?1:t),this.a,this.b,this.opacity)},rgb(){var t=(this.l+16)/116,e=isNaN(this.a)?t:t+this.a/500,r=isNaN(this.b)?t:t-this.b/200;return e=m5*ld(e),t=b5*ld(t),r=_5*ld(r),new Er(cd(3.1338561*e-1.6168667*t-.4906146*r),cd(-.9787684*e+1.9161415*t+.033454*r),cd(.0719453*e-.2289914*t+1.4052427*r),this.opacity)}}));function od(t){return t>LB?Math.pow(t,1/3):t/x5+v5}function ld(t){return t>go?t*t*t:x5*(t-v5)}function cd(t){return 255*(t<=.0031308?12.92*t:1.055*Math.pow(t,1/2.4)-.055)}function ud(t){return(t/=255)<=.04045?t/12.92:Math.pow((t+.055)/1.055,2.4)}function w5(t){if(t instanceof Ti)return new Ti(t.h,t.c,t.l,t.opacity);if(t instanceof Zn||(t=k5(t)),t.a===0&&t.b===0)return new Ti(NaN,0=1?(r=1,e-1):Math.floor(r*e),i=t[n],a=t[n+1],s=n>0?t[n-1]:2*i-a,o=n()=>t;function I5(t,e){return function(r){return t+r*e}}function BB(t,e,r){return t=Math.pow(t,r),e=Math.pow(e,r)-t,r=1/r,function(n){return Math.pow(t+n*e,r)}}function Gu(t,e){var r=e-t;return r?I5(t,r>180||r<-180?r-360*Math.round(r/360):r):Hu(isNaN(t)?e:t)}function DB(t){return(t=+t)==1?Cr:function(e,r){return r-e?BB(e,r,t):Hu(isNaN(e)?r:e)}}function Cr(t,e){var r=e-t;return r?I5(t,r):Hu(isNaN(t)?e:t)}const Nl=function t(e){var r=DB(e);function n(i,a){var s=r((i=po(i)).r,(a=po(a)).r),o=r(i.g,a.g),l=r(i.b,a.b),u=Cr(i.opacity,a.opacity);return function(h){return i.r=s(h),i.g=o(h),i.b=l(h),i.opacity=u(h),i+""}}return n.gamma=t,n}(1);function N5(t){return function(e){var r=e.length,n=new Array(r),i=new Array(r),a=new Array(r),s,o;for(s=0;sr&&(a=e.slice(r,a),o[s]?o[s]+=a:o[++s]=a),(n=n[0])===(i=i[0])?o[s]?o[s]+=i:o[++s]=i:(o[++s]=null,l.push({i:s,x:Bn(n,i)})),r=gd.lastIndex;return r180?h+=360:h-u>180&&(u+=360),f.push({i:d.push(i(d)+"rotate(",null,n)-2,x:Bn(u,h)})):h&&d.push(i(d)+"rotate("+h+n)}function o(u,h,d,f){u!==h?f.push({i:d.push(i(d)+"skewX(",null,n)-2,x:Bn(u,h)}):h&&d.push(i(d)+"skewX("+h+n)}function l(u,h,d,f,p,m){if(u!==d||h!==f){var _=p.push(i(p)+"scale(",null,",",null,")");m.push({i:_-4,x:Bn(u,d)},{i:_-2,x:Bn(h,f)})}else(d!==1||f!==1)&&p.push(i(p)+"scale("+d+","+f+")")}return function(u,h){var d=[],f=[];return u=t(u),h=t(h),a(u.translateX,u.translateY,h.translateX,h.translateY,d,f),s(u.rotate,h.rotate,d,f),o(u.skewX,h.skewX,d,f),l(u.scaleX,u.scaleY,h.scaleX,h.scaleY,d,f),u=h=null,function(p){for(var m=-1,_=f.length,y;++m<_;)d[(y=f[m]).i]=y.x(p);return d.join("")}}}var Y5=z5(YB,"px, ","px)","deg)"),U5=z5(UB,", ",")",")"),WB=1e-12;function W5(t){return((t=Math.exp(t))+1/t)/2}function HB(t){return((t=Math.exp(t))-1/t)/2}function GB(t){return((t=Math.exp(2*t))-1)/(t+1)}const H5=function t(e,r,n){function i(a,s){var o=a[0],l=a[1],u=a[2],h=s[0],d=s[1],f=s[2],p=h-o,m=d-l,_=p*p+m*m,y,b;if(_=0&&t._call.call(void 0,e),t=t._next;--yo}function tv(){_s=(Zu=Fl.now())+Qu,yo=Bl=0;try{J5()}finally{yo=0,eD(),_s=0}}function tD(){var t=Fl.now(),e=t-Zu;e>Z5&&(Qu-=e,Zu=t)}function eD(){for(var t,e=Ku,r,n=1/0;e;)e._call?(n>e._time&&(n=e._time),t=e,e=e._next):(r=e._next,e._next=null,e=t?t._next=r:Ku=r);Ol=t,bd(n)}function bd(t){if(!yo){Bl&&(Bl=clearTimeout(Bl));var e=t-_s;e>24?(t<1/0&&(Bl=setTimeout(tv,t-Fl.now()-Qu)),Dl&&(Dl=clearInterval(Dl))):(Dl||(Zu=Fl.now(),Dl=setInterval(tD,Z5)),yo=1,Q5(tv))}}function _d(t,e,r){var n=new ql;return e=e==null?0:+e,n.restart(i=>{n.stop(),t(i+e)},e,r),n}function rD(t,e,r){var n=new ql,i=e;return e==null?(n.restart(t,e,r),n):(n._restart=n.restart,n.restart=function(a,s,o){s=+s,o=o==null?Pl():+o,n._restart(function l(u){u+=i,n._restart(l,i+=s,o),a(u)},s,o)},n.restart(t,e,r),n)}var nD=fs("start","end","cancel","interrupt"),iD=[],ev=0,vd=1,xd=2,th=3,rv=4,kd=5,eh=6;function rh(t,e,r,n,i,a){var s=t.__transition;if(!s)t.__transition={};else if(r in s)return;aD(t,r,{name:e,index:n,group:i,on:nD,tween:iD,time:a.time,delay:a.delay,duration:a.duration,ease:a.ease,timer:null,state:ev})}function wd(t,e){var r=Jn(t,e);if(r.state>ev)throw new Error("too late; already scheduled");return r}function Ei(t,e){var r=Jn(t,e);if(r.state>th)throw new Error("too late; already running");return r}function Jn(t,e){var r=t.__transition;if(!r||!(r=r[e]))throw new Error("transition not found");return r}function aD(t,e,r){var n=t.__transition,i;n[e]=r,r.timer=Ju(a,0,r.time);function a(u){r.state=vd,r.timer.restart(s,r.delay,r.time),r.delay<=u&&s(u-r.delay)}function s(u){var h,d,f,p;if(r.state!==vd)return l();for(h in n)if(p=n[h],p.name===r.name){if(p.state===th)return _d(s);p.state===rv?(p.state=eh,p.timer.stop(),p.on.call("interrupt",t,t.__data__,p.index,p.group),delete n[h]):+hxd&&n.state=0&&(e=e.slice(0,r)),!e||e==="start"})}function DD(t,e,r){var n,i,a=BD(e)?wd:Ei;return function(){var s=a(this,t),o=s.on;o!==n&&(i=(n=o).copy()).on(e,r),s.on=i}}function OD(t,e){var r=this._id;return arguments.length<2?Jn(this.node(),r).on.on(t):this.each(DD(r,t,e))}function FD(t){return function(){var e=this.parentNode;for(var r in this.__transition)if(+r!==t)return;e&&e.removeChild(this)}}function PD(){return this.on("end.remove",FD(this._id))}function qD(t){var e=this._name,r=this._id;typeof t!="function"&&(t=Ru(t));for(var n=this._groups,i=n.length,a=new Array(i),s=0;s+t;function oO(t){return t*t}function lO(t){return t*(2-t)}function ov(t){return((t*=2)<=1?t*t:--t*(2-t)+1)/2}function cO(t){return t*t*t}function uO(t){return--t*t*t+1}function Ed(t){return((t*=2)<=1?t*t*t:(t-=2)*t*t+2)/2}var Cd=3,hO=function t(e){e=+e;function r(n){return Math.pow(n,e)}return r.exponent=t,r}(Cd),fO=function t(e){e=+e;function r(n){return 1-Math.pow(1-n,e)}return r.exponent=t,r}(Cd),lv=function t(e){e=+e;function r(n){return((n*=2)<=1?Math.pow(n,e):2-Math.pow(2-n,e))/2}return r.exponent=t,r}(Cd),cv=Math.PI,uv=cv/2;function dO(t){return+t==1?1:1-Math.cos(t*uv)}function pO(t){return Math.sin(t*uv)}function hv(t){return(1-Math.cos(cv*t))/2}function La(t){return(Math.pow(2,-10*t)-.0009765625)*1.0009775171065494}function gO(t){return La(1-+t)}function yO(t){return 1-La(t)}function fv(t){return((t*=2)<=1?La(1-t):2-La(t-1))/2}function mO(t){return 1-Math.sqrt(1-t*t)}function bO(t){return Math.sqrt(1- --t*t)}function dv(t){return((t*=2)<=1?1-Math.sqrt(1-t*t):Math.sqrt(1-(t-=2)*t)+1)/2}var Sd=4/11,_O=6/11,vO=8/11,xO=3/4,kO=9/11,wO=10/11,TO=15/16,EO=21/22,CO=63/64,nh=1/Sd/Sd;function SO(t){return 1-Vl(1-t)}function Vl(t){return(t=+t)vd&&n.name===e)return new Ci([[t]],OO,e,+i)}return null}const Rd=t=>()=>t;function PO(t,{sourceEvent:e,target:r,selection:n,mode:i,dispatch:a}){Object.defineProperties(this,{type:{value:t,enumerable:!0,configurable:!0},sourceEvent:{value:e,enumerable:!0,configurable:!0},target:{value:r,enumerable:!0,configurable:!0},selection:{value:n,enumerable:!0,configurable:!0},mode:{value:i,enumerable:!0,configurable:!0},_:{value:a}})}function qO(t){t.stopImmediatePropagation()}function Id(t){t.preventDefault(),t.stopImmediatePropagation()}var yv={name:"drag"},Nd={name:"space"},bo={name:"handle"},_o={name:"center"};const{abs:mv,max:Or,min:Fr}=Math;function bv(t){return[+t[0],+t[1]]}function Bd(t){return[bv(t[0]),bv(t[1])]}var ih={name:"x",handles:["w","e"].map(zl),input:function(t,e){return t==null?null:[[+t[0],e[0][1]],[+t[1],e[1][1]]]},output:function(t){return t&&[t[0][0],t[1][0]]}},ah={name:"y",handles:["n","s"].map(zl),input:function(t,e){return t==null?null:[[e[0][0],+t[0]],[e[1][0],+t[1]]]},output:function(t){return t&&[t[0][1],t[1][1]]}},VO={name:"xy",handles:["n","w","e","s","nw","ne","sw","se"].map(zl),input:function(t){return t==null?null:Bd(t)},output:function(t){return t}},Xi={overlay:"crosshair",selection:"move",n:"ns-resize",e:"ew-resize",s:"ns-resize",w:"ew-resize",nw:"nwse-resize",ne:"nesw-resize",se:"nwse-resize",sw:"nesw-resize"},_v={e:"w",w:"e",nw:"ne",ne:"nw",se:"sw",sw:"se"},vv={n:"s",s:"n",nw:"sw",ne:"se",se:"ne",sw:"nw"},zO={overlay:1,selection:1,n:null,e:1,s:null,w:-1,nw:-1,ne:1,se:1,sw:-1},YO={overlay:1,selection:1,n:-1,e:null,s:1,w:null,nw:-1,ne:-1,se:1,sw:1};function zl(t){return{type:t}}function UO(t){return!t.ctrlKey&&!t.button}function WO(){var t=this.ownerSVGElement||this;return t.hasAttribute("viewBox")?(t=t.viewBox.baseVal,[[t.x,t.y],[t.x+t.width,t.y+t.height]]):[[0,0],[t.width.baseVal.value,t.height.baseVal.value]]}function HO(){return navigator.maxTouchPoints||"ontouchstart"in this}function Dd(t){for(;!t.__brush;)if(!(t=t.parentNode))return;return t.__brush}function GO(t){return t[0][0]===t[1][0]||t[0][1]===t[1][1]}function jO(t){var e=t.__brush;return e?e.dim.output(e.selection):null}function $O(){return Od(ih)}function XO(){return Od(ah)}function KO(){return Od(VO)}function Od(t){var e=WO,r=UO,n=HO,i=!0,a=fs("start","brush","end"),s=6,o;function l(y){var b=y.property("__brush",_).selectAll(".overlay").data([zl("overlay")]);b.enter().append("rect").attr("class","overlay").attr("pointer-events","all").attr("cursor",Xi.overlay).merge(b).each(function(){var k=Dd(this).extent;St(this).attr("x",k[0][0]).attr("y",k[0][1]).attr("width",k[1][0]-k[0][0]).attr("height",k[1][1]-k[0][1])}),y.selectAll(".selection").data([zl("selection")]).enter().append("rect").attr("class","selection").attr("cursor",Xi.selection).attr("fill","#777").attr("fill-opacity",.3).attr("stroke","#fff").attr("shape-rendering","crispEdges");var x=y.selectAll(".handle").data(t.handles,function(k){return k.type});x.exit().remove(),x.enter().append("rect").attr("class",function(k){return"handle handle--"+k.type}).attr("cursor",function(k){return Xi[k.type]}),y.each(u).attr("fill","none").attr("pointer-events","all").on("mousedown.brush",f).filter(n).on("touchstart.brush",f).on("touchmove.brush",p).on("touchend.brush touchcancel.brush",m).style("touch-action","none").style("-webkit-tap-highlight-color","rgba(0,0,0,0)")}l.move=function(y,b,x){y.tween?y.on("start.brush",function(k){h(this,arguments).beforestart().start(k)}).on("interrupt.brush end.brush",function(k){h(this,arguments).end(k)}).tween("brush",function(){var k=this,T=k.__brush,C=h(k,arguments),M=T.selection,S=t.input(typeof b=="function"?b.apply(this,arguments):b,T.extent),R=Ma(M,S);function A(L){T.selection=L===1&&S===null?null:R(L),u.call(k),C.brush()}return M!==null&&S!==null?A:A(1)}):y.each(function(){var k=this,T=arguments,C=k.__brush,M=t.input(typeof b=="function"?b.apply(k,T):b,C.extent),S=h(k,T).beforestart();vs(k),C.selection=M===null?null:M,u.call(k),S.start(x).brush(x).end(x)})},l.clear=function(y,b){l.move(y,null,b)};function u(){var y=St(this),b=Dd(this).selection;b?(y.selectAll(".selection").style("display",null).attr("x",b[0][0]).attr("y",b[0][1]).attr("width",b[1][0]-b[0][0]).attr("height",b[1][1]-b[0][1]),y.selectAll(".handle").style("display",null).attr("x",function(x){return x.type[x.type.length-1]==="e"?b[1][0]-s/2:b[0][0]-s/2}).attr("y",function(x){return x.type[0]==="s"?b[1][1]-s/2:b[0][1]-s/2}).attr("width",function(x){return x.type==="n"||x.type==="s"?b[1][0]-b[0][0]+s:s}).attr("height",function(x){return x.type==="e"||x.type==="w"?b[1][1]-b[0][1]+s:s})):y.selectAll(".selection,.handle").style("display","none").attr("x",null).attr("y",null).attr("width",null).attr("height",null)}function h(y,b,x){var k=y.__brush.emitter;return k&&(!x||!k.clean)?k:new d(y,b,x)}function d(y,b,x){this.that=y,this.args=b,this.state=y.__brush,this.active=0,this.clean=x}d.prototype={beforestart:function(){return++this.active===1&&(this.state.emitter=this,this.starting=!0),this},start:function(y,b){return this.starting?(this.starting=!1,this.emit("start",y,b)):this.emit("brush",y),this},brush:function(y,b){return this.emit("brush",y,b),this},end:function(y,b){return--this.active===0&&(delete this.state.emitter,this.emit("end",y,b)),this},emit:function(y,b,x){var k=St(this.that).datum();a.call(y,this.that,new PO(y,{sourceEvent:b,target:l,selection:t.output(this.state.selection),mode:x,dispatch:a}),k)}};function f(y){if(o&&!y.touches||!r.apply(this,arguments))return;var b=this,x=y.target.__data__.type,k=(i&&y.metaKey?x="overlay":x)==="selection"?yv:i&&y.altKey?_o:bo,T=t===ah?null:zO[x],C=t===ih?null:YO[x],M=Dd(b),S=M.extent,R=M.selection,A=S[0][0],L,v,B=S[0][1],w,D,N=S[1][0],z,X,ct=S[1][1],J,Y,$=0,lt=0,ut,W=T&&C&&i&&y.shiftKey,tt,K,it=Array.from(y.touches||[y],at=>{const It=at.identifier;return at=Tn(at,b),at.point0=at.slice(),at.identifier=It,at});vs(b);var Z=h(b,arguments,!0).beforestart();if(x==="overlay"){R&&(ut=!0);const at=[it[0],it[1]||it[0]];M.selection=R=[[L=t===ah?A:Fr(at[0][0],at[1][0]),w=t===ih?B:Fr(at[0][1],at[1][1])],[z=t===ah?N:Or(at[0][0],at[1][0]),J=t===ih?ct:Or(at[0][1],at[1][1])]],it.length>1&&F(y)}else L=R[0][0],w=R[0][1],z=R[1][0],J=R[1][1];v=L,D=w,X=z,Y=J;var V=St(b).attr("pointer-events","none"),Q=V.selectAll(".overlay").attr("cursor",Xi[x]);if(y.touches)Z.moved=U,Z.ended=j;else{var q=St(y.view).on("mousemove.brush",U,!0).on("mouseup.brush",j,!0);i&&q.on("keydown.brush",P,!0).on("keyup.brush",et,!0),Bu(y.view)}u.call(b),Z.start(y,k.name);function U(at){for(const It of at.changedTouches||[at])for(const Lt of it)Lt.identifier===It.identifier&&(Lt.cur=Tn(It,b));if(W&&!tt&&!K&&it.length===1){const It=it[0];mv(It.cur[0]-It[0])>mv(It.cur[1]-It[1])?K=!0:tt=!0}for(const It of it)It.cur&&(It[0]=It.cur[0],It[1]=It.cur[1]);ut=!0,Id(at),F(at)}function F(at){const It=it[0],Lt=It.point0;var Rt;switch($=It[0]-Lt[0],lt=It[1]-Lt[1],k){case Nd:case yv:{T&&($=Or(A-L,Fr(N-z,$)),v=L+$,X=z+$),C&&(lt=Or(B-w,Fr(ct-J,lt)),D=w+lt,Y=J+lt);break}case bo:{it[1]?(T&&(v=Or(A,Fr(N,it[0][0])),X=Or(A,Fr(N,it[1][0])),T=1),C&&(D=Or(B,Fr(ct,it[0][1])),Y=Or(B,Fr(ct,it[1][1])),C=1)):(T<0?($=Or(A-L,Fr(N-L,$)),v=L+$,X=z):T>0&&($=Or(A-z,Fr(N-z,$)),v=L,X=z+$),C<0?(lt=Or(B-w,Fr(ct-w,lt)),D=w+lt,Y=J):C>0&&(lt=Or(B-J,Fr(ct-J,lt)),D=w,Y=J+lt));break}case _o:{T&&(v=Or(A,Fr(N,L-$*T)),X=Or(A,Fr(N,z+$*T))),C&&(D=Or(B,Fr(ct,w-lt*C)),Y=Or(B,Fr(ct,J+lt*C)));break}}X0&&(L=v-$),C<0?J=Y-lt:C>0&&(w=D-lt),k=Nd,Q.attr("cursor",Xi.selection),F(at));break}default:return}Id(at)}function et(at){switch(at.keyCode){case 16:{W&&(tt=K=W=!1,F(at));break}case 18:{k===_o&&(T<0?z=X:T>0&&(L=v),C<0?J=Y:C>0&&(w=D),k=bo,F(at));break}case 32:{k===Nd&&(at.altKey?(T&&(z=X-$*T,L=v+$*T),C&&(J=Y-lt*C,w=D+lt*C),k=_o):(T<0?z=X:T>0&&(L=v),C<0?J=Y:C>0&&(w=D),k=bo),Q.attr("cursor",Xi[x]),F(at));break}default:return}Id(at)}}function p(y){h(this,arguments).moved(y)}function m(y){h(this,arguments).ended(y)}function _(){var y=this.__brush||{selection:null};return y.extent=Bd(e.apply(this,arguments)),y.dim=t,y}return l.extent=function(y){return arguments.length?(e=typeof y=="function"?y:Rd(Bd(y)),l):e},l.filter=function(y){return arguments.length?(r=typeof y=="function"?y:Rd(!!y),l):r},l.touchable=function(y){return arguments.length?(n=typeof y=="function"?y:Rd(!!y),l):n},l.handleSize=function(y){return arguments.length?(s=+y,l):s},l.keyModifiers=function(y){return arguments.length?(i=!!y,l):i},l.on=function(){var y=a.on.apply(a,arguments);return y===a?l:y},l}var xv=Math.abs,vo=Math.cos,xo=Math.sin,kv=Math.PI,sh=kv/2,wv=kv*2,Tv=Math.max,Fd=1e-12;function Pd(t,e){return Array.from({length:e-t},(r,n)=>t+n)}function ZO(t){return function(e,r){return t(e.source.value+e.target.value,r.source.value+r.target.value)}}function QO(){return qd(!1,!1)}function JO(){return qd(!1,!0)}function tF(){return qd(!0,!1)}function qd(t,e){var r=0,n=null,i=null,a=null;function s(o){var l=o.length,u=new Array(l),h=Pd(0,l),d=new Array(l*l),f=new Array(l),p=0,m;o=Float64Array.from({length:l*l},e?(_,y)=>o[y%l][y/l|0]:(_,y)=>o[y/l|0][y%l]);for(let _=0;_n(u[y],u[b]));for(const y of h){const b=_;if(t){const x=Pd(~l+1,l).filter(k=>k<0?o[~k*l+y]:o[y*l+k]);i&&x.sort((k,T)=>i(k<0?-o[~k*l+y]:o[y*l+k],T<0?-o[~T*l+y]:o[y*l+T]));for(const k of x)if(k<0){const T=d[~k*l+y]||(d[~k*l+y]={source:null,target:null});T.target={index:y,startAngle:_,endAngle:_+=o[~k*l+y]*p,value:o[~k*l+y]}}else{const T=d[y*l+k]||(d[y*l+k]={source:null,target:null});T.source={index:y,startAngle:_,endAngle:_+=o[y*l+k]*p,value:o[y*l+k]}}f[y]={index:y,startAngle:b,endAngle:_,value:u[y]}}else{const x=Pd(0,l).filter(k=>o[y*l+k]||o[k*l+y]);i&&x.sort((k,T)=>i(o[y*l+k],o[y*l+T]));for(const k of x){let T;if(yxs)if(!(Math.abs(h*o-l*u)>xs)||!i)this._+="L"+(this._x1=t)+","+(this._y1=e);else{var f=r-a,p=n-s,m=o*o+l*l,_=f*f+p*p,y=Math.sqrt(m),b=Math.sqrt(d),x=i*Math.tan((Vd-Math.acos((m+d-_)/(2*y*b)))/2),k=x/b,T=x/y;Math.abs(k-1)>xs&&(this._+="L"+(t+k*u)+","+(e+k*h)),this._+="A"+i+","+i+",0,0,"+ +(h*f>u*p)+","+(this._x1=t+T*o)+","+(this._y1=e+T*l)}},arc:function(t,e,r,n,i,a){t=+t,e=+e,r=+r,a=!!a;var s=r*Math.cos(n),o=r*Math.sin(n),l=t+s,u=e+o,h=1^a,d=a?n-i:i-n;if(r<0)throw new Error("negative radius: "+r);this._x1===null?this._+="M"+l+","+u:(Math.abs(this._x1-l)>xs||Math.abs(this._y1-u)>xs)&&(this._+="L"+l+","+u),r&&(d<0&&(d=d%zd+zd),d>eF?this._+="A"+r+","+r+",0,1,"+h+","+(t-s)+","+(e-o)+"A"+r+","+r+",0,1,"+h+","+(this._x1=l)+","+(this._y1=u):d>xs&&(this._+="A"+r+","+r+",0,"+ +(d>=Vd)+","+h+","+(this._x1=t+r*Math.cos(i))+","+(this._y1=e+r*Math.sin(i))))},rect:function(t,e,r,n){this._+="M"+(this._x0=this._x1=+t)+","+(this._y0=this._y1=+e)+"h"+ +r+"v"+ +n+"h"+-r+"Z"},toString:function(){return this._}};var rF=Array.prototype.slice;function ks(t){return function(){return t}}function nF(t){return t.source}function iF(t){return t.target}function Ev(t){return t.radius}function aF(t){return t.startAngle}function sF(t){return t.endAngle}function oF(){return 0}function lF(){return 10}function Cv(t){var e=nF,r=iF,n=Ev,i=Ev,a=aF,s=sF,o=oF,l=null;function u(){var h,d=e.apply(this,arguments),f=r.apply(this,arguments),p=o.apply(this,arguments)/2,m=rF.call(arguments),_=+n.apply(this,(m[0]=d,m)),y=a.apply(this,m)-sh,b=s.apply(this,m)-sh,x=+i.apply(this,(m[0]=f,m)),k=a.apply(this,m)-sh,T=s.apply(this,m)-sh;if(l||(l=h=Ra()),p>Fd&&(xv(b-y)>p*2+Fd?b>y?(y+=p,b-=p):(y-=p,b+=p):y=b=(y+b)/2,xv(T-k)>p*2+Fd?T>k?(k+=p,T-=p):(k-=p,T+=p):k=T=(k+T)/2),l.moveTo(_*vo(y),_*xo(y)),l.arc(0,0,_,y,b),y!==k||b!==T)if(t){var C=+t.apply(this,arguments),M=x-C,S=(k+T)/2;l.quadraticCurveTo(0,0,M*vo(k),M*xo(k)),l.lineTo(x*vo(S),x*xo(S)),l.lineTo(M*vo(T),M*xo(T))}else l.quadraticCurveTo(0,0,x*vo(k),x*xo(k)),l.arc(0,0,x,k,T);if(l.quadraticCurveTo(0,0,_*vo(y),_*xo(y)),l.closePath(),h)return l=null,h+""||null}return t&&(u.headRadius=function(h){return arguments.length?(t=typeof h=="function"?h:ks(+h),u):t}),u.radius=function(h){return arguments.length?(n=i=typeof h=="function"?h:ks(+h),u):n},u.sourceRadius=function(h){return arguments.length?(n=typeof h=="function"?h:ks(+h),u):n},u.targetRadius=function(h){return arguments.length?(i=typeof h=="function"?h:ks(+h),u):i},u.startAngle=function(h){return arguments.length?(a=typeof h=="function"?h:ks(+h),u):a},u.endAngle=function(h){return arguments.length?(s=typeof h=="function"?h:ks(+h),u):s},u.padAngle=function(h){return arguments.length?(o=typeof h=="function"?h:ks(+h),u):o},u.source=function(h){return arguments.length?(e=h,u):e},u.target=function(h){return arguments.length?(r=h,u):r},u.context=function(h){return arguments.length?(l=h==null?null:h,u):l},u}function cF(){return Cv()}function uF(){return Cv(lF)}var hF=Array.prototype,Sv=hF.slice;function fF(t,e){return t-e}function dF(t){for(var e=0,r=t.length,n=t[r-1][1]*t[0][0]-t[r-1][0]*t[0][1];++e()=>t;function pF(t,e){for(var r=-1,n=e.length,i;++rn!=p>n&&r<(f-u)*(n-h)/(p-h)+u&&(i=-i)}return i}function yF(t,e,r){var n;return mF(t,e,r)&&bF(t[n=+(t[0]===e[0])],r[n],e[n])}function mF(t,e,r){return(e[0]-t[0])*(r[1]-t[1])===(r[0]-t[0])*(e[1]-t[1])}function bF(t,e,r){return t<=e&&e<=r||r<=e&&e<=t}function _F(){}var Ki=[[],[[[1,1.5],[.5,1]]],[[[1.5,1],[1,1.5]]],[[[1.5,1],[.5,1]]],[[[1,.5],[1.5,1]]],[[[1,1.5],[.5,1]],[[1,.5],[1.5,1]]],[[[1,.5],[1,1.5]]],[[[1,.5],[.5,1]]],[[[.5,1],[1,.5]]],[[[1,1.5],[1,.5]]],[[[.5,1],[1,.5]],[[1.5,1],[1,1.5]]],[[[1.5,1],[1,.5]]],[[[.5,1],[1.5,1]]],[[[1,1.5],[1.5,1]]],[[[.5,1],[1,1.5]]],[]];function Ud(){var t=1,e=1,r=W0,n=l;function i(u){var h=r(u);if(Array.isArray(h))h=h.slice().sort(fF);else{const d=xl(u),f=wl(d[0],d[1],h);h=hs(Math.floor(d[0]/f)*f,Math.floor(d[1]/f-1)*f,h)}return h.map(d=>a(u,d))}function a(u,h){var d=[],f=[];return s(u,h,function(p){n(p,u,h),dF(p)>0?d.push([p]):f.push(p)}),f.forEach(function(p){for(var m=0,_=d.length,y;m<_;++m)if(pF((y=d[m])[0],p)!==-1){y.push(p);return}}),{type:"MultiPolygon",value:h,coordinates:d}}function s(u,h,d){var f=new Array,p=new Array,m,_,y,b,x,k;for(m=_=-1,b=u[0]>=h,Ki[b<<1].forEach(T);++m=h,Ki[y|b<<1].forEach(T);for(Ki[b<<0].forEach(T);++_=h,x=u[_*t]>=h,Ki[b<<1|x<<2].forEach(T);++m=h,k=x,x=u[_*t+m+1]>=h,Ki[y|b<<1|x<<2|k<<3].forEach(T);Ki[b|x<<3].forEach(T)}for(m=-1,x=u[_*t]>=h,Ki[x<<2].forEach(T);++m=h,Ki[x<<2|k<<3].forEach(T);Ki[x<<3].forEach(T);function T(C){var M=[C[0][0]+m,C[0][1]+_],S=[C[1][0]+m,C[1][1]+_],R=o(M),A=o(S),L,v;(L=p[R])?(v=f[A])?(delete p[L.end],delete f[v.start],L===v?(L.ring.push(S),d(L.ring)):f[L.start]=p[v.end]={start:L.start,end:v.end,ring:L.ring.concat(v.ring)}):(delete p[L.end],L.ring.push(S),p[L.end=A]=L):(L=f[A])?(v=p[R])?(delete f[L.start],delete p[v.end],L===v?(L.ring.push(S),d(L.ring)):f[v.start]=p[L.end]={start:v.start,end:L.end,ring:v.ring.concat(L.ring)}):(delete f[L.start],L.ring.unshift(M),f[L.start=R]=L):f[R]=p[A]={start:R,end:A,ring:[M,S]}}}function o(u){return u[0]*2+u[1]*(t+1)*4}function l(u,h,d){u.forEach(function(f){var p=f[0],m=f[1],_=p|0,y=m|0,b,x=h[y*t+_];p>0&&p0&&m=0&&d>=0))throw new Error("invalid size");return t=h,e=d,i},i.thresholds=function(u){return arguments.length?(r=typeof u=="function"?u:Array.isArray(u)?Ia(Sv.call(u)):Ia(u),i):r},i.smooth=function(u){return arguments.length?(n=u?l:_F,i):n===l},i}function vF(t){return t[0]}function xF(t){return t[1]}function kF(){return 1}function wF(){var t=vF,e=xF,r=kF,n=960,i=500,a=20,s=2,o=a*3,l=n+o*2>>s,u=i+o*2>>s,h=Ia(20);function d(x){var k=new Float32Array(l*u),T=Math.pow(2,-s),C=-1;for(const w of x){var M=(t(w,++C,x)+o)*T,S=(e(w,C,x)+o)*T,R=+r(w,C,x);if(M>=0&&M=0&&SM*C))(k).map((M,S)=>(M.value=+T[S],p(M)))}f.contours=function(x){var k=d(x),T=Ud().size([l,u]),C=Math.pow(2,2*s),M=S=>{S=+S;var R=p(T.contour(k,S*C));return R.value=S,R};return Object.defineProperty(M,"max",{get:()=>lo(k)/C}),M};function p(x){return x.coordinates.forEach(m),x}function m(x){x.forEach(_)}function _(x){x.forEach(y)}function y(x){x[0]=x[0]*Math.pow(2,s)-o,x[1]=x[1]*Math.pow(2,s)-o}function b(){return o=a*3,l=n+o*2>>s,u=i+o*2>>s,f}return f.x=function(x){return arguments.length?(t=typeof x=="function"?x:Ia(+x),f):t},f.y=function(x){return arguments.length?(e=typeof x=="function"?x:Ia(+x),f):e},f.weight=function(x){return arguments.length?(r=typeof x=="function"?x:Ia(+x),f):r},f.size=function(x){if(!arguments.length)return[n,i];var k=+x[0],T=+x[1];if(!(k>=0&&T>=0))throw new Error("invalid size");return n=k,i=T,b()},f.cellSize=function(x){if(!arguments.length)return 1<=1))throw new Error("invalid cell size");return s=Math.floor(Math.log(x)/Math.LN2),b()},f.thresholds=function(x){return arguments.length?(h=typeof x=="function"?x:Array.isArray(x)?Ia(Sv.call(x)):Ia(x),f):h},f.bandwidth=function(x){if(!arguments.length)return Math.sqrt(a*(a+1));if(!((x=+x)>=0))throw new Error("invalid bandwidth");return a=(Math.sqrt(4*x*x+1)-1)/2,b()},f}const Zi=11102230246251565e-32,Pr=134217729,TF=(3+8*Zi)*Zi;function Wd(t,e,r,n,i){let a,s,o,l,u=e[0],h=n[0],d=0,f=0;h>u==h>-u?(a=u,u=e[++d]):(a=h,h=n[++f]);let p=0;if(du==h>-u?(s=u+a,o=a-(s-u),u=e[++d]):(s=h+a,o=a-(s-h),h=n[++f]),a=s,o!==0&&(i[p++]=o);du==h>-u?(s=a+u,l=s-a,o=a-(s-l)+(u-l),u=e[++d]):(s=a+h,l=s-a,o=a-(s-l)+(h-l),h=n[++f]),a=s,o!==0&&(i[p++]=o);for(;d=D||-w>=D||(d=t-A,o=t-(A+d)+(d-i),d=r-L,u=r-(L+d)+(d-i),d=e-v,l=e-(v+d)+(d-a),d=n-B,h=n-(B+d)+(d-a),o===0&&l===0&&u===0&&h===0)||(D=AF*s+TF*Math.abs(w),w+=A*h+B*o-(v*u+L*l),w>=D||-w>=D))return w;T=o*B,f=Pr*o,p=f-(f-o),m=o-p,f=Pr*B,_=f-(f-B),y=B-_,C=m*y-(T-p*_-m*_-p*y),M=l*L,f=Pr*l,p=f-(f-l),m=l-p,f=Pr*L,_=f-(f-L),y=L-_,S=m*y-(M-p*_-m*_-p*y),b=C-S,d=C-b,Xr[0]=C-(b+d)+(d-S),x=T+b,d=x-T,k=T-(x-d)+(b-d),b=k-M,d=k-b,Xr[1]=k-(b+d)+(d-M),R=x+b,d=R-x,Xr[2]=x-(R-d)+(b-d),Xr[3]=R;const N=Wd(4,ko,4,Xr,Av);T=A*h,f=Pr*A,p=f-(f-A),m=A-p,f=Pr*h,_=f-(f-h),y=h-_,C=m*y-(T-p*_-m*_-p*y),M=v*u,f=Pr*v,p=f-(f-v),m=v-p,f=Pr*u,_=f-(f-u),y=u-_,S=m*y-(M-p*_-m*_-p*y),b=C-S,d=C-b,Xr[0]=C-(b+d)+(d-S),x=T+b,d=x-T,k=T-(x-d)+(b-d),b=k-M,d=k-b,Xr[1]=k-(b+d)+(d-M),R=x+b,d=R-x,Xr[2]=x-(R-d)+(b-d),Xr[3]=R;const z=Wd(N,Av,4,Xr,Mv);T=o*h,f=Pr*o,p=f-(f-o),m=o-p,f=Pr*h,_=f-(f-h),y=h-_,C=m*y-(T-p*_-m*_-p*y),M=l*u,f=Pr*l,p=f-(f-l),m=l-p,f=Pr*u,_=f-(f-u),y=u-_,S=m*y-(M-p*_-m*_-p*y),b=C-S,d=C-b,Xr[0]=C-(b+d)+(d-S),x=T+b,d=x-T,k=T-(x-d)+(b-d),b=k-M,d=k-b,Xr[1]=k-(b+d)+(d-M),R=x+b,d=R-x,Xr[2]=x-(R-d)+(b-d),Xr[3]=R;const X=Wd(z,Mv,4,Xr,Lv);return Lv[X-1]}function oh(t,e,r,n,i,a){const s=(e-a)*(r-i),o=(t-i)*(n-a),l=s-o;if(s===0||o===0||s>0!=o>0)return l;const u=Math.abs(s+o);return Math.abs(l)>=CF*u?l:-MF(t,e,r,n,i,a,u)}const Rv=Math.pow(2,-52),lh=new Uint32Array(512);class ch{static from(e,r=BF,n=DF){const i=e.length,a=new Float64Array(i*2);for(let s=0;s>1;if(r>0&&typeof e[0]!="number")throw new Error("Expected coords to contain numbers.");this.coords=e;const n=Math.max(2*r-5,0);this._triangles=new Uint32Array(n*3),this._halfedges=new Int32Array(n*3),this._hashSize=Math.ceil(Math.sqrt(r)),this._hullPrev=new Uint32Array(r),this._hullNext=new Uint32Array(r),this._hullTri=new Uint32Array(r),this._hullHash=new Int32Array(this._hashSize).fill(-1),this._ids=new Uint32Array(r),this._dists=new Float64Array(r),this.update()}update(){const{coords:e,_hullPrev:r,_hullNext:n,_hullTri:i,_hullHash:a}=this,s=e.length>>1;let o=1/0,l=1/0,u=-1/0,h=-1/0;for(let L=0;Lu&&(u=v),B>h&&(h=B),this._ids[L]=L}const d=(o+u)/2,f=(l+h)/2;let p=1/0,m,_,y;for(let L=0;L0&&(_=L,p=v)}let k=e[2*_],T=e[2*_+1],C=1/0;for(let L=0;Lw&&(L[v++]=D,w=this._dists[D])}this.hull=L.subarray(0,v),this.triangles=new Uint32Array(0),this.halfedges=new Uint32Array(0);return}if(oh(b,x,k,T,M,S)<0){const L=_,v=k,B=T;_=y,k=M,T=S,y=L,M=v,S=B}const R=NF(b,x,k,T,M,S);this._cx=R.x,this._cy=R.y;for(let L=0;L0&&Math.abs(D-v)<=Rv&&Math.abs(N-B)<=Rv||(v=D,B=N,w===m||w===_||w===y))continue;let z=0;for(let $=0,lt=this._hashKey(D,N);$=0;)if(X=ct,X===z){X=-1;break}if(X===-1)continue;let J=this._addTriangle(X,w,n[X],-1,-1,i[X]);i[w]=this._legalize(J+2),i[X]=J,A++;let Y=n[X];for(;ct=n[Y],oh(D,N,e[2*Y],e[2*Y+1],e[2*ct],e[2*ct+1])<0;)J=this._addTriangle(Y,w,ct,i[w],-1,i[Y]),i[w]=this._legalize(J+2),n[Y]=Y,A--,Y=ct;if(X===z)for(;ct=r[X],oh(D,N,e[2*ct],e[2*ct+1],e[2*X],e[2*X+1])<0;)J=this._addTriangle(ct,w,X,-1,i[X],i[ct]),this._legalize(J+2),i[ct]=J,n[X]=X,A--,X=ct;this._hullStart=r[w]=X,n[X]=r[Y]=w,n[w]=Y,a[this._hashKey(D,N)]=w,a[this._hashKey(e[2*X],e[2*X+1])]=X}this.hull=new Uint32Array(A);for(let L=0,v=this._hullStart;L0?3-r:1+r)/4}function Hd(t,e,r,n){const i=t-r,a=e-n;return i*i+a*a}function RF(t,e,r,n,i,a,s,o){const l=t-s,u=e-o,h=r-s,d=n-o,f=i-s,p=a-o,m=l*l+u*u,_=h*h+d*d,y=f*f+p*p;return l*(d*y-_*p)-u*(h*y-_*f)+m*(h*p-d*f)<0}function IF(t,e,r,n,i,a){const s=r-t,o=n-e,l=i-t,u=a-e,h=s*s+o*o,d=l*l+u*u,f=.5/(s*u-o*l),p=(u*h-o*d)*f,m=(s*d-l*h)*f;return p*p+m*m}function NF(t,e,r,n,i,a){const s=r-t,o=n-e,l=i-t,u=a-e,h=s*s+o*o,d=l*l+u*u,f=.5/(s*u-o*l),p=t+(u*h-o*d)*f,m=e+(s*d-l*h)*f;return{x:p,y:m}}function wo(t,e,r,n){if(n-r<=20)for(let i=r+1;i<=n;i++){const a=t[i],s=e[a];let o=i-1;for(;o>=r&&e[t[o]]>s;)t[o+1]=t[o--];t[o+1]=a}else{const i=r+n>>1;let a=r+1,s=n;Ul(t,i,a),e[t[r]]>e[t[n]]&&Ul(t,r,n),e[t[a]]>e[t[n]]&&Ul(t,a,n),e[t[r]]>e[t[a]]&&Ul(t,r,a);const o=t[a],l=e[o];for(;;){do a++;while(e[t[a]]l);if(s=s-r?(wo(t,e,a,n),wo(t,e,r,s-1)):(wo(t,e,r,s-1),wo(t,e,a,n))}}function Ul(t,e,r){const n=t[e];t[e]=t[r],t[r]=n}function BF(t){return t[0]}function DF(t){return t[1]}const Iv=1e-6;class ws{constructor(){this._x0=this._y0=this._x1=this._y1=null,this._=""}moveTo(e,r){this._+=`M${this._x0=this._x1=+e},${this._y0=this._y1=+r}`}closePath(){this._x1!==null&&(this._x1=this._x0,this._y1=this._y0,this._+="Z")}lineTo(e,r){this._+=`L${this._x1=+e},${this._y1=+r}`}arc(e,r,n){e=+e,r=+r,n=+n;const i=e+n,a=r;if(n<0)throw new Error("negative radius");this._x1===null?this._+=`M${i},${a}`:(Math.abs(this._x1-i)>Iv||Math.abs(this._y1-a)>Iv)&&(this._+="L"+i+","+a),n&&(this._+=`A${n},${n},0,1,1,${e-n},${r}A${n},${n},0,1,1,${this._x1=i},${this._y1=a}`)}rect(e,r,n,i){this._+=`M${this._x0=this._x1=+e},${this._y0=this._y1=+r}h${+n}v${+i}h${-n}Z`}value(){return this._||null}}class Gd{constructor(){this._=[]}moveTo(e,r){this._.push([e,r])}closePath(){this._.push(this._[0].slice())}lineTo(e,r){this._.push([e,r])}value(){return this._.length?this._:null}}class Nv{constructor(e,[r,n,i,a]=[0,0,960,500]){if(!((i=+i)>=(r=+r))||!((a=+a)>=(n=+n)))throw new Error("invalid bounds");this.delaunay=e,this._circumcenters=new Float64Array(e.points.length*2),this.vectors=new Float64Array(e.points.length*2),this.xmax=i,this.xmin=r,this.ymax=a,this.ymin=n,this._init()}update(){return this.delaunay.update(),this._init(),this}_init(){const{delaunay:{points:e,hull:r,triangles:n},vectors:i}=this,a=this.circumcenters=this._circumcenters.subarray(0,n.length/3*2);for(let p=0,m=0,_=n.length,y,b;p<_;p+=3,m+=2){const x=n[p]*2,k=n[p+1]*2,T=n[p+2]*2,C=e[x],M=e[x+1],S=e[k],R=e[k+1],A=e[T],L=e[T+1],v=S-C,B=R-M,w=A-C,D=L-M,N=(v*D-B*w)*2;if(Math.abs(N)<1e-9){let z=1e9;const X=n[0]*2;z*=Math.sign((e[X]-C)*D-(e[X+1]-M)*w),y=(C+A)/2-z*D,b=(M+L)/2+z*w}else{const z=1/N,X=v*v+B*B,ct=w*w+D*D;y=C+(D*X-B*ct)*z,b=M+(v*ct-w*X)*z}a[m]=y,a[m+1]=b}let s=r[r.length-1],o,l=s*4,u,h=e[2*s],d,f=e[2*s+1];i.fill(0);for(let p=0;p1;)a-=2;for(let s=2;s4)for(let s=0;s0){if(r>=this.ymax)return null;(s=(this.ymax-r)/i)0){if(e>=this.xmax)return null;(s=(this.xmax-e)/n)this.xmax?2:0)|(rthis.ymax?8:0)}}const OF=2*Math.PI,To=Math.pow;function FF(t){return t[0]}function PF(t){return t[1]}function qF(t){const{triangles:e,coords:r}=t;for(let n=0;n1e-10)return!1}return!0}function VF(t,e,r){return[t+Math.sin(t+e)*r,e+Math.cos(t-e)*r]}class jd{static from(e,r=FF,n=PF,i){return new jd("length"in e?zF(e,r,n,i):Float64Array.from(YF(e,r,n,i)))}constructor(e){this._delaunator=new ch(e),this.inedges=new Int32Array(e.length/2),this._hullIndex=new Int32Array(e.length/2),this.points=this._delaunator.coords,this._init()}update(){return this._delaunator.update(),this._init(),this}_init(){const e=this._delaunator,r=this.points;if(e.hull&&e.hull.length>2&&qF(e)){this.collinear=Int32Array.from({length:r.length/2},(f,p)=>p).sort((f,p)=>r[2*f]-r[2*p]||r[2*f+1]-r[2*p+1]);const l=this.collinear[0],u=this.collinear[this.collinear.length-1],h=[r[2*l],r[2*l+1],r[2*u],r[2*u+1]],d=1e-8*Math.hypot(h[3]-h[1],h[2]-h[0]);for(let f=0,p=r.length/2;f0&&(this.triangles=new Int32Array(3).fill(-1),this.halfedges=new Int32Array(3).fill(-1),this.triangles[0]=i[0],s[i[0]]=1,i.length===2&&(s[i[1]]=0,this.triangles[1]=i[1],this.triangles[2]=i[1]))}voronoi(e){return new Nv(this,e)}*neighbors(e){const{inedges:r,hull:n,_hullIndex:i,halfedges:a,triangles:s,collinear:o}=this;if(o){const d=o.indexOf(e);d>0&&(yield o[d-1]),d=0&&a!==n&&a!==i;)n=a;return a}_step(e,r,n){const{inedges:i,hull:a,_hullIndex:s,halfedges:o,triangles:l,points:u}=this;if(i[e]===-1||!u.length)return(e+1)%(u.length>>1);let h=e,d=To(r-u[e*2],2)+To(n-u[e*2+1],2);const f=i[e];let p=f;do{let m=l[p];const _=To(r-u[m*2],2)+To(n-u[m*2+1],2);if(_9999?"+"+dn(t,6):dn(t,4)}function HF(t){var e=t.getUTCHours(),r=t.getUTCMinutes(),n=t.getUTCSeconds(),i=t.getUTCMilliseconds();return isNaN(t)?"Invalid Date":WF(t.getUTCFullYear())+"-"+dn(t.getUTCMonth()+1,2)+"-"+dn(t.getUTCDate(),2)+(i?"T"+dn(e,2)+":"+dn(r,2)+":"+dn(n,2)+"."+dn(i,3)+"Z":n?"T"+dn(e,2)+":"+dn(r,2)+":"+dn(n,2)+"Z":r||e?"T"+dn(e,2)+":"+dn(r,2)+"Z":"")}function uh(t){var e=new RegExp('["'+t+` +\r]`),r=t.charCodeAt(0);function n(d,f){var p,m,_=i(d,function(y,b){if(p)return p(y,b-1);m=y,p=f?UF(y,f):Dv(y)});return _.columns=m||[],_}function i(d,f){var p=[],m=d.length,_=0,y=0,b,x=m<=0,k=!1;d.charCodeAt(m-1)===Wl&&--m,d.charCodeAt(m-1)===Kd&&--m;function T(){if(x)return $d;if(k)return k=!1,Bv;var M,S=_,R;if(d.charCodeAt(S)===Xd){for(;_++=m?x=!0:(R=d.charCodeAt(_++))===Wl?k=!0:R===Kd&&(k=!0,d.charCodeAt(_)===Wl&&++_),d.slice(S+1,M-1).replace(/""/g,'"')}for(;_hh(e,r).then(n=>new DOMParser().parseFromString(n,t))}const mP=Zd("application/xml");var bP=Zd("text/html"),_P=Zd("image/svg+xml");function vP(t,e){var r,n=1;t==null&&(t=0),e==null&&(e=0);function i(){var a,s=r.length,o,l=0,u=0;for(a=0;a=(d=(o+u)/2))?o=d:u=d,(y=r>=(f=(l+h)/2))?l=f:h=f,i=a,!(a=a[b=y<<1|_]))return i[b]=s,t;if(p=+t._x.call(null,a.data),m=+t._y.call(null,a.data),e===p&&r===m)return s.next=a,i?i[b]=s:t._root=s,t;do i=i?i[b]=new Array(4):t._root=new Array(4),(_=e>=(d=(o+u)/2))?o=d:u=d,(y=r>=(f=(l+h)/2))?l=f:h=f;while((b=y<<1|_)===(x=(m>=f)<<1|p>=d));return i[x]=a,i[b]=s,t}function kP(t){var e,r,n=t.length,i,a,s=new Array(n),o=new Array(n),l=1/0,u=1/0,h=-1/0,d=-1/0;for(r=0;rh&&(h=i),ad&&(d=a));if(l>h||u>d)return this;for(this.cover(l,u).cover(h,d),r=0;rt||t>=i||n>e||e>=a;)switch(u=(eh||(o=m.y0)>d||(l=m.x1)=b)<<1|t>=y)&&(m=f[f.length-1],f[f.length-1]=f[f.length-1-_],f[f.length-1-_]=m)}else{var x=t-+this._x.call(null,p.data),k=e-+this._y.call(null,p.data),T=x*x+k*k;if(T=(f=(s+l)/2))?s=f:l=f,(_=d>=(p=(o+u)/2))?o=p:u=p,e=r,!(r=r[y=_<<1|m]))return this;if(!r.length)break;(e[y+1&3]||e[y+2&3]||e[y+3&3])&&(n=e,b=y)}for(;r.data!==t;)if(i=r,!(r=r.next))return this;return(a=r.next)&&delete r.next,i?(a?i.next=a:delete i.next,this):e?(a?e[y]=a:delete e[y],(r=e[0]||e[1]||e[2]||e[3])&&r===(e[3]||e[2]||e[1]||e[0])&&!r.length&&(n?n[b]=r:this._root=r),this):(this._root=a,this)}function AP(t){for(var e=0,r=t.length;ef.index){var v=p-R.x-R.vx,B=m-R.y-R.vy,w=v*v+B*B;wp+L||Mm+L||Su.r&&(u.r=u[h].r)}function l(){if(!!e){var u,h=e.length,d;for(r=new Array(h),u=0;u[e(C,M,s),C])),T;for(y=0,o=new Array(b);y(t=(YP*t+UP)%Uv)/Uv}function HP(t){return t.x}function GP(t){return t.y}var jP=10,$P=Math.PI*(3-Math.sqrt(5));function XP(t){var e,r=1,n=.001,i=1-Math.pow(n,1/300),a=0,s=.6,o=new Map,l=Ju(d),u=fs("tick","end"),h=WP();t==null&&(t=[]);function d(){f(),u.call("tick",e),r1?(y==null?o.delete(_):o.set(_,m(y)),e):o.get(_)},find:function(_,y,b){var x=0,k=t.length,T,C,M,S,R;for(b==null?b=1/0:b*=b,x=0;x1?(u.on(_,y),e):u.on(_)}}}function KP(){var t,e,r,n,i=vr(-30),a,s=1,o=1/0,l=.81;function u(p){var m,_=t.length,y=fh(t,HP,GP).visitAfter(d);for(n=p,m=0;m<_;++m)e=t[m],y.visit(f)}function h(){if(!!t){var p,m=t.length,_;for(a=new Array(m),p=0;p=o)return;(p.data!==e||p.next)&&(b===0&&(b=Na(r),T+=b*b),x===0&&(x=Na(r),T+=x*x),T=1e21?t.toLocaleString("en").replace(/,/g,""):t.toString(10)}function dh(t,e){if((r=(t=e?t.toExponential(e-1):t.toExponential()).indexOf("e"))<0)return null;var r,n=t.slice(0,r);return[n.length>1?n[0]+n.slice(2):n,+t.slice(r+1)]}function Eo(t){return t=dh(Math.abs(t)),t?t[1]:NaN}function eq(t,e){return function(r,n){for(var i=r.length,a=[],s=0,o=t[0],l=0;i>0&&o>0&&(l+o+1>n&&(o=Math.max(1,n-l)),a.push(r.substring(i-=o,i+o)),!((l+=o+1)>n));)o=t[s=(s+1)%t.length];return a.reverse().join(e)}}function rq(t){return function(e){return e.replace(/[0-9]/g,function(r){return t[+r]})}}var nq=/^(?:(.)?([<>=^]))?([+\-( ])?([$#])?(0)?(\d+)?(,)?(\.\d+)?(~)?([a-z%])?$/i;function Co(t){if(!(e=nq.exec(t)))throw new Error("invalid format: "+t);var e;return new ph({fill:e[1],align:e[2],sign:e[3],symbol:e[4],zero:e[5],width:e[6],comma:e[7],precision:e[8]&&e[8].slice(1),trim:e[9],type:e[10]})}Co.prototype=ph.prototype;function ph(t){this.fill=t.fill===void 0?" ":t.fill+"",this.align=t.align===void 0?">":t.align+"",this.sign=t.sign===void 0?"-":t.sign+"",this.symbol=t.symbol===void 0?"":t.symbol+"",this.zero=!!t.zero,this.width=t.width===void 0?void 0:+t.width,this.comma=!!t.comma,this.precision=t.precision===void 0?void 0:+t.precision,this.trim=!!t.trim,this.type=t.type===void 0?"":t.type+""}ph.prototype.toString=function(){return this.fill+this.align+this.sign+this.symbol+(this.zero?"0":"")+(this.width===void 0?"":Math.max(1,this.width|0))+(this.comma?",":"")+(this.precision===void 0?"":"."+Math.max(0,this.precision|0))+(this.trim?"~":"")+this.type};function iq(t){t:for(var e=t.length,r=1,n=-1,i;r0&&(n=0);break}return n>0?t.slice(0,n)+t.slice(i+1):t}var Wv;function aq(t,e){var r=dh(t,e);if(!r)return t+"";var n=r[0],i=r[1],a=i-(Wv=Math.max(-8,Math.min(8,Math.floor(i/3)))*3)+1,s=n.length;return a===s?n:a>s?n+new Array(a-s+1).join("0"):a>0?n.slice(0,a)+"."+n.slice(a):"0."+new Array(1-a).join("0")+dh(t,Math.max(0,e+a-1))[0]}function Hv(t,e){var r=dh(t,e);if(!r)return t+"";var n=r[0],i=r[1];return i<0?"0."+new Array(-i).join("0")+n:n.length>i+1?n.slice(0,i+1)+"."+n.slice(i+1):n+new Array(i-n.length+2).join("0")}const Gv={"%":(t,e)=>(t*100).toFixed(e),b:t=>Math.round(t).toString(2),c:t=>t+"",d:tq,e:(t,e)=>t.toExponential(e),f:(t,e)=>t.toFixed(e),g:(t,e)=>t.toPrecision(e),o:t=>Math.round(t).toString(8),p:(t,e)=>Hv(t*100,e),r:Hv,s:aq,X:t=>Math.round(t).toString(16).toUpperCase(),x:t=>Math.round(t).toString(16)};function jv(t){return t}var $v=Array.prototype.map,Xv=["y","z","a","f","p","n","\xB5","m","","k","M","G","T","P","E","Z","Y"];function Kv(t){var e=t.grouping===void 0||t.thousands===void 0?jv:eq($v.call(t.grouping,Number),t.thousands+""),r=t.currency===void 0?"":t.currency[0]+"",n=t.currency===void 0?"":t.currency[1]+"",i=t.decimal===void 0?".":t.decimal+"",a=t.numerals===void 0?jv:rq($v.call(t.numerals,String)),s=t.percent===void 0?"%":t.percent+"",o=t.minus===void 0?"\u2212":t.minus+"",l=t.nan===void 0?"NaN":t.nan+"";function u(d){d=Co(d);var f=d.fill,p=d.align,m=d.sign,_=d.symbol,y=d.zero,b=d.width,x=d.comma,k=d.precision,T=d.trim,C=d.type;C==="n"?(x=!0,C="g"):Gv[C]||(k===void 0&&(k=12),T=!0,C="g"),(y||f==="0"&&p==="=")&&(y=!0,f="0",p="=");var M=_==="$"?r:_==="#"&&/[boxX]/.test(C)?"0"+C.toLowerCase():"",S=_==="$"?n:/[%p]/.test(C)?s:"",R=Gv[C],A=/[defgprs%]/.test(C);k=k===void 0?6:/[gprs]/.test(C)?Math.max(1,Math.min(21,k)):Math.max(0,Math.min(20,k));function L(v){var B=M,w=S,D,N,z;if(C==="c")w=R(v)+w,v="";else{v=+v;var X=v<0||1/v<0;if(v=isNaN(v)?l:R(Math.abs(v),k),T&&(v=iq(v)),X&&+v==0&&m!=="+"&&(X=!1),B=(X?m==="("?m:o:m==="-"||m==="("?"":m)+B,w=(C==="s"?Xv[8+Wv/3]:"")+w+(X&&m==="("?")":""),A){for(D=-1,N=v.length;++Dz||z>57){w=(z===46?i+v.slice(D+1):v.slice(D))+w,v=v.slice(0,D);break}}}x&&!y&&(v=e(v,1/0));var ct=B.length+v.length+w.length,J=ct>1)+B+v+w+J.slice(ct);break;default:v=J+B+v+w;break}return a(v)}return L.toString=function(){return d+""},L}function h(d,f){var p=u((d=Co(d),d.type="f",d)),m=Math.max(-8,Math.min(8,Math.floor(Eo(f)/3)))*3,_=Math.pow(10,-m),y=Xv[8+m/3];return function(b){return p(_*b)+y}}return{format:u,formatPrefix:h}}var gh,yh,Jd;Zv({thousands:",",grouping:[3],currency:["$",""]});function Zv(t){return gh=Kv(t),yh=gh.format,Jd=gh.formatPrefix,gh}function Qv(t){return Math.max(0,-Eo(Math.abs(t)))}function Jv(t,e){return Math.max(0,Math.max(-8,Math.min(8,Math.floor(Eo(e)/3)))*3-Eo(Math.abs(t)))}function t6(t,e){return t=Math.abs(t),e=Math.abs(e)-t,Math.max(0,Eo(e)-Eo(t))+1}var te=1e-6,Hl=1e-12,Ae=Math.PI,rr=Ae/2,mh=Ae/4,Qr=Ae*2,Ue=180/Ae,re=Ae/180,Ne=Math.abs,So=Math.atan,Jr=Math.atan2,Kt=Math.cos,bh=Math.ceil,e6=Math.exp,t2=Math.hypot,_h=Math.log,e2=Math.pow,Ht=Math.sin,Dn=Math.sign||function(t){return t>0?1:t<0?-1:0},Sr=Math.sqrt,r2=Math.tan;function r6(t){return t>1?0:t<-1?Ae:Math.acos(t)}function tn(t){return t>1?rr:t<-1?-rr:Math.asin(t)}function n6(t){return(t=Ht(t/2))*t}function Je(){}function vh(t,e){t&&a6.hasOwnProperty(t.type)&&a6[t.type](t,e)}var i6={Feature:function(t,e){vh(t.geometry,e)},FeatureCollection:function(t,e){for(var r=t.features,n=-1,i=r.length;++n=0?1:-1,i=n*r,a=Kt(e),s=Ht(e),o=s2*s,l=a2*a+o*Kt(i),u=o*n*Ht(i);xh.add(Jr(u,l)),i2=t,a2=a,s2=s}function cq(t){return kh=new _r,ti(t,Si),kh*2}function wh(t){return[Jr(t[1],t[0]),tn(t[2])]}function Cs(t){var e=t[0],r=t[1],n=Kt(r);return[n*Kt(e),n*Ht(e),Ht(r)]}function Th(t,e){return t[0]*e[0]+t[1]*e[1]+t[2]*e[2]}function Ao(t,e){return[t[1]*e[2]-t[2]*e[1],t[2]*e[0]-t[0]*e[2],t[0]*e[1]-t[1]*e[0]]}function o2(t,e){t[0]+=e[0],t[1]+=e[1],t[2]+=e[2]}function Eh(t,e){return[t[0]*e,t[1]*e,t[2]*e]}function Ch(t){var e=Sr(t[0]*t[0]+t[1]*t[1]+t[2]*t[2]);t[0]/=e,t[1]/=e,t[2]/=e}var tr,pn,nr,En,Ss,u6,h6,Mo,Gl,Ba,Qi,Ji={point:l2,lineStart:d6,lineEnd:p6,polygonStart:function(){Ji.point=g6,Ji.lineStart=uq,Ji.lineEnd=hq,Gl=new _r,Si.polygonStart()},polygonEnd:function(){Si.polygonEnd(),Ji.point=l2,Ji.lineStart=d6,Ji.lineEnd=p6,xh<0?(tr=-(nr=180),pn=-(En=90)):Gl>te?En=90:Gl<-te&&(pn=-90),Qi[0]=tr,Qi[1]=nr},sphere:function(){tr=-(nr=180),pn=-(En=90)}};function l2(t,e){Ba.push(Qi=[tr=t,nr=t]),eEn&&(En=e)}function f6(t,e){var r=Cs([t*re,e*re]);if(Mo){var n=Ao(Mo,r),i=[n[1],-n[0],0],a=Ao(i,n);Ch(a),a=wh(a);var s=t-Ss,o=s>0?1:-1,l=a[0]*Ue*o,u,h=Ne(s)>180;h^(o*SsEn&&(En=u)):(l=(l+360)%360-180,h^(o*SsEn&&(En=e))),h?tCn(tr,nr)&&(nr=t):Cn(t,nr)>Cn(tr,nr)&&(tr=t):nr>=tr?(tnr&&(nr=t)):t>Ss?Cn(tr,t)>Cn(tr,nr)&&(nr=t):Cn(t,nr)>Cn(tr,nr)&&(tr=t)}else Ba.push(Qi=[tr=t,nr=t]);eEn&&(En=e),Mo=r,Ss=t}function d6(){Ji.point=f6}function p6(){Qi[0]=tr,Qi[1]=nr,Ji.point=l2,Mo=null}function g6(t,e){if(Mo){var r=t-Ss;Gl.add(Ne(r)>180?r+(r>0?360:-360):r)}else u6=t,h6=e;Si.point(t,e),f6(t,e)}function uq(){Si.lineStart()}function hq(){g6(u6,h6),Si.lineEnd(),Ne(Gl)>te&&(tr=-(nr=180)),Qi[0]=tr,Qi[1]=nr,Mo=null}function Cn(t,e){return(e-=t)<0?e+360:e}function fq(t,e){return t[0]-e[0]}function y6(t,e){return t[0]<=t[1]?t[0]<=e&&e<=t[1]:eCn(n[0],n[1])&&(n[1]=i[1]),Cn(i[0],n[1])>Cn(n[0],n[1])&&(n[0]=i[0])):a.push(n=i);for(s=-1/0,r=a.length-1,e=0,n=a[r];e<=r;n=i,++e)i=a[e],(o=Cn(n[1],i[0]))>s&&(s=o,tr=i[0],nr=n[1])}return Ba=Qi=null,tr===1/0||pn===1/0?[[NaN,NaN],[NaN,NaN]]:[[tr,pn],[nr,En]]}var jl,Sh,Ah,Mh,Lh,Rh,Ih,Nh,c2,u2,h2,m6,b6,en,rn,nn,ei={sphere:Je,point:f2,lineStart:_6,lineEnd:v6,polygonStart:function(){ei.lineStart=yq,ei.lineEnd=mq},polygonEnd:function(){ei.lineStart=_6,ei.lineEnd=v6}};function f2(t,e){t*=re,e*=re;var r=Kt(e);$l(r*Kt(t),r*Ht(t),Ht(e))}function $l(t,e,r){++jl,Ah+=(t-Ah)/jl,Mh+=(e-Mh)/jl,Lh+=(r-Lh)/jl}function _6(){ei.point=pq}function pq(t,e){t*=re,e*=re;var r=Kt(e);en=r*Kt(t),rn=r*Ht(t),nn=Ht(e),ei.point=gq,$l(en,rn,nn)}function gq(t,e){t*=re,e*=re;var r=Kt(e),n=r*Kt(t),i=r*Ht(t),a=Ht(e),s=Jr(Sr((s=rn*a-nn*i)*s+(s=nn*n-en*a)*s+(s=en*i-rn*n)*s),en*n+rn*i+nn*a);Sh+=s,Rh+=s*(en+(en=n)),Ih+=s*(rn+(rn=i)),Nh+=s*(nn+(nn=a)),$l(en,rn,nn)}function v6(){ei.point=f2}function yq(){ei.point=bq}function mq(){x6(m6,b6),ei.point=f2}function bq(t,e){m6=t,b6=e,t*=re,e*=re,ei.point=x6;var r=Kt(e);en=r*Kt(t),rn=r*Ht(t),nn=Ht(e),$l(en,rn,nn)}function x6(t,e){t*=re,e*=re;var r=Kt(e),n=r*Kt(t),i=r*Ht(t),a=Ht(e),s=rn*a-nn*i,o=nn*n-en*a,l=en*i-rn*n,u=t2(s,o,l),h=tn(u),d=u&&-h/u;c2.add(d*s),u2.add(d*o),h2.add(d*l),Sh+=h,Rh+=h*(en+(en=n)),Ih+=h*(rn+(rn=i)),Nh+=h*(nn+(nn=a)),$l(en,rn,nn)}function _q(t){jl=Sh=Ah=Mh=Lh=Rh=Ih=Nh=0,c2=new _r,u2=new _r,h2=new _r,ti(t,ei);var e=+c2,r=+u2,n=+h2,i=t2(e,r,n);return iAe?t+Math.round(-t/Qr)*Qr:t,e]}p2.invert=p2;function g2(t,e,r){return(t%=Qr)?e||r?d2(w6(t),T6(e,r)):w6(t):e||r?T6(e,r):p2}function k6(t){return function(e,r){return e+=t,[e>Ae?e-Qr:e<-Ae?e+Qr:e,r]}}function w6(t){var e=k6(t);return e.invert=k6(-t),e}function T6(t,e){var r=Kt(t),n=Ht(t),i=Kt(e),a=Ht(e);function s(o,l){var u=Kt(l),h=Kt(o)*u,d=Ht(o)*u,f=Ht(l),p=f*r+h*n;return[Jr(d*i-p*a,h*r-f*n),tn(p*i+d*a)]}return s.invert=function(o,l){var u=Kt(l),h=Kt(o)*u,d=Ht(o)*u,f=Ht(l),p=f*i-d*a;return[Jr(d*i+f*a,h*r+p*n),tn(p*r-h*n)]},s}function E6(t){t=g2(t[0]*re,t[1]*re,t.length>2?t[2]*re:0);function e(r){return r=t(r[0]*re,r[1]*re),r[0]*=Ue,r[1]*=Ue,r}return e.invert=function(r){return r=t.invert(r[0]*re,r[1]*re),r[0]*=Ue,r[1]*=Ue,r},e}function C6(t,e,r,n,i,a){if(!!r){var s=Kt(e),o=Ht(e),l=n*r;i==null?(i=e+n*Qr,a=e-l/2):(i=S6(s,i),a=S6(s,a),(n>0?ia)&&(i+=n*Qr));for(var u,h=i;n>0?h>a:h1&&t.push(t.pop().concat(t.shift()))},result:function(){var r=t;return t=[],e=null,r}}}function Bh(t,e){return Ne(t[0]-e[0])=0;--o)i.point((d=h[o])[0],d[1]);else n(f.x,f.p.x,-1,i);f=f.p}f=f.o,h=f.z,p=!p}while(!f.v);i.lineEnd()}}}function L6(t){if(!!(e=t.length)){for(var e,r=0,n=t[0],i;++r=0?1:-1,L=A*R,v=L>Ae,B=y*M;if(l.add(Jr(B*A*Ht(L),b*S+B*Kt(L))),s+=v?R+A*Qr:R,v^m>=r^T>=r){var w=Ao(Cs(p),Cs(k));Ch(w);var D=Ao(a,w);Ch(D);var N=(v^R>=0?-1:1)*tn(D[2]);(n>N||n===N&&(w[0]||w[1]))&&(o+=v^R>=0?1:-1)}}return(s<-te||s0){for(l||(i.polygonStart(),l=!0),i.lineStart(),M=0;M1&&T&2&&C.push(C.pop().concat(C.shift())),h.push(C.filter(xq))}}return f}}function xq(t){return t.length>1}function kq(t,e){return((t=t.x)[0]<0?t[1]-rr-te:rr-t[1])-((e=e.x)[0]<0?e[1]-rr-te:rr-e[1])}const m2=I6(function(){return!0},wq,Eq,[-Ae,-rr]);function wq(t){var e=NaN,r=NaN,n=NaN,i;return{lineStart:function(){t.lineStart(),i=1},point:function(a,s){var o=a>0?Ae:-Ae,l=Ne(a-e);Ne(l-Ae)0?rr:-rr),t.point(n,r),t.lineEnd(),t.lineStart(),t.point(o,r),t.point(a,r),i=0):n!==o&&l>=Ae&&(Ne(e-n)te?So((Ht(e)*(a=Kt(n))*Ht(r)-Ht(n)*(i=Kt(e))*Ht(t))/(i*a*s)):(e+n)/2}function Eq(t,e,r,n){var i;if(t==null)i=r*rr,n.point(-Ae,i),n.point(0,i),n.point(Ae,i),n.point(Ae,0),n.point(Ae,-i),n.point(0,-i),n.point(-Ae,-i),n.point(-Ae,0),n.point(-Ae,i);else if(Ne(t[0]-e[0])>te){var a=t[0]0,i=Ne(e)>te;function a(h,d,f,p){C6(p,t,r,f,h,d)}function s(h,d){return Kt(h)*Kt(d)>e}function o(h){var d,f,p,m,_;return{lineStart:function(){m=p=!1,_=1},point:function(y,b){var x=[y,b],k,T=s(y,b),C=n?T?0:u(y,b):T?u(y+(y<0?Ae:-Ae),b):0;if(!d&&(m=p=T)&&h.lineStart(),T!==p&&(k=l(d,x),(!k||Bh(d,k)||Bh(x,k))&&(x[2]=1)),T!==p)_=0,T?(h.lineStart(),k=l(x,d),h.point(k[0],k[1])):(k=l(d,x),h.point(k[0],k[1],2),h.lineEnd()),d=k;else if(i&&d&&n^T){var M;!(C&f)&&(M=l(x,d,!0))&&(_=0,n?(h.lineStart(),h.point(M[0][0],M[0][1]),h.point(M[1][0],M[1][1]),h.lineEnd()):(h.point(M[1][0],M[1][1]),h.lineEnd(),h.lineStart(),h.point(M[0][0],M[0][1],3)))}T&&(!d||!Bh(d,x))&&h.point(x[0],x[1]),d=x,p=T,f=C},lineEnd:function(){p&&h.lineEnd(),d=null},clean:function(){return _|(m&&p)<<1}}}function l(h,d,f){var p=Cs(h),m=Cs(d),_=[1,0,0],y=Ao(p,m),b=Th(y,y),x=y[0],k=b-x*x;if(!k)return!f&&h;var T=e*b/k,C=-e*x/k,M=Ao(_,y),S=Eh(_,T),R=Eh(y,C);o2(S,R);var A=M,L=Th(S,A),v=Th(A,A),B=L*L-v*(Th(S,S)-1);if(!(B<0)){var w=Sr(B),D=Eh(A,(-L-w)/v);if(o2(D,S),D=wh(D),!f)return D;var N=h[0],z=d[0],X=h[1],ct=d[1],J;z0^D[1]<(Ne(D[0]-N)Ae^(N<=D[0]&&D[0]<=z)){var ut=Eh(A,(-L+w)/v);return o2(ut,S),[D,wh(ut)]}}}function u(h,d){var f=n?t:Ae-t,p=0;return h<-f?p|=1:h>f&&(p|=2),d<-f?p|=4:d>f&&(p|=8),p}return I6(s,o,a,n?[0,-t]:[-Ae,t-Ae])}function Cq(t,e,r,n,i,a){var s=t[0],o=t[1],l=e[0],u=e[1],h=0,d=1,f=l-s,p=u-o,m;if(m=r-s,!(!f&&m>0)){if(m/=f,f<0){if(m0){if(m>d)return;m>h&&(h=m)}if(m=i-s,!(!f&&m<0)){if(m/=f,f<0){if(m>d)return;m>h&&(h=m)}else if(f>0){if(m0)){if(m/=p,p<0){if(m0){if(m>d)return;m>h&&(h=m)}if(m=a-o,!(!p&&m<0)){if(m/=p,p<0){if(m>d)return;m>h&&(h=m)}else if(p>0){if(m0&&(t[0]=s+h*f,t[1]=o+h*p),d<1&&(e[0]=s+d*f,e[1]=o+d*p),!0}}}}}var Xl=1e9,Oh=-Xl;function Fh(t,e,r,n){function i(u,h){return t<=u&&u<=r&&e<=h&&h<=n}function a(u,h,d,f){var p=0,m=0;if(u==null||(p=s(u,d))!==(m=s(h,d))||l(u,h)<0^d>0)do f.point(p===0||p===3?t:r,p>1?n:e);while((p=(p+d+4)%4)!==m);else f.point(h[0],h[1])}function s(u,h){return Ne(u[0]-t)0?0:3:Ne(u[0]-r)0?2:1:Ne(u[1]-e)0?1:0:h>0?3:2}function o(u,h){return l(u.x,h.x)}function l(u,h){var d=s(u,1),f=s(h,1);return d!==f?d-f:d===0?h[1]-u[1]:d===1?u[0]-h[0]:d===2?u[1]-h[1]:h[0]-u[0]}return function(u){var h=u,d=A6(),f,p,m,_,y,b,x,k,T,C,M,S={point:R,lineStart:B,lineEnd:w,polygonStart:L,polygonEnd:v};function R(N,z){i(N,z)&&h.point(N,z)}function A(){for(var N=0,z=0,X=p.length;zn&&(W-lt)*(n-ut)>(tt-ut)*(t-lt)&&++N:tt<=n&&(W-lt)*(n-ut)<(tt-ut)*(t-lt)&&--N;return N}function L(){h=d,f=[],p=[],M=!0}function v(){var N=A(),z=M&&N,X=(f=j0(f)).length;(z||X)&&(u.polygonStart(),z&&(u.lineStart(),a(null,null,1,u),u.lineEnd()),X&&M6(f,o,N,a,u),u.polygonEnd()),h=u,f=p=m=null}function B(){S.point=D,p&&p.push(m=[]),C=!0,T=!1,x=k=NaN}function w(){f&&(D(_,y),b&&T&&d.rejoin(),f.push(d.result())),S.point=R,T&&h.lineEnd()}function D(N,z){var X=i(N,z);if(p&&m.push([N,z]),C)_=N,y=z,b=X,C=!1,X&&(h.lineStart(),h.point(N,z));else if(X&&T)h.point(N,z);else{var ct=[x=Math.max(Oh,Math.min(Xl,x)),k=Math.max(Oh,Math.min(Xl,k))],J=[N=Math.max(Oh,Math.min(Xl,N)),z=Math.max(Oh,Math.min(Xl,z))];Cq(ct,J,t,e,r,n)?(T||(h.lineStart(),h.point(ct[0],ct[1])),h.point(J[0],J[1]),X||h.lineEnd(),M=!1):X&&(h.lineStart(),h.point(N,z),M=!1)}x=N,k=z,T=X}return S}}function Sq(){var t=0,e=0,r=960,n=500,i,a,s;return s={stream:function(o){return i&&a===o?i:i=Fh(t,e,r,n)(a=o)},extent:function(o){return arguments.length?(t=+o[0][0],e=+o[0][1],r=+o[1][0],n=+o[1][1],i=a=null,s):[[t,e],[r,n]]}}}var b2,_2,Ph,qh,Ro={sphere:Je,point:Je,lineStart:Aq,lineEnd:Je,polygonStart:Je,polygonEnd:Je};function Aq(){Ro.point=Lq,Ro.lineEnd=Mq}function Mq(){Ro.point=Ro.lineEnd=Je}function Lq(t,e){t*=re,e*=re,_2=t,Ph=Ht(e),qh=Kt(e),Ro.point=Rq}function Rq(t,e){t*=re,e*=re;var r=Ht(e),n=Kt(e),i=Ne(t-_2),a=Kt(i),s=Ht(i),o=n*s,l=qh*r-Ph*n*a,u=Ph*r+qh*n*a;b2.add(Jr(Sr(o*o+l*l),u)),_2=t,Ph=r,qh=n}function B6(t){return b2=new _r,ti(t,Ro),+b2}var v2=[null,null],Iq={type:"LineString",coordinates:v2};function Vh(t,e){return v2[0]=t,v2[1]=e,B6(Iq)}var D6={Feature:function(t,e){return zh(t.geometry,e)},FeatureCollection:function(t,e){for(var r=t.features,n=-1,i=r.length;++n0&&(i=Vh(t[a],t[a-1]),i>0&&r<=i&&n<=i&&(r+n-i)*(1-Math.pow((r-n)/i,2))te}).map(f)).concat(Ca(bh(a/u)*u,i,u).filter(function(k){return Ne(k%d)>te}).map(p))}return b.lines=function(){return x().map(function(k){return{type:"LineString",coordinates:k}})},b.outline=function(){return{type:"Polygon",coordinates:[m(n).concat(_(s).slice(1),m(r).reverse().slice(1),_(o).reverse().slice(1))]}},b.extent=function(k){return arguments.length?b.extentMajor(k).extentMinor(k):b.extentMinor()},b.extentMajor=function(k){return arguments.length?(n=+k[0][0],r=+k[1][0],o=+k[0][1],s=+k[1][1],n>r&&(k=n,n=r,r=k),o>s&&(k=o,o=s,s=k),b.precision(y)):[[n,o],[r,s]]},b.extentMinor=function(k){return arguments.length?(e=+k[0][0],t=+k[1][0],a=+k[0][1],i=+k[1][1],e>t&&(k=e,e=t,t=k),a>i&&(k=a,a=i,i=k),b.precision(y)):[[e,a],[t,i]]},b.step=function(k){return arguments.length?b.stepMajor(k).stepMinor(k):b.stepMinor()},b.stepMajor=function(k){return arguments.length?(h=+k[0],d=+k[1],b):[h,d]},b.stepMinor=function(k){return arguments.length?(l=+k[0],u=+k[1],b):[l,u]},b.precision=function(k){return arguments.length?(y=+k,f=z6(a,i,90),p=Y6(e,t,y),m=z6(o,s,90),_=Y6(n,r,y),b):y},b.extentMajor([[-180,-90+te],[180,90-te]]).extentMinor([[-180,-80-te],[180,80+te]])}function Dq(){return U6()()}function Oq(t,e){var r=t[0]*re,n=t[1]*re,i=e[0]*re,a=e[1]*re,s=Kt(n),o=Ht(n),l=Kt(a),u=Ht(a),h=s*Kt(r),d=s*Ht(r),f=l*Kt(i),p=l*Ht(i),m=2*tn(Sr(n6(a-n)+s*l*n6(i-r))),_=Ht(m),y=m?function(b){var x=Ht(b*=m)/_,k=Ht(m-b)/_,T=k*h+x*f,C=k*d+x*p,M=k*o+x*u;return[Jr(C,T)*Ue,Jr(M,Sr(T*T+C*C))*Ue]}:function(){return[r*Ue,n*Ue]};return y.distance=m,y}const Kl=t=>t;var x2=new _r,k2=new _r,W6,H6,w2,T2,Da={point:Je,lineStart:Je,lineEnd:Je,polygonStart:function(){Da.lineStart=Fq,Da.lineEnd=qq},polygonEnd:function(){Da.lineStart=Da.lineEnd=Da.point=Je,x2.add(Ne(k2)),k2=new _r},result:function(){var t=x2/2;return x2=new _r,t}};function Fq(){Da.point=Pq}function Pq(t,e){Da.point=G6,W6=w2=t,H6=T2=e}function G6(t,e){k2.add(T2*t-w2*e),w2=t,T2=e}function qq(){G6(W6,H6)}const j6=Da;var Io=1/0,Yh=Io,Zl=-Io,Uh=Zl,Vq={point:zq,lineStart:Je,lineEnd:Je,polygonStart:Je,polygonEnd:Je,result:function(){var t=[[Io,Yh],[Zl,Uh]];return Zl=Uh=-(Yh=Io=1/0),t}};function zq(t,e){tZl&&(Zl=t),eUh&&(Uh=e)}const Wh=Vq;var E2=0,C2=0,Ql=0,Hh=0,Gh=0,No=0,S2=0,A2=0,Jl=0,$6,X6,Ai,Mi,ri={point:As,lineStart:K6,lineEnd:Z6,polygonStart:function(){ri.lineStart=Wq,ri.lineEnd=Hq},polygonEnd:function(){ri.point=As,ri.lineStart=K6,ri.lineEnd=Z6},result:function(){var t=Jl?[S2/Jl,A2/Jl]:No?[Hh/No,Gh/No]:Ql?[E2/Ql,C2/Ql]:[NaN,NaN];return E2=C2=Ql=Hh=Gh=No=S2=A2=Jl=0,t}};function As(t,e){E2+=t,C2+=e,++Ql}function K6(){ri.point=Yq}function Yq(t,e){ri.point=Uq,As(Ai=t,Mi=e)}function Uq(t,e){var r=t-Ai,n=e-Mi,i=Sr(r*r+n*n);Hh+=i*(Ai+t)/2,Gh+=i*(Mi+e)/2,No+=i,As(Ai=t,Mi=e)}function Z6(){ri.point=As}function Wq(){ri.point=Gq}function Hq(){Q6($6,X6)}function Gq(t,e){ri.point=Q6,As($6=Ai=t,X6=Mi=e)}function Q6(t,e){var r=t-Ai,n=e-Mi,i=Sr(r*r+n*n);Hh+=i*(Ai+t)/2,Gh+=i*(Mi+e)/2,No+=i,i=Mi*t-Ai*e,S2+=i*(Ai+t),A2+=i*(Mi+e),Jl+=i*3,As(Ai=t,Mi=e)}const J6=ri;function tx(t){this._context=t}tx.prototype={_radius:4.5,pointRadius:function(t){return this._radius=t,this},polygonStart:function(){this._line=0},polygonEnd:function(){this._line=NaN},lineStart:function(){this._point=0},lineEnd:function(){this._line===0&&this._context.closePath(),this._point=NaN},point:function(t,e){switch(this._point){case 0:{this._context.moveTo(t,e),this._point=1;break}case 1:{this._context.lineTo(t,e);break}default:{this._context.moveTo(t+this._radius,e),this._context.arc(t,e,this._radius,0,Qr);break}}},result:Je};var M2=new _r,L2,ex,rx,tc,ec,jh={point:Je,lineStart:function(){jh.point=jq},lineEnd:function(){L2&&nx(ex,rx),jh.point=Je},polygonStart:function(){L2=!0},polygonEnd:function(){L2=null},result:function(){var t=+M2;return M2=new _r,t}};function jq(t,e){jh.point=nx,ex=tc=t,rx=ec=e}function nx(t,e){tc-=t,ec-=e,M2.add(Sr(tc*tc+ec*ec)),tc=t,ec=e}const ix=jh;function ax(){this._string=[]}ax.prototype={_radius:4.5,_circle:sx(4.5),pointRadius:function(t){return(t=+t)!==this._radius&&(this._radius=t,this._circle=null),this},polygonStart:function(){this._line=0},polygonEnd:function(){this._line=NaN},lineStart:function(){this._point=0},lineEnd:function(){this._line===0&&this._string.push("Z"),this._point=NaN},point:function(t,e){switch(this._point){case 0:{this._string.push("M",t,",",e),this._point=1;break}case 1:{this._string.push("L",t,",",e);break}default:{this._circle==null&&(this._circle=sx(this._radius)),this._string.push("M",t,",",e,this._circle);break}}},result:function(){if(this._string.length){var t=this._string.join("");return this._string=[],t}else return null}};function sx(t){return"m0,"+t+"a"+t+","+t+" 0 1,1 0,"+-2*t+"a"+t+","+t+" 0 1,1 0,"+2*t+"z"}function $q(t,e){var r=4.5,n,i;function a(s){return s&&(typeof r=="function"&&i.pointRadius(+r.apply(this,arguments)),ti(s,n(i))),i.result()}return a.area=function(s){return ti(s,n(j6)),j6.result()},a.measure=function(s){return ti(s,n(ix)),ix.result()},a.bounds=function(s){return ti(s,n(Wh)),Wh.result()},a.centroid=function(s){return ti(s,n(J6)),J6.result()},a.projection=function(s){return arguments.length?(n=s==null?(t=null,Kl):(t=s).stream,a):t},a.context=function(s){return arguments.length?(i=s==null?(e=null,new ax):new tx(e=s),typeof r!="function"&&i.pointRadius(r),a):e},a.pointRadius=function(s){return arguments.length?(r=typeof s=="function"?s:(i.pointRadius(+s),+s),a):r},a.projection(t).context(e)}function Xq(t){return{stream:rc(t)}}function rc(t){return function(e){var r=new R2;for(var n in t)r[n]=t[n];return r.stream=e,r}}function R2(){}R2.prototype={constructor:R2,point:function(t,e){this.stream.point(t,e)},sphere:function(){this.stream.sphere()},lineStart:function(){this.stream.lineStart()},lineEnd:function(){this.stream.lineEnd()},polygonStart:function(){this.stream.polygonStart()},polygonEnd:function(){this.stream.polygonEnd()}};function I2(t,e,r){var n=t.clipExtent&&t.clipExtent();return t.scale(150).translate([0,0]),n!=null&&t.clipExtent(null),ti(r,t.stream(Wh)),e(Wh.result()),n!=null&&t.clipExtent(n),t}function $h(t,e,r){return I2(t,function(n){var i=e[1][0]-e[0][0],a=e[1][1]-e[0][1],s=Math.min(i/(n[1][0]-n[0][0]),a/(n[1][1]-n[0][1])),o=+e[0][0]+(i-s*(n[1][0]+n[0][0]))/2,l=+e[0][1]+(a-s*(n[1][1]+n[0][1]))/2;t.scale(150*s).translate([o,l])},r)}function N2(t,e,r){return $h(t,[[0,0],e],r)}function B2(t,e,r){return I2(t,function(n){var i=+e,a=i/(n[1][0]-n[0][0]),s=(i-a*(n[1][0]+n[0][0]))/2,o=-a*n[0][1];t.scale(150*a).translate([s,o])},r)}function D2(t,e,r){return I2(t,function(n){var i=+e,a=i/(n[1][1]-n[0][1]),s=-a*n[0][0],o=(i-a*(n[1][1]+n[0][1]))/2;t.scale(150*a).translate([s,o])},r)}var ox=16,Kq=Kt(30*re);function lx(t,e){return+e?Qq(t,e):Zq(t)}function Zq(t){return rc({point:function(e,r){e=t(e,r),this.stream.point(e[0],e[1])}})}function Qq(t,e){function r(n,i,a,s,o,l,u,h,d,f,p,m,_,y){var b=u-n,x=h-i,k=b*b+x*x;if(k>4*e&&_--){var T=s+f,C=o+p,M=l+m,S=Sr(T*T+C*C+M*M),R=tn(M/=S),A=Ne(Ne(M)-1)e||Ne((b*w+x*D)/k-.5)>.3||s*f+o*p+l*m2?N[2]%360*re:0,w()):[o*Ue,l*Ue,u*Ue]},v.angle=function(N){return arguments.length?(d=N%360*re,w()):d*Ue},v.reflectX=function(N){return arguments.length?(f=N?-1:1,w()):f<0},v.reflectY=function(N){return arguments.length?(p=N?-1:1,w()):p<0},v.precision=function(N){return arguments.length?(M=lx(S,C=N*N),D()):Sr(C)},v.fitExtent=function(N,z){return $h(v,N,z)},v.fitSize=function(N,z){return N2(v,N,z)},v.fitWidth=function(N,z){return B2(v,N,z)},v.fitHeight=function(N,z){return D2(v,N,z)};function w(){var N=cx(r,0,0,f,p,d).apply(null,e(a,s)),z=cx(r,n-N[0],i-N[1],f,p,d);return h=g2(o,l,u),S=d2(e,z),R=d2(h,S),M=lx(S,C),D()}function D(){return A=L=null,v}return function(){return e=t.apply(this,arguments),v.invert=e.invert&&B,w()}}function F2(t){var e=0,r=Ae/3,n=O2(t),i=n(e,r);return i.parallels=function(a){return arguments.length?n(e=a[0]*re,r=a[1]*re):[e*Ue,r*Ue]},i}function rV(t){var e=Kt(t);function r(n,i){return[n*e,Ht(i)/e]}return r.invert=function(n,i){return[n/e,tn(i*e)]},r}function ux(t,e){var r=Ht(t),n=(r+Ht(e))/2;if(Ne(n)=.12&&y<.234&&_>=-.425&&_<-.214?i:y>=.166&&y<.234&&_>=-.214&&_<-.115?s:r).invert(f)},h.stream=function(f){return t&&e===f?t:t=nV([r.stream(e=f),i.stream(f),s.stream(f)])},h.precision=function(f){return arguments.length?(r.precision(f),i.precision(f),s.precision(f),d()):r.precision()},h.scale=function(f){return arguments.length?(r.scale(f),i.scale(f*.35),s.scale(f),h.translate(r.translate())):r.scale()},h.translate=function(f){if(!arguments.length)return r.translate();var p=r.scale(),m=+f[0],_=+f[1];return n=r.translate(f).clipExtent([[m-.455*p,_-.238*p],[m+.455*p,_+.238*p]]).stream(u),a=i.translate([m-.307*p,_+.201*p]).clipExtent([[m-.425*p+te,_+.12*p+te],[m-.214*p-te,_+.234*p-te]]).stream(u),o=s.translate([m-.205*p,_+.212*p]).clipExtent([[m-.214*p+te,_+.166*p+te],[m-.115*p-te,_+.234*p-te]]).stream(u),d()},h.fitExtent=function(f,p){return $h(h,f,p)},h.fitSize=function(f,p){return N2(h,f,p)},h.fitWidth=function(f,p){return B2(h,f,p)},h.fitHeight=function(f,p){return D2(h,f,p)};function d(){return t=e=null,h}return h.scale(1070)}function fx(t){return function(e,r){var n=Kt(e),i=Kt(r),a=t(n*i);return a===1/0?[2,0]:[a*i*Ht(e),a*Ht(r)]}}function nc(t){return function(e,r){var n=Sr(e*e+r*r),i=t(n),a=Ht(i),s=Kt(i);return[Jr(e*a,n*s),tn(n&&r*a/n)]}}var P2=fx(function(t){return Sr(2/(1+t))});P2.invert=nc(function(t){return 2*tn(t/2)});function aV(){return Li(P2).scale(124.75).clipAngle(180-.001)}var q2=fx(function(t){return(t=r6(t))&&t/Ht(t)});q2.invert=nc(function(t){return t});function sV(){return Li(q2).scale(79.4188).clipAngle(180-.001)}function ic(t,e){return[t,_h(r2((rr+e)/2))]}ic.invert=function(t,e){return[t,2*So(e6(e))-rr]};function oV(){return dx(ic).scale(961/Qr)}function dx(t){var e=Li(t),r=e.center,n=e.scale,i=e.translate,a=e.clipExtent,s=null,o,l,u;e.scale=function(d){return arguments.length?(n(d),h()):n()},e.translate=function(d){return arguments.length?(i(d),h()):i()},e.center=function(d){return arguments.length?(r(d),h()):r()},e.clipExtent=function(d){return arguments.length?(d==null?s=o=l=u=null:(s=+d[0][0],o=+d[0][1],l=+d[1][0],u=+d[1][1]),h()):s==null?null:[[s,o],[l,u]]};function h(){var d=Ae*n(),f=e(E6(e.rotate()).invert([0,0]));return a(s==null?[[f[0]-d,f[1]-d],[f[0]+d,f[1]+d]]:t===ic?[[Math.max(f[0]-d,s),o],[Math.min(f[0]+d,l),u]]:[[s,Math.max(f[1]-d,o)],[l,Math.min(f[1]+d,u)]])}return h()}function Kh(t){return r2((rr+t)/2)}function px(t,e){var r=Kt(t),n=t===e?Ht(t):_h(r/Kt(e))/_h(Kh(e)/Kh(t)),i=r*e2(Kh(t),n)/n;if(!n)return ic;function a(s,o){i>0?o<-rr+te&&(o=-rr+te):o>rr-te&&(o=rr-te);var l=i/e2(Kh(o),n);return[l*Ht(n*s),i-l*Kt(n*s)]}return a.invert=function(s,o){var l=i-o,u=Dn(n)*Sr(s*s+l*l),h=Jr(s,Ne(l))*Dn(l);return l*n<0&&(h-=Ae*Dn(s)*Dn(l)),[h/n,2*So(e2(i/u,1/n))-rr]},a}function lV(){return F2(px).scale(109.5).parallels([30,30])}function ac(t,e){return[t,e]}ac.invert=ac;function cV(){return Li(ac).scale(152.63)}function gx(t,e){var r=Kt(t),n=t===e?Ht(t):(r-Kt(e))/(e-t),i=r/n+t;if(Ne(n)te&&--n>0);return[t/(.8707+(a=r*r)*(-.131979+a*(-.013791+a*a*a*(.003971-.001529*a)))),r]};function gV(){return Li(Y2).scale(175.295)}function U2(t,e){return[Kt(e)*Ht(t),Ht(e)]}U2.invert=nc(tn);function yV(){return Li(U2).scale(249.5).clipAngle(90+te)}function W2(t,e){var r=Kt(e),n=1+Kt(t)*r;return[r*Ht(t)/n,Ht(e)/n]}W2.invert=nc(function(t){return 2*So(t)});function mV(){return Li(W2).scale(250).clipAngle(142)}function H2(t,e){return[_h(r2((rr+e)/2)),-t]}H2.invert=function(t,e){return[-e,2*So(e6(t))-rr]};function bV(){var t=dx(H2),e=t.center,r=t.rotate;return t.center=function(n){return arguments.length?e([-n[1],n[0]]):(n=e(),[n[1],-n[0]])},t.rotate=function(n){return arguments.length?r([n[0],n[1],n.length>2?n[2]+90:90]):(n=r(),[n[0],n[1],n[2]-90])},r([0,0,90]).scale(159.155)}function _V(t,e){return t.parent===e.parent?1:2}function vV(t){return t.reduce(xV,0)/t.length}function xV(t,e){return t+e.x}function kV(t){return 1+t.reduce(wV,0)}function wV(t,e){return Math.max(t,e.y)}function TV(t){for(var e;e=t.children;)t=e[0];return t}function EV(t){for(var e;e=t.children;)t=e[e.length-1];return t}function CV(){var t=_V,e=1,r=1,n=!1;function i(a){var s,o=0;a.eachAfter(function(f){var p=f.children;p?(f.x=vV(p),f.y=kV(p)):(f.x=s?o+=t(f,s):0,f.y=0,s=f)});var l=TV(a),u=EV(a),h=l.x-t(l,u)/2,d=u.x+t(u,l)/2;return a.eachAfter(n?function(f){f.x=(f.x-a.x)*e,f.y=(a.y-f.y)*r}:function(f){f.x=(f.x-h)/(d-h)*e,f.y=(1-(a.y?f.y/a.y:1))*r})}return i.separation=function(a){return arguments.length?(t=a,i):t},i.size=function(a){return arguments.length?(n=!1,e=+a[0],r=+a[1],i):n?null:[e,r]},i.nodeSize=function(a){return arguments.length?(n=!0,e=+a[0],r=+a[1],i):n?[e,r]:null},i}function SV(t){var e=0,r=t.children,n=r&&r.length;if(!n)e=1;else for(;--n>=0;)e+=r[n].value;t.value=e}function AV(){return this.eachAfter(SV)}function MV(t,e){let r=-1;for(const n of this)t.call(e,n,++r,this);return this}function LV(t,e){for(var r=this,n=[r],i,a,s=-1;r=n.pop();)if(t.call(e,r,++s,this),i=r.children)for(a=i.length-1;a>=0;--a)n.push(i[a]);return this}function RV(t,e){for(var r=this,n=[r],i=[],a,s,o,l=-1;r=n.pop();)if(i.push(r),a=r.children)for(s=0,o=a.length;s=0;)r+=n[i].value;e.value=r})}function BV(t){return this.eachBefore(function(e){e.children&&e.children.sort(t)})}function DV(t){for(var e=this,r=OV(e,t),n=[e];e!==r;)e=e.parent,n.push(e);for(var i=n.length;t!==r;)n.splice(i,0,t),t=t.parent;return n}function OV(t,e){if(t===e)return t;var r=t.ancestors(),n=e.ancestors(),i=null;for(t=r.pop(),e=n.pop();t===e;)i=t,t=r.pop(),e=n.pop();return i}function FV(){for(var t=this,e=[t];t=t.parent;)e.push(t);return e}function PV(){return Array.from(this)}function qV(){var t=[];return this.eachBefore(function(e){e.children||t.push(e)}),t}function VV(){var t=this,e=[];return t.each(function(r){r!==t&&e.push({source:r.parent,target:r})}),e}function*zV(){var t=this,e,r=[t],n,i,a;do for(e=r.reverse(),r=[];t=e.pop();)if(yield t,n=t.children)for(i=0,a=n.length;i=0;--o)i.push(a=s[o]=new Ms(s[o])),a.parent=n,a.depth=n.depth+1;return r.eachBefore(yx)}function YV(){return G2(this).eachBefore(HV)}function UV(t){return t.children}function WV(t){return Array.isArray(t)?t[1]:null}function HV(t){t.data.value!==void 0&&(t.value=t.data.value),t.data=t.data.data}function yx(t){var e=0;do t.height=e;while((t=t.parent)&&t.height<++e)}function Ms(t){this.data=t,this.depth=this.height=0,this.parent=null}Ms.prototype=G2.prototype={constructor:Ms,count:AV,each:MV,eachAfter:RV,eachBefore:LV,find:IV,sum:NV,sort:BV,path:DV,ancestors:FV,descendants:PV,leaves:qV,links:VV,copy:YV,[Symbol.iterator]:zV};function Qh(t){return t==null?null:mx(t)}function mx(t){if(typeof t!="function")throw new Error;return t}function Ls(){return 0}function Bo(t){return function(){return t}}const GV=1664525,jV=1013904223,bx=4294967296;function j2(){let t=1;return()=>(t=(GV*t+jV)%bx)/bx}function $V(t){return typeof t=="object"&&"length"in t?t:Array.from(t)}function XV(t,e){let r=t.length,n,i;for(;r;)i=e()*r--|0,n=t[r],t[r]=t[i],t[i]=n;return t}function KV(t){return _x(t,j2())}function _x(t,e){for(var r=0,n=(t=XV(Array.from(t),e)).length,i=[],a,s;r0&&r*r>n*n+i*i}function $2(t,e){for(var r=0;r1e-6?(v+Math.sqrt(v*v-4*L*B))/(2*L):B/v);return{x:n+M+S*w,y:i+R+A*w,r:w}}function kx(t,e,r){var n=t.x-e.x,i,a,s=t.y-e.y,o,l,u=n*n+s*s;u?(a=e.r+r.r,a*=a,l=t.r+r.r,l*=l,a>l?(i=(u+l-a)/(2*u),o=Math.sqrt(Math.max(0,l/u-i*i)),r.x=t.x-i*n-o*s,r.y=t.y-i*s+o*n):(i=(u+a-l)/(2*u),o=Math.sqrt(Math.max(0,a/u-i*i)),r.x=e.x+i*n-o*s,r.y=e.y+i*s+o*n)):(r.x=e.x+r.r,r.y=e.y)}function wx(t,e){var r=t.r+e.r-1e-6,n=e.x-t.x,i=e.y-t.y;return r>0&&r*r>n*n+i*i}function Tx(t){var e=t._,r=t.next._,n=e.r+r.r,i=(e.x*r.r+r.x*e.r)/n,a=(e.y*r.r+r.y*e.r)/n;return i*i+a*a}function tf(t){this._=t,this.next=null,this.previous=null}function Ex(t,e){if(!(a=(t=$V(t)).length))return 0;var r,n,i,a,s,o,l,u,h,d,f;if(r=t[0],r.x=0,r.y=0,!(a>1))return r.r;if(n=t[1],r.x=-n.r,n.x=r.r,n.y=0,!(a>2))return r.r+n.r;kx(n,r,i=t[2]),r=new tf(r),n=new tf(n),i=new tf(i),r.next=i.previous=n,n.next=r.previous=i,i.next=n.previous=r;t:for(l=3;llz(r(T,C,i))),x=b.map(Lx),k=new Set(b).add("");for(const T of x)k.has(T)||(k.add(T),b.push(T),x.push(Lx(T)),a.push(K2));s=(T,C)=>b[C],o=(T,C)=>x[C]}for(h=0,l=a.length;h=0&&(p=a[b],p.data===K2);--b)p.data=null}if(d.parent=iz,d.eachBefore(function(b){b.depth=b.parent.depth+1,--l}).eachBefore(yx),d.parent=null,l>0)throw new Error("cycle");return d}return n.id=function(i){return arguments.length?(t=Qh(i),n):t},n.parentId=function(i){return arguments.length?(e=Qh(i),n):e},n.path=function(i){return arguments.length?(r=Qh(i),n):r},n}function lz(t){t=`${t}`;let e=t.length;return Z2(t,e-1)&&!Z2(t,e-2)&&(t=t.slice(0,-1)),t[0]==="/"?t:`/${t}`}function Lx(t){let e=t.length;if(e<2)return"";for(;--e>1&&!Z2(t,e););return t.slice(0,e)}function Z2(t,e){if(t[e]==="/"){let r=0;for(;e>0&&t[--e]==="\\";)++r;if((r&1)===0)return!0}return!1}function cz(t,e){return t.parent===e.parent?1:2}function Q2(t){var e=t.children;return e?e[0]:t.t}function J2(t){var e=t.children;return e?e[e.length-1]:t.t}function uz(t,e,r){var n=r/(e.i-t.i);e.c-=n,e.s+=r,t.c+=n,e.z+=r,e.m+=r}function hz(t){for(var e=0,r=0,n=t.children,i=n.length,a;--i>=0;)a=n[i],a.z+=e,a.m+=e,e+=a.s+(r+=a.c)}function fz(t,e,r){return t.a.parent===e.parent?t.a:r}function ef(t,e){this._=t,this.parent=null,this.children=null,this.A=null,this.a=this,this.z=0,this.m=0,this.c=0,this.s=0,this.t=null,this.i=e}ef.prototype=Object.create(Ms.prototype);function dz(t){for(var e=new ef(t,0),r,n=[e],i,a,s,o;r=n.pop();)if(a=r._.children)for(r.children=new Array(o=a.length),s=o-1;s>=0;--s)n.push(i=r.children[s]=new ef(a[s],s)),i.parent=r;return(e.parent=new ef(null,0)).children=[e],e}function pz(){var t=cz,e=1,r=1,n=null;function i(u){var h=dz(u);if(h.eachAfter(a),h.parent.m=-h.z,h.eachBefore(s),n)u.eachBefore(l);else{var d=u,f=u,p=u;u.eachBefore(function(x){x.xf.x&&(f=x),x.depth>p.depth&&(p=x)});var m=d===f?1:t(d,f)/2,_=m-d.x,y=e/(f.x+m+_),b=r/(p.depth||1);u.eachBefore(function(x){x.x=(x.x+_)*y,x.y=x.depth*b})}return u}function a(u){var h=u.children,d=u.parent.children,f=u.i?d[u.i-1]:null;if(h){hz(u);var p=(h[0].z+h[h.length-1].z)/2;f?(u.z=f.z+t(u._,f._),u.m=u.z-p):u.z=p}else f&&(u.z=f.z+t(u._,f._));u.parent.A=o(u,f,u.parent.A||d[0])}function s(u){u._.x=u.z+u.parent.m,u.m+=u.parent.m}function o(u,h,d){if(h){for(var f=u,p=u,m=h,_=f.parent.children[0],y=f.m,b=p.m,x=m.m,k=_.m,T;m=J2(m),f=Q2(f),m&&f;)_=Q2(_),p=J2(p),p.a=u,T=m.z+x-f.z-y+t(m._,f._),T>0&&(uz(fz(m,u,d),u,T),y+=T,b+=T),x+=m.m,y+=f.m,k+=_.m,b+=p.m;m&&!J2(p)&&(p.t=m,p.m+=x-b),f&&!Q2(_)&&(_.t=f,_.m+=y-k,d=u)}return d}function l(u){u.x*=e,u.y=u.depth*r}return i.separation=function(u){return arguments.length?(t=u,i):t},i.size=function(u){return arguments.length?(n=!1,e=+u[0],r=+u[1],i):n?null:[e,r]},i.nodeSize=function(u){return arguments.length?(n=!0,e=+u[0],r=+u[1],i):n?[e,r]:null},i}function rf(t,e,r,n,i){for(var a=t.children,s,o=-1,l=a.length,u=t.value&&(i-r)/t.value;++ox&&(x=u),M=y*y*C,k=Math.max(x/M,M/b),k>T){y-=u;break}T=k}s.push(l={value:y,dice:p1?n:1)},r}(Rx);function gz(){var t=Nx,e=!1,r=1,n=1,i=[0],a=Ls,s=Ls,o=Ls,l=Ls,u=Ls;function h(f){return f.x0=f.y0=0,f.x1=r,f.y1=n,f.eachBefore(d),i=[0],e&&f.eachBefore(Ax),f}function d(f){var p=i[f.depth],m=f.x0+p,_=f.y0+p,y=f.x1-p,b=f.y1-p;y=f-1){var x=a[d];x.x0=m,x.y0=_,x.x1=y,x.y1=b;return}for(var k=u[d],T=p/2+k,C=d+1,M=f-1;C>>1;u[S]b-_){var L=p?(m*A+y*R)/p:y;h(d,C,R,m,_,L,b),h(C,f,A,L,_,y,b)}else{var v=p?(_*A+b*R)/p:b;h(d,C,R,m,_,y,v),h(C,f,A,m,v,y,b)}}}function mz(t,e,r,n,i){(t.depth&1?rf:hc)(t,e,r,n,i)}const bz=function t(e){function r(n,i,a,s,o){if((l=n._squarify)&&l.ratio===e)for(var l,u,h,d,f=-1,p,m=l.length,_=n.value;++f1?n:1)},r}(Rx);function _z(t){for(var e=-1,r=t.length,n,i=t[r-1],a=0;++e1&&xz(t[r[n-2]],t[r[n-1]],t[i])<=0;)--n;r[n++]=i}return r.slice(0,n)}function wz(t){if((r=t.length)<3)return null;var e,r,n=new Array(r),i=new Array(r);for(e=0;e=0;--e)u.push(t[n[a[e]][2]]);for(e=+o;ea!=o>a&&i<(s-l)*(a-u)/(o-u)+l&&(h=!h),s=l,o=u;return h}function Ez(t){for(var e=-1,r=t.length,n=t[r-1],i,a,s=n[0],o=n[1],l=0;++e1);return n+i*o*Math.sqrt(-2*Math.log(s)/s)}}return r.source=t,r}(Ir),Az=function t(e){var r=tp.source(e);function n(){var i=r.apply(this,arguments);return function(){return Math.exp(i())}}return n.source=t,n}(Ir),Dx=function t(e){function r(n){return(n=+n)<=0?()=>0:function(){for(var i=0,a=n;a>1;--a)i+=e();return i+a*e()}}return r.source=t,r}(Ir),Mz=function t(e){var r=Dx.source(e);function n(i){if((i=+i)==0)return e;var a=r(i);return function(){return a()/i}}return n.source=t,n}(Ir),Lz=function t(e){function r(n){return function(){return-Math.log1p(-e())/n}}return r.source=t,r}(Ir),Rz=function t(e){function r(n){if((n=+n)<0)throw new RangeError("invalid alpha");return n=1/-n,function(){return Math.pow(1-e(),n)}}return r.source=t,r}(Ir),Iz=function t(e){function r(n){if((n=+n)<0||n>1)throw new RangeError("invalid p");return function(){return Math.floor(e()+n)}}return r.source=t,r}(Ir),Ox=function t(e){function r(n){if((n=+n)<0||n>1)throw new RangeError("invalid p");return n===0?()=>1/0:n===1?()=>1:(n=Math.log1p(-n),function(){return 1+Math.floor(Math.log1p(-e())/n)})}return r.source=t,r}(Ir),ep=function t(e){var r=tp.source(e)();function n(i,a){if((i=+i)<0)throw new RangeError("invalid k");if(i===0)return()=>0;if(a=a==null?1:+a,i===1)return()=>-Math.log1p(-e())*a;var s=(i<1?i+1:i)-1/3,o=1/(3*Math.sqrt(s)),l=i<1?()=>Math.pow(e(),1/i):()=>1;return function(){do{do var u=r(),h=1+o*u;while(h<=0);h*=h*h;var d=1-e()}while(d>=1-.0331*u*u*u*u&&Math.log(d)>=.5*u*u+s*(1-h+Math.log(h)));return s*h*l()*a}}return n.source=t,n}(Ir),Fx=function t(e){var r=ep.source(e);function n(i,a){var s=r(i),o=r(a);return function(){var l=s();return l===0?0:l/(l+o())}}return n.source=t,n}(Ir),Px=function t(e){var r=Ox.source(e),n=Fx.source(e);function i(a,s){return a=+a,(s=+s)>=1?()=>a:s<=0?()=>0:function(){for(var o=0,l=a,u=s;l*u>16&&l*(1-u)>16;){var h=Math.floor((l+1)*u),d=n(h,l-h+1)();d<=u?(o+=h,l-=h,u=(u-d)/(1-d)):(l=h-1,u/=d)}for(var f=u<.5,p=f?u:1-u,m=r(p),_=m(),y=0;_<=l;++y)_+=m();return o+(f?y:l-y)}}return i.source=t,i}(Ir),Nz=function t(e){function r(n,i,a){var s;return(n=+n)==0?s=o=>-Math.log(o):(n=1/n,s=o=>Math.pow(o,n)),i=i==null?0:+i,a=a==null?1:+a,function(){return i+a*s(-Math.log1p(-e()))}}return r.source=t,r}(Ir),Bz=function t(e){function r(n,i){return n=n==null?0:+n,i=i==null?1:+i,function(){return n+i*Math.tan(Math.PI*e())}}return r.source=t,r}(Ir),Dz=function t(e){function r(n,i){return n=n==null?0:+n,i=i==null?1:+i,function(){var a=e();return n+i*Math.log(a/(1-a))}}return r.source=t,r}(Ir),Oz=function t(e){var r=ep.source(e),n=Px.source(e);function i(a){return function(){for(var s=0,o=a;o>16;){var l=Math.floor(.875*o),u=r(l)();if(u>o)return s+n(l-1,o/u)();s+=l,o-=u}for(var h=-Math.log1p(-e()),d=0;h<=o;++d)h-=Math.log1p(-e());return s+d}}return i.source=t,i}(Ir),Fz=1664525,Pz=1013904223,qx=1/4294967296;function qz(t=Math.random()){let e=(0<=t&&t<1?t/qx:Math.abs(t))|0;return()=>(e=Fz*e+Pz|0,qx*(e>>>0))}function On(t,e){switch(arguments.length){case 0:break;case 1:this.range(t);break;default:this.range(e).domain(t);break}return this}function ta(t,e){switch(arguments.length){case 0:break;case 1:{typeof t=="function"?this.interpolator(t):this.range(t);break}default:{this.domain(t),typeof e=="function"?this.interpolator(e):this.range(e);break}}return this}const rp=Symbol("implicit");function nf(){var t=new kl,e=[],r=[],n=rp;function i(a){let s=t.get(a);if(s===void 0){if(n!==rp)return n;t.set(a,s=e.push(a)-1)}return r[s%r.length]}return i.domain=function(a){if(!arguments.length)return e.slice();e=[],t=new kl;for(const s of a)t.has(s)||t.set(s,e.push(s)-1);return i},i.range=function(a){return arguments.length?(r=Array.from(a),i):r.slice()},i.unknown=function(a){return arguments.length?(n=a,i):n},i.copy=function(){return nf(e,r).unknown(n)},On.apply(i,arguments),i}function np(){var t=nf().unknown(void 0),e=t.domain,r=t.range,n=0,i=1,a,s,o=!1,l=0,u=0,h=.5;delete t.unknown;function d(){var f=e().length,p=ie&&(r=t,t=e,e=r),function(n){return Math.max(t,Math.min(e,n))}}function Uz(t,e,r){var n=t[0],i=t[1],a=e[0],s=e[1];return i2?Wz:Uz,l=u=null,d}function d(f){return f==null||isNaN(f=+f)?a:(l||(l=o(t.map(n),e,r)))(n(s(f)))}return d.invert=function(f){return s(i((u||(u=o(e,t.map(n),Bn)))(f)))},d.domain=function(f){return arguments.length?(t=Array.from(f,af),h()):t.slice()},d.range=function(f){return arguments.length?(e=Array.from(f),h()):e.slice()},d.rangeRound=function(f){return e=Array.from(f),r=ju,h()},d.clamp=function(f){return arguments.length?(s=f?!0:an,h()):s!==an},d.interpolate=function(f){return arguments.length?(r=f,h()):r},d.unknown=function(f){return arguments.length?(a=f,d):a},function(f,p){return n=f,i=p,h()}}function ap(){return sf()(an,an)}function Yx(t,e,r,n){var i=wl(t,e,r),a;switch(n=Co(n==null?",f":n),n.type){case"s":{var s=Math.max(Math.abs(t),Math.abs(e));return n.precision==null&&!isNaN(a=Jv(i,s))&&(n.precision=a),Jd(n,s)}case"":case"e":case"g":case"p":case"r":{n.precision==null&&!isNaN(a=t6(i,Math.max(Math.abs(t),Math.abs(e))))&&(n.precision=a-(n.type==="e"));break}case"f":case"%":{n.precision==null&&!isNaN(a=Qv(i))&&(n.precision=a-(n.type==="%")*2);break}}return yh(n)}function Oa(t){var e=t.domain;return t.ticks=function(r){var n=e();return hs(n[0],n[n.length-1],r==null?10:r)},t.tickFormat=function(r,n){var i=e();return Yx(i[0],i[i.length-1],r==null?10:r,n)},t.nice=function(r){r==null&&(r=10);var n=e(),i=0,a=n.length-1,s=n[i],o=n[a],l,u,h=10;for(o0;){if(u=oo(s,o,r),u===l)return n[i]=s,n[a]=o,e(n);if(u>0)s=Math.floor(s/u)*u,o=Math.ceil(o/u)*u;else if(u<0)s=Math.ceil(s*u)/u,o=Math.floor(o*u)/u;else break;l=u}return t},t}function sp(){var t=ap();return t.copy=function(){return fc(t,sp())},On.apply(t,arguments),Oa(t)}function Ux(t){var e;function r(n){return n==null||isNaN(n=+n)?e:n}return r.invert=r,r.domain=r.range=function(n){return arguments.length?(t=Array.from(n,af),r):t.slice()},r.unknown=function(n){return arguments.length?(e=n,r):e},r.copy=function(){return Ux(t).unknown(e)},t=arguments.length?Array.from(t,af):[0,1],Oa(r)}function Wx(t,e){t=t.slice();var r=0,n=t.length-1,i=t[r],a=t[n],s;return aMath.pow(t,e)}function Xz(t){return t===Math.E?Math.log:t===10&&Math.log10||t===2&&Math.log2||(t=Math.log(t),e=>Math.log(e)/t)}function jx(t){return(e,r)=>-t(-e,r)}function op(t){const e=t(Hx,Gx),r=e.domain;let n=10,i,a;function s(){return i=Xz(n),a=$z(n),r()[0]<0?(i=jx(i),a=jx(a),t(Hz,Gz)):t(Hx,Gx),e}return e.base=function(o){return arguments.length?(n=+o,s()):n},e.domain=function(o){return arguments.length?(r(o),s()):r()},e.ticks=o=>{const l=r();let u=l[0],h=l[l.length-1];const d=h0){for(;f<=p;++f)for(m=1;mh)break;b.push(_)}}else for(;f<=p;++f)for(m=n-1;m>=1;--m)if(_=f>0?m/a(-f):m*a(f),!(_h)break;b.push(_)}b.length*2{if(o==null&&(o=10),l==null&&(l=n===10?"s":","),typeof l!="function"&&(!(n%1)&&(l=Co(l)).precision==null&&(l.trim=!0),l=yh(l)),o===1/0)return l;const u=Math.max(1,n*o/e.ticks().length);return h=>{let d=h/a(Math.round(i(h)));return d*nr(Wx(r(),{floor:o=>a(Math.floor(i(o))),ceil:o=>a(Math.ceil(i(o)))})),e}function $x(){const t=op(sf()).domain([1,10]);return t.copy=()=>fc(t,$x()).base(t.base()),On.apply(t,arguments),t}function Xx(t){return function(e){return Math.sign(e)*Math.log1p(Math.abs(e/t))}}function Kx(t){return function(e){return Math.sign(e)*Math.expm1(Math.abs(e))*t}}function lp(t){var e=1,r=t(Xx(e),Kx(e));return r.constant=function(n){return arguments.length?t(Xx(e=+n),Kx(e)):e},Oa(r)}function Zx(){var t=lp(sf());return t.copy=function(){return fc(t,Zx()).constant(t.constant())},On.apply(t,arguments)}function Qx(t){return function(e){return e<0?-Math.pow(-e,t):Math.pow(e,t)}}function Kz(t){return t<0?-Math.sqrt(-t):Math.sqrt(t)}function Zz(t){return t<0?-t*t:t*t}function cp(t){var e=t(an,an),r=1;function n(){return r===1?t(an,an):r===.5?t(Kz,Zz):t(Qx(r),Qx(1/r))}return e.exponent=function(i){return arguments.length?(r=+i,n()):r},Oa(e)}function up(){var t=cp(sf());return t.copy=function(){return fc(t,up()).exponent(t.exponent())},On.apply(t,arguments),t}function Qz(){return up.apply(null,arguments).exponent(.5)}function Jx(t){return Math.sign(t)*t*t}function Jz(t){return Math.sign(t)*Math.sqrt(Math.abs(t))}function t8(){var t=ap(),e=[0,1],r=!1,n;function i(a){var s=Jz(t(a));return isNaN(s)?n:r?Math.round(s):s}return i.invert=function(a){return t.invert(Jx(a))},i.domain=function(a){return arguments.length?(t.domain(a),i):t.domain()},i.range=function(a){return arguments.length?(t.range((e=Array.from(a,af)).map(Jx)),i):e.slice()},i.rangeRound=function(a){return i.range(a).round(!0)},i.round=function(a){return arguments.length?(r=!!a,i):r},i.clamp=function(a){return arguments.length?(t.clamp(a),i):t.clamp()},i.unknown=function(a){return arguments.length?(n=a,i):n},i.copy=function(){return t8(t.domain(),e).round(r).clamp(t.clamp()).unknown(n)},On.apply(i,arguments),Oa(i)}function e8(){var t=[],e=[],r=[],n;function i(){var s=0,o=Math.max(1,e.length);for(r=new Array(o-1);++s0?r[o-1]:t[0],o=r?[n[r-1],e]:[n[u-1],n[u]]},s.unknown=function(l){return arguments.length&&(a=l),s},s.thresholds=function(){return n.slice()},s.copy=function(){return r8().domain([t,e]).range(i).unknown(a)},On.apply(Oa(s),arguments)}function n8(){var t=[.5],e=[0,1],r,n=1;function i(a){return a!=null&&a<=a?e[cs(t,a,0,n)]:r}return i.domain=function(a){return arguments.length?(t=Array.from(a),n=Math.min(t.length,e.length-1),i):t.slice()},i.range=function(a){return arguments.length?(e=Array.from(a),n=Math.min(t.length,e.length-1),i):e.slice()},i.invertExtent=function(a){var s=e.indexOf(a);return[t[s-1],t[s]]},i.unknown=function(a){return arguments.length?(r=a,i):r},i.copy=function(){return n8().domain(t).range(e).unknown(r)},On.apply(i,arguments)}var hp=new Date,fp=new Date;function xr(t,e,r,n){function i(a){return t(a=arguments.length===0?new Date:new Date(+a)),a}return i.floor=function(a){return t(a=new Date(+a)),a},i.ceil=function(a){return t(a=new Date(a-1)),e(a,1),t(a),a},i.round=function(a){var s=i(a),o=i.ceil(a);return a-s0))return l;do l.push(u=new Date(+a)),e(a,o),t(a);while(u=s)for(;t(s),!a(s);)s.setTime(s-1)},function(s,o){if(s>=s)if(o<0)for(;++o<=0;)for(;e(s,-1),!a(s););else for(;--o>=0;)for(;e(s,1),!a(s););})},r&&(i.count=function(a,s){return hp.setTime(+a),fp.setTime(+s),t(hp),t(fp),Math.floor(r(hp,fp))},i.every=function(a){return a=Math.floor(a),!isFinite(a)||!(a>0)?null:a>1?i.filter(n?function(s){return n(s)%a===0}:function(s){return i.count(0,s)%a===0}):i}),i}var of=xr(function(){},function(t,e){t.setTime(+t+e)},function(t,e){return e-t});of.every=function(t){return t=Math.floor(t),!isFinite(t)||!(t>0)?null:t>1?xr(function(e){e.setTime(Math.floor(e/t)*t)},function(e,r){e.setTime(+e+r*t)},function(e,r){return(r-e)/t}):of};const dp=of;var i8=of.range;const ea=1e3,Fn=ea*60,ra=Fn*60,Rs=ra*24,pp=Rs*7,a8=Rs*30,gp=Rs*365;var s8=xr(function(t){t.setTime(t-t.getMilliseconds())},function(t,e){t.setTime(+t+e*ea)},function(t,e){return(e-t)/ea},function(t){return t.getUTCSeconds()});const Fa=s8;var o8=s8.range,l8=xr(function(t){t.setTime(t-t.getMilliseconds()-t.getSeconds()*ea)},function(t,e){t.setTime(+t+e*Fn)},function(t,e){return(e-t)/Fn},function(t){return t.getMinutes()});const yp=l8;var tY=l8.range,c8=xr(function(t){t.setTime(t-t.getMilliseconds()-t.getSeconds()*ea-t.getMinutes()*Fn)},function(t,e){t.setTime(+t+e*ra)},function(t,e){return(e-t)/ra},function(t){return t.getHours()});const mp=c8;var eY=c8.range,u8=xr(t=>t.setHours(0,0,0,0),(t,e)=>t.setDate(t.getDate()+e),(t,e)=>(e-t-(e.getTimezoneOffset()-t.getTimezoneOffset())*Fn)/Rs,t=>t.getDate()-1);const dc=u8;var rY=u8.range;function Is(t){return xr(function(e){e.setDate(e.getDate()-(e.getDay()+7-t)%7),e.setHours(0,0,0,0)},function(e,r){e.setDate(e.getDate()+r*7)},function(e,r){return(r-e-(r.getTimezoneOffset()-e.getTimezoneOffset())*Fn)/pp})}var Do=Is(0),pc=Is(1),h8=Is(2),f8=Is(3),Ns=Is(4),d8=Is(5),p8=Is(6),g8=Do.range,nY=pc.range,iY=h8.range,aY=f8.range,sY=Ns.range,oY=d8.range,lY=p8.range,y8=xr(function(t){t.setDate(1),t.setHours(0,0,0,0)},function(t,e){t.setMonth(t.getMonth()+e)},function(t,e){return e.getMonth()-t.getMonth()+(e.getFullYear()-t.getFullYear())*12},function(t){return t.getMonth()});const bp=y8;var cY=y8.range,_p=xr(function(t){t.setMonth(0,1),t.setHours(0,0,0,0)},function(t,e){t.setFullYear(t.getFullYear()+e)},function(t,e){return e.getFullYear()-t.getFullYear()},function(t){return t.getFullYear()});_p.every=function(t){return!isFinite(t=Math.floor(t))||!(t>0)?null:xr(function(e){e.setFullYear(Math.floor(e.getFullYear()/t)*t),e.setMonth(0,1),e.setHours(0,0,0,0)},function(e,r){e.setFullYear(e.getFullYear()+r*t)})};const Pa=_p;var uY=_p.range,m8=xr(function(t){t.setUTCSeconds(0,0)},function(t,e){t.setTime(+t+e*Fn)},function(t,e){return(e-t)/Fn},function(t){return t.getUTCMinutes()});const vp=m8;var hY=m8.range,b8=xr(function(t){t.setUTCMinutes(0,0,0)},function(t,e){t.setTime(+t+e*ra)},function(t,e){return(e-t)/ra},function(t){return t.getUTCHours()});const xp=b8;var fY=b8.range,_8=xr(function(t){t.setUTCHours(0,0,0,0)},function(t,e){t.setUTCDate(t.getUTCDate()+e)},function(t,e){return(e-t)/Rs},function(t){return t.getUTCDate()-1});const gc=_8;var dY=_8.range;function Bs(t){return xr(function(e){e.setUTCDate(e.getUTCDate()-(e.getUTCDay()+7-t)%7),e.setUTCHours(0,0,0,0)},function(e,r){e.setUTCDate(e.getUTCDate()+r*7)},function(e,r){return(r-e)/pp})}var Oo=Bs(0),yc=Bs(1),v8=Bs(2),x8=Bs(3),Ds=Bs(4),k8=Bs(5),w8=Bs(6),T8=Oo.range,pY=yc.range,gY=v8.range,yY=x8.range,mY=Ds.range,bY=k8.range,_Y=w8.range,E8=xr(function(t){t.setUTCDate(1),t.setUTCHours(0,0,0,0)},function(t,e){t.setUTCMonth(t.getUTCMonth()+e)},function(t,e){return e.getUTCMonth()-t.getUTCMonth()+(e.getUTCFullYear()-t.getUTCFullYear())*12},function(t){return t.getUTCMonth()});const kp=E8;var vY=E8.range,wp=xr(function(t){t.setUTCMonth(0,1),t.setUTCHours(0,0,0,0)},function(t,e){t.setUTCFullYear(t.getUTCFullYear()+e)},function(t,e){return e.getUTCFullYear()-t.getUTCFullYear()},function(t){return t.getUTCFullYear()});wp.every=function(t){return!isFinite(t=Math.floor(t))||!(t>0)?null:xr(function(e){e.setUTCFullYear(Math.floor(e.getUTCFullYear()/t)*t),e.setUTCMonth(0,1),e.setUTCHours(0,0,0,0)},function(e,r){e.setUTCFullYear(e.getUTCFullYear()+r*t)})};const qa=wp;var xY=wp.range;function C8(t,e,r,n,i,a){const s=[[Fa,1,ea],[Fa,5,5*ea],[Fa,15,15*ea],[Fa,30,30*ea],[a,1,Fn],[a,5,5*Fn],[a,15,15*Fn],[a,30,30*Fn],[i,1,ra],[i,3,3*ra],[i,6,6*ra],[i,12,12*ra],[n,1,Rs],[n,2,2*Rs],[r,1,pp],[e,1,a8],[e,3,3*a8],[t,1,gp]];function o(u,h,d){const f=hy).right(s,f);if(p===s.length)return t.every(wl(u/gp,h/gp,d));if(p===0)return dp.every(Math.max(wl(u,h,d),1));const[m,_]=s[f/s[p-1][2]53)return null;"w"in U||(U.w=1),"Z"in U?(j=Ep(mc(U.y,0,1)),P=j.getUTCDay(),j=P>4||P===0?yc.ceil(j):yc(j),j=gc.offset(j,(U.V-1)*7),U.y=j.getUTCFullYear(),U.m=j.getUTCMonth(),U.d=j.getUTCDate()+(U.w+6)%7):(j=Tp(mc(U.y,0,1)),P=j.getDay(),j=P>4||P===0?pc.ceil(j):pc(j),j=dc.offset(j,(U.V-1)*7),U.y=j.getFullYear(),U.m=j.getMonth(),U.d=j.getDate()+(U.w+6)%7)}else("W"in U||"U"in U)&&("w"in U||(U.w="u"in U?U.u%7:"W"in U?1:0),P="Z"in U?Ep(mc(U.y,0,1)).getUTCDay():Tp(mc(U.y,0,1)).getDay(),U.m=0,U.d="W"in U?(U.w+6)%7+U.W*7-(P+5)%7:U.w+U.U*7-(P+6)%7);return"Z"in U?(U.H+=U.Z/100|0,U.M+=U.Z%100,Ep(U)):Tp(U)}}function R(V,Q,q,U){for(var F=0,j=Q.length,P=q.length,et,at;F=P)return-1;if(et=Q.charCodeAt(F++),et===37){if(et=Q.charAt(F++),at=C[et in I8?Q.charAt(F++):et],!at||(U=at(V,q,U))<0)return-1}else if(et!=q.charCodeAt(U++))return-1}return U}function A(V,Q,q){var U=u.exec(Q.slice(q));return U?(V.p=h.get(U[0].toLowerCase()),q+U[0].length):-1}function L(V,Q,q){var U=p.exec(Q.slice(q));return U?(V.w=m.get(U[0].toLowerCase()),q+U[0].length):-1}function v(V,Q,q){var U=d.exec(Q.slice(q));return U?(V.w=f.get(U[0].toLowerCase()),q+U[0].length):-1}function B(V,Q,q){var U=b.exec(Q.slice(q));return U?(V.m=x.get(U[0].toLowerCase()),q+U[0].length):-1}function w(V,Q,q){var U=_.exec(Q.slice(q));return U?(V.m=y.get(U[0].toLowerCase()),q+U[0].length):-1}function D(V,Q,q){return R(V,e,Q,q)}function N(V,Q,q){return R(V,r,Q,q)}function z(V,Q,q){return R(V,n,Q,q)}function X(V){return s[V.getDay()]}function ct(V){return a[V.getDay()]}function J(V){return l[V.getMonth()]}function Y(V){return o[V.getMonth()]}function $(V){return i[+(V.getHours()>=12)]}function lt(V){return 1+~~(V.getMonth()/3)}function ut(V){return s[V.getUTCDay()]}function W(V){return a[V.getUTCDay()]}function tt(V){return l[V.getUTCMonth()]}function K(V){return o[V.getUTCMonth()]}function it(V){return i[+(V.getUTCHours()>=12)]}function Z(V){return 1+~~(V.getUTCMonth()/3)}return{format:function(V){var Q=M(V+="",k);return Q.toString=function(){return V},Q},parse:function(V){var Q=S(V+="",!1);return Q.toString=function(){return V},Q},utcFormat:function(V){var Q=M(V+="",T);return Q.toString=function(){return V},Q},utcParse:function(V){var Q=S(V+="",!0);return Q.toString=function(){return V},Q}}}var I8={"-":"",_:" ",0:"0"},Ar=/^\s*\d+/,kY=/^%/,wY=/[\\^$*+?|[\]().{}]/g;function Oe(t,e,r){var n=t<0?"-":"",i=(n?-t:t)+"",a=i.length;return n+(a[e.toLowerCase(),r]))}function EY(t,e,r){var n=Ar.exec(e.slice(r,r+1));return n?(t.w=+n[0],r+n[0].length):-1}function CY(t,e,r){var n=Ar.exec(e.slice(r,r+1));return n?(t.u=+n[0],r+n[0].length):-1}function SY(t,e,r){var n=Ar.exec(e.slice(r,r+2));return n?(t.U=+n[0],r+n[0].length):-1}function AY(t,e,r){var n=Ar.exec(e.slice(r,r+2));return n?(t.V=+n[0],r+n[0].length):-1}function MY(t,e,r){var n=Ar.exec(e.slice(r,r+2));return n?(t.W=+n[0],r+n[0].length):-1}function N8(t,e,r){var n=Ar.exec(e.slice(r,r+4));return n?(t.y=+n[0],r+n[0].length):-1}function B8(t,e,r){var n=Ar.exec(e.slice(r,r+2));return n?(t.y=+n[0]+(+n[0]>68?1900:2e3),r+n[0].length):-1}function LY(t,e,r){var n=/^(Z)|([+-]\d\d)(?::?(\d\d))?/.exec(e.slice(r,r+6));return n?(t.Z=n[1]?0:-(n[2]+(n[3]||"00")),r+n[0].length):-1}function RY(t,e,r){var n=Ar.exec(e.slice(r,r+1));return n?(t.q=n[0]*3-3,r+n[0].length):-1}function IY(t,e,r){var n=Ar.exec(e.slice(r,r+2));return n?(t.m=n[0]-1,r+n[0].length):-1}function D8(t,e,r){var n=Ar.exec(e.slice(r,r+2));return n?(t.d=+n[0],r+n[0].length):-1}function NY(t,e,r){var n=Ar.exec(e.slice(r,r+3));return n?(t.m=0,t.d=+n[0],r+n[0].length):-1}function O8(t,e,r){var n=Ar.exec(e.slice(r,r+2));return n?(t.H=+n[0],r+n[0].length):-1}function BY(t,e,r){var n=Ar.exec(e.slice(r,r+2));return n?(t.M=+n[0],r+n[0].length):-1}function DY(t,e,r){var n=Ar.exec(e.slice(r,r+2));return n?(t.S=+n[0],r+n[0].length):-1}function OY(t,e,r){var n=Ar.exec(e.slice(r,r+3));return n?(t.L=+n[0],r+n[0].length):-1}function FY(t,e,r){var n=Ar.exec(e.slice(r,r+6));return n?(t.L=Math.floor(n[0]/1e3),r+n[0].length):-1}function PY(t,e,r){var n=kY.exec(e.slice(r,r+1));return n?r+n[0].length:-1}function qY(t,e,r){var n=Ar.exec(e.slice(r));return n?(t.Q=+n[0],r+n[0].length):-1}function VY(t,e,r){var n=Ar.exec(e.slice(r));return n?(t.s=+n[0],r+n[0].length):-1}function F8(t,e){return Oe(t.getDate(),e,2)}function zY(t,e){return Oe(t.getHours(),e,2)}function YY(t,e){return Oe(t.getHours()%12||12,e,2)}function UY(t,e){return Oe(1+dc.count(Pa(t),t),e,3)}function P8(t,e){return Oe(t.getMilliseconds(),e,3)}function WY(t,e){return P8(t,e)+"000"}function HY(t,e){return Oe(t.getMonth()+1,e,2)}function GY(t,e){return Oe(t.getMinutes(),e,2)}function jY(t,e){return Oe(t.getSeconds(),e,2)}function $Y(t){var e=t.getDay();return e===0?7:e}function XY(t,e){return Oe(Do.count(Pa(t)-1,t),e,2)}function q8(t){var e=t.getDay();return e>=4||e===0?Ns(t):Ns.ceil(t)}function KY(t,e){return t=q8(t),Oe(Ns.count(Pa(t),t)+(Pa(t).getDay()===4),e,2)}function ZY(t){return t.getDay()}function QY(t,e){return Oe(pc.count(Pa(t)-1,t),e,2)}function JY(t,e){return Oe(t.getFullYear()%100,e,2)}function tU(t,e){return t=q8(t),Oe(t.getFullYear()%100,e,2)}function eU(t,e){return Oe(t.getFullYear()%1e4,e,4)}function rU(t,e){var r=t.getDay();return t=r>=4||r===0?Ns(t):Ns.ceil(t),Oe(t.getFullYear()%1e4,e,4)}function nU(t){var e=t.getTimezoneOffset();return(e>0?"-":(e*=-1,"+"))+Oe(e/60|0,"0",2)+Oe(e%60,"0",2)}function V8(t,e){return Oe(t.getUTCDate(),e,2)}function iU(t,e){return Oe(t.getUTCHours(),e,2)}function aU(t,e){return Oe(t.getUTCHours()%12||12,e,2)}function sU(t,e){return Oe(1+gc.count(qa(t),t),e,3)}function z8(t,e){return Oe(t.getUTCMilliseconds(),e,3)}function oU(t,e){return z8(t,e)+"000"}function lU(t,e){return Oe(t.getUTCMonth()+1,e,2)}function cU(t,e){return Oe(t.getUTCMinutes(),e,2)}function uU(t,e){return Oe(t.getUTCSeconds(),e,2)}function hU(t){var e=t.getUTCDay();return e===0?7:e}function fU(t,e){return Oe(Oo.count(qa(t)-1,t),e,2)}function Y8(t){var e=t.getUTCDay();return e>=4||e===0?Ds(t):Ds.ceil(t)}function dU(t,e){return t=Y8(t),Oe(Ds.count(qa(t),t)+(qa(t).getUTCDay()===4),e,2)}function pU(t){return t.getUTCDay()}function gU(t,e){return Oe(yc.count(qa(t)-1,t),e,2)}function yU(t,e){return Oe(t.getUTCFullYear()%100,e,2)}function mU(t,e){return t=Y8(t),Oe(t.getUTCFullYear()%100,e,2)}function bU(t,e){return Oe(t.getUTCFullYear()%1e4,e,4)}function _U(t,e){var r=t.getUTCDay();return t=r>=4||r===0?Ds(t):Ds.ceil(t),Oe(t.getUTCFullYear()%1e4,e,4)}function vU(){return"+0000"}function U8(){return"%"}function W8(t){return+t}function H8(t){return Math.floor(+t/1e3)}var Fo,vc,G8,lf,Cp;j8({dateTime:"%x, %X",date:"%-m/%-d/%Y",time:"%-I:%M:%S %p",periods:["AM","PM"],days:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],shortDays:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],months:["January","February","March","April","May","June","July","August","September","October","November","December"],shortMonths:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"]});function j8(t){return Fo=R8(t),vc=Fo.format,G8=Fo.parse,lf=Fo.utcFormat,Cp=Fo.utcParse,Fo}var $8="%Y-%m-%dT%H:%M:%S.%LZ";function xU(t){return t.toISOString()}var kU=Date.prototype.toISOString?xU:lf($8);const wU=kU;function TU(t){var e=new Date(t);return isNaN(e)?null:e}var EU=+new Date("2000-01-01T00:00:00.000Z")?TU:Cp($8);const CU=EU;function SU(t){return new Date(t)}function AU(t){return t instanceof Date?+t:+new Date(+t)}function Sp(t,e,r,n,i,a,s,o,l,u){var h=ap(),d=h.invert,f=h.domain,p=u(".%L"),m=u(":%S"),_=u("%I:%M"),y=u("%I %p"),b=u("%a %d"),x=u("%b %d"),k=u("%B"),T=u("%Y");function C(M){return(l(M)e(i/(t.length-1)))},r.quantiles=function(n){return Array.from({length:n+1},(i,a)=>Cl(t,a/n))},r.copy=function(){return J8(e).domain(t)},ta.apply(r,arguments)}function uf(){var t=0,e=.5,r=1,n=1,i,a,s,o,l,u=an,h,d=!1,f;function p(_){return isNaN(_=+_)?f:(_=.5+((_=+h(_))-a)*(n*_B5(t[t.length-1]);var n7=new Array(3).concat("d8b365f5f5f55ab4ac","a6611adfc27d80cdc1018571","a6611adfc27df5f5f580cdc1018571","8c510ad8b365f6e8c3c7eae55ab4ac01665e","8c510ad8b365f6e8c3f5f5f5c7eae55ab4ac01665e","8c510abf812ddfc27df6e8c3c7eae580cdc135978f01665e","8c510abf812ddfc27df6e8c3f5f5f5c7eae580cdc135978f01665e","5430058c510abf812ddfc27df6e8c3c7eae580cdc135978f01665e003c30","5430058c510abf812ddfc27df6e8c3f5f5f5c7eae580cdc135978f01665e003c30").map(Ee);const YU=We(n7);var i7=new Array(3).concat("af8dc3f7f7f77fbf7b","7b3294c2a5cfa6dba0008837","7b3294c2a5cff7f7f7a6dba0008837","762a83af8dc3e7d4e8d9f0d37fbf7b1b7837","762a83af8dc3e7d4e8f7f7f7d9f0d37fbf7b1b7837","762a839970abc2a5cfe7d4e8d9f0d3a6dba05aae611b7837","762a839970abc2a5cfe7d4e8f7f7f7d9f0d3a6dba05aae611b7837","40004b762a839970abc2a5cfe7d4e8d9f0d3a6dba05aae611b783700441b","40004b762a839970abc2a5cfe7d4e8f7f7f7d9f0d3a6dba05aae611b783700441b").map(Ee);const UU=We(i7);var a7=new Array(3).concat("e9a3c9f7f7f7a1d76a","d01c8bf1b6dab8e1864dac26","d01c8bf1b6daf7f7f7b8e1864dac26","c51b7de9a3c9fde0efe6f5d0a1d76a4d9221","c51b7de9a3c9fde0eff7f7f7e6f5d0a1d76a4d9221","c51b7dde77aef1b6dafde0efe6f5d0b8e1867fbc414d9221","c51b7dde77aef1b6dafde0eff7f7f7e6f5d0b8e1867fbc414d9221","8e0152c51b7dde77aef1b6dafde0efe6f5d0b8e1867fbc414d9221276419","8e0152c51b7dde77aef1b6dafde0eff7f7f7e6f5d0b8e1867fbc414d9221276419").map(Ee);const WU=We(a7);var s7=new Array(3).concat("998ec3f7f7f7f1a340","5e3c99b2abd2fdb863e66101","5e3c99b2abd2f7f7f7fdb863e66101","542788998ec3d8daebfee0b6f1a340b35806","542788998ec3d8daebf7f7f7fee0b6f1a340b35806","5427888073acb2abd2d8daebfee0b6fdb863e08214b35806","5427888073acb2abd2d8daebf7f7f7fee0b6fdb863e08214b35806","2d004b5427888073acb2abd2d8daebfee0b6fdb863e08214b358067f3b08","2d004b5427888073acb2abd2d8daebf7f7f7fee0b6fdb863e08214b358067f3b08").map(Ee);const HU=We(s7);var o7=new Array(3).concat("ef8a62f7f7f767a9cf","ca0020f4a58292c5de0571b0","ca0020f4a582f7f7f792c5de0571b0","b2182bef8a62fddbc7d1e5f067a9cf2166ac","b2182bef8a62fddbc7f7f7f7d1e5f067a9cf2166ac","b2182bd6604df4a582fddbc7d1e5f092c5de4393c32166ac","b2182bd6604df4a582fddbc7f7f7f7d1e5f092c5de4393c32166ac","67001fb2182bd6604df4a582fddbc7d1e5f092c5de4393c32166ac053061","67001fb2182bd6604df4a582fddbc7f7f7f7d1e5f092c5de4393c32166ac053061").map(Ee);const GU=We(o7);var l7=new Array(3).concat("ef8a62ffffff999999","ca0020f4a582bababa404040","ca0020f4a582ffffffbababa404040","b2182bef8a62fddbc7e0e0e09999994d4d4d","b2182bef8a62fddbc7ffffffe0e0e09999994d4d4d","b2182bd6604df4a582fddbc7e0e0e0bababa8787874d4d4d","b2182bd6604df4a582fddbc7ffffffe0e0e0bababa8787874d4d4d","67001fb2182bd6604df4a582fddbc7e0e0e0bababa8787874d4d4d1a1a1a","67001fb2182bd6604df4a582fddbc7ffffffe0e0e0bababa8787874d4d4d1a1a1a").map(Ee);const jU=We(l7);var c7=new Array(3).concat("fc8d59ffffbf91bfdb","d7191cfdae61abd9e92c7bb6","d7191cfdae61ffffbfabd9e92c7bb6","d73027fc8d59fee090e0f3f891bfdb4575b4","d73027fc8d59fee090ffffbfe0f3f891bfdb4575b4","d73027f46d43fdae61fee090e0f3f8abd9e974add14575b4","d73027f46d43fdae61fee090ffffbfe0f3f8abd9e974add14575b4","a50026d73027f46d43fdae61fee090e0f3f8abd9e974add14575b4313695","a50026d73027f46d43fdae61fee090ffffbfe0f3f8abd9e974add14575b4313695").map(Ee);const $U=We(c7);var u7=new Array(3).concat("fc8d59ffffbf91cf60","d7191cfdae61a6d96a1a9641","d7191cfdae61ffffbfa6d96a1a9641","d73027fc8d59fee08bd9ef8b91cf601a9850","d73027fc8d59fee08bffffbfd9ef8b91cf601a9850","d73027f46d43fdae61fee08bd9ef8ba6d96a66bd631a9850","d73027f46d43fdae61fee08bffffbfd9ef8ba6d96a66bd631a9850","a50026d73027f46d43fdae61fee08bd9ef8ba6d96a66bd631a9850006837","a50026d73027f46d43fdae61fee08bffffbfd9ef8ba6d96a66bd631a9850006837").map(Ee);const XU=We(u7);var h7=new Array(3).concat("fc8d59ffffbf99d594","d7191cfdae61abdda42b83ba","d7191cfdae61ffffbfabdda42b83ba","d53e4ffc8d59fee08be6f59899d5943288bd","d53e4ffc8d59fee08bffffbfe6f59899d5943288bd","d53e4ff46d43fdae61fee08be6f598abdda466c2a53288bd","d53e4ff46d43fdae61fee08bffffbfe6f598abdda466c2a53288bd","9e0142d53e4ff46d43fdae61fee08be6f598abdda466c2a53288bd5e4fa2","9e0142d53e4ff46d43fdae61fee08bffffbfe6f598abdda466c2a53288bd5e4fa2").map(Ee);const KU=We(h7);var f7=new Array(3).concat("e5f5f999d8c92ca25f","edf8fbb2e2e266c2a4238b45","edf8fbb2e2e266c2a42ca25f006d2c","edf8fbccece699d8c966c2a42ca25f006d2c","edf8fbccece699d8c966c2a441ae76238b45005824","f7fcfde5f5f9ccece699d8c966c2a441ae76238b45005824","f7fcfde5f5f9ccece699d8c966c2a441ae76238b45006d2c00441b").map(Ee);const ZU=We(f7);var d7=new Array(3).concat("e0ecf49ebcda8856a7","edf8fbb3cde38c96c688419d","edf8fbb3cde38c96c68856a7810f7c","edf8fbbfd3e69ebcda8c96c68856a7810f7c","edf8fbbfd3e69ebcda8c96c68c6bb188419d6e016b","f7fcfde0ecf4bfd3e69ebcda8c96c68c6bb188419d6e016b","f7fcfde0ecf4bfd3e69ebcda8c96c68c6bb188419d810f7c4d004b").map(Ee);const QU=We(d7);var p7=new Array(3).concat("e0f3dba8ddb543a2ca","f0f9e8bae4bc7bccc42b8cbe","f0f9e8bae4bc7bccc443a2ca0868ac","f0f9e8ccebc5a8ddb57bccc443a2ca0868ac","f0f9e8ccebc5a8ddb57bccc44eb3d32b8cbe08589e","f7fcf0e0f3dbccebc5a8ddb57bccc44eb3d32b8cbe08589e","f7fcf0e0f3dbccebc5a8ddb57bccc44eb3d32b8cbe0868ac084081").map(Ee);const JU=We(p7);var g7=new Array(3).concat("fee8c8fdbb84e34a33","fef0d9fdcc8afc8d59d7301f","fef0d9fdcc8afc8d59e34a33b30000","fef0d9fdd49efdbb84fc8d59e34a33b30000","fef0d9fdd49efdbb84fc8d59ef6548d7301f990000","fff7ecfee8c8fdd49efdbb84fc8d59ef6548d7301f990000","fff7ecfee8c8fdd49efdbb84fc8d59ef6548d7301fb300007f0000").map(Ee);const tW=We(g7);var y7=new Array(3).concat("ece2f0a6bddb1c9099","f6eff7bdc9e167a9cf02818a","f6eff7bdc9e167a9cf1c9099016c59","f6eff7d0d1e6a6bddb67a9cf1c9099016c59","f6eff7d0d1e6a6bddb67a9cf3690c002818a016450","fff7fbece2f0d0d1e6a6bddb67a9cf3690c002818a016450","fff7fbece2f0d0d1e6a6bddb67a9cf3690c002818a016c59014636").map(Ee);const eW=We(y7);var m7=new Array(3).concat("ece7f2a6bddb2b8cbe","f1eef6bdc9e174a9cf0570b0","f1eef6bdc9e174a9cf2b8cbe045a8d","f1eef6d0d1e6a6bddb74a9cf2b8cbe045a8d","f1eef6d0d1e6a6bddb74a9cf3690c00570b0034e7b","fff7fbece7f2d0d1e6a6bddb74a9cf3690c00570b0034e7b","fff7fbece7f2d0d1e6a6bddb74a9cf3690c00570b0045a8d023858").map(Ee);const rW=We(m7);var b7=new Array(3).concat("e7e1efc994c7dd1c77","f1eef6d7b5d8df65b0ce1256","f1eef6d7b5d8df65b0dd1c77980043","f1eef6d4b9dac994c7df65b0dd1c77980043","f1eef6d4b9dac994c7df65b0e7298ace125691003f","f7f4f9e7e1efd4b9dac994c7df65b0e7298ace125691003f","f7f4f9e7e1efd4b9dac994c7df65b0e7298ace125698004367001f").map(Ee);const nW=We(b7);var _7=new Array(3).concat("fde0ddfa9fb5c51b8a","feebe2fbb4b9f768a1ae017e","feebe2fbb4b9f768a1c51b8a7a0177","feebe2fcc5c0fa9fb5f768a1c51b8a7a0177","feebe2fcc5c0fa9fb5f768a1dd3497ae017e7a0177","fff7f3fde0ddfcc5c0fa9fb5f768a1dd3497ae017e7a0177","fff7f3fde0ddfcc5c0fa9fb5f768a1dd3497ae017e7a017749006a").map(Ee);const iW=We(_7);var v7=new Array(3).concat("edf8b17fcdbb2c7fb8","ffffcca1dab441b6c4225ea8","ffffcca1dab441b6c42c7fb8253494","ffffccc7e9b47fcdbb41b6c42c7fb8253494","ffffccc7e9b47fcdbb41b6c41d91c0225ea80c2c84","ffffd9edf8b1c7e9b47fcdbb41b6c41d91c0225ea80c2c84","ffffd9edf8b1c7e9b47fcdbb41b6c41d91c0225ea8253494081d58").map(Ee);const aW=We(v7);var x7=new Array(3).concat("f7fcb9addd8e31a354","ffffccc2e69978c679238443","ffffccc2e69978c67931a354006837","ffffccd9f0a3addd8e78c67931a354006837","ffffccd9f0a3addd8e78c67941ab5d238443005a32","ffffe5f7fcb9d9f0a3addd8e78c67941ab5d238443005a32","ffffe5f7fcb9d9f0a3addd8e78c67941ab5d238443006837004529").map(Ee);const sW=We(x7);var k7=new Array(3).concat("fff7bcfec44fd95f0e","ffffd4fed98efe9929cc4c02","ffffd4fed98efe9929d95f0e993404","ffffd4fee391fec44ffe9929d95f0e993404","ffffd4fee391fec44ffe9929ec7014cc4c028c2d04","ffffe5fff7bcfee391fec44ffe9929ec7014cc4c028c2d04","ffffe5fff7bcfee391fec44ffe9929ec7014cc4c02993404662506").map(Ee);const oW=We(k7);var w7=new Array(3).concat("ffeda0feb24cf03b20","ffffb2fecc5cfd8d3ce31a1c","ffffb2fecc5cfd8d3cf03b20bd0026","ffffb2fed976feb24cfd8d3cf03b20bd0026","ffffb2fed976feb24cfd8d3cfc4e2ae31a1cb10026","ffffccffeda0fed976feb24cfd8d3cfc4e2ae31a1cb10026","ffffccffeda0fed976feb24cfd8d3cfc4e2ae31a1cbd0026800026").map(Ee);const lW=We(w7);var T7=new Array(3).concat("deebf79ecae13182bd","eff3ffbdd7e76baed62171b5","eff3ffbdd7e76baed63182bd08519c","eff3ffc6dbef9ecae16baed63182bd08519c","eff3ffc6dbef9ecae16baed64292c62171b5084594","f7fbffdeebf7c6dbef9ecae16baed64292c62171b5084594","f7fbffdeebf7c6dbef9ecae16baed64292c62171b508519c08306b").map(Ee);const cW=We(T7);var E7=new Array(3).concat("e5f5e0a1d99b31a354","edf8e9bae4b374c476238b45","edf8e9bae4b374c47631a354006d2c","edf8e9c7e9c0a1d99b74c47631a354006d2c","edf8e9c7e9c0a1d99b74c47641ab5d238b45005a32","f7fcf5e5f5e0c7e9c0a1d99b74c47641ab5d238b45005a32","f7fcf5e5f5e0c7e9c0a1d99b74c47641ab5d238b45006d2c00441b").map(Ee);const uW=We(E7);var C7=new Array(3).concat("f0f0f0bdbdbd636363","f7f7f7cccccc969696525252","f7f7f7cccccc969696636363252525","f7f7f7d9d9d9bdbdbd969696636363252525","f7f7f7d9d9d9bdbdbd969696737373525252252525","fffffff0f0f0d9d9d9bdbdbd969696737373525252252525","fffffff0f0f0d9d9d9bdbdbd969696737373525252252525000000").map(Ee);const hW=We(C7);var S7=new Array(3).concat("efedf5bcbddc756bb1","f2f0f7cbc9e29e9ac86a51a3","f2f0f7cbc9e29e9ac8756bb154278f","f2f0f7dadaebbcbddc9e9ac8756bb154278f","f2f0f7dadaebbcbddc9e9ac8807dba6a51a34a1486","fcfbfdefedf5dadaebbcbddc9e9ac8807dba6a51a34a1486","fcfbfdefedf5dadaebbcbddc9e9ac8807dba6a51a354278f3f007d").map(Ee);const fW=We(S7);var A7=new Array(3).concat("fee0d2fc9272de2d26","fee5d9fcae91fb6a4acb181d","fee5d9fcae91fb6a4ade2d26a50f15","fee5d9fcbba1fc9272fb6a4ade2d26a50f15","fee5d9fcbba1fc9272fb6a4aef3b2ccb181d99000d","fff5f0fee0d2fcbba1fc9272fb6a4aef3b2ccb181d99000d","fff5f0fee0d2fcbba1fc9272fb6a4aef3b2ccb181da50f1567000d").map(Ee);const dW=We(A7);var M7=new Array(3).concat("fee6cefdae6be6550d","feeddefdbe85fd8d3cd94701","feeddefdbe85fd8d3ce6550da63603","feeddefdd0a2fdae6bfd8d3ce6550da63603","feeddefdd0a2fdae6bfd8d3cf16913d948018c2d04","fff5ebfee6cefdd0a2fdae6bfd8d3cf16913d948018c2d04","fff5ebfee6cefdd0a2fdae6bfd8d3cf16913d94801a636037f2704").map(Ee);const pW=We(M7);function gW(t){return t=Math.max(0,Math.min(1,t)),"rgb("+Math.max(0,Math.min(255,Math.round(-4.54-t*(35.34-t*(2381.73-t*(6402.7-t*(7024.72-t*2710.57)))))))+", "+Math.max(0,Math.min(255,Math.round(32.49+t*(170.73+t*(52.82-t*(131.46-t*(176.58-t*67.37)))))))+", "+Math.max(0,Math.min(255,Math.round(81.24+t*(442.36-t*(2482.43-t*(6167.24-t*(6614.94-t*2475.67)))))))+")"}const yW=Xu(Qn(300,.5,0),Qn(-240,.5,1));var mW=Xu(Qn(-100,.75,.35),Qn(80,1.5,.8)),bW=Xu(Qn(260,.75,.35),Qn(80,1.5,.8)),hf=Qn();function _W(t){(t<0||t>1)&&(t-=Math.floor(t));var e=Math.abs(t-.5);return hf.h=360*t-100,hf.s=1.5-1.5*e,hf.l=.8-.9*e,hf+""}var ff=po(),vW=Math.PI/3,xW=Math.PI*2/3;function kW(t){var e;return t=(.5-t)*Math.PI,ff.r=255*(e=Math.sin(t))*e,ff.g=255*(e=Math.sin(t+vW))*e,ff.b=255*(e=Math.sin(t+xW))*e,ff+""}function wW(t){return t=Math.max(0,Math.min(1,t)),"rgb("+Math.max(0,Math.min(255,Math.round(34.61+t*(1172.33-t*(10793.56-t*(33300.12-t*(38394.49-t*14825.05)))))))+", "+Math.max(0,Math.min(255,Math.round(23.31+t*(557.33+t*(1225.33-t*(3574.96-t*(1073.77+t*707.56)))))))+", "+Math.max(0,Math.min(255,Math.round(27.2+t*(3211.1-t*(15327.97-t*(27814-t*(22569.18-t*6838.66)))))))+")"}function df(t){var e=t.length;return function(r){return t[Math.max(0,Math.min(e-1,Math.floor(r*e)))]}}const TW=df(Ee("44015444025645045745055946075a46085c460a5d460b5e470d60470e6147106347116447136548146748166848176948186a481a6c481b6d481c6e481d6f481f70482071482173482374482475482576482677482878482979472a7a472c7a472d7b472e7c472f7d46307e46327e46337f463480453581453781453882443983443a83443b84433d84433e85423f854240864241864142874144874045884046883f47883f48893e49893e4a893e4c8a3d4d8a3d4e8a3c4f8a3c508b3b518b3b528b3a538b3a548c39558c39568c38588c38598c375a8c375b8d365c8d365d8d355e8d355f8d34608d34618d33628d33638d32648e32658e31668e31678e31688e30698e306a8e2f6b8e2f6c8e2e6d8e2e6e8e2e6f8e2d708e2d718e2c718e2c728e2c738e2b748e2b758e2a768e2a778e2a788e29798e297a8e297b8e287c8e287d8e277e8e277f8e27808e26818e26828e26828e25838e25848e25858e24868e24878e23888e23898e238a8d228b8d228c8d228d8d218e8d218f8d21908d21918c20928c20928c20938c1f948c1f958b1f968b1f978b1f988b1f998a1f9a8a1e9b8a1e9c891e9d891f9e891f9f881fa0881fa1881fa1871fa28720a38620a48621a58521a68522a78522a88423a98324aa8325ab8225ac8226ad8127ad8128ae8029af7f2ab07f2cb17e2db27d2eb37c2fb47c31b57b32b67a34b67935b77937b87838b9773aba763bbb753dbc743fbc7340bd7242be7144bf7046c06f48c16e4ac16d4cc26c4ec36b50c46a52c56954c56856c66758c7655ac8645cc8635ec96260ca6063cb5f65cb5e67cc5c69cd5b6ccd5a6ece5870cf5773d05675d05477d1537ad1517cd2507fd34e81d34d84d44b86d54989d5488bd6468ed64590d74393d74195d84098d83e9bd93c9dd93ba0da39a2da37a5db36a8db34aadc32addc30b0dd2fb2dd2db5de2bb8de29bade28bddf26c0df25c2df23c5e021c8e020cae11fcde11dd0e11cd2e21bd5e21ad8e219dae319dde318dfe318e2e418e5e419e7e419eae51aece51befe51cf1e51df4e61ef6e620f8e621fbe723fde725"));var EW=df(Ee("00000401000501010601010802010902020b02020d03030f03031204041405041606051806051a07061c08071e0907200a08220b09240c09260d0a290e0b2b100b2d110c2f120d31130d34140e36150e38160f3b180f3d19103f1a10421c10441d11471e114920114b21114e22115024125325125527125829115a2a115c2c115f2d11612f116331116533106734106936106b38106c390f6e3b0f703d0f713f0f72400f74420f75440f764510774710784910784a10794c117a4e117b4f127b51127c52137c54137d56147d57157e59157e5a167e5c167f5d177f5f187f601880621980641a80651a80671b80681c816a1c816b1d816d1d816e1e81701f81721f817320817521817621817822817922827b23827c23827e24828025828125818326818426818627818827818928818b29818c29818e2a81902a81912b81932b80942c80962c80982d80992d809b2e7f9c2e7f9e2f7fa02f7fa1307ea3307ea5317ea6317da8327daa337dab337cad347cae347bb0357bb2357bb3367ab5367ab73779b83779ba3878bc3978bd3977bf3a77c03a76c23b75c43c75c53c74c73d73c83e73ca3e72cc3f71cd4071cf4070d0416fd2426fd3436ed5446dd6456cd8456cd9466bdb476adc4869de4968df4a68e04c67e24d66e34e65e44f64e55064e75263e85362e95462ea5661eb5760ec5860ed5a5fee5b5eef5d5ef05f5ef1605df2625df2645cf3655cf4675cf4695cf56b5cf66c5cf66e5cf7705cf7725cf8745cf8765cf9785df9795df97b5dfa7d5efa7f5efa815ffb835ffb8560fb8761fc8961fc8a62fc8c63fc8e64fc9065fd9266fd9467fd9668fd9869fd9a6afd9b6bfe9d6cfe9f6dfea16efea36ffea571fea772fea973feaa74feac76feae77feb078feb27afeb47bfeb67cfeb77efeb97ffebb81febd82febf84fec185fec287fec488fec68afec88cfeca8dfecc8ffecd90fecf92fed194fed395fed597fed799fed89afdda9cfddc9efddea0fde0a1fde2a3fde3a5fde5a7fde7a9fde9aafdebacfcecaefceeb0fcf0b2fcf2b4fcf4b6fcf6b8fcf7b9fcf9bbfcfbbdfcfdbf")),CW=df(Ee("00000401000501010601010802010a02020c02020e03021004031204031405041706041907051b08051d09061f0a07220b07240c08260d08290e092b10092d110a30120a32140b34150b37160b39180c3c190c3e1b0c411c0c431e0c451f0c48210c4a230c4c240c4f260c51280b53290b552b0b572d0b592f0a5b310a5c320a5e340a5f3609613809623909633b09643d09653e0966400a67420a68440a68450a69470b6a490b6a4a0c6b4c0c6b4d0d6c4f0d6c510e6c520e6d540f6d550f6d57106e59106e5a116e5c126e5d126e5f136e61136e62146e64156e65156e67166e69166e6a176e6c186e6d186e6f196e71196e721a6e741a6e751b6e771c6d781c6d7a1d6d7c1d6d7d1e6d7f1e6c801f6c82206c84206b85216b87216b88226a8a226a8c23698d23698f24699025689225689326679526679727669827669a28659b29649d29649f2a63a02a63a22b62a32c61a52c60a62d60a82e5fa92e5eab2f5ead305dae305cb0315bb1325ab3325ab43359b63458b73557b93556ba3655bc3754bd3853bf3952c03a51c13a50c33b4fc43c4ec63d4dc73e4cc83f4bca404acb4149cc4248ce4347cf4446d04545d24644d34743d44842d54a41d74b3fd84c3ed94d3dda4e3cdb503bdd513ade5238df5337e05536e15635e25734e35933e45a31e55c30e65d2fe75e2ee8602de9612bea632aeb6429eb6628ec6726ed6925ee6a24ef6c23ef6e21f06f20f1711ff1731df2741cf3761bf37819f47918f57b17f57d15f67e14f68013f78212f78410f8850ff8870ef8890cf98b0bf98c0af98e09fa9008fa9207fa9407fb9606fb9706fb9906fb9b06fb9d07fc9f07fca108fca309fca50afca60cfca80dfcaa0ffcac11fcae12fcb014fcb216fcb418fbb61afbb81dfbba1ffbbc21fbbe23fac026fac228fac42afac62df9c72ff9c932f9cb35f8cd37f8cf3af7d13df7d340f6d543f6d746f5d949f5db4cf4dd4ff4df53f4e156f3e35af3e55df2e661f2e865f2ea69f1ec6df1ed71f1ef75f1f179f2f27df2f482f3f586f3f68af4f88ef5f992f6fa96f8fb9af9fc9dfafda1fcffa4")),SW=df(Ee("0d088710078813078916078a19068c1b068d1d068e20068f2206902406912605912805922a05932c05942e05952f059631059733059735049837049938049a3a049a3c049b3e049c3f049c41049d43039e44039e46039f48039f4903a04b03a14c02a14e02a25002a25102a35302a35502a45601a45801a45901a55b01a55c01a65e01a66001a66100a76300a76400a76600a76700a86900a86a00a86c00a86e00a86f00a87100a87201a87401a87501a87701a87801a87a02a87b02a87d03a87e03a88004a88104a78305a78405a78606a68707a68808a68a09a58b0aa58d0ba58e0ca48f0da4910ea3920fa39410a29511a19613a19814a099159f9a169f9c179e9d189d9e199da01a9ca11b9ba21d9aa31e9aa51f99a62098a72197a82296aa2395ab2494ac2694ad2793ae2892b02991b12a90b22b8fb32c8eb42e8db52f8cb6308bb7318ab83289ba3388bb3488bc3587bd3786be3885bf3984c03a83c13b82c23c81c33d80c43e7fc5407ec6417dc7427cc8437bc9447aca457acb4679cc4778cc4977cd4a76ce4b75cf4c74d04d73d14e72d24f71d35171d45270d5536fd5546ed6556dd7566cd8576bd9586ada5a6ada5b69db5c68dc5d67dd5e66de5f65de6164df6263e06363e16462e26561e26660e3685fe4695ee56a5de56b5de66c5ce76e5be76f5ae87059e97158e97257ea7457eb7556eb7655ec7754ed7953ed7a52ee7b51ef7c51ef7e50f07f4ff0804ef1814df1834cf2844bf3854bf3874af48849f48948f58b47f58c46f68d45f68f44f79044f79143f79342f89441f89540f9973ff9983ef99a3efa9b3dfa9c3cfa9e3bfb9f3afba139fba238fca338fca537fca636fca835fca934fdab33fdac33fdae32fdaf31fdb130fdb22ffdb42ffdb52efeb72dfeb82cfeba2cfebb2bfebd2afebe2afec029fdc229fdc328fdc527fdc627fdc827fdca26fdcb26fccd25fcce25fcd025fcd225fbd324fbd524fbd724fad824fada24f9dc24f9dd25f8df25f8e125f7e225f7e425f6e626f6e826f5e926f5eb27f4ed27f3ee27f3f027f2f227f1f426f1f525f0f724f0f921"));function xe(t){return function(){return t}}const L7=Math.abs,qr=Math.atan2,na=Math.cos,AW=Math.max,Po=Math.min,gn=Math.sin,He=Math.sqrt,Vr=1e-12,za=Math.PI,pf=za/2,Ya=2*za;function MW(t){return t>1?0:t<-1?za:Math.acos(t)}function R7(t){return t>=1?pf:t<=-1?-pf:Math.asin(t)}function LW(t){return t.innerRadius}function RW(t){return t.outerRadius}function IW(t){return t.startAngle}function NW(t){return t.endAngle}function BW(t){return t&&t.padAngle}function DW(t,e,r,n,i,a,s,o){var l=r-t,u=n-e,h=s-i,d=o-a,f=d*l-h*u;if(!(f*fD*D+N*N&&(R=L,A=v),{cx:R,cy:A,x01:-h,y01:-d,x11:R*(i/C-1),y11:A*(i/C-1)}}function yf(){var t=LW,e=RW,r=xe(0),n=null,i=IW,a=NW,s=BW,o=null;function l(){var u,h,d=+t.apply(this,arguments),f=+e.apply(this,arguments),p=i.apply(this,arguments)-pf,m=a.apply(this,arguments)-pf,_=L7(m-p),y=m>p;if(o||(o=u=Ra()),fVr))o.moveTo(0,0);else if(_>Ya-Vr)o.moveTo(f*na(p),f*gn(p)),o.arc(0,0,f,p,m,!y),d>Vr&&(o.moveTo(d*na(m),d*gn(m)),o.arc(0,0,d,m,p,y));else{var b=p,x=m,k=p,T=m,C=_,M=_,S=s.apply(this,arguments)/2,R=S>Vr&&(n?+n.apply(this,arguments):He(d*d+f*f)),A=Po(L7(f-d)/2,+r.apply(this,arguments)),L=A,v=A,B,w;if(R>Vr){var D=R7(R/d*gn(S)),N=R7(R/f*gn(S));(C-=D*2)>Vr?(D*=y?1:-1,k+=D,T-=D):(C=0,k=T=(p+m)/2),(M-=N*2)>Vr?(N*=y?1:-1,b+=N,x-=N):(M=0,b=x=(p+m)/2)}var z=f*na(b),X=f*gn(b),ct=d*na(T),J=d*gn(T);if(A>Vr){var Y=f*na(x),$=f*gn(x),lt=d*na(k),ut=d*gn(k),W;if(_Vr?v>Vr?(B=gf(lt,ut,z,X,f,v,y),w=gf(Y,$,ct,J,f,v,y),o.moveTo(B.cx+B.x01,B.cy+B.y01),vVr)||!(C>Vr)?o.lineTo(ct,J):L>Vr?(B=gf(ct,J,Y,$,d,-L,y),w=gf(z,X,lt,ut,d,-L,y),o.lineTo(B.cx+B.x01,B.cy+B.y01),L=f;--p)o.point(x[p],k[p]);o.lineEnd(),o.areaEnd()}y&&(x[d]=+t(_,d,h),k[d]=+e(_,d,h),o.point(n?+n(_,d,h):x[d],r?+r(_,d,h):k[d]))}if(b)return o=null,b+""||null}function u(){return Ua().defined(i).curve(s).context(a)}return l.x=function(h){return arguments.length?(t=typeof h=="function"?h:xe(+h),n=null,l):t},l.x0=function(h){return arguments.length?(t=typeof h=="function"?h:xe(+h),l):t},l.x1=function(h){return arguments.length?(n=h==null?null:typeof h=="function"?h:xe(+h),l):n},l.y=function(h){return arguments.length?(e=typeof h=="function"?h:xe(+h),r=null,l):e},l.y0=function(h){return arguments.length?(e=typeof h=="function"?h:xe(+h),l):e},l.y1=function(h){return arguments.length?(r=h==null?null:typeof h=="function"?h:xe(+h),l):r},l.lineX0=l.lineY0=function(){return u().x(t).y(e)},l.lineY1=function(){return u().x(t).y(r)},l.lineX1=function(){return u().x(n).y(e)},l.defined=function(h){return arguments.length?(i=typeof h=="function"?h:xe(!!h),l):i},l.curve=function(h){return arguments.length?(s=h,a!=null&&(o=s(a)),l):s},l.context=function(h){return arguments.length?(h==null?a=o=null:o=s(a=h),l):a},l}function FW(t,e){return et?1:e>=t?0:NaN}function PW(t){return t}function B7(){var t=PW,e=FW,r=null,n=xe(0),i=xe(Ya),a=xe(0);function s(o){var l,u=(o=mf(o)).length,h,d,f=0,p=new Array(u),m=new Array(u),_=+n.apply(this,arguments),y=Math.min(Ya,Math.max(-Ya,i.apply(this,arguments)-_)),b,x=Math.min(Math.abs(y)/u,a.apply(this,arguments)),k=x*(y<0?-1:1),T;for(l=0;l0&&(f+=T);for(e!=null?p.sort(function(C,M){return e(m[C],m[M])}):r!=null&&p.sort(function(C,M){return r(o[C],o[M])}),l=0,d=f?(y-u*k)/f:0;l0?T*d:0)+k,m[h]={data:o[h],index:l,value:T,startAngle:_,endAngle:b,padAngle:x};return m}return s.value=function(o){return arguments.length?(t=typeof o=="function"?o:xe(+o),s):t},s.sortValues=function(o){return arguments.length?(e=o,r=null,s):e},s.sort=function(o){return arguments.length?(r=o,e=null,s):r},s.startAngle=function(o){return arguments.length?(n=typeof o=="function"?o:xe(+o),s):n},s.endAngle=function(o){return arguments.length?(i=typeof o=="function"?o:xe(+o),s):i},s.padAngle=function(o){return arguments.length?(a=typeof o=="function"?o:xe(+o),s):a},s}var D7=Ip(yn);function O7(t){this._curve=t}O7.prototype={areaStart:function(){this._curve.areaStart()},areaEnd:function(){this._curve.areaEnd()},lineStart:function(){this._curve.lineStart()},lineEnd:function(){this._curve.lineEnd()},point:function(t,e){this._curve.point(e*Math.sin(t),e*-Math.cos(t))}};function Ip(t){function e(r){return new O7(t(r))}return e._curve=t,e}function xc(t){var e=t.curve;return t.angle=t.x,delete t.x,t.radius=t.y,delete t.y,t.curve=function(r){return arguments.length?e(Ip(r)):e()._curve},t}function F7(){return xc(Ua().curve(D7))}function P7(){var t=N7().curve(D7),e=t.curve,r=t.lineX0,n=t.lineX1,i=t.lineY0,a=t.lineY1;return t.angle=t.x,delete t.x,t.startAngle=t.x0,delete t.x0,t.endAngle=t.x1,delete t.x1,t.radius=t.y,delete t.y,t.innerRadius=t.y0,delete t.y0,t.outerRadius=t.y1,delete t.y1,t.lineStartAngle=function(){return xc(r())},delete t.lineX0,t.lineEndAngle=function(){return xc(n())},delete t.lineX1,t.lineInnerRadius=function(){return xc(i())},delete t.lineY0,t.lineOuterRadius=function(){return xc(a())},delete t.lineY1,t.curve=function(s){return arguments.length?e(Ip(s)):e()._curve},t}function kc(t,e){return[(e=+e)*Math.cos(t-=Math.PI/2),e*Math.sin(t)]}class q7{constructor(e,r){this._context=e,this._x=r}areaStart(){this._line=0}areaEnd(){this._line=NaN}lineStart(){this._point=0}lineEnd(){(this._line||this._line!==0&&this._point===1)&&this._context.closePath(),this._line=1-this._line}point(e,r){switch(e=+e,r=+r,this._point){case 0:{this._point=1,this._line?this._context.lineTo(e,r):this._context.moveTo(e,r);break}case 1:this._point=2;default:{this._x?this._context.bezierCurveTo(this._x0=(this._x0+e)/2,this._y0,this._x0,r,e,r):this._context.bezierCurveTo(this._x0,this._y0=(this._y0+r)/2,e,this._y0,e,r);break}}this._x0=e,this._y0=r}}class qW{constructor(e){this._context=e}lineStart(){this._point=0}lineEnd(){}point(e,r){if(e=+e,r=+r,this._point++===0)this._x0=e,this._y0=r;else{const n=kc(this._x0,this._y0),i=kc(this._x0,this._y0=(this._y0+r)/2),a=kc(e,this._y0),s=kc(e,r);this._context.moveTo(...n),this._context.bezierCurveTo(...i,...a,...s)}}}function V7(t){return new q7(t,!0)}function z7(t){return new q7(t,!1)}function VW(t){return new qW(t)}function zW(t){return t.source}function YW(t){return t.target}function bf(t){let e=zW,r=YW,n=Lp,i=Rp,a=null,s=null;function o(){let l;const u=OW.call(arguments),h=e.apply(this,u),d=r.apply(this,u);if(a==null&&(s=t(l=Ra())),s.lineStart(),u[0]=h,s.point(+n.apply(this,u),+i.apply(this,u)),u[0]=d,s.point(+n.apply(this,u),+i.apply(this,u)),s.lineEnd(),l)return s=null,l+""||null}return o.source=function(l){return arguments.length?(e=l,o):e},o.target=function(l){return arguments.length?(r=l,o):r},o.x=function(l){return arguments.length?(n=typeof l=="function"?l:xe(+l),o):n},o.y=function(l){return arguments.length?(i=typeof l=="function"?l:xe(+l),o):i},o.context=function(l){return arguments.length?(l==null?a=s=null:s=t(a=l),o):a},o}function UW(){return bf(V7)}function WW(){return bf(z7)}function HW(){const t=bf(VW);return t.angle=t.x,delete t.x,t.radius=t.y,delete t.y,t}const GW=He(3),Y7={draw(t,e){const r=He(e+Po(e/28,.75))*.59436,n=r/2,i=n*GW;t.moveTo(0,r),t.lineTo(0,-r),t.moveTo(-i,-n),t.lineTo(i,n),t.moveTo(-i,n),t.lineTo(i,-n)}},_f={draw(t,e){const r=He(e/za);t.moveTo(r,0),t.arc(0,0,r,0,Ya)}},U7={draw(t,e){const r=He(e/5)/2;t.moveTo(-3*r,-r),t.lineTo(-r,-r),t.lineTo(-r,-3*r),t.lineTo(r,-3*r),t.lineTo(r,-r),t.lineTo(3*r,-r),t.lineTo(3*r,r),t.lineTo(r,r),t.lineTo(r,3*r),t.lineTo(-r,3*r),t.lineTo(-r,r),t.lineTo(-3*r,r),t.closePath()}},W7=He(1/3),jW=W7*2,H7={draw(t,e){const r=He(e/jW),n=r*W7;t.moveTo(0,-r),t.lineTo(n,0),t.lineTo(0,r),t.lineTo(-n,0),t.closePath()}},G7={draw(t,e){const r=He(e)*.62625;t.moveTo(0,-r),t.lineTo(r,0),t.lineTo(0,r),t.lineTo(-r,0),t.closePath()}},j7={draw(t,e){const r=He(e-Po(e/7,2))*.87559;t.moveTo(-r,0),t.lineTo(r,0),t.moveTo(0,r),t.lineTo(0,-r)}},$7={draw(t,e){const r=He(e),n=-r/2;t.rect(n,n,r,r)}},X7={draw(t,e){const r=He(e)*.4431;t.moveTo(r,r),t.lineTo(r,-r),t.lineTo(-r,-r),t.lineTo(-r,r),t.closePath()}},$W=.8908130915292852,K7=gn(za/10)/gn(7*za/10),XW=gn(Ya/10)*K7,KW=-na(Ya/10)*K7,Z7={draw(t,e){const r=He(e*$W),n=XW*r,i=KW*r;t.moveTo(0,-r),t.lineTo(n,i);for(let a=1;a<5;++a){const s=Ya*a/5,o=na(s),l=gn(s);t.lineTo(l*r,-o*r),t.lineTo(o*n-l*i,l*n+o*i)}t.closePath()}},Np=He(3),Q7={draw(t,e){const r=-He(e/(Np*3));t.moveTo(0,r*2),t.lineTo(-Np*r,-r),t.lineTo(Np*r,-r),t.closePath()}},ZW=He(3),J7={draw(t,e){const r=He(e)*.6824,n=r/2,i=r*ZW/2;t.moveTo(0,-r),t.lineTo(i,n),t.lineTo(-i,n),t.closePath()}},Pn=-.5,qn=He(3)/2,Bp=1/He(12),QW=(Bp/2+1)*3,tk={draw(t,e){const r=He(e/QW),n=r/2,i=r*Bp,a=n,s=r*Bp+r,o=-a,l=s;t.moveTo(n,i),t.lineTo(a,s),t.lineTo(o,l),t.lineTo(Pn*n-qn*i,qn*n+Pn*i),t.lineTo(Pn*a-qn*s,qn*a+Pn*s),t.lineTo(Pn*o-qn*l,qn*o+Pn*l),t.lineTo(Pn*n+qn*i,Pn*i-qn*n),t.lineTo(Pn*a+qn*s,Pn*s-qn*a),t.lineTo(Pn*o+qn*l,Pn*l-qn*o),t.closePath()}},ek={draw(t,e){const r=He(e-Po(e/6,1.7))*.6189;t.moveTo(-r,-r),t.lineTo(r,r),t.moveTo(-r,r),t.lineTo(r,-r)}},rk=[_f,U7,H7,$7,Z7,Q7,tk],JW=[_f,j7,ek,J7,Y7,X7,G7];function tH(t,e){let r=null;t=typeof t=="function"?t:xe(t||_f),e=typeof e=="function"?e:xe(e===void 0?64:+e);function n(){let i;if(r||(r=i=Ra()),t.apply(this,arguments).draw(r,+e.apply(this,arguments)),i)return r=null,i+""||null}return n.type=function(i){return arguments.length?(t=typeof i=="function"?i:xe(i),n):t},n.size=function(i){return arguments.length?(e=typeof i=="function"?i:xe(+i),n):e},n.context=function(i){return arguments.length?(r=i==null?null:i,n):r},n}function Wa(){}function vf(t,e,r){t._context.bezierCurveTo((2*t._x0+t._x1)/3,(2*t._y0+t._y1)/3,(t._x0+2*t._x1)/3,(t._y0+2*t._y1)/3,(t._x0+4*t._x1+e)/6,(t._y0+4*t._y1+r)/6)}function xf(t){this._context=t}xf.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._y0=this._y1=NaN,this._point=0},lineEnd:function(){switch(this._point){case 3:vf(this,this._x1,this._y1);case 2:this._context.lineTo(this._x1,this._y1);break}(this._line||this._line!==0&&this._point===1)&&this._context.closePath(),this._line=1-this._line},point:function(t,e){switch(t=+t,e=+e,this._point){case 0:this._point=1,this._line?this._context.lineTo(t,e):this._context.moveTo(t,e);break;case 1:this._point=2;break;case 2:this._point=3,this._context.lineTo((5*this._x0+this._x1)/6,(5*this._y0+this._y1)/6);default:vf(this,t,e);break}this._x0=this._x1,this._x1=t,this._y0=this._y1,this._y1=e}};function Os(t){return new xf(t)}function nk(t){this._context=t}nk.prototype={areaStart:Wa,areaEnd:Wa,lineStart:function(){this._x0=this._x1=this._x2=this._x3=this._x4=this._y0=this._y1=this._y2=this._y3=this._y4=NaN,this._point=0},lineEnd:function(){switch(this._point){case 1:{this._context.moveTo(this._x2,this._y2),this._context.closePath();break}case 2:{this._context.moveTo((this._x2+2*this._x3)/3,(this._y2+2*this._y3)/3),this._context.lineTo((this._x3+2*this._x2)/3,(this._y3+2*this._y2)/3),this._context.closePath();break}case 3:{this.point(this._x2,this._y2),this.point(this._x3,this._y3),this.point(this._x4,this._y4);break}}},point:function(t,e){switch(t=+t,e=+e,this._point){case 0:this._point=1,this._x2=t,this._y2=e;break;case 1:this._point=2,this._x3=t,this._y3=e;break;case 2:this._point=3,this._x4=t,this._y4=e,this._context.moveTo((this._x0+4*this._x1+t)/6,(this._y0+4*this._y1+e)/6);break;default:vf(this,t,e);break}this._x0=this._x1,this._x1=t,this._y0=this._y1,this._y1=e}};function ik(t){return new nk(t)}function ak(t){this._context=t}ak.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._y0=this._y1=NaN,this._point=0},lineEnd:function(){(this._line||this._line!==0&&this._point===3)&&this._context.closePath(),this._line=1-this._line},point:function(t,e){switch(t=+t,e=+e,this._point){case 0:this._point=1;break;case 1:this._point=2;break;case 2:this._point=3;var r=(this._x0+4*this._x1+t)/6,n=(this._y0+4*this._y1+e)/6;this._line?this._context.lineTo(r,n):this._context.moveTo(r,n);break;case 3:this._point=4;default:vf(this,t,e);break}this._x0=this._x1,this._x1=t,this._y0=this._y1,this._y1=e}};function sk(t){return new ak(t)}function ok(t,e){this._basis=new xf(t),this._beta=e}ok.prototype={lineStart:function(){this._x=[],this._y=[],this._basis.lineStart()},lineEnd:function(){var t=this._x,e=this._y,r=t.length-1;if(r>0)for(var n=t[0],i=e[0],a=t[r]-n,s=e[r]-i,o=-1,l;++o<=r;)l=o/r,this._basis.point(this._beta*t[o]+(1-this._beta)*(n+l*a),this._beta*e[o]+(1-this._beta)*(i+l*s));this._x=this._y=null,this._basis.lineEnd()},point:function(t,e){this._x.push(+t),this._y.push(+e)}};const eH=function t(e){function r(n){return e===1?new xf(n):new ok(n,e)}return r.beta=function(n){return t(+n)},r}(.85);function kf(t,e,r){t._context.bezierCurveTo(t._x1+t._k*(t._x2-t._x0),t._y1+t._k*(t._y2-t._y0),t._x2+t._k*(t._x1-e),t._y2+t._k*(t._y1-r),t._x2,t._y2)}function Dp(t,e){this._context=t,this._k=(1-e)/6}Dp.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._x2=this._y0=this._y1=this._y2=NaN,this._point=0},lineEnd:function(){switch(this._point){case 2:this._context.lineTo(this._x2,this._y2);break;case 3:kf(this,this._x1,this._y1);break}(this._line||this._line!==0&&this._point===1)&&this._context.closePath(),this._line=1-this._line},point:function(t,e){switch(t=+t,e=+e,this._point){case 0:this._point=1,this._line?this._context.lineTo(t,e):this._context.moveTo(t,e);break;case 1:this._point=2,this._x1=t,this._y1=e;break;case 2:this._point=3;default:kf(this,t,e);break}this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=e}};const rH=function t(e){function r(n){return new Dp(n,e)}return r.tension=function(n){return t(+n)},r}(0);function Op(t,e){this._context=t,this._k=(1-e)/6}Op.prototype={areaStart:Wa,areaEnd:Wa,lineStart:function(){this._x0=this._x1=this._x2=this._x3=this._x4=this._x5=this._y0=this._y1=this._y2=this._y3=this._y4=this._y5=NaN,this._point=0},lineEnd:function(){switch(this._point){case 1:{this._context.moveTo(this._x3,this._y3),this._context.closePath();break}case 2:{this._context.lineTo(this._x3,this._y3),this._context.closePath();break}case 3:{this.point(this._x3,this._y3),this.point(this._x4,this._y4),this.point(this._x5,this._y5);break}}},point:function(t,e){switch(t=+t,e=+e,this._point){case 0:this._point=1,this._x3=t,this._y3=e;break;case 1:this._point=2,this._context.moveTo(this._x4=t,this._y4=e);break;case 2:this._point=3,this._x5=t,this._y5=e;break;default:kf(this,t,e);break}this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=e}};const nH=function t(e){function r(n){return new Op(n,e)}return r.tension=function(n){return t(+n)},r}(0);function Fp(t,e){this._context=t,this._k=(1-e)/6}Fp.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._x2=this._y0=this._y1=this._y2=NaN,this._point=0},lineEnd:function(){(this._line||this._line!==0&&this._point===3)&&this._context.closePath(),this._line=1-this._line},point:function(t,e){switch(t=+t,e=+e,this._point){case 0:this._point=1;break;case 1:this._point=2;break;case 2:this._point=3,this._line?this._context.lineTo(this._x2,this._y2):this._context.moveTo(this._x2,this._y2);break;case 3:this._point=4;default:kf(this,t,e);break}this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=e}};const iH=function t(e){function r(n){return new Fp(n,e)}return r.tension=function(n){return t(+n)},r}(0);function Pp(t,e,r){var n=t._x1,i=t._y1,a=t._x2,s=t._y2;if(t._l01_a>Vr){var o=2*t._l01_2a+3*t._l01_a*t._l12_a+t._l12_2a,l=3*t._l01_a*(t._l01_a+t._l12_a);n=(n*o-t._x0*t._l12_2a+t._x2*t._l01_2a)/l,i=(i*o-t._y0*t._l12_2a+t._y2*t._l01_2a)/l}if(t._l23_a>Vr){var u=2*t._l23_2a+3*t._l23_a*t._l12_a+t._l12_2a,h=3*t._l23_a*(t._l23_a+t._l12_a);a=(a*u+t._x1*t._l23_2a-e*t._l12_2a)/h,s=(s*u+t._y1*t._l23_2a-r*t._l12_2a)/h}t._context.bezierCurveTo(n,i,a,s,t._x2,t._y2)}function lk(t,e){this._context=t,this._alpha=e}lk.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._x2=this._y0=this._y1=this._y2=NaN,this._l01_a=this._l12_a=this._l23_a=this._l01_2a=this._l12_2a=this._l23_2a=this._point=0},lineEnd:function(){switch(this._point){case 2:this._context.lineTo(this._x2,this._y2);break;case 3:this.point(this._x2,this._y2);break}(this._line||this._line!==0&&this._point===1)&&this._context.closePath(),this._line=1-this._line},point:function(t,e){if(t=+t,e=+e,this._point){var r=this._x2-t,n=this._y2-e;this._l23_a=Math.sqrt(this._l23_2a=Math.pow(r*r+n*n,this._alpha))}switch(this._point){case 0:this._point=1,this._line?this._context.lineTo(t,e):this._context.moveTo(t,e);break;case 1:this._point=2;break;case 2:this._point=3;default:Pp(this,t,e);break}this._l01_a=this._l12_a,this._l12_a=this._l23_a,this._l01_2a=this._l12_2a,this._l12_2a=this._l23_2a,this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=e}};const aH=function t(e){function r(n){return e?new lk(n,e):new Dp(n,0)}return r.alpha=function(n){return t(+n)},r}(.5);function ck(t,e){this._context=t,this._alpha=e}ck.prototype={areaStart:Wa,areaEnd:Wa,lineStart:function(){this._x0=this._x1=this._x2=this._x3=this._x4=this._x5=this._y0=this._y1=this._y2=this._y3=this._y4=this._y5=NaN,this._l01_a=this._l12_a=this._l23_a=this._l01_2a=this._l12_2a=this._l23_2a=this._point=0},lineEnd:function(){switch(this._point){case 1:{this._context.moveTo(this._x3,this._y3),this._context.closePath();break}case 2:{this._context.lineTo(this._x3,this._y3),this._context.closePath();break}case 3:{this.point(this._x3,this._y3),this.point(this._x4,this._y4),this.point(this._x5,this._y5);break}}},point:function(t,e){if(t=+t,e=+e,this._point){var r=this._x2-t,n=this._y2-e;this._l23_a=Math.sqrt(this._l23_2a=Math.pow(r*r+n*n,this._alpha))}switch(this._point){case 0:this._point=1,this._x3=t,this._y3=e;break;case 1:this._point=2,this._context.moveTo(this._x4=t,this._y4=e);break;case 2:this._point=3,this._x5=t,this._y5=e;break;default:Pp(this,t,e);break}this._l01_a=this._l12_a,this._l12_a=this._l23_a,this._l01_2a=this._l12_2a,this._l12_2a=this._l23_2a,this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=e}};const sH=function t(e){function r(n){return e?new ck(n,e):new Op(n,0)}return r.alpha=function(n){return t(+n)},r}(.5);function uk(t,e){this._context=t,this._alpha=e}uk.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._x2=this._y0=this._y1=this._y2=NaN,this._l01_a=this._l12_a=this._l23_a=this._l01_2a=this._l12_2a=this._l23_2a=this._point=0},lineEnd:function(){(this._line||this._line!==0&&this._point===3)&&this._context.closePath(),this._line=1-this._line},point:function(t,e){if(t=+t,e=+e,this._point){var r=this._x2-t,n=this._y2-e;this._l23_a=Math.sqrt(this._l23_2a=Math.pow(r*r+n*n,this._alpha))}switch(this._point){case 0:this._point=1;break;case 1:this._point=2;break;case 2:this._point=3,this._line?this._context.lineTo(this._x2,this._y2):this._context.moveTo(this._x2,this._y2);break;case 3:this._point=4;default:Pp(this,t,e);break}this._l01_a=this._l12_a,this._l12_a=this._l23_a,this._l01_2a=this._l12_2a,this._l12_2a=this._l23_2a,this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=e}};const oH=function t(e){function r(n){return e?new uk(n,e):new Fp(n,0)}return r.alpha=function(n){return t(+n)},r}(.5);function hk(t){this._context=t}hk.prototype={areaStart:Wa,areaEnd:Wa,lineStart:function(){this._point=0},lineEnd:function(){this._point&&this._context.closePath()},point:function(t,e){t=+t,e=+e,this._point?this._context.lineTo(t,e):(this._point=1,this._context.moveTo(t,e))}};function fk(t){return new hk(t)}function dk(t){return t<0?-1:1}function pk(t,e,r){var n=t._x1-t._x0,i=e-t._x1,a=(t._y1-t._y0)/(n||i<0&&-0),s=(r-t._y1)/(i||n<0&&-0),o=(a*i+s*n)/(n+i);return(dk(a)+dk(s))*Math.min(Math.abs(a),Math.abs(s),.5*Math.abs(o))||0}function gk(t,e){var r=t._x1-t._x0;return r?(3*(t._y1-t._y0)/r-e)/2:e}function qp(t,e,r){var n=t._x0,i=t._y0,a=t._x1,s=t._y1,o=(a-n)/3;t._context.bezierCurveTo(n+o,i+o*e,a-o,s-o*r,a,s)}function wf(t){this._context=t}wf.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._y0=this._y1=this._t0=NaN,this._point=0},lineEnd:function(){switch(this._point){case 2:this._context.lineTo(this._x1,this._y1);break;case 3:qp(this,this._t0,gk(this,this._t0));break}(this._line||this._line!==0&&this._point===1)&&this._context.closePath(),this._line=1-this._line},point:function(t,e){var r=NaN;if(t=+t,e=+e,!(t===this._x1&&e===this._y1)){switch(this._point){case 0:this._point=1,this._line?this._context.lineTo(t,e):this._context.moveTo(t,e);break;case 1:this._point=2;break;case 2:this._point=3,qp(this,gk(this,r=pk(this,t,e)),r);break;default:qp(this,this._t0,r=pk(this,t,e));break}this._x0=this._x1,this._x1=t,this._y0=this._y1,this._y1=e,this._t0=r}}};function yk(t){this._context=new mk(t)}(yk.prototype=Object.create(wf.prototype)).point=function(t,e){wf.prototype.point.call(this,e,t)};function mk(t){this._context=t}mk.prototype={moveTo:function(t,e){this._context.moveTo(e,t)},closePath:function(){this._context.closePath()},lineTo:function(t,e){this._context.lineTo(e,t)},bezierCurveTo:function(t,e,r,n,i,a){this._context.bezierCurveTo(e,t,n,r,a,i)}};function bk(t){return new wf(t)}function _k(t){return new yk(t)}function vk(t){this._context=t}vk.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x=[],this._y=[]},lineEnd:function(){var t=this._x,e=this._y,r=t.length;if(r)if(this._line?this._context.lineTo(t[0],e[0]):this._context.moveTo(t[0],e[0]),r===2)this._context.lineTo(t[1],e[1]);else for(var n=xk(t),i=xk(e),a=0,s=1;s=0;--e)i[e]=(s[e]-i[e+1])/a[e];for(a[r-1]=(t[r]+i[r-1])/2,e=0;e=0&&(this._t=1-this._t,this._line=1-this._line)},point:function(t,e){switch(t=+t,e=+e,this._point){case 0:this._point=1,this._line?this._context.lineTo(t,e):this._context.moveTo(t,e);break;case 1:this._point=2;default:{if(this._t<=0)this._context.lineTo(this._x,e),this._context.lineTo(t,e);else{var r=this._x*(1-this._t)+t*this._t;this._context.lineTo(r,this._y),this._context.lineTo(r,e)}break}}this._x=t,this._y=e}};function wk(t){return new Tf(t,.5)}function Tk(t){return new Tf(t,0)}function Ek(t){return new Tf(t,1)}function qo(t,e){if((s=t.length)>1)for(var r=1,n,i,a=t[e[0]],s,o=a.length;r=0;)r[e]=e;return r}function lH(t,e){return t[e]}function cH(t){const e=[];return e.key=t,e}function uH(){var t=xe([]),e=Vo,r=qo,n=lH;function i(a){var s=Array.from(t.apply(this,arguments),cH),o,l=s.length,u=-1,h;for(const d of a)for(o=0,++u;o0){for(var r,n,i=0,a=t[0].length,s;i0)for(var r,n=0,i,a,s,o,l,u=t[e[0]].length;n0?(i[0]=s,i[1]=s+=a):a<0?(i[1]=o,i[0]=o+=a):(i[0]=0,i[1]=a)}function dH(t,e){if((i=t.length)>0){for(var r=0,n=t[e[0]],i,a=n.length;r0)||!((a=(i=t[e[0]]).length)>0))){for(var r=0,n=1,i,a,s;na&&(a=i,r=e);return r}function Sk(t){var e=t.map(Ak);return Vo(t).sort(function(r,n){return e[r]-e[n]})}function Ak(t){for(var e=0,r=-1,n=t.length,i;++r()=>t;function _H(t,{sourceEvent:e,target:r,transform:n,dispatch:i}){Object.defineProperties(this,{type:{value:t,enumerable:!0,configurable:!0},sourceEvent:{value:e,enumerable:!0,configurable:!0},target:{value:r,enumerable:!0,configurable:!0},transform:{value:n,enumerable:!0,configurable:!0},_:{value:i}})}function Ri(t,e,r){this.k=t,this.x=e,this.y=r}Ri.prototype={constructor:Ri,scale:function(t){return t===1?this:new Ri(this.k*t,this.x,this.y)},translate:function(t,e){return t===0&e===0?this:new Ri(this.k,this.x+this.k*t,this.y+this.k*e)},apply:function(t){return[t[0]*this.k+this.x,t[1]*this.k+this.y]},applyX:function(t){return t*this.k+this.x},applyY:function(t){return t*this.k+this.y},invert:function(t){return[(t[0]-this.x)/this.k,(t[1]-this.y)/this.k]},invertX:function(t){return(t-this.x)/this.k},invertY:function(t){return(t-this.y)/this.k},rescaleX:function(t){return t.copy().domain(t.range().map(this.invertX,this).map(t.invert,t))},rescaleY:function(t){return t.copy().domain(t.range().map(this.invertY,this).map(t.invert,t))},toString:function(){return"translate("+this.x+","+this.y+") scale("+this.k+")"}};var Cf=new Ri(1,0,0);Mk.prototype=Ri.prototype;function Mk(t){for(;!t.__zoom;)if(!(t=t.parentNode))return Cf;return t.__zoom}function Vp(t){t.stopImmediatePropagation()}function wc(t){t.preventDefault(),t.stopImmediatePropagation()}function vH(t){return(!t.ctrlKey||t.type==="wheel")&&!t.button}function xH(){var t=this;return t instanceof SVGElement?(t=t.ownerSVGElement||t,t.hasAttribute("viewBox")?(t=t.viewBox.baseVal,[[t.x,t.y],[t.x+t.width,t.y+t.height]]):[[0,0],[t.width.baseVal.value,t.height.baseVal.value]]):[[0,0],[t.clientWidth,t.clientHeight]]}function Lk(){return this.__zoom||Cf}function kH(t){return-t.deltaY*(t.deltaMode===1?.05:t.deltaMode?1:.002)*(t.ctrlKey?10:1)}function wH(){return navigator.maxTouchPoints||"ontouchstart"in this}function TH(t,e,r){var n=t.invertX(e[0][0])-r[0][0],i=t.invertX(e[1][0])-r[1][0],a=t.invertY(e[0][1])-r[0][1],s=t.invertY(e[1][1])-r[1][1];return t.translate(i>n?(n+i)/2:Math.min(0,n)||Math.max(0,i),s>a?(a+s)/2:Math.min(0,a)||Math.max(0,s))}function EH(){var t=vH,e=xH,r=TH,n=kH,i=wH,a=[0,1/0],s=[[-1/0,-1/0],[1/0,1/0]],o=250,l=H5,u=fs("start","zoom","end"),h,d,f,p=500,m=150,_=0,y=10;function b(D){D.property("__zoom",Lk).on("wheel.zoom",R,{passive:!1}).on("mousedown.zoom",A).on("dblclick.zoom",L).filter(i).on("touchstart.zoom",v).on("touchmove.zoom",B).on("touchend.zoom touchcancel.zoom",w).style("-webkit-tap-highlight-color","rgba(0,0,0,0)")}b.transform=function(D,N,z,X){var ct=D.selection?D.selection():D;ct.property("__zoom",Lk),D!==ct?C(D,N,z,X):ct.interrupt().each(function(){M(this,arguments).event(X).start().zoom(null,typeof N=="function"?N.apply(this,arguments):N).end()})},b.scaleBy=function(D,N,z,X){b.scaleTo(D,function(){var ct=this.__zoom.k,J=typeof N=="function"?N.apply(this,arguments):N;return ct*J},z,X)},b.scaleTo=function(D,N,z,X){b.transform(D,function(){var ct=e.apply(this,arguments),J=this.__zoom,Y=z==null?T(ct):typeof z=="function"?z.apply(this,arguments):z,$=J.invert(Y),lt=typeof N=="function"?N.apply(this,arguments):N;return r(k(x(J,lt),Y,$),ct,s)},z,X)},b.translateBy=function(D,N,z,X){b.transform(D,function(){return r(this.__zoom.translate(typeof N=="function"?N.apply(this,arguments):N,typeof z=="function"?z.apply(this,arguments):z),e.apply(this,arguments),s)},null,X)},b.translateTo=function(D,N,z,X,ct){b.transform(D,function(){var J=e.apply(this,arguments),Y=this.__zoom,$=X==null?T(J):typeof X=="function"?X.apply(this,arguments):X;return r(Cf.translate($[0],$[1]).scale(Y.k).translate(typeof N=="function"?-N.apply(this,arguments):-N,typeof z=="function"?-z.apply(this,arguments):-z),J,s)},X,ct)};function x(D,N){return N=Math.max(a[0],Math.min(a[1],N)),N===D.k?D:new Ri(N,D.x,D.y)}function k(D,N,z){var X=N[0]-z[0]*D.k,ct=N[1]-z[1]*D.k;return X===D.x&&ct===D.y?D:new Ri(D.k,X,ct)}function T(D){return[(+D[0][0]+ +D[1][0])/2,(+D[0][1]+ +D[1][1])/2]}function C(D,N,z,X){D.on("start.zoom",function(){M(this,arguments).event(X).start()}).on("interrupt.zoom end.zoom",function(){M(this,arguments).event(X).end()}).tween("zoom",function(){var ct=this,J=arguments,Y=M(ct,J).event(X),$=e.apply(ct,J),lt=z==null?T($):typeof z=="function"?z.apply(ct,J):z,ut=Math.max($[1][0]-$[0][0],$[1][1]-$[0][1]),W=ct.__zoom,tt=typeof N=="function"?N.apply(ct,J):N,K=l(W.invert(lt).concat(ut/W.k),tt.invert(lt).concat(ut/tt.k));return function(it){if(it===1)it=tt;else{var Z=K(it),V=ut/Z[2];it=new Ri(V,lt[0]-Z[0]*V,lt[1]-Z[1]*V)}Y.zoom(null,it)}})}function M(D,N,z){return!z&&D.__zooming||new S(D,N)}function S(D,N){this.that=D,this.args=N,this.active=0,this.sourceEvent=null,this.extent=e.apply(D,N),this.taps=0}S.prototype={event:function(D){return D&&(this.sourceEvent=D),this},start:function(){return++this.active===1&&(this.that.__zooming=this,this.emit("start")),this},zoom:function(D,N){return this.mouse&&D!=="mouse"&&(this.mouse[1]=N.invert(this.mouse[0])),this.touch0&&D!=="touch"&&(this.touch0[1]=N.invert(this.touch0[0])),this.touch1&&D!=="touch"&&(this.touch1[1]=N.invert(this.touch1[0])),this.that.__zoom=N,this.emit("zoom"),this},end:function(){return--this.active===0&&(delete this.that.__zooming,this.emit("end")),this},emit:function(D){var N=St(this.that).datum();u.call(D,this.that,new _H(D,{sourceEvent:this.sourceEvent,target:b,type:D,transform:this.that.__zoom,dispatch:u}),N)}};function R(D,...N){if(!t.apply(this,arguments))return;var z=M(this,N).event(D),X=this.__zoom,ct=Math.max(a[0],Math.min(a[1],X.k*Math.pow(2,n.apply(this,arguments)))),J=Tn(D);if(z.wheel)(z.mouse[0][0]!==J[0]||z.mouse[0][1]!==J[1])&&(z.mouse[1]=X.invert(z.mouse[0]=J)),clearTimeout(z.wheel);else{if(X.k===ct)return;z.mouse=[J,X.invert(J)],vs(this),z.start()}wc(D),z.wheel=setTimeout(Y,m),z.zoom("mouse",r(k(x(X,ct),z.mouse[0],z.mouse[1]),z.extent,s));function Y(){z.wheel=null,z.end()}}function A(D,...N){if(f||!t.apply(this,arguments))return;var z=D.currentTarget,X=M(this,N,!0).event(D),ct=St(D.view).on("mousemove.zoom",lt,!0).on("mouseup.zoom",ut,!0),J=Tn(D,z),Y=D.clientX,$=D.clientY;Bu(D.view),Vp(D),X.mouse=[J,this.__zoom.invert(J)],vs(this),X.start();function lt(W){if(wc(W),!X.moved){var tt=W.clientX-Y,K=W.clientY-$;X.moved=tt*tt+K*K>_}X.event(W).zoom("mouse",r(k(X.that.__zoom,X.mouse[0]=Tn(W,z),X.mouse[1]),X.extent,s))}function ut(W){ct.on("mousemove.zoom mouseup.zoom",null),Du(W.view,X.moved),wc(W),X.event(W).end()}}function L(D,...N){if(!!t.apply(this,arguments)){var z=this.__zoom,X=Tn(D.changedTouches?D.changedTouches[0]:D,this),ct=z.invert(X),J=z.k*(D.shiftKey?.5:2),Y=r(k(x(z,J),X,ct),e.apply(this,N),s);wc(D),o>0?St(this).transition().duration(o).call(C,Y,X,D):St(this).call(b.transform,Y,X,D)}}function v(D,...N){if(!!t.apply(this,arguments)){var z=D.touches,X=z.length,ct=M(this,N,D.changedTouches.length===X).event(D),J,Y,$,lt;for(Vp(D),Y=0;Y"u"||!Reflect.construct||Reflect.construct.sham)return!1;if(typeof Proxy=="function")return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],function(){})),!0}catch{return!1}}function Sf(t,e,r){return SH()?Sf=Reflect.construct:Sf=function(i,a,s){var o=[null];o.push.apply(o,a);var l=Function.bind.apply(i,o),u=new l;return s&&zp(u,s.prototype),u},Sf.apply(null,arguments)}function ni(t){return AH(t)||MH(t)||LH(t)||RH()}function AH(t){if(Array.isArray(t))return Yp(t)}function MH(t){if(typeof Symbol<"u"&&t[Symbol.iterator]!=null||t["@@iterator"]!=null)return Array.from(t)}function LH(t,e){if(!!t){if(typeof t=="string")return Yp(t,e);var r=Object.prototype.toString.call(t).slice(8,-1);if(r==="Object"&&t.constructor&&(r=t.constructor.name),r==="Map"||r==="Set")return Array.from(t);if(r==="Arguments"||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(r))return Yp(t,e)}}function Yp(t,e){(e==null||e>t.length)&&(e=t.length);for(var r=0,n=new Array(e);r1?r-1:0),i=1;i/gm),GH=Ii(/^data-[\-\w.\u00B7-\uFFFF]/),jH=Ii(/^aria-[\-\w]+$/),$H=Ii(/^(?:(?:(?:f|ht)tps?|mailto|tel|callto|cid|xmpp):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i),XH=Ii(/^(?:\w+script|data):/i),KH=Ii(/[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205F\u3000]/g),ZH=Ii(/^html$/i),QH=function(){return typeof window>"u"?null:window},JH=function(e,r){if(Ha(e)!=="object"||typeof e.createPolicy!="function")return null;var n=null,i="data-tt-policy-suffix";r.currentScript&&r.currentScript.hasAttribute(i)&&(n=r.currentScript.getAttribute(i));var a="dompurify"+(n?"#"+n:"");try{return e.createPolicy(a,{createHTML:function(o){return o},createScriptURL:function(o){return o}})}catch{return console.warn("TrustedTypes policy "+a+" could not be created."),null}};function Pk(){var t=arguments.length>0&&arguments[0]!==void 0?arguments[0]:QH(),e=function(st){return Pk(st)};if(e.version="2.4.0",e.removed=[],!t||!t.document||t.document.nodeType!==9)return e.isSupported=!1,e;var r=t.document,n=t.document,i=t.DocumentFragment,a=t.HTMLTemplateElement,s=t.Node,o=t.Element,l=t.NodeFilter,u=t.NamedNodeMap,h=u===void 0?t.NamedNodeMap||t.MozNamedAttrMap:u,d=t.HTMLFormElement,f=t.DOMParser,p=t.trustedTypes,m=o.prototype,_=Lf(m,"cloneNode"),y=Lf(m,"nextSibling"),b=Lf(m,"childNodes"),x=Lf(m,"parentNode");if(typeof a=="function"){var k=n.createElement("template");k.content&&k.content.ownerDocument&&(n=k.content.ownerDocument)}var T=JH(p,r),C=T?T.createHTML(""):"",M=n,S=M.implementation,R=M.createNodeIterator,A=M.createDocumentFragment,L=M.getElementsByTagName,v=r.importNode,B={};try{B=Fs(n).documentMode?n.documentMode:{}}catch{}var w={};e.isSupported=typeof x=="function"&&S&&typeof S.createHTMLDocument<"u"&&B!==9;var D=WH,N=HH,z=GH,X=jH,ct=XH,J=KH,Y=$H,$=null,lt=Me({},[].concat(ni(Bk),ni(Hp),ni(Gp),ni(jp),ni(Dk))),ut=null,W=Me({},[].concat(ni(Ok),ni($p),ni(Fk),ni(Rf))),tt=Object.seal(Object.create(null,{tagNameCheck:{writable:!0,configurable:!1,enumerable:!0,value:null},attributeNameCheck:{writable:!0,configurable:!1,enumerable:!0,value:null},allowCustomizedBuiltInElements:{writable:!0,configurable:!1,enumerable:!0,value:!1}})),K=null,it=null,Z=!0,V=!0,Q=!1,q=!1,U=!1,F=!1,j=!1,P=!1,et=!1,at=!1,It=!0,Lt=!1,Rt="user-content-",Ct=!0,pt=!1,mt={},vt=null,Tt=Me({},["annotation-xml","audio","colgroup","desc","foreignobject","head","iframe","math","mi","mn","mo","ms","mtext","noembed","noframes","noscript","plaintext","script","style","svg","template","thead","title","video","xmp"]),ft=null,le=Me({},["audio","video","img","source","image","track"]),Dt=null,Gt=Me({},["alt","class","for","id","label","name","pattern","placeholder","role","summary","title","value","style","xmlns"]),$t="http://www.w3.org/1998/Math/MathML",Qt="http://www.w3.org/2000/svg",we="http://www.w3.org/1999/xhtml",jt=we,Ft=!1,zt,wt=["application/xhtml+xml","text/html"],bt="text/html",Et,kt=null,Ut=n.createElement("form"),gt=function(st){return st instanceof RegExp||st instanceof Function},he=function(st){kt&&kt===st||((!st||Ha(st)!=="object")&&(st={}),st=Fs(st),zt=wt.indexOf(st.PARSER_MEDIA_TYPE)===-1?zt=bt:zt=st.PARSER_MEDIA_TYPE,Et=zt==="application/xhtml+xml"?function(At){return At}:Mf,$="ALLOWED_TAGS"in st?Me({},st.ALLOWED_TAGS,Et):lt,ut="ALLOWED_ATTR"in st?Me({},st.ALLOWED_ATTR,Et):W,Dt="ADD_URI_SAFE_ATTR"in st?Me(Fs(Gt),st.ADD_URI_SAFE_ATTR,Et):Gt,ft="ADD_DATA_URI_TAGS"in st?Me(Fs(le),st.ADD_DATA_URI_TAGS,Et):le,vt="FORBID_CONTENTS"in st?Me({},st.FORBID_CONTENTS,Et):Tt,K="FORBID_TAGS"in st?Me({},st.FORBID_TAGS,Et):{},it="FORBID_ATTR"in st?Me({},st.FORBID_ATTR,Et):{},mt="USE_PROFILES"in st?st.USE_PROFILES:!1,Z=st.ALLOW_ARIA_ATTR!==!1,V=st.ALLOW_DATA_ATTR!==!1,Q=st.ALLOW_UNKNOWN_PROTOCOLS||!1,q=st.SAFE_FOR_TEMPLATES||!1,U=st.WHOLE_DOCUMENT||!1,P=st.RETURN_DOM||!1,et=st.RETURN_DOM_FRAGMENT||!1,at=st.RETURN_TRUSTED_TYPE||!1,j=st.FORCE_BODY||!1,It=st.SANITIZE_DOM!==!1,Lt=st.SANITIZE_NAMED_PROPS||!1,Ct=st.KEEP_CONTENT!==!1,pt=st.IN_PLACE||!1,Y=st.ALLOWED_URI_REGEXP||Y,jt=st.NAMESPACE||we,st.CUSTOM_ELEMENT_HANDLING&>(st.CUSTOM_ELEMENT_HANDLING.tagNameCheck)&&(tt.tagNameCheck=st.CUSTOM_ELEMENT_HANDLING.tagNameCheck),st.CUSTOM_ELEMENT_HANDLING&>(st.CUSTOM_ELEMENT_HANDLING.attributeNameCheck)&&(tt.attributeNameCheck=st.CUSTOM_ELEMENT_HANDLING.attributeNameCheck),st.CUSTOM_ELEMENT_HANDLING&&typeof st.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements=="boolean"&&(tt.allowCustomizedBuiltInElements=st.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements),q&&(V=!1),et&&(P=!0),mt&&($=Me({},ni(Dk)),ut=[],mt.html===!0&&(Me($,Bk),Me(ut,Ok)),mt.svg===!0&&(Me($,Hp),Me(ut,$p),Me(ut,Rf)),mt.svgFilters===!0&&(Me($,Gp),Me(ut,$p),Me(ut,Rf)),mt.mathMl===!0&&(Me($,jp),Me(ut,Fk),Me(ut,Rf))),st.ADD_TAGS&&($===lt&&($=Fs($)),Me($,st.ADD_TAGS,Et)),st.ADD_ATTR&&(ut===W&&(ut=Fs(ut)),Me(ut,st.ADD_ATTR,Et)),st.ADD_URI_SAFE_ATTR&&Me(Dt,st.ADD_URI_SAFE_ATTR,Et),st.FORBID_CONTENTS&&(vt===Tt&&(vt=Fs(vt)),Me(vt,st.FORBID_CONTENTS,Et)),Ct&&($["#text"]=!0),U&&Me($,["html","head","body"]),$.table&&(Me($,["tbody"]),delete K.tbody),sn&&sn(st),kt=st)},yt=Me({},["mi","mo","mn","ms","mtext"]),ne=Me({},["foreignobject","desc","title","annotation-xml"]),ve=Me({},["title","style","font","a","script"]),ye=Me({},Hp);Me(ye,Gp),Me(ye,YH);var be=Me({},jp);Me(be,UH);var Te=function(st){var At=x(st);(!At||!At.tagName)&&(At={namespaceURI:we,tagName:"template"});var Nt=Mf(st.tagName),Jt=Mf(At.tagName);return st.namespaceURI===Qt?At.namespaceURI===we?Nt==="svg":At.namespaceURI===$t?Nt==="svg"&&(Jt==="annotation-xml"||yt[Jt]):Boolean(ye[Nt]):st.namespaceURI===$t?At.namespaceURI===we?Nt==="math":At.namespaceURI===Qt?Nt==="math"&&ne[Jt]:Boolean(be[Nt]):st.namespaceURI===we?At.namespaceURI===Qt&&!ne[Jt]||At.namespaceURI===$t&&!yt[Jt]?!1:!be[Nt]&&(ve[Nt]||!ye[Nt]):!1},Wt=function(st){Tc(e.removed,{element:st});try{st.parentNode.removeChild(st)}catch{try{st.outerHTML=C}catch{st.remove()}}},se=function(st,At){try{Tc(e.removed,{attribute:At.getAttributeNode(st),from:At})}catch{Tc(e.removed,{attribute:null,from:At})}if(At.removeAttribute(st),st==="is"&&!ut[st])if(P||et)try{Wt(At)}catch{}else try{At.setAttribute(st,"")}catch{}},me=function(st){var At,Nt;if(j)st=""+st;else{var Jt=PH(st,/^[\r\n\t ]+/);Nt=Jt&&Jt[0]}zt==="application/xhtml+xml"&&(st=''+st+"");var ze=T?T.createHTML(st):st;if(jt===we)try{At=new f().parseFromString(ze,zt)}catch{}if(!At||!At.documentElement){At=S.createDocument(jt,"template",null);try{At.documentElement.innerHTML=Ft?"":ze}catch{}}var Pe=At.body||At.documentElement;return st&&Nt&&Pe.insertBefore(n.createTextNode(Nt),Pe.childNodes[0]||null),jt===we?L.call(At,U?"html":"body")[0]:U?At.documentElement:Pe},ue=function(st){return R.call(st.ownerDocument||st,st,l.SHOW_ELEMENT|l.SHOW_COMMENT|l.SHOW_TEXT,null,!1)},_a=function(st){return st instanceof d&&(typeof st.nodeName!="string"||typeof st.textContent!="string"||typeof st.removeChild!="function"||!(st.attributes instanceof h)||typeof st.removeAttribute!="function"||typeof st.setAttribute!="function"||typeof st.namespaceURI!="string"||typeof st.insertBefore!="function")},Hr=function(st){return Ha(s)==="object"?st instanceof s:st&&Ha(st)==="object"&&typeof st.nodeType=="number"&&typeof st.nodeName=="string"},Ie=function(st,At,Nt){!w[st]||FH(w[st],function(Jt){Jt.call(e,At,Nt,kt)})},oe=function(st){var At;if(Ie("beforeSanitizeElements",st,null),_a(st)||on(/[\u0080-\uFFFF]/,st.nodeName))return Wt(st),!0;var Nt=Et(st.nodeName);if(Ie("uponSanitizeElement",st,{tagName:Nt,allowedTags:$}),st.hasChildNodes()&&!Hr(st.firstElementChild)&&(!Hr(st.content)||!Hr(st.content.firstElementChild))&&on(/<[/\w]/g,st.innerHTML)&&on(/<[/\w]/g,st.textContent)||Nt==="select"&&on(/