Fork me on GitHub
侧边栏

【ARM Cache 及 MMU 系列文章 6.2 -- ARMv8v9 如何读取 Cache 内部数据并对其进行解析?】

Direct access to internal memory

在ARMv8架构中,缓存(Cache)是用来加速数据访问的关键组件,它利用了程序执行中的局部性原理来提高性能。缓存中的每一行(Cache Line)通常包含了两个主要部分:实际的数据和一个标记(Tag)。这个标记用来标识存储在缓存行中的数据在内存中的位置。

1

关于cache 中 set 和 way 的定义见文章【ARM Cache 系列文章 1 – Cache基础概念学习】

ARMv9架构中,核心提供了一种机制,可以通过实现定义的系统寄存器读取L1缓存、L2缓存和 TLB 中的内容。当cache中据与系统内存数据之间的一致性出现问题时,可以使用这个机制来调查问题。直接访问 internal memory 只在EL3(异常级别3)中可用。在所有其他异常级别中执行这些指令会导致 未定义的指令异常

以 ARM A720为例,可以通过下表中的12个只读寄存器来读取 internal memory中的内容。

Register name Description Access encoding
IMP_ISIDE_DATA0_EL3 Instruction Data register 0 MRS , S3_6_C15_C0_0
IMP_ISIDE_DATA1_EL3 Instruction Data register 1 MRS , S3_6_C15_C0_1
IMP_ISIDE_DATA2_EL3 Instruction Data register 2 MRS , S3_6_C15_C0_2
IMP_DSIDE_DATA0_EL3 L1D Data register 0 MRS , S3_6_C15_C1_0
IMP_DSIDE_DATA1_EL3 L1D Data register 1 MRS , S3_6_C15_C1_1
IMP_DSIDE_DATA2_EL3 L1D Data register 2 MRS , S3_6_C15_C1_2
IMP_L2_DATA0_EL3 L2 Data register 0 MRS , S3_6_C15_C1_3
IMP_L2_DATA1_EL3 L2 Data register 1 MRS , S3_6_C15_C1_5
IMP_L2_DATA2_EL3 L2 Data register 2 MRS , S3_6_C15_C1_4
IMP_MMU_DATA0_EL3 TLB Data register 0 MRS , S3_6_C15_C0_3
IMP_MMU_DATA1_EL3 TLB Data register 1 MRS , S3_6_C15_C0_4
IMP_MMU_DATA2_EL3 TLB Data register 2 MRS , S3_6_C15_C0_5

通过配置 IMPLEMENTATION DEFINED RAMINDEX 寄存器来选择要访问的 internal memory。

指令如下

SYS #6, C15, C0, #0, <Xt>

<Xt> 为通用寄存器

注意:通过上面指令可以选择读取 Cache 的 TAG值和DATA的值。

L1 cache encodings

上文中通用寄存器的值需要按照下面表格中格式进行配置:

  • 如果L1 指令cache 大小为 64KB, 那么读取它的TAG中的值需要按照下面表格进行:

2

  • 如果L1 指令cache 大小为 32KB, 那么读取它的TAG中的值需要按照下面表格进行:

3

  • 如果L1 指令cache 大小为 64KB, 那么读取它的 CacheLine 中的数据 需要按照下面表格进行:

1

  • 如果L1 指令cache 大小为 32KB, 那么读取它的 CacheLine 中的数据 需要按照下面表格进行:

1
2

  • 如果要读取TAG中的内容,对于64KB的L1 Cache, 如果虚拟地址域使用的bit范围超过bit13,或者对于32KB 的L1 Cache 其虚拟地址使用的bit 范围超过bit12,那么就需要按照下面表格来配置:

1

  • 如果要读取Cache Line 中的内容,对于64KB的L1 Cache, 如果虚拟地址域使用的bit范围超过bit13,或者对于32KB 的L1 Cache 其虚拟地址使用的bit 范围超过bit12,那么就需要按照下面表格来配置:

1

L1 Cache Data 寄存器

在通过上节寄存器的配置选择好读取 TAG内容还是 Cacheline 之后,就可以来读取数据了,数据的读取是通过下面寄存器进行的:

2

3

前面三行是给L1 指令cache使用的,后面三行是给 L1 data cache 使用的。我们继续看下指令寄存器数据格式。

  • 对于L1 指令cache TAG内容使用下表内容解析

4

  • 对于L1 data cache TAG内容使用下表内容解析
    1

  • 对于L1 data cache cacheline 中的内容使用下表内容解析

2

对于A520 其读取指令与A720有所差异,如下:

3

下面以读取A520 L1 Data Cache 中的内容为例进行简单介绍如何读取。

Cache 数据读取代码实现

假设A520 L1 data cache 的 CacheLine 的大小为64bytes, Cache 有 4 路(way) 128组(set),由于组数比较多,这里只读去第0组,代码如下:

void a520_cache_read(void)
{
        *(uint64_t *)(0x90000000) = 0x5a5a5a5a5a;

        for (int way = 0; way < 4; way++) {
                for (int offset = 0; offset < 8; offset ++) {
                        uint64_t val = cache_read(0, way, offset);
                        log_info("set %d, way %d, offset %d, val %lx\n", 0, way, offset, val);
                }
        }
}

L1 Data cache 的读取操作应该按照下面格式去配置
1

2

汇编代码实现,如下:

/*
 * w0: set
 * w1: way
 * w2: offset
 * /
cache_read:
    LSL  w0, w0, #6
    LSL  w1, w1, #30
    LSL  w2, w2, #3
    orr  w0, w0, w1
    orr  w0, w0, w2
    sys #6,c15,c4,#0, x0
    isb
    mrs x0, S3_6_C15_C0_0
    ret

bit[5:3] 一共2bits可以组成 2 2 =4 种选择, 由于cacheline 为64bytes 且每次只能读取8bytes,所以需要通过offset来决定读取哪8bytes。

读取完成后需要按照下面格式解析:

3

测试结果

可以看到第0组,第1路,offset 为0的cacheline 中存放的数据是写入的数据。

1

posted @ 2025-08-15 20:12  yooooooo  阅读(60)  评论(0)    收藏  举报