FPU 使用注意事项

RTL8773E 增加了对 FPU的支持,用于硬件加速浮点数的运算,开启方法如图所示:

keil proj ‣ options for target ‣ C/C++ ‣ Misc Controls ‣ 添加参数 ‣ fpu=FPv5-SP

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

FPU 开启方法

Watch 工程包含了多个 lib,按照正常编译流程,所有 lib 和 watch 工程需要一起开启 FPU 编译选项。 如果用户引用第三方 lib,若此时 lib 没有开启 FPU 编译选项,而 watch 工程开启了 FPU 编译选项。则会在以下两个场景出现取不到正确浮点值的问题:

  1. 函数 funcA 定义在 lib,返回值类型为 float/double,并在 watch 调用 funcA。

  2. 函数 funcB 定义在 lib,参数类型为 float/double,并在 watch 调用 funcB。

本文将说明问题出现的原因和在 watch 工程中 workaround 的方法。

备注

  • 如果 lib 工程和 watch 工程都开启/都不开启 FPU 编译选项,则不会出现该问题。

测试代码示例

  1. 定义在 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));
}
  1. Watch project 测试代码:

    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 工程不开启 FPU,watch 工程开启 FPU 测试结果

出现问题的 disasm:

  1. Float 传参错误

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

    测试结果:传参前后操作寄存器不一致

    错误输出:

    [APP] FPU_TestParaFloat: test_para1 = 0.000000, test_para2 = -30800760000000000.000000
    
  2. Float return 错误

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

    测试结果: return 前后操作寄存器不一致

    错误输出:

    [APP] FPU_TestReturnFloat: para3 = 12345.680000
    

解决方法

通过以下写法 workaround:

  1. 在 watch project 修改声明:

    uint32_t FPU_TestReturnFloat(void);
    void FPU_TestParaFloat(uint32_t para1, uint32_t para2);
    
  2. 使用指针取值的方式传参或使用返回值:

    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));
    

Workround 后 filesystem 工程不开启 FPU,watch 工程开启 FPU 测试结果

Workaround 后的 disasm:

  1. Float 传参正确

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

    测试结果:传参前后操作寄存器一致

    测试输出:

    [APP] FPU_TestParaFloat: test_para1 = 12345.680000, test_para2 = 98765.430000
    
  2. Float return 正确

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

    测试输出:

    [APP] FPU_TestReturnFloat: para3 = 4567.129000