1 /******************************************************************************
2 * Filename: interrupt.c
3 *
4 * Description: Utility functions to interact with interrupts and the NVIC
5 *
6 * Copyright (c) 2022-2023 Texas Instruments Incorporated
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions are met:
10 *
11 * 1) Redistributions of source code must retain the above copyright notice,
12 * this list of conditions and the following disclaimer.
13 *
14 * 2) Redistributions in binary form must reproduce the above copyright notice,
15 * this list of conditions and the following disclaimer in the documentation
16 * and/or other materials provided with the distribution.
17 *
18 * 3) Neither the name of the copyright holder nor the names of its
19 * contributors may be used to endorse or promote products derived from this
20 * software without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
23 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
26 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32 * POSSIBILITY OF SUCH DAMAGE.
33 *
34 ******************************************************************************/
35
36 #include "interrupt.h"
37 #include "cpu.h"
38 #include "debug.h"
39
40 #include "../inc/hw_ints.h"
41 #include "../inc/hw_types.h"
42
43 //*****************************************************************************
44 //
45 //! \brief The default interrupt handler.
46 //!
47 //! This is the default interrupt handler for all interrupts. It simply loops
48 //! forever so that the system state is preserved for observation by a
49 //! debugger. Since interrupts should be disabled before unregistering the
50 //! corresponding handler, this should never be called.
51 //!
52 //! \return None
53 //
54 //*****************************************************************************
IntDefaultHandler(void)55 static void IntDefaultHandler(void)
56 {
57 // Go into an infinite loop.
58 while (1) {}
59 }
60
61 //*****************************************************************************
62 //
63 //! \brief Global pointer to the (dynamic) interrupt vector table when placed in SRAM.
64 //!
65 //! Interrupt vector table is placed at ".ramVecs" defined in the linker file
66 //! provided by Texas Instruments.
67 //!
68 //! \note See \ti_code{interrupt.c} for compiler specific implementation!
69 //
70 //*****************************************************************************
71 #if defined(DOXYGEN)
72 // Dummy void pointer used as placeholder to generate Doxygen documentation.
73 void (*ramVectorTable[NUM_INTERRUPTS])(void);
74 #elif defined(__IAR_SYSTEMS_ICC__)
75 #pragma data_alignment = 256
76 static __no_init void (*ramVectorTable[NUM_INTERRUPTS])(void) @ ".ramVecs";
77 #elif defined(__clang__)
78 static __attribute__((section(".ramVecs"), aligned(256))) void (*ramVectorTable[NUM_INTERRUPTS])(void);
79 #else
80 static __attribute__((section(".ramVecs"), aligned(256))) void (*ramVectorTable[NUM_INTERRUPTS])(void);
81 #endif
82
83 //*****************************************************************************
84 //
85 // Registers a function to be called when an interrupt occurs.
86 //
87 //*****************************************************************************
IntRegister(uint32_t intNum,void (* handler)(void))88 void IntRegister(uint32_t intNum, void (*handler)(void))
89 {
90 uint32_t idx;
91 uint32_t tmpVtor;
92
93 // Check the arguments.
94 ASSERT(intNum < NUM_INTERRUPTS);
95
96 // Make sure that the RAM vector table is correctly aligned.
97 ASSERT(((uint32_t)ramVectorTable & 0x000000ff) == 0);
98
99 // See if the RAM vector table has been initialized.
100 if (SCB->VTOR != (uint32_t)ramVectorTable)
101 {
102 // Copy the vector table from the beginning of FLASH to the RAM vector
103 // table.
104 tmpVtor = SCB->VTOR;
105 for (idx = 0; idx < NUM_INTERRUPTS; idx++)
106 {
107 ramVectorTable[idx] = (void (*)(void))HWREG((idx * 4) + tmpVtor);
108 }
109
110 // Point NVIC at the RAM vector table.
111 SCB->VTOR = (uint32_t)ramVectorTable;
112 }
113
114 // Save the interrupt handler.
115 ramVectorTable[intNum] = handler;
116 }
117
118 //*****************************************************************************
119 //
120 // Unregisters the function to be called when an interrupt occurs.
121 //
122 //*****************************************************************************
IntUnregister(uint32_t intNum)123 void IntUnregister(uint32_t intNum)
124 {
125 // Check the arguments.
126 ASSERT(intNum < NUM_INTERRUPTS);
127
128 // Reset the interrupt handler.
129 ramVectorTable[intNum] = IntDefaultHandler;
130 }
131
132 //*****************************************************************************
133 //
134 // Sets the priority of an interrupt
135 //
136 //*****************************************************************************
IntSetPriority(uint32_t intNum,uint8_t priority)137 void IntSetPriority(uint32_t intNum, uint8_t priority)
138 {
139 // Check the arguments.
140 ASSERT((intNum >= 11) && (intNum < NUM_INTERRUPTS));
141 ASSERT((intNum != 12) && (intNum != 13));
142 ASSERT(priority <= INT_PRI_LEVEL3);
143
144 uint32_t ipr;
145 uint32_t mask;
146 uint32_t shift;
147 uint32_t offset;
148 bool intNumWasEnabled;
149 uint32_t priorityRegIndex;
150 volatile uint32_t *address;
151
152 // System exception priorities are set in the SHPR registers.
153 // Indexing starts at SHPR2 (8 <= intNum <= 11)
154 if (intNum < 16)
155 {
156 address = SCB->SHP;
157 priorityRegIndex = intNum - 8;
158 }
159
160 // User interrupt (id >= 16) priorities are set in the IPR registers
161 // Indexing starts at IPR0 (16 <= intNum <= 19)
162 else
163 {
164 address = NVIC->IP;
165 priorityRegIndex = intNum - 16;
166 }
167
168 offset = priorityRegIndex >> 2;
169 shift = (priorityRegIndex & 0x3) * 8;
170 mask = 0xff << shift;
171
172 intNumWasEnabled = IntIsEnabled(intNum);
173
174 // Disable the interrupt before changing it's priority
175 IntDisable(intNum);
176
177 ipr = address[offset] & ~mask;
178 ipr |= (priority & INT_PRIORITY_MASK) << shift;
179 address[offset] = ipr;
180
181 if (intNumWasEnabled)
182 {
183 // Re-enable the interrupt
184 IntEnable(intNum);
185 }
186 }
187
188 //*****************************************************************************
189 //
190 // Gets the priority of an interrupt
191 //
192 //*****************************************************************************
IntGetPriority(uint32_t intNum)193 int32_t IntGetPriority(uint32_t intNum)
194 {
195 // Check the arguments.
196 ASSERT((intNum >= 11) && (intNum < NUM_INTERRUPTS));
197 ASSERT((intNum != 12) && (intNum != 13));
198
199 uint32_t index = intNum >> 2;
200
201 if (intNum < 16)
202 {
203 /* System exception priorities are set in the SHPR registers.
204 * Indexing starts at SHPR2 (8 <= intNum <= 11)
205 */
206 return ((SCB->SHP[index - 2] >> (8 * (intNum & 3))) & INT_PRIORITY_MASK);
207 }
208 else
209 {
210 /* User interrupt (id >= 16) priorities are set in the IPR registers
211 * Indexing starts at IPR0 (16 <= intNum <= 19)
212 */
213 return ((NVIC->IP[index - 4] >> (8 * (intNum & 3))) & INT_PRIORITY_MASK);
214 }
215 }
216
217 //*****************************************************************************
218 //
219 // Checks whether an interrupt is enabled
220 //
221 //*****************************************************************************
IntIsEnabled(uint32_t intNum)222 bool IntIsEnabled(uint32_t intNum)
223 {
224 // Check the arguments.
225 ASSERT(intNum < NUM_INTERRUPTS);
226
227 bool isEnabled = false;
228
229 // Determine the interrupt to check.
230 if (intNum == INT_SYSTICK)
231 {
232 // Check the System Tick interrupt.
233 isEnabled = SysTick->CTRL & SysTick_CTRL_TICKINT_Msk ? true : false;
234 }
235 else if ((intNum >= 16) && (intNum <= 47))
236 {
237 // Check the general interrupt.
238 isEnabled = (NVIC->ISER[0] & (1 << (intNum - 16))) ? true : false;
239 }
240
241 return isEnabled;
242 }
243
244 //*****************************************************************************
245 //
246 // Enables an interrupt
247 //
248 //*****************************************************************************
IntEnable(uint32_t intNum)249 void IntEnable(uint32_t intNum)
250 {
251 // Check the arguments.
252 ASSERT(intNum < NUM_INTERRUPTS);
253
254 // Determine the interrupt to enable.
255 if (intNum == INT_SYSTICK)
256 {
257 // Enable the System Tick interrupt.
258 SysTick->CTRL |= SysTick_CTRL_TICKINT_Msk;
259 }
260 else if ((intNum >= 16) && (intNum <= 47))
261 {
262 // Enable the general interrupt.
263 NVIC->ISER[0] = 1 << (intNum - 16);
264 }
265 }
266
267 //*****************************************************************************
268 //
269 // Disables an interrupt
270 //
271 //*****************************************************************************
IntDisable(uint32_t intNum)272 void IntDisable(uint32_t intNum)
273 {
274 // Check the arguments.
275 ASSERT(intNum < NUM_INTERRUPTS);
276
277 // Determine the interrupt to disable.
278 if (intNum == INT_SYSTICK)
279 {
280 // Disable the System Tick interrupt.
281 SysTick->CTRL &= ~SysTick_CTRL_TICKINT_Msk;
282 }
283 else if ((intNum >= 16) && (intNum <= 47))
284 {
285 // Disable the general interrupt.
286 NVIC->ICER[0] = 1 << (intNum - 16);
287 }
288 }
289
290 //*****************************************************************************
291 //
292 // Pends an interrupt
293 //
294 //*****************************************************************************
IntSetPend(uint32_t intNum)295 void IntSetPend(uint32_t intNum)
296 {
297 // Check the arguments.
298 ASSERT(intNum < NUM_INTERRUPTS);
299
300 // Determine the interrupt to pend.
301 if ((intNum >= 16) && (intNum <= 47))
302 {
303 // Pend the general interrupt.
304 NVIC->ISPR[0] = 1 << (intNum - 16);
305 }
306 else if (intNum == INT_NMI_FAULT)
307 {
308 // Pend the NMI interrupt.
309 SCB->ICSR |= SCB_ICSR_NMIPENDSET_Msk;
310 }
311 else if (intNum == INT_PENDSV)
312 {
313 // Pend the PendSV interrupt.
314 SCB->ICSR |= SCB_ICSR_PENDSVSET_Msk;
315 }
316 else if (intNum == INT_SYSTICK)
317 {
318 // Pend the SysTick interrupt.
319 SCB->ICSR |= SCB_ICSR_PENDSTSET_Msk;
320 }
321 }
322
323 //*****************************************************************************
324 //
325 // Query whether an interrupt is pending
326 //
327 //*****************************************************************************
IntGetPend(uint32_t intNum)328 bool IntGetPend(uint32_t intNum)
329 {
330 uint32_t intPending;
331
332 // Check the arguments.
333 ASSERT(intNum < NUM_INTERRUPTS);
334
335 // Assume no interrupts are pending.
336 intPending = 0;
337
338 // The lower 16 IRQ vectors are unsupported by this function
339 if (intNum < 16)
340 {
341
342 return false;
343 }
344
345 // Subtract lower 16 irq vectors
346 intNum -= 16;
347
348 // Check if the interrupt is pending
349 intPending = NVIC->ISPR[0] & (1 << intNum);
350
351 return intPending ? true : false;
352 }
353
354 //*****************************************************************************
355 //
356 // Unpends an interrupt
357 //
358 //*****************************************************************************
IntClearPend(uint32_t intNum)359 void IntClearPend(uint32_t intNum)
360 {
361 // Check the arguments.
362 ASSERT(intNum < NUM_INTERRUPTS);
363
364 // Determine the interrupt to unpend.
365 if (intNum == INT_PENDSV)
366 {
367 // Unpend the PendSV interrupt.
368 SCB->ICSR |= SCB_ICSR_PENDSVCLR_Msk;
369 }
370 else if (intNum == INT_SYSTICK)
371 {
372 // Unpend the SysTick interrupt.
373 SCB->ICSR |= SCB_ICSR_PENDSTCLR_Msk;
374 }
375 else if ((intNum >= 16) && (intNum <= 47))
376 {
377 // Unpend the general interrupt.
378 NVIC->ICPR[0] = 1 << (intNum - 16);
379 }
380 }
381