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