mirror of
https://github.com/rtic-rs/rtic.git
synced 2024-11-25 21:19:35 +01:00
Merge #691
691: Basic cfg support, kind of, for Monotonics r=korken89 a=AfoHT - Enable at least masking out a Monotonic - Add example cfg-ing a Monotonic, showing limitations imposed by rtic-syntax - Update changelog The use case detailed in linked issue seems to be covered: Fixes #664 Co-authored-by: Henrik Tjäder <henrik@tjaders.com>
This commit is contained in:
commit
a601c6e449
8 changed files with 162 additions and 10 deletions
|
@ -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
|
||||
|
||||
|
|
0
ci/expected/cfg-monotonic.run
Normal file
0
ci/expected/cfg-monotonic.run
Normal file
121
examples/cfg-monotonic.rs
Normal file
121
examples/cfg-monotonic.rs
Normal file
|
@ -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!");
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -43,21 +43,28 @@ pub fn codegen(app: &App, analysis: &Analysis) -> Vec<TokenStream2> {
|
|||
}
|
||||
}
|
||||
|
||||
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
|
||||
|
|
|
@ -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,)*]);
|
||||
|
|
|
@ -13,7 +13,6 @@ pub fn codegen(app: &App, analysis: &Analysis, _extra: &Extra) -> Vec<TokenStrea
|
|||
// 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)]
|
||||
|
@ -56,6 +55,7 @@ pub fn codegen(app: &App, analysis: &Analysis, _extra: &Extra) -> Vec<TokenStrea
|
|||
let tq = util::tq_ident(&monotonic_name);
|
||||
let t = util::schedule_t_ident();
|
||||
let mono_type = &monotonic.ty;
|
||||
let cfgs = &monotonic.cfgs;
|
||||
let m_ident = util::monotonic_ident(&monotonic_name);
|
||||
|
||||
// Static variables and resource proxy
|
||||
|
@ -76,6 +76,7 @@ pub fn codegen(app: &App, analysis: &Analysis, _extra: &Extra) -> Vec<TokenStrea
|
|||
#[doc(hidden)]
|
||||
#[allow(non_camel_case_types)]
|
||||
#[allow(non_upper_case_globals)]
|
||||
#(#cfgs)*
|
||||
static #tq: rtic::RacyCell<#tq_ty> =
|
||||
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<TokenStrea
|
|||
#[doc(hidden)]
|
||||
#[allow(non_camel_case_types)]
|
||||
#[allow(non_upper_case_globals)]
|
||||
#(#cfgs)*
|
||||
static #mono: rtic::RacyCell<Option<#mono_type>> = rtic::RacyCell::new(None);
|
||||
));
|
||||
}
|
||||
|
@ -126,6 +128,7 @@ pub fn codegen(app: &App, analysis: &Analysis, _extra: &Extra) -> Vec<TokenStrea
|
|||
})
|
||||
.collect::<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<TokenStrea
|
|||
items.push(quote!(
|
||||
#[no_mangle]
|
||||
#[allow(non_snake_case)]
|
||||
#(#cfgs)*
|
||||
unsafe fn #bound_interrupt() {
|
||||
while let Some((task, index)) = rtic::export::interrupt::free(|_|
|
||||
if let Some(mono) = (&mut *#m_ident.get_mut()).as_mut() {
|
||||
|
|
Loading…
Reference in a new issue