Cover image for ARM Cache

ARM Cache

字数 6.9k
阅读
访客
时间轴

时间轴

2025-10-25

init

本文介绍了ARM架构中Cache的基本概念与工作原理。文章首先阐述了经典Cache架构中的关键术语,包括高速缓存行、索引、标记、偏移、组和路,并说明了组相联结构对防止缓存颠簸的作用。随后介绍了ARM64平台上Cache的主要类型,如指令缓存、数据缓存和统一缓存,以及直接映射、全相联和组相联三种映射方式,并通过具体示例展示了组相联Cache的容量与索引计算方法。文章还对比了物理高速缓存与虚拟高速缓存的优缺点,重点分析了虚拟缓存中存在的别名问题和同名问题及其产生原因。在此基础上,进一步介绍了三种缓存寻址方式:VIVT、PIPT和VIPT,详细说明了VIPT利用虚拟地址页内偏移作为索引、物理地址作为标记的工作机制,以及其避免别名问题的条件和受页面大小限制的原因。最后,文章概述了多级Cache的层级结构及访问流程,帮助读者理解从L1到L3缓存的数据查找过程。

参考文档:

经典的cache架构

经典cache架构
经典cache架构

Cache内部架构图

Cache内部架构图
Cache内部架构图

  • 高速缓存行:高速缓存中的最小的访问单元
  • 索引(index)域: 用于索引和查找是在高速缓存中的哪一行
  • 标记(tag):高速缓存地址编码的一部分,通常是高速缓存地址的高位部分,用来判断高速缓存行缓存的数据地址是否和处理器寻址地址一致
  • 偏移(offset): 高速缓存行中的偏移。处理器可以按字(word)或者字节(Byte)来寻址高速缓存行的内容
  • 组(set)相同索引域的高速缓存行组成一个组
  • 路(way): 在组相联的高速缓存中,高速缓存被分成大小相同的几个块

组的作用主要是防止cache的"颠簸"

Cache内部划分
Cache内部划分

Cache的类型

ARM64 架构上主要有:

  • Instruction Cache (I-Cache)
    专门缓存指令流,加快取指速度。
  • Data Cache (D-Cache)
    缓存数据读写(Load/Store)。
  • Unified Cache
    一些层级(如 L2、L3)往往是统一缓存(既存指令也存数据),不像 L1 那样严格分指令/数据。

有时称既有I-Cache也有D-Cache的为Separate Cache,它是分离缓存结构:既有独立的 I-Cache,又有独立的 D-Cache。典型于 L1 Cache

Cache映射方式

直接映射

每个组只有一行高速缓存行时,称为直接映射高速缓存(direct-mapping)

直接映射
直接映射

例子:

直接映射的例子
直接映射的例子

0x00,0x40,0x80都映射了同一个高速缓存行里,会频繁发生高速缓存替换性能较低

全相联

当cache只有一个组,即主存中的任意一个地址可以映射到任意一个cache line,称为全相联

全相联的例子
全相联的例子

组相联

  • 以二路组相联高速缓存为例,每个组包含2个高速缓存行(way),同一组内的两个cache line可以相互替换
  • 减小高速缓存的颠簸

组相联
组相联

举例:

  • 高速缓存的总大小是32KB,并且是4路(way),所以每一路的大小为8KB:way_size = 32/4 = 8(KB)
  • 高速缓存行的大小为32字节,所以每一路包含的高速缓存行数量为:num_cache_line = 8KB/32B=256

由此可以画出高速缓存的结构图:

高速缓存结构图
高速缓存结构图

索引值为12-5+1 = 8位,共2^8=256,可以索引256个cache line

12345678910
Cache Level (L1 / L2 / L3)┌──────────────┐│ Set 0        │── Way 0 → Cache Line│              │── Way 1 → Cache Line│              │── Way 2 → Cache Line│              │── Way 3 → Cache Line├──────────────┤│ Set 1        │── Way 0 → Cache Line│ ...          │└──────────────┘

物理高速缓存

当处理器查询MMU和TLB得到物理地址之后,使用物理地址去查询高速缓存

缺点:处理器在查询MMU和TLB后才能访问高速缓存,增加了流水线的延迟

物理高速缓存
物理高速缓存

虚拟高速缓存

处理器使用虚拟地址来寻址高速缓存

缺点:会引入不少问题:

  • 重名(Aliasing)问题
  • 同名(Homonyms)问题

虚拟高速缓存
虚拟高速缓存

