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

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

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

. S5 z0 T: X: A01引言9 {% s/ T/ ?1 }
客户在使用 STM32G474 时,希望使用 FPU 进行浮点运算,并最大化其性能。本文 从 STM32G474 系统的角度、ARM DSP Lib、编译选项的影响等几个方面探讨如何提升整体性能,并介绍如何使用 KEIL 工具进行测量。; u( N6 x. u8 g  y( ]
1 |, A, G) e+ g
02STM32G474 FPU 运算性能优化 ! b4 m! T8 f6 S7 K  i; d
2.1. STM32G474 系统性能优化 6 n( b( x) u: O( ~

# o. h" P$ L6 }0 S- q& F. Z! F+ @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 并发访问产生的性能下降。应用则需要根据实 际情况,合理使用内存区域。/ u9 Q6 n, g0 M* X& q' R
12.png
▲ 图1. STM32G474 架构
9 ]: _3 Q; z: c
3 P  C- A9 ~/ }/ Q
2.2. ARM DSP Lib 的使用
# o# W0 y( _# I( L在 ARM DSP 库实现了很多 math 算法,可进行浮点乘加、点积、卷积、FFT、NN 等 多种算法 API,可以使用 ARM DSP 库高效使用 FPU。ARM DSP 代码位置如下:  s6 |5 x; @: h* z  t. _. Q
14.png
+ k) G4 |: d' W
2.3. 示例代码 ( }0 t/ l4 D5 ^9 U( c4 Y) ?
下面示例代码中对浮点乘法运算进行了测试。用户可以使用 STM32CubeMX 生成 STM32G474 KEIL 工程,在 main.c 文件中加入如下示例代码:
! c. {, G* L" i( |3 }% o. n( r

  1. / ^% e" Q, q" u/ F7 s. m
  2. __attribute__((section (".TEST_INPUT_A"))) float32_t testInputA[1024] = 3 l0 N0 a# Y, C2 |- X1 I( x
  3. {) F# u/ ~, B1 S  b4 Z7 X  X" E
  4. 0.623234f, 0.799049f, 0.940890f, -0.992092f, 0.212035f, 0.237882f, -8 ]1 N: L* ^4 w* d+ D) j+ d3 N
  5. 1.007763f, -0.742045f,
    8 c, [7 N! J/ b
  6. ~~ 这里数组使用动态生成的float数据,数据量较大,略
    2 }, R4 c% Z3 U4 v" a8 S3 v
  7. -0.417470f, -0.205806f, -0.174323f, 0.217577f, 1.684295f, 0.119528f,
    ; N, t* M" q2 x* G' p! |
  8. 0.650667f, 2.080061f
    , L& }( T, R6 h( l; a
  9. };! s6 A/ g4 |: z2 ~( |/ U3 t+ h
  10. __attribute__((section (".TEST_INPUT_B"))) float32_t testInputB[1024] =
    / ]9 T! ?+ g1 M& \2 }% a( u6 ?3 n
  11. {4 ?8 t' K3 w9 _- ?# u. ~
  12. -2.423957f, -0.223831f, 0.058070f, -0.424614f, -0.202918f, -1.513077f, -/ p9 [3 V6 _" ^( [
  13. 1.126352f, -0.815002f,
    : w5 k2 d- l# D' b- X: l6 G/ b+ P9 D
  14. / P1 g, O7 J  V/ ?/ v( G/ ~9 S( t
  15. ~~ 这里数组使用动态生成的float数据,数据量较大,略
    8 j3 W& M% o9 E$ V% Q( m
  16. -0.447001f, -0.725993f, 0.354045f, -0.506772f, -2.103747f, -0.664684f, 1.450110f, -0.329805f
    / l0 h& C9 K( t0 t5 e
  17. };
    : s/ ?5 {. A+ f/ H3 r8 ~* X

  18. ! O# s* B7 r" m3 B
  19. __attribute__((section (".TEST_RESULT_D"))) float32_t testResult[1024]; - H, M/ D/ }7 u2 ], T) A

  20. / A) F1 C. K0 W
  21. float32_t* pA;
    7 u5 w( M( l3 A" P
  22. float32_t* pB;
    + [6 g! B0 s# z0 x7 c$ K6 j" [/ K) m' A
  23. float32_t* pR;
      c- d' H& r& B/ |( O
  24. /* Private user code --------------------------------------------------*/ 6 p( _" G- B; H
  25. /* USER CODE BEGIN 0 */ % q. T; S  o& e) H
  26. void test_normal_mul(uint32_t kLoops, float32_t* ( m1 F2 N% v: |$ u" v# E2 L! `
  27. pSrcA, float32_t* pSrcB, float32_t* 1 E! H  g2 o4 U/ v
  28. pResult, uint32_t lenVector)
    , f0 {# G- l' w) U" E, F: k2 J6 n  k
  29. { , N& U- C7 v  }% H) ^
  30. for (uint32_t j = 0; j < kLoops; j++)
    & z+ N  Y5 q* A+ J& x# N' x
  31. { : {( ]" ]# }8 s1 ^
  32. pA = pSrcA;
    0 g( y) [: ?( ]( ^
  33. pB = pSrcB; * B! w! }! r( p+ e) t
  34. pR = pResult; 9 f" ~! `) l# F, [1 o0 X/ h- F) G4 u

  35. : i# s# p; |( P1 I8 _/ R
  36. for (uint32_t i = 0; i < lenVector; i++)   Z! t* K; v( @9 v
  37. { *pR++ = (*pA++) * (*pB++) ;
    ! |/ c+ z0 b% s3 l/ M
  38.    }
    , A0 t9 T4 X" t" w- ~# l  J
  39.   }
    6 R1 a, c3 B- _( L
  40. }
    ! p$ l0 E5 b# \. a1 I/ d
  41. 4 H; f/ G: @+ l
  42. #if defined (__FPU_USED) && (__FPU_USED == 1U)
    9 A% c; o0 X8 ?/ {$ I, K9 A* T
  43. /* Use arm dsp lib to test basic operation Multiply, FPU enabled */
    ' V, b4 B. Q, V2 h' D/ M* E8 E: G2 c2 F
  44. void test_arm_math_mul(uint32_t kLoops, float32_t* pSrcA, float32_t* pSrcB, float32_t* & v4 i9 L6 w' n
  45. pResult, uint32_t lenVector) 9 ~' J$ A# d3 b9 J
  46. { ! v8 d# ]7 P; i6 D/ E
  47. for (uint32_t j = 0; j < kLoops; j++)
    : Y- E/ g9 |. j' h
  48. {
    0 w6 U: V! `- a1 g8 c) X8 W
  49. pA = pSrcA; //Code alignment with the function without FPU ; c0 o8 j7 D2 N9 w- @! d" x) R
  50. pB = pSrcB;
    8 W$ Z, ?7 z$ E1 I- p$ e
  51. pR = pResult;9 q" b9 @% K8 {2 Y; q0 g/ G  h
  52. arm_mult_f32(pA, pB, pR, lenVector); 0 l# V& z3 G7 v6 V
  53. } ' H6 W$ }+ D* G$ k. U& n2 K
  54. }
    # |# a( t2 L! z( T% o/ O$ u
  55. #endif 4 Y6 k1 f7 o: r

  56. * [* x  N+ U% p' b" E' L
  57. /**
    ) u  ^* C8 ?! o& {
  58. * @brief The application entry point.
    ' t  y5 d( P% g+ t9 x
  59. * @retval int
    5 y( ]3 h, X: ^  r# H
  60. */
    ' z/ r& s% c& ]  M8 C3 l9 x
  61. int main(void) $ A; |5 {/ M! j* l+ ?
  62. { 7 x" v: z% j5 e, u3 g: |: Y3 x
  63. /* MCU Configuration------------------------------------------------*/
    2 V% |" k6 S# g+ w7 r9 ?. g, @: H

  64. , b1 q  @- V! J# ]& H4 K
  65. /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
    8 C% a; A" S; i6 s7 S; B$ q
  66. HAL_Init(); 1 ~& b' w3 M3 Z0 M  m; I

  67. 8 M- K& g+ }1 J0 J. c1 y& V: {9 R
  68. /* Configure the system clock */ * G1 N( G2 V$ Y% V6 ^7 ~  ~. R- C
  69. SystemClock_Config();
    7 F9 E, [& N/ c7 [2 b3 a7 j

  70. 9 N6 N+ U# a: I0 ?1 a
  71. …
    ( v) N* ^( [" w4 L" n

  72. 3 q- f$ {/ H) \- V4 I0 A
  73. HAL_Delay(100); 9 x) [8 _0 ]$ V/ _2 X
  74. 2 h+ N+ C  E3 w9 i2 |3 Q
  75. /* USER CODE BEGIN 2 */
      H% ~$ y- r) h7 f( H  x- H
  76. test_normal_mul(10, testInputA, testInputB, testResult, 1024);
    7 }  q0 q/ S  ]$ a% e4 t/ U
  77. test_normal_mul(10, testInputA, testInputB, testResult, 1024);
      Y0 `  c" D' E+ V
  78. % b* K7 D) {' {
  79. #if defined (__FPU_USED) && (__FPU_USED == 1U) " }* L' X2 d3 H9 o, j( H8 a
  80. // Multiply calculation with arm dsp lib
    $ j% g5 i$ N7 V% _1 B/ ^' n8 A
  81. test_arm_math_mul(10, testInputA, testInputB, testResult, 1024);7 z9 @5 @! u2 R& v" K& [
  82. test_arm_math_mul(10, testInputA, testInputB, testResult, 1024); 9 z3 }0 t0 }& z. K8 {) ]5 K6 Z
  83. #endif
    ; I" @$ S0 \6 o6 I' k' T! y8 ~

  84. 9 x* D, V- `% n2 t
  85. /* USER CODE END 2 */
    + P+ K& }( J5 v$ E
  86. ( r- J: ~: R% c7 L3 u7 M
  87. /* Infinite loop */ 0 e6 t6 D% B' \+ v" [# ^4 c
  88. /* USER CODE BEGIN WHILE */
    1 N4 A' q/ K+ E4 D
  89. while (1)
    " I9 j* g  D- m
  90. {. f' C- @7 Z: `0 N3 R9 R6 D
  91. /* USER CODE END WHILE */
    5 t6 }$ Z+ b1 U5 q( ?: s3 w' T- ^# U% W

  92. 3 I! R1 G) m' W0 ]2 e7 M
  93. /* USER CODE BEGIN 3 */
    / Q' V0 L! o6 |! r- P, j
  94. } 7 T) f  J6 y* Z: A, r5 R' L
  95. /* USER CODE END 3 */
    # R) {5 P$ v* C8 e
  96. }
复制代码
2.4. 工程配置 : ]; b% u/ ~5 P" y% G
通过 KEIL 工程 Options / Target, Floating Point Hardware, 确定 FPU On/Off。
  Z5 t* @( \  a7 g
15.png
5 }  a# T  \6 p2 G4 r

4 B, ]. ?+ w- @8 B
▲ 图3. KEIL 项目工程 FPU 单精度浮点设置
通过 STM32G474_FPU_TEST.sct 文件配置 Data 存放区域,如下例,将测试数据置 于 SRAM2。
! Y; V, [9 Z7 {
  1. 0 [/ C$ A: k2 ?( R7 {* w
  2. RW_IRAM1 0x20000000 0x00014000 { ; RW data
    7 C& W1 {0 z/ c
  3. .ANY (+RW +ZI)
    $ B8 K" ?" ~% H( N1 G# N  n
  4. }
    9 g5 x, a: Q, }0 {- r( @% q! f
  5. RW_IRAM2 0x20014000 0x00004000 {* h( O1 I, ~* E4 ~+ j
  6. *(.TEST_INPUT_A)7 c, Y8 v3 \) [: H" S  k
  7. *(.TEST_INPUT_B)" {9 E/ j6 E4 x4 E8 Y  p6 H' e9 Z
  8. *(.TEST_RESULT_D), l# A' r7 a* b+ ^
  9. }- x- @, `) z/ }" h2 X, c0 ]8 d
  10. RW_CCM 0x20018000 0x00008000 {
    1 d8 w9 K+ R, P8 |" G3 O3 ^  e
  11. }
复制代码
完成后,进行编译链接,即可进行 STM32G474 FPU 性能的测试。3 Y) j! D) [4 ^0 v
1 T1 k# x3 {9 u, q2 S
2.5. 编译选项 5 G( m7 p7 `5 \+ o% |" X
本文中我们使用的是 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  w7 p" o1 z' Q2 S
16.png
▲ 图4. KEIL 工程,编译选项设置
03使用 KEIL Trace 工具进行测量
8 O  Z8 r# c& v) G3.1. KEIL 工程设置
, R6 ?5 X0 w) w/ s: N4 S
+ D* }/ W- X- [" `: I, H) p0 L8 bKEIL 工程下,首先选择工程选项设置,在 Debug 选项页中,右上部使用 Debugger 工具栏中选 Settings,如下图 5 和图 6 设置。注意 KEIL Trace 设置的时钟必须要与实际 STM32 使用的系统时钟相一致,如图 6 中,STM32G474 使用了 170MHz 的系统时钟, KEIL Trace 中也要相应设置为 170MHz。
: u& e' I* V2 W# r. N
17.png
▲ 图5. KEIL 工程,Debugger 设置入口
18.png
▲ 图6. KEIL 工程,Cortex-M Trace 功能设置
运行KEIL debugger,如下图7所示,将断点设置在要测量的语句前及其后,执行 代码,当Debugger停在断点时,其状态栏中t1指示的即为当前代码的已执行时间。测试代码起止时间差即为代码执行用时。该Trace功能计时是比较准确的。当然如果您希望掌控更多,也可以通过代码来实现,如增加诸如如下代码:$ [- z+ C# W0 P- F
nStart = DWT->CYCCNT; 0 v2 o7 q! w6 B, O* ?. U
~~~需测试执行时间的代码~~~ ( n+ G6 v+ Q2 Q  x
nStop = DWT->CYCCNT;
+ C; z6 V  N% X$ \/ q然后用(nStop – nStart)/系统时钟,换算成时间即可。(我们这里没有考虑中断,一 般测量前需要禁用中断)
/ F( r' g4 n, g* X5 t# z
19.png
▲ 图7. KEIL 工程,Debug 模式下 Trace 程序执行时间
3.2. 测试结果: K* `7 R. c) a$ X8 {: L3 b
下表列出了STM32G474 10K次 浮点“乘”用时统计。
1 G5 \& ^" l/ @1 l* l8 ~
20.png
▲ 表1. STM32G474 10K 次 浮点“乘”用时统计表
10 X 1024次浮点乘
( X; e- h3 I5 p% f' u* c# g

4 }. l  b2 @3 f$ V0 Z增加--loop_optimization_level=2 编译选项8 u. C: Z: ^: T! r9 C$ |  e' z0 j

' [% U# ~) }+ e4 d+ O. D& DFPU 核心汇编代码的比较,见图8和图9。
8 P) }7 W  W1 W  L* C+ j
21.png
▲ 图8. 使用--loop_optimization_level=2 编译选项的常规代码汇编
22.png
▲ 图9. ARM DSP 库 arm_mult_f32 函数汇编

; e. e$ ]" U" A! V1 u9 Q7 m
: m' r: r4 R1 n
使用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是很高效的。. I1 ]4 {5 g; X4 I$ g  x

: ^7 G7 U0 Q0 f0 x+ j, V04小结 0 w8 X  i/ F  q% f- B) c* b
本文介绍了使用 STM32G474 FPU 进行浮点运算,从系统的角度、ARM DSP Lib、 编译选项的影响等几个方面探讨如何提升整体性能,并介绍了如何利用 KEIL Trace 工具进 行测量。以供在系统性能方面有需求的客户参考借鉴。* _8 S6 f+ n6 L& b9 N2 p9 N
) N/ S% E. w  {, v0 K8 n" ?

* g3 A3 q4 J% r* }# A! Y- b! g8 c6 r如果你有其他想要的实战笔记,可评论区留言,管管来跟工程师沟通!1 D# y6 _" P5 |
# }) U  p6 u; |# C! X: e* ^
' B8 G# e) B! ^/ U/ O! q  D6 m; }

5 G6 Z# G, w% w0 f
6 Q. J$ }5 R" X- g$ I8 p( \8 q! \! M5 Z$ t+ m' v5 n0 r
$ ?/ Y/ y' n( i# E: {/ R7 I

7 w% G+ m, j# h; a3 b6 u
$ I( C3 y& J9 {* X8 P- Q( O$ r0 m9 [! `. {
' n- j1 r; \9 O; O/ ~/ O% D2 W

" u2 Z0 r1 s* Z* V8 z
$ H* m* W0 L6 `
( E, }8 c0 H0 F5 p6 u
赞 收藏 评论0 发布时间:2024-10-30 16:57

举报

0个回答

所属标签

相似技术帖

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