FreeRTOS (ARM CM3) hardfaults when built with IAR optimisation enabled

I’ve spent the past two days chasing down an issue with FreeRTOS failing to run correctly when I build my application in “release” mode. Aside from turning off debug messages, this also enables some optimisations in the IAR C compiler. Long story short, if I build port.c, queue.c or tasks.c with IAR 7.20.5 and set optimisation any higher than Low, the code hard-faults shortly after the first task (a temporary, ephemeral Initialisation task named “INIT”) starts up. On further investigation, enabling Medium optimisation with Common Subexpression Elimination disabled allows a working version of port.c to be built. Sadly the same does not apply to tasks.c and queue.c; if either is built with optimisation set higher than Low, the hardfaults return. The optimisation level of my own code is irrelevant to the problem. I can build my application with optimisation on or off and the hardfaults remain. I’ve also found that disabling my idle hook (a low-power hook similar to the one in the examples) will also resolve the problem, irrespective of the optimisation level: ~~~~~~ void vApplicationIdleHook( void ) { PWREnterSleepMode(PWRRegulatorON, PWRSLEEPEntry_WFI); } ~~~~~~ What is going on here? Is this a compiler bug, a bug in my code or a bug in FreeRTOS? The target is an ST STM32L151RC (ARM Cortex M3), FreeRTOS 8.1.2, compiler is IAR 7.20.5. Thanks, Phil.

FreeRTOS (ARM CM3) hardfaults when built with IAR optimisation enabled

I have just tried running a Cortex-M4 ‘full’ demo with maximum ‘balanced’ optimisation and it is running quite happily.
Is this a compiler bug,
Could be, I have found bugs before, but generally the IAR tools are excellent.
a bug in my code
Could be.
or a bug in FreeRTOS?
Could be, but I’m not aware of any. Getting a definitive answer to those questions would be days of work, however your comment:
I’ve also found that disabling my idle hook (a low-power hook similar to the one in the examples) will also resolve the problem, irrespective of the optimisation level:
…would seem to negate the need for that if you can tie the issue down to one line of code. Superficially at least, it would see that PWR_EnterSleepMode() is not compatible with high optimisation in combination with FreeRTOS. That would seem to indicate neither a compiler, or FreeRTOS bug. Regards.

FreeRTOS (ARM CM3) hardfaults when built with IAR optimisation enabled

I’ve narrowed it down a little. My serial driver is based on ‘serial.c’ from the CORTEXSTM32F103IAR demo, with a few modifications to simplify the API and allow it to interface with IAR’s printf() implementation. The interrupt handler is identical to that in the demo. If I try to printf() more characters than there is space in the transmit queue, then the CPU starts executing code from an invalid address, soon causing a hardfault. I was expecting FreeRTOS to block the calling task until the queue emptied a little, resuming it when space was available. The task in question (INIT) is the only task running on the system (it’s responsible for running a few basic hardware checks and booting the drivers and the rest of the tasks). Unfortunately the data I’ve obtained from SWO is fairly useless – I don’t have enough program counter samples to say for definite where the bug is creeping in, I’ve checked interrupt priorities — here’s what I’m doing from the start of main():
// Set up the clock security system (clock monitor)
RCC_ClockSecuritySystemCmd(ENABLE);

/**
 * Set up the NVIC interrupt priority mode -- assigns all priority bits as
 * preempt priority bits (no subpriority).
 * This must be done before calling NVIC_Init(); it's easiest to do it as
 * the first init task.
 * Also see <http://www.freertos.org/RTOS-Cortex-M3-M4.html>.
 */
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);

// Trap divide-by-zero events
// NOTE - we used to trap unaligned accesses too, but IAR's CLib assumes that
// unaligned accesses are available on any platform which supports them (ewww).
SCB->CCR |= SCB_CCR_DIV_0_TRP;

// ----- Set up serial interface
debug_Init();

// Allow SWD debugging in low power mode
DBGMCU_Config(DBGMCU_SLEEP,   ENABLE);
DBGMCU_Config(DBGMCU_STOP,    ENABLE);
DBGMCU_Config(DBGMCU_STANDBY, ENABLE);

// Create the Init task
if (xTaskCreate(prvInitTask,
                "Init",
                configMINIMAL_STACK_SIZE + 500,
                NULL,
                tskIDLE_PRIORITY + (configMAX_PRIORITIES - 1),
                NULL) != pdPASS)
{
    //NVIC_SystemReset();
    for (;;);
}

// Start the scheduler
vTaskStartScheduler();
And if I turn optimisation off, everything works perfectly, no matter how big the queue is…

FreeRTOS (ARM CM3) hardfaults when built with IAR optimisation enabled

Oops – forgot to post my NVIC setup for the serial driver:
#define INTERRUPT_PRIORITY_DEBUG_UART       (configLIBRARY_LOWEST_INTERRUPT_PRIORITY)

// Configure NVIC
nvic.NVIC_IRQChannel = DEBUG_UART_NVIC_IRQ;
nvic.NVIC_IRQChannelPreemptionPriority = INTERRUPT_PRIORITY_DEBUG_UART;
nvic.NVIC_IRQChannelSubPriority = 0;
nvic.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&nvic);

FreeRTOS (ARM CM3) hardfaults when built with IAR optimisation enabled

So you are using a queue to send characters into and out of an interrupt. Please note that demos do that to demonstrate queues being used from interrupts, but generally it is not a good solution for production hardware if you require high throughput. It is fine for interfaces that, for example, except slow command line input. If the queue becomes full and you specify a block time then the task will be held in the Blocked space until either there is space in the queue or the block time times out. If the queue becomes full and you don’t specify a block time then the queue send function will return immediately (having not posted to the queue and returning pdFALSE). The interrupt configuration looks fine. Can you show the code that creates the queue, writes to the queue, and receives from the queue please. Regards.

FreeRTOS (ARM CM3) hardfaults when built with IAR optimisation enabled

Here’s the “create queue” code:
/// Receive queue
static xQueueHandle rxQueue;
/// Transmit queue
static xQueueHandle txQueue;

#define DEBUG_QUEUE_SIZE 64

portBASE_TYPE debug_Init(void)
{
GPIO_InitTypeDef init;
NVIC_InitTypeDef nvic;
USART_InitTypeDef uart;

// Create RX and TX queues
if ((rxQueue = xQueueCreate(DEBUG_QUEUE_SIZE, sizeof(portCHAR))) == NULL) {
    return pdFAIL;
}
vQueueAddToRegistry(rxQueue, "DBG Receive");
if ((txQueue = xQueueCreate(DEBUG_QUEUE_SIZE, sizeof(portCHAR))) == NULL) {
    vQueueDelete(rxQueue);
    return pdFAIL;
}
vQueueAddToRegistry(txQueue, "DBG Transmit");
Receive and Transmit:
portBASE_TYPE debug_getchar(portTickType timeout)
{
    uint8_t ch;
    if (xQueueReceive(rxQueue, &ch, timeout) == pdPASS) {
        return ch;
    } else {
        return -1;
    }
}

void debug_putchar(const uint8_t ch)
{
    if (xQueueSend(txQueue, &ch, portMAX_DELAY) == pdPASS) {
        // success - enable the TX Empty interrupt (which causes an immediate interrupt if the buffer is empty)
        USART_ITConfig(DEBUG_UART, USART_IT_TXE, ENABLE);
    }
}
And the ISR:
void UART_HANDLER(void)
{
    long xHigherPriorityTaskWoken = pdFALSE;
    uint8_t ch;

    // Check for receive interrupt
    if (USART_GetITStatus(DEBUG_UART, USART_IT_RXNE) != RESET) {
        // RX interrupt -- read the received byte and push it on the queue
        ch = (uint8_t) USART_ReceiveData(DEBUG_UART);
        xQueueSendToBackFromISR(rxQueue, &ch, &xHigherPriorityTaskWoken);
    }

    // Check for transmit-buffer-empty interrupt
    if (USART_GetITStatus(DEBUG_UART, USART_IT_TXE) != RESET) {
        if (xQueueReceiveFromISR(txQueue, &ch, &xHigherPriorityTaskWoken)) {
            // Byte in the queue -- send it
            USART_SendData(DEBUG_UART, ch);
        } else {
            // Nothing left in the queue; disable the USART TX Empty interrupt
            USART_ITConfig(DEBUG_UART, USART_IT_TXE, DISABLE);
        }
    }

    /***
     * If a higher-priority task was awoken, we need to perform a context
     * switch.
     */
    portEND_SWITCHING_ISR(xHigherPriorityTaskWoken);
}
Now here’s the interesting bit — if I modify the ISR to waste a few cycles between the “Check for RX interrupt” and “Check for TX buffer empty interrupt” blocks, it works. That modification is:
    }

    static int foo = 50;
    while (foo-- > 0);

    // Check for transmit-buffer-empty interrupt
The CPU has to be running at 32MHz SYSCLK and HCLK, 1.8V V-reg, 1 flash wait state, with the idle-sleep hook installed. Slowing down the CPU, adding the above for-loop, or disabling idle-sleep is enough to stop the hardfaults. The smaller the queue, the less characters you have to send before it falls over and hard-faults. Thanks, Phil.

FreeRTOS (ARM CM3) hardfaults when built with IAR optimisation enabled

Very curious. There is nothing obviously amiss with the code. Did you say before that removing the tick hook was enough by itself? I’m wondering if there is some kind of wake up settling time issue that could mess up the clocks on the UART. Regards.

FreeRTOS (ARM CM3) hardfaults when built with IAR optimisation enabled

