aboutsummaryrefslogtreecommitdiff
path: root/macros/src/codegen/monotonic.rs
diff options
context:
space:
mode:
Diffstat (limited to 'macros/src/codegen/monotonic.rs')
-rw-r--r--macros/src/codegen/monotonic.rs251
1 files changed, 251 insertions, 0 deletions
diff --git a/macros/src/codegen/monotonic.rs b/macros/src/codegen/monotonic.rs
new file mode 100644
index 00000000..685502ed
--- /dev/null
+++ b/macros/src/codegen/monotonic.rs
@@ -0,0 +1,251 @@
+use proc_macro2::TokenStream as TokenStream2;
+use quote::quote;
+use rtic_syntax::ast::App;
+
+use crate::{analyze::Analysis, check::Extra, codegen::util};
+
+/// Generates monotonic module dispatchers
+pub fn codegen(app: &App, _analysis: &Analysis, _extra: &Extra) -> TokenStream2 {
+ let mut monotonic_parts: Vec<_> = Vec::new();
+
+ let tq_marker = util::timer_queue_marker_ident();
+
+ for (_, monotonic) in &app.monotonics {
+ // let instants = util::monotonic_instants_ident(name, &monotonic.ident);
+ let monotonic_name = monotonic.ident.to_string();
+
+ let tq = util::tq_ident(&monotonic_name);
+ let m = &monotonic.ident;
+ let m_ident = util::monotonic_ident(&monotonic_name);
+ let m_isr = &monotonic.args.binds;
+ let enum_ = util::interrupt_ident();
+ let name_str = &m.to_string();
+ let ident = util::monotonic_ident(name_str);
+ let doc = &format!(
+ "This module holds the static implementation for `{}::now()`",
+ name_str
+ );
+
+ let (enable_interrupt, pend) = if &*m_isr.to_string() == "SysTick" {
+ (
+ quote!(core::mem::transmute::<_, rtic::export::SYST>(()).enable_interrupt()),
+ quote!(rtic::export::SCB::set_pendst()),
+ )
+ } else {
+ let rt_err = util::rt_err_ident();
+ (
+ quote!(rtic::export::NVIC::unmask(#rt_err::#enum_::#m_isr)),
+ quote!(rtic::pend(#rt_err::#enum_::#m_isr)),
+ )
+ };
+
+ let default_monotonic = if monotonic.args.default {
+ quote!(
+ #[doc(inline)]
+ pub use #m::now;
+ #[doc(inline)]
+ pub use #m::delay;
+ #[doc(inline)]
+ pub use #m::timeout_at;
+ #[doc(inline)]
+ pub use #m::timeout_after;
+ )
+ } else {
+ quote!()
+ };
+
+ monotonic_parts.push(quote! {
+ #default_monotonic
+
+ #[doc = #doc]
+ #[allow(non_snake_case)]
+ pub mod #m {
+
+ /// Read the current time from this monotonic
+ pub fn now() -> <super::super::#m as rtic::Monotonic>::Instant {
+ rtic::export::interrupt::free(|_| {
+ use rtic::Monotonic as _;
+ if let Some(m) = unsafe{ &mut *super::super::#ident.get_mut() } {
+ m.now()
+ } else {
+ <super::super::#m as rtic::Monotonic>::zero()
+ }
+ })
+ }
+
+ fn enqueue_waker(
+ instant: <super::super::#m as rtic::Monotonic>::Instant,
+ waker: core::task::Waker
+ ) -> Result<u32, ()> {
+ unsafe {
+ rtic::export::interrupt::free(|_| {
+ let marker = super::super::#tq_marker.get().read();
+ super::super::#tq_marker.get_mut().write(marker.wrapping_add(1));
+
+ let nr = rtic::export::WakerNotReady {
+ waker,
+ instant,
+ marker,
+ };
+
+ let tq = &mut *super::super::#tq.get_mut();
+
+ tq.enqueue_waker(
+ nr,
+ || #enable_interrupt,
+ || #pend,
+ (&mut *super::super::#m_ident.get_mut()).as_mut()).map(|_| marker)
+ })
+ }
+ }
+
+ /// Delay
+ #[inline(always)]
+ #[allow(non_snake_case)]
+ pub fn delay(duration: <super::super::#m as rtic::Monotonic>::Duration)
+ -> DelayFuture {
+ let until = now() + duration;
+ DelayFuture { until, tq_marker: None }
+ }
+
+ /// Delay future.
+ #[allow(non_snake_case)]
+ #[allow(non_camel_case_types)]
+ pub struct DelayFuture {
+ until: <super::super::#m as rtic::Monotonic>::Instant,
+ tq_marker: Option<u32>,
+ }
+
+ impl core::future::Future for DelayFuture {
+ type Output = Result<(), ()>;
+
+ fn poll(
+ mut self: core::pin::Pin<&mut Self>,
+ cx: &mut core::task::Context<'_>
+ ) -> core::task::Poll<Self::Output> {
+ let mut s = self.as_mut();
+ let now = now();
+
+ if now >= s.until {
+ core::task::Poll::Ready(Ok(()))
+ } else {
+ if s.tq_marker.is_some() {
+ core::task::Poll::Pending
+ } else {
+ match enqueue_waker(s.until, cx.waker().clone()) {
+ Ok(marker) => {
+ s.tq_marker = Some(marker);
+ core::task::Poll::Pending
+ },
+ Err(()) => core::task::Poll::Ready(Err(())),
+ }
+ }
+ }
+ }
+ }
+
+ /// Timeout future.
+ #[allow(non_snake_case)]
+ #[allow(non_camel_case_types)]
+ pub struct TimeoutFuture<F: core::future::Future> {
+ future: F,
+ until: <super::super::#m as rtic::Monotonic>::Instant,
+ tq_marker: Option<u32>,
+ }
+
+ /// Timeout after
+ #[allow(non_snake_case)]
+ #[inline(always)]
+ pub fn timeout_after<F: core::future::Future>(
+ future: F,
+ duration: <super::super::#m as rtic::Monotonic>::Duration
+ ) -> TimeoutFuture<F> {
+ let until = now() + duration;
+ TimeoutFuture {
+ future,
+ until,
+ tq_marker: None,
+ }
+ }
+
+ /// Timeout at
+ #[allow(non_snake_case)]
+ #[inline(always)]
+ pub fn timeout_at<F: core::future::Future>(
+ future: F,
+ instant: <super::super::#m as rtic::Monotonic>::Instant
+ ) -> TimeoutFuture<F> {
+ TimeoutFuture {
+ future,
+ until: instant,
+ tq_marker: None,
+ }
+ }
+
+ impl<F> core::future::Future for TimeoutFuture<F>
+ where
+ F: core::future::Future,
+ {
+ type Output = Result<Result<F::Output, super::TimeoutError>, ()>;
+
+ fn poll(
+ self: core::pin::Pin<&mut Self>,
+ cx: &mut core::task::Context<'_>
+ ) -> core::task::Poll<Self::Output> {
+ let now = now();
+
+ // SAFETY: We don't move the underlying pinned value.
+ let mut s = unsafe { self.get_unchecked_mut() };
+ let future = unsafe { core::pin::Pin::new_unchecked(&mut s.future) };
+
+ match future.poll(cx) {
+ core::task::Poll::Ready(r) => {
+ if let Some(marker) = s.tq_marker {
+ rtic::export::interrupt::free(|_| unsafe {
+ let tq = &mut *super::super::#tq.get_mut();
+ tq.cancel_waker_marker(marker);
+ });
+ }
+
+ core::task::Poll::Ready(Ok(Ok(r)))
+ }
+ core::task::Poll::Pending => {
+ if now >= s.until {
+ // Timeout
+ core::task::Poll::Ready(Ok(Err(super::TimeoutError)))
+ } else if s.tq_marker.is_none() {
+ match enqueue_waker(s.until, cx.waker().clone()) {
+ Ok(marker) => {
+ s.tq_marker = Some(marker);
+ core::task::Poll::Pending
+ },
+ Err(()) => core::task::Poll::Ready(Err(())), // TQ full
+ }
+ } else {
+ core::task::Poll::Pending
+ }
+ }
+ }
+ }
+ }
+ }
+ });
+ }
+
+ if monotonic_parts.is_empty() {
+ quote!()
+ } else {
+ quote!(
+ pub use rtic::Monotonic as _;
+
+ /// Holds static methods for each monotonic.
+ pub mod monotonics {
+ /// A timeout error.
+ #[derive(Debug)]
+ pub struct TimeoutError;
+
+ #(#monotonic_parts)*
+ }
+ )
+ }
+}