An unknown function is invoked during the ISR's execution

So…I have an idle task and then a UART interrupt fires upon a character being sent over the serial, and somewhere during the execution of the ISR, it attempts to execute an “unknown” function as shown in the callstack here. I don’t see this error when I comment out the vTaskStartScheduler() in main.

And here’s the respective ASM code but I can’t seem to trace down the C/C++ function being executed

IDE: Segger
Platform: nRF52

well, don’t rely on the call stack. First of all, you should make it a habit to at least reveal to us the target platform and the IDE. Tons of what you see and don’t see depend on these.

Your IDE may or may not know that FreeRTOS switches stacks upon ISR invocation and likewise not attempt to follow the entire chain of events. Also, the call stack is not reliable, for example on Cortex M platforms where the compiler may make lots of optimizationd such as tail recursion collating or function inlining that will make the call stack look very different from what you think it should look. In short, don’t waste your time barking up a possibly wrong tree.

Very likely your application suffers from one of the top three errors:

  1. Stack overflow
  2. Misconfigured interrupt priorities
  3. Synchronization problems.

That’s where you should look first, they account for > 90% of all problems related to FreeRTOS (as the discussions in this forum prove over and over again).

And of course, if you don’t start the OS in the first place, none of the top three problems will show.

Thanks. I have mentioned the platform aloing with the IDE.

  1. How would you track the stack possibly getting overflowed?
  2. What do you think may happen in case of misconfigured IRQ priorities? There’s currently only one IRQ and one task (which is idling)
  3. Why synchronization when the IRQ is supposed to fire? There’s a stream buffer waiting on the data in a task but it should be idling

you can start here:

or type “stack overflow” into the search box in the forum. Almost no topic has been discussed here as extensively.

Misconfigured task priorites refer to IRQs configured to run above MAX_SYSCALL priority but use FreeRTOS API functions or systems configured to run the sys tick or service isr on too high priorities.

About synchronization: what I refer to is code that fails to use appropriate synchronization mechanisms to prevent shared data structures from being corrupted. That’s also been discussed here many many times.

1 Like

Thanks for posting the link.

  1. So one way to test is to have void vApplicationStackOverflowHook( xTaskHandle *pxTask, signed portCHAR *pcTaskName ) in the application which would get invoked as soon as stack overflow is detected, yes? Thing is the hook function is never called

  2. I don’t have any IRQs running freeRTOS APIs

  3. I doubt if that’s causing this as currently I only have one stream buffer which is being written to and read from through a single write() function which isn’t even being used at this point.

Also now, after enabling the configCHECK_FOR_STACK_OVERFLOW flag, I don’t see the hook function being called but I do see the IRQ is being invoked and the actual registers aren’t being modified inside it.
For instance the following line is what’s used to disable NRF_UART_EVENT_RXDRDY but now it’s not being disabled and the interrupt keeps firing. Why would having a scheduler mess with this?

pInstance->setNrfEvent(NRF_UART_EVENT_RXDRDY, StatusDisable);

If I read it correctly, the Nordic chip is based on a Cortex M4. If the interrupt keeps firing though disabled on the UART level, next thing to look at is whether you clear the ISR on the NVIC level.

Thing though is, the same operation works as expected when the scheduler isn’t running (the same register gets disabled and IRQ doesn’t get fired after until the event happens). What’s concerning to me is the register not being disabled when scheduler is running

well, as long as we don’t see any code, pretty much everything is guess work…

// logger.cpp
TaskHandle_t Logger::loggerThread;
TaskHandle_t Logger::taskMsgBuffer;
StreamBufferHandle_t Logger::streamBuffer;

typedef enum
{
    totalStreamBufferBytes = 100,
    triggerLevel = 5
} streamBufferParams;

Logger::Logger()
{
   Logger::streamBuffer = xStreamBufferCreate(totalStreamBufferBytes, triggerLevel);

    // create a task
    if (pdPASS != xTaskCreate(Logger::Process, "PROCESS_LOG", 256, this, 5, &Logger::loggerThread))
    {
        // error handle
    }
}

// uart.cpp
static Uart* pInstance;

extern "C" {
    void UARTE0_UART0_IRQHandler(void)
    {
       // ...
       if (rxIrqEnabled && dataRxd)
       {
           pInstance->setEvent(NRF_UART_EVENT_RXDRDY, 0); // disable event -- DOESN'T SEEM TO WORK AS EXPECTED WHEN SCHEDULER RUNS!
       }
	 
    }
} 

Uart::Uart()
{
   pInstance = this;
   // ...
}

// main.c
int main(void)
{
   Logger logger;
   Uart uart;
   clockInit();
   vTaskStartScheduler();
}

