你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

实战经验 | STM32G474 FPU 性能优化与测量

[复制链接]
STMCU-管管 发布时间:2024-10-30 16:57

01引言
客户在使用 STM32G474 时,希望使用 FPU 进行浮点运算,并最大化其性能。本文 从 STM32G474 系统的角度、ARM DSP Lib、编译选项的影响等几个方面探讨如何提升整体性能,并介绍如何使用 KEIL 工具进行测量。

02STM32G474 FPU 运算性能优化
2.1. STM32G474 系统性能优化

STM32G474 使用的是 ARM Cortex-M4 内核(+FPU)。一般代码会放在 FLASH 区, 通过 I-Bus 读取。这里 STM32G474 有 FLASH 预取指及 CACHE Line, 无需放入 IRAM 或 CCM。因为 Cortex-M4 DSP 指令中没有运算指令与加载指令并行的混合指令,所以数据 存放区域及 Bus 的选择理论上对性能的影响不大。如下图 1 所示,可将 FPU 运算数据放 在 SRAM1。另外还需尽量避免 SRAM 的并发访问,如使能了 DMA,DMA 传输目的地 可以使用 SRAM2,从而减少潜在的 SRAM 并发访问产生的性能下降。应用则需要根据实 际情况,合理使用内存区域。
12.png
▲ 图1. STM32G474 架构


2.2. ARM DSP Lib 的使用
在 ARM DSP 库实现了很多 math 算法,可进行浮点乘加、点积、卷积、FFT、NN 等 多种算法 API,可以使用 ARM DSP 库高效使用 FPU。ARM DSP 代码位置如下:
14.png

2.3. 示例代码
下面示例代码中对浮点乘法运算进行了测试。用户可以使用 STM32CubeMX 生成 STM32G474 KEIL 工程,在 main.c 文件中加入如下示例代码:

  1. __attribute__((section (".TEST_INPUT_A"))) float32_t testInputA[1024] =
  2. {
  3. 0.623234f, 0.799049f, 0.940890f, -0.992092f, 0.212035f, 0.237882f, -
  4. 1.007763f, -0.742045f,
  5. ~~ 这里数组使用动态生成的float数据,数据量较大,略
  6. -0.417470f, -0.205806f, -0.174323f, 0.217577f, 1.684295f, 0.119528f,
  7. 0.650667f, 2.080061f
  8. };
  9. __attribute__((section (".TEST_INPUT_B"))) float32_t testInputB[1024] =
  10. {
  11. -2.423957f, -0.223831f, 0.058070f, -0.424614f, -0.202918f, -1.513077f, -
  12. 1.126352f, -0.815002f,

  13. ~~ 这里数组使用动态生成的float数据,数据量较大,略
  14. -0.447001f, -0.725993f, 0.354045f, -0.506772f, -2.103747f, -0.664684f, 1.450110f, -0.329805f
  15. };

  16. __attribute__((section (".TEST_RESULT_D"))) float32_t testResult[1024];

  17. float32_t* pA;
  18. float32_t* pB;
  19. float32_t* pR;
  20. /* Private user code --------------------------------------------------*/
  21. /* USER CODE BEGIN 0 */
  22. void test_normal_mul(uint32_t kLoops, float32_t*
  23. pSrcA, float32_t* pSrcB, float32_t*
  24. pResult, uint32_t lenVector)
  25. {
  26. for (uint32_t j = 0; j < kLoops; j++)
  27. {
  28. pA = pSrcA;
  29. pB = pSrcB;
  30. pR = pResult;

  31. for (uint32_t i = 0; i < lenVector; i++)
  32. { *pR++ = (*pA++) * (*pB++) ;
  33.    }
  34.   }
  35. }

  36. #if defined (__FPU_USED) && (__FPU_USED == 1U)
  37. /* Use arm dsp lib to test basic operation Multiply, FPU enabled */
  38. void test_arm_math_mul(uint32_t kLoops, float32_t* pSrcA, float32_t* pSrcB, float32_t*
  39. pResult, uint32_t lenVector)
  40. {
  41. for (uint32_t j = 0; j < kLoops; j++)
  42. {
  43. pA = pSrcA; //Code alignment with the function without FPU
  44. pB = pSrcB;
  45. pR = pResult;
  46. arm_mult_f32(pA, pB, pR, lenVector);
  47. }
  48. }
  49. #endif

  50. /**
  51. * @brief The application entry point.
  52. * @retval int
  53. */
  54. int main(void)
  55. {
  56. /* MCU Configuration------------------------------------------------*/

  57. /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  58. HAL_Init();

  59. /* Configure the system clock */
  60. SystemClock_Config();



  61. HAL_Delay(100);

  62. /* USER CODE BEGIN 2 */
  63. test_normal_mul(10, testInputA, testInputB, testResult, 1024);
  64. test_normal_mul(10, testInputA, testInputB, testResult, 1024);

  65. #if defined (__FPU_USED) && (__FPU_USED == 1U)
  66. // Multiply calculation with arm dsp lib
  67. test_arm_math_mul(10, testInputA, testInputB, testResult, 1024);
  68. test_arm_math_mul(10, testInputA, testInputB, testResult, 1024);
  69. #endif

  70. /* USER CODE END 2 */

  71. /* Infinite loop */
  72. /* USER CODE BEGIN WHILE */
  73. while (1)
  74. {
  75. /* USER CODE END WHILE */

  76. /* USER CODE BEGIN 3 */
  77. }
  78. /* USER CODE END 3 */
  79. }
