aboutsummaryrefslogtreecommitdiff
path: root/macros/src
diff options
context:
space:
mode:
Diffstat (limited to 'macros/src')
-rw-r--r--macros/src/analyze.rs47
-rw-r--r--macros/src/check.rs86
-rw-r--r--macros/src/codegen.rs213
-rw-r--r--macros/src/codegen/assertions.rs60
-rw-r--r--macros/src/codegen/dispatchers.rs125
-rw-r--r--macros/src/codegen/hardware_tasks.rs115
-rw-r--r--macros/src/codegen/idle.rs94
-rw-r--r--macros/src/codegen/init.rs122
-rw-r--r--macros/src/codegen/local_resources.rs76
-rw-r--r--macros/src/codegen/local_resources_struct.rs111
-rw-r--r--macros/src/codegen/module.rs468
-rw-r--r--macros/src/codegen/post_init.rs74
-rw-r--r--macros/src/codegen/pre_init.rs147
-rw-r--r--macros/src/codegen/shared_resources.rs196
-rw-r--r--macros/src/codegen/shared_resources_struct.rs159
-rw-r--r--macros/src/codegen/software_tasks.rs159
-rw-r--r--macros/src/codegen/timer_queue.rs167
-rw-r--r--macros/src/codegen/util.rs283
-rw-r--r--macros/src/lib.rs112
-rw-r--r--macros/src/tests.rs4
-rw-r--r--macros/src/tests/single.rs40
21 files changed, 0 insertions, 2858 deletions
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,
- &quote!(#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");
-}