内存管理单元MMU除了使用页表完成物理地址和虚拟地址的映射外,还包括对这些地址的管理。这些管理是通过设置内存属性完成的,比如是否过cache、cache属性、是否可读、可写、可执行、内存类型(normal 、device-GRE)、是否共享,共享范围等等… 这些属性都在页表项中定义,属性的有效范围至少是一个页面。
现在Cache一般组相连方式的,比如sharkl3 L1Dcache是4路128组相连。简单说,就是先把cache分成相等的4块,每块叫做一个way;每个way中有若干行cache line(缓存行),缓存行的编号就是set(行索引)。
内存中的地址到cache的映射是按照一定的规则确定的,下面是这个规则的定义:
对于32bit的地址,分为三部分: Tag-----Set-----offset,offset(字节索引)和缓存行的大小相关,比如64Bytes的缓存行,[5:0]是offset;set(行索引)和cache的中每个way的缓存行的数目有关,32KB的cache除以4way,每个way是8KB,每个way中的缓存行size是64Bytes,那一个way中有128个缓存行,[12:6]是set,剩余的高地址成为Tag(页帧号),作为不同地址的区分标记。
31----------Tag-----------13 12--------Set--------6 5-----offset-----0
如果不同地址中的[12:6]是相同的,它们会映射到相同的set编号上,因为是4ways,所以最多能支持4个set相同的地址映射到cache上,如果多于4个就会涉及到cache的替换策略,一般有循环计数(把最老的替换出去)、FIFO和伪随机策略,我之前见过的cache替换策略都是伪随机的。
MMU支持地址映射,所以cache的地址根据地址是虚拟地址还是物理地址分为VIVT、PIPT、VIPT三种。I就是上文的set。T就是上文的Tag。
VIVT:就是用虚拟地址访问cache
PIPT:用物理地址访问cache
VIPT:虚拟地址的set和物理地址的Tag
V的好处是不用经过TLB和MMU,直接使用虚拟地址访问cache,比较方便。但是也有坏处:
● 因为多个虚拟地址可以对用一个物理地址,所以会造成一个物理地址的内容可能在多个缓存行中,即便一个虚拟地址对应的缓存行内容发生变化,其他对应缓存行都变成无效,需要无效化清洗,比较麻烦。所以又出现了P的方式。
● 进程间的虚拟地址是相互独立的,当发生进程切换时,新进程中可能有相同的虚拟地址空间,显然新进程访问这个地址空间如果从cache访问是有问题的,需要把之前进程的虚拟地址对应的cache都无效化。每次切换都需要cache无效化(TLB也有这个问题,linux采用ASID改善)。
● 因为这样cache模块会在mmu模块之前,造成页面的保护机制缺失。
目前CA55应该是PIPT的方式,那么cpu访问内存的路径就是下面的样子
Cpu core <------------------> MMU(包括TLB) <------------->Cache <------------>DDR
P的坏处:需要经过mmu模块,有翻译地址映射的时间成本。
VIPT因为I部分(set行索引)和实际的物理地址部分未必完全一样,需用“着色”技术。
这个属性实际上是某个地址空间上的cache属性。
● 分配策略
Cache对读写操作是分开管理的,所以有读分配和写分配的说法,当然都是发生在cache miss时(不命中)
读分配:为读操作分配缓存行
写分配:为写操作分配缓存行,当前地址会多次被写时分配。
分开管理有利于一些特殊场合的管理,比如读多写少时,仅使用读分配比较合适。一些算法,原始的数据仅仅用于运算,所以对于原始数据的地址只需要读分配就好了。
下面用memcpy介绍一下读分配的过程。每次都数据时,比如每次读4字节数据(不考虑SIMD情况),然后把这4个数据写入写地址上。开了读分配后,在读4字节时发现未命中,cpu会预取多个缓存行(由CPUECTLR. L1PCTL定义,最多8 outstanding,默认5 outstanding),比如预取4行数据(64x4=256Bytes数据,AXI上开启burst传输,最大长度256Bytes),cpu再去读第二个4字节数据时,就不用去ddr读,直接从cache中读就可以了。
● 写策略,目前arm都采用写回策略。
写通:当cache写命中时,cache与主存同时发生写修改,会造成AXI总线时刻繁忙,效率也低。
写回:当CPU对cache写命中时,只修改cache的内容不立即写入主存,只当此行被换出时才写回主存。有脏页的情况,需要刷新操作。
● 共享策略 指明了共享的范围,sharkl3的cache都是inner的
Inner:内部集成,由协处理器方式访问的
Outer:外挂的,控制状态寄存器映射到总线地址空间
PS:关于地址空间共享的inner和outer,像自旋锁的arm实现使用的独占访问指令需要开启shareable才能执行成功。独占访问指令由独占监视器监视,inner和outer决定使用那个监视器。
●(简化一下,仅考虑一级cache)
clean:将cache中的数据写入ddr中,一般是cpu更改了cache中的数据
invalid:将cache中的数据无效化,cpu重新从ddr中读,一般是外设更改了ddr中的数据
zero:将cache中的部分数据清0,比如为ZI段分配缓存行。
lock:锁定某些缓存行,不被替换出去
多核多cluster的cpu,它们cache间的一致性是由硬件保证的(如cci、scu等,以及MOESI协议)
● POU和POC 数据一致性视角: 比如说clean数据到poc或者pou是什么意思?
POU:统一点(处理器视角的局部性一致),如cpu和它的内部模块看到L2 cache的内容是一样的,但看到L3 cache的内容可能不是一样的,那么L2 cache就是统一点。
POC:一致点(整个系统视角的全局性一致),所有的外设和cpu(注意是所有,不是一部分)对DDR的内容看到是一样的,那么DDR称之为一致点。可见一致点比统一点的要求严格。
如果POU是L3 cache且要求clean到POU,那么cpu刷cache最终刷到L3 cache为止,而不需要到ddr。
Sharkl3的POC和POU是一样的 都是DDR
有些CMP的POC和POU是不一样的,SMP的似乎没有必要分清这个区别。
● 多级cache访问,以L1、L2两级cache为例
LDR r0, [r1] 从r1的保存地址中读数据,如果这个地址是cacheable的,那么