mirror of
https://github.com/rtic-rs/rtic.git
synced 2024-11-30 15:34:33 +01:00
Min codegen
This commit is contained in:
parent
d7ed7a8b9f
commit
f8352122a3
22 changed files with 129 additions and 1694 deletions
|
@ -10,8 +10,7 @@ use syn::Ident;
|
||||||
/// Extend the upstream `Analysis` struct with our field
|
/// Extend the upstream `Analysis` struct with our field
|
||||||
pub struct Analysis {
|
pub struct Analysis {
|
||||||
parent: analyze::Analysis,
|
parent: analyze::Analysis,
|
||||||
pub interrupts_normal: BTreeMap<Priority, (Ident, Dispatcher)>,
|
pub interrupts: BTreeMap<Priority, (Ident, Dispatcher)>,
|
||||||
pub interrupts_async: BTreeMap<Priority, (Ident, Dispatcher)>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ops::Deref for Analysis {
|
impl ops::Deref for Analysis {
|
||||||
|
@ -30,27 +29,12 @@ pub fn app(analysis: analyze::Analysis, app: &App) -> Analysis {
|
||||||
let priorities = app
|
let priorities = app
|
||||||
.software_tasks
|
.software_tasks
|
||||||
.values()
|
.values()
|
||||||
.filter(|task| !task.is_async)
|
|
||||||
.map(|task| task.args.priority)
|
|
||||||
.collect::<BTreeSet<_>>();
|
|
||||||
|
|
||||||
let priorities_async = app
|
|
||||||
.software_tasks
|
|
||||||
.values()
|
|
||||||
.filter(|task| task.is_async)
|
|
||||||
.map(|task| task.args.priority)
|
.map(|task| task.args.priority)
|
||||||
.collect::<BTreeSet<_>>();
|
.collect::<BTreeSet<_>>();
|
||||||
|
|
||||||
// map from priorities to interrupts (holding name and attributes)
|
// map from priorities to interrupts (holding name and attributes)
|
||||||
|
|
||||||
let interrupts_normal: BTreeMap<Priority, _> = priorities
|
let interrupts: BTreeMap<Priority, _> = priorities
|
||||||
.iter()
|
|
||||||
.copied()
|
|
||||||
.rev()
|
|
||||||
.map(|p| (p, available_interrupt.pop().expect("UNREACHABLE")))
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
let interrupts_async: BTreeMap<Priority, _> = priorities_async
|
|
||||||
.iter()
|
.iter()
|
||||||
.copied()
|
.copied()
|
||||||
.rev()
|
.rev()
|
||||||
|
@ -59,7 +43,6 @@ pub fn app(analysis: analyze::Analysis, app: &App) -> Analysis {
|
||||||
|
|
||||||
Analysis {
|
Analysis {
|
||||||
parent: analysis,
|
parent: analysis,
|
||||||
interrupts_normal,
|
interrupts,
|
||||||
interrupts_async,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,20 +6,20 @@ use crate::syntax::ast::App;
|
||||||
|
|
||||||
mod assertions;
|
mod assertions;
|
||||||
mod async_dispatchers;
|
mod async_dispatchers;
|
||||||
mod dispatchers;
|
// mod dispatchers;
|
||||||
mod hardware_tasks;
|
mod hardware_tasks;
|
||||||
mod idle;
|
mod idle;
|
||||||
mod init;
|
mod init;
|
||||||
mod local_resources;
|
mod local_resources;
|
||||||
mod local_resources_struct;
|
mod local_resources_struct;
|
||||||
mod module;
|
mod module;
|
||||||
mod monotonic;
|
// mod monotonic;
|
||||||
mod post_init;
|
mod post_init;
|
||||||
mod pre_init;
|
mod pre_init;
|
||||||
mod shared_resources;
|
mod shared_resources;
|
||||||
mod shared_resources_struct;
|
mod shared_resources_struct;
|
||||||
mod software_tasks;
|
// mod software_tasks;
|
||||||
mod timer_queue;
|
// mod timer_queue;
|
||||||
mod util;
|
mod util;
|
||||||
|
|
||||||
#[allow(clippy::too_many_lines)]
|
#[allow(clippy::too_many_lines)]
|
||||||
|
@ -92,14 +92,7 @@ pub fn app(app: &App, analysis: &Analysis) -> TokenStream2 {
|
||||||
let (mod_app_hardware_tasks, root_hardware_tasks, user_hardware_tasks) =
|
let (mod_app_hardware_tasks, root_hardware_tasks, user_hardware_tasks) =
|
||||||
hardware_tasks::codegen(app, analysis);
|
hardware_tasks::codegen(app, analysis);
|
||||||
|
|
||||||
let (mod_app_software_tasks, root_software_tasks, user_software_tasks) =
|
|
||||||
software_tasks::codegen(app, analysis);
|
|
||||||
|
|
||||||
let monotonics = monotonic::codegen(app, analysis);
|
|
||||||
|
|
||||||
let mod_app_dispatchers = dispatchers::codegen(app, analysis);
|
|
||||||
let mod_app_async_dispatchers = async_dispatchers::codegen(app, analysis);
|
let mod_app_async_dispatchers = async_dispatchers::codegen(app, analysis);
|
||||||
let mod_app_timer_queue = timer_queue::codegen(app, analysis);
|
|
||||||
let user_imports = &app.user_imports;
|
let user_imports = &app.user_imports;
|
||||||
let user_code = &app.user_code;
|
let user_code = &app.user_code;
|
||||||
let name = &app.name;
|
let name = &app.name;
|
||||||
|
@ -113,8 +106,6 @@ pub fn app(app: &App, analysis: &Analysis) -> TokenStream2 {
|
||||||
/// Always include the device crate which contains the vector table
|
/// Always include the device crate which contains the vector table
|
||||||
use #device as #rt_err;
|
use #device as #rt_err;
|
||||||
|
|
||||||
#monotonics
|
|
||||||
|
|
||||||
#(#user_imports)*
|
#(#user_imports)*
|
||||||
|
|
||||||
/// User code from within the module
|
/// User code from within the module
|
||||||
|
@ -125,8 +116,6 @@ pub fn app(app: &App, analysis: &Analysis) -> TokenStream2 {
|
||||||
|
|
||||||
#(#user_hardware_tasks)*
|
#(#user_hardware_tasks)*
|
||||||
|
|
||||||
#(#user_software_tasks)*
|
|
||||||
|
|
||||||
#(#root)*
|
#(#root)*
|
||||||
|
|
||||||
#mod_shared_resources
|
#mod_shared_resources
|
||||||
|
@ -135,9 +124,7 @@ pub fn app(app: &App, analysis: &Analysis) -> TokenStream2 {
|
||||||
|
|
||||||
#(#root_hardware_tasks)*
|
#(#root_hardware_tasks)*
|
||||||
|
|
||||||
#(#root_software_tasks)*
|
/// app module
|
||||||
|
|
||||||
/// App module
|
|
||||||
#(#mod_app)*
|
#(#mod_app)*
|
||||||
|
|
||||||
#(#mod_app_shared_resources)*
|
#(#mod_app_shared_resources)*
|
||||||
|
@ -146,14 +133,8 @@ pub fn app(app: &App, analysis: &Analysis) -> TokenStream2 {
|
||||||
|
|
||||||
#(#mod_app_hardware_tasks)*
|
#(#mod_app_hardware_tasks)*
|
||||||
|
|
||||||
#(#mod_app_software_tasks)*
|
|
||||||
|
|
||||||
#(#mod_app_dispatchers)*
|
|
||||||
|
|
||||||
#(#mod_app_async_dispatchers)*
|
#(#mod_app_async_dispatchers)*
|
||||||
|
|
||||||
#(#mod_app_timer_queue)*
|
|
||||||
|
|
||||||
#(#mains)*
|
#(#mains)*
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
|
@ -16,11 +16,6 @@ pub fn codegen(app: &App, analysis: &Analysis) -> Vec<TokenStream2> {
|
||||||
stmts.push(quote!(rtic::export::assert_sync::<#ty>();));
|
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 = &app.args.device;
|
let device = &app.args.device;
|
||||||
let chunks_name = util::priority_mask_chunks_ident();
|
let chunks_name = util::priority_mask_chunks_ident();
|
||||||
let no_basepri_checks: Vec<_> = app
|
let no_basepri_checks: Vec<_> = app
|
||||||
|
|
|
@ -7,65 +7,47 @@ use quote::quote;
|
||||||
pub fn codegen(app: &App, analysis: &Analysis) -> Vec<TokenStream2> {
|
pub fn codegen(app: &App, analysis: &Analysis) -> Vec<TokenStream2> {
|
||||||
let mut items = vec![];
|
let mut items = vec![];
|
||||||
|
|
||||||
let interrupts = &analysis.interrupts_async;
|
let interrupts = &analysis.interrupts;
|
||||||
|
|
||||||
// Generate executor definition and priority in global scope
|
// Generate executor definition and priority in global scope
|
||||||
for (name, task) in app.software_tasks.iter() {
|
for (name, _) in app.software_tasks.iter() {
|
||||||
if task.is_async {
|
let type_name = util::internal_task_ident(name, "F");
|
||||||
let type_name = util::internal_task_ident(name, "F");
|
let exec_name = util::internal_task_ident(name, "EXEC");
|
||||||
let exec_name = util::internal_task_ident(name, "EXEC");
|
let prio_name = util::internal_task_ident(name, "PRIORITY");
|
||||||
let prio_name = util::internal_task_ident(name, "PRIORITY");
|
|
||||||
|
|
||||||
items.push(quote!(
|
items.push(quote!(
|
||||||
#[allow(non_camel_case_types)]
|
#[allow(non_camel_case_types)]
|
||||||
type #type_name = impl core::future::Future + 'static;
|
type #type_name = impl core::future::Future + 'static;
|
||||||
#[allow(non_upper_case_globals)]
|
#[allow(non_upper_case_globals)]
|
||||||
static #exec_name:
|
static #exec_name:
|
||||||
rtic::RacyCell<rtic::export::executor::AsyncTaskExecutor<#type_name>> =
|
rtic::RacyCell<rtic::export::executor::AsyncTaskExecutor<#type_name>> =
|
||||||
rtic::RacyCell::new(rtic::export::executor::AsyncTaskExecutor::new());
|
rtic::RacyCell::new(rtic::export::executor::AsyncTaskExecutor::new());
|
||||||
|
|
||||||
// The executors priority, this can be any value - we will overwrite it when we
|
// The executors priority, this can be any value - we will overwrite it when we
|
||||||
// start a task
|
// start a task
|
||||||
#[allow(non_upper_case_globals)]
|
#[allow(non_upper_case_globals)]
|
||||||
static #prio_name: rtic::RacyCell<rtic::export::Priority> =
|
static #prio_name: rtic::RacyCell<rtic::export::Priority> =
|
||||||
unsafe { rtic::RacyCell::new(rtic::export::Priority::new(0)) };
|
unsafe { rtic::RacyCell::new(rtic::export::Priority::new(0)) };
|
||||||
));
|
));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (&level, channel) in &analysis.channels {
|
for (&level, channel) in &analysis.channels {
|
||||||
if channel
|
|
||||||
.tasks
|
|
||||||
.iter()
|
|
||||||
.map(|task_name| !app.software_tasks[task_name].is_async)
|
|
||||||
.all(|is_not_async| is_not_async)
|
|
||||||
{
|
|
||||||
// check if all tasks are not async, if so don't generate this.
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut stmts = vec![];
|
let mut stmts = vec![];
|
||||||
let device = &app.args.device;
|
let device = &app.args.device;
|
||||||
let enum_ = util::interrupt_ident();
|
let enum_ = util::interrupt_ident();
|
||||||
let interrupt = util::suffixed(&interrupts[&level].0.to_string());
|
let interrupt = util::suffixed(&interrupts[&level].0.to_string());
|
||||||
|
|
||||||
for name in channel
|
for name in channel.tasks.iter() {
|
||||||
.tasks
|
|
||||||
.iter()
|
|
||||||
.filter(|name| app.software_tasks[*name].is_async)
|
|
||||||
{
|
|
||||||
let exec_name = util::internal_task_ident(name, "EXEC");
|
let exec_name = util::internal_task_ident(name, "EXEC");
|
||||||
let prio_name = util::internal_task_ident(name, "PRIORITY");
|
let prio_name = util::internal_task_ident(name, "PRIORITY");
|
||||||
let task = &app.software_tasks[name];
|
// let task = &app.software_tasks[name];
|
||||||
// let cfgs = &task.cfgs;
|
// let cfgs = &task.cfgs;
|
||||||
let (_, tupled, pats, input_types) = util::regroup_inputs(&task.inputs);
|
|
||||||
let executor_run_ident = util::executor_run_ident(name);
|
let executor_run_ident = util::executor_run_ident(name);
|
||||||
|
|
||||||
let n = util::capacity_literal(channel.capacity as usize + 1);
|
|
||||||
let rq = util::rq_async_ident(name);
|
let rq = util::rq_async_ident(name);
|
||||||
let (rq_ty, rq_expr) = {
|
let (rq_ty, rq_expr) = {
|
||||||
(
|
(
|
||||||
quote!(rtic::export::ASYNCRQ<#input_types, #n>),
|
quote!(rtic::export::ASYNCRQ<(), 2>), // TODO: This needs updating to a counter instead of a queue
|
||||||
quote!(rtic::export::Queue::new()),
|
quote!(rtic::export::Queue::new()),
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
@ -79,13 +61,13 @@ pub fn codegen(app: &App, analysis: &Analysis) -> Vec<TokenStream2> {
|
||||||
|
|
||||||
stmts.push(quote!(
|
stmts.push(quote!(
|
||||||
if !(&*#exec_name.get()).is_running() {
|
if !(&*#exec_name.get()).is_running() {
|
||||||
if let Some(#tupled) = rtic::export::interrupt::free(|_| (&mut *#rq.get_mut()).dequeue()) {
|
if let Some(()) = rtic::export::interrupt::free(|_| (&mut *#rq.get_mut()).dequeue()) {
|
||||||
|
|
||||||
// The async executor needs a static priority
|
// The async executor needs a static priority
|
||||||
#prio_name.get_mut().write(rtic::export::Priority::new(PRIORITY));
|
#prio_name.get_mut().write(rtic::export::Priority::new(PRIORITY));
|
||||||
let priority: &'static _ = &*#prio_name.get();
|
let priority: &'static _ = &*#prio_name.get();
|
||||||
|
|
||||||
(&mut *#exec_name.get_mut()).spawn(#name(#name::Context::new(priority) #(,#pats)*));
|
(&mut *#exec_name.get_mut()).spawn(#name(#name::Context::new(priority)));
|
||||||
#executor_run_ident.store(true, core::sync::atomic::Ordering::Relaxed);
|
#executor_run_ident.store(true, core::sync::atomic::Ordering::Relaxed);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,146 +0,0 @@
|
||||||
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) -> Vec<TokenStream2> {
|
|
||||||
let mut items = vec![];
|
|
||||||
|
|
||||||
let interrupts = &analysis.interrupts_normal;
|
|
||||||
|
|
||||||
for (&level, channel) in &analysis.channels {
|
|
||||||
if channel
|
|
||||||
.tasks
|
|
||||||
.iter()
|
|
||||||
.map(|task_name| app.software_tasks[task_name].is_async)
|
|
||||||
.all(|is_async| is_async)
|
|
||||||
{
|
|
||||||
// check if all tasks are async, if so don't generate this.
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut stmts = vec![];
|
|
||||||
|
|
||||||
let variants = channel
|
|
||||||
.tasks
|
|
||||||
.iter()
|
|
||||||
.filter(|name| !app.software_tasks[*name].is_async)
|
|
||||||
.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 (_, _, _, input_ty) = util::regroup_inputs(inputs);
|
|
||||||
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 interrupt = util::suffixed(
|
|
||||||
&interrupts
|
|
||||||
.get(&level)
|
|
||||||
.expect("RTIC-ICE: Unable to get interrrupt")
|
|
||||||
.0
|
|
||||||
.to_string(),
|
|
||||||
);
|
|
||||||
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);
|
|
||||||
|
|
||||||
if !task.is_async {
|
|
||||||
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)*
|
|
||||||
)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
quote!()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.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 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
|
|
||||||
}
|
|
|
@ -102,33 +102,6 @@ pub fn codegen(
|
||||||
values.push(quote!(shared: #name::SharedResources::new(#priority)));
|
values.push(quote!(shared: #name::SharedResources::new(#priority)));
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Context::Init = ctxt {
|
|
||||||
let monotonic_types: Vec<_> = app
|
|
||||||
.monotonics
|
|
||||||
.iter()
|
|
||||||
.map(|(_, monotonic)| {
|
|
||||||
let mono = &monotonic.ty;
|
|
||||||
quote! {#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(
|
|
||||||
#(pub #monotonic_types),*
|
|
||||||
);
|
|
||||||
));
|
|
||||||
|
|
||||||
module_items.push(quote!(
|
|
||||||
#[doc(inline)]
|
|
||||||
pub use super::#internal_monotonics_ident as Monotonics;
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
let doc = match ctxt {
|
let doc = match ctxt {
|
||||||
Context::Idle => "Idle loop",
|
Context::Idle => "Idle loop",
|
||||||
Context::Init => "Initialization function",
|
Context::Init => "Initialization function",
|
||||||
|
@ -192,280 +165,45 @@ pub fn codegen(
|
||||||
if let Context::SoftwareTask(..) = ctxt {
|
if let Context::SoftwareTask(..) = ctxt {
|
||||||
let spawnee = &app.software_tasks[name];
|
let spawnee = &app.software_tasks[name];
|
||||||
let priority = spawnee.args.priority;
|
let priority = spawnee.args.priority;
|
||||||
let t = util::spawn_t_ident(priority);
|
|
||||||
let cfgs = &spawnee.cfgs;
|
let cfgs = &spawnee.cfgs;
|
||||||
// Store a copy of the task cfgs
|
// Store a copy of the task cfgs
|
||||||
task_cfgs = cfgs.clone();
|
task_cfgs = cfgs.clone();
|
||||||
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 = &app.args.device;
|
let device = &app.args.device;
|
||||||
let enum_ = util::interrupt_ident();
|
let enum_ = util::interrupt_ident();
|
||||||
let interrupt = if spawnee.is_async {
|
let interrupt = &analysis
|
||||||
&analysis
|
.interrupts
|
||||||
.interrupts_async
|
.get(&priority)
|
||||||
.get(&priority)
|
.expect("RTIC-ICE: interrupt identifer not found")
|
||||||
.expect("RTIC-ICE: interrupt identifer not found")
|
.0;
|
||||||
.0
|
|
||||||
} else {
|
|
||||||
&analysis
|
|
||||||
.interrupts_normal
|
|
||||||
.get(&priority)
|
|
||||||
.expect("RTIC-ICE: interrupt identifer not found")
|
|
||||||
.0
|
|
||||||
};
|
|
||||||
|
|
||||||
let internal_spawn_ident = util::internal_task_ident(name, "spawn");
|
let internal_spawn_ident = util::internal_task_ident(name, "spawn");
|
||||||
|
|
||||||
// Spawn caller
|
// Spawn caller
|
||||||
if spawnee.is_async {
|
let rq = util::rq_async_ident(name);
|
||||||
let rq = util::rq_async_ident(name);
|
items.push(quote!(
|
||||||
items.push(quote!(
|
|
||||||
|
|
||||||
#(#cfgs)*
|
#(#cfgs)*
|
||||||
/// Spawns the task directly
|
/// Spawns the task directly
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub fn #internal_spawn_ident(#(#args,)*) -> Result<(), #ty> {
|
pub fn #internal_spawn_ident() -> Result<(), ()> {
|
||||||
let input = #tupled;
|
unsafe {
|
||||||
|
let r = rtic::export::interrupt::free(|_| (&mut *#rq.get_mut()).enqueue(()));
|
||||||
|
|
||||||
unsafe {
|
if r.is_ok() {
|
||||||
let r = rtic::export::interrupt::free(|_| (&mut *#rq.get_mut()).enqueue(input));
|
rtic::pend(#device::#enum_::#interrupt);
|
||||||
|
|
||||||
if r.is_ok() {
|
|
||||||
rtic::pend(#device::#enum_::#interrupt);
|
|
||||||
}
|
|
||||||
|
|
||||||
r
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
} else {
|
|
||||||
items.push(quote!(
|
|
||||||
|
|
||||||
#(#cfgs)*
|
|
||||||
/// Spawns the task directly
|
|
||||||
#[allow(non_snake_case)]
|
|
||||||
#[doc(hidden)]
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}));
|
r
|
||||||
}
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
module_items.push(quote!(
|
module_items.push(quote!(
|
||||||
#(#cfgs)*
|
#(#cfgs)*
|
||||||
#[doc(inline)]
|
#[doc(inline)]
|
||||||
pub use super::#internal_spawn_ident as spawn;
|
pub use super::#internal_spawn_ident as spawn;
|
||||||
));
|
));
|
||||||
|
|
||||||
// Schedule caller
|
|
||||||
if !spawnee.is_async {
|
|
||||||
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 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();
|
|
||||||
|
|
||||||
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!(
|
|
||||||
#[doc(inline)]
|
|
||||||
pub use #m::spawn_after;
|
|
||||||
#[doc(inline)]
|
|
||||||
pub use #m::spawn_at;
|
|
||||||
#[doc(inline)]
|
|
||||||
pub use #m::SpawnHandle;
|
|
||||||
));
|
|
||||||
}
|
|
||||||
module_items.push(quote!(
|
|
||||||
pub mod #m {
|
|
||||||
#[doc(inline)]
|
|
||||||
pub use super::super::#internal_spawn_after_ident as spawn_after;
|
|
||||||
#[doc(inline)]
|
|
||||||
pub use super::super::#internal_spawn_at_ident as spawn_at;
|
|
||||||
#[doc(inline)]
|
|
||||||
pub use super::super::#internal_spawn_handle_ident as SpawnHandle;
|
|
||||||
}
|
|
||||||
));
|
|
||||||
|
|
||||||
items.push(quote!(
|
|
||||||
#(#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 {
|
|
||||||
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_task_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(())
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn reschedule_after(
|
|
||||||
self,
|
|
||||||
duration: <#m as rtic::Monotonic>::Duration
|
|
||||||
) -> Result<Self, ()> {
|
|
||||||
self.reschedule_at(monotonics::#m::now() + duration)
|
|
||||||
}
|
|
||||||
|
|
||||||
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_task_marker(self.marker, marker, instant, || #pend).map(|_| #name::#m::SpawnHandle { marker })
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#(#cfgs)*
|
|
||||||
/// 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]`
|
|
||||||
#[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::TaskNotReady {
|
|
||||||
task: #t::#name,
|
|
||||||
index,
|
|
||||||
instant,
|
|
||||||
marker,
|
|
||||||
};
|
|
||||||
|
|
||||||
#tq_marker.get_mut().write(#tq_marker.get().read().wrapping_add(1));
|
|
||||||
|
|
||||||
let tq = &mut *#tq.get_mut();
|
|
||||||
|
|
||||||
tq.enqueue_task_unchecked(
|
|
||||||
nr,
|
|
||||||
|| #enable_interrupt,
|
|
||||||
|| #pend,
|
|
||||||
(&mut *#m_ident.get_mut()).as_mut());
|
|
||||||
|
|
||||||
Ok(#name::#m::SpawnHandle { marker })
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
Err(input)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if items.is_empty() {
|
if items.is_empty() {
|
||||||
|
|
|
@ -1,280 +0,0 @@
|
||||||
use crate::syntax::ast::App;
|
|
||||||
use proc_macro2::TokenStream as TokenStream2;
|
|
||||||
use quote::quote;
|
|
||||||
|
|
||||||
use crate::{analyze::Analysis, codegen::util};
|
|
||||||
|
|
||||||
/// Generates monotonic module dispatchers
|
|
||||||
pub fn codegen(app: &App, _analysis: &Analysis) -> TokenStream2 {
|
|
||||||
let mut monotonic_parts: Vec<_> = Vec::new();
|
|
||||||
|
|
||||||
let tq_marker = util::timer_queue_marker_ident();
|
|
||||||
|
|
||||||
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_name);
|
|
||||||
let m = &monotonic.ident;
|
|
||||||
let m_ident = util::monotonic_ident(&monotonic_name);
|
|
||||||
let m_isr = &monotonic.args.binds;
|
|
||||||
let enum_ = util::interrupt_ident();
|
|
||||||
let name_str = &m.to_string();
|
|
||||||
let ident = util::monotonic_ident(name_str);
|
|
||||||
let doc = &format!(
|
|
||||||
"This module holds the static implementation for `{}::now()`",
|
|
||||||
name_str
|
|
||||||
);
|
|
||||||
|
|
||||||
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(super::super::#rt_err::#enum_::#m_isr)),
|
|
||||||
quote!(rtic::pend(super::super::#rt_err::#enum_::#m_isr)),
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
let default_monotonic = if monotonic.args.default {
|
|
||||||
quote!(
|
|
||||||
#[doc(inline)]
|
|
||||||
pub use #m::now;
|
|
||||||
#[doc(inline)]
|
|
||||||
pub use #m::delay;
|
|
||||||
#[doc(inline)]
|
|
||||||
pub use #m::delay_until;
|
|
||||||
#[doc(inline)]
|
|
||||||
pub use #m::timeout_at;
|
|
||||||
#[doc(inline)]
|
|
||||||
pub use #m::timeout_after;
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
quote!()
|
|
||||||
};
|
|
||||||
|
|
||||||
monotonic_parts.push(quote! {
|
|
||||||
#default_monotonic
|
|
||||||
|
|
||||||
#[doc = #doc]
|
|
||||||
#[allow(non_snake_case)]
|
|
||||||
pub mod #m {
|
|
||||||
/// Read the current time from this monotonic
|
|
||||||
pub fn now() -> <super::super::#m 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::#m as rtic::Monotonic>::zero()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Delay
|
|
||||||
#[inline(always)]
|
|
||||||
#[allow(non_snake_case)]
|
|
||||||
pub fn delay(duration: <super::super::#m as rtic::Monotonic>::Duration)
|
|
||||||
-> DelayFuture {
|
|
||||||
let until = now() + duration;
|
|
||||||
DelayFuture { until, waker_storage: None }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Delay until a specific time
|
|
||||||
#[inline(always)]
|
|
||||||
#[allow(non_snake_case)]
|
|
||||||
pub fn delay_until(instant: <super::super::#m as rtic::Monotonic>::Instant)
|
|
||||||
-> DelayFuture {
|
|
||||||
let until = instant;
|
|
||||||
DelayFuture { until, waker_storage: None }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Delay future.
|
|
||||||
#[allow(non_snake_case)]
|
|
||||||
#[allow(non_camel_case_types)]
|
|
||||||
pub struct DelayFuture {
|
|
||||||
until: <super::super::#m as rtic::Monotonic>::Instant,
|
|
||||||
waker_storage: Option<rtic::export::IntrusiveNode<rtic::export::WakerNotReady<super::super::#m>>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Drop for DelayFuture {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
if let Some(waker_storage) = &mut self.waker_storage {
|
|
||||||
rtic::export::interrupt::free(|_| unsafe {
|
|
||||||
let tq = &mut *super::super::#tq.get_mut();
|
|
||||||
tq.cancel_waker_marker(waker_storage.val.marker);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl core::future::Future for DelayFuture {
|
|
||||||
type Output = ();
|
|
||||||
|
|
||||||
fn poll(
|
|
||||||
mut self: core::pin::Pin<&mut Self>,
|
|
||||||
cx: &mut core::task::Context<'_>
|
|
||||||
) -> core::task::Poll<Self::Output> {
|
|
||||||
let mut s = self.as_mut();
|
|
||||||
let now = now();
|
|
||||||
let until = s.until;
|
|
||||||
let is_ws_none = s.waker_storage.is_none();
|
|
||||||
|
|
||||||
if now >= until {
|
|
||||||
return core::task::Poll::Ready(());
|
|
||||||
} else if is_ws_none {
|
|
||||||
rtic::export::interrupt::free(|_| unsafe {
|
|
||||||
let marker = super::super::#tq_marker.get().read();
|
|
||||||
super::super::#tq_marker.get_mut().write(marker.wrapping_add(1));
|
|
||||||
|
|
||||||
let nr = s.waker_storage.insert(rtic::export::IntrusiveNode::new(rtic::export::WakerNotReady {
|
|
||||||
waker: cx.waker().clone(),
|
|
||||||
instant: until,
|
|
||||||
marker,
|
|
||||||
}));
|
|
||||||
|
|
||||||
let tq = &mut *super::super::#tq.get_mut();
|
|
||||||
|
|
||||||
tq.enqueue_waker(
|
|
||||||
core::mem::transmute(nr), // Transmute the reference to static
|
|
||||||
|| #enable_interrupt,
|
|
||||||
|| #pend,
|
|
||||||
(&mut *super::super::#m_ident.get_mut()).as_mut());
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
core::task::Poll::Pending
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Timeout future.
|
|
||||||
#[allow(non_snake_case)]
|
|
||||||
#[allow(non_camel_case_types)]
|
|
||||||
pub struct TimeoutFuture<F: core::future::Future> {
|
|
||||||
future: F,
|
|
||||||
until: <super::super::#m as rtic::Monotonic>::Instant,
|
|
||||||
waker_storage: Option<rtic::export::IntrusiveNode<rtic::export::WakerNotReady<super::super::#m>>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<F: core::future::Future> Drop for TimeoutFuture<F> {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
if let Some(waker_storage) = &mut self.waker_storage {
|
|
||||||
rtic::export::interrupt::free(|_| unsafe {
|
|
||||||
let tq = &mut *super::super::#tq.get_mut();
|
|
||||||
tq.cancel_waker_marker(waker_storage.val.marker);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Timeout after
|
|
||||||
#[allow(non_snake_case)]
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn timeout_after<F: core::future::Future>(
|
|
||||||
future: F,
|
|
||||||
duration: <super::super::#m as rtic::Monotonic>::Duration
|
|
||||||
) -> TimeoutFuture<F> {
|
|
||||||
let until = now() + duration;
|
|
||||||
TimeoutFuture {
|
|
||||||
future,
|
|
||||||
until,
|
|
||||||
waker_storage: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Timeout at
|
|
||||||
#[allow(non_snake_case)]
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn timeout_at<F: core::future::Future>(
|
|
||||||
future: F,
|
|
||||||
instant: <super::super::#m as rtic::Monotonic>::Instant
|
|
||||||
) -> TimeoutFuture<F> {
|
|
||||||
TimeoutFuture {
|
|
||||||
future,
|
|
||||||
until: instant,
|
|
||||||
waker_storage: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<F> core::future::Future for TimeoutFuture<F>
|
|
||||||
where
|
|
||||||
F: core::future::Future,
|
|
||||||
{
|
|
||||||
type Output = Result<F::Output, super::TimeoutError>;
|
|
||||||
|
|
||||||
fn poll(
|
|
||||||
self: core::pin::Pin<&mut Self>,
|
|
||||||
cx: &mut core::task::Context<'_>
|
|
||||||
) -> core::task::Poll<Self::Output> {
|
|
||||||
// SAFETY: We don't move the underlying pinned value.
|
|
||||||
let mut s = unsafe { self.get_unchecked_mut() };
|
|
||||||
let future = unsafe { core::pin::Pin::new_unchecked(&mut s.future) };
|
|
||||||
let now = now();
|
|
||||||
let until = s.until;
|
|
||||||
let is_ws_none = s.waker_storage.is_none();
|
|
||||||
|
|
||||||
match future.poll(cx) {
|
|
||||||
core::task::Poll::Ready(r) => {
|
|
||||||
if let Some(waker_storage) = &mut s.waker_storage {
|
|
||||||
rtic::export::interrupt::free(|_| unsafe {
|
|
||||||
let tq = &mut *super::super::#tq.get_mut();
|
|
||||||
tq.cancel_waker_marker(waker_storage.val.marker);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return core::task::Poll::Ready(Ok(r));
|
|
||||||
}
|
|
||||||
core::task::Poll::Pending => {
|
|
||||||
if now >= until {
|
|
||||||
// Timeout
|
|
||||||
return core::task::Poll::Ready(Err(super::TimeoutError));
|
|
||||||
} else if is_ws_none {
|
|
||||||
rtic::export::interrupt::free(|_| unsafe {
|
|
||||||
let marker = super::super::#tq_marker.get().read();
|
|
||||||
super::super::#tq_marker.get_mut().write(marker.wrapping_add(1));
|
|
||||||
|
|
||||||
let nr = s.waker_storage.insert(rtic::export::IntrusiveNode::new(rtic::export::WakerNotReady {
|
|
||||||
waker: cx.waker().clone(),
|
|
||||||
instant: until,
|
|
||||||
marker,
|
|
||||||
}));
|
|
||||||
|
|
||||||
let tq = &mut *super::super::#tq.get_mut();
|
|
||||||
|
|
||||||
tq.enqueue_waker(
|
|
||||||
core::mem::transmute(nr), // Transmute the reference to static
|
|
||||||
|| #enable_interrupt,
|
|
||||||
|| #pend,
|
|
||||||
(&mut *super::super::#m_ident.get_mut()).as_mut());
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
core::task::Poll::Pending
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if monotonic_parts.is_empty() {
|
|
||||||
quote!()
|
|
||||||
} else {
|
|
||||||
quote!(
|
|
||||||
pub use rtic::Monotonic as _;
|
|
||||||
|
|
||||||
/// Holds static methods for each monotonic.
|
|
||||||
pub mod monotonics {
|
|
||||||
/// A timeout error.
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct TimeoutError;
|
|
||||||
|
|
||||||
#(#monotonic_parts)*
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,7 +1,6 @@
|
||||||
use crate::syntax::ast::App;
|
use crate::syntax::ast::App;
|
||||||
use proc_macro2::{Span, TokenStream as TokenStream2};
|
use proc_macro2::TokenStream as TokenStream2;
|
||||||
use quote::quote;
|
use quote::quote;
|
||||||
use syn::Index;
|
|
||||||
|
|
||||||
use crate::{analyze::Analysis, codegen::util};
|
use crate::{analyze::Analysis, codegen::util};
|
||||||
|
|
||||||
|
@ -43,23 +42,6 @@ pub fn codegen(app: &App, analysis: &Analysis) -> Vec<TokenStream2> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i, (monotonic, _)) in app.monotonics.iter().enumerate() {
|
|
||||||
// For future use
|
|
||||||
// let doc = format!(" RTIC internal: {}:{}", file!(), line!());
|
|
||||||
// stmts.push(quote!(#[doc = #doc]));
|
|
||||||
|
|
||||||
#[allow(clippy::cast_possible_truncation)]
|
|
||||||
let idx = Index {
|
|
||||||
index: i as u32,
|
|
||||||
span: Span::call_site(),
|
|
||||||
};
|
|
||||||
stmts.push(quote!(monotonics.#idx.reset();));
|
|
||||||
|
|
||||||
// Store the monotonic
|
|
||||||
let name = util::monotonic_ident(&monotonic.to_string());
|
|
||||||
stmts.push(quote!(#name.get_mut().write(Some(monotonics.#idx));));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Enable the interrupts -- this completes the `init`-ialization phase
|
// Enable the interrupts -- this completes the `init`-ialization phase
|
||||||
stmts.push(quote!(rtic::export::interrupt::enable();));
|
stmts.push(quote!(rtic::export::interrupt::enable();));
|
||||||
|
|
||||||
|
|
|
@ -13,20 +13,6 @@ pub fn codegen(app: &App, analysis: &Analysis) -> Vec<TokenStream2> {
|
||||||
// Disable interrupts -- `init` must run with interrupts disabled
|
// Disable interrupts -- `init` must run with interrupts disabled
|
||||||
stmts.push(quote!(rtic::export::interrupt::disable();));
|
stmts.push(quote!(rtic::export::interrupt::disable();));
|
||||||
|
|
||||||
// Populate the FreeQueue
|
|
||||||
for (name, task) in &app.software_tasks {
|
|
||||||
if task.is_async {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
let cap = task.args.capacity;
|
|
||||||
let fq_ident = util::fq_ident(name);
|
|
||||||
|
|
||||||
stmts.push(quote!(
|
|
||||||
(0..#cap).for_each(|i| (&mut *#fq_ident.get_mut()).enqueue_unchecked(i));
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
stmts.push(quote!(
|
stmts.push(quote!(
|
||||||
// To set the variable in cortex_m so the peripherals cannot be taken multiple times
|
// 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 mut core: rtic::export::Peripherals = rtic::export::Peripherals::steal().into();
|
||||||
|
@ -42,11 +28,7 @@ pub fn codegen(app: &App, analysis: &Analysis) -> Vec<TokenStream2> {
|
||||||
stmts.push(quote!(let _ = #rt_err::#interrupt::#name;));
|
stmts.push(quote!(let _ = #rt_err::#interrupt::#name;));
|
||||||
}
|
}
|
||||||
|
|
||||||
let interrupt_ids = analysis
|
let interrupt_ids = analysis.interrupts.iter().map(|(p, (id, _))| (p, id));
|
||||||
.interrupts_normal
|
|
||||||
.iter()
|
|
||||||
.map(|(p, (id, _))| (p, id))
|
|
||||||
.chain(analysis.interrupts_async.iter().map(|(p, (id, _))| (p, id)));
|
|
||||||
|
|
||||||
// Unmask interrupts and set their priorities
|
// Unmask interrupts and set their priorities
|
||||||
for (&priority, name) in interrupt_ids.chain(app.hardware_tasks.values().filter_map(|task| {
|
for (&priority, name) in interrupt_ids.chain(app.hardware_tasks.values().filter_map(|task| {
|
||||||
|
@ -101,53 +83,5 @@ pub fn codegen(app: &App, analysis: &Analysis) -> Vec<TokenStream2> {
|
||||||
);));
|
);));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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
|
stmts
|
||||||
}
|
}
|
||||||
|
|
|
@ -111,11 +111,7 @@ pub fn codegen(
|
||||||
};
|
};
|
||||||
|
|
||||||
// Computing mapping of used interrupts to masks
|
// Computing mapping of used interrupts to masks
|
||||||
let interrupt_ids = analysis
|
let interrupt_ids = analysis.interrupts.iter().map(|(p, (id, _))| (p, id));
|
||||||
.interrupts_normal
|
|
||||||
.iter()
|
|
||||||
.map(|(p, (id, _))| (p, id))
|
|
||||||
.chain(analysis.interrupts_async.iter().map(|(p, (id, _))| (p, id)));
|
|
||||||
|
|
||||||
let mut prio_to_masks = HashMap::new();
|
let mut prio_to_masks = HashMap::new();
|
||||||
let device = &app.args.device;
|
let device = &app.args.device;
|
||||||
|
|
|
@ -1,179 +0,0 @@
|
||||||
use crate::syntax::{ast::App, Context};
|
|
||||||
use crate::{
|
|
||||||
analyze::Analysis,
|
|
||||||
codegen::{local_resources_struct, module, shared_resources_struct, util},
|
|
||||||
};
|
|
||||||
use proc_macro2::TokenStream as TokenStream2;
|
|
||||||
use quote::quote;
|
|
||||||
|
|
||||||
pub fn codegen(
|
|
||||||
app: &App,
|
|
||||||
analysis: &Analysis,
|
|
||||||
) -> (
|
|
||||||
// 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![];
|
|
||||||
|
|
||||||
// Any task
|
|
||||||
for (name, task) in app.software_tasks.iter() {
|
|
||||||
let inputs = &task.inputs;
|
|
||||||
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);
|
|
||||||
|
|
||||||
if !task.is_async {
|
|
||||||
// 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
|
|
||||||
#[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 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)]
|
|
||||||
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);
|
|
||||||
|
|
||||||
// Buffer that holds the inputs of a task
|
|
||||||
mod_app.push(quote!(
|
|
||||||
#uninit
|
|
||||||
#[allow(non_camel_case_types)]
|
|
||||||
#[allow(non_upper_case_globals)]
|
|
||||||
#[doc(hidden)]
|
|
||||||
static #inputs_ident: rtic::RacyCell<[core::mem::MaybeUninit<#input_ty>; #cap_lit]> =
|
|
||||||
rtic::RacyCell::new([#(#elems,)*]);
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
if task.is_async {
|
|
||||||
let executor_ident = util::executor_run_ident(name);
|
|
||||||
mod_app.push(quote!(
|
|
||||||
#[allow(non_camel_case_types)]
|
|
||||||
#[allow(non_upper_case_globals)]
|
|
||||||
#[doc(hidden)]
|
|
||||||
static #executor_ident: core::sync::atomic::AtomicBool =
|
|
||||||
core::sync::atomic::AtomicBool::new(false);
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
let inputs = &task.inputs;
|
|
||||||
|
|
||||||
// `${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 (async_marker, context_lifetime) = if task.is_async {
|
|
||||||
(
|
|
||||||
quote!(async),
|
|
||||||
if shared_needs_lt || local_needs_lt {
|
|
||||||
quote!(<'static>)
|
|
||||||
} else {
|
|
||||||
quote!()
|
|
||||||
},
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
(quote!(), quote!())
|
|
||||||
};
|
|
||||||
|
|
||||||
user_tasks.push(quote!(
|
|
||||||
#(#attrs)*
|
|
||||||
#(#cfgs)*
|
|
||||||
#[allow(non_snake_case)]
|
|
||||||
#async_marker fn #name(#context: #name::Context #context_lifetime #(,#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,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
(mod_app, root, user_tasks)
|
|
||||||
}
|
|
|
@ -1,170 +0,0 @@
|
||||||
use crate::syntax::ast::App;
|
|
||||||
use crate::{analyze::Analysis, codegen::util};
|
|
||||||
use proc_macro2::TokenStream as TokenStream2;
|
|
||||||
use quote::quote;
|
|
||||||
|
|
||||||
/// Generates timer queues and timer queue handlers
|
|
||||||
#[allow(clippy::too_many_lines)]
|
|
||||||
pub fn codegen(app: &App, analysis: &Analysis) -> 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 = #doc]
|
|
||||||
#[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()
|
|
||||||
.filter(|(_, task)| !task.is_async)
|
|
||||||
.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 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_task = util::capacity_literal(cap);
|
|
||||||
let tq_ty = quote!(rtic::export::TimerQueue<#mono_type, #t, #n_task>);
|
|
||||||
|
|
||||||
// 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)]
|
|
||||||
static #tq: rtic::RacyCell<#tq_ty> = rtic::RacyCell::new(
|
|
||||||
rtic::export::TimerQueue {
|
|
||||||
task_queue: rtic::export::SortedLinkedList::new_u16(),
|
|
||||||
waker_queue: rtic::export::IntrusiveSortedLinkedList::new(),
|
|
||||||
}
|
|
||||||
);
|
|
||||||
));
|
|
||||||
|
|
||||||
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)]
|
|
||||||
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()
|
|
||||||
.filter(|(_, task)| !task.is_async)
|
|
||||||
.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_normal.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 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)]
|
|
||||||
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
|
|
||||||
}
|
|
|
@ -3,20 +3,10 @@ use core::sync::atomic::{AtomicUsize, Ordering};
|
||||||
use crate::syntax::{ast::App, Context};
|
use crate::syntax::{ast::App, Context};
|
||||||
use proc_macro2::{Span, TokenStream as TokenStream2};
|
use proc_macro2::{Span, TokenStream as TokenStream2};
|
||||||
use quote::quote;
|
use quote::quote;
|
||||||
use syn::{Attribute, Ident, LitInt, PatType};
|
use syn::{Attribute, Ident};
|
||||||
|
|
||||||
const RTIC_INTERNAL: &str = "__rtic_internal";
|
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
|
/// Generates a `Mutex` implementation
|
||||||
pub fn impl_mutex(
|
pub fn impl_mutex(
|
||||||
app: &App,
|
app: &App,
|
||||||
|
@ -60,30 +50,16 @@ pub fn impl_mutex(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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 `EXECUTOR_RUN` atomics (`async` API)
|
/// Generates an identifier for the `EXECUTOR_RUN` atomics (`async` API)
|
||||||
pub fn executor_run_ident(task: &Ident) -> Ident {
|
pub fn executor_run_ident(task: &Ident) -> Ident {
|
||||||
mark_internal_name(&format!("{}_EXECUTOR_RUN", task))
|
mark_internal_name(&format!("{}_EXECUTOR_RUN", 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 {
|
pub fn interrupt_ident() -> Ident {
|
||||||
let span = Span::call_site();
|
let span = Span::call_site();
|
||||||
Ident::new("interrupt", span)
|
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
|
/// Whether `name` is an exception with configurable priority
|
||||||
pub fn is_exception(name: &Ident) -> bool {
|
pub fn is_exception(name: &Ident) -> bool {
|
||||||
let s = name.to_string();
|
let s = name.to_string();
|
||||||
|
@ -106,11 +82,6 @@ pub fn mark_internal_name(name: &str) -> Ident {
|
||||||
Ident::new(&format!("{}_{}", RTIC_INTERNAL, name), Span::call_site())
|
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
|
/// Generate an internal identifier for tasks
|
||||||
pub fn internal_task_ident(task: &Ident, ident_name: &str) -> Ident {
|
pub fn internal_task_ident(task: &Ident, ident_name: &str) -> Ident {
|
||||||
mark_internal_name(&format!("{}_{}", task, ident_name))
|
mark_internal_name(&format!("{}_{}", task, ident_name))
|
||||||
|
@ -129,55 +100,6 @@ pub fn link_section_uninit() -> TokenStream2 {
|
||||||
quote!(#[link_section = #section])
|
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
|
/// Get the ident for the name of the task
|
||||||
pub fn get_task_name(ctxt: Context, app: &App) -> Ident {
|
pub fn get_task_name(ctxt: Context, app: &App) -> Ident {
|
||||||
let s = match ctxt {
|
let s = match ctxt {
|
||||||
|
@ -230,48 +152,17 @@ pub fn local_resources_ident(ctxt: Context, app: &App) -> Ident {
|
||||||
mark_internal_name(&s)
|
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 a ready queue, async task version
|
/// Generates an identifier for a ready queue, async task version
|
||||||
pub fn rq_async_ident(async_task_name: &Ident) -> Ident {
|
pub fn rq_async_ident(async_task_name: &Ident) -> Ident {
|
||||||
mark_internal_name(&format!("ASYNC_TACK_{}_RQ", async_task_name))
|
mark_internal_name(&format!("ASYNC_TACK_{}_RQ", async_task_name))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generates an identifier for the `enum` of `schedule`-able tasks
|
|
||||||
pub fn schedule_t_ident() -> Ident {
|
|
||||||
mark_internal_name("SCHED_T")
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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 {
|
|
||||||
mark_internal_name(&format!("P{}_T", priority))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Suffixed identifier
|
/// Suffixed identifier
|
||||||
pub fn suffixed(name: &str) -> Ident {
|
pub fn suffixed(name: &str) -> Ident {
|
||||||
let span = Span::call_site();
|
let span = Span::call_site();
|
||||||
Ident::new(name, span)
|
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 {
|
pub fn static_shared_resource_ident(name: &Ident) -> Ident {
|
||||||
mark_internal_name(&format!("shared_resource_{}", name))
|
mark_internal_name(&format!("shared_resource_{}", name))
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,19 +16,11 @@ pub(crate) fn app(app: &App) -> Result<Analysis, syn::Error> {
|
||||||
type TaskName = String;
|
type TaskName = String;
|
||||||
type Priority = u8;
|
type Priority = u8;
|
||||||
|
|
||||||
// The task list is a Tuple (Name, Shared Resources, Local Resources, Priority, IsAsync)
|
// The task list is a Tuple (Name, Shared Resources, Local Resources, Priority)
|
||||||
let task_resources_list: Vec<(TaskName, Vec<&Ident>, &LocalResources, Priority, bool)> =
|
let task_resources_list: Vec<(TaskName, Vec<&Ident>, &LocalResources, Priority)> =
|
||||||
Some(&app.init)
|
Some(&app.init)
|
||||||
.iter()
|
.iter()
|
||||||
.map(|ht| {
|
.map(|ht| ("init".to_string(), Vec::new(), &ht.args.local_resources, 0))
|
||||||
(
|
|
||||||
"init".to_string(),
|
|
||||||
Vec::new(),
|
|
||||||
&ht.args.local_resources,
|
|
||||||
0,
|
|
||||||
false,
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.chain(app.idle.iter().map(|ht| {
|
.chain(app.idle.iter().map(|ht| {
|
||||||
(
|
(
|
||||||
"idle".to_string(),
|
"idle".to_string(),
|
||||||
|
@ -39,7 +31,6 @@ pub(crate) fn app(app: &App) -> Result<Analysis, syn::Error> {
|
||||||
.collect::<Vec<_>>(),
|
.collect::<Vec<_>>(),
|
||||||
&ht.args.local_resources,
|
&ht.args.local_resources,
|
||||||
0,
|
0,
|
||||||
false,
|
|
||||||
)
|
)
|
||||||
}))
|
}))
|
||||||
.chain(app.software_tasks.iter().map(|(name, ht)| {
|
.chain(app.software_tasks.iter().map(|(name, ht)| {
|
||||||
|
@ -52,7 +43,6 @@ pub(crate) fn app(app: &App) -> Result<Analysis, syn::Error> {
|
||||||
.collect::<Vec<_>>(),
|
.collect::<Vec<_>>(),
|
||||||
&ht.args.local_resources,
|
&ht.args.local_resources,
|
||||||
ht.args.priority,
|
ht.args.priority,
|
||||||
ht.is_async,
|
|
||||||
)
|
)
|
||||||
}))
|
}))
|
||||||
.chain(app.hardware_tasks.iter().map(|(name, ht)| {
|
.chain(app.hardware_tasks.iter().map(|(name, ht)| {
|
||||||
|
@ -65,7 +55,6 @@ pub(crate) fn app(app: &App) -> Result<Analysis, syn::Error> {
|
||||||
.collect::<Vec<_>>(),
|
.collect::<Vec<_>>(),
|
||||||
&ht.args.local_resources,
|
&ht.args.local_resources,
|
||||||
ht.args.priority,
|
ht.args.priority,
|
||||||
false,
|
|
||||||
)
|
)
|
||||||
}))
|
}))
|
||||||
.collect();
|
.collect();
|
||||||
|
@ -84,21 +73,20 @@ pub(crate) fn app(app: &App) -> Result<Analysis, syn::Error> {
|
||||||
|
|
||||||
// Check that lock_free resources are correct
|
// Check that lock_free resources are correct
|
||||||
for lf_res in lock_free.iter() {
|
for lf_res in lock_free.iter() {
|
||||||
for (task, tr, _, priority, is_async) in task_resources_list.iter() {
|
for (task, tr, _, priority) in task_resources_list.iter() {
|
||||||
for r in tr {
|
for r in tr {
|
||||||
// Get all uses of resources annotated lock_free
|
// Get all uses of resources annotated lock_free
|
||||||
if lf_res == r {
|
if lf_res == r {
|
||||||
// lock_free resources are not allowed in async tasks
|
// lock_free resources are not allowed in async tasks
|
||||||
if *is_async {
|
error.push(syn::Error::new(
|
||||||
error.push(syn::Error::new(
|
|
||||||
r.span(),
|
r.span(),
|
||||||
format!(
|
format!(
|
||||||
"Lock free shared resource {:?} is used by an async tasks, which is forbidden",
|
"Lock free shared resource {:?} is used by an async tasks, which is forbidden",
|
||||||
r.to_string(),
|
r.to_string(),
|
||||||
),
|
),
|
||||||
));
|
));
|
||||||
}
|
|
||||||
|
|
||||||
|
// TODO: Should this be removed?
|
||||||
// HashMap returns the previous existing object if old.key == new.key
|
// HashMap returns the previous existing object if old.key == new.key
|
||||||
if let Some(lf_res) = lf_hash.insert(r.to_string(), (task, r, priority)) {
|
if let Some(lf_res) = lf_hash.insert(r.to_string(), (task, r, priority)) {
|
||||||
// Check if priority differ, if it does, append to
|
// Check if priority differ, if it does, append to
|
||||||
|
@ -150,7 +138,7 @@ pub(crate) fn app(app: &App) -> Result<Analysis, syn::Error> {
|
||||||
|
|
||||||
// Check that local resources are not shared
|
// Check that local resources are not shared
|
||||||
for lr in local {
|
for lr in local {
|
||||||
for (task, _, local_resources, _, _) in task_resources_list.iter() {
|
for (task, _, local_resources, _) in task_resources_list.iter() {
|
||||||
for (name, res) in local_resources.iter() {
|
for (name, res) in local_resources.iter() {
|
||||||
// Get all uses of resources annotated lock_free
|
// Get all uses of resources annotated lock_free
|
||||||
if lr == name {
|
if lr == name {
|
||||||
|
@ -193,18 +181,7 @@ pub(crate) fn app(app: &App) -> Result<Analysis, syn::Error> {
|
||||||
error.push(syn::Error::new(
|
error.push(syn::Error::new(
|
||||||
name.span(),
|
name.span(),
|
||||||
format!(
|
format!(
|
||||||
"Software task {:?} has priority 0, but `#[idle]` is defined. 0-priority software tasks are only allowed if there is no `#[idle]`.",
|
"Async task {:?} has priority 0, but `#[idle]` is defined. 0-priority async tasks are only allowed if there is no `#[idle]`.",
|
||||||
name.to_string(),
|
|
||||||
)
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
// 0-priority tasks must be async
|
|
||||||
if !task.is_async {
|
|
||||||
error.push(syn::Error::new(
|
|
||||||
name.span(),
|
|
||||||
format!(
|
|
||||||
"Software task {:?} has priority 0, but is not `async`. 0-priority software tasks must be `async`.",
|
|
||||||
name.to_string(),
|
name.to_string(),
|
||||||
)
|
)
|
||||||
));
|
));
|
||||||
|
@ -263,7 +240,7 @@ pub(crate) fn app(app: &App) -> Result<Analysis, syn::Error> {
|
||||||
// Create the list of used local resource Idents
|
// Create the list of used local resource Idents
|
||||||
let mut used_local_resource = IndexSet::new();
|
let mut used_local_resource = IndexSet::new();
|
||||||
|
|
||||||
for (_, _, locals, _, _) in task_resources_list {
|
for (_, _, locals, _) in task_resources_list {
|
||||||
for (local, _) in locals {
|
for (local, _) in locals {
|
||||||
used_local_resource.insert(local.clone());
|
used_local_resource.insert(local.clone());
|
||||||
}
|
}
|
||||||
|
@ -307,27 +284,11 @@ pub(crate) fn app(app: &App) -> Result<Analysis, syn::Error> {
|
||||||
|
|
||||||
let channel = channels.entry(spawnee_prio).or_default();
|
let channel = channels.entry(spawnee_prio).or_default();
|
||||||
channel.tasks.insert(name.clone());
|
channel.tasks.insert(name.clone());
|
||||||
|
|
||||||
if !spawnee.args.only_same_priority_spawn {
|
|
||||||
// Require `Send` if the task can be spawned from other priorities
|
|
||||||
spawnee.inputs.iter().for_each(|input| {
|
|
||||||
send_types.insert(input.ty.clone());
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// No channel should ever be empty
|
// No channel should ever be empty
|
||||||
debug_assert!(channels.values().all(|channel| !channel.tasks.is_empty()));
|
debug_assert!(channels.values().all(|channel| !channel.tasks.is_empty()));
|
||||||
|
|
||||||
// Compute channel capacities
|
|
||||||
for channel in channels.values_mut() {
|
|
||||||
channel.capacity = channel
|
|
||||||
.tasks
|
|
||||||
.iter()
|
|
||||||
.map(|name| app.software_tasks[name].args.capacity)
|
|
||||||
.sum();
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(Analysis {
|
Ok(Analysis {
|
||||||
channels,
|
channels,
|
||||||
shared_resources: used_shared_resource,
|
shared_resources: used_shared_resource,
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
//! Abstract Syntax Tree
|
//! Abstract Syntax Tree
|
||||||
|
|
||||||
use syn::{Attribute, Expr, Ident, Item, ItemUse, Pat, PatType, Path, Stmt, Type};
|
use syn::{Attribute, Expr, Ident, Item, ItemUse, Pat, Path, Stmt, Type};
|
||||||
|
|
||||||
use crate::syntax::Map;
|
use crate::syntax::Map;
|
||||||
|
|
||||||
|
@ -20,9 +20,6 @@ pub struct App {
|
||||||
/// The `#[idle]` function
|
/// The `#[idle]` function
|
||||||
pub idle: Option<Idle>,
|
pub idle: Option<Idle>,
|
||||||
|
|
||||||
/// Monotonic clocks
|
|
||||||
pub monotonics: Map<Monotonic>,
|
|
||||||
|
|
||||||
/// Resources shared between tasks defined in `#[shared]`
|
/// Resources shared between tasks defined in `#[shared]`
|
||||||
pub shared_resources: Map<SharedResource>,
|
pub shared_resources: Map<SharedResource>,
|
||||||
|
|
||||||
|
@ -38,7 +35,7 @@ pub struct App {
|
||||||
/// Hardware tasks: `#[task(binds = ..)]`s
|
/// Hardware tasks: `#[task(binds = ..)]`s
|
||||||
pub hardware_tasks: Map<HardwareTask>,
|
pub hardware_tasks: Map<HardwareTask>,
|
||||||
|
|
||||||
/// Software tasks: `#[task]`
|
/// Async software tasks: `#[task]`
|
||||||
pub software_tasks: Map<SoftwareTask>,
|
pub software_tasks: Map<SoftwareTask>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -192,38 +189,7 @@ pub struct LocalResource {
|
||||||
pub ty: Box<Type>,
|
pub ty: Box<Type>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Monotonic
|
/// An async software task
|
||||||
#[derive(Debug)]
|
|
||||||
#[non_exhaustive]
|
|
||||||
pub struct Monotonic {
|
|
||||||
/// `#[cfg]` attributes like `#[cfg(debug_assertions)]`
|
|
||||||
pub cfgs: Vec<Attribute>,
|
|
||||||
|
|
||||||
/// The identifier of the monotonic
|
|
||||||
pub ident: Ident,
|
|
||||||
|
|
||||||
/// The type of this monotonic
|
|
||||||
pub ty: Box<Type>,
|
|
||||||
|
|
||||||
/// Monotonic args
|
|
||||||
pub args: MonotonicArgs,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Monotonic metadata
|
|
||||||
#[derive(Debug)]
|
|
||||||
#[non_exhaustive]
|
|
||||||
pub struct MonotonicArgs {
|
|
||||||
/// The interrupt or exception that this monotonic is bound to
|
|
||||||
pub binds: Ident,
|
|
||||||
|
|
||||||
/// The priority of this monotonic
|
|
||||||
pub priority: Option<u8>,
|
|
||||||
|
|
||||||
/// If this is the default monotonic
|
|
||||||
pub default: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A software task
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
pub struct SoftwareTask {
|
pub struct SoftwareTask {
|
||||||
|
@ -239,26 +205,17 @@ pub struct SoftwareTask {
|
||||||
/// The context argument
|
/// The context argument
|
||||||
pub context: Box<Pat>,
|
pub context: Box<Pat>,
|
||||||
|
|
||||||
/// The inputs of this software task
|
|
||||||
pub inputs: Vec<PatType>,
|
|
||||||
|
|
||||||
/// The statements that make up the task handler
|
/// The statements that make up the task handler
|
||||||
pub stmts: Vec<Stmt>,
|
pub stmts: Vec<Stmt>,
|
||||||
|
|
||||||
/// The task is declared externally
|
/// The task is declared externally
|
||||||
pub is_extern: bool,
|
pub is_extern: bool,
|
||||||
|
|
||||||
/// If the task is marked as `async`
|
|
||||||
pub is_async: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Software task metadata
|
/// Software task metadata
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
pub struct SoftwareTaskArgs {
|
pub struct SoftwareTaskArgs {
|
||||||
/// The task capacity: the maximum number of pending messages that can be queued
|
|
||||||
pub capacity: u8,
|
|
||||||
|
|
||||||
/// The priority of this task
|
/// The priority of this task
|
||||||
pub priority: u8,
|
pub priority: u8,
|
||||||
|
|
||||||
|
@ -275,7 +232,6 @@ pub struct SoftwareTaskArgs {
|
||||||
impl Default for SoftwareTaskArgs {
|
impl Default for SoftwareTaskArgs {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
capacity: 1,
|
|
||||||
priority: 1,
|
priority: 1,
|
||||||
local_resources: LocalResources::new(),
|
local_resources: LocalResources::new(),
|
||||||
shared_resources: SharedResources::new(),
|
shared_resources: SharedResources::new(),
|
||||||
|
|
|
@ -2,7 +2,6 @@ mod app;
|
||||||
mod hardware_task;
|
mod hardware_task;
|
||||||
mod idle;
|
mod idle;
|
||||||
mod init;
|
mod init;
|
||||||
mod monotonic;
|
|
||||||
mod resource;
|
mod resource;
|
||||||
mod software_task;
|
mod software_task;
|
||||||
mod util;
|
mod util;
|
||||||
|
@ -11,15 +10,12 @@ use proc_macro2::TokenStream as TokenStream2;
|
||||||
use syn::{
|
use syn::{
|
||||||
braced, parenthesized,
|
braced, parenthesized,
|
||||||
parse::{self, Parse, ParseStream, Parser},
|
parse::{self, Parse, ParseStream, Parser},
|
||||||
token::{self, Brace},
|
token::Brace,
|
||||||
Ident, Item, LitBool, LitInt, Path, Token,
|
Ident, Item, LitInt, Token,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::syntax::{
|
use crate::syntax::{
|
||||||
ast::{
|
ast::{App, AppArgs, HardwareTaskArgs, IdleArgs, InitArgs, SoftwareTaskArgs, TaskLocal},
|
||||||
App, AppArgs, HardwareTaskArgs, IdleArgs, InitArgs, MonotonicArgs, SoftwareTaskArgs,
|
|
||||||
TaskLocal,
|
|
||||||
},
|
|
||||||
Either,
|
Either,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -388,7 +384,6 @@ fn task_args(tokens: TokenStream2) -> parse::Result<Either<HardwareTaskArgs, Sof
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
Either::Right(SoftwareTaskArgs {
|
Either::Right(SoftwareTaskArgs {
|
||||||
capacity: capacity.unwrap_or(1),
|
|
||||||
priority,
|
priority,
|
||||||
shared_resources,
|
shared_resources,
|
||||||
local_resources,
|
local_resources,
|
||||||
|
@ -398,113 +393,3 @@ fn task_args(tokens: TokenStream2) -> parse::Result<Either<HardwareTaskArgs, Sof
|
||||||
})
|
})
|
||||||
.parse2(tokens)
|
.parse2(tokens)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn monotonic_args(path: Path, tokens: TokenStream2) -> parse::Result<MonotonicArgs> {
|
|
||||||
(|input: ParseStream<'_>| -> parse::Result<MonotonicArgs> {
|
|
||||||
let mut binds = None;
|
|
||||||
let mut priority = None;
|
|
||||||
let mut default = None;
|
|
||||||
|
|
||||||
if !input.peek(token::Paren) {
|
|
||||||
return Err(parse::Error::new(
|
|
||||||
path.segments.first().unwrap().ident.span(),
|
|
||||||
"expected opening ( in #[monotonic( ... )]",
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
let content;
|
|
||||||
parenthesized!(content in input);
|
|
||||||
|
|
||||||
if !content.is_empty() {
|
|
||||||
loop {
|
|
||||||
// Parse identifier name
|
|
||||||
let ident: Ident = content.parse()?;
|
|
||||||
// Handle equal sign
|
|
||||||
let _: Token![=] = content.parse()?;
|
|
||||||
|
|
||||||
match &*ident.to_string() {
|
|
||||||
"binds" => {
|
|
||||||
if binds.is_some() {
|
|
||||||
return Err(parse::Error::new(
|
|
||||||
ident.span(),
|
|
||||||
"argument appears more than once",
|
|
||||||
));
|
|
||||||
}
|
|
||||||
// Parse identifier name
|
|
||||||
let ident = content.parse()?;
|
|
||||||
|
|
||||||
binds = Some(ident);
|
|
||||||
}
|
|
||||||
|
|
||||||
"priority" => {
|
|
||||||
if priority.is_some() {
|
|
||||||
return Err(parse::Error::new(
|
|
||||||
ident.span(),
|
|
||||||
"argument appears more than once",
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
// #lit
|
|
||||||
let lit: LitInt = content.parse()?;
|
|
||||||
|
|
||||||
if !lit.suffix().is_empty() {
|
|
||||||
return Err(parse::Error::new(
|
|
||||||
lit.span(),
|
|
||||||
"this literal must be unsuffixed",
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
let value = lit.base10_parse::<u8>().ok();
|
|
||||||
if value.is_none() || value == Some(0) {
|
|
||||||
return Err(parse::Error::new(
|
|
||||||
lit.span(),
|
|
||||||
"this literal must be in the range 1...255",
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
priority = Some(value.unwrap());
|
|
||||||
}
|
|
||||||
|
|
||||||
"default" => {
|
|
||||||
if default.is_some() {
|
|
||||||
return Err(parse::Error::new(
|
|
||||||
ident.span(),
|
|
||||||
"argument appears more than once",
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
let lit: LitBool = content.parse()?;
|
|
||||||
default = Some(lit.value);
|
|
||||||
}
|
|
||||||
|
|
||||||
_ => {
|
|
||||||
return Err(parse::Error::new(ident.span(), "unexpected argument"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if content.is_empty() {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle comma: ,
|
|
||||||
let _: Token![,] = content.parse()?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let binds = if let Some(r) = binds {
|
|
||||||
r
|
|
||||||
} else {
|
|
||||||
return Err(parse::Error::new(
|
|
||||||
content.span(),
|
|
||||||
"`binds = ...` is missing",
|
|
||||||
));
|
|
||||||
};
|
|
||||||
let default = default.unwrap_or(false);
|
|
||||||
|
|
||||||
Ok(MonotonicArgs {
|
|
||||||
binds,
|
|
||||||
priority,
|
|
||||||
default,
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.parse2(tokens)
|
|
||||||
}
|
|
||||||
|
|
|
@ -5,14 +5,14 @@ use proc_macro2::TokenStream as TokenStream2;
|
||||||
use syn::{
|
use syn::{
|
||||||
parse::{self, ParseStream, Parser},
|
parse::{self, ParseStream, Parser},
|
||||||
spanned::Spanned,
|
spanned::Spanned,
|
||||||
Expr, ExprArray, Fields, ForeignItem, Ident, Item, LitBool, Path, Token, Type, Visibility,
|
Expr, ExprArray, Fields, ForeignItem, Ident, Item, LitBool, Path, Token, Visibility,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::Input;
|
use super::Input;
|
||||||
use crate::syntax::{
|
use crate::syntax::{
|
||||||
ast::{
|
ast::{
|
||||||
App, AppArgs, Dispatcher, Dispatchers, HardwareTask, Idle, IdleArgs, Init, InitArgs,
|
App, AppArgs, Dispatcher, Dispatchers, HardwareTask, Idle, IdleArgs, Init, InitArgs,
|
||||||
LocalResource, Monotonic, MonotonicArgs, SharedResource, SoftwareTask,
|
LocalResource, SharedResource, SoftwareTask,
|
||||||
},
|
},
|
||||||
parse::{self as syntax_parse, util},
|
parse::{self as syntax_parse, util},
|
||||||
Either, Map, Set,
|
Either, Map, Set,
|
||||||
|
@ -150,7 +150,6 @@ impl App {
|
||||||
let mut shared_resources = Map::new();
|
let mut shared_resources = Map::new();
|
||||||
let mut local_resources_ident = None;
|
let mut local_resources_ident = None;
|
||||||
let mut local_resources = Map::new();
|
let mut local_resources = Map::new();
|
||||||
let mut monotonics = Map::new();
|
|
||||||
let mut hardware_tasks = Map::new();
|
let mut hardware_tasks = Map::new();
|
||||||
let mut software_tasks = Map::new();
|
let mut software_tasks = Map::new();
|
||||||
let mut user_imports = vec![];
|
let mut user_imports = vec![];
|
||||||
|
@ -158,7 +157,6 @@ impl App {
|
||||||
|
|
||||||
let mut seen_idents = HashSet::<Ident>::new();
|
let mut seen_idents = HashSet::<Ident>::new();
|
||||||
let mut bindings = HashSet::<Ident>::new();
|
let mut bindings = HashSet::<Ident>::new();
|
||||||
let mut monotonic_types = HashSet::<Type>::new();
|
|
||||||
|
|
||||||
let mut check_binding = |ident: &Ident| {
|
let mut check_binding = |ident: &Ident| {
|
||||||
if bindings.contains(ident) {
|
if bindings.contains(ident) {
|
||||||
|
@ -186,19 +184,6 @@ impl App {
|
||||||
Ok(())
|
Ok(())
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut check_monotonic = |ty: &Type| {
|
|
||||||
if monotonic_types.contains(ty) {
|
|
||||||
return Err(parse::Error::new(
|
|
||||||
ty.span(),
|
|
||||||
"this type is already used by another monotonic",
|
|
||||||
));
|
|
||||||
} else {
|
|
||||||
monotonic_types.insert(ty.clone());
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
};
|
|
||||||
|
|
||||||
for mut item in input.items {
|
for mut item in input.items {
|
||||||
match item {
|
match item {
|
||||||
Item::Fn(mut item) => {
|
Item::Fn(mut item) => {
|
||||||
|
@ -448,44 +433,6 @@ impl App {
|
||||||
// Store the user provided use-statements
|
// Store the user provided use-statements
|
||||||
user_imports.push(itemuse_.clone());
|
user_imports.push(itemuse_.clone());
|
||||||
}
|
}
|
||||||
Item::Type(ref mut type_item) => {
|
|
||||||
// Match types with the attribute #[monotonic]
|
|
||||||
if let Some(pos) = type_item
|
|
||||||
.attrs
|
|
||||||
.iter()
|
|
||||||
.position(|attr| util::attr_eq(attr, "monotonic"))
|
|
||||||
{
|
|
||||||
let span = type_item.ident.span();
|
|
||||||
|
|
||||||
if monotonics.contains_key(&type_item.ident) {
|
|
||||||
return Err(parse::Error::new(
|
|
||||||
span,
|
|
||||||
"`#[monotonic(...)]` on a specific type must appear at most once",
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
if type_item.vis != Visibility::Inherited {
|
|
||||||
return Err(parse::Error::new(
|
|
||||||
type_item.span(),
|
|
||||||
"this item must have inherited / private visibility",
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
check_monotonic(&*type_item.ty)?;
|
|
||||||
|
|
||||||
let m = type_item.attrs.remove(pos);
|
|
||||||
let args = MonotonicArgs::parse(m)?;
|
|
||||||
|
|
||||||
check_binding(&args.binds)?;
|
|
||||||
|
|
||||||
let monotonic = Monotonic::parse(args, type_item, span)?;
|
|
||||||
|
|
||||||
monotonics.insert(type_item.ident.clone(), monotonic);
|
|
||||||
}
|
|
||||||
|
|
||||||
// All types are passed on
|
|
||||||
user_code.push(item.clone());
|
|
||||||
}
|
|
||||||
_ => {
|
_ => {
|
||||||
// Anything else within the module should not make any difference
|
// Anything else within the module should not make any difference
|
||||||
user_code.push(item.clone());
|
user_code.push(item.clone());
|
||||||
|
@ -524,7 +471,6 @@ impl App {
|
||||||
name: input.ident,
|
name: input.ident,
|
||||||
init,
|
init,
|
||||||
idle,
|
idle,
|
||||||
monotonics,
|
|
||||||
shared_resources,
|
shared_resources,
|
||||||
local_resources,
|
local_resources,
|
||||||
user_imports,
|
user_imports,
|
||||||
|
|
|
@ -23,19 +23,17 @@ impl HardwareTask {
|
||||||
}
|
}
|
||||||
|
|
||||||
if valid_signature {
|
if valid_signature {
|
||||||
if let Some((context, Ok(rest))) = util::parse_inputs(item.sig.inputs, &name) {
|
if let Some(context) = util::parse_inputs(item.sig.inputs, &name) {
|
||||||
if rest.is_empty() {
|
let FilterAttrs { cfgs, attrs, .. } = util::filter_attributes(item.attrs);
|
||||||
let FilterAttrs { cfgs, attrs, .. } = util::filter_attributes(item.attrs);
|
|
||||||
|
|
||||||
return Ok(HardwareTask {
|
return Ok(HardwareTask {
|
||||||
args,
|
args,
|
||||||
cfgs,
|
cfgs,
|
||||||
attrs,
|
attrs,
|
||||||
context,
|
context,
|
||||||
stmts: item.block.stmts,
|
stmts: item.block.stmts,
|
||||||
is_extern: false,
|
is_extern: false,
|
||||||
});
|
});
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -69,19 +67,17 @@ impl HardwareTask {
|
||||||
}
|
}
|
||||||
|
|
||||||
if valid_signature {
|
if valid_signature {
|
||||||
if let Some((context, Ok(rest))) = util::parse_inputs(item.sig.inputs, &name) {
|
if let Some(context) = util::parse_inputs(item.sig.inputs, &name) {
|
||||||
if rest.is_empty() {
|
let FilterAttrs { cfgs, attrs, .. } = util::filter_attributes(item.attrs);
|
||||||
let FilterAttrs { cfgs, attrs, .. } = util::filter_attributes(item.attrs);
|
|
||||||
|
|
||||||
return Ok(HardwareTask {
|
return Ok(HardwareTask {
|
||||||
args,
|
args,
|
||||||
cfgs,
|
cfgs,
|
||||||
attrs,
|
attrs,
|
||||||
context,
|
context,
|
||||||
stmts: Vec::<Stmt>::new(),
|
stmts: Vec::<Stmt>::new(),
|
||||||
is_extern: true,
|
is_extern: true,
|
||||||
});
|
});
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,16 +21,14 @@ impl Idle {
|
||||||
let name = item.sig.ident.to_string();
|
let name = item.sig.ident.to_string();
|
||||||
|
|
||||||
if valid_signature {
|
if valid_signature {
|
||||||
if let Some((context, Ok(rest))) = util::parse_inputs(item.sig.inputs, &name) {
|
if let Some(context) = util::parse_inputs(item.sig.inputs, &name) {
|
||||||
if rest.is_empty() {
|
return Ok(Idle {
|
||||||
return Ok(Idle {
|
args,
|
||||||
args,
|
attrs: item.attrs,
|
||||||
attrs: item.attrs,
|
context,
|
||||||
context,
|
name: item.sig.ident,
|
||||||
name: item.sig.ident,
|
stmts: item.block.stmts,
|
||||||
stmts: item.block.stmts,
|
});
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -25,18 +25,16 @@ impl Init {
|
||||||
if let Ok((user_shared_struct, user_local_struct)) =
|
if let Ok((user_shared_struct, user_local_struct)) =
|
||||||
util::type_is_init_return(&item.sig.output, &name)
|
util::type_is_init_return(&item.sig.output, &name)
|
||||||
{
|
{
|
||||||
if let Some((context, Ok(rest))) = util::parse_inputs(item.sig.inputs, &name) {
|
if let Some(context) = util::parse_inputs(item.sig.inputs, &name) {
|
||||||
if rest.is_empty() {
|
return Ok(Init {
|
||||||
return Ok(Init {
|
args,
|
||||||
args,
|
attrs: item.attrs,
|
||||||
attrs: item.attrs,
|
context,
|
||||||
context,
|
name: item.sig.ident,
|
||||||
name: item.sig.ident,
|
stmts: item.block.stmts,
|
||||||
stmts: item.block.stmts,
|
user_shared_struct,
|
||||||
user_shared_struct,
|
user_local_struct,
|
||||||
user_local_struct,
|
});
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,17 +8,16 @@ use crate::syntax::{
|
||||||
|
|
||||||
impl SoftwareTask {
|
impl SoftwareTask {
|
||||||
pub(crate) fn parse(args: SoftwareTaskArgs, item: ItemFn) -> parse::Result<Self> {
|
pub(crate) fn parse(args: SoftwareTaskArgs, item: ItemFn) -> parse::Result<Self> {
|
||||||
let valid_signature =
|
let valid_signature = util::check_fn_signature(&item, true)
|
||||||
util::check_fn_signature(&item, true) && util::type_is_unit(&item.sig.output);
|
&& util::type_is_unit(&item.sig.output)
|
||||||
|
&& item.sig.asyncness.is_some();
|
||||||
|
|
||||||
let span = item.sig.ident.span();
|
let span = item.sig.ident.span();
|
||||||
|
|
||||||
let name = item.sig.ident.to_string();
|
let name = item.sig.ident.to_string();
|
||||||
|
|
||||||
let is_async = item.sig.asyncness.is_some();
|
|
||||||
|
|
||||||
if valid_signature {
|
if valid_signature {
|
||||||
if let Some((context, Ok(inputs))) = util::parse_inputs(item.sig.inputs, &name) {
|
if let Some(context) = util::parse_inputs(item.sig.inputs, &name) {
|
||||||
let FilterAttrs { cfgs, attrs, .. } = util::filter_attributes(item.attrs);
|
let FilterAttrs { cfgs, attrs, .. } = util::filter_attributes(item.attrs);
|
||||||
|
|
||||||
return Ok(SoftwareTask {
|
return Ok(SoftwareTask {
|
||||||
|
@ -26,10 +25,8 @@ impl SoftwareTask {
|
||||||
attrs,
|
attrs,
|
||||||
cfgs,
|
cfgs,
|
||||||
context,
|
context,
|
||||||
inputs,
|
|
||||||
stmts: item.block.stmts,
|
stmts: item.block.stmts,
|
||||||
is_extern: false,
|
is_extern: false,
|
||||||
is_async,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -37,7 +34,7 @@ impl SoftwareTask {
|
||||||
Err(parse::Error::new(
|
Err(parse::Error::new(
|
||||||
span,
|
span,
|
||||||
&format!(
|
&format!(
|
||||||
"this task handler must have type signature `(async) fn({}::Context, ..)`",
|
"this task handler must have type signature `async fn({}::Context)`",
|
||||||
name
|
name
|
||||||
),
|
),
|
||||||
))
|
))
|
||||||
|
@ -49,17 +46,16 @@ impl SoftwareTask {
|
||||||
args: SoftwareTaskArgs,
|
args: SoftwareTaskArgs,
|
||||||
item: ForeignItemFn,
|
item: ForeignItemFn,
|
||||||
) -> parse::Result<Self> {
|
) -> parse::Result<Self> {
|
||||||
let valid_signature =
|
let valid_signature = util::check_foreign_fn_signature(&item, true)
|
||||||
util::check_foreign_fn_signature(&item, true) && util::type_is_unit(&item.sig.output);
|
&& util::type_is_unit(&item.sig.output)
|
||||||
|
&& item.sig.asyncness.is_some();
|
||||||
|
|
||||||
let span = item.sig.ident.span();
|
let span = item.sig.ident.span();
|
||||||
|
|
||||||
let name = item.sig.ident.to_string();
|
let name = item.sig.ident.to_string();
|
||||||
|
|
||||||
let is_async = item.sig.asyncness.is_some();
|
|
||||||
|
|
||||||
if valid_signature {
|
if valid_signature {
|
||||||
if let Some((context, Ok(inputs))) = util::parse_inputs(item.sig.inputs, &name) {
|
if let Some(context) = util::parse_inputs(item.sig.inputs, &name) {
|
||||||
let FilterAttrs { cfgs, attrs, .. } = util::filter_attributes(item.attrs);
|
let FilterAttrs { cfgs, attrs, .. } = util::filter_attributes(item.attrs);
|
||||||
|
|
||||||
return Ok(SoftwareTask {
|
return Ok(SoftwareTask {
|
||||||
|
@ -67,10 +63,8 @@ impl SoftwareTask {
|
||||||
attrs,
|
attrs,
|
||||||
cfgs,
|
cfgs,
|
||||||
context,
|
context,
|
||||||
inputs,
|
|
||||||
stmts: Vec::<Stmt>::new(),
|
stmts: Vec::<Stmt>::new(),
|
||||||
is_extern: true,
|
is_extern: true,
|
||||||
is_async,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -78,7 +72,7 @@ impl SoftwareTask {
|
||||||
Err(parse::Error::new(
|
Err(parse::Error::new(
|
||||||
span,
|
span,
|
||||||
&format!(
|
&format!(
|
||||||
"this task handler must have type signature `(async) fn({}::Context, ..)`",
|
"this task handler must have type signature `async fn({}::Context)`",
|
||||||
name
|
name
|
||||||
),
|
),
|
||||||
))
|
))
|
||||||
|
|
|
@ -3,8 +3,8 @@ use syn::{
|
||||||
parse::{self, ParseStream},
|
parse::{self, ParseStream},
|
||||||
punctuated::Punctuated,
|
punctuated::Punctuated,
|
||||||
spanned::Spanned,
|
spanned::Spanned,
|
||||||
Abi, AttrStyle, Attribute, Expr, FnArg, ForeignItemFn, Ident, ItemFn, Pat, PatType, Path,
|
Abi, AttrStyle, Attribute, Expr, FnArg, ForeignItemFn, Ident, ItemFn, Pat, Path, PathArguments,
|
||||||
PathArguments, ReturnType, Token, Type, Visibility,
|
ReturnType, Token, Type, Visibility,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::syntax::{
|
use crate::syntax::{
|
||||||
|
@ -231,29 +231,23 @@ pub fn parse_local_resources(content: ParseStream<'_>) -> parse::Result<LocalRes
|
||||||
Ok(resources)
|
Ok(resources)
|
||||||
}
|
}
|
||||||
|
|
||||||
type ParseInputResult = Option<(Box<Pat>, Result<Vec<PatType>, FnArg>)>;
|
pub fn parse_inputs(inputs: Punctuated<FnArg, Token![,]>, name: &str) -> Option<Box<Pat>> {
|
||||||
|
|
||||||
pub fn parse_inputs(inputs: Punctuated<FnArg, Token![,]>, name: &str) -> ParseInputResult {
|
|
||||||
let mut inputs = inputs.into_iter();
|
let mut inputs = inputs.into_iter();
|
||||||
|
|
||||||
match inputs.next() {
|
match inputs.next() {
|
||||||
Some(FnArg::Typed(first)) => {
|
Some(FnArg::Typed(first)) => {
|
||||||
if type_is_path(&first.ty, &[name, "Context"]) {
|
if type_is_path(&first.ty, &[name, "Context"]) {
|
||||||
let rest = inputs
|
// No more inputs
|
||||||
.map(|arg| match arg {
|
if inputs.next().is_none() {
|
||||||
FnArg::Typed(arg) => Ok(arg),
|
return Some(first.pat);
|
||||||
_ => Err(arg),
|
}
|
||||||
})
|
|
||||||
.collect::<Result<Vec<_>, _>>();
|
|
||||||
|
|
||||||
Some((first.pat, rest))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_ => None,
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn type_is_bottom(ty: &ReturnType) -> bool {
|
pub fn type_is_bottom(ty: &ReturnType) -> bool {
|
||||||
|
|
Loading…
Reference in a new issue