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

【经验分享】STM32H7的Cache和MPU

[复制链接]
STMCU小助手 发布时间:2021-12-18 17:42
一、Cache
1 U" X+ d+ P2 i4 K- {2 O
- u& }, C' m/ |! w( z# j& O, F+ @1、介绍
8 ~$ W, |. n* c5 [8 \4 s
: Y6 M* z* s1 ]        Cache又分数据缓存D-Cache和指令缓冲I-Cache,STM32H7的数据缓存和指令缓存大小都是16KB。STM32H7主频是400MHz,除了TCM和Cache以400MHz工作,其它AXI SRAM,SRAM1,SRAM2等都是以200MHz工作。数据缓存D-Cache就是解决CPU加速访问SRAM。9 a; S- p. F3 D/ b- K  h
3 q' j; w' p3 @( j7 o! Z
        如果每次CPU要读写SRAM区的数据,都能够在Cache里面进行,自然是最好的,实现了200MHz到400MHz的飞跃,实际是做不到的,因为数据Cache只有16KB大小,总有用完的时候。
1 V$ ]. P* r; O& M# p, [# O: p% _+ ~2 \4 j) I6 U7 t
2、操作,分为读操作和写操作
4 g& I9 ]8 D% M& l5 b# T
9 y! w4 H9 A) H. e0 R/ f/ e" U# }        读操作: 如果CPU要读取的SRAM区数据在Cache中已经加载好,这就叫读命中(Cache hit),如果Cache里面没有怎么办,这就是所谓的读Cache Miss。
' c! S+ I( H3 ^" [/ ?, X5 q9 j3 T, j, u3 X, i) S
       写操作: 如果CPU要写的SRAM区数据在Cache中已经开辟了对应的区域(专业词汇叫Cache Line,以32字节为单位),这就叫写命中(Cache hit),如果Cache里面没有开辟对应的区域怎么办,这就是所谓的写Cache Miss。
