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

【经验分享】STM32H7的Cache和MPU

[复制链接]
STMCU小助手 发布时间:2021-12-18 17:42
一、Cache: h% W/ T# G/ S/ ^( [8 f
2 c: R6 P4 i9 H) g; V. T* I
1、介绍
2 l4 @: p7 p9 m+ K9 ^2 I
1 O5 Q# x2 @, a2 P        Cache又分数据缓存D-Cache和指令缓冲I-Cache,STM32H7的数据缓存和指令缓存大小都是16KB。STM32H7主频是400MHz,除了TCM和Cache以400MHz工作,其它AXI SRAM,SRAM1,SRAM2等都是以200MHz工作。数据缓存D-Cache就是解决CPU加速访问SRAM。
) C4 l' h, t5 h. E6 ]8 e3 h9 E2 L5 F& P1 \* e2 }" P7 {# ]- Z
        如果每次CPU要读写SRAM区的数据,都能够在Cache里面进行,自然是最好的,实现了200MHz到400MHz的飞跃,实际是做不到的,因为数据Cache只有16KB大小,总有用完的时候。2 l+ O& o3 H% @9 q8 j+ _9 S
/ t, }. Q9 e! c$ @
2、操作,分为读操作和写操作
( K" z  q# [8 K3 O" R5 {! Y# K- H' f  x: ?# `+ ~
        读操作: 如果CPU要读取的SRAM区数据在Cache中已经加载好,这就叫读命中(Cache hit),如果Cache里面没有怎么办,这就是所谓的读Cache Miss。
: E' N; U# s" o6 K7 `9 g( M2 E
       写操作: 如果CPU要写的SRAM区数据在Cache中已经开辟了对应的区域(专业词汇叫Cache Line,以32字节为单位),这就叫写命中(Cache hit),如果Cache里面没有开辟对应的区域怎么办,这就是所谓的写Cache Miss。! f5 d4 \. y2 E. T

8 q! `& q3 n# d3、H7支持的Cache策略,共4种. G+ P! f4 ~; F+ c
. L7 a& F8 Y: q& m$ p
20191118102334153.png
5 M7 `: X& w( D  B, d

" x2 w* o' |2 o& a<回写:如果Cache中有,写数据只写到Cache,不写到RAM。>% U- Q4 ]5 @6 E/ ^8 ]

$ X) r" P* d  b$ y- I' D* G2 r* F" m<透写:如果Cache中有,写数据也要同时写到Cache和RAM。>6 m" u' ^4 v$ N

2 g8 D" f: R1 B+ W4 G* y<write allocate:写数据时,如果Cache中没有,那么就要在Cache中开辟一个空间,把数据写入Cache,同时把RAM中的相邻数据加载进来填充Cache。>; a/ n6 T" \! F/ R8 y1 a

9 D' z2 l0 O- O& c8 b) s) C7 Z<no write allocate:写数据时,如果Cache中没有,那么把数据直接写入RAM。>
. r1 l, B2 C4 q4 R  R1 c% u" E0 D- ~6 R+ R* j4 `2 p
<read allocate:读数据时,如果Cache中没有,那么就要在Cache中开辟一个空间,把数据从RAM中加载进来,后续的读操作,就可以直接从Cache中读取了。>! m" f3 j3 x+ Q* \& v

8 ]  B. y; `7 x1 v- Q8 L' z  t<no read allocate:读数据时,如果Cache中没有,那么直接从RAM中读。>
3 Z8 X0 B" }: w6 l
2 l! B- ~- y2 d. X4、风险
/ A: w2 P. I) p/ a. H6 s9 b4 w
20191118112913153.png

: d# _, c& T0 n* ~2 @
- X0 j: D$ h! O, j: I  @        从上面的图就看出来使用Cache的风险,因为DMA是直接与SRAM交换数据的,而CPU与SRAM之间隔了一个Cache,如果DMA更新了某个数据到SRAM,CPU要去访问,而恰好Cache中有,那么CPU就不会去SRAM中拿,就会拿到Cache中已经过时的数据。因此使用了DMA的内存区要配置为无Cache或者拿数据前清一次Cache。
8 q; p" e; W! u# H( T2 h* u
( @0 N+ x- U- h. }5、相关函数0 M& g& j. S; j" |8 k, }/ Q( i$ B

) c, y7 \4 `) M4 Y. t6 r  ~% B        SCB_EnableICache(void) :用于使能指令Cache,系统上电后优先初始化即可。
( U- N6 \9 `! p6 @/ e: W' E* J
; I& m+ t+ q; E6 C; l+ x        SCB_DisableICache(void) :用于禁止指令Cache。# y, O: j: |% ?2 d( K, K
% l5 x, C, F4 D
        SCB_InvalidateICache(void) :用于将指令Cache无效化,无效化的意思是将Cache Line标记为无效,等同于删除操作。这样Cache空间就都腾出来了,可以加载新的指令。  h9 X# h' `' R/ D0 S9 i
+ [* R1 k/ t4 u
        SCB_EnableDCache(void) :用于使能数据Cache,系统上电后优先初始化即可。
1 C: K( f1 N8 v" p! [4 `* J# Z# b0 C3 C
        SCB_DisableDCache(void) :用于禁止数据Cache。
3 i  e3 y: t+ _' Q7 N* U0 b+ }: z. g4 p
        SCB_InvalidateDCache(void) :用于将数据Cache无效化,无效化的意思是将Cache Line标记为无效,等同于删除操作。这样Cache空间就都腾出来了,可以加载新的数据。
- w' Y! j; [5 i8 v; k6 ~
; h7 |! m0 D+ ]' ~$ z: E        SCB_CleanDCache(void):用于将数据Cache清除,清除的意思是将Cache Line中标记为dirty的数据写入到相应的存储区。# e# T, z. `" R! U  a
2 }3 W9 r# S1 j" ^9 R
        SCB_CleanInvalidateDCache(void) :此函数是前面两个函数SCB_InvalidateDCache和SCB_CleanDCache的二合一。将Cache Line中标记为dirty的数据写入到相应的存储区后,再将Cache Line标记为无效,表示删除。这样Cache空间就都腾出来了,可以加载新的数据。
3 J: w8 [0 t' ?+ f" X1 t" m$ i0 I% g5 Q/ F
        SCB_InvalidateDCache_by_Addr(uint32_t *addr,int32_t dsize):可以指定地址和存储区大小,地址要32字节对齐,大小要是32字节的整数倍。用于将数据Cache无效化,无效化的意思是将Cache Line标记为无效,等同于删除操作。这样Cache空间就都腾出来了,可以加载新的数据。' e( ~& w# W1 y/ @/ V0 ^7 Q

: J* z  S7 a+ G1 X7 `        SCB_CleanDCache_by_Addr(uint32_t *addr,int32_t dsize):可以指定地址和存储区大小,地址要32字节对齐,大小要是32字节的整数倍。用于将数据Cache清除,清除的意思是将Cache Line中标记为dirty的数据写入到相应的存储区。3 N* F5 L. E5 }- ^- N8 w

7 m, H8 S* g, s1 R7 q/ q        SCB_CleanInvalidateDCache_by_Addr(uint32_t *addr,int32_t dsize):可以指定地址和存储区大小,地址要32字节对齐,大小要是32字节的整数倍。将Cache Line中标记为dirty的数据写入到相应的存储区后,再将Cache Line标记为无效,表示删除。这样Cache空间就都腾出来了,可以加载新的数据。
* z* t' s) D( |2 \8 e2 U. B9 S+ J5 z0 ?% F8 a. j

( x: D; A8 }% R1 e6 T7 ^
- H! r' G9 X4 _6 E: G1 T: q' g二、MPU( k; l3 h9 d7 E5 G9 Q8 f
$ A+ e9 Q5 {" ]. p
1、作用+ |* v" \, g1 I9 ^8 O
' m+ |3 ~9 |! c# m  @0 v( [, A
        防止不受信任的应用程序访问受保护的内存区域; 防止用户应用程序破坏操作系统使用的数据;通过阻止任务访问其它任务的数据区;允许将内存区域定义为只读,以便保护重要数据;检测意外的内存访问。 简单的说就是内存保护、外设保护和代码访问保护。) B5 I! M/ A! ?; K
0 Q4 y1 S( G% t" V) F0 \, u, e4 J
2、MPU可以配置的三种内存类型2 s, p/ S. P0 d* Q; p! x8 g

' o0 E! i: ~7 q/ f1)、Normal memory
# d- l: T+ p, x0 P- Q" H+ S. f- l: P6 G0 x: g8 @4 V  I
        CPU以最高效的方式加载和存储字节、半字和字,对于这种内存区,CPU的加载或存储不一定要按照程序列出的顺序执行。
# i; m$ l# b' c+ C" S. {
6 @  S6 a* o5 v+ z! _2)、Device memory
% M9 e9 k. F" y  C3 D  l( J; |, q" D' p  F) G+ l
        对于这种类型的内存区,加载和存储要严格按照次序进行,这样是为了确保寄存器按照正确顺序设置。
$ j: w3 v. s( L: p3 e' i7 l! {4 d$ n+ v1 ?3 r2 t6 O0 m
3)、Strongly ordered memory
# n2 B4 D3 |) H, k% R/ ~6 X+ @
' ]7 p0 {, X6 M5 D$ E2 O        程序完全按照代码顺序执行,CPU需要等待当前的加载/存储指令执行完毕后才执行下一条指令。这样会导致性能下降。& g9 ~: p3 D" U' x

# Y6 z+ X" k% r( o* b8 n3、MPU的使用
$ g2 T0 P; C* y" @) a/ f. w8 p; j/ L& s
        MPU可以配置保护16个内存区域(这16个内存域是独立配置的),每个区域最小要求256字节,每个区域还可以配置为8个子区域。由于子区域一般都相同大小,这样每个子区域的大小就是32字节,正好跟Cache的Cache Line大小一样。! T# V, z! y/ @7 K! ]& o% J+ K4 |/ {
1 O0 a, `' L! {- L7 L+ z( w8 Y
        使用时把一段连续的内存区配置为一个MPU保护区域,然后再配置这个MPU保护区域的特性。比如128KB的DTCM、64KB的SRAM4、32MB的SDRAM。MPU保护区域的特性使用MPU_RASR寄存器来配置,描述如下:
( m" f+ w1 x" O: L8 c% _- f' ?) }4 A4 Q9 U0 m+ a) y& m) u  a
20191118111700740.png

4 w& j2 D$ S9 p* L% W7 Z+ W+ m# y
1)、XN:用于控制这个MPU保护区域能否执行程序代码。& B& n. g5 {# ]# h! h9 a

3 Y" N+ [7 {  I; h/ T4 h2)、AP:用于控制这个MPU保护区域的特权级和非特权级的读写访问权限。: n% W. Q" E* J1 }
; M* Y  K/ g( `7 {, {7 q7 y6 W
20191118111401294.png
' o. G7 j; u( W
, X/ |- C3 D5 Q- F2 [  H
3)、TEX、C、B、S:H7支持4种Cache策略,这几位就是用来控制这个MPU保护区域使用哪一种。
- h3 ^7 }+ Q, ^! F4 @$ Y* Y1 _: q1 L) q
2019111811214681.png

0 d6 B2 s. W4 y6 ~1 Q/ P1 F7 d; B" ?) i* T
S位用于解决多总线或者多核访问的共享问题,一般不要开启。
( _- s$ r+ F5 t0 y& w% [3 Q, Q: F$ \2 F; W! c0 _) i: W8 p8 @
4)、SRD:这个位用于控制内存区的子区域 ,使用的是bit[15:8],共计8个bit,一个bit控制一个子区域, 0表示使能此子区域, 1表示禁止。一般情况下,取值0x00,表示8个子区域都使能。
( `" M0 ^& m( {& h, t8 {4 a$ e! q$ y0 C$ k( ^/ _2 A4 B- f& j& I
5)、SIZE:配置这个MPU保护区域的大小。7 Q) S: ?3 W% z* |) w& v" G6 ^, p

