From fe516b93a9f1eb58688adfbc67f74f02101e3dba Mon Sep 17 00:00:00 2001 From: Alberto Escolar Piedras Date: Thu, 10 Dec 2020 13:23:29 +0100 Subject: [PATCH] board: native_posix: Add test for k_busy_wait and cpu_hold Add a new test for k_busy_wait and cpu_hold Signed-off-by: Alberto Escolar Piedras Signed-off-by: Wolfgang Puffitsch --- drivers/timer/native_posix_timer.c | 8 + .../native_posix/cpu_wait/CMakeLists.txt | 8 + tests/boards/native_posix/cpu_wait/prj.conf | 3 + tests/boards/native_posix/cpu_wait/src/main.c | 301 ++++++++++++++++++ .../native_posix/cpu_wait/testcase.yaml | 4 + 5 files changed, 324 insertions(+) create mode 100644 tests/boards/native_posix/cpu_wait/CMakeLists.txt create mode 100644 tests/boards/native_posix/cpu_wait/prj.conf create mode 100644 tests/boards/native_posix/cpu_wait/src/main.c create mode 100644 tests/boards/native_posix/cpu_wait/testcase.yaml diff --git a/drivers/timer/native_posix_timer.c b/drivers/timer/native_posix_timer.c index 0c38af08043..68093bffab0 100644 --- a/drivers/timer/native_posix_timer.c +++ b/drivers/timer/native_posix_timer.c @@ -47,6 +47,14 @@ static void np_timer_isr(const void *arg) z_clock_announce(elapsed_ticks); } +/** + * This function exists only to enable tests to call into the timer ISR + */ +void np_timer_isr_test_hook(const void *arg) +{ + np_timer_isr(NULL); +} + /* * @brief Initialize system timer driver * diff --git a/tests/boards/native_posix/cpu_wait/CMakeLists.txt b/tests/boards/native_posix/cpu_wait/CMakeLists.txt new file mode 100644 index 00000000000..bcaa9bc56ea --- /dev/null +++ b/tests/boards/native_posix/cpu_wait/CMakeLists.txt @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.13.1) + +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(cpu_wait) + +target_sources(app PRIVATE src/main.c) diff --git a/tests/boards/native_posix/cpu_wait/prj.conf b/tests/boards/native_posix/cpu_wait/prj.conf new file mode 100644 index 00000000000..868a51c52b0 --- /dev/null +++ b/tests/boards/native_posix/cpu_wait/prj.conf @@ -0,0 +1,3 @@ +CONFIG_ZTEST=y +CONFIG_TICKLESS_KERNEL=n +CONFIG_ZTEST_THREAD_PRIORITY=1 diff --git a/tests/boards/native_posix/cpu_wait/src/main.c b/tests/boards/native_posix/cpu_wait/src/main.c new file mode 100644 index 00000000000..95b5ef7f91c --- /dev/null +++ b/tests/boards/native_posix/cpu_wait/src/main.c @@ -0,0 +1,301 @@ +/* + * Copyright (c) 2020 Oticon A/S + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include + +#include +#include +#include "board_soc.h" + +/** + * @brief Basic test of the POSIX arch k_busy_wait() and cpu_hold() + * functions + * + * In this basic case, only one k_busy_wait() or posix_cpu_hold executes + * at a time + */ +static void test_cpu_hold_basic(void) +{ + uint32_t wait_times[] = {1, 30, 0, 121, 10000}; + uint64_t time2, time1 = posix_get_hw_cycle(); + + for (int i = 0; i < ARRAY_SIZE(wait_times); i++) { + k_busy_wait(wait_times[i]); + time2 = posix_get_hw_cycle(); + zassert_true(time2 - time1 == wait_times[i], + "k_busy_wait failed " + PRIu64"-"PRIu64"!="PRIu32"\n", + time2, time1, wait_times[i]); + time1 = time2; + } + + for (int i = 0; i < ARRAY_SIZE(wait_times); i++) { + posix_cpu_hold(wait_times[i]); + time2 = posix_get_hw_cycle(); + zassert_true(time2 - time1 == wait_times[i], + "posix_cpu_hold failed " + PRIu64"-"PRIu64"!="PRIu32"\n", + time2, time1, wait_times[i]); + time1 = time2; + } +} + +#define WASTED_TIME 1000 /* 1ms */ +#define THREAD_PRIO 0 +#define THREAD_DELAY 0 +/* Note: the duration of WASTED_TIME and thread priorities should not be changed + * without thought, as they do matter for the test + */ + +#define ONE_TICK_TIME (1000000ul / CONFIG_SYS_CLOCK_TICKS_PER_SEC) +#define TWO_TICKS_TIME (2*1000000ul / CONFIG_SYS_CLOCK_TICKS_PER_SEC) +#define ONE_AND_HALF_TICKS (ONE_TICK_TIME + (ONE_TICK_TIME>>1)) +#define TWO_AND_HALF_TICKS ((ONE_TICK_TIME<<1) + (ONE_TICK_TIME>>1)) + +#if (WASTED_TIME > ONE_TICK_TIME/2) +#error "This test will not work with this system tick period" +#endif + +static void thread_entry(void *p1, void *p2, void *p3); + +K_THREAD_DEFINE(TIME_WASTER, CONFIG_ARCH_POSIX_RECOMMENDED_STACK_SIZE, + thread_entry, 0, 0, 0, + THREAD_PRIO, 0, THREAD_DELAY); +K_SEM_DEFINE(start_sema, 0, 1); +K_SEM_DEFINE(end_sema, 0, 1); + +/** + * Thread meant to come up and waste time during the k_busy_wait() and + * posix_cpu_hold() calls of test_cpu_hold_with_another_thread() + */ +static void thread_entry(void *p1, void *p2, void *p3) +{ + int i; + + ARG_UNUSED(p1); + ARG_UNUSED(p2); + ARG_UNUSED(p3); + + for (i = 0; i < 4; i++) { + /* Synchronize start of subtest with test thread */ + k_sem_take(&start_sema, K_FOREVER); + /* Sleep until next tick + * This sleep will take 2 ticks as the semaphore will + * be given just after the previous tick boundary + */ + k_sleep(Z_TIMEOUT_TICKS(1)); + /* Waste time */ + k_busy_wait(WASTED_TIME); + /* Synchronize end of subtest with test thread */ + k_sem_give(&end_sema); + } +} + +/** + * @brief Test the POSIX arch k_busy_wait and cpu_hold while another thread + * takes time during this test thread waits + * + * Note: This test relies on the exact timing of the ticks. + * For native_posix it works, with a tick of 10ms. In general this test will + * probably give problems if the tick time is not a relatively even number + * of microseconds + */ +static void test_cpu_hold_with_another_thread(void) +{ + uint64_t time2, time1; + + /* k_busy_wait part: */ + + k_sleep(Z_TIMEOUT_TICKS(1)); /* Wait until tick boundary */ + k_sem_give(&start_sema); + + time1 = posix_get_hw_cycle(); + k_busy_wait(TWO_TICKS_TIME + 1); + /* The thread should have switched in and have used + * WASTED_TIME us (1ms) right after 2*one_tick_time + * As that is longer than 2 ticks + 1us, the total + * should be 2 ticks + WASTED_TIME + */ + time2 = posix_get_hw_cycle(); + + zassert_true(time2 - time1 == TWO_TICKS_TIME + WASTED_TIME, + "k_busy_wait failed " + PRIu64"-"PRIu64"!="PRIu32"\n", + time2, time1, TWO_TICKS_TIME + WASTED_TIME); + + k_sem_take(&end_sema, K_FOREVER); + + k_sleep(Z_TIMEOUT_TICKS(1)); /* Wait until tick boundary */ + k_sem_give(&start_sema); + + time1 = posix_get_hw_cycle(); + k_busy_wait(TWO_AND_HALF_TICKS); + /* The thread should have used WASTED_TIME us (1ms) after + * 2*one_tick_time, but as that is lower than 2.5 ticks, in + * total the wait should be 2.5 ticks + */ + time2 = posix_get_hw_cycle(); + + zassert_true(time2 - time1 == TWO_AND_HALF_TICKS, + "k_busy_wait failed " + PRIu64"-"PRIu64"!="PRIu32"\n", + time2, time1, TWO_AND_HALF_TICKS); + + k_sem_take(&end_sema, K_FOREVER); + + /* CPU hold part: */ + + k_sleep(Z_TIMEOUT_TICKS(1)); /* Wait until tick boundary */ + k_sem_give(&start_sema); + + time1 = posix_get_hw_cycle(); + posix_cpu_hold(TWO_TICKS_TIME + 1); + /* The thread should have used WASTED_TIME us after 2*one_tick_time, + * so the total should be 2 ticks + WASTED_TIME + 1. + * That is we spend 2 ticks + 1 us in this context as requested. + */ + time2 = posix_get_hw_cycle(); + + zassert_true(time2 - time1 == TWO_TICKS_TIME + WASTED_TIME + 1, + "k_busy_wait failed " + PRIu64"-"PRIu64"!="PRIu32"\n", + time2, time1, TWO_TICKS_TIME + WASTED_TIME + 1); + + k_sem_take(&end_sema, K_FOREVER); + + k_sleep(Z_TIMEOUT_TICKS(1)); /* Wait until tick boundary */ + k_sem_give(&start_sema); + + time1 = posix_get_hw_cycle(); + posix_cpu_hold(TWO_AND_HALF_TICKS); + /* The thread should have used WASTED_TIME us after 2*one_tick_time, + * so the total wait should be 2.5 ticks + WASTED_TIME. + * That is 2.5 ticks in this context, and WASTED_TIME in the other + * thread context + */ + + time2 = posix_get_hw_cycle(); + + zassert_true(time2 - time1 == TWO_AND_HALF_TICKS + WASTED_TIME, + "k_busy_wait failed " + PRIu64"-"PRIu64"!="PRIu32"\n", + time2, time1, TWO_AND_HALF_TICKS + WASTED_TIME); + + k_sem_take(&end_sema, K_FOREVER); +} + +/** + * Replacement system tick timer interrupt handler which wastes time + * before calling the real one + */ +static void np_timer_isr_test_replacement(const void *arg) +{ + ARG_UNUSED(arg); + + k_busy_wait(WASTED_TIME); + + void np_timer_isr_test_hook(const void *arg); + np_timer_isr_test_hook(NULL); +} + +/** + * @brief Test posix arch k_busy_wait and cpu_hold with interrupts that take + * time. + * The test is timed so that interrupts arrive during the wait times. + * + * The kernel is configured as NOT-tickless, and the default tick period is + * 10ms + */ +static void test_cpu_hold_with_interrupts(void) +{ +#if defined(CONFIG_BOARD_NATIVE_POSIX) + /* So far we only have a test for native_posix. + * As the test hooks into an interrupt to cause an extra delay + * this is very platform specific + */ + uint64_t time2, time1; + + k_sleep(Z_TIMEOUT_TICKS(1)); /* Wait until a tick boundary */ + + IRQ_CONNECT(TIMER_TICK_IRQ, 1, np_timer_isr_test_replacement, 0, 0); + + time1 = posix_get_hw_cycle(); + k_busy_wait(ONE_TICK_TIME + 1); + /* Just after ONE_TICK_TIME (10ms) the timer interrupt has come, + * causing a delay of WASTED_TIME (1ms), so the k_busy_wait() + * returns immediately as it was waiting for 10.001 ms + */ + time2 = posix_get_hw_cycle(); + + zassert_true(time2 - time1 == ONE_TICK_TIME + WASTED_TIME, + "k_busy_wait failed " + PRIu64"-"PRIu64"!="PRIu32"\n", + time2, time1, ONE_TICK_TIME); + + + k_sleep(Z_TIMEOUT_TICKS(1)); /* Wait until tick boundary */ + + time1 = posix_get_hw_cycle(); + k_busy_wait(ONE_AND_HALF_TICKS); + /* Just after ONE_TICK_TIME (10ms) the timer interrupt has come, + * causing a delay of WASTED_TIME (1ms), after that, the k_busy_wait() + * continues until 15ms + */ + time2 = posix_get_hw_cycle(); + + zassert_true(time2 - time1 == ONE_AND_HALF_TICKS, + "k_busy_wait failed " + PRIu64"-"PRIu64"!="PRIu32"\n", + time2, time1, ONE_TICK_TIME); + + + + k_sleep(Z_TIMEOUT_TICKS(1)); /* Wait until tick boundary */ + + time1 = posix_get_hw_cycle(); + posix_cpu_hold(ONE_TICK_TIME + 1); + /* Just after ONE_TICK_TIME (10ms) the timer interrupt has come, + * causing a delay of WASTED_TIME (1ms), but posix_cpu_hold continues + * until it spends 10.001 ms in this context. That is 11.001ms in total + */ + time2 = posix_get_hw_cycle(); + + zassert_true(time2 - time1 == ONE_TICK_TIME + 1 + WASTED_TIME, + "k_busy_wait failed " + PRIu64"-"PRIu64"!="PRIu32"\n", + time2, time1, ONE_TICK_TIME); + + + k_sleep(Z_TIMEOUT_TICKS(1)); /* Wait until tick boundary */ + + time1 = posix_get_hw_cycle(); + posix_cpu_hold(ONE_AND_HALF_TICKS); + /* Just after ONE_TICK_TIME (10ms) the timer interrupt has come, + * causing a delay of WASTED_TIME (1ms), but posix_cpu_hold continues + * until it spends 15ms in this context. That is 16ms in total + */ + time2 = posix_get_hw_cycle(); + + zassert_true(time2 - time1 == ONE_AND_HALF_TICKS + WASTED_TIME, + "k_busy_wait failed " + PRIu64"-"PRIu64"!="PRIu32"\n", + time2, time1, ONE_TICK_TIME); + +#endif /* defined(CONFIG_BOARD_NATIVE_POSIX) */ +} + +void test_main(void) +{ + ztest_test_suite(native_cpu_hold, + ztest_unit_test(test_cpu_hold_basic), + ztest_unit_test(test_cpu_hold_with_another_thread), + ztest_unit_test(test_cpu_hold_with_interrupts) + ); + + ztest_run_test_suite(native_cpu_hold); +} diff --git a/tests/boards/native_posix/cpu_wait/testcase.yaml b/tests/boards/native_posix/cpu_wait/testcase.yaml new file mode 100644 index 00000000000..7915a22de1d --- /dev/null +++ b/tests/boards/native_posix/cpu_wait/testcase.yaml @@ -0,0 +1,4 @@ +# Test of the posix_arch k_busy_wait & cpu_hold() functionality +tests: + boards.native_posix.cpu_wait: + platform_allow: native_posix native_posix_64