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