portYIELD_FROM_ISR causes INVPC

Hi,

I’m developing a program for the XMC4400 processor (ARM Cortex M4)
I have a problem when trying to use portYIELD_FROM_ISR, this causes an INVPC.

The problem have been localized to a single ISR, so only one task is running.
I’m really stuck and could use some help.

There is only one task running:

extern void LoadTask( void *pvParameters )
{
	INTERRUPT_Enable(&INTERRUPT_3);
	while(1) {
		vTaskDelay(2000);
		DIGITAL_IO_ToggleOutput(&DIGITAL_IO_DBG);
	}
}

The failing ISR:

void AdcRdyIRQHandler(void)
{
	BaseType_t xHigherPriorityTaskWoken; 

	xEventGroupSetBitsFromISR(xLoadcellTaskEventGroup, evADCReady, &xHigherPriorityTaskWoken);
	portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
}

The Registers:

sp 0x1fffc7b0
lr 0xfffffffd (Hex)
pc 0x8000298 <VADC0_G3_3_IRQHandler>
xpsr 0x1000006 (Hex)
msp 0x1fffc7b0 (Hex)
psp 0x1fffed28 (Hex)

Debugger output:
Thread #1 57005 (Suspended : Signal : SIGTRAP:Trace/breakpoint trap)
VADC0_G3_3_IRQHandler() at startup_XMC4400.S:353 0x8000298
() at 0xfffffffd
CCU41_1_IRQHandler() at AD7730.c:349 0x80005e4
0x0

Does it happen already on the 1st invocation of the ISR ?
Are al handles like xLoadcellTaskEventGroup initialized before the interrupt occurs/is enabled ?
Did you define configASSERT and enabled stack checking during development/debug builds ?
Which FreeRTOS port and version do you use ?
And you also have to initialize xHigherPriorityTaskWoken = pdFALSE.

Yes, it happens on the first invocation of the ISR.
The handler is initialized in main before task is created and scheduler is started.

xLoadcellTaskEventGroup = xEventGroupCreate();

configASSERT is defined as:

#define configASSERT( x )                       if( ( x ) == 0 ) { taskDISABLE_INTERRUPTS(); for( ;; ); }

The version is: FreeRTOS Kernel V10.4.3
It is the version included in the DAVE ide from Infineon.
Have tried to initialize xHigherPriorityTaskWoken to pdFALSE, but that did not help.

Yes, the initialization is not directly related to the crash. But you have to do it to get correct behavior of the flag.
However, your code and the setup looks ok so far.
Are you sure that the task stacks and the main/ISR stack are large enough ?
It helps a lot that it happens systematically. I’d set a breakpoint at the ISR and step through the code to see where it goes wild.

I tried to set a breakpoint at the line “portYIELD_FROM_ISR”, and used the debugger to “Step Over”. But when I do that, the exception does not occur ?
If I just do Run, from the break point the exception occurs. So there is something weird happening.

Indeed that’s weird… Those symptoms could be related to memory/data structure corruption or wrong interrupt priorities. I think the latter should be catched by FreeRTOS ASSERTs. For the 1st potential problem stack overflow checks help. Note on Cortex-M4 it’s not a 100% guarantee.
I guess you check all FreeRTOS API return codes that they were successful where applicable.

I probably have located the instruction that causes the fault, but still having trouble to understand why.

348       	portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
08000530:   ldr     r3, [r7, #4]
08000532:   cmp     r3, #0
08000534:   beq.n   0x8000546 <CCU41_1_IRQHandler+58>
08000536:   ldr     r3, [pc, #28]   ; (0x8000554 <CCU41_1_IRQHandler+72>)
08000538:   mov.w   r2, #268435456  ; 0x10000000
0800053c:   str     r2, [r3, #0]
0800053e:   dsb     sy
08000542:   isb     sy
349       }

If I set a break point at the “str” instruction, it does not crash, but if I set a break point at the next instruction “dsb”, then the crash happens.

The registers are:

r2 0x10000000 (Hex)
r3 0xe000ed04 (Hex)

EDIT:
Found that this is done in the YIELD.
portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT;

Where: 0xe000ed04 is portNVIC_INT_CTRL_REG
and 0x10000000 is portNVIC_PENDSVSET_BIT

0xe000ed04 is somewhere in the Private peripheral bus.
image

Well, the NVIC is a standard peripheral on Cortex-M ARMs. That should be perfectly right.
It’s very strange… is AdcRdyIRQHandler the installed ISR or is it a callback from the real ISR ?
Is it possible that the calling code makes the trouble ?
The code posted is ok so far and is simple enough that it can’t cause any problem. Something else must be wrong :thinking:

How is configKERNEL_INTERRUPT_PRIORITY set?

I think I found the problem.
configKERNEL_INTERRUPT_PRIORITY was set to 20
configMAX_API_CALL_INTERRUPT_PRIORITY was set to 30

But if I set configKERNEL_INTERRUPT_PRIORITY to 63 (lowest prio possible) then it works.

Not sure why configKERNEL_INTERRUPT_PRIORITY must be set lowest possible?

The next kernel release is expected to prevent this misconfiguration. This PR was merged last month.

1 Like

Ok, this was a hard problem to find.
Thank you both for your help.

That is because this ISR is ultimately in charge of saving a task context to eventually swith back to, and thus must interrupt only tasks, not ISRs. If it were not lowest pri, it might interrupt other ISRs which would violate that requirement.

1 Like

Ah, okay, I understand. Thank you. :slight_smile: