FreeRTOS + GCC + Cortex A9 interrupt float value corruption and resolution - need some insight

Hello,

We ran into a peculiar problem last week. We are using FreeRTOS v10.0 on a Zynq 7020 SoC (Cortex A9 processor) with the ARM GCC toolchain. Essentially, we had one task running doing some simple floating point computations and a DMA triggered interrupt, which invoked a deferred interrupt handler task blocking on a queue from the interrupt.

When running in isolation, the task and the interrupt behaved as expected. However, when they are running together, sometimes the float variables get random values. On some digging, we found this thread (the float operation got corruption on zynq - FreeRTOS) that seems to report the same issue. Following the suggestions provided in the thread, we made the following changes :

  • configUSETASKFPU_SUPPORT is set to 2
  • The function vApplicationIRQHandler() is renamed to vApplicationFPUSafeIRQHandler() in the portZynq7000.c file which is part of the BSP auto-generated by Xilinx SDK.

These changes seem to have fixed the issue. However, I would like some insight as to what has actually happened.

  1. The GCC Cortex A9 port that comes with the FreeRTOS distribution does not have the portZynq7000.c file. Is this file needed or just the three files - port.c, portASM.s and portmacro.h sufficient ?

  2. I am not able to locate the vApplicationIRQHandler() function in the files in GCC/ARM_CA9. Where do I find it?

Would appreciate some insight as to what’s really happening. Thanks for the help.
-yogesh

1 Like

Hello @jyogesh, as you probably know, the FPU has many registers. By default, these registers are not stored on the stack during a task-switch.

The FPU registers are also used in some implementations of memcpy() and memset(). I have also seen that FPU registers were used in stead of the stack when using an ellipsis function.

I often use my own optimised implementation of memcpy().

And yes that is all solved when defining configUSETASKFPU_SUPPORT.

@jyogesh wrote:

the portZynq7000.c file. Is this file needed or just the three files - port.c, portASM.s and portmacro.h sufficient ?

I have never heard of a portZynq7000.c file, and the mentioned files should be enough.

I am not able to locate the vApplicationIRQHandler() function

What code is asking for this function?

can you post the map files of your firmware built both ways?

Hello @htibosch,

Thanks. The portZynq7000.c file comes when the BSP is generated by Xilinx SDK using the FreeRTOS. Just to be clear, I am using the FreeRTOS port provided by Xilinx SDK BSP instead of manually adding FreeRTOS source files and compiling as a part of the application, as mentioned here (FreeRTOS BSP for Xilinx Software Development Kit (SDK)).

I didn’t see any calls made to this function, but I suspect that is because the call be being made in the assembly file portASM.S. Interestingly, the port.c has the following section :

/*
 * If the application provides an implementation of vApplicationIRQHandler(),
 * then it will get called directly without saving the FPU registers on
 * interrupt entry, and this weak implementation of
 * vApplicationFPUSafeIRQHandler() is just provided to remove linkage errors -
 * it should never actually get called so its implementation contains a
 * call to configASSERT() that will always fail.
 *
 * If the application provides its own implementation of
 * vApplicationFPUSafeIRQHandler() then the implementation of
 * vApplicationIRQHandler() provided in portASM.S will save the FPU registers
 * before calling it.
 *
 * Therefore, if the application writer wants FPU registers to be saved on
 * interrupt entry their IRQ handler must be called
 * vApplicationFPUSafeIRQHandler(), and if the application writer does not want
 * FPU registers to be saved on interrupt entry their IRQ handler must be
 * called vApplicationIRQHandler().
 */
void vApplicationFPUSafeIRQHandler( uint32_t ulICCIAR ) __attribute__((weak) );

while the portZynq7000.c defines the following function :
void vApplicationIRQHandler( uint32_t ulICCIAR ){

Not sure if this answers your question though.

Hello @RAc,

Not sure how to do that unfortunately. Can you please provide some links for how to do that with Xilinx SDK?

just check what your implementation of rand() is. If unsure, trace into it from one of its callers in the TCP stack.

@RAc wrote:

just check what your implementation of rand() is

Isn’t rand() a standard C function that returns a pseudo random number? And what would that have to do with the FPU registers?

oops, I responded to the wrong thread… too much work . Sincere apologies. Pls disregard my previous answer.

so back to the issue: How to generate the map files will depend on the tool set and IDE you are using. Very likely it is gcc based, so you will need to add appropriate command line parameters to the compiler or linker. Some IDEs already allow to do that with a check box. Are you using command line based tools (if yes you may want to share your make file)?

I generated the map file using the -Wl,-M=d:/test.map option. I am hitting the forum limitation on directly pasting it here. Which sections would be relevant for this discussion?

Another question I had was about the relationship between the XScuGic Interrupt handler function that is done in baremetal code using :
Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_IRQ_INT, (Xil_ExceptionHandler)XScuGic_InterruptHandler, &my_Gic); where my_GIC is the GIC instance and the vApplicationIRQHandler() / vApplicationFPUSafeIRQHandler()
functions.

One of the things I would look for is the location of the vApplicationIRQHandler that you can not find in the code.

Normally Richard will remove the upload limit fairly speedy so you should ba able show us the files soon.

@jyogesh - you should be able to add an attachment to your posts now.

@rtel thanks, but getting the error Body is limited to 88000 characters; you entered 293740 while attempting to directly copy the map file contents here. I get an error about invalid extension when uploading a map file. I’ve attached the map file in the rar
map_file.zip (22.5 KB)
.

is this the map file of the code that works or the one that does not? Do you have the other one as well?

@RAc, my apologies for not the delay in responding. Got stuck with some personal issues, hope to send this in a couple of days.

Hi jyogesh,

We are dealing with the same problem. Did you come to any conclusion?

Have a good work.

Hey @ozanagma,
I was hoping to get some more information about the problem you’re facing and what you’ve tried to fix it?
I’m going to make the assumption that you’re attempting to use the GCC Cortex A9 port and seeing your floating point registers have their values over-written when using an IRQ.
If this is the case it is likely caused by not installing a function called vApplicationFPUSafeIRQHandler(), like this one,
which causes vApplicationIRQHandler() to be called first, which saves the floating point registers as described here

If you’re running a different demo or using a different port please provide that information so I can assist you better!

Hello @skptak,

Our current FPU configurtion is configUSE_TASK_FPU_SUPPORT =1, before all tasks we call vPortTaskUsesFPU(), we do not overwrite any standart library memory functions( like memcpy and memset), and we changed vApplicationIRQHandler() to vApplicationFPUSafeIRQHandler(). As of now, we don’t have any floating point problem. Thank you for asking. :+1:

Have a good work.

1 Like