aboutsummaryrefslogtreecommitdiff
path: root/macros/src/codegen/post_init.rs
diff options
context:
space:
mode:
authorGravatar Jorge Aparicio <jorge@japaric.io> 2019-06-13 23:56:59 +0200
committerGravatar Jorge Aparicio <jorge@japaric.io> 2019-06-13 23:56:59 +0200
commit81275bfa4f41e2066770087f3a33cad4227eab41 (patch)
treec779a68e7cecf4c2613c7593376f980cea5dbc05 /macros/src/codegen/post_init.rs
parentfafeeb27270ef24fc3852711c6032f65aa7dbcc0 (diff)
downloadrtic-81275bfa4f41e2066770087f3a33cad4227eab41.tar.gz
rtic-81275bfa4f41e2066770087f3a33cad4227eab41.tar.zst
rtic-81275bfa4f41e2066770087f3a33cad4227eab41.zip
rtfm-syntax refactor + heterogeneous multi-core support
Diffstat (limited to 'macros/src/codegen/post_init.rs')
-rw-r--r--macros/src/codegen/post_init.rs139
1 files changed, 139 insertions, 0 deletions
diff --git a/macros/src/codegen/post_init.rs b/macros/src/codegen/post_init.rs
new file mode 100644
index 00000000..f492d31d
--- /dev/null
+++ b/macros/src/codegen/post_init.rs
@@ -0,0 +1,139 @@
+use proc_macro2::TokenStream as TokenStream2;
+use quote::quote;
+
+use crate::{analyze::Analysis, check::Extra, codegen::util};
+
+/// Generates code that runs after `#[init]` returns
+pub fn codegen(
+ core: u8,
+ analysis: &Analysis,
+ extra: &Extra,
+) -> (Vec<TokenStream2>, Vec<TokenStream2>) {
+ let mut const_app = vec![];
+ let mut stmts = vec![];
+
+ // initialize late resources
+ if let Some(late_resources) = analysis.late_resources.get(&core) {
+ for name in late_resources {
+ // if it's live
+ if analysis.locations.get(name).is_some() {
+ stmts.push(quote!(#name.as_mut_ptr().write(late.#name);));
+ }
+ }
+ }
+
+ if analysis.timer_queues.is_empty() {
+ // cross-initialization barriers -- notify *other* cores that their resources have been
+ // initialized
+ if analysis.initialization_barriers.contains_key(&core) {
+ let ib = util::init_barrier(core);
+
+ const_app.push(quote!(
+ #[rtfm::export::shared]
+ static #ib: rtfm::export::Barrier = rtfm::export::Barrier::new();
+ ));
+
+ stmts.push(quote!(
+ #ib.release();
+ ));
+ }
+
+ // then wait until the other cores have initialized *our* resources
+ for (&initializer, users) in &analysis.initialization_barriers {
+ if users.contains(&core) {
+ let ib = util::init_barrier(initializer);
+
+ stmts.push(quote!(
+ #ib.wait();
+ ));
+ }
+ }
+
+ // cross-spawn barriers: wait until other cores are ready to receive messages
+ for (&receiver, senders) in &analysis.spawn_barriers {
+ if senders.get(&core) == Some(&false) {
+ let sb = util::spawn_barrier(receiver);
+
+ stmts.push(quote!(
+ #sb.wait();
+ ));
+ }
+ }
+ } else {
+ // if the `schedule` API is used then we'll synchronize all cores to leave the
+ // `init`-ialization phase at the same time. In this case the rendezvous barrier makes the
+ // cross-initialization and spawn barriers unnecessary
+
+ let m = extra.monotonic();
+
+ if analysis.timer_queues.len() == 1 {
+ // reset the monotonic timer / counter
+ stmts.push(quote!(
+ <#m as rtfm::Monotonic>::reset();
+ ));
+ } else {
+ // in the multi-core case we need a rendezvous (RV) barrier between *all* the cores that
+ // use the `schedule` API; otherwise one of the cores could observe the before-reset
+ // value of the monotonic counter
+ // (this may be easier to implement with `AtomicU8.fetch_sub` but that API is not
+ // available on ARMv6-M)
+
+ // this core will reset the monotonic counter
+ const FIRST: u8 = 0;
+
+ if core == FIRST {
+ for &i in analysis.timer_queues.keys() {
+ let rv = util::rendezvous_ident(i);
+
+ const_app.push(quote!(
+ #[rtfm::export::shared]
+ static #rv: rtfm::export::Barrier = rtfm::export::Barrier::new();
+ ));
+
+ // wait until all the other cores have reached the RV point
+ if i != FIRST {
+ stmts.push(quote!(
+ #rv.wait();
+ ));
+ }
+ }
+
+ let rv = util::rendezvous_ident(core);
+ stmts.push(quote!(
+ // the compiler fences are used to prevent `reset` from being re-ordering wrt to
+ // the atomic operations -- we don't know if `reset` contains load or store
+ // operations
+
+ core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst);
+
+ // reset the counter
+ <#m as rtfm::Monotonic>::reset();
+
+ core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst);
+
+ // now unblock all the other cores
+ #rv.release();
+ ));
+ } else {
+ let rv = util::rendezvous_ident(core);
+
+ // let the first core know that we have reached the RV point
+ stmts.push(quote!(
+ #rv.release();
+ ));
+
+ let rv = util::rendezvous_ident(FIRST);
+
+ // wait until the first core has reset the monotonic timer
+ stmts.push(quote!(
+ #rv.wait();
+ ));
+ }
+ }
+ }
+
+ // enable the interrupts -- this completes the `init`-ialization phase
+ stmts.push(quote!(rtfm::export::interrupt::enable();));
+
+ (const_app, stmts)
+}