diff options
| author | bors[bot] <26634292+bors[bot]@users.noreply.github.com> | 2023-03-04 21:10:24 +0000 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-03-04 21:10:24 +0000 |
| commit | 7c7d6558f6d9c50fbb4d2487c98c9a5be15f2f7b (patch) | |
| tree | 80a47f0dc40059014e9448c4c2eb34c54dff45fe /macros | |
| parent | 1c5db277e4161470136dbd2a11e914ff1d383581 (diff) | |
| parent | 98c5490d94950608d31cd5ad9dd260f2f853735c (diff) | |
Merge #694
694: RTIC 2 r=AfoHT a=korken89
Co-authored-by: Emil Fresk <emil.fresk@gmail.com>
Co-authored-by: Per Lindgren <per.lindgren@ltu.se>
Diffstat (limited to 'macros')
| -rw-r--r-- | macros/Cargo.toml | 28 | ||||
| -rw-r--r-- | macros/src/analyze.rs | 47 | ||||
| -rw-r--r-- | macros/src/check.rs | 86 | ||||
| -rw-r--r-- | macros/src/codegen.rs | 213 | ||||
| -rw-r--r-- | macros/src/codegen/assertions.rs | 60 | ||||
| -rw-r--r-- | macros/src/codegen/dispatchers.rs | 125 | ||||
| -rw-r--r-- | macros/src/codegen/hardware_tasks.rs | 115 | ||||
| -rw-r--r-- | macros/src/codegen/idle.rs | 94 | ||||
| -rw-r--r-- | macros/src/codegen/init.rs | 122 | ||||
| -rw-r--r-- | macros/src/codegen/local_resources.rs | 76 | ||||
| -rw-r--r-- | macros/src/codegen/local_resources_struct.rs | 111 | ||||
| -rw-r--r-- | macros/src/codegen/module.rs | 468 | ||||
| -rw-r--r-- | macros/src/codegen/post_init.rs | 74 | ||||
| -rw-r--r-- | macros/src/codegen/pre_init.rs | 147 | ||||
| -rw-r--r-- | macros/src/codegen/shared_resources.rs | 196 | ||||
| -rw-r--r-- | macros/src/codegen/shared_resources_struct.rs | 159 | ||||
| -rw-r--r-- | macros/src/codegen/software_tasks.rs | 159 | ||||
| -rw-r--r-- | macros/src/codegen/timer_queue.rs | 167 | ||||
| -rw-r--r-- | macros/src/codegen/util.rs | 283 | ||||
| -rw-r--r-- | macros/src/lib.rs | 112 | ||||
| -rw-r--r-- | macros/src/tests.rs | 4 | ||||
| -rw-r--r-- | macros/src/tests/single.rs | 40 |
22 files changed, 0 insertions, 2886 deletions
diff --git a/macros/Cargo.toml b/macros/Cargo.toml deleted file mode 100644 index c3f0561..0000000 --- a/macros/Cargo.toml +++ /dev/null @@ -1,28 +0,0 @@ -[package] -authors = [ - "The Real-Time Interrupt-driven Concurrency developers", - "Jorge Aparicio <jorge@japaric.io>", -] -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 = "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.6" - -[lib] -proc-macro = true - -[dependencies] -proc-macro2 = "1" -proc-macro-error = "1" -quote = "1" -syn = "1" -rtic-syntax = "1.0.3" - -[features] -debugprint = [] diff --git a/macros/src/analyze.rs b/macros/src/analyze.rs deleted file mode 100644 index d255b7f..0000000 --- a/macros/src/analyze.rs +++ /dev/null @@ -1,47 +0,0 @@ -use core::ops; -use std::collections::{BTreeMap, BTreeSet}; - -use rtic_syntax::{ - analyze::{self, Priority}, - ast::{App, ExternInterrupt}, - P, -}; -use syn::Ident; - -/// Extend the upstream `Analysis` struct with our field -pub struct Analysis { - parent: P<analyze::Analysis>, - pub interrupts: BTreeMap<Priority, (Ident, ExternInterrupt)>, -} - -impl ops::Deref for Analysis { - type Target = analyze::Analysis; - - fn deref(&self) -> &Self::Target { - &self.parent - } -} - -// Assign an interrupt to each priority level -pub fn app(analysis: P<analyze::Analysis>, app: &App) -> P<Analysis> { - // the set of priorities (each priority only once) - let priorities = app - .software_tasks - .values() - .map(|task| task.args.priority) - .collect::<BTreeSet<_>>(); - - // map from priorities to interrupts (holding name and attributes) - let interrupts: BTreeMap<Priority, _> = priorities - .iter() - .copied() - .rev() - .zip(&app.args.extern_interrupts) - .map(|(p, (id, ext))| (p, (id.clone(), ext.clone()))) - .collect(); - - P::new(Analysis { - parent: analysis, - interrupts, - }) -} diff --git a/macros/src/check.rs b/macros/src/check.rs deleted file mode 100644 index b0ad6f8..0000000 --- a/macros/src/check.rs +++ /dev/null @@ -1,86 +0,0 @@ -use std::collections::HashSet; - -use proc_macro2::Span; -use rtic_syntax::{analyze::Analysis, ast::App}; -use syn::{parse, Path}; - -pub struct Extra { - pub device: Path, - pub peripherals: bool, -} - -pub fn app(app: &App, _analysis: &Analysis) -> parse::Result<Extra> { - // Check that external (device-specific) interrupts are not named after known (Cortex-M) - // exceptions - for name in app.args.extern_interrupts.keys() { - let name_s = name.to_string(); - - match &*name_s { - "NonMaskableInt" | "HardFault" | "MemoryManagement" | "BusFault" | "UsageFault" - | "SecureFault" | "SVCall" | "DebugMonitor" | "PendSV" | "SysTick" => { - return Err(parse::Error::new( - name.span(), - "Cortex-M exceptions can't be used as `extern` interrupts", - )); - } - - _ => {} - } - } - - // Check that there are enough external interrupts to dispatch the software tasks and the timer - // queue handler - let mut first = None; - let priorities = app - .software_tasks - .iter() - .map(|(name, task)| { - first = Some(name); - task.args.priority - }) - .collect::<HashSet<_>>(); - - let need = priorities.len(); - let given = app.args.extern_interrupts.len(); - if need > given { - let s = { - format!( - "not enough interrupts to dispatch \ - all software tasks (need: {}; given: {})", - need, given - ) - }; - - // 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)); - } - - // Check that all exceptions are valid; only exceptions with configurable priorities are - // accepted - for (name, task) in &app.hardware_tasks { - let name_s = task.args.binds.to_string(); - match &*name_s { - "NonMaskableInt" | "HardFault" => { - return Err(parse::Error::new( - name.span(), - "only exceptions with configurable priority can be used as hardware tasks", - )); - } - - _ => {} - } - } - - 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]`", - )) - } -} diff --git a/macros/src/codegen.rs b/macros/src/codegen.rs deleted file mode 100644 index 89173d4..0000000 --- a/macros/src/codegen.rs +++ /dev/null @@ -1,213 +0,0 @@ -use proc_macro2::TokenStream as TokenStream2; -use quote::quote; -use rtic_syntax::ast::App; - -use crate::{analyze::Analysis, check::Extra}; - -mod assertions; -mod dispatchers; -mod hardware_tasks; -mod idle; -mod init; -mod local_resources; -mod local_resources_struct; -mod module; -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)] -pub fn app(app: &App, analysis: &Analysis, extra: &Extra) -> 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 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 (mod_app_idle, root_idle, user_idle, call_idle) = idle::codegen(app, analysis, extra); - - 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 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: 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, extra); - let (mod_app_local_resources, mod_local_resources) = - local_resources::codegen(app, analysis, extra); - - let (mod_app_hardware_tasks, root_hardware_tasks, user_hardware_tasks) = - hardware_tasks::codegen(app, analysis, extra); - - let (mod_app_software_tasks, root_software_tasks, user_software_tasks) = - software_tasks::codegen(app, analysis, extra); - - let mod_app_dispatchers = dispatchers::codegen(app, analysis, extra); - let mod_app_timer_queue = timer_queue::codegen(app, analysis, extra); - let user_imports = &app.user_imports; - let user_code = &app.user_code; - let name = &app.name; - let device = &extra.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() -> <super::super::#name as rtic::Monotonic>::Instant { - rtic::export::interrupt::free(|_| { - use rtic::Monotonic as _; - if let Some(m) = unsafe{ &mut *super::super::#ident.get_mut() } { - m.now() - } else { - <super::super::#name as rtic::Monotonic>::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!( - /// The RTIC application module - pub mod #name { - /// Always include the device crate which contains the vector table - use #device as #rt_err; - - #monotonics - - #(#user_imports)* - - /// User code from within the module - #(#user_code)* - /// User code end - - #(#user)* - - #(#user_hardware_tasks)* - - #(#user_software_tasks)* - - #(#root)* - - #mod_shared_resources - - #mod_local_resources - - #(#root_hardware_tasks)* - - #(#root_software_tasks)* - - /// App module - #(#mod_app)* - - #(#mod_app_shared_resources)* - - #(#mod_app_local_resources)* - - #(#mod_app_hardware_tasks)* - - #(#mod_app_software_tasks)* - - #(#mod_app_dispatchers)* - - #(#mod_app_timer_queue)* - - #(#mains)* - } - ) -} diff --git a/macros/src/codegen/assertions.rs b/macros/src/codegen/assertions.rs deleted file mode 100644 index 3e0ad61..0000000 --- a/macros/src/codegen/assertions.rs +++ /dev/null @@ -1,60 +0,0 @@ -use proc_macro2::TokenStream as TokenStream2; -use quote::quote; - -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, extra: &Extra) -> Vec<TokenStream2> { - let mut stmts = vec![]; - - for ty in &analysis.send_types { - stmts.push(quote!(rtic::export::assert_send::<#ty>();)); - } - - for ty in &analysis.sync_types { - 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 = &extra.device; - 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; - let cfgs = &task.cfgs; - Some(quote!( - #(#cfgs)* - 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 { - None - } - }) - .collect(); - - let const_check = quote! { - const _CONST_CHECK: () = { - if !rtic::export::have_basepri() { - #(#no_basepri_checks)* - } else { - // TODO: Add armv7 checks here - } - }; - - let _ = _CONST_CHECK; - }; - - stmts.push(const_check); - - stmts -} diff --git a/macros/src/codegen/dispatchers.rs b/macros/src/codegen/dispatchers.rs deleted file mode 100644 index a90a97c..0000000 --- a/macros/src/codegen/dispatchers.rs +++ /dev/null @@ -1,125 +0,0 @@ -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<TokenStream2> { - let mut items = vec![]; - - let interrupts = &analysis.interrupts; - - for (&level, channel) in &analysis.channels { - let mut stmts = vec![]; - - let variants = channel - .tasks - .iter() - .map(|name| { - let cfgs = &app.software_tasks[name].cfgs; - - quote!( - #(#cfgs)* - #name - ) - }) - .collect::<Vec<_>>(); - - // 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 (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 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); - - 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)* - ) - } - ) - }) - .collect::<Vec<_>>(); - - 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 interrupt = util::suffixed(&interrupts[&level].0.to_string()); - 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/hardware_tasks.rs b/macros/src/codegen/hardware_tasks.rs deleted file mode 100644 index b3f05d2..0000000 --- a/macros/src/codegen/hardware_tasks.rs +++ /dev/null @@ -1,115 +0,0 @@ -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, shared_resources_struct}, -}; - -/// Generate support code for hardware tasks (`#[exception]`s and `#[interrupt]`s) -pub fn codegen( - app: &App, - analysis: &Analysis, - extra: &Extra, -) -> ( - // mod_app_hardware_tasks -- interrupt handlers and `${task}Resources` constructors - Vec<TokenStream2>, - // root_hardware_tasks -- items that must be placed in the root of the crate: - // - `${task}Locals` structs - // - `${task}Resources` structs - // - `${task}` modules - Vec<TokenStream2>, - // user_hardware_tasks -- the `#[task]` functions written by the user - Vec<TokenStream2>, -) { - let mut mod_app = vec![]; - let mut root = vec![]; - let mut user_tasks = vec![]; - - for (name, task) in &app.hardware_tasks { - let symbol = task.args.binds.clone(); - 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() { - const PRIORITY: u8 = #priority; - - rtic::export::run(PRIORITY, || { - #name( - #name::Context::new(&rtic::export::Priority::new(PRIORITY)) - ) - }); - } - )); - - 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, - ); - - root.push(item); - - mod_app.push(constructor); - } - - // `${task}Resources` - if !task.args.shared_resources.is_empty() { - let (item, constructor) = shared_resources_struct::codegen( - Context::HardwareTask(name), - &mut shared_needs_lt, - app, - ); - - root.push(item); - - mod_app.push(constructor); - } - - root.push(module::codegen( - Context::HardwareTask(name), - shared_needs_lt, - 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 _; - use rtic::mutex::prelude::*; - - #(#stmts)* - } - )); - } - } - - (mod_app, root, user_tasks) -} diff --git a/macros/src/codegen/idle.rs b/macros/src/codegen/idle.rs deleted file mode 100644 index 77a7f9f..0000000 --- a/macros/src/codegen/idle.rs +++ /dev/null @@ -1,94 +0,0 @@ -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, shared_resources_struct}, -}; - -/// Generates support code for `#[idle]` functions -pub fn codegen( - app: &App, - analysis: &Analysis, - extra: &Extra, -) -> ( - // mod_app_idle -- the `${idle}Resources` constructor - Vec<TokenStream2>, - // 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<TokenStream2>, - // user_idle - Option<TokenStream2>, - // call_idle - 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); - - 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); - - root_idle.push(item); - - mod_app.push(constructor); - } - - root_idle.push(module::codegen( - Context::Idle, - shared_needs_lt, - 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 _; - use rtic::mutex::prelude::*; - - #(#stmts)* - } - )); - - let call_idle = quote!(#name( - #name::Context::new(&rtic::export::Priority::new(0)) - )); - - (mod_app, root_idle, user_idle, call_idle) - } else { - ( - vec![], - vec![], - None, - quote!(loop { - rtic::export::nop() - }), - ) - } -} diff --git a/macros/src/codegen/init.rs b/macros/src/codegen/init.rs deleted file mode 100644 index 34f86f2..0000000 --- a/macros/src/codegen/init.rs +++ /dev/null @@ -1,122 +0,0 @@ -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}, -}; - -type CodegenResult = ( - // mod_app_idle -- the `${init}Resources` constructor - Option<TokenStream2>, - // 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<TokenStream2>, - // 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 -pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> CodegenResult { - let init = &app.init; - let mut local_needs_lt = false; - let name = &init.name; - - let mut root_init = vec![]; - - let context = &init.context; - let attrs = &init.attrs; - let stmts = &init.stmts; - let shared = &init.user_shared_struct; - let local = &init.user_local_struct; - - let shared_resources: Vec<_> = app - .shared_resources - .iter() - .map(|(k, v)| { - let ty = &v.ty; - let cfgs = &v.cfgs; - let docs = &v.docs; - quote!( - #(#cfgs)* - #(#docs)* - #k: #ty, - ) - }) - .collect(); - let local_resources: Vec<_> = app - .local_resources - .iter() - .map(|(k, v)| { - let ty = &v.ty; - let cfgs = &v.cfgs; - let docs = &v.docs; - quote!( - #(#cfgs)* - #(#docs)* - #k: #ty, - ) - }) - .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 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) { - #(#stmts)* - } - ); - - let mut mod_app = None; - - // `${task}Locals` - if !init.args.local_resources.is_empty() { - let (item, constructor) = - local_resources_struct::codegen(Context::Init, &mut local_needs_lt, app); - - root_init.push(item); - - mod_app = Some(constructor); - } - - let call_init = quote! { - let (shared_resources, local_resources, mut monotonics) = #name(#name::Context::new(core.into())); - }; - - root_init.push(module::codegen( - Context::Init, - false, - 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 deleted file mode 100644 index 6e7c1da..0000000 --- a/macros/src/codegen/local_resources.rs +++ /dev/null @@ -1,76 +0,0 @@ -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 -/// -/// I.e. the `static` variables and theirs proxies. -pub fn codegen( - app: &App, - _analysis: &Analysis, - _extra: &Extra, -) -> ( - // mod_app -- the `static` variables behind the proxies - Vec<TokenStream2>, - // mod_resources -- the `resources` module - 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 { - let cfgs = &res.cfgs; - let ty = &res.ty; - let mangled_name = util::static_local_resource_ident(name); - - 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!( - #[allow(non_camel_case_types)] - #[allow(non_upper_case_globals)] - // #[doc = #doc] - #[doc(hidden)] - #(#attrs)* - #(#cfgs)* - #section - static #mangled_name: rtic::RacyCell<core::mem::MaybeUninit<#ty>> = rtic::RacyCell::new(core::mem::MaybeUninit::uninit()); - )); - } - - // All declared `local = [NAME: TY = EXPR]` local resources - for (task_name, resource_name, task_local) in app.declared_local_resources() { - let cfgs = &task_local.cfgs; - let ty = &task_local.ty; - let expr = &task_local.expr; - let attrs = &task_local.attrs; - - let mangled_name = util::declared_static_local_resource_ident(resource_name, task_name); - - // 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)] - #(#attrs)* - #(#cfgs)* - static #mangled_name: rtic::RacyCell<#ty> = rtic::RacyCell::new(#expr); - )); - } - - (mod_app, TokenStream2::new()) -} diff --git a/macros/src/codegen/local_resources_struct.rs b/macros/src/codegen/local_resources_struct.rs deleted file mode 100644 index 74bdbf8..0000000 --- a/macros/src/codegen/local_resources_struct.rs +++ /dev/null @@ -1,111 +0,0 @@ -use proc_macro2::TokenStream as TokenStream2; -use quote::quote; -use rtic_syntax::{ - ast::{App, TaskLocal}, - Context, -}; - -use crate::codegen::util; - -/// Generates local resources structs -pub fn codegen(ctxt: Context, needs_lt: &mut bool, app: &App) -> (TokenStream2, TokenStream2) { - let mut lt = None; - - let resources = match ctxt { - Context::Init => &app.init.args.local_resources, - Context::Idle => &app.idle.as_ref().unwrap().args.local_resources, - Context::HardwareTask(name) => &app.hardware_tasks[name].args.local_resources, - Context::SoftwareTask(name) => &app.software_tasks[name].args.local_resources, - }; - - let task_name = util::get_task_name(ctxt, app); - - 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 { - TaskLocal::External => { - let r = app.local_resources.get(name).expect("UNREACHABLE"); - (&r.cfgs, &r.ty, false) - } - TaskLocal::Declared(r) => (&r.cfgs, &r.ty, true), - _ => unreachable!(), - }; - - has_cfgs |= !cfgs.is_empty(); - - let lt = if ctxt.runs_once() { - quote!('static) - } else { - lt = Some(quote!('a)); - quote!('a) - }; - - let mangled_name = if matches!(task_local, TaskLocal::External) { - util::static_local_resource_ident(name) - } else { - 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 - )); - - 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!(&mut *#mangled_name.get_mut()) - } else { - quote!(&mut *(&mut *#mangled_name.get_mut()).as_mut_ptr()) - }; - - values.push(quote!( - #(#cfgs)* - #name: #expr - )); - } - - 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 ()> - )); - - values.push(quote!(__marker__: core::marker::PhantomData)); - } - } - - 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)] - #[allow(non_camel_case_types)] - #[doc = #doc] - pub struct #ident<#lt> { - #(#fields,)* - } - ); - - let constructor = quote!( - impl<#lt> #ident<#lt> { - #[inline(always)] - #[doc(hidden)] - pub unsafe fn new() -> Self { - #ident { - #(#values,)* - } - } - } - ); - - (item, constructor) -} diff --git a/macros/src/codegen/module.rs b/macros/src/codegen/module.rs deleted file mode 100644 index 8dcdbcf..0000000 --- a/macros/src/codegen/module.rs +++ /dev/null @@ -1,468 +0,0 @@ -use crate::{analyze::Analysis, check::Extra, 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( - ctxt: Context, - shared_resources_tick: bool, - 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![]; - - let name = ctxt.ident(app); - - let mut lt = None; - match ctxt { - Context::Init => { - fields.push(quote!( - /// Core (Cortex-M) peripherals - pub core: rtic::export::Peripherals - )); - - if extra.peripherals { - let device = &extra.device; - - fields.push(quote!( - /// Device peripherals - pub device: #device::Peripherals - )); - - 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> - )); - - values.push(quote!(cs: rtic::export::CriticalSection::new())); - - values.push(quote!(core)); - } - - 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 { - lt = Some(quote!('a)); - Some(quote!('a)) - } else { - None - }; - - module_items.push(quote!( - #[doc(inline)] - pub use super::#ident as LocalResources; - )); - - fields.push(quote!( - /// Local Resources this task has access to - pub local: #name::LocalResources<#lt> - )); - - values.push(quote!(local: #name::LocalResources::new())); - } - - 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)] - pub use super::#ident as SharedResources; - )); - - fields.push(quote!( - /// Shared Resources this task has access to - pub shared: #name::SharedResources<#lt> - )); - - let priority = if ctxt.is_init() { - None - } else { - Some(quote!(priority)) - }; - values.push(quote!(shared: #name::SharedResources::new(#priority))); - } - - if let Context::Init = ctxt { - let monotonic_types: Vec<_> = app - .monotonics - .iter() - .map(|(_, monotonic)| { - let cfgs = &monotonic.cfgs; - let mono = &monotonic.ty; - quote! { - #(#cfgs)* - pub #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( - #(#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", - Context::HardwareTask(_) => " Hardware task", - Context::SoftwareTask(_) => " Software task", - }; - - let v = Vec::new(); - let cfgs = match ctxt { - Context::HardwareTask(t) => { - &app.hardware_tasks[t].cfgs - // ... - } - Context::SoftwareTask(t) => { - &app.software_tasks[t].cfgs - // ... - } - _ => &v, - }; - - let core = if ctxt.is_init() { - Some(quote!(core: rtic::export::Peripherals,)) - } else { - 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!( - /// Execution context - #(#cfgs)* - #[allow(non_snake_case)] - #[allow(non_camel_case_types)] - pub struct #internal_context_name<#lt> { - #(#fields,)* - } - - #(#cfgs)* - impl<#lt> #internal_context_name<#lt> { - #[doc(hidden)] - #[inline(always)] - pub unsafe fn new(#core #priority) -> Self { - #internal_context_name { - #(#values,)* - } - } - } - )); - - module_items.push(quote!( - #[doc(inline)] - #(#cfgs)* - pub use super::#internal_context_name as Context; - )); - - 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; - 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 = &extra.device; - let enum_ = util::interrupt_ident(); - 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 - items.push(quote!( - - /// Spawns the task directly - #(#cfgs)* - 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)* - 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(); - - 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 (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(); - - // 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"); - - 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; - } - )); - - items.push(quote!( - #[doc(hidden)] - #(#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 { - #[doc(hidden)] - 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_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(()) - } - }) - } - - /// Reschedule after - #[inline] - #(#cfgs)* - pub fn reschedule_after( - self, - duration: <#m as rtic::Monotonic>::Duration - ) -> Result<Self, ()> { - self.reschedule_at(monotonics::#m::now() + duration) - } - - /// Reschedule at - #(#cfgs)* - pub fn reschedule_at( - self, - instant: <#m as rtic::Monotonic>::Instant - ) -> Result<Self, ()> { - 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_marker(self.marker, marker, instant, || #pend).map(|_| #name::#m::SpawnHandle { marker }) - }) - } - } - - /// 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 - #(,#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::NotReady { - instant, - index, - task: #t::#name, - marker, - }; - - #tq_marker.get_mut().write(#tq_marker.get().read().wrapping_add(1)); - - let tq = &mut *#tq.get_mut(); - - tq.enqueue_unchecked( - nr, - || #enable_interrupt, - || #pend, - (&mut *#m_ident.get_mut()).as_mut()); - - Ok(#name::#m::SpawnHandle { marker }) - }) - } else { - Err(input) - } - } - } - )); - } - } - - if items.is_empty() { - quote!() - } else { - quote!( - #(#items)* - #[allow(non_snake_case)] - #(#cfgs)* - #[doc = #doc] - pub mod #name { - #(#module_items)* - } - ) - } -} diff --git a/macros/src/codegen/post_init.rs b/macros/src/codegen/post_init.rs deleted file mode 100644 index 460b4e2..0000000 --- a/macros/src/codegen/post_init.rs +++ /dev/null @@ -1,74 +0,0 @@ -use proc_macro2::{Span, TokenStream as TokenStream2}; -use quote::quote; -use rtic_syntax::ast::App; -use syn::Index; - -use crate::{analyze::Analysis, codegen::util}; - -/// Generates code that runs after `#[init]` returns -pub fn codegen(app: &App, analysis: &Analysis) -> Vec<TokenStream2> { - let mut stmts = vec![]; - - // Initialize shared resources - for (name, res) in &app.shared_resources { - let mangled_name = util::static_shared_resource_ident(name); - // If it's live - let cfgs = res.cfgs.clone(); - if analysis.shared_resources.get(name).is_some() { - stmts.push(quote!( - // We include the cfgs - #(#cfgs)* - // Resource is a RacyCell<MaybeUninit<T>> - // - `get_mut` to obtain a raw pointer to `MaybeUninit<T>` - // - `write` the defined value for the late resource T - #mangled_name.get_mut().write(core::mem::MaybeUninit::new(shared_resources.#name)); - )); - } - } - - // Initialize local resources - for (name, res) in &app.local_resources { - let mangled_name = util::static_local_resource_ident(name); - // If it's live - let cfgs = res.cfgs.clone(); - if analysis.local_resources.get(name).is_some() { - stmts.push(quote!( - // We include the cfgs - #(#cfgs)* - // Resource is a RacyCell<MaybeUninit<T>> - // - `get_mut` to obtain a raw pointer to `MaybeUninit<T>` - // - `write` the defined value for the late resource T - #mangled_name.get_mut().write(core::mem::MaybeUninit::new(local_resources.#name)); - )); - } - } - - 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!( - #(#cfgs)* - 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)); - )); - } - - // Enable the interrupts -- this completes the `init`-ialization phase - stmts.push(quote!(rtic::export::interrupt::enable();)); - - stmts -} diff --git a/macros/src/codegen/pre_init.rs b/macros/src/codegen/pre_init.rs deleted file mode 100644 index 2362cb7..0000000 --- a/macros/src/codegen/pre_init.rs +++ /dev/null @@ -1,147 +0,0 @@ -use proc_macro2::TokenStream as TokenStream2; -use quote::quote; -use rtic_syntax::ast::App; - -use crate::{analyze::Analysis, check::Extra, codegen::util}; - -/// Generates code that runs before `#[init]` -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();)); - - // Populate the FreeQueue - for (name, task) in &app.software_tasks { - let cap = task.args.capacity; - let cfgs = &task.cfgs; - let fq_ident = util::fq_ident(name); - - stmts.push(quote!( - #(#cfgs)* - (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(); - )); - - let device = &extra.device; - let nvic_prio_bits = quote!(#device::NVIC_PRIO_BITS); - - // check that all dispatchers exists in the `Interrupt` enumeration regardless of whether - // they are used or not - let interrupt = util::interrupt_ident(); - for name in app.args.extern_interrupts.keys() { - stmts.push(quote!(let _ = #rt_err::#interrupt::#name;)); - } - - 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| { - if util::is_exception(&task.args.binds) { - // We do exceptions in another pass - None - } else { - Some((&task.args.priority, &task.args.binds)) - } - })) { - let es = format!( - "Maximum priority used by interrupt vector '{}' 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); }; - )); - - stmts.push(quote!( - core.NVIC.set_priority( - #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(#rt_err::#interrupt::#name);)); - } - - // Set exception priorities - for (name, priority) in app.hardware_tasks.values().filter_map(|task| { - if util::is_exception(&task.args.binds) { - Some((&task.args.binds, task.args.priority)) - } else { - None - } - }) { - let es = format!( - "Maximum priority used by interrupt vector '{}' 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); }; - )); - - stmts.push(quote!(core.SCB.set_priority( - rtic::export::SystemHandler::#name, - rtic::export::logical2hw(#priority, #nvic_prio_bits), - );)); - } - - // 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 deleted file mode 100644 index b5dff09..0000000 --- a/macros/src/codegen/shared_resources.rs +++ /dev/null @@ -1,196 +0,0 @@ -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 std::collections::HashMap; - -/// Generates `static` variables and shared resource proxies -pub fn codegen( - app: &App, - analysis: &Analysis, - extra: &Extra, -) -> ( - // mod_app -- the `static` variables behind the proxies - Vec<TokenStream2>, - // mod_resources -- the `resources` module - TokenStream2, -) { - let mut mod_app = vec![]; - let mut mod_resources = vec![]; - - 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 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!( - #[allow(non_camel_case_types)] - #[allow(non_upper_case_globals)] - // #[doc = #doc] - #[doc(hidden)] - #(#attrs)* - #(#cfgs)* - #section - static #mangled_name: rtic::RacyCell<core::mem::MaybeUninit<#ty>> = rtic::RacyCell::new(core::mem::MaybeUninit::uninit()); - )); - - // 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 #shared_name<'a> { - priority: &'a Priority, - } - - #(#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 - } - } - )); - - let ptr = quote!( - #(#cfgs)* - #mangled_name.get_mut() as *mut _ - ); - - let ceiling = match analysis.ownerships.get(name) { - Some(Ownership::Owned { priority } | Ownership::CoOwned { priority }) => *priority, - Some(Ownership::Contended { ceiling }) => *ceiling, - None => 0, - }; - - // For future use - // let doc = format!(" RTIC internal ({} resource): {}:{}", doc, file!(), line!()); - - mod_app.push(util::impl_mutex( - extra, - cfgs, - true, - &shared_name, - "e!(#ty), - ceiling, - &ptr, - )); - } - } - - let mod_resources = if mod_resources.is_empty() { - quote!() - } else { - quote!(mod shared_resources { - use rtic::export::Priority; - - #(#mod_resources)* - }) - }; - - // Computing mapping of used interrupts to masks - let interrupt_ids = analysis.interrupts.iter().map(|(p, (id, _))| (p, id)); - - let mut prio_to_masks = HashMap::new(); - let device = &extra.device; - let mut uses_exceptions_with_resources = false; - - let mut mask_ids = Vec::new(); - - 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 { - // 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 - } - })) { - #[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)); - } - - // Call rtic::export::create_mask([Mask; 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, M23 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),*]) - )); - } - - // Generate a constant for the number of chunks needed by Mask. - let chunks_name = util::priority_mask_chunks_ident(); - mod_app.push(quote!( - #[doc(hidden)] - #[allow(non_upper_case_globals)] - const #chunks_name: usize = rtic::export::compute_mask_chunks([#(#mask_ids),*]); - )); - - let masks_name = util::priority_masks_ident(); - mod_app.push(quote!( - #[doc(hidden)] - #[allow(non_upper_case_globals)] - const #masks_name: [rtic::export::Mask<#chunks_name>; 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::no_basepri_panic(); - )); - } - - (mod_app, mod_resources) -} diff --git a/macros/src/codegen/shared_resources_struct.rs b/macros/src/codegen/shared_resources_struct.rs deleted file mode 100644 index df36271..0000000 --- a/macros/src/codegen/shared_resources_struct.rs +++ /dev/null @@ -1,159 +0,0 @@ -use proc_macro2::TokenStream as TokenStream2; -use quote::quote; -use rtic_syntax::{ast::App, Context}; - -use crate::codegen::util; - -/// Generate shared resources structs -pub fn codegen(ctxt: Context, needs_lt: &mut bool, 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 => &app.idle.as_ref().unwrap().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; - - 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() { - Some(quote!(mut)) - } else { - None - }; - 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 { - // Lock free resources of `idle` and `init` get 'static lifetime - let lt = if ctxt.runs_once() { - quote!('static) - } else { - lt = Some(quote!('a)); - 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 - )); - } else { - // 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) - - )); - - // continue as the value has been filled, - 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 - )); - } - - 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 ()> - )); - - values.push(quote!(__marker__: core::marker::PhantomData)); - } - } - - 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,)* - } - ); - - let arg = if ctxt.is_init() { - None - } else { - 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 { - #(#values,)* - } - } - } - ); - - (item, constructor) -} diff --git a/macros/src/codegen/software_tasks.rs b/macros/src/codegen/software_tasks.rs deleted file mode 100644 index 226121d..0000000 --- a/macros/src/codegen/software_tasks.rs +++ /dev/null @@ -1,159 +0,0 @@ -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, shared_resources_struct, util}, -}; - -pub fn codegen( - app: &App, - analysis: &Analysis, - extra: &Extra, -) -> ( - // mod_app_software_tasks -- free queues, buffers and `${task}Resources` constructors - Vec<TokenStream2>, - // root_software_tasks -- items that must be placed in the root of the crate: - // - `${task}Locals` structs - // - `${task}Resources` structs - // - `${task}` modules - Vec<TokenStream2>, - // user_software_tasks -- the `#[task]` functions written by the user - Vec<TokenStream2>, -) { - let mut mod_app = vec![]; - let mut root = vec![]; - let mut user_tasks = vec![]; - - 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; - 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); - - #[allow(clippy::redundant_closure)] - let (fq_ty, fq_expr, mk_uninit): (_, _, Box<dyn Fn() -> 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); - )); - - let elems = &(0..cap) - .map(|_| quote!(core::mem::MaybeUninit::uninit())) - .collect::<Vec<_>>(); - - 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!( - #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); - 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,)*]); - )); - - // `${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 user_task_doc = format!(" User SW task {name}"); - user_tasks.push(quote!( - #[doc = #user_task_doc] - #(#attrs)* - #(#cfgs)* - #[allow(non_snake_case)] - fn #name(#context: #name::Context #(,#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, - extra, - )); - } - - (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 f5867dc..0000000 --- a/macros/src/codegen/timer_queue.rs +++ /dev/null @@ -1,167 +0,0 @@ -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<TokenStream2> { - 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(hidden)] - #[allow(non_camel_case_types)] - #[allow(non_upper_case_globals)] - static #tq_marker: rtic::RacyCell<u32> = rtic::RacyCell::new(0); - )); - - let t = util::schedule_t_ident(); - - // Enumeration of `schedule`-able tasks - { - let variants = app - .software_tasks - .iter() - .map(|(name, task)| { - let cfgs = &task.cfgs; - - quote!( - #(#cfgs)* - #name - ) - }) - .collect::<Vec<_>>(); - - // 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 cfgs = &monotonic.cfgs; - 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 = 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)] - #[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())); - )); - - 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)] - #(#cfgs)* - static #mono: rtic::RacyCell<Option<#mono_type>> = 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() - .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.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::<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()) - } else { - quote!(rtic::export::NVIC::mask(#rt_err::#enum_::#bound_interrupt)) - }; - - 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() { - (&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 deleted file mode 100644 index 0a3edc2..0000000 --- a/macros/src/codegen/util.rs +++ /dev/null @@ -1,283 +0,0 @@ -use core::sync::atomic::{AtomicUsize, Ordering}; - -use proc_macro2::{Span, TokenStream as TokenStream2}; -use quote::quote; -use rtic_syntax::{ast::App, Context}; -use syn::{Attribute, Ident, LitInt, PatType}; - -use crate::check::Extra; - -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( - extra: &Extra, - cfgs: &[Attribute], - resources_prefix: bool, - name: &Ident, - ty: &TokenStream2, - ceiling: u8, - ptr: &TokenStream2, -) -> TokenStream2 { - let (path, priority) = if resources_prefix { - (quote!(shared_resources::#name), quote!(self.priority())) - } else { - (quote!(#name), quote!(self.priority)) - }; - - let device = &extra.device; - let masks_name = priority_masks_ident(); - quote!( - #(#cfgs)* - impl<'a> rtic::Mutex for #path<'a> { - type T = #ty; - - #[inline(always)] - fn lock<RTIC_INTERNAL_R>(&mut self, f: impl FnOnce(&mut #ty) -> RTIC_INTERNAL_R) -> RTIC_INTERNAL_R { - /// Priority ceiling - const CEILING: u8 = #ceiling; - - unsafe { - rtic::export::lock( - #ptr, - #priority, - CEILING, - #device::NVIC_PRIO_BITS, - &#masks_name, - f, - ) - } - } - } - ) -} - -/// 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 `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(); - - matches!( - &*s, - "MemoryManagement" - | "BusFault" - | "UsageFault" - | "SecureFault" - | "SVCall" - | "DebugMonitor" - | "PendSV" - | "SysTick" - ) -} - -/// 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 { - 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)) -} - -fn link_section_index() -> usize { - static INDEX: AtomicUsize = AtomicUsize::new(0); - - INDEX.fetch_add(1, Ordering::Relaxed) -} - -/// Add `link_section` attribute -pub fn link_section_uninit() -> TokenStream2 { - let section = format!(".uninit.rtic{}", link_section_index()); - - 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<TokenStream2>, - // tupled e.g. `_0`, `(_0, _1)` - TokenStream2, - // untupled e.g. &[`_0`], &[`_0`, `_1`] - Vec<TokenStream2>, - // 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 { - Context::Init => app.init.name.to_string(), - Context::Idle => app.idle.as_ref().unwrap().name.to_string(), - Context::HardwareTask(ident) | Context::SoftwareTask(ident) => ident.to_string(), - }; - - Ident::new(&s, Span::call_site()) -} - -/// Generates a pre-reexport identifier for the "shared resources" struct -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::HardwareTask(ident) | Context::SoftwareTask(ident) => ident.to_string(), - }; - - s.push_str("SharedResources"); - - mark_internal_name(&s) -} - -/// Generates a pre-reexport identifier for the "local resources" struct -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::HardwareTask(ident) | Context::SoftwareTask(ident) => ident.to_string(), - }; - - s.push_str("LocalResources"); - - 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 the `enum` of `schedule`-able tasks -pub fn schedule_t_ident() -> Ident { - Ident::new("SCHED_T", Span::call_site()) -} - -/// 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 { - 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 -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)) -} - -/// 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") -} - -pub fn static_local_resource_ident(name: &Ident) -> Ident { - 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)) -} - -pub fn need_to_lock_ident(name: &Ident) -> Ident { - Ident::new(&format!("{}_that_needs_to_be_locked", name), name.span()) -} - -/// 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 deleted file mode 100644 index 2b52601..0000000 --- a/macros/src/lib.rs +++ /dev/null @@ -1,112 +0,0 @@ -#![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; - -use proc_macro::TokenStream; -use std::{env, fs, path::Path}; - -use rtic_syntax::Settings; - -mod analyze; -mod check; -mod codegen; -#[cfg(test)] -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(); - 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) { - Err(e) => return e.to_compile_error().into(), - Ok(x) => x, - }; - - let analysis = analyze::app(analysis, &app); - - let ts = codegen::app(&app, &analysis, &extra); - - // Default output path: <project_dir>/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() { - #[cfg(feature = "debugprint")] - println!("\ntarget/ exists\n"); - } else { - // 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-<HASH>/out/ - // <project_dir>/<target-dir>/<TARGET>/debug/build/cortex-m-rtic-<HASH>/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, <project_dir>/<out_dir_root> 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) - { - if let Some(out) = path.parent() { - out_dir = out; - #[cfg(feature = "debugprint")] - println!("{:#?}\n", out_dir); - break; - } - // If no parent, just use it - out_dir = path; - break; - } - } - } - } - - // 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(); - } - - ts.into() -} diff --git a/macros/src/tests.rs b/macros/src/tests.rs deleted file mode 100644 index e9e3326..0000000 --- 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 f20c9cc..0000000 --- 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"); -} |
