mirror of
https://github.com/rtic-rs/rtic.git
synced 2025-12-19 14:25:18 +01:00
Move rtic macros to repo root, tune xtask
This commit is contained in:
parent
4124fbdd61
commit
9e445b3583
134 changed files with 31 additions and 29 deletions
53
rtic-macros/src/codegen/assertions.rs
Normal file
53
rtic-macros/src/codegen/assertions.rs
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
use proc_macro2::TokenStream as TokenStream2;
|
||||
use quote::quote;
|
||||
|
||||
use crate::syntax::ast::App;
|
||||
use crate::{analyze::Analysis, codegen::util};
|
||||
|
||||
/// Generates compile-time assertions that check that types implement the `Send` / `Sync` traits
|
||||
pub fn codegen(app: &App, analysis: &Analysis) -> 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>();));
|
||||
}
|
||||
|
||||
let device = &app.args.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;
|
||||
Some(quote!(
|
||||
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
|
||||
}
|
||||
89
rtic-macros/src/codegen/async_dispatchers.rs
Normal file
89
rtic-macros/src/codegen/async_dispatchers.rs
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
use crate::syntax::ast::App;
|
||||
use crate::{analyze::Analysis, codegen::util};
|
||||
use proc_macro2::TokenStream as TokenStream2;
|
||||
use quote::quote;
|
||||
|
||||
/// Generates task dispatchers
|
||||
pub fn codegen(app: &App, analysis: &Analysis) -> TokenStream2 {
|
||||
let mut items = vec![];
|
||||
|
||||
let interrupts = &analysis.interrupts;
|
||||
|
||||
// Generate executor definition and priority in global scope
|
||||
for (name, _) in app.software_tasks.iter() {
|
||||
let type_name = util::internal_task_ident(name, "F");
|
||||
let exec_name = util::internal_task_ident(name, "EXEC");
|
||||
|
||||
items.push(quote!(
|
||||
#[allow(non_camel_case_types)]
|
||||
type #type_name = impl core::future::Future;
|
||||
#[allow(non_upper_case_globals)]
|
||||
static #exec_name: rtic::export::executor::AsyncTaskExecutor<#type_name> =
|
||||
rtic::export::executor::AsyncTaskExecutor::new();
|
||||
));
|
||||
}
|
||||
|
||||
for (&level, channel) in &analysis.channels {
|
||||
let mut stmts = vec![];
|
||||
|
||||
let dispatcher_name = if level > 0 {
|
||||
util::suffixed(&interrupts.get(&level).expect("UNREACHABLE").0.to_string())
|
||||
} else {
|
||||
util::zero_prio_dispatcher_ident()
|
||||
};
|
||||
|
||||
let pend_interrupt = if level > 0 {
|
||||
let device = &app.args.device;
|
||||
let enum_ = util::interrupt_ident();
|
||||
|
||||
quote!(rtic::pend(#device::#enum_::#dispatcher_name);)
|
||||
} else {
|
||||
// For 0 priority tasks we don't need to pend anything
|
||||
quote!()
|
||||
};
|
||||
|
||||
for name in channel.tasks.iter() {
|
||||
let exec_name = util::internal_task_ident(name, "EXEC");
|
||||
// TODO: Fix cfg
|
||||
// let task = &app.software_tasks[name];
|
||||
// let cfgs = &task.cfgs;
|
||||
|
||||
stmts.push(quote!(
|
||||
#exec_name.poll(|| {
|
||||
#exec_name.set_pending();
|
||||
#pend_interrupt
|
||||
});
|
||||
));
|
||||
}
|
||||
|
||||
if level > 0 {
|
||||
let doc = format!("Interrupt handler to dispatch async tasks at priority {level}");
|
||||
let attribute = &interrupts.get(&level).expect("UNREACHABLE").1.attrs;
|
||||
items.push(quote!(
|
||||
#[allow(non_snake_case)]
|
||||
#[doc = #doc]
|
||||
#[no_mangle]
|
||||
#(#attribute)*
|
||||
unsafe fn #dispatcher_name() {
|
||||
/// The priority of this interrupt handler
|
||||
const PRIORITY: u8 = #level;
|
||||
|
||||
rtic::export::run(PRIORITY, || {
|
||||
#(#stmts)*
|
||||
});
|
||||
}
|
||||
));
|
||||
} else {
|
||||
items.push(quote!(
|
||||
#[allow(non_snake_case)]
|
||||
unsafe fn #dispatcher_name() -> ! {
|
||||
loop {
|
||||
#(#stmts)*
|
||||
}
|
||||
}
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
quote!(#(#items)*)
|
||||
}
|
||||
87
rtic-macros/src/codegen/hardware_tasks.rs
Normal file
87
rtic-macros/src/codegen/hardware_tasks.rs
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
use crate::syntax::{ast::App, Context};
|
||||
use crate::{
|
||||
analyze::Analysis,
|
||||
codegen::{local_resources_struct, module, shared_resources_struct},
|
||||
};
|
||||
use proc_macro2::TokenStream as TokenStream2;
|
||||
use quote::quote;
|
||||
|
||||
/// Generate support code for hardware tasks (`#[exception]`s and `#[interrupt]`s)
|
||||
pub fn codegen(app: &App, analysis: &Analysis) -> 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;
|
||||
|
||||
mod_app.push(quote!(
|
||||
#[allow(non_snake_case)]
|
||||
#[no_mangle]
|
||||
#(#attrs)*
|
||||
#(#cfgs)*
|
||||
unsafe fn #symbol() {
|
||||
const PRIORITY: u8 = #priority;
|
||||
|
||||
rtic::export::run(PRIORITY, || {
|
||||
#name(
|
||||
#name::Context::new()
|
||||
)
|
||||
});
|
||||
}
|
||||
));
|
||||
|
||||
// `${task}Locals`
|
||||
if !task.args.local_resources.is_empty() {
|
||||
let (item, constructor) =
|
||||
local_resources_struct::codegen(Context::HardwareTask(name), 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), app);
|
||||
|
||||
root.push(item);
|
||||
|
||||
mod_app.push(constructor);
|
||||
}
|
||||
|
||||
// Module generation...
|
||||
|
||||
root.push(module::codegen(Context::HardwareTask(name), app, analysis));
|
||||
|
||||
// End module generation
|
||||
|
||||
if !task.is_extern {
|
||||
let attrs = &task.attrs;
|
||||
let context = &task.context;
|
||||
let stmts = &task.stmts;
|
||||
user_tasks.push(quote!(
|
||||
#(#attrs)*
|
||||
#[allow(non_snake_case)]
|
||||
fn #name(#context: #name::Context) {
|
||||
use rtic::Mutex as _;
|
||||
use rtic::mutex::prelude::*;
|
||||
|
||||
#(#stmts)*
|
||||
}
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
quote!(
|
||||
#(#mod_app)*
|
||||
|
||||
#(#root)*
|
||||
|
||||
#(#user_tasks)*
|
||||
)
|
||||
}
|
||||
58
rtic-macros/src/codegen/idle.rs
Normal file
58
rtic-macros/src/codegen/idle.rs
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
use crate::syntax::{ast::App, Context};
|
||||
use crate::{
|
||||
analyze::Analysis,
|
||||
codegen::{local_resources_struct, module, shared_resources_struct},
|
||||
};
|
||||
use proc_macro2::TokenStream as TokenStream2;
|
||||
use quote::quote;
|
||||
|
||||
/// Generates support code for `#[idle]` functions
|
||||
pub fn codegen(app: &App, analysis: &Analysis) -> TokenStream2 {
|
||||
if let Some(idle) = &app.idle {
|
||||
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, 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, app);
|
||||
|
||||
root_idle.push(item);
|
||||
|
||||
mod_app.push(constructor);
|
||||
}
|
||||
|
||||
root_idle.push(module::codegen(Context::Idle, app, analysis));
|
||||
|
||||
let attrs = &idle.attrs;
|
||||
let context = &idle.context;
|
||||
let stmts = &idle.stmts;
|
||||
let user_idle = Some(quote!(
|
||||
#(#attrs)*
|
||||
#[allow(non_snake_case)]
|
||||
fn #name(#context: #name::Context) -> ! {
|
||||
use rtic::Mutex as _;
|
||||
use rtic::mutex::prelude::*;
|
||||
|
||||
#(#stmts)*
|
||||
}
|
||||
));
|
||||
|
||||
quote!(
|
||||
#(#mod_app)*
|
||||
|
||||
#(#root_idle)*
|
||||
|
||||
#user_idle
|
||||
)
|
||||
} else {
|
||||
quote!()
|
||||
}
|
||||
}
|
||||
95
rtic-macros/src/codegen/init.rs
Normal file
95
rtic-macros/src/codegen/init.rs
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
use proc_macro2::TokenStream as TokenStream2;
|
||||
use quote::quote;
|
||||
|
||||
use crate::{
|
||||
analyze::Analysis,
|
||||
codegen::{local_resources_struct, module},
|
||||
syntax::{ast::App, Context},
|
||||
};
|
||||
|
||||
/// Generates support code for `#[init]` functions
|
||||
pub fn codegen(app: &App, analysis: &Analysis) -> TokenStream2 {
|
||||
let init = &app.init;
|
||||
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();
|
||||
|
||||
root_init.push(quote! {
|
||||
struct #shared {
|
||||
#(#shared_resources)*
|
||||
}
|
||||
|
||||
struct #local {
|
||||
#(#local_resources)*
|
||||
}
|
||||
});
|
||||
|
||||
// let locals_pat = locals_pat.iter();
|
||||
|
||||
let user_init_return = quote! {#shared, #local};
|
||||
|
||||
let user_init = quote!(
|
||||
#(#attrs)*
|
||||
#[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, app);
|
||||
|
||||
root_init.push(item);
|
||||
|
||||
mod_app = Some(constructor);
|
||||
}
|
||||
|
||||
root_init.push(module::codegen(Context::Init, app, analysis));
|
||||
|
||||
quote!(
|
||||
#mod_app
|
||||
|
||||
#(#root_init)*
|
||||
|
||||
#user_init
|
||||
)
|
||||
}
|
||||
65
rtic-macros/src/codegen/local_resources.rs
Normal file
65
rtic-macros/src/codegen/local_resources.rs
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
use crate::syntax::ast::App;
|
||||
use crate::{analyze::Analysis, codegen::util};
|
||||
use proc_macro2::TokenStream as TokenStream2;
|
||||
use quote::quote;
|
||||
|
||||
/// Generates `local` variables and local resource proxies
|
||||
///
|
||||
/// I.e. the `static` variables and theirs proxies.
|
||||
pub fn codegen(app: &App, _analysis: &Analysis) -> TokenStream2 {
|
||||
let mut mod_app = 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);
|
||||
));
|
||||
}
|
||||
|
||||
quote!(#(#mod_app)*)
|
||||
}
|
||||
102
rtic-macros/src/codegen/local_resources_struct.rs
Normal file
102
rtic-macros/src/codegen/local_resources_struct.rs
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
use crate::syntax::{
|
||||
ast::{App, TaskLocal},
|
||||
Context,
|
||||
};
|
||||
use proc_macro2::TokenStream as TokenStream2;
|
||||
use quote::quote;
|
||||
|
||||
use crate::codegen::util;
|
||||
|
||||
/// Generates local resources structs
|
||||
pub fn codegen(ctxt: Context, app: &App) -> (TokenStream2, TokenStream2) {
|
||||
let resources = match ctxt {
|
||||
Context::Init => &app.init.args.local_resources,
|
||||
Context::Idle => {
|
||||
&app.idle
|
||||
.as_ref()
|
||||
.expect("RTIC-ICE: unable to get idle name")
|
||||
.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![];
|
||||
|
||||
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),
|
||||
};
|
||||
|
||||
let lt = if ctxt.runs_once() {
|
||||
quote!('static)
|
||||
} else {
|
||||
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)
|
||||
};
|
||||
|
||||
fields.push(quote!(
|
||||
#(#cfgs)*
|
||||
#[allow(missing_docs)]
|
||||
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
|
||||
));
|
||||
}
|
||||
|
||||
fields.push(quote!(
|
||||
#[doc(hidden)]
|
||||
pub __rtic_internal_marker: ::core::marker::PhantomData<&'a ()>
|
||||
));
|
||||
|
||||
values.push(quote!(__rtic_internal_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<'a> {
|
||||
#(#fields,)*
|
||||
}
|
||||
);
|
||||
|
||||
let constructor = quote!(
|
||||
impl<'a> #ident<'a> {
|
||||
#[inline(always)]
|
||||
#[allow(missing_docs)]
|
||||
pub unsafe fn new() -> Self {
|
||||
#ident {
|
||||
#(#values,)*
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
(item, constructor)
|
||||
}
|
||||
52
rtic-macros/src/codegen/main.rs
Normal file
52
rtic-macros/src/codegen/main.rs
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
use crate::{analyze::Analysis, codegen::util, syntax::ast::App};
|
||||
use proc_macro2::TokenStream as TokenStream2;
|
||||
use quote::quote;
|
||||
|
||||
use super::{assertions, post_init, pre_init};
|
||||
|
||||
/// Generates code for `fn main`
|
||||
pub fn codegen(app: &App, analysis: &Analysis) -> TokenStream2 {
|
||||
let assertion_stmts = assertions::codegen(app, analysis);
|
||||
|
||||
let pre_init_stmts = pre_init::codegen(app, analysis);
|
||||
|
||||
let post_init_stmts = post_init::codegen(app, analysis);
|
||||
|
||||
let call_idle = if let Some(idle) = &app.idle {
|
||||
let name = &idle.name;
|
||||
quote!(#name(#name::Context::new()))
|
||||
} else if analysis.channels.get(&0).is_some() {
|
||||
let dispatcher = util::zero_prio_dispatcher_ident();
|
||||
quote!(#dispatcher();)
|
||||
} else {
|
||||
quote!(loop {
|
||||
rtic::export::nop()
|
||||
})
|
||||
};
|
||||
|
||||
let main = util::suffixed("main");
|
||||
let init_name = &app.init.name;
|
||||
quote!(
|
||||
#[doc(hidden)]
|
||||
#[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(||{
|
||||
let (shared_resources, local_resources) = #init_name(#init_name::Context::new(core.into()));
|
||||
|
||||
#(#post_init_stmts)*
|
||||
});
|
||||
|
||||
#call_idle
|
||||
}
|
||||
)
|
||||
}
|
||||
197
rtic-macros/src/codegen/module.rs
Normal file
197
rtic-macros/src/codegen/module.rs
Normal file
|
|
@ -0,0 +1,197 @@
|
|||
use crate::syntax::{ast::App, Context};
|
||||
use crate::{analyze::Analysis, codegen::util};
|
||||
use proc_macro2::TokenStream as TokenStream2;
|
||||
use quote::quote;
|
||||
|
||||
#[allow(clippy::too_many_lines)]
|
||||
pub fn codegen(ctxt: Context, app: &App, analysis: &Analysis) -> TokenStream2 {
|
||||
let mut items = vec![];
|
||||
let mut module_items = vec![];
|
||||
let mut fields = vec![];
|
||||
let mut values = vec![];
|
||||
// Used to copy task cfgs to the whole module
|
||||
let mut task_cfgs = vec![];
|
||||
|
||||
let name = ctxt.ident(app);
|
||||
|
||||
match ctxt {
|
||||
Context::Init => {
|
||||
fields.push(quote!(
|
||||
/// Core (Cortex-M) peripherals
|
||||
pub core: rtic::export::Peripherals
|
||||
));
|
||||
|
||||
if app.args.peripherals {
|
||||
let device = &app.args.device;
|
||||
|
||||
fields.push(quote!(
|
||||
/// Device peripherals
|
||||
pub device: #device::Peripherals
|
||||
));
|
||||
|
||||
values.push(quote!(device: #device::Peripherals::steal()));
|
||||
}
|
||||
|
||||
fields.push(quote!(
|
||||
/// Critical section token for init
|
||||
pub cs: rtic::export::CriticalSection<'a>
|
||||
));
|
||||
|
||||
values.push(quote!(cs: rtic::export::CriticalSection::new()));
|
||||
|
||||
values.push(quote!(core));
|
||||
}
|
||||
|
||||
Context::Idle | Context::HardwareTask(_) | Context::SoftwareTask(_) => {}
|
||||
}
|
||||
|
||||
if ctxt.has_local_resources(app) {
|
||||
let ident = util::local_resources_ident(ctxt, app);
|
||||
|
||||
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<'a>
|
||||
));
|
||||
|
||||
values.push(quote!(local: #name::LocalResources::new()));
|
||||
}
|
||||
|
||||
if ctxt.has_shared_resources(app) {
|
||||
let ident = util::shared_resources_ident(ctxt, app);
|
||||
|
||||
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<'a>
|
||||
));
|
||||
|
||||
values.push(quote!(shared: #name::SharedResources::new()));
|
||||
}
|
||||
|
||||
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 internal_context_name = util::internal_task_ident(name, "Context");
|
||||
let exec_name = util::internal_task_ident(name, "EXEC");
|
||||
|
||||
items.push(quote!(
|
||||
#(#cfgs)*
|
||||
/// Execution context
|
||||
#[allow(non_snake_case)]
|
||||
#[allow(non_camel_case_types)]
|
||||
pub struct #internal_context_name<'a> {
|
||||
#[doc(hidden)]
|
||||
__rtic_internal_p: ::core::marker::PhantomData<&'a ()>,
|
||||
#(#fields,)*
|
||||
}
|
||||
|
||||
#(#cfgs)*
|
||||
impl<'a> #internal_context_name<'a> {
|
||||
#[inline(always)]
|
||||
#[allow(missing_docs)]
|
||||
pub unsafe fn new(#core) -> Self {
|
||||
#internal_context_name {
|
||||
__rtic_internal_p: ::core::marker::PhantomData,
|
||||
#(#values,)*
|
||||
}
|
||||
}
|
||||
}
|
||||
));
|
||||
|
||||
module_items.push(quote!(
|
||||
#(#cfgs)*
|
||||
#[doc(inline)]
|
||||
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 cfgs = &spawnee.cfgs;
|
||||
// Store a copy of the task cfgs
|
||||
task_cfgs = cfgs.clone();
|
||||
|
||||
let pend_interrupt = if priority > 0 {
|
||||
let device = &app.args.device;
|
||||
let enum_ = util::interrupt_ident();
|
||||
let interrupt = &analysis.interrupts.get(&priority).expect("UREACHABLE").0;
|
||||
quote!(rtic::pend(#device::#enum_::#interrupt);)
|
||||
} else {
|
||||
quote!()
|
||||
};
|
||||
|
||||
let internal_spawn_ident = util::internal_task_ident(name, "spawn");
|
||||
let (input_args, input_tupled, input_untupled, input_ty) =
|
||||
util::regroup_inputs(&spawnee.inputs);
|
||||
|
||||
// Spawn caller
|
||||
items.push(quote!(
|
||||
#(#cfgs)*
|
||||
/// Spawns the task directly
|
||||
#[allow(non_snake_case)]
|
||||
#[doc(hidden)]
|
||||
pub fn #internal_spawn_ident(#(#input_args,)*) -> Result<(), #input_ty> {
|
||||
// SAFETY: If `try_allocate` suceeds one must call `spawn`, which we do.
|
||||
unsafe {
|
||||
if #exec_name.try_allocate() {
|
||||
let f = #name(unsafe { #name::Context::new() } #(,#input_untupled)*);
|
||||
#exec_name.spawn(f);
|
||||
#pend_interrupt
|
||||
|
||||
Ok(())
|
||||
} else {
|
||||
Err(#input_tupled)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
));
|
||||
|
||||
module_items.push(quote!(
|
||||
#(#cfgs)*
|
||||
#[doc(inline)]
|
||||
pub use super::#internal_spawn_ident as spawn;
|
||||
));
|
||||
}
|
||||
|
||||
if items.is_empty() {
|
||||
quote!()
|
||||
} else {
|
||||
quote!(
|
||||
#(#items)*
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
#(#task_cfgs)*
|
||||
#[doc = #doc]
|
||||
pub mod #name {
|
||||
#(#module_items)*
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
47
rtic-macros/src/codegen/post_init.rs
Normal file
47
rtic-macros/src/codegen/post_init.rs
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
use crate::{analyze::Analysis, codegen::util, syntax::ast::App};
|
||||
use proc_macro2::TokenStream as TokenStream2;
|
||||
use quote::quote;
|
||||
|
||||
/// 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));
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
// Enable the interrupts -- this completes the `init`-ialization phase
|
||||
stmts.push(quote!(rtic::export::interrupt::enable();));
|
||||
|
||||
stmts
|
||||
}
|
||||
85
rtic-macros/src/codegen/pre_init.rs
Normal file
85
rtic-macros/src/codegen/pre_init.rs
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
use crate::syntax::ast::App;
|
||||
use proc_macro2::TokenStream as TokenStream2;
|
||||
use quote::quote;
|
||||
|
||||
use crate::{analyze::Analysis, codegen::util};
|
||||
|
||||
/// Generates code that runs before `#[init]`
|
||||
pub fn codegen(app: &App, analysis: &Analysis) -> 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();));
|
||||
|
||||
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 = &app.args.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.dispatchers.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 '{name}' is more than supported by hardware"
|
||||
);
|
||||
// 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 '{name}' is more than supported by hardware"
|
||||
);
|
||||
// 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),
|
||||
);));
|
||||
}
|
||||
|
||||
stmts
|
||||
}
|
||||
183
rtic-macros/src/codegen/shared_resources.rs
Normal file
183
rtic-macros/src/codegen/shared_resources.rs
Normal file
|
|
@ -0,0 +1,183 @@
|
|||
use crate::syntax::{analyze::Ownership, ast::App};
|
||||
use crate::{analyze::Analysis, codegen::util};
|
||||
use proc_macro2::TokenStream as TokenStream2;
|
||||
use quote::quote;
|
||||
use std::collections::HashMap;
|
||||
|
||||
/// Generates `static` variables and shared resource proxies
|
||||
pub fn codegen(app: &App, analysis: &Analysis) -> 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> {
|
||||
__rtic_internal_p: ::core::marker::PhantomData<&'a ()>,
|
||||
}
|
||||
|
||||
#(#cfgs)*
|
||||
impl<'a> #shared_name<'a> {
|
||||
#[inline(always)]
|
||||
pub unsafe fn new() -> Self {
|
||||
#shared_name { __rtic_internal_p: ::core::marker::PhantomData }
|
||||
}
|
||||
}
|
||||
));
|
||||
|
||||
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(
|
||||
app,
|
||||
cfgs,
|
||||
true,
|
||||
&shared_name,
|
||||
"e!(#ty),
|
||||
ceiling,
|
||||
&ptr,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
let mod_resources = if mod_resources.is_empty() {
|
||||
quote!()
|
||||
} else {
|
||||
quote!(mod shared_resources {
|
||||
#(#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 = &app.args.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
|
||||
}
|
||||
})) {
|
||||
let v: &mut Vec<_> = prio_to_masks.entry(priority - 1).or_default();
|
||||
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();
|
||||
));
|
||||
}
|
||||
|
||||
quote!(
|
||||
#(#mod_app)*
|
||||
|
||||
#mod_resources
|
||||
)
|
||||
}
|
||||
119
rtic-macros/src/codegen/shared_resources_struct.rs
Normal file
119
rtic-macros/src/codegen/shared_resources_struct.rs
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
use crate::syntax::{ast::App, Context};
|
||||
use proc_macro2::TokenStream as TokenStream2;
|
||||
use quote::quote;
|
||||
|
||||
use crate::codegen::util;
|
||||
|
||||
/// Generate shared resources structs
|
||||
pub fn codegen(ctxt: Context, app: &App) -> (TokenStream2, TokenStream2) {
|
||||
let resources = match ctxt {
|
||||
Context::Init => unreachable!("Tried to generate shared resources struct for init"),
|
||||
Context::Idle => {
|
||||
&app.idle
|
||||
.as_ref()
|
||||
.expect("RTIC-ICE: unable to get idle name")
|
||||
.args
|
||||
.shared_resources
|
||||
}
|
||||
Context::HardwareTask(name) => &app.hardware_tasks[name].args.shared_resources,
|
||||
Context::SoftwareTask(name) => &app.software_tasks[name].args.shared_resources,
|
||||
};
|
||||
|
||||
let mut fields = vec![];
|
||||
let mut values = vec![];
|
||||
|
||||
for (name, access) in resources {
|
||||
let res = app.shared_resources.get(name).expect("UNREACHABLE");
|
||||
|
||||
let cfgs = &res.cfgs;
|
||||
|
||||
// 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 {
|
||||
quote!('a)
|
||||
};
|
||||
|
||||
fields.push(quote!(
|
||||
#(#cfgs)*
|
||||
#[allow(missing_docs)]
|
||||
pub #name: &#lt #mut_ #ty
|
||||
));
|
||||
} else if access.is_shared() {
|
||||
fields.push(quote!(
|
||||
#(#cfgs)*
|
||||
#[allow(missing_docs)]
|
||||
pub #name: &'a #ty
|
||||
));
|
||||
} else {
|
||||
fields.push(quote!(
|
||||
#(#cfgs)*
|
||||
#[allow(missing_docs)]
|
||||
pub #name: shared_resources::#shared_name<'a>
|
||||
));
|
||||
|
||||
values.push(quote!(
|
||||
#(#cfgs)*
|
||||
#name: shared_resources::#shared_name::new()
|
||||
|
||||
));
|
||||
|
||||
// continue as the value has been filled,
|
||||
continue;
|
||||
}
|
||||
|
||||
let expr = if access.is_exclusive() {
|
||||
quote!(&mut *(&mut *#mangled_name.get_mut()).as_mut_ptr())
|
||||
} else {
|
||||
quote!(&*(&*#mangled_name.get()).as_ptr())
|
||||
};
|
||||
|
||||
values.push(quote!(
|
||||
#(#cfgs)*
|
||||
#name: #expr
|
||||
));
|
||||
}
|
||||
|
||||
fields.push(quote!(
|
||||
#[doc(hidden)]
|
||||
pub __rtic_internal_marker: core::marker::PhantomData<&'a ()>
|
||||
));
|
||||
|
||||
values.push(quote!(__rtic_internal_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]
|
||||
pub struct #ident<'a> {
|
||||
#(#fields,)*
|
||||
}
|
||||
);
|
||||
|
||||
let constructor = quote!(
|
||||
impl<'a> #ident<'a> {
|
||||
#[inline(always)]
|
||||
#[allow(missing_docs)]
|
||||
pub unsafe fn new() -> Self {
|
||||
#ident {
|
||||
#(#values,)*
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
(item, constructor)
|
||||
}
|
||||
64
rtic-macros/src/codegen/software_tasks.rs
Normal file
64
rtic-macros/src/codegen/software_tasks.rs
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
use crate::syntax::{ast::App, Context};
|
||||
use crate::{
|
||||
analyze::Analysis,
|
||||
codegen::{local_resources_struct, module, shared_resources_struct},
|
||||
};
|
||||
use proc_macro2::TokenStream as TokenStream2;
|
||||
use quote::quote;
|
||||
|
||||
pub fn codegen(app: &App, analysis: &Analysis) -> TokenStream2 {
|
||||
let mut mod_app = vec![];
|
||||
let mut root = vec![];
|
||||
let mut user_tasks = vec![];
|
||||
|
||||
// Any task
|
||||
for (name, task) in app.software_tasks.iter() {
|
||||
if !task.args.local_resources.is_empty() {
|
||||
let (item, constructor) =
|
||||
local_resources_struct::codegen(Context::SoftwareTask(name), 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), 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 inputs = &task.inputs;
|
||||
|
||||
user_tasks.push(quote!(
|
||||
#(#attrs)*
|
||||
#(#cfgs)*
|
||||
#[allow(non_snake_case)]
|
||||
async fn #name<'a>(#context: #name::Context<'a> #(,#inputs)*) {
|
||||
use rtic::Mutex as _;
|
||||
use rtic::mutex::prelude::*;
|
||||
|
||||
#(#stmts)*
|
||||
}
|
||||
));
|
||||
}
|
||||
|
||||
root.push(module::codegen(Context::SoftwareTask(name), app, analysis));
|
||||
}
|
||||
|
||||
quote!(
|
||||
#(#mod_app)*
|
||||
|
||||
#(#root)*
|
||||
|
||||
#(#user_tasks)*
|
||||
)
|
||||
}
|
||||
238
rtic-macros/src/codegen/util.rs
Normal file
238
rtic-macros/src/codegen/util.rs
Normal file
|
|
@ -0,0 +1,238 @@
|
|||
use crate::syntax::{ast::App, Context};
|
||||
use core::sync::atomic::{AtomicUsize, Ordering};
|
||||
use proc_macro2::{Span, TokenStream as TokenStream2};
|
||||
use quote::quote;
|
||||
use syn::{Attribute, Ident, PatType};
|
||||
|
||||
const RTIC_INTERNAL: &str = "__rtic_internal";
|
||||
|
||||
/// Generates a `Mutex` implementation
|
||||
pub fn impl_mutex(
|
||||
app: &App,
|
||||
cfgs: &[Attribute],
|
||||
resources_prefix: bool,
|
||||
name: &Ident,
|
||||
ty: &TokenStream2,
|
||||
ceiling: u8,
|
||||
ptr: &TokenStream2,
|
||||
) -> TokenStream2 {
|
||||
let path = if resources_prefix {
|
||||
quote!(shared_resources::#name)
|
||||
} else {
|
||||
quote!(#name)
|
||||
};
|
||||
|
||||
let device = &app.args.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,
|
||||
CEILING,
|
||||
#device::NVIC_PRIO_BITS,
|
||||
&#masks_name,
|
||||
f,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
pub fn interrupt_ident() -> Ident {
|
||||
let span = Span::call_site();
|
||||
Ident::new("interrupt", span)
|
||||
}
|
||||
|
||||
/// 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 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()
|
||||
.expect("RTIC-ICE: unable to find idle name")
|
||||
.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()
|
||||
.expect("RTIC-ICE: unable to find idle name")
|
||||
.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()
|
||||
.expect("RTIC-ICE: unable to find idle name")
|
||||
.name
|
||||
.to_string(),
|
||||
Context::HardwareTask(ident) | Context::SoftwareTask(ident) => ident.to_string(),
|
||||
};
|
||||
|
||||
s.push_str("LocalResources");
|
||||
|
||||
mark_internal_name(&s)
|
||||
}
|
||||
|
||||
/// Suffixed identifier
|
||||
pub fn suffixed(name: &str) -> Ident {
|
||||
let span = Span::call_site();
|
||||
Ident::new(name, span)
|
||||
}
|
||||
|
||||
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!("{name}_that_needs_to_be_locked"), name.span())
|
||||
}
|
||||
|
||||
pub fn zero_prio_dispatcher_ident() -> Ident {
|
||||
Ident::new("__rtic_internal_async_0_prio_dispatcher", Span::call_site())
|
||||
}
|
||||
|
||||
/// The name to get better RT flag errors
|
||||
pub fn rt_err_ident() -> Ident {
|
||||
Ident::new(
|
||||
"you_must_enable_the_rt_feature_for_the_pac_in_your_cargo_toml",
|
||||
Span::call_site(),
|
||||
)
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue