/* * Copyright (c) 2011-2014 Wind River Systems, Inc. * Copyright (c) 2020 Stephanos Ioannidis * * SPDX-License-Identifier: Apache-2.0 */ /* * @file * @brief pi computation portion of FPU sharing test * * @ingroup kernel_fpsharing_tests * * This module is used for the FPU sharing test, and supplements the basic * load/store test by incorporating two additional threads that utilize the * floating point unit. * * Testing utilizes a pair of tasks that independently compute pi. The lower * priority task is regularly preempted by the higher priority task, thereby * testing whether floating point context information is properly preserved. * * The following formula is used to compute pi: * * pi = 4 * (1 - 1/3 + 1/5 - 1/7 + 1/9 - ... ) * * This series converges to pi very slowly. For example, performing 50,000 * iterations results in an accuracy of 3 decimal places. * * A reference value of pi is computed once at the start of the test. All * subsequent computations must produce the same value, otherwise an error * has occurred. */ #include #include "float_context.h" #include "test_common.h" #ifdef CONFIG_CPU_HAS_FPU_DOUBLE_PRECISION #define FP_TYPE double #define FP_CONSTANT(x) x #else #define FP_TYPE float #define FP_CONSTANT(x) x##f #endif /* * PI_NUM_ITERATIONS: This macro is defined in the project's Makefile and * is configurable from the command line. */ static FP_TYPE reference_pi = FP_CONSTANT(0.0); /* * Test counters are "volatile" because GCC wasn't properly updating * calc_pi_low_count properly when calculate_pi_low() contained a "return" * in its error handling logic -- the value was incremented in a register, * but never written back to memory. (Seems to be a compiler bug!) */ static volatile unsigned int calc_pi_low_count; static volatile unsigned int calc_pi_high_count; /* Indicates that the load/store test exited */ static volatile bool test_exited; /* Semaphore for signaling end of test */ static K_SEM_DEFINE(test_exit_sem, 0, 1); /** * @brief Entry point for the low priority pi compute task * * @ingroup kernel_fpsharing_tests */ static void calculate_pi_low(void) { volatile FP_TYPE pi; /* volatile to avoid optimizing out of loop */ FP_TYPE divisor = FP_CONSTANT(3.0); FP_TYPE sign = FP_CONSTANT(-1.0); unsigned int ix; /* Loop until the test finishes, or an error is detected. */ for (calc_pi_low_count = 0; !test_exited; calc_pi_low_count++) { sign = FP_CONSTANT(-1.0); pi = FP_CONSTANT(1.0); divisor = FP_CONSTANT(3.0); for (ix = 0; ix < PI_NUM_ITERATIONS; ix++) { pi += sign / divisor; divisor += FP_CONSTANT(2.0); sign *= FP_CONSTANT(-1.0); } pi *= FP_CONSTANT(4.0); if (reference_pi == FP_CONSTANT(0.0)) { reference_pi = pi; } else if (reference_pi != pi) { printf("Computed pi %1.6f, reference pi %1.6f\n", (double)pi, (double)reference_pi); } zassert_equal(reference_pi, pi, "pi computation error"); } } /** * @brief Entry point for the high priority pi compute task * * @ingroup kernel_fpsharing_tests */ static void calculate_pi_high(void) { volatile FP_TYPE pi; /* volatile to avoid optimizing out of loop */ FP_TYPE divisor = FP_CONSTANT(3.0); FP_TYPE sign = FP_CONSTANT(-1.0); unsigned int ix; /* Run the test until the specified maximum test count is reached */ for (calc_pi_high_count = 0; calc_pi_high_count <= MAX_TESTS; calc_pi_high_count++) { sign = FP_CONSTANT(-1.0); pi = FP_CONSTANT(1.0); divisor = FP_CONSTANT(3.0); for (ix = 0; ix < PI_NUM_ITERATIONS; ix++) { pi += sign / divisor; divisor += FP_CONSTANT(2.0); sign *= FP_CONSTANT(-1.0); } /* * Relinquish the processor for the remainder of the current * system clock tick, so that lower priority threads get a * chance to run. * * This exercises the ability of the kernel to restore the * FPU state of a low priority thread _and_ the ability of the * kernel to provide a "clean" FPU state to this thread * once the sleep ends. */ k_sleep(K_MSEC(10)); pi *= FP_CONSTANT(4.0); if (reference_pi == FP_CONSTANT(0.0)) { reference_pi = pi; } else if (reference_pi != pi) { printf("Computed pi %1.6f, reference pi %1.6f\n", (double)pi, (double)reference_pi); } zassert_equal(reference_pi, pi, "pi computation error"); /* Periodically issue progress report */ if ((calc_pi_high_count % 100) == 50) { printf("Pi calculation OK after %u (high) +" " %u (low) tests (computed %1.6lf)\n", calc_pi_high_count, calc_pi_low_count, (double)pi); } } /* Signal end of test */ test_exited = true; k_sem_give(&test_exit_sem); } K_THREAD_DEFINE(pi_low, THREAD_STACK_SIZE, calculate_pi_low, NULL, NULL, NULL, THREAD_LOW_PRIORITY, THREAD_FP_FLAGS, K_TICKS_FOREVER); K_THREAD_DEFINE(pi_high, THREAD_STACK_SIZE, calculate_pi_high, NULL, NULL, NULL, THREAD_HIGH_PRIORITY, THREAD_FP_FLAGS, K_TICKS_FOREVER); ZTEST(fpu_sharing_generic, test_pi) { /* Initialise test states */ test_exited = false; k_sem_reset(&test_exit_sem); /* Start test threads */ k_thread_start(pi_low); k_thread_start(pi_high); /* Wait for test threads to exit */ k_sem_take(&test_exit_sem, K_FOREVER); }