Skip to content

Commit 44dba78

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 44dba78

File tree

2 files changed

+53
-7
lines changed

2 files changed

+53
-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: 52 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,66 @@
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+
return (cycles % CYCLES_PER_TICK) > (9 * CYCLES_PER_TICK / 10);
32+
}
3133

3234
static void ch32v00x_systick_irq(const void *unused)
3335
{
36+
uint32_t ticks = 0;
37+
uint64_t cnt = SYSTICK->CNT;
38+
3439
ARG_UNUSED(unused);
3540

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

4180
uint32_t sys_clock_cycle_get_32(void)
4281
{
43-
return ch32v00x_systick_count + SYSTICK->CNT;
82+
return (uint32_t)SYSTICK->CNT;
83+
}
84+
85+
uint64_t sys_clock_cycle_get_64(void)
86+
{
87+
return SYSTICK->CNT;
4488
}
4589

4690
uint32_t sys_clock_elapsed(void)
@@ -55,10 +99,11 @@ static int ch32v00x_systick_init(void)
5599
SYSTICK->SR = 0;
56100
SYSTICK->CMP = CYCLES_PER_TICK;
57101
SYSTICK->CNT = 0;
58-
SYSTICK->CTLR = STK_STRE | STK_STCLK | STK_STIE | STK_STE;
59102

60103
irq_enable(DT_INST_IRQN(0));
61104

105+
SYSTICK->CTLR = STK_STE | STK_STCLK | STK_STIE;
106+
62107
return 0;
63108
}
64109

0 commit comments

Comments
 (0)