cpu/cc2538: RTT: implement missing API functions#13630
cpu/cc2538: RTT: implement missing API functions#13630benpicco wants to merge 3 commits intoRIOT-OS:masterfrom
Conversation
We can't set the hardware counter directly, so always add an offset.
We can't read back the alarm, so just store it in a variable.
Previously the setting the alarm would overwrite the overflow callback and vice versa. Since we can only set one alarm in hardware, always set the alarm to the closest event of the two.
|
When implementing the driver I had the same questions. I decided to implement a dump interface thinking that the smarted I made If more stuff is happening in the function call then this constraint might be extended. I ended up thinking that this would be something that a (I also based my self on another I still think this is a good question, but I don't have a strong opinion, maybe @kaspar030 @MichelRottleuthner have a stronger opinion on this? |
|
DL;DR, short term solution: A bit more details:
Yes, probably related to clock/bus synchronization between the HF and LF clock domains.
There a few things to keep in mind here:
I agree on the point that a timer extension should be in place to handle that. Long term solution: |
I think in this case, may I suggest to just "fake" a timer that actually cannot do this. E.g., if the minimum that should be set is 5, round to something in base 2 (8 or 2**3) and expose the 32khz 32bit timer as 32768>>3 32bit>>3 timer. We shouldn't complicate our lives around crappy hardware. I think it is totally acceptable to do add some missing functionality in software as done in this PR. |
Is this something you have on your roadmap? |
Absolutely :) As I said a while ago I want to approach this a bit differently, starting from a comprehensive overview of our targeted hardware to put our design decisions on solid grounds. At the moment this is not moving super fast but I'd say pretty thoroughly. Once all the information is polished up for wider discussion it is planned to contribute that to the timer related RDM. |
|
@benpicco I think we can go ahead with this one then, is it in a state to be reviewed? |
|
It can be reviewed, the question is if we want these features at the cost of the overhead they add to the common case. |
fjmolinas
left a comment
There was a problem hiding this comment.
It can be reviewed, the question is if we want these features at the cost of the overhead they add to the common case.
@benpicco I used #14146 to see if there was an impact on RTT_MIN_OFFSET and there was none. So I think the impact is negligible. Regarding the part about implementing functionality that is not in hardware. IMO the best is to have an upper layer that implement in software all the missing hardware functionalities for all platforms, but since there is not such thing yet, lets do it in this platform. So from the mentioned changes:
-
rtt_get_alarm: we should have this, its easy to implement as you did with a static variable. -
rtt_set_overflow_cb()overwrite handling: lets have this since the overflow doesn't seem important. -
rtt_set_counter: this one I'm on the fence. There should be at least an empty function declaration, not having it was an error on my side. Regarding the functionality itselfnrf52doesn't have the functionality and chose not to implement. Below is a patch that removes only this part from your PR. I'll let you as the contributor decide on this one.git diff cpu
diff --git a/cpu/cc2538/periph/rtt.c b/cpu/cc2538/periph/rtt.c index 58ca39cdbb..a0b7c05b33 100644 --- a/cpu/cc2538/periph/rtt.c +++ b/cpu/cc2538/periph/rtt.c @@ -36,7 +36,6 @@ static rtt_cb_t overflow_cb = NULL; static void *overflow_arg; static uint32_t rtt_alarm; -static uint32_t rtt_offset; static enum { RTT_ALARM, @@ -68,10 +67,11 @@ void rtt_poweroff(void) void rtt_init(void) { + rtt_alarm = 0; rtt_poweron(); } -static inline uint32_t _rtt_get_counter(void) +uint32_t rtt_get_counter(void) :...skipping... diff --git a/cpu/cc2538/periph/rtt.c b/cpu/cc2538/periph/rtt.c index 58ca39cdbb..a0b7c05b33 100644 --- a/cpu/cc2538/periph/rtt.c +++ b/cpu/cc2538/periph/rtt.c @@ -36,7 +36,6 @@ static rtt_cb_t overflow_cb = NULL; static void *overflow_arg; static uint32_t rtt_alarm; -static uint32_t rtt_offset; static enum { RTT_ALARM, @@ -68,10 +67,11 @@ void rtt_poweroff(void) void rtt_init(void) { + rtt_alarm = 0; rtt_poweron(); } -static inline uint32_t _rtt_get_counter(void) +uint32_t rtt_get_counter(void) { return ((SMWDTHROSC_ST0 & 0xFF) | ((SMWDTHROSC_ST1 & 0xFF) << 8) @@ -79,21 +79,9 @@ static inline uint32_t _rtt_get_counter(void) | ((SMWDTHROSC_ST3 & 0xFF) << 24)); } -uint32_t rtt_get_counter(void) -{ - return _rtt_get_counter() - rtt_offset; -} - void rtt_set_counter(uint32_t counter) { - rtt_alarm -= rtt_offset; - rtt_offset = _rtt_get_counter() + counter; - rtt_alarm += rtt_offset; - - /* re-set the overflow callback */ - if (overflow_cb) { - rtt_set_overflow_cb(overflow_cb, overflow_arg); - } + (void) counter; } static void _set_alarm(uint32_t alarm) @@ -110,19 +98,19 @@ void rtt_set_alarm(uint32_t alarm, rtt_cb_t cb, void *arg) assert(cb && !(alarm & ~RTT_MAX_VALUE)); unsigned irq = irq_disable(); - uint32_t now = _rtt_get_counter(); + uint32_t now = rtt_get_counter(); - rtt_alarm = alarm + rtt_offset; + rtt_alarm = alarm; /* set callback*/ alarm_cb = cb; alarm_arg = arg; - DEBUG("rtt_set_alarm(%lu), alarm in %lu ticks, overflow in %lu ticks\n", - alarm, rtt_alarm - now, rtt_offset - now); + DEBUG("rtt_set_alarm(%lu), now %lu ticks, overflow in %lu ticks\n", + alarm, now, RTT_MAX_VALUE - now); /* only set overflow alarm if it happens before the scheduled alarm */ - if (overflow_cb == NULL || (rtt_offset - now >= rtt_alarm - now)) { + if (overflow_cb == NULL || (rtt_alarm >= now)) { rtt_next_alarm = RTT_ALARM; _set_alarm(rtt_alarm); } @@ -132,7 +120,7 @@ void rtt_set_alarm(uint32_t alarm, rtt_cb_t cb, void *arg) uint32_t rtt_get_alarm(void) { - return rtt_alarm - rtt_offset; + return rtt_alarm; } void rtt_clear_alarm(void) @@ -147,16 +135,16 @@ void rtt_clear_alarm(void) void rtt_set_overflow_cb(rtt_cb_t cb, void *arg) { unsigned irq = irq_disable(); - uint32_t now = _rtt_get_counter(); + uint32_t now = rtt_get_counter(); /* set callback*/ overflow_cb = cb; overflow_arg = arg; /* only set overflow alarm if it happens before the scheduled alarm */ - if (alarm_cb == NULL || (rtt_alarm - now > rtt_offset - now)) { + if (alarm_cb == NULL || (rtt_alarm <= now)) { rtt_next_alarm = RTT_OVERFLOW; - _set_alarm(rtt_offset); + _set_alarm(RTT_MAX_VALUE); } irq_restore(irq); @@ -174,7 +162,7 @@ void rtt_clear_overflow_cb(void) void isr_sleepmode(void) { rtt_cb_t tmp; - bool both = (rtt_alarm == rtt_offset); + bool both = (rtt_alarm == RTT_MAX_VALUE); switch (rtt_next_alarm) { case RTT_ALARM: @@ -194,18 +182,18 @@ void isr_sleepmode(void) break; } - uint32_t now = _rtt_get_counter(); + uint32_t now = rtt_get_counter(); - if (alarm_cb && (rtt_offset - now >= rtt_alarm - now)) { + if (alarm_cb && (rtt_alarm >= now)) { DEBUG("rtt: next alarm in %lu ticks (RTT)\n", rtt_alarm - now); rtt_next_alarm = RTT_ALARM; _set_alarm(rtt_alarm); - } else if (overflow_cb && (rtt_alarm - now > rtt_offset - now)) { - DEBUG("rtt: next alarm in %lu ticks (OVERFLOW)\n", rtt_offset - now); + } else if (overflow_cb && (rtt_alarm < now)) { + DEBUG("rtt: next alarm in %lu ticks (OVERFLOW)\n", RTT_MAX_VALUE - now); rtt_next_alarm = RTT_OVERFLOW; - _set_alarm(rtt_offset); + _set_alarm(RTT_MAX_VALUE); } cortexm_isr_end();
|
|
||
| rtt_next_alarm = RTT_ALARM; | ||
| _set_alarm(rtt_alarm); | ||
| } else if (overflow_cb && (rtt_alarm - now > rtt_offset - now)) { |
There was a problem hiding this comment.
| } else if (overflow_cb && (rtt_alarm - now > rtt_offset - now)) { | |
| } else if (overflow_cb && (rtt_alarm > rtt_offset)) { |
|
|
||
| uint32_t now = _rtt_get_counter(); | ||
|
|
||
| if (alarm_cb && (rtt_offset - now >= rtt_alarm - now)) { |
There was a problem hiding this comment.
| if (alarm_cb && (rtt_offset - now >= rtt_alarm - now)) { | |
| if (alarm_cb && (rtt_offse >= rtt_alarm)) { |
| overflow_arg = arg; | ||
|
|
||
| /* only set overflow alarm if it happens before the scheduled alarm */ | ||
| if (alarm_cb == NULL || (rtt_alarm - now > rtt_offset - now)) { |
There was a problem hiding this comment.
| if (alarm_cb == NULL || (rtt_alarm - now > rtt_offset - now)) { | |
| if (alarm_cb == NULL || (rtt_alarm > rtt_offset )) { |
| alarm, rtt_alarm - now, rtt_offset - now); | ||
|
|
||
| /* only set overflow alarm if it happens before the scheduled alarm */ | ||
| if (overflow_cb == NULL || (rtt_offset - now >= rtt_alarm - now)) { |
There was a problem hiding this comment.
| if (overflow_cb == NULL || (rtt_offset - now >= rtt_alarm - now)) { | |
| if (overflow_cb == NULL || (rtt_offset >= rtt_alarm )) { |
There was a problem hiding this comment.
I wonder if this was to handle overflows, but can't remember.
|
Taken over (hopefully in agreement with @benpicco, the conversation at hand does not provide that info) by @fjmolinas in #14700. |
Sorry yes, @benpicco did not have time/interest to maintain the PR, he agreed with me doing so. |
Contribution description
The RTT implementation of
cpu/cc2538is based on the sleep timer, a 32 bit 32kHz timer that can only be read and that only supports one alarm. (No overflow interrupt)This implements functions that are defined by the RIOT API, but are not directly supported by the hardware:
rtt_set_counter(): as we can't set the counter directly, add an offset to the raw counter value insteadrtt_get_alarm(): we can't read back the alarm time, so store it in a local variable. This is also needed forrtt_set_overflow_cb(): In the previous implementation the overflow callback would always overwrite the alarm callback and vice versa.To fix this, check which event will occur first and set that alarm as well as a flag to tell which alarm it was.
When the alarm rings, set the alarm to the other event if there is one.
Testing procedure
There are no tests for those functions.
Issues/PRs references
I implemented those for #13519, but then discovered I don't need them.
So I'm not sure if this should be added just for completeness' sake when we could just as well do without.