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

【经验分享】STM32H7的Cache和MPU

[复制链接]
STMCU小助手 发布时间:2021-12-18 17:42
一、Cache
7 u1 O6 w, ?6 O) n7 d! t  ~9 E# v. X, }. v! ]
1、介绍
" g8 {9 n; s3 R! {( i2 q
1 j8 ^3 p5 t; J0 A$ a2 i        Cache又分数据缓存D-Cache和指令缓冲I-Cache,STM32H7的数据缓存和指令缓存大小都是16KB。STM32H7主频是400MHz,除了TCM和Cache以400MHz工作,其它AXI SRAM,SRAM1,SRAM2等都是以200MHz工作。数据缓存D-Cache就是解决CPU加速访问SRAM。
* b3 O1 D9 A# z7 C
# T$ f7 e0 `: G        如果每次CPU要读写SRAM区的数据,都能够在Cache里面进行,自然是最好的,实现了200MHz到400MHz的飞跃,实际是做不到的,因为数据Cache只有16KB大小,总有用完的时候。, |1 G, A5 m+ v* A, [4 w% e- I/ U
/ I7 d& B: C/ ^8 U8 K
2、操作,分为读操作和写操作
+ X) s, S7 }  S: }4 W
. }* \+ G, G5 S7 I        读操作: 如果CPU要读取的SRAM区数据在Cache中已经加载好,这就叫读命中(Cache hit),如果Cache里面没有怎么办,这就是所谓的读Cache Miss。& a3 t- z, N  D1 f

' s6 R* ^9 t$ H1 C* ?) A       写操作: 如果CPU要写的SRAM区数据在Cache中已经开辟了对应的区域(专业词汇叫Cache Line,以32字节为单位),这就叫写命中(Cache hit),如果Cache里面没有开辟对应的区域怎么办,这就是所谓的写Cache Miss。4 ]) _& i" U" j! ^0 A( t

: \( j: M& I, x, E) b  g0 m3、H7支持的Cache策略,共4种
# a" ^2 ^5 E* `4 u; E: C6 V' e+ i7 T) b, w+ M& b+ |! @: I. I
20191118102334153.png

