rtic/macros/src/codegen/module.rs

344 lines
11 KiB
Rust
Raw Normal View History

use proc_macro2::TokenStream as TokenStream2;
use quote::quote;
2020-06-11 19:18:29 +02:00
use rtic_syntax::{ast::App, Context};
2020-10-05 21:57:44 +02:00
use crate::{analyze::Analysis, check::Extra, codegen::util};
pub fn codegen(
ctxt: Context,
resources_tick: bool,
app: &App,
analysis: &Analysis,
extra: &Extra,
) -> TokenStream2 {
let mut 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);
let app_name = &app.name;
let app_path = quote! {crate::#app_name};
let mut lt = None;
match ctxt {
2020-08-27 13:21:56 +02:00
Context::Init => {
2020-12-10 20:33:13 +01:00
fields.push(quote!(
/// Core (Cortex-M) peripherals
pub core: rtic::export::Peripherals
));
2020-08-27 13:21:56 +02:00
if extra.peripherals {
2020-10-23 10:35:56 +02:00
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));
}
2020-08-27 13:21:56 +02:00
Context::Idle => {}
2021-02-25 17:32:12 +01:00
Context::HardwareTask(_) => {}
2021-02-25 17:32:12 +01:00
Context::SoftwareTask(_) => {}
}
if ctxt.has_locals(app) {
let ident = util::locals_ident(ctxt, app);
items.push(quote!(
#[doc(inline)]
pub use super::#ident as Locals;
));
}
if ctxt.has_resources(app) {
let ident = util::resources_ident(ctxt, app);
let lt = if resources_tick {
lt = Some(quote!('a));
Some(quote!('a))
} else {
None
};
items.push(quote!(
#[doc(inline)]
pub use super::#ident as Resources;
));
fields.push(quote!(
/// Resources this task has access to
pub resources: Resources<#lt>
));
let priority = if ctxt.is_init() {
None
} else {
Some(quote!(priority))
};
values.push(quote!(resources: Resources::new(#priority)));
}
2020-08-27 13:21:56 +02:00
if let Context::Init = ctxt {
2020-12-10 20:33:13 +01:00
let late_fields = analysis
.late_resources
.iter()
.flat_map(|resources| {
resources.iter().map(|name| {
let ty = &app.late_resources[name].ty;
let cfgs = &app.late_resources[name].cfgs;
quote!(
#(#cfgs)*
pub #name: #ty
)
})
})
.collect::<Vec<_>>();
2020-10-01 19:38:49 +02:00
items.push(quote!(
2020-12-10 20:33:13 +01:00
/// Resources initialized at runtime
#[allow(non_snake_case)]
pub struct LateResources {
#(#late_fields),*
}
2020-10-01 19:38:49 +02:00
));
2020-12-03 21:04:06 +01:00
2020-12-10 20:33:13 +01:00
let monotonic_types: Vec<_> = app
.monotonics
.iter()
.map(|(_, monotonic)| {
2021-02-18 19:30:59 +01:00
let mono = util::mangle_monotonic_type(&monotonic.ident.to_string());
quote! {#app_path::#mono}
2020-12-10 20:33:13 +01:00
})
.collect();
2020-12-03 21:04:06 +01:00
items.push(quote!(
2020-12-10 20:33:13 +01:00
/// Monotonics used by the system
#[allow(non_snake_case)]
pub struct Monotonics(
2021-02-04 20:22:02 +01:00
#(pub #monotonic_types),*
2020-12-10 20:33:13 +01:00
);
2020-12-03 21:04:06 +01:00
));
}
let doc = match ctxt {
2020-08-27 13:21:56 +02:00
Context::Idle => "Idle loop",
Context::Init => "Initialization function",
Context::HardwareTask(_) => "Hardware task",
Context::SoftwareTask(_) => "Software task",
};
let core = if ctxt.is_init() {
2020-12-08 20:49:13 +01:00
Some(quote!(core: rtic::export::Peripherals,))
} else {
None
};
let priority = if ctxt.is_init() {
None
} else {
2020-06-11 19:18:29 +02:00
Some(quote!(priority: &#lt rtic::export::Priority))
};
items.push(quote!(
/// Execution context
pub struct Context<#lt> {
#(#fields,)*
}
impl<#lt> Context<#lt> {
#[inline(always)]
2020-12-10 20:33:13 +01:00
pub unsafe fn new(#core #priority) -> Self {
Context {
#(#values,)*
}
}
}
));
2020-10-05 21:57:44 +02:00
// not sure if this is the right way, maybe its backwards,
// that spawn_module should put in in root
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;
// Store a copy of the task cfgs
task_cfgs = cfgs.clone();
2020-12-10 20:33:13 +01:00
let (args, tupled, untupled, ty) = util::regroup_inputs(&spawnee.inputs);
2020-10-05 21:57:44 +02:00
let args = &args;
let tupled = &tupled;
let fq = util::fq_ident(name);
let rq = util::rq_ident(priority);
let inputs = util::inputs_ident(name);
2020-10-23 10:35:56 +02:00
let device = &extra.device;
2020-10-05 21:57:44 +02:00
let enum_ = util::interrupt_ident();
2020-10-23 10:35:56 +02:00
let interrupt = &analysis
.interrupts
.get(&priority)
.expect("RTIC-ICE: interrupt identifer not found")
.0;
2020-10-05 21:57:44 +02:00
2020-10-11 18:38:38 +02:00
// Spawn caller
2020-10-05 21:57:44 +02:00
items.push(quote!(
#(#cfgs)*
pub fn spawn(#(#args,)*) -> Result<(), #ty> {
let input = #tupled;
2020-10-08 17:33:16 +02:00
2020-10-11 18:38:38 +02:00
unsafe {
if let Some(index) = rtic::export::interrupt::free(|_| #app_path::#fq.dequeue()) {
2020-10-05 21:57:44 +02:00
#app_path::#inputs
.get_unchecked_mut(usize::from(index))
.as_mut_ptr()
.write(input);
2020-10-11 18:38:38 +02:00
rtic::export::interrupt::free(|_| {
#app_path::#rq.enqueue_unchecked((#app_path::#t::#name, index));
});
2020-10-05 21:57:44 +02:00
2020-10-11 18:38:38 +02:00
rtic::pend(#device::#enum_::#interrupt);
2020-10-05 21:57:44 +02:00
2020-10-11 18:38:38 +02:00
Ok(())
} else {
Err(input)
}
2020-10-05 21:57:44 +02:00
}
2020-10-08 17:33:16 +02:00
2020-10-05 21:57:44 +02:00
}));
2020-10-11 18:38:38 +02:00
2020-12-10 20:33:13 +01:00
// Schedule caller
for (_, monotonic) in &app.monotonics {
let instants = util::monotonic_instants_ident(name, &monotonic.ident);
2021-02-22 20:59:03 +01:00
let monotonic_name = monotonic.ident.to_string();
2020-12-10 20:33:13 +01:00
let tq = util::tq_ident(&monotonic.ident.to_string());
let t = util::schedule_t_ident();
let m = &monotonic.ident;
2021-02-22 20:59:03 +01:00
let m_mangled = util::mangle_monotonic_type(&monotonic_name);
let m_ident = util::monotonic_ident(&monotonic_name);
let m_isr = &monotonic.args.binds;
let enum_ = util::interrupt_ident();
2020-12-10 20:33:13 +01:00
if monotonic.args.default {
items.push(quote!(pub use #m::spawn_after;));
items.push(quote!(pub use #m::spawn_at;));
}
2021-02-18 19:30:59 +01:00
let (enable_interrupt, pend) = if &*m_isr.to_string() == "SysTick" {
2020-12-13 14:52:16 +01:00
(
2021-02-18 19:30:59 +01:00
quote!(core::mem::transmute::<_, cortex_m::peripheral::SYST>(())
.enable_interrupt()),
2020-12-13 14:52:16 +01:00
quote!(cortex_m::peripheral::SCB::set_pendst()),
)
} else {
let rt_err = util::rt_err_ident();
(
quote!(rtic::export::NVIC::unmask(#app_path::#rt_err::#enum_::#m_isr)),
quote!(rtic::pend(#app_path::#rt_err::#enum_::#m_isr)),
)
};
2020-12-10 20:33:13 +01:00
items.push(quote!(
pub mod #m {
#(#cfgs)*
2021-02-04 20:22:02 +01:00
pub fn spawn_after<D>(
duration: D
2020-12-10 20:33:13 +01:00
#(,#args)*
2021-02-04 20:22:02 +01:00
) -> Result<(), #ty>
where D: rtic::time::duration::Duration + rtic::time::fixed_point::FixedPoint,
2021-02-18 19:30:59 +01:00
D::T: Into<<#app_path::#m_mangled as rtic::time::Clock>::T>,
2021-02-04 20:22:02 +01:00
{
let instant = if rtic::export::interrupt::free(|_| unsafe { #app_path::#m_ident.is_none() }) {
rtic::time::Instant::new(0)
} else {
#app_path::#m::now()
};
2020-12-10 20:33:13 +01:00
spawn_at(instant + duration #(,#untupled)*)
2020-12-10 20:33:13 +01:00
}
#(#cfgs)*
pub fn spawn_at(
2021-02-18 19:30:59 +01:00
instant: rtic::time::Instant<#app_path::#m_mangled>
2020-12-10 20:33:13 +01:00
#(,#args)*
) -> Result<(), #ty> {
unsafe {
let input = #tupled;
if let Some(index) = rtic::export::interrupt::free(|_| #app_path::#fq.dequeue()) {
#app_path::#inputs
.get_unchecked_mut(usize::from(index))
.as_mut_ptr()
.write(input);
#app_path::#instants
.get_unchecked_mut(usize::from(index))
.as_mut_ptr()
.write(instant);
let nr = rtic::export::NotReady {
instant,
index,
task: #app_path::#t::#name,
};
2021-02-22 20:59:03 +01:00
rtic::export::interrupt::free(|_|
if let Some(mono) = #app_path::#m_ident.as_mut() {
#app_path::#tq.enqueue_unchecked(
nr,
|| #enable_interrupt,
|| #pend,
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()
});
2020-12-10 20:33:13 +01:00
Ok(())
} else {
Err(input)
}
}
}
}));
}
2020-10-05 21:57:44 +02:00
}
if !items.is_empty() {
let user_imports = &app.user_imports;
quote!(
#[allow(non_snake_case)]
#(#task_cfgs)*
#[doc = #doc]
pub mod #name {
#(
#[allow(unused_imports)]
#user_imports
)*
#(#items)*
}
)
} else {
quote!()
}
}