EmbeddedRelated.com
Forums
Memfault Beyond the Launch

FreeRTOS 6.1.0, LPC1768, using queue from ISR causes hardfault

Started by Bastiaan van Kesteren January 10, 2011
Hi,

This might be more of a FreeRTOS question then an LPC one, buth this being
such a high-traffic list with lots of FreeRTOS
questions anyways, I'll ask here first...

I'm using FreeRTOS 6.1.0 on an LPC1768. Now i'm trying to use a queue from the
UART0 ISR, which causes a hardfault
in the pendsv_handler, which is called when leaving the ISR. I *think* i got
the Cortex-M3 interrupt priority thing right.
Anyone got some idea's what i'm doing wrong?

I have configKERNEL_INTERRUPT_PRIORITY and
configMAX_SYSCALL_INTERRUPT_PRIORITY set at the defaults:

#define configKERNEL_INTERRUPT_PRIORITY ( 31 << (8 - configPRIO_BITS) )
#define configMAX_SYSCALL_INTERRUPT_PRIORITY ( 5 << (8 -
configPRIO_BITS) )

(with __NVIC_PRIO_BITS being 5)

I have the UART0 interrupt set up like this:

LPC_UART0->IER |= (1<<0);
NVIC_SetPriority(UART0_IRQn, INTPRIO_UART0);
NVIC_EnableIRQ(UART0_IRQn);

with INTPRIO_UART0 at 10 (the NVIC_* functions are the unmodified CMSIS
functions, by the way)

I got the queue defined as follows:

xQueueHandle ConsoleQueue;

And initialised from main() like this:

if((ConsoleQueue = xQueueCreate( 5, sizeof(unsigned char))) == NULL) {
dprint("FATAL: not enough RAM for ConsoleQueue creation\n\r");
while(1);
}

The UART0 ISR looks like this:

void uart0_irqhandler(void) __attribute__((__interrupt__));
void uart0_irqhandler(void)
{
portBASE_TYPE xHigherPriorityTaskWoken;
unsigned char c;

/* We have not woken a task at the start of the ISR. */
xHigherPriorityTaskWoken = pdFALSE;

while(uart0_hasdata()) {
c = uart0_getchar();

if(xQueueSendToBackFromISR(ConsoleQueue,
&c,&xHigherPriorityTaskWoken) != pdPASS) {
dprint("uart0_irqhandler(): queue full\n\r");
}
}

/* Now the buffer is empty we can switch context if necessary. */
portEND_SWITCHING_ISR(xHigherPriorityTaskWoken);
}

On exit of the ISR, it is about to yield, thus calling the pendsv_handler,
which triggers a hardfault.

Any help is greatly appreciated.. I'm kind'a lost here, as all examples and
documentation i can find all seem to indicate i'm doing it the right way...

An Engineer's Guide to the LPC2100 Series

Point 3 on the following page is where I normally point people with
similar issues, but in this case you seem to have most bases covered:
http://www.freertos.org/FAQHelp.html

> #define configKERNEL_INTERRUPT_PRIORITY ( 31 << (8 - configPRIO_BITS)
> ) #define configMAX_SYSCALL_INTERRUPT_PRIORITY ( 5 << (8 -
> configPRIO_BITS) )>
So configMAX_SYSCALL_INTERRUPT_PRIORITY is 5 - a high priority.....

> with INTPRIO_UART0 at 10

...and the UART interrupt at 10, which is a lower priority than 5. So
this is correct, and what most people get wrong.

---------------------

What you don't say is how you have the priority grouping set. Are all 5
bits defined as being pre-emption priority, with zero bits as the sub
priority?

---------------------
> void uart0_irqhandler(void) __attribute__((__interrupt__));

I don't know what the __attribute__((__interrupt__) is going to do in
this case. I don't think it is necessary on a CM3 device (and rarely
works on an ARM7 device).