: h- k0 `( ^# z" y' D三、HAL配置例程; S* |$ L; k- C

& `4 J/ N/ ?+ @
  1. //设置某个区域的MPU保护
    . K% ^6 K3 L! w2 i" y. \
  2. //baseaddr:MPU保护区域的基址(首地址)- ]6 @& [  E0 f& K8 ^
  3. //size:MPU保护区域的大小(必须是32的倍数,单位为字节),可设置的值参考:CORTEX_MPU_Region_Size4 H) |- A( V: A& D1 p3 f& N
  4. //rnum:MPU保护区编号,范围:0~7,最大支持8个保护区域,可设置的值参考:CORTEX_MPU_Region_Number
    9 w- C) Y. ^/ }/ Z. l( f
  5. //ap:访问权限,访问关系如下:可设置的值参考:CORTEX_MPU_Region_Permission_Attributes& F- F. A( r$ [4 _( }
  6. //0,无访问(特权&用户都不可访问)
    : B. I2 p" e; Z# U' R
  7. //1,仅支持特权读写访问
    ( [# Y; @4 ^$ P/ Y- P" s, n+ Z5 H
  8. //2,禁止用户写访问(特权可读写访问)$ f0 o8 ]5 l9 B  V; U, K3 ?( ^7 X# c
  9. //3,全访问(特权&用户都可访问)
    3 e6 V! n- t  X4 b( H  `9 {
  10. //4,无法预测(禁止设置为4!!!)
    " D: W' k7 _- Z5 s% f' ?7 b
  11. //5,仅支持特权读访问
    + K  C3 ]5 V: H5 G& @
  12. //6,只读(特权&用户都不可以写). E  X  t# E; C+ ?# N1 S9 n
  13. //详见:STM32F7 Series Cortex-M7 processor programming manual.pdf,4.6节,Table 89.1 l  N0 o/ c3 w8 L
  14. //sen:是否允许共用;0,不允许;1,允许
    5 }/ ^9 z8 i9 f6 q; V
  15. //cen:是否允许catch;0,不允许;1,允许5 N9 v7 Q* a" k; |& N
  16. //返回值;0,成功., D2 E8 _) g: k0 q8 ~) a
  17. //    其他,错误.4 p  o2 q+ I" a5 T7 F* x; Z- a
  18. u8 MPU_Set_Protection(u32 baseaddr,u32 size,u32 rnum,u32 ap,u8 sen,u8 cen,u8 ben,u8 Tex)
    6 F% L( w" v  E1 _
  19. {
    ' l& `- E& p7 Z5 r3 P2 L6 Y8 E
  20.         MPU_Region_InitTypeDef MPU_Initure;
    ! z& x" K1 [) F' i; h2 W
  21.         HAL_MPU_Disable();                                                                        //配置MPU之前先关闭MPU,配置完成以后在使能MPU
    1 ?3 E( R9 j' B, l0 a

  22. 8 s/ H7 ~# Z, X4 X& Y7 K, K
  23.         MPU_Initure.Enable=MPU_REGION_ENABLE;                                //使能该保护区域 ( Q* g1 u% m9 t/ o' V5 Y9 X+ p
  24.         MPU_Initure.Number=rnum;                                            //设置保护区域
    ; ]4 N7 H4 {! ~) k
  25.         MPU_Initure.BaseAddress=baseaddr;                            //设置基址( i" n8 p0 ]1 E- y! \; @
  26.         MPU_Initure.Size=size;                                                    //设置保护区域大小
    7 g7 G6 `. |3 G3 q+ L" P, q2 _8 q
  27.         MPU_Initure.SubRegionDisable=0X00;                      //禁止子区域9 u6 w2 ^) r8 Y7 a
  28.         MPU_Initure.TypeExtField=Tex;                           //设置类型扩展域* v. x3 ^( i6 B! e
  29.         MPU_Initure.AccessPermission=(u8)ap;                            //设置访问权限,2 ~, l  J0 y& L5 H3 D
  30.         MPU_Initure.DisableExec=MPU_INSTRUCTION_ACCESS_ENABLE;        //允许指令访问(允许读取指令)
    % Q$ e) h, i8 g4 u/ O& I: l" b
  31.         MPU_Initure.IsShareable=sen;                            //是否允许共用
    ; [) |* A" R& u1 t
  32.         MPU_Initure.IsCacheable=cen;                            //是否允许cache, f* C$ C4 R  ?: U6 L7 B/ S
  33.         MPU_Initure.IsBufferable=ben;                           //是否允许缓冲" {& f5 H7 Y6 R7 Q! ^' E* ]
  34.         HAL_MPU_ConfigRegion(&MPU_Initure);                     //配置MPU$ H" C# o% B* Z. B( V% _( S, W
  35.         HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);                                //开启MPU
    ) i% A0 y3 J2 U+ T* @
  36.     return 0;+ |" n9 M) R5 g6 J2 n- D
  37. }2 f. w9 D- i1 i9 @, j

  38. - ]0 C: r' [7 j: r9 d
  39. //设置需要保护的存储块! u/ {# A# b7 g& x$ q% T' `
  40. //必须对部分存储区域进行MPU保护,否则可能导致程序运行异常
    5 F: j/ d# C5 H
  41. //比如MCU屏不显示,摄像头采集数据出错等等问题...
    * E% s6 ]  D9 B6 T
  42. void MPU_Memory_Protection(void)   //特意把SRAM4设置为不允许cache,使用DMA的变量可以放在这里。但要注意相应DMA能否访问SRAM4
    + F6 d: ]/ o( y4 H. H* ]
  43. {
    ' h' o, `7 k% m
  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,允许缓冲; A9 n& k. I% Q1 u
  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字节
    6 }' D2 _/ e8 A/ C; ?) K0 p% N
  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! J4 j1 v5 U  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 _' w  u' r0 G2 x
  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,禁止缓冲$ E5 e& l) ?( b- U. g9 n, d
  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. a8 d% t# }7 c, `) W
  50. }
复制代码
" Q7 H9 ]( k& B; j
四、其他
4 F; Y( J6 N) Y) u9 D' T
! o' ^5 O1 V4 r9 x; F        值得一提的是,LTDC也是直接从RAM拿数据的,如果你使用了GUI(比如EMWIN),你的显示数据可能会暂存在Cache,而LTDC直接从RAM拿数据,就可能造成画面撕裂、重影、斑点之类的问题。解决方法是,把显存设置成透写。: o4 i  _2 U  u

  O* q+ J7 i- m4 I, U# @% B        从下面的图可以看到,Cache是在M7那个框里面的。而框外面的外设都可以直接与RAM交换数据,因此使用外设操作数据时都要考虑一下Cache的影响,不然异常可能难以预料。5 W5 s8 T; j0 j: r2 O2 |) B5 H/ `

% _, O* B6 t6 m% t
20191119145451828.png
" c0 G6 ]" D0 D
6 K! F# @5 K4 O7 ]4 k* p
5 r% e( c9 W5 `
收藏 评论0 发布时间:2021-12-18 17:42

举报

0个回答

所属标签

相似技术帖

官网相关资源

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