/ P& Z8 a" z0 R. ?
1 g* S- ~8 {+ k, u2 m* [/ x3、H7支持的Cache策略,共4种
: G8 W* {3 [( a, G
! W2 k5 {( s' A2 a. H
20191118102334153.png

& g7 t& ?  T# [6 M: z- e- S1 {9 K+ P* i' t7 H7 f# p: o
<回写:如果Cache中有,写数据只写到Cache,不写到RAM。>" _/ i4 [* z; n/ W+ r! {  z
/ j  v+ B3 s5 X3 ^2 y
<透写:如果Cache中有,写数据也要同时写到Cache和RAM。>
$ S/ k2 }0 @& ?, G  ~! ~5 q3 |, Z9 M) Y4 Y
<write allocate:写数据时,如果Cache中没有,那么就要在Cache中开辟一个空间,把数据写入Cache,同时把RAM中的相邻数据加载进来填充Cache。>
. r' r7 x! R2 q, Y2 i& P
" Y  d/ R( m/ c3 n: B<no write allocate:写数据时,如果Cache中没有,那么把数据直接写入RAM。>
( c, p$ }1 b0 w' a- T
3 H) O8 S% J/ C9 o: N, D<read allocate:读数据时,如果Cache中没有,那么就要在Cache中开辟一个空间,把数据从RAM中加载进来,后续的读操作,就可以直接从Cache中读取了。>. |+ `; [) @; j2 D

+ X  m6 R5 l: D+ |& [! @<no read allocate:读数据时,如果Cache中没有,那么直接从RAM中读。>
. y8 p. i: o7 p, v) j0 [% ~7 q3 ]; [3 _* T
4、风险' `" y3 |& ~! r9 l# g' s* `8 G
, O+ @7 |5 ?) X  f2 R9 g7 s6 U
20191118112913153.png

1 F( g& l* N/ e+ e+ z
0 N& Q# M) f( P  q. k: |- W        从上面的图就看出来使用Cache的风险,因为DMA是直接与SRAM交换数据的,而CPU与SRAM之间隔了一个Cache,如果DMA更新了某个数据到SRAM,CPU要去访问,而恰好Cache中有,那么CPU就不会去SRAM中拿,就会拿到Cache中已经过时的数据。因此使用了DMA的内存区要配置为无Cache或者拿数据前清一次Cache。
) u, y1 H$ Z! |; P5 F% v0 P& \7 e6 \' X+ Z$ a
5、相关函数0 W1 X1 c  z$ b5 p* E- Z1 G
8 w5 w8 ]! i. i" u. c
        SCB_EnableICache(void) :用于使能指令Cache,系统上电后优先初始化即可。' I" @0 K1 Z" H* |
2 e0 R* S& e1 w6 B. ~
        SCB_DisableICache(void) :用于禁止指令Cache。; ^" ?8 F. F0 Y3 h4 o9 o

% p9 }0 `/ P6 n, n        SCB_InvalidateICache(void) :用于将指令Cache无效化,无效化的意思是将Cache Line标记为无效,等同于删除操作。这样Cache空间就都腾出来了,可以加载新的指令。
' q  e, x  E. v: d
7 X' }* T$ ~4 O( M3 a        SCB_EnableDCache(void) :用于使能数据Cache,系统上电后优先初始化即可。0 a& N" o! w& y1 a5 ~
# o5 r" V9 {) {
        SCB_DisableDCache(void) :用于禁止数据Cache。
; C! {4 U4 x3 J& w
; |/ n0 \1 `! A0 _8 |. ]        SCB_InvalidateDCache(void) :用于将数据Cache无效化,无效化的意思是将Cache Line标记为无效,等同于删除操作。这样Cache空间就都腾出来了,可以加载新的数据。
- ]7 Q  `  R  K) E, u# G/ @% s6 j% ?9 O
        SCB_CleanDCache(void):用于将数据Cache清除,清除的意思是将Cache Line中标记为dirty的数据写入到相应的存储区。
6 Y% m5 k3 c1 d5 b& Z- j/ c4 j3 y) K9 g" d  O' ]7 z  @. ?
        SCB_CleanInvalidateDCache(void) :此函数是前面两个函数SCB_InvalidateDCache和SCB_CleanDCache的二合一。将Cache Line中标记为dirty的数据写入到相应的存储区后,再将Cache Line标记为无效,表示删除。这样Cache空间就都腾出来了,可以加载新的数据。
4 Q- y4 S! V, r* r+ w! f% ?6 O
        SCB_InvalidateDCache_by_Addr(uint32_t *addr,int32_t dsize):可以指定地址和存储区大小,地址要32字节对齐,大小要是32字节的整数倍。用于将数据Cache无效化,无效化的意思是将Cache Line标记为无效,等同于删除操作。这样Cache空间就都腾出来了,可以加载新的数据。
6 y+ K: u" j. u0 s) v& ?
/ G+ n2 O/ Q5 g' j8 w/ K        SCB_CleanDCache_by_Addr(uint32_t *addr,int32_t dsize):可以指定地址和存储区大小,地址要32字节对齐,大小要是32字节的整数倍。用于将数据Cache清除,清除的意思是将Cache Line中标记为dirty的数据写入到相应的存储区。
' N2 H% u- Z% [: y/ s0 V) T1 S/ I# c5 A' q: k8 x, R* Y
        SCB_CleanInvalidateDCache_by_Addr(uint32_t *addr,int32_t dsize):可以指定地址和存储区大小,地址要32字节对齐,大小要是32字节的整数倍。将Cache Line中标记为dirty的数据写入到相应的存储区后,再将Cache Line标记为无效,表示删除。这样Cache空间就都腾出来了,可以加载新的数据。
2 s; W7 |$ t) [: G6 a1 u) p  x% i+ d6 N, Z# B+ y0 Z  K

3 F3 m# G' J5 O" e) t+ B
; T3 X+ o) w8 B. q  q二、MPU
: A7 s4 n: ~" N: _8 E6 A) d$ C9 J" w/ n2 P- R6 X& {
1、作用' O$ o9 C8 U) f! C3 u: I4 |
2 c% e5 X: H" d  ^8 i, Y2 _; I  z- V( w
        防止不受信任的应用程序访问受保护的内存区域; 防止用户应用程序破坏操作系统使用的数据;通过阻止任务访问其它任务的数据区;允许将内存区域定义为只读,以便保护重要数据;检测意外的内存访问。 简单的说就是内存保护、外设保护和代码访问保护。
2 P& B+ ~: T, m" }9 W7 H# V
: w) N+ B% E/ i# j  {' j# x) `5 d2、MPU可以配置的三种内存类型
6 r2 l7 ^" ~$ h* @) L/ i! y6 d; |+ R& R" P$ V4 j/ U
1)、Normal memory
8 |* n3 |0 N. }& W
! R" g1 j+ K6 m7 X8 H        CPU以最高效的方式加载和存储字节、半字和字,对于这种内存区,CPU的加载或存储不一定要按照程序列出的顺序执行。
% S3 G% i7 q' X; k! o. F$ ~# ~/ Z8 U) n" \7 Y7 v
2)、Device memory
- C! F4 F& {/ Q+ H/ R2 m# y9 b& o! u. b
        对于这种类型的内存区,加载和存储要严格按照次序进行,这样是为了确保寄存器按照正确顺序设置。1 t6 I5 k- o3 J

! g! Y- v- Z& C4 ^. ]7 Y4 @5 e3)、Strongly ordered memory
5 K$ s; O5 X# M! D6 U' p' z0 `9 Y6 H* [- X
        程序完全按照代码顺序执行,CPU需要等待当前的加载/存储指令执行完毕后才执行下一条指令。这样会导致性能下降。
