cache组织方式
本文主要讲述如何根据虚拟地址或物理地址来寻找cache,及各种方案的优劣比较。在阅读前,需要对cache基本工作原理及MMU工作原理有一定了解,比如cache的映射方式(直接映射,组相联,全相联),虚拟地址到物理地址的转换过程及TLB等。
VIVT(Virtual Index Virtual Tag)
使用虚拟地址做索引,虚拟地址做Tag。早期的ARM处理器一般采用这种方式,在查找cache line
过程中不借助物理地址,这种方式会导致cache别名(cache alias
)问题。比如当两个虚拟地址对应相同物理地址,并且没有映射到同一cache行,那么就会产生问题。另外,当发生进程切换时,由于页表可能发生变化,所以要对cache进行invalidate
等操作,效率较低。
VIPT(Virtual Index Physical Tag)
使用虚拟地址做索引,物理地址做Tag。在利用虚拟地址索引cache同时,同时会利用TLB/MMU
将虚拟地址转换为物理地址。然后将转换后的物理地址,与虚拟地址索引到的cache line
中的Tag作比较,如果匹配则命中。这种方式要比VIVT
实现复杂,当进程切换时,不在需要对cache进行invalidate
等操作(因为匹配过程中需要借物理地址)。但是这种方法仍然存在cache别名的问题(即两个不同的虚拟地址映射到同一物理地址,且位于不同的cache line
),但是可以通过一定手段解决。
了解MMU转换过程的话应该知道,以4KB页大小为例,对于物理地址与其对应的虚拟地址,bit[0] -- bit[11]
低12位是相同的,如果两个虚拟地址映射到了同一个物理地址,那么这两个虚拟地址的低12位一定是相同的。在利用Virtual Index
查找cache line
时(这里假定cache使用4路组相联的方式),如果能保证Virtual Index
的位数包含在bit[0] -- bit[11]
中,那么就能保证两个虚拟地址会查找到同一个cache line
组内,由于物理地址相同,在利用Tag在组内进行匹配时,一定会找到同一个cache line
,进而解决cache alias
问题。针对上述描述,这里详细举例说明:假定L1 cache大小为16KB
, 4路组相联, cache line
为32。现有物理地址0x00000120
,虚拟地址为0xe0001120
及0xf0002120
同时映射到该物理地址(4KB页映射,虚拟地址和物理地址低12位相同)。由于cache大小为16KB
,cache line
为32(bit[0] – bit[4]),由于4路组相联,所以用于Virtual Index
的位数为 log (16KB / 32 / 4) = 7
即 (bit[5] – bit[11]),这样针对两个虚拟地址0xe0001120
及0xf0002120
由于低12位相同,那么一定会映射到同一个cache组内,然后利用物理地址作为Tag在组内匹配cache line,一定会找到同一个cache line。那么问题来了,我现在的cache大小为32KB
,那么用于Virtual Index的位数为 log (32KB / 32 / 4) = 8
即 (bit[5] – bit[12]),这样两个个虚拟地址0xe0001120
及0xf0002120
的bit[12]不同,所以就会寻找到不同的cache组内,这样就会造成同一物理地址存在于两个cache line
内,造成cache alias
。针对这种情况,可以有两种解决方法,调整映射页大小,超过4KB,这样能保证两个虚拟地址低位相同的位数涵盖用于Virtual Index
的位数。另一种方式通过软件解决,Page Colouring Restrictions。详细方法可以查看链接网址。无论是通过修改页大小还是通过软件方式解决VIPT
的cache alias
问题,核心思想就是让不同的虚拟地址查找到同一个cache组,这样在利用物理Tag进行组内多路cache匹配时,就一定会找到同一个cache line
,那么如何能保证不同的虚拟地址查找到同一个cache组呢?那就是让两个不同的虚拟地址地址低位相同的位数涵盖用于Virtual Index
的位数
PIPT(Physical Index Physical Tag)
使用物理地址做索引,物理地址做Tag。现代的ARM Cortex-A大多采用PIPT
方式,由于采用物理地址作为Index
和Tag
,所以不会产生cache alias
问题。不过PIPT
的方式在芯片的设计要比VIPT
复杂得多,而且需要等待TLB/MMU
将虚拟地址转换为物理地址后,才能进行cache line
寻找操作。