blob: 88598346b230259036051654f21a09d28a75cb62 [file] [log] [blame]
/* test_tickless.c - tickless idle tests */
/*
* Copyright (c) 2011, 2013-2014 Wind River Systems, Inc.
*
* SPDX-License-Identifier: Apache-2.0
*/
/* DESCRIPTION Unit test for tickless idle feature. */
#include <zephyr.h>
#include <ztest.h>
#include <misc/printk.h>
#include <arch/cpu.h>
#include <tc_util.h>
#if defined(CONFIG_ARCH_POSIX)
#include "posix_board_if.h"
#endif
#define STACKSIZE 4096
#define PRIORITY 6
#define SLEEP_TICKS 10
static struct k_thread thread_tickless;
static K_THREAD_STACK_DEFINE(thread_tickless_stack, STACKSIZE);
#ifdef CONFIG_TICKLESS_IDLE
/* This used to poke an internal kernel variable, which doesn't exit
* any more. It was never documented as an API, and the test never
* failed when it was removed. So just leave it here as a vestigial
* thing until the test gets reworked
*/
s32_t _sys_idle_threshold_ticks;
#endif
#define TICKS_TO_MS (MSEC_PER_SEC / CONFIG_SYS_CLOCK_TICKS_PER_SEC)
/* NOTE: Clock speed may change between platforms */
#define CAL_REPS 16 /* # of loops in timestamp calibration */
/*
* Arch-specific timer resolution/size types, definitions and
* timestamp routines.
*/
#if defined(CONFIG_X86) || defined(CONFIG_ARC) || defined(CONFIG_ARCH_POSIX)
typedef u64_t _timer_res_t;
#define _TIMER_ZERO 0ULL
/* timestamp routines */
#define _TIMESTAMP_OPEN()
#if defined(CONFIG_ARCH_POSIX)
#define _TIMESTAMP_READ() (posix_get_hw_cycle())
#else
#define _TIMESTAMP_READ() (z_tsc_read())
#endif
#define _TIMESTAMP_CLOSE()
#elif defined(CONFIG_ARM)
# if defined(CONFIG_SOC_TI_LM3S6965_QEMU)
/* A bug in the QEMU ARMv7-M sysTick timer prevents tickless idle support */
#error "This QEMU target does not support tickless idle!"
# endif
typedef u32_t _timer_res_t;
#define _TIMER_ZERO 0
/* timestamp routines, from timestamps.c */
extern void _timestamp_open(void);
extern u32_t _timestamp_read(void);
extern void _timestamp_close(void);
#define _TIMESTAMP_OPEN() (_timestamp_open())
#define _TIMESTAMP_READ() (_timestamp_read())
#define _TIMESTAMP_CLOSE() (_timestamp_close())
#else
#error "Unknown target"
#endif
void ticklessTestThread(void)
{
s32_t start_time;
s32_t end_time;
s32_t diff_time;
s32_t diff_ticks;
_timer_res_t start_tsc;
_timer_res_t end_tsc;
_timer_res_t cal_tsc = _TIMER_ZERO;
_timer_res_t diff_tsc = _TIMER_ZERO;
_timer_res_t diff_per;
#ifdef CONFIG_TICKLESS_IDLE
s32_t oldThreshold;
#endif
int i;
printk("Tickless Idle Test\n");
#ifndef CONFIG_TICKLESS_IDLE
printk("WARNING! Tickless idle support has not been enabled!\n");
#endif
printk("Calibrating TSC...\n");
#ifdef CONFIG_TICKLESS_IDLE
oldThreshold = _sys_idle_threshold_ticks;
/* make sure we do not enter tickless idle mode */
_sys_idle_threshold_ticks = 0x7FFFFFFF;
#endif
/* initialize the timer, if necessary */
_TIMESTAMP_OPEN();
for (i = 0; i < CAL_REPS; i++) {
/*
* Do a single tick sleep to get us as close to a tick boundary
* as we can.
*/
k_sleep(TICKS_TO_MS);
start_time = k_uptime_get_32();
start_tsc = _TIMESTAMP_READ();
/* FIXME: one tick less to account for
* one extra tick for _TICK_ALIGN in k_sleep
*/
k_sleep((SLEEP_TICKS - 1) * TICKS_TO_MS);
end_tsc = _TIMESTAMP_READ();
end_time = k_uptime_get_32();
cal_tsc += end_tsc - start_tsc;
}
cal_tsc /= CAL_REPS;
#if defined(CONFIG_X86) || defined(CONFIG_ARC)
printk("Calibrated time stamp period = 0x%x%x\n",
(u32_t)(cal_tsc >> 32), (u32_t)(cal_tsc & 0xFFFFFFFFLL));
#elif defined(CONFIG_ARCH_POSIX)
printk("Calibrated time stamp period = %llu\n", cal_tsc);
#elif defined(CONFIG_ARM)
printk("Calibrated time stamp period = 0x%x\n", cal_tsc);
#endif
printk("Do the real test with tickless enabled\n");
#ifdef CONFIG_TICKLESS_IDLE
_sys_idle_threshold_ticks = oldThreshold;
#endif
printk("Going idle for %d ticks...\n", SLEEP_TICKS);
for (i = 0; i < CAL_REPS; i++) {
/*
* Do a single tick sleep to get us as close to a tick boundary
* as we can.
*/
k_sleep(TICKS_TO_MS);
start_time = k_uptime_get_32();
start_tsc = _TIMESTAMP_READ();
/* FIXME: one tick less to account for
* one extra tick for _TICK_ALIGN in k_sleep
*/
k_sleep((SLEEP_TICKS - 1) * TICKS_TO_MS);
end_tsc = _TIMESTAMP_READ();
end_time = k_uptime_get_32();
diff_tsc += end_tsc - start_tsc;
}
diff_tsc /= CAL_REPS;
diff_time = (end_time - start_time);
/* Convert ms to ticks*/
diff_ticks = (diff_time / TICKS_TO_MS);
printk("start time : %d\n", start_time);
printk("end time : %d\n", end_time);
printk("diff time : %d\n", diff_time);
printk("diff ticks : %d\n", diff_ticks);
#if defined(CONFIG_X86) || defined(CONFIG_ARC)
printk("diff time stamp: 0x%x%x\n",
(u32_t)(diff_tsc >> 32), (u32_t)(diff_tsc & 0xFFFFFFFFULL));
printk("Cal time stamp: 0x%x%x\n",
(u32_t)(cal_tsc >> 32), (u32_t)(cal_tsc & 0xFFFFFFFFLL));
#elif defined(CONFIG_ARCH_POSIX)
printk("diff time stamp: %llu\n", diff_tsc);
printk("Cal time stamp: %llu\n", cal_tsc);
#elif defined(CONFIG_ARM) || defined(CONFIG_SOC_QUARK_SE_C1000_SS)
printk("diff time stamp: 0x%x\n", diff_tsc);
printk("Cal time stamp: 0x%x\n", cal_tsc);
#endif
/*
* Calculate percentage difference between
* calibrated TSC diff and measured result
*/
if (diff_tsc > cal_tsc) {
diff_per = ((diff_tsc - cal_tsc) * 100U) / cal_tsc;
} else {
diff_per = ((cal_tsc - diff_tsc) * 100U) / cal_tsc;
}
printk("variance in time stamp diff: %d percent\n", (s32_t)diff_per);
zassert_equal(diff_ticks, SLEEP_TICKS,
"* TEST FAILED. TICK COUNT INCORRECT *");
/* release the timer, if necessary */
_TIMESTAMP_CLOSE();
}
/**
* @brief Tests to verify tickless feature
*
* @defgroup kernel_tickless_tests Tickless
* @ingroup all_tests
* @{
*/
/**
* @brief Test to verify tickless functionality
*
* @details Test verifies tickless_idle and tickless
* functionality
*/
void test_tickless(void)
{
k_thread_create(&thread_tickless, thread_tickless_stack,
STACKSIZE,
(k_thread_entry_t) ticklessTestThread,
NULL, NULL, NULL,
PRIORITY, 0, K_NO_WAIT);
k_sleep(4000);
}
/**
* @}
*/
void test_main(void)
{
ztest_test_suite(tickless,
ztest_unit_test(test_tickless));
ztest_run_test_suite(tickless);
}