6 I* m8 {! `  a$ c; s  \- E: Y/ m. ^. U
<回写:如果Cache中有,写数据只写到Cache,不写到RAM。>, B9 u& w! s7 y3 b( m* S8 ]/ ^( {- P
  y! L) s. ~8 q
<透写:如果Cache中有,写数据也要同时写到Cache和RAM。>) _% }2 s5 T; T/ D4 y7 D3 L9 s
/ J( y, ]! Z( E+ W) s0 i9 B8 z/ |
<write allocate:写数据时,如果Cache中没有,那么就要在Cache中开辟一个空间,把数据写入Cache,同时把RAM中的相邻数据加载进来填充Cache。>8 ]7 @/ ~7 |# [9 ?3 h4 Y6 c

( j" N  S4 D, A, C0 C7 L" h  s1 S4 a6 C. ]<no write allocate:写数据时,如果Cache中没有,那么把数据直接写入RAM。>
$ c6 I6 R! S1 I, M9 O. v5 G8 J5 s/ t1 R. {
<read allocate:读数据时,如果Cache中没有,那么就要在Cache中开辟一个空间,把数据从RAM中加载进来,后续的读操作,就可以直接从Cache中读取了。>
* ], X' A2 ]/ e# S. C4 t1 P
$ b+ P2 T/ r! u6 y3 V<no read allocate:读数据时,如果Cache中没有,那么直接从RAM中读。>, ^: w/ S$ B3 ^: z2 j3 B5 Z% z
* n% c/ |7 |4 n# ]+ w8 \  G
4、风险
2 z# f* J& R0 s& O# r! N8 g( e# E9 {2 l2 |! X! z2 K/ Y
20191118112913153.png

6 Q3 |( B( e6 B4 u' x$ i
. b$ Y. u/ e2 Z8 R0 c  P) x8 f2 V        从上面的图就看出来使用Cache的风险,因为DMA是直接与SRAM交换数据的,而CPU与SRAM之间隔了一个Cache,如果DMA更新了某个数据到SRAM,CPU要去访问,而恰好Cache中有,那么CPU就不会去SRAM中拿,就会拿到Cache中已经过时的数据。因此使用了DMA的内存区要配置为无Cache或者拿数据前清一次Cache。
# I8 J$ r/ P& I9 e, Y4 G1 s
/ b. \% H2 j" n& |; M& m) [5、相关函数
0 r" x7 o- f9 k
  G/ n( S; k1 n: r1 u: s! @        SCB_EnableICache(void) :用于使能指令Cache,系统上电后优先初始化即可。8 K5 Q. Y! b6 X, b5 w) R
* A; l: `% \6 A
        SCB_DisableICache(void) :用于禁止指令Cache。
* G6 V7 \0 R' s+ ?) q$ ^& @
, w( E8 D2 J6 `- l' a. d' u        SCB_InvalidateICache(void) :用于将指令Cache无效化,无效化的意思是将Cache Line标记为无效,等同于删除操作。这样Cache空间就都腾出来了,可以加载新的指令。
: L. ?1 \5 N. s/ V4 @/ V  S8 h' u9 w: R) x) u* ]
        SCB_EnableDCache(void) :用于使能数据Cache,系统上电后优先初始化即可。- I( k' ?) V- @. m& I& v. X4 `
- D! R( g+ Q5 B" s4 ]+ w3 c
        SCB_DisableDCache(void) :用于禁止数据Cache。: Q7 N0 u' S" H/ v) h9 k

% d- B! |9 ~2 V        SCB_InvalidateDCache(void) :用于将数据Cache无效化,无效化的意思是将Cache Line标记为无效,等同于删除操作。这样Cache空间就都腾出来了,可以加载新的数据。
/ M2 F7 F% _, j5 `9 K- y
2 X4 L+ R# Y5 Y& R' ~        SCB_CleanDCache(void):用于将数据Cache清除,清除的意思是将Cache Line中标记为dirty的数据写入到相应的存储区。
6 N( l5 F! ^, v# a$ i; K6 I
+ Z& |) K( \# S6 [        SCB_CleanInvalidateDCache(void) :此函数是前面两个函数SCB_InvalidateDCache和SCB_CleanDCache的二合一。将Cache Line中标记为dirty的数据写入到相应的存储区后,再将Cache Line标记为无效,表示删除。这样Cache空间就都腾出来了,可以加载新的数据。! k! p+ B) F* p' F) h; l
7 Y5 p4 g. L% \' ^5 O8 t, C- {7 q
        SCB_InvalidateDCache_by_Addr(uint32_t *addr,int32_t dsize):可以指定地址和存储区大小,地址要32字节对齐,大小要是32字节的整数倍。用于将数据Cache无效化,无效化的意思是将Cache Line标记为无效,等同于删除操作。这样Cache空间就都腾出来了,可以加载新的数据。
$ k& ^6 B2 A/ k& Z; {: l' l& e! s9 o* w; m. O$ P2 J1 Z- r4 B  k
        SCB_CleanDCache_by_Addr(uint32_t *addr,int32_t dsize):可以指定地址和存储区大小,地址要32字节对齐,大小要是32字节的整数倍。用于将数据Cache清除,清除的意思是将Cache Line中标记为dirty的数据写入到相应的存储区。
: e9 l& S8 @7 m, Y" h) Z, F! I4 K+ B0 j0 A/ E5 W1 S( X
        SCB_CleanInvalidateDCache_by_Addr(uint32_t *addr,int32_t dsize):可以指定地址和存储区大小,地址要32字节对齐,大小要是32字节的整数倍。将Cache Line中标记为dirty的数据写入到相应的存储区后,再将Cache Line标记为无效,表示删除。这样Cache空间就都腾出来了,可以加载新的数据。* S/ M+ _" |( {
* M( p+ C" B2 |" B
( n1 [! |  u. k" ]
0 k' p' q: T1 t" m) ^& c& H
二、MPU" _% A- ]: |  f& b/ V, }

9 i! A' I& n" V8 ~, J* f0 G0 C1、作用- p1 \/ g* l- {" o6 M$ `

) A  w/ x1 Q/ K. X        防止不受信任的应用程序访问受保护的内存区域; 防止用户应用程序破坏操作系统使用的数据;通过阻止任务访问其它任务的数据区;允许将内存区域定义为只读,以便保护重要数据;检测意外的内存访问。 简单的说就是内存保护、外设保护和代码访问保护。8 T( m9 v/ Z  v" d- a. w

8 g6 ]- `3 \5 u8 h" o2、MPU可以配置的三种内存类型  Y: k. f! ~' [1 V( n5 p* x  V

$ Y: k8 d4 K7 e0 m; A1)、Normal memory7 `! Q) W0 j: ]% b/ K+ j

, D; o/ j/ v2 h% G+ ~: J4 s8 i        CPU以最高效的方式加载和存储字节、半字和字,对于这种内存区,CPU的加载或存储不一定要按照程序列出的顺序执行。
1 s9 t  t* p! m+ `
9 V7 R6 d6 m" s/ x2)、Device memory
/ k) y: w) a3 [9 e* K
, n, ]3 _# d0 Q  M        对于这种类型的内存区,加载和存储要严格按照次序进行,这样是为了确保寄存器按照正确顺序设置。  S+ ]9 T: _1 u0 D. G4 ~1 t

0 b% j" s: z& l  ~3)、Strongly ordered memory
5 `9 [! j; Z# z8 O" `
# Y% r; f  M$ P9 e' @4 `% z+ n0 p: Y        程序完全按照代码顺序执行,CPU需要等待当前的加载/存储指令执行完毕后才执行下一条指令。这样会导致性能下降。% L8 \+ ?( l) e6 O* I/ m9 q

$ x  z# O3 k; P$ @$ n8 R* L3、MPU的使用
" s7 ^' N8 ?/ h' x6 V
- j5 `' L5 a( b        MPU可以配置保护16个内存区域(这16个内存域是独立配置的),每个区域最小要求256字节,每个区域还可以配置为8个子区域。由于子区域一般都相同大小,这样每个子区域的大小就是32字节,正好跟Cache的Cache Line大小一样。( _  a, @# F" l6 x# I0 |

! {* r4 |# H8 c3 P; x  `, o, l        使用时把一段连续的内存区配置为一个MPU保护区域,然后再配置这个MPU保护区域的特性。比如128KB的DTCM、64KB的SRAM4、32MB的SDRAM。MPU保护区域的特性使用MPU_RASR寄存器来配置,描述如下:% y8 |+ [; i8 L/ a6 p
, t; \# m( N2 j, u2 L
20191118111700740.png

% {; K7 G. @. x" M+ `- v
+ i* O: h; `: i6 j1)、XN:用于控制这个MPU保护区域能否执行程序代码。
/ `6 ^( P  g, M5 L7 Y3 s& Q- u- d; k  |5 k+ N% V
2)、AP:用于控制这个MPU保护区域的特权级和非特权级的读写访问权限。
# m& ], \) G2 g
) L# R; g' B2 q* a! @# E; p
20191118111401294.png
, E' F% W( z, d8 e9 f/ X
1 o: X# h% ?4 R3 D! w
3)、TEX、C、B、S:H7支持4种Cache策略,这几位就是用来控制这个MPU保护区域使用哪一种。
9 q' [% B" r* _+ i; `/ C+ `1 ]( u1 L0 w( u
2019111811214681.png
8 Q- C9 B7 c. x8 R6 b# _5 w2 R

( N  A* \- _) g1 T, ]  J. F% wS位用于解决多总线或者多核访问的共享问题,一般不要开启。
! j0 L9 V4 C2 }* k# v) h& e5 j1 Z( L; k. {/ Y' C. x0 h7 R# r
4)、SRD:这个位用于控制内存区的子区域 ,使用的是bit[15:8],共计8个bit,一个bit控制一个子区域, 0表示使能此子区域, 1表示禁止。一般情况下,取值0x00,表示8个子区域都使能。
0 u5 ]$ j, C0 e/ O% G0 Z3 r8 t4 w
+ y0 Z  Y. e/ g8 I; m5)、SIZE:配置这个MPU保护区域的大小。
2 f! m& p, D: o8 s  R, G
6 s1 |( H. i5 T) @2 Y! O9 \8 ^  N5 u三、HAL配置例程
* ~2 A% N. r7 f- [7 `* ]; ?; J/ A$ W; V, s
  1. //设置某个区域的MPU保护* P! Q6 @: T# J
  2. //baseaddr:MPU保护区域的基址(首地址). _( t$ X2 s) _; W( f& C
  3. //size:MPU保护区域的大小(必须是32的倍数,单位为字节),可设置的值参考:CORTEX_MPU_Region_Size& w9 e/ M4 }( l" A/ m) B  ?( Z
  4. //rnum:MPU保护区编号,范围:0~7,最大支持8个保护区域,可设置的值参考:CORTEX_MPU_Region_Number
      j. e& V% S# F5 R
  5. //ap:访问权限,访问关系如下:可设置的值参考:CORTEX_MPU_Region_Permission_Attributes
    # _0 S+ S/ h$ b+ C; o% m" f0 R5 T
  6. //0,无访问(特权&用户都不可访问)
    * r  O  K0 D: e# W3 O0 t: {
  7. //1,仅支持特权读写访问! s! l/ P/ `6 y' ?6 z
  8. //2,禁止用户写访问(特权可读写访问)$ ^5 |9 a) S7 {  `. b
  9. //3,全访问(特权&用户都可访问), v! y1 c( p& H" @: d8 M$ A
  10. //4,无法预测(禁止设置为4!!!)
    7 s# ?/ Y' _1 I( x2 `  A) f
  11. //5,仅支持特权读访问' K; r! A' @: X$ X# e& h; M' ^
  12. //6,只读(特权&用户都不可以写)+ c- m: m$ F: z4 [' f, U
  13. //详见:STM32F7 Series Cortex-M7 processor programming manual.pdf,4.6节,Table 89.: g4 L  m* d# ^3 t+ y$ ^
  14. //sen:是否允许共用;0,不允许;1,允许
    , n6 V7 k( Q: [  v4 i( ~, D
  15. //cen:是否允许catch;0,不允许;1,允许
    : b. K1 l" H8 [8 c* V
  16. //返回值;0,成功.
    5 r8 I( `2 t- u  |. m
  17. //    其他,错误.+ D0 D/ T$ r) A6 c
  18. u8 MPU_Set_Protection(u32 baseaddr,u32 size,u32 rnum,u32 ap,u8 sen,u8 cen,u8 ben,u8 Tex)
    8 L2 F! W- a# R, l  V- m0 [
  19. {
    9 l; Q& G# y: }. ]/ Y' X9 T- w$ ]  i
  20.         MPU_Region_InitTypeDef MPU_Initure;
    * k/ U5 b3 b( k2 F
  21.         HAL_MPU_Disable();                                                                        //配置MPU之前先关闭MPU,配置完成以后在使能MPU3 E* A3 U5 @" K% j
  22. 9 X# v/ |- t# r# H5 {4 {3 Q( }
  23.         MPU_Initure.Enable=MPU_REGION_ENABLE;                                //使能该保护区域 + m( H& U* j, z& K& b  z
  24.         MPU_Initure.Number=rnum;                                            //设置保护区域; f. b" G* I! l( h/ r. C
  25.         MPU_Initure.BaseAddress=baseaddr;                            //设置基址
    7 R  d& e! J7 ~; V, Y9 a
  26.         MPU_Initure.Size=size;                                                    //设置保护区域大小2 V2 f, l! m% G( T( T) _4 o! _
  27.         MPU_Initure.SubRegionDisable=0X00;                      //禁止子区域; I8 Z3 i, x0 x0 l/ \9 ~
  28.         MPU_Initure.TypeExtField=Tex;                           //设置类型扩展域
    5 s  t9 W: f+ q9 @/ P! j& s0 U; D, v
  29.         MPU_Initure.AccessPermission=(u8)ap;                            //设置访问权限,
    * E; ^) q; H/ l. f( X4 U- o
  30.         MPU_Initure.DisableExec=MPU_INSTRUCTION_ACCESS_ENABLE;        //允许指令访问(允许读取指令)1 _% q& ?7 g* ]
  31.         MPU_Initure.IsShareable=sen;                            //是否允许共用
    : O3 E0 m% B, y8 r/ x: O2 w& r. o
  32.         MPU_Initure.IsCacheable=cen;                            //是否允许cache
    , ^6 f! `9 g% r3 o( S8 N  X7 r
  33.         MPU_Initure.IsBufferable=ben;                           //是否允许缓冲- g- }. E5 K5 Z. Z  ]
  34.         HAL_MPU_ConfigRegion(&MPU_Initure);                     //配置MPU
    & |8 L  l. L- M% O4 h  J9 Y
  35.         HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);                                //开启MPU
    4 h( Q; p1 @$ U: k: b
  36.     return 0;) P* p6 ~$ p0 r
  37. }6 @& _- z5 _7 O- I, M

  38. 2 F' u! u% @& Z  x5 O0 h
  39. //设置需要保护的存储块
    # ~, \# M: ]0 z) I* @
  40. //必须对部分存储区域进行MPU保护,否则可能导致程序运行异常
    2 p( }' s$ S1 F2 j( Y! ^
  41. //比如MCU屏不显示,摄像头采集数据出错等等问题...1 z9 y1 Y' d7 N8 I& `
  42. void MPU_Memory_Protection(void)   //特意把SRAM4设置为不允许cache,使用DMA的变量可以放在这里。但要注意相应DMA能否访问SRAM4: U% ?9 i# Y' B3 {( i  c- ~' {
  43. {
    5 K* _# |. o6 [" r. ?, t, [. q, j& ]" R
  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,允许缓冲
    2 ~% B$ Z8 T  ^: j. h/ C6 L$ O
  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字节
    $ \8 U( l$ _2 Z3 }+ v6 A' s
  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: `' h, s; M9 E( K5 U: 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,允许缓冲
    2 h( l2 M% u. M5 Q! k3 P( I% k
  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,禁止缓冲
    ( O/ K9 S$ g! ?4 e
  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,允许缓冲- X! Q! Z9 }8 E
  50. }
复制代码
' n) @$ }4 V* s  G+ G3 I- \# H
四、其他
$ ^  ^- L- H+ I3 z! T0 M+ `) N" E  d
        值得一提的是,LTDC也是直接从RAM拿数据的,如果你使用了GUI(比如EMWIN),你的显示数据可能会暂存在Cache,而LTDC直接从RAM拿数据,就可能造成画面撕裂、重影、斑点之类的问题。解决方法是,把显存设置成透写。) g% R# @4 H" K1 e" H! r

$ i$ F/ D. I) u/ p6 ^" E* y7 V/ r        从下面的图可以看到,Cache是在M7那个框里面的。而框外面的外设都可以直接与RAM交换数据,因此使用外设操作数据时都要考虑一下Cache的影响,不然异常可能难以预料。2 a0 F  V( _4 a
8 f9 _/ y9 R" L8 s% x
20191119145451828.png

% g! k, v; N! O# o0 H3 E2 q" p3 T! _/ ?: P
# T% S. {" a, ]+ r+ y: F
收藏 评论0 发布时间:2021-12-18 17:42

举报

0个回答

所属标签

相似技术帖

官网相关资源

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