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

【经验分享】STM32H7的Cache和MPU

[复制链接]
STMCU小助手 发布时间:2021-12-18 17:42
一、Cache
3 A. L: I2 c& V( M, e# O3 l& r6 E/ \
1、介绍, l; [6 |+ R( @# ~# V  k  N/ \  Z
$ h3 Y% o* C. m  `9 N  b( q
        Cache又分数据缓存D-Cache和指令缓冲I-Cache,STM32H7的数据缓存和指令缓存大小都是16KB。STM32H7主频是400MHz,除了TCM和Cache以400MHz工作,其它AXI SRAM,SRAM1,SRAM2等都是以200MHz工作。数据缓存D-Cache就是解决CPU加速访问SRAM。0 _8 v# B8 x4 U9 S1 t8 x: c

  d" M- ]% [! m( X& ]        如果每次CPU要读写SRAM区的数据,都能够在Cache里面进行,自然是最好的,实现了200MHz到400MHz的飞跃,实际是做不到的,因为数据Cache只有16KB大小,总有用完的时候。
4 ^  G- I6 l! V! U- p5 M) h; M+ O3 U5 W* y! o  g7 W
2、操作,分为读操作和写操作
! Y% v+ K: J9 j/ a- e8 P) c
0 p) l4 M3 M( ?9 }" h- `        读操作: 如果CPU要读取的SRAM区数据在Cache中已经加载好,这就叫读命中(Cache hit),如果Cache里面没有怎么办,这就是所谓的读Cache Miss。8 e* `. i+ P- s9 F( s7 L1 ]% k5 u1 j
; O% E: O' B, {2 {
       写操作: 如果CPU要写的SRAM区数据在Cache中已经开辟了对应的区域(专业词汇叫Cache Line,以32字节为单位),这就叫写命中(Cache hit),如果Cache里面没有开辟对应的区域怎么办,这就是所谓的写Cache Miss。" X& k; ^4 {8 M  P6 V; q( p8 Q
7 I5 H9 W" G3 p' k: G8 J( A  M
3、H7支持的Cache策略,共4种
3 A( f7 f. v+ r' Y/ c- @( q( y7 P! C* L
20191118102334153.png

8 n1 f$ J8 _* M) V% P- v7 i( r
4 g' F7 a2 q' K<回写:如果Cache中有,写数据只写到Cache,不写到RAM。>
+ f9 ~+ o1 x8 F& ^: l
) A- G. [' R9 F2 Y1 g<透写:如果Cache中有,写数据也要同时写到Cache和RAM。>( W8 Z: u( x" @0 x+ ]

: B' r. V$ I3 C0 n; y4 i" w<write allocate:写数据时,如果Cache中没有,那么就要在Cache中开辟一个空间,把数据写入Cache,同时把RAM中的相邻数据加载进来填充Cache。>
9 W' M! f0 y$ L
9 e9 K4 ~7 W3 l4 S" _3 H9 _<no write allocate:写数据时,如果Cache中没有,那么把数据直接写入RAM。>
: {; n- ?/ G5 v  ?3 G) S( u
. k3 z' c' m7 @5 M! K<read allocate:读数据时,如果Cache中没有,那么就要在Cache中开辟一个空间,把数据从RAM中加载进来,后续的读操作,就可以直接从Cache中读取了。>( @, q% L6 v1 F" Q& `1 F. d8 w

6 Q7 A8 i/ U! R<no read allocate:读数据时,如果Cache中没有,那么直接从RAM中读。>" h) T5 j  x& \8 |
9 L- {7 f" r! _1 {
4、风险
- i5 s# Y1 v$ `' K/ ~/ Y& J" \
- r& s+ v% Z; g: p+ S+ U  _6 a' F! ~7 I/ m
20191118112913153.png
5 u& P$ E) P& z( ?& e6 b9 Q, n

8 T" x9 `, L6 P        从上面的图就看出来使用Cache的风险,因为DMA是直接与SRAM交换数据的,而CPU与SRAM之间隔了一个Cache,如果DMA更新了某个数据到SRAM,CPU要去访问,而恰好Cache中有,那么CPU就不会去SRAM中拿,就会拿到Cache中已经过时的数据。因此使用了DMA的内存区要配置为无Cache或者拿数据前清一次Cache。6 \3 n  m0 D9 Z9 ^, h1 O4 t6 U
9 L9 t2 I; z* }5 b) c2 R, Q$ J
5、相关函数: ^& i/ ?: z% G2 ^
; [+ ^' ?( Z. N
        SCB_EnableICache(void) :用于使能指令Cache,系统上电后优先初始化即可。
9 e: ?! _' h3 N- k. \. w/ e# e9 ~/ b0 V  u
        SCB_DisableICache(void) :用于禁止指令Cache。
$ c  J" W- P& L* U  M
. p# R+ H' o+ p/ a' s        SCB_InvalidateICache(void) :用于将指令Cache无效化,无效化的意思是将Cache Line标记为无效,等同于删除操作。这样Cache空间就都腾出来了,可以加载新的指令。( u4 T) W; k4 C& A- b0 z

% ]" Z* Y/ y  x1 _1 r; b( s# c- a- O        SCB_EnableDCache(void) :用于使能数据Cache,系统上电后优先初始化即可。
% r* K2 H5 c1 |7 I' i4 x* H: q8 @$ x. g3 y1 h9 C. X1 N/ A- K( \
        SCB_DisableDCache(void) :用于禁止数据Cache。8 E; a9 C5 s( s' L) i0 U
. D/ X8 _2 _7 k: O- x
        SCB_InvalidateDCache(void) :用于将数据Cache无效化,无效化的意思是将Cache Line标记为无效,等同于删除操作。这样Cache空间就都腾出来了,可以加载新的数据。
  D9 m3 L* ], O  Y+ Q9 h5 L4 |1 M0 h! W1 [4 l8 C
        SCB_CleanDCache(void):用于将数据Cache清除,清除的意思是将Cache Line中标记为dirty的数据写入到相应的存储区。) l! u6 L9 [+ C6 P2 d

3 _2 F4 U+ O$ L( m. d) Z! E, O" k        SCB_CleanInvalidateDCache(void) :此函数是前面两个函数SCB_InvalidateDCache和SCB_CleanDCache的二合一。将Cache Line中标记为dirty的数据写入到相应的存储区后,再将Cache Line标记为无效,表示删除。这样Cache空间就都腾出来了,可以加载新的数据。
' N3 C4 A9 y$ h$ I! p: x
  `- _1 D5 ?( G% v$ r        SCB_InvalidateDCache_by_Addr(uint32_t *addr,int32_t dsize):可以指定地址和存储区大小,地址要32字节对齐,大小要是32字节的整数倍。用于将数据Cache无效化,无效化的意思是将Cache Line标记为无效,等同于删除操作。这样Cache空间就都腾出来了,可以加载新的数据。
' P: o- x2 H" P! a. v$ D! c. e2 @  P4 v" f0 D
        SCB_CleanDCache_by_Addr(uint32_t *addr,int32_t dsize):可以指定地址和存储区大小,地址要32字节对齐,大小要是32字节的整数倍。用于将数据Cache清除,清除的意思是将Cache Line中标记为dirty的数据写入到相应的存储区。
" x) k5 M3 _4 `+ ]) Y: i! L+ ~8 j- t2 m) g, l& j
        SCB_CleanInvalidateDCache_by_Addr(uint32_t *addr,int32_t dsize):可以指定地址和存储区大小,地址要32字节对齐,大小要是32字节的整数倍。将Cache Line中标记为dirty的数据写入到相应的存储区后,再将Cache Line标记为无效,表示删除。这样Cache空间就都腾出来了,可以加载新的数据。$ h" J" d8 U: U% D) h* E! D
