mirror of
https://github.com/rtic-rs/rtic.git
synced 2024-12-24 02:49:34 +01:00
Now with spawn/schedule from anywhere
This commit is contained in:
parent
c83b15b643
commit
524273c96a
19 changed files with 172 additions and 694 deletions
|
@ -13,7 +13,7 @@ use rtic::cyccnt::{Instant, U32Ext as _};
|
|||
// NOTE: does NOT work on QEMU!
|
||||
#[rtic::app(device = lm3s6965, monotonic = rtic::cyccnt::CYCCNT)]
|
||||
mod app {
|
||||
#[init(schedule = [foo, bar])]
|
||||
#[init()]
|
||||
fn init(mut cx: init::Context) -> init::LateResources {
|
||||
// Initialize (enable) the monotonic timer (CYCCNT)
|
||||
cx.core.DCB.enable_trace();
|
||||
|
@ -28,10 +28,10 @@ mod app {
|
|||
hprintln!("init @ {:?}", now).unwrap();
|
||||
|
||||
// Schedule `foo` to run 8e6 cycles (clock cycles) in the future
|
||||
cx.schedule.foo(now + 8_000_000.cycles()).unwrap();
|
||||
foo::schedule(now + 8_000_000.cycles()).unwrap();
|
||||
|
||||
// Schedule `bar` to run 4e6 cycles in the future
|
||||
cx.schedule.bar(now + 4_000_000.cycles()).unwrap();
|
||||
bar::schedule(now + 4_000_000.cycles()).unwrap();
|
||||
|
||||
init::LateResources {}
|
||||
}
|
||||
|
|
|
@ -11,8 +11,10 @@ use panic_semihosting as _;
|
|||
#[rtic::app(device = lm3s6965)]
|
||||
mod app {
|
||||
#[init(spawn = [foo])]
|
||||
fn init(_c: init::Context) {
|
||||
fn init(_c: init::Context) -> init::LateResources {
|
||||
foo::spawn(1, 2).unwrap();
|
||||
|
||||
init::LateResources {}
|
||||
}
|
||||
|
||||
#[task()]
|
||||
|
|
|
@ -10,12 +10,14 @@ use panic_semihosting as _;
|
|||
|
||||
#[rtic::app(device = lm3s6965)]
|
||||
mod app {
|
||||
#[init(spawn = [foo, foo2])]
|
||||
fn init(_c: init::Context) {
|
||||
#[init]
|
||||
fn init(_c: init::Context) -> init::LateResources {
|
||||
foo::spawn(1, 2).unwrap();
|
||||
|
||||
init::LateResources {}
|
||||
}
|
||||
|
||||
#[task()]
|
||||
#[task]
|
||||
fn foo(_c: foo::Context, x: i32, y: u32) {
|
||||
hprintln!("foo {}, {}", x, y).unwrap();
|
||||
if x == 2 {
|
||||
|
@ -24,7 +26,7 @@ mod app {
|
|||
foo2::spawn(2).unwrap();
|
||||
}
|
||||
|
||||
#[task()]
|
||||
#[task]
|
||||
fn foo2(_c: foo2::Context, x: i32) {
|
||||
hprintln!("foo2 {}", x).unwrap();
|
||||
foo::spawn(x, 0).unwrap();
|
||||
|
|
|
@ -21,5 +21,5 @@ proc-macro = true
|
|||
proc-macro2 = "1"
|
||||
quote = "1"
|
||||
syn = "1"
|
||||
rtic-syntax = { git = "https://github.com/rtic-rs/rtic-syntax", branch = "master", version = "0.4.0" }
|
||||
rtic-syntax = { path = "../../rtic-syntax", version = "0.4.0" }
|
||||
|
||||
|
|
|
@ -29,7 +29,6 @@ pub fn app(analysis: P<analyze::Analysis>, app: &App) -> P<Analysis> {
|
|||
.software_tasks
|
||||
.values()
|
||||
.filter_map(|task| Some(task.args.priority))
|
||||
.chain(analysis.timer_queues.first().map(|tq| tq.priority))
|
||||
.collect::<BTreeSet<_>>();
|
||||
|
||||
if !priorities.is_empty() {
|
||||
|
|
|
@ -19,35 +19,7 @@ impl<'a> Extra<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn app<'a>(app: &'a App, analysis: &Analysis) -> parse::Result<Extra<'a>> {
|
||||
// Check that all exceptions are valid; only exceptions with configurable priorities are
|
||||
// accepted
|
||||
for (name, task) in &app.hardware_tasks {
|
||||
let name_s = task.args.binds.to_string();
|
||||
match &*name_s {
|
||||
"SysTick" => {
|
||||
// If the timer queue is used, then SysTick is unavailable
|
||||
if !analysis.timer_queues.is_empty() {
|
||||
return Err(parse::Error::new(
|
||||
name.span(),
|
||||
"this exception can't be used because it's being used by the runtime",
|
||||
));
|
||||
} else {
|
||||
// OK
|
||||
}
|
||||
}
|
||||
|
||||
"NonMaskableInt" | "HardFault" => {
|
||||
return Err(parse::Error::new(
|
||||
name.span(),
|
||||
"only exceptions with configurable priority can be used as hardware tasks",
|
||||
));
|
||||
}
|
||||
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn app<'a>(app: &'a App, _analysis: &Analysis) -> parse::Result<Extra<'a>> {
|
||||
// Check that external (device-specific) interrupts are not named after known (Cortex-M)
|
||||
// exceptions
|
||||
for name in app.extern_interrupts.keys() {
|
||||
|
@ -76,7 +48,6 @@ pub fn app<'a>(app: &'a App, analysis: &Analysis) -> parse::Result<Extra<'a>> {
|
|||
first = Some(name);
|
||||
Some(task.args.priority)
|
||||
})
|
||||
.chain(analysis.timer_queues.first().map(|tq| tq.priority))
|
||||
.collect::<HashSet<_>>();
|
||||
|
||||
let need = priorities.len();
|
||||
|
@ -141,11 +112,32 @@ pub fn app<'a>(app: &'a App, analysis: &Analysis) -> parse::Result<Extra<'a>> {
|
|||
}
|
||||
}
|
||||
|
||||
if !&analysis.timer_queues.is_empty() && monotonic.is_none() {
|
||||
return Err(parse::Error::new(
|
||||
Span::call_site(),
|
||||
"a `monotonic` timer must be specified to use the `schedule` API",
|
||||
));
|
||||
// Check that all exceptions are valid; only exceptions with configurable priorities are
|
||||
// accepted
|
||||
for (name, task) in &app.hardware_tasks {
|
||||
let name_s = task.args.binds.to_string();
|
||||
match &*name_s {
|
||||
"SysTick" => {
|
||||
// If the timer queue is used, then SysTick is unavailable
|
||||
if monotonic.is_some() {
|
||||
return Err(parse::Error::new(
|
||||
name.span(),
|
||||
"this exception can't be used because it's being used by the runtime",
|
||||
));
|
||||
} else {
|
||||
// OK
|
||||
}
|
||||
}
|
||||
|
||||
"NonMaskableInt" | "HardFault" => {
|
||||
return Err(parse::Error::new(
|
||||
name.span(),
|
||||
"only exceptions with configurable priority can be used as hardware tasks",
|
||||
));
|
||||
}
|
||||
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(device) = device {
|
||||
|
|
|
@ -15,11 +15,11 @@ mod post_init;
|
|||
mod pre_init;
|
||||
mod resources;
|
||||
mod resources_struct;
|
||||
mod schedule;
|
||||
mod schedule_body;
|
||||
// mod schedule;
|
||||
// mod schedule_body;
|
||||
mod software_tasks;
|
||||
mod spawn;
|
||||
mod spawn_body;
|
||||
// mod spawn;
|
||||
// mod spawn_body;
|
||||
mod timer_queue;
|
||||
mod util;
|
||||
|
||||
|
@ -116,11 +116,11 @@ pub fn app(app: &App, analysis: &Analysis, extra: &Extra) -> TokenStream2 {
|
|||
|
||||
let mod_app_dispatchers = dispatchers::codegen(app, analysis, extra);
|
||||
|
||||
let mod_app_spawn = spawn::codegen(app, analysis, extra);
|
||||
// let mod_app_spawn = spawn::codegen(app, analysis, extra);
|
||||
|
||||
let mod_app_timer_queue = timer_queue::codegen(app, analysis, extra);
|
||||
|
||||
let mod_app_schedule = schedule::codegen(app, extra);
|
||||
// let mod_app_schedule = schedule::codegen(app, extra);
|
||||
|
||||
let user_imports = app.user_imports.clone();
|
||||
let user_code = app.user_code.clone();
|
||||
|
@ -170,11 +170,11 @@ pub fn app(app: &App, analysis: &Analysis, extra: &Extra) -> TokenStream2 {
|
|||
|
||||
#(#mod_app_dispatchers)*
|
||||
|
||||
#(#mod_app_spawn)*
|
||||
// #(#mod_app_spawn)*
|
||||
|
||||
#(#mod_app_timer_queue)*
|
||||
|
||||
#(#mod_app_schedule)*
|
||||
// #(#mod_app_schedule)*
|
||||
|
||||
#(#mains)*
|
||||
}
|
||||
|
|
|
@ -60,24 +60,6 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec<TokenStream
|
|||
pub static mut #rq: #rq_ty = #rq_expr;
|
||||
));
|
||||
|
||||
if let Some(ceiling) = channel.ceiling {
|
||||
items.push(quote!(
|
||||
struct #rq<'a> {
|
||||
priority: &'a rtic::export::Priority,
|
||||
}
|
||||
));
|
||||
|
||||
items.push(util::impl_mutex(
|
||||
extra,
|
||||
&[],
|
||||
false,
|
||||
&rq,
|
||||
rq_ty,
|
||||
ceiling,
|
||||
quote!(&mut #rq),
|
||||
));
|
||||
}
|
||||
|
||||
let arms = channel
|
||||
.tasks
|
||||
.iter()
|
||||
|
@ -88,7 +70,7 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec<TokenStream
|
|||
let inputs = util::inputs_ident(name);
|
||||
let (_, tupled, pats, _) = util::regroup_inputs(&task.inputs);
|
||||
|
||||
let (let_instant, instant) = if app.uses_schedule() {
|
||||
let (let_instant, instant) = if extra.monotonic.is_some() {
|
||||
let instants = util::instants_ident(name);
|
||||
|
||||
(
|
||||
|
|
|
@ -32,7 +32,7 @@ pub fn codegen(
|
|||
let mut hardware_tasks_imports = vec![];
|
||||
|
||||
for (name, task) in &app.hardware_tasks {
|
||||
let (let_instant, instant) = if app.uses_schedule() {
|
||||
let (let_instant, instant) = if extra.monotonic.is_some() {
|
||||
let m = extra.monotonic();
|
||||
|
||||
(
|
||||
|
|
|
@ -21,7 +21,7 @@ pub fn codegen(
|
|||
let mut lt = None;
|
||||
match ctxt {
|
||||
Context::Init => {
|
||||
if app.uses_schedule() {
|
||||
if extra.monotonic.is_some() {
|
||||
let m = extra.monotonic();
|
||||
|
||||
fields.push(quote!(
|
||||
|
@ -67,7 +67,7 @@ pub fn codegen(
|
|||
Context::Idle => {}
|
||||
|
||||
Context::HardwareTask(..) => {
|
||||
if app.uses_schedule() {
|
||||
if extra.monotonic.is_some() {
|
||||
let m = extra.monotonic();
|
||||
|
||||
fields.push(quote!(
|
||||
|
@ -82,7 +82,7 @@ pub fn codegen(
|
|||
}
|
||||
|
||||
Context::SoftwareTask(..) => {
|
||||
if app.uses_schedule() {
|
||||
if extra.monotonic.is_some() {
|
||||
let m = extra.monotonic();
|
||||
|
||||
fields.push(quote!(
|
||||
|
@ -132,139 +132,6 @@ pub fn codegen(
|
|||
values.push(quote!(resources: Resources::new(#priority)));
|
||||
}
|
||||
|
||||
if ctxt.uses_schedule(app) {
|
||||
let doc = "Tasks that can be `schedule`-d from this context";
|
||||
if ctxt.is_init() {
|
||||
items.push(quote!(
|
||||
#[doc = #doc]
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct Schedule {
|
||||
_not_send: core::marker::PhantomData<*mut ()>,
|
||||
}
|
||||
));
|
||||
|
||||
fields.push(quote!(
|
||||
#[doc = #doc]
|
||||
pub schedule: Schedule
|
||||
));
|
||||
|
||||
values.push(quote!(
|
||||
schedule: Schedule { _not_send: core::marker::PhantomData }
|
||||
));
|
||||
} else {
|
||||
lt = Some(quote!('a));
|
||||
|
||||
items.push(quote!(
|
||||
#[doc = #doc]
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct Schedule<'a> {
|
||||
priority: &'a rtic::export::Priority,
|
||||
}
|
||||
|
||||
impl<'a> Schedule<'a> {
|
||||
#[doc(hidden)]
|
||||
#[inline(always)]
|
||||
pub unsafe fn priority(&self) -> &rtic::export::Priority {
|
||||
&self.priority
|
||||
}
|
||||
}
|
||||
));
|
||||
|
||||
fields.push(quote!(
|
||||
#[doc = #doc]
|
||||
pub schedule: Schedule<'a>
|
||||
));
|
||||
|
||||
values.push(quote!(
|
||||
schedule: Schedule { priority }
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
if ctxt.uses_spawn(app) {
|
||||
let doc = "Tasks that can be `spawn`-ed from this context";
|
||||
if ctxt.is_init() {
|
||||
fields.push(quote!(
|
||||
#[doc = #doc]
|
||||
pub spawn: Spawn
|
||||
));
|
||||
|
||||
items.push(quote!(
|
||||
#[doc = #doc]
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct Spawn {
|
||||
_not_send: core::marker::PhantomData<*mut ()>,
|
||||
}
|
||||
));
|
||||
|
||||
values.push(quote!(spawn: Spawn { _not_send: core::marker::PhantomData }));
|
||||
} else {
|
||||
lt = Some(quote!('a));
|
||||
|
||||
fields.push(quote!(
|
||||
#[doc = #doc]
|
||||
pub spawn: Spawn<'a>
|
||||
));
|
||||
|
||||
let mut instant_method = None;
|
||||
if ctxt.is_idle() {
|
||||
items.push(quote!(
|
||||
#[doc = #doc]
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct Spawn<'a> {
|
||||
priority: &'a rtic::export::Priority,
|
||||
}
|
||||
));
|
||||
|
||||
values.push(quote!(spawn: Spawn { priority }));
|
||||
} else {
|
||||
let instant_field = if app.uses_schedule() {
|
||||
let m = extra.monotonic();
|
||||
|
||||
needs_instant = true;
|
||||
instant_method = Some(quote!(
|
||||
pub unsafe fn instant(&self) -> <#m as rtic::Monotonic>::Instant {
|
||||
self.instant
|
||||
}
|
||||
));
|
||||
Some(quote!(instant: <#m as rtic::Monotonic>::Instant,))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
items.push(quote!(
|
||||
/// Tasks that can be spawned from this context
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct Spawn<'a> {
|
||||
#instant_field
|
||||
priority: &'a rtic::export::Priority,
|
||||
}
|
||||
));
|
||||
|
||||
let _instant = if needs_instant {
|
||||
Some(quote!(, instant))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
values.push(quote!(
|
||||
spawn: Spawn { priority #_instant }
|
||||
));
|
||||
}
|
||||
|
||||
items.push(quote!(
|
||||
impl<'a> Spawn<'a> {
|
||||
#[doc(hidden)]
|
||||
#[inline(always)]
|
||||
pub unsafe fn priority(&self) -> &rtic::export::Priority {
|
||||
self.priority
|
||||
}
|
||||
|
||||
#instant_method
|
||||
}
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
if let Context::Init = ctxt {
|
||||
let init = &app.inits.first().unwrap();
|
||||
let late_resources = util::late_resources_ident(&init.name);
|
||||
|
@ -283,7 +150,7 @@ pub fn codegen(
|
|||
};
|
||||
|
||||
let core = if ctxt.is_init() {
|
||||
if app.uses_schedule() {
|
||||
if extra.monotonic.is_some() {
|
||||
Some(quote!(core: rtic::Peripherals,))
|
||||
} else {
|
||||
Some(quote!(core: rtic::export::Peripherals,))
|
||||
|
@ -337,11 +204,6 @@ pub fn codegen(
|
|||
let rq = util::rq_ident(priority);
|
||||
let inputs = util::inputs_ident(name);
|
||||
|
||||
eprintln!("app name: {}", app.name);
|
||||
eprintln!("inputs {}", &inputs);
|
||||
eprintln!("task name: {}", name);
|
||||
eprintln!("fq {}", fq);
|
||||
eprintln!("rq {}", rq);
|
||||
let app_name = &app.name;
|
||||
let app_path = quote! {crate::#app_name};
|
||||
|
||||
|
@ -349,6 +211,7 @@ pub fn codegen(
|
|||
let enum_ = util::interrupt_ident();
|
||||
let interrupt = &analysis.interrupts.get(&priority);
|
||||
|
||||
// Spawn caller
|
||||
items.push(quote!(
|
||||
#(#cfgs)*
|
||||
pub fn spawn(#(#args,)*) -> Result<(), #ty> {
|
||||
|
@ -357,26 +220,71 @@ pub fn codegen(
|
|||
|
||||
let input = #tupled;
|
||||
|
||||
if let Some(index) = rtic::export::interrupt::free(|_| #app_path::#fq.dequeue()) {
|
||||
unsafe {
|
||||
unsafe {
|
||||
if let Some(index) = rtic::export::interrupt::free(|_| #app_path::#fq.dequeue()) {
|
||||
#app_path::#inputs
|
||||
.get_unchecked_mut(usize::from(index))
|
||||
.as_mut_ptr()
|
||||
.write(input);
|
||||
|
||||
rtic::export::interrupt::free(|_| {
|
||||
#app_path::#rq.enqueue_unchecked((#app_path::#t::#name, index));
|
||||
});
|
||||
|
||||
rtic::pend(#device::#enum_::#interrupt);
|
||||
|
||||
Ok(())
|
||||
} else {
|
||||
Err(input)
|
||||
}
|
||||
|
||||
rtic::export::interrupt::free(|_| {
|
||||
#app_path::#rq.enqueue_unchecked((#app_path::#t::#name, index));
|
||||
});
|
||||
|
||||
rtic::pend(#device::#enum_::#interrupt);
|
||||
|
||||
Ok(())
|
||||
} else {
|
||||
Err(input)
|
||||
}
|
||||
|
||||
}));
|
||||
|
||||
// Schedule caller
|
||||
if extra.monotonic.is_some() {
|
||||
let instants = util::instants_ident(name);
|
||||
|
||||
let tq = util::tq_ident();
|
||||
let m = extra.monotonic();
|
||||
let t = util::schedule_t_ident();
|
||||
|
||||
items.push(quote!(
|
||||
#(#cfgs)*
|
||||
pub fn schedule(
|
||||
instant: <#m as rtic::Monotonic>::Instant
|
||||
#(,#args)*
|
||||
) -> Result<(), #ty> {
|
||||
unsafe {
|
||||
use rtic::Mutex as _;
|
||||
|
||||
let input = #tupled;
|
||||
if let Some(index) = rtic::export::interrupt::free(|_| #app_path::#fq.dequeue()) {
|
||||
#app_path::#inputs
|
||||
.get_unchecked_mut(usize::from(index))
|
||||
.as_mut_ptr()
|
||||
.write(input);
|
||||
|
||||
#app_path::#instants
|
||||
.get_unchecked_mut(usize::from(index))
|
||||
.as_mut_ptr()
|
||||
.write(instant);
|
||||
|
||||
let nr = rtic::export::NotReady {
|
||||
instant,
|
||||
index,
|
||||
task: #app_path::#t::#name,
|
||||
};
|
||||
|
||||
rtic::export::interrupt::free(|_| #app_path::#tq.enqueue_unchecked(nr));
|
||||
|
||||
Ok(())
|
||||
} else {
|
||||
Err(input)
|
||||
}
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
if !items.is_empty() {
|
||||
|
|
|
@ -12,10 +12,10 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec<TokenStream
|
|||
stmts.push(quote!(rtic::export::interrupt::disable();));
|
||||
|
||||
// Populate the FreeQueue
|
||||
for fq in &analysis.free_queues {
|
||||
for fq in &app.software_tasks {
|
||||
// Get the task name
|
||||
let name = fq.0;
|
||||
let task = &app.software_tasks[name];
|
||||
let task = fq.1;
|
||||
let cap = task.args.capacity;
|
||||
|
||||
let fq_ident = util::fq_ident(name);
|
||||
|
@ -81,8 +81,8 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec<TokenStream
|
|||
}
|
||||
|
||||
// Initialize the SysTick if there exist a TimerQueue
|
||||
if let Some(tq) = analysis.timer_queues.first() {
|
||||
let priority = tq.priority;
|
||||
if extra.monotonic.is_some() {
|
||||
let priority = analysis.channels.keys().max().unwrap();
|
||||
|
||||
// Compile time assert that this priority is supported by the device
|
||||
stmts.push(quote!(let _ = [(); ((1 << #nvic_prio_bits) - #priority as usize)];));
|
||||
|
|
|
@ -1,90 +0,0 @@
|
|||
use std::collections::HashSet;
|
||||
|
||||
use proc_macro2::TokenStream as TokenStream2;
|
||||
use quote::quote;
|
||||
use rtic_syntax::ast::App;
|
||||
|
||||
use crate::{
|
||||
check::Extra,
|
||||
codegen::{schedule_body, util},
|
||||
};
|
||||
|
||||
/// Generates all `${ctxt}::Schedule` methods
|
||||
pub fn codegen(app: &App, extra: &Extra) -> Vec<TokenStream2> {
|
||||
let mut items = vec![];
|
||||
|
||||
let mut seen = HashSet::<_>::new();
|
||||
for (scheduler, schedulees) in app.schedule_callers() {
|
||||
let m = extra.monotonic();
|
||||
let instant = quote!(<#m as rtic::Monotonic>::Instant);
|
||||
|
||||
let mut methods = vec![];
|
||||
|
||||
for name in schedulees {
|
||||
let schedulee = &app.software_tasks[name];
|
||||
let cfgs = &schedulee.cfgs;
|
||||
let (args, _, untupled, ty) = util::regroup_inputs(&schedulee.inputs);
|
||||
let args = &args;
|
||||
|
||||
if scheduler.is_init() {
|
||||
// `init` uses a special `schedule` implementation; it doesn't use the
|
||||
// `schedule_${name}` functions which are shared by other contexts
|
||||
|
||||
let body = schedule_body::codegen(scheduler, &name, app);
|
||||
|
||||
methods.push(quote!(
|
||||
#(#cfgs)*
|
||||
pub fn #name(&self, instant: #instant #(,#args)*) -> Result<(), #ty> {
|
||||
#body
|
||||
}
|
||||
));
|
||||
} else {
|
||||
let schedule = util::schedule_ident(name);
|
||||
|
||||
if !seen.contains(name) {
|
||||
// Generate a `schedule_${name}_S${sender}` function
|
||||
seen.insert(name);
|
||||
|
||||
let body = schedule_body::codegen(scheduler, &name, app);
|
||||
|
||||
items.push(quote!(
|
||||
#(#cfgs)*
|
||||
pub unsafe fn #schedule(
|
||||
priority: &rtic::export::Priority,
|
||||
instant: #instant
|
||||
#(,#args)*
|
||||
) -> Result<(), #ty> {
|
||||
#body
|
||||
}
|
||||
));
|
||||
}
|
||||
|
||||
methods.push(quote!(
|
||||
#(#cfgs)*
|
||||
#[inline(always)]
|
||||
pub fn #name(&self, instant: #instant #(,#args)*) -> Result<(), #ty> {
|
||||
unsafe {
|
||||
#schedule(self.priority(), instant #(,#untupled)*)
|
||||
}
|
||||
}
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
let lt = if scheduler.is_init() {
|
||||
None
|
||||
} else {
|
||||
Some(quote!('a))
|
||||
};
|
||||
|
||||
let scheduler = scheduler.ident(app);
|
||||
debug_assert!(!methods.is_empty());
|
||||
items.push(quote!(
|
||||
impl<#lt> #scheduler::Schedule<#lt> {
|
||||
#(#methods)*
|
||||
}
|
||||
));
|
||||
}
|
||||
|
||||
items
|
||||
}
|
|
@ -1,59 +0,0 @@
|
|||
use proc_macro2::TokenStream as TokenStream2;
|
||||
use quote::quote;
|
||||
use rtic_syntax::{ast::App, Context};
|
||||
use syn::Ident;
|
||||
|
||||
use crate::codegen::util;
|
||||
|
||||
pub fn codegen(scheduler: Context, name: &Ident, app: &App) -> TokenStream2 {
|
||||
let schedulee = &app.software_tasks[name];
|
||||
|
||||
let fq = util::fq_ident(name);
|
||||
let tq = util::tq_ident();
|
||||
let (dequeue, enqueue) = if scheduler.is_init() {
|
||||
(quote!(#fq.dequeue()), quote!(#tq.enqueue_unchecked(nr);))
|
||||
} else {
|
||||
(
|
||||
quote!((#fq { priority }).lock(|fq| fq.split().1.dequeue())),
|
||||
quote!((#tq { priority }).lock(|tq| tq.enqueue_unchecked(nr));),
|
||||
)
|
||||
};
|
||||
|
||||
let write_instant = if app.uses_schedule() {
|
||||
let instants = util::instants_ident(name);
|
||||
|
||||
Some(quote!(
|
||||
#instants.get_unchecked_mut(usize::from(index)).as_mut_ptr().write(instant);
|
||||
))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let (_, tupled, _, _) = util::regroup_inputs(&schedulee.inputs);
|
||||
let inputs = util::inputs_ident(name);
|
||||
let t = util::schedule_t_ident();
|
||||
quote!(
|
||||
unsafe {
|
||||
use rtic::Mutex as _;
|
||||
|
||||
let input = #tupled;
|
||||
if let Some(index) = #dequeue {
|
||||
#inputs.get_unchecked_mut(usize::from(index)).as_mut_ptr().write(input);
|
||||
|
||||
#write_instant
|
||||
|
||||
let nr = rtic::export::NotReady {
|
||||
instant,
|
||||
index,
|
||||
task: #t::#name,
|
||||
};
|
||||
|
||||
#enqueue
|
||||
|
||||
Ok(())
|
||||
} else {
|
||||
Err(input)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
|
@ -39,71 +39,50 @@ pub fn codegen(
|
|||
let cap_ty = util::capacity_typenum(cap, true);
|
||||
|
||||
// Create free queues and inputs / instants buffers
|
||||
if let Some(&ceiling) = analysis.free_queues.get(name) {
|
||||
let fq = util::fq_ident(name);
|
||||
let fq = util::fq_ident(name);
|
||||
|
||||
let (fq_ty, fq_expr, mk_uninit): (_, _, Box<dyn Fn() -> Option<_>>) = {
|
||||
(
|
||||
quote!(rtic::export::SCFQ<#cap_ty>),
|
||||
quote!(rtic::export::Queue(unsafe {
|
||||
rtic::export::iQueue::u8_sc()
|
||||
})),
|
||||
Box::new(|| util::link_section_uninit(true)),
|
||||
)
|
||||
};
|
||||
mod_app.push(quote!(
|
||||
/// Queue version of a free-list that keeps track of empty slots in
|
||||
/// the following buffers
|
||||
pub static mut #fq: #fq_ty = #fq_expr;
|
||||
));
|
||||
let (fq_ty, fq_expr, mk_uninit): (_, _, Box<dyn Fn() -> Option<_>>) = {
|
||||
(
|
||||
quote!(rtic::export::SCFQ<#cap_ty>),
|
||||
quote!(rtic::export::Queue(unsafe {
|
||||
rtic::export::iQueue::u8_sc()
|
||||
})),
|
||||
Box::new(|| util::link_section_uninit(true)),
|
||||
)
|
||||
};
|
||||
mod_app.push(quote!(
|
||||
/// Queue version of a free-list that keeps track of empty slots in
|
||||
/// the following buffers
|
||||
pub static mut #fq: #fq_ty = #fq_expr;
|
||||
));
|
||||
|
||||
// Generate a resource proxy if needed
|
||||
if let Some(ceiling) = ceiling {
|
||||
mod_app.push(quote!(
|
||||
struct #fq<'a> {
|
||||
priority: &'a rtic::export::Priority,
|
||||
}
|
||||
));
|
||||
let ref elems = (0..cap)
|
||||
.map(|_| quote!(core::mem::MaybeUninit::uninit()))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
mod_app.push(util::impl_mutex(
|
||||
extra,
|
||||
&[],
|
||||
false,
|
||||
&fq,
|
||||
fq_ty,
|
||||
ceiling,
|
||||
quote!(&mut #fq),
|
||||
));
|
||||
}
|
||||
|
||||
let ref elems = (0..cap)
|
||||
.map(|_| quote!(core::mem::MaybeUninit::uninit()))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
if app.uses_schedule() {
|
||||
let m = extra.monotonic();
|
||||
let instants = util::instants_ident(name);
|
||||
|
||||
let uninit = mk_uninit();
|
||||
mod_app.push(quote!(
|
||||
#uninit
|
||||
/// Buffer that holds the instants associated to the inputs of a task
|
||||
pub static mut #instants:
|
||||
[core::mem::MaybeUninit<<#m as rtic::Monotonic>::Instant>; #cap_lit] =
|
||||
[#(#elems,)*];
|
||||
));
|
||||
}
|
||||
if extra.monotonic.is_some() {
|
||||
let m = extra.monotonic();
|
||||
let instants = util::instants_ident(name);
|
||||
|
||||
let uninit = mk_uninit();
|
||||
let inputs = util::inputs_ident(name);
|
||||
mod_app.push(quote!(
|
||||
#uninit
|
||||
/// Buffer that holds the inputs of a task
|
||||
pub static mut #inputs: [core::mem::MaybeUninit<#input_ty>; #cap_lit] =
|
||||
/// Buffer that holds the instants associated to the inputs of a task
|
||||
pub static mut #instants:
|
||||
[core::mem::MaybeUninit<<#m as rtic::Monotonic>::Instant>; #cap_lit] =
|
||||
[#(#elems,)*];
|
||||
));
|
||||
}
|
||||
|
||||
let uninit = mk_uninit();
|
||||
let inputs_ident = util::inputs_ident(name);
|
||||
mod_app.push(quote!(
|
||||
#uninit
|
||||
/// Buffer that holds the inputs of a task
|
||||
pub static mut #inputs_ident: [core::mem::MaybeUninit<#input_ty>; #cap_lit] =
|
||||
[#(#elems,)*];
|
||||
));
|
||||
|
||||
// `${task}Resources`
|
||||
let mut needs_lt = false;
|
||||
if !task.args.resources.is_empty() {
|
||||
|
|
|
@ -1,121 +0,0 @@
|
|||
use std::collections::HashSet;
|
||||
|
||||
use proc_macro2::TokenStream as TokenStream2;
|
||||
use quote::quote;
|
||||
use rtic_syntax::ast::App;
|
||||
|
||||
use crate::{
|
||||
analyze::Analysis,
|
||||
check::Extra,
|
||||
codegen::{spawn_body, util},
|
||||
};
|
||||
|
||||
/// Generates all `${ctxt}::Spawn` methods
|
||||
pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec<TokenStream2> {
|
||||
let mut items = vec![];
|
||||
|
||||
let mut seen = HashSet::<_>::new();
|
||||
for (spawner, spawnees) in app.spawn_callers() {
|
||||
let mut methods = vec![];
|
||||
|
||||
for name in spawnees {
|
||||
let spawnee = &app.software_tasks[name];
|
||||
let cfgs = &spawnee.cfgs;
|
||||
let (args, _, untupled, ty) = util::regroup_inputs(&spawnee.inputs);
|
||||
let args = &args;
|
||||
|
||||
if spawner.is_init() {
|
||||
// `init` uses a special spawn implementation; it doesn't use the `spawn_${name}`
|
||||
// functions which are shared by other contexts
|
||||
|
||||
let body = spawn_body::codegen(spawner, &name, app, analysis, extra);
|
||||
|
||||
let let_instant = if app.uses_schedule() {
|
||||
let m = extra.monotonic();
|
||||
|
||||
Some(quote!(let instant = unsafe { <#m as rtic::Monotonic>::zero() };))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
methods.push(quote!(
|
||||
#(#cfgs)*
|
||||
pub fn #name(&self #(,#args)*) -> Result<(), #ty> {
|
||||
#let_instant
|
||||
#body
|
||||
}
|
||||
));
|
||||
} else {
|
||||
let spawn = util::spawn_ident(name);
|
||||
|
||||
if !seen.contains(name) {
|
||||
// Generate a `spawn_${name}_S${sender}` function
|
||||
seen.insert(name);
|
||||
|
||||
let instant = if app.uses_schedule() {
|
||||
let m = extra.monotonic();
|
||||
|
||||
Some(quote!(, instant: <#m as rtic::Monotonic>::Instant))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let body = spawn_body::codegen(spawner, &name, app, analysis, extra);
|
||||
|
||||
items.push(quote!(
|
||||
#(#cfgs)*
|
||||
unsafe fn #spawn(
|
||||
priority: &rtic::export::Priority
|
||||
#instant
|
||||
#(,#args)*
|
||||
) -> Result<(), #ty> {
|
||||
#body
|
||||
}
|
||||
));
|
||||
}
|
||||
|
||||
let (let_instant, instant) = if app.uses_schedule() {
|
||||
let m = extra.monotonic();
|
||||
|
||||
(
|
||||
Some(if spawner.is_idle() {
|
||||
quote!(let instant = <#m as rtic::Monotonic>::now();)
|
||||
} else {
|
||||
quote!(let instant = self.instant();)
|
||||
}),
|
||||
Some(quote!(, instant)),
|
||||
)
|
||||
} else {
|
||||
(None, None)
|
||||
};
|
||||
|
||||
methods.push(quote!(
|
||||
#(#cfgs)*
|
||||
#[inline(always)]
|
||||
pub fn #name(&self #(,#args)*) -> Result<(), #ty> {
|
||||
unsafe {
|
||||
#let_instant
|
||||
#spawn(self.priority() #instant #(,#untupled)*)
|
||||
}
|
||||
}
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
let lt = if spawner.is_init() {
|
||||
None
|
||||
} else {
|
||||
Some(quote!('a))
|
||||
};
|
||||
|
||||
let spawner = spawner.ident(app);
|
||||
debug_assert!(!methods.is_empty());
|
||||
items.push(quote!(
|
||||
impl<#lt> #spawner::Spawn<#lt> {
|
||||
#(#methods)*
|
||||
}
|
||||
));
|
||||
}
|
||||
|
||||
items
|
||||
}
|
|
@ -1,76 +0,0 @@
|
|||
use proc_macro2::TokenStream as TokenStream2;
|
||||
use quote::quote;
|
||||
use rtic_syntax::{ast::App, Context};
|
||||
use syn::Ident;
|
||||
|
||||
use crate::{analyze::Analysis, check::Extra, codegen::util};
|
||||
|
||||
pub fn codegen(
|
||||
spawner: Context,
|
||||
name: &Ident,
|
||||
app: &App,
|
||||
analysis: &Analysis,
|
||||
extra: &Extra,
|
||||
) -> TokenStream2 {
|
||||
let spawnee = &app.software_tasks[name];
|
||||
let priority = spawnee.args.priority;
|
||||
|
||||
let write_instant = if app.uses_schedule() {
|
||||
let instants = util::instants_ident(name);
|
||||
|
||||
Some(quote!(
|
||||
#instants.get_unchecked_mut(usize::from(index)).as_mut_ptr().write(instant);
|
||||
))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let t = util::spawn_t_ident(priority);
|
||||
let fq = util::fq_ident(name);
|
||||
let rq = util::rq_ident(priority);
|
||||
let (dequeue, enqueue) = if spawner.is_init() {
|
||||
(
|
||||
quote!(#fq.dequeue()),
|
||||
quote!(#rq.enqueue_unchecked((#t::#name, index));),
|
||||
)
|
||||
} else {
|
||||
(
|
||||
quote!((#fq { priority }.lock(|fq| fq.split().1.dequeue()))),
|
||||
quote!((#rq { priority }.lock(|rq| {
|
||||
rq.split().0.enqueue_unchecked((#t::#name, index))
|
||||
}));),
|
||||
)
|
||||
};
|
||||
|
||||
let device = extra.device;
|
||||
let enum_ = util::interrupt_ident();
|
||||
let interrupt = &analysis.interrupts.get(&priority);
|
||||
let pend = {
|
||||
quote!(
|
||||
rtic::pend(#device::#enum_::#interrupt);
|
||||
)
|
||||
};
|
||||
|
||||
let (_, tupled, _, _) = util::regroup_inputs(&spawnee.inputs);
|
||||
let inputs = util::inputs_ident(name);
|
||||
quote!(
|
||||
unsafe {
|
||||
use rtic::Mutex as _;
|
||||
|
||||
let input = #tupled;
|
||||
if let Some(index) = #dequeue {
|
||||
#inputs.get_unchecked_mut(usize::from(index)).as_mut_ptr().write(input);
|
||||
|
||||
#write_instant
|
||||
|
||||
#enqueue
|
||||
|
||||
#pend
|
||||
|
||||
Ok(())
|
||||
} else {
|
||||
Err(input)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
|
@ -8,16 +8,16 @@ use crate::{analyze::Analysis, check::Extra, codegen::util};
|
|||
pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec<TokenStream2> {
|
||||
let mut items = vec![];
|
||||
|
||||
if let Some(timer_queue) = &analysis.timer_queues.first() {
|
||||
if extra.monotonic.is_some() {
|
||||
let t = util::schedule_t_ident();
|
||||
|
||||
// Enumeration of `schedule`-able tasks
|
||||
{
|
||||
let variants = timer_queue
|
||||
.tasks
|
||||
let variants = app
|
||||
.software_tasks
|
||||
.iter()
|
||||
.map(|name| {
|
||||
let cfgs = &app.software_tasks[name].cfgs;
|
||||
.map(|(name, task)| {
|
||||
let cfgs = &task.cfgs;
|
||||
|
||||
quote!(
|
||||
#(#cfgs)*
|
||||
|
@ -31,7 +31,7 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec<TokenStream
|
|||
#[doc = #doc]
|
||||
#[allow(non_camel_case_types)]
|
||||
#[derive(Clone, Copy)]
|
||||
enum #t {
|
||||
pub enum #t {
|
||||
#(#variants,)*
|
||||
}
|
||||
));
|
||||
|
@ -43,42 +43,31 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec<TokenStream
|
|||
{
|
||||
let doc = format!("Timer queue");
|
||||
let m = extra.monotonic();
|
||||
let n = util::capacity_typenum(timer_queue.capacity, false);
|
||||
let cap = app
|
||||
.software_tasks
|
||||
.iter()
|
||||
.map(|(_name, task)| task.args.capacity)
|
||||
.sum();
|
||||
let n = util::capacity_typenum(cap, false);
|
||||
let tq_ty = quote!(rtic::export::TimerQueue<#m, #t, #n>);
|
||||
|
||||
items.push(quote!(
|
||||
#[doc = #doc]
|
||||
static mut #tq: #tq_ty = rtic::export::TimerQueue(
|
||||
pub static mut #tq: #tq_ty = rtic::export::TimerQueue(
|
||||
rtic::export::BinaryHeap(
|
||||
rtic::export::iBinaryHeap::new()
|
||||
)
|
||||
);
|
||||
|
||||
struct #tq<'a> {
|
||||
priority: &'a rtic::export::Priority,
|
||||
}
|
||||
));
|
||||
|
||||
items.push(util::impl_mutex(
|
||||
extra,
|
||||
&[],
|
||||
false,
|
||||
&tq,
|
||||
tq_ty,
|
||||
timer_queue.ceiling,
|
||||
quote!(&mut #tq),
|
||||
));
|
||||
}
|
||||
|
||||
// Timer queue handler
|
||||
{
|
||||
let device = extra.device;
|
||||
let arms = timer_queue
|
||||
.tasks
|
||||
let arms = app
|
||||
.software_tasks
|
||||
.iter()
|
||||
.map(|name| {
|
||||
let task = &app.software_tasks[name];
|
||||
|
||||
.map(|(name, task)| {
|
||||
let cfgs = &task.cfgs;
|
||||
let priority = task.args.priority;
|
||||
let rq = util::rq_ident(priority);
|
||||
|
@ -95,9 +84,7 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec<TokenStream
|
|||
quote!(
|
||||
#(#cfgs)*
|
||||
#t::#name => {
|
||||
(#rq { priority: &rtic::export::Priority::new(PRIORITY) }).lock(|rq| {
|
||||
rq.split().0.enqueue_unchecked((#rqt::#name, index))
|
||||
});
|
||||
rtic::export::interrupt::free(|_| #rq.split().0.enqueue_unchecked((#rqt::#name, index)));
|
||||
|
||||
#pend
|
||||
}
|
||||
|
@ -105,30 +92,18 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec<TokenStream
|
|||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let priority = timer_queue.priority;
|
||||
let sys_tick = util::suffixed("SysTick");
|
||||
items.push(quote!(
|
||||
#[no_mangle]
|
||||
unsafe fn #sys_tick() {
|
||||
use rtic::Mutex as _;
|
||||
|
||||
/// The priority of this handler
|
||||
const PRIORITY: u8 = #priority;
|
||||
|
||||
rtic::export::run(PRIORITY, || {
|
||||
while let Some((task, index)) = (#tq {
|
||||
// NOTE dynamic priority is always the static priority at this point
|
||||
priority: &rtic::export::Priority::new(PRIORITY),
|
||||
})
|
||||
// NOTE `inline(always)` produces faster and smaller code
|
||||
.lock(#[inline(always)]
|
||||
|tq| tq.dequeue())
|
||||
{
|
||||
match task {
|
||||
#(#arms)*
|
||||
}
|
||||
while let Some((task, index)) = rtic::export::interrupt::free(|_| #tq.dequeue())
|
||||
{
|
||||
match task {
|
||||
#(#arms)*
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
));
|
||||
}
|
||||
|
|
|
@ -207,23 +207,9 @@ pub fn rq_ident(priority: u8) -> Ident {
|
|||
Ident::new(&format!("P{}_RQ", priority), Span::call_site())
|
||||
}
|
||||
|
||||
/// Generates an identifier for a "schedule" function
|
||||
///
|
||||
/// The methods of the `Schedule` structs invoke these functions.
|
||||
pub fn schedule_ident(name: &Ident) -> Ident {
|
||||
Ident::new(&format!("schedule_{}", name.to_string()), Span::call_site())
|
||||
}
|
||||
|
||||
/// Generates an identifier for the `enum` of `schedule`-able tasks
|
||||
pub fn schedule_t_ident() -> Ident {
|
||||
Ident::new(&format!("T"), Span::call_site())
|
||||
}
|
||||
|
||||
/// Generates an identifier for a "spawn" function
|
||||
///
|
||||
/// The methods of the `Spawn` structs invoke these functions.
|
||||
pub fn spawn_ident(name: &Ident) -> Ident {
|
||||
Ident::new(&format!("spawn_{}", name.to_string()), Span::call_site())
|
||||
Ident::new(&format!("SCHED_T"), Span::call_site())
|
||||
}
|
||||
|
||||
/// Generates an identifier for the `enum` of `spawn`-able tasks
|
||||
|
|
|
@ -207,7 +207,6 @@ pub fn app(args: TokenStream, input: TokenStream) -> TokenStream {
|
|||
settings.optimize_priorities = true;
|
||||
settings.parse_binds = true;
|
||||
settings.parse_extern_interrupt = true;
|
||||
settings.parse_schedule = true;
|
||||
|
||||
let (app, analysis) = match rtic_syntax::parse(args, input, settings) {
|
||||
Err(e) => return e.to_compile_error().into(),
|
||||
|
|
Loading…
Reference in a new issue