Skip to content

[nrf fromlist] GRTC optimizations #2740

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 58 additions & 31 deletions drivers/timer/nrf_grtc_timer.c
Original file line number Diff line number Diff line change
Expand Up @@ -49,21 +49,27 @@
#define COUNTER_SPAN (GRTC_SYSCOUNTERL_VALUE_Msk | ((uint64_t)GRTC_SYSCOUNTERH_VALUE_Msk << 32))
#define MAX_ABS_TICKS (COUNTER_SPAN / CYC_PER_TICK)

#define MAX_TICKS \
(((COUNTER_SPAN / CYC_PER_TICK) > INT_MAX) ? INT_MAX : (COUNTER_SPAN / CYC_PER_TICK))

#define MAX_CYCLES (MAX_TICKS * 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)

Check notice on line 55 in drivers/timer/nrf_grtc_timer.c

View workflow job for this annotation

GitHub Actions / Run compliance checks on patch series (PR)

You may want to run clang-format on this change

drivers/timer/nrf_grtc_timer.c:55 -#define MAX_REL_TICKS (MAX_REL_CYCLES / CYC_PER_TICK) +#define MAX_REL_TICKS (MAX_REL_CYCLES / CYC_PER_TICK)
#define LFCLK_FREQUENCY_HZ DT_PROP(LFCLK_NODE, clock_frequency)

/* Threshold used to determine if there is a risk of unexpected GRTC COMPARE event coming
* from previous CC value.
*/
#define LATENCY_THR_TICKS 200

#if defined(CONFIG_TEST)
const int32_t z_sys_timer_irq_for_test = DT_IRQN(GRTC_NODE);
#endif

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. */

Check notice on line 72 in drivers/timer/nrf_grtc_timer.c

View workflow job for this annotation

GitHub Actions / Run compliance checks on patch series (PR)

You may want to run clang-format on this change

drivers/timer/nrf_grtc_timer.c:72 -static uint64_t cc_value; /* Value that is expected to be in CC register. */ +static uint64_t cc_value; /* Value that is expected to be in CC register. */
static atomic_t int_mask;
static uint8_t ext_channels_allocated;
static nrfx_grtc_channel_t system_clock_channel_data = {
Expand Down Expand Up @@ -145,17 +151,13 @@
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);
uint64_t dticks;
uint64_t now = counter();

if (unlikely(now < cc_val)) {
return;
}
uint32_t dticks;

dticks = counter_sub(cc_val, last_count) / CYC_PER_TICK;

last_count += dticks * CYC_PER_TICK;
last_count += (dticks * CYC_PER_TICK);
expired_cc = cc_val;

if (!IS_ENABLED(CONFIG_TICKLESS_KERNEL)) {
/* protection is not needed because we are in the GRTC interrupt
Expand All @@ -164,6 +166,7 @@
system_timeout_set_abs(last_count + CYC_PER_TICK);
}

last_elapsed = 0;
sys_clock_announce((int32_t)dticks);
}

Expand Down Expand Up @@ -362,6 +365,7 @@
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;
Expand Down Expand Up @@ -422,22 +426,15 @@
}
#endif /* CONFIG_POWEROFF */


uint32_t sys_clock_cycle_get_32(void)

Check notice on line 430 in drivers/timer/nrf_grtc_timer.c

View workflow job for this annotation

GitHub Actions / Run compliance checks on patch series (PR)

You may want to run clang-format on this change

drivers/timer/nrf_grtc_timer.c:430 -
{
k_spinlock_key_t key = k_spin_lock(&lock);
uint32_t ret = (uint32_t)counter();

k_spin_unlock(&lock, key);
return ret;
return (uint32_t)counter();
}

uint64_t sys_clock_cycle_get_64(void)
{
k_spinlock_key_t key = k_spin_lock(&lock);
uint64_t ret = counter();

k_spin_unlock(&lock, key);
return ret;
return counter();
}

uint32_t sys_clock_elapsed(void)
Expand All @@ -446,7 +443,9 @@
return 0;
}

return (uint32_t)(counter_sub(counter(), last_count) / CYC_PER_TICK);
last_elapsed = (uint32_t)counter_sub(counter(), last_count);

return last_elapsed / CYC_PER_TICK;
}

static int sys_clock_driver_init(void)
Expand Down Expand Up @@ -485,6 +484,9 @@
}
#endif /* CONFIG_NRF_GRTC_START_SYSCOUNTER */

nrfx_grtc_channel_callback_set(system_clock_channel_data.channel,
sys_clock_timeout_handler, NULL);

Check notice on line 489 in drivers/timer/nrf_grtc_timer.c

View workflow job for this annotation

GitHub Actions / Run compliance checks on patch series (PR)

You may want to run clang-format on this change

drivers/timer/nrf_grtc_timer.c:489 - nrfx_grtc_channel_callback_set(system_clock_channel_data.channel, - sys_clock_timeout_handler, NULL); + 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);
Expand Down Expand Up @@ -539,22 +541,47 @@
{
ARG_UNUSED(idle);

if (ticks == 0) {
return;
}

if (!IS_ENABLED(CONFIG_TICKLESS_KERNEL)) {
return;
}

ticks = (ticks == K_TICKS_FOREVER) ? MAX_TICKS : MIN(MAX_TICKS, MAX(ticks, 0));
uint32_t ch = system_clock_channel_data.channel;

uint64_t delta_time = ticks * CYC_PER_TICK;
if ((cc_value == expired_cc) && (ticks < MAX_REL_TICKS)) {
uint32_t cyc = ticks * CYC_PER_TICK;

uint64_t target_time = counter() + delta_time;
/* 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;

/* Rounded down target_time to the tick boundary
* (but not less than one tick after the last)
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.
*/
target_time = MAX((target_time - last_count)/CYC_PER_TICK, 1)*CYC_PER_TICK + last_count;
if (prev_cc_val < cc_value) {
int64_t now = last_count + last_elapsed;

safe_setting = (prev_cc_val - now) < LATENCY_THR_TICKS;
}

system_timeout_set_abs(target_time);
nrfx_grtc_syscounter_cc_abs_set(ch, cc_value, safe_setting);
}

#if defined(CONFIG_NRF_GRTC_TIMER_APP_DEFINED_INIT)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,21 @@

&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;
};
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/* 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>;
};
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/* 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>;
};
};
6 changes: 6 additions & 0 deletions tests/drivers/timer/nrf_grtc_timer/prj.conf
Original file line number Diff line number Diff line change
@@ -1,2 +1,8 @@
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
Loading
Loading