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

【经验分享】STM32H7的Cache和MPU

[复制链接]
STMCU小助手 发布时间:2021-12-18 17:42
一、Cache! a/ E2 Q- B: I: g0 t9 y- y
) `5 e5 }1 t; Y0 ^& v
1、介绍8 T* E- J# j$ _( D# w1 Y6 \

4 v# X0 }: p, c# O        Cache又分数据缓存D-Cache和指令缓冲I-Cache,STM32H7的数据缓存和指令缓存大小都是16KB。STM32H7主频是400MHz,除了TCM和Cache以400MHz工作,其它AXI SRAM,SRAM1,SRAM2等都是以200MHz工作。数据缓存D-Cache就是解决CPU加速访问SRAM。
6 j# `3 \- y' f
/ Z5 g8 V  C8 x3 r( b0 E        如果每次CPU要读写SRAM区的数据,都能够在Cache里面进行,自然是最好的,实现了200MHz到400MHz的飞跃,实际是做不到的,因为数据Cache只有16KB大小,总有用完的时候。
5 p# k, h$ V: P; `4 ~3 ^: b- N, ]) {  w3 ]" ?& h' m' Q6 c
2、操作,分为读操作和写操作
9 a1 v% {% f% Y; P9 d9 Z/ D- _
+ j1 x6 V% M, C! b; @5 D, U, @6 I        读操作: 如果CPU要读取的SRAM区数据在Cache中已经加载好,这就叫读命中(Cache hit),如果Cache里面没有怎么办,这就是所谓的读Cache Miss。
+ Y; T& i1 Y& {. _
/ q+ m+ F& S1 `8 Z+ n" E7 t+ w       写操作: 如果CPU要写的SRAM区数据在Cache中已经开辟了对应的区域(专业词汇叫Cache Line,以32字节为单位),这就叫写命中(Cache hit),如果Cache里面没有开辟对应的区域怎么办,这就是所谓的写Cache Miss。
& d! b2 K7 K- e+ `- A" O
) Z# U8 e1 \% F1 J  K; z  B3、H7支持的Cache策略,共4种! @9 \7 a! Q/ ?0 m4 l2 Q- W# g4 v! _0 v
8 g& B1 y( n5 Q) q; Q1 X, C
20191118102334153.png

8 q4 v" m4 |- m; {' x+ m3 Q7 t2 p' [$ N7 Z7 @8 v
<回写:如果Cache中有,写数据只写到Cache,不写到RAM。>' s4 ?( H- ~/ V7 g

- @% [1 F9 J8 a( g* U: k<透写:如果Cache中有,写数据也要同时写到Cache和RAM。>- k0 f& `9 R* c, z. e* ^6 l
6 a% k/ a* l( O* N8 x& F
<write allocate:写数据时,如果Cache中没有,那么就要在Cache中开辟一个空间,把数据写入Cache,同时把RAM中的相邻数据加载进来填充Cache。>& X1 |( d. X3 C/ _# {

0 n# Z3 i" r& k2 R; }& Q- N; v<no write allocate:写数据时,如果Cache中没有,那么把数据直接写入RAM。>
* @- |: W- N$ }  T' V( N8 ]- ]
) W/ q' z& N4 j2 b<read allocate:读数据时,如果Cache中没有,那么就要在Cache中开辟一个空间,把数据从RAM中加载进来,后续的读操作,就可以直接从Cache中读取了。>
) k- O8 W. c2 G0 M
, {' _+ K; s* G1 G<no read allocate:读数据时,如果Cache中没有,那么直接从RAM中读。>" W2 H) _+ ?2 q6 B8 R0 k

1 E4 i+ V& r6 Y4 e& M4、风险4 @% G  a$ N" l# q- {* o
" @! E& P1 s: g6 k4 K8 @- t. ^
20191118112913153.png

' H9 h! f3 A# s) y" H1 h0 R: N5 b" h
        从上面的图就看出来使用Cache的风险,因为DMA是直接与SRAM交换数据的,而CPU与SRAM之间隔了一个Cache,如果DMA更新了某个数据到SRAM,CPU要去访问,而恰好Cache中有,那么CPU就不会去SRAM中拿,就会拿到Cache中已经过时的数据。因此使用了DMA的内存区要配置为无Cache或者拿数据前清一次Cache。
& [/ o  B, f) ]7 _
  b" \. c* |/ j% ]  m+ ?, ~, {! b5、相关函数
( _2 @7 v& O) a2 I
2 N$ B# f' j; K4 t; x        SCB_EnableICache(void) :用于使能指令Cache,系统上电后优先初始化即可。2 h% D% ~4 d$ ?# a$ d8 ~

, E5 ]$ L$ p6 o1 I        SCB_DisableICache(void) :用于禁止指令Cache。
$ V% Z* \! T# M4 S/ L
: g+ Q; V6 `2 V. c2 P( p        SCB_InvalidateICache(void) :用于将指令Cache无效化,无效化的意思是将Cache Line标记为无效,等同于删除操作。这样Cache空间就都腾出来了,可以加载新的指令。
" Z; `% ?& [: }3 ?. ~- V" n7 r; \* e7 I
        SCB_EnableDCache(void) :用于使能数据Cache,系统上电后优先初始化即可。1 o4 L% g" I' `2 \% J* z6 T2 ~

5 c! \. a0 j5 k: C- ?, l! O        SCB_DisableDCache(void) :用于禁止数据Cache。' D8 k/ @& ?4 v7 U& j3 A
( J4 S, N& c/ L4 J# V- @& ]4 X! G  F
        SCB_InvalidateDCache(void) :用于将数据Cache无效化,无效化的意思是将Cache Line标记为无效,等同于删除操作。这样Cache空间就都腾出来了,可以加载新的数据。
7 {! |( ]" Q- H' O$ S0 w
- u, p/ x5 e/ M" w& U' q        SCB_CleanDCache(void):用于将数据Cache清除,清除的意思是将Cache Line中标记为dirty的数据写入到相应的存储区。, Z1 |0 O# ]* t6 ]# R

  s1 V% c/ w  @1 e" h, x        SCB_CleanInvalidateDCache(void) :此函数是前面两个函数SCB_InvalidateDCache和SCB_CleanDCache的二合一。将Cache Line中标记为dirty的数据写入到相应的存储区后,再将Cache Line标记为无效,表示删除。这样Cache空间就都腾出来了,可以加载新的数据。
; n: }) m8 Q  n4 E/ Q# E* f
7 ~0 o7 }" ~# l+ |; M/ u8 m8 l        SCB_InvalidateDCache_by_Addr(uint32_t *addr,int32_t dsize):可以指定地址和存储区大小,地址要32字节对齐,大小要是32字节的整数倍。用于将数据Cache无效化,无效化的意思是将Cache Line标记为无效,等同于删除操作。这样Cache空间就都腾出来了,可以加载新的数据。$ I! v/ o: Y; a. r& p# G: F3 a
4 X1 K( Z. d7 n) w$ L
        SCB_CleanDCache_by_Addr(uint32_t *addr,int32_t dsize):可以指定地址和存储区大小,地址要32字节对齐,大小要是32字节的整数倍。用于将数据Cache清除,清除的意思是将Cache Line中标记为dirty的数据写入到相应的存储区。1 U- p* U1 N7 Y6 G
' N( p( o9 G$ n0 m; ]9 g
        SCB_CleanInvalidateDCache_by_Addr(uint32_t *addr,int32_t dsize):可以指定地址和存储区大小,地址要32字节对齐,大小要是32字节的整数倍。将Cache Line中标记为dirty的数据写入到相应的存储区后,再将Cache Line标记为无效,表示删除。这样Cache空间就都腾出来了,可以加载新的数据。
# w2 _% m: w9 t3 d6 `; w% q+ d: |* o( B" r* ~. F" h
5 X% c/ u& b6 t% C- c* p7 B

5 t7 V7 M; k$ t! Z二、MPU
, R. u" N( Y; l2 C
7 D" L) l9 c* Z& U* C: ]/ _3 I1、作用
3 z. n! ~/ V% ~  U' c' h4 l
) n. S( g( c) r$ F        防止不受信任的应用程序访问受保护的内存区域; 防止用户应用程序破坏操作系统使用的数据;通过阻止任务访问其它任务的数据区;允许将内存区域定义为只读,以便保护重要数据;检测意外的内存访问。 简单的说就是内存保护、外设保护和代码访问保护。
. s% o7 o  u6 @7 F
( ]" y- h  ]9 s' k% u+ F2、MPU可以配置的三种内存类型5 {. P; F- F5 o- T% X  h
- |5 Q. A' t& l
1)、Normal memory9 b; ~9 r, Y2 X

7 `# J4 ]* ]( n& f1 W& E        CPU以最高效的方式加载和存储字节、半字和字,对于这种内存区,CPU的加载或存储不一定要按照程序列出的顺序执行。
3 r: F/ l+ c% J9 {& z, o0 j) ^+ E; B6 s
2)、Device memory
- z6 r4 v/ T3 b! k$ j) }/ V: Y! Y# {: e* e1 P( V( }2 e
        对于这种类型的内存区,加载和存储要严格按照次序进行,这样是为了确保寄存器按照正确顺序设置。
/ H1 L% K/ \0 |' ?8 s& e
. p7 u; z! M5 L( D2 z% W3)、Strongly ordered memory, n) b+ }+ Q# A

- W4 D. _7 {- D0 t! g, ?! M        程序完全按照代码顺序执行,CPU需要等待当前的加载/存储指令执行完毕后才执行下一条指令。这样会导致性能下降。
+ ~% w( C% O5 m+ s. q* z6 D
5 v% |# R4 v8 s$ N3、MPU的使用
0 X, E4 n. M' K' U6 f, j  m4 P  [6 U8 [( `3 q# J
        MPU可以配置保护16个内存区域(这16个内存域是独立配置的),每个区域最小要求256字节,每个区域还可以配置为8个子区域。由于子区域一般都相同大小,这样每个子区域的大小就是32字节,正好跟Cache的Cache Line大小一样。
7 H% w1 g& Y' y- s2 W) J& l! A$ f; x$ Y1 }3 K" n
        使用时把一段连续的内存区配置为一个MPU保护区域,然后再配置这个MPU保护区域的特性。比如128KB的DTCM、64KB的SRAM4、32MB的SDRAM。MPU保护区域的特性使用MPU_RASR寄存器来配置,描述如下:5 P  k6 s' v" k  u5 T

) Q9 V% u. z1 V) g7 g
20191118111700740.png

' b7 M3 \, ]& b3 P0 e
5 I; O& W( r* B1)、XN:用于控制这个MPU保护区域能否执行程序代码。9 V% O, S' X# Z: w, ^% P3 ?
  G; _1 c! ~  O
2)、AP:用于控制这个MPU保护区域的特权级和非特权级的读写访问权限。
8 u& _& d) i0 i! f
2 [' V- J! E- a; B
20191118111401294.png
+ r2 b  A. k' X. W
0 ^' t/ c  f2 M
3)、TEX、C、B、S:H7支持4种Cache策略,这几位就是用来控制这个MPU保护区域使用哪一种。
% m2 A5 S* _  l  J3 \% p5 z7 E2 y) _) c& G7 O
2019111811214681.png
; m5 I2 c; p! `! ^. y

6 B0 w) o* `( i$ W0 A3 }. S' |5 W, PS位用于解决多总线或者多核访问的共享问题,一般不要开启。
# q9 y+ x0 h8 G) K) p; m: n# d
4 _# O# D/ g8 s4)、SRD:这个位用于控制内存区的子区域 ,使用的是bit[15:8],共计8个bit,一个bit控制一个子区域, 0表示使能此子区域, 1表示禁止。一般情况下,取值0x00,表示8个子区域都使能。
! ^6 V; g' G5 _/ r8 ~6 x' w
6 Z. t, {$ L3 \( m+ h+ R: R5)、SIZE:配置这个MPU保护区域的大小。% r) L0 x. `! U/ \$ I4 _9 Z% G) `

