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

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

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

- P6 Q- Y: R: g8 G! q& `# N/ A. W* W# y01引言. A% o7 G% W) ?; k, N( I
客户在使用 STM32G474 时,希望使用 FPU 进行浮点运算,并最大化其性能。本文 从 STM32G474 系统的角度、ARM DSP Lib、编译选项的影响等几个方面探讨如何提升整体性能,并介绍如何使用 KEIL 工具进行测量。
6 s8 `1 _6 G  g0 S) u6 |- e# W1 v/ _7 h! Z. F7 l
02STM32G474 FPU 运算性能优化
6 D2 T  k2 s& F2.1. STM32G474 系统性能优化 0 g% l6 A* s' u0 S: F& s. l# m8 Y4 J
! I1 ?# F- J7 r  T1 b
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 并发访问产生的性能下降。应用则需要根据实 际情况,合理使用内存区域。
* ~  n1 [  v' h9 I
12.png
▲ 图1. STM32G474 架构

" O( L: ^0 ^5 H' ]- a4 d
1 O0 ~5 i" [" }* y
2.2. ARM DSP Lib 的使用 ( q/ x+ W' a3 Z% P& o2 Z
在 ARM DSP 库实现了很多 math 算法,可进行浮点乘加、点积、卷积、FFT、NN 等 多种算法 API,可以使用 ARM DSP 库高效使用 FPU。ARM DSP 代码位置如下:
, t4 t( b9 Q# d: R' p9 x. ]
14.png
* x/ Z4 ?& K6 U  l" E. K3 z# y1 x2 x
2.3. 示例代码 % j5 y; s7 [% |; w0 U( H
下面示例代码中对浮点乘法运算进行了测试。用户可以使用 STM32CubeMX 生成 STM32G474 KEIL 工程,在 main.c 文件中加入如下示例代码:
+ P1 W0 L9 D+ {( n* t
  1. * A9 r3 G2 O+ g
  2. __attribute__((section (".TEST_INPUT_A"))) float32_t testInputA[1024] =
    , C( a9 ?; C- D% `5 W% @
  3. {, f- S0 s, X& \3 ?# {* r' Y3 d
  4. 0.623234f, 0.799049f, 0.940890f, -0.992092f, 0.212035f, 0.237882f, -
    ; V5 s: n9 D0 j5 A
  5. 1.007763f, -0.742045f,
    7 U" \5 F% ]' S! k0 M3 r9 g
  6. ~~ 这里数组使用动态生成的float数据,数据量较大,略  [0 N4 D$ G7 Q
  7. -0.417470f, -0.205806f, -0.174323f, 0.217577f, 1.684295f, 0.119528f,+ |  k+ s1 x) J
  8. 0.650667f, 2.080061f9 F6 |5 F" Q, s
  9. };8 P$ R; f- O" K; T
  10. __attribute__((section (".TEST_INPUT_B"))) float32_t testInputB[1024] =
    * y. d% k. j% k9 @! F/ G' L( J# R
  11. {
    ' @6 x7 I6 N7 q
  12. -2.423957f, -0.223831f, 0.058070f, -0.424614f, -0.202918f, -1.513077f, -* r3 B) b6 v/ Y# z+ J" R3 \
  13. 1.126352f, -0.815002f,
    6 ?! o- x8 f2 k

  14. 4 H( o; x: ]1 ]. O2 a, S
  15. ~~ 这里数组使用动态生成的float数据,数据量较大,略
    % a) }! d7 C  W. [; S7 ^% P5 m
  16. -0.447001f, -0.725993f, 0.354045f, -0.506772f, -2.103747f, -0.664684f, 1.450110f, -0.329805f
    / R4 \& S$ Q, y8 G
  17. }; - m* V% Y% B, C2 s: X; Z

  18. 4 ^8 D/ K4 u/ e, @/ n- X3 ]) X1 N
  19. __attribute__((section (".TEST_RESULT_D"))) float32_t testResult[1024];
    1 |) [* y( v  @0 @% C5 @

  20. * [8 z$ O( B3 e3 `3 d% W1 x3 j* ]! r
  21. float32_t* pA;
    & x% P9 s- ~% g# X4 Y
  22. float32_t* pB;
    : P: X- Z; M- c, Z  `; m
  23. float32_t* pR;
    % p, \% _1 {) N3 Y4 O5 r9 A
  24. /* Private user code --------------------------------------------------*/ * D( Q) ]) _: h9 Q+ g
  25. /* USER CODE BEGIN 0 */ 2 ?' R: i( K* K7 P$ n: K$ V! G' `
  26. void test_normal_mul(uint32_t kLoops, float32_t* ! Z8 q- Z5 v( Q! j
  27. pSrcA, float32_t* pSrcB, float32_t*
    : Y0 G! r; I% c8 t
  28. pResult, uint32_t lenVector)
    4 g. i8 k5 O4 f* u# l
  29. {
    + }1 y# G2 T( a9 K
  30. for (uint32_t j = 0; j < kLoops; j++)
    0 z- |" u) h: M6 f8 J& O; p+ W
  31. { 5 f8 Z5 W; e4 @' X; s3 H$ a: r
  32. pA = pSrcA;
    - v/ e; x6 \- g% T9 u0 k0 m
  33. pB = pSrcB; : E. h: b4 T$ ~1 A  ^: h# H9 |
  34. pR = pResult; 4 n/ Q; a. h" _8 O

  35. ) P# m; Z- H' n- z2 X; i1 ?# L
  36. for (uint32_t i = 0; i < lenVector; i++) 8 N" |" E0 \( c' w
  37. { *pR++ = (*pA++) * (*pB++) ;
    / o# r, G5 x2 L) R% M' f4 X( Y5 U
  38.    }
    + m" |: M5 }) @
  39.   } ' W6 l! X0 E1 k, e  x
  40. } 6 G; X2 ^& \6 u' \2 [
  41. 8 E; r% X: c" t8 v
  42. #if defined (__FPU_USED) && (__FPU_USED == 1U) $ C2 a  m" ?1 _) ^- \+ U
  43. /* Use arm dsp lib to test basic operation Multiply, FPU enabled */
    # C- ], ]# B: h6 x( X5 x$ K
  44. void test_arm_math_mul(uint32_t kLoops, float32_t* pSrcA, float32_t* pSrcB, float32_t* 2 b, E0 y" J: ^7 {  U; A2 U
  45. pResult, uint32_t lenVector) 7 p: k4 {2 T& q+ b
  46. { ( A: b/ v) I+ E- D& u1 S
  47. for (uint32_t j = 0; j < kLoops; j++)
    ( }3 x: N* _6 ^& }$ r' \
  48. { ( b0 p, R; Y! a8 `! y8 h( W
  49. pA = pSrcA; //Code alignment with the function without FPU % ^! y  n. ~- h! I: S5 l8 R
  50. pB = pSrcB; : P. w6 {  ]/ c9 j+ x0 F3 m
  51. pR = pResult;
    4 ]( o) A% J( i/ K; V# ~: r
  52. arm_mult_f32(pA, pB, pR, lenVector);
    ) _  d( Q0 }1 X
  53. } & a4 t- U. c2 U
  54. }
    ( ^& v5 c0 ^9 t5 i4 r& D/ V
  55. #endif ' E9 J3 N1 |( T

  56. ) F# v$ J3 ?( r! o; ~
  57. /** & R0 O  K8 _' \( B0 y
  58. * @brief The application entry point.
    ) C' E  X% e2 ~% n; W  i- _
  59. * @retval int * r0 \1 e" K; ?, K3 [% F: A
  60. */
    $ l( Z" X) [7 T9 }$ m% @
  61. int main(void) ' |- @/ V7 X9 v( j" z
  62. { + d+ Y' `0 n$ Y  j( p
  63. /* MCU Configuration------------------------------------------------*/ ( Q1 N2 e& x/ I, T4 v6 W& S

  64. : a% q' B! Z+ f7 [' o% I
  65. /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
    & L. z' i) R0 W6 U  V7 _
  66. HAL_Init();
    ) X5 x! |/ b4 {
  67. - z9 ]6 C: T' L1 T
  68. /* Configure the system clock */ 7 E/ T" g4 F: q0 @  e  s- I
  69. SystemClock_Config();
    ; J+ Y; N- G1 [8 H1 Z0 r) d
  70. + ?) h$ V; r5 O- P- b# R
  71. …
    ) r$ k/ {% X4 ]2 N2 G& W2 @

  72. 2 E( Y+ T% V. r  u
  73. HAL_Delay(100); + E+ _% \8 n+ ^7 ^* ~" v1 L
  74. $ \) g* y" d$ ~9 n
  75. /* USER CODE BEGIN 2 */
    3 z& T- v6 P  t3 i3 f8 T7 W
  76. test_normal_mul(10, testInputA, testInputB, testResult, 1024);. b$ i/ T5 j+ o7 D8 l" a
  77. test_normal_mul(10, testInputA, testInputB, testResult, 1024); 7 G# }2 p! R- r7 H

  78. 9 m' W( h: s. d& j2 N4 x) b: ]
  79. #if defined (__FPU_USED) && (__FPU_USED == 1U)
    / l1 E$ k% T6 L3 M5 ?; _
  80. // Multiply calculation with arm dsp lib
    0 A, J( z+ R: J0 @9 d. H
  81. test_arm_math_mul(10, testInputA, testInputB, testResult, 1024);
    2 x' [+ F! L# ]) }/ ?  l
  82. test_arm_math_mul(10, testInputA, testInputB, testResult, 1024);
    % G0 ]- h0 [7 W$ P
  83. #endif
    1 s# Z8 x) o2 E5 Q. G5 {

  84. 5 v0 g9 q1 u6 `7 j
  85. /* USER CODE END 2 */ : K6 D$ ^( I3 Y- o' D) k9 ~! I

  86. 4 @) y6 R2 N* q' E7 {
  87. /* Infinite loop */
    * \: W8 m4 R) P4 k& Z
  88. /* USER CODE BEGIN WHILE */
    - ~+ m6 O, j/ u* V1 ^
  89. while (1) & c8 y: L, ~3 t
  90. {
    3 H0 b9 F7 |1 t1 ]9 F6 E
  91. /* USER CODE END WHILE */ 0 i7 j7 J9 b& H+ a3 h3 d" B
  92. + C; [. F: ]0 N8 Z( A- x/ n
  93. /* USER CODE BEGIN 3 */
    4 x) ~6 @( A( i+ Q6 l$ G
  94. }
    4 _7 ?& r9 {, O  h0 M5 N
  95. /* USER CODE END 3 */
    1 H8 M2 j# k0 t6 ^
  96. }
复制代码
2.4. 工程配置
& z* d7 p9 I/ B$ X+ M+ M$ S8 [通过 KEIL 工程 Options / Target, Floating Point Hardware, 确定 FPU On/Off。
) `' H4 p/ z4 s1 a; w/ Q! z) ]% f
15.png
/ b: l6 D# }% v3 w# {0 ]  |1 R

0 U( Y2 n* j$ M2 N" {! R+ I3 Q
▲ 图3. KEIL 项目工程 FPU 单精度浮点设置
通过 STM32G474_FPU_TEST.sct 文件配置 Data 存放区域,如下例,将测试数据置 于 SRAM2。+ H/ K0 P+ N. i# \9 o4 e

  1. ; Q# f  D' X4 l" ], T
  2. RW_IRAM1 0x20000000 0x00014000 { ; RW data
    1 [2 h( @, N: |9 j+ e9 S6 j
  3. .ANY (+RW +ZI)7 v8 J  I5 |3 y5 `. ?
  4. }; K2 h) v! D+ d6 U. V
  5. RW_IRAM2 0x20014000 0x00004000 {
    - i/ `) @: l6 A  w: o6 k
  6. *(.TEST_INPUT_A)8 r0 P$ X$ K) D7 s7 u
  7. *(.TEST_INPUT_B)( t1 ~; f( X3 j2 S
  8. *(.TEST_RESULT_D)
    ) l2 E% f# c1 p3 L. c
  9. }
    * ?* B& ~' Y. H4 y# x% v! F
  10. RW_CCM 0x20018000 0x00008000 {$ B1 Q9 {6 X" E" a9 l
  11. }
复制代码
完成后,进行编译链接,即可进行 STM32G474 FPU 性能的测试。
  E* Z+ F( f9 A- }
) T' S* U6 g3 X! g5 r2.5. 编译选项
( Q$ l# h6 P; `8 e$ B8 ^本文中我们使用的是 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 浮点运 算扩展)等选项,不过一般使用默认即可。, c+ _9 n* q1 K
16.png
▲ 图4. KEIL 工程,编译选项设置
03使用 KEIL Trace 工具进行测量
& d, M1 s4 G% h: {3.1. KEIL 工程设置
: Z$ U  l; X. v
3 j, V6 D. \6 E5 d4 W1 NKEIL 工程下,首先选择工程选项设置,在 Debug 选项页中,右上部使用 Debugger 工具栏中选 Settings,如下图 5 和图 6 设置。注意 KEIL Trace 设置的时钟必须要与实际 STM32 使用的系统时钟相一致,如图 6 中,STM32G474 使用了 170MHz 的系统时钟, KEIL Trace 中也要相应设置为 170MHz。
3 J* w4 c& G5 M, u
17.png
▲ 图5. KEIL 工程,Debugger 设置入口
18.png
▲ 图6. KEIL 工程,Cortex-M Trace 功能设置
运行KEIL debugger,如下图7所示,将断点设置在要测量的语句前及其后,执行 代码,当Debugger停在断点时,其状态栏中t1指示的即为当前代码的已执行时间。测试代码起止时间差即为代码执行用时。该Trace功能计时是比较准确的。当然如果您希望掌控更多,也可以通过代码来实现,如增加诸如如下代码:/ n2 [0 a( l7 G
nStart = DWT->CYCCNT; ' d/ s  l' Y# f* a
~~~需测试执行时间的代码~~~ $ u" @9 M* [2 O
nStop = DWT->CYCCNT;
$ u. M( i. f( n然后用(nStop – nStart)/系统时钟,换算成时间即可。(我们这里没有考虑中断,一 般测量前需要禁用中断)
1 @* A. }' t9 l5 o4 L0 e
19.png
▲ 图7. KEIL 工程,Debug 模式下 Trace 程序执行时间
3.2. 测试结果
# u/ T3 f. C  O" m1 j: y% ?) k下表列出了STM32G474 10K次 浮点“乘”用时统计。4 m& J  a9 o/ p0 E# S: \# U2 E
20.png
▲ 表1. STM32G474 10K 次 浮点“乘”用时统计表
10 X 1024次浮点乘4 m6 e" a' z' f$ E7 m( P

( F( q# a1 h5 W+ O增加--loop_optimization_level=2 编译选项! M7 F: \# s3 I& @" j  U5 C6 _+ L

- @9 N6 i: Y0 H; E0 t2 h: E) O, w( J% e$ BFPU 核心汇编代码的比较,见图8和图9。5 p* e& m2 d) s* j. t8 z" e0 e3 i6 C
21.png
▲ 图8. 使用--loop_optimization_level=2 编译选项的常规代码汇编
22.png
▲ 图9. ARM DSP 库 arm_mult_f32 函数汇编
; |5 p* X" B9 x6 O: y8 c5 T

/ |8 d9 M- |, o0 ^使用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是很高效的。
; X4 \2 A2 l( W( S4 ]& n. ~: e: v3 b/ C7 h8 A
04小结
( Q% Y* Z* j) ]1 _5 M# s- Y本文介绍了使用 STM32G474 FPU 进行浮点运算,从系统的角度、ARM DSP Lib、 编译选项的影响等几个方面探讨如何提升整体性能,并介绍了如何利用 KEIL Trace 工具进 行测量。以供在系统性能方面有需求的客户参考借鉴。0 N6 i& F0 m% _, c$ O0 C) t0 x
. c& k- ?& b) L/ t: d' _

9 K6 M/ J- S' c6 T8 J- j: S如果你有其他想要的实战笔记,可评论区留言,管管来跟工程师沟通!
% {% L- B: s9 s$ E! \( N0 l# L5 t- Y7 P

; n0 ^5 }. E% T* G- b) ]& r
0 n: q6 {& W, d% q& f5 d* L0 Z7 z( j% Q$ m' Q1 J1 t% V: S* Y- L8 |. C

8 R. a3 k5 S8 W" I: s5 V, B9 ~, s1 h5 F1 J* Q6 `$ @# N/ N  q

2 k# K" N5 ~+ A$ c# y. _1 o! h9 Q) [2 C. N! O1 z
2 }, @5 c  R+ ]" Y& h# b; Z
* Q0 X/ g" Q  i. O% q8 o
( J7 R! i0 p9 U" |+ p0 q" b" ~
1 ~3 J" z0 g+ L' Y

! M& N+ z  D! d. X; L/ ]
赞 收藏 评论0 发布时间:2024-10-30 16:57

举报

0个回答

所属标签

相似技术帖

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版