1 /***************************************************************************
2  * Copyright (c) 2024 Microsoft Corporation
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the MIT License which is available at
6  * https://opensource.org/licenses/MIT.
7  *
8  * SPDX-License-Identifier: MIT
9  **************************************************************************/
10 
11 /**************************************************************************/
12 /**************************************************************************/
13 /**                                                                       */
14 /** Thread-Metric Component                                               */
15 /**                                                                       */
16 /**   Interrupt Preemption Processing Test                                */
17 /**                                                                       */
18 /**************************************************************************/
19 /**************************************************************************/
20 
21 
22 /**************************************************************************/
23 /*                                                                        */
24 /*  FUNCTION                                               RELEASE        */
25 /*                                                                        */
26 /*    tm_interrupt_preemption_processing_test             PORTABLE C      */
27 /*                                                           6.1.7        */
28 /*  AUTHOR                                                                */
29 /*                                                                        */
30 /*    William E. Lamie, Microsoft Corporation                             */
31 /*                                                                        */
32 /*  DESCRIPTION                                                           */
33 /*                                                                        */
34 /*    This file defines the preemptive scheduling test.                   */
35 /*                                                                        */
36 /*  RELEASE HISTORY                                                       */
37 /*                                                                        */
38 /*    DATE              NAME                      DESCRIPTION             */
39 /*                                                                        */
40 /*  10-15-2021     William E. Lamie         Initial Version 6.1.7         */
41 /*                                                                        */
42 /**************************************************************************/
43 
44 #include "tm_api.h"
45 
46 
47 /* Define the counters used in the demo application...  */
48 
49 unsigned long   tm_interrupt_preemption_thread_0_counter;
50 unsigned long   tm_interrupt_preemption_thread_1_counter;
51 unsigned long   tm_interrupt_preemption_handler_counter;
52 
53 
54 /* Define the test thread prototypes.  */
55 
56 void            tm_interrupt_preemption_thread_0_entry(void);
57 void            tm_interrupt_preemption_thread_1_entry(void);
58 void            tm_interrupt_preemption_handler_entry(void);
59 
60 
61 /* Define the reporting thread prototype.  */
62 
63 void            tm_interrupt_preemption_thread_report(void);
64 
65 
66 /* Define the interrupt handler.  This must be called from the RTOS.  */
67 
68 void            tm_interrupt_preemption_handler(void);
69 
70 
71 /* Define the initialization prototype.  */
72 
73 void            tm_interrupt_preemption_processing_initialize(void);
74 
75 
76 /* Define main entry point.  */
77 
tm_main()78 void tm_main()
79 {
80 
81     /* Initialize the test.  */
82     tm_initialize(tm_interrupt_preemption_processing_initialize);
83 }
84 
85 
86 /* Define the interrupt processing test initialization.  */
87 
tm_interrupt_preemption_processing_initialize(void)88 void  tm_interrupt_preemption_processing_initialize(void)
89 {
90 
91     /* Create interrupt thread at priority 3.  */
92     tm_thread_create(0, 3, tm_interrupt_preemption_thread_0_entry);
93 
94     /* Create thread that generates the interrupt at priority 10.  */
95     tm_thread_create(1, 10, tm_interrupt_preemption_thread_1_entry);
96 
97     /* Resume just thread 1.  */
98     tm_thread_resume(1);
99 
100     /* Create the reporting thread. It will preempt the other
101        threads and print out the test results.  */
102     tm_thread_create(5, 2, tm_interrupt_preemption_thread_report);
103     tm_thread_resume(5);
104 }
105 
106 
107 /* Define the interrupt thread.  This thread is resumed from the
108    interrupt handler.  It runs and suspends.  */
tm_interrupt_preemption_thread_0_entry(void)109 void  tm_interrupt_preemption_thread_0_entry(void)
110 {
111 
112     while(1)
113     {
114 
115         /* Increment this thread's counter.  */
116         tm_interrupt_preemption_thread_0_counter++;
117 
118         /* Suspend. This will allow the thread generating the
119            interrupt to run again.  */
120         tm_thread_suspend(0);
121     }
122 }
123 
124 /* Define the thread that generates the interrupt.  */
tm_interrupt_preemption_thread_1_entry(void)125 void  tm_interrupt_preemption_thread_1_entry(void)
126 {
127 
128     while(1)
129     {
130 
131         /* Force an interrupt. The underlying RTOS must see that the
132            the interrupt handler is called from the appropriate software
133            interrupt or trap. */
134         TM_CAUSE_INTERRUPT
135 
136         /* We won't get back here until the interrupt processing is complete,
137            including the execution of the higher priority thread made ready
138            by the interrupt.  */
139 
140         /* Increment this thread's counter.  */
141         tm_interrupt_preemption_thread_1_counter++;
142     }
143 }
144 
145 
146 /* Define the interrupt handler.  This must be called from the RTOS trap handler.
147    To be fair, it must behave just like a processor interrupt, i.e. it must save
148    the full context of the interrupted thread during the preemption processing. */
tm_interrupt_preemption_handler(void)149 void  tm_interrupt_preemption_handler(void)
150 {
151 
152     /* Increment the interrupt count.  */
153     tm_interrupt_preemption_handler_counter++;
154 
155     /* Resume the higher priority thread from the ISR.  */
156     tm_thread_resume(0);
157 }
158 
159 
160 /* Define the interrupt test reporting thread.  */
tm_interrupt_preemption_thread_report(void)161 void  tm_interrupt_preemption_thread_report(void)
162 {
163 
164 unsigned long   total;
165 unsigned long   relative_time;
166 unsigned long   last_total;
167 unsigned long   average;
168 
169 
170     /* Initialize the last total.  */
171     last_total =  0;
172 
173     /* Initialize the relative time.  */
174     relative_time =  0;
175 
176     while(1)
177     {
178 
179         /* Sleep to allow the test to run.  */
180         tm_thread_sleep(TM_TEST_DURATION);
181 
182         /* Increment the relative time.  */
183         relative_time =  relative_time + TM_TEST_DURATION;
184 
185         /* Print results to the stdio window.  */
186         printf("**** Thread-Metric Interrupt Preemption Processing Test **** Relative Time: %lu\n", relative_time);
187 
188         /* Calculate the total of all the counters.  */
189         total =  tm_interrupt_preemption_thread_0_counter + tm_interrupt_preemption_thread_1_counter + tm_interrupt_preemption_handler_counter;
190 
191         /* Calculate the average of all the counters.  */
192         average =  total/3;
193 
194         /* See if there are any errors.  */
195         if ((tm_interrupt_preemption_thread_0_counter < (average - 1)) ||
196             (tm_interrupt_preemption_thread_0_counter > (average + 1)) ||
197             (tm_interrupt_preemption_thread_1_counter < (average - 1)) ||
198             (tm_interrupt_preemption_thread_1_counter > (average + 1)) ||
199             (tm_interrupt_preemption_handler_counter < (average - 1)) ||
200             (tm_interrupt_preemption_handler_counter > (average + 1)))
201         {
202 
203             printf("ERROR: Invalid counter value(s). Interrupt processing test has failed!\n");
204         }
205 
206         /* Show the total interrupts for the time period.  */
207         printf("Time Period Total:  %lu\n\n", tm_interrupt_preemption_handler_counter - last_total);
208 
209         /* Save the last total number of interrupts.  */
210         last_total =  tm_interrupt_preemption_handler_counter;
211     }
212 }
213