FPU Usage Notes
The RTL8773E has added support for the FPU, used for hardware acceleration of floating-point calculations. The method to enable it is shown in the diagram:
Method to Enable FPU
The Watch project includes multiple libs, and according to the normal compilation process, all libs and the watch project need to enable the FPU compile option together. If a user references a third-party lib, and if the lib has not enabled the FPU compile option while the watch project has, issues with incorrect floating-point values may occur in the following two scenarios:
Function funcA is defined in the lib, with the return type as float/double, and funcA is called in the watch.
Function funcB is defined in the lib, with parameter types as float/double, and funcB is called in the watch.
This document will explain the reasons for these issues and how to implement workarounds in the watch project.
Note
If both the lib project and watch project have enabled/not enabled the FPU compile option, this issue will not occur.
Test Code Example
-
Two test function prototypes defined in the filesystem lib:
float FPU_TestReturnFloat(void) { float test_para = 4567.1289f; return test_para; } void FPU_TestParaFloat(float para1, float para2) { float test_para1 = para1; float test_para2 = para2; APP_PRINT_TRACE2("FPU_TestParaFloat: test_para1 = %f, test_para2 = %f", TRACE_FLOAT(test_para1), TRACE_FLOAT(test_para2)); }
-
Watch project test code:
float FPU_TestReturnFloat(void); void FPU_TestParaFloat(float para1, float para2); float para1 = 12345.6789f; float para2 = 98765.4321f; float para3 = 0.0f; FPU_TestParaFloat(para1, para2); para3 = FPU_TestReturnFloat(); APP_PRINT_TRACE1("FPU_TestReturnFloat: para3 = %f", TRACE_FLOAT(para3));
Filesystem project does not enable FPU, watch project enables FPU test results
Disassembly with issues:
-
Float parameter passing error
... ;;;581 float para1 = 12345.6789f; 0x020a9dde: ed9f0a9c .... VLDR s0,[pc,#624] ; [0x20aa050] = 0x4640e6b7 ;;;582 float para2 = 98765.4321f; 0x020a9de2: eddf0a9c .... VLDR s1,[pc,#624] ; [0x20aa054] = 0x47c0e6b7 ;;;584 FPU_TestParaFloat(para1, para2); 0x020a9de6: f020fa34 .4. BL FPU_TestParaFloat ; 0x20ca252 ... ... ;;;899 void FPU_TestParaFloat(float para1, float para2) ;;;900 { 0x020ca252: e92d43f8 -..C PUSH {r3-r9,lr} 0x020ca256: 4605 .F MOV r5,r0 0x020ca258: 460e .F MOV r6,r1 ;;;901 float test_para1 = para1; 0x020ca25a: 462f /F MOV r7,r5 ;;;902 float test_para2 = para2; 0x020ca25c: 4634 4F MOV r4,r6 ...
Test Result: Register operations before and after parameter passing are inconsistent
Error Output:
[APP] FPU_TestParaFloat: test_para1 = 0.000000, test_para2 = -30800760000000000.000000
-
Float return error
... ;;;893 float FPU_TestReturnFloat(void) ;;;894 { ;;;895 float test_para = 4567.1289f; 0x020ca24e: 4861 aH LDR r0,[pc,#388] ; [0x20ca3d4] = 0x458eb908 ;;;896 return test_para; ;;;897 } ... ... ;;;585 para3 = FPU_TestReturnFloat(); 0x020a9dea: f020fa30 .0. BL FPU_TestReturnFloat ; 0x20ca24e 0x020a9dee: 4a5d ]J LDR r2,[pc,#372] ; [0x20a9f64] = 0x88054c4 0x020a9df0: ee103a10 ...: VMOV r3,s0 ...
Test result: Registers before and after return are inconsistent
Error Output:
[APP] FPU_TestReturnFloat: para3 = 12345.680000
Solution
Workaround by the following method:
-
Modify the declaration in the watch project:
uint32_t FPU_TestReturnFloat(void); void FPU_TestParaFloat(uint32_t para1, uint32_t para2);
-
Pass parameters or use return values by pointer dereferencing:
float para1 = 12345.6789f; float para2 = 98765.4321f; uint32_t para3 = 0; uint32_t *p_para1 = (uint32_t *)¶1; uint32_t *p_para2 = (uint32_t *)¶2; float *p_para3 = (float *)¶3; FPU_TestParaFloat(*p_para1, *p_para2); para3 = FPU_TestReturnFloat(); APP_PRINT_TRACE1("FPU_TestReturnFloat: para3 = %f", TRACE_FLOAT(*p_para3));
After the workaround, the filesystem project does not enable FPU, while the watch project enables FPU test results
Disassembly after the workaround:
-
Float parameter passing is correct
... ;;;589 FPU_TestParaFloat(*p_para1, *p_para2); 0x020a9dee: ee101a10 .... VMOV r1,s0 0x020a9df2: 9407 .. STR r4,[sp,#0x1c] 0x020a9df4: 9809 .. LDR r0,[sp,#0x24] 0x020a9df6: f020fa34 .4. BL FPU_TestParaFloat ; 0x20ca262 ... ... ;;;899 void FPU_TestParaFloat(float para1, float para2) ;;;900 { 0x020ca262: e92d43f8 -..C PUSH {r3-r9,lr} 0x020ca266: 4605 .F MOV r5,r0 0x020ca268: 460e .F MOV r6,r1 ;;;901 float test_para1 = para1; 0x020ca26a: 462f /F MOV r7,r5 ;;;902 float test_para2 = para2; 0x020ca26c: 4634 4F MOV r4,r6 ...
Test result: The registers are consistent before and after passing parameters
Test output:
[APP] FPU_TestParaFloat: test_para1 = 12345.680000, test_para2 = 98765.430000
-
Float return is correct
... ;;;893 float FPU_TestReturnFloat(void) ;;;894 { ;;;895 float test_para = 4567.1289f; 0x020ca25e: 4861 aH LDR r0,[pc,#388] ; [0x20ca3e4] = 0x458eb908 ;;;896 return test_para; ;;;897 } ... ... ;;590 para3 = FPU_TestReturnFloat(); 0x020a9dfa: f020fa30 .0. BL FPU_TestReturnFloat ; 0x20ca25e ;;;591 APP_PRINT_TRACE1("FPU_TestReturnFloat: para3 = %f", TRACE_FLOAT(*p_para3)); 0x020a9dfe: 4a5d ]J LDR r2,[pc,#372] ; [0x20a9f74] = 0x88054c4 0x020a9e00: 4603 .F MOV r3, r0 ...
Test Output:
[APP] FPU_TestReturnFloat: para3 = 4567.129000