-
Notifications
You must be signed in to change notification settings - Fork 2k
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
cpu/esp8266: add RTT implementation #13640
Conversation
0688c85
to
3c11801
Compare
{ | ||
/* save counters before going to sleep or reboot */ | ||
_rtt_counter_saved = frc2.count; | ||
_rtc_counter_saved = RTC.COUNTER; |
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.
Where does RTC.COUNTER
come from?
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.
In contrast to all other peripherals for which the SDK defines the registers as a struct, the registers of the RTC module aren't defined by the SDK at all, see rtc_register.h.
Therefore, another piece of vendor code is used which was initially extracted from the esp-open-rtos. It tries to define the RTC registers as a struct. BTW, this struct is defined exactly in the way you mentioned with volatile members and without linker stuff.
ESP8266 is a terrible platform. It has the worst documentation I have ever seen and there is no consistent development environment.
|
||
void rtt_set_alarm(uint32_t alarm, rtt_cb_t cb, void *arg) | ||
{ | ||
rtt_config.alarm = alarm; |
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.
alarm
can happen after the wrap around, so the overflow callback should be triggered before.
e.g. frc2.count = 1000
, rtt_set_alarm(50, …)
is called.
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.
Indeed. It requires also to change the interrupt handling in _rtt_cb
.
BTW, configured alarms will not survive deep sleep and reboots. Otherwise the whole rtt_config_t
structure would have to be placed in RTC RAM which has only 256 bytes. But since the system is restarted after a reboot or deep sleep, preserving configured interrupts makes no sense.
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.
OK, seems to work now:
> rtt setalarm 1000
> rtt set 4294967290
The alarm rang
> rtt setoverflow
> rtt setalarm 1000
> rtt set 4294967290
RTT overflow
The alarm rang
> rtt setalarm 4294967295
> rtt set 4294967290
The alarm rang
RTT overflow
> rtt set 4294967290
RTT overflow
> rtt setalarm 2000000000
> rtt set 4294967290
RTT overflow
> rtt set 1999999990
The alarm rang
Because of the interrupt latency, the only case that cannot be handled with this emulated overflow interrupt is when the alarm is set to 0.
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.
Because of the interrupt latency, the only case that cannot be handled with this emulated overflow interrupt is when the alarm is set to 0.
But if you can set an overflow 'alarm' you can also set an alarm at 0, they should happen at the same time.
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.
@benpicco Thanks for your suggestions. I had simpler solution already
> rtt setoverflow
> rtt setalarm 0
> rtt set 4294967290
The alarm rang
RTT overflow
> rtt set 4294967290
RTT overflow
> rtt setalarm 1
> rtt set 4294967290
RTT overflow
The alarm rang
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.
My problem was that I tried to test the current counter value, which of course can be larger than the configured alarm value due to the interrupt latency.
So the solution was not to test the actual counter value but the configured alarm value. Since _rtt_cb
is always called for the actually set alarm value, the value in the frc2.alarm
register is always exactly the relevant alarm.
|
||
void rtt_set_alarm(uint32_t alarm, rtt_cb_t cb, void *arg) | ||
{ | ||
rtt_config.alarm = alarm; |
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.
Because of the interrupt latency, the only case that cannot be handled with this emulated overflow interrupt is when the alarm is set to 0.
But if you can set an overflow 'alarm' you can also set an alarm at 0, they should happen at the same time.
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.
Code looks good and works like a charm!
Please squash.
dcd7d18
to
474af47
Compare
Murdock isn't happy yet. |
This should make Murdock happy, please squash directly. |
4c2ccbc
to
464e3a8
Compare
I had to remove and set |
@benpicco Thanks for reviewing, testing and merging. |
@benpicco I need some advice regarding this RTT implementation. You have a lot of experience with RTT implementations. The background: Finally I was able to figure out the reason for the instability of the upgrade for ESP8266 RTOS SDK version 3.3. This version of the SDK now uses the FRC2 as WiFi timer for WiFi power management time and other PHY management functions. This is new with this version of the SDK: concerned: This means that the RTT implementation, which was also based on this FRC2 in active CPU mode, will no longer work. Simply using the existing RTC counter is still not possible because it is still not clear how to enable the alarm interrupt. It is completely undocumented. Furthermore, it seems that is also used by the SoC phy SDK library, at least during startup. A possible solution would be to use the WiFi timer, which provides an accurate millisecond timer API, to emulate the RTT in active CPU mode, and use the RTC counter in sleep mode as before. The question Which
Option 3 would be the easiest one. For RTT based RTC implementation that would be fine. But is that still a counter as usually required. Usual RTTs are driven by 32.768 kHz crystals. But, I have seen a number of boards and CPUs that also use a 1 Hz counter. But having a 1 Hz RTT and an RTC based on this RTT is better than nothing. What do you think? |
Hm but if you can't use the alarm interrupt of the RTC counter, how does it help you in sleep mode? |
It is of course possible to activate an interrupt for this. But it is not known how. The application simply calls either I spent several days disassembling all the code and figuring out how to activate and use the interrupt without any progress. Also in other projects several cracks have tried to solve the puzzle of the RCT in ESP8266. The resutl is something like that: struct RTC_REGS {
uint32_t volatile CTRL0; // 0x00
uint32_t volatile COUNTER_ALARM; // 0x04
uint32_t volatile RESET_REASON0; // 0x08 //FIXME: need better name
uint32_t volatile _unknownc; // 0x0c
uint32_t volatile _unknown10; // 0x10
uint32_t volatile RESET_REASON1; // 0x14 //FIXME: need better name
uint32_t volatile RESET_REASON2; // 0x18 //FIXME: need better name
uint32_t volatile COUNTER; // 0x1c
uint32_t volatile INT_SET; // 0x20
uint32_t volatile INT_CLEAR; // 0x24
uint32_t volatile INT_ENABLE; // 0x28
uint32_t volatile _unknown2c; // 0x2c
uint32_t volatile SCRATCH[4]; // 0x30 - 3c
uint32_t volatile _unknown40; // 0x40
uint32_t volatile _unknown44; // 0x44
uint32_t volatile _unknown48; // 0x48
uint32_t volatile _unknown4c[7]; // 0x4c - 0x64
uint32_t volatile GPIO_OUT; // 0x68
uint32_t volatile _unknown6c[2]; // 0x6c - 0x70
uint32_t volatile GPIO_ENABLE; // 0x74
uint32_t volatile _unknown80[5]; // 0x78 - 0x88
uint32_t volatile GPIO_IN; // 0x8c
uint32_t volatile GPIO_CONF; // 0x90
uint32_t volatile GPIO_CFG[6]; // 0x94 - 0xa8
};
It is working in that way as long as we rely on SDK functions. The problem that we can't control the hardware directly because we have no information how 😟 |
Hm that's gnarly. So I would say use the frequency of the 160 kHz RTC counter, so you can align sleep times with that. At least that's how I understood it, not sure if this is possible with the hardware / available documentation 😕 |
This is exactly how it is implemented in the ESP32 RTC if it hasn't connected an external 32.768kHz crystal. |
Contribution description
This PR adds an RTT implementation for the ESP8266.
The ESP8266 RTC module integrates a RTT. Unfortunately it is completely undocumented. It is not known what interrupts it has, nor how they are configured, nor how they are used by the binary SDK libraries. The only available information about this RTT is that it is driven by a 160 kHz RC oscillator and how the counter value can be read.
Therefore, this RTT implementation uses a combination of the hardware RTT and a CPU counter. While the CPU counter is used to count and handle interrupts when the CPU is active, the hardware RTT is used to maintain the counter during reboot or deep sleep. For this purpose, the values of both the CPU counter and the RTT counter are stored in RTC RAM when the CPU enters the reboot or deep sleep. After the restart, the CPU counter is reinitialized with the values of the RTC counter updated by RTT counter progress.
The CPU counter is clocked by a PLL-driven 80 MHz clock divided by 256. This results into a RTT frequency of 312 kHz. That is, the maximum time that can be used with this RTT are about 3 hours, 49 mins and 3 seconds.
Testing procedure
Use
tests/periph_rtt
to test the counterTo test the preservation of the counter during the restart (requires PR #13638):
Issues/PRs references
RTT based RTC implementation in PR #13519 can then be used to realize a RTC.