blob: ec36fcce657f3118ac9199570029e3f2b1583144 [file] [log] [blame]
/*
* Copyright (c) 2020 Oticon A/S
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/ztest.h>
#include <zephyr/kernel.h>
#include <zephyr/sys/printk.h>
#include <stdlib.h>
#include <stdio.h>
#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
*/
ZTEST(native_cpu_hold, test_cpu_hold_basic)
{
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
*/
ZTEST(native_cpu_hold, test_cpu_hold_with_another_thread)
{
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
*/
ZTEST(native_cpu_hold, test_cpu_hold_with_interrupts)
{
#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) */
}
ZTEST_SUITE(native_cpu_hold, NULL, NULL, NULL, NULL, NULL);