Description
Answers checklist.
- I have read the documentation ESP-IDF Programming Guide and the issue is not addressed there.
- I have updated my IDF branch (master or release) to the latest version and checked that the issue is present there.
- I have searched the issue tracker for a similar issue and not found a similar issue.
IDF version.
5.5
Espressif SoC revision.
ESP32C
Operating System used.
Linux
How did you build your project?
Command line with idf.py
If you are using Windows, please specify command line type.
None
Development Kit.
All of them
Power Supply used.
External 3.3V
What is the expected behavior?
Smaller code on the LP core, less memory usage, no silent breakage of the user code, clearer intend of the API.
What is the actual behavior?
Followup of #15838 remark about observed behavior of the ESP32C6 LP core.
This applies to all C and P class of ESP32 (all that have the LP core with interrupt handler).
What's wrong ?
Currently, this class of ESP32 have the possibility to program multiple wake up source for the LP core (unlike ESP32/ESP32 S). This is very useful for numerous usage case:
- Monitoring the battery level periodically while the HP core is sleeping, and monitoring the power button (to wake up the HP core)
- Monitoring some IO while having a safety timeout (for example, when running an actuator and expecting the end stop detector to trigger)
- Monitor energy harvester status periodically via I2C while also counting pulses on a sensor
- ...
The current API however prevents this usage (or requires hack to work as expected)
In the following example, I've set up 2 wake up source (LP Timer and one LP IO pin). When woken up, the LP core is busy looping (burning power) for 2s then program the next wake up in 3s and halt. If the IO pin triggers, the wait time (in deep sleep) is shorter.
There are numerous issue in the current implementation:
- When querying the wake up source, the LP core never clear the previous wake up source, so it's returning either LP TIMER or LP TIMER + IO. This issue was partially fixed in ESP32-C6 ULP wakeup cause (IDFGH-15115) #15794 but it introduced a regression (see below)
- If I trigger the IO while the LP core is running, it'll wake up instantly when it's halted, resulting in double runtime (the burn time is twice as long as seen above)
- The wake up source querying API is dumb.
Issue 1
IMHO, the main issue with the current API is that it tries to abstract a lot more than it should do.
It's default behavior is to collect/query all possible wake up source and clear the interrupt for the wake source and return a bitmap of all it found.
This leads to a get/reset issue, if you collect the wakeup source twice, the second time will returns none since they were cleared in a previous run.
The current master which included the fix in #15794, moved the collect&clear function call to the startup code of the LP core, which is breaking current user code in multiple way since:
- If the user called the collect&clear function (as previously documented), it'll get back no wake up source since it was already cleared before. Thus this breaks the existing code silently.
- This slows the LP core boot time (in a low power system, it's a no-no)
- Most user don't care about the wake up source, so it's a code that's useless for them
- If the wake up reason is still present while the LP core is running, the next call to halt will immediately wake the LP core since it wasn't cleared
Issue 2
Wake up source are just latching interrupts on these system. Having an abstraction over those to mean something else is troublesome. Clearing the wakeup source is just clear the interrupt status in the interrupt handler.
If stated like this in the documentation, it would be clear that lp_core_enable_intr
must be done after the wakeup_cause is updated, else it'll immediately trigger. It would be clear that WFI (wait for interrupt) function call will pass if the wake up cause isn't updated and so on. In fact, all interrupt related code should state "wake up cause is stored in the interrupt handler, so it must be cleared before being used if the wake up source is using the same interrupt"
In order to correct the "interrupt happens while the LP core is running = immediate wake up on sleep", the LP core must clear the interrupt before entering sleep, which it doesn't do.
The current master and API is large and wasteful. Since the user is usually interested in one or, be ambitious, two wake up sources, why pay for the code to check all wake up sources?
This is a waste because:
- A variable is required to remember the wake sources (
lp_wakeup_source
) in the size-limited RTC RAM - This check function is large and consume both Flash space and RTC RAM too
- Being stateful, it suffers from dual call.
Proposed solution
Remove ulp_lp_core_update_wakeup_cause()
Using this function requires code like this (pseudo code):
ulp_lp_core_update_wakeup_cause();
uint32_t cause = ulp_lp_core_get_wakeup_cause();
if (cause & LP_WAKEUP_CAUSE_LPTIMER)
{
// Do something
}
if (cause & LP_WAKEUP_CAUSE_IO)
{
// Do something else
}
This is a O(N) search in the bitmap called twice, once for filling the bitmap and a second time to check it.
Since the user need to explicitely declare what wake source to use when entering deep sleep on the HP core, there's no need to have this useless code for checking and clearing unused wake sources.
This function should be split in numerous function, each checking a single wake up source, so it only consume a fraction of the code size. It should be like this (pseudo code):
if (ulp_lp_core_check_wakeup_timer())
{
// Do something
}
if (ulp_lp_core_check_wakeup_io())
{
// Do something
}
Explicitly clear the interrupt if another wake up is required
Since the user knows what the application should do, it's better if the clearing of the wake up clause is done explicitly and not automatically. This can be done like this (pseudo code):
ulp_lp_core_lp_timer_clear_int();
// or
ulp_lp_core_clear_wakeup_timer_flag();
This must be done before any code using interrupt (and should be documented) or before halting. In general, it's better if the behavior is clearly documented than hidden behind a HAL (like wakeup source are).
That way, the code becomes clearer and doesn't require any hidden cleaning behind your back.
Deprecate previous method
Since wakeup cause is specific to this ESP C and P series, changing the API shouldn't impact user code with breaking changes (unlike master). I propose to deprecate ulp_lp_core_X_wakeup_cause
functions (so that previous code would not compile anymore) and orient the user toward the new API (with independent functions for each source). This also simplifies the lp_core_startup
code.
Steps to reproduce.
See issue #15732 for example code. I can provide a stronger and more complex code showing both issue if requested
Debug Logs.
Diagnostic report archive.
No response
More Information.
No response