---------------------
> if(xQueueSendToBackFromISR(ConsoleQueue,
> &c,&xHigherPriorityTaskWoken) != pdPASS) {
> dprint("uart0_irqhandler(): queue full\n\r");
> }
I don't think this is related to your problem, but it is worth pointing
out that, although sending each character on a queue is used extensively
in demos as a convenient method of showing queues being used in
interrupts to communicate with tasks, it is an inefficient method. It
is generally best to use a simple circular RAM buffer, then a single
give to a semaphore to unblock a task when there is received data that
needs processing.

...however, the use of dprint() in an ISR might be relevant. What does
this function do? Is it ever being called? What happens when you
remove it?
---------------------

> On exit of the ISR, it is about to yield, thus calling the
> pendsv_handler,
> which triggers a hardfault.
At which point is the hard fault triggered? Setting the PendSV bit,
leaving the UART interrupt, entering the PendSV handler, or within the
PendSV handler? If its within the handler, can you say where?
---------------------
Usual other stuff also applies:

+ Do you have task stack overflow checking switched on?

+ Is your interrupt stack large enough?

+ Are you sure this is the source of your problem? Or is there a
possibility that you have other interrupts in your system that are
nesting with this interrupt, or executing between this interrupt
executing and the PendSV interrupt executing?

+ What happens when you set the priority of the UART interrupt down to
the priority of the PendSV interrupt?

Probably a longer answer than you wanted...
Regards,
Richard.

+ http://www.FreeRTOS.org
Designed for Microcontrollers. More than 7000 downloads per month.

+ http://www.SafeRTOS.com
Certified by T as meeting the requirements for safety related systems.

On 10/01/2011 11:34, Bastiaan van Kesteren wrote:
>
>
> Hi,
>
> This might be more of a FreeRTOS question then an LPC one, buth this being
> such a high-traffic list with lots of FreeRTOS
> questions anyways, I'll ask here first...
>
> I'm using FreeRTOS 6.1.0 on an LPC1768. Now i'm trying to use a queue
> from the
> UART0 ISR, which causes a hardfault
> in the pendsv_handler, which is called when leaving the ISR. I *think* i
> got
> the Cortex-M3 interrupt priority thing right.
> Anyone got some idea's what i'm doing wrong?
>
> I have configKERNEL_INTERRUPT_PRIORITY and
> configMAX_SYSCALL_INTERRUPT_PRIORITY set at the defaults:
>
> #define configKERNEL_INTERRUPT_PRIORITY ( 31 << (8 - configPRIO_BITS)
> ) #define configMAX_SYSCALL_INTERRUPT_PRIORITY ( 5 << (8 -
> configPRIO_BITS) )>
> (with __NVIC_PRIO_BITS being 5)
>
> I have the UART0 interrupt set up like this:
>
> LPC_UART0->IER |= (1<<0);
> NVIC_SetPriority(UART0_IRQn, INTPRIO_UART0);
> NVIC_EnableIRQ(UART0_IRQn);
>
> with INTPRIO_UART0 at 10 (the NVIC_* functions are the unmodified CMSIS
> functions, by the way)
>
> I got the queue defined as follows:
>
> xQueueHandle ConsoleQueue;
>
> And initialised from main() like this:
>
> if((ConsoleQueue = xQueueCreate( 5, sizeof(unsigned char))) == NULL) {
> dprint("FATAL: not enough RAM for ConsoleQueue creation\n\r");
> while(1);
> }
>
> The UART0 ISR looks like this:
>
> void uart0_irqhandler(void) __attribute__((__interrupt__));
> void uart0_irqhandler(void)
> {
> portBASE_TYPE xHigherPriorityTaskWoken;
> unsigned char c;
>
> /* We have not woken a task at the start of the ISR. */
> xHigherPriorityTaskWoken = pdFALSE;
>
> while(uart0_hasdata()) {
> c = uart0_getchar();
>
> if(xQueueSendToBackFromISR(ConsoleQueue,
> &c,&xHigherPriorityTaskWoken) != pdPASS) {
> dprint("uart0_irqhandler(): queue full\n\r");
> }
> }
>
> /* Now the buffer is empty we can switch context if necessary. */
> portEND_SWITCHING_ISR(xHigherPriorityTaskWoken);
> }
>
> On exit of the ISR, it is about to yield, thus calling the pendsv_handler,
> which triggers a hardfault.
>
> Any help is greatly appreciated.. I'm kind'a lost here, as all examples and
> documentation i can find all seem to indicate i'm doing it the right way...
>
>

Thanks for the reply and solution (jup, it works now!)

> What you don't say is how you have the priority grouping set. Are all 5
> bits defined as being pre-emption priority, with zero bits as the sub
> priority?

I had that going at the default reset value (0), but just added a

NVIC_SetPriorityGrouping(0);

to be sure it is really zero. No change in behavior

> > void uart0_irqhandler(void) __attribute__((__interrupt__));
>
> I don't know what the __attribute__((__interrupt__) is going to do in
> this case. I don't think it is necessary on a CM3 device (and rarely
> works on an ARM7 device).

I got that from a CodeSourgery hint:

"Because of a discrepancy between the ARMv7M Architecture and the ARM EABI it is not safe to use normal C functions directly as interrupt handlers.
The EABI requires the stack be 8-byte aligned, whereas ARMv7M only guarantees 4-byte alignment when calling an interrupt vector. This can cause subtle runtime failures, usually when 8-byte types are used. Functions that are used directly as
interrupt handlers should be annotated with __attribute__((__interrupt__)). This tells the compiler to add special stack alignment code to the function prologue."

After removing the attribute from the UART0 ISR, the software runs as expected! Perhaps the pendsv assembly assumes a certain stack-layout which this attribute changes?

The crash happend inside the pendsv function, after the 'mov r0, %0'. See below:

void pendsv_handler( void )
{
/* This is a naked function. */
__asm volatile
(
" mrs r0, psp \n"
" \n"
" ldr r3, pxCurrentTCBConst \n" /* Get the location of the current TCB. */
" ldr r2, [r3] \n"
" \n"
" stmdb r0!, {r4-r11} \n" /* Save the remaining registers. */
" str r0, [r2] \n" /* Save the new top of stack into the first member of the TCB. */
" \n"
" stmdb sp!, {r3, r14} \n"
" mov r0, %0 \n" <<<< HARDFAULT AT THIS INSTRUCTION
" msr basepri, r0 \n"
" bl vTaskSwitchContext \n"
" mov r0, #0 \n"
" msr basepri, r0 \n"
" ldmia sp!, {r3, r14} \n"
" \n" /* Restore the context, including the critical nesting count. */
" ldr r1, [r3] \n"
" ldr r0, [r1] \n" /* The first item in pxCurrentTCB is the task top of stack. */
" ldmia r0!, {r4-r11} \n" /* Pop the registers. */
" msr psp, r0 \n"
" bx r14 \n"
" \n"
" .align 2 \n"
"pxCurrentTCBConst: .word pxCurrentTCB \n"
::"i"(configMAX_SYSCALL_INTERRUPT_PRIORITY)
);
}

I'll investigate this further once i get the time, first i need to get the customer-requested functionallity going :)
> It is generally best to use a simple circular RAM buffer, then a single
> give to a semaphore to unblock a task when there is received data that
> needs processing.

Thanks for the tip, I'll look into it.

> ...however, the use of dprint() in an ISR might be relevant. What does
> this function do? Is it ever being called? What happens when you
> remove it?

It's a self-cooked basic printf function. In this case only called when the queue gets full, which never happend because of the crash.

> Probably a longer answer than you wanted...

But one with good suggestions and help.. Thanks!

Regards,
-Bastiaan

Memfault Beyond the Launch