diff options
Diffstat (limited to 'testsuites/isvv/13_priority_inheritance/priority_inheritance.c')
-rw-r--r-- | testsuites/isvv/13_priority_inheritance/priority_inheritance.c | 449 |
1 files changed, 449 insertions, 0 deletions
diff --git a/testsuites/isvv/13_priority_inheritance/priority_inheritance.c b/testsuites/isvv/13_priority_inheritance/priority_inheritance.c new file mode 100644 index 0000000000..e8b4484744 --- /dev/null +++ b/testsuites/isvv/13_priority_inheritance/priority_inheritance.c @@ -0,0 +1,449 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ + +/* + * Copyright (C) 2022 Critical Software SA + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <rtems.h> +#include <rtems/bspIo.h> +#include "../shared/utils.h" +#include "../shared/isvv_rtems_aux.h" +#include <string.h> + +/** + * + * @brief Tests Priority Inheritance protocol of semaphores with two configurations: + * + * 1. Configured WITH Priority Inheritance + * One test case which creates a mutex WITH Priority Inheritance configured, + * and shows that our high priority task NEVER overruns the time in its critical region + * + * 2. Configured WITHOUT Priority Inheritance + * One test case which creates a mutex WITHOUT Priority Inheritance configured, + * and shows that our high priority task will FREQUENTLY overrun the time in its critical region + */ + +// Different periods for our periodic tasks needed for robustness and reliability as +// Execution is dependant on processor clock speed i.e 250 MHz for gr740 and 80 MHz for gr712rc +#ifdef gr740 +#define T0_PERIOD 400 +#define T1_PERIOD 200 +#else +#define T0_PERIOD 600 +#define T1_PERIOD 300 +#endif + +#ifdef PRIO_INHERIT +#define PRIO_PROTOCOL_SWITCH RTEMS_INHERIT_PRIORITY +#else +#define PRIO_PROTOCOL_SWITCH RTEMS_NO_INHERIT_PRIORITY +#endif + +#define MAX_ITER (256U) +#define FFT_SIZE (1024U) +#define FFT_SIZE_LOG2 (10U) +#define TC_MAX (16U) +#define FILTER_REPETITIONS (2U) +#define SNR_THRESHOLD (1000000.0f) + +const uint16_t FS = 48000U; +const uint16_t FREQ[TC_MAX] = {2500U, 1500U, 15000U, 500U, + 1000U, 800U, 440U, 8000U, + 100U, 3500U, 12345U, 1200U, + 20000U, 715U, 5000U, 4500U}; + +const float NOISE_FACTOR[TC_MAX] = {0.0001f, 0.2250f, 0.00068f, 0.30f, + 0.004f, 0.0123f, 0.0054f, 0.00054f, + 0.01f, 0.0325f, 0.012f, 0.00032f, + 0.075f, 0.0423f, 0.0354f, 0.00002f}; + +static float inSensorDataRe[FFT_SIZE]; +static float inSensorDataIm[FFT_SIZE]; + +#define ITOA_STR_SIZE (8 * sizeof(int) + 1) + +#define MAX_TLS_SIZE RTEMS_ALIGN_UP(64, RTEMS_TASK_STORAGE_ALIGNMENT) + +#define TASK_ATTRIBUTES RTEMS_FLOATING_POINT + +#define TASK_STORAGE_SIZE \ + RTEMS_TASK_STORAGE_SIZE( \ + MAX_TLS_SIZE + RTEMS_MINIMUM_STACK_SIZE, \ + TASK_ATTRIBUTES) + +#undef TEST_PROCESSORS +#define TEST_PROCESSORS 1 +#define TASK_COUNT 3 +#define EVENT_FINISHED_PROCESSING RTEMS_EVENT_0 + +#define T1_TUNE_REPS 50 +#define T2_COARSE_TUNING_REPS 2 +#define T2_FINE_TUNING_REPS 1300 + +typedef struct +{ + rtems_id main_task; + rtems_id task_ids[TASK_COUNT]; + rtems_id period_0_id; + rtems_id period_1_id; + int64_t t2_max_ticks; + uint16_t priority_inversion_occurrences; + float m1_data; + rtems_id m1_data_mutex; + float scaling_fft_factor; +} test_context; + +/* create storage areas for each worker, using task construct forces +the user to create these manually*/ +RTEMS_ALIGNED(RTEMS_TASK_STORAGE_ALIGNMENT) +static char task_storage[TASK_COUNT][TASK_STORAGE_SIZE]; + +// TASK 0 - (highest priority w.r.t. the other two) - a SC S/W task +// Checks data after request for recent evidence of solar flare +static void solar_flare_analysis(rtems_task_argument arg) +{ + test_context *ctx = (test_context *)arg; + + uint16_t flare_count = 0; + uint32_t start_time, end_time, time_in_critical_region; + + ctx->period_0_id = CreateRateMonotonic(); + + while (1) + { + WaitPeriod(ctx->period_0_id, T0_PERIOD); + + // Timer to see how long it takes to enter and exit critical region + start_time = rtems_clock_get_ticks_since_boot(); + ObtainMutex(ctx->m1_data_mutex); + + if (ctx->m1_data >= SNR_THRESHOLD) + flare_count++; + + ReleaseMutex(ctx->m1_data_mutex); + + end_time = rtems_clock_get_ticks_since_boot(); + time_in_critical_region = end_time - start_time; + + // If time spent in critical region is greater than bound for T2 then we know priority inversion has occurred + if (time_in_critical_region > ctx->t2_max_ticks) + ctx->priority_inversion_occurrences++; + } + + // Although this periodic task is executed ad infinitum it is good practice to finalize tasks + rtems_task_exit(); +} + +// Task 1 - (medium priority w.r.t. the other two) - a COMS task +// A communication task which periodically receives and has to decode a message +// To simulate this, we are running the lucas-lehmer primality test +static void decode_communications(rtems_task_argument arg) +{ + uint32_t start_time, end_time, elapsed_time; + test_context *ctx = (test_context *)arg; + + uint32_t res = 0; + + ctx->period_1_id = CreateRateMonotonic(); + + while (1) + { + WaitPeriod(ctx->period_1_id, T1_PERIOD); + + start_time = rtems_clock_get_ticks_since_boot(); + + for (uint16_t i = 0; i < T1_TUNE_REPS; i++) + { + for (uint8_t p = 2; p < 100; p++) + { + if (is_prime(p) && is_mersenne_prime(p)) + { + res = p; + } + } + } + + end_time = rtems_clock_get_ticks_since_boot(); + elapsed_time = end_time - start_time; + + // If the execution time is greater than the period, and hence implicit deadline, then + // this test cannot sufficiently be completed with the resources and parameters must be changed + if (elapsed_time >= T1_PERIOD) + print_string("[ERROR] Execution time of T1 is greater than period. Please reduce T1_TUNE_REPS \n"); + } + + (void)res; + // Although this periodic task is executed ad infinitum it is good practice to finalize tasks + rtems_task_exit(); +} + +// Computes the signal-to-noise ratio for our given data at a given position +static float compute_snr(float scaling_factor, uint32_t iter) +{ + for (uint32_t i = 0; i < FFT_SIZE; i++) + { + inSensorDataRe[i] = 0.4995f * cos_aprox(i * 2.0f * PI * FREQ[iter % TC_MAX] / FS) + (noise_generator(0) * NOISE_FACTOR[iter % TC_MAX]); + inSensorDataIm[i] = 0.4995f * sin_aprox(i * 2.0f * PI * FREQ[iter % TC_MAX] / FS) + (noise_generator(0) * NOISE_FACTOR[iter % TC_MAX]); + } + + for (uint32_t i = 0; i < FFT_SIZE; i++) + { + inSensorDataRe[i] = inSensorDataRe[i] * blackman_harris(i, FFT_SIZE) / scaling_factor; + inSensorDataIm[i] = inSensorDataIm[i] * blackman_harris(i, FFT_SIZE) / scaling_factor; + } + + fft(&inSensorDataRe[0], &inSensorDataIm[0], FFT_SIZE_LOG2); + + // Look for the peak Value and its index (no DC) + float maxValue = 0.0; + int32_t maxValueIndex = -1; + for (uint32_t i = 3; i < (FFT_SIZE / 2); i++) + { + if (inSensorDataRe[i] > maxValue) + { + maxValue = inSensorDataRe[i]; + maxValueIndex = i; + } + } + + // Calculates the signal and noise power (no DC) + float sig = 0.0f; + float noise = 0.0f; + + for (uint32_t i = 3; i < (FFT_SIZE / 2); i++) + { + if ((i > maxValueIndex - 8) && (i < maxValueIndex + 8)) + { + sig += (2 * inSensorDataRe[i]); + } + else + { + noise += (2 * inSensorDataRe[i]); + } + } + + if (!(sig > 0.0f)) + { + sig = 0.0000000001f; + } + if (!(noise > 0.0f)) + { + noise = 0.0000000001f; + } + + return sig / noise; +} + +// Task 2 - (lowest priority w.r.t. the other two) - a SC S/W task +// Gathers signal data from sensors, computes the signal to noise ratio, then writes to a shared buffer +static void gather_meteorological_data(rtems_task_argument arg) +{ + test_context *ctx = (test_context *)arg; + + uint32_t iter = 0; + + while (iter < MAX_ITER) + { + ObtainMutex(ctx->m1_data_mutex); + + ctx->m1_data = compute_snr(ctx->scaling_fft_factor, iter); + + ReleaseMutex(ctx->m1_data_mutex); + + iter++; + } + + print_string("Finished Processing Data \n"); + SendEvents(ctx->main_task, EVENT_FINISHED_PROCESSING); + rtems_task_exit(); +} + +static void Init(rtems_task_argument arg) +{ + (void)arg; + test_context ctx; + + char str[ITOA_STR_SIZE]; + + uint32_t t1_start_time, t1_end_time, t1_elapsed_time; + uint32_t t2_start_time, t2_end_time, t2_elapsed_time_in_critical_region; + + rtems_status_code sc; + sc = rtems_semaphore_create(rtems_build_name('M', '1', 'M', 'X'), 1, + RTEMS_BINARY_SEMAPHORE | RTEMS_PRIORITY | + PRIO_PROTOCOL_SWITCH, + 0, &ctx.m1_data_mutex); + ASSERT_SUCCESS(sc); + + // FFT Initialization + (void)memset(&inSensorDataRe[0], 0, FFT_SIZE); + (void)memset(&inSensorDataIm[0], 0, FFT_SIZE); + + // Calculates mean of Blackman-Harris terms + ctx.scaling_fft_factor = 0.0; + for (uint32_t i = 0; i < FFT_SIZE; i++) + { + ctx.scaling_fft_factor += blackman_harris(i, FFT_SIZE); + } + + ctx.scaling_fft_factor = ctx.scaling_fft_factor / ((float)FFT_SIZE); + + // Find length of time T1 to execute + uint32_t res = 0; + + t1_start_time = rtems_clock_get_ticks_since_boot(); + + for (uint16_t i = 0; i < T1_TUNE_REPS; i++) + { + for (uint8_t p = 2; p < 100; p++) + { + if (is_prime(p) && is_mersenne_prime(p)) + { + res = p; + } + } + } + + t1_end_time = rtems_clock_get_ticks_since_boot(); + t1_elapsed_time = t1_end_time - t1_start_time; + + print_string("[DEBUG] e(T1) is: "); + print_string(itoa(t1_elapsed_time, &str[0], 10)); + print_string("\n"); + + (void)res; + + // Find the amount of time it takes for T2 to complete its critical region on + // an empirical basis of the max, for any of our given data, to find the upper bound + // that T0 should wait, enabling us to detect priority inversion + ctx.t2_max_ticks = -1; + + for (uint8_t i = 0; i < TC_MAX; i++) + { + t2_start_time = rtems_clock_get_ticks_since_boot(); + + ObtainMutex(ctx.m1_data_mutex); + + ctx.m1_data = compute_snr(ctx.scaling_fft_factor, i); + + ReleaseMutex(ctx.m1_data_mutex); + + t2_end_time = rtems_clock_get_ticks_since_boot(); + t2_elapsed_time_in_critical_region = t2_end_time - t2_start_time; + + ctx.t2_max_ticks = MAX(ctx.t2_max_ticks, t2_elapsed_time_in_critical_region); + } + + print_string("[DEBUG] MAX eCR(T2) is: "); + print_string(itoa(ctx.t2_max_ticks, &str[0], 10)); + print_string("\n"); + + // Build up our task configs + rtems_task_config configs[TASK_COUNT]; + for (uint8_t i = 0; i < TASK_COUNT; i++) + { + configs[i] = (rtems_task_config){ + .name = rtems_build_name('T', 'S', 'K', (char)i), + .storage_size = TASK_STORAGE_SIZE, + .storage_area = &task_storage[i][0], + .maximum_thread_local_storage_size = MAX_TLS_SIZE, + .initial_modes = RTEMS_DEFAULT_MODES, + .attributes = RTEMS_FLOATING_POINT}; + } + + // Initialize relevant test variables + ctx.main_task = rtems_task_self(); + ctx.priority_inversion_occurrences = 0; + ctx.period_0_id = INVALID_ID; + ctx.period_1_id = INVALID_ID; + + // Explicitly set the priorities of our tasks + configs[2].initial_priority = PRIO_VERY_LOW; + ctx.task_ids[2] = DoCreateTask(configs[2]); + + configs[1].initial_priority = PRIO_NORMAL; + ctx.task_ids[1] = DoCreateTask(configs[1]); + + configs[0].initial_priority = PRIO_VERY_HIGH; + ctx.task_ids[0] = DoCreateTask(configs[0]); + + // Start Tasks + StartTask(ctx.task_ids[2], gather_meteorological_data, &ctx); + StartTask(ctx.task_ids[1], decode_communications, &ctx); + StartTask(ctx.task_ids[0], solar_flare_analysis, &ctx); + + // Blocking wait for relevant event until T2 is finished + ReceiveAllEvents(EVENT_FINISHED_PROCESSING); + + print_string("Total Priority Inversion occurrences: "); + print_string(itoa(ctx.priority_inversion_occurrences, &str[0], 10)); + print_string("\n"); + + rtems_rate_monotonic_delete(ctx.period_0_id); + rtems_rate_monotonic_delete(ctx.period_1_id); + rtems_fatal(RTEMS_FATAL_SOURCE_EXIT, 0); +} + +#define CONFIGURE_APPLICATION_NEEDS_CLOCK_DRIVER + +#define CONFIGURE_MAXIMUM_PROCESSORS TEST_PROCESSORS + +#define CONFIGURE_MAXIMUM_TASKS (TASK_COUNT + 1) + +#define CONFIGURE_MAXIMUM_PERIODS 2 + +#define CONFIGURE_MAXIMUM_SEMAPHORES 1 + +#define CONFIGURE_MINIMUM_TASK_STACK_SIZE RTEMS_MINIMUM_STACK_SIZE + CPU_STACK_ALIGNMENT + +#define CONFIGURE_EXTRA_TASK_STACKS RTEMS_MINIMUM_STACK_SIZE + +#define CONFIGURE_INIT_TASK_CONSTRUCT_STORAGE_SIZE 2 * TASK_STORAGE_SIZE + +#define CONFIGURE_MINIMUM_TASKS_WITH_USER_PROVIDED_STORAGE \ + CONFIGURE_MAXIMUM_TASKS + +#define CONFIGURE_MICROSECONDS_PER_TICK 1000 + +#define CONFIGURE_MAXIMUM_FILE_DESCRIPTORS 0 + +#define CONFIGURE_DISABLE_NEWLIB_REENTRANCY + +#define CONFIGURE_APPLICATION_DISABLE_FILESYSTEM + +#define CONFIGURE_MAXIMUM_THREAD_LOCAL_STORAGE_SIZE MAX_TLS_SIZE + +#define CONFIGURE_RTEMS_INIT_TASKS_TABLE + +#define CONFIGURE_INIT_TASK_ATTRIBUTES (RTEMS_SYSTEM_TASK | TASK_ATTRIBUTES) + +#define CONFIGURE_INIT_TASK_INITIAL_MODES RTEMS_DEFAULT_MODES + +#define CONFIGURE_INIT + +#include <rtems/confdefs.h> |