Skip to content
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

testsuite: Add busy simulator #36662

Merged
merged 2 commits into from
Jul 29, 2021
Merged
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
20 changes: 20 additions & 0 deletions dts/bindings/test/vnd,busy-sim.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Copyright (c) 2021 Nordic Semiconductor ASA
# SPDX-License-Identifier: Apache-2.0

description: |
CPU load simulator

compatible: "vnd,busy-sim"

properties:
counter:
type: phandle
required: true
description: |
Counter device used for generating intervals.

active-gpios:
type: phandle-array
required: false
description: |
Debug pin is set to active state when cpu load is active.
2 changes: 2 additions & 0 deletions subsys/testsuite/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,5 @@ zephyr_include_directories_ifdef(CONFIG_TEST
${ZEPHYR_BASE}/subsys/testsuite/include
)
add_subdirectory_ifdef(CONFIG_COVERAGE_GCOV coverage)

zephyr_library_sources_ifdef(CONFIG_TEST_BUSY_SIM busy_sim/busy_sim.c)
11 changes: 11 additions & 0 deletions subsys/testsuite/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -144,4 +144,15 @@ config TEST_ARM_CORTEX_M
the testing suite to utilize these exceptions, in tests.
Note that by default, when building with ARM_SECURE_FIRMWARE
set, these exceptions are set to target the Non-Secure state.

config TEST_BUSY_SIM
bool "Enable busy simulator"
depends on TEST
select ENTROPY_GENERATOR
select RING_BUFFER
select COUNTER
help
It simulates cpu load by using counter device to generate interrupts
with random intervals and random busy looping in the interrupt.

endmenu
194 changes: 194 additions & 0 deletions subsys/testsuite/busy_sim/busy_sim.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
/*
* Copyright (c) 2021 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <drivers/entropy.h>
#include <drivers/counter.h>
#include <drivers/gpio.h>
#include "busy_sim.h"
#include <sys/ring_buffer.h>

#define BUFFER_SIZE 32

struct busy_sim_data {
uint32_t idle_avg;
uint32_t active_avg;
uint16_t idle_delta;
uint16_t active_delta;
uint32_t us_tick;
struct counter_alarm_cfg alarm_cfg;
struct k_work work;
struct ring_buf rnd_rbuf;
uint8_t rnd_buf[BUFFER_SIZE];
};

struct busy_sim_config {
const struct device *entropy;
const struct device *counter;
struct gpio_dt_spec pin_spec;
};

#define DT_BUSY_SIM DT_COMPAT_GET_ANY_STATUS_OKAY(vnd_busy_sim)

BUILD_ASSERT(DT_NUM_INST_STATUS_OKAY(vnd_busy_sim) == 1,
"add exactly one vnd,busy-sim node to the devicetree");

static const struct busy_sim_config sim_config = {
.entropy = DEVICE_DT_GET(DT_CHOSEN(zephyr_entropy)),
.counter = DEVICE_DT_GET(DT_PHANDLE(DT_BUSY_SIM, counter)),
.pin_spec = GPIO_DT_SPEC_GET_OR(DT_BUSY_SIM, active_gpios, {0}),
};

static struct busy_sim_data sim_data;
static const struct device *busy_sim_dev = DEVICE_DT_GET_ONE(vnd_busy_sim);

static inline struct busy_sim_data *get_dev_data(const struct device *dev)
{
return dev->data;
}

static inline const struct busy_sim_config *get_dev_config(const struct device *dev)
{
return dev->config;
}

static void rng_pool_work_handler(struct k_work *work)
{
uint8_t *buf;
uint32_t len;
struct busy_sim_data *data = get_dev_data(busy_sim_dev);
const struct busy_sim_config *config = get_dev_config(busy_sim_dev);

len = ring_buf_put_claim(&data->rnd_rbuf, &buf, BUFFER_SIZE - 1);
if (len) {
int err = entropy_get_entropy(config->entropy, buf, len);

if (err == 0) {
ring_buf_put_finish(&data->rnd_rbuf, len);
return;
}
}

k_work_submit(work);
}


static uint32_t get_timeout(bool idle)
{
struct busy_sim_data *data = get_dev_data(busy_sim_dev);
uint32_t avg = idle ? data->idle_avg : data->active_avg;
uint32_t delta = idle ? data->idle_delta : data->active_delta;
uint16_t rand_val;
uint32_t len;

len = ring_buf_get(&data->rnd_rbuf, (uint8_t *)&rand_val, sizeof(rand_val));
if (len < sizeof(rand_val)) {
k_work_submit(&data->work);
rand_val = 0;
}

avg *= data->us_tick;
delta *= data->us_tick;

return avg - delta + 2 * (rand_val % delta);
}

static void counter_alarm_callback(const struct device *dev,
uint8_t chan_id, uint32_t ticks,
void *user_data)
{
int err;
const struct busy_sim_config *config = get_dev_config(busy_sim_dev);
struct busy_sim_data *data = get_dev_data(busy_sim_dev);

data->alarm_cfg.ticks = get_timeout(true);

if (config->pin_spec.port) {
err = gpio_pin_set_dt(&config->pin_spec, 1);
__ASSERT_NO_MSG(err >= 0);
}

/* Busy loop */
k_busy_wait(get_timeout(false) / data->us_tick);

