Skip to content

cpu/cc2538: RTT: implement missing API functions#13630

Closed
benpicco wants to merge 3 commits intoRIOT-OS:masterfrom
benpicco:cc2538_enhance_rtt
Closed

cpu/cc2538: RTT: implement missing API functions#13630
benpicco wants to merge 3 commits intoRIOT-OS:masterfrom
benpicco:cc2538_enhance_rtt

Conversation

@benpicco
Copy link
Contributor

@benpicco benpicco commented Mar 13, 2020

Contribution description

The RTT implementation of cpu/cc2538 is 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 instead
  • rtt_get_alarm(): we can't read back the alarm time, so store it in a local variable. This is also needed for
  • rtt_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.

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.
@benpicco benpicco added Type: new feature The issue requests / The PR implemements a new feature for RIOT Area: cpu Area: CPU/MCU ports State: demonstrator State: This PR (loosely) demonstrates a concept and is not necessarily meant to be merged. labels Mar 13, 2020
@benpicco benpicco requested a review from MrKevinWeiss as a code owner March 13, 2020 11:06
@fjmolinas
Copy link
Contributor

When implementing the driver I had the same questions. I decided to implement a dump interface thinking that the smarted I made rtt.c the more overhead it would have. The documentation already says that timeout shouldn't be set closer that 5cycles from the target time. I though this probably had to do with time taken to read the rtt current value and set an alarm.

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 timer extension would handle, since setting an overflow alarm and alarm is just the same as setting multiple alarms for this platform.

(I also based my self on another rtt that didn't implement what it didn't support).

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?

@MichelRottleuthner
Copy link
Contributor

DL;DR, short term solution:
(A): not use this specific timer for the rtt as the missing features break the api
(b): do it as this PR does and provide missing functionality in software

A bit more details:

The documentation already says that timeout shouldn't be set closer that 5cycles from the target time. I though this probably had to do with time taken to read the rtt current value and set an alarm.

Yes, probably related to clock/bus synchronization between the HF and LF clock domains.

If more stuff is happening in the function call then this constraint might be extended.

There a few things to keep in mind here:
-A few cycles of the LF clock are quite long compared to instructions executed on the HF clock so there is at least some room for additional operations.
-the 5 cycles are completely platform dependent and as of now we have no api or define for it. So for now these constraints are practically out of scope of the rtt api and the only safe way out is "know the specific constraints of your target platform". (This should be improved IMO as it is against the usual RIOT philosophy)

I ended up thinking that this would be something that a timer extension would handle, since setting an overflow alarm and alarm is just the same as setting multiple alarms for this platform.

I agree on the point that a timer extension should be in place to handle that.
But a problem with the current design is that the rtt api is not designed to have optional functionality or means for supplementing missing functionality in a generic way.

Long term solution:
There is absolutely no need for a separate rtt api. IMO the rtt api is just an artifact of the fact that we don't have a proper hardware presentation / hardware abstraction architecture.
What we actually need is a common periph_timer api that can cope with things like constraints, optional functionality and software extensions where needed.

@kaspar030
Copy link
Contributor

The documentation already says that timeout shouldn't be set closer that 5cycles from the target time.

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.
The software overhead is probably negligible (a couple of extra instructions).

I think it is totally acceptable to do add some missing functionality in software as done in this PR.

@fjmolinas
Copy link
Contributor

There is absolutely no need for a separate rtt api. IMO the rtt api is just an artifact of the fact that we don't have a proper hardware presentation / hardware abstraction architecture.
What we actually need is a common periph_timer api that can cope with things like constraints, optional functionality and software extensions where needed.

Is this something you have on your roadmap?

@MichelRottleuthner
Copy link
Contributor

MichelRottleuthner commented Apr 17, 2020

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.

@fjmolinas
Copy link
Contributor

@benpicco I think we can go ahead with this one then, is it in a state to be reviewed?

@miri64
Copy link
Member

miri64 commented Jun 25, 2020

@benpicco I think we can go ahead with this one then, is it in a state to be reviewed?

Ping @benpicco?

@benpicco
Copy link
Contributor Author

It can be reviewed, the question is if we want these features at the cost of the overhead they add to the common case.

Copy link
Contributor

@fjmolinas fjmolinas left a comment

Choose a reason for hiding this comment

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

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 itself nrf52 doesn'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)) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
} 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)) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
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)) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
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)) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
if (overflow_cb == NULL || (rtt_offset - now >= rtt_alarm - now)) {
if (overflow_cb == NULL || (rtt_offset >= rtt_alarm )) {

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I wonder if this was to handle overflows, but can't remember.

@benpicco benpicco closed this Aug 4, 2020
@benpicco benpicco deleted the cc2538_enhance_rtt branch August 4, 2020 17:14
@miri64
Copy link
Member

miri64 commented Aug 4, 2020

Taken over (hopefully in agreement with @benpicco, the conversation at hand does not provide that info) by @fjmolinas in #14700.

@fjmolinas
Copy link
Contributor

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Area: cpu Area: CPU/MCU ports State: demonstrator State: This PR (loosely) demonstrates a concept and is not necessarily meant to be merged. Type: new feature The issue requests / The PR implemements a new feature for RIOT

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants