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 Processing Test                                           */
17 /**                                                                       */
18 /**************************************************************************/
19 /**************************************************************************/
20 
21 /**************************************************************************/
22 /*                                                                        */
23 /*  FUNCTION                                               RELEASE        */
24 /*                                                                        */
25 /*    tm_interrupt_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 No-preemption interrupt processing 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 #include "tm_api.h"
43 
44 
45 /* Define the counters used in the demo application...  */
46 
47 unsigned long   tm_interrupt_thread_0_counter;
48 unsigned long   tm_interrupt_handler_counter;
49 
50 
51 /* Define the test thread prototypes.  */
52 
53 void            tm_interrupt_thread_0_entry(void);
54 void            tm_interrupt_handler_entry(void);
55 
56 
57 /* Define the reporting thread prototype.  */
58 
59 void            tm_interrupt_thread_report(void);
60 
61 
62 /* Define the interrupt handler.  This must be called from the RTOS.  */
63 
64 void            tm_interrupt_handler(void);
65 
66 
67 /* Define the initialization prototype.  */
68 
69 void            tm_interrupt_processing_initialize(void);
70 
71 
72 /* Define main entry point.  */
73 
tm_main()74 void tm_main()
75 {
76 
77     /* Initialize the test.  */
78     tm_initialize(tm_interrupt_processing_initialize);
79 }
80 
81 
82 /* Define the interrupt processing test initialization.  */
83 
tm_interrupt_processing_initialize(void)84 void  tm_interrupt_processing_initialize(void)
85 {
86 
87     /* Create thread that generates the interrupt at priority 10.  */
88     tm_thread_create(0, 10, tm_interrupt_thread_0_entry);
89 
90     /* Create a semaphore that will be posted from the interrupt
91        handler.  */
92     tm_semaphore_create(0);
93 
94     /* Resume just thread 0.  */
95     tm_thread_resume(0);
96 
97     /* Create the reporting thread. It will preempt the other
98        threads and print out the test results.  */
99     tm_thread_create(5, 2, tm_interrupt_thread_report);
100     tm_thread_resume(5);
101 }
102 
103 
104 /* Define the thread that generates the interrupt.  */
tm_interrupt_thread_0_entry(void)105 void  tm_interrupt_thread_0_entry(void)
106 {
107 
108 int status;
109 
110 
111     /* Pickup the semaphore since it is initialized to 1 by default. */
112     status = tm_semaphore_get(0);
113 
114     /* Check for good status.  */
115     if (status != TM_SUCCESS)
116         return;
117 
118     while(1)
119     {
120 
121         /* Force an interrupt. The underlying RTOS must see that the
122            the interrupt handler is called from the appropriate software
123            interrupt or trap. */
124        TM_CAUSE_INTERRUPT
125 
126         /* We won't get back here until the interrupt processing is complete,
127            including the setting of the semaphore from the interrupt
128            handler.  */
129 
130         /* Pickup the semaphore set by the interrupt handler. */
131         status = tm_semaphore_get(0);
132 
133         /* Check for good status.  */
134         if (status != TM_SUCCESS)
135             return;
136 
137         /* Increment this thread's counter.  */
138         tm_interrupt_thread_0_counter++;
139     }
140 }
141 
142 
143 /* Define the interrupt handler.  This must be called from the RTOS trap handler.
144    To be fair, it must behave just like a processor interrupt, i.e. it must save
145    the full context of the interrupted thread during the preemption processing. */
tm_interrupt_handler(void)146 void  tm_interrupt_handler(void)
147 {
148 
149     /* Increment the interrupt count.  */
150     tm_interrupt_handler_counter++;
151 
152     /* Put the semaphore from the interrupt handler.  */
153     tm_semaphore_put(0);
154 }
155 
156 
157 /* Define the interrupt test reporting thread.  */
tm_interrupt_thread_report(void)158 void  tm_interrupt_thread_report(void)
159 {
160 
161 unsigned long   total;
162 unsigned long   last_total;
163 unsigned long   relative_time;
164 unsigned long   average;
165 
166 
167     /* Initialize the last total.  */
168     last_total =  0;
169 
170     /* Initialize the relative time.  */
171     relative_time =  0;
172 
173     while(1)
174     {
175 
176         /* Sleep to allow the test to run.  */
177         tm_thread_sleep(TM_TEST_DURATION);
178 
179         /* Increment the relative time.  */
180         relative_time =  relative_time + TM_TEST_DURATION;
181 
182         /* Print results to the stdio window.  */
183         printf("**** Thread-Metric Interrupt Processing Test **** Relative Time: %lu\n", relative_time);
184 
185         /* Calculate the total of all the counters.  */
186         total =  tm_interrupt_thread_0_counter + tm_interrupt_handler_counter;
187 
188         /* Calculate the average of all the counters.  */
189         average =  total/2;
190 
191         /* See if there are any errors.  */
192         if ((tm_interrupt_thread_0_counter < (average - 1)) ||
193             (tm_interrupt_thread_0_counter > (average + 1)) ||
194             (tm_interrupt_handler_counter < (average - 1)) ||
195             (tm_interrupt_handler_counter > (average + 1)))
196         {
197 
198             printf("ERROR: Invalid counter value(s). Interrupt processing test has failed!\n");
199         }
200 
201         /* Show the total interrupts for the time period.  */
202         printf("Time Period Total:  %lu\n\n", tm_interrupt_handler_counter - last_total);
203 
204         /* Save the last total number of interrupts.  */
205         last_total =  tm_interrupt_handler_counter;
206     }
207 }
208