Search code examples
cgccembeddedmodekeil

Why does my program not enter handler mode under GCC?


I have this code which works under KEIL but not under GCC and I do not know why is this happening. It tests some of RTX OS functionality.

Handler:

void GenWait_IRQHandler (void) {

  switch (ISR_ExNum) {
    case 0: Stat_Isr = osDelay (10); break;
  #if (osFeatureWait)
    case 1: Stat_Isr = osWait  (10); break;
  #endif
  }
}

The above code is entered by setting pending IRQ from main:

...
ISR_ExNum = 0; /* Test: osDelay */
NVIC_SetPendingIRQ((IRQn_Type)SWI_HANDLER);
ASSERT_TRUE (Stat_Isr == osErrorISR);
...

The problem is that this ASSERT_TRUE() fails because Stat_Isr is not equal to osErrorISR which it should be as calling osDelay() is not allowed from Handler mode:

osStatus osDelay (uint32_t millisec) {
  if (__get_IPSR() != 0U) {
    return osErrorISR;                          // Not allowed in ISR
  }
  return __svcDelay(millisec);
}

As I said when compiled under KEIL it works fine but when compiled under GCC it fails. It looks like IPSR is not updated when entering handler and osDelay() does not know it should return error. Any idea why is this happening ?

SWI_Handler is software handler, and I call GenWait_IRQHandler() in it.

EDIT:

This is implementation available from KEIL Packs as RTX validation, I just try to make it work on chip I am working with. So it should work even if I call functions from ISR.

Moreover as I wrote in comment:

(from www.keil.com):

Interrupt Service Routines (ISR) can call some CMSIS-RTOS functions. When a CMSIS-RTOS function cannot be called from ISR context, it rejects the invocation.

And then:

Functions that cannot be called from an ISR are verifying the interrupt status and return, in case they are called from an ISR context, the status code osErrorISR. In some implementations, this condition might be caught using the HARD FAULT vector.

EDIT2:

Reducing optimization from -O3 to -O1 fixed isse but I still do not know why it was optimized like this and how I can easily prevent compiler from doing this. I know that simplest answer is to add couple of "volatile's" but this is not this simple in this case, I think.


Solution

  • Thanks @kkrambo for the right trail. The problem was with instructions order. Adding volatiles to Stat_Isr was not enough but adding memory barrier made this work for me:

    ...
    ISR_ExNum = 0; /* Test: osDelay */
    NVIC_SetPendingIRQ((IRQn_Type)SWI_HANDLER);
    __DMB();
    ASSERT_TRUE (Stat_Isr == osErrorISR);
    ...
    

    It happend because code optimalization made ISR to be called after ASSERT_TRUE (Stat_Isr == osErrorISR);. I think this is clear now.