9 p* Q4 \1 H" ^% ^$ _3 |三、HAL配置例程
5 y' I9 K9 J; a8 j7 J: @7 b
* A: j( j4 K8 S! y
  1. //设置某个区域的MPU保护# Y0 J! t2 P/ j& S, Z" C5 I4 l
  2. //baseaddr:MPU保护区域的基址(首地址)6 ?/ w4 ?7 ^% i& c5 Y
  3. //size:MPU保护区域的大小(必须是32的倍数,单位为字节),可设置的值参考:CORTEX_MPU_Region_Size& c7 j6 s8 `7 y' b' l
  4. //rnum:MPU保护区编号,范围:0~7,最大支持8个保护区域,可设置的值参考:CORTEX_MPU_Region_Number- {. c/ G+ p3 T8 t; l7 L# E
  5. //ap:访问权限,访问关系如下:可设置的值参考:CORTEX_MPU_Region_Permission_Attributes
    . G7 c/ }5 e6 U* M
  6. //0,无访问(特权&用户都不可访问)
    5 B8 o8 f0 g& X3 ^) S) c& f# K  X
  7. //1,仅支持特权读写访问
    6 Q$ |0 k# a( V: d* X
  8. //2,禁止用户写访问(特权可读写访问)
    3 q- T9 w( h; K8 Z+ t( `0 q
  9. //3,全访问(特权&用户都可访问)
      V4 ^  t  v  ^1 R0 ~
  10. //4,无法预测(禁止设置为4!!!)
    5 y  o( z) n$ l1 c+ Y' y
  11. //5,仅支持特权读访问: b% A# P0 `  R% |3 B% X6 c# I
  12. //6,只读(特权&用户都不可以写)" z. W9 c) |- P! a0 t7 f& e1 P4 C
  13. //详见:STM32F7 Series Cortex-M7 processor programming manual.pdf,4.6节,Table 89.0 G9 Y9 [1 y$ {) R- \
  14. //sen:是否允许共用;0,不允许;1,允许
    ) u7 O/ \6 E, n; \' L8 F2 Q
  15. //cen:是否允许catch;0,不允许;1,允许9 V8 s/ j7 Z( V# b' V( u
  16. //返回值;0,成功.
    9 r7 \: g: ^0 Y2 t# @9 }
  17. //    其他,错误.
    * r- p/ F9 R9 I5 K% Z
  18. u8 MPU_Set_Protection(u32 baseaddr,u32 size,u32 rnum,u32 ap,u8 sen,u8 cen,u8 ben,u8 Tex)( U+ Q( o  k. \2 @& M; [" ]4 k; j" {+ E
  19. {
    - R  Y" A, l. K
  20.         MPU_Region_InitTypeDef MPU_Initure;
    ' U2 W- ~. W) q! ?5 W
  21.         HAL_MPU_Disable();                                                                        //配置MPU之前先关闭MPU,配置完成以后在使能MPU9 b9 _0 H% k; V) j9 m- ~5 ~
  22. . p( B% p. h  H7 x. b/ o4 t. q
  23.         MPU_Initure.Enable=MPU_REGION_ENABLE;                                //使能该保护区域
    0 A8 F' W  X$ N+ y* ~$ E  j
  24.         MPU_Initure.Number=rnum;                                            //设置保护区域; X) X' W5 T6 G' c; }" p& k
  25.         MPU_Initure.BaseAddress=baseaddr;                            //设置基址
    5 a- g3 s; l) _* M8 o; L
  26.         MPU_Initure.Size=size;                                                    //设置保护区域大小
    / _1 L% c$ p6 f' Z/ r/ s' F- \
  27.         MPU_Initure.SubRegionDisable=0X00;                      //禁止子区域
    ' X  d! t9 }2 r, w1 v2 G& _8 x
  28.         MPU_Initure.TypeExtField=Tex;                           //设置类型扩展域% g( Z: [6 ?' p7 S
  29.         MPU_Initure.AccessPermission=(u8)ap;                            //设置访问权限,* M* w1 W% `8 p
  30.         MPU_Initure.DisableExec=MPU_INSTRUCTION_ACCESS_ENABLE;        //允许指令访问(允许读取指令)
    + j0 L3 m) g7 i2 ^4 ]1 u
  31.         MPU_Initure.IsShareable=sen;                            //是否允许共用1 M/ y- }$ @* Y: C
  32.         MPU_Initure.IsCacheable=cen;                            //是否允许cache
    - C: S+ f( W. I0 A1 l" e& ?$ C3 s, _" u  J
  33.         MPU_Initure.IsBufferable=ben;                           //是否允许缓冲
    + r) Q+ U" ]3 t- Z) D* {, }0 G
  34.         HAL_MPU_ConfigRegion(&MPU_Initure);                     //配置MPU5 W/ a5 I, ?4 n) g
  35.         HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);                                //开启MPU/ A" u# q' ]' P
  36.     return 0;$ k3 X- x0 y1 D, D! ]5 j( Q; X
  37. }0 j# ]  ]# `4 n  b' a/ p2 }

  38. 3 D. W. ?: `6 V6 e1 s& W  S
  39. //设置需要保护的存储块( c& f  U+ U$ _
  40. //必须对部分存储区域进行MPU保护,否则可能导致程序运行异常8 s- W- M% d+ L$ _( @4 `
  41. //比如MCU屏不显示,摄像头采集数据出错等等问题...8 k' G" l0 N$ F- S
  42. void MPU_Memory_Protection(void)   //特意把SRAM4设置为不允许cache,使用DMA的变量可以放在这里。但要注意相应DMA能否访问SRAM43 B0 i/ a; D- L
  43. {9 E' L# o& E/ i+ K) r0 H( x
  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 D: @; a! r( m: {! H
  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字节
    ' I1 \. b/ |' y2 D( i8 ?: P
  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,允许缓冲/ Y/ ~; f7 c: F5 @9 M; {
  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,允许缓冲
      {1 q; k5 b: ?1 G
  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,禁止缓冲
    . Q! N1 P% C3 f1 k1 F: b' |5 c& K7 @  J7 g
  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,允许缓冲
    ! F6 X0 Y3 Q. z: Y  U& u, B7 e# q7 C' s, h
  50. }
复制代码

& n5 k* Q6 @! }! x- @7 k9 F四、其他; s1 ]3 W! e9 E1 T

3 g7 W3 q: U& t        值得一提的是,LTDC也是直接从RAM拿数据的,如果你使用了GUI(比如EMWIN),你的显示数据可能会暂存在Cache,而LTDC直接从RAM拿数据,就可能造成画面撕裂、重影、斑点之类的问题。解决方法是,把显存设置成透写。
. g/ H5 ?( \; W  ]
6 r2 R9 B( t+ [/ p3 N% m3 }2 {        从下面的图可以看到,Cache是在M7那个框里面的。而框外面的外设都可以直接与RAM交换数据,因此使用外设操作数据时都要考虑一下Cache的影响,不然异常可能难以预料。1 F3 U: W3 i9 l6 h- M9 x0 A1 b

, Z, a, U/ K. b* A# Y" `8 {
20191119145451828.png
% ]7 V$ |4 v) w5 v
- V: n' U! @1 r0 p) h
  P/ k0 I! q& s# y6 e
赞 收藏 评论0 发布时间:2021-12-18 17:42

举报

0个回答

所属标签

相似技术帖

官网相关资源

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