3 * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
5 * Permission is hereby granted, free of charge, to any person obtaining a copy of
6 * this software and associated documentation files (the "Software"), to deal in
7 * the Software without restriction, including without limitation the rights to
8 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9 * the Software, and to permit persons to whom the Software is furnished to do so,
10 * subject to the following conditions:
12 * The above copyright notice and this permission notice shall be included in all
13 * copies or substantial portions of the Software.
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 * https://www.FreeRTOS.org
23 * https://github.com/FreeRTOS
28 * This demo demonstrates the best practice of deferring majority of the
29 * interrupt processing work from IRQ handlers to an unprivileged task. IRQ
30 * handlers execute in privileged mode and therefore, have access to all the
31 * memories. Keeping the IRQ handlers small and deferring most of the work to
32 * unprivileged task provides better security.
34 * This demo creates a queue, known as interrupt queue. IRQ handlers post
35 * requests to this queue which are later served by an unprivileged interrupt
38 * +-------------------------+
39 * +--------->+ Interrupt Queue +------------+
40 * | +-------------------------+ |
45 * +------+--------+ | |
47 * | (Privileged) | | Interrupt |
48 * +---------------+ | Handler |
55 * This demo show-cases the following 2 demos:
56 * 1. LED Demo - The LED demo creates a task which periodically toggles the on
57 * board RGB LED. Whenever user presses the USER button, the
58 * user button pressed IRQ handler changes the color of the LED.
59 * The user button pressed IRQ handler posts a request to the
60 * interrupt queue and task of changing the LED color is deferred
61 * to the unprivileged handler.
62 * 2. UART Demo - The UART demo creates a task which prints a command menu on
63 * the serial console and then waits for the user input. Whenever
64 * user enters any input on the serial console to run a command,
65 * the UART received IRQ handler handles it and prints the
66 * appropriate response on the serial console. The UART received
67 * IRQ handler posts a request to the interrupt queue and task of
68 * handling the command and providing the response is deferred
69 * to the unprivileged handler.
72 /* FreeRTOS kernel includes. */
79 #include "clock_config.h"
80 #include "fsl_debug_console.h"
81 #include "fsl_device_registers.h"
82 #include "fsl_inputmux.h"
83 #include "fsl_power.h"
85 #include "fsl_usart.h"
90 #include "uart_demo.h"
91 #include "interrupt_handler_task.h"
94 * @brief Length of the interrupt queue.
96 #define USER_IRQ_QUEUE_LEN 8
97 #define USER_IRQ_QUEUE_BUF_SIZE ( sizeof( UserIrqRequest_t ) * USER_IRQ_QUEUE_LEN )
100 * @brief UART_MEM_START to (UART_MEM_START + UART_MEM_LEN) cover the range
101 * used by USART_WriteBlocking().
103 #define UART_MEM_START ( USART0 )
104 #define UART_MEM_LEN ( 3648 )
107 * @brief GPIO_MEM_START to (GPIO_MEM_START + GPIO_MEM_LEN) covers the range
108 * used for setting and clearing GPIO pins.
110 #define GPIO_MEM_START ( 0x4008E200u )
111 #define GPIO_MEM_LEN ( 256 )
114 * @brief Handle and storage for the interrupt queue shared between IRQ handlers
115 * and the interrupt handler task.
117 static QueueHandle_t xUserIrqQueueHandle;
118 static StaticQueue_t xStaticQueue;
119 static uint8_t ucQueueBuffer[ USER_IRQ_QUEUE_BUF_SIZE ];
120 /*-----------------------------------------------------------*/
123 * @brief Initialize hardware.
125 static void prvInitializeHardware( void );
128 * @brief Initialize user button pressed interrupt.
130 static void prvInitializeUserButtonInterrupt( void );
133 * @brief Initialize UART received interrupt.
135 static void prvInitializeUartReceivedInterrupt( void );
138 * @brief Handler for pin interrupt when USER button is pressed.
140 * The handler enqueues a user IRQ request for the IRQ to be further processed
141 * in the unprivileged interrupt handler.
143 static void userButtonPressedHandler( pint_pin_int_t xInterruptType, uint32_t ulMatchStatus );
146 * @brief Handler for UART interrupt when UART data is received.
148 * The handler enqueues a user IRQ request for the IRQ to be further processed
149 * in the unprivileged interrupt handler.
151 void FLEXCOMM0_IRQHandler( void );
154 * @brief Create the demo tasks.
156 static void prvCreateDemoTasks( void );
157 /*-----------------------------------------------------------*/
159 static void prvInitializeHardware( void )
161 /* Set BOD VBAT level to 1.65V. */
162 POWER_SetBodVbatLevel( kPOWER_BodVbatLevel1650mv, kPOWER_BodHystLevel50mv, false );
164 /* Attach main clock divide to FLEXCOMM0 (debug console). */
165 CLOCK_AttachClk( BOARD_DEBUG_UART_CLK_ATTACH );
167 /* Board initialization. */
168 BOARD_InitBootPins();
169 BOARD_InitBootClocks();
170 BOARD_InitDebugConsole();
172 /*-----------------------------------------------------------*/
174 static void prvInitializeUserButtonInterrupt( void )
176 /* Connect trigger sources to PINT. */
177 INPUTMUX_Init( INPUTMUX );
178 INPUTMUX_AttachSignal( INPUTMUX, kPINT_PinInt0, kINPUTMUX_GpioPort1Pin9ToPintsel );
180 /* Turnoff clock to inputmux to save power. Clock is only needed to make changes. */
181 INPUTMUX_Deinit( INPUTMUX );
183 /* Initialize PINT. */
186 /* Setup Pin Interrupt 0 for rising edge. */
187 PINT_PinInterruptConfig( PINT, kPINT_PinInt0, kPINT_PinIntEnableRiseEdge, userButtonPressedHandler );
189 /* Enable callback for PINT0 by Index. */
190 PINT_EnableCallbackByIndex( PINT, kPINT_PinInt0 );
192 /*-----------------------------------------------------------*/
194 static void prvInitializeUartReceivedInterrupt( void )
196 usart_config_t config;
197 USART_GetDefaultConfig( &( config ) );
198 config.enableTx = true;
199 config.enableRx = true;
200 USART_Init( USART0, &( config ), CLOCK_GetFlexCommClkFreq( 0U ) );
202 /* Enable RX interrupt. */
203 USART_EnableInterrupts( USART0, kUSART_RxLevelInterruptEnable | kUSART_RxErrorInterruptEnable );
204 EnableIRQ( FLEXCOMM0_IRQn );
206 /*-----------------------------------------------------------*/
208 static void userButtonPressedHandler( pint_pin_int_t xInterruptType, uint32_t ulMatchStatus )
210 UserIrqRequest_t xIrqRequest;
211 BaseType_t xHigherPriorityTaskWoken;
213 /* Silence warnings about unused variables. */
214 ( void ) xInterruptType;
215 ( void ) ulMatchStatus;
217 /* We have not woken a task at the start of the ISR. */
218 xHigherPriorityTaskWoken = pdFALSE;
220 /* Enqueue a request to user IRQ queue to be processed in the unprivileged
221 * interrupt handler. */
222 xIrqRequest.xHandlerFunction = vButtonPressedIRQHandler;
223 xIrqRequest.ulData = 0; /* Not used. */
224 xQueueSendFromISR( xUserIrqQueueHandle, &( xIrqRequest ), &( xHigherPriorityTaskWoken ) );
226 /* Posting the above request might have unblocked the interrupt handler task.
227 * Make sure to return to the interrupt handler task in case it was not already
229 portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
231 /*-----------------------------------------------------------*/
233 /* Override weak definition of FLEXCOMM0_IRQHandler */
234 void FLEXCOMM0_IRQHandler(void)
236 UserIrqRequest_t xIrqRequest;
237 BaseType_t xHigherPriorityTaskWoken;
239 /* We have not woken a task at the start of the ISR. */
240 xHigherPriorityTaskWoken = pdFALSE;
242 /* If new data arrived. */
243 if( ( kUSART_RxFifoNotEmptyFlag | kUSART_RxError ) & USART_GetStatusFlags( USART0 ) )
245 /* Enqueue a request to user IRQ queue to be processed in the unprivileged
246 * interrupt handler. */
247 xIrqRequest.xHandlerFunction = vUartDataReceivedIRQHandler;
248 xIrqRequest.ulData = ( uint32_t ) USART_ReadByte( USART0 );
249 xQueueSendFromISR( xUserIrqQueueHandle, &( xIrqRequest ), &( xHigherPriorityTaskWoken ) );
252 /* Posting the above request might have unblocked the interrupt handler task.
253 * Make sure to return to the interrupt handler task in case it was not already
255 portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
257 /*-----------------------------------------------------------*/
259 static void prvCreateDemoTasks( void )
261 static StackType_t xInterruptHandlerTaskStack[ configMINIMAL_STACK_SIZE ] __attribute__( ( aligned( 32 ) ) );
262 static StackType_t xLedDemoTaskStack[ configMINIMAL_STACK_SIZE ] __attribute__( ( aligned( 32 ) ) );
263 static StackType_t xUartDemoTaskStack[ configMINIMAL_STACK_SIZE ] __attribute__( ( aligned( 32 ) ) );
265 extern TaskHandle_t xUartDemoTaskHandle;
266 extern uint32_t __user_irq_shared_memory_start__[];
267 extern uint32_t __user_irq_shared_memory_end__[];
268 uint32_t ulSharedMemoryLength = ( uint32_t )__user_irq_shared_memory_end__ - ( uint32_t )__user_irq_shared_memory_start__ + 1;
270 /* The interrupt handler task needs access to UART memory region too as we
271 * write the response to UART from the interrupt handler in the UART demo. */
272 TaskParameters_t xInterruptHandlerTaskParameters =
274 .pvTaskCode = vInterruptHandlerTask,
275 .pcName = "InterruptHandlerTask",
276 .usStackDepth = configMINIMAL_STACK_SIZE,
277 .pvParameters = xUserIrqQueueHandle,
278 .uxPriority = configMAX_PRIORITIES - 1, /* Run the interrupt handler task at the highest priority. */
279 .puxStackBuffer = xInterruptHandlerTaskStack,
282 { __user_irq_shared_memory_start__, ulSharedMemoryLength, tskMPU_REGION_READ_WRITE | tskMPU_REGION_EXECUTE_NEVER },
283 { ( void * ) UART_MEM_START, UART_MEM_LEN, tskMPU_REGION_READ_WRITE | tskMPU_REGION_EXECUTE_NEVER },
287 TaskParameters_t xLedDemoTaskParameters =
289 .pvTaskCode = vLedDemoTask,
290 .pcName = "LedDemoTask",
291 .usStackDepth = configMINIMAL_STACK_SIZE,
292 .pvParameters = NULL,
293 .uxPriority = tskIDLE_PRIORITY,
294 .puxStackBuffer = xLedDemoTaskStack,
297 { __user_irq_shared_memory_start__, ulSharedMemoryLength, tskMPU_REGION_READ_WRITE | tskMPU_REGION_EXECUTE_NEVER },
298 { ( void * ) GPIO_MEM_START, GPIO_MEM_LEN, tskMPU_REGION_READ_WRITE | tskMPU_REGION_EXECUTE_NEVER },
302 TaskParameters_t xUartDemoTaskParameters =
304 .pvTaskCode = vUartDemoTask,
305 .pcName = "UartDemoTask",
306 .usStackDepth = configMINIMAL_STACK_SIZE,
307 .pvParameters = NULL,
308 .uxPriority = tskIDLE_PRIORITY,
309 .puxStackBuffer = xUartDemoTaskStack,
312 { __user_irq_shared_memory_start__, ulSharedMemoryLength, tskMPU_REGION_READ_WRITE | tskMPU_REGION_EXECUTE_NEVER },
313 { ( void * ) UART_MEM_START, UART_MEM_LEN, tskMPU_REGION_READ_WRITE | tskMPU_REGION_EXECUTE_NEVER },
318 xTaskCreateRestricted( &( xInterruptHandlerTaskParameters ), NULL );
319 xTaskCreateRestricted( &( xLedDemoTaskParameters ), NULL );
320 xTaskCreateRestricted( &( xUartDemoTaskParameters ), &( xUartDemoTaskHandle ) );
322 /*-----------------------------------------------------------*/
325 * @brief Entry point.
329 /* Initialize board hardware. */
330 prvInitializeHardware();
332 /* Create the interrupt queue for deferring work from ISRs to the
333 * unprivileged interrupt handler task. */
334 xUserIrqQueueHandle = xQueueCreateStatic( USER_IRQ_QUEUE_LEN,
335 sizeof( UserIrqRequest_t ),
339 prvCreateDemoTasks();
340 prvInitializeUserButtonInterrupt();
341 prvInitializeUartReceivedInterrupt();
343 vTaskStartScheduler();
345 /* Should never reach here. */
348 /*-----------------------------------------------------------*/
350 /* Stack overflow hook. */
351 void vApplicationStackOverflowHook( TaskHandle_t xTask, char *pcTaskName )
353 /* Force an assert. */
354 configASSERT( pcTaskName == 0 );
356 /*-----------------------------------------------------------*/
358 /* configUSE_STATIC_ALLOCATION is set to 1, so the application must provide an
359 * implementation of vApplicationGetIdleTaskMemory() to provide the memory that
360 * is used by the Idle task. */
361 void vApplicationGetIdleTaskMemory( StaticTask_t ** ppxIdleTaskTCBBuffer,
362 StackType_t ** ppxIdleTaskStackBuffer,
363 uint32_t * pulIdleTaskStackSize )
365 /* If the buffers to be provided to the Idle task are declared inside this
366 * function then they must be declared static - otherwise they will be
367 * allocated on the stack and so not exists after this function exits. */
368 static StaticTask_t xIdleTaskTCB;
369 static StackType_t uxIdleTaskStack[ configMINIMAL_STACK_SIZE ] __attribute__( ( aligned( 32 ) ) );
371 /* Pass out a pointer to the StaticTask_t structure in which the Idle
372 * task's state will be stored. */
373 *ppxIdleTaskTCBBuffer = &xIdleTaskTCB;
375 /* Pass out the array that will be used as the Idle task's stack. */
376 *ppxIdleTaskStackBuffer = uxIdleTaskStack;
378 /* Pass out the size of the array pointed to by *ppxIdleTaskStackBuffer.
379 * Note that, as the array is necessarily of type StackType_t,
380 * configMINIMAL_STACK_SIZE is specified in words, not bytes. */
381 *pulIdleTaskStackSize = configMINIMAL_STACK_SIZE;
383 /*-----------------------------------------------------------*/
385 /* configUSE_STATIC_ALLOCATION and configUSE_TIMERS are both set to 1, so the
386 * application must provide an implementation of vApplicationGetTimerTaskMemory()
387 * to provide the memory that is used by the Timer service task. */
388 void vApplicationGetTimerTaskMemory( StaticTask_t ** ppxTimerTaskTCBBuffer,
389 StackType_t ** ppxTimerTaskStackBuffer,
390 uint32_t * pulTimerTaskStackSize )
392 /* If the buffers to be provided to the Timer task are declared inside this
393 * function then they must be declared static - otherwise they will be
394 * allocated on the stack and so not exists after this function exits. */
395 static StaticTask_t xTimerTaskTCB;
396 static StackType_t uxTimerTaskStack[ configTIMER_TASK_STACK_DEPTH ] __attribute__( ( aligned( 32 ) ) );
398 /* Pass out a pointer to the StaticTask_t structure in which the Timer
399 * task's state will be stored. */
400 *ppxTimerTaskTCBBuffer = &xTimerTaskTCB;
402 /* Pass out the array that will be used as the Timer task's stack. */
403 *ppxTimerTaskStackBuffer = uxTimerTaskStack;
405 /* Pass out the size of the array pointed to by *ppxTimerTaskStackBuffer.
406 * Note that, as the array is necessarily of type StackType_t,
407 * configTIMER_TASK_STACK_DEPTH is specified in words, not bytes. */
408 *pulTimerTaskStackSize = configTIMER_TASK_STACK_DEPTH;
410 /*-----------------------------------------------------------*/