Skip to content
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

Fix stm32 timer periodic #19363

Merged
merged 2 commits into from
Mar 8, 2023
Merged

Conversation

Enoch247
Copy link
Contributor

@Enoch247 Enoch247 commented Mar 7, 2023

Contribution description

From the commit msg:

cpu/stm32/periph/timer: remove unneeded header

I see no reason this header should be included. It does not exist in
RIOT's source tree. This patch removes the include.

and

cpu/stm32/periph/timer: fix execution flow

The implmentation of `timer_set_absolute()` has The following problems.
First, it attempts to restore the auto reload register (ARR) to it's
default if the ARR was previosly set by `timer_set_periodic()` by
comparing it to the channel's capture compare (CC) register _after_ it
has already set the CC register. Secondly, it clears spurious IRQs
_after_ the CC register has been set. If the value being set is equal to
the timer's current count (or the two become equal before the supurios
IRQ clearing happens), this could cause a legitimate IRQ to be cleared.

The implmentation of `timer_set()` has the same error in handling the
ARR as described above.

This patch reorders the operations of both functions to do:

1. handle ARR
2. clear spurious IRQs
3. set channel's CC
4. enable IRQ

Additionally, the calulation of `value` in `timer_set()` is moved
earlier in the function's exec path as a pedantic measure.

Testing procedure

I tested by doing the following:

  1. make -C tests/periph_timer BOARD=nucleo-f767zi all flash term
  2. press s
  3. press [ENTER]
  4. observe test passes
  5. make -C tests/periph_timer_periodic BOARD=nucleo-f767zi all flash term
  6. press s
  7. press [ENTER]
  8. observe test passes
  9. make -C tests/periph_timer_short_relative_set BOARD=nucleo-f767zi all flash term
  10. press s
  11. press [ENTER]
  12. observe test passes

Issues/PRs references

  • none known

I see no reason this header should be included. It does not exist in
RIOT's source tree. This patch removes the include.
The implmentation of `timer_set_absolute()` has The following problems.
First, it attempts to restore the auto reload register (ARR) to it's
default if the ARR was previosly set by `timer_set_periodic()` by
comparing it to the channel's capture compare (CC) register _after_ it
has already set the CC register. Secondly, it clears spurious IRQs
_after_ the CC register has been set. If the value being set is equal to
the timer's current count (or the two become equal before the supurios
IRQ clearing happens), this could cause a legitimate IRQ to be cleared.

The implmentation of `timer_set()` has the same error in handling the
ARR as described above.

This patch reorders the operations of both functions to do:

1. handle ARR
2. clear spurious IRQs
3. set channel's CC
4. enable IRQ

Additionally, the calulation of `value` in `timer_set()` is moved
earlier in the function's exec path as a pedantic measure.
@github-actions github-actions bot added Area: cpu Area: CPU/MCU ports Platform: ARM Platform: This PR/issue effects ARM-based platforms labels Mar 7, 2023
@benpicco benpicco added the CI: ready for build If set, CI server will compile all applications for all available boards for the labeled PR label Mar 7, 2023
@benpicco benpicco requested a review from maribu March 7, 2023 18:47
Copy link
Contributor

@benpicco benpicco left a comment

Choose a reason for hiding this comment

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

Thank you for the fix!
Just one thing:

@@ -149,28 +148,29 @@ int timer_set_absolute(tim_t tim, int channel, unsigned int value)

int timer_set(tim_t tim, int channel, unsigned int timeout)
{
unsigned value = (dev(tim)->CNT + timeout) & timer_config[tim].max;
Copy link
Contributor

@benpicco benpicco Mar 7, 2023

Choose a reason for hiding this comment

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

You want to move that below the irq_disable() otherwise if this thread is interrupted, the value will no longer be correct.

Copy link
Contributor Author

@Enoch247 Enoch247 Mar 7, 2023

Choose a reason for hiding this comment

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

I debated doing that before submitting the PR. I didn't feel strongly either way, but my reasoning for making it as it is follows. Please check my thinking for flaws.

I reasoned that from a caller's stand point, the value of timeout is an offset from "now". "Now" is a moving target until value is computed, therefore it should be computed as soon as possible, but it doesn't have to be in the critical section. There is no harm in servicing IRQs or higher priority threads between calculating value and scheduling the timer to fire once it reaches value, so long as the timer doesn't go past value + one rollover period of the timer. If the caller needs this guarantee, I think the IRQs need disabled at a higher level then any part of the body of this function can provide.

If this line were moved to the critical section, this function could sill be blocked before entering the critical section. Once unblocked and inside the critical section value could be computed, but it would be not what the caller expected. It would be "now" + however long the blocked time was before the critical section. Note that my current implementation also has this flaw too since it can be blocked between being called and the first line of c code of the body of the function.

In both cases, if the caller is worried about interrupts and other threads messing with setting a timer, the caller better handle it, because the driver will do something bad either way. So best to just keep the critical section as small as possible.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

As I understand it. The critical sections in this driver exists so that the read-modify-writes on the timers' registers and _oneshot are safely synchronized between the timer's ISR and any concurrent threads accessing the driver. The critical sections aren't meant to ensure any kind of timing/blocking guarantees.

Copy link
Contributor

Choose a reason for hiding this comment

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

That makes sense - and we do have a check for value > timeout when in the meantime the timeout already passed, so this is indeed a valid solution.

@riot-ci
Copy link

riot-ci commented Mar 7, 2023

Murdock results

✔️ PASSED

eeb359e cpu/stm32/periph/timer: fix execution flow

Success Failures Total Runtime
6919 0 6919 11m:26s

Artifacts

@@ -22,7 +22,6 @@

#include "cpu.h"
#include "periph/timer.h"
#include <sys/_intsup.h>
Copy link
Member

Choose a reason for hiding this comment

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

This looks like clangd header insertion artifact. I had a lot of those until I finally just globally disabled it. Maybe we should add some sane default clangd configuration to the repo, so that others don't run into the same issue.

@benpicco
Copy link
Contributor

benpicco commented Mar 8, 2023

bors merge

@bors
Copy link
Contributor

bors bot commented Mar 8, 2023

Build succeeded:

@bors bors bot merged commit fae992e into RIOT-OS:master Mar 8, 2023
@Enoch247 Enoch247 deleted the fix-stm32-timer-periodic branch March 8, 2023 13:06
@MrKevinWeiss MrKevinWeiss added this to the Release 2023.04 milestone Apr 25, 2023
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 CI: ready for build If set, CI server will compile all applications for all available boards for the labeled PR Platform: ARM Platform: This PR/issue effects ARM-based platforms
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants