diff --git a/include/sys/util.h b/include/sys/util.h index 872f3cbe2000..f76303442c5d 100644 --- a/include/sys/util.h +++ b/include/sys/util.h @@ -456,6 +456,31 @@ char *utf8_lcpy(char *dst, const char *src, size_t n); /** @brief Number of Hz in @p x MHz */ #define MHZ(x) (KHZ(x) * 1000) +/** + * @brief Wait for an expression to return true with a timeout + * + * Spin on an expression with a timeout and optional delay between iterations + * + * Commonly needed when waiting on hardware to complete an asynchronous + * request to read/write/initialize/reset, but useful for any expression. + * + * @param expr Truth expression upon which to poll, e.g.: XYZREG & XYZREG_EN + * @param timeout Timeout to wait for in microseconds, e.g.: 1000 (1ms) + * @param delay_stmt Delay statement to perform each poll iteration + * e.g.: NULL, k_yield(), k_msleep(1) or k_busy_wait(1) + * + * @retval expr As a boolean return, if false then it has timed out. + */ +#define wait_for(expr, timeout, delay_stmt) \ + ({ \ + uint32_t cycle_count = (sys_clock_hw_cycles_per_sec() / USEC_PER_SEC) * (timeout); \ + uint32_t start = k_cycle_get_32(); \ + while (!(expr) && (cycle_count > (k_cycle_get_32() - start))) { \ + delay_stmt; \ + } \ + (expr); \ + }) + /** * @} */ diff --git a/tests/lib/sys_util/CMakeLists.txt b/tests/lib/sys_util/CMakeLists.txt new file mode 100644 index 000000000000..9f335ab14f2e --- /dev/null +++ b/tests/lib/sys_util/CMakeLists.txt @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.20.0) +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(sys_util) + +target_sources(app PRIVATE + src/main.c + src/wait_for.c + ) diff --git a/tests/lib/sys_util/prj.conf b/tests/lib/sys_util/prj.conf new file mode 100644 index 000000000000..9467c2926896 --- /dev/null +++ b/tests/lib/sys_util/prj.conf @@ -0,0 +1 @@ +CONFIG_ZTEST=y diff --git a/tests/lib/sys_util/src/main.c b/tests/lib/sys_util/src/main.c new file mode 100644 index 000000000000..792541667bfa --- /dev/null +++ b/tests/lib/sys_util/src/main.c @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2022 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +extern void test_wait_for(void); + +/** + * @defgroup sys_util_tests Sys Util Tests + * @ingroup all_tests + * @{ + * @} + */ + +void test_main(void) +{ + ztest_test_suite(sys_util, ztest_unit_test(test_wait_for)); + ztest_run_test_suite(sys_util); +} diff --git a/tests/lib/sys_util/src/wait_for.c b/tests/lib/sys_util/src/wait_for.c new file mode 100644 index 000000000000..d2c9d5ddf5b7 --- /dev/null +++ b/tests/lib/sys_util/src/wait_for.c @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2022 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include + +/** + * @addtogroup sys_util_tests + * @{ + */ + +/** + * @brief Test wait_for works as expected with typical use cases + * + * @see wait_for() + */ +void test_wait_for(void) +{ + uint32_t start, end, expected; + + zassert_true(wait_for(true, 0, NULL), "true, no wait, NULL"); + zassert_true(wait_for(true, 0, k_yield()), "true, no wait, yield"); + zassert_false(wait_for(false, 0, k_yield()), "false, no wait, yield"); + zassert_true(wait_for(true, 1, k_yield()), "true, 1usec, yield"); + zassert_false(wait_for(false, 1, k_yield()), "false, 1usec, yield"); + zassert_true(wait_for(true, 1000, k_yield()), "true, 1msec, yield"); + + + expected = 1000*(sys_clock_hw_cycles_per_sec()/USEC_PER_SEC); + start = k_cycle_get_32(); + zassert_false(wait_for(false, 1000, k_yield()), "true, 1msec, yield"); + end = k_cycle_get_32(); + zassert_true(end-start >= expected, "wait for 1ms"); +} + +/** + * @} + */ diff --git a/tests/lib/sys_util/testcase.yaml b/tests/lib/sys_util/testcase.yaml new file mode 100644 index 000000000000..e0ce8317e939 --- /dev/null +++ b/tests/lib/sys_util/testcase.yaml @@ -0,0 +1,4 @@ +tests: + lib.sys_util: + platform_exclude: native_posix native_posix_64 + tags: sys_util