diff options
-rw-r--r-- | Cargo.toml | 3 | ||||
-rw-r--r-- | examples/test_new_monotonic.rs | 25 | ||||
-rw-r--r-- | macros/Cargo.toml | 2 | ||||
-rw-r--r-- | macros/src/check.rs | 14 | ||||
-rw-r--r-- | macros/src/codegen.rs | 24 | ||||
-rw-r--r-- | macros/src/codegen/assertions.rs | 8 | ||||
-rw-r--r-- | macros/src/codegen/dispatchers.rs | 19 | ||||
-rw-r--r-- | macros/src/codegen/hardware_tasks.rs | 13 | ||||
-rw-r--r-- | macros/src/codegen/init.rs | 35 | ||||
-rw-r--r-- | macros/src/codegen/module.rs | 220 | ||||
-rw-r--r-- | macros/src/codegen/post_init.rs | 7 | ||||
-rw-r--r-- | macros/src/codegen/pre_init.rs | 44 | ||||
-rw-r--r-- | macros/src/codegen/software_tasks.rs | 10 | ||||
-rw-r--r-- | macros/src/codegen/timer_queue.rs | 41 | ||||
-rw-r--r-- | macros/src/codegen/util.rs | 32 | ||||
-rw-r--r-- | macros/src/lib.rs | 2 | ||||
-rw-r--r-- | src/export.rs | 8 | ||||
-rw-r--r-- | src/lib.rs | 117 | ||||
-rw-r--r-- | src/tq.rs | 145 |
19 files changed, 356 insertions, 413 deletions
@@ -57,7 +57,8 @@ required-features = ["__v7"] [dependencies] cortex-m = "0.7.0" cortex-m-rtic-macros = { path = "macros", version = "0.6.0-alpha.0" } -rtic-core = "0.3.1" +rtic-core = { git = "https://github.com/rtic-rs/rtic-core", branch = "new_monotonic" } +#rtic-core = "0.3.1" heapless = "0.5.0" bare-metal = "1.0.0" diff --git a/examples/test_new_monotonic.rs b/examples/test_new_monotonic.rs new file mode 100644 index 00000000..67883465 --- /dev/null +++ b/examples/test_new_monotonic.rs @@ -0,0 +1,25 @@ +//! examples/test_new_monotonic.rs + +#![no_main] +#![no_std] + +use panic_semihosting as _; // panic handler +use rtic::app; + +#[app(device = lm3s6965, dispatchers = [UART])] +mod app { + #[monotonic(binds = SysTick)] + type MyMono1 = hal::Mono1; + + #[monotonic(binds = SomeISR2, default = true)] + type MyMono2 = hal::Mono2; + + #[init] + fn init(cx: init::Context) -> (init::LateResources, init::Monotonics) {} + + #[task] + fn task1(_: task1::Context) {} + + #[task] + fn task2(_: task2::Context) {} +} diff --git a/macros/Cargo.toml b/macros/Cargo.toml index 3af48c76..6996bef4 100644 --- a/macros/Cargo.toml +++ b/macros/Cargo.toml @@ -22,5 +22,5 @@ proc-macro2 = "1" proc-macro-error = "1" quote = "1" syn = "1" -rtic-syntax = { git = "https://github.com/rtic-rs/rtic-syntax", branch = "master", version = "0.5.0-alpha.0" } +rtic-syntax = { git = "https://github.com/rtic-rs/rtic-syntax", branch = "new_monotonic", version = "0.5.0-alpha.0" } diff --git a/macros/src/check.rs b/macros/src/check.rs index e3161cb9..374fcedd 100644 --- a/macros/src/check.rs +++ b/macros/src/check.rs @@ -6,7 +6,6 @@ use syn::{parse, Path}; pub struct Extra { pub device: Path, - pub monotonic: Option<Path>, pub peripherals: bool, } @@ -62,18 +61,6 @@ pub fn app(app: &App, _analysis: &Analysis) -> parse::Result<Extra> { for (name, task) in &app.hardware_tasks { let name_s = task.args.binds.to_string(); match &*name_s { - "SysTick" => { - // If the timer queue is used, then SysTick is unavailable - if app.args.monotonic.is_some() { - return Err(parse::Error::new( - name.span(), - "this exception can't be used because it's being used by the runtime", - )); - } else { - // OK - } - } - "NonMaskableInt" | "HardFault" => { return Err(parse::Error::new( name.span(), @@ -88,7 +75,6 @@ pub fn app(app: &App, _analysis: &Analysis) -> parse::Result<Extra> { if let Some(device) = app.args.device.clone() { Ok(Extra { device, - monotonic: app.args.monotonic.clone(), peripherals: app.args.peripherals, }) } else { diff --git a/macros/src/codegen.rs b/macros/src/codegen.rs index 3cddf570..bb8aa4e7 100644 --- a/macros/src/codegen.rs +++ b/macros/src/codegen.rs @@ -27,13 +27,13 @@ pub fn app(app: &App, analysis: &Analysis, extra: &Extra) -> TokenStream2 { let mut user = vec![]; // Generate the `main` function - let assertion_stmts = assertions::codegen(analysis); + 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, extra); let (mod_app_init, root_init, user_init, call_init) = init::codegen(app, analysis, extra); - let post_init_stmts = post_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); @@ -61,8 +61,6 @@ pub fn app(app: &App, analysis: &Analysis, extra: &Extra) -> TokenStream2 { use super::*; #[no_mangle] unsafe extern "C" fn #main() -> ! { - let _TODO: () = (); - #(#assertion_stmts)* #(#pre_init_stmts)* @@ -106,11 +104,25 @@ pub fn app(app: &App, analysis: &Analysis, extra: &Extra) -> TokenStream2 { )); } + let monotonic_imports: Vec<_> = app + .monotonics + .iter() + .map(|(_, monotonic)| { + let name = &monotonic.ident; + let ty = &monotonic.ty; + quote!(pub type #name = #ty;) + }) + .collect(); + + let rt_err = util::rt_err_ident(); + quote!( /// Implementation details pub mod #name { /// Always include the device crate which contains the vector table - use #device as you_must_enable_the_rt_feature_for_the_pac_in_your_cargo_toml; + use #device as #rt_err; + + #(#monotonic_imports)* #(#user_imports)* diff --git a/macros/src/codegen/assertions.rs b/macros/src/codegen/assertions.rs index 4d9aae47..a8a4491b 100644 --- a/macros/src/codegen/assertions.rs +++ b/macros/src/codegen/assertions.rs @@ -2,9 +2,10 @@ use proc_macro2::TokenStream as TokenStream2; use quote::quote; use crate::analyze::Analysis; +use rtic_syntax::ast::App; /// Generates compile-time assertions that check that types implement the `Send` / `Sync` traits -pub fn codegen(analysis: &Analysis) -> Vec<TokenStream2> { +pub fn codegen(app: &App, analysis: &Analysis) -> Vec<TokenStream2> { let mut stmts = vec![]; for ty in &analysis.send_types { @@ -15,5 +16,10 @@ pub fn codegen(analysis: &Analysis) -> Vec<TokenStream2> { 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>();)); + } + stmts } diff --git a/macros/src/codegen/dispatchers.rs b/macros/src/codegen/dispatchers.rs index a6c695f1..d3adee0d 100644 --- a/macros/src/codegen/dispatchers.rs +++ b/macros/src/codegen/dispatchers.rs @@ -5,7 +5,7 @@ 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<TokenStream2> { +pub fn codegen(app: &App, analysis: &Analysis, _extra: &Extra) -> Vec<TokenStream2> { let mut items = vec![]; let interrupts = &analysis.interrupts; @@ -70,20 +70,6 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec<TokenStream let inputs = util::inputs_ident(name); let (_, tupled, pats, _) = util::regroup_inputs(&task.inputs); - let (let_instant, instant) = if extra.monotonic.is_some() { - let instants = util::instants_ident(name); - - ( - quote!( - let instant = - #instants.get_unchecked(usize::from(index)).as_ptr().read(); - ), - quote!(, instant), - ) - } else { - (quote!(), quote!()) - }; - let locals_new = if task.locals.is_empty() { quote!() } else { @@ -97,12 +83,11 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec<TokenStream #t::#name => { let #tupled = #inputs.get_unchecked(usize::from(index)).as_ptr().read(); - #let_instant #fq.split().0.enqueue_unchecked(index); let priority = &rtic::export::Priority::new(PRIORITY); #app_path::#name( #locals_new - #name::Context::new(priority #instant) + #name::Context::new(priority) #(,#pats)* ) } diff --git a/macros/src/codegen/hardware_tasks.rs b/macros/src/codegen/hardware_tasks.rs index 6930d3e0..4a1d7492 100644 --- a/macros/src/codegen/hardware_tasks.rs +++ b/macros/src/codegen/hardware_tasks.rs @@ -29,15 +29,6 @@ pub fn codegen( let mut user_tasks = vec![]; for (name, task) in &app.hardware_tasks { - let (let_instant, instant) = if let Some(ref m) = extra.monotonic { - ( - Some(quote!(let instant = <#m as rtic::Monotonic>::now();)), - Some(quote!(, instant)), - ) - } else { - (None, None) - }; - let locals_new = if task.locals.is_empty() { quote!() } else { @@ -55,12 +46,10 @@ pub fn codegen( unsafe fn #symbol() { const PRIORITY: u8 = #priority; - #let_instant - rtic::export::run(PRIORITY, || { #app_path::#name( #locals_new - #name::Context::new(&rtic::export::Priority::new(PRIORITY) #instant) + #name::Context::new(&rtic::export::Priority::new(PRIORITY)) ) }); } diff --git a/macros/src/codegen/init.rs b/macros/src/codegen/init.rs index 6376ce31..66c3bc4e 100644 --- a/macros/src/codegen/init.rs +++ b/macros/src/codegen/init.rs @@ -5,7 +5,7 @@ use rtic_syntax::{ast::App, Context}; use crate::{ analyze::Analysis, check::Extra, - codegen::{locals, module, resources_struct, util}, + codegen::{locals, module, resources_struct}, }; type CodegenResult = ( @@ -32,32 +32,6 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> CodegenResult { let mut root_init = vec![]; - let late_fields = analysis - .late_resources - .iter() - .flat_map(|resources| { - resources.iter().map(|name| { - let ty = &app.late_resources[name].ty; - let cfgs = &app.late_resources[name].cfgs; - - quote!( - #(#cfgs)* - pub #name: #ty - ) - }) - }) - .collect::<Vec<_>>(); - - let late_resources = util::late_resources_ident(&name); - - root_init.push(quote!( - /// Resources initialized at runtime - #[allow(non_snake_case)] - pub struct #late_resources { - #(#late_fields),* - } - )); - let mut locals_pat = None; let mut locals_new = None; if !init.locals.is_empty() { @@ -72,10 +46,13 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> CodegenResult { let attrs = &init.attrs; let stmts = &init.stmts; let locals_pat = locals_pat.iter(); + + let user_init_return = quote! {#name::LateResources, #name::Monotonics}; + let user_init = Some(quote!( #(#attrs)* #[allow(non_snake_case)] - fn #name(#(#locals_pat,)* #context: #name::Context) -> #name::LateResources { + fn #name(#(#locals_pat,)* #context: #name::Context) -> (#user_init_return) { #(#stmts)* } )); @@ -92,7 +69,7 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> CodegenResult { let app_path = quote! {crate::#app_name}; let locals_new = locals_new.iter(); let call_init = Some( - quote!(let late = #app_path::#name(#(#locals_new,)* #name::Context::new(core.into()));), + quote!(let (late, monotonics) = #app_path::#name(#(#locals_new,)* #name::Context::new(core.into()));), ); root_init.push(module::codegen( diff --git a/macros/src/codegen/module.rs b/macros/src/codegen/module.rs index 2ff4801e..93fbeaef 100644 --- a/macros/src/codegen/module.rs +++ b/macros/src/codegen/module.rs @@ -18,29 +18,16 @@ pub fn codegen( let mut task_cfgs = vec![]; let name = ctxt.ident(app); + let app_name = &app.name; + let app_path = quote! {crate::#app_name}; - let mut needs_instant = false; let mut lt = None; match ctxt { Context::Init => { - if let Some(m) = &extra.monotonic { - fields.push(quote!( - /// System start time = `Instant(0 /* cycles */)` - pub start: <#m as rtic::Monotonic>::Instant - )); - - values.push(quote!(start: <#m as rtic::Monotonic>::zero())); - - fields.push(quote!( - /// Core (Cortex-M) peripherals minus the SysTick - pub core: rtic::Peripherals - )); - } else { - fields.push(quote!( - /// Core (Cortex-M) peripherals - pub core: rtic::export::Peripherals - )); - } + fields.push(quote!( + /// Core (Cortex-M) peripherals + pub core: rtic::export::Peripherals + )); if extra.peripherals { let device = &extra.device; @@ -67,29 +54,11 @@ pub fn codegen( Context::Idle => {} Context::HardwareTask(..) => { - if let Some(m) = &extra.monotonic { - fields.push(quote!( - /// Time at which this handler started executing - pub start: <#m as rtic::Monotonic>::Instant - )); - - values.push(quote!(start: instant)); - - needs_instant = true; - } + // None for now. } Context::SoftwareTask(..) => { - if let Some(m) = &extra.monotonic { - fields.push(quote!( - /// The time at which this task was scheduled to run - pub scheduled: <#m as rtic::Monotonic>::Instant - )); - - values.push(quote!(scheduled: instant)); - - needs_instant = true; - } + // None for now. } } @@ -129,12 +98,45 @@ pub fn codegen( } if let Context::Init = ctxt { - let init = &app.inits.first().unwrap(); - let late_resources = util::late_resources_ident(&init.name); + let late_fields = analysis + .late_resources + .iter() + .flat_map(|resources| { + resources.iter().map(|name| { + let ty = &app.late_resources[name].ty; + let cfgs = &app.late_resources[name].cfgs; + + quote!( + #(#cfgs)* + pub #name: #ty + ) + }) + }) + .collect::<Vec<_>>(); items.push(quote!( - #[doc(inline)] - pub use super::#late_resources as LateResources; + /// Resources initialized at runtime + #[allow(non_snake_case)] + pub struct LateResources { + #(#late_fields),* + } + )); + + let monotonic_types: Vec<_> = app + .monotonics + .iter() + .map(|(_, monotonic)| { + let mono = &monotonic.ident; + quote! {#app_path::#mono} + }) + .collect(); + + items.push(quote!( + /// Monotonics used by the system + #[allow(non_snake_case)] + pub struct Monotonics( + #(pub #monotonic_types),* + ); )); } @@ -146,11 +148,7 @@ pub fn codegen( }; let core = if ctxt.is_init() { - if extra.monotonic.is_some() { - Some(quote!(core: rtic::Peripherals,)) - } else { - Some(quote!(core: rtic::export::Peripherals,)) - } + Some(quote!(core: rtic::export::Peripherals,)) } else { None }; @@ -161,14 +159,6 @@ pub fn codegen( Some(quote!(priority: &#lt rtic::export::Priority)) }; - let instant = if needs_instant { - let m = extra.monotonic.clone().expect("RTIC-ICE: UNREACHABLE"); - - Some(quote!(, instant: <#m as rtic::Monotonic>::Instant)) - } else { - None - }; - items.push(quote!( /// Execution context pub struct Context<#lt> { @@ -177,7 +167,7 @@ pub fn codegen( impl<#lt> Context<#lt> { #[inline(always)] - pub unsafe fn new(#core #priority #instant) -> Self { + pub unsafe fn new(#core #priority) -> Self { Context { #(#values,)* } @@ -195,16 +185,13 @@ pub fn codegen( 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, 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 app_name = &app.name; - let app_path = quote! {crate::#app_name}; - let device = &extra.device; let enum_ = util::interrupt_ident(); let interrupt = &analysis @@ -217,10 +204,6 @@ pub fn codegen( items.push(quote!( #(#cfgs)* pub fn spawn(#(#args,)*) -> Result<(), #ty> { - // #let_instant // do we need it? - use rtic::Mutex as _; - use rtic::mutex_prelude::*; - let input = #tupled; unsafe { @@ -245,45 +228,82 @@ pub fn codegen( })); // Schedule caller - if let Some(m) = &extra.monotonic { - let instants = util::instants_ident(name); + for (_, monotonic) in &app.monotonics { + let instants = util::monotonic_instants_ident(name, &monotonic.ident); - let tq = util::tq_ident(); + let tq = util::tq_ident(&monotonic.ident.to_string()); let t = util::schedule_t_ident(); + let m = &monotonic.ident; + let m_isr = &monotonic.args.binds; + let enum_ = util::interrupt_ident(); + + if monotonic.args.default { + items.push(quote!(pub use #m::spawn_after;)); + items.push(quote!(pub use #m::spawn_at;)); + } + + let (unmask, pend) = if &*m_isr.to_string() == "SysTick" { + ( + quote!(core::mem::transmute::<_, cortex_m::peripheral::SYST>(()).disable_interrupt()), + quote!(cortex_m::peripheral::SCB::set_pendst()), + ) + } else { + let rt_err = util::rt_err_ident(); + ( + quote!(rtic::export::NVIC::unmask(#app_path::#rt_err::#enum_::#m_isr)), + quote!(rtic::pend(#app_path::#rt_err::#enum_::#m_isr)), + ) + }; items.push(quote!( - #(#cfgs)* - pub fn schedule( - instant: <#m as rtic::Monotonic>::Instant - #(,#args)* - ) -> Result<(), #ty> { - unsafe { - use rtic::Mutex as _; - use rtic::mutex_prelude::*; - - let input = #tupled; - if let Some(index) = rtic::export::interrupt::free(|_| #app_path::#fq.dequeue()) { - #app_path::#inputs - .get_unchecked_mut(usize::from(index)) - .as_mut_ptr() - .write(input); - - #app_path::#instants - .get_unchecked_mut(usize::from(index)) - .as_mut_ptr() - .write(instant); - - let nr = rtic::export::NotReady { - instant, - index, - task: #app_path::#t::#name, - }; - - rtic::export::interrupt::free(|_| #app_path::#tq.enqueue_unchecked(nr)); - - Ok(()) - } else { - Err(input) + pub mod #m { + #(#cfgs)* + pub fn spawn_after<D>( + duration: D, + #(,#args)* + ) -> Result<(), #ty> + where D: rtic::time::duration::Duration + rtic::time::fixed_point::FixedPoint, + D::T: Into<<#app_path::#m as rtic::time::Clock>::T>, + { + let instant = <#app_path::#m as rtic::Monotonic>::now(); + + spawn_at(instant + duration, #(,#untupled)*) + } + + #(#cfgs)* + pub fn spawn_at( + instant: rtic::time::Instant<#app_path::#m> + #(,#args)* + ) -> Result<(), #ty> { + unsafe { + let input = #tupled; + if let Some(index) = rtic::export::interrupt::free(|_| #app_path::#fq.dequeue()) { + #app_path::#inputs + .get_unchecked_mut(usize::from(index)) + .as_mut_ptr() + .write(input); + + #app_path::#instants + .get_unchecked_mut(usize::from(index)) + .as_mut_ptr() + .write(instant); + + let nr = rtic::export::NotReady { + instant, + index, + task: #app_path::#t::#name, + }; + + rtic::export::interrupt::free(|_| #app_path::#tq.enqueue_unchecked( + nr, + || #unmask, + || #pend, + )); + + Ok(()) + } else { + Err(input) + } } } })); diff --git a/macros/src/codegen/post_init.rs b/macros/src/codegen/post_init.rs index 5545944d..9268e040 100644 --- a/macros/src/codegen/post_init.rs +++ b/macros/src/codegen/post_init.rs @@ -25,6 +25,13 @@ pub fn codegen(app: &App, analysis: &Analysis) -> Vec<TokenStream2> { } } + for (monotonic, _) in app.monotonics.iter() { + stmts.push(quote!(#monotonic::reset();)); + } + + // Forget the monotonics so they won't be dropped. + stmts.push(quote!(core::mem::forget(monotonics);)); + // 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 969de84a..e7b1b03b 100644 --- a/macros/src/codegen/pre_init.rs +++ b/macros/src/codegen/pre_init.rs @@ -8,6 +8,8 @@ use crate::{analyze::Analysis, check::Extra, codegen::util}; pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec<TokenStream2> { let mut stmts = vec![]; + let rt_err = util::rt_err_ident(); + // Disable interrupts -- `init` must run with interrupts disabled stmts.push(quote!(rtic::export::interrupt::disable();)); @@ -47,14 +49,14 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec<TokenStream let interrupt = util::interrupt_ident(); stmts.push(quote!( core.NVIC.set_priority( - you_must_enable_the_rt_feature_for_the_pac_in_your_cargo_toml::#interrupt::#name, + #rt_err::#interrupt::#name, rtic::export::logical2hw(#priority, #nvic_prio_bits), ); )); // NOTE unmask the interrupt *after* setting its priority: changing the priority of a pended // interrupt is implementation defined - stmts.push(quote!(rtic::export::NVIC::unmask(you_must_enable_the_rt_feature_for_the_pac_in_your_cargo_toml::#interrupt::#name);)); + stmts.push(quote!(rtic::export::NVIC::unmask(#rt_err::#interrupt::#name);)); } // Set exception priorities @@ -74,23 +76,35 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec<TokenStream );)); } - // Initialize the SysTick if there exist a TimerQueue - if extra.monotonic.is_some() { - let priority = analysis.channels.keys().max().unwrap(); - + // Initialize monotonic's interrupts + for (priority, name) in app + .monotonics + .iter() + .map(|(_, monotonic)| (&monotonic.args.priority, &monotonic.args.binds)) + { // 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!(core.SCB.set_priority( - rtic::export::SystemHandler::SysTick, - rtic::export::logical2hw(#priority, #nvic_prio_bits), - );)); - stmts.push(quote!( - core.SYST.set_clock_source(rtic::export::SystClkSource::Core); - core.SYST.enable_counter(); - core.DCB.enable_trace(); - )); + if &*name.to_string() == "SysTick" { + stmts.push(quote!( + core.SCB.set_priority( + rtic::export::SystemHandler::SysTick, + rtic::export::logical2hw(#priority, #nvic_prio_bits), + ); + )); + } else { + // NOTE this also checks that the interrupt exists in the `Interrupt` enumeration + let interrupt = util::interrupt_ident(); + stmts.push(quote!( + core.NVIC.set_priority( + #rt_err::#interrupt::#name, + rtic::export::logical2hw(#priority, #nvic_prio_bits), + ); + )); + } + + // 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 diff --git a/macros/src/codegen/software_tasks.rs b/macros/src/codegen/software_tasks.rs index 9cb5f7a9..53de50b9 100644 --- a/macros/src/codegen/software_tasks.rs +++ b/macros/src/codegen/software_tasks.rs @@ -57,15 +57,19 @@ pub fn codegen( .map(|_| quote!(core::mem::MaybeUninit::uninit())) .collect::<Vec<_>>(); - if let Some(m) = &extra.monotonic { - let instants = util::instants_ident(name); + let app_name = &app.name; + let app_path = quote! {crate::#app_name}; + + for (_, monotonic) in &app.monotonics { + let instants = util::monotonic_instants_ident(name, &monotonic.ident); + let m = &monotonic.ident; let uninit = mk_uninit(); mod_app.push(quote!( #uninit /// Buffer that holds the instants associated to the inputs of a task static mut #instants: - [core::mem::MaybeUninit<<#m as rtic::Monotonic>::Instant>; #cap_lit] = + [core::mem::MaybeUninit<rtic::time::Instant<#app_path::#m>>; #cap_lit] = [#(#elems,)*]; )); } diff --git a/macros/src/codegen/timer_queue.rs b/macros/src/codegen/timer_queue.rs index fa2c7b36..9a430a07 100644 --- a/macros/src/codegen/timer_queue.rs +++ b/macros/src/codegen/timer_queue.rs @@ -5,10 +5,10 @@ use rtic_syntax::ast::App; use crate::{analyze::Analysis, check::Extra, codegen::util}; /// Generates timer queues and timer queue handlers -pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec<TokenStream2> { +pub fn codegen(app: &App, analysis: &Analysis, _extra: &Extra) -> Vec<TokenStream2> { let mut items = vec![]; - if let Some(m) = &extra.monotonic { + if !app.monotonics.is_empty() { let t = util::schedule_t_ident(); // Enumeration of `schedule`-able tasks @@ -36,12 +36,17 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec<TokenStream } )); } + } - let tq = util::tq_ident(); + 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 m = &monotonic.ident; - // Static variable and resource proxy + // Static variables and resource proxy { - let doc = "Timer queue".to_string(); + let doc = &format!("Timer queue for {}", monotonic_name); let cap = app .software_tasks .iter() @@ -62,6 +67,9 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec<TokenStream // Timer queue handler { + let enum_ = util::interrupt_ident(); + let rt_err = util::rt_err_ident(); + let arms = app .software_tasks .iter() @@ -70,12 +78,13 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec<TokenStream let priority = task.args.priority; let rq = util::rq_ident(priority); let rqt = util::spawn_t_ident(priority); - let enum_ = util::interrupt_ident(); + + // The interrupt that runs the task dispatcher let interrupt = &analysis.interrupts.get(&priority).expect("RTIC-ICE: interrupt not found").0; let pend = { quote!( - rtic::pend(you_must_enable_the_rt_feature_for_the_pac_in_your_cargo_toml::#enum_::#interrupt); + rtic::pend(#rt_err::#enum_::#interrupt); ) }; @@ -90,13 +99,20 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec<TokenStream }) .collect::<Vec<_>>(); - let sys_tick = util::suffixed("SysTick"); + let bound_interrupt = &monotonic.args.binds; + let enable_isr = if &*bound_interrupt.to_string() == "SysTick" { + quote!(core::mem::transmute::<_, cortex_m::peripheral::SYST>(()).enable_interrupt()) + } else { + quote!(rtic::export::NVIC::mask(#rt_err::#enum_::#bound_interrupt)) + }; + items.push(quote!( #[no_mangle] - unsafe fn #sys_tick() { - use rtic::Mutex as _; - - while let Some((task, index)) = rtic::export::interrupt::free(|_| #tq.dequeue()) + #[allow(non_snake_case)] + unsafe fn #bound_interrupt() { + while let Some((task, index)) = rtic::export::interrupt::free(|_| #tq.dequeue( + || #enable_isr, + )) { match task { #(#arms)* @@ -106,5 +122,6 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec<TokenStream )); } } + items } diff --git a/macros/src/codegen/util.rs b/macros/src/codegen/util.rs index fb8f1a84..287ba408 100644 --- a/macros/src/codegen/util.rs +++ b/macros/src/codegen/util.rs @@ -77,8 +77,11 @@ pub fn inputs_ident(task: &Ident) -> Ident { } /// Generates an identifier for the `INSTANTS` buffer (`schedule` API) -pub fn instants_ident(task: &Ident) -> Ident { - Ident::new(&format!("{}_INSTANTS", task), Span::call_site()) +pub fn monotonic_instants_ident(task: &Ident, monotonic: &Ident) -> Ident { + Ident::new( + &format!("{}_{}_INSTANTS", task, monotonic), + Span::call_site(), + ) } pub fn interrupt_ident() -> Ident { @@ -103,14 +106,6 @@ pub fn is_exception(name: &Ident) -> bool { ) } -/// Generates a pre-reexport identifier for the "late resources" struct -pub fn late_resources_ident(init: &Ident) -> Ident { - Ident::new( - &format!("{}LateResources", init.to_string()), - Span::call_site(), - ) -} - /// Mangle an ident pub fn mangle_ident(ident: &Ident) -> Ident { Ident::new( @@ -222,7 +217,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".to_string(), Span::call_site()) + Ident::new(&"SCHED_T", Span::call_site()) } /// Generates an identifier for the `enum` of `spawn`-able tasks @@ -233,14 +228,21 @@ pub fn spawn_t_ident(priority: u8) -> Ident { Ident::new(&format!("P{}_T", priority), Span::call_site()) } +/// Suffixed identifier pub fn suffixed(name: &str) -> Ident { let span = Span::call_site(); Ident::new(name, span) } /// Generates an identifier for a timer queue -/// -/// At most there is one timer queue -pub fn tq_ident() -> Ident { - Ident::new(&"TQ".to_string(), Span::call_site()) +pub fn tq_ident(name: &str) -> Ident { + Ident::new(&format!("TQ_{}", name), Span::call_site()) +} + +/// 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", + Span::call_site(), + ) } diff --git a/macros/src/lib.rs b/macros/src/lib.rs index dc37eaea..c9136e55 100644 --- a/macros/src/lib.rs +++ b/macros/src/lib.rs @@ -1,4 +1,4 @@ -#![deny(warnings)] +// #![deny(warnings)] extern crate proc_macro; diff --git a/src/export.rs b/src/export.rs index 72d954ab..ab5984e8 100644 --- a/src/export.rs +++ b/src/export.rs @@ -16,6 +16,7 @@ pub use cortex_m::{ use heapless::spsc::SingleCore; pub use heapless::{consts, i::Queue as iQueue, spsc::Queue}; pub use heapless::{i::BinaryHeap as iBinaryHeap, BinaryHeap}; +pub use rtic_core::monotonic::Monotonic; pub type SCFQ<N> = Queue<u8, N, u8, SingleCore>; pub type SCRQ<T, N> = Queue<(T, u8), N, u8, SingleCore>; @@ -112,6 +113,13 @@ where { } +#[inline(always)] +pub fn assert_monotonic<T>() +where + T: Monotonic, +{ +} + /// Lock the resource proxy by setting the BASEPRI /// and running the closure with interrupt::free /// @@ -32,126 +32,21 @@ #![deny(missing_docs)] #![deny(rust_2018_compatibility)] #![deny(rust_2018_idioms)] -#![deny(warnings)] +// #![deny(warnings)] #![no_std] -use core::ops::Sub; - -use cortex_m::{ - interrupt::InterruptNumber, - peripheral::{CBP, CPUID, DCB, DWT, FPB, FPU, ITM, MPU, NVIC, SCB, TPIU}, -}; +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_core::{ + monotonic::{self, embedded_time as time, Monotonic}, + prelude as mutex_prelude, Exclusive, Mutex, +}; -#[cfg(armv7m)] -pub mod cyccnt; #[doc(hidden)] pub mod export; #[doc(hidden)] mod tq; -/// `cortex_m::Peripherals` minus `SYST` -#[allow(non_snake_case)] -pub struct Peripherals { - /// Cache and branch predictor maintenance operations (not present on Cortex-M0 variants) - pub CBP: CBP, - - /// CPUID - pub CPUID: CPUID, - - /// Debug Control Block - pub DCB: DCB, - - /// Data Watchpoint and Trace unit - pub DWT: DWT, - - /// Flash Patch and Breakpoint unit (not present on Cortex-M0 variants) - pub FPB: FPB, - - /// Floating Point Unit (only present on `thumbv7em-none-eabihf`) - pub FPU: FPU, - - /// Instrumentation Trace Macrocell (not present on Cortex-M0 variants) - pub ITM: ITM, - - /// Memory Protection Unit - pub MPU: MPU, - - /// Nested Vector Interrupt Controller - pub NVIC: NVIC, - - /// System Control Block - pub SCB: SCB, - - // SysTick: System Timer - // pub SYST: SYST, - /// Trace Port Interface Unit (not present on Cortex-M0 variants) - pub TPIU: TPIU, -} - -impl From<cortex_m::Peripherals> for Peripherals { - fn from(p: cortex_m::Peripherals) -> Self { - Self { - CBP: p.CBP, - CPUID: p.CPUID, - DCB: p.DCB, - DWT: p.DWT, - FPB: p.FPB, - FPU: p.FPU, - ITM: p.ITM, - MPU: p.MPU, - NVIC: p.NVIC, - SCB: p.SCB, - TPIU: p.TPIU, - } - } -} - -/// A fraction -pub struct Fraction { - /// The numerator - pub numerator: u32, - - /// The denominator - pub denominator: u32, -} - -/// A monotonic clock / counter -pub trait Monotonic { - /// A measurement of this clock, use `CYCCNT` as a reference implementation for `Instant`. - /// Note that the Instant must be a signed value such as `i32`. - type Instant: Copy + Ord + Sub; - - /// The ratio between the system timer (SysTick) frequency and this clock frequency, i.e. - /// `Monotonic clock * Fraction = System clock` - /// - /// The ratio must be expressed in *reduced* `Fraction` form to prevent overflows. That is - /// `2 / 3` instead of `4 / 6` - fn ratio() -> Fraction; - - /// Returns the current time - /// - /// # Correctness - /// - /// This function is *allowed* to return nonsensical values if called before `reset` is invoked - /// by the runtime. Therefore application authors should *not* call this function during the - /// `#[init]` phase. - fn now() -> Self::Instant; - - /// Resets the counter to *zero* - /// - /// # Safety - /// - /// This function will be called *exactly once* by the RTIC runtime after `#[init]` returns and - /// before tasks can start; this is also the case in multi-core applications. User code must - /// *never* call this function. - unsafe fn reset(); - - /// A `Self::Instant` that represents a count of *zero* - fn zero() -> Self::Instant; -} - /// Sets the given `interrupt` as pending /// /// This is a convenience function around @@ -1,28 +1,18 @@ -use core::{ - cmp::{self, Ordering}, - convert::TryInto, - mem, - ops::Sub, -}; - -use cortex_m::peripheral::{SCB, SYST}; +use crate::{time::Instant, Monotonic}; +use core::cmp::Ordering; use heapless::{binary_heap::Min, ArrayLength, BinaryHeap}; -use crate::Monotonic; - -pub struct TimerQueue<M, T, N>(pub BinaryHeap<NotReady<M, T>, N, Min>) +pub struct TimerQueue<Mono, Task, N>(pub BinaryHeap<NotReady<Mono, Task>, N, Min>) where - M: Monotonic, - <M::Instant as Sub>::Output: TryInto<u32>, - N: ArrayLength<NotReady<M, T>>, - T: Copy; + Mono: Monotonic, + N: ArrayLength<NotReady<Mono, Task>>, + Task: Copy; -impl<M, T, N> TimerQueue<M, T, N> +impl<Mono, Task, N> TimerQueue<Mono, Task, N> where - M: Monotonic, - <M::Instant as Sub>::Output: TryInto<u32>, - N: ArrayLength<NotReady<M, T>>, - T: Copy, + Mono: Monotonic, + N: ArrayLength<NotReady<Mono, Task>>, + Task: Copy, { /// # Safety /// @@ -31,7 +21,15 @@ where /// /// Enqueue a task without checking if it is full #[inline] - pub unsafe fn enqueue_unchecked(&mut self, nr: NotReady<M, T>) { + pub unsafe fn enqueue_unchecked<F1, F2>( + &mut self, + nr: NotReady<Mono, Task>, + enable_interrupt: F1, + pend_handler: F2, + ) where + F1: FnOnce(), + F2: FnOnce(), + { let mut is_empty = true; // Check if the top contains a non-empty element and if that element is // greater than nr @@ -45,57 +43,59 @@ where .unwrap_or(true); if if_heap_max_greater_than_nr { if is_empty { - mem::transmute::<_, SYST>(()).enable_interrupt(); + // mem::transmute::<_, SYST>(()).enable_interrupt(); + enable_interrupt(); } // Set SysTick pending - SCB::set_pendst(); + // SCB::set_pendst(); + pend_handler(); } self.0.push_unchecked(nr); } + /// Check if the timer queue is empty. + #[inline] + pub fn is_empty(&self) -> bool { + self.0.is_empty() + } + /// Dequeue a task from the TimerQueue #[inline] - pub fn dequeue(&mut self) -> Option<(T, u8)> { + pub fn dequeue<F>(&mut self, disable_interrupt: F) -> Option<(Task, u8)> + where + F: FnOnce(), + { unsafe { - if let Some(instant) = self.0.peek().map(|p| p.instant) { - let now = M::now(); + Mono::clear_compare(); - if instant < now { + if let Some(instant) = self.0.peek().map(|p| p.instant) { + if instant < Mono::now() { // task became ready let nr = self.0.pop_unchecked(); Some((nr.task, nr.index)) } else { - // set a new timeout - const MAX: u32 = 0x00ffffff; - - let ratio = M::ratio(); - let dur = match (instant - now).try_into().ok().and_then(|x| { - x.checked_mul(ratio.numerator) - .map(|x| x / ratio.denominator) - }) { - None => MAX, - - // ARM Architecture Reference Manual says: - // "Setting SYST_RVR to zero has the effect of - // disabling the SysTick counter independently - // of the counter enable bit." - Some(0) => 1, - - Some(x) => cmp::min(MAX, x), - }; - mem::transmute::<_, SYST>(()).set_reload(dur); - - // Start counting down from the new reload - mem::transmute::<_, SYST>(()).clear_current(); - - None + // TODO: Fix this hack... + // Extract the compare time. + Mono::set_compare(*instant.duration_since_epoch().integer()); + + // 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 = self.0.pop_unchecked(); + + Some((nr.task, nr.index)) + } else { + None + } } } else { - // The queue is empty - mem::transmute::<_, SYST>(()).disable_interrupt(); + // The queue is empty, disable the interrupt. + disable_interrupt(); None } @@ -103,52 +103,47 @@ where } } -pub struct NotReady<M, T> +pub struct NotReady<Mono, Task> where - T: Copy, - M: Monotonic, - <M::Instant as Sub>::Output: TryInto<u32>, + Task: Copy, + Mono: Monotonic, { pub index: u8, - pub instant: M::Instant, - pub task: T, + pub instant: Instant<Mono>, + pub task: Task, } -impl<M, T> Eq for NotReady<M, T> +impl<Mono, Task> Eq for NotReady<Mono, Task> where - T: Copy, - M: Monotonic, - <M::Instant as Sub>::Output: TryInto<u32>, + Task: Copy, + Mono: Monotonic, { } -impl<M, T> Ord for NotReady<M, T> +impl<Mono, Task> Ord for NotReady<Mono, Task> where - T: Copy, - M: Monotonic, - <M::Instant as Sub>::Output: TryInto<u32>, + Task: Copy, + Mono: Monotonic, { fn cmp(&self, other: &Self) -> Ordering { self.instant.cmp(&other.instant) } } -impl<M, T> PartialEq for NotReady<M, T> +impl<Mono, Task> PartialEq for NotReady<Mono, Task> where - T: Copy, - M: Monotonic, - <M::Instant as Sub>::Output: TryInto<u32>, + Task: Copy, + Mono: Monotonic, { fn eq(&self, other: &Self) -> bool { self.instant == other.instant } } -impl<M, T> PartialOrd for NotReady<M, T> +impl<Mono, Task> PartialOrd for NotReady<Mono, Task> where - T: Copy, - M: Monotonic, - <M::Instant as Sub>::Output: TryInto<u32>, + Task: Copy, + Mono: Monotonic, { fn partial_cmp(&self, other: &Self) -> Option<Ordering> { Some(self.cmp(&other)) |