别名问题(Aliasing)

也称重名问题

  • 操作系统中,多个不同的虚拟地址可能映射相同的物理地址。由于采用虚拟高速缓存架构,那么这些不同的虚拟地址会占用高速缓存中不同的高速缓存行,但是它们对应的是相同的物理地址
  • 举个例子:VA1和VA2都映射到PA,在cache中有两个cache line缓存了VA1和VA2
    • 当程序往VA1写入数据时,VA1对应的高速缓存行以及PA的内容会被更改,但是VA2还保存着旧数据。这样一个物理地址在虚拟高速缓存中就保存了两份数据,这样会产生歧义

重名问题
重名问题

同名问题(Homonyms)

  • 相同的虚拟地址对应着不同的物理地址,因为操作系统中不同的进程会存在很多相同的虚拟地址,而这些相同的虚拟地址在经过MMU转换后得到不同的物理地址,这就产生了同名问题
  • 同名问题最常见的地方是进程切换。当一个进程切换到另外一个进程时,新进程使用虚拟地址来访问高速缓存的话,新进程会访问到旧进程遗留下来的高速缓存,这些高速缓存数据对于新进程来说是错误和没用的。解决办法是在进程切换时把旧进程遗留下来的高速缓存都置为无效,这样就能保证新进程执行时得到一个干净的虚拟高速缓存。

高速缓存分类

  • VIVT (Virtual Index Virtual Tag): 使用虚拟地址的索引域和虚拟地址的标记域,相当于是虚拟高速缓存

    • CPU 在访问缓存时,不需要先进行地址转换(虚拟地址 → 物理地址),直接用虚拟地址来决定缓存行(index)和匹配标记(tag)。
    • 存在别名问题 (Synonym/Aliasing):不同虚拟地址映射到同一物理地址时,会在缓存中出现多份数据,可能导致数据不一致。
  • PIPT (Physical Index Physical Tag): 使用物理地址索引域和物理地址标记域,相当于是物理高速缓存

    • CPU 先通过 MMU 将虚拟地址转换成物理地址,然后用物理地址进行缓存索引和匹配。
    • 不存在别名问题
    • 常见L2 Cache
  • VIPT (Virtual Index Physical Tag): 使用虚拟地址索引域和物理地址的标记域

    • CPU 使用虚拟地址的低位部分作为缓存索引(Index)使用物理地址作为标记(Tag),用于匹配判断。因为缓存行对齐(通常是 64 字节或 128 字节),虚拟地址低位与物理地址低位一致,所以可以安全地用虚拟地址索引。
    • 避免了别名问题,因为使用了物理地址作为标记
    • Page Size 限制:虚拟索引长度必须 ≤ 页面偏移位数,否则不同页的同一虚拟索引会冲突。(VIPT 用虚拟地址的低位索引缓存行)
    • 常见L1 Cache

VIPT工作过程

一般视角下:

1
VA = 虚拟页号 (VPN) | 页内偏移 (PO)

VIPT视角下PO分成:

1
PO = Cache Index | Cache Line Offset
  • VIPT 缓存的索引位仅来自虚拟地址的页内偏移部分(而非 VPN),这是为了避免索引位受虚拟地址转换影响(PO 在虚实地址中是一致的)。
  • 缓存行偏移(Line Offset)用于定位缓存行内的具体字节

假设 L1 Cache 有 64KB,缓存行 64B:

  • 索引位数 = log₂(64KB / 64B) = log₂(1024) = 10 位

  • 所以缓存索引使用虚拟地址的 10 位

  • 页内偏移 = 12 位(4KB 页)

  • 页内偏移在虚拟地址和物理地址中是完全相同的(分页机制只转换 VPN,不改变 PO)。

  • 若索引位完全落在页内偏移中(即索引位长度 ≤ 页内偏移位长度),则 Cache 索引使用的是 “虚实一致” 的地址位,可避免因 VPN 不同但物理页相同导致的缓存别名问题(Alias Problem)。

所以要求:索引位 ≤ 页内偏移位

VIPT工作流程图
VIPT工作流程图

左右两步同时进行

VIPT别名问题

两个虚拟页面同时映射到同一个物理页面时,两个虚拟页面同时一起填满了Cache的一路

两个虚拟地址映射同一个物理页面
两个虚拟地址映射同一个物理页面

如图,Virtual Page1改写后,Virtual Page2访问的仍然是原来的数据

例子
例子

