-
Notifications
You must be signed in to change notification settings - Fork 195
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement embedded_hal_async::delay::DelayNs
for TIMGx
timers
#2084
base: main
Are you sure you want to change the base?
Conversation
The HIL failure for the S2 is a bit perplexing. I will have to find my programmer and try to run that locally. |
async fn test_systimer_async_delay_ns(ctx: Context) { | ||
let mut alarms = SystemTimer::new(ctx.peripherals.SYSTIMER); | ||
let unit = FrozenUnit::new(&mut alarms.unit0); | ||
let alarm0 = Alarm::new_async(alarms.comparator0, &unit).into_periodic(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
not related to this PR but isn't it a bit surprising that Delay
is implemented for Periodic
? - I would have expected it to be available for Target
f3b203a
to
1f8fdad
Compare
I've rebased this and made the necessary updates. Interestingly the ESP32 is failing in addition to the ESP32-S2, so maybe that will help me hunt down this issue. |
esp-hal/src/timer/timg.rs
Outdated
pub(crate) fn new(timer: &'a Timer<T, crate::Async>) -> Self { | ||
use crate::timer::Timer as _; | ||
|
||
timer.clear_interrupt(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is racey. You could end up clearing the interrupt bit that just fired for this delay causing this to hang forever.
This should use the semantics introduced in #1835
I'm moving soon and accidentally packed my programmer already 😅 So @JurajSadel has offered to wrap this up (thanks!) |
Do we ever want to support type-erased timers that provide async delays? If so, I'm not sure this direction is the way forward - I'd probably not invest time in trying to provide async mode at all for the lower level timers (both Systimer and Timg). At this point, our API supports creating OneShotTimer out of periodic systimers, and PeriodicTimer out of Target systimers (through ErasedTimer). Without this PR, Async mode in TimerGroup has absolutely no use and systimer is just a complex heap of... something. What's the design behind all this and where are we headed? |
That is something I was wondering about, too. Should we care about implementing foreign traits on the low-level timers at all? |
I'm leaning towards no, the low-level timers should be, where possible, only backends for higher level abstractions. Ideally we shouldn't provide multiple, drastically different ways to do the same thing, and where possible I'd love to group timer functionality behind PeriodicTimer/OneShotTimer. Doing so allows us to expose a single API regardless of the backend. A single API reduces the barrier of entry for new users as they don't have to consider details. And we have lots of details :) I'd like to see an ergonimics-first approach to our APIs, with optional complexity if the user needs it. We can provide the first one by prioritising AnyTimer and PeriodicTimer/OneShotTimer. The latter is more tricky with timers, but not impossible if our timers have functionality that can't be abstracted over. |
I think we agreed we don't want to have the traits implemented on low-level timers but on the abstractions - nevertheless the failing tests are suspicious I had a look and apparently on ESP32 and ESP32-S2 disabling INT_ENA doesn't de-assert the pending interrupt - i.e. after returning from the handler, it gets immediately called again You can verify this by changing the That's easy to fix by clearing the interrupt pending flag via INT_CLR The tests then pass for me locally Patchdiff --git a/esp-hal/src/timer/timg.rs b/esp-hal/src/timer/timg.rs
index 9d1f8157..77083926 100644
--- a/esp-hal/src/timer/timg.rs
+++ b/esp-hal/src/timer/timg.rs
@@ -1180,6 +1180,10 @@ mod asynch {
.int_ena()
.modify(|_, w| w.t(0).clear_bit());
+ unsafe { &*crate::peripherals::TIMG0::PTR }
+ .int_clr()
+ .write(|w| w.t(0).clear_bit_by_one());
+
WAKERS[0].wake();
}
@@ -1190,6 +1194,10 @@ mod asynch {
.int_ena()
.modify(|_, w| w.t(0).clear_bit());
+ unsafe { &*crate::peripherals::TIMG1::PTR }
+ .int_clr()
+ .write(|w| w.t(0).clear_bit_by_one());
+
WAKERS[1].wake();
}
@@ -1200,6 +1208,10 @@ mod asynch {
.int_ena()
.modify(|_, w| w.t(1).clear_bit());
+ unsafe { &*crate::peripherals::TIMG0::PTR }
+ .int_clr()
+ .write(|w| w.t(1).clear_bit_by_one());
+
WAKERS[2].wake();
}
@@ -1210,6 +1222,10 @@ mod asynch {
.int_ena()
.modify(|_, w| w.t(1).clear_bit());
+ unsafe { &*crate::peripherals::TIMG1::PTR }
+ .int_clr()
+ .write(|w| w.t(1).clear_bit_by_one());
+
WAKERS[3].wake();
}
} |
That's really strange. esp-hal/esp-hal/src/timer/timg.rs Lines 483 to 488 in 562c891
Clearing the interrupt bit without setting some kind of flag is a little dangerous, in terms of race conditions. |
When I looked into this issue on Tuesday, tests were timing out with this exact change for me 🤔 |
ab22e3f
to
816c47d
Compare
…ay with `SYSTIMER`
…s, move interrupt binds from Future constructor into new_async constructor
816c47d
to
de9c2ac
Compare
…delay_us and delay_ms
} | ||
} | ||
|
||
impl<'a, T> core::future::Future for TimerFuture<'a, T> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When this future is dropped (early) it should disable the interrupt.
.modify(|_, w| w.t(0).clear_bit()) | ||
}); | ||
|
||
unsafe { &*crate::peripherals::TIMG0::PTR } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would you mind adding a comment here explaining why this INT_CLR is necessary even though the INT_ENA has been cleared above.
type Output = (); | ||
|
||
fn poll(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<Self::Output> { | ||
let index = (self.timer.timer_number() << 1) | self.timer.timer_group(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mind making a (const) function for this math? Then it can be used in the interrupt handlers as well, just so we know the indexing is consistent/correct.
self.start(); | ||
self.listen(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably not super important for this PR but if the user cancels the future returned by this delay function, the call to start
and listen
should be undone. I'm guessing there's a stop
and unlisten
somewhere.
peripherals: Peripherals, | ||
} | ||
|
||
async fn test_async_delay_ns(mut timer: impl DelayNs, duration: u32) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mind adding a loop in here to ensure repeated calls to delay_ns
work as expected.
It happens quite often in drivers that the first one works but the next ones don't.
Both
SYSTIMER
alarms andTIMGx
timers now implement this trait, and I've added some simple tests to cover the implementations.I guess this closes #1864 because I am not sure there is a non-hacky and portable way to implement this trait for our
Delay
struct.