if (config->pin_spec.port) {
err = gpio_pin_set_dt(&config->pin_spec, 0);
__ASSERT_NO_MSG(err >= 0);
}

err = counter_set_channel_alarm(config->counter, 0, &data->alarm_cfg);
__ASSERT_NO_MSG(err == 0);

}

void busy_sim_start(uint32_t active_avg, uint32_t active_delta,
uint32_t idle_avg, uint32_t idle_delta)
{
int err;
const struct busy_sim_config *config = get_dev_config(busy_sim_dev);
struct busy_sim_data *data = get_dev_data(busy_sim_dev);

data->active_avg = active_avg;
data->active_delta = active_delta;
data->idle_avg = idle_avg;
data->idle_delta = idle_delta;

err = k_work_submit(&data->work);
__ASSERT_NO_MSG(err >= 0);

data->alarm_cfg.ticks = counter_us_to_ticks(config->counter, 100);
err = counter_set_channel_alarm(config->counter, 0, &data->alarm_cfg);
__ASSERT_NO_MSG(err == 0);

err = counter_start(config->counter);
__ASSERT_NO_MSG(err == 0);
}

void busy_sim_stop(void)
{
int err;
const struct busy_sim_config *config = get_dev_config(busy_sim_dev);
struct busy_sim_data *data = get_dev_data(busy_sim_dev);

k_work_cancel(&data->work);
err = counter_stop(config->counter);
__ASSERT_NO_MSG(err == 0);
}

static int busy_sim_init(const struct device *dev)
{
uint32_t freq;
const struct busy_sim_config *config = get_dev_config(dev);
struct busy_sim_data *data = get_dev_data(dev);

if ((config->pin_spec.port && !device_is_ready(config->pin_spec.port)) ||
!device_is_ready(config->counter) || !device_is_ready(config->entropy)) {
__ASSERT(0, "Devices needed by busy simulator not ready.");
return -EIO;
}

if (config->pin_spec.port) {
gpio_pin_configure_dt(&config->pin_spec, GPIO_OUTPUT | GPIO_OUTPUT_INIT_LOW);
}

freq = counter_get_frequency(config->counter);
if (freq < 1000000) {
__ASSERT(0, "Counter device has too low frequency for busy simulator.");
return -EINVAL;
}

k_work_init(&data->work, rng_pool_work_handler);
ring_buf_init(&data->rnd_rbuf, BUFFER_SIZE, data->rnd_buf);

data->us_tick = freq / 1000000;
data->alarm_cfg.callback = counter_alarm_callback;
data->alarm_cfg.flags = COUNTER_ALARM_CFG_EXPIRE_WHEN_LATE;

return 0;
}

DEVICE_DT_DEFINE(DT_BUSY_SIM, busy_sim_init, NULL,
&sim_data, &sim_config,
APPLICATION, CONFIG_APPLICATION_INIT_PRIORITY,
NULL);
29 changes: 29 additions & 0 deletions subsys/testsuite/include/busy_sim.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* Copyright (c) 2021 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef BUSY_SIM_H__
#define BUSY_SIM_H__

