WIP generators tasks

This commit is contained in:
Jorge Aparicio 2019-10-28 21:11:13 -05:00
parent f9b30a1ff8
commit a3783a6d3d
12 changed files with 348 additions and 13 deletions

61
examples/generator.rs Normal file
View 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) {}
};

View file

@ -21,7 +21,8 @@ proc-macro = true
proc-macro2 = "1" proc-macro2 = "1"
quote = "1" quote = "1"
syn = "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] [features]
heterogeneous = [] heterogeneous = []

View file

@ -39,7 +39,8 @@ pub fn app(app: &App, analysis: &Analysis, extra: &Extra) -> TokenStream2 {
let (const_app_init, root_init, user_init, call_init) = let (const_app_init, root_init, user_init, call_init) =
init::codegen(core, app, analysis, extra); 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) = let (const_app_idle, root_idle, user_idle, call_idle) =
idle::codegen(core, app, analysis, extra); idle::codegen(core, app, analysis, extra);
@ -53,6 +54,8 @@ pub fn app(app: &App, analysis: &Analysis, extra: &Extra) -> TokenStream2 {
root.push(quote!( root.push(quote!(
#(#root_init)* #(#root_init)*
#(#root_post_init)*
#(#root_idle)* #(#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) = let (const_app_hardware_tasks, root_hardware_tasks, user_hardware_tasks) =
hardware_tasks::codegen(app, analysis, extra); hardware_tasks::codegen(app, analysis, extra);
@ -128,6 +132,8 @@ pub fn app(app: &App, analysis: &Analysis, extra: &Extra) -> TokenStream2 {
#mod_resources #mod_resources
#mod_gresources
#(#root_hardware_tasks)* #(#root_hardware_tasks)*
#(#root_software_tasks)* #(#root_software_tasks)*

View file

@ -29,6 +29,46 @@ pub fn codegen(
let mut user_tasks = vec![]; let mut user_tasks = vec![];
for (name, task) in &app.hardware_tasks { 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 core = task.args.core;
let cfg_core = util::cfg_core(core, app.args.cores); let cfg_core = util::cfg_core(core, app.args.cores);

View file

@ -20,7 +20,7 @@ pub fn codegen(
) { ) {
assert!(!locals.is_empty()); 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 ident = util::locals_ident(ctxt, app);
let mut lt = None; let mut lt = None;

View file

@ -111,7 +111,7 @@ pub fn codegen(ctxt: Context, resources_tick: bool, app: &App, extra: &Extra) ->
pub resources: Resources<#lt> pub resources: Resources<#lt>
)); ));
let priority = if ctxt.is_init() { let priority = if ctxt.is_init() || ctxt.is_generator(app) {
None None
} else { } else {
Some(quote!(priority)) Some(quote!(priority))
@ -281,7 +281,7 @@ pub fn codegen(ctxt: Context, resources_tick: bool, app: &App, extra: &Extra) ->
None None
}; };
let priority = if ctxt.is_init() { let priority = if ctxt.is_init() || ctxt.is_generator(app) {
None None
} else { } else {
Some(quote!(priority: &#lt rtfm::export::Priority)) Some(quote!(priority: &#lt rtfm::export::Priority))

View file

@ -1,15 +1,29 @@
use proc_macro2::TokenStream as TokenStream2; use proc_macro2::TokenStream as TokenStream2;
use quote::quote; 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 /// Generates code that runs after `#[init]` returns
pub fn codegen( pub fn codegen(
core: u8, core: u8,
app: &App,
analysis: &Analysis, analysis: &Analysis,
extra: &Extra, extra: &Extra,
) -> (Vec<TokenStream2>, Vec<TokenStream2>) { ) -> (
// const_app
Vec<TokenStream2>,
// root
Vec<TokenStream2>,
// stmts
Vec<TokenStream2>,
) {
let mut const_app = vec![]; let mut const_app = vec![];
let mut root = vec![];
let mut stmts = vec![]; let mut stmts = vec![];
// initialize late resources // 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() { if analysis.timer_queues.is_empty() {
// cross-initialization barriers -- notify *other* cores that their resources have been // cross-initialization barriers -- notify *other* cores that their resources have been
// initialized // initialized
@ -151,5 +207,5 @@ pub fn codegen(
// enable the interrupts -- this completes the `init`-ialization phase // enable the interrupts -- this completes the `init`-ialization phase
stmts.push(quote!(rtfm::export::interrupt::enable();)); stmts.push(quote!(rtfm::export::interrupt::enable();));
(const_app, stmts) (const_app, root, stmts)
} }

View file

@ -17,9 +17,12 @@ pub fn codegen(
Vec<TokenStream2>, Vec<TokenStream2>,
// mod_resources -- the `resources` module // mod_resources -- the `resources` module
TokenStream2, TokenStream2,
// mod_gresources -- the `gresources` module
TokenStream2,
) { ) {
let mut const_app = vec![]; let mut const_app = vec![];
let mut mod_resources = vec![]; let mut mod_resources = vec![];
let mut mod_gresources = vec![];
for (name, res, expr, loc) in app.resources(analysis) { for (name, res, expr, loc) in app.resources(analysis) {
let cfgs = &res.cfgs; let cfgs = &res.cfgs;
@ -77,6 +80,8 @@ pub fn codegen(
if let Some(Ownership::Contended { ceiling }) = analysis.ownerships.get(name) { if let Some(Ownership::Contended { ceiling }) = analysis.ownerships.get(name) {
let cfg_core = util::cfg_core(loc.core().expect("UNREACHABLE"), app.args.cores); 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!( mod_resources.push(quote!(
#[allow(non_camel_case_types)] #[allow(non_camel_case_types)]
#(#cfgs)* #(#cfgs)*
@ -114,8 +119,37 @@ pub fn codegen(
name, name,
quote!(#ty), quote!(#ty),
*ceiling, *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, 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)
} }

View file

@ -4,6 +4,7 @@ use rtfm_syntax::{ast::App, Context};
use crate::{analyze::Analysis, codegen::util}; use crate::{analyze::Analysis, codegen::util};
// TODO need to do something different when generators are involved
pub fn codegen( pub fn codegen(
ctxt: Context, ctxt: Context,
priority: u8, priority: u8,
@ -63,6 +64,67 @@ pub fn codegen(
#name: &mut #name #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 { } else {
let ownership = &analysis.ownerships[name]; let ownership = &analysis.ownerships[name];
@ -92,7 +154,7 @@ pub fn codegen(
continue; continue;
} }
} else { } else {
let lt = if ctxt.runs_once() { let lt = if ctxt.runs_once(app) {
quote!('static) quote!('static)
} else { } else {
lt = Some(quote!('a)); 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 None
} else { } else {
Some(quote!(priority: &#lt rtfm::export::Priority)) Some(quote!(priority: &#lt rtfm::export::Priority))

View file

@ -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 /// Generates an identifier for a cross-initialization barrier
pub fn init_barrier(initializer: Core) -> Ident { pub fn init_barrier(initializer: Core) -> Ident {
Ident::new(&format!("IB{}", initializer), Span::call_site()) 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 { pub fn tq_ident(core: Core) -> Ident {
Ident::new(&format!("TQ{}", core), Span::call_site()) 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())
}

View file

@ -209,6 +209,7 @@ pub fn app(args: TokenStream, input: TokenStream) -> TokenStream {
settings.parse_cores = cfg!(feature = "heterogeneous") || cfg!(feature = "homogeneous"); settings.parse_cores = cfg!(feature = "heterogeneous") || cfg!(feature = "homogeneous");
settings.parse_extern_interrupt = true; settings.parse_extern_interrupt = true;
settings.parse_schedule = true; settings.parse_schedule = true;
settings.parse_impl_generator = true;
let (app, analysis) = match rtfm_syntax::parse(args, input, settings) { let (app, analysis) = match rtfm_syntax::parse(args, input, settings) {
Err(e) => return e.to_compile_error().into(), Err(e) => return e.to_compile_error().into(),

View file

@ -5,7 +5,7 @@ use core::{
pub use crate::tq::{NotReady, TimerQueue}; pub use crate::tq::{NotReady, TimerQueue};
#[cfg(armv7m)] #[cfg(armv7m)]
pub use cortex_m::register::basepri; pub use cortex_m::register::{basepri, basepri_max};
pub use cortex_m::{ pub use cortex_m::{
asm::wfi, asm::wfi,
interrupt, 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))] #[cfg(not(armv7m))]
#[inline(always)] #[inline(always)]
pub unsafe fn lock<T, R>( pub unsafe fn lock<T, R>(
@ -166,6 +185,8 @@ pub unsafe fn lock<T, R>(
} }
} }
// TODO glock for ARMv6-M
#[inline] #[inline]
pub fn logical2hw(logical: u8, nvic_prio_bits: u8) -> u8 { pub fn logical2hw(logical: u8, nvic_prio_bits: u8) -> u8 {
((1 << nvic_prio_bits) - logical) << (8 - nvic_prio_bits) ((1 << nvic_prio_bits) - logical) << (8 - nvic_prio_bits)