Linux Audio

Check our new training course

Loading...
/*
 * 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 <zephyr/irq.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);