Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Description
Fix several problems in the QMK timer API implementation for ChibiOS:
The code uses a 32-bit tick counter internally, and that counter is overflowing and wrapping around faster than the 32-bit millisecond counter used by QMK:
CH_CFG_ST_FREQUENCY
set to 10000 (typically used with the periodic tick mode, or with 16-bit hardware timers), the tick counter overflow happens after slightly less than 5 days of continuous runtime.CH_CFG_ST_FREQUENCY
set to 100000 (default for most chips with 32-bit hardware timers), the tick counter overflow happens after slightly less than 5 hours.However, the existing timer code did not handle the tick counter overflow correctly — instead of continuing the millisecond counting further, the QMK timer value was reset back to 0; this broke all code that used timers in various ways (in particular, the recently added deferred executors stopped working completely, because the expected timer values were never reached).
Fixing the problem required adding some code to detect overflows of the 32-bit tick counter and handle them by adjusting two separate offsets — one for the tick counter, another for the converted millisecond value. Using just a single offset was not really possible — passing 64-bit tick values to the time conversion routines could cause overflows during conversion, and adjusting the millisecond value for 232 ticks would result in timing errors, because 232 ticks in many cases do not correspond to an integer number of milliseconds.
I tested the fix with some ridiculous values (
#define CH_CFG_ST_FREQUENCY 96000000
on F411, which results in a 32-bit tick counter overflow every ≈45 seconds), and it seems to work.For the 16-bit hardware timer case the timer code relied on
timer_read32()
calls being performed at least as frequently as the hardware timer overflows (otherwise some overflow periods would be skipped without advancing the timer value appropriately). However, if the QMK code is blocked waiting for something without doing any timer checks (maybe there is just some large ChibiOS-based timeout for the operation), and at the same time the timer frequency is relatively high (e.g., on some SN32 chips the minimum possible timer frequency is 187500 Hz, because the prescaler is only 8-bit), some hardware timer overflows may still be missed.To make the timekeeping with a 16-bit hardware timer more reliable, I added a ChibiOS virtual timer to the code; that virtual timer fires every half of the hardware timer overflow period, and updates the tick counter state to account for possible hardware timer overflow and wraparound. These periodic calls are usually not very frequent (every ≈3.28 seconds for 10000 Hz, or every ≈328 ms for 100000 Hz; certainly much less frequent than periodic timer interrupts) and should not result in much system load.
In addition to making the timekeeping more reliable, these periodic calls also work around the recently discovered bug in ChibiOS for the 16-bit hardware timer case: when all active virtual timers have delays longer than the hardware timer overflow period, the handling of virtual timers stops completely. In QMK this bug can result in a
wait_ms()
call with a delay larger than the hardware timer overflow period just hanging indefinitely. However, when the timer update code adds a virtual timer with a shorter delay, all other virtual timers are also handled properly, even when their delay is longer.Types of Changes
Checklist