CortexR4: Spurious Data Abort Interrupt

Hello,
I have to finish a software based on TMS5730LS3137 + HALCOGEN (TI Software Generation Tool) + FreeRTOS. This software was started by someone else which left the company.

I do not have any experience with this CPU, so it takes me many weeks to understand how it works!
I finally found the root cause of the application spurious crashes, it was a DABT on address 0xFFFFFFB0 (SSIR1 Register).

In my application, MPU is disabled.

After further research, I saw that this error was raised in FreeRTOS xTaskResumeAll() function. Sometimes, when taskYIELD_IF_USING_PREEMPTION() is called, the CPU is still in user mode!
This happens, when calling vPortMalloc()/vPortFree().

What I cannot understand, is why sometimes CPU is in user mode at this moment.
Is this a known issue?

TI uses FreeRTOS 9.0.0, as workaround for this issue I changed portYIELD_WITHIN_API()to enable privileged mode when required:

#define portYIELD_WITHIN_API()		{\
    BaseType_t x = (_get_CPSR() & 0x1F); \
    if (x == 0x10) prvRaisePrivilege(); \
    portSYS_SSIR1_REG = portSYS_SSIR1_SSKEY; \
    asm( " DSB " ); asm( " ISB " ); \
    if (x == 0x10) asm( " CPS #0x10"); \
    }

I know this is a bad hack, is there a better solution for this?

A better solution would be find out why the CPU is in user mode while inside the xTaskResumeAll API. You can try to put a breakpoint at prvRaisePrivilege in your modified portYIELD_WITHIN_API. This would give us a callstack to analyze when CPU is in the user mode inside a FreeRTOS API.

Since you are not using MPU in the application, we need to find how is CPU entering user mode.

This is obvious to me, when calling vPortMalloc() from a Task, there is no switch into privileged mode, in heap_4.c, the MPU wrappers are disabled (MPU_WRAPPERS_INCLUDED_FROM_API_FILE).

So, it look like vPortMalloc()/vPortFree() may not work in user mode… But is this really wanted like this?

You said here that you are not using MPU in your application. Is that right? Are you using MPU or not?

Assuming that you are using MPU, the mpu_wrappers.c in V9.0.0 does provide entry points for pvPortMalloc and vPortFree - FreeRTOS-Kernel/FreeRTOS/Source/portable/Common/mpu_wrappers.c at V9.0.0 · FreeRTOS/FreeRTOS-Kernel · GitHub. However, the corresponding mapping does not exist in mpu_wrappers.h - FreeRTOS-Kernel/FreeRTOS/Source/include/mpu_wrappers.h at V9.0.0 · FreeRTOS/FreeRTOS-Kernel · GitHub. Would you please add the following 2 lines in mpu_wrappers.h:

#define pvPortMalloc   MPU_pvPortMalloc
#define vPortFree      MPU_vPortFree

As I wrote before, I have to finish this project, started from another developer.
I had a hard time to understand how CortexR4 works, and have not spend too much time to analyse the TI FreeRTOS implementation.

MPU usage is disabled in TI-HALCOGEN, I supposed this suffisant:

By the way, there is no implementation of MPU_pvPortMalloc() or MPU_vPortFree() in TI code base!

Would you please help answer the following questions:

  1. Are you compiling mpu_wrappers.c in your project?
  2. Put a breakpoint in any other FreeRTOS API and see if the CPU is running in user mode or not.

Yes

Here is a screenshot, I’ve made a little hack in xTaskResumeAll() to debug the issue:

note: HALCOGEN prefixes all FreeRTOS files with os_

Can you put a breakpoint in any other FreeRTOS API in your application which is not coming from logging (something like queue send/receive, semaphore give/take) etc?

When pvPortMalloc()/vPortFree() are called from other API function, like xQueueGenericSend(), the corresponding MPU routine is used. This way the privilege mode is enabled, and no Data Abort Interrupt is generated!

This is confusing because earlier you said the following -

Are you saying that MPU_xQueueGenericSend is called? Would you instead paste a screenshot of the callstack to confirm?

With all FreeRTOS API routines, MPU “variants” are used, only for pvPortMalloc() and pPortFree() it is not the case!
I agree with you, this is very confusing!

And here the code which calls xQueueGenericReceive()

Thank you for sharing. I understand now. Can you also share the definition of MPU_xQueueGenericReceive from mpu_wrappers.c?

Of course:

BaseType_t MPU_xQueueGenericReceive( QueueHandle_t xQueue, void * const pvBuffer, TickType_t xTicksToWait, const BaseType_t xJustPeek )
{
	BaseType_t xReturn;
	BaseType_t xRunningPrivileged = prvRaisePrivilege();
	xReturn = xQueueGenericReceive( xQueue, pvBuffer, xTicksToWait, xJustPeek );
	portRESET_PRIVILEGE( xRunningPrivileged );
	return xReturn;
}

Please add the following to mpu_wrappers.c:

#if( configSUPPORT_DYNAMIC_ALLOCATION == 1 )

	void *MPU_pvPortMalloc( size_t xSize )
	{
	void *pvReturn;
	BaseType_t xRunningPrivileged = prvRaisePrivilege();

		pvReturn = pvPortMalloc( xSize );

		vPortResetPrivilege( xRunningPrivileged );

		return pvReturn;
	}

#endif /* configSUPPORT_DYNAMIC_ALLOCATION */
/*-----------------------------------------------------------*/

#if( configSUPPORT_DYNAMIC_ALLOCATION == 1 )

	void MPU_vPortFree( void *pv )
	{
	BaseType_t xRunningPrivileged = prvRaisePrivilege();

		vPortFree( pv );

		vPortResetPrivilege( xRunningPrivileged );
	}

#endif /* configSUPPORT_DYNAMIC_ALLOCATION */
/*-----------------------------------------------------------*/

And the following to mpu_wrappers.h:

#define pvPortMalloc   MPU_pvPortMalloc
#define vPortFree      MPU_vPortFree

Give it a try and see if it fixes your issue.

1 Like

I will try and give you feedback tomorrow.

Thanks for your support.

Good morning Gaurav,

adding MPU_pvPortMalloc() and MPU_vPortFree() solve my issue in a much cleaner way!

To complete my reply, I had to change a little bit the implementation of prvRaisePrivilege(). Because the implementation done by TI always force the switching into System mode!
I added a simple check, to only switch into System mode when CPU is in User mode (as MPU_vPortFree() / MPU_pvPortMalloc() may be called for IRQ mode)

Thanks a lot for your time and your support

Best regards

Fabrice

Thank you for sharing your solution!

Have you made sure that vPortMalloc/vPortFree are safe to use in an ISR? That would generally require that the functions do their work inside a big critical section, which would add a lot of possible latency to ISR responce.