/*************************************************************************** * Copyright (c) 2024 Microsoft Corporation * * This program and the accompanying materials are made available under the * terms of the MIT License which is available at * https://opensource.org/licenses/MIT. * * SPDX-License-Identifier: MIT **************************************************************************/ /**************************************************************************/ /**************************************************************************/ /** */ /** Thread-Metric Component */ /** */ /** Interrupt Preemption Processing Test */ /** */ /**************************************************************************/ /**************************************************************************/ /**************************************************************************/ /* */ /* FUNCTION RELEASE */ /* */ /* tm_interrupt_preemption_processing_test PORTABLE C */ /* 6.1.7 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ /* */ /* DESCRIPTION */ /* */ /* This file defines the preemptive scheduling test. */ /* */ /* RELEASE HISTORY */ /* */ /* DATE NAME DESCRIPTION */ /* */ /* 10-15-2021 William E. Lamie Initial Version 6.1.7 */ /* */ /**************************************************************************/ #include "tm_api.h" /* Define the counters used in the demo application... */ unsigned long tm_interrupt_preemption_thread_0_counter; unsigned long tm_interrupt_preemption_thread_1_counter; unsigned long tm_interrupt_preemption_handler_counter; /* Define the test thread prototypes. */ void tm_interrupt_preemption_thread_0_entry(void); void tm_interrupt_preemption_thread_1_entry(void); void tm_interrupt_preemption_handler_entry(void); /* Define the reporting thread prototype. */ void tm_interrupt_preemption_thread_report(void); /* Define the interrupt handler. This must be called from the RTOS. */ void tm_interrupt_preemption_handler(void); /* Define the initialization prototype. */ void tm_interrupt_preemption_processing_initialize(void); /* Define main entry point. */ void tm_main() { /* Initialize the test. */ tm_initialize(tm_interrupt_preemption_processing_initialize); } /* Define the interrupt processing test initialization. */ void tm_interrupt_preemption_processing_initialize(void) { /* Create interrupt thread at priority 3. */ tm_thread_create(0, 3, tm_interrupt_preemption_thread_0_entry); /* Create thread that generates the interrupt at priority 10. */ tm_thread_create(1, 10, tm_interrupt_preemption_thread_1_entry); /* Resume just thread 1. */ tm_thread_resume(1); /* Create the reporting thread. It will preempt the other threads and print out the test results. */ tm_thread_create(5, 2, tm_interrupt_preemption_thread_report); tm_thread_resume(5); } /* Define the interrupt thread. This thread is resumed from the interrupt handler. It runs and suspends. */ void tm_interrupt_preemption_thread_0_entry(void) { while(1) { /* Increment this thread's counter. */ tm_interrupt_preemption_thread_0_counter++; /* Suspend. This will allow the thread generating the interrupt to run again. */ tm_thread_suspend(0); } } /* Define the thread that generates the interrupt. */ void tm_interrupt_preemption_thread_1_entry(void) { while(1) { /* Force an interrupt. The underlying RTOS must see that the the interrupt handler is called from the appropriate software interrupt or trap. */ TM_CAUSE_INTERRUPT /* We won't get back here until the interrupt processing is complete, including the execution of the higher priority thread made ready by the interrupt. */ /* Increment this thread's counter. */ tm_interrupt_preemption_thread_1_counter++; } } /* Define the interrupt handler. This must be called from the RTOS trap handler. To be fair, it must behave just like a processor interrupt, i.e. it must save the full context of the interrupted thread during the preemption processing. */ void tm_interrupt_preemption_handler(void) { /* Increment the interrupt count. */ tm_interrupt_preemption_handler_counter++; /* Resume the higher priority thread from the ISR. */ tm_thread_resume(0); } /* Define the interrupt test reporting thread. */ void tm_interrupt_preemption_thread_report(void) { unsigned long total; unsigned long relative_time; unsigned long last_total; unsigned long average; /* Initialize the last total. */ last_total = 0; /* Initialize the relative time. */ relative_time = 0; while(1) { /* Sleep to allow the test to run. */ tm_thread_sleep(TM_TEST_DURATION); /* Increment the relative time. */ relative_time = relative_time + TM_TEST_DURATION; /* Print results to the stdio window. */ printf("**** Thread-Metric Interrupt Preemption Processing Test **** Relative Time: %lu\n", relative_time); /* Calculate the total of all the counters. */ total = tm_interrupt_preemption_thread_0_counter + tm_interrupt_preemption_thread_1_counter + tm_interrupt_preemption_handler_counter; /* Calculate the average of all the counters. */ average = total/3; /* See if there are any errors. */ if ((tm_interrupt_preemption_thread_0_counter < (average - 1)) || (tm_interrupt_preemption_thread_0_counter > (average + 1)) || (tm_interrupt_preemption_thread_1_counter < (average - 1)) || (tm_interrupt_preemption_thread_1_counter > (average + 1)) || (tm_interrupt_preemption_handler_counter < (average - 1)) || (tm_interrupt_preemption_handler_counter > (average + 1))) { printf("ERROR: Invalid counter value(s). Interrupt processing test has failed!\n"); } /* Show the total interrupts for the time period. */ printf("Time Period Total: %lu\n\n", tm_interrupt_preemption_handler_counter - last_total); /* Save the last total number of interrupts. */ last_total = tm_interrupt_preemption_handler_counter; } }