Get exceptions when interrupts are nested

ayazar wrote on Wednesday, November 01, 2017:

Hello,

I am experimenting with FreeRTOS v9.0.0 on Zynq by expanding the example project. There are 3 interrupt sources and they are async. to each other, for example one interrupt is from serial port receive action and another one is generated from an input pin which occurs independently from serial channel. I also log during runtime on RAM using trace provided by FreeRTOS and analyze the results using free edition of Tracealyzer. At the beginning, I assigned different priorites to each interrupt. These priorites were appropriate such that I was able to call …FromISR() functions inside ISRs. However the code showed up problems when it ran about 5 minutes. At some point, in order to debug problem I assigned same priorites to all interrupts. Then, I did lots of change and since it is a learning project for me I didn’t use version control (!).

Now, the code runs more than 24 hours without any problem but I realized that if I configure interrupts with different priorites as in before, I get different ARM exceptions (sometimes “undefined”, somtimes “data abort” and some times “prefetch abort”). I verify that problem occurs when an interrupt is nested with another one by looking trace logs with Tracealyzer.

I know that this is an general question and it may not be related with FreeRTOS directly but I want to hear suggestions if any. Can there be any misusage of FreeRTOS API functions such that there is a problem only a nested interrupt is occured? As I said, code works well if I don’t use nested interrupts. Additionaly, there is no shared data between ISR functions. I am not very familiar with FreeRTOS internals and ARM itself so I can’t comment by thinking what FreeRTOS does when a nested interrupt occurs. In interrupts, I either send a message to a queue or give a semaphore. Enabling/disabling trace functions on the code doesn’t change the result.

I would appreciate any ideas.

Best,
Alper

rtel wrote on Wednesday, November 01, 2017:

First thing - do you have configASSERT() defined to something that will
halt the processor?

You can cause a problem if you use an API function that does not end in
“FromISR” inside an ISR, or if you call an API function that does end in
“FromISR” but from an interrupt that has a priority above
configMAX_API_CALL_INTERRUPT_PRIORITY (see
http://www.freertos.org/Using-FreeRTOS-on-Cortex-A-Embedded-Processors.html)

Finally, depending on GCC version, you can get into problems if nested
interrupts use floating point registers, and that can happen even if you
don’t explicitly perform floating point operations - for example if the
compiler’s libraries are optimized to the point that floating point
registers are used (because they are wide and can move more bytes with
fewer operations) in memcpy() calls (which are used inside FreeRTOS API
functions). There are some notes on that on the same link above.

ayazar wrote on Friday, November 10, 2017:

Hi,

configASSERT() is defined as in the example Zynq code. This is the assert handler:

void vAssertCalled( const char * pcFile, unsigned long ulLine )
{
volatile unsigned long ul = 0;

	( void ) pcFile;
	( void ) ulLine;

	taskENTER_CRITICAL();
	{
		/* Set ul to a non-zero value using the debugger to step out of this
		function. */
		while( ul == 0 )
		{
			portNOP();
		}
	}
	taskEXIT_CRITICAL();
}

I have verified that when a FreeRTOS function is asserted, I could catch this behaviour by putting a breakpoint on line with “portNOP();”. So when this problem occurs, assert() is never called.

I make sure that all API functions end in FromISR.

For priorities, if I assign a wrong priority without considering configMAX_API_CALL_INTERRUPT_PRIORITY, vAssertCalled() is called immediately even interrupts aren’t nested. Therefore, I am sure that priorites are OK.

This port defines its own memcpy() function and I compile the code with no optimization. So I think that, VFP registers aren’t used but i will check the compiled code. Even if they are used, is nested intterrupt sitution the only trigger of the error. I mean that FreeRTOS API functions are called from tasks and ISRs even if interrupts aren’t nested. If floating point registers are used, do they cause error only if interrupts are nested?

BTW, configUSE_TASK_FPU_SUPPORT is 2 in my case.

Thank you.

P.S. Disassembly of memcpy() function is posted below. I am not ARM assembly expert but I don’t think that there is any floating point instruction.

123       void *memcpy( void *pvDest, const void *pvSource, size_t xBytes )
0010ed7c:   sub     sp, r11, #0
0010ed80:   pop     {r11}
0010ed84:   bx      lr
124       {
125       /* The compiler used during development seems to err unless these volatiles are
126       included at -O3 optimisation.  */
          memcpy:
0010ed88:   push    {r11}
0010ed8c:   add     r11, sp, #0
0010ed90:   sub     sp, sp, #36
0010ed94:   str     r0, [r11, #-24]
0010ed98:   str     r1, [r11, #-28]
0010ed9c:   str     r2, [r11, #-32]
127       volatile unsigned char *pcDest = ( volatile unsigned char * ) pvDest, *pcSource = ( volatile unsigned char * ) pvSource;
128       size_t x;
129       
130       	/* Extremely crude standard library implementations in lieu of having a C
131       	library. */
0010eda0:   ldr     r3, [r11, #-24]
0010eda4:   str     r3, [r11, #-12]
0010eda8:   ldr     r3, [r11, #-28]
0010edac:   str     r3, [r11, #-16]
132       	if( pvDest != pvSource )
133       	{
0010edb0:   ldr     r2, [r11, #-24]
0010edb4:   ldr     r3, [r11, #-28]
0010edb8:   cmp     r2, r3
0010edbc:   beq     +72     ; addr=0x0010ee0c: memcpy + 0x00000084
134       		for( x = 0; x < xBytes; x++ )
135       		{
0010edc0:   mov     r3, #0
0010edc4:   str     r3, [r11, #-8]
0010edc8:   b       +44     ; addr=0x0010edfc: memcpy + 0x00000074
136       			pcDest[ x ] = pcSource[ x ];
137       		}
138       	}
139       
0010edcc:   ldr     r2, [r11, #-12]
0010edd0:   ldr     r3, [r11, #-8]
0010edd4:   add     r3, r2, r3
0010edd8:   ldr     r1, [r11, #-16]
0010eddc:   ldr     r2, [r11, #-8]
0010ede0:   add     r2, r1, r2
0010ede4:   ldrb    r2, [r2]
0010ede8:   uxtb    r2, r2
0010edec:   strb    r2, [r3]
134       		for( x = 0; x < xBytes; x++ )
135       		{
0010edf0:   ldr     r3, [r11, #-8]
0010edf4:   add     r3, r3, #1
0010edf8:   str     r3, [r11, #-8]
0010edfc:   ldr     r2, [r11, #-8]
0010ee00:   ldr     r3, [r11, #-32]
0010ee04:   cmp     r2, r3
0010ee08:   bcc     -68     ; addr=0x0010edcc: memcpy + 0x00000044
140       	return pvDest;
0010ee0c:   ldr     r3, [r11, #-24]
141       }