aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Jorge Aparicio <jorge@japaric.io> 2019-10-28 21:11:13 -0500
committerGravatar Jorge Aparicio <jorge@japaric.io> 2019-10-28 21:11:13 -0500
commita3783a6d3d90ab549766b13651ec8ff8013762c5 (patch)
tree29fc5a2ed50c73ebfeca65fd52ad241d9995677d
parentf9b30a1ff87acd5f3c29a32369f0537e8e3d2bf1 (diff)
downloadrtic-a3783a6d3d90ab549766b13651ec8ff8013762c5.tar.gz
rtic-a3783a6d3d90ab549766b13651ec8ff8013762c5.tar.zst
rtic-a3783a6d3d90ab549766b13651ec8ff8013762c5.zip
WIP generators tasksgenerator-tasks
-rw-r--r--examples/generator.rs61
-rw-r--r--macros/Cargo.toml3
-rw-r--r--macros/src/codegen.rs10
-rw-r--r--macros/src/codegen/hardware_tasks.rs40
-rw-r--r--macros/src/codegen/locals.rs2
-rw-r--r--macros/src/codegen/module.rs4
-rw-r--r--macros/src/codegen/post_init.rs62
-rw-r--r--macros/src/codegen/resources.rs44
-rw-r--r--macros/src/codegen/resources_struct.rs66
-rw-r--r--macros/src/codegen/util.rs45
-rw-r--r--macros/src/lib.rs1
-rw-r--r--src/export.rs23
12 files changed, 348 insertions, 13 deletions
diff --git a/examples/generator.rs b/examples/generator.rs
new file mode 100644
index 00000000..28083480
--- /dev/null
+++ b/examples/generator.rs
@@ -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) {}
+};
diff --git a/macros/Cargo.toml b/macros/Cargo.toml
index 46598038..9169bf5c 100644
--- a/macros/Cargo.toml
+++ b/macros/Cargo.toml
@@ -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 = []
diff --git a/macros/src/codegen.rs b/macros/src/codegen.rs
index 02138481..9439467b 100644
--- a/macros/src/codegen.rs
+++ b/macros/src/codegen.rs
@@ -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)*
diff --git a/macros/src/codegen/hardware_tasks.rs b/macros/src/codegen/hardware_tasks.rs
index a9c2a2bd..81f0b62f 100644
--- a/macros/src/codegen/hardware_tasks.rs
+++ b/macros/src/codegen/hardware_tasks.rs
@@ -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);
diff --git a/macros/src/codegen/locals.rs b/macros/src/codegen/locals.rs
index cbfe05fb..1758c8e7 100644
--- a/macros/src/codegen/locals.rs
+++ b/macros/src/codegen/locals.rs
@@ -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;
diff --git a/macros/src/codegen/module.rs b/macros/src/codegen/module.rs
index 5f077a22..3634fcc0 100644
--- a/macros/src/codegen/module.rs
+++ b/macros/src/codegen/module.rs
@@ -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))
diff --git a/macros/src/codegen/post_init.rs b/macros/src/codegen/post_init.rs
index 19773e45..d4b13181 100644
--- a/macros/src/codegen/post_init.rs
+++ b/macros/src/codegen/post_init.rs
@@ -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)
}
diff --git a/macros/src/codegen/resources.rs b/macros/src/codegen/resources.rs
index 8cb788d1..1af95ada 100644
--- a/macros/src/codegen/resources.rs
+++ b/macros/src/codegen/resources.rs
@@ -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)
}
diff --git a/macros/src/codegen/resources_struct.rs b/macros/src/codegen/resources_struct.rs
index 07a60616..17503a66 100644
--- a/macros/src/codegen/resources_struct.rs
+++ b/macros/src/codegen/resources_struct.rs
@@ -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))
diff --git a/macros/src/codegen/util.rs b/macros/src/codegen/util.rs
index 207272dc..3ee55bfa 100644
--- a/macros/src/codegen/util.rs
+++ b/macros/src/codegen/util.rs
@@ -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())
+}
diff --git a/macros/src/lib.rs b/macros/src/lib.rs
index 54282e1e..9bace086 100644
--- a/macros/src/lib.rs
+++ b/macros/src/lib.rs
@@ -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(),
diff --git a/src/export.rs b/src/export.rs
index 96c444bf..44eb5348 100644
--- a/src/export.rs
+++ b/src/export.rs
@@ -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)