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