1 //*****************************************************************************
2 //
3 //! @file am_hal_interrupt.c
4 //!
5 //! @brief Helper functions supporting DSP interrupts.
6 //!
7 //! @addtogroup dsp_interrupt Interrupt (DSP NVIC support functions)
8 //! @ingroup apollo4p_hal
9 //! @{
10 //
11 //*****************************************************************************
12 
13 //*****************************************************************************
14 //
15 // Copyright (c) 2023, Ambiq Micro, Inc.
16 // All rights reserved.
17 //
18 // Redistribution and use in source and binary forms, with or without
19 // modification, are permitted provided that the following conditions are met:
20 //
21 // 1. Redistributions of source code must retain the above copyright notice,
22 // this list of conditions and the following disclaimer.
23 //
24 // 2. Redistributions in binary form must reproduce the above copyright
25 // notice, this list of conditions and the following disclaimer in the
26 // documentation and/or other materials provided with the distribution.
27 //
28 // 3. Neither the name of the copyright holder nor the names of its
29 // contributors may be used to endorse or promote products derived from this
30 // software without specific prior written permission.
31 //
32 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
33 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
34 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
35 // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
36 // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
37 // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
38 // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
39 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
40 // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
41 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
42 // POSSIBILITY OF SUCH DAMAGE.
43 //
44 // This is part of revision release_sdk_4_4_0-3c5977e664 of the AmbiqSuite Development Package.
45 //
46 //*****************************************************************************
47 
48 #include <stdint.h>
49 #include <stddef.h>
50 #include <stdbool.h>
51 #include "am_mcu_apollo.h"
52 #include "am_hal_internal_dsp.h"
53 
54 //#define NULL 0
55 #define MAX_NO_OF_HANDLERS    32   //!< number of handlers supported per DSP core
56 //*****************************************************************************
57 //
58 //! @brief the linked list structure for the interrupt handlers defined by the user code
59 //!
60 //! This structure keeps the handler address with some information about the
61 //! status register bit associated with the interrupt.
62 //!
63 //!
64 //
65 //*****************************************************************************
66 /*
67    typedef struct
68    {
69         void     *pHandler;
70         void     *pHandlerCtxt;
71         void     *pIRQStatusRegAdd;
72         uint32_t  IRQStatusRegVal;
73         void     *dNextIntHandler;
74         uint8_t   ui5IntterruptNo;// This is a 5bit value(3 to 25) for the current interrupt for this handler
75         //  this is used by the am_hal_interrupt_register_handler to find all interrupts associated for the current
76         // DSP interrupt.
77         uint8_t   ui1FirstInterrupt; // when this flag is set. Then this Handler is the head of the linked list.
78         uint8_t   ui8PeripheralIntNo; // The source interrupt number for this handler. This is useful for unregister function
79         uint8_t   ui8PreviousElementIndex; // when 0xff then this Handler is the head of the linked list, else this is the link.
80         uint32_t reserve[2]; // two of the members here can be used for one extra register access
81    }IntHandlerStruct;
82  */
83 void     am_hal_dsp_dispatcher(IntHandlerStruct * ihs); // this function is defined in assembly
84 
85 //*****************************************************************************
86 //
87 // Global Variables
88 //
89 //*****************************************************************************
90 //!
91 IntHandlerStruct sIntHandler[MAX_NO_OF_HANDLERS];
92 
93 // The next lines should be removed
94 #define DSP_CORE0
95 #ifdef DSP_CORE0
96 #define DSPRAWIRQSTAT0    DSP->DSP0INTORMASK   // DSP0RAWIRQSTAT0
97 // replace the next line with actual register definition
98 int abc[10];
99 #define REG_DSP_DSP0OR1_0    abc
100 #define REG_DSP_DSP0OR2_0    abc
101 #elif DSP_CORE1
102 #define DSPRAWIRQSTAT0    DSP1RAWIRQSTAT0
103 #endif
104 // assumption:
105 // REG_DSP_DSP0OR1_0,...4
106 // REG_DSP_DSP0OR2_0,...4
107 
108 //*****************************************************************************
109 //
110 //! @brief am_hal_interrupt_disable disables the raw interrupt( no more interrupt from the source)
111 //!
112 //! The function disable the raw interrupt by disconnecting the interrupt on the
113 //! source. The interrupt can be disable from the source or disable inside the DSP
114 //! by INTERRUPT register. This function masks the interrupt on the source and does
115 //! change the INTERRUPT register.
116 //!
117 //! @param ui8InterruptNo - The raw interrupt number (0 to 159)
118 //
119 //*****************************************************************************
120 void
am_hal_interrupt_disable(uint32_t ui8InterruptNo)121 am_hal_interrupt_disable(uint32_t ui8InterruptNo)
122 {
123     uint8_t OrIndex;
124     OrIndex = ui8InterruptNo>>5;
125     if ( sHalSharedMem->sHalDSPInt[ui8InterruptNo].Or1 )
126     {
127         //clears OR1 bit
128         REG_DSP_DSP0OR1_0[OrIndex] &= ~( 1 << (ui8InterruptNo & 0x1f) );
129         // DOn't need to disable inside the DSP     _xtos_interrupt_disable();
130     }
131     if ( sHalSharedMem->sHalDSPInt[ui8InterruptNo].Or2 )
132     {
133         //clears OR1 bit
134         REG_DSP_DSP0OR2_0[OrIndex] &= ~( 1 << (ui8InterruptNo & 0x1f) );
135         // DOn't need to disable inside the DSP     _xtos_interrupt_disable();
136     }
137 }
138 
139 //*****************************************************************************
140 //
141 //! @brief am_hal_interrupt_enable enables the raw interrupt
142 //!
143 //! The function restore the raw interrupt connection between the source and DSP
144 //! pin. To enable an interrupt the raw source should be connected to DSP input
145 //! pins and then the corresponding INTERRUPT flag should be enabled.
146 //!
147 //! @param ui8InterruptNo  : The raw interrupt number (0 to 159)
148 //
149 //*****************************************************************************
150 void
am_hal_interrupt_enable(uint32_t ui8InterruptNo)151 am_hal_interrupt_enable(uint32_t ui8InterruptNo)
152 {
153     uint8_t OrIndex;
154     OrIndex = ui8InterruptNo>>5;
155     if ( sHalSharedMem->sHalDSPInt[ui8InterruptNo].Or1 )
156     {
157         //clears OR1 bit
158         REG_DSP_DSP0OR1_0[OrIndex] |= ( 1 << (ui8InterruptNo & 0x1f) );
159         if ( sHalSharedMem->sHalDSPInt[ui8InterruptNo].intNo != INVALID_INTERRUPT)
160         {
161             _xtos_interrupt_enable(sHalSharedMem->sHalDSPInt[ui8InterruptNo].intNo);
162         }
163     }
164     if ( sHalSharedMem->sHalDSPInt[ui8InterruptNo].Or2 )
165     {
166         //clears OR1 bit
167         REG_DSP_DSP0OR2_0[OrIndex] |= ( 1 << (ui8InterruptNo & 0x1f) );
168         if ( sHalSharedMem->sHalDSPInt[ui8InterruptNo].intNo != INVALID_INTERRUPT)
169         {
170             _xtos_interrupt_enable(sHalSharedMem->sHalDSPInt[ui8InterruptNo].intNo);
171         }
172     }
173 
174 }
175 
176 //*****************************************************************************
177 //
178 //! @brief am_hal_interrupt_master_disable disables all interrupt on DSP
179 //!
180 //! To disable all interrupts in DSP, the interrupt level should be raised to a
181 //! level higher the maximum level. Currently the maximum level is 6. This function
182 //! raises the interrupt level to 15 to disable all interrupt (the PS register have
183 //! 4 bits for interrupt level).
184 //! Note: disabling interrupt can not disable the exceptions.
185 //!
186 //! @return the contents of PS register.
187 //
188 //*****************************************************************************
189 
190 uint32_t
am_hal_interrupt_master_disable(void)191 am_hal_interrupt_master_disable(void)
192 {
193     // return a value that represents the previous interrupt level mask. This
194     // value can be passed to _xtos_restore_intlevel() to restore the interrupt
195     // level mask to what it was before the interrupt level mask was set. The
196     // format of the return value cannot be relied upon (In the current implementation,
197     // this interrupt level mask value is the contents of the PS special register).
198     return _xtos_set_intlevel(15);
199 }
200 
201 //*****************************************************************************
202 //
203 //! @brief am_hal_interrupt_master_enable enables all interrupt on DSP
204 //!
205 //! To enable all interrupts in DSP, the interrupt level should be set to zero.
206 //! This function regardless of the current interrupt level, changes the interrupt
207 //! level to zero. The interrupt level is specified by 4 bits inside PS register.
208 //!
209 //! @return the contents of PS register.
210 //
211 //*****************************************************************************
212 uint32_t
am_hal_interrupt_master_enable(void)213 am_hal_interrupt_master_enable(void)
214 {
215     // return a value that represents the previous interrupt level mask. This
216     // value can be passed to _xtos_restore_intlevel() to restore the interrupt
217     // level mask to what it was before the interrupt level mask was set. The
218     // format of the return value cannot be relied upon (In the current implementation,
219     // this interrupt level mask value is the contents of the PS special register.
220     return _xtos_set_intlevel(0);
221 }
222 
223 //*****************************************************************************
224 //
225 //! @brief am_hal_interrupt_master_restore restores the interrupt level
226 //!
227 //! The function restore the interrupt level to its previous known state. DSP has
228 //! six level of interrupt. 18+7 of interrupts are level one and 5 more interrupts
229 //! are at level 2 to 6. changing the interrupt level can enable or disable all
230 //! interrupts with the same levels.
231 //!
232 //! @param ui32Restoreval
233 //! @return the contents of PS register.
234 //
235 //*****************************************************************************
236 uint32_t
am_hal_interrupt_master_restore(uint32_t ui32Restoreval)237 am_hal_interrupt_master_restore(uint32_t ui32Restoreval)
238 {
239     // This function restore the current interrupt level mask according to a value
240     // returned by _xtos_set_intlevel().
241     _xtos_set_intlevel(ui32Restoreval);
242     return 1;
243 }
244 
245 //*****************************************************************************
246 //
247 //! @brief am_hal_interrupt_master_set set the interrupt level
248 //!
249 //! The function can change the interrupt level to any level. By changing the
250 //! interrupt level, all interrupts with the lower level are disabled.
251 //!
252 //! @param ui32Level - The required interrupt level
253 //! @return the contents of PS register.
254 //
255 //*****************************************************************************
256 uint32_t
am_hal_interrupt_master_set(uint32_t ui32Level)257 am_hal_interrupt_master_set(uint32_t ui32Level)
258 {
259     // return a value that represents the previous interrupt level mask. This value
260     // can be passed to _xtos_restore_intlevel() to restore the interrupt level
261     // mask to what it was before the interrupt level mask was set. The format of
262     // the return value cannot be relied upon (In the current implementation,
263     // this interrupt level mask value is the contents of the PS special register.
264     return _xtos_set_intlevel(ui32Level);
265 }
266 
267 //*****************************************************************************
268 //
269 //! @brief am_hal_interrupt_register_handler registers a handler for interrupt
270 //!
271 //! The function can change the interrupt level to any level. By changing the
272 //! interrupt level, all interrupts with the lower level are disabled.
273 //!
274 //! @param ui32Interrupt - The interrupt number (0 .. XCHAL_NUM_INTERRUPTS - 1)
275 //!
276 //! @param pHandler      - Address of interrupt handler to be registered.
277 //!                        Passing NULL will uninstall an existing handler.
278 //!
279 //! @param pHandlerCtxt  - Parameter to be passed to handler when invoked.
280 //!
281 //! @return             Zero on success, negative number on error.
282 //
283 //*****************************************************************************
284 
285 uint32_t
am_hal_interrupt_register_handler(uint32_t ui32Interrupt,void * pHandler,void * pHandlerCtxt)286 am_hal_interrupt_register_handler(uint32_t ui32Interrupt, void *pHandler, void *pHandlerCtxt)
287 {
288 // This function registers the input function address(*pHandler) for the interrupt number ui32Interrupt.
289 // The external 160 interrupts are passing through ORed structured and then connected to 23 input interrupt
290 // of the DSP(interrupt No 3 to No 25). Each of these 23 interrupts has a unique handler inside the XTOS.
291 // If only one interrupt is connected to DSP interrupt, then the handler(*pHandler) is directly registered
292 // in XTOS. If more than one external interrupt is connected to a single DSP interrupt, then a linked list
293 // is created for all interrupts and am_hal_dsp_dispatcher() is registered as an handler inside  XTOS.
294 
295     // To keep track of all handlers, a handler pool is created. Handlers are stored inside the pool.
296     // The maximum number of handler is limited by the handler pool capacity(MAX_NO_OF_HANDLERS).
297     int16_t nextFreeHandlerIndex, lastHandlerIndex, firstHandlerIndex, handlerIndex;
298     int statusIndex;
299     nextFreeHandlerIndex = -1;
300     lastHandlerIndex = -1;
301     firstHandlerIndex = -1;
302 
303     if ( pHandler == NULL)
304     {
305         // unregister all the functions associated with ui32Interrupt
306         // The for-loop is used because it is possible that an interrupt is registered for multiple interrupt level!!
307         for ( handlerIndex = 0; handlerIndex < MAX_NO_OF_HANDLERS; handlerIndex++)
308         {
309             if ( sIntHandler[handlerIndex].pHandler != 0)
310             {
311                 if (sIntHandler[handlerIndex].ui8PeripheralIntNo == ui32Interrupt)
312                 {
313                     // The handler found
314                     // Now removes this handler from the link-list. Removing the first element inside a linked-list
315                     // is different than non-first elements.
316 // this should be done in critical section
317                     sIntHandler[handlerIndex].pHandler = NULL;
318                     //
319                     if ( sIntHandler[handlerIndex].ui8PreviousElementIndex == 0xff)
320                     {
321                         // if this is the first(or the only) handler in the chain.
322                         if ( sIntHandler[handlerIndex].dNextIntHandler == NULL)
323                         {
324                             // no more handler on the chain. Unregister the function.
325 #ifdef XTOS
326                             xtos_set_interrupt_handler( sIntHandler[handlerIndex].ui5IntterruptNo, NULL, NULL, NULL);
327 #elif XOS
328                             xos_register_interrupt_handler( sIntHandler[handlerIndex].ui5IntterruptNo, NULL, NULL );
329 #endif
330                         }
331                         else
332                         {
333                             // more handler on the chain.
334                             // add assert here if the new handler is not valid.
335                             ((IntHandlerStruct *)(sIntHandler[handlerIndex].dNextIntHandler))->ui8PreviousElementIndex = 0xff;
336                             if ( ((IntHandlerStruct *)(sIntHandler[handlerIndex].dNextIntHandler))->dNextIntHandler == NULL  )
337                             {
338                                 // If the new handler is the only handler, then register the handler directly.
339 #ifdef XTOS
340                                 xtos_set_interrupt_handler( sIntHandler[handlerIndex].ui5IntterruptNo, ((IntHandlerStruct *)(sIntHandler[handlerIndex].dNextIntHandler))->pHandler, ((IntHandlerStruct *)(sIntHandler[handlerIndex].dNextIntHandler)), NULL);
341 #elif XOS
342                                 xos_register_interrupt_handler(sIntHandler[handlerIndex].ui5IntterruptNo, ((IntHandlerStruct *)(sIntHandler[handlerIndex].dNextIntHandler))->pHandler, ((IntHandlerStruct *)(sIntHandler[handlerIndex].dNextIntHandler)) );
343 #endif
344                             }
345                             else
346                             {
347                                 // if more than one handler is in the list, then update the chain with the new linked-list.
348 #ifdef XTOS
349                                 xtos_set_interrupt_handler( sIntHandler[handlerIndex].ui5IntterruptNo, (void *) am_hal_dsp_dispatcher, ((void *)(sIntHandler[handlerIndex].dNextIntHandler)), NULL);
350 #elif XOS
351                                 xos_register_interrupt_handler(sIntHandler[handlerIndex].ui5IntterruptNo, am_hal_dsp_dispatcher, sIntHandler[nextFreeHandlerIndex] );
352 #endif
353                             }
354                         }
355                     }
356                     else
357                     {
358                         // This is not the first element in the list.
359                         // Find the previous element and then connect the previous element to the next element.
360                         sIntHandler[ sIntHandler[handlerIndex].ui8PreviousElementIndex ].dNextIntHandler = sIntHandler[handlerIndex].dNextIntHandler;
361                     }
362 // end of critical section
363                 }
364             }
365         }
366         return 0;
367     }
368 
369     // find the next free handler from the pool
370     if ( sHalSharedMem->sHalDSPInt[ui32Interrupt].intNo == 31 )
371     {
372         //  no valid interrupt is assigned for this peripherals in shared memory
373         return -2;
374     }
375     for ( handlerIndex = 0; handlerIndex < MAX_NO_OF_HANDLERS; handlerIndex++ )
376     {
377         if ( sIntHandler[handlerIndex].pHandler == NULL )
378         {
379             if ( nextFreeHandlerIndex == -1)
380             {
381                 // a free Handler found in the pool
382                 // add the handler to the pool
383                 sIntHandler[handlerIndex].pHandler        = pHandler;
384                 sIntHandler[handlerIndex].pHandlerCtxt    = pHandlerCtxt;
385                 sIntHandler[handlerIndex].ui5IntterruptNo = sHalSharedMem->sHalDSPInt[ui32Interrupt].intNo;
386                 statusIndex = ui32Interrupt >> 5;         // find the index address for the status register
387                 //sIntHandler[handlerIndex].pIRQStatusRegAdd= &(( (uint32_t *)(&(DSPRAWIRQSTAT0)) )[statusIndex]);
388                 // the status register bit
389                 sIntHandler[handlerIndex].IRQStatusRegVal = 1 << (ui32Interrupt & 0x1f );
390                 sIntHandler[handlerIndex].dNextIntHandler = 0;
391                 sIntHandler[handlerIndex].ui8PreviousElementIndex = 0xff;         // for now assume this is not the first interrupt in the linked-list
392                 nextFreeHandlerIndex = handlerIndex;
393             }
394         }
395         else
396         {
397             // Here  a brute force search is done to find the first and last element in the linked-list. This is just for more safety.
398             // for all Handlers in the pool, look for those with the same DSP interrupt No.
399             if (sIntHandler[handlerIndex].ui5IntterruptNo == sHalSharedMem->sHalDSPInt[ui32Interrupt].intNo )
400             {
401                 // find the first handler in the linked-list.
402                 if ( sIntHandler[handlerIndex].ui8PreviousElementIndex == 0xff)
403                 {
404                     // locate the first handler and use its address for dispatcher
405                     firstHandlerIndex = handlerIndex;
406                 }
407                 // find the last handler in the linked-list.
408                 if ( sIntHandler[handlerIndex].dNextIntHandler == 0 )
409                 {
410                     if ( lastHandlerIndex != -1)
411                     {
412                         //assert. The linked-list has multiple ends.
413                     }
414                     lastHandlerIndex = handlerIndex;
415                 }
416             }
417         }
418     }
419     if ( nextFreeHandlerIndex == -1 )
420     {
421         // No free space available in the pool. Solution:
422         // 1- Make sure to use unregister function when any handler is not needed any more. The unregister function
423         // remove the handler from the pool.
424         // 2- If the number of handlers are more than MAX_NO_OF_HANDLERS, increase MAX_NO_OF_HANDLERS.
425         return -1;
426     }
427 // critical section starts here
428     if ( lastHandlerIndex >= 0)
429     {
430         // add the handler to the end of the linked-list.
431         sIntHandler[lastHandlerIndex].dNextIntHandler = (void*)&sIntHandler[nextFreeHandlerIndex];
432         // Connect the previous handler in the linked-list to the current handler.
433         // The previous handler is useful for unregister an interrupt(removing from the linked-list)
434         sIntHandler[nextFreeHandlerIndex].ui8PreviousElementIndex = lastHandlerIndex;
435 
436         // Multiple handlers are registered for a single DSP interrupt.
437         // So register the common interrupt handler (am_hal_dsp_dispatcher).
438         if ( firstHandlerIndex == -1 )
439         {
440             //assert
441         }
442 #ifdef XTOS
443         xtos_set_interrupt_handler( sIntHandler[nextFreeHandlerIndex].ui5IntterruptNo, (xtos_handler)(void * )am_hal_dsp_dispatcher, (void *)&sIntHandler[firstHandlerIndex], NULL);
444 #elif XOS
445         xos_register_interrupt_handler(sIntHandler[nextFreeHandlerIndex].ui5IntterruptNo, am_hal_dsp_dispatcher, sIntHandler[firstHandlerIndex] );
446 #else
447         #error "XTOS or XOS must be defined."
448 #endif
449     }
450     else
451     {
452         // There is no other handlers registered for this interrupt
453         // So register the pHandler directly inside the XTOS
454         // sIntHandler[nextFreeHandlerIndex].ui8PreviousElementIndex = 0xff;// this is the first interrupt in the linked-list
455 #ifdef XTOS
456         xtos_set_interrupt_handler(sIntHandler[nextFreeHandlerIndex].ui5IntterruptNo, pHandler, pHandlerCtxt, NULL);
457 #elif XOS
458         xos_register_interrupt_handler(sIntHandler[nextFreeHandlerIndex].ui5IntterruptNo, pHandler, pHandlerCtxt );
459 #else
460         #error "XTOS or XOS must be defined."
461 #endif
462     }
463 // end of critical section
464 }
465 
466 //*****************************************************************************
467 //
468 // End Doxygen group.
469 //! @}
470 //
471 //*****************************************************************************
472