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

【经验分享】STM32H7的Cache和MPU

[复制链接]
STMCU小助手 发布时间:2021-12-18 17:42
一、Cache1 e+ ^6 x2 W9 m' U
5 T6 i4 q2 ]4 @* C0 P5 `7 N/ Z- _
1、介绍
1 i/ k' e# ~- J2 |9 N8 M$ F
  o% R$ g- w: q* F2 ^8 W        Cache又分数据缓存D-Cache和指令缓冲I-Cache,STM32H7的数据缓存和指令缓存大小都是16KB。STM32H7主频是400MHz,除了TCM和Cache以400MHz工作,其它AXI SRAM,SRAM1,SRAM2等都是以200MHz工作。数据缓存D-Cache就是解决CPU加速访问SRAM。
. l+ W+ d; T1 m- l8 b+ f' ^5 t7 J4 X7 h+ l7 Y
        如果每次CPU要读写SRAM区的数据,都能够在Cache里面进行,自然是最好的,实现了200MHz到400MHz的飞跃,实际是做不到的,因为数据Cache只有16KB大小,总有用完的时候。; `0 p/ m% W0 k7 B8 G5 W

6 T2 X0 {  b2 J* T9 D7 {& Q2、操作,分为读操作和写操作
8 T5 [. J- m. K1 U0 F, T
6 ^  v9 f& J( F        读操作: 如果CPU要读取的SRAM区数据在Cache中已经加载好,这就叫读命中(Cache hit),如果Cache里面没有怎么办,这就是所谓的读Cache Miss。
* n$ }  z9 Y3 V! H; F; @- j+ d+ l6 @  v& \) z+ I
       写操作: 如果CPU要写的SRAM区数据在Cache中已经开辟了对应的区域(专业词汇叫Cache Line,以32字节为单位),这就叫写命中(Cache hit),如果Cache里面没有开辟对应的区域怎么办,这就是所谓的写Cache Miss。
( M- O: Y  X* i4 M: i9 G( Y* ~% d- C* m: e' z8 p2 D/ E1 E
3、H7支持的Cache策略,共4种& Q( U/ n' c) j* F4 {
% w  k+ y. d9 m
20191118102334153.png
1 i& D7 E  ]0 `

: d# f, ~# t# t( p* n$ o. P<回写:如果Cache中有,写数据只写到Cache,不写到RAM。>
& N, B2 M: p& E/ V/ W  ^1 N8 L) r
: l5 S! s' J- k; C5 W<透写:如果Cache中有,写数据也要同时写到Cache和RAM。>
. Z9 v% s8 a3 P& s" ^8 ~: R# `& p
<write allocate:写数据时,如果Cache中没有,那么就要在Cache中开辟一个空间,把数据写入Cache,同时把RAM中的相邻数据加载进来填充Cache。>+ M- i" k: P2 o. v6 [4 E: P- z

5 K) a8 S/ }5 T" v& Y$ J5 f0 I<no write allocate:写数据时,如果Cache中没有,那么把数据直接写入RAM。>7 H' k5 L1 m9 T8 F
% m1 c, v3 Z' o1 a4 R# \
<read allocate:读数据时,如果Cache中没有,那么就要在Cache中开辟一个空间,把数据从RAM中加载进来,后续的读操作,就可以直接从Cache中读取了。>
1 I( t) O- T" ?9 m; k! h8 c* ]) o9 H; j: E. k
<no read allocate:读数据时,如果Cache中没有,那么直接从RAM中读。>
- `5 S' z/ w  X8 q1 ]4 V8 A) M1 D1 h$ P7 V( p% n. O5 L- A: p
4、风险. f8 _( {# f  K$ }

  r! ]! d# }& Q1 A% ^
20191118112913153.png

6 X9 d8 M$ D  O, z9 l( q# A( ^* y; U
        从上面的图就看出来使用Cache的风险,因为DMA是直接与SRAM交换数据的,而CPU与SRAM之间隔了一个Cache,如果DMA更新了某个数据到SRAM,CPU要去访问,而恰好Cache中有,那么CPU就不会去SRAM中拿,就会拿到Cache中已经过时的数据。因此使用了DMA的内存区要配置为无Cache或者拿数据前清一次Cache。1 T& u- |; G7 G1 D
4 J$ J, m4 ]( x" ]8 G7 J9 l& c+ c
5、相关函数! T4 d. t1 \& G' T& |: I! M  i
5 B0 U* c6 m1 S0 C: o2 l
        SCB_EnableICache(void) :用于使能指令Cache,系统上电后优先初始化即可。; O. V( L' S5 ]8 P

/ e7 L8 y$ `, T+ X% ~6 a        SCB_DisableICache(void) :用于禁止指令Cache。
% K! V' L' x0 {' u- U2 @  z6 }. u. Y. L  f3 X
        SCB_InvalidateICache(void) :用于将指令Cache无效化,无效化的意思是将Cache Line标记为无效,等同于删除操作。这样Cache空间就都腾出来了,可以加载新的指令。7 V* W4 }2 W' [& r
6 \; R% ~- h( L! @; ~4 m
        SCB_EnableDCache(void) :用于使能数据Cache,系统上电后优先初始化即可。
/ ?9 v; J% i: N0 `  c6 R, `6 V; b# z6 H9 g3 L! U
        SCB_DisableDCache(void) :用于禁止数据Cache。
+ a5 D$ `& K7 q0 O& {8 W
6 g3 `- k# i2 F; y* D, ?        SCB_InvalidateDCache(void) :用于将数据Cache无效化,无效化的意思是将Cache Line标记为无效,等同于删除操作。这样Cache空间就都腾出来了,可以加载新的数据。4 w. ~* H6 F( B# k

. S! k# i5 ^. z& P7 W* A4 [# n        SCB_CleanDCache(void):用于将数据Cache清除,清除的意思是将Cache Line中标记为dirty的数据写入到相应的存储区。
+ L# }' p2 `/ F( c; P' W* n4 x/ O( I, Q- V
        SCB_CleanInvalidateDCache(void) :此函数是前面两个函数SCB_InvalidateDCache和SCB_CleanDCache的二合一。将Cache Line中标记为dirty的数据写入到相应的存储区后,再将Cache Line标记为无效,表示删除。这样Cache空间就都腾出来了,可以加载新的数据。# n5 \. e0 E( i, B

( E6 Y1 f) {! y4 T# s2 ~        SCB_InvalidateDCache_by_Addr(uint32_t *addr,int32_t dsize):可以指定地址和存储区大小,地址要32字节对齐,大小要是32字节的整数倍。用于将数据Cache无效化,无效化的意思是将Cache Line标记为无效,等同于删除操作。这样Cache空间就都腾出来了,可以加载新的数据。. s3 ^: B. G; W- L- y) M( A! a5 M

% j5 v" n! D( Q1 q7 Z        SCB_CleanDCache_by_Addr(uint32_t *addr,int32_t dsize):可以指定地址和存储区大小,地址要32字节对齐,大小要是32字节的整数倍。用于将数据Cache清除,清除的意思是将Cache Line中标记为dirty的数据写入到相应的存储区。# h8 I) ]& l0 j8 w3 l2 t2 C: y
! x1 ^6 v$ b7 l; @7 T% F; s
        SCB_CleanInvalidateDCache_by_Addr(uint32_t *addr,int32_t dsize):可以指定地址和存储区大小,地址要32字节对齐,大小要是32字节的整数倍。将Cache Line中标记为dirty的数据写入到相应的存储区后,再将Cache Line标记为无效,表示删除。这样Cache空间就都腾出来了,可以加载新的数据。; G1 @! r+ D5 a* v8 i9 d, O! ~
# n3 E0 |' a6 ~1 T! R- ~8 Z* N  k
  N3 i8 d* v3 D. ]

4 Q2 ~& s+ {& U# O3 q, e" A/ h二、MPU, n7 d) `+ t' V7 Z

, {8 r6 y% a& M$ Z! o0 z1、作用
+ n$ G* w% Q  v; m: X) C9 T% s" E# w9 [* ^6 d* Q. E  D
        防止不受信任的应用程序访问受保护的内存区域; 防止用户应用程序破坏操作系统使用的数据;通过阻止任务访问其它任务的数据区;允许将内存区域定义为只读,以便保护重要数据;检测意外的内存访问。 简单的说就是内存保护、外设保护和代码访问保护。
& m! ]. S: r1 m8 ?( l
: D$ g8 _! D9 W! I+ G4 U. l" F2、MPU可以配置的三种内存类型* r8 p% [; J0 y; ]. c

! |- p$ {. G, x, h: v% s1)、Normal memory
7 Y0 T9 p/ x5 y
8 c# ?6 o3 l7 w        CPU以最高效的方式加载和存储字节、半字和字,对于这种内存区,CPU的加载或存储不一定要按照程序列出的顺序执行。
( y. j# o1 k+ C) ?
# `  |% o% g5 B: I2)、Device memory: m1 R+ G* b. ^8 l( M
: d7 N+ W6 H  X1 A$ a; o; F% Y
        对于这种类型的内存区,加载和存储要严格按照次序进行,这样是为了确保寄存器按照正确顺序设置。; s# ?: f" h( U3 J7 ^, Z3 J
, A, [& c8 l' k8 {6 q& a
3)、Strongly ordered memory
: O" a9 I+ k1 k, N
; r1 }9 q8 o7 ^3 q5 ?, O1 C        程序完全按照代码顺序执行,CPU需要等待当前的加载/存储指令执行完毕后才执行下一条指令。这样会导致性能下降。
/ `$ L" ]! L8 X% j
$ z9 j$ _/ @' n% i& N1 v3、MPU的使用1 y6 {6 u6 L4 M3 ]/ o0 a, g8 V  U, f

+ ]: ]% r7 x; e- o. @7 z' J7 K/ h& Y8 F        MPU可以配置保护16个内存区域(这16个内存域是独立配置的),每个区域最小要求256字节,每个区域还可以配置为8个子区域。由于子区域一般都相同大小,这样每个子区域的大小就是32字节,正好跟Cache的Cache Line大小一样。2 O( J% T- N4 b2 o0 P& q# Q: {  \
, X; P4 J2 V5 V/ {* a0 u( {" S
        使用时把一段连续的内存区配置为一个MPU保护区域,然后再配置这个MPU保护区域的特性。比如128KB的DTCM、64KB的SRAM4、32MB的SDRAM。MPU保护区域的特性使用MPU_RASR寄存器来配置,描述如下:& o' r8 Q, O( X; l" \1 Q
! ?5 F. F/ h: J. i1 Z
20191118111700740.png

5 e$ a' u8 H4 T7 j( \$ V
# H- }1 a! K) u7 s8 T1)、XN:用于控制这个MPU保护区域能否执行程序代码。6 r4 F% `4 a6 ]7 |2 z0 t

/ K- q# f/ ^- i- F7 V2)、AP:用于控制这个MPU保护区域的特权级和非特权级的读写访问权限。
9 @/ z; O( `. j1 t! h" v5 D! t5 n+ O$ T  T1 t5 P, d- l5 o7 J6 a; |
20191118111401294.png
% |: E- S1 R* U. Q
3 u& L  Z( E. P' W$ a8 ]
3)、TEX、C、B、S:H7支持4种Cache策略,这几位就是用来控制这个MPU保护区域使用哪一种。
1 w9 y) ~0 P% u( C5 r$ Y0 x2 R; [) O6 S: Z8 w
2019111811214681.png

) @3 z) n$ @2 L' l* m: M
; `9 I  _1 D5 hS位用于解决多总线或者多核访问的共享问题,一般不要开启。
. C3 N" `6 z: h2 F- H1 _1 Z
5 ?9 S1 ?: g6 j. F6 C6 e4)、SRD:这个位用于控制内存区的子区域 ,使用的是bit[15:8],共计8个bit,一个bit控制一个子区域, 0表示使能此子区域, 1表示禁止。一般情况下,取值0x00,表示8个子区域都使能。6 s6 m, g) I$ Z$ C; Q
0 I- h6 r8 q2 X& e4 _* u7 r5 k1 G" }
5)、SIZE:配置这个MPU保护区域的大小。) }+ \% q( Q$ c

: E0 D  ?8 k* N1 I& o, u0 B2 ?三、HAL配置例程
9 `+ w) R; j- p5 B+ J8 _
* P  M% Y5 X6 g+ _! F) C5 G
  1. //设置某个区域的MPU保护* T) g- Y+ L0 T: K. ?
  2. //baseaddr:MPU保护区域的基址(首地址)  {# D$ J; V$ F/ T1 k# I3 U) X4 }
  3. //size:MPU保护区域的大小(必须是32的倍数,单位为字节),可设置的值参考:CORTEX_MPU_Region_Size: G& }! U1 }, y5 ?8 v8 C0 k8 b
  4. //rnum:MPU保护区编号,范围:0~7,最大支持8个保护区域,可设置的值参考:CORTEX_MPU_Region_Number
    ; d  |- D( Q# |: U- k+ @  U
  5. //ap:访问权限,访问关系如下:可设置的值参考:CORTEX_MPU_Region_Permission_Attributes
    5 K+ b) Y5 Y5 N& U
  6. //0,无访问(特权&用户都不可访问)
    . _* h4 n: ^$ D( s
  7. //1,仅支持特权读写访问
    # o6 f0 s# W- N2 I5 _( T+ Y
  8. //2,禁止用户写访问(特权可读写访问)! S8 x1 A' ?1 ?
  9. //3,全访问(特权&用户都可访问)$ |: P) m+ T+ E' S  M, d9 }
  10. //4,无法预测(禁止设置为4!!!)
    + S$ v4 I1 `1 ~; g) \/ o& D
  11. //5,仅支持特权读访问
    . L4 g7 k' |; M3 @2 z) A: P2 g
  12. //6,只读(特权&用户都不可以写); E& w( x/ y5 K$ l6 S( [
  13. //详见:STM32F7 Series Cortex-M7 processor programming manual.pdf,4.6节,Table 89.% z- p* V; n5 w( |) Y6 q( G5 n
  14. //sen:是否允许共用;0,不允许;1,允许
    ' W( a3 d7 n, B1 u) g6 x* @5 O3 f/ _
  15. //cen:是否允许catch;0,不允许;1,允许" n( ~; P8 I3 k  w# H" h5 Y
  16. //返回值;0,成功.& C8 f5 S' U. v
  17. //    其他,错误.) b' m' ^; O  ^/ o5 F& B& X
  18. u8 MPU_Set_Protection(u32 baseaddr,u32 size,u32 rnum,u32 ap,u8 sen,u8 cen,u8 ben,u8 Tex)
    9 }2 n4 c5 Q7 S
  19. {/ u& |- R( \& g: o- X5 D& `
  20.         MPU_Region_InitTypeDef MPU_Initure;
    $ n- m( K5 L7 B+ W
  21.         HAL_MPU_Disable();                                                                        //配置MPU之前先关闭MPU,配置完成以后在使能MPU* H: ?4 y+ G$ I  C% y

  22. / Q5 _/ C0 W* L' T. f. [$ K& X: X: o
  23.         MPU_Initure.Enable=MPU_REGION_ENABLE;                                //使能该保护区域
    & h) T) e, [1 L. D. t
  24.         MPU_Initure.Number=rnum;                                            //设置保护区域8 b6 o% v) `6 g  T; P% o
  25.         MPU_Initure.BaseAddress=baseaddr;                            //设置基址
    - X4 M, `6 ~3 _0 s$ ]; Z
  26.         MPU_Initure.Size=size;                                                    //设置保护区域大小. M  X. y: F2 y* j2 [+ z% x9 r
  27.         MPU_Initure.SubRegionDisable=0X00;                      //禁止子区域
    : E6 ?4 I* z/ T
  28.         MPU_Initure.TypeExtField=Tex;                           //设置类型扩展域
    5 H& Z3 J5 i4 T% V6 t" [
  29.         MPU_Initure.AccessPermission=(u8)ap;                            //设置访问权限,& E4 i9 X' J' i4 `* b& y" v" x
  30.         MPU_Initure.DisableExec=MPU_INSTRUCTION_ACCESS_ENABLE;        //允许指令访问(允许读取指令)
    * c- K9 g5 ^* T4 A; k
  31.         MPU_Initure.IsShareable=sen;                            //是否允许共用
    1 ^" E! F' C1 v+ y" k( u
  32.         MPU_Initure.IsCacheable=cen;                            //是否允许cache- z$ v' ?% q' Q2 p
  33.         MPU_Initure.IsBufferable=ben;                           //是否允许缓冲
    ' |- F8 s* C7 z* d1 Z( V. B
  34.         HAL_MPU_ConfigRegion(&MPU_Initure);                     //配置MPU- B* @- z8 Q" A
  35.         HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);                                //开启MPU1 x. j; T- [' R" O
  36.     return 0;) \0 C/ c' d/ F5 J# x
  37. }  U& c( h% }6 Q" \' @

  38. - L. E4 D, `3 t
  39. //设置需要保护的存储块. q/ r: a$ d/ X8 u  ?
  40. //必须对部分存储区域进行MPU保护,否则可能导致程序运行异常% J' C; Y# E. d/ w' C
  41. //比如MCU屏不显示,摄像头采集数据出错等等问题.... d$ a( v' Z2 b( t
  42. void MPU_Memory_Protection(void)   //特意把SRAM4设置为不允许cache,使用DMA的变量可以放在这里。但要注意相应DMA能否访问SRAM4- D; B, k8 l, j) f/ z
  43. {- l7 M! B' [* S% S. Z* `! R' p
  44.         MPU_Set_Protection(0x20000000,MPU_REGION_SIZE_128KB,MPU_REGION_NUMBER1,MPU_REGION_FULL_ACCESS,0,1,1,MPU_TEX_LEVEL0);        //保护整个DTCM,共128K字节,禁止共用,允许cache,允许缓冲3 M9 z. M8 P; H9 b% D
  45.         MPU_Set_Protection(0x24000000,MPU_REGION_SIZE_512KB,MPU_REGION_NUMBER2,MPU_REGION_FULL_ACCESS,0,1,1,MPU_TEX_LEVEL0);        //保护整个内部SRAM,包括SRAM1,SRAM2和DTCM,共512K字节( w* n! M* q1 r: h
  46.         MPU_Set_Protection(0x30000000,MPU_REGION_SIZE_512KB,MPU_REGION_NUMBER3,MPU_REGION_FULL_ACCESS,0,1,1,MPU_TEX_LEVEL0);        //保护整个SRAM1~SRAM3,共288K字节,禁止共用,允许cache,允许缓冲
    / [; w( a( ^1 Y5 X7 a
  47.         MPU_Set_Protection(0x38000000,MPU_REGION_SIZE_64KB ,MPU_REGION_NUMBER4,MPU_REGION_FULL_ACCESS,0,0,1,MPU_TEX_LEVEL0);        //保护整个SRAM4,共64K字节,禁止共用,不允许cache,允许缓冲
    7 d- R1 ~& p2 m, b- z
  48.         MPU_Set_Protection(0x60000000,MPU_REGION_SIZE_64MB ,MPU_REGION_NUMBER5,MPU_REGION_FULL_ACCESS,0,0,0,MPU_TEX_LEVEL0);        //保护MCU LCD屏所在的FMC区域,,共64M字节,禁止共用,禁止cache,禁止缓冲" Y* K/ W7 N( w- M: W
  49.         MPU_Set_Protection(0xC0000000,MPU_REGION_SIZE_64MB ,MPU_REGION_NUMBER6,MPU_REGION_FULL_ACCESS,0,1,1,MPU_TEX_LEVEL0);        //保护SDRAM区域,共32M字节,禁止共用,允许cache,允许缓冲
    - y0 T6 S# L3 Q" Y& Z7 b) g9 p; Q
  50. }
复制代码

3 N2 w! s/ |1 G  L, K! A( ^+ B四、其他
/ G$ r/ A1 Q& M, |( s( x7 h) k9 K( W9 G5 y' W6 r
        值得一提的是,LTDC也是直接从RAM拿数据的,如果你使用了GUI(比如EMWIN),你的显示数据可能会暂存在Cache,而LTDC直接从RAM拿数据,就可能造成画面撕裂、重影、斑点之类的问题。解决方法是,把显存设置成透写。1 z! p6 u- T3 D+ S
3 g/ A( S# y6 T! k7 I) k
        从下面的图可以看到,Cache是在M7那个框里面的。而框外面的外设都可以直接与RAM交换数据,因此使用外设操作数据时都要考虑一下Cache的影响,不然异常可能难以预料。
  S0 z; N, V8 L$ r# K" v2 b1 E4 S0 v8 L
20191119145451828.png
- v+ _3 J% Q' B' B
4 x& ?+ q/ c" {2 D- @
8 H  _/ S; U6 c& R( W0 S, F$ s# }
收藏 评论0 发布时间:2021-12-18 17:42

举报

0个回答

所属标签

相似技术帖

官网相关资源

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