cpu/atmega256rfr2: symbol counter based RTT support#12852
cpu/atmega256rfr2: symbol counter based RTT support#12852benpicco merged 1 commit intoRIOT-OS:masterfrom
Conversation
b27d9ca to
240de2b
Compare
|
Since this shares a lot of code with I recon the advantage here is better sleep performance (no unnecessary wake-ups for overflow interrupts). TBH I don't even know how well the current implementation handles Deep Sleep. |
|
Nice,
|
@chudov do you get tests/gnrc_gomach running with the rtt based on TIMER2? |
|
The crash also happens with TIMER2 #12857 |
|
OK. IRQ race with radio? One generic problem we have with timer callbacks is that they are running in IRQ context. In Linux we have a softirq. |
240de2b to
98b8b98
Compare
Reworked as proposed.
Whould you recommend branch with well working power management for AVR? Then I could try to test. |
That work had been started in #8207, but it hasn't been updated in a while. |
There was a problem hiding this comment.
This sure looks doable!
But rtt_set_alarm() and rtt_set_counter() are drowning in a soup of #ifdefs and rtt_init() is just on the edge. Either factor out the differing parts into helper functions (with just one #ifdef) or simply have two versions of those functions.
The 'common code' in those cases is trivial anyway.
98b8b98 to
652737f
Compare
|
I hope it is better now. |
|
If the code gets cleaner by doing so, you can always drop some |
c2d2800 to
0a11872
Compare
|
Hello, Did you test the last incarnation? ``Stack Pointer: 0x81f3 *** halted. ` |
|
Huh, `
|
maribu
left a comment
There was a problem hiding this comment.
Nice to see an RTT implementation for the RFA1 and RFR2 family :-) See inline comments for some preliminary feedback
cpu/atmega_common/periph/rtt.c
Outdated
| * For MCUs with MAC symbol counter (atmega*rfr2): | ||
| * The MAC symbol counter is automatically switch to TOSC1 32.768kHz clock | ||
| * when transceiver or CPU is going to sleep. The MAC symbol counter is | ||
| * sourced by 62.500 kHz derived from 32.768kHz TOSC1 or 16 MHz system clock | ||
| * For current implementation symbol counter is always use 32.768kHz TOSC1 clock | ||
| * For alarms the SCOCR2 register is used. |
There was a problem hiding this comment.
How about some rephrasing of this, e.g.:
* For MCUs with a MAC symbol counter (ATmegaXXXRFA1 and ATmegaXXXRFR2):
* The MAC symbol counter can be configured to be sourced by the 32.768kHz
* RTC (TOSC1), or from a 62.500 kHz clock generated by dividing the 16 MHz
* system clock. When either the CPU or the transceivers is going to sleep,
* the MAC symbol counter is sourced by the RTC for both options. In order to
* not have to compensate for a changing clock frequency, this RTT
* implementation uses the 32.768kHz RTC as source even when both CPU and
* transceiver are active. The 32 bit comparator in SCOCR2 is used for alarms.
There was a problem hiding this comment.
I wil take the proposed text, except place where it is little bit incorrect: according to the datasheet "The symbol counter is a 32 bit counter which can be sourced by a 62.5 kHz clock, derived from the 16 MHz system clock or from the RTC (32.768 kHz)."
There was a problem hiding this comment.
You're right. I got confused while reading the data sheet. Thanks for pointing this out!
There was a problem hiding this comment.
Hmm, on another page the data sheet says "The counter time base can be derived from the 16 MHz crystal or the RTC (32.768 kHz crystal on TOSC) during operation."
Maybe the "62.5 kHz clock, derived from the 16 MHz system clock" does not mean it can be either a 62.5 kHz clock or the be derived from the system clock. Maybe it means that the 62.5 kHz clock is derived from the system clock by dividing the 16 MHz by 256?
There was a problem hiding this comment.
Actually in case of deriving from 16MHz clock it is possible to use another divider. See 10.11.35 SCCR1 section where all possible values are mentioned. This is nice flexibility but it is unclear for me how to use it in real 802.15.4 applications.
Even worse, the counter can be clocked from PG2 pin.
I'm not sure it make sense to paste datasheet text to sources...
There was a problem hiding this comment.
I'm not sure it make sense to paste datasheet text to sources...
No, only the takeaway needed for understanding the code and the design decisions are needed. To me, here it is: There are multiple options for the clock source. But only the 32.768kHz RTT clock source is available when either the CPU or the transceiver is not active. So therefore, the RTC is chosen as clock source here.
There was a problem hiding this comment.
Greate feature of the symbol counter that in case of deep sleep it is automatically switched from XTAL to TOSC1 and back. But that leads to some delays for oscillator stabilization and some counter resync that are not yet clear for me. This is why 32K is always used :)
How to reflect that in comments?
There was a problem hiding this comment.
The comment you just made explains it quite good ;-)
The counter resync would also increase ROM and wake up time. It would increase the complexity of the code. To me, it is just not worth the effort.
Let me try to extend your comment above:
The ATmegaXXXRFA1/RFR2 provide multiple clock sources. But only the 32.768kHz RTT (TOSC1) is available when CPU or transceiver or not active. Thus, it is used automatically as a fallback during sleep modes regardless of configured clock source. To avoid the calculations needed to compensate for a change of the clock frequency during sleep, this implementation just uses TOSC1(32.768 kHz) at any point in time.
cpu/atmega_common/periph/rtt.c
Outdated
| __attribute__((always_inline)) | ||
| static inline uint32_t rg_read32(volatile uint8_t *hh, | ||
| volatile uint8_t *hl, | ||
| volatile uint8_t *lh, | ||
| volatile uint8_t *ll) | ||
| { | ||
| le_uint32_t rg; | ||
|
|
||
| rg.u8[0] = *ll; | ||
| rg.u8[1] = *lh; | ||
| rg.u8[2] = *hl; | ||
| rg.u8[3] = *hh; | ||
|
|
||
| return rg.u32; | ||
| } |
There was a problem hiding this comment.
The __attribute__((always_inline)) should not be needed, unless someone compiles with -O0. But anyone doing so will expect additional overhead in CPU cycles and ROM size anyway...
This could be more simplified, as the layout of those 32 bit registers are always consecutive with the least significant byte having the lowest address.
The current implementation contains a data race: According to the data sheet a single 32 bit temporary register is used to capture the current contents of all available 32 bit registers. And a read of the least significant byte of any 32 bit registers cause the temporary register to be updated. Let's say thread A starts to read a 32 bit registers and has read two bytes, then an IRQ is triggered. If the ISR is now reading a 32 bit registers, it will cause the temporary register to be updated. After the ISR completes, thread A will continue to read the remaining two bytes, but those will return the updated contents of the temporary register.
Also, a short summary of the reason the accesses are atomic as a comment would make the life of people reading the code easier. E.g:
/*
* Read a 32 bit register as described in section 10.3 of the datasheet: A read
* of the least significant byte causes the current value to be atomically
* captured in atemporary 32 bit registers. The remaining reads will access this
* register instead. Only a single 32 bit temporary register is used to provide
* means to atomically access them. Thus, interrupts must be disabled during the
* read sequence in order to prevent other threads (or ISRs) from updating the
* temporary 32 bit register before the reading sequence has completed.
*/
static inline uint32_t reg32_read(volatile uint8_t *reg_ll)
{
le_uint32_t reg;
unsigned state = irq_disable();
reg.u8[0] = reg_ll[0];
reg.u8[1] = reg_ll[1];
reg.u8[2] = reg_ll[2];
reg.u8[3] = reg_ll[3];
irq_restore(state);
return reg.u32;
}There was a problem hiding this comment.
The implementation only calls rg_read32() when interrupts are already disabled though, no need to disable them again.
There was a problem hiding this comment.
Wait, in rtt_get_alarm() it does so. But that is the only exception; so adding irq_disable() and irq_restore() there and dropping it here would indeed be better.
There was a problem hiding this comment.
It's a pity that we cannot tell the compiler the semantics of irq_disable() and irq_restore() to allow it to just optimize out all inner pairs of irq_disable() and irq_restore() when inlining a function.
There was a problem hiding this comment.
But the alarm value is not being modified by the MCU.
Only reading SCCNT is racy because this is continuously changing. But this happens inside _safe_cnt_get() where interrupts are disabled.
There was a problem hiding this comment.
Seems these functions should be extracted to another perepherial module named "sc" as they could be used by optimized 802.15.4 network drivers to read other 32-bit registers related to the MAC symbol counter. And in this case it make sense to protect them from interrupts.
There was a problem hiding this comment.
But the alarm value is not being modified by the MCU.
Doesn't matter. It is read throw the 32 bit temporary register. And if while reading the temporary registers another thread or ISR is executed, the contents of the temporary register might be modified.
0a11872 to
579c2c3
Compare
There was a problem hiding this comment.
Code looks good from my side, just one more thing:
I wanted to test if the Timer2 based code also still works, but all my ATmegas with 32kHz quarts are RF ones.
Turns out disabling RTT_BACKEND_SC is not as simple as it could be, but that's an easy fix.
Move one define and then run a search & replace #ifdef … -> #if ….
cpu/atmega_common/periph/rtt.c
Outdated
| #include <avr/interrupt.h> | ||
|
|
||
| #ifdef SCCR0 | ||
| #define RTT_BACKEND_SC (1) |
| * @name RTT configuration | ||
| * @{ | ||
| */ | ||
| #ifdef SCCR0 |
There was a problem hiding this comment.
| #ifdef SCCR0 | |
| #if defined(SCCR0) && !defined(RTT_BACKEND_SC) | |
| #define RTT_BACKEND_SC (1) | |
| #endif | |
| #if RTT_BACKEND_SC |
and move it here.
Also s/#ifdef RTT_BACKEND_SC/#if RTT_BACKEND_SC/g, s/#ifdnef RTT_BACKEND_SC/#if RTT_BACKEND_SC == 0/g so CFLAGS+=-DRTT_BACKEND_SC=0 works.
There was a problem hiding this comment.
Please check if it meets expectations
There was a problem hiding this comment.
Both backends work fine, please squash!
TIMER2 can have some merits and compliment the symbol counter even on RF ones. No radio is involved and can source OS with both events and wake-up with almost nil in power. No progress with gomach... What is lwmac? |
1c14cb0 to
efa9bb8
Compare
benpicco
left a comment
There was a problem hiding this comment.
Code looks good and both code paths still work on avr-rss2.
Does using the symbol counter use more power?
Not sure, does it also crash? |
Yes but you can set a prescaler to slow things down. OS:es still needs some events otherwise things gets weird.
OK. Fine. |
At least test is not compilable (#12869) |
|
Complementing the symbol counter (SC) vs TIMER2 discussion. MCU can also be sourced via the internal oscillator (not 16MHz xtal) for lower power. I don't think the SC is working in this settning... Should be verified. |
|
Happy. Elegant. Constructive collaboration! |
|
As a follow up work it could interesting to see how the deep MCU sleep can be implemented in RIOT thread architecture. In Contiki the sleep code is rtimer (xtimer in RIOT) which fallbacks to TIMER2. |
|
🎉 @chudov: Thanks for you work! |
|
Yes you mentioned. Not crystal clear howto to get rtt running. Seems like xtimer is the focus. Need to read... |
Contribution description
This adds RTT support to atmega256rfr2 based on MAC symbol counter. The MAC symbol conter is 32-bit wide working on 62.500 kHz derived from XTAL1 16MHz or TOSC1 32.786 kHz oscillator. Symbol counter automatically switches from XTAL1 oscillator to TOSC1 oscillator when CPU is going to sleep and fallback when CPU wakes up.
Current implementation uses 32.786 kHz oscillator for both modes. The SCOCR2 compare register is used for alarm generation.
The RTT requires a 32.768 kHz oscillator to be connected to TOSC1, TOSC2 of MCU. All supported atmega256rfr2-based boards has it.
###Testing procedure
Tests were executed on two ConBee I USB dongles that are based on deRFmega256-23M12 modules.
tests/periph_rttsucessfully executed.Also
tests/gnrc_gomachwas adopted for testing. Packets were sent out succesfully (checked with 802.15.4 sniffer), but RIOT on receiver module crashed with kernel panic.###Issues/PRs references
inspired by and based on #12815