/* * Copyright (c) 2024, Nordic Semiconductor ASA * * SPDX-License-Identifier: Apache-2.0 */ #include #include #include #include LOG_MODULE_REGISTER(cpu_load, CONFIG_CPU_LOAD_LOG_LEVEL); BUILD_ASSERT(!IS_ENABLED(CONFIG_CPU_LOAD_USE_COUNTER) || DT_HAS_CHOSEN(zephyr_cpu_load_counter)); #ifndef CONFIG_CPU_LOAD_LOG_PERIODICALLY #define CONFIG_CPU_LOAD_LOG_PERIODICALLY 0 #endif static const struct device *counter = COND_CODE_1(CONFIG_CPU_LOAD_USE_COUNTER, (DEVICE_DT_GET(DT_CHOSEN(zephyr_cpu_load_counter))), (NULL)); static uint32_t enter_ts; static uint64_t cyc_start; static uint64_t ticks_idle; static cpu_load_cb_t load_cb; static uint8_t cpu_load_threshold_percent; static void cpu_load_log_fn(struct k_timer *dummy) { int load = cpu_load_get(true); uint32_t percent = load / 10; uint32_t fraction = load % 10; LOG_INF("Load:%d.%03d%%", percent, fraction); if (load_cb != NULL && percent >= cpu_load_threshold_percent) { load_cb(percent); } } K_TIMER_DEFINE(cpu_load_timer, cpu_load_log_fn, NULL); void cpu_load_log_control(bool enable) { if (CONFIG_CPU_LOAD_LOG_PERIODICALLY == 0) { return; } if (enable) { k_timer_start(&cpu_load_timer, K_MSEC(CONFIG_CPU_LOAD_LOG_PERIODICALLY), K_MSEC(CONFIG_CPU_LOAD_LOG_PERIODICALLY)); } else { k_timer_stop(&cpu_load_timer); } } int cpu_load_cb_reg(cpu_load_cb_t cb, uint8_t threshold_percent) { if (threshold_percent > 100) { return -EINVAL; } cpu_load_threshold_percent = threshold_percent; load_cb = cb; return 0; } #if CONFIG_CPU_LOAD_USE_COUNTER || CONFIG_CPU_LOAD_LOG_PERIODICALLY static int cpu_load_init(void) { if (IS_ENABLED(CONFIG_CPU_LOAD_USE_COUNTER)) { int err = counter_start(counter); (void)err; __ASSERT_NO_MSG(err == 0); } if (CONFIG_CPU_LOAD_LOG_PERIODICALLY > 0) { k_timer_start(&cpu_load_timer, K_MSEC(CONFIG_CPU_LOAD_LOG_PERIODICALLY), K_MSEC(CONFIG_CPU_LOAD_LOG_PERIODICALLY)); } return 0; } SYS_INIT(cpu_load_init, POST_KERNEL, 0); #endif void cpu_load_on_enter_idle(void) { if (IS_ENABLED(CONFIG_CPU_LOAD_USE_COUNTER)) { counter_get_value(counter, &enter_ts); return; } enter_ts = k_cycle_get_32(); } void cpu_load_on_exit_idle(void) { uint32_t now; if (IS_ENABLED(CONFIG_CPU_LOAD_USE_COUNTER)) { counter_get_value(counter, &now); } else { now = k_cycle_get_32(); } ticks_idle += now - enter_ts; } int cpu_load_get(bool reset) { uint64_t idle_us; uint64_t now = IS_ENABLED(CONFIG_TIMER_HAS_64BIT_CYCLE_COUNTER) ? k_cycle_get_64() : k_cycle_get_32(); uint64_t total = now - cyc_start; uint64_t total_us = k_cyc_to_us_floor64(total); uint32_t res; uint64_t active_us; if (IS_ENABLED(CONFIG_CPU_LOAD_USE_COUNTER)) { if (ticks_idle > (uint64_t)UINT32_MAX) { return -ERANGE; } idle_us = counter_ticks_to_us(counter, (uint32_t)ticks_idle); } else { idle_us = k_cyc_to_us_floor64(ticks_idle); } idle_us = MIN(idle_us, total_us); active_us = total_us - idle_us; res = (uint32_t)((1000 * active_us) / total_us); if (reset) { cyc_start = now; ticks_idle = 0; } return res; }