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 1/3] 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 2/3] 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 3/3] 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