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

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

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

0 U( a" w/ }0 Q+ }2 W01引言
% z2 g; ?' {8 W' {: Z4 V0 F客户在使用 STM32G474 时,希望使用 FPU 进行浮点运算,并最大化其性能。本文 从 STM32G474 系统的角度、ARM DSP Lib、编译选项的影响等几个方面探讨如何提升整体性能,并介绍如何使用 KEIL 工具进行测量。- c+ i7 S% ?9 r
# z8 a* }! h' a/ q
02STM32G474 FPU 运算性能优化
4 l: N& O4 {( F& N* g2.1. STM32G474 系统性能优化
7 h. f: k3 j0 o% P
2 [$ v% S5 d3 J0 R. B# O- aSTM32G474 使用的是 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 并发访问产生的性能下降。应用则需要根据实 际情况,合理使用内存区域。: F5 i+ b6 p$ k
12.png
▲ 图1. STM32G474 架构

6 E4 K+ t8 T5 ?
6 U. i/ |3 i6 S$ b, w9 }8 B9 D! ]3 D
2.2. ARM DSP Lib 的使用 , N+ L: W' ^* u+ ]# b
在 ARM DSP 库实现了很多 math 算法,可进行浮点乘加、点积、卷积、FFT、NN 等 多种算法 API,可以使用 ARM DSP 库高效使用 FPU。ARM DSP 代码位置如下:
& y. k7 b. S# B1 P
14.png
( J" m/ r. a2 R$ G/ ]0 Q6 U% r
2.3. 示例代码 + b: w  s/ G) r  {7 t
下面示例代码中对浮点乘法运算进行了测试。用户可以使用 STM32CubeMX 生成 STM32G474 KEIL 工程,在 main.c 文件中加入如下示例代码:; X7 H1 A/ Y, ]/ [6 q  }7 v4 f" Y

  1. + A$ ^, k4 K% ~0 W9 `  J
  2. __attribute__((section (".TEST_INPUT_A"))) float32_t testInputA[1024] = + U. S8 P; s/ t5 D* X/ n4 s
  3. {5 x+ C" g% [; T1 L0 h
  4. 0.623234f, 0.799049f, 0.940890f, -0.992092f, 0.212035f, 0.237882f, -1 H) d9 b0 j- e/ L
  5. 1.007763f, -0.742045f,
      X, k8 R9 r7 [, h8 p
  6. ~~ 这里数组使用动态生成的float数据,数据量较大,略: B2 p5 i& W* U$ Y
  7. -0.417470f, -0.205806f, -0.174323f, 0.217577f, 1.684295f, 0.119528f,
    # r* Q, h! Y6 j( }% l
  8. 0.650667f, 2.080061f
    ) G- {* R+ z, w( d
  9. };
    ( H9 `8 _7 \- P! ~1 z
  10. __attribute__((section (".TEST_INPUT_B"))) float32_t testInputB[1024] =
    $ H7 q& N! O) z; ?
  11. {
    4 n# Q( V# M1 V4 {8 x% F( Y+ m
  12. -2.423957f, -0.223831f, 0.058070f, -0.424614f, -0.202918f, -1.513077f, -9 s+ x' m: M* y5 y0 n
  13. 1.126352f, -0.815002f,
    9 H. j+ F# E0 H
  14. $ e- J" s5 M; Q2 C" N( s
  15. ~~ 这里数组使用动态生成的float数据,数据量较大,略   |3 |7 b5 X9 p, H1 t& b
  16. -0.447001f, -0.725993f, 0.354045f, -0.506772f, -2.103747f, -0.664684f, 1.450110f, -0.329805f 3 J$ P# {3 B8 T" G: D& q5 ~' F
  17. };
    : w: ~0 \! E, G8 v0 R' _5 d* j
  18. 8 ]6 G$ {$ F' q- o- X* u9 t
  19. __attribute__((section (".TEST_RESULT_D"))) float32_t testResult[1024]; % P# w& \- M: I2 n% K+ B( d6 G9 y/ w

  20. : p# V5 ^! K* \  X) p! y
  21. float32_t* pA;
    ' W! E, n% v' K3 B6 S
  22. float32_t* pB;
    ) X# e1 q# S* R7 a! N  e0 v" g
  23. float32_t* pR; % P" C8 R# ~" a: m3 X+ E
  24. /* Private user code --------------------------------------------------*/
    3 `$ C0 S& X/ M+ C  f, w5 v3 V
  25. /* USER CODE BEGIN 0 */
    ' V3 z2 c; X  e8 ^* f" d" D
  26. void test_normal_mul(uint32_t kLoops, float32_t* 2 `# e0 N* e2 q& g
  27. pSrcA, float32_t* pSrcB, float32_t*
    " a- r! _9 h0 b" q2 H
  28. pResult, uint32_t lenVector) 7 j8 z0 q1 E5 L' G
  29. { + {$ v8 N" M5 m2 R
  30. for (uint32_t j = 0; j < kLoops; j++)
    3 p6 x8 V9 s# [$ j
  31. {
    # U' n6 g6 d: b! m1 z# b6 H
  32. pA = pSrcA; # H+ ?9 n0 {/ K, }0 V
  33. pB = pSrcB;
    ) M  n1 `& i$ O  \) f" S) {7 h
  34. pR = pResult;
    9 P5 }0 D! o2 y; z8 P
  35. $ H# U; ]7 x7 o) e  ^- D, b2 Y+ o& `
  36. for (uint32_t i = 0; i < lenVector; i++)
    . M9 O6 Q; I; B8 N' R! ~. p
  37. { *pR++ = (*pA++) * (*pB++) ; 3 B0 m$ l/ }* p
  38.    } . f# Q! E7 h" f7 x2 l
  39.   }
    9 q& ~1 B6 f/ k% F0 |
  40. }
    $ N3 N" g7 I! z1 U8 ]+ K

  41. ( G2 R+ O$ R/ g' q  D0 x
  42. #if defined (__FPU_USED) && (__FPU_USED == 1U) # N6 x! ^! d' i( e
  43. /* Use arm dsp lib to test basic operation Multiply, FPU enabled */ - f5 j# _% Y- w" y! a3 M% N
  44. void test_arm_math_mul(uint32_t kLoops, float32_t* pSrcA, float32_t* pSrcB, float32_t* 2 e: X. i# B$ Y3 p5 P/ e
  45. pResult, uint32_t lenVector) ; |6 i" P4 e" Z* E& [9 @  }. n: Q
  46. { % Q5 U' c6 Q# h. }
  47. for (uint32_t j = 0; j < kLoops; j++) 9 O* R% n; O2 p9 @/ V" D6 Z* l+ f$ D
  48. {   B3 @1 r. t) s7 E9 a
  49. pA = pSrcA; //Code alignment with the function without FPU
    / d( G% y7 ^5 Z$ |: h
  50. pB = pSrcB;
    4 K! g, D: k/ k/ n
  51. pR = pResult;1 X2 K5 ?$ N, d, b. G
  52. arm_mult_f32(pA, pB, pR, lenVector); / a5 t: K7 L2 C8 J
  53. } ' A( [  l! A$ C
  54. }
    ; t! u6 b6 L% ~! D, L! m2 @5 V9 n
  55. #endif
    : c0 e7 k8 V8 [4 S

  56. $ }1 ^( C2 j- B) A9 T9 s
  57. /** ! P3 @9 I" x5 i
  58. * @brief The application entry point. 6 c4 ~: ?/ e- H% f$ D
  59. * @retval int
    8 s0 ]& p. j2 Y0 e# K1 T8 g
  60. */
    4 F+ V; h3 T$ V# i. [; X$ Q7 T
  61. int main(void) ) x7 |7 t; ?8 E" S- O; H* u. i, \
  62. { ) Y( c' ]2 b$ E8 @1 {# E
  63. /* MCU Configuration------------------------------------------------*/ + ^- w+ N1 U7 L+ K" w
  64. $ C% \) v4 q. n" b6 s) X
  65. /* Reset of all peripherals, Initializes the Flash interface and the Systick. */ 1 ?/ |! P/ b- B9 v. a( P: t
  66. HAL_Init(); 7 C# e# B7 o7 N' @
  67. & I* C$ d3 `* k
  68. /* Configure the system clock */
    ( s3 S( w+ c# S# n- P* Q) j
  69. SystemClock_Config(); : J( E4 a2 a2 p' U  D
  70.   u' F! e) n8 H. c- O; X

  71. ! Z  O; D& ]: ?0 I# m
  72. . j0 s, v1 u' ]2 Q! @, p% u
  73. HAL_Delay(100);
    ' r4 V* }6 O: m
  74. 3 K$ b- Y. b2 v8 t+ o
  75. /* USER CODE BEGIN 2 */ % G1 M6 P" D8 E% `4 U( I% c# y8 }! @! I
  76. test_normal_mul(10, testInputA, testInputB, testResult, 1024);
    . U3 L5 I, H* ~" U: E5 Z) }5 U: S
  77. test_normal_mul(10, testInputA, testInputB, testResult, 1024); 1 Z! O; u: `% |3 ^& z7 f. e

  78. - J# |( n) y( k
  79. #if defined (__FPU_USED) && (__FPU_USED == 1U) # }0 I* ~9 b( P
  80. // Multiply calculation with arm dsp lib
    , j: T6 e- Y  o8 _( `, B* g; e
  81. test_arm_math_mul(10, testInputA, testInputB, testResult, 1024);. `) k" U( j( p, ?
  82. test_arm_math_mul(10, testInputA, testInputB, testResult, 1024); / i" S  h, ?, g$ C6 K
  83. #endif
    4 w$ b3 q% l& w+ Y2 H. |# J' Z

  84. & O3 d8 D/ D; R8 J- y
  85. /* USER CODE END 2 */
    5 i$ p4 L( W$ D8 Z' g

  86. & \! E' e1 e) `: L6 f! F/ c0 h" t2 J
  87. /* Infinite loop */
    0 c4 S- K3 H9 e# M$ A0 y
  88. /* USER CODE BEGIN WHILE */
    , [  \4 n( s. u6 X
  89. while (1)
    6 N" A# `/ f  b$ i
  90. {/ b& \2 n! ]: l) n/ W
  91. /* USER CODE END WHILE */ ) ]0 v! V# c" C: @6 S  F4 N, L2 n
  92. 8 C& {" z# E; V5 O% K& X
  93. /* USER CODE BEGIN 3 */
    ; p- u' O: [- H' B
  94. }
      N) t  [  x& I% j& W" U
  95. /* USER CODE END 3 */ 1 B# u+ {6 {) O
  96. }
