Dear Group:
I am having a FreeRTOS problem which I believe is due to incorrect
interrupt priority configuration.
The generic hardware is the Cortex M4 and the LPC4088 processor running on
on an Embedded Artist's LPC4088-32 Developer's Kit in particular. (i.e. The FreeRTOS
version is V8.0.1.)
Interrupts generated by LPC4088 UART2 are associated with NVIC interrupt
UART2IRQn and handled by the UART2IRQHandler() interrupt handler. It signals
the ModemUARTEventHandler() task with the BTUARTDataSignal semaphore.
The symptoms of the problem are as follow:
1) A UART2 interrupt is generated, UART2IRQHandler() signals MODEMUARTEventHandler()
of the event via the BTUARTDataSignal semaphore and the event is successfully
handled. At this point MODEMUARTEventHandler() task has called xSemaphoreTake()
once.
2) The MODEMUARTEvent_Handler() call xSemaphoreTake() again and crashes as follows:
In the FreeRTOS ListInsert() routine the crash occurs when the following line
of code executes:
pxNewListItem->pxPrevious = pXIterator;
The sequence of FreeRTOS calls is as follows:
SemaphoreTake();
QueueGenericReceive();
TaskPlaceOnEventList();
ListInsert();
The definitions and code I consider relevant follow below.
The relevant UART definitions are as follows:
~~~
define BTUART LPCUART2
define BTIRQ UART2IRQn
~~~
The relevant definitions from FreeRTOSConfig.h are as follows.
~~~
define configMAX_PRIORITIES ( 5 )
define configPRIO_BITS 4 /* 15 priority levels */
define configLIBRARYLOWESTINTERRUPT_PRIORITY 0xf
define configLIBRARYMAXSYSCALLINTERRUPTPRIORITY 5
/* Interrupt priorities used by the kernel port layer itself. These are generic
to all Cortex-M ports, and do not rely on any particular library functions. */
define configKERNELINTERRUPTPRIORITY ( configLIBRARYLOWESTINTERRUPTPRIORITY << (8 - configPRIOBITS) )
/* !!!! configMAXSYSCALLINTERRUPT_PRIORITY must not be set to zero !!!!
See http://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html. */
define configMAXSYSCALLINTERRUPTPRIORITY ( configLIBRARYMAXSYSCALLINTERRUPTPRIORITY << (8 - configPRIOBITS) )
~~~
The priority initialization for the interrupt handler for UART2 is as follows and
the interrupt handler follows below:
~~~
unsigned int pri;
//
// Finally enable the IRQ interrupt.
// The priority of SysTickIRQn is 30, and that of TIMER1IRQn is 29
// Set the UART1IRQn to 28 as it is the highest priority less than
// that of TIME1IRQn.
//
// The LPC4088 interrupt priority range is as follows:
//
// HIGH: 0 - 15
// LOW: 16 - 31
//
pri = ((1u << __NVICPRIOBITS) - 2u) - 2u; // SysTickIRQn
pri -= 3;
NVICSetPriority(BT_IRQ, pri);
~~~
This is the relevant task and semaphore initialization:
~~~
BTUARTDataSignal = xSemaphoreCreateBinary();
//
// BTMODEMHANDLERTASK - Highest Priority - Almost
//
xTaskCreate(ModemUARTEventHandler, (signed char *) "MODEM/UART",
configMINIMALSTACKSIZE, NULL, (tskIDLEPRIORITY + 3UL),
&task_id);
~~~
This is the relevant portion of the task:
~~~
TASK void ModemUARTEventHandler(void *parm)
{
INTERTASKMSG msg;
UINT ecause, n;
vTaskDelay(configTICK_RATE_HZ * 3);
for(;;)
{
//
// Wait until the UART #2 interrupt handler signals that data is available.
//
xSemaphoreTake(BT_UART_Data_Signal, portMAX_DELAY);
switch(MU_Event.type)
{
case MU_XE_TXFIFO_EMPTY:
if(MU_Data->mode == FRAME_MODE)
{
msg.event = IEV_BT_FIFO_EMPTY;
msg.data = NULL;
msg.length = 0;
X_SEND_MSG(LINK_HANDLER_TASK, &msg, X_HI_PRIO);
}
else
atd_tx_stored(TRUE);
break;
case MU_XE_RXDATA:
n = Chip_UART_ReadRB(BT_UART, &MU_Data->rx_ring,
(void *) MU_Data->rx_data_buf, BT_MAX_RING_BUF);
if(MU_Data->mode == FRAME_MODE)
link_handle_data(MU_Data->rx_data_buf, n);
else
atd_rx_data(MU_Data->rx_data_buf, n);
break;
case MU_XE_PHY_ERROR:
ecause = MU_Event.cause;
bt_rx_phy_error(ecause);
break;
default:
//
// BUG
//
abort();
}
}
}
~~~
Finally, this is the interrupt handler:
~~~
ROUTINE void UART2IRQHandler(void)
{
BaseTypet hiprioalert;
UINT cause;
MU_Event.type = MU_XE_INVALID;
MU_Event.cause = 0;
//
// Examine te IIR register to determine the interrupt cause.
//
cause = Chip_UART_ReadIntIDReg(BT_UART);
cause &= 0xFF;
//
// Ignore a spurious interrupt.
//
if( !(cause & UART_IIR_INTSTAT_PEND) )
{
if( (cause & (UART_IIR_INTID_THRE | UART_IIR_INTID_RDA)) ||
( (cause & UART_IIR_INTID_CTI) == UART_IIR_INTID_CTI ) )
{
//
// In the case of a RBR or THR interrupt, this the following routine
// manages Ring Buffer and FIFO data transfer transactions.
//
// After the data transfer, notify the BT_MODEM_HANLDER_TASK that
// data is available for reading, or that a data transmission operation
// is complete.
//
Chip_UART_IRQRBHandler(BT_UART, &MU_Data->rx_ring, &MU_Data->tx_ring);
if( cause & UART_IIR_INTID_THRE )
MU_Event.type = MU_XE_TXFIFO_EMPTY;
else
MU_Event.type = MU_XE_RXDATA;
}
else
if( (cause & UART_IIR_INTID_RLS) == UART_IIR_INTID_RLS )
{
MU_Event.type = MU_XE_PHY_ERROR;
MU_Event.cause = Chip_UART_ReadLineStatus(BT_UART);
MU_Event.cause &= 0xFF;
}
else
{
//
// BUG: Invalid cause.
//
abort();
}
//
// Signal the BT_MODEM_HANDLER_TASK of an event.
//
xSemaphoreGiveFromISR(BT_UART_Data_Signal, &hi_prio_alert);
}
else
{
//
// Spurious interrupt
//
;
}
//
// The BT_MODEM_HANDLER_TASK has a higher priority than almost
// all other tasks which could be executing. If that is the case,
// force a context switch to the BTMODEMHANDLERTASK.
//
portENDSWITCHINGISR(hiprio_alert);
}
~~~
First, tripple check the value of
__NVICPRIOBITS
,
as this has been the cause of some confusion over the years with the NXP headers and documentation mismatching. For example, a quick web search finds it set to 3 in this file: http://www.keil.com/dd/docs/arm/nxp/lpc43xx/lpc43xx.h (you have it set to 4) and a comment that it has been corrected in this change history: http://cdn.rowleydownload.co.uk/arm/packages/LPC4300.htm
Do you have configASSERT() defined? http://www.freertos.org/a00110.html#configASSERT That should assist in finding a priority assignment error - it might be worth updating to a newer version of FreeRTOS to as newer versions have additional assert() points for better error detection.
There is a conflict between the number of NVIC interrupt priority bits of the LPC4088 Cortex M4
processor as defined by NXP versus CMIS3. NXP defines 5 bits and 32 priority levels split
into two groups--the maskable group is appropriate for use with FreeRTOS. CMIS3 defines 4
bits and 16 priority levels by necessity.
(i.e. The target environment is an LPC4088 processor on the Embedded Artist's LPC4088-32
Developer's Kit board.)
I am fairly certain that the NXP 5 bit definition is correct.
When _NVICPRIOBITS is set to 5 bits in my environment and xSemaporeGiveFromISR(), called from the interrupt handler,
correctly signals the ModemUARTHandler() task with
BTUARTDataSignal semaphore. In that case the priority
of SysTickIRQn = 30 and can be computed as follows:
(1 << NVIC_PRIO_BITS) - 2;
The important FreeRTOSConfig.h definitions should be as follows:
#define configPRIO_BITS 5 /* 32 priority levels */
#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY 0x1f
It isn't clear if configLIBRARYMAXSYSCALLINTERRUPTPRIORITY should
be set but it is set to 5 as in the current configuration and any clues
would be appreciated. If the value is 5, the encoded value,
configMAXSYSCALLINTERRUPT_PRIORITY, is 40.
In LPC407x8x177x_8x.h the value is as follows:
#define __NVIC_PRIO_BITS 5 /* !< Number of Bits used for Priority Levels */
This seems reasonable since a great deal of Cortex M4 documentation indicates
there are 32 rather than 15 priority levels. Chapter 5 section 3.5
of the LPC17xx edition of "Using the FreeRTOS Real Time Kernel" indicates there
are 32 priority levels. Section 5.5 of the UM10562--The LPC408x/LPC407x User Manual,
indicates there are 5 interrupt bits and 32 levels as well.
In CMSIS/Include/core_cm3.h the value is as follows:
#define __NVIC_PRIO_BITS 4
Best Regards,
Paul R.
It isn't so clear how configLIBRARYMAXSYSCALLINTERRUPTPRIORITY should
be set but it is set to 5 in the current configuration. This is important
because the priority of an interrupt that uses xSemaphoreGiveFromISR() must
be numerically greater than configLIBRARYMAXSYSCALLINTERRUPTPRIORITY
if I
understand the documentation correctly. Is this right ?
Here is an example for 5 bits:
/* Use the system definition, if there is one */
#ifdef __NVIC_PRIO_BITS
#define configPRIO_BITS __NVIC_PRIO_BITS
#else
#define configPRIO_BITS 5
#endif
#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY 0x1f
#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 5
/* The lowest priority. */
#define configKERNEL_INTERRUPT_PRIORITY (
configLIBRARY_LOWEST_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )
#define configMAX_SYSCALL_INTERRUPT_PRIORITY (
configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )
See http://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html.
Yes it must be lower than configMAXSYSCALLINTERRUPT_PRIORITY - which
means a higher numerical value. See the link above.
NVICSetPriority(irq, (1 << (__NVICPRIO_BITS - 1)) + priority);
Why not just
NVIC_SetPriority(irq, configMAX_LIBRARY_INTERRUPT_PRIORITY + whatever );
Please clarify the recommendation about NVICSetPriority(). Do you
really mean to use configMAXLIBRARYINTERRUPTPRIORITY = 5 or
configMAXSYSCALLINTERRUPT_PRIORITY = 40 ?
I mean you really have to set the parameter to whatever the processor
actually needs - but you will find, as others have, that different
documents and source files say different things about what the processor
actually needs, so you will first have to work that out (which can be
done by experimentation - you can write 0xff into one of the priority
registers, then read the value back, and see how many of the bits
actually got set).
Copyright (C) Amazon Web Services, Inc. or its affiliates. All rights reserved.