Now with new monotonic trait and crate

This commit is contained in:
Emil Fresk 2021-02-18 19:30:59 +01:00
parent b57ef0bf9d
commit ebf2f058a4
12 changed files with 135 additions and 59 deletions

View file

@ -58,6 +58,7 @@ required-features = ["__v7"]
cortex-m = "0.7.0" cortex-m = "0.7.0"
cortex-m-rtic-macros = { path = "macros", version = "0.6.0-alpha.0" } cortex-m-rtic-macros = { path = "macros", version = "0.6.0-alpha.0" }
rtic-core = { git = "https://github.com/rtic-rs/rtic-core", branch = "new_monotonic" } rtic-core = { git = "https://github.com/rtic-rs/rtic-core", branch = "new_monotonic" }
rtic-monotonic = { git = "https://github.com/rtic-rs/rtic-monotonic", branch = "master" }
#rtic-core = "0.3.1" #rtic-core = "0.3.1"
heapless = "0.5.0" heapless = "0.5.0"
bare-metal = "1.0.0" bare-metal = "1.0.0"

View file

@ -104,13 +104,35 @@ pub fn app(app: &App, analysis: &Analysis, extra: &Extra) -> TokenStream2 {
)); ));
} }
let app_name = &app.name;
let app_path = quote! {crate::#app_name};
let monotonic_imports: Vec<_> = app let monotonic_imports: Vec<_> = app
.monotonics .monotonics
.iter() .iter()
.map(|(_, monotonic)| { .map(|(_, monotonic)| {
let name = &monotonic.ident; let name = &monotonic.ident;
let ty = &monotonic.ty; let ty = &monotonic.ty;
quote!(pub type #name = #ty;) let mangled_name = util::mangle_monotonic_type(&name.to_string());
let ident = util::monotonic_ident(&name.to_string());
quote! {
#[doc(hidden)]
pub type #mangled_name = #ty;
pub mod #name {
pub fn now() -> rtic::time::Instant<#app_path::#mangled_name> {
rtic::export::interrupt::free(|_| {
use rtic::Monotonic as _;
use rtic::time::Clock as _;
if let Ok(v) = unsafe{ (&*#app_path::#ident.as_ptr()).try_now() } {
v
} else {
unreachable!("Your monotonic is not infallible!")
}
})
}
}
}
}) })
.collect(); .collect();

View file

@ -69,7 +69,7 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> CodegenResult {
let app_path = quote! {crate::#app_name}; let app_path = quote! {crate::#app_name};
let locals_new = locals_new.iter(); let locals_new = locals_new.iter();
let call_init = Some( let call_init = Some(
quote!(let (late, monotonics) = #app_path::#name(#(#locals_new,)* #name::Context::new(core.into()));), quote!(let (late, mut monotonics) = #app_path::#name(#(#locals_new,)* #name::Context::new(core.into()));),
); );
root_init.push(module::codegen( root_init.push(module::codegen(

View file

@ -126,7 +126,7 @@ pub fn codegen(
.monotonics .monotonics
.iter() .iter()
.map(|(_, monotonic)| { .map(|(_, monotonic)| {
let mono = &monotonic.ident; let mono = util::mangle_monotonic_type(&monotonic.ident.to_string());
quote! {#app_path::#mono} quote! {#app_path::#mono}
}) })
.collect(); .collect();
@ -234,6 +234,7 @@ pub fn codegen(
let tq = util::tq_ident(&monotonic.ident.to_string()); let tq = util::tq_ident(&monotonic.ident.to_string());
let t = util::schedule_t_ident(); let t = util::schedule_t_ident();
let m = &monotonic.ident; let m = &monotonic.ident;
let m_mangled = util::mangle_monotonic_type(&monotonic.ident.to_string());
let m_isr = &monotonic.args.binds; let m_isr = &monotonic.args.binds;
let enum_ = util::interrupt_ident(); let enum_ = util::interrupt_ident();
@ -242,9 +243,10 @@ pub fn codegen(
items.push(quote!(pub use #m::spawn_at;)); items.push(quote!(pub use #m::spawn_at;));
} }
let (unmask, pend) = if &*m_isr.to_string() == "SysTick" { let (enable_interrupt, pend) = if &*m_isr.to_string() == "SysTick" {
( (
quote!(core::mem::transmute::<_, cortex_m::peripheral::SYST>(()).disable_interrupt()), quote!(core::mem::transmute::<_, cortex_m::peripheral::SYST>(())
.enable_interrupt()),
quote!(cortex_m::peripheral::SCB::set_pendst()), quote!(cortex_m::peripheral::SCB::set_pendst()),
) )
} else { } else {
@ -263,16 +265,16 @@ pub fn codegen(
#(,#args)* #(,#args)*
) -> Result<(), #ty> ) -> Result<(), #ty>
where D: rtic::time::duration::Duration + rtic::time::fixed_point::FixedPoint, where D: rtic::time::duration::Duration + rtic::time::fixed_point::FixedPoint,
D::T: Into<<#app_path::#m as rtic::time::Clock>::T>, D::T: Into<<#app_path::#m_mangled as rtic::time::Clock>::T>,
{ {
let instant = <#app_path::#m as rtic::Monotonic>::now(); let instant = #app_path::#m::now();
spawn_at(instant + duration, #(,#untupled)*) spawn_at(instant + duration, #(,#untupled)*)
} }
#(#cfgs)* #(#cfgs)*
pub fn spawn_at( pub fn spawn_at(
instant: rtic::time::Instant<#app_path::#m> instant: rtic::time::Instant<#app_path::#m_mangled>
#(,#args)* #(,#args)*
) -> Result<(), #ty> { ) -> Result<(), #ty> {
unsafe { unsafe {
@ -296,7 +298,7 @@ pub fn codegen(
rtic::export::interrupt::free(|_| #app_path::#tq.enqueue_unchecked( rtic::export::interrupt::free(|_| #app_path::#tq.enqueue_unchecked(
nr, nr,
|| #unmask, || #enable_interrupt,
|| #pend, || #pend,
)); ));

View file

@ -1,6 +1,7 @@
use proc_macro2::TokenStream as TokenStream2; use proc_macro2::{Span, TokenStream as TokenStream2};
use quote::quote; use quote::quote;
use rtic_syntax::ast::App; use rtic_syntax::ast::App;
use syn::Index;
use crate::{analyze::Analysis, codegen::util}; use crate::{analyze::Analysis, codegen::util};
@ -25,12 +26,17 @@ pub fn codegen(app: &App, analysis: &Analysis) -> Vec<TokenStream2> {
} }
} }
for (monotonic, _) in app.monotonics.iter() { for (i, (monotonic, _)) in app.monotonics.iter().enumerate() {
stmts.push(quote!(#monotonic::reset();)); let idx = Index {
} index: i as u32,
span: Span::call_site(),
};
stmts.push(quote!(monotonics.#idx.reset();));
// Forget the monotonics so they won't be dropped. // Store the monotonic
stmts.push(quote!(core::mem::forget(monotonics);)); let name = util::monotonic_ident(&monotonic.to_string());
stmts.push(quote!(#name.as_mut_ptr().write(monotonics.#idx);));
}
// Enable the interrupts -- this completes the `init`-ialization phase // Enable the interrupts -- this completes the `init`-ialization phase
stmts.push(quote!(rtic::export::interrupt::enable();)); stmts.push(quote!(rtic::export::interrupt::enable();));

View file

@ -77,14 +77,17 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec<TokenStream
} }
// Initialize monotonic's interrupts // Initialize monotonic's interrupts
for (priority, name) in app for (ident, priority, name) in app
.monotonics .monotonics
.iter() .iter()
.map(|(_, monotonic)| (&monotonic.args.priority, &monotonic.args.binds)) .map(|(ident, monotonic)| (ident, &monotonic.args.priority, &monotonic.args.binds))
{ {
// Compile time assert that this priority is supported by the device // Compile time assert that this priority is supported by the device
stmts.push(quote!(let _ = [(); ((1 << #nvic_prio_bits) - #priority as usize)];)); stmts.push(quote!(let _ = [(); ((1 << #nvic_prio_bits) - #priority as usize)];));
let app_name = &app.name;
let app_path = quote! {crate::#app_name};
let mono_type = util::mangle_monotonic_type(&ident.to_string());
if &*name.to_string() == "SysTick" { if &*name.to_string() == "SysTick" {
stmts.push(quote!( stmts.push(quote!(
@ -92,6 +95,12 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec<TokenStream
rtic::export::SystemHandler::SysTick, rtic::export::SystemHandler::SysTick,
rtic::export::logical2hw(#priority, #nvic_prio_bits), rtic::export::logical2hw(#priority, #nvic_prio_bits),
); );
// Always enable monotonic interrupts if they should never be off
if !#app_path::#mono_type::DISABLE_INTERRUPT_ON_EMPTY_QUEUE {
core::mem::transmute::<_, cortex_m::peripheral::SYST>(())
.enable_interrupt();
}
)); ));
} else { } else {
// NOTE this also checks that the interrupt exists in the `Interrupt` enumeration // NOTE this also checks that the interrupt exists in the `Interrupt` enumeration
@ -101,10 +110,13 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec<TokenStream
#rt_err::#interrupt::#name, #rt_err::#interrupt::#name,
rtic::export::logical2hw(#priority, #nvic_prio_bits), rtic::export::logical2hw(#priority, #nvic_prio_bits),
); );
// Always enable monotonic interrupts if they should never be off
if !#app_path::#mono_type::DISABLE_INTERRUPT_ON_EMPTY_QUEUE {
rtic::export::NVIC::unmask(#app_path::#rt_err::#interrupt::#name);
}
)); ));
} }
// NOTE we do not unmask the interrupt as this is part of the monotonic to keep track of
} }
// If there's no user `#[idle]` then optimize returning from interrupt handlers // If there's no user `#[idle]` then optimize returning from interrupt handlers

View file

@ -62,7 +62,7 @@ pub fn codegen(
for (_, monotonic) in &app.monotonics { for (_, monotonic) in &app.monotonics {
let instants = util::monotonic_instants_ident(name, &monotonic.ident); let instants = util::monotonic_instants_ident(name, &monotonic.ident);
let m = &monotonic.ident; let m = util::mangle_monotonic_type(&monotonic.ident.to_string());
let uninit = mk_uninit(); let uninit = mk_uninit();
mod_app.push(quote!( mod_app.push(quote!(

View file

@ -42,7 +42,10 @@ pub fn codegen(app: &App, analysis: &Analysis, _extra: &Extra) -> Vec<TokenStrea
let monotonic_name = monotonic.ident.to_string(); let monotonic_name = monotonic.ident.to_string();
let tq = util::tq_ident(&monotonic_name); let tq = util::tq_ident(&monotonic_name);
let t = util::schedule_t_ident(); let t = util::schedule_t_ident();
let m = &monotonic.ident; let m = util::mangle_monotonic_type(&monotonic_name);
let m_ident = util::monotonic_ident(&monotonic_name);
let app_name = &app.name;
let app_path = quote! {crate::#app_name};
// Static variables and resource proxy // Static variables and resource proxy
{ {
@ -63,6 +66,15 @@ pub fn codegen(app: &App, analysis: &Analysis, _extra: &Extra) -> Vec<TokenStrea
) )
); );
)); ));
let mono = util::monotonic_ident(&monotonic_name);
let doc = &format!("Storage for {}", monotonic_name);
let mono_ty = quote!(core::mem::MaybeUninit<#m>);
items.push(quote!(
#[doc = #doc]
static mut #mono: #mono_ty = core::mem::MaybeUninit::uninit();
));
} }
// Timer queue handler // Timer queue handler
@ -100,8 +112,8 @@ pub fn codegen(app: &App, analysis: &Analysis, _extra: &Extra) -> Vec<TokenStrea
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let bound_interrupt = &monotonic.args.binds; let bound_interrupt = &monotonic.args.binds;
let enable_isr = if &*bound_interrupt.to_string() == "SysTick" { let disable_isr = if &*bound_interrupt.to_string() == "SysTick" {
quote!(core::mem::transmute::<_, cortex_m::peripheral::SYST>(()).enable_interrupt()) quote!(core::mem::transmute::<_, cortex_m::peripheral::SYST>(()).disable_interrupt())
} else { } else {
quote!(rtic::export::NVIC::mask(#rt_err::#enum_::#bound_interrupt)) quote!(rtic::export::NVIC::mask(#rt_err::#enum_::#bound_interrupt))
}; };
@ -111,7 +123,8 @@ pub fn codegen(app: &App, analysis: &Analysis, _extra: &Extra) -> Vec<TokenStrea
#[allow(non_snake_case)] #[allow(non_snake_case)]
unsafe fn #bound_interrupt() { unsafe fn #bound_interrupt() {
while let Some((task, index)) = rtic::export::interrupt::free(|_| #tq.dequeue( while let Some((task, index)) = rtic::export::interrupt::free(|_| #tq.dequeue(
|| #enable_isr, || #disable_isr,
&mut *#app_path::#m_ident.as_mut_ptr(),
)) ))
{ {
match task { match task {

View file

@ -239,6 +239,16 @@ pub fn tq_ident(name: &str) -> Ident {
Ident::new(&format!("TQ_{}", name), Span::call_site()) Ident::new(&format!("TQ_{}", name), Span::call_site())
} }
/// Generates an identifier for monotonic timer storage
pub fn monotonic_ident(name: &str) -> Ident {
Ident::new(&format!("MONOTONIC_STORAGE_{}", name), Span::call_site())
}
/// Generates an identifier for monotonic timer storage
pub fn mangle_monotonic_type(name: &str) -> Ident {
Ident::new(&format!("MonotonicMangled{}", name), Span::call_site())
}
/// The name to get better RT flag errors /// The name to get better RT flag errors
pub fn rt_err_ident() -> Ident { pub fn rt_err_ident() -> Ident {
Ident::new( Ident::new(

View file

@ -16,7 +16,7 @@ pub use cortex_m::{
use heapless::spsc::SingleCore; use heapless::spsc::SingleCore;
pub use heapless::{consts, i::Queue as iQueue, spsc::Queue}; pub use heapless::{consts, i::Queue as iQueue, spsc::Queue};
pub use heapless::{i::BinaryHeap as iBinaryHeap, BinaryHeap}; pub use heapless::{i::BinaryHeap as iBinaryHeap, BinaryHeap};
pub use rtic_core::monotonic::Monotonic; pub use rtic_monotonic as monotonic;
pub type SCFQ<N> = Queue<u8, N, u8, SingleCore>; pub type SCFQ<N> = Queue<u8, N, u8, SingleCore>;
pub type SCRQ<T, N> = Queue<(T, u8), N, u8, SingleCore>; pub type SCRQ<T, N> = Queue<(T, u8), N, u8, SingleCore>;
@ -116,7 +116,7 @@ where
#[inline(always)] #[inline(always)]
pub fn assert_monotonic<T>() pub fn assert_monotonic<T>()
where where
T: Monotonic, T: monotonic::Monotonic,
{ {
} }

View file

@ -37,10 +37,8 @@
use cortex_m::{interrupt::InterruptNumber, peripheral::NVIC}; use cortex_m::{interrupt::InterruptNumber, peripheral::NVIC};
pub use cortex_m_rtic_macros::app; pub use cortex_m_rtic_macros::app;
pub use rtic_core::{ pub use rtic_core::{prelude as mutex_prelude, Exclusive, Mutex};
monotonic::{self, embedded_time as time, Monotonic}, pub use rtic_monotonic::{self, embedded_time as time, Monotonic};
prelude as mutex_prelude, Exclusive, Mutex,
};
#[doc(hidden)] #[doc(hidden)]
pub mod export; pub mod export;

View file

@ -1,4 +1,7 @@
use crate::{time::Instant, Monotonic}; use crate::{
time::{Clock, Instant},
Monotonic,
};
use core::cmp::Ordering; use core::cmp::Ordering;
use heapless::{binary_heap::Min, ArrayLength, BinaryHeap}; use heapless::{binary_heap::Min, ArrayLength, BinaryHeap};
@ -42,7 +45,7 @@ where
}) })
.unwrap_or(true); .unwrap_or(true);
if if_heap_max_greater_than_nr { if if_heap_max_greater_than_nr {
if is_empty { if Mono::DISABLE_INTERRUPT_ON_EMPTY_QUEUE && is_empty {
// mem::transmute::<_, SYST>(()).enable_interrupt(); // mem::transmute::<_, SYST>(()).enable_interrupt();
enable_interrupt(); enable_interrupt();
} }
@ -61,32 +64,40 @@ where
self.0.is_empty() self.0.is_empty()
} }
#[inline]
fn unwrapper<T, E>(val: Result<T, E>) -> T {
if let Ok(v) = val {
v
} else {
unreachable!("Your monotonic is not infallible")
}
}
/// Dequeue a task from the TimerQueue /// Dequeue a task from the TimerQueue
#[inline] #[inline]
pub fn dequeue<F>(&mut self, disable_interrupt: F) -> Option<(Task, u8)> pub fn dequeue<F>(&mut self, disable_interrupt: F, mono: &mut Mono) -> Option<(Task, u8)>
where where
F: FnOnce(), F: FnOnce(),
{ {
unsafe { mono.clear_compare_flag();
Mono::clear_compare();
if let Some(instant) = self.0.peek().map(|p| p.instant) { if let Some(instant) = self.0.peek().map(|p| p.instant) {
if instant < Mono::now() { if instant < Self::unwrapper(Clock::try_now(mono)) {
// task became ready // task became ready
let nr = self.0.pop_unchecked(); let nr = unsafe { self.0.pop_unchecked() };
Some((nr.task, nr.index)) Some((nr.task, nr.index))
} else { } else {
// TODO: Fix this hack... // TODO: Fix this hack...
// Extract the compare time. // Extract the compare time.
Mono::set_compare(*instant.duration_since_epoch().integer()); mono.set_compare(*instant.duration_since_epoch().integer());
// Double check that the instant we set is really in the future, else // 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 // 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 // read of now to the set of the compare, the time can overflow. This is to
// guard against this. // guard against this.
if instant < Mono::now() { if instant < Self::unwrapper(Clock::try_now(mono)) {
let nr = self.0.pop_unchecked(); let nr = unsafe { self.0.pop_unchecked() };
Some((nr.task, nr.index)) Some((nr.task, nr.index))
} else { } else {
@ -95,12 +106,13 @@ where
} }
} else { } else {
// The queue is empty, disable the interrupt. // The queue is empty, disable the interrupt.
if Mono::DISABLE_INTERRUPT_ON_EMPTY_QUEUE {
disable_interrupt(); disable_interrupt();
}
None None
} }
} }
}
} }
pub struct NotReady<Mono, Task> pub struct NotReady<Mono, Task>