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

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

[复制链接]
STMCU-管管 发布时间:2024-10-30 16:57
( c; |4 C7 i5 s' G# K
01引言  U- K7 \  o1 T4 W6 {
客户在使用 STM32G474 时,希望使用 FPU 进行浮点运算,并最大化其性能。本文 从 STM32G474 系统的角度、ARM DSP Lib、编译选项的影响等几个方面探讨如何提升整体性能,并介绍如何使用 KEIL 工具进行测量。
1 j& j6 v4 r  r4 p9 a2 e  w" E5 n% W
8 \/ B6 b0 E* B4 B' {02STM32G474 FPU 运算性能优化 6 h/ \- G$ g  ~$ l3 S8 C
2.1. STM32G474 系统性能优化
2 j( b7 B- H% r' K) ]9 {
. [% ~0 y+ X, K% Z6 j4 K5 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 并发访问产生的性能下降。应用则需要根据实 际情况,合理使用内存区域。
" o7 d5 C9 o& S& V* {2 L$ m
12.png
▲ 图1. STM32G474 架构

& _+ M2 o3 s" p' @: v

# P5 c5 V9 w" M7 c1 s2.2. ARM DSP Lib 的使用
4 z. I1 D: k( ?在 ARM DSP 库实现了很多 math 算法,可进行浮点乘加、点积、卷积、FFT、NN 等 多种算法 API,可以使用 ARM DSP 库高效使用 FPU。ARM DSP 代码位置如下:
- Z6 z( N. b/ i% s$ @
14.png

+ z6 T. W0 d* O1 V* V7 x3 |/ M2.3. 示例代码 . l8 Y; y7 ?. W! k7 K+ A9 y
下面示例代码中对浮点乘法运算进行了测试。用户可以使用 STM32CubeMX 生成 STM32G474 KEIL 工程,在 main.c 文件中加入如下示例代码:
: {0 @4 z4 ~1 _/ n0 W

  1. 5 p: S* g" G3 X  K
  2. __attribute__((section (".TEST_INPUT_A"))) float32_t testInputA[1024] = 8 B% x( i7 L7 H% [
  3. {
    6 ?5 F2 E& N: n4 t  Z) O# n
  4. 0.623234f, 0.799049f, 0.940890f, -0.992092f, 0.212035f, 0.237882f, -
    " @) I3 O2 e; E1 p! ~5 S  U  G1 q( E
  5. 1.007763f, -0.742045f,5 a3 g; \# }- L
  6. ~~ 这里数组使用动态生成的float数据,数据量较大,略" n0 p) ^4 o! h5 w% q/ }' g0 h; i" A
  7. -0.417470f, -0.205806f, -0.174323f, 0.217577f, 1.684295f, 0.119528f,
    2 G2 V9 J4 y8 ~1 M
  8. 0.650667f, 2.080061f
    0 C5 A( }% U8 i
  9. };
    6 S& @' u; k& {! A6 l% i0 {9 e
  10. __attribute__((section (".TEST_INPUT_B"))) float32_t testInputB[1024] =
    0 {( x! _: R: u
  11. {
    9 W' ~; E% P/ s% A" h
  12. -2.423957f, -0.223831f, 0.058070f, -0.424614f, -0.202918f, -1.513077f, -
    ' B. H4 }2 g. z+ W/ c! k
  13. 1.126352f, -0.815002f,
    , R& h" M) I/ h# y- ~  x
  14. ) p1 Z) O5 T% ~- s4 W
  15. ~~ 这里数组使用动态生成的float数据,数据量较大,略 ; I- t- a/ L( ?/ Z  p
  16. -0.447001f, -0.725993f, 0.354045f, -0.506772f, -2.103747f, -0.664684f, 1.450110f, -0.329805f
    ! A9 Q$ `+ m' S9 e& a+ y
  17. };
    0 _$ I9 L) ~0 n4 @) E- d/ a

  18. # {2 X0 I/ ?# Q. C. K/ o& |5 ]
  19. __attribute__((section (".TEST_RESULT_D"))) float32_t testResult[1024]; 2 r5 s6 p$ ?2 z$ G

  20. 6 p, u) q  K2 J, \! q: i
  21. float32_t* pA;* X) a+ p2 l/ M1 m8 g
  22. float32_t* pB;
      A, b+ }4 R/ {: K1 f
  23. float32_t* pR; 7 s+ \& B9 k- n' Y! o* b; `2 A
  24. /* Private user code --------------------------------------------------*/ ' V! M, T) P( R% E
  25. /* USER CODE BEGIN 0 */ ) Q$ U. X2 B; }, E# j
  26. void test_normal_mul(uint32_t kLoops, float32_t*
    & G5 z# W, L1 k* I
  27. pSrcA, float32_t* pSrcB, float32_t* ( v' g% A1 _- U1 q  F: S
  28. pResult, uint32_t lenVector)
    , k4 n& A3 H' M8 P4 v7 U8 ?% c& [
  29. {
      ^5 N* D' T& ~
  30. for (uint32_t j = 0; j < kLoops; j++) ( Y$ g: A, R8 X- b3 G3 O8 g
  31. { 3 X2 r* [7 Z8 l' {8 x
  32. pA = pSrcA;
    & s1 @. A9 B6 |" Z6 s7 O( P
  33. pB = pSrcB;
    9 f9 D4 X0 p7 y) k3 I8 P
  34. pR = pResult;   g% I  l2 }; o% Q$ f

  35. 7 o; _- I7 m$ s8 t) ]
  36. for (uint32_t i = 0; i < lenVector; i++) - q4 q' u; b% \7 g5 W) h
  37. { *pR++ = (*pA++) * (*pB++) ;
    7 p: ?4 W$ W+ k1 T; B" E. M
  38.    } # j4 G* y# B- ]) T
  39.   }
    " C# M% [, x8 ^) n" _0 F# ^
  40. }
    5 i: r3 x7 u1 q7 X. x' @5 l( |, |
  41. : q& A/ m( F5 g: s5 }3 B( W
  42. #if defined (__FPU_USED) && (__FPU_USED == 1U) 6 Z# ?7 P) g% u
  43. /* Use arm dsp lib to test basic operation Multiply, FPU enabled */ 4 Y% \" n& |& d* |) R; k
  44. void test_arm_math_mul(uint32_t kLoops, float32_t* pSrcA, float32_t* pSrcB, float32_t* / q4 r$ A3 g* x- A2 V1 h% ~# Z
  45. pResult, uint32_t lenVector)
    8 R: ~" S7 Q' ?+ u, D3 k
  46. { " ^1 j2 u) ^) Y; S
  47. for (uint32_t j = 0; j < kLoops; j++) ' k0 V0 b& E8 f
  48. {
    % b/ H* o5 q8 _* A' w1 W
  49. pA = pSrcA; //Code alignment with the function without FPU
    . x5 u* N% |/ A2 P; u+ j2 `
  50. pB = pSrcB;   X9 Z5 a% Z4 t9 N
  51. pR = pResult;
    3 F( y/ J, B, h& R8 B4 q
  52. arm_mult_f32(pA, pB, pR, lenVector);
    9 N+ l+ k: V! [- q" @: a4 |
  53. }
    - j( r3 M! ~& f$ z( x/ \
  54. }
    ) s" V: K, u( G- ^% r
  55. #endif , l3 e  V4 ^  g3 y4 N! s+ ?

  56. 8 O" M- u& `- |, Z, X, I, S
  57. /** ; Q) ?- G1 _( h) m2 U
  58. * @brief The application entry point. 1 b& L/ v/ c1 |( e( U' |5 A
  59. * @retval int ! e/ _4 T" J' r5 |4 p
  60. */ 5 c3 [7 ]# S3 L5 U" G9 T
  61. int main(void) # R; o( p6 `/ v( I1 M4 r
  62. { * a9 x1 Y/ {9 x* Q- J, [- G) |: _4 W
  63. /* MCU Configuration------------------------------------------------*/ 2 u; t' ]. {. o
  64.   {5 O4 j6 j& B( [
  65. /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
    8 X9 j0 f8 S8 ~6 ?5 i2 Q
  66. HAL_Init(); + x, l9 s8 v' \1 ~  p* {7 R

  67. 6 y( y, G+ ]  O) g! g" H# w  ]: {+ q
  68. /* Configure the system clock */ ' o- q' T8 u5 u+ F+ `; \8 X: N  H8 M
  69. SystemClock_Config();
    : D6 z" @/ {+ T( |2 x! r

  70. 8 S; s+ x6 P$ s. ]' C0 \7 D2 m' l
  71. …
    2 |2 D: {8 ?  u  E" j. ~

  72. 7 U4 j: G1 i2 p2 T
  73. HAL_Delay(100);
    + z7 ?, @# S- V5 U9 \
  74. ) _* N7 |: I7 K. |9 N. `/ N
  75. /* USER CODE BEGIN 2 */ : Z* N5 g1 C% ?0 x! ~3 ]: D
  76. test_normal_mul(10, testInputA, testInputB, testResult, 1024);
    * o- F: s& O& J0 N9 x4 q' S" S
  77. test_normal_mul(10, testInputA, testInputB, testResult, 1024);
    7 E/ G* Q1 `9 c0 x9 {
  78. * [+ S$ C9 h8 z4 @. c# V7 @6 S
  79. #if defined (__FPU_USED) && (__FPU_USED == 1U)
    ' ^1 y: g% D9 C/ d7 h
  80. // Multiply calculation with arm dsp lib   x# }) V% H! s+ n4 K
  81. test_arm_math_mul(10, testInputA, testInputB, testResult, 1024);5 B# V4 M$ X9 O
  82. test_arm_math_mul(10, testInputA, testInputB, testResult, 1024);
    / `6 Z* f2 }' c' Y
  83. #endif
    3 q# L, B) S& f! i" b

  84. ; ]$ g! ]! R- \4 u$ K! t2 a
  85. /* USER CODE END 2 */
    , {  D% x7 w9 N% X" l

  86. . Z7 d' m) b8 H9 U9 H* \
  87. /* Infinite loop */ ( g6 t# X+ ~, y( w4 Q; _
  88. /* USER CODE BEGIN WHILE */
    * M. p3 i& T! P* V" u+ p  q
  89. while (1) 4 m+ a1 r5 c% @+ i9 n
  90. {
    & M$ l7 A, D- i! i  q9 D% [
  91. /* USER CODE END WHILE */ 5 ?: T4 Y- y7 Q9 Z" @8 F

  92. 6 a/ Y9 k" ]  F* V  u& p' F" n
  93. /* USER CODE BEGIN 3 */ ' U' q2 u5 i" u, a7 n
  94. } 6 @: x* k' |3 r0 ^( N" c& d
  95. /* USER CODE END 3 */ ( C6 K9 W: w1 G9 t" X( U* T+ j3 U# G
  96. }
复制代码
2.4. 工程配置
9 G% e7 b% E6 j通过 KEIL 工程 Options / Target, Floating Point Hardware, 确定 FPU On/Off。9 w9 B1 ~, j6 c5 }$ {
15.png

' ~1 [/ w/ F) z) w- `

( W: A& N9 u8 c7 s
▲ 图3. KEIL 项目工程 FPU 单精度浮点设置
通过 STM32G474_FPU_TEST.sct 文件配置 Data 存放区域,如下例,将测试数据置 于 SRAM2。
. l1 c3 W4 V+ ]6 _4 E
  1. 3 u& p' }5 h: x" R8 A  ~8 j
  2. RW_IRAM1 0x20000000 0x00014000 { ; RW data6 @' H' N8 s: m) x4 u& m  c
  3. .ANY (+RW +ZI)
    6 z. Q5 z! Y3 J% r
  4. }
    8 Y$ B6 D! ^/ M, C, I/ I9 r
  5. RW_IRAM2 0x20014000 0x00004000 {, c- O$ r# J: Y% m
  6. *(.TEST_INPUT_A)
    + F6 H7 e7 M) c2 |; Q  ~! V# d5 `
  7. *(.TEST_INPUT_B)$ u. y9 W# A0 |* P% Z7 O5 |
  8. *(.TEST_RESULT_D)( E8 }+ D. Y' u0 [
  9. }: u+ z  @& d2 c  }  w. R0 G. O
  10. RW_CCM 0x20018000 0x00008000 {4 |! a: h8 Y0 s
  11. }
复制代码
完成后,进行编译链接,即可进行 STM32G474 FPU 性能的测试。
1 b/ X* ~7 B2 }# G1 G- z+ c1 M9 f5 X" N
2.5. 编译选项
& E) V9 M* |, a4 s+ N; M  p* _本文中我们使用的是 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 浮点运 算扩展)等选项,不过一般使用默认即可。
) i- x( O/ q) w$ g; ]
16.png
▲ 图4. KEIL 工程,编译选项设置
03使用 KEIL Trace 工具进行测量
' }( K( n- H% j/ e7 E9 Q3.1. KEIL 工程设置
* F& W5 A( N6 N: R& E; \7 I# A# r% s8 w% m8 \4 u
KEIL 工程下,首先选择工程选项设置,在 Debug 选项页中,右上部使用 Debugger 工具栏中选 Settings,如下图 5 和图 6 设置。注意 KEIL Trace 设置的时钟必须要与实际 STM32 使用的系统时钟相一致,如图 6 中,STM32G474 使用了 170MHz 的系统时钟, KEIL Trace 中也要相应设置为 170MHz。6 }$ v( ?( A: p) L$ W% Z4 R9 h6 U
17.png
▲ 图5. KEIL 工程,Debugger 设置入口
18.png
▲ 图6. KEIL 工程,Cortex-M Trace 功能设置
运行KEIL debugger,如下图7所示,将断点设置在要测量的语句前及其后,执行 代码,当Debugger停在断点时,其状态栏中t1指示的即为当前代码的已执行时间。测试代码起止时间差即为代码执行用时。该Trace功能计时是比较准确的。当然如果您希望掌控更多,也可以通过代码来实现,如增加诸如如下代码:
" b2 ~$ B% ~4 n' \* l& q" KnStart = DWT->CYCCNT; / m5 M" z& w! a& {
~~~需测试执行时间的代码~~~
1 U: |$ s* V1 C: xnStop = DWT->CYCCNT;' M( ~: V6 @% ~6 h* P
然后用(nStop – nStart)/系统时钟,换算成时间即可。(我们这里没有考虑中断,一 般测量前需要禁用中断)8 `- z9 K& X6 O5 F  R( G
19.png
▲ 图7. KEIL 工程,Debug 模式下 Trace 程序执行时间
3.2. 测试结果
/ h; j& w" M( L  p6 G下表列出了STM32G474 10K次 浮点“乘”用时统计。
9 y1 r9 s5 v2 S! }4 l/ j
20.png
▲ 表1. STM32G474 10K 次 浮点“乘”用时统计表
10 X 1024次浮点乘
* [: F: N4 X+ \( l) r/ c/ {- ]" x

% o1 H8 ]0 G; \- f% \增加--loop_optimization_level=2 编译选项
- \9 P% d( Y, t2 d! V
7 N3 e0 P$ m# v7 }- m/ r/ h
FPU 核心汇编代码的比较,见图8和图9。2 m3 W" E3 D* ^% g
21.png
▲ 图8. 使用--loop_optimization_level=2 编译选项的常规代码汇编
22.png
▲ 图9. ARM DSP 库 arm_mult_f32 函数汇编
+ I1 R! n7 F$ l
7 a" F3 Y. N! g3 h4 A5 K
使用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 P, e. _$ L3 k3 p1 r, X! C
' w; w; Z" h) ], ^
04小结
* m$ L' r1 i7 h. Q本文介绍了使用 STM32G474 FPU 进行浮点运算,从系统的角度、ARM DSP Lib、 编译选项的影响等几个方面探讨如何提升整体性能,并介绍了如何利用 KEIL Trace 工具进 行测量。以供在系统性能方面有需求的客户参考借鉴。
$ M" x1 y- Q$ G% c; e" i2 m2 p9 d* p3 L4 S. ~

+ w/ l4 y+ _/ A  [1 U4 w( [如果你有其他想要的实战笔记,可评论区留言,管管来跟工程师沟通!* d3 O6 S, s9 i& p. a/ Q
+ h9 j  M- U' W6 b( N3 H, |
* H7 A: J+ v4 s+ y% d5 o! \; L, `

: j6 F& e* H% g! B7 y- b
: }: Z9 x! I; n
2 c) I+ R' l+ y# N9 z& x+ r( |
$ I: k% k+ Y6 f' ~! P/ K
7 p1 u8 {0 |1 M
+ X/ W* a3 b# O, w  B3 W& X; h8 R5 r% Z+ O: w

4 B3 A( X1 `* u0 Z- u; _2 R$ d8 K6 {# b2 d' B3 Q
. v8 T  b$ E) E) `2 ~

. y4 T. Z  p- ~! i/ l4 w
赞 收藏 评论0 发布时间:2024-10-30 16:57

举报

0个回答

所属标签

相似技术帖

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