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

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

[复制链接]
STMCU-管管 发布时间:2024-10-30 16:57
# C6 p1 Z) ]( u# P% m# t2 Z+ J
01引言' v2 y3 v: u. _6 Z: }
客户在使用 STM32G474 时,希望使用 FPU 进行浮点运算,并最大化其性能。本文 从 STM32G474 系统的角度、ARM DSP Lib、编译选项的影响等几个方面探讨如何提升整体性能,并介绍如何使用 KEIL 工具进行测量。) d2 A# B2 }8 @1 `6 Q0 C) Z

! J3 U' t) w. `% g# ?- d2 B02STM32G474 FPU 运算性能优化
0 E, d9 _8 u' f* ~9 G7 F2.1. STM32G474 系统性能优化 - f1 N; r) B% E1 s

. v+ n. n, x, M5 J# A: \* ]2 FSTM32G474 使用的是 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 并发访问产生的性能下降。应用则需要根据实 际情况,合理使用内存区域。
( a2 L2 d! r& N6 u7 ]- l  d
12.png
▲ 图1. STM32G474 架构

; _3 b! S( Y: W8 j) V' Q

: Z5 e6 ?, t3 S" x9 s: c& m2.2. ARM DSP Lib 的使用
3 {* L" M+ }7 k  E$ ~  b& h- B在 ARM DSP 库实现了很多 math 算法,可进行浮点乘加、点积、卷积、FFT、NN 等 多种算法 API,可以使用 ARM DSP 库高效使用 FPU。ARM DSP 代码位置如下:1 j# ~& N* P0 t$ A
14.png

0 E3 F1 f0 W, Y+ j/ U2 b$ I2.3. 示例代码
* E8 B. j8 W2 S$ N+ N; X' k0 ?) V下面示例代码中对浮点乘法运算进行了测试。用户可以使用 STM32CubeMX 生成 STM32G474 KEIL 工程,在 main.c 文件中加入如下示例代码:
# ^: `9 P4 m+ n  o
  1. 4 J7 m, x" ?' _, U/ ]' Y! K9 r
  2. __attribute__((section (".TEST_INPUT_A"))) float32_t testInputA[1024] =
    & b( B+ T8 j8 t' ]; E
  3. {
    ' n& H( I1 h& s1 G
  4. 0.623234f, 0.799049f, 0.940890f, -0.992092f, 0.212035f, 0.237882f, -, I" r: H- G* a: B
  5. 1.007763f, -0.742045f,4 E& F3 R0 Q2 Z  h! y1 P
  6. ~~ 这里数组使用动态生成的float数据,数据量较大,略
    # _# x. ?, y3 z. Q4 K  o( q
  7. -0.417470f, -0.205806f, -0.174323f, 0.217577f, 1.684295f, 0.119528f,
    1 b+ ]3 x9 |5 U
  8. 0.650667f, 2.080061f0 ]9 t: W, j7 j5 Z( U( z; N4 W
  9. };& ?) o! \" G% Y# x5 c1 ?( ]7 C
  10. __attribute__((section (".TEST_INPUT_B"))) float32_t testInputB[1024] = , S/ W% p5 d8 `  F
  11. {# h4 ]7 d8 e. G5 w, J& B
  12. -2.423957f, -0.223831f, 0.058070f, -0.424614f, -0.202918f, -1.513077f, -
    8 @7 f2 ~6 f# c9 n: [8 ^1 i
  13. 1.126352f, -0.815002f,
    5 f8 {  v, M  F+ P! K8 L: C
  14. % m$ }% z# [1 ~  D  U
  15. ~~ 这里数组使用动态生成的float数据,数据量较大,略
    9 f  v8 i$ g% _8 ]
  16. -0.447001f, -0.725993f, 0.354045f, -0.506772f, -2.103747f, -0.664684f, 1.450110f, -0.329805f ) K7 W3 l% t3 I! ~2 {
  17. };
    - n3 ~; w+ @1 S, @: {2 k
  18. ; {) G( v5 D! q. w" x" C: [+ X
  19. __attribute__((section (".TEST_RESULT_D"))) float32_t testResult[1024];
    # f1 M& D/ H) I4 P+ C

  20. 0 T7 {0 ]2 n' z- W9 b$ L' R& B7 Z
  21. float32_t* pA;3 J# I9 Q, U; Q; v. @; {+ A% n
  22. float32_t* pB; , b0 |8 \/ D4 J' a/ X
  23. float32_t* pR; 9 J+ \9 S8 {. Q8 S, r8 Y7 I8 y
  24. /* Private user code --------------------------------------------------*/ % s0 d0 c) X3 u. L6 q, h6 l
  25. /* USER CODE BEGIN 0 */ 8 Y8 r4 G" j$ L4 ?
  26. void test_normal_mul(uint32_t kLoops, float32_t* / c" M2 y6 d- g* j- o
  27. pSrcA, float32_t* pSrcB, float32_t* 8 A% b+ L% L. j/ S% ?' ]
  28. pResult, uint32_t lenVector)
    8 k9 Q" l& u* Y  a
  29. { 3 K- b. n( N: y
  30. for (uint32_t j = 0; j < kLoops; j++)
    $ {( C1 G& A7 Z. D: H- U
  31. { " h! T# e- o6 M7 O3 o; ~, v- `
  32. pA = pSrcA;
    % T6 q) z$ g) Q. ]. k0 v% r# ?
  33. pB = pSrcB;
    % F1 o$ q: v3 Q8 c  m3 h$ k
  34. pR = pResult;
    ' }4 s$ X. n+ Y1 M) a& s
  35. ( }% F( d/ p" }; p1 A! ?" b8 b
  36. for (uint32_t i = 0; i < lenVector; i++) " C! s4 A+ l: _% {) Y* `
  37. { *pR++ = (*pA++) * (*pB++) ;
    - V& M" {/ I7 A: Z9 ^
  38.    }
    1 c- l7 w) x; @, c5 T
  39.   } - p; C& a/ e8 q5 D0 K
  40. }
    ' j9 L' i2 N5 K5 H& x
  41. 6 a: J& V$ b5 C8 D6 _
  42. #if defined (__FPU_USED) && (__FPU_USED == 1U) ; }: u2 @' |( P7 t: U
  43. /* Use arm dsp lib to test basic operation Multiply, FPU enabled */
    * _1 y7 J) ^% X: |, n7 I
  44. void test_arm_math_mul(uint32_t kLoops, float32_t* pSrcA, float32_t* pSrcB, float32_t*
    3 [( s/ a/ C" e: x- I
  45. pResult, uint32_t lenVector)
    ! h( q" h# p  `( {1 E
  46. {
    , J5 n6 k  X; m
  47. for (uint32_t j = 0; j < kLoops; j++)
    % B' u& V5 q/ n6 U
  48. { : s5 f- _- w/ L; c( ]
  49. pA = pSrcA; //Code alignment with the function without FPU
    4 q3 n3 N8 g0 M9 C
  50. pB = pSrcB; ( S8 k9 p: y5 ?! e* u& g
  51. pR = pResult;& g- ?- i' O& s1 P
  52. arm_mult_f32(pA, pB, pR, lenVector); 8 J( \% x5 h) {+ S
  53. }
    % h: V$ i+ K$ J7 c2 j! q' y, m/ }
  54. }
    ) e/ j. `, p5 H/ u- f# R' ~
  55. #endif / @4 Q+ g% X2 H6 q

  56. 9 g0 R9 |' @- C+ \8 i
  57. /**
    * M' k6 `5 U0 F
  58. * @brief The application entry point.
    : v' t* @* q) \; n
  59. * @retval int
    1 B8 Y! b, \$ Y+ ~' q6 M
  60. */ 5 g- h) |7 H/ ~4 H
  61. int main(void) . r! X. C/ G& x% M) d
  62. { % _; K2 ?6 \- _. C9 T( K
  63. /* MCU Configuration------------------------------------------------*/
    4 q+ i+ M7 E1 U& [2 @4 U- C. r6 v

  64. 0 H2 g1 U  Q& C1 I! M( @
  65. /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
    3 N8 t$ @! Y% i6 z$ w2 V
  66. HAL_Init();
    : _% n. M' D; I7 |1 p  Y7 Y
  67. ! |6 \) @2 N" k; c/ o
  68. /* Configure the system clock */ ; @& U& w, F& ?2 _4 p+ M% S
  69. SystemClock_Config();
    0 n* m2 K8 s& g

  70. 4 {( e; D; e' G. L: K5 d. v

  71. 0 ^# X# L2 P' E" J- L) c2 b4 M9 W
  72. ! I! B( ?: _  x2 m& ]) S# p  o/ T
  73. HAL_Delay(100); 2 U& G8 s* `. S0 e# W! s6 l! c7 A
  74. , v, a/ x& b# C$ {0 ^
  75. /* USER CODE BEGIN 2 */
    1 b/ Z. W6 p5 ~0 w) x, e
  76. test_normal_mul(10, testInputA, testInputB, testResult, 1024);
    3 \5 r' R8 t1 G4 `
  77. test_normal_mul(10, testInputA, testInputB, testResult, 1024); ! ?8 [8 e6 W2 ]8 ~9 d' X% W# o

  78. ! m8 G6 q, X* Z* h% ~
  79. #if defined (__FPU_USED) && (__FPU_USED == 1U) , m3 }( v; d+ m* x3 v6 M7 ?
  80. // Multiply calculation with arm dsp lib
    / c! z. r& @  h" f, s
  81. test_arm_math_mul(10, testInputA, testInputB, testResult, 1024);% t1 W% Z# c' W) n. s# P
  82. test_arm_math_mul(10, testInputA, testInputB, testResult, 1024); / K' `+ s9 u# E
  83. #endif 5 C* z" }/ {* t8 U) ~0 a% W

  84. 1 X' \* M0 E; l) L: y6 Q. Q
  85. /* USER CODE END 2 */ & ]9 ]; U7 y0 Q/ j7 a# L1 w
  86. ( v' {' _! x1 o5 j9 T
  87. /* Infinite loop */ / T7 R& \0 i. q+ C# @" Z
  88. /* USER CODE BEGIN WHILE */   r7 x- l5 r6 _! z, B" u
  89. while (1) ' _/ j; o, z/ D8 R
  90. {
    % e0 k/ |6 B% R3 H' B& d
  91. /* USER CODE END WHILE */ # k% T/ P- I- i( q1 ~
  92. * w, z& @+ h$ s
  93. /* USER CODE BEGIN 3 */
    8 u( {/ M) y* H
  94. }
    : ^. G4 R% b% v1 H6 p" q
  95. /* USER CODE END 3 */
    " {  M5 K; m1 o1 c/ q
  96. }
复制代码
2.4. 工程配置
  Z$ M  q4 ^7 j2 G% f% C. k, E通过 KEIL 工程 Options / Target, Floating Point Hardware, 确定 FPU On/Off。
! E7 b3 {0 o) U9 _# ^2 J
15.png
( K) D# Q  v; t; e
8 N; m! M- g/ t; H! p. j
▲ 图3. KEIL 项目工程 FPU 单精度浮点设置
通过 STM32G474_FPU_TEST.sct 文件配置 Data 存放区域,如下例,将测试数据置 于 SRAM2。. z1 v$ [) H! n# z' p
  1. , L( H' O# y" y! ~7 R( }
  2. RW_IRAM1 0x20000000 0x00014000 { ; RW data9 X' i2 W; A1 [, u  B
  3. .ANY (+RW +ZI)& t# C. c& @0 d6 U
  4. }
    6 K0 j: B& |; C
  5. RW_IRAM2 0x20014000 0x00004000 {
    + _3 T7 q& M+ w% D2 _+ `, ~! o7 z
  6. *(.TEST_INPUT_A)
    & T- z$ p6 o5 P: U: X8 c% |5 o5 w2 r
  7. *(.TEST_INPUT_B)$ e- A# b5 p( I1 A1 {/ w
  8. *(.TEST_RESULT_D)
    + M6 W5 J( ]& m; j' v% T
  9. }3 z2 W3 w$ D3 j: I! a  v2 Q5 A3 g4 q
  10. RW_CCM 0x20018000 0x00008000 {
    0 s8 G2 ~9 d3 n- I/ d
  11. }
复制代码
完成后,进行编译链接,即可进行 STM32G474 FPU 性能的测试。  Z) g3 f* {+ J& E# b( L

$ d4 n1 H! _4 q$ U" o3 }& Q. }2.5. 编译选项 1 Q  ]; h/ l" H
本文中我们使用的是 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 浮点运 算扩展)等选项,不过一般使用默认即可。, M# R3 `. u5 K
16.png
▲ 图4. KEIL 工程,编译选项设置
03使用 KEIL Trace 工具进行测量
; D$ q. p* p, S/ {  t3.1. KEIL 工程设置 , z4 \$ V6 f" ]& P

. X& \) d- A9 z% R( B, [KEIL 工程下,首先选择工程选项设置,在 Debug 选项页中,右上部使用 Debugger 工具栏中选 Settings,如下图 5 和图 6 设置。注意 KEIL Trace 设置的时钟必须要与实际 STM32 使用的系统时钟相一致,如图 6 中,STM32G474 使用了 170MHz 的系统时钟, KEIL Trace 中也要相应设置为 170MHz。1 N. B3 ]  J& A' p" }
17.png
▲ 图5. KEIL 工程,Debugger 设置入口
18.png
▲ 图6. KEIL 工程,Cortex-M Trace 功能设置
运行KEIL debugger,如下图7所示,将断点设置在要测量的语句前及其后,执行 代码,当Debugger停在断点时,其状态栏中t1指示的即为当前代码的已执行时间。测试代码起止时间差即为代码执行用时。该Trace功能计时是比较准确的。当然如果您希望掌控更多,也可以通过代码来实现,如增加诸如如下代码:
6 H& r) e- e$ U, GnStart = DWT->CYCCNT;
5 _* p! _( s/ }! P~~~需测试执行时间的代码~~~
$ A4 C) M! S" s% ^% fnStop = DWT->CYCCNT;
/ Y0 }" {1 |/ @# j% q/ g然后用(nStop – nStart)/系统时钟,换算成时间即可。(我们这里没有考虑中断,一 般测量前需要禁用中断)+ Y; V8 I9 Z+ ?  g# t
19.png
▲ 图7. KEIL 工程,Debug 模式下 Trace 程序执行时间
3.2. 测试结果
! Z4 L3 P/ ~$ L* B1 I. W9 |# g) k. Q下表列出了STM32G474 10K次 浮点“乘”用时统计。4 \* ~# C% W! T% a6 F
20.png
▲ 表1. STM32G474 10K 次 浮点“乘”用时统计表
10 X 1024次浮点乘
# r4 N  J# M& O3 q$ V

2 J, p$ k4 f: I增加--loop_optimization_level=2 编译选项. a0 N) t8 X) D% H+ D! d
- w$ S5 f2 H- S2 Q) H8 j# W. w8 U! q
FPU 核心汇编代码的比较,见图8和图9。
9 U1 r* u9 R+ g( x& s2 u, U
21.png
▲ 图8. 使用--loop_optimization_level=2 编译选项的常规代码汇编
22.png
▲ 图9. ARM DSP 库 arm_mult_f32 函数汇编

3 ^8 C2 s" I1 c
& T  E9 m+ ]5 `2 B" X7 c* u
使用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是很高效的。
+ c( \4 u. ^7 `& Z" P& A5 A
6 U0 R+ U$ f0 {* ]* t4 I04小结
' t7 o/ h' `0 F: |本文介绍了使用 STM32G474 FPU 进行浮点运算,从系统的角度、ARM DSP Lib、 编译选项的影响等几个方面探讨如何提升整体性能,并介绍了如何利用 KEIL Trace 工具进 行测量。以供在系统性能方面有需求的客户参考借鉴。
# e& ?8 r# |5 ^0 e( n) K( _* ]+ N7 V1 T) x
7 R4 N- a3 J3 _& O6 O9 U/ G
如果你有其他想要的实战笔记,可评论区留言,管管来跟工程师沟通!# w) i. P1 @2 A% k
  W4 b7 Z! |. I- D# C
9 [4 A8 _$ z1 E" Y8 L
8 p- }8 T& l8 f$ y9 F( s6 s

* t5 E6 V* w* T7 i8 d- i2 }6 {& G+ w( {

9 ]- o7 d2 @+ O5 ~) F
0 y% Y( C4 _9 n! D' F: g
1 K8 X5 p6 i3 a* E# k. b" D
: c% S6 J/ J8 A; a# L2 G. ]: s0 E
) y. i( l0 L$ i; w5 F+ t$ d

) e% v) b' @6 O+ c2 y! q& c" y. L  l+ K
收藏 评论0 发布时间:2024-10-30 16:57

举报

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