blob: 27262031dbcb67cd27fb3db7c4cc15faba3bce88 [file] [log] [blame]
/*
* Copyright (c) 2024, Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/debug/cpu_load.h>
#include <zephyr/kernel.h>
#include <zephyr/drivers/counter.h>
#include <zephyr/logging/log.h>
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;
}