Skip to content
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

Merged
merged 18 commits into from
Sep 24, 2024

Conversation

jessebraham
Copy link
Member

@jessebraham jessebraham commented Sep 4, 2024

Both SYSTIMER alarms and TIMGx 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.

@jessebraham
Copy link
Member Author

The HIL failure for the S2 is a bit perplexing. I will have to find my programmer and try to run that locally.

@jessebraham
Copy link
Member Author

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 Show resolved Hide resolved
esp-hal/src/timer/timg.rs Outdated Show resolved Hide resolved
esp-hal/src/timer/timg.rs Outdated Show resolved Hide resolved
@JurajSadel JurajSadel self-assigned this Sep 9, 2024
@jessebraham
Copy link
Member Author

I'm moving soon and accidentally packed my programmer already 😅 So @JurajSadel has offered to wrap this up (thanks!)

@bugadani
Copy link
Contributor

bugadani commented Sep 10, 2024

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?

@bjoernQ
Copy link
Contributor

bjoernQ commented Sep 11, 2024

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).

That is something I was wondering about, too. Should we care about implementing foreign traits on the low-level timers at all?
(My gut feeling is no but maybe there are good reasons to do so)

@bugadani
Copy link
Contributor

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.

@bjoernQ
Copy link
Contributor

bjoernQ commented Sep 12, 2024

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 timer_interrupt example accordingly

That's easy to fix by clearing the interrupt pending flag via INT_CLR

The tests then pass for me locally

Patch
diff --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();
     }
 }

@Dominaezzz
Copy link
Collaborator

Dominaezzz commented Sep 12, 2024

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

That's really strange.
Perhaps it's due to

// always use level interrupt
#[cfg(any(esp32, esp32s2))]
self.register_block()
.t(self.timer_number().into())
.config()
.modify(|_, w| w.level_int_en().set_bit());
?

Clearing the interrupt bit without setting some kind of flag is a little dangerous, in terms of race conditions.

@JurajSadel
Copy link
Contributor

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 timer_interrupt example accordingly

That's easy to fix by clearing the interrupt pending flag via INT_CLR

The tests then pass for me locally

Patch

diff --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();
     }
 }

When I looked into this issue on Tuesday, tests were timing out with this exact change for me 🤔

esp-hal/src/timer/timg.rs Outdated Show resolved Hide resolved
esp-hal/src/timer/timg.rs Outdated Show resolved Hide resolved
esp-hal/src/timer/timg.rs Outdated Show resolved Hide resolved
esp-hal/src/timer/timg.rs Outdated Show resolved Hide resolved
esp-hal/src/timer/timg.rs Outdated Show resolved Hide resolved
hil-test/tests/delay_async.rs Outdated Show resolved Hide resolved
hil-test/tests/delay_async.rs Outdated Show resolved Hide resolved
esp-hal/src/timer/timg.rs Show resolved Hide resolved
esp-hal/src/timer/timg.rs Show resolved Hide resolved
esp-hal/src/timer/timg.rs Show resolved Hide resolved
esp-hal/src/timer/timg.rs Show resolved Hide resolved
hil-test/tests/delay_async.rs Show resolved Hide resolved
esp-hal/src/timer/systimer.rs Outdated Show resolved Hide resolved
esp-hal/src/timer/systimer.rs Outdated Show resolved Hide resolved
esp-hal/src/timer/systimer.rs Outdated Show resolved Hide resolved
Copy link
Contributor

@bugadani bugadani left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! I have some nits to pick but I can also just clean them up myself :)

esp-hal/src/timer/timg.rs Outdated Show resolved Hide resolved
esp-hal/src/timer/timg.rs Outdated Show resolved Hide resolved
Copy link
Contributor

@bjoernQ bjoernQ left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@JurajSadel JurajSadel added this pull request to the merge queue Sep 24, 2024
@bjoernQ bjoernQ removed this pull request from the merge queue due to a manual request Sep 24, 2024
@JurajSadel JurajSadel added this pull request to the merge queue Sep 24, 2024
Merged via the queue into esp-rs:main with commit 4534ee1 Sep 24, 2024
28 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

The embedded_hal_async::delay::DelayNs trait is not implemented for delay::Delay or timer::timg::Timer
6 participants