Skip to content

Commit bcc31ff

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 bcc31ff

File tree

2 files changed

+54
-7
lines changed

2 files changed

+54
-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: 53 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,67 @@
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+
uint32_t ticks = 0;
38+
uint64_t cnt = SYSTICK->CNT;
39+
3440
ARG_UNUSED(unused);
3541

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

4181
uint32_t sys_clock_cycle_get_32(void)
4282
{
43-
return ch32v00x_systick_count + SYSTICK->CNT;
83+
return (uint32_t)SYSTICK->CNT;
84+
}
85+
86+
uint64_t sys_clock_cycle_get_64(void)
87+
{
88+
return SYSTICK->CNT;
4489
}
4590

4691
uint32_t sys_clock_elapsed(void)
@@ -55,10 +100,11 @@ static int ch32v00x_systick_init(void)
55100
SYSTICK->SR = 0;
56101
SYSTICK->CMP = CYCLES_PER_TICK;
57102
SYSTICK->CNT = 0;
58-
SYSTICK->CTLR = STK_STRE | STK_STCLK | STK_STIE | STK_STE;
59103

60104
irq_enable(DT_INST_IRQN(0));
61105

106+
SYSTICK->CTLR = STK_STE | STK_STCLK | STK_STIE;
107+
62108
return 0;
63109
}
64110

0 commit comments

Comments
 (0)