There’s a good article on hard fault debugging here: http://mcuoneclipse.com/2012/11/24/debugging-hard-faults-on-arm-cortex-m/ You can probably use the hard fault exception code listed there in your project – the devolved LR register will point to the problem instruction, and you can figure out what the optimizer is doing to break things. I used this to track down a similar issue where the optimizer was breaking some C code for kicking a watchdog timer. On Sep 19, 2014, at 5:48 AMEDT, Philip Pemberton philpem@users.sf.net wrote:
Here’s the “create queue” code: /// Receive queue static xQueueHandle rxQueue; /// Transmit queue static xQueueHandle txQueue; portBASETYPE debugInit(void) { GPIOInitTypeDef init; NVICInitTypeDef nvic; USART_InitTypeDef uart; // Create RX and TX queues if ((rxQueue = xQueueCreate(DEBUGQUEUESIZE, sizeof(portCHAR))) == NULL) { return pdFAIL; } vQueueAddToRegistry(rxQueue, “DBG Receive”); if ((txQueue = xQueueCreate(DEBUGQUEUESIZE, sizeof(portCHAR))) == NULL) { vQueueDelete(rxQueue); return pdFAIL; } vQueueAddToRegistry(txQueue, “DBG Transmit”); Receive and Transmit: portBASETYPE debuggetchar(portTickType timeout) { uint8_t ch; if (xQueueReceive(rxQueue, &ch, timeout) == pdPASS) { return ch; } else { return -1; } } void debugputchar(const uint8t ch) { if (xQueueSend(txQueue, &ch, portMAXDELAY) == pdPASS) { // success – enable the TX Empty interrupt (which causes an immediate interrupt if the buffer is empty) USARTITConfig(DEBUGUART, USARTIT_TXE, ENABLE); } } And the ISR: void UARTHANDLER(void) { long xHigherPriorityTaskWoken = pdFALSE; uint8t ch;
// Check for receive interrupt
if (USART_GetITStatus(DEBUG_UART, USART_IT_RXNE) != RESET) {
    // RX interrupt -- read the received byte and push it on the queue
    ch = (uint8_t) USART_ReceiveData(DEBUG_UART);
    xQueueSendToBackFromISR(rxQueue, &ch, &xHigherPriorityTaskWoken);
}

// Check for transmit-buffer-empty interrupt
if (USART_GetITStatus(DEBUG_UART, USART_IT_TXE) != RESET) {
    if (xQueueReceiveFromISR(txQueue, &ch, &xHigherPriorityTaskWoken)) {
        // Byte in the queue -- send it
        USART_SendData(DEBUG_UART, ch);
    } else {
        // Nothing left in the queue; disable the USART TX Empty interrupt
        USART_ITConfig(DEBUG_UART, USART_IT_TXE, DISABLE);
    }
}

/***
 * If a higher-priority task was awoken, we need to perform a context
 * switch.
 */
portEND_SWITCHING_ISR(xHigherPriorityTaskWoken);
} Now here’s the interesting bit — if I modify the ISR to waste a few cycles between the “Check for RX interrupt” and “Check for TX buffer empty interrupt” blocks, it works. That modification is:
}

static int foo = 30;
while (foo-- > 0);

// Check for transmit-buffer-empty interrupt
The CPU has to be running at 32MHz SYSCLK and HCLK, 1.8V V-reg, 1 flash wait state, with the idle-sleep hook installed. Slowing down the CPU, adding the above for-loop, or disabling idle-sleep is enough to stop the hardfaults. Thanks, Phil. FreeRTOS (ARM CM3) hardfaults when built with IAR optimisation enabled Sent from sourceforge.net because you indicated interest in https://sourceforge.net/p/freertos/discussion/382005/ To unsubscribe from further messages, please visit https://sourceforge.net/auth/subscriptions/

FreeRTOS (ARM CM3) hardfaults when built with IAR optimisation enabled

I already have a very similar hard fault handler in place — sadly the PC, LR and Stack are corrupt when my code enters the hander. The addresses are way outside the flash and RAM areas of the memory map 🙁 Thanks, Phil.

FreeRTOS (ARM CM3) hardfaults when built with IAR optimisation enabled

Removing any one of the three conditions is enough to prevent the issue from occurring. I can reduce the clock frequency from 32MHz to 24MHz or 16MHz, disable sleep-on-idle (the tick hook) or increase the queue size so the task never blocks. Any one or combination thereof is enough to stop the fault from appearing. I’m not certain it’s the clocks on the UART – I suspect that the clock to the CPU core is somehow being affected during standby or wakeup. It’s all very strange…

FreeRTOS (ARM CM3) hardfaults when built with IAR optimisation enabled

Just to make sure – after entering the handler, you run the handler until the software BRK? Because when you enter the handler those values are corrupt, then handler code loads saved values to registers and then breaks, where you can then inspect them. On Sep 22, 2014, at 4:58 AM, “Philip Pemberton” philpem@users.sf.net wrote:
I already have a very similar hard fault handler in place — sadly the PC, LR and Stack are corrupt when my code enters the hander. The addresses are way outside the flash and RAM areas of the memory map 🙁 Thanks, Phil. FreeRTOS (ARM CM3) hardfaults when built with IAR optimisation enabled Sent from sourceforge.net because you indicated interest in https://sourceforge.net/p/freertos/discussion/382005/ To unsubscribe from further messages, please visit https://sourceforge.net/auth/subscriptions/