! U' K- G3 Z5 \  C0 o! z  b" a$ m
8 g" p* E. y1 w& `3 q
0 ?& O9 W1 q% L2 ]
二、MPU9 o* [1 `: T. I8 e- G$ i5 F6 ~

- t& T* m: i* ]# ^$ V1、作用
7 \: Z( w" t1 ?2 m2 \! l4 g) j, p  j6 e  D
        防止不受信任的应用程序访问受保护的内存区域; 防止用户应用程序破坏操作系统使用的数据;通过阻止任务访问其它任务的数据区;允许将内存区域定义为只读,以便保护重要数据;检测意外的内存访问。 简单的说就是内存保护、外设保护和代码访问保护。& d# n  F) c& |" y3 ?

' m1 A" M6 b2 f% f1 t. V2 F$ l6 t2、MPU可以配置的三种内存类型
: G% \) f+ H) P3 \1 F5 `$ K2 U5 K: ^1 d' O6 [7 U8 ]
1)、Normal memory
- }( W7 p5 W& d1 N. Y* N3 O8 ]
' {% ~# r8 L, k# J9 ]4 P        CPU以最高效的方式加载和存储字节、半字和字,对于这种内存区,CPU的加载或存储不一定要按照程序列出的顺序执行。
2 t3 F" v( U* L3 L6 m" ?0 |) H7 N  D& D* W  h4 M
2)、Device memory
7 p6 s- ?  R+ f! _( q+ W: W* D# O- I" n4 w4 M- m& E* i
        对于这种类型的内存区,加载和存储要严格按照次序进行,这样是为了确保寄存器按照正确顺序设置。
4 p' V6 r2 f7 N2 ~! ?* y* ~
5 g$ I/ W2 ?. o6 X) Q& V3)、Strongly ordered memory
- Q5 b1 N' n7 t) I' Y: f% h
9 C: N. p! g- {: }& J5 ?        程序完全按照代码顺序执行,CPU需要等待当前的加载/存储指令执行完毕后才执行下一条指令。这样会导致性能下降。
4 a  A, Q: w% B% p$ u: l) c, C
0 X- A3 Z+ ~9 J+ G) X0 F3、MPU的使用
/ \1 G! B) K9 R4 r: F0 q) m+ W$ _
) T) L/ k" n" U- `$ z* k        MPU可以配置保护16个内存区域(这16个内存域是独立配置的),每个区域最小要求256字节,每个区域还可以配置为8个子区域。由于子区域一般都相同大小,这样每个子区域的大小就是32字节,正好跟Cache的Cache Line大小一样。9 x/ p3 S' y0 A+ d5 [! B& v- r; g& [0 ^
3 Z% i4 @% z* C8 x% X
        使用时把一段连续的内存区配置为一个MPU保护区域,然后再配置这个MPU保护区域的特性。比如128KB的DTCM、64KB的SRAM4、32MB的SDRAM。MPU保护区域的特性使用MPU_RASR寄存器来配置,描述如下:
6 c4 d( ^9 L7 G' Y
! Q1 n+ n4 Z# U- o4 R
20191118111700740.png

2 _/ q- |5 V6 S! u+ a0 \6 n6 o. T, z' T4 r
1)、XN:用于控制这个MPU保护区域能否执行程序代码。+ V$ G9 t) v8 j$ y0 r5 ^, E

, v9 u+ O9 f: b: u2)、AP:用于控制这个MPU保护区域的特权级和非特权级的读写访问权限。6 w% r" s' ~9 W" S( ~& ~5 R" d

& k7 M- S" k- y0 k' i* z& i# W
20191118111401294.png

+ g( G. y$ n! h9 p# ?2 l, l- q" v* t: `# ~! y7 @
3)、TEX、C、B、S:H7支持4种Cache策略,这几位就是用来控制这个MPU保护区域使用哪一种。9 u  w, c' Y- g' }5 r) u
. |9 L6 s  A* n( _
2019111811214681.png
! F, ?) k2 ~, |1 H/ c  D8 F  O. _% h
/ [' b( e, @, H: V
S位用于解决多总线或者多核访问的共享问题,一般不要开启。3 }! ~6 \! o; q0 O# d; g
  L- @/ K7 d, A+ ]# F