复制代码
2.4. 工程配置
通过 KEIL 工程 Options / Target, Floating Point Hardware, 确定 FPU On/Off。
15.png


▲ 图3. KEIL 项目工程 FPU 单精度浮点设置
通过 STM32G474_FPU_TEST.sct 文件配置 Data 存放区域,如下例,将测试数据置 于 SRAM2。

  1. RW_IRAM1 0x20000000 0x00014000 { ; RW data
  2. .ANY (+RW +ZI)
  3. }
  4. RW_IRAM2 0x20014000 0x00004000 {
  5. *(.TEST_INPUT_A)
  6. *(.TEST_INPUT_B)
  7. *(.TEST_RESULT_D)
  8. }
  9. RW_CCM 0x20018000 0x00008000 {
  10. }
复制代码
完成后,进行编译链接,即可进行 STM32G474 FPU 性能的测试。

2.5. 编译选项
本文中我们使用的是 KEIL IDE,设置使用的是 KEIL Compiler V5。为了获得代码最 大程度上优化,我们使用了-O3 优化选项,与-Otime(Optimize for Time)结合使用。该组合选项意味着会进行更多代码优化,如循环展开,更激进的函数内联和自动函数内联 (-O3 默认使用--autoinline)等,当然副作用是二进制代码大小会有所增加。另外,增 加设置 --loop_optimization_level=2 来控制循环展开的优化等级。(注意:-- loop_optimization_level=2 选项只能与-O3 -Otime 一起使用。)如果您对 FPU 架构比 较熟悉,也可以尝试增加—fpu=fpv4-sp(Cortex-M4F FPU 实现的是 FPv4-SP 浮点运 算扩展)等选项,不过一般使用默认即可。
16.png
▲ 图4. KEIL 工程,编译选项设置
03使用 KEIL Trace 工具进行测量
3.1. KEIL 工程设置

KEIL 工程下,首先选择工程选项设置,在 Debug 选项页中,右上部使用 Debugger 工具栏中选 Settings,如下图 5 和图 6 设置。注意 KEIL Trace 设置的时钟必须要与实际 STM32 使用的系统时钟相一致,如图 6 中,STM32G474 使用了 170MHz 的系统时钟, KEIL Trace 中也要相应设置为 170MHz。
17.png
▲ 图5. KEIL 工程,Debugger 设置入口
18.png
▲ 图6. KEIL 工程,Cortex-M Trace 功能设置
运行KEIL debugger,如下图7所示,将断点设置在要测量的语句前及其后,执行 代码,当Debugger停在断点时,其状态栏中t1指示的即为当前代码的已执行时间。测试代码起止时间差即为代码执行用时。该Trace功能计时是比较准确的。当然如果您希望掌控更多,也可以通过代码来实现,如增加诸如如下代码:
nStart = DWT->CYCCNT;
~~~需测试执行时间的代码~~~
nStop = DWT->CYCCNT;
然后用(nStop – nStart)/系统时钟,换算成时间即可。(我们这里没有考虑中断,一 般测量前需要禁用中断)
19.png
▲ 图7. KEIL 工程,Debug 模式下 Trace 程序执行时间
3.2. 测试结果
下表列出了STM32G474 10K次 浮点“乘”用时统计。
20.png
▲ 表1. STM32G474 10K 次 浮点“乘”用时统计表
10 X 1024次浮点乘

增加--loop_optimization_level=2 编译选项

FPU 核心汇编代码的比较,见图8和图9。
21.png
▲ 图8. 使用--loop_optimization_level=2 编译选项的常规代码汇编
22.png
▲ 图9. ARM DSP 库 arm_mult_f32 函数汇编


使用loop_optimization_level=2, 常规代码使用KEIL compiler V5编译结果与 arm DSP Lib 的核心汇编基本相同。如果不使用loop_optimization_level=2编译选项, 则可以看到其主要区别在于KEIL Compiler V5 与ARM库对loop的unroll 处理程度不 同。在实际应用时,需要根据应用自身需求判断是否需要使用ARM DSP Lib,基本上 ARM DSP Lib是很高效的。

04小结
本文介绍了使用 STM32G474 FPU 进行浮点运算,从系统的角度、ARM DSP Lib、 编译选项的影响等几个方面探讨如何提升整体性能,并介绍了如何利用 KEIL Trace 工具进 行测量。以供在系统性能方面有需求的客户参考借鉴。


如果你有其他想要的实战笔记,可评论区留言,管管来跟工程师沟通!













收藏 评论0 发布时间:2024-10-30 16:57

举报

0个回答
关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版