/**
* @brief Start busy simulator.
*
* When started, it is using counter device to generate interrupts at random
* intervals and busy loop for random period of time in that interrupt. Interrupt
* source and priority is configured in the devicetree. Default entropy source
* is used for getting random numbers. System work queue is used to get random
* values and keep them in a ring buffer.
*
* @param active_avg Avarage time of busy looping in the counter callback (in microseconds).
* @param active_delta Specifies deviation from avarage time of busy looping (in microseconds).
* @param idle_avg Avarage time of counter alarm timeout (in microseconds).
* @param idle_delta Specifies deviation from avarage time of counter alarm (in microseconds).
*/
void busy_sim_start(uint32_t active_avg, uint32_t active_delta,
uint32_t idle_avg, uint32_t idle_delta);
Comment on lines +23 to +24
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some descriptions of these parameters would be helpful.


/** @brief Stop busy simulator. */
void busy_sim_stop(void);

#endif /* BUSY_SIM_H__ */
11 changes: 11 additions & 0 deletions tests/ztest/busy_sim/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# SPDX-License-Identifier: Apache-2.0

# SPDX-License-Identifier: Apache-2.0

cmake_minimum_required(VERSION 3.13.1)
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(uart_basic_api)

target_sources(app PRIVATE
src/main.c
)
1 change: 1 addition & 0 deletions tests/ztest/busy_sim/boards/nrf52840dk_nrf52840.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
CONFIG_COUNTER_TIMER0=y
18 changes: 18 additions & 0 deletions tests/ztest/busy_sim/boards/nrf52840dk_nrf52840.overlay
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/*
* Copyright (c) 2021 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/

&timer0 {
status = "okay";
interrupts = <8 0>;
};

/ {
busy-sim {
compatible = "vnd,busy-sim";
status = "okay";
counter = <&timer0>;
};
};
19 changes: 19 additions & 0 deletions tests/ztest/busy_sim/boards/nrf52840dk_nrf52840_pin.overlay
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/*
* Copyright (c) 2021 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/

&timer0 {
status = "okay";
interrupts = <8 0>;
};

/ {
busy-sim {
compatible = "vnd,busy-sim";
status = "okay";
counter = <&timer0>;
active-gpios = <&gpio0 27 GPIO_ACTIVE_HIGH>;
};
};
3 changes: 3 additions & 0 deletions tests/ztest/busy_sim/prj.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
CONFIG_ZTEST=y
CONFIG_TEST_BUSY_SIM=y
CONFIG_ZTEST_THREAD_PRIORITY=1
51 changes: 51 additions & 0 deletions tests/ztest/busy_sim/src/main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
* Copyright (c) 2021 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/

#include <ztest.h>
#include <busy_sim.h>

static void test_busy_sim(void)
{
uint32_t ms = 1000;
uint32_t delta = 80;
uint32_t busy_ms;
uint32_t t = k_uptime_get_32();

k_busy_wait(1000 * ms);
t = k_uptime_get_32() - t;

zassert_true((t > (ms - delta)) && (t < (ms + delta)), NULL);

/* Start busy simulator and check that k_busy_wait last longer */
t = k_uptime_get_32();
busy_sim_start(500, 200, 1000, 400);
k_busy_wait(1000 * ms);
t = k_uptime_get_32() - t;
busy_ms = (3 * ms) / 2;

busy_sim_stop();
/* due to clock imprecision, randomness and addtional cpu load overhead
* expected time range is increased.
*/
zassert_true((t > (busy_ms - 2 * delta)) && (t < (busy_ms + 4 * delta)),
"expected in range: %d-%d, k_busy_wait lasted %d",
busy_ms - 2 * delta, busy_ms + 4 * delta, t);

/* Check that k_busy_wait is not interrupted after busy_sim_stop. */
t = k_uptime_get_32();
k_busy_wait(1000 * ms);
t = k_uptime_get_32() - t;
zassert_true((t > (ms - delta)) && (t < (ms + delta)), NULL);
}

void test_main(void)
{
ztest_test_suite(busy_sim_tests,
ztest_unit_test(test_busy_sim)
);

ztest_run_test_suite(busy_sim_tests);
}
9 changes: 9 additions & 0 deletions tests/ztest/busy_sim/testcase.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
common:
tags: test_framework
depends_on: counter
tests:
testing.ztest.busy_sim:
platform_allow: nrf52840dk_nrf52840
testing.ztest.busy_sim_nrf52840dk_pin:
platform_allow: nrf52840dk_nrf52840
extra_args: DTC_OVERLAY_FILE=boards/nrf52840dk_nrf52840_pin.overlay