We use FreeRTOS ARM CA53-64bit port w/ single core. We start two threads to compute bellow float point calculation, and occasionally, we get incorrect result in both threads. Check bellow snapshot w/ the incorrect result in red box.
Similar to Zynq Ultrascale MPSoC task floating point corruption - #17 by wat recently. Has the compiler version changed recently? Are you using floating point instructions in interrupts, or something like memcpy() in an interrupt that may be optimised to use floating point registers?
It is curious that corruption would occur even with every floating point register being saved on interrupt entry and exit. Can you please zip up your FreeRTOS/Source directory and send it to me (with these changes made) so I can look into it a bit further. You should be able to attach the zip to the post, if not you can send it to r dot barry at freertos dot org. Thanks.
Thank you for offering this kind help. We found the reason.
Function vPortTaskUsesFPU() should be called from each float point computation task function. Originally I put vPortTaskUsesFPU into xTaskCreate which attempts to globally enable the float point save/restore for all tasks. This way doesn’t work.
More illustration for other people who may have doubt about it (apply for ARM CA63):
vPortTaskUsesFPU sets the global variable ullPortTaskHasFPUContext which is defined in port.c. If want to enable all tasks by default to use FPU, we may think to manually set ullPortTaskHasFPUContext to pdTRUE, or force to call vPortTaskUsesFPU in every xTaskCreate, but these two ways don’t work (the reason is as bellow) (port.c)
/* Saved as part of the task context. If ullPortTaskHasFPUContext is non-zero
then floating point context must be saved and restored for the task. */
uint64_t ullPortTaskHasFPUContext = pdFALSE;
The first task stack restore after a task is created will always reset ullPortTaskHasFPUContext to zero
/* The task will start without a floating point context. A task that uses
the floating point hardware must call vPortTaskUsesFPU() before executing
any floating point instructions. */
*pxTopOfStack = portNO_FLOATING_POINT_CONTEXT;
If at this point, the float point computation task is scheduled to run, ullPortTaskHasFPUContext is zero, then after a while, when it is scheduled out, the float point register will not be saved into stack (portASM.S)
If at this point, the float point computation task is scheduled to run, and it calls vPortTaskUsesFPU, ullPortTaskHasFPUContext will be 1, then after a while, when it is scheduled out, the float point register will be saved into stack (portASM.S)
/* Save the critical section nesting depth. */
LDR X0, ullCriticalNestingConst
LDR X3, [X0]
/* Save the FPU context indicator. */
LDR X0, ullPortTaskHasFPUContextConst
LDR X2, [X0]
/* Save the FPU context, if any (32 128-bit registers). */
CMP X2, #0
B.EQ 1f
STP Q0, Q1, [SP,#-0x20]!
STP Q2, Q3, [SP,#-0x20]!
STP Q4, Q5, [SP,#-0x20]!
STP Q6, Q7, [SP,#-0x20]!
I am running on MiniZed, Zynq 7000, Cortex A9. I inserted the float_calc() into two tasks, and I saw similar results, that is, errors detected in response from float_calc(). I have since inserted vPortTaskUsesFPU() into each of the tasks (at top of task, and I have not seen an error since. (I have been running for about 20 minutes now.)
BTW, I had also set up use_task_fpu_support in the board support settings, but I have now turned if off when experimenting with vPortTaskUsesFPU().
Update on this - the reason float_calc() fails is because a float was used in an interrupt service routine. After changing that float to uint16, I no longer get failures with float_calc().
If float is necessary in isr, what registers need to be saved/restored? (Or is it a different set depending on the processor?)