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

【经验分享】STM32H7的Cache和MPU

[复制链接]
STMCU小助手 发布时间:2021-12-18 17:42
一、Cache
  J  y: t4 O' |; R" U  U/ \2 Q# M3 Z+ G( g+ L
1、介绍
. j! d) N% c& f( B5 M$ D  ]7 _* I2 ]: M
        Cache又分数据缓存D-Cache和指令缓冲I-Cache,STM32H7的数据缓存和指令缓存大小都是16KB。STM32H7主频是400MHz,除了TCM和Cache以400MHz工作,其它AXI SRAM,SRAM1,SRAM2等都是以200MHz工作。数据缓存D-Cache就是解决CPU加速访问SRAM。  ^% v( D0 p1 C- k
5 `9 Q2 N$ Q6 p( A% o# C/ U
        如果每次CPU要读写SRAM区的数据,都能够在Cache里面进行,自然是最好的,实现了200MHz到400MHz的飞跃,实际是做不到的,因为数据Cache只有16KB大小,总有用完的时候。% Y: \! r+ l3 a0 K' Q

8 }3 `3 Y* P8 X( F7 K2、操作,分为读操作和写操作
8 g% i3 S; Z1 x" T8 v+ _8 Q; v: @2 \' }
        读操作: 如果CPU要读取的SRAM区数据在Cache中已经加载好,这就叫读命中(Cache hit),如果Cache里面没有怎么办,这就是所谓的读Cache Miss。
  K- _1 |5 F  ~0 ^0 N" n$ B4 k# ^( _  d+ g
       写操作: 如果CPU要写的SRAM区数据在Cache中已经开辟了对应的区域(专业词汇叫Cache Line,以32字节为单位),这就叫写命中(Cache hit),如果Cache里面没有开辟对应的区域怎么办,这就是所谓的写Cache Miss。: k/ h% Q2 _+ t8 c3 P
$ v) e- ]( s# e1 o8 `8 y
3、H7支持的Cache策略,共4种
( b' P- m& T, t) W6 J0 B7 Q( C( I1 }
* H% h' e5 ^7 ^7 u/ @/ ~
20191118102334153.png
* b" D3 q, g  v: b7 x, B0 c

5 c: Z+ j% `- B5 r7 |) i<回写:如果Cache中有,写数据只写到Cache,不写到RAM。>
0 @) r4 b& n8 x. v5 a! l# H8 G3 X7 W& A* N5 u: A0 q2 S
<透写:如果Cache中有,写数据也要同时写到Cache和RAM。>
6 C/ {) I4 z( X6 G
3 G* E3 H$ R  @8 t  M0 X# E) d<write allocate:写数据时,如果Cache中没有,那么就要在Cache中开辟一个空间,把数据写入Cache,同时把RAM中的相邻数据加载进来填充Cache。>
; j6 ~/ ]8 A4 P  V0 n
9 S& @9 a: N6 y, [, b5 B4 ^; T; ]: c<no write allocate:写数据时,如果Cache中没有,那么把数据直接写入RAM。>- Y3 x7 W% ]% \$ `

& ^0 B4 e2 M$ W<read allocate:读数据时,如果Cache中没有,那么就要在Cache中开辟一个空间,把数据从RAM中加载进来,后续的读操作,就可以直接从Cache中读取了。>
! O: M7 L' z) F+ W* A1 X- }* u0 P
7 c0 m4 z0 j0 t: E<no read allocate:读数据时,如果Cache中没有,那么直接从RAM中读。>7 h2 F6 q! x; V  C2 l- ~1 d

- P; o) \- }7 z+ s4、风险$ l1 w+ q6 K0 ^5 @) K

