rtic/macros/src/codegen/module.rs

453 lines
15 KiB
Rust
Raw Normal View History

2021-05-06 19:40:37 +02:00
use crate::{analyze::Analysis, check::Extra, codegen::util};
use proc_macro2::TokenStream as TokenStream2;
use quote::quote;
2020-06-11 19:18:29 +02:00
use rtic_syntax::{ast::App, Context};
2022-02-18 19:38:48 +01:00
#[allow(clippy::too_many_lines)]
2020-10-05 21:57:44 +02:00
pub fn codegen(
ctxt: Context,
2021-07-07 21:03:56 +02:00
shared_resources_tick: bool,
local_resources_tick: bool,
2020-10-05 21:57:44 +02:00
app: &App,
analysis: &Analysis,
extra: &Extra,
) -> TokenStream2 {
let mut items = vec![];
2021-05-06 19:40:37 +02:00
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);
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));
}
2022-02-18 19:38:48 +01:00
Context::Idle | Context::HardwareTask(_) | Context::SoftwareTask(_) => {}
}
2021-07-06 22:47:48 +02:00
// 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);
2021-07-07 21:03:56 +02:00
let lt = if local_resources_tick {
2021-07-06 22:47:48 +02:00
lt = Some(quote!('a));
Some(quote!('a))
} else {
None
};
2021-05-06 19:40:37 +02:00
module_items.push(quote!(
#[doc(inline)]
2021-07-06 22:47:48 +02:00
pub use super::#ident as LocalResources;
));
2021-07-06 22:47:48 +02:00
fields.push(quote!(
/// Local Resources this task has access to
pub local: #name::LocalResources<#lt>
));
values.push(quote!(local: #name::LocalResources::new()));
}
2021-07-06 22:47:48 +02:00
if ctxt.has_shared_resources(app) {
let ident = util::shared_resources_ident(ctxt, app);
2021-07-07 21:03:56 +02:00
let lt = if shared_resources_tick {
lt = Some(quote!('a));
Some(quote!('a))
} else {
None
};
2021-05-06 19:40:37 +02:00
module_items.push(quote!(
#[doc(inline)]
2021-07-06 22:47:48 +02:00
pub use super::#ident as SharedResources;
));
fields.push(quote!(
2021-07-06 22:47:48 +02:00
/// Shared Resources this task has access to
pub shared: #name::SharedResources<#lt>
));
let priority = if ctxt.is_init() {
None
} else {
Some(quote!(priority))
};
2021-07-06 22:47:48 +02:00
values.push(quote!(shared: #name::SharedResources::new(#priority)));
}
2020-08-27 13:21:56 +02:00
if let Context::Init = ctxt {
2020-12-10 20:33:13 +01:00
let monotonic_types: Vec<_> = app
.monotonics
.iter()
.map(|(_, monotonic)| {
let mono = &monotonic.ty;
quote! {#mono}
2020-12-10 20:33:13 +01:00
})
.collect();
2021-05-06 19:40:37 +02:00
let internal_monotonics_ident = util::mark_internal_name("Monotonics");
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)]
#[allow(non_camel_case_types)]
2021-05-06 19:40:37 +02:00
pub struct #internal_monotonics_ident(
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
));
2021-05-06 19:40:37 +02:00
module_items.push(quote!(
pub use super::#internal_monotonics_ident as Monotonics;
));
}
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",
};
2021-05-06 19:40:37 +02:00
let v = Vec::new();
let cfgs = match ctxt {
Context::HardwareTask(t) => {
&app.hardware_tasks[t].cfgs
// ...
2021-05-27 18:06:28 +02:00
}
2021-05-06 19:40:37 +02:00
Context::SoftwareTask(t) => {
&app.software_tasks[t].cfgs
// ...
2021-05-27 18:06:28 +02:00
}
2021-05-06 19:40:37 +02:00
_ => &v,
};
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))
};
2021-05-06 19:40:37 +02:00
let internal_context_name = util::internal_task_ident(name, "Context");
items.push(quote!(
2021-05-06 19:40:37 +02:00
#(#cfgs)*
/// Execution context
#[allow(non_snake_case)]
#[allow(non_camel_case_types)]
2021-05-06 19:40:37 +02:00
pub struct #internal_context_name<#lt> {
#(#fields,)*
}
2021-05-06 19:40:37 +02:00
#(#cfgs)*
impl<#lt> #internal_context_name<#lt> {
#[inline(always)]
2020-12-10 20:33:13 +01:00
pub unsafe fn new(#core #priority) -> Self {
2021-05-06 19:40:37 +02:00
#internal_context_name {
#(#values,)*
}
}
}
));
2021-05-06 19:40:37 +02:00
module_items.push(quote!(
#(#cfgs)*
pub use super::#internal_context_name as Context;
));
2020-10-05 21:57:44 +02:00
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
2021-05-06 19:40:37 +02:00
let internal_spawn_ident = util::internal_task_ident(name, "spawn");
2020-10-11 18:38:38 +02:00
// Spawn caller
2020-10-05 21:57:44 +02:00
items.push(quote!(
2020-10-05 21:57:44 +02:00
#(#cfgs)*
/// Spawns the task directly
2021-05-06 19:40:37 +02:00
pub fn #internal_spawn_ident(#(#args,)*) -> Result<(), #ty> {
2020-10-05 21:57:44 +02:00
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(|_| (&mut *#fq.get_mut()).dequeue()) {
(&mut *#inputs
.get_mut())
2020-10-05 21:57:44 +02:00
.get_unchecked_mut(usize::from(index))
.as_mut_ptr()
.write(input);
2020-10-11 18:38:38 +02:00
rtic::export::interrupt::free(|_| {
(&mut *#rq.get_mut()).enqueue_unchecked((#t::#name, index));
2020-10-11 18:38:38 +02:00
});
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
2021-05-06 19:40:37 +02:00
module_items.push(quote!(
#(#cfgs)*
pub use super::#internal_spawn_ident as spawn;
));
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_ident = util::monotonic_ident(&monotonic_name);
let m_isr = &monotonic.args.binds;
let enum_ = util::interrupt_ident();
2022-02-09 18:56:35 +01:00
let spawn_handle_string = format!("{}::SpawnHandle", m);
2020-12-10 20:33:13 +01:00
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-09-28 10:18:43 +02:00
quote!(core::mem::transmute::<_, rtic::export::SYST>(()).enable_interrupt()),
quote!(rtic::export::SCB::set_pendst()),
2020-12-13 14:52:16 +01:00
)
} else {
let rt_err = util::rt_err_ident();
(
2021-05-06 19:40:37 +02:00
quote!(rtic::export::NVIC::unmask(#rt_err::#enum_::#m_isr)),
quote!(rtic::pend(#rt_err::#enum_::#m_isr)),
2020-12-13 14:52:16 +01:00
)
};
let tq_marker = &util::timer_queue_marker_ident();
2021-04-22 18:38:42 +02:00
// For future use
2021-04-08 18:25:09 +02:00
// let doc = format!(" RTIC internal: {}:{}", file!(), line!());
// items.push(quote!(#[doc = #doc]));
2021-05-06 19:40:37 +02:00
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!(
pub use #m::spawn_after;
pub use #m::spawn_at;
pub use #m::SpawnHandle;
));
}
module_items.push(quote!(
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;
}
));
2021-04-08 18:25:09 +02:00
2020-12-10 20:33:13 +01:00
items.push(quote!(
#(#cfgs)*
#[allow(non_snake_case)]
#[allow(non_camel_case_types)]
2021-05-06 19:40:37 +02:00
pub struct #internal_spawn_handle_ident {
#[doc(hidden)]
marker: u32,
}
2021-09-22 13:22:45 +02:00
impl core::fmt::Debug for #internal_spawn_handle_ident {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct(#spawn_handle_string).finish()
}
}
#(#cfgs)*
2021-05-06 19:40:37 +02:00
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())
2021-04-08 18:25:09 +02:00
.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(())
}
})
}
#[inline]
2021-10-31 10:09:40 +01:00
pub fn reschedule_after(
self,
duration: <#m as rtic::Monotonic>::Duration
) -> Result<Self, ()> {
2021-05-06 19:40:37 +02:00
self.reschedule_at(monotonics::#m::now() + duration)
}
2021-10-31 10:09:40 +01:00
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());
2021-05-06 19:40:37 +02:00
tq.update_marker(self.marker, marker, instant, || #pend).map(|_| #name::#m::SpawnHandle { marker })
})
}
}
2020-12-10 20:33:13 +01:00
#(#cfgs)*
/// Spawns the task after a set duration relative to the current time
2021-03-02 19:31:47 +01:00
///
/// 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]`
2021-08-20 09:21:02 +02:00
#[allow(non_snake_case)]
2021-10-31 10:09:40 +01:00
pub fn #internal_spawn_after_ident(
duration: <#m as rtic::Monotonic>::Duration
2020-12-10 20:33:13 +01:00
#(,#args)*
2021-05-06 19:40:37 +02:00
) -> Result<#name::#m::SpawnHandle, #ty>
2021-02-04 20:22:02 +01:00
{
2021-10-31 10:09:40 +01:00
let instant = monotonics::#m::now();
2020-12-10 20:33:13 +01:00
2021-05-06 19:40:37 +02:00
#internal_spawn_at_ident(instant + duration #(,#untupled)*)
2020-12-10 20:33:13 +01:00
}
#(#cfgs)*
/// Spawns the task at a fixed time instant
2021-08-20 09:21:02 +02:00
#[allow(non_snake_case)]
2021-05-06 19:40:37 +02:00
pub fn #internal_spawn_at_ident(
2021-10-31 10:09:40 +01:00
instant: <#m as rtic::Monotonic>::Instant
2020-12-10 20:33:13 +01:00
#(,#args)*
2021-05-06 19:40:37 +02:00
) -> Result<#name::#m::SpawnHandle, #ty> {
2020-12-10 20:33:13 +01:00
unsafe {
let input = #tupled;
if let Some(index) = rtic::export::interrupt::free(|_| (&mut *#fq.get_mut()).dequeue()) {
(&mut *#inputs
.get_mut())
2020-12-10 20:33:13 +01:00
.get_unchecked_mut(usize::from(index))
.as_mut_ptr()
.write(input);
(&mut *#instants
.get_mut())
2020-12-10 20:33:13 +01:00
.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,
2021-05-06 19:40:37 +02:00
task: #t::#name,
marker,
};
#tq_marker.get_mut().write(#tq_marker.get().read().wrapping_add(1));
2020-12-10 20:33:13 +01:00
let tq = &mut *#tq.get_mut();
2021-04-08 09:12:08 +02:00
tq.enqueue_unchecked(
nr,
|| #enable_interrupt,
|| #pend,
(&mut *#m_ident.get_mut()).as_mut());
2020-12-10 20:33:13 +01:00
2021-05-06 19:40:37 +02:00
Ok(#name::#m::SpawnHandle { marker })
})
2020-12-10 20:33:13 +01:00
} else {
Err(input)
}
}
}
2021-05-06 19:40:37 +02:00
));
2020-12-10 20:33:13 +01:00
}
2020-10-05 21:57:44 +02:00
}
2022-02-18 19:38:48 +01:00
if items.is_empty() {
quote!()
} else {
quote!(
2021-05-06 19:40:37 +02:00
#(#items)*
#[allow(non_snake_case)]
#(#task_cfgs)*
#[doc = #doc]
pub mod #name {
2021-05-06 19:40:37 +02:00
#(#module_items)*
}
)
}
}