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

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

[复制链接]
STMCU-管管 发布时间:2024-10-30 16:57
8 v* L* u$ N5 H. E
01引言4 r: E+ T3 _0 b' J7 |
客户在使用 STM32G474 时,希望使用 FPU 进行浮点运算,并最大化其性能。本文 从 STM32G474 系统的角度、ARM DSP Lib、编译选项的影响等几个方面探讨如何提升整体性能,并介绍如何使用 KEIL 工具进行测量。" k, _! C: a) C! W  w

/ \) U& P7 E# R5 }( g8 W02STM32G474 FPU 运算性能优化
# q) H. P; ^) C7 j0 [2.1. STM32G474 系统性能优化
! |2 a4 [& i/ B8 N$ o
* {2 Q* _7 A  o& S1 h$ @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 并发访问产生的性能下降。应用则需要根据实 际情况,合理使用内存区域。/ E1 g3 e! z- W0 ^  e6 g0 D+ n: f9 K
12.png
▲ 图1. STM32G474 架构
6 u+ B1 `  |" e' ~: d1 O9 j

# ~$ Y+ A! u  c9 K/ D3 F2 H) S' d2.2. ARM DSP Lib 的使用 1 l0 q& `: k6 M( S; P& I
在 ARM DSP 库实现了很多 math 算法,可进行浮点乘加、点积、卷积、FFT、NN 等 多种算法 API,可以使用 ARM DSP 库高效使用 FPU。ARM DSP 代码位置如下:
5 a5 C' y1 N9 D# s) m! ~  ]
14.png
, M5 c; _) t: J8 }0 y
2.3. 示例代码 4 Y% z$ o! @# m% i$ y5 C$ F1 `
下面示例代码中对浮点乘法运算进行了测试。用户可以使用 STM32CubeMX 生成 STM32G474 KEIL 工程,在 main.c 文件中加入如下示例代码:
& f+ O1 R% |7 n2 m# E# f2 r) P1 x

  1. ) \, J! f# Q: Z5 r
  2. __attribute__((section (".TEST_INPUT_A"))) float32_t testInputA[1024] = 5 A: L7 y+ k8 ~$ N, ~( C
  3. {
      T* r' _8 _( o% {% B& d% m5 ]8 j$ g
  4. 0.623234f, 0.799049f, 0.940890f, -0.992092f, 0.212035f, 0.237882f, -9 o# _2 x. g4 s. J# X
  5. 1.007763f, -0.742045f,
    $ t; [6 `: d( W! L- r
  6. ~~ 这里数组使用动态生成的float数据,数据量较大,略2 O: H* |% W9 {" ~1 _
  7. -0.417470f, -0.205806f, -0.174323f, 0.217577f, 1.684295f, 0.119528f,
    9 B; u, M$ O/ b) y( x
  8. 0.650667f, 2.080061f2 @7 v2 t/ e) v2 {& r- n1 R' J* f
  9. };
    0 Q2 h) r7 n3 v4 o- @, z4 f9 _
  10. __attribute__((section (".TEST_INPUT_B"))) float32_t testInputB[1024] =
    7 B0 H6 R" j. h7 N' M- i
  11. {( v& V  j+ \8 H, o( ?: }! N0 {
  12. -2.423957f, -0.223831f, 0.058070f, -0.424614f, -0.202918f, -1.513077f, -5 @8 i8 @- L' D2 e& u9 \8 |$ i
  13. 1.126352f, -0.815002f,, n8 l* l# q/ L* f  Z4 ]

  14. 3 \- r+ n) I0 ?, u% {8 k
  15. ~~ 这里数组使用动态生成的float数据,数据量较大,略
    5 {0 f, H: [: o
  16. -0.447001f, -0.725993f, 0.354045f, -0.506772f, -2.103747f, -0.664684f, 1.450110f, -0.329805f
    1 {8 H8 y; |4 O6 ~! _* `; q- ?* |
  17. };
    * J, R9 G3 J# v- ~3 O8 j

  18. . {/ @% |, z* G/ U: ?8 [+ _
  19. __attribute__((section (".TEST_RESULT_D"))) float32_t testResult[1024];
    0 L4 B. W" ]$ H' j4 q7 t2 [" A

  20. ( C0 ?6 ?- W: |4 u# ~7 l
  21. float32_t* pA;: P9 X3 J* K8 z
  22. float32_t* pB; / o3 G2 y/ O0 e" K5 v) A
  23. float32_t* pR;
    , a8 j/ K+ o, G! M5 b9 {! W
  24. /* Private user code --------------------------------------------------*/ . z# w% t% I  G7 l# n& \. p" s' {3 z
  25. /* USER CODE BEGIN 0 */
    $ `- k6 k  K, U8 G0 W3 B
  26. void test_normal_mul(uint32_t kLoops, float32_t* $ E, }2 \& s4 A0 j0 w! q
  27. pSrcA, float32_t* pSrcB, float32_t* , d8 T( G4 l1 X$ M+ E( q
  28. pResult, uint32_t lenVector)
    3 g4 t" C1 J# x5 S: @' o
  29. {
    1 X* {8 J( D1 C# u& P8 z; k4 `; P
  30. for (uint32_t j = 0; j < kLoops; j++) ( D" x6 G1 H2 D: G
  31. { 8 l+ L8 }! _/ o0 b9 N! D2 d
  32. pA = pSrcA; $ P- o. w$ ^! l+ |  X0 M
  33. pB = pSrcB;
    4 ]- `9 w* S5 U# v
  34. pR = pResult; 9 x% h! Q  L5 [/ [

  35. 6 J" u) ]% |, _8 B. N* o. Y4 X
  36. for (uint32_t i = 0; i < lenVector; i++)
    0 W9 V* M: I/ ~0 j8 l& E/ n. D/ `, O
  37. { *pR++ = (*pA++) * (*pB++) ; ) z/ h( T! m# _: N) |
  38.    } . s) I% ?7 d( U4 l
  39.   } 0 o$ }! _* y+ B& I/ F  ]2 \
  40. } 9 s1 u' P. v( `$ y- {: \8 |  o

  41. 3 D9 {% G7 V/ ~  L. i3 G1 a
  42. #if defined (__FPU_USED) && (__FPU_USED == 1U) . S" X4 I4 J( s& I( _; {
  43. /* Use arm dsp lib to test basic operation Multiply, FPU enabled */ & x4 k" B; ^# _; B% s0 i4 t
  44. void test_arm_math_mul(uint32_t kLoops, float32_t* pSrcA, float32_t* pSrcB, float32_t* 8 p5 y9 S6 h, w/ s# @: v. Y, a. q
  45. pResult, uint32_t lenVector)
    : u9 C0 \* d7 e! K
  46. { 8 @5 Z' u4 m4 e  ~, G. o0 V
  47. for (uint32_t j = 0; j < kLoops; j++)
    & b4 t. s% F& K; F$ j" ]. m
  48. { 6 x) w* f* ]2 h
  49. pA = pSrcA; //Code alignment with the function without FPU 4 S* `6 |7 l- e7 P1 G
  50. pB = pSrcB;
      J+ A' k6 ~2 j- }6 v; p! U
  51. pR = pResult;$ U8 Z% x; O6 H6 D$ K( L2 R+ _
  52. arm_mult_f32(pA, pB, pR, lenVector); " m# Y" L0 p) q* |* f
  53. } # {8 a) F- A5 v4 x
  54. } - }, |( B% W1 ]3 x
  55. #endif
    % V$ \/ K/ T  e" m
  56. 9 I0 j& Y. a* }: y! D. L
  57. /**
      f# r& g; c6 h4 J1 [* g
  58. * @brief The application entry point.
    % Z5 [. V; n& T2 U8 [7 {
  59. * @retval int + E9 T0 d, a% S: u9 ]( S
  60. */
    7 t: H& D" N, M# d/ h  l. F* Q3 \
  61. int main(void)
    , S  ^  y: ]* v
  62. {
    ! M* E2 r; G9 N, l  ^6 ~# h' ^  {
  63. /* MCU Configuration------------------------------------------------*/
    3 r& t, P$ j( [
  64. ( Y2 i2 L5 B# [; Y) ?" O
  65. /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
    + X$ k' V+ w3 o' Y4 L
  66. HAL_Init();
    # }) U+ w9 a0 v+ n6 A

  67. $ u. }# X8 n: z, [& E( l
  68. /* Configure the system clock */ . _5 Q1 ?1 @; c( m( t" E7 c
  69. SystemClock_Config(); , W0 f4 E% e% ~3 V( M+ ]6 {+ D

  70. + L6 Y4 I+ |& m8 h, y7 P

  71. , b4 B7 s! s3 ]# C9 Q

  72. + N7 l8 ~' t$ ?$ H; a
  73. HAL_Delay(100); 8 X8 o+ r& {# w

  74. % _% b8 e8 o' f/ D
  75. /* USER CODE BEGIN 2 */
    ( T4 X4 R2 Z- H1 K" g/ D) C! \
  76. test_normal_mul(10, testInputA, testInputB, testResult, 1024);: I) l( G  a, x/ w) x( i
  77. test_normal_mul(10, testInputA, testInputB, testResult, 1024);
    , l. |+ A% U# ^* w1 R

  78. 2 T- \" Y$ U$ s! L0 D9 o6 t
  79. #if defined (__FPU_USED) && (__FPU_USED == 1U) 2 C& T+ H, M  w/ {& ~% c, R
  80. // Multiply calculation with arm dsp lib
    ; S0 u: D2 ?1 f. R6 ]
  81. test_arm_math_mul(10, testInputA, testInputB, testResult, 1024);
    - F* Q+ t! _" i8 K
  82. test_arm_math_mul(10, testInputA, testInputB, testResult, 1024);
    2 L! T& g+ r2 `1 _& x
  83. #endif
    $ T- l: I# L- ~
  84. $ r9 o# \$ f7 j! z1 S
  85. /* USER CODE END 2 */ ' i3 I; B! J. `: j  Q

  86. ! C8 N5 W4 R) ?/ Q
  87. /* Infinite loop */ ' g% P5 H, l( k$ x& ~* o
  88. /* USER CODE BEGIN WHILE */
    " V" m3 k* r; g4 B: n% G5 q+ q
  89. while (1)
    + m# o2 w2 z) _6 `$ f
  90. {
      O0 _3 D( C& s( M- j, y+ v$ S/ h
  91. /* USER CODE END WHILE */
    * L5 J* b( Y$ \* [  _

  92. # ^( r- b9 D. O
  93. /* USER CODE BEGIN 3 */
    . O1 ^+ y, ?, m% ^: I8 J
  94. }
    9 p$ }! ]' }* J
  95. /* USER CODE END 3 */ * f7 R) o- `# b& v. T# F# ]
  96. }
复制代码
2.4. 工程配置 1 m4 T- {- v! f
通过 KEIL 工程 Options / Target, Floating Point Hardware, 确定 FPU On/Off。
8 b  z  S% G) w3 @' M
15.png

2 [8 Q1 J: b" ^+ B6 P1 ?1 u

5 W! V0 r/ \( i$ D1 H1 [6 m
▲ 图3. KEIL 项目工程 FPU 单精度浮点设置
通过 STM32G474_FPU_TEST.sct 文件配置 Data 存放区域,如下例,将测试数据置 于 SRAM2。
  p' |) \9 w* x/ R9 q$ e! j
  1. : A1 ^( _2 q6 j0 v2 E  r: j5 `
  2. RW_IRAM1 0x20000000 0x00014000 { ; RW data8 r  X3 a; N/ V; v; _, D& V& S' u7 P
  3. .ANY (+RW +ZI)
    / j: r7 l& U0 T# b9 K$ Z
  4. }: O' l& ?2 T6 C0 c
  5. RW_IRAM2 0x20014000 0x00004000 {
    " c/ d# ^% Q7 }& |+ o+ m
  6. *(.TEST_INPUT_A): B5 ?9 M  b" j: f4 h
  7. *(.TEST_INPUT_B)
    7 F: d- O" h. ?; a6 @8 y0 y
  8. *(.TEST_RESULT_D)
    $ E0 u3 S1 K( x" B* [; Q
  9. }7 ]# }! N6 d4 m3 L- H+ t- s
  10. RW_CCM 0x20018000 0x00008000 {* p/ |* ?+ w+ H8 \* l/ z% r
  11. }
复制代码
完成后,进行编译链接,即可进行 STM32G474 FPU 性能的测试。+ I/ ?7 X& O+ L; X) q% H- X2 r! y4 W

4 l" X0 S& G4 i/ L2.5. 编译选项 ; l) ^5 g4 V: G$ Z' H. u
本文中我们使用的是 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 浮点运 算扩展)等选项,不过一般使用默认即可。0 G' O, e) l$ R
16.png
▲ 图4. KEIL 工程,编译选项设置
03使用 KEIL Trace 工具进行测量 3 R& A9 w3 j' F+ \
3.1. KEIL 工程设置
/ }# O3 r: m: F6 s( ~) o# {
* V- `' r, e) a/ L) x) y, pKEIL 工程下,首先选择工程选项设置,在 Debug 选项页中,右上部使用 Debugger 工具栏中选 Settings,如下图 5 和图 6 设置。注意 KEIL Trace 设置的时钟必须要与实际 STM32 使用的系统时钟相一致,如图 6 中,STM32G474 使用了 170MHz 的系统时钟, KEIL Trace 中也要相应设置为 170MHz。) O) H) {: j, T$ o
17.png
▲ 图5. KEIL 工程,Debugger 设置入口
18.png
▲ 图6. KEIL 工程,Cortex-M Trace 功能设置
运行KEIL debugger,如下图7所示,将断点设置在要测量的语句前及其后,执行 代码,当Debugger停在断点时,其状态栏中t1指示的即为当前代码的已执行时间。测试代码起止时间差即为代码执行用时。该Trace功能计时是比较准确的。当然如果您希望掌控更多,也可以通过代码来实现,如增加诸如如下代码:  B# }1 G% A2 e, {1 K" `, w) {
nStart = DWT->CYCCNT; , Z: E6 `5 g9 u1 c+ _" l
~~~需测试执行时间的代码~~~
' y& C9 `* r$ X  b  K2 \% ?nStop = DWT->CYCCNT;
8 p5 |: |' t3 l5 `3 [然后用(nStop – nStart)/系统时钟,换算成时间即可。(我们这里没有考虑中断,一 般测量前需要禁用中断)
) T0 @8 t2 k/ C5 Y7 V2 y
19.png
▲ 图7. KEIL 工程,Debug 模式下 Trace 程序执行时间
3.2. 测试结果
) J* H  m8 b- T5 y) `0 H下表列出了STM32G474 10K次 浮点“乘”用时统计。0 e7 {' a: M( R
20.png
▲ 表1. STM32G474 10K 次 浮点“乘”用时统计表
10 X 1024次浮点乘* W1 }6 r5 I6 T$ A+ V3 M4 s4 n* V( U* L

+ y0 g; A/ q5 ^$ M9 K" W增加--loop_optimization_level=2 编译选项
$ }" g! _: R6 z) _1 i) K

! P: A- x  P/ k( I8 A5 t- V7 m8 I  J1 zFPU 核心汇编代码的比较,见图8和图9。
% \) \( I* H) j7 A) V$ G  t
21.png
▲ 图8. 使用--loop_optimization_level=2 编译选项的常规代码汇编
22.png
▲ 图9. ARM DSP 库 arm_mult_f32 函数汇编
1 Q8 q, c7 G% q1 [
4 c/ [2 h1 G% t& V" x2 E
使用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是很高效的。
4 j4 \+ C; n: B$ g2 C/ K! ?: Q% t: l" I4 H# p# n
04小结 3 ]+ E! z, y/ `! V9 E1 Q" e0 s
本文介绍了使用 STM32G474 FPU 进行浮点运算,从系统的角度、ARM DSP Lib、 编译选项的影响等几个方面探讨如何提升整体性能,并介绍了如何利用 KEIL Trace 工具进 行测量。以供在系统性能方面有需求的客户参考借鉴。
7 ]" a! t/ d4 t! m
- D) R- w- w( g6 Z) D0 O
) ?% I: ~5 h$ p9 n) s' p如果你有其他想要的实战笔记,可评论区留言,管管来跟工程师沟通!1 r8 {" A$ @7 K, U, ]4 g
( O3 y! x$ s# ~" N! S
% E7 G& `5 Z5 H+ }! w
0 ?: t4 Z4 Y# L2 X- U) h) p5 k- N9 z1 c
8 r6 g. k, b; w: v

1 T2 @1 P/ j1 ^
$ L; X+ E! t( f1 N- ]
! q* ?  L# h. A& z% |) }; [7 ^3 v, X9 U

% S) C+ W5 R7 z2 Z! f+ q
3 `6 C! H% j5 T, v, c/ d
) Q) P0 g+ o  [- e  Z' `, x, V, s
% e2 Q/ ?; }* g
, l* j5 J, i" z- z! G' O' H
收藏 评论0 发布时间:2024-10-30 16:57

举报

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