4)、SRD:这个位用于控制内存区的子区域 ,使用的是bit[15:8],共计8个bit,一个bit控制一个子区域, 0表示使能此子区域, 1表示禁止。一般情况下,取值0x00,表示8个子区域都使能。
4 r8 c# O; D" p6 I) Y3 P( ^2 x  B$ D3 ~9 k
5)、SIZE:配置这个MPU保护区域的大小。/ ^: i- g' G$ G; H

4 q3 s7 a7 }/ r# V* j三、HAL配置例程4 }* h7 S8 \; u

2 Z8 g1 T! }. w. v
  1. //设置某个区域的MPU保护
    9 ?. O% X' B6 Q, e" Y. F5 i- W
  2. //baseaddr:MPU保护区域的基址(首地址)
    0 v2 U9 B8 A/ E* e6 F
  3. //size:MPU保护区域的大小(必须是32的倍数,单位为字节),可设置的值参考:CORTEX_MPU_Region_Size
    ; H- V7 ~: G2 C% }  J( p" `8 L& [
  4. //rnum:MPU保护区编号,范围:0~7,最大支持8个保护区域,可设置的值参考:CORTEX_MPU_Region_Number0 n. h; \8 [- N- }  @) @1 I* D
  5. //ap:访问权限,访问关系如下:可设置的值参考:CORTEX_MPU_Region_Permission_Attributes
    $ Q- Z- J. o  L* _8 o; u6 P4 q" W) g
  6. //0,无访问(特权&用户都不可访问)
    ; e5 b( q- ?- ]3 S2 A
  7. //1,仅支持特权读写访问' U: e. K5 F4 r6 P& ^; S1 o7 h
  8. //2,禁止用户写访问(特权可读写访问)
    - S9 E! O% H7 V: D* g* l& ^7 E2 K
  9. //3,全访问(特权&用户都可访问)
    * m6 q/ }8 v* \# c3 ^
  10. //4,无法预测(禁止设置为4!!!)% R+ f6 U( z- @! @9 S  ~2 A
  11. //5,仅支持特权读访问
    4 n% j+ J* Y6 N! W9 S
  12. //6,只读(特权&用户都不可以写)7 f8 y( c4 e" e% S) y8 [
  13. //详见:STM32F7 Series Cortex-M7 processor programming manual.pdf,4.6节,Table 89., z5 n: _& f9 ]' W
  14. //sen:是否允许共用;0,不允许;1,允许
    . c! C: F9 E, l0 m( Z. H3 l9 ^
  15. //cen:是否允许catch;0,不允许;1,允许) M. u9 d6 i* \0 s: U$ n5 B
  16. //返回值;0,成功.- Y$ U- i; d) x2 c$ |$ }; `
  17. //    其他,错误.
    + r" E5 d! |4 n! G" }
  18. u8 MPU_Set_Protection(u32 baseaddr,u32 size,u32 rnum,u32 ap,u8 sen,u8 cen,u8 ben,u8 Tex)
    + s8 r" f0 `" Z  g
  19. {
    + Y) A$ B$ R$ ~8 I
  20.         MPU_Region_InitTypeDef MPU_Initure;
      c6 H) b& [" F( ~
  21.         HAL_MPU_Disable();                                                                        //配置MPU之前先关闭MPU,配置完成以后在使能MPU
    2 a8 `( x' f. F7 X; ]  ?- `

  22. % a+ X/ b# H# t- B# I. J: M
  23.         MPU_Initure.Enable=MPU_REGION_ENABLE;                                //使能该保护区域 / Z' D, C/ j+ W) Z- f1 g
  24.         MPU_Initure.Number=rnum;                                            //设置保护区域* f! u  s) u7 w; Q. X# \9 f
  25.         MPU_Initure.BaseAddress=baseaddr;                            //设置基址/ J9 V# a# i* z5 f+ _  p" W9 u- p# |
  26.         MPU_Initure.Size=size;                                                    //设置保护区域大小; e9 H; }, t. v4 F- V
  27.         MPU_Initure.SubRegionDisable=0X00;                      //禁止子区域
      E' h; w( K7 U' z3 E- q# m8 S
  28.         MPU_Initure.TypeExtField=Tex;                           //设置类型扩展域
    + T8 v8 [  _- r" g) J. a
  29.         MPU_Initure.AccessPermission=(u8)ap;                            //设置访问权限,2 f) b9 n% s, h! [4 ]1 X0 d7 C
  30.         MPU_Initure.DisableExec=MPU_INSTRUCTION_ACCESS_ENABLE;        //允许指令访问(允许读取指令)2 Z8 s' b( ~$ H5 @
  31.         MPU_Initure.IsShareable=sen;                            //是否允许共用7 n8 U9 ^: |3 M* \% q3 M
  32.         MPU_Initure.IsCacheable=cen;                            //是否允许cache3 p6 r, E& E( R+ a
  33.         MPU_Initure.IsBufferable=ben;                           //是否允许缓冲
    6 L6 ?! U( y, F- U$ d; }: e- s
  34.         HAL_MPU_ConfigRegion(&MPU_Initure);                     //配置MPU/ I& v, m( p0 k: I
  35.         HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);                                //开启MPU# w- E; z$ y( ]7 _
  36.     return 0;
    7 Y8 B* l. k* _1 w7 f- a" K/ W
  37. }
    ) ?- l: s9 N% L' f; M1 W

  38. ! _: u- W% ^1 _2 g. ^, j; x. m) `. {
  39. //设置需要保护的存储块
    + k! }) T2 K3 O( [! A& G
  40. //必须对部分存储区域进行MPU保护,否则可能导致程序运行异常
    6 T+ C2 I0 e7 |. c' B' s
  41. //比如MCU屏不显示,摄像头采集数据出错等等问题.../ f& q9 m2 z; |3 Y
  42. void MPU_Memory_Protection(void)   //特意把SRAM4设置为不允许cache,使用DMA的变量可以放在这里。但要注意相应DMA能否访问SRAM4% K1 C* |8 P9 ~2 I0 E: H
  43. {" t4 h8 N6 b4 Y
  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,允许缓冲! u0 E: ?$ G3 t: T9 F1 I4 m$ z
  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字节
    7 x" s8 P5 z0 \3 ^0 f3 d
  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,允许缓冲
    ( X8 t8 g7 V$ L. K+ Z
  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,允许缓冲0 G: w) Z( E4 |( B, ?( E; m
  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,禁止缓冲
    # r. L+ {$ J4 V7 n
  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,允许缓冲$ Y* f- u& X2 K6 o% ]) e
  50. }
复制代码

4 ^& ]: J. ~1 Z: S* U四、其他
) U  U$ c0 s! [$ E' b) l/ p
  q# c" H4 g; X* P* X4 _        值得一提的是,LTDC也是直接从RAM拿数据的,如果你使用了GUI(比如EMWIN),你的显示数据可能会暂存在Cache,而LTDC直接从RAM拿数据,就可能造成画面撕裂、重影、斑点之类的问题。解决方法是,把显存设置成透写。
' G5 Q3 H0 K3 A) ]$ x/ O7 o
& n- x% L1 ~" \: [        从下面的图可以看到,Cache是在M7那个框里面的。而框外面的外设都可以直接与RAM交换数据,因此使用外设操作数据时都要考虑一下Cache的影响,不然异常可能难以预料。. v! b8 ~1 I3 a# s  d1 a$ T

& `9 T: D4 r2 H! N8 S( V; R
20191119145451828.png

6 T* [# J) q+ ?7 E+ v! }
% o9 e$ L, p: }$ X$ P0 n
' u0 Q2 D- C& E) r0 Y9 z
收藏 评论0 发布时间:2021-12-18 17:42

举报

0个回答
关于意法半导体
我们是谁
投资者关系
意法半导体可持续发展举措
创新和工艺
招聘信息
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
关注我们
st-img 微信公众号
st-img 手机版