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

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

[复制链接]
STMCU-管管 发布时间:2024-10-30 16:57
+ t( A# h% _1 A2 k3 `( P
01引言
) Z4 l6 s2 ~9 V8 P客户在使用 STM32G474 时,希望使用 FPU 进行浮点运算,并最大化其性能。本文 从 STM32G474 系统的角度、ARM DSP Lib、编译选项的影响等几个方面探讨如何提升整体性能,并介绍如何使用 KEIL 工具进行测量。: A  H% W: e; K

2 m3 z6 S+ e( E: d' H02STM32G474 FPU 运算性能优化 ( O% g0 `5 M# Z+ k5 {" _1 I
2.1. STM32G474 系统性能优化 1 q2 |7 p% ^, _: m1 Y
+ R; X4 a! f4 |) _: o7 \( q
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 并发访问产生的性能下降。应用则需要根据实 际情况,合理使用内存区域。
' i; i% R2 M4 L7 j' Y
12.png
▲ 图1. STM32G474 架构

, ]7 D$ }8 U/ }# {; S$ ]8 S/ k6 z0 K

5 H( g' c6 l  F0 Z( J( Z2.2. ARM DSP Lib 的使用 / g5 e4 y+ J' Y7 x1 |% W/ V  T$ T
在 ARM DSP 库实现了很多 math 算法,可进行浮点乘加、点积、卷积、FFT、NN 等 多种算法 API,可以使用 ARM DSP 库高效使用 FPU。ARM DSP 代码位置如下:3 p: B  [; p/ B( r& `. k& Z
14.png

! |# J1 I1 E( Y0 s3 O' y2.3. 示例代码 $ l: v/ @3 b1 V( y. ]
下面示例代码中对浮点乘法运算进行了测试。用户可以使用 STM32CubeMX 生成 STM32G474 KEIL 工程,在 main.c 文件中加入如下示例代码:6 L# t7 X* [" K" D4 K2 h; [% y2 y
  1. # f& M. T1 n' q: b/ h1 x
  2. __attribute__((section (".TEST_INPUT_A"))) float32_t testInputA[1024] = 3 U4 t. Q$ g+ j
  3. {
    3 Z8 o# O9 r! [* l* q( j$ \
  4. 0.623234f, 0.799049f, 0.940890f, -0.992092f, 0.212035f, 0.237882f, -  t! b% g& u$ }: a/ K: x
  5. 1.007763f, -0.742045f,
    " U. c! k! N/ M
  6. ~~ 这里数组使用动态生成的float数据,数据量较大,略  R, M7 e/ p& f3 Z5 I% `
  7. -0.417470f, -0.205806f, -0.174323f, 0.217577f, 1.684295f, 0.119528f,
    8 `' p0 |8 c# W2 r+ [
  8. 0.650667f, 2.080061f
    4 Y6 G, m& Z3 I0 P& @4 ]
  9. };2 T: F* X' s; W" a0 G; {+ K3 ^: ~
  10. __attribute__((section (".TEST_INPUT_B"))) float32_t testInputB[1024] = . f/ d; z# W# o
  11. {
    & C. C) h" |- {
  12. -2.423957f, -0.223831f, 0.058070f, -0.424614f, -0.202918f, -1.513077f, -
    7 e5 A+ V% k( [; m4 y1 j; x. E0 ]
  13. 1.126352f, -0.815002f,
    3 P3 [8 K- C. _( e8 I; r

  14. + m' o4 P1 h: n6 _3 R
  15. ~~ 这里数组使用动态生成的float数据,数据量较大,略 " m& W# {0 ]5 u/ U3 A- Y
  16. -0.447001f, -0.725993f, 0.354045f, -0.506772f, -2.103747f, -0.664684f, 1.450110f, -0.329805f
    , ?; d# J3 q( M
  17. };
    / o. `$ X2 \: n+ \2 J
  18. 2 L3 w! ^7 ~/ D& q
  19. __attribute__((section (".TEST_RESULT_D"))) float32_t testResult[1024];
    ' l/ u3 i" u' F
  20. ( C4 Z. _3 _( F6 u! d$ h
  21. float32_t* pA;
    ( ^: f, O( N# F, u" ?8 L" d
  22. float32_t* pB;
    6 F. {5 Z# `3 B7 n% Y
  23. float32_t* pR;
    - u/ ~0 y* a+ o# \  P
  24. /* Private user code --------------------------------------------------*/ " P% q& L+ d2 D2 A4 D0 m) X
  25. /* USER CODE BEGIN 0 */
    0 M2 N  _+ s# ]/ J
  26. void test_normal_mul(uint32_t kLoops, float32_t*
    ! D* F; W3 e9 o3 f
  27. pSrcA, float32_t* pSrcB, float32_t* ) A2 V7 e3 F8 U) y
  28. pResult, uint32_t lenVector) 2 j) A8 N; z" `0 N
  29. { $ k! }* W7 H8 x8 P8 V, X
  30. for (uint32_t j = 0; j < kLoops; j++)
    ! N) w4 m) I6 [; J* _$ U2 i- S6 i
  31. { + `+ I5 m" r4 c
  32. pA = pSrcA;
      h& B" v% T' i# Z- a
  33. pB = pSrcB; 9 `4 }* T8 g0 w2 W' ^
  34. pR = pResult;
    1 R, y/ N) S. l4 |- n+ Q

  35.   d. u& j6 P  ~3 T4 N
  36. for (uint32_t i = 0; i < lenVector; i++) . s, Z5 r; H" }5 K9 T5 A
  37. { *pR++ = (*pA++) * (*pB++) ; ! l/ Y* q" z9 |0 I  Q8 S
  38.    } 2 Q5 i. `# o* P: y4 H# }
  39.   }
    0 S5 E. s: B( p
  40. }
    2 m8 Y, G9 w3 p+ u% z# z

  41. % p+ p( T5 s; {# P
  42. #if defined (__FPU_USED) && (__FPU_USED == 1U)
    7 U$ J8 @2 X' @- p$ g
  43. /* Use arm dsp lib to test basic operation Multiply, FPU enabled */
    : l5 D- ~# @" D/ @! c
  44. void test_arm_math_mul(uint32_t kLoops, float32_t* pSrcA, float32_t* pSrcB, float32_t*
    + b. C  k+ H) z4 `3 }: j; S; C
  45. pResult, uint32_t lenVector) ' u! g8 C' t' Q$ l  }- Y8 K
  46. { $ t/ h7 v  G# B( {$ i
  47. for (uint32_t j = 0; j < kLoops; j++)
    6 L, w# V& I# l+ e/ W) I+ d# @
  48. {
    2 [( K' y$ P  b5 U( a
  49. pA = pSrcA; //Code alignment with the function without FPU
    " i& U1 [9 R- w: j. ?
  50. pB = pSrcB;
    " y: }) j  G5 j  [. H$ m
  51. pR = pResult;
    - _) Y, \$ N! N
  52. arm_mult_f32(pA, pB, pR, lenVector); 6 l- o6 i" }! D& ?3 t
  53. }
      X# K! V$ c& {3 I
  54. }
    + v- }8 e" ?0 o; C- |- f3 c
  55. #endif - q6 H$ _$ m6 d, ?2 G9 C
  56. ! m6 U( `0 g4 p) U
  57. /**   Y3 u1 q$ A+ u$ d
  58. * @brief The application entry point. ( s" ]5 b2 |! r, I' o) s
  59. * @retval int $ U7 C/ ^6 l# P, S, T" x& ]+ x9 e
  60. */
    2 g* V5 j( |9 u# B$ @& i/ E
  61. int main(void) - Z+ G3 `* ~" y9 [: D; d& S
  62. {
    7 S  H/ H: F% a
  63. /* MCU Configuration------------------------------------------------*/ 4 z7 A2 Z' o  I# G; D+ z2 P

  64. - c8 W( S6 f' F  H) X! R
  65. /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
    * S/ |* ?! I- O4 z
  66. HAL_Init(); 7 X9 y  E. g1 C8 d2 K7 C/ z

  67. 2 G% n/ ?3 g$ c( G+ I
  68. /* Configure the system clock */ ) g% p# l1 E+ o/ }4 g, g& p4 H# |
  69. SystemClock_Config(); 9 I- w' a; l9 Z- H
  70. " S6 q5 i5 F2 `2 b4 t

  71. & r7 _, N- e0 ^* g* n4 {) I* \

  72. + x* ^5 N7 _' G1 s
  73. HAL_Delay(100);
    # j  d/ |6 C4 z; r. E& V7 ?

  74. 4 c/ {* _7 f8 y" j# V9 i
  75. /* USER CODE BEGIN 2 */ & y' l; j( ~' d% W
  76. test_normal_mul(10, testInputA, testInputB, testResult, 1024);
    5 b6 C+ o4 e0 p
  77. test_normal_mul(10, testInputA, testInputB, testResult, 1024); ; v+ r$ J# }3 N' |
  78. ' `# j  g# v2 z
  79. #if defined (__FPU_USED) && (__FPU_USED == 1U)
      n" |2 f0 J; X
  80. // Multiply calculation with arm dsp lib
    6 c- k( m- b6 p* g$ j; P5 O7 g4 e
  81. test_arm_math_mul(10, testInputA, testInputB, testResult, 1024);
    $ j, ]* y+ u- {' I4 }% q
  82. test_arm_math_mul(10, testInputA, testInputB, testResult, 1024); & H0 @1 }( P: s) n' o# O" b6 S
  83. #endif
    & w) K; F# `; K+ P

  84. 3 H* |- H6 J, Q* a5 ~* _
  85. /* USER CODE END 2 */ ) U, \/ y, b' M8 Z% F0 J/ P
  86. ' {, l! ^  T2 A
  87. /* Infinite loop */ & w$ g* q, L+ ~4 Q+ R( a
  88. /* USER CODE BEGIN WHILE */ 4 q9 z+ c: d: V! ?) O1 L
  89. while (1) ' p3 r7 S5 M' Z+ c3 O
  90. {
      j- S( o& D) F8 X7 f  ]
  91. /* USER CODE END WHILE */
    ! F. E' Q8 S, b- x+ ^
  92.   O/ B7 z9 C; N2 v+ C( c
  93. /* USER CODE BEGIN 3 */
    8 T/ w$ K3 r% o
  94. } 0 z7 k% m' t& i5 n  l# H$ J. W
  95. /* USER CODE END 3 */
    & H' [1 k# b8 L) c3 [( |. u
  96. }
复制代码
2.4. 工程配置
1 m8 I2 N9 Q% D通过 KEIL 工程 Options / Target, Floating Point Hardware, 确定 FPU On/Off。6 k* M+ V9 v& v9 ~. p# U' N
15.png
. k+ S' v; [9 W
/ l( |5 u& l" x( B1 U" N/ ~7 }4 [& \
▲ 图3. KEIL 项目工程 FPU 单精度浮点设置
通过 STM32G474_FPU_TEST.sct 文件配置 Data 存放区域,如下例,将测试数据置 于 SRAM2。
( @. ]7 M- f0 Y0 ^
  1. ( b: ?  k. p# z% Y" ?8 F
  2. RW_IRAM1 0x20000000 0x00014000 { ; RW data
    7 ?( M4 S/ [9 n$ x
  3. .ANY (+RW +ZI)
    2 Y' ]3 c; S- j$ L0 x
  4. }4 _6 ^8 V6 {2 h7 }) f" U6 r. v+ x
  5. RW_IRAM2 0x20014000 0x00004000 {" F" U' a8 G, @" @: W& c6 i
  6. *(.TEST_INPUT_A)# O) B1 H% f+ @+ l& N
  7. *(.TEST_INPUT_B)
    % Y1 o2 t( T0 c! L1 w% s' N
  8. *(.TEST_RESULT_D)- S* A9 w/ F( `7 O3 A" p
  9. }
    , v. j! V# y" d# a
  10. RW_CCM 0x20018000 0x00008000 {
    ( j$ U- \/ ?/ M$ O5 h
  11. }
复制代码
完成后,进行编译链接,即可进行 STM32G474 FPU 性能的测试。1 |1 e7 [) O) P; O- R

" `, Q: i1 `" v, g. Y" A2.5. 编译选项
  z, F0 y% p7 A" `5 ~( E: ?本文中我们使用的是 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 浮点运 算扩展)等选项,不过一般使用默认即可。
8 L- U% V% Z, H0 m
16.png
▲ 图4. KEIL 工程,编译选项设置
03使用 KEIL Trace 工具进行测量
/ P" m8 h+ o7 ?" S6 ^5 i3.1. KEIL 工程设置 1 u* j  h, c' ^0 |' m
# f+ P+ `3 |$ Y% l2 U+ c6 ~
KEIL 工程下,首先选择工程选项设置,在 Debug 选项页中,右上部使用 Debugger 工具栏中选 Settings,如下图 5 和图 6 设置。注意 KEIL Trace 设置的时钟必须要与实际 STM32 使用的系统时钟相一致,如图 6 中,STM32G474 使用了 170MHz 的系统时钟, KEIL Trace 中也要相应设置为 170MHz。  N: g7 x3 I; ^) x( B& r* X3 D
17.png
▲ 图5. KEIL 工程,Debugger 设置入口
18.png
▲ 图6. KEIL 工程,Cortex-M Trace 功能设置
运行KEIL debugger,如下图7所示,将断点设置在要测量的语句前及其后,执行 代码,当Debugger停在断点时,其状态栏中t1指示的即为当前代码的已执行时间。测试代码起止时间差即为代码执行用时。该Trace功能计时是比较准确的。当然如果您希望掌控更多,也可以通过代码来实现,如增加诸如如下代码:8 d) T( W/ g. o1 k9 }9 b
nStart = DWT->CYCCNT;
& `3 ~; K, q+ T  Z~~~需测试执行时间的代码~~~ 4 e; V* o% l$ }5 X' L
nStop = DWT->CYCCNT;' x+ N0 J* ]( W( T/ N3 Q2 X3 h2 R  m
然后用(nStop – nStart)/系统时钟,换算成时间即可。(我们这里没有考虑中断,一 般测量前需要禁用中断)
* [( S5 a4 m& g# n' ?( e2 s
19.png
▲ 图7. KEIL 工程,Debug 模式下 Trace 程序执行时间
3.2. 测试结果9 f- J7 ^1 W5 r" G$ }, ~2 G
下表列出了STM32G474 10K次 浮点“乘”用时统计。) {+ {& Q5 \9 V4 c* W- _
20.png
▲ 表1. STM32G474 10K 次 浮点“乘”用时统计表
10 X 1024次浮点乘9 Z) j+ T! \; W' ]* d# x! {$ j

* {* h2 }) d- [- U2 v. y增加--loop_optimization_level=2 编译选项
  Z2 X9 h0 V6 S  X2 s6 i* E

, n/ B+ S8 }( i1 }/ HFPU 核心汇编代码的比较,见图8和图9。* ~( q0 E3 k4 \# j' J0 L
21.png
▲ 图8. 使用--loop_optimization_level=2 编译选项的常规代码汇编
22.png
▲ 图9. ARM DSP 库 arm_mult_f32 函数汇编
* z4 g+ Z. g4 ]7 ^% o" ^$ Z' m
6 [5 J) }; E% e7 H9 z
使用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是很高效的。
; H2 \+ [. U+ V5 `/ o4 _" g2 @! `3 b4 i; N
04小结 5 _3 d+ D. H4 H3 ]5 I, K  |5 H; l
本文介绍了使用 STM32G474 FPU 进行浮点运算,从系统的角度、ARM DSP Lib、 编译选项的影响等几个方面探讨如何提升整体性能,并介绍了如何利用 KEIL Trace 工具进 行测量。以供在系统性能方面有需求的客户参考借鉴。
! [+ n7 f1 E1 a8 j8 v5 n; B0 ^7 U* m; `
& V& E+ J& S2 M! ~2 a8 j0 l
如果你有其他想要的实战笔记,可评论区留言,管管来跟工程师沟通!6 ?" h# O/ s$ m, @
8 s! S7 n# m7 V" U

: E: q. E" w7 |8 {  W+ ]) l$ Z* j0 q8 f) e
2 ^2 I% D4 d" \5 K
4 F  R* B/ [# D- i
2 O: u9 ]7 Z0 }* h
, Q$ N5 t" R7 r& O" K! r3 H

; |8 X: M0 t" W4 w
: n) r; _+ S2 ^9 N- q; i' Y7 `: p2 H0 c2 `. n# S6 Q3 e; a# u) w: Z

: e1 Z: y" ~, U& ]4 Z

  \* m9 {4 q: b3 |, Y" ^* W* F: c  @; K0 T& I3 r4 ?
收藏 评论0 发布时间:2024-10-30 16:57

举报

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