drivers/timer/nrf_grtc_timer: Misc fixes
Misc fixes for the grtc timer driver: * In non tickless mode: * The tick time would drift a bit with each interrupt * If something would cause a very significant delay in handling the tick interrupt the number of announcements would be incorrect * Fortickless mode: The calculation of the next tick time in sys_clock_set_timeout() was incorrectly done, resulting in two spurious, too early, wakes of the kernel before each correct wake. This caused tests/kernel/context/ to fail. Signed-off-by: Alberto Escolar Piedras <alberto.escolar.piedras@nordicsemi.no>
This commit is contained in:
parent
0b173b100c
commit
d599e2b670
1 changed files with 28 additions and 30 deletions
|
@ -65,7 +65,7 @@ const int32_t z_sys_timer_irq_for_test = DT_IRQN(GRTC_NODE);
|
|||
static void sys_clock_timeout_handler(int32_t id, uint64_t cc_val, void *p_context);
|
||||
|
||||
static struct k_spinlock lock;
|
||||
static uint64_t last_count;
|
||||
static uint64_t last_count; /* Time (SYSCOUNTER value) @last sys_clock_announce() */
|
||||
static atomic_t int_mask;
|
||||
static uint8_t ext_channels_allocated;
|
||||
static nrfx_grtc_channel_t system_clock_channel_data = {
|
||||
|
@ -137,7 +137,10 @@ static inline int get_comparator(uint32_t chan, uint64_t *cc)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void system_timeout_set(uint64_t value)
|
||||
/*
|
||||
* Program a new callback <value> microseconds in the future
|
||||
*/
|
||||
static void system_timeout_set_relative(uint64_t value)
|
||||
{
|
||||
if (value <= NRF_GRTC_SYSCOUNTER_CCADD_MASK) {
|
||||
grtc_wakeup();
|
||||
|
@ -150,6 +153,15 @@ static void system_timeout_set(uint64_t value)
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Program a new callback in the absolute time given by <value>
|
||||
*/
|
||||
static void system_timeout_set_abs(uint64_t value)
|
||||
{
|
||||
nrfx_grtc_syscounter_cc_absolute_set(&system_clock_channel_data, value,
|
||||
true);
|
||||
}
|
||||
|
||||
static bool compare_int_lock(int32_t chan)
|
||||
{
|
||||
atomic_val_t prev = atomic_and(&int_mask, ~BIT(chan));
|
||||
|
@ -172,22 +184,19 @@ static void sys_clock_timeout_handler(int32_t id, uint64_t cc_val, void *p_conte
|
|||
ARG_UNUSED(id);
|
||||
ARG_UNUSED(p_context);
|
||||
uint64_t dticks;
|
||||
uint64_t now = counter();
|
||||
|
||||
if (unlikely(now < cc_val)) {
|
||||
return;
|
||||
}
|
||||
dticks = counter_sub(cc_val, last_count) / CYC_PER_TICK;
|
||||
|
||||
last_count += dticks * CYC_PER_TICK;
|
||||
|
||||
if (!IS_ENABLED(CONFIG_TICKLESS_KERNEL)) {
|
||||
/* protection is not needed because we are in the GRTC interrupt
|
||||
* so it won't get preempted by the interrupt.
|
||||
*/
|
||||
system_timeout_set(CYC_PER_TICK);
|
||||
system_timeout_set_abs(last_count + CYC_PER_TICK);
|
||||
}
|
||||
|
||||
dticks = counter_sub(now, last_count) / CYC_PER_TICK;
|
||||
|
||||
last_count += dticks * CYC_PER_TICK;
|
||||
sys_clock_announce(IS_ENABLED(CONFIG_TICKLESS_KERNEL) ? (int32_t)dticks : (dticks > 0));
|
||||
sys_clock_announce((int32_t)dticks);
|
||||
}
|
||||
|
||||
int32_t z_nrf_grtc_timer_chan_alloc(void)
|
||||
|
@ -520,7 +529,7 @@ static int sys_clock_driver_init(void)
|
|||
|
||||
int_mask = NRFX_GRTC_CONFIG_ALLOWED_CC_CHANNELS_MASK;
|
||||
if (!IS_ENABLED(CONFIG_TICKLESS_KERNEL)) {
|
||||
system_timeout_set(CYC_PER_TICK);
|
||||
system_timeout_set_relative(CYC_PER_TICK);
|
||||
}
|
||||
|
||||
#if defined(CONFIG_CLOCK_CONTROL_NRF)
|
||||
|
@ -540,34 +549,23 @@ static int sys_clock_driver_init(void)
|
|||
void sys_clock_set_timeout(int32_t ticks, bool idle)
|
||||
{
|
||||
ARG_UNUSED(idle);
|
||||
uint64_t cyc, off, now;
|
||||
|
||||
if (!IS_ENABLED(CONFIG_TICKLESS_KERNEL)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ticks = (ticks == K_TICKS_FOREVER) ? MAX_TICKS : MIN(MAX_TICKS, MAX(ticks - 1, 0));
|
||||
ticks = (ticks == K_TICKS_FOREVER) ? MAX_TICKS : MIN(MAX_TICKS, MAX(ticks, 0));
|
||||
|
||||
now = counter();
|
||||
uint64_t delta_time = ticks * CYC_PER_TICK;
|
||||
|
||||
/* Round up to the next tick boundary */
|
||||
off = (now - last_count) + (CYC_PER_TICK - 1);
|
||||
off = (off / CYC_PER_TICK) * CYC_PER_TICK;
|
||||
uint64_t target_time = counter() + delta_time;
|
||||
|
||||
/* Get the offset with respect to now */
|
||||
off -= (now - last_count);
|
||||
|
||||
/* Add the offset to get to the next tick boundary */
|
||||
cyc = (uint64_t)ticks * CYC_PER_TICK + off;
|
||||
|
||||
/* Due to elapsed time the calculation above might produce a
|
||||
* duration that laps the counter. Don't let it.
|
||||
/* Rounded down target_time to the tick boundary
|
||||
* (but not less than one tick after the last)
|
||||
*/
|
||||
if (cyc > MAX_CYCLES) {
|
||||
cyc = MAX_CYCLES;
|
||||
}
|
||||
target_time = MAX((target_time - last_count)/CYC_PER_TICK, 1)*CYC_PER_TICK + last_count;
|
||||
|
||||
system_timeout_set(cyc == 0 ? 1 : cyc);
|
||||
system_timeout_set_abs(target_time);
|
||||
}
|
||||
|
||||
#if defined(CONFIG_NRF_GRTC_TIMER_APP_DEFINED_INIT)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue