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:

keil proj ‣ options for target ‣ C/C++ ‣ Misc Controls ‣ Add parameter ‣ fpu=FPv5-SP

../../../_images/FPU.png

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:

  1. Function funcA is defined in the lib, with the return type as float/double, and funcA is called in the watch.

  2. 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

  1. 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));
    }
    
  2. 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:

  1. 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
    
  2. 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:

  1. Modify the declaration in the watch project:

    uint32_t FPU_TestReturnFloat(void);
    void FPU_TestParaFloat(uint32_t para1, uint32_t para2);
    
  2. 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 *)&para1;
    uint32_t *p_para2 = (uint32_t *)&para2;
    float *p_para3 = (float *)&para3;
    
    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:

  1. 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
    
  2. 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