0 j/ r* x. l. ?$ o4 r  p. @! k
20191118112913153.png
- [, z! T# W' \1 S) h9 x' c  y2 R
" F  J3 e0 |" x* J8 i' g; `
        从上面的图就看出来使用Cache的风险,因为DMA是直接与SRAM交换数据的,而CPU与SRAM之间隔了一个Cache,如果DMA更新了某个数据到SRAM,CPU要去访问,而恰好Cache中有,那么CPU就不会去SRAM中拿,就会拿到Cache中已经过时的数据。因此使用了DMA的内存区要配置为无Cache或者拿数据前清一次Cache。
& \8 u! ~7 Q9 [% u6 w) G
2 Q% D: ?( H7 S6 M5、相关函数
2 h& H( c7 H2 F; D
2 Y( z, `8 i( S* _8 |4 E        SCB_EnableICache(void) :用于使能指令Cache,系统上电后优先初始化即可。
  n5 c0 B: {9 |2 E8 a
1 U1 V7 r& j3 i% f0 v        SCB_DisableICache(void) :用于禁止指令Cache。
4 x- h# V1 U& ~9 ]) d& F# P/ f
$ M7 N( J) p2 k* ~. M% u        SCB_InvalidateICache(void) :用于将指令Cache无效化,无效化的意思是将Cache Line标记为无效,等同于删除操作。这样Cache空间就都腾出来了,可以加载新的指令。  ~6 W5 {4 o" e$ Y* z) h' p
% o8 q6 p# G' I7 E
        SCB_EnableDCache(void) :用于使能数据Cache,系统上电后优先初始化即可。( `. t# c4 ~; f
9 O# ~4 K* n7 G; Z0 y
        SCB_DisableDCache(void) :用于禁止数据Cache。! q% P" ^3 r2 s2 B- C7 E
, E) e. |; m) [! q( ]' _/ f  C; H
        SCB_InvalidateDCache(void) :用于将数据Cache无效化,无效化的意思是将Cache Line标记为无效,等同于删除操作。这样Cache空间就都腾出来了,可以加载新的数据。& ^/ c6 K) f3 g4 J; g% g% v& D
2 Y' N' @( i$ g& v* D1 s% ]
        SCB_CleanDCache(void):用于将数据Cache清除,清除的意思是将Cache Line中标记为dirty的数据写入到相应的存储区。
# _( ~$ T5 D) R; R' ^& W+ }$ n% O; C; E5 m5 R
        SCB_CleanInvalidateDCache(void) :此函数是前面两个函数SCB_InvalidateDCache和SCB_CleanDCache的二合一。将Cache Line中标记为dirty的数据写入到相应的存储区后,再将Cache Line标记为无效,表示删除。这样Cache空间就都腾出来了,可以加载新的数据。7 U9 P9 m  |3 ~- [3 a9 |% ]5 x( e1 v
7 ~- `- x6 x5 Y/ G: c" F) P
        SCB_InvalidateDCache_by_Addr(uint32_t *addr,int32_t dsize):可以指定地址和存储区大小,地址要32字节对齐,大小要是32字节的整数倍。用于将数据Cache无效化,无效化的意思是将Cache Line标记为无效,等同于删除操作。这样Cache空间就都腾出来了,可以加载新的数据。
6 F5 X* r8 T' s! ^' Z+ A1 q& O/ ^, b
        SCB_CleanDCache_by_Addr(uint32_t *addr,int32_t dsize):可以指定地址和存储区大小,地址要32字节对齐,大小要是32字节的整数倍。用于将数据Cache清除,清除的意思是将Cache Line中标记为dirty的数据写入到相应的存储区。6 |% m- I6 s' M+ Z3 a5 a
3 p& D! N+ R) D/ v: A
        SCB_CleanInvalidateDCache_by_Addr(uint32_t *addr,int32_t dsize):可以指定地址和存储区大小,地址要32字节对齐,大小要是32字节的整数倍。将Cache Line中标记为dirty的数据写入到相应的存储区后,再将Cache Line标记为无效,表示删除。这样Cache空间就都腾出来了,可以加载新的数据。! n3 T$ C) e- V+ R* }- y1 Z8 j
- {0 P' ?4 m; A
* ?. W5 \+ w0 u9 O: [. p
; r4 N% n: s5 I
二、MPU" U' k+ n/ V9 F) }  P8 F5 \
% [6 y8 l) _0 ]# M+ `
1、作用
3 }" A: p* [2 n  A! {. A
' s* d! g0 E8 v" K        防止不受信任的应用程序访问受保护的内存区域; 防止用户应用程序破坏操作系统使用的数据;通过阻止任务访问其它任务的数据区;允许将内存区域定义为只读,以便保护重要数据;检测意外的内存访问。 简单的说就是内存保护、外设保护和代码访问保护。( R$ X) B3 k& `5 f1 P$ w: w1 u3 b

# b+ [" O' S- y8 p2、MPU可以配置的三种内存类型
8 e. p5 f: j% D, _! {* s8 ~1 y( w( N! W7 A
1)、Normal memory
6 m  {/ C! g% h& R" \# N4 R1 b/ ]2 q- w! b/ M8 Q4 u: e/ y+ @
        CPU以最高效的方式加载和存储字节、半字和字,对于这种内存区,CPU的加载或存储不一定要按照程序列出的顺序执行。
  h' Q9 L: A( T" m" x' g. b( V& \4 \1 V# L$ L
2)、Device memory
. S" c9 c$ t! g$ ?/ u6 V
3 R. i) s. E0 U% ?$ F4 E* J        对于这种类型的内存区,加载和存储要严格按照次序进行,这样是为了确保寄存器按照正确顺序设置。
- ?, D9 n, K9 [: a* m( y
6 [0 F" R5 H4 o3)、Strongly ordered memory
0 C2 V9 j# g4 a8 f  E! p. @
# j, j" L' ^3 C/ _7 i        程序完全按照代码顺序执行,CPU需要等待当前的加载/存储指令执行完毕后才执行下一条指令。这样会导致性能下降。
; e3 O3 e/ u8 c. u6 H" T( ~8 ~0 Q: }+ v/ W# }' j2 [/ G# T) g
3、MPU的使用
$ k" s1 _8 \# T7 n
7 U( a/ h, a  z        MPU可以配置保护16个内存区域(这16个内存域是独立配置的),每个区域最小要求256字节,每个区域还可以配置为8个子区域。由于子区域一般都相同大小,这样每个子区域的大小就是32字节,正好跟Cache的Cache Line大小一样。
" q( J7 H6 ]' d5 }+ f2 Z& z- x0 j" g, j
        使用时把一段连续的内存区配置为一个MPU保护区域,然后再配置这个MPU保护区域的特性。比如128KB的DTCM、64KB的SRAM4、32MB的SDRAM。MPU保护区域的特性使用MPU_RASR寄存器来配置,描述如下:
8 t, y5 s: o  ~; W5 [
( U& E) M# G, e; q$ L
20191118111700740.png
" E8 T7 ~# e) e( f6 R+ o2 Y

0 h8 r2 p6 I, ?/ N" K5 f5 f- O1)、XN:用于控制这个MPU保护区域能否执行程序代码。, O: X8 G: O: U2 M+ ~7 K
; C' U7 C$ E* ?& i8 A3 x- x+ q3 ]
2)、AP:用于控制这个MPU保护区域的特权级和非特权级的读写访问权限。0 v- O9 W% v' m5 B

( O7 r3 A' ?, ?' t4 @
20191118111401294.png
9 {6 l+ g$ L5 w
; C- H; k. i$ H% E. h
3)、TEX、C、B、S:H7支持4种Cache策略,这几位就是用来控制这个MPU保护区域使用哪一种。2 I" D: }5 r. R) Y
4 _* o) R$ ~; `
2019111811214681.png
4 T7 W" n1 F7 @

$ H. V& q2 E; v' dS位用于解决多总线或者多核访问的共享问题,一般不要开启。
& G& ^4 Y0 G3 T; b& a5 {3 y8 E8 u
4)、SRD:这个位用于控制内存区的子区域 ,使用的是bit[15:8],共计8个bit,一个bit控制一个子区域, 0表示使能此子区域, 1表示禁止。一般情况下,取值0x00,表示8个子区域都使能。
0 a0 n6 Z4 \3 ^( r# s0 J- ?9 m. v+ R9 H0 h: N& X
5)、SIZE:配置这个MPU保护区域的大小。
1 \$ w3 K5 b, U8 c0 O. r3 v) N6 }. J1 S  R' S1 y+ }( i0 x
三、HAL配置例程& _: X/ \$ L; @0 [
$ p( x) }- }# X& U* G
  1. //设置某个区域的MPU保护
    $ N4 H8 j( {3 [; R% u# O
  2. //baseaddr:MPU保护区域的基址(首地址): w% {5 M! j' Q* Z
  3. //size:MPU保护区域的大小(必须是32的倍数,单位为字节),可设置的值参考:CORTEX_MPU_Region_Size
    * c) v. j# ^. `
  4. //rnum:MPU保护区编号,范围:0~7,最大支持8个保护区域,可设置的值参考:CORTEX_MPU_Region_Number
    2 G" s9 h/ M5 n, b
  5. //ap:访问权限,访问关系如下:可设置的值参考:CORTEX_MPU_Region_Permission_Attributes1 r. c0 a5 ]7 U
  6. //0,无访问(特权&用户都不可访问)
    3 M! M" B3 L3 [$ \) S+ n  `, o% O
  7. //1,仅支持特权读写访问- f4 A/ H6 I/ j) ^1 q7 O( d# h
  8. //2,禁止用户写访问(特权可读写访问)
    9 `" c- g* {) H3 [' c! n! B
  9. //3,全访问(特权&用户都可访问)
    7 I& [: t9 p) J: a) l4 q4 @0 U
  10. //4,无法预测(禁止设置为4!!!)
    - f4 l2 N& l7 l
  11. //5,仅支持特权读访问' O7 m# q- |: A8 d4 G" y  X; P& k
  12. //6,只读(特权&用户都不可以写)/ v( A1 L9 F8 K' n$ M, B
  13. //详见:STM32F7 Series Cortex-M7 processor programming manual.pdf,4.6节,Table 89.% }4 K  H  t$ W, r
  14. //sen:是否允许共用;0,不允许;1,允许3 h0 S" L. o0 u
  15. //cen:是否允许catch;0,不允许;1,允许, c3 g8 d$ k6 b
  16. //返回值;0,成功.7 ]$ L, \! N. C! A0 p- \
  17. //    其他,错误.1 L$ }+ j  N' T4 c. P' \7 h
  18. u8 MPU_Set_Protection(u32 baseaddr,u32 size,u32 rnum,u32 ap,u8 sen,u8 cen,u8 ben,u8 Tex)- |+ y, I( j! r- g  ], v, C
  19. {
    7 A8 r+ a. ]+ B8 d4 K+ G2 m! ~
  20.         MPU_Region_InitTypeDef MPU_Initure;' B( _1 u. i  R
  21.         HAL_MPU_Disable();                                                                        //配置MPU之前先关闭MPU,配置完成以后在使能MPU- _, q, k4 T& t& R3 F, r+ q

  22. - A  Q3 M7 i8 a2 u
  23.         MPU_Initure.Enable=MPU_REGION_ENABLE;                                //使能该保护区域 + ^9 _; I7 J1 x, P3 T$ ~
  24.         MPU_Initure.Number=rnum;                                            //设置保护区域
    7 o0 H+ V  I: H( y
  25.         MPU_Initure.BaseAddress=baseaddr;                            //设置基址
    ) W$ H- B/ T" p- R# T
  26.         MPU_Initure.Size=size;                                                    //设置保护区域大小
    & c8 o; e, o% E8 N5 z' I  s
  27.         MPU_Initure.SubRegionDisable=0X00;                      //禁止子区域
    3 F, M3 G! S# S9 r( D
  28.         MPU_Initure.TypeExtField=Tex;                           //设置类型扩展域" V6 x2 ^- x: j8 y
  29.         MPU_Initure.AccessPermission=(u8)ap;                            //设置访问权限,! y' y, k1 F  D0 {- `  N0 H8 n
  30.         MPU_Initure.DisableExec=MPU_INSTRUCTION_ACCESS_ENABLE;        //允许指令访问(允许读取指令)/ G0 j% ]5 O! ^9 P/ t1 S
  31.         MPU_Initure.IsShareable=sen;                            //是否允许共用
    ; P8 |' |0 {: W/ G- j) K
  32.         MPU_Initure.IsCacheable=cen;                            //是否允许cache
    ; s+ f4 r4 `6 a( Y
  33.         MPU_Initure.IsBufferable=ben;                           //是否允许缓冲
    - {+ l3 |6 A( r( }+ z6 u5 U
  34.         HAL_MPU_ConfigRegion(&MPU_Initure);                     //配置MPU
    # X) e; V1 Y# D2 e+ [  v
  35.         HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);                                //开启MPU
    8 Z" X/ V3 r( Y) T* E8 ]
  36.     return 0;
    & _. V' Q- N5 c- \" m- n4 }* ]
  37. }
    ; G: s2 w" r, O" s% m9 S

  38.   I0 d! v) _4 b
  39. //设置需要保护的存储块* c* E0 o! o/ V+ U/ J
  40. //必须对部分存储区域进行MPU保护,否则可能导致程序运行异常! a  ]" _- t9 k& R# T
  41. //比如MCU屏不显示,摄像头采集数据出错等等问题...
    ' b2 ?+ @1 R5 K- [- E
  42. void MPU_Memory_Protection(void)   //特意把SRAM4设置为不允许cache,使用DMA的变量可以放在这里。但要注意相应DMA能否访问SRAM4: A$ E& n& I5 W% k
  43. {
    4 n* o& ]( j" J# `, G
  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 s" m* @1 P% f+ C% D6 J: P
  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字节
    , |& p0 q) {0 ]
  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,允许缓冲  \2 ]6 I# r( Z" h' T
  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,允许缓冲
    6 H2 F  w. o' d# {9 B9 T
  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,禁止缓冲
    3 ]5 [9 h6 G, k: }  m
  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,允许缓冲$ F, S( R/ r+ I% J) |$ A9 d7 F
  50. }
复制代码
, W1 B# |! u* O4 l7 W( T
四、其他
& q# l# t2 _7 @0 [5 h' G5 `
2 N( V7 P+ W1 u5 n        值得一提的是,LTDC也是直接从RAM拿数据的,如果你使用了GUI(比如EMWIN),你的显示数据可能会暂存在Cache,而LTDC直接从RAM拿数据,就可能造成画面撕裂、重影、斑点之类的问题。解决方法是,把显存设置成透写。
7 H9 g, Y/ R* \. v. I, R2 e* B$ F% J0 g& p6 k
        从下面的图可以看到,Cache是在M7那个框里面的。而框外面的外设都可以直接与RAM交换数据,因此使用外设操作数据时都要考虑一下Cache的影响,不然异常可能难以预料。
! K  r# S8 _" |+ J, w$ P) r
5 W/ P- c. E4 v
20191119145451828.png

- Q" V, \8 k2 H0 P2 J4 l; q3 W( ?: F* Q

: B3 _& i$ s1 }' B
收藏 评论0 发布时间:2021-12-18 17:42

举报

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