the float operation got corruption on zynq

fanksting wrote on Tuesday, May 17, 2016:


I am working on 150825 version of tcp lab code package, and now found an tricky issue, that is, the float operation got corruption when an fpga interrupt occurs periodically whose period is about 3ms. FreeRTOS is running on Zynq. The code snippets are as below:

float operation running in one task:

real32_t fGain = 0.0015f;
real32_t fValue = 0.0f;
int32_t iValue = 0;

for (idx = 0; idx < uiLen; idx ++)
iValue = puiSampBuf[idx];
fValue = (real32_t) iValue;
pAdcSamp[idx] = fValue * fGain;

ISR routine:

void adc_isr(void *CallbackRef)
XScuGic_Disable(pScuGicInst, INTC_ADC_INT_ID);
… … …
xQueueSendFromISR(PSMeasEventQueue, &event, &xTaskWoken);
XScuGic_Enable(pScuGicInst, INTC_ADC_INT_ID);


By some logging, I found some corruption, for example, iValue = 6517, but fValue = 1105510016.000000. The float operation is not triggered by interrupt, where they are running independently to each other.

If I add “volatile” on float variable, I notice the corruption occurring frequency go down, but still occur.
If I add the macro definition of portTASK_USES_FLOATING_POINT() at the beginning of task entry function (not add into all tasks with float operation, but add into the task that capture float corruption), the issue still occur.

Really an tricky issue, I don’t know how to handle it so far. Please give me help. Thank you!

rtel wrote on Tuesday, May 17, 2016:

Search for “Note for GCC (and possibly other compiler) users” on the
following page:

  • I think this is most likely your problem.

xQueueSendFromISR() will call memcpy(), and it is likely the GCC
implementation of memcpy() is using FPU registers - hence your corruption.

There are a couple of ways around this:

  1. Provide your own basic implementations of the library functions that
    are used. For example, this file, which is from the official FreeRTOS
    distribution, contains basic and unoptimised implementations of memcpy,
    memset and memcmp which you could copy:

  2. Use the head revision FreeRTOS code from the repository, found here:
In that code, if you rename your vApplicationIRQHandler() to
vApplicationFPUSafeIRQHandler() (that is the callback function provided
by your application) then all interrupts will be given a floating point
context too.


fanksting wrote on Friday, May 20, 2016:

It is now quite stable, my compress test has already run stably for about 30 minutes and now is still running, after doing the following updates as you pointed out:

  1. update three functions of memcpy(), memcpy() and memset();
  2. set configUSE_TASK_FPU_SUPPORT = 2;
  3. update three files, port.c, portASM.c and portmacro.h
  4. rename to vApplicationFPUSafeIRQHandler()

When I rename to vApplicationFPUSafeIRQHandler(), I compare the latest version with my version, and found there two additional lines of code in latest version as below:

/* Re-enable interrupts. */
__asm ( “cpsie i” );

I don’t understand why they are here and copied to my code, and the float corruption still occur, after I remove the two additional lines, no float corruption occur. So, what’s the meaning of that two lines of code? Thanks.

rtel wrote on Friday, May 20, 2016:

Are these lines in the callback function, or in the port layer itself?

The line re-enables interrupts (that much is obvious from the comment
;o) - I think that was always done to allow interrupts to nest, the
difference is the callback is now called before interrupts are
re-enabled to allow the application writer the option of using nested
interrupts or now. If you re-enable interrupts in the interrupt handler
callback (by including the line) then interrupts can nest. If you omit
the line then interrupts won’t nest.

Note that interrupts nesting or not does not change the fact that
corruption will occur if the interrupt uses FPU registers. Defining
your own memcpy(), etc., prevents the library from doing that though.