diff --git a/drivers/timer/nrf_grtc_timer.c b/drivers/timer/nrf_grtc_timer.c index 68c8d8863df..2809d8b3d60 100644 --- a/drivers/timer/nrf_grtc_timer.c +++ b/drivers/timer/nrf_grtc_timer.c @@ -49,16 +49,12 @@ #define COUNTER_SPAN (GRTC_SYSCOUNTERL_VALUE_Msk | ((uint64_t)GRTC_SYSCOUNTERH_VALUE_Msk << 32)) #define MAX_ABS_TICKS (COUNTER_SPAN / CYC_PER_TICK) -/* To allow use of CCADD we need to limit max cycles to 31 bits. */ -#define MAX_REL_CYCLES BIT_MASK(31) -#define MAX_REL_TICKS (MAX_REL_CYCLES / CYC_PER_TICK) +#define MAX_TICKS \ + (((COUNTER_SPAN / CYC_PER_TICK) > INT_MAX) ? INT_MAX : (COUNTER_SPAN / CYC_PER_TICK)) -#define LFCLK_FREQUENCY_HZ DT_PROP(LFCLK_NODE, clock_frequency) +#define MAX_CYCLES (MAX_TICKS * CYC_PER_TICK) -/* Threshold used to determine if there is a risk of unexpected GRTC COMPARE event coming - * from previous CC value. - */ -#define LATENCY_THR_TICKS 200 +#define LFCLK_FREQUENCY_HZ DT_PROP(LFCLK_NODE, clock_frequency) #if defined(CONFIG_TEST) const int32_t z_sys_timer_irq_for_test = DT_IRQN(GRTC_NODE); @@ -66,10 +62,8 @@ 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; /* Time (SYSCOUNTER value) @last sys_clock_announce() */ -static uint32_t last_elapsed; -static uint64_t cc_value; /* Value that is expected to be in CC register. */ -static uint64_t expired_cc; /* Value that is expected to be in CC register. */ static atomic_t int_mask; static uint8_t ext_channels_allocated; static uint64_t grtc_start_value; @@ -152,13 +146,17 @@ static void compare_int_unlock(int32_t chan, bool key) static void sys_clock_timeout_handler(int32_t id, uint64_t cc_val, void *p_context) { ARG_UNUSED(id); - ARG_UNUSED(cc_val); ARG_UNUSED(p_context); - uint32_t dticks; + 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); - expired_cc = cc_val; + + last_count += dticks * CYC_PER_TICK; if (!IS_ENABLED(CONFIG_TICKLESS_KERNEL)) { /* protection is not needed because we are in the GRTC interrupt @@ -167,7 +165,6 @@ static void sys_clock_timeout_handler(int32_t id, uint64_t cc_val, void *p_conte system_timeout_set_abs(last_count + CYC_PER_TICK); } - last_elapsed = 0; sys_clock_announce((int32_t)dticks); } @@ -371,7 +368,6 @@ uint64_t z_nrf_grtc_timer_startup_value_get(void) int z_nrf_grtc_wakeup_prepare(uint64_t wake_time_us) { nrfx_err_t err_code; - static struct k_spinlock lock; static uint8_t systemoff_channel; uint64_t now = counter(); nrfx_grtc_sleep_config_t sleep_cfg; @@ -434,12 +430,20 @@ int z_nrf_grtc_wakeup_prepare(uint64_t wake_time_us) uint32_t sys_clock_cycle_get_32(void) { - return nrf_grtc_sys_counter_low_get(NRF_GRTC); + k_spinlock_key_t key = k_spin_lock(&lock); + uint32_t ret = (uint32_t)counter(); + + k_spin_unlock(&lock, key); + return ret; } uint64_t sys_clock_cycle_get_64(void) { - return counter(); + k_spinlock_key_t key = k_spin_lock(&lock); + uint64_t ret = counter(); + + k_spin_unlock(&lock, key); + return ret; } uint32_t sys_clock_elapsed(void) @@ -448,9 +452,7 @@ uint32_t sys_clock_elapsed(void) return 0; } - last_elapsed = (uint32_t)counter_sub(counter(), last_count); - - return last_elapsed / CYC_PER_TICK; + return (uint32_t)(counter_sub(counter(), last_count) / CYC_PER_TICK); } static int sys_clock_driver_init(void) @@ -491,10 +493,6 @@ static int sys_clock_driver_init(void) last_count = (counter() / CYC_PER_TICK) * CYC_PER_TICK; grtc_start_value = last_count; - expired_cc = UINT64_MAX; - nrfx_grtc_channel_callback_set(system_clock_channel_data.channel, - sys_clock_timeout_handler, NULL); - int_mask = NRFX_GRTC_CONFIG_ALLOWED_CC_CHANNELS_MASK; if (!IS_ENABLED(CONFIG_TICKLESS_KERNEL)) { system_timeout_set_relative(CYC_PER_TICK); @@ -549,47 +547,22 @@ void sys_clock_set_timeout(int32_t ticks, bool idle) { ARG_UNUSED(idle); - if (ticks == 0) { - return; - } - if (!IS_ENABLED(CONFIG_TICKLESS_KERNEL)) { return; } - uint32_t ch = system_clock_channel_data.channel; + ticks = (ticks == K_TICKS_FOREVER) ? MAX_TICKS : MIN(MAX_TICKS, MAX(ticks, 0)); - if ((cc_value == expired_cc) && (ticks < MAX_REL_TICKS)) { - uint32_t cyc = ticks * CYC_PER_TICK; + uint64_t delta_time = ticks * CYC_PER_TICK; - /* If it's the first timeout setting after previous expiration and timeout - * is short so fast method can be used which utilizes relative CC configuration. - */ - cc_value += cyc; - nrfx_grtc_syscounter_cc_rel_set(ch, cyc, NRFX_GRTC_CC_RELATIVE_COMPARE); - return; - } - - uint64_t cyc = (uint64_t)ticks * CYC_PER_TICK; - bool safe_setting = false; - int64_t prev_cc_val = cc_value; + uint64_t target_time = counter() + delta_time; - cc_value = last_count + last_elapsed + cyc; - - /* In case of timeout abort it may happen that CC is being set to a value - * that later than previous CC. If previous CC value is not far in the - * future, there is a risk that COMPARE event will be triggered for that - * previous CC value. If there is such risk safe procedure must be applied - * which is more time consuming but ensures that there will be no spurious - * event. + /* Rounded down target_time to the tick boundary + * (but not less than one tick after the last) */ - if (prev_cc_val < cc_value) { - int64_t now = last_count + last_elapsed; - - safe_setting = (prev_cc_val - now) < LATENCY_THR_TICKS; - } + target_time = MAX((target_time - last_count)/CYC_PER_TICK, 1)*CYC_PER_TICK + last_count; - nrfx_grtc_syscounter_cc_abs_set(ch, cc_value, safe_setting); + system_timeout_set_abs(target_time); } #if defined(CONFIG_NRF_GRTC_TIMER_APP_DEFINED_INIT) diff --git a/tests/drivers/timer/nrf_grtc_timer/boards/nrf54h20dk_nrf54h20_cpuapp.overlay b/tests/drivers/timer/nrf_grtc_timer/boards/nrf54h20dk_nrf54h20_cpuapp.overlay index 7ae95926703..04580b71480 100644 --- a/tests/drivers/timer/nrf_grtc_timer/boards/nrf54h20dk_nrf54h20_cpuapp.overlay +++ b/tests/drivers/timer/nrf_grtc_timer/boards/nrf54h20dk_nrf54h20_cpuapp.overlay @@ -2,21 +2,4 @@ &grtc { /delete-property/ child-owned-channels; - interrupts = <109 2>; -}; - -test_timer: &timer131 { - status = "okay"; - interrupts = <419 1>; -}; - -&timer130 { - status = "okay"; - prescaler = <0>; -}; - -/ { - chosen { - zephyr,cpu-load-counter = &timer130; - }; }; diff --git a/tests/drivers/timer/nrf_grtc_timer/boards/nrf54l15dk_nrf54l15_cpuapp.overlay b/tests/drivers/timer/nrf_grtc_timer/boards/nrf54l15dk_nrf54l15_cpuapp.overlay deleted file mode 100644 index 7bcede529b0..00000000000 --- a/tests/drivers/timer/nrf_grtc_timer/boards/nrf54l15dk_nrf54l15_cpuapp.overlay +++ /dev/null @@ -1,32 +0,0 @@ -/* SPDX-License-Identifier: Apache-2.0 */ - -&grtc { - interrupts = <228 2>; -}; - -test_timer: &timer21 { - status = "okay"; - interrupts = <203 1>; -}; - -&timer20 { - status = "okay"; - interrupts = <202 0>; -}; - -&timer00 { - status = "okay"; - prescaler = <0>; -}; - -/ { - chosen { - zephyr,cpu-load-counter = &timer00; - }; - - busy-sim { - compatible = "vnd,busy-sim"; - status = "okay"; - counter = <&timer20>; - }; -}; diff --git a/tests/drivers/timer/nrf_grtc_timer/boards/nrf54l15dk_nrf54l15_cpuflpr.overlay b/tests/drivers/timer/nrf_grtc_timer/boards/nrf54l15dk_nrf54l15_cpuflpr.overlay deleted file mode 100644 index cfa72ed0273..00000000000 --- a/tests/drivers/timer/nrf_grtc_timer/boards/nrf54l15dk_nrf54l15_cpuflpr.overlay +++ /dev/null @@ -1,32 +0,0 @@ -/* SPDX-License-Identifier: Apache-2.0 */ - -&grtc { - /*interrupts = <226 2>;*/ -}; - -test_timer: &timer21 { - status = "okay"; - /*interrupts = <203 2>;*/ -}; - -&timer20 { - status = "okay"; - interrupts = <202 0>; -}; - -&timer00 { - status = "okay"; - prescaler = <0>; -}; - -/ { - chosen { - zephyr,cpu-load-counter = &timer00; - }; - - busy-sim { - compatible = "vnd,busy-sim"; - status = "okay"; - counter = <&timer20>; - }; -}; diff --git a/tests/drivers/timer/nrf_grtc_timer/prj.conf b/tests/drivers/timer/nrf_grtc_timer/prj.conf index 93926f090b7..dea03477519 100644 --- a/tests/drivers/timer/nrf_grtc_timer/prj.conf +++ b/tests/drivers/timer/nrf_grtc_timer/prj.conf @@ -1,8 +1,2 @@ CONFIG_ZTEST=y CONFIG_NRF_GRTC_TIMER=y -CONFIG_COUNTER=y -CONFIG_TEST_RANDOM_GENERATOR=y -CONFIG_XOSHIRO_RANDOM_GENERATOR=y -CONFIG_LOG_PRINTK=y -CONFIG_CPU_LOAD=y -CONFIG_CPU_LOAD_USE_COUNTER=y diff --git a/tests/drivers/timer/nrf_grtc_timer/src/main.c b/tests/drivers/timer/nrf_grtc_timer/src/main.c index 9de554256b4..cede54f026f 100644 --- a/tests/drivers/timer/nrf_grtc_timer/src/main.c +++ b/tests/drivers/timer/nrf_grtc_timer/src/main.c @@ -5,15 +5,7 @@ */ #include #include -#include -#include -#include -#include -#include -#include -#include #include -LOG_MODULE_REGISTER(test, 1); #define GRTC_SLEW_TICKS 10 #define NUMBER_OF_TRIES 2000 @@ -161,243 +153,4 @@ ZTEST(nrf_grtc_timer, test_timer_abort_in_compare_mode) z_nrf_grtc_timer_chan_free(channel); } -enum test_timer_state { - TIMER_IDLE, - TIMER_PREPARE, - TIMER_ACTIVE -}; - -enum test_ctx { - TEST_HIGH_PRI, - TEST_TIMER_CB, - TEST_THREAD -}; - -struct test_grtc_timer { - struct k_timer timer; - uint32_t ticks; - uint32_t expire; - uint32_t start_cnt; - uint32_t expire_cnt; - uint32_t abort_cnt; - uint32_t exp_expire; - int max_late; - int min_late; - int avg_late; - uint32_t early_cnt; - enum test_timer_state state; -}; - -static atomic_t test_active_cnt; -static struct test_grtc_timer timers[8]; -static uint32_t test_end; -static k_tid_t test_tid; -static volatile bool test_run; -static uint32_t ctx_cnt[3]; -static const char *const ctx_name[] = { "HIGH PRIO ISR", "TIMER CALLBACK", "THREAD" }; - -static bool stress_test_action(int ctx, int id) -{ - struct test_grtc_timer *timer = &timers[id]; - - ctx_cnt[ctx]++; - if (timer->state == TIMER_ACTIVE) { - /* Aborting soon to expire timers from higher interrupt priority may lead - * to test failures. - */ - if (ctx == 0 && (k_timer_remaining_get(&timer->timer) < 5)) { - return true; - } - - if (timer->abort_cnt < timer->expire_cnt / 2) { - bool any_active; - - timer->state = TIMER_PREPARE; - k_timer_stop(&timer->timer); - timer->abort_cnt++; - any_active = atomic_dec(&test_active_cnt) > 1; - timer->state = TIMER_IDLE; - - return any_active; - } - } else if (timer->state == TIMER_IDLE) { - int ticks = 10 + (sys_rand32_get() & 0x3F); - k_timeout_t t = K_TICKS(ticks); - - timer->exp_expire = k_ticks_to_cyc_floor32(sys_clock_tick_get_32() + ticks); - timer->state = TIMER_PREPARE; - timer->ticks = ticks; - k_timer_start(&timer->timer, t, K_NO_WAIT); - atomic_inc(&test_active_cnt); - timer->start_cnt++; - timer->state = TIMER_ACTIVE; - } - - return true; -} - -static void stress_test_actions(int ctx) -{ - uint32_t r = sys_rand32_get(); - int action_cnt = Z_MAX(r & 0x3, 1); - int tmr_id = (r >> 8) % ARRAY_SIZE(timers); - - /* Occasionally wake thread context from which timer actions are also executed. */ - if ((((r >> 2) & 0x3) == 0) || test_active_cnt < 2) { - LOG_DBG("ctx:%d thread wakeup", ctx); - k_wakeup(test_tid); - } - - for (int i = 0; i < action_cnt; i++) { - if (stress_test_action(ctx, tmr_id) == false) { - stress_test_action(ctx, tmr_id); - } - } -} - -static void timer_cb(struct k_timer *timer) -{ - struct test_grtc_timer *test_timer = CONTAINER_OF(timer, struct test_grtc_timer, timer); - uint32_t now = k_cycle_get_32(); - int diff = now - test_timer->exp_expire; - - atomic_dec(&test_active_cnt); - zassert_true(diff >= 0); - test_timer->max_late = MAX(diff, test_timer->max_late); - test_timer->min_late = MIN(diff, test_timer->min_late); - - if (test_timer->expire_cnt == 0) { - test_timer->avg_late = diff; - } else { - test_timer->avg_late = (test_timer->avg_late * test_timer->expire_cnt + diff) / - (test_timer->expire_cnt + 1); - } - - test_timer->expire_cnt++; - test_timer->state = TIMER_IDLE; - - if (test_run) { - stress_test_actions(TEST_TIMER_CB); - } -} - -static void counter_set(const struct device *dev, struct counter_alarm_cfg *cfg) -{ - int err; - uint32_t us = 150 + (sys_rand32_get() & 0x3F); - - cfg->ticks = counter_us_to_ticks(dev, us); - err = counter_set_channel_alarm(dev, 0, cfg); - zassert_equal(err, 0); -} - -static void counter_cb(const struct device *dev, uint8_t chan_id, uint32_t ticks, void *user_data) -{ - struct counter_alarm_cfg *config = user_data; - - if (test_run) { - stress_test_actions(TEST_HIGH_PRI); - counter_set(dev, config); - } -} - -static void report_progress(uint32_t start, uint32_t end) -{ - static uint32_t next_report; - static uint32_t step; - static uint32_t progress; - - if (next_report == 0) { - step = (end - start) / 10; - next_report = start + step; - } - - if (k_uptime_get_32() > next_report) { - next_report += step; - progress += 10; - printk("%d%%\r", progress); - } -} - -static void grtc_stress_test(bool busy_sim_en) -{ - static struct counter_alarm_cfg alarm_cfg; -#if DT_NODE_EXISTS(DT_NODELABEL(test_timer)) && DT_NODE_HAS_STATUS(DT_NODELABEL(test_timer), okay) - const struct device *const counter_dev = DEVICE_DT_GET(DT_NODELABEL(test_timer)); -#else - const struct device *const counter_dev = NULL; -#endif - uint32_t test_ms = 5000; - uint32_t test_start = k_uptime_get_32(); - uint32_t load; - - test_end = k_cycle_get_32() + k_ms_to_cyc_floor32(test_ms); - test_tid = k_current_get(); - - for (size_t i = 0; i < ARRAY_SIZE(timers); i++) { - k_timer_init(&timers[i].timer, timer_cb, NULL); - } - - if (IS_ENABLED(CONFIG_CPU_LOAD)) { - (void)cpu_load_get(true); - } - - if (counter_dev) { - counter_start(counter_dev); - } - - alarm_cfg.callback = counter_cb; - alarm_cfg.user_data = &alarm_cfg; - test_run = true; - - if (counter_dev) { - counter_set(counter_dev, &alarm_cfg); - } - - if (busy_sim_en) { - busy_sim_start(500, 200, 1000, 400, NULL); - } - - LOG_DBG("Starting test, will end at %d", test_end); - while (k_cycle_get_32() < test_end) { - report_progress(test_start, test_start + test_ms); - stress_test_actions(TEST_THREAD); - k_sleep(K_MSEC(test_ms)); - } - - load = IS_ENABLED(CONFIG_CPU_LOAD) ? cpu_load_get(true) : 0; - - test_run = false; - k_msleep(50); - - for (size_t i = 0; i < ARRAY_SIZE(timers); i++) { - zassert_equal(timers[i].state, TIMER_IDLE, "Unexpected timer %d state:%d", - i, timers[i].state); - TC_PRINT("Timer%d (%p)\r\n\tstart_cnt:%d abort_cnt:%d expire_cnt:%d\n", - i, &timers[i], timers[i].start_cnt, timers[i].abort_cnt, - timers[i].expire_cnt); - TC_PRINT("\tavarage late:%d ticks, max late:%d, min late:%d early:%d\n", - timers[i].avg_late, timers[i].max_late, timers[i].min_late, - timers[i].early_cnt); - } - - for (size_t i = 0; i < ARRAY_SIZE(ctx_cnt); i++) { - TC_PRINT("Context: %s executed %d times\n", ctx_name[i], ctx_cnt[i]); - } - TC_PRINT("CPU load during test:%d.%d\n", load / 10, load % 10); - - if (busy_sim_en) { - busy_sim_stop(); - } - - if (counter_dev) { - counter_stop(counter_dev); - } -} - -ZTEST(nrf_grtc_timer, test_stress) -{ - grtc_stress_test(false); -} - ZTEST_SUITE(nrf_grtc_timer, NULL, NULL, NULL, NULL, NULL); diff --git a/tests/drivers/timer/nrf_grtc_timer/testcase.yaml b/tests/drivers/timer/nrf_grtc_timer/testcase.yaml index 57835bc386a..cd4f99daede 100644 --- a/tests/drivers/timer/nrf_grtc_timer/testcase.yaml +++ b/tests/drivers/timer/nrf_grtc_timer/testcase.yaml @@ -1,24 +1,21 @@ -common: - tags: - - drivers - platform_allow: - - nrf54l15dk/nrf54l15/cpuapp - - nrf54l15dk/nrf54l15/cpuflpr - - nrf54l15bsim/nrf54l15/cpuapp - - nrf54h20dk/nrf54h20/cpuapp - - nrf54h20dk/nrf54h20/cpurad - - nrf54h20dk/nrf54h20/cpuppr - - nrf54l20pdk/nrf54l20/cpuapp - - nrf54lm20dk/nrf54lm20a/cpuapp - - nrf54l20pdk/nrf54l20/cpuflpr - - nrf54lm20dk/nrf54lm20a/cpuflpr - - ophelia4ev/nrf54l15/cpuapp - - ophelia4ev/nrf54l15/cpuflpr - integration_platforms: - - nrf54l15dk/nrf54l15/cpuapp - - nrf54lm20dk/nrf54lm20a/cpuapp tests: - drivers.timer.nrf_grtc_timer: {} - drivers.timer.nrf_grtc_timer.no_assert: - extra_configs: - - CONFIG_ASSERT=n + drivers.timer.nrf_grtc_timer: + tags: drivers + platform_allow: + - nrf54l09pdk/nrf54l09/cpuapp + - nrf54l09pdk/nrf54l09/cpuflpr + - nrf54l15dk/nrf54l15/cpuapp + - nrf54l15dk/nrf54l15/cpuflpr + - nrf54l15bsim/nrf54l15/cpuapp + - nrf54h20dk/nrf54h20/cpuapp + - nrf54h20dk/nrf54h20/cpurad + - nrf54h20dk/nrf54h20/cpuppr + - nrf54l20pdk/nrf54l20/cpuapp + - nrf54lm20dk/nrf54lm20a/cpuapp + - nrf54l20pdk/nrf54l20/cpuflpr + - nrf54lm20dk/nrf54lm20a/cpuflpr + - ophelia4ev/nrf54l15/cpuapp + - ophelia4ev/nrf54l15/cpuflpr + integration_platforms: + - nrf54l20pdk/nrf54l20/cpuapp + - nrf54lm20dk/nrf54lm20a/cpuapp