You know the main stack is reset and REUSED as ISR stack for Cortex-M ports.
This might cause corruptions of your logger and UART instances allocated on main stack.
The MSP is reset when the scheduler is started.
Either allocate the instances from a (main) task stack right before entering it’s forever task loop or take from heap or make them static/global.

Update: now I see the IRQ being invoked but the NRF_UART_EVENT_RXDRDY isn’t being set. With no scheduler, I had no issues setting it to 0.

You know the main stack is reset and REUSED as ISR stack for Cortex-M ports.

how do we ensure that though? I have a hook function that doesn’t get called atll.

Either allocate the instances from a (main) task stack right before entering it’s forever task loop or take from heap or make the static/global.

Sorry, still trying to understand this. The UART and Logger instances are already defined inside main and before the task scheduler. Are you implying to make these instances global or static?

The reuse/reset of the main stack is done internally by FreeRTOS when starting the scheduler and can’t be hooked.
Cortex-M MCUs have a main stack (see MSP register) and a process stack (see PSP register). The latter is used for tasks stack managed by FreeRTOS. The main stack is used by ISRs.
You create the logger and UART instances on the main stack and start FreeRTOS.
Hence both objects living in the main stack might and likely will be corrupted as soon as an ISR runs.
In short DON‘T use the main stack for anything you need after starting FreeRTOS.
So make the instances e.g. global or static as mentioned before to avoid their corruption.

Edit. This is also found multiple times in the forum and for instance here:

Thanks. I tried making these two instances global and it worked. Follow ups:

  • I understood that the main task is being used by the ISR but is the overflow really caused by ISR taking up too much memory hence corrupting the instances allocated in main i.e uart and. logger?

  • I tried monitoring MSP register but having trouble figuring out how it could be used to see how overflow is caused. From my understanding, overflow happens when the MSP exceeds the stack bound which means it starts writing to ‘external’ addresses. If that’s the case, why would the stuff allocated within the stack get corrupted?

  • Where would the following instance get allocated? uart is on a stack which no longer exists after the function is done executing. Would this still mess up with memory taken up by ISR?

void Init()
{
  Uart uart;
}
int main(void)
{
   Init();
}

It’s not a stack overflow ! Main stack and process stack are different stacks.
Again the memory where the objects were created (in main) on main stack before FreeRTOS is started is let’s say freed and used for something else (as stack for ISRs) as soon as FreeRTOS scheduler is started.
I don‘t understand point 3.

I see. So it’s more of a memory reusage where the same main stack which was initially used by the logger, uart instances is now used by the ISR since the scheduler reset the MSP (?) hence the corruption, yes?

So instead of defining Uart uart inside the main, you do it inside another function Init(). Is that also a part of “main stack”?

The basic rule is that you can’t create object ‘inside’ main or any function that it calls that aren’t marked as static, globals or put on the heap. All variables created as locals in main are possibly wiped out when the scheduler starts.

For small machines with limited memory, this makes a lot of sense. It does cause this sort of confusion to others on bigger processors. Sometimes I wish that it just set the ISR stack to the current main stack instead of its begining, losing a bit of stack space that is likly just ‘dead’ now, but avoids this sort of problem.

Just an additional hint: Even though the code of a FreeRTOS application in main() until invoking vTaskStartScheduler looks like normal C-code with some function calls it‘s not exactly true. In fact it should be considered as bootstrap or bootloader code finally booting the real application by vTaskStartScheduler starting the first task.
Starting the scheduler might switch e.g. the processor mode and change some parts of the C-runtime environment (stack). These things are obviously not covered by the C-standard and might cause surprising side effects if relying on environment (e.g. variables resp. objects on main stack) before the scheduler was started.
I think it‘s a good and simple model to create just a main or the first, initial task without arguments where all initializations (maybe including creating all other tasks) are done in it‘s preamble part before entering it‘s forever loop.

So basically everything defined on the stack in main get wiped out prior to a scheduler call, hence accessing any of it elsewhere in the code would result in garbage.

  • Is this how most RTOS’s work or is it specific to FreeRTOS? as in why wipe out whatever was initially defined?

  • how can you monitor this using MSP?

That pretty much sums it up, although it’s possible that some ports handle this differently. You could even rewrite your port to maintain the startup stack if you wanted to.

Please remember your own expression before - only what is ON THE STARTUP stack is “wiped out.” Everything residing in other memory areas remains untouched - for example global semaphores, tasks etc.

Impossible to say if “most” RTOSs handle it the same way. The RTOS market is still fairly heterogeneous including a number of home brews.

I don’t understand the question, sorry. What you could do if you wanted to is to memset() your startup stack in your vPortStartFirstTask() to a known signature, but since I don’t know what you intend to do by “monitoring”, this may or may not accomplish it.

Using a debugger capable of displaying the registers of your MCU.