mirror of
https://github.com/rtic-rs/rtic.git
synced 2024-11-25 21:19:35 +01:00
WIP generators tasks
This commit is contained in:
parent
f9b30a1ff8
commit
a3783a6d3d
12 changed files with 348 additions and 13 deletions
61
examples/generator.rs
Normal file
61
examples/generator.rs
Normal file
|
@ -0,0 +1,61 @@
|
|||
//! examples/hardware.rs
|
||||
|
||||
#![feature(generator_trait)]
|
||||
#![feature(generators)]
|
||||
#![feature(never_type)]
|
||||
#![feature(type_alias_impl_trait)]
|
||||
#![deny(unsafe_code)]
|
||||
#![deny(warnings)]
|
||||
#![no_main]
|
||||
#![no_std]
|
||||
|
||||
use cortex_m_semihosting::{debug, hprintln};
|
||||
use lm3s6965::Interrupt;
|
||||
use panic_semihosting as _;
|
||||
|
||||
#[rtfm::app(device = lm3s6965)]
|
||||
const APP: () = {
|
||||
struct Resources {
|
||||
#[init(0)]
|
||||
x: i64,
|
||||
}
|
||||
|
||||
#[init]
|
||||
fn init(_: init::Context) {
|
||||
hprintln!("init").ok();
|
||||
}
|
||||
|
||||
#[idle]
|
||||
fn idle(_: idle::Context) -> ! {
|
||||
hprintln!("idle").ok();
|
||||
|
||||
rtfm::pend(Interrupt::UART0);
|
||||
hprintln!("C").ok();
|
||||
rtfm::pend(Interrupt::UART0);
|
||||
hprintln!("E").ok();
|
||||
debug::exit(debug::EXIT_SUCCESS);
|
||||
|
||||
loop {}
|
||||
}
|
||||
|
||||
#[task(binds = UART0, priority = 1, resources = [x])]
|
||||
fn uart0(mut cx: uart0::Context) -> impl Generator<Yield = (), Return = !> {
|
||||
hprintln!("A").ok();
|
||||
|
||||
move || loop {
|
||||
hprintln!("B").ok();
|
||||
yield;
|
||||
|
||||
cx.resources.x.lock(|x| {
|
||||
hprintln!("lock").ok();
|
||||
*x += 1;
|
||||
});
|
||||
|
||||
hprintln!("D").ok();
|
||||
yield;
|
||||
}
|
||||
}
|
||||
|
||||
#[task(binds = UART1, priority = 2, resources = [x])]
|
||||
fn uart1(_: uart1::Context) {}
|
||||
};
|
|
@ -21,7 +21,8 @@ proc-macro = true
|
|||
proc-macro2 = "1"
|
||||
quote = "1"
|
||||
syn = "1"
|
||||
rtfm-syntax = "0.4.0-beta.2"
|
||||
# rtfm-syntax = "0.4.0-beta.2"
|
||||
rtfm-syntax = { git = "https://github.com/rtfm-rs/rtfm-syntax", branch = "impl-generator" }
|
||||
|
||||
[features]
|
||||
heterogeneous = []
|
||||
|
|
|
@ -39,7 +39,8 @@ pub fn app(app: &App, analysis: &Analysis, extra: &Extra) -> TokenStream2 {
|
|||
let (const_app_init, root_init, user_init, call_init) =
|
||||
init::codegen(core, app, analysis, extra);
|
||||
|
||||
let (const_app_post_init, post_init_stmts) = post_init::codegen(core, analysis, extra);
|
||||
let (const_app_post_init, root_post_init, post_init_stmts) =
|
||||
post_init::codegen(core, &app, analysis, extra);
|
||||
|
||||
let (const_app_idle, root_idle, user_idle, call_idle) =
|
||||
idle::codegen(core, app, analysis, extra);
|
||||
|
@ -53,6 +54,8 @@ pub fn app(app: &App, analysis: &Analysis, extra: &Extra) -> TokenStream2 {
|
|||
root.push(quote!(
|
||||
#(#root_init)*
|
||||
|
||||
#(#root_post_init)*
|
||||
|
||||
#(#root_idle)*
|
||||
));
|
||||
|
||||
|
@ -87,7 +90,8 @@ pub fn app(app: &App, analysis: &Analysis, extra: &Extra) -> TokenStream2 {
|
|||
));
|
||||
}
|
||||
|
||||
let (const_app_resources, mod_resources) = resources::codegen(app, analysis, extra);
|
||||
let (const_app_resources, mod_resources, mod_gresources) =
|
||||
resources::codegen(app, analysis, extra);
|
||||
|
||||
let (const_app_hardware_tasks, root_hardware_tasks, user_hardware_tasks) =
|
||||
hardware_tasks::codegen(app, analysis, extra);
|
||||
|
@ -128,6 +132,8 @@ pub fn app(app: &App, analysis: &Analysis, extra: &Extra) -> TokenStream2 {
|
|||
|
||||
#mod_resources
|
||||
|
||||
#mod_gresources
|
||||
|
||||
#(#root_hardware_tasks)*
|
||||
|
||||
#(#root_software_tasks)*
|
||||
|
|
|
@ -29,6 +29,46 @@ pub fn codegen(
|
|||
let mut user_tasks = vec![];
|
||||
|
||||
for (name, task) in &app.hardware_tasks {
|
||||
// TODO split this big conditional to reuse code below
|
||||
if task.is_generator {
|
||||
let symbol = task.args.binds.clone();
|
||||
let gen_i = util::generator_ident(&name.to_string());
|
||||
const_app.push(quote!(
|
||||
#[allow(non_snake_case)]
|
||||
#[no_mangle]
|
||||
unsafe fn #symbol() {
|
||||
use core::ops::Generator;
|
||||
|
||||
core::pin::Pin::new_unchecked(&mut *#gen_i.as_mut_ptr()).resume();
|
||||
}
|
||||
));
|
||||
|
||||
let priority = task.args.priority;
|
||||
// `${task}Resources`
|
||||
if !task.args.resources.is_empty() {
|
||||
let (item, constructor) = resources_struct::codegen(
|
||||
Context::HardwareTask(name),
|
||||
priority,
|
||||
&mut false,
|
||||
app,
|
||||
analysis,
|
||||
);
|
||||
|
||||
root.push(item);
|
||||
|
||||
const_app.push(constructor);
|
||||
}
|
||||
|
||||
root.push(module::codegen(
|
||||
Context::HardwareTask(name),
|
||||
false,
|
||||
app,
|
||||
extra,
|
||||
));
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
let core = task.args.core;
|
||||
let cfg_core = util::cfg_core(core, app.args.cores);
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@ pub fn codegen(
|
|||
) {
|
||||
assert!(!locals.is_empty());
|
||||
|
||||
let runs_once = ctxt.runs_once();
|
||||
let runs_once = ctxt.runs_once(app);
|
||||
let ident = util::locals_ident(ctxt, app);
|
||||
|
||||
let mut lt = None;
|
||||
|
|
|
@ -111,7 +111,7 @@ pub fn codegen(ctxt: Context, resources_tick: bool, app: &App, extra: &Extra) ->
|
|||
pub resources: Resources<#lt>
|
||||
));
|
||||
|
||||
let priority = if ctxt.is_init() {
|
||||
let priority = if ctxt.is_init() || ctxt.is_generator(app) {
|
||||
None
|
||||
} else {
|
||||
Some(quote!(priority))
|
||||
|
@ -281,7 +281,7 @@ pub fn codegen(ctxt: Context, resources_tick: bool, app: &App, extra: &Extra) ->
|
|||
None
|
||||
};
|
||||
|
||||
let priority = if ctxt.is_init() {
|
||||
let priority = if ctxt.is_init() || ctxt.is_generator(app) {
|
||||
None
|
||||
} else {
|
||||
Some(quote!(priority: &#lt rtfm::export::Priority))
|
||||
|
|
|
@ -1,15 +1,29 @@
|
|||
use proc_macro2::TokenStream as TokenStream2;
|
||||
use quote::quote;
|
||||
use rtfm_syntax::{ast::App, Context};
|
||||
|
||||
use crate::{analyze::Analysis, check::Extra, codegen::util};
|
||||
use crate::{
|
||||
analyze::Analysis,
|
||||
check::Extra,
|
||||
codegen::{locals, util},
|
||||
};
|
||||
|
||||
/// Generates code that runs after `#[init]` returns
|
||||
pub fn codegen(
|
||||
core: u8,
|
||||
app: &App,
|
||||
analysis: &Analysis,
|
||||
extra: &Extra,
|
||||
) -> (Vec<TokenStream2>, Vec<TokenStream2>) {
|
||||
) -> (
|
||||
// const_app
|
||||
Vec<TokenStream2>,
|
||||
// root
|
||||
Vec<TokenStream2>,
|
||||
// stmts
|
||||
Vec<TokenStream2>,
|
||||
) {
|
||||
let mut const_app = vec![];
|
||||
let mut root = vec![];
|
||||
let mut stmts = vec![];
|
||||
|
||||
// initialize late resources
|
||||
|
@ -22,6 +36,48 @@ pub fn codegen(
|
|||
}
|
||||
}
|
||||
|
||||
// TODO WIP
|
||||
for (name, task) in &app.hardware_tasks {
|
||||
if task.is_generator {
|
||||
let name_s = name.to_string();
|
||||
let gen_i = util::generator_ident(&name_s);
|
||||
let gen_t = util::generator_type(&name_s);
|
||||
const_app.push(quote!(
|
||||
static mut #gen_i: core::mem::MaybeUninit<#gen_t> =
|
||||
core::mem::MaybeUninit::uninit();
|
||||
));
|
||||
|
||||
let (locals_pat, locals_new) = if task.locals.is_empty() {
|
||||
(None, quote!())
|
||||
} else {
|
||||
let (struct_, pat) =
|
||||
locals::codegen(Context::HardwareTask(name), &task.locals, core, app);
|
||||
|
||||
root.push(struct_);
|
||||
|
||||
(Some(pat), quote!(#name::Locals::new(),))
|
||||
};
|
||||
|
||||
let context = &task.context;
|
||||
let task_stmts = &task.stmts;
|
||||
let locals_pat = locals_pat.iter();
|
||||
root.push(quote!(
|
||||
type #gen_t = impl core::ops::Generator<Yield = (), Return = !>;
|
||||
|
||||
// #[allow(non_snake_case)]
|
||||
fn #name(#(#locals_pat,)* #context: #name::Context) -> #gen_t {
|
||||
use rtfm::Mutex as _;
|
||||
|
||||
#(#task_stmts)*
|
||||
}
|
||||
));
|
||||
|
||||
stmts.push(quote!(
|
||||
#gen_i.as_mut_ptr().write(#name(#locals_new #name::Context::new()));
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
if analysis.timer_queues.is_empty() {
|
||||
// cross-initialization barriers -- notify *other* cores that their resources have been
|
||||
// initialized
|
||||
|
@ -151,5 +207,5 @@ pub fn codegen(
|
|||
// enable the interrupts -- this completes the `init`-ialization phase
|
||||
stmts.push(quote!(rtfm::export::interrupt::enable();));
|
||||
|
||||
(const_app, stmts)
|
||||
(const_app, root, stmts)
|
||||
}
|
||||
|
|
|
@ -17,9 +17,12 @@ pub fn codegen(
|
|||
Vec<TokenStream2>,
|
||||
// mod_resources -- the `resources` module
|
||||
TokenStream2,
|
||||
// mod_gresources -- the `gresources` module
|
||||
TokenStream2,
|
||||
) {
|
||||
let mut const_app = vec![];
|
||||
let mut mod_resources = vec![];
|
||||
let mut mod_gresources = vec![];
|
||||
|
||||
for (name, res, expr, loc) in app.resources(analysis) {
|
||||
let cfgs = &res.cfgs;
|
||||
|
@ -77,6 +80,8 @@ pub fn codegen(
|
|||
if let Some(Ownership::Contended { ceiling }) = analysis.ownerships.get(name) {
|
||||
let cfg_core = util::cfg_core(loc.core().expect("UNREACHABLE"), app.args.cores);
|
||||
|
||||
// TODO generate less code -- we don't always need both `gresources::foo` and
|
||||
// `resources::foo`
|
||||
mod_resources.push(quote!(
|
||||
#[allow(non_camel_case_types)]
|
||||
#(#cfgs)*
|
||||
|
@ -114,8 +119,37 @@ pub fn codegen(
|
|||
name,
|
||||
quote!(#ty),
|
||||
*ceiling,
|
||||
ptr.clone(),
|
||||
));
|
||||
|
||||
mod_gresources.push(quote!(
|
||||
#[allow(non_camel_case_types)]
|
||||
#(#cfgs)*
|
||||
#cfg_core
|
||||
pub struct #name {
|
||||
_not_send_or_sync: core::marker::PhantomData<*mut ()>,
|
||||
}
|
||||
|
||||
#(#cfgs)*
|
||||
#cfg_core
|
||||
impl #name {
|
||||
#[inline(always)]
|
||||
pub unsafe fn new() -> Self {
|
||||
#name { _not_send_or_sync: core::marker::PhantomData }
|
||||
}
|
||||
}
|
||||
));
|
||||
|
||||
const_app.push(util::impl_gmutex(
|
||||
extra,
|
||||
cfgs,
|
||||
cfg_core.as_ref(),
|
||||
name,
|
||||
quote!(#ty),
|
||||
*ceiling,
|
||||
ptr,
|
||||
));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -129,5 +163,13 @@ pub fn codegen(
|
|||
})
|
||||
};
|
||||
|
||||
(const_app, mod_resources)
|
||||
let mod_gresources = if mod_gresources.is_empty() {
|
||||
quote!()
|
||||
} else {
|
||||
quote!(mod gresources {
|
||||
#(#mod_gresources)*
|
||||
})
|
||||
};
|
||||
|
||||
(const_app, mod_resources, mod_gresources)
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ use rtfm_syntax::{ast::App, Context};
|
|||
|
||||
use crate::{analyze::Analysis, codegen::util};
|
||||
|
||||
// TODO need to do something different when generators are involved
|
||||
pub fn codegen(
|
||||
ctxt: Context,
|
||||
priority: u8,
|
||||
|
@ -63,6 +64,67 @@ pub fn codegen(
|
|||
#name: &mut #name
|
||||
));
|
||||
}
|
||||
} else if ctxt.is_generator(app) {
|
||||
let ownership = &analysis.ownerships[name];
|
||||
|
||||
if ownership.needs_lock(priority) {
|
||||
if mut_.is_none() {
|
||||
// mod gresourcesd
|
||||
unimplemented!()
|
||||
} else {
|
||||
// resource proxy
|
||||
fields.push(quote!(
|
||||
#(#cfgs)*
|
||||
pub #name: gresources::#name
|
||||
));
|
||||
|
||||
values.push(quote!(
|
||||
#(#cfgs)*
|
||||
#name: gresources::#name::new()
|
||||
|
||||
));
|
||||
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
let lt = if ctxt.runs_once(app) {
|
||||
quote!('static)
|
||||
} else {
|
||||
lt = Some(quote!('a));
|
||||
quote!('a)
|
||||
};
|
||||
|
||||
if ownership.is_owned() || mut_.is_none() {
|
||||
fields.push(quote!(
|
||||
#(#cfgs)*
|
||||
pub #name: &#lt #mut_ #ty
|
||||
));
|
||||
} else {
|
||||
fields.push(quote!(
|
||||
#(#cfgs)*
|
||||
pub #name: &#lt mut #ty
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
let is_late = expr.is_none();
|
||||
if is_late {
|
||||
let expr = if mut_.is_some() {
|
||||
quote!(&mut *#name.as_mut_ptr())
|
||||
} else {
|
||||
quote!(&*#name.as_ptr())
|
||||
};
|
||||
|
||||
values.push(quote!(
|
||||
#(#cfgs)*
|
||||
#name: #expr
|
||||
));
|
||||
} else {
|
||||
values.push(quote!(
|
||||
#(#cfgs)*
|
||||
#name: &#mut_ #name
|
||||
));
|
||||
}
|
||||
} else {
|
||||
let ownership = &analysis.ownerships[name];
|
||||
|
||||
|
@ -92,7 +154,7 @@ pub fn codegen(
|
|||
continue;
|
||||
}
|
||||
} else {
|
||||
let lt = if ctxt.runs_once() {
|
||||
let lt = if ctxt.runs_once(app) {
|
||||
quote!('static)
|
||||
} else {
|
||||
lt = Some(quote!('a));
|
||||
|
@ -161,7 +223,7 @@ pub fn codegen(
|
|||
}
|
||||
);
|
||||
|
||||
let arg = if ctxt.is_init() {
|
||||
let arg = if ctxt.is_init() || ctxt.is_generator(app) {
|
||||
None
|
||||
} else {
|
||||
Some(quote!(priority: &#lt rtfm::export::Priority))
|
||||
|
|
|
@ -91,6 +91,43 @@ pub fn impl_mutex(
|
|||
)
|
||||
}
|
||||
|
||||
/// Generates a `Mutex` implementation for a resource seen from a generator task
|
||||
pub fn impl_gmutex(
|
||||
extra: &Extra,
|
||||
cfgs: &[Attribute],
|
||||
cfg_core: Option<&TokenStream2>,
|
||||
name: &Ident,
|
||||
ty: TokenStream2,
|
||||
ceiling: u8,
|
||||
ptr: TokenStream2,
|
||||
) -> TokenStream2 {
|
||||
let path = quote!(gresources::#name);
|
||||
|
||||
let device = extra.device;
|
||||
quote!(
|
||||
#(#cfgs)*
|
||||
#cfg_core
|
||||
impl rtfm::Mutex for #path {
|
||||
type T = #ty;
|
||||
|
||||
#[inline(always)]
|
||||
fn lock<R>(&mut self, f: impl FnOnce(&mut #ty) -> R) -> R {
|
||||
/// Priority ceiling
|
||||
const CEILING: u8 = #ceiling;
|
||||
|
||||
unsafe {
|
||||
rtfm::export::glock(
|
||||
#ptr,
|
||||
CEILING,
|
||||
#device::NVIC_PRIO_BITS,
|
||||
f,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/// Generates an identifier for a cross-initialization barrier
|
||||
pub fn init_barrier(initializer: Core) -> Ident {
|
||||
Ident::new(&format!("IB{}", initializer), Span::call_site())
|
||||
|
@ -323,3 +360,11 @@ pub fn suffixed(name: &str, core: u8) -> Ident {
|
|||
pub fn tq_ident(core: Core) -> Ident {
|
||||
Ident::new(&format!("TQ{}", core), Span::call_site())
|
||||
}
|
||||
|
||||
pub fn generator_ident(task: &str) -> Ident {
|
||||
Ident::new(&format!("{}S", task), Span::call_site())
|
||||
}
|
||||
|
||||
pub fn generator_type(task: &str) -> Ident {
|
||||
Ident::new(&format!("{}T", task), Span::call_site())
|
||||
}
|
||||
|
|
|
@ -209,6 +209,7 @@ pub fn app(args: TokenStream, input: TokenStream) -> TokenStream {
|
|||
settings.parse_cores = cfg!(feature = "heterogeneous") || cfg!(feature = "homogeneous");
|
||||
settings.parse_extern_interrupt = true;
|
||||
settings.parse_schedule = true;
|
||||
settings.parse_impl_generator = true;
|
||||
|
||||
let (app, analysis) = match rtfm_syntax::parse(args, input, settings) {
|
||||
Err(e) => return e.to_compile_error().into(),
|
||||
|
|
|
@ -5,7 +5,7 @@ use core::{
|
|||
|
||||
pub use crate::tq::{NotReady, TimerQueue};
|
||||
#[cfg(armv7m)]
|
||||
pub use cortex_m::register::basepri;
|
||||
pub use cortex_m::register::{basepri, basepri_max};
|
||||
pub use cortex_m::{
|
||||
asm::wfi,
|
||||
interrupt,
|
||||
|
@ -145,6 +145,25 @@ pub unsafe fn lock<T, R>(
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(armv7m)]
|
||||
#[inline(always)]
|
||||
pub unsafe fn glock<T, R>(
|
||||
ptr: *mut T,
|
||||
ceiling: u8,
|
||||
nvic_prio_bits: u8,
|
||||
f: impl FnOnce(&mut T) -> R,
|
||||
) -> R {
|
||||
if ceiling == (1 << nvic_prio_bits) {
|
||||
interrupt::free(|_| f(&mut *ptr))
|
||||
} else {
|
||||
let current = basepri::read();
|
||||
basepri_max::write(logical2hw(ceiling, nvic_prio_bits));
|
||||
let r = f(&mut *ptr);
|
||||
basepri::write(logical2hw(current, nvic_prio_bits));
|
||||
r
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(armv7m))]
|
||||
#[inline(always)]
|
||||
pub unsafe fn lock<T, R>(
|
||||
|
@ -166,6 +185,8 @@ pub unsafe fn lock<T, R>(
|
|||
}
|
||||
}
|
||||
|
||||
// TODO glock for ARMv6-M
|
||||
|
||||
#[inline]
|
||||
pub fn logical2hw(logical: u8, nvic_prio_bits: u8) -> u8 {
|
||||
((1 << nvic_prio_bits) - logical) << (8 - nvic_prio_bits)
|
||||
|
|
Loading…
Reference in a new issue