5 y/ j. j* V2 E2 U" O
- k: x% k6 s9 p2 v/ q3、MPU的使用
* K( Y6 Q8 p0 Y* L8 o- h# k; l0 t# G/ o) ]
        MPU可以配置保护16个内存区域(这16个内存域是独立配置的),每个区域最小要求256字节,每个区域还可以配置为8个子区域。由于子区域一般都相同大小,这样每个子区域的大小就是32字节,正好跟Cache的Cache Line大小一样。
' [7 K! F  p' {5 p1 W
& X& B) W' I& ]( m. f  \        使用时把一段连续的内存区配置为一个MPU保护区域,然后再配置这个MPU保护区域的特性。比如128KB的DTCM、64KB的SRAM4、32MB的SDRAM。MPU保护区域的特性使用MPU_RASR寄存器来配置,描述如下:9 m) r- X* t- u! N8 e, d
! F  S  _2 j' L$ z
20191118111700740.png

/ J, K$ C7 v4 k/ M6 H0 i' j
3 c8 T+ F3 s! Q# P1)、XN:用于控制这个MPU保护区域能否执行程序代码。$ }" n- {" ~2 i# [! f, k" z

' h0 s( r+ B4 W2)、AP:用于控制这个MPU保护区域的特权级和非特权级的读写访问权限。7 U  C! d. T" X2 s* f
: W+ {" P& n. r7 L* y) W. u4 j) u
20191118111401294.png

- q( B9 W7 u' r! V2 B1 j
: l7 q1 d- h- o7 d% f5 i, j' l3)、TEX、C、B、S:H7支持4种Cache策略,这几位就是用来控制这个MPU保护区域使用哪一种。
: Q+ C, B2 A8 m1 A' C: z8 c' h* ?; q
# _8 ?; I; k$ o1 z
2019111811214681.png
/ S5 V0 j; c5 j! a! W  v# t1 ~

8 S$ @. M  O& dS位用于解决多总线或者多核访问的共享问题,一般不要开启。
& C& U$ S! P" X4 U5 O; l! W# b- j) z9 l$ K1 D* S& R: ]( B) j
4)、SRD:这个位用于控制内存区的子区域 ,使用的是bit[15:8],共计8个bit,一个bit控制一个子区域, 0表示使能此子区域, 1表示禁止。一般情况下,取值0x00,表示8个子区域都使能。
7 T2 S8 P/ a# d8 Y! f0 G2 W' D& ?5 m" w4 h
5)、SIZE:配置这个MPU保护区域的大小。5 f# _$ X* t' m1 }7 z3 Q
  P" b* Y7 v% P  [- }- i
三、HAL配置例程" J) a6 k- s( O8 I

5 M/ v5 x! E' x. y' u) Q. a
  1. //设置某个区域的MPU保护% t9 ?; C' A0 U
  2. //baseaddr:MPU保护区域的基址(首地址)
    & t$ X! A- A4 H* z' I. `
  3. //size:MPU保护区域的大小(必须是32的倍数,单位为字节),可设置的值参考:CORTEX_MPU_Region_Size
    6 {8 C+ K8 K1 I# d$ c6 m
  4. //rnum:MPU保护区编号,范围:0~7,最大支持8个保护区域,可设置的值参考:CORTEX_MPU_Region_Number
    & [5 C& R6 p6 G+ l9 J
  5. //ap:访问权限,访问关系如下:可设置的值参考:CORTEX_MPU_Region_Permission_Attributes( G* n- X8 i$ @+ J) |
  6. //0,无访问(特权&用户都不可访问)
    $ O8 ^2 k: W7 u  y
  7. //1,仅支持特权读写访问
    6 m0 |" e& C! }$ A3 t& R$ [( X& B
  8. //2,禁止用户写访问(特权可读写访问)
    , N' u! K2 w/ @" W
  9. //3,全访问(特权&用户都可访问)6 \/ |' Y: b# o1 C% H3 E2 y
  10. //4,无法预测(禁止设置为4!!!)
    ; l$ L' @  b& [- V7 d
  11. //5,仅支持特权读访问
    9 [5 y: U3 G# X' r  h
  12. //6,只读(特权&用户都不可以写)! D' S+ Y- F* _8 C
  13. //详见:STM32F7 Series Cortex-M7 processor programming manual.pdf,4.6节,Table 89.
    2 L# Q/ y7 Z  ~
  14. //sen:是否允许共用;0,不允许;1,允许; y4 P! B$ d1 r6 A) n% J
  15. //cen:是否允许catch;0,不允许;1,允许" p& _8 H) n, b5 D& D
  16. //返回值;0,成功.
    & R1 k. T5 h5 `0 Q* x/ T
  17. //    其他,错误.0 I9 `$ t7 L7 @4 {& d
  18. u8 MPU_Set_Protection(u32 baseaddr,u32 size,u32 rnum,u32 ap,u8 sen,u8 cen,u8 ben,u8 Tex)  T( P# n5 T  Y
  19. {4 C6 R  ^/ l% }0 ^3 G; W
  20.         MPU_Region_InitTypeDef MPU_Initure;
    : f, I( x% D* C& H
  21.         HAL_MPU_Disable();                                                                        //配置MPU之前先关闭MPU,配置完成以后在使能MPU- z4 i1 w1 G6 Y" d6 @& h

  22. " }  L, t: `1 U2 u# t, n# f
  23.         MPU_Initure.Enable=MPU_REGION_ENABLE;                                //使能该保护区域
    2 g2 N8 s% e/ K$ p! @
  24.         MPU_Initure.Number=rnum;                                            //设置保护区域
    7 z4 \4 A6 H3 n+ `* {) Q6 d
  25.         MPU_Initure.BaseAddress=baseaddr;                            //设置基址; e' Q, Z/ u9 \$ z
  26.         MPU_Initure.Size=size;                                                    //设置保护区域大小
    3 s( @) d9 |2 m) f3 _, a1 f4 Y
  27.         MPU_Initure.SubRegionDisable=0X00;                      //禁止子区域, d3 o1 Z8 S7 u* \4 |! J0 H0 [
  28.         MPU_Initure.TypeExtField=Tex;                           //设置类型扩展域$ H/ f+ V0 V' k5 |: Z
  29.         MPU_Initure.AccessPermission=(u8)ap;                            //设置访问权限,$ A$ |) v9 e  ^- f' R. E
  30.         MPU_Initure.DisableExec=MPU_INSTRUCTION_ACCESS_ENABLE;        //允许指令访问(允许读取指令)
    . x; H$ [: U0 K
  31.         MPU_Initure.IsShareable=sen;                            //是否允许共用% m- b$ T% l7 y
  32.         MPU_Initure.IsCacheable=cen;                            //是否允许cache
    ) K; z4 i2 T! v
  33.         MPU_Initure.IsBufferable=ben;                           //是否允许缓冲
    ' V' [, X% M6 X5 Z' o6 I( y8 A! o" V$ `" o
  34.         HAL_MPU_ConfigRegion(&MPU_Initure);                     //配置MPU5 p9 t/ ]8 X3 J9 J; O9 Q! Q
  35.         HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);                                //开启MPU3 `9 p* b" H6 ]- \  n
  36.     return 0;0 K4 u, W6 p* v* e
  37. }& i' Y5 s- {2 @, J* e
  38. 7 \9 I) Q: ]. z
  39. //设置需要保护的存储块" q4 b* }/ \* c2 |& q
  40. //必须对部分存储区域进行MPU保护,否则可能导致程序运行异常
    & j! l# _( ~( D3 `" X" J) v3 j& I. E
  41. //比如MCU屏不显示,摄像头采集数据出错等等问题...
    6 U, J2 X0 N/ H0 C
  42. void MPU_Memory_Protection(void)   //特意把SRAM4设置为不允许cache,使用DMA的变量可以放在这里。但要注意相应DMA能否访问SRAM4
      A2 C& E. L: f! K! n4 l
  43. {' s1 b3 A0 l2 g% ^( ], Q
  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,允许缓冲
    8 C" t1 v! y& M4 @. q; M
  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字节+ P9 W6 @1 L/ P) q# s1 u
  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,允许缓冲* A3 I$ D! }" `3 k: V
  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,允许缓冲
    : `/ q# N' F- s4 L. K5 d7 Y
  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,禁止缓冲
    7 M$ e- S1 F# T4 x1 d- h
  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+ y& C7 t: Y! v
  50. }
复制代码

! R5 J$ v% w" B' w四、其他
3 u0 h) k  ~& H: R" |8 w; t* J! U; n, _9 a- ?
        值得一提的是,LTDC也是直接从RAM拿数据的,如果你使用了GUI(比如EMWIN),你的显示数据可能会暂存在Cache,而LTDC直接从RAM拿数据,就可能造成画面撕裂、重影、斑点之类的问题。解决方法是,把显存设置成透写。
9 a7 T) H4 Q+ R$ ]2 p
0 Z; N+ s! ?+ C. r2 M        从下面的图可以看到,Cache是在M7那个框里面的。而框外面的外设都可以直接与RAM交换数据,因此使用外设操作数据时都要考虑一下Cache的影响,不然异常可能难以预料。# w7 I6 J, f% _% a& g
2 y( p, p1 E/ r: X( @
20191119145451828.png
' s2 J( ]: y1 a. `" I# z# b

- P: a; i! U  C+ ]( G
( k" j5 F. }8 `) Q3 e- ?
收藏 评论0 发布时间:2021-12-18 17:42

举报

0个回答

所属标签

相似分享

官网相关资源

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