复制代码
2.4. 工程配置
' B3 I  M6 n8 `4 v通过 KEIL 工程 Options / Target, Floating Point Hardware, 确定 FPU On/Off。
' P2 S5 q" _0 i
15.png
2 a. M0 e+ b. A( d, p9 E4 J  x3 |8 [
+ R. L7 n' a; r
▲ 图3. KEIL 项目工程 FPU 单精度浮点设置
通过 STM32G474_FPU_TEST.sct 文件配置 Data 存放区域,如下例,将测试数据置 于 SRAM2。
6 Z" r$ U1 i& h$ S
  1. * U2 h8 d9 s# a- r  x+ e
  2. RW_IRAM1 0x20000000 0x00014000 { ; RW data% r' @: R/ r. ?/ L
  3. .ANY (+RW +ZI)
    ) P% n2 n1 h" c  H, S% H5 M1 ?
  4. }! J" F8 [  k; z% ?8 M$ o1 k- s
  5. RW_IRAM2 0x20014000 0x00004000 {
    # ^1 b# M# E8 Z4 P; Y  S
  6. *(.TEST_INPUT_A)/ C& o3 ?$ |3 b9 ^! S
  7. *(.TEST_INPUT_B)
    - t/ X$ @+ e# B6 E/ K9 b
  8. *(.TEST_RESULT_D)) o# l9 P! L  Q+ {
  9. }
    , E9 R5 M0 E6 F7 A  B
  10. RW_CCM 0x20018000 0x00008000 {
    ; ~7 `2 \& t" L! y! Z' q
  11. }
复制代码
完成后,进行编译链接,即可进行 STM32G474 FPU 性能的测试。: @5 _0 l( o$ u4 T
$ B7 _9 n2 n. D7 N3 h  K8 d3 F$ G
2.5. 编译选项 # W. a0 W0 Q! K3 q+ I! d/ Y
本文中我们使用的是 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 浮点运 算扩展)等选项,不过一般使用默认即可。$ H" \" _7 [8 L) k0 c( I# X
16.png
▲ 图4. KEIL 工程,编译选项设置
03使用 KEIL Trace 工具进行测量
. H7 I$ l( ~: }3.1. KEIL 工程设置
* T  {9 C! A, B5 t4 P/ h
& U# _; n+ z: N3 o# tKEIL 工程下,首先选择工程选项设置,在 Debug 选项页中,右上部使用 Debugger 工具栏中选 Settings,如下图 5 和图 6 设置。注意 KEIL Trace 设置的时钟必须要与实际 STM32 使用的系统时钟相一致,如图 6 中,STM32G474 使用了 170MHz 的系统时钟, KEIL Trace 中也要相应设置为 170MHz。& n$ T7 r1 \. f
17.png
▲ 图5. KEIL 工程,Debugger 设置入口
18.png
▲ 图6. KEIL 工程,Cortex-M Trace 功能设置
运行KEIL debugger,如下图7所示,将断点设置在要测量的语句前及其后,执行 代码,当Debugger停在断点时,其状态栏中t1指示的即为当前代码的已执行时间。测试代码起止时间差即为代码执行用时。该Trace功能计时是比较准确的。当然如果您希望掌控更多,也可以通过代码来实现,如增加诸如如下代码:5 _7 g5 @2 x: E# s8 t
nStart = DWT->CYCCNT;
* z5 e  D/ L( Y. O6 J8 _~~~需测试执行时间的代码~~~
! F/ X& o% V/ @1 H3 g4 z. enStop = DWT->CYCCNT;
* X3 s* p2 b: Y" Q3 U- m然后用(nStop – nStart)/系统时钟,换算成时间即可。(我们这里没有考虑中断,一 般测量前需要禁用中断)
0 u) U( u0 F% F: o8 g
19.png
▲ 图7. KEIL 工程,Debug 模式下 Trace 程序执行时间
3.2. 测试结果3 i2 H+ c+ z3 b: \; q
下表列出了STM32G474 10K次 浮点“乘”用时统计。
) y( p* P# X. p$ N9 W! b
20.png
▲ 表1. STM32G474 10K 次 浮点“乘”用时统计表
10 X 1024次浮点乘7 w. ~, A1 j5 ~4 x2 L2 W, K

+ m: T& b% }' o( r增加--loop_optimization_level=2 编译选项5 d0 c9 r9 P, n0 j  o2 l' z8 W: P

) S! C2 o; g1 S- G" |. ~FPU 核心汇编代码的比较,见图8和图9。8 G' J* H. D& |1 b1 B4 S+ y. r' ^
21.png
▲ 图8. 使用--loop_optimization_level=2 编译选项的常规代码汇编
22.png
▲ 图9. ARM DSP 库 arm_mult_f32 函数汇编
3 \7 H  ^/ u' D1 U+ {) K

5 Y$ e( O$ o! d3 P5 x1 l/ ~( K使用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是很高效的。% o. Y5 d, Q3 w; j" E2 |$ H# z
+ T3 y1 j! K' ?
04小结
7 b: }3 |: X, j. p本文介绍了使用 STM32G474 FPU 进行浮点运算,从系统的角度、ARM DSP Lib、 编译选项的影响等几个方面探讨如何提升整体性能,并介绍了如何利用 KEIL Trace 工具进 行测量。以供在系统性能方面有需求的客户参考借鉴。
' ~1 {8 D0 e. N/ h3 \; k: @
# {. U. G2 A$ B7 t! X* H
. v  f# T5 F* |如果你有其他想要的实战笔记,可评论区留言,管管来跟工程师沟通!! |4 Y, B8 s' i. y+ _

: i6 h; Q0 o2 m  z: @  Z# C, V9 ?
7 e) Q* w0 s7 f; C1 W2 j
' @8 Y. y( o6 C
) n: @$ ^+ j6 |9 n/ {3 C/ W3 W- _2 _0 n6 |1 f
+ V7 K" z7 z9 V% q' w

1 v4 j3 ~! c! v5 y, N; Z6 \, ]1 |1 h

  D' x+ v: T4 I
" ?+ t# P4 L/ A/ C& V) o4 E+ G. m1 d3 W! j0 A0 H

9 J% D5 A! `6 R% I+ R& J9 T: D
) ^' k/ J1 Q5 g& T7 E
收藏 评论0 发布时间:2024-10-30 16:57

举报

0个回答
关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32Cube扩展软件包
意法半导体边缘AI套件
ST - 理想汽车豪华SUV案例
ST意法半导体智能家居案例
STM32 ARM Cortex 32位微控制器
关注我们
st-img 微信公众号
st-img 手机版