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

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

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

2 }1 Y! `$ N' o6 }3 v6 G5 ?% Y01引言. p- v! X. t4 A; X' W
客户在使用 STM32G474 时,希望使用 FPU 进行浮点运算,并最大化其性能。本文 从 STM32G474 系统的角度、ARM DSP Lib、编译选项的影响等几个方面探讨如何提升整体性能,并介绍如何使用 KEIL 工具进行测量。0 {* L' X- u5 A; [( k% j1 h
: _% @, u# v, q- B
02STM32G474 FPU 运算性能优化 , d, B+ F( f# F( Z' ?9 S8 l# V# P$ [
2.1. STM32G474 系统性能优化
" p1 C3 @: X2 G  ]0 i2 e3 b7 a3 `! A2 @4 o7 ?& K0 v# y! p/ C
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 并发访问产生的性能下降。应用则需要根据实 际情况,合理使用内存区域。2 E7 Q4 t% t$ p3 d& j4 ^
12.png
▲ 图1. STM32G474 架构

0 C: l8 T0 L$ h: u. [4 z3 N( \

7 m; e- r5 A2 l- t' {: W2.2. ARM DSP Lib 的使用 ! k2 O* `# L5 j+ n( p4 ^- i( k
在 ARM DSP 库实现了很多 math 算法,可进行浮点乘加、点积、卷积、FFT、NN 等 多种算法 API,可以使用 ARM DSP 库高效使用 FPU。ARM DSP 代码位置如下:$ |8 ?, f6 z  m- m
14.png
: n9 v3 {) D+ p1 c
2.3. 示例代码 2 Z7 y! C3 C4 I4 {5 e7 o7 a
下面示例代码中对浮点乘法运算进行了测试。用户可以使用 STM32CubeMX 生成 STM32G474 KEIL 工程,在 main.c 文件中加入如下示例代码:  E4 h5 @4 G3 A' W8 m) H
  1.   w  j. z+ Q# T, g5 @0 w
  2. __attribute__((section (".TEST_INPUT_A"))) float32_t testInputA[1024] =
    2 O" m' V- u+ b( p1 t$ T  ^% M
  3. {7 B" X, E( T% Z, n* i  w; m  l
  4. 0.623234f, 0.799049f, 0.940890f, -0.992092f, 0.212035f, 0.237882f, -9 Y' v( E+ y9 u& Q' R, n
  5. 1.007763f, -0.742045f,0 W7 A4 B& V( x6 b
  6. ~~ 这里数组使用动态生成的float数据,数据量较大,略% l5 `  h. Z  l5 Y
  7. -0.417470f, -0.205806f, -0.174323f, 0.217577f, 1.684295f, 0.119528f,
    ! p1 D3 Y5 l5 G* V5 x9 D
  8. 0.650667f, 2.080061f
    8 h# E1 [$ v5 s
  9. };" f# G. ~2 q. t% [. c' _
  10. __attribute__((section (".TEST_INPUT_B"))) float32_t testInputB[1024] = - c! `: P* M& X6 V4 \! ~; B: Y: l
  11. {
    8 ~( R3 L+ v- L0 `6 n
  12. -2.423957f, -0.223831f, 0.058070f, -0.424614f, -0.202918f, -1.513077f, -9 l( l5 P6 c) h5 Y0 b
  13. 1.126352f, -0.815002f,
    , @) w$ ]1 ^* n0 x2 w

  14.   s5 W1 u; {. j% s( ?- S
  15. ~~ 这里数组使用动态生成的float数据,数据量较大,略
    9 i' D% P7 ~) l0 Q/ o4 D( ~3 x
  16. -0.447001f, -0.725993f, 0.354045f, -0.506772f, -2.103747f, -0.664684f, 1.450110f, -0.329805f 8 c9 z7 D6 y( z$ e
  17. };
    2 j  D* ^  O8 n8 |8 @+ B8 t. ~/ ]
  18. 2 N, E3 P: U" f' i5 L; ?1 A
  19. __attribute__((section (".TEST_RESULT_D"))) float32_t testResult[1024]; ; j/ _; E3 q2 s( F, }4 y
  20. ! g$ s3 u: t8 l- n( [+ X
  21. float32_t* pA;
    / f% Q; s! @4 _
  22. float32_t* pB; # ]& ]$ C7 M: y
  23. float32_t* pR;
    ) P9 p' k6 _. R' m' I! ?
  24. /* Private user code --------------------------------------------------*/
    8 l) F: S. f1 M9 ^% {
  25. /* USER CODE BEGIN 0 */ % C' \2 g# M! r/ [& M; {9 V
  26. void test_normal_mul(uint32_t kLoops, float32_t*
    ; S! Q" _& e! L$ E! C1 R) x
  27. pSrcA, float32_t* pSrcB, float32_t*
    ! g0 V# s. {- k3 `4 y: z
  28. pResult, uint32_t lenVector) , c/ A) T5 `+ ]
  29. {
    5 N1 ?, `* Q- M1 Q; [. A2 \
  30. for (uint32_t j = 0; j < kLoops; j++) , k" L" d' z/ O2 A' e0 I5 }/ |
  31. {
    5 [; @$ f# ^2 R0 Z
  32. pA = pSrcA;
    5 ~  C# d& n" M# h% Q4 M; {/ r
  33. pB = pSrcB; 5 F) y3 ~6 X5 D2 }+ s
  34. pR = pResult;
    7 L9 g4 O2 G6 _5 S+ d$ B

  35. $ r  H$ R& x% F2 T
  36. for (uint32_t i = 0; i < lenVector; i++)
    ! `% Q8 V9 P" K. Z% }  m
  37. { *pR++ = (*pA++) * (*pB++) ;
    4 V( S+ P' y9 N
  38.    }
    : p# ]" b/ K# J, x; ]/ |- [
  39.   }
    " U! B# l- O% \( _; |  N
  40. } ! B$ e3 g- v/ W+ b- x

  41. ' R- H7 K! ^7 k2 h  n9 O9 W
  42. #if defined (__FPU_USED) && (__FPU_USED == 1U)   }8 \2 Q% f4 E% j
  43. /* Use arm dsp lib to test basic operation Multiply, FPU enabled */ ) F8 F' [: u4 e; h1 S
  44. void test_arm_math_mul(uint32_t kLoops, float32_t* pSrcA, float32_t* pSrcB, float32_t* : q' P  B2 S/ i  W- {; e+ o
  45. pResult, uint32_t lenVector) " Q4 y; n. c! y+ U$ m
  46. {
    * c$ ^% t. t+ i) Z
  47. for (uint32_t j = 0; j < kLoops; j++) 5 P8 e" ~4 K, L( C" w# g! e
  48. {
    * b; ~- ]+ o( a# G
  49. pA = pSrcA; //Code alignment with the function without FPU
    " Q5 m6 j$ v2 c8 }' c9 y
  50. pB = pSrcB; $ ^4 e2 \2 B) A
  51. pR = pResult;4 @1 ^8 g1 d1 L0 \! e6 G' F
  52. arm_mult_f32(pA, pB, pR, lenVector); & h, \0 W. ~' I2 z" c6 Q6 h
  53. } ! G5 r( \% B( Q. W0 Q
  54. } * v6 T* C5 F) {# _+ X* ?
  55. #endif
    3 N/ n5 u# v, D
  56.   C( z% F5 `1 U  ]! V8 a! h8 q3 j
  57. /** , _. e2 ~4 F0 X& Z. f8 d/ d( k
  58. * @brief The application entry point. 9 e! h& @9 [# g! g( u) Q. ^& i5 @
  59. * @retval int 4 p* ~9 F. U! L" {3 u$ U
  60. */
    " b- c4 `. k9 ]  ~* }# t4 `1 Q
  61. int main(void)
    ; r) A: h! r: \0 k/ A3 i' C3 N1 o; E: M
  62. {
    # a" [4 b$ V% b: e. ]9 u) x
  63. /* MCU Configuration------------------------------------------------*/ 6 N$ M& ^! ~- m1 `( ]
  64. 1 U3 r2 ^2 n$ P& v7 C
  65. /* Reset of all peripherals, Initializes the Flash interface and the Systick. */ % ~; L" H# ]5 v. {
  66. HAL_Init();
    4 m, G/ d% ~! ^' |# @! G+ g
  67. ; r4 j7 [* R! M4 [4 p3 V/ J' H" e
  68. /* Configure the system clock */ 0 ?0 w4 \" L: x2 }4 z
  69. SystemClock_Config(); - J4 m( v9 g7 h2 b5 Z- B7 }0 V8 D6 j

  70. + |2 r4 p; z& [# m# a" h) G& u

  71. + w9 R& T/ A  Q& A) o; z

  72. . l. W; y* W# F. G! v2 W
  73. HAL_Delay(100); & ?8 E7 b. D4 t
  74. 3 s2 g( @% S4 V  I% n  y
  75. /* USER CODE BEGIN 2 */ 7 t, z- v, t; H
  76. test_normal_mul(10, testInputA, testInputB, testResult, 1024);
    * ]( r1 o, V& H& G' w
  77. test_normal_mul(10, testInputA, testInputB, testResult, 1024);
    + E& P' K$ ]- N7 E3 @

  78. ; w. L4 K, J9 r" J
  79. #if defined (__FPU_USED) && (__FPU_USED == 1U) * F( ^+ `$ s- g" {
  80. // Multiply calculation with arm dsp lib
    : A3 g7 i! B! f) ]9 l+ X
  81. test_arm_math_mul(10, testInputA, testInputB, testResult, 1024);
    ' v; v$ C; o5 e; A0 @% G/ y
  82. test_arm_math_mul(10, testInputA, testInputB, testResult, 1024);
    & i5 Q$ \5 F' c2 p) r, R
  83. #endif
    # C2 t6 N8 }% i; q( y% U2 k
  84. ' E- ?$ I' R, Y9 I- q' R+ a$ e% J
  85. /* USER CODE END 2 */
    1 T) w  v, `- h( o

  86. - P6 |6 w6 K, J8 [: J% B
  87. /* Infinite loop */ , V! S5 ^7 D3 d1 J9 ^5 ~. G9 X: a( u
  88. /* USER CODE BEGIN WHILE */ ( Q5 o9 k+ T) G( o
  89. while (1) # P+ ?( x6 j) S2 s8 i
  90. {
    9 Z- s2 z! i" t  @2 I) y
  91. /* USER CODE END WHILE */ # Q0 D8 W+ m+ v7 i
  92. % P) U6 r( S3 E! m. g
  93. /* USER CODE BEGIN 3 */
    ' g5 u2 D* h5 f9 v& g( C
  94. }
    8 X" W/ V+ s0 v, b6 A" l9 u$ v
  95. /* USER CODE END 3 */
    * h+ x& Y# p' [- V! M
  96. }
复制代码
2.4. 工程配置
. H$ a1 Y7 z  r, G4 Q7 R, }通过 KEIL 工程 Options / Target, Floating Point Hardware, 确定 FPU On/Off。
6 I( y% n( ~: Z8 C% ~! C
15.png
1 h* O) _% v" t0 n" b$ d9 n
1 b; p8 y- R! ?2 ?. ^/ {
▲ 图3. KEIL 项目工程 FPU 单精度浮点设置
通过 STM32G474_FPU_TEST.sct 文件配置 Data 存放区域,如下例,将测试数据置 于 SRAM2。
9 q8 }/ l2 G- i1 d7 O7 m% u& ~' \

  1. 7 H( q/ g2 F7 G3 b$ L9 E/ Z3 l
  2. RW_IRAM1 0x20000000 0x00014000 { ; RW data9 x  ^2 h# J; q  S" [3 W# O
  3. .ANY (+RW +ZI)
    4 L+ V5 |" p3 n; a4 a3 b
  4. }
    0 l& x. p4 r1 @) k% _1 A2 Z
  5. RW_IRAM2 0x20014000 0x00004000 {
    ! ~: D9 E" C4 I  Z. m( T' M$ J
  6. *(.TEST_INPUT_A)
    9 F4 w% k+ u2 t
  7. *(.TEST_INPUT_B); i7 M% @! p, L
  8. *(.TEST_RESULT_D)
    $ i/ j5 Z0 H) D
  9. }2 L7 y% p9 e  `4 `  T2 g' K
  10. RW_CCM 0x20018000 0x00008000 {
    2 I4 {# D1 o, x, Z# |
  11. }
复制代码
完成后,进行编译链接,即可进行 STM32G474 FPU 性能的测试。$ V5 x, y6 d6 c! o; |' N6 ?- t

) M) q! E+ S' B/ Y- h2.5. 编译选项 % L; T- Z0 e, `& m
本文中我们使用的是 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 浮点运 算扩展)等选项,不过一般使用默认即可。& B& z! J& P4 Q! Z, e/ p7 [
16.png
▲ 图4. KEIL 工程,编译选项设置
03使用 KEIL Trace 工具进行测量 . ~( ~: |( h# o' @9 \+ {
3.1. KEIL 工程设置
7 P' L/ U4 d0 X8 [3 d3 f. R- r2 [9 @9 R
KEIL 工程下,首先选择工程选项设置,在 Debug 选项页中,右上部使用 Debugger 工具栏中选 Settings,如下图 5 和图 6 设置。注意 KEIL Trace 设置的时钟必须要与实际 STM32 使用的系统时钟相一致,如图 6 中,STM32G474 使用了 170MHz 的系统时钟, KEIL Trace 中也要相应设置为 170MHz。  h( `% g8 c0 M0 ~# Q" m
17.png
▲ 图5. KEIL 工程,Debugger 设置入口
18.png
▲ 图6. KEIL 工程,Cortex-M Trace 功能设置
运行KEIL debugger,如下图7所示,将断点设置在要测量的语句前及其后,执行 代码,当Debugger停在断点时,其状态栏中t1指示的即为当前代码的已执行时间。测试代码起止时间差即为代码执行用时。该Trace功能计时是比较准确的。当然如果您希望掌控更多,也可以通过代码来实现,如增加诸如如下代码:
' @5 v: u; p1 f5 P0 O' `9 D+ j5 h' snStart = DWT->CYCCNT; 9 ?& V5 i% ^5 b( t
~~~需测试执行时间的代码~~~
/ K' f. w( _" o( `nStop = DWT->CYCCNT;) d5 R! ~( E2 x: q9 b$ T3 T
然后用(nStop – nStart)/系统时钟,换算成时间即可。(我们这里没有考虑中断,一 般测量前需要禁用中断)
- D; S! N# d+ X- j
19.png
▲ 图7. KEIL 工程,Debug 模式下 Trace 程序执行时间
3.2. 测试结果
/ T/ x) s8 x4 ?下表列出了STM32G474 10K次 浮点“乘”用时统计。
, S6 y  B& _0 m0 i
20.png
▲ 表1. STM32G474 10K 次 浮点“乘”用时统计表
10 X 1024次浮点乘
- \8 _1 \* W1 K" }. C: _2 K1 X5 A2 Q

; x# _  ~; Q* }! i, w- G# R增加--loop_optimization_level=2 编译选项) ?& G3 Q- x# W* s
, v" Q3 x2 }$ C- q) J& e3 V! A1 W& ^
FPU 核心汇编代码的比较,见图8和图9。
) \( C" u  ~4 w5 Z, O: K1 x
21.png
▲ 图8. 使用--loop_optimization_level=2 编译选项的常规代码汇编
22.png
▲ 图9. ARM DSP 库 arm_mult_f32 函数汇编

* z3 I8 @4 Y  E* }+ Q

  w& k* U9 I4 P, P% m2 p使用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是很高效的。
  s3 W2 m6 X8 M. N; I
9 r4 m  g2 ~7 G5 T' V04小结
  V; X) `! Y- m) s1 u# e+ x- y本文介绍了使用 STM32G474 FPU 进行浮点运算,从系统的角度、ARM DSP Lib、 编译选项的影响等几个方面探讨如何提升整体性能,并介绍了如何利用 KEIL Trace 工具进 行测量。以供在系统性能方面有需求的客户参考借鉴。
. v5 l3 c) `) x/ O- z+ v7 s) C8 C1 o- p  g! a* N* C' C& D
2 ^. J' F8 L4 m( k* F
如果你有其他想要的实战笔记,可评论区留言,管管来跟工程师沟通!
9 u* Z: R# f! h9 U" |# m5 ~  A
# Z! B: _' _* U& R) ]$ ~; P/ C* `! J3 d; m8 S
3 H- v$ ~; r" P/ w& S

/ L. \6 A9 a5 n. E0 S, b
  |% Q; o* \8 t+ H2 a( \6 a
% L' i$ E6 t: X& y5 @; I) I* s% v' p! L( y: x
9 c  B0 c' a: c. i$ S

7 q" A. J1 \' b5 ^+ s- z
' v6 M" ?9 Y7 i; i2 }- R7 V0 g' Y$ f9 w, j
  u& D% f7 Q) Q8 x4 E9 d

- m8 V) j/ A! f- o& [/ _; c5 O& k
收藏 评论0 发布时间:2024-10-30 16:57

举报

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