Files
csapp2025/cachelab/Cache.c
2025-04-25 08:29:08 +08:00

996 lines
35 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "common.h"
#include <stdio.h>
#define DEBUG 0
#define GET_POWER_OF_2(X) __builtin_ctz(X)
/*
组相联映射Data Cache16KB大小
每行存放16个字节共1024行
*/
#define DCACHE_LINE_PER_SET 256
#define DCACHE_SIZE 16384
#define DCACHE_DATA_PER_LINE 16 // 必须是8字节的倍数
#define DCACHE_DATA_PER_LINE_ADDR_BITS \
GET_POWER_OF_2( \
DCACHE_DATA_PER_LINE) // 必须与上面设置一致即64字节需要6位地址
#define DCACHE_SET (DCACHE_SIZE / DCACHE_DATA_PER_LINE / DCACHE_LINE_PER_SET)
#define DCACHE_SET_ADDR_BITS \
GET_POWER_OF_2(DCACHE_SET) // 必须与上面设置一致即256行需要8位地址
/*
L2 Cache配置1MB大小
每行存放128个字节共4096行
*/
#define L2CACHE_LINE_PER_SET 4096
#define L2CACHE_SIZE 1048576
#define L2CACHE_DATA_PER_LINE 128 // 必须是8字节的倍数
#define L2CACHE_DATA_PER_LINE_ADDR_BITS GET_POWER_OF_2(L2CACHE_DATA_PER_LINE)
#define L2CACHE_SET \
(L2CACHE_SIZE / L2CACHE_DATA_PER_LINE / L2CACHE_LINE_PER_SET)
#define L2CACHE_SET_ADDR_BITS GET_POWER_OF_2(L2CACHE_SET)
/*
组相联映射Instruction Cache16KB大小
每行存放16个字节共1024行
*/
#define ICACHE_LINE_PER_SET 64
#define ICACHE_SIZE 16384
#define ICACHE_DATA_PER_LINE 16 // 必须是8字节的倍数
#define ICACHE_DATA_PER_LINE_ADDR_BITS GET_POWER_OF_2(ICACHE_DATA_PER_LINE)
#define ICACHE_SET (ICACHE_SIZE / ICACHE_DATA_PER_LINE / ICACHE_LINE_PER_SET)
#define ICACHE_SET_ADDR_BITS GET_POWER_OF_2(ICACHE_SET)
// DCache行的结构
struct DCACHE_LineStruct {
UINT8 Valid;
UINT8 Age;
UINT8 Dirty;
UINT64 Tag;
UINT8 Data[DCACHE_DATA_PER_LINE];
};
struct DCACHE_Set {
struct DCACHE_LineStruct Line[DCACHE_LINE_PER_SET];
} DCache[DCACHE_SET];
// L2Cache行的结构
struct L2CACHE_LineStruct {
UINT8 Valid;
UINT8 Age;
UINT8 Dirty;
UINT64 Tag;
UINT8 Data[L2CACHE_DATA_PER_LINE];
};
struct L2CACHE_Set {
struct L2CACHE_LineStruct Line[L2CACHE_LINE_PER_SET];
} L2Cache[L2CACHE_SET];
// ICache行的结构
struct ICACHE_LineStruct {
UINT8 Valid;
UINT8 Age;
UINT64 Tag;
UINT8 Data[ICACHE_DATA_PER_LINE];
};
struct ICACHE_Set {
struct ICACHE_LineStruct Line[ICACHE_LINE_PER_SET];
} ICache[ICACHE_SET];
// 函数声明部分 - 防止隐式声明错误
void LoadDataCacheLineFromMemory(UINT64 Address, UINT32 set, UINT8 line);
void StoreDataCacheLineToMemory(UINT64 Address, UINT32 set, UINT8 line);
void LoadInstCacheLineFromMemory(UINT64 Address, UINT32 set, UINT8 line);
void InitL2Cache(void);
/*
DCache初始化代码一般需要把DCache的有效位Valid设置为0
模拟器启动时会调用此InitDataCache函数
*/
void InitDataCache() {
UINT32 i, j;
printf("[%s] +-----------------------------------+\n", __func__);
printf("[%s] | Cikki 的Data Cache初始化ing.... |\n", __func__);
printf("[%s] +-----------------------------------+\n", __func__);
for (i = 0; i < DCACHE_SET; i++) {
for (j = 0; j < DCACHE_LINE_PER_SET; j++) {
DCache[i].Line[j].Valid = 0;
DCache[i].Line[j].Dirty = 0;
DCache[i].Line[j].Tag = 0;
DCache[i].Line[j].Age = j;
}
}
}
/*
L2Cache初始化代码
*/
void InitL2Cache() {
UINT32 i, j;
printf("[%s] +-----------------------------------+\n", __func__);
printf("[%s] | Cikki 的L2 Cache初始化ing.... |\n", __func__);
printf("[%s] +-----------------------------------+\n", __func__);
for (i = 0; i < L2CACHE_SET; i++) {
for (j = 0; j < L2CACHE_LINE_PER_SET; j++) {
L2Cache[i].Line[j].Valid = 0;
L2Cache[i].Line[j].Dirty = 0;
L2Cache[i].Line[j].Tag = 0;
L2Cache[i].Line[j].Age = j;
}
}
}
/*
ICache初始化代码
*/
void InitInstCache(void) {
UINT32 i, j;
printf("[%s] +-----------------------------------+\n", __func__);
printf("[%s] | Cikki 的Inst Cache初始化ing.... |\n", __func__);
printf("[%s] +-----------------------------------+\n", __func__);
for (i = 0; i < ICACHE_SET; i++) {
for (j = 0; j < ICACHE_LINE_PER_SET; j++) {
ICache[i].Line[j].Valid = 0;
ICache[i].Line[j].Tag = 0;
ICache[i].Line[j].Age = j;
}
}
// 初始化L2缓存
InitL2Cache();
}
// 在第Set组中从DCACHE_LINE_PER_SET路中找到需要替换的Cache行
// 如果DCACHE_LINE_PER_SET行中有某行的Valid=0则返回该行行号
// 否则返回Age最大的行的行号
UINT8 GetReplaceLineData(UINT32 Set) {
int max_index = 0;
int max_age = -1;
for (int i = 0; i < DCACHE_LINE_PER_SET; i++) {
if (DCache[Set].Line[i].Valid == 0)
return i;
if (DCache[Set].Line[i].Age > max_age) {
max_index = i;
max_age = DCache[Set].Line[i].Age;
}
}
return max_index;
}
// 在L2 Cache的第Set组中找到需要替换的行
UINT8 GetReplaceLineL2(UINT32 Set) {
int max_index = 0;
int max_age = -1;
for (int i = 0; i < L2CACHE_LINE_PER_SET; i++) {
if (L2Cache[Set].Line[i].Valid == 0)
return i;
if (L2Cache[Set].Line[i].Age > max_age) {
max_index = i;
max_age = L2Cache[Set].Line[i].Age;
}
}
return max_index;
}
// LRU替换策略 - ICache
UINT8 GetReplaceLineInst(UINT32 Set) {
int max_index = 0;
int max_age = -1;
for (int i = 0; i < ICACHE_LINE_PER_SET; i++) {
if (ICache[Set].Line[i].Valid == 0)
return i;
if (ICache[Set].Line[i].Age > max_age) {
max_index = i;
max_age = ICache[Set].Line[i].Age;
}
}
return max_index;
}
// 更新Age在第Set组中将HitLine指定的Cache行的Age设置为0其他行的Age要相应调整
// 确保DCACHE_LINE_PER_SET行的Age分别为0~DCACHE_LINE_PER_SET-1且唯一
void UpdateAgeData(UINT32 Set, UINT8 HitLine) {
UINT8 old_age = DCache[Set].Line[HitLine].Age;
for (int i = 0; i < DCACHE_LINE_PER_SET; ++i) {
if (DCache[Set].Line[i].Age < old_age)
DCache[Set].Line[i].Age++;
}
DCache[Set].Line[HitLine].Age = 0;
}
// 更新L2 Cache的Age
void UpdateAgeL2(UINT32 Set, UINT8 HitLine) {
UINT8 old_age = L2Cache[Set].Line[HitLine].Age;
for (int i = 0; i < L2CACHE_LINE_PER_SET; ++i) {
if (L2Cache[Set].Line[i].Age < old_age)
L2Cache[Set].Line[i].Age++;
}
L2Cache[Set].Line[HitLine].Age = 0;
}
// 更新Age - ICache
void UpdateAgeInst(UINT32 Set, UINT8 HitLine) {
int HitAge = ICache[Set].Line[HitLine].Age;
ICache[Set].Line[HitLine].Age = 0;
for (int i = 0; i < ICACHE_LINE_PER_SET; i++) {
if (i != HitLine && ICache[Set].Line[i].Age < HitAge)
ICache[Set].Line[i].Age++;
}
}
/*
从Memory中读入一行数据到Data Cache中
*/
void LoadDataCacheLineFromMemory(UINT64 Address, UINT32 set, UINT8 line) {
// 一次性从Memory中将DCACHE_DATA_PER_LINE数据读入某个Data Cache行
// 提供了一个函数一次可以读入8个字节
// 地址对齐到缓存行边界
const UINT64 AlignAddress = Address & ~(DCACHE_DATA_PER_LINE - 1);
UINT64 *const cache_line_ptr = (UINT64 *)DCache[set].Line[line].Data;
if (DEBUG) {
printf("[%s] Loading cache line (set=%u, line=%u) from memory %016llX\n",
__func__, set, line, AlignAddress);
}
// 分8字节块读取
for (UINT32 i = 0; i < DCACHE_DATA_PER_LINE / sizeof(UINT64); i++) {
const UINT64 read_addr = AlignAddress + i * sizeof(UINT64);
const UINT64 data = ReadMemory(read_addr);
cache_line_ptr[i] = data;
if (DEBUG) {
printf(" [LOAD] Address=%016llX -> Data=%016llX\n", read_addr, data);
}
}
}
/*
从内存加载一行数据到指令缓存中
*/
void LoadInstCacheLineFromMemory(UINT64 Address, UINT32 set, UINT8 line) {
// 一次性从Memory中将ICACHE_DATA_PER_LINE数据读入某个Instruction Cache行
// 提供了一个函数一次可以读入8个字节
// 地址对齐到缓存行边界
const UINT64 AlignAddress = Address & ~(ICACHE_DATA_PER_LINE - 1);
UINT64 *const cache_line_ptr = (UINT64 *)ICache[set].Line[line].Data;
if (DEBUG) {
printf("[%s] Loading cache line (set=%u, line=%u) from memory %016llX\n",
__func__, set, line, AlignAddress);
}
// 分8字节块读取
for (UINT32 i = 0; i < ICACHE_DATA_PER_LINE / sizeof(UINT64); i++) {
const UINT64 read_addr = AlignAddress + i * sizeof(UINT64);
const UINT64 data = ReadMemory(read_addr);
cache_line_ptr[i] = data;
if (DEBUG) {
printf(" [LOAD] Address=%016llX -> Data=%016llX\n", read_addr, data);
}
}
}
/*
将Data Cache中的一行数据写入存储器
*/
void StoreDataCacheLineToMemory(UINT64 Address, UINT32 set, UINT8 line) {
// 一次性将DCACHE_DATA_PER_LINE数据从某个Data Cache行写入Memory中
// 提供了一个函数一次可以写入8个字节
// 地址对齐到缓存行边界
const UINT64 AlignAddress = Address & ~(DCACHE_DATA_PER_LINE - 1);
UINT64 *const cache_line_ptr = (UINT64 *)DCache[set].Line[line].Data;
if (DEBUG) {
printf("[%s] Storing cache line (set=%u, line=%u) to memory %016llX\n",
__func__, set, line, AlignAddress);
}
// 分8字节块写入
for (UINT32 i = 0; i < DCACHE_DATA_PER_LINE / sizeof(UINT64); i++) {
const UINT64 write_addr = AlignAddress + i * sizeof(UINT64);
const UINT64 data = cache_line_ptr[i];
WriteMemory(write_addr, data);
if (DEBUG) {
printf(" [STORE] Address=%016llX <- Data=%016llX\n", write_addr, data);
}
}
}
/*
从L2 Cache中读取数据到L1 DCache中
如果L2命中则从L2读取数据
如果L2未命中则从内存读取数据并更新L2
*/
UINT8 LoadDataFromL2ToL1(UINT64 Address, UINT32 L1_set, UINT8 L1_line) {
UINT32 L2_set = (Address >> L2CACHE_DATA_PER_LINE_ADDR_BITS) % L2CACHE_SET;
UINT64 L2_tag =
Address >> (L2CACHE_DATA_PER_LINE_ADDR_BITS + L2CACHE_SET_ADDR_BITS);
UINT8 L2_hit = 0;
UINT8 L2_line = 0;
// 检查L2 Cache是否命中
for (int i = 0; i < L2CACHE_LINE_PER_SET; i++) {
if (L2Cache[L2_set].Line[i].Valid &&
L2Cache[L2_set].Line[i].Tag == L2_tag) {
L2_hit = 1;
L2_line = i;
break;
}
}
if (L2_hit) {
// L2命中从L2读取数据到L1
if (DEBUG) {
printf("[%s] L2 Cache hit! Loading from L2 to L1 (Address=%016llX)\n",
__func__, Address);
}
// 由于L2缓存行可能比L1大需要找到正确的偏移量
UINT64 L1_aligned_addr = Address & ~(DCACHE_DATA_PER_LINE - 1);
UINT64 L2_aligned_addr = Address & ~(L2CACHE_DATA_PER_LINE - 1);
UINT32 offset_in_L2 = (L1_aligned_addr - L2_aligned_addr);
// 从L2复制数据到L1
for (int i = 0; i < DCACHE_DATA_PER_LINE; i++) {
DCache[L1_set].Line[L1_line].Data[i] =
L2Cache[L2_set].Line[L2_line].Data[offset_in_L2 + i];
}
// 更新L2的LRU信息
UpdateAgeL2(L2_set, L2_line);
return 'H'; // L2命中
} else {
// L2未命中从内存加载数据
if (DEBUG) {
printf("[%s] L2 Cache miss! Loading from memory (Address=%016llX)\n",
__func__, Address);
}
// 首先从内存加载到L1
LoadDataCacheLineFromMemory(Address, L1_set, L1_line);
// 然后更新L2
UINT8 L2_replace_line = GetReplaceLineL2(L2_set);
// 如果L2中要替换的行是脏的需要先写回内存
if (L2Cache[L2_set].Line[L2_replace_line].Valid &&
L2Cache[L2_set].Line[L2_replace_line].Dirty) {
UINT64 victim_addr =
(L2Cache[L2_set].Line[L2_replace_line].Tag
<< (L2CACHE_DATA_PER_LINE_ADDR_BITS + L2CACHE_SET_ADDR_BITS)) |
(L2_set << L2CACHE_DATA_PER_LINE_ADDR_BITS);
// 写回内存
for (UINT32 i = 0; i < L2CACHE_DATA_PER_LINE / sizeof(UINT64); i++) {
const UINT64 write_addr = victim_addr + i * sizeof(UINT64);
const UINT64 *data_ptr = (UINT64 *)&L2Cache[L2_set]
.Line[L2_replace_line]
.Data[i * sizeof(UINT64)];
WriteMemory(write_addr, *data_ptr);
}
}
// 从内存加载数据到L2
UINT64 L2_aligned_addr = Address & ~(L2CACHE_DATA_PER_LINE - 1);
for (UINT32 i = 0; i < L2CACHE_DATA_PER_LINE / sizeof(UINT64); i++) {
const UINT64 read_addr = L2_aligned_addr + i * sizeof(UINT64);
const UINT64 data = ReadMemory(read_addr);
UINT64 *data_ptr = (UINT64 *)&L2Cache[L2_set]
.Line[L2_replace_line]
.Data[i * sizeof(UINT64)];
*data_ptr = data;
}
// 更新L2缓存行信息
L2Cache[L2_set].Line[L2_replace_line].Valid = 1;
L2Cache[L2_set].Line[L2_replace_line].Dirty = 0;
L2Cache[L2_set].Line[L2_replace_line].Tag = L2_tag;
UpdateAgeL2(L2_set, L2_replace_line);
return 'M'; // L2未命中
}
}
/*
将L1 DCache中的一行数据写入L2 Cache
*/
void StoreDataFromL1ToL2(UINT64 Address, UINT32 L1_set, UINT8 L1_line) {
UINT32 L2_set = (Address >> L2CACHE_DATA_PER_LINE_ADDR_BITS) % L2CACHE_SET;
UINT64 L2_tag =
Address >> (L2CACHE_DATA_PER_LINE_ADDR_BITS + L2CACHE_SET_ADDR_BITS);
UINT8 L2_hit = 0;
UINT8 L2_line = 0;
// 检查L2 Cache是否命中
for (int i = 0; i < L2CACHE_LINE_PER_SET; i++) {
if (L2Cache[L2_set].Line[i].Valid &&
L2Cache[L2_set].Line[i].Tag == L2_tag) {
L2_hit = 1;
L2_line = i;
break;
}
}
if (!L2_hit) {
// L2未命中需要分配一个新的L2缓存行
L2_line = GetReplaceLineL2(L2_set);
// 如果要替换的L2行是脏的需要先写回内存
if (L2Cache[L2_set].Line[L2_line].Valid &&
L2Cache[L2_set].Line[L2_line].Dirty) {
UINT64 victim_addr =
(L2Cache[L2_set].Line[L2_line].Tag
<< (L2CACHE_DATA_PER_LINE_ADDR_BITS + L2CACHE_SET_ADDR_BITS)) |
(L2_set << L2CACHE_DATA_PER_LINE_ADDR_BITS);
// 写回内存
for (UINT32 i = 0; i < L2CACHE_DATA_PER_LINE / sizeof(UINT64); i++) {
const UINT64 write_addr = victim_addr + i * sizeof(UINT64);
const UINT64 *data_ptr =
(UINT64 *)&L2Cache[L2_set].Line[L2_line].Data[i * sizeof(UINT64)];
WriteMemory(write_addr, *data_ptr);
}
}
// 从内存加载完整的L2缓存行
UINT64 L2_aligned_addr = Address & ~(L2CACHE_DATA_PER_LINE - 1);
for (UINT32 i = 0; i < L2CACHE_DATA_PER_LINE / sizeof(UINT64); i++) {
const UINT64 read_addr = L2_aligned_addr + i * sizeof(UINT64);
const UINT64 data = ReadMemory(read_addr);
UINT64 *data_ptr =
(UINT64 *)&L2Cache[L2_set].Line[L2_line].Data[i * sizeof(UINT64)];
*data_ptr = data;
}
L2Cache[L2_set].Line[L2_line].Valid = 1;
L2Cache[L2_set].Line[L2_line].Tag = L2_tag;
}
// 更新L2缓存行中对应的数据
UINT64 L1_aligned_addr = Address & ~(DCACHE_DATA_PER_LINE - 1);
UINT64 L2_aligned_addr = Address & ~(L2CACHE_DATA_PER_LINE - 1);
UINT32 offset_in_L2 = (L1_aligned_addr - L2_aligned_addr);
// 从L1复制数据到L2
for (int i = 0; i < DCACHE_DATA_PER_LINE; i++) {
L2Cache[L2_set].Line[L2_line].Data[offset_in_L2 + i] =
DCache[L1_set].Line[L1_line].Data[i];
}
// 标记L2缓存行为脏
L2Cache[L2_set].Line[L2_line].Dirty = 1;
UpdateAgeL2(L2_set, L2_line);
}
/*
从L2 Cache中加载指令到I-Cache
如果L2缓存命中则从L2读取数据
如果L2缓存未命中则从内存读取数据并同时更新L2缓存
*/
UINT8 LoadInstFromL2ToICache(UINT64 Address, UINT32 I_set, UINT8 I_line) {
UINT32 L2_set = (Address >> L2CACHE_DATA_PER_LINE_ADDR_BITS) % L2CACHE_SET;
UINT64 L2_tag =
Address >> (L2CACHE_DATA_PER_LINE_ADDR_BITS + L2CACHE_SET_ADDR_BITS);
UINT8 L2_hit = 0;
UINT8 L2_line = 0;
// 检查L2 Cache是否命中
for (int i = 0; i < L2CACHE_LINE_PER_SET; i++) {
if (L2Cache[L2_set].Line[i].Valid &&
L2Cache[L2_set].Line[i].Tag == L2_tag) {
L2_hit = 1;
L2_line = i;
break;
}
}
if (L2_hit) {
// L2命中从L2读取数据到I-Cache
if (DEBUG) {
printf("[%s] L2 Cache hit! Loading instruction from L2 to I-Cache "
"(Address=%016llX)\n",
__func__, Address);
}
// 由于L2缓存行可能比I-Cache大需要找到正确的偏移量
UINT64 I_aligned_addr = Address & ~(ICACHE_DATA_PER_LINE - 1);
UINT64 L2_aligned_addr = Address & ~(L2CACHE_DATA_PER_LINE - 1);
UINT32 offset_in_L2 = (I_aligned_addr - L2_aligned_addr);
// 从L2复制数据到I-Cache
for (int i = 0; i < ICACHE_DATA_PER_LINE; i++) {
ICache[I_set].Line[I_line].Data[i] =
L2Cache[L2_set].Line[L2_line].Data[offset_in_L2 + i];
}
// 更新L2的LRU信息
UpdateAgeL2(L2_set, L2_line);
return 'H'; // L2命中
} else {
// L2未命中从内存加载数据
if (DEBUG) {
printf("[%s] L2 Cache miss! Loading instruction from memory "
"(Address=%016llX)\n",
__func__, Address);
}
// 首先从内存加载到I-Cache
LoadInstCacheLineFromMemory(Address, I_set, I_line);
// 然后更新L2
UINT8 L2_replace_line = GetReplaceLineL2(L2_set);
// 如果L2中要替换的行是脏的需要先写回内存
if (L2Cache[L2_set].Line[L2_replace_line].Valid &&
L2Cache[L2_set].Line[L2_replace_line].Dirty) {
UINT64 victim_addr =
(L2Cache[L2_set].Line[L2_replace_line].Tag
<< (L2CACHE_DATA_PER_LINE_ADDR_BITS + L2CACHE_SET_ADDR_BITS)) |
(L2_set << L2CACHE_DATA_PER_LINE_ADDR_BITS);
// 写回内存
for (UINT32 i = 0; i < L2CACHE_DATA_PER_LINE / sizeof(UINT64); i++) {
const UINT64 write_addr = victim_addr + i * sizeof(UINT64);
const UINT64 *data_ptr = (UINT64 *)&L2Cache[L2_set]
.Line[L2_replace_line]
.Data[i * sizeof(UINT64)];
WriteMemory(write_addr, *data_ptr);
}
}
// 从内存加载数据到L2
UINT64 L2_aligned_addr = Address & ~(L2CACHE_DATA_PER_LINE - 1);
for (UINT32 i = 0; i < L2CACHE_DATA_PER_LINE / sizeof(UINT64); i++) {
const UINT64 read_addr = L2_aligned_addr + i * sizeof(UINT64);
const UINT64 data = ReadMemory(read_addr);
UINT64 *data_ptr = (UINT64 *)&L2Cache[L2_set]
.Line[L2_replace_line]
.Data[i * sizeof(UINT64)];
*data_ptr = data;
}
// 更新L2缓存行信息
L2Cache[L2_set].Line[L2_replace_line].Valid = 1;
L2Cache[L2_set].Line[L2_replace_line].Dirty = 0;
L2Cache[L2_set].Line[L2_replace_line].Tag = L2_tag;
UpdateAgeL2(L2_set, L2_replace_line);
return 'M'; // L2未命中
}
}
/*
* 访问数据缓存的主函数
* 参数:
* Address - 访问的内存地址
* Operation - 'L'表示读取,'S'表示写入,'M'表示修改(先读后写)
* DataSize - 访问的数据大小1、2、4或8字节
* StoreValue - 要写入的数据(如果是写入操作)
* LoadResult - 指向读取结果的指针(如果是读取操作)
* 返回值:
* 'H' - 缓存命中
* 'M' - 缓存未命中
*/
UINT8 AccessDataCache(UINT64 Address, UINT8 Operation, UINT8 DataSize,
UINT64 StoreValue, UINT64 *LoadResult) {
UINT32 Set = (Address >> DCACHE_DATA_PER_LINE_ADDR_BITS) % DCACHE_SET;
UINT8 Block = Address % DCACHE_DATA_PER_LINE;
UINT64 Tag =
Address >> (DCACHE_DATA_PER_LINE_ADDR_BITS + DCACHE_SET_ADDR_BITS);
UINT8 HitLine;
UINT8 MissFlag = 'M';
UINT64 ReadValue = 0;
char L2Result;
// 检查命中
for (int i = 0; i < DCACHE_LINE_PER_SET; i++) {
if (DCache[Set].Line[i].Valid && DCache[Set].Line[i].Tag == Tag) {
HitLine = i;
MissFlag = 'H';
break;
}
}
if (MissFlag == 'H') // Cache命中
{
if (Operation == 'L') // 读操作
{
// 读取数据注意这里要按DataSize读取
switch (DataSize) {
case 1: // 1个字节
ReadValue = DCache[Set].Line[HitLine].Data[Block];
break;
case 2: // 2个字节
Block = Block & 0xFE; // 需对齐到2字节边界
ReadValue = DCache[Set].Line[HitLine].Data[Block + 1];
ReadValue = ReadValue << 8;
ReadValue |= DCache[Set].Line[HitLine].Data[Block + 0];
break;
case 4: // 4个字节
Block = Block & 0xFC; // 需对齐到4字节边界
ReadValue = DCache[Set].Line[HitLine].Data[Block + 3];
ReadValue = ReadValue << 8;
ReadValue |= DCache[Set].Line[HitLine].Data[Block + 2];
ReadValue = ReadValue << 8;
ReadValue |= DCache[Set].Line[HitLine].Data[Block + 1];
ReadValue = ReadValue << 8;
ReadValue |= DCache[Set].Line[HitLine].Data[Block + 0];
break;
case 8: // 8个字节
Block = Block & 0xF8; // 需对齐到8字节边界
ReadValue = DCache[Set].Line[HitLine].Data[Block + 7];
ReadValue = ReadValue << 8;
ReadValue |= DCache[Set].Line[HitLine].Data[Block + 6];
ReadValue = ReadValue << 8;
ReadValue |= DCache[Set].Line[HitLine].Data[Block + 5];
ReadValue = ReadValue << 8;
ReadValue |= DCache[Set].Line[HitLine].Data[Block + 4];
ReadValue = ReadValue << 8;
ReadValue |= DCache[Set].Line[HitLine].Data[Block + 3];
ReadValue = ReadValue << 8;
ReadValue |= DCache[Set].Line[HitLine].Data[Block + 2];
ReadValue = ReadValue << 8;
ReadValue |= DCache[Set].Line[HitLine].Data[Block + 1];
ReadValue = ReadValue << 8;
ReadValue |= DCache[Set].Line[HitLine].Data[Block + 0];
break;
}
*LoadResult = ReadValue;
if (DEBUG) {
printf("[%s] Address=%016llX Operation=%c DataSize=%u "
"StoreValue=%016llX ReadValue=%016llX\n",
__func__, Address, Operation, DataSize, StoreValue, ReadValue);
}
} else if (Operation == 'S' ||
Operation == 'M') // 写操作(修改操作在此等价于写操作)
{
if (DEBUG) {
printf("[%s] Address=%016llX Operation=%c DataSize=%u "
"StoreValue=%016llX\n",
__func__, Address, Operation, DataSize, StoreValue);
}
// 写操作需要将新的StoreValue更新到CacheLine中
switch (DataSize) {
case 1: // 1个字节
DCache[Set].Line[HitLine].Data[Block + 0] = StoreValue & 0xFF;
break;
case 2: // 2个字节
Block = Block & 0xFE; // 需对齐到2字节边界
DCache[Set].Line[HitLine].Data[Block + 0] = StoreValue & 0xFF;
StoreValue = StoreValue >> 8;
DCache[Set].Line[HitLine].Data[Block + 1] = StoreValue & 0xFF;
break;
case 4: // 4个字节
Block = Block & 0xFC; // 需对齐到4字节边界
DCache[Set].Line[HitLine].Data[Block + 0] = StoreValue & 0xFF;
StoreValue = StoreValue >> 8;
DCache[Set].Line[HitLine].Data[Block + 1] = StoreValue & 0xFF;
StoreValue = StoreValue >> 8;
DCache[Set].Line[HitLine].Data[Block + 2] = StoreValue & 0xFF;
StoreValue = StoreValue >> 8;
DCache[Set].Line[HitLine].Data[Block + 3] = StoreValue & 0xFF;
break;
case 8: // 8个字节
Block = Block & 0xF8; // 需对齐到8字节边界
DCache[Set].Line[HitLine].Data[Block + 0] = StoreValue & 0xFF;
StoreValue = StoreValue >> 8;
DCache[Set].Line[HitLine].Data[Block + 1] = StoreValue & 0xFF;
StoreValue = StoreValue >> 8;
DCache[Set].Line[HitLine].Data[Block + 2] = StoreValue & 0xFF;
StoreValue = StoreValue >> 8;
DCache[Set].Line[HitLine].Data[Block + 3] = StoreValue & 0xFF;
StoreValue = StoreValue >> 8;
DCache[Set].Line[HitLine].Data[Block + 4] = StoreValue & 0xFF;
StoreValue = StoreValue >> 8;
DCache[Set].Line[HitLine].Data[Block + 5] = StoreValue & 0xFF;
StoreValue = StoreValue >> 8;
DCache[Set].Line[HitLine].Data[Block + 6] = StoreValue & 0xFF;
StoreValue = StoreValue >> 8;
DCache[Set].Line[HitLine].Data[Block + 7] = StoreValue & 0xFF;
break;
}
DCache[Set].Line[HitLine].Dirty = 1;
// 写直达同时更新L2
StoreDataFromL1ToL2(Address, Set, HitLine);
}
UpdateAgeData(Set, HitLine);
} else // Cache未命中
{
UINT8 replace_Line = GetReplaceLineData(Set);
if (DEBUG) {
printf(
"[%s] Address=%016llX Operation=%c DataSize=%u StoreValue=%016llX\n",
__func__, Address, Operation, DataSize, StoreValue);
}
if (Operation == 'L') // 读操作
{
// 写回脏行到L2
if (DCache[Set].Line[replace_Line].Valid &&
DCache[Set].Line[replace_Line].Dirty) {
UINT64 victim_addr =
(DCache[Set].Line[replace_Line].Tag
<< (DCACHE_DATA_PER_LINE_ADDR_BITS + DCACHE_SET_ADDR_BITS)) |
(Set << DCACHE_DATA_PER_LINE_ADDR_BITS);
StoreDataFromL1ToL2(victim_addr, Set, replace_Line);
}
// 先从L2加载新行到L1
L2Result = LoadDataFromL2ToL1(Address, Set, replace_Line);
DCache[Set].Line[replace_Line].Valid = 1;
DCache[Set].Line[replace_Line].Tag = Tag;
DCache[Set].Line[replace_Line].Dirty = 0;
// 再读取数据按DataSize读取
switch (DataSize) {
case 1: // 1个字节
ReadValue = DCache[Set].Line[replace_Line].Data[Block];
break;
case 2: // 2个字节
Block = Block & 0xFE; // 需对齐到2字节边界
ReadValue = DCache[Set].Line[replace_Line].Data[Block + 1];
ReadValue = ReadValue << 8;
ReadValue |= DCache[Set].Line[replace_Line].Data[Block + 0];
break;
case 4: // 4个字节
Block = Block & 0xFC; // 需对齐到4字节边界
ReadValue = DCache[Set].Line[replace_Line].Data[Block + 3];
ReadValue = ReadValue << 8;
ReadValue |= DCache[Set].Line[replace_Line].Data[Block + 2];
ReadValue = ReadValue << 8;
ReadValue |= DCache[Set].Line[replace_Line].Data[Block + 1];
ReadValue = ReadValue << 8;
ReadValue |= DCache[Set].Line[replace_Line].Data[Block + 0];
break;
case 8: // 8个字节
Block = Block & 0xF8; // 需对齐到8字节边界
ReadValue = DCache[Set].Line[replace_Line].Data[Block + 7];
ReadValue = ReadValue << 8;
ReadValue |= DCache[Set].Line[replace_Line].Data[Block + 6];
ReadValue = ReadValue << 8;
ReadValue |= DCache[Set].Line[replace_Line].Data[Block + 5];
ReadValue = ReadValue << 8;
ReadValue |= DCache[Set].Line[replace_Line].Data[Block + 4];
ReadValue = ReadValue << 8;
ReadValue |= DCache[Set].Line[replace_Line].Data[Block + 3];
ReadValue = ReadValue << 8;
ReadValue |= DCache[Set].Line[replace_Line].Data[Block + 2];
ReadValue = ReadValue << 8;
ReadValue |= DCache[Set].Line[replace_Line].Data[Block + 1];
ReadValue = ReadValue << 8;
ReadValue |= DCache[Set].Line[replace_Line].Data[Block + 0];
break;
}
*LoadResult = ReadValue;
} else if (Operation == 'S' ||
Operation == 'M') // 写操作(修改操作在此等价于写操作)
{
// 写回脏行到L2
if (DCache[Set].Line[replace_Line].Valid &&
DCache[Set].Line[replace_Line].Dirty) {
UINT64 victim_addr =
(DCache[Set].Line[replace_Line].Tag
<< (DCACHE_DATA_PER_LINE_ADDR_BITS + DCACHE_SET_ADDR_BITS)) |
(Set << DCACHE_DATA_PER_LINE_ADDR_BITS);
StoreDataFromL1ToL2(victim_addr, Set, replace_Line);
}
// 先从L2加载新行到L1
L2Result = LoadDataFromL2ToL1(Address, Set, replace_Line);
DCache[Set].Line[replace_Line].Valid = 1;
DCache[Set].Line[replace_Line].Tag = Tag;
DCache[Set].Line[replace_Line].Dirty = 0;
// 写操作需要将新的StoreValue更新到CacheLine中
switch (DataSize) {
case 1: // 1个字节
DCache[Set].Line[replace_Line].Data[Block + 0] = StoreValue & 0xFF;
break;
case 2: // 2个字节
Block = Block & 0xFE; // 需对齐到2字节边界
DCache[Set].Line[replace_Line].Data[Block + 0] = StoreValue & 0xFF;
StoreValue = StoreValue >> 8;
DCache[Set].Line[replace_Line].Data[Block + 1] = StoreValue & 0xFF;
break;
case 4: // 4个字节
Block = Block & 0xFC; // 需对齐到4字节边界
DCache[Set].Line[replace_Line].Data[Block + 0] = StoreValue & 0xFF;
StoreValue = StoreValue >> 8;
DCache[Set].Line[replace_Line].Data[Block + 1] = StoreValue & 0xFF;
StoreValue = StoreValue >> 8;
DCache[Set].Line[replace_Line].Data[Block + 2] = StoreValue & 0xFF;
StoreValue = StoreValue >> 8;
DCache[Set].Line[replace_Line].Data[Block + 3] = StoreValue & 0xFF;
break;
case 8: // 8个字节
Block = Block & 0xF8; // 需对齐到8字节边界
DCache[Set].Line[replace_Line].Data[Block + 0] = StoreValue & 0xFF;
StoreValue = StoreValue >> 8;
DCache[Set].Line[replace_Line].Data[Block + 1] = StoreValue & 0xFF;
StoreValue = StoreValue >> 8;
DCache[Set].Line[replace_Line].Data[Block + 2] = StoreValue & 0xFF;
StoreValue = StoreValue >> 8;
DCache[Set].Line[replace_Line].Data[Block + 3] = StoreValue & 0xFF;
StoreValue = StoreValue >> 8;
DCache[Set].Line[replace_Line].Data[Block + 4] = StoreValue & 0xFF;
StoreValue = StoreValue >> 8;
DCache[Set].Line[replace_Line].Data[Block + 5] = StoreValue & 0xFF;
StoreValue = StoreValue >> 8;
DCache[Set].Line[replace_Line].Data[Block + 6] = StoreValue & 0xFF;
StoreValue = StoreValue >> 8;
DCache[Set].Line[replace_Line].Data[Block + 7] = StoreValue & 0xFF;
break;
}
DCache[Set].Line[replace_Line].Dirty = 1;
// 写直达同时更新L2
StoreDataFromL1ToL2(Address, Set, replace_Line);
}
UpdateAgeData(Set, replace_Line);
}
// 返回L1的命中/未命中标志
return MissFlag;
}
/*
* 访问指令缓存的主函数
* 参数:
* Address - 访问的内存地址
* Operation - 通常只用于读取指令,但我们保留此参数以保持接口一致
* InstSize - 指令大小通常为4字节但支持1、2、4、8字节
* InstResult - 指向读取结果的指针
* 返回值:
* 'H' - 缓存命中
* 'M' - 缓存未命中
*/
UINT8 AccessInstCache(UINT64 Address, UINT8 Operation, UINT8 InstSize,
UINT64 *InstResult) {
UINT32 set = (Address >> ICACHE_DATA_PER_LINE_ADDR_BITS) % ICACHE_SET;
UINT8 block = Address % ICACHE_DATA_PER_LINE;
UINT64 tag =
Address >> (ICACHE_DATA_PER_LINE_ADDR_BITS + ICACHE_SET_ADDR_BITS);
UINT8 HitLine;
UINT8 MissFlag = 'M';
UINT64 ReadValue = 0;
UINT32 L2Result;
*InstResult = 0;
// 检查命中
for (int i = 0; i < ICACHE_LINE_PER_SET; i++) {
if (ICache[set].Line[i].Valid && ICache[set].Line[i].Tag == tag) {
HitLine = i;
MissFlag = 'H';
break;
}
}
if (MissFlag == 'H') {
// 命中处理
switch (InstSize) {
case 1: // 8位指令
block = block & 0xFF; // 对齐到1字节边界
ReadValue = ICache[set].Line[HitLine].Data[block + 0];
break;
case 2: // 16位指令
block = block & 0xFE; // 对齐到2字节边界
ReadValue = ICache[set].Line[HitLine].Data[block + 1];
ReadValue = ReadValue << 8;
ReadValue |= ICache[set].Line[HitLine].Data[block + 0];
break;
case 4: // 32位指令
block = block & 0xFC; // 对齐到4字节边界
ReadValue = ICache[set].Line[HitLine].Data[block + 3];
ReadValue = ReadValue << 8;
ReadValue |= ICache[set].Line[HitLine].Data[block + 2];
ReadValue = ReadValue << 8;
ReadValue |= ICache[set].Line[HitLine].Data[block + 1];
ReadValue = ReadValue << 8;
ReadValue |= ICache[set].Line[HitLine].Data[block + 0];
break;
case 8: // 64位指令(如RISC-V的128位指令集扩展)
block = block & 0xF8; // 对齐到8字节边界
ReadValue = ICache[set].Line[HitLine].Data[block + 7];
ReadValue = ReadValue << 8;
ReadValue |= ICache[set].Line[HitLine].Data[block + 6];
ReadValue = ReadValue << 8;
ReadValue |= ICache[set].Line[HitLine].Data[block + 5];
ReadValue = ReadValue << 8;
ReadValue |= ICache[set].Line[HitLine].Data[block + 4];
ReadValue = ReadValue << 8;
ReadValue |= ICache[set].Line[HitLine].Data[block + 3];
ReadValue = ReadValue << 8;
ReadValue |= ICache[set].Line[HitLine].Data[block + 2];
ReadValue = ReadValue << 8;
ReadValue |= ICache[set].Line[HitLine].Data[block + 1];
ReadValue = ReadValue << 8;
ReadValue |= ICache[set].Line[HitLine].Data[block + 0];
break;
default:
// 不支持的指令长度
return 'M';
}
*InstResult = ReadValue;
UpdateAgeInst(set, HitLine);
} else {
// 未命中处理
UINT8 replace_line = GetReplaceLineInst(set);
// 从L2加载数据到I-Cache
L2Result = LoadInstFromL2ToICache(Address, set, replace_line);
// 重新读取指令
switch (InstSize) {
case 1:
block = block & 0xFF;
ReadValue = ICache[set].Line[replace_line].Data[block + 0];
break;
case 2:
block = block & 0xFE;
ReadValue = ICache[set].Line[replace_line].Data[block + 1];
ReadValue = ReadValue << 8;
ReadValue |= ICache[set].Line[replace_line].Data[block + 0];
break;
case 4:
block = block & 0xFC;
ReadValue = ICache[set].Line[replace_line].Data[block + 3];
ReadValue = ReadValue << 8;
ReadValue |= ICache[set].Line[replace_line].Data[block + 2];
ReadValue = ReadValue << 8;
ReadValue |= ICache[set].Line[replace_line].Data[block + 1];
ReadValue = ReadValue << 8;
ReadValue |= ICache[set].Line[replace_line].Data[block + 0];
break;
case 8:
block = block & 0xF8;
ReadValue = ICache[set].Line[replace_line].Data[block + 7];
ReadValue = ReadValue << 8;
ReadValue |= ICache[set].Line[replace_line].Data[block + 6];
ReadValue = ReadValue << 8;
ReadValue |= ICache[set].Line[replace_line].Data[block + 5];
ReadValue = ReadValue << 8;
ReadValue |= ICache[set].Line[replace_line].Data[block + 4];
ReadValue = ReadValue << 8;
ReadValue |= ICache[set].Line[replace_line].Data[block + 3];
ReadValue = ReadValue << 8;
ReadValue |= ICache[set].Line[replace_line].Data[block + 2];
ReadValue = ReadValue << 8;
ReadValue |= ICache[set].Line[replace_line].Data[block + 1];
ReadValue = ReadValue << 8;
ReadValue |= ICache[set].Line[replace_line].Data[block + 0];
break;
default:
// 不支持的指令长度
return 'M';
}
*InstResult = ReadValue;
ICache[set].Line[replace_line].Valid = 1;
ICache[set].Line[replace_line].Tag = tag;
UpdateAgeInst(set, replace_line);
}
// 返回I-Cache的命中/未命中标志
return MissFlag;
}