这个别名问题可以通过cache layout 来避免,即让VIPT 缓存的索引位仅来自虚拟地址的页内偏移部分(而非 VPN)

Cache层级

两级cache的系统

两级cache
两级cache

三级cache的系统

三级cache
三级cache

多级cache的处理流程

举例:

1
LDR x0,[x1]

加载x1地址的值到x0,假设x1是cachable的

多级cache
多级cache

  • Case1: 如果x1的值在L1 cache中,那么CPU直接从L1 cache获取了数据
  • Case2: 如果x1的值不在L1 cache中,而是在L2 cache
    • 如果L1 cache中没有空间,那么会淘汰一些cache line
    • 数据从L2 cache line加载到L1 cache line
    • CPU从L1 cache line中读取数据
  • Case3: x1的值都不在L1和L2 cache中,但是在内存中
    • 如果L1 cacheL2 cache中没有空间,那么会淘汰一些cache line
    • 数据从内存中加载到L2和L1的cache line
    • CPU从L1 cache line中读取数据

多级cache的访问延迟

多级访问的延迟
多级访问的延迟

Cache的策略(Cache Policies)

  • Cache相关的策略是在MMU页表中配置只有Normal内存可以被cacheable

  • Cache策略包括:

    • Cacheable/non-cacheable
    • Cacheable细分
      • Read/write-allocate
      • Write-Back cacheable, write-through cacheable
      • Shareability
  • Cache 的分配策略

    • Write allocation(WA): 当write miss的时候才分配一个新的cache line
    • Read allocation(RA): 当read miss的时候才分配一个新的cache line
  • Cache回写策略:

    • Write-back(WB): 回写操作仅仅更新到cache,并没有马上更新回内存(cache line is marked as dirty)
    • Write through(WT): 回写操作会直接更新cache和内存

Write Back和Write Through

Write Back和Write Through
Write Back和Write Through

  • WT写直通模式
    • 进行写操作时,数据同时写入当前的高速缓存,下一级高速缓存或主存储器中
    • 直写模式可以降低高速缓存一致性的实现难度,最大的缺点是消耗比较多的总线带宽
    • ARM Cortex-A系列处理器把WT模式看成Non-cacheable
      • The Cortex-A72 processor memory system treats all Write-Through pages as Non-cacheable

WT
WT

  • WB模式回写模式

    • 写入仅更新缓存并将缓存行标记为脏。仅当cache line被flush或明确清除时,才会更新外部存储器。
    • Cache line变成Dirty data

    WB
    WB

Inner 和 Outer Shareability

  • Normal memory可以设置inner或者outer shareability
  • 怎么区分是inner或者outer,不同设计有不同的区分
    • inner attribute通常是CPU IP集成的caches
    • outer attribute are exported on the bus

Inner和Outer Shareability
Inner和Outer Shareability

inner and outer
inner and outer

inner and outer
inner and outer

  • inner attribute是内部集成的cache
  • outer attribute是挂载在外部总线的外部cache

内部共享与外部共享
内部共享与外部共享

预取指令

AArch64(ARMv8 64-bit)中的预取指令

在 A64 中,使用的是 PRFM(Prefetch Memory)。

指令格式

1
PRFM <prfop>, [Xn, #imm]  // 从 Xn + offset 的地址预取缓存

prfop 语法结构说明

1
<prfop> = <type><target><policy> | #uimm5
字段含义示例值
<type>预取目的PLD读,PST
<target>预取目标缓存层级L1,L2,L3
<policy>缓存使用策略KEEP(重复使用),STRM(流式访问/一次性用)
#uimm55 位编码形式#0~#31

示例

预取读取数据到 L1,保留在缓存
1
PRFM PLDL1KEEP, [X0, #32]

意思是:把地址(X0 + 32)对应的数据提前加载到 L1 Cache,并且标记为可能反复使用


预取一次性读取(流式访问)
1
PRFM PLDL1STRM, [X1]

意思是:把[X1]预加载进缓存,但不长期保留(适合 memcpy 这种顺序流式读取)。


等价立即数写法
1
PRFM #0, [X0]

#0在 ARM 预取 hint 表中对应默认的PLDL1KEEP

uimm5值对应 prfop含义
#0PLDL1KEEP读预取到 L1,重用(最常用)
#1PLDL1STRM读预取到 L1,流式(只用一次)
#4PLDL2KEEP读预取到 L2
#8PLDL3KEEP读预取到 L3
#16PSTL1KEEP写预取
#17PSTL1STRM写预取(流式)

Point of Unification(PoU)和Point of Coherency(PoC)

  • PoU: 表示一个CPU中的指令cache数据cache还有MMUTLB等看到的是同一份的内存拷贝
    • PoU for a PE,是说保证PE看到的I/D cacheMMU是同一份拷贝。大多情况下,PoU是站在单核系统的角度来观察的
    • PoU for inner share,意思是说在inner share里面的所有PE都能看到相同的一份拷贝
  • PoC: 系统中所有的观察者例如DSP, GPU, CPU, DMA等都能看到同一份内存拷贝

PoU
PoU

PoU和Poc
PoU和Poc

PoU和PoC的区别

  • PoC是系统一个概念,和系统配置相关
  • 例如,Cortex-A53可以配置L2 cache和没有L2 cache,可能会影响PoU的范围

PoU和PoC的区别
PoU和PoC的区别

PoU和PoC
PoU和PoC

Cache维护

  • Cache的管理操作
    • 无效 (Invalidate) 整个高速缓存或者某个高速缓存行。高速缓存上的数据会被丢弃。
    • 清除 (Clean) 整个高速缓存或者某个高速缓存行。把脏的cache line数据写回到下一级高速缓存或者主存储器中(也可称为flush)
    • 清零 (Zero) 操作
  • Cache管理的对象
    • ALL : 整块高速缓存
    • VA: 某个虚拟地址,有时称MVA(修改后的虚拟地址,是包含特定虚拟地址的高速缓存行)
    • Set/Way: 特定的高速缓存行或者组和路
  • Cache管理的范围
    • PoC
    • PoU
  • Shareability
    • inner

Cache指令格式

Cache指令格式
Cache指令格式

高速缓存操作指令
高速缓存操作指令

System instructions for cache maintenance
System instructions for cache maintenance

接受地址参数的指令采用一个 64 位寄存器,该寄存器保存要维护的虚拟地址。此地址没有对齐限制。

AArch64 数据缓存按地址无效指令 DC IVAC 需要写权限,否则会生成权限错误。

例子1

遍历所有 CPU 数据缓存(Data Cache)层级(L1/L2/L3…),并对每一级缓存执行逐路逐组的清理(Clean by Set/Way),确保缓存中的数据都回写到内存:

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273
// AArch64: Data Cache Clean by Set/Way// 可在 EL1(内核态)执行.global clean_data_cacheclean_data_cache:    // 读取缓存等级 ID(CLIDR_EL1)    MRS     X0, CLIDR_EL1    // 提取 Level of Coherency(3-bit per level)    AND     W3, W0, #0x07000000     // mask[26:24]    LSR     W3, W3, #23             // W3 = 2 * LoC    CBZ     W3, Finished            // 无缓存则退出    MOV     W10, #0                 // W10 = 2 * cache level index    MOV     W8, #1                  // 常数 1Loop1:    // 计算 3 × cache level (每层3bit描述cache类型)    ADD     W2, W10, W10, LSR #1    // W2 = 3*level = level*2 + level/2    LSR     W1, W0, W2              // 取当前层 cache 类型    AND     W1, W1, #0x7            // 取最低三位    CMP     W1, #2                  // 2 = Data Cache,3 = Unified    B.LT    Skip                    // 跳过没有 D cache 的层    // ---- 选择当前缓存层进行操作 ----    MSR     CSSELR_EL1, X10         // X10低位bit指定 level    ISB                             // 等待选择生效    // 从 CCSIDR_EL1 读 cache 配置    MRS     X1, CCSIDR_EL1          // X1 holds CCSIDR    // ---- 解析 Cache Line 大小 ----    AND     W2, W1, #7              // bits[2:0]: log2(line_len)-4    ADD     W2, W2, #4              // W2 = log2(line_len)    // ---- 解析 Way ----    UBFX    W4, W1, #3, #10         // bits[12:3]: ways-1    CLZ     W5, W4                  // W5 = leading zero count    LSL     W9, W4, W5              // 初始化 way 位偏移值    LSL     W16, W8, W5             // 每次递减步长Loop2:    // ---- 解析 Set ----    UBFX    W7, W1, #13, #15        // bits[27:13]: sets-1    LSL     W7, W7, W2              // 对齐 set 位    LSL     W17, W8, W2             // 每次递减步长Loop3:    // ---- 拼装 DC 操作参数 ----    ORR     W11, W10, W9            // 组合 level + way    ORR     W11, W11, W7            // 加 set		// X11 中低位字段编码 Way + Set + Cache level,告诉 CPU 哪一条 Cache 行要清理。    DC      CSW, X11               // CLEAN by set/way     SUBS    W7, W7, W17            // set-- 循环    B.GE    Loop3    SUBS    X9, X9, X16            // way-- 循环    B.GE    Loop2Skip:    ADD     W10, W10, #2           // 下一个 cache level    CMP     W3, W10    DSB                             // 确保上次 clean 完全执行    B.GT    Loop1Finished:    DSB     SY                      // 同步屏障    ISB    RET

CLIDR_EL1

位段含义
bits [26:24]LoC = Level of Coherency(缓存一致性层级 × 2)
bits [2:0]L1 cache type
bits [5:3]L2 cache type

代码整体流程:

步骤动作解释
1读 CLIDR_EL1获取系统中有几级 Cache 以及类型
2解析 LoC确定需要遍历多少级缓存
3逐层循环从 L1 → L2 → L3… 依次处理每一级缓存
4用 CSSELR_EL1 选择缓存层告诉 CPU:接下来我要访问哪一级的 Cache
5读 CCSIDR_EL1查询该 Cache 的详细配置(组数、路数、行大小)
6双重循环遍历 Set(组)和 Way(路),逐行清理缓存
7DC CSW执行 Clean by Set/Way
8DSB+ISB数据同步屏障,确保清理完成后再继续执行

在正常情况下,清理或使整个缓存无效是只有固件应该做的事情,作为内核加电或断电序列的一部分。它也可能需要大量时间,L2 缓存中的行数可能非常大,并且有必要逐个循环它们。因此,这种清理绝对只适用于特殊场合!

例子2

123456789
/* Coherency example for data and instruction accesses within the same InnerShareable domain. Enter this code with <Wt> containing a new 32-bit instruction,to be held in Cacheable space at a location pointed to by Xn. */STR Wt, [Xn]DC CVAU, Xn // Clean data cache by VA to point of unification (PoU)DSB ISH // Ensure visibility of the data cleaned from cacheIC IVAU, Xn // Invalidate instruction cache by VA to PoUDSB ISH // Ensure completion of the invalidationsISB // Synchronize the fetched instruction stream

这段代码是标准的 Self-modifying code / JIT 编译同步步骤:

  1. 写入新指令到内存(可能在 D-Cache)
  2. 清 D-Cache,把数据写回 PoU
  3. DSB:保证写回完成
  4. I-Cache 作废对应地址
  5. DSB:保证作废完成
  6. ISB:刷新取指流水线,CPU 执行新指令

Cache discovery

  • 在我们做cache指令管理的时候,你需要知道如下信息:
    • 系统支持多少级的cache?
    • Cache line是多少
    • 每一级的cache,它的set和way是多少
    • 对于zero操作,我们需要知道多少data可以被zeroed?

Cache
Cache

  • Cache Level ID Register (CLIDR, CLIDR_EL1):列出有多少level的cache,可以读取缓存级别的数量
  • Cache Type Register(CTR, CTR_EL0): cache line大小,可以读取缓存行的大小
  • 如果这需要由运行在执行级别EL0的用户代码访问, 这可以通过设置系统控制寄存器(SCTLR/SCTLR_EL1) 的 UCT 位来完成。
  • sets and ways: 需要访问两个寄存器来获取
    • 告诉Cache Size Selection Register(CSSELR, CSSELR_EL1)要查询哪个cache
    • 从Cache Size ID Register(CCSIDR, CCSIDR_EL1)中读取相关信息

Cache Discovery其他说明

  • 数据缓存零 ID 寄存器 (DCZID_EL0) 包含要为零操作归零的块大小。

    • DC ZVA (Data Cache Zero by Virtual Address)

      • 将一条 cache line 对应的内存清零
      • DCZID_EL0 保存可以归零的块大小
      • 访问权限:只有特权级别(EL1 及以上)能访问 DCZID_EL0
    • 控制位

      • SCTLR_EL1.DZE:控制 EL0 是否允许 DC ZVA
      • HCR_EL2.TDZ:控制 EL0/EL1 在非安全世界是否允许 DC ZVA
  • SCTLR/SCTLR_EL1 的 [DZE] 位和 Hypervisor 配置寄存器 (HCR/HCR_EL2) 中的 [TDZ] 位控制哪些执行级别和哪些世界可以访问 DCZID_EL0。CLIDR_EL1、CSSELR_EL1 和 CCSIDR_EL1 只能通过特权代码访问,即 AArch32 中的 PL1 或更高版本,或 AArch64 中的 EL1 或更高版本。

  • 如果在异常级别禁止通过虚拟地址执行数据高速缓存零 (DC ZVA) 指令,EL0 由 SCTLR_EL1.DZE 位控制,EL1 和 EL0 中的非安全执行由 HCR_EL2 的 TDZ 位控制,此时读取 DCZID_EL0 返回 0,指示该指令不受支持。

  • CLIDR 寄存器只知道处理器本身集成了多少级缓存。它不能提供有关外部存储系统中任何缓存的信息。例如,如果只集成了 L1 和 L2,CLIDR/CLIDR_EL1 标识了两个级别的缓存,处理器不知道任何外部 L3 缓存。在执行缓存维护或与集成缓存保持一致性的代码时,可能需要考虑非集成缓存。

Cache实验一:cache的枚举 Cache Discovery

实验一
实验一

实验运行结果:

实验运行结果与树莓派官网对比
实验运行结果与树莓派官网对比

代码

核心思路:

  1. 先识别每一级 cache 类型(CLIDR)
  2. 选择层级和类型,读取配置寄存器(CSSELR/CCSIDR)
  3. 计算每一级 cache 的 set/way/line size 和总大小
  4. 打印边界信息和 L1 I-Cache 寻址策略**
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
#include "cache_info.h"static const char *cache_type_string[] = {"nocache", "i-cache", "d-cache",                                          "separate cache", "unified cache"};// instruction cache的policies// L1 指令缓存寻址策略// 0 = VPIPT// 1 = Reserved// 2 = VIPT// 3 = PIPTstatic const char *icache_policy_str[] = {    [0 ... ICACHE_POLICY_PIPT] = "RESERVED/UNKNOWN",    [ICACHE_POLICY_VIPT] = "VIPT",    [ICACHE_POLICY_PIPT] = "PIPT",    [ICACHE_POLICY_VPIPT] = "VPIPT",};// 读取CTR_EL0的CTR_CWG(Cache WriteBack Granule)// 缓存写回的最大粒度2^(val) * 4Bytestatic inline unsigned int cache_type_cwg(void) {  return (read_sysreg(CTR_EL0) >> CTR_CWG_SHIFT) & CTR_CWG_MASK;}// 通常情况下 cache_line_size = CWG// 获取cache的line size 2^(CWG) * 4 = 4 << CWGstatic inline int cache_line_size(void) {  unsigned int cwg = cache_type_cwg();  return 4 << cwg;}// 读取CLIDR_EL1, Cache Level ID Register// 获取cache的类型,读取CLIDR_EL1的CTYPE字段static inline enum cache_type get_cache_type(int level) {  unsigned long clidr;  if (level > MAX_CACHE_LEVEL)    return CACHE_TYPE_NOCACHE;  clidr = read_sysreg(clidr_el1);  return CLIDR_CTYPE(clidr, level);}/* * 获取每一级cache的way和set * * 从树莓派官网可以知道: * https://www.raspberrypi.org/documentation/hardware/raspberrypi/bcm2711/README.md * * Caches: 32 KB data + 48 KB instruction L1 cache per core. 1MB L2 cache. * */static void get_cache_set_way(unsigned int level, unsigned int ind) {  unsigned long val;  unsigned int line_size, set, way;  int tmp;  /* 1. 先写CSSELR_EL1寄存器(Cache Size Selection Register),告知要查询哪个cache   */  // 写入level和缓存类型(0b0 data cache or unified cache 还是0b1 instruction  // cache)  tmp = (level - 1) << CSSELR_LEVEL_SHIFT | ind;  write_sysreg(tmp, CSSELR_EL1);  /*   * 2.   * 读取CCSIDR_EL1寄存器的值,当没有实现ARMv8.3-CCIDX时,这个寄存器只有低32为有效。   * 注意这个寄存器有两种layout的方式。   * */  val = read_sysreg(CCSIDR_EL1);  // NumSets位域,描述cache的set数  set = (val & CCSIDR_NUMSETS_MASK) >> CCSIDR_NUMSETS_SHIFT;  set += 1;  // Associativity位域,描述cache的way数  way = (val & CCSIDR_ASS_MASK) >> CCSIDR_ASS_SHIFT;  way += 1;  // CCSIDR_EL1的LineSize位域  // line_size_bytes = 1 << (LineSize + 4)  line_size = (val & CCSIDR_LINESIZE_MASK);  line_size = 1 << (line_size + 4);  printk("          %s: set %u way %u line_size %u size %uKB\n",         ind ? "i-cache" : "d/u cache", set, way, line_size,         (line_size * way * set) / 1024);}int init_cache_info(void) {  int level;  unsigned long ctype;  printk("parse cache info:\n");  // 遍历每一级cache  for (level = 1; level <= MAX_CACHE_LEVEL; level++) {    /* 获取cache type */    ctype = get_cache_type(level);    /* 如果cache type为NONCACHE,则退出循环 */    if (ctype == CACHE_TYPE_NOCACHE) {      level--;      break;    }    printk("   L%u: %s, cache line size(CWG) %u\n", level,           cache_type_string[ctype], cache_line_size());    if (ctype == CACHE_TYPE_SEPARATE) {      get_cache_set_way(level, 1);      get_cache_set_way(level, 0);    } else if (ctype == CACHE_TYPE_UNIFIED)      get_cache_set_way(level, 0);  }  /*   * 获取ICB,LOUU,LOC和LOUIS   * ICB: Inner cache boundary   * LOUU: 单核处理器PoU的cache边界。   * LOC: PoC的cache边界   * LOUIS:PoU for inner share的cache边界。   * */  unsigned clidr = read_sysreg(clidr_el1);  printk("   IBC:%u LOUU:%u LoC:%u LoUIS:%u\n", CLIDR_ICB(clidr),         CLIDR_LOUU(clidr), CLIDR_LOC(clidr), CLIDR_LOUIS(clidr));  unsigned ctr = read_sysreg(ctr_el0);  printk("   Detected %s I-cache\n", icache_policy_str[CTR_L1IP(ctr)]);  return level;}

相关寄存器

CSSELR_EL1

Cache Size Selection Register

CSSELR_EL1
CSSELR_EL1

Bits名称含义
63:5RES0保留位,读写无意义
4TnDAllocation Tag not Data:是否选择单独的 Allocation Tag cache
0b0 → 数据、指令或统一缓存
0b1 → 单独 Allocation Tag cache
注意:当InD = 1(指令缓存)时,这个位为 RES0(无效)
3:1Level要查询的缓存级别(Cache Level)
0b000 → L1
0b001 → L2
0b010 → L3
0b011 → L4
0b100 → L5
0b101 → L6
0b110 → L7
其他值保留注意:如果选择了未实现的缓存级别,读取 CSSELR_EL1 返回值是不确定的
0InDInstruction not Data:缓存类型:
0b0 → 数据或统一缓存 ( Data Cache or Unified Cache )
0b1 → 指令缓存 ( Instruction Cache )
如果选择未实现的缓存级别,读取 CSSELR_EL1 的 Level 和 InD 返回值是不确定的

TnD
TnD

Level
Level

InD
InD

CCSIDR_EL1

Current Cache Size ID Register

CCSIDR_EL1
CCSIDR_EL1

Note
Note

Bits名称含义
63:56RES0保留位
55:32NumSetsCache 中的 set 数减 1 → 实际 set 数 = NumSets + 1注意:set 数不一定是 2 的幂
31:24RES0保留位
23:3Associativity描述 cache 的 way 数,Cache 的关联度(ways)减 1 → 实际 associativity = Associativity + 1注意:不一定是 2 的幂
2:0LineSizeCache line 大小的 log2 减 4计算公式:line_size_bytes = 1 << (LineSize + 4)

Accessing the CCSIDR_EL1
Accessing the CCSIDR_EL1

如果ARMv8.5-MemTag is implemented and enabed

if ARMv8.5-MemTag is implemented and enabled
if ARMv8.5-MemTag is implemented and enabled

CCSIDR2_EL1

Current Cache Size ID Register 2

ARMv8.3-CCIDX is implemented才有效

CCSIDR2_EL1
CCSIDR2_EL1

Bits名称含义
63:24RES0保留位,读写无意义
23:0NumSetsCache 中的 set 数目减 1计算方法NumSets + 1得到实际的 set 数注意:set 数不一定是 2 的幂
  • 用途:查询指定 cache 的 set 数量
  • 该寄存器一般与 CSSELR_EL1 配合使用:
    1. 写 CSSELR_EL1 选择 Cache Level 和类型(数据/指令/Tag)
    2. 读 CCSIDR2_EL1 得到该 cache 的 set 数
  • NumSets = 0 表示 1 个 set
  • NumSets > 0 表示 实际 set 数 = NumSets + 1

NumSets
NumSets

读取CCSIDR2_EL1注意事项:

accessing CCSIDR2_EL1
accessing CCSIDR2_EL1

CLIDR_EL1

Cache Level ID Register

CLIDR_EL1
CLIDR_EL1

  • 用途
    • 标识处理器每一级 cache 的类型(Instruction/Data/Unified/Tag)。
    • 指出可以使用 Set/Way cache maintenance instructions 管理的 cache。
    • 提供 缓存层次的一致性与共享级别信息(LoC、LoU、LoUIS)。
    • 支持最多 7 级缓存。
Bits名称含义
63:47RES0保留位
46:35Ttype(n=1…7)Tag Cache 类型
0b00: 无 Tag Cache
0b01: Separate Allocation Tag Cache
0b10: Unified Allocation Tag + Data (同一行)
0b11: Unified Allocation Tag + Data (分行)
34:32ICBInner Cache Boundary(内部缓存边界)
0b000: Not disclosed by this mechanism.
0b001: L1 is the highest Inner Cacheable level
0b010: L2 is the highest Inner Cacheable level
……
0b110: L6 is the highest Inner Cacheable level
0b111: L7 is the highest Inner Cacheable level
31:29LoUULevel of Unification Uniprocessor
28:26LoCLevel of Coherence
25:23LoUISLevel of Unification Inner Shareable
22:0Ctype(n=1…7)Cache Type(每一级缓存类型)
0b000: No cache
0b001: Instruction cache only
0b010: Data cache only
0b011: Separate instruction and data cache
0b100: Unified cache
CTR_EL0

Cache Type Register

作用是提供cache的架构信息

CTR_EL0
CTR_EL0

位域Bits名称描述
63-38RES0Reserved保留,读出为 0
37-32TimeLineTag minimum LineTag 最小 line 粒度,表示 Allocation Tag 覆盖的最小 cache line 的大小(log2(以 word=4B 为单位))与**Memory Tagging Extension (MTE)**有关
31RES1Reserved保留,固定为 1
30RES0Reserved保留,固定为 0
29DICInstruction cache invalidation requirements for data to instruction coherence决定是否需要做 I-cache invalidate 来保证数据写入对 I-cache 可见
0 = 数据写入后,必须 invalidate I-cache 才能让指令取到最新数据。
1 = 不需要 invalidate I-cache。
28IDCData cache clean requirements for instruction to data coherence决定是否需要清理(clean)D-cache 来保证 I/D 一致性
0 = 数据 cache 需要 clean 到 PoU 才能保证 I/D 一致性。
1 = 不需要 D-cache clean。
通常现代核都置 1,表示硬件自动保证一致性
27-24CWGCache Writeback GranuleCache Writeback Granule,缓存写回的最大粒度(log2(以 word=4B 为单位))。表示当一个 cache line 写回时,可能影响的最大内存块大小(以 word=4B 为单位)。
例如 CWG=0b0100 → 2^(4) = 16 words = 64 bytes。
23-20ERGExclusives reservation granuleExclusive Reservation Granule,原子指令(LDXR/STXR)的最大 reservation 范围粒度(log2(以 word=4B 为单位))
19-16DminLineD minlineData cache 最小 line 大小(log2(以 word=4B 为单位))
15-14L1IPLevel 1 Instruction cache policyL1 指令缓存寻址策略:
0 = VPIPT
1 = Reserved
2 = VIPT
3 = PIPT
13-12RES1Reserved保留
3-0IMINLINE / DMINLINEInstruction minline / Data minlineInstruction cache 最小 line 大小(log2(以 word=4B 为单位))

L1Ip

表示L1 Instrucion Cache的类型:

L1Ip
L1Ip

Instruction cache invalidation requirements for data to instruction coherence

DIC
DIC

Data cache clean requirements from instruction to data coherence

IDC
IDC

Cache Writeback Granule

CWG
CWG

通常情况下,CWG = 最大 cache line 大小,因为写回是以整行(cache line)为单位的。

但是,ARM 规范允许微架构不同:

  1. CWG ≥ cache line size:有些实现可能在写回时会把多个 cache line 合并成更大的突发写(burst),比如 128B。
  2. CWG = cache line size:常见情况,比如 line=64B → CWG=0b0100(16 words)。
  3. CWG 未提供 (0b0000):必须假设最大写回粒度是 2KB,或者自己去读 Cache Size ID Registers 推算。
评论加载中…