/* * Copyright (c) 2011-2014 Wind River Systems, Inc. * * SPDX-License-Identifier: Apache-2.0 */ /* * @file * pi computation portion of FPU sharing test * * 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 #include #include "float_context.h" /* * PI_NUM_ITERATIONS: This macro is defined in the project's Makefile and * is configurable from the command line. */ static double reference_pi = 0.0f; /* * 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; /** * * @brief Entry point for the low priority pi compute task * * @ingroup kernel_fpsharing_tests */ void calculate_pi_low(void) { volatile double pi; /* volatile to avoid optimizing out of loop */ double divisor = 3.0; double sign = -1.0; unsigned int ix; /* loop forever, unless an error is detected */ while (1) { sign = -1.0; pi = 1.0; divisor = 3.0; for (ix = 0U; ix < PI_NUM_ITERATIONS; ix++) { pi += sign / divisor; divisor += 2.0; sign *= -1.0; } pi *= 4; if (reference_pi == 0.0f) { reference_pi = pi; } else if (reference_pi != pi) { TC_ERROR("Computed pi %1.6f, reference pi %1.6f\n", pi, reference_pi); fpu_sharing_error = 1; return; } ++calc_pi_low_count; } } /** * * @brief Entry point for the high priority pi compute task * * @ingroup kernel_fpsharing_tests */ void calculate_pi_high(void) { volatile double pi; /* volatile to avoid optimizing out of loop */ double divisor = 3.0; double sign = -1.0; unsigned int ix; /* loop forever, unless an error is detected */ while (1) { sign = -1.0; pi = 1.0; divisor = 3.0; for (ix = 0U; ix < PI_NUM_ITERATIONS; ix++) { pi += sign / divisor; divisor += 2.0; sign *= -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(10); pi *= 4; if (reference_pi == 0.0f) { reference_pi = pi; } else if (reference_pi != pi) { TC_ERROR("Computed pi %1.6f, reference pi %1.6f\n", pi, reference_pi); fpu_sharing_error = 1; return; } /* periodically issue progress report */ if ((++calc_pi_high_count % 100) == 50U) { PRINT_DATA("Pi calculation OK after %u (high) +" " %u (low) tests (computed %1.6f)\n", calc_pi_high_count, calc_pi_low_count, pi); } } }