Skip to content

Commit 0890140

Browse files
committed
drivers: timer: fix a race in the WCH systick time monoticity
To ensure proper monotonic system time, don't rely on SysTick reset, instead increasing the compare value as needed to trigger the next interrupt/tick. Update to support 64-bit cycles as well. Signed-off-by: Peter Johanson <peter@peterjohanson.com>
1 parent 9ab11e0 commit 0890140

File tree

2 files changed

+55
-7
lines changed

2 files changed

+55
-7
lines changed

drivers/timer/Kconfig.wch_ch32v00x

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,4 @@ config CH32V00X_SYSTICK
66
depends on SOC_SERIES_QINGKE_V2A || SOC_SERIES_QINGKE_V4C || SOC_SERIES_CH32V00X || SOC_SERIES_QINGKE_V4B || SOC_SERIES_QINGKE_V4F
77
default y
88
depends on DT_HAS_WCH_SYSTICK_ENABLED
9+
select TIMER_HAS_64BIT_CYCLE_COUNTER

drivers/timer/wch_systick_ch32v00x.c

Lines changed: 54 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,6 @@
1414

1515
#include <hal_ch32fun.h>
1616

17-
#define STK_SWIE BIT(31)
18-
#define STK_STRE BIT(3)
1917
#define STK_STCLK BIT(2)
2018
#define STK_STIE BIT(1)
2119
#define STK_STE BIT(0)
@@ -27,20 +25,68 @@
2725

2826
#define SYSTICK ((SysTick_Type *)(DT_INST_REG_ADDR(0)))
2927

30-
static volatile uint32_t ch32v00x_systick_count;
28+
static uint64_t last_cycles_announced;
29+
30+
static inline bool cycles_close_to_next_cmp(uint32_t cycles)
31+
{
32+
return (cycles % CYCLES_PER_TICK) > (9 * CYCLES_PER_TICK / 10);
33+
}
3134

3235
static void ch32v00x_systick_irq(const void *unused)
3336
{
37+
uint64_t elapsed_cycles;
38+
uint32_t ticks = 0;
39+
uint64_t cnt = SYSTICK->CNT;
40+
3441
ARG_UNUSED(unused);
3542

43+
if (cnt < last_cycles_announced) {
44+
elapsed_cycles = (UINT64_MAX - last_cycles_announced) + cnt;
45+
ticks = elapsed_cycles / CYCLES_PER_TICK;
46+
47+
/* If we're too close to the next tick, announce that tick early now rather than
48+
* miss it
49+
*/
50+
if (cycles_close_to_next_cmp(elapsed_cycles % CYCLES_PER_TICK)) {
51+
ticks++;
52+
last_cycles_announced = (cnt % CYCLES_PER_TICK) + CYCLES_PER_TICK;
53+
} else {
54+
last_cycles_announced = cnt % CYCLES_PER_TICK;
55+
}
56+
} else {
57+
ticks = (cnt - last_cycles_announced) / CYCLES_PER_TICK;
58+
59+
/* If we're too close to the next tick, announce that tick early now rather than
60+
* miss it
61+
*/
62+
if (cycles_close_to_next_cmp(cnt - last_cycles_announced)) {
63+
ticks++;
64+
}
65+
66+
last_cycles_announced += ticks * CYCLES_PER_TICK;
67+
}
68+
69+
70+
/* Ensure we trigger when CNT resets to zero */
71+
if (UINT64_MAX - SYSTICK->CMP < CYCLES_PER_TICK) {
72+
SYSTICK->CMP = SYSTICK->CMP % CYCLES_PER_TICK;
73+
} else {
74+
SYSTICK->CMP = (last_cycles_announced + CYCLES_PER_TICK);
75+
}
76+
3677
SYSTICK->SR = 0;
37-
ch32v00x_systick_count += CYCLES_PER_TICK; /* Track cycles. */
38-
sys_clock_announce(1); /* Poke the scheduler. */
78+
79+
sys_clock_announce(ticks);
3980
}
4081

4182
uint32_t sys_clock_cycle_get_32(void)
4283
{
43-
return ch32v00x_systick_count + SYSTICK->CNT;
84+
return (uint32_t)SYSTICK->CNT;
85+
}
86+
87+
uint64_t sys_clock_cycle_get_64(void)
88+
{
89+
return SYSTICK->CNT;
4490
}
4591

4692
uint32_t sys_clock_elapsed(void)
@@ -55,10 +101,11 @@ static int ch32v00x_systick_init(void)
55101
SYSTICK->SR = 0;
56102
SYSTICK->CMP = CYCLES_PER_TICK;
57103
SYSTICK->CNT = 0;
58-
SYSTICK->CTLR = STK_STRE | STK_STCLK | STK_STIE | STK_STE;
59104

60105
irq_enable(DT_INST_IRQN(0));
61106

107+
SYSTICK->CTLR = STK_STE | STK_STCLK | STK_STIE;
108+
62109
return 0;
63110
}
64111

0 commit comments

Comments
 (0)