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