diff --git a/cachelab/Cache b/cachelab/Cache new file mode 100755 index 0000000..7d65892 Binary files /dev/null and b/cachelab/Cache differ diff --git a/cachelab/Cache.bak b/cachelab/Cache.bak new file mode 100644 index 0000000..b8ab651 --- /dev/null +++ b/cachelab/Cache.bak @@ -0,0 +1,1930 @@ +#include +#include "common.h" + +// debug=1:输出额外调试信息 +int debug = 0; + +#define MAX_AGE 3 + +// 组相联Data Cache +// 容量为16384字节,4路组相联,每行容纳16个字节 +#define DCACHE_SIZE 16384 +#define DCACHE_LINES_PER_SET 4 +#define DCACHE_BYTES_PER_LINE 16 // 必须是8字节的倍数 + +// 下面参数是自动计算的,你无须需改 +#define DCACHE_BYTES_PER_LINE_ADDR_BITS GET_LOG_OF_2(DCACHE_BYTES_PER_LINE) +#define DCACHE_SET (DCACHE_SIZE/(DCACHE_BYTES_PER_LINE*DCACHE_LINES_PER_SET)) +#define DCACHE_SET_ADDR_BITS GET_LOG_OF_2(DCACHE_SET) + +// Cache行的结构,包括Valid、Age、Tag和Data。你所有的状态信息,只能记录在Cache行中! +struct DCACHE_LineStruct +{ + UINT8 Valid; + UINT8 Age; + UINT64 Tag; + UINT8 Data[DCACHE_BYTES_PER_LINE]; +}; + +struct DCACHE_SetStruct { + struct DCACHE_LineStruct Line[4]; // 4路组相联Cache,每组有4行 +} DCache[DCACHE_SET]; + + +// DCache初始化代码,模拟器启动时,会调用此InitDataCache函数 +void InitDataCache() +{ + // 遍历每个组 + for (int set_idx = 0; set_idx < DCACHE_SET; set_idx++) { + struct DCACHE_SetStruct *set = &DCache[set_idx]; + // 遍历每组中的四个Cache行 + for (int line_idx = 0; line_idx < DCACHE_LINES_PER_SET; line_idx++) { + set->Line[line_idx].Valid = 0; // 有效位置0 + set->Line[line_idx].Age = line_idx; // 年龄依次设置为0、1、2、3 + } + } +} + +// 在第Set组中,从4路中,找到需要替换的Cache行 +int GetReplaceLine(UINT32 Set) +{ + struct DCACHE_SetStruct *set = &DCache[Set]; + int invalid_line = -1; + int max_age = -1; + int replace_line = 0; + + for (int i = 0; i < DCACHE_LINES_PER_SET; i++) { + struct DCACHE_LineStruct *line = &set->Line[i]; + if (line->Valid == 0) { + // 发现无效行,记录第一个找到的无效行 + if (invalid_line == -1) { + invalid_line = i; + } + } else { + // 更新最大年龄和对应的行号 + if (line->Age > max_age) { + max_age = line->Age; + replace_line = i; + } + } + } + + // 优先返回无效行,否则返回年龄最大的有效行 + return (invalid_line != -1) ? invalid_line : replace_line; +} + +// 更新Age,在第Set组中,将HitLine指定的Cache行的Age设置为0,其他行的Age要相应调整 +void UpdateAge(UINT32 Set, int HitLine) +{ + struct DCACHE_SetStruct *set = &DCache[Set]; + UINT8 old_age = set->Line[HitLine].Age; // 保存命中行原来的年龄 + set->Line[HitLine].Age = 0; // 命中行年龄置0 + + // 调整其他行的年龄 + for (int i = 0; i < DCACHE_LINES_PER_SET; i++) { + if (i == HitLine) continue; // 跳过命中行 + if (set->Line[i].Age < old_age) { + set->Line[i].Age += 1; // 原年龄小于命中行原年龄的加1 + } + } +} +// Data Cache访问接口,Cache模拟器会调用此接口,来实现对你的Data Cache访问 +// Address: 访存字节地址 +// Operation: 操作:读操作('L')、写操作('S')、读-修改-写操作('M') +// DataSize: 数据大小:1字节、2字节、4字节、8字节 +// StoreValue: 当执行写操作的时候,需要写入的数据 +// LoadResult: 当执行读操作的时候,从Cache读出的数据 +// 返回值:'M'表示Miss;'H'表示Hit +UINT8 AccessDataCache(UINT64 Address, UINT8 Operation, UINT8 DataSize, + UINT64 StoreValue, UINT64* LoadResult) +{ + UINT32 Set; + UINT8 Block; + UINT64 Tag; + UINT8 MissFlag = 'M'; + UINT64 ReadValue; + int HitLine = 0; + UINT64 HitLineAddress = 0; + + *LoadResult = 0; + + + // Address被划分为 Tag + Set + Block + + // Set Cache的组索引(每组4行) + Set = (Address >> DCACHE_BYTES_PER_LINE_ADDR_BITS) % DCACHE_SET; + Block = Address % DCACHE_BYTES_PER_LINE; + Tag = (Address >> DCACHE_BYTES_PER_LINE_ADDR_BITS) >> DCACHE_SET_ADDR_BITS; // 地址去掉DCACHE_SET、DCACHE_BYTES_PER_LINE,剩下的作为Tag。警告!不能将整个地址作为Tag!! + + // 1. 首先需要检查对应的DCache行,其Valid位是否有效?Tag是否与AddressTag相等? + // 2. 如果Valid有效,且AddressTag相等,则意味着Cache访问“命中” + // 3. 如果“命中”,需要进一步判断是要读('L')还是写('S')或者是修改('M')? + MissFlag = 'M'; + for (int i = 0; i < DCACHE_LINES_PER_SET; i++) + { + if (DCache[Set].Line[i].Valid == 1 && DCache[Set].Line[i].Tag == Tag) + { + MissFlag = 'H'; + HitLine = i; + HitLineAddress = ((DCache[Set].Line[HitLine].Tag << DCACHE_SET_ADDR_BITS) << + DCACHE_BYTES_PER_LINE_ADDR_BITS) | ((UINT64)Set << + DCACHE_BYTES_PER_LINE_ADDR_BITS); // 从Tag中恢复旧的地址 + break; + } + } + if (MissFlag == 'H') + { + if (Operation == 'L') // 读操作。从DCache对应行中,读取数据,注意数据宽度与对齐要求 + { + ReadValue = 0; + switch (DataSize) + { + case 1: // 1个字节 + ReadValue = DCache[Set].Line[HitLine].Data[Block + 0]; + 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); + 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; + } + // 写穿透:每次更新Cache行,都需要把数据同时写入Memory + StoreCacheLineToMemory(HitLineAddress, DCache[Set].Line[HitLine].Data, DCACHE_BYTES_PER_LINE); + } + UpdateAge(Set, HitLine); + } + else + { + if (debug) + printf("[%s] Address=%016llX Operation=%c DataSize=%u StoreValue=%016llX\n", + __func__, Address, Operation, DataSize, StoreValue); + if (Operation == 'L') // 读操作 + { + // 读操作不需要做事情,因为已经MISS了。Cache模拟器会直接从Memory中读取数据,而不需要DCache提供 + // 需要从Memory中读入新的行(真实情况下,这个LoadCacheLineFromMemory需要很长时间的) + HitLine = GetReplaceLine(Set); + LoadCacheLineFromMemory(Address, DCache[Set].Line[HitLine].Data, DCACHE_BYTES_PER_LINE); + DCache[Set].Line[HitLine].Valid = 1; + DCache[Set].Line[HitLine].Tag = Tag; + UpdateAge(Set, HitLine); + } + else if (Operation == 'S' || Operation == 'M') // 写操作(修改操作在此等价于写操作) + { + // 写操作,需要将新的StoreValue写到Memory中 + // 由于存储器访问每次都是8个字节,所以首先需要把旧的8个字节读取回来,然后根据需要更新 + UINT64 AlignAddress = Address & 0xFFFFFFFFFFFFFFF8; // 地址必须对齐到8字节边界 + UINT8 Offset; // 在8字节中的第几个字节? + UINT64 ReadData = (DataSize == 8) ? 0 : ReadMemory(AlignAddress); + UINT64 WriteData = ReadData; + switch (DataSize) + { + case 1: // 1个字节,要确定写入8个字节中的哪1个字节? + Offset = Address & 0x07; // 0~7 + WriteData = (ReadData & ~(0xFF<<8*Offset)) | ((StoreValue &0xFF) << 8*Offset); + break; + case 2: // 2个字节,要确定写入8个字节中的哪2个字节? + Offset = Address & 0x06; // 0、2、4、6 + WriteData = (ReadData & ~(0xFFFF<<8*Offset)) | ((StoreValue &0xFFFF) << 8*Offset); + break; + case 4: // 4个字节,要确定写入8个字节中的哪4个字节? + Offset = Address & 0x04; // 0、4 + WriteData = (ReadData & ~(0xFFFFFFFF << 8*Offset)) | ((StoreValue &0xFFFFFFFF) << 8*Offset); + break; + case 8: // 8个字节 + WriteData = StoreValue; + break; + } + WriteMemory(AlignAddress, WriteData); + // 写非分配。所以对于MISS的写操作,不在Cache中分配行 + } + } + return MissFlag; +} + +#ifdef ICACHE_ENABLE + +/* 指令Cache实现部分,可选实现 */ +void InitInstCache(void) +{ + return; +} + +UINT8 AccessInstCache(UINT64 Address, UINT8 Operation, UINT8 InstSize, + UINT64* InstResult) +{ + // ICache只有Operation = 'I'的操作,只会读。不会写 + // 返回值'M' = Miss,'H'=Hit + return 'M'; +} +#endif + +3 +#include +#include "common.h" + +// debug=1:输出额外调试信息 +int debug = 0; + +#define MAX_AGE 3 + +// 组相联Data Cache +// 容量为16384字节,4路组相联,每行容纳16个字节 +#define DCACHE_SIZE 16384 +#define DCACHE_LINES_PER_SET 4 +#define DCACHE_BYTES_PER_LINE 16 // 必须是8字节的倍数 + +// 下面参数是自动计算的,你无须需改 +#define DCACHE_BYTES_PER_LINE_ADDR_BITS GET_LOG_OF_2(DCACHE_BYTES_PER_LINE) +#define DCACHE_SET (DCACHE_SIZE/(DCACHE_BYTES_PER_LINE*DCACHE_LINES_PER_SET)) +#define DCACHE_SET_ADDR_BITS GET_LOG_OF_2(DCACHE_SET) + +// Cache行的结构,包括Valid、Age、Dirty、Tag和Data。你所有的状态信息,只能记录在Cache行中! +struct DCACHE_LineStruct +{ + UINT8 Valid; + UINT8 Age; + UINT8 Dirty; + UINT64 Tag; + UINT8 Data[DCACHE_BYTES_PER_LINE]; +}; + +struct DCACHE_SetStruct { + struct DCACHE_LineStruct Line[4]; // 4路组相联Cache,每组有4行 +} DCache[DCACHE_SET]; + + +// DCache初始化代码,模拟器启动时,会调用此InitDataCache函数 +void InitDataCache() +{ + // *********** 你需要在下面书写代码 *********** + for (int i = 0; i < DCACHE_SET; i++) + { + for (int j = 0; j < 4; j++) + { + DCache[i].Line[j].Valid = 0; + DCache[i].Line[j].Age = j; + DCache[i].Line[j].Dirty = j; + DCache[i].Line[j].Tag = 0; + } + } + // *********** 你需要在上面书写代码 *********** +} + +// 在第Set组中,从4路中,找到需要替换的Cache行 +// 如果4行中,有某行的Valid=0,则返回该行行号 +// 否则,返回Age最大的行的行号 +int GetReplaceLine(UINT32 Set) +{ + // *********** 你需要在下面书写代码 *********** + int replace_line = -1; + UINT8 max_age = 0; + + for (int i = 0; i < 4; i++) + { + if (!DCache[Set].Line[i].Valid) + { + return i; + } + if (DCache[Set].Line[i].Age > max_age) + { + max_age = DCache[Set].Line[i].Age; + replace_line = i; + } + } + return replace_line; + // *********** 你需要在上面书写代码 *********** +} + +// 更新Age,在第Set组中,将HitLine指定的Cache行的Age设置为0,其他行的Age要相应调整 +// 注意!要确保4行的Age分别为0~3,且唯一 +void UpdateAge(UINT32 Set, int HitLine) +{ + // *********** 你需要在下面书写代码 *********** + UINT8 old_age = DCache[Set].Line[HitLine].Age; + DCache[Set].Line[HitLine].Age = 0; + + for (int i = 0; i < 4; i++) + { + if (i != HitLine && DCache[Set].Line[i].Valid) + { + if (DCache[Set].Line[i].Age < old_age) + { + DCache[Set].Line[i].Age++; + } + } + } + // *********** 你需要在上面书写代码 *********** +} + +// Data Cache访问接口,Cache模拟器会调用此接口,来实现对你的Data Cache访问 +// Address: 访存字节地址 +// Operation: 操作:读操作('L')、写操作('S')、读-修改-写操作('M') +// DataSize: 数据大小:1字节、2字节、4字节、8字节 +// StoreValue: 当执行写操作的时候,需要写入的数据 +// LoadResult: 当执行读操作的时候,从Cache读出的数据 +// 返回值:'M'表示Miss;'H'表示Hit +UINT8 AccessDataCache(UINT64 Address, UINT8 Operation, UINT8 DataSize, + UINT64 StoreValue, UINT64* LoadResult) +{ + UINT32 Set; + UINT8 Block; + UINT64 Tag; + UINT8 MissFlag = 'M'; + UINT64 ReadValue; + int HitLine = 0; + UINT64 HitLineAddress = 0; + + *LoadResult = 0; + + + // Address被划分为 Tag + Set + Block + + // Set Cache的组索引(每组4行) + Set = (Address >> DCACHE_BYTES_PER_LINE_ADDR_BITS) % DCACHE_SET; + Block = Address % DCACHE_BYTES_PER_LINE; + Tag = (Address >> DCACHE_BYTES_PER_LINE_ADDR_BITS) >> DCACHE_SET_ADDR_BITS; // 地址去掉DCACHE_SET、DCACHE_BYTES_PER_LINE,剩下的作为Tag。警告!不能将整个地址作为Tag!! + + // 1. 首先需要检查对应的DCache行,其Valid位是否有效?Tag是否与AddressTag相等? + // 2. 如果Valid有效,且AddressTag相等,则意味着Cache访问“命中” + // 3. 如果“命中”,需要进一步判断是要读('L')还是写('S')或者是修改('M')? + MissFlag = 'M'; + for (int i = 0; i < DCACHE_LINES_PER_SET; i++) + { + if (DCache[Set].Line[i].Valid == 1 && DCache[Set].Line[i].Tag == Tag) + { + MissFlag = 'H'; + HitLine = i; + HitLineAddress = ((DCache[Set].Line[HitLine].Tag << DCACHE_SET_ADDR_BITS) << + DCACHE_BYTES_PER_LINE_ADDR_BITS) | ((UINT64)Set << + DCACHE_BYTES_PER_LINE_ADDR_BITS); // 从Tag中恢复旧的地址 + break; + } + } + if (MissFlag == 'H') + { + UpdateAge(Set, HitLine); + if (Operation == 'L') // 读操作。从DCache对应行中,读取数据,注意数据宽度与对齐要求 + { + ReadValue = 0; + switch (DataSize) + { + case 1: // 1个字节 + ReadValue = DCache[Set].Line[HitLine].Data[Block + 0]; + 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); + 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; + } + // 写穿透:每次更新Cache行,都需要把数据同时写入Memory + //StoreCacheLineToMemory(HitLineAddress, DCache[Set].Line[HitLine].Data, DCACHE_BYTES_PER_LINE); + DCache[Set].Line[HitLine].Dirty = 1; //标记脏数据 + } + UpdateAge(Set, HitLine); + } + else + { + if (debug) + printf("[%s] Address=%016llX Operation=%c DataSize=%u StoreValue=%016llX\n", + __func__, Address, Operation, DataSize, StoreValue); + if (Operation == 'L' || Operation == 'M' || Operation == 'S') // 读操作 + { + // 需要从Memory中读入新的行(真实情况下,这个LoadCacheLineFromMemory需要很长时间的) + int ReLine = GetReplaceLine(Set); + UINT64 ReLineAddress = 0; + + //写回:当被替换的数据为脏数据时,我们需要先将该行数据首先写入到Memory中,再执行替换 + if(DCache[Set].Line[ReLine].Dirty == 1 && DCache[Set].Line[ReLine].Valid == 1) + { + ReLineAddress = (DCache[Set].Line[ReLine].Tag << (DCACHE_SET_ADDR_BITS + DCACHE_BYTES_PER_LINE_ADDR_BITS)) | ((UINT64)Set << DCACHE_BYTES_PER_LINE_ADDR_BITS); // 从Tag中恢复旧的地址 + StoreCacheLineToMemory(ReLineAddress, DCache[Set].Line[ReLine].Data, DCACHE_BYTES_PER_LINE); + } + + //开始替换,加载新数据 + LoadCacheLineFromMemory(Address, DCache[Set].Line[ReLine].Data, DCACHE_BYTES_PER_LINE); + DCache[Set].Line[ReLine].Valid = 1; + DCache[Set].Line[ReLine].Tag = Tag; + DCache[Set].Line[ReLine].Dirty = 0; + UpdateAge(Set, ReLine); + + if(Operation == 'M' || Operation == 'S') + { + switch (DataSize) + { + case 1: // 1个字节 + DCache[Set].Line[ReLine].Data[Block + 0] = StoreValue & 0xFF; + break; + case 2: // 2个字节 + Block = Block & 0xFE; // 需对齐到2字节边界 + DCache[Set].Line[ReLine].Data[Block + 0] = StoreValue & 0xFF; + StoreValue = StoreValue >> 8; + DCache[Set].Line[ReLine].Data[Block + 1] = StoreValue & 0xFF; + break; + case 4: // 4个字节 + Block = Block & 0xFC; // 需对齐到4字节边界 + DCache[Set].Line[ReLine].Data[Block + 0] = StoreValue & 0xFF; + StoreValue = StoreValue >> 8; + DCache[Set].Line[ReLine].Data[Block + 1] = StoreValue & 0xFF; + StoreValue = StoreValue >> 8; + DCache[Set].Line[ReLine].Data[Block + 2] = StoreValue & 0xFF; + StoreValue = StoreValue >> 8; + DCache[Set].Line[ReLine].Data[Block + 3] = StoreValue & 0xFF; + break; + case 8: // 8个字节 + Block = Block & 0xF8; // 需对齐到8字节边界 + DCache[Set].Line[ReLine].Data[Block + 0] = StoreValue & 0xFF; + StoreValue = StoreValue >> 8; + DCache[Set].Line[ReLine].Data[Block + 1] = StoreValue & 0xFF; + StoreValue = StoreValue >> 8; + DCache[Set].Line[ReLine].Data[Block + 2] = StoreValue & 0xFF; + StoreValue = StoreValue >> 8; + DCache[Set].Line[ReLine].Data[Block + 3] = StoreValue & 0xFF; + StoreValue = StoreValue >> 8; + DCache[Set].Line[ReLine].Data[Block + 4] = StoreValue & 0xFF; + StoreValue = StoreValue >> 8; + DCache[Set].Line[ReLine].Data[Block + 5] = StoreValue & 0xFF; + StoreValue = StoreValue >> 8; + DCache[Set].Line[ReLine].Data[Block + 6] = StoreValue & 0xFF; + StoreValue = StoreValue >> 8; + DCache[Set].Line[ReLine].Data[Block + 7] = StoreValue & 0xFF; + break; + } + DCache[Set].Line[ReLine].Dirty = 1; + } + UpdateAge(Set, ReLine); + } + } + return MissFlag; +} + +#ifdef ICACHE_ENABLE + +/* 指令Cache实现部分,可选实现 */ +void InitInstCache(void) +{ + return; +} + +UINT8 AccessInstCache(UINT64 Address, UINT8 Operation, UINT8 InstSize, + UINT64* InstResult) +{ + // ICache只有Operation = 'I'的操作,只会读。不会写 + // 返回值'M' = Miss,'H'=Hit + return 'M'; +} +#endif + +4 +#include +#include "common.h" + +// debug=1:输出额外调试信息 +int debug = 0; + +// 组相联Data Cache +// 容量为16384字节,4路组相联,每行容纳16个字节 +#define DCACHE_SIZE 16384 +#define DCACHE_LINES_PER_SET 4 +#define DCACHE_BYTES_PER_LINE 16 // 必须是8字节的倍数 + +// 下面参数是自动计算的,你无须需改 +#define DCACHE_BYTES_PER_LINE_ADDR_BITS GET_LOG_OF_2(DCACHE_BYTES_PER_LINE) +#define DCACHE_SET (DCACHE_SIZE/(DCACHE_BYTES_PER_LINE*DCACHE_LINES_PER_SET)) +#define DCACHE_SET_ADDR_BITS GET_LOG_OF_2(DCACHE_SET) + +// Cache行的结构,包括Valid、Age、Dirty、Tag和Data。你所有的状态信息,只能记录在Cache行中! +struct DCACHE_LineStruct +{ + UINT8 Valid; + UINT8 Age; + UINT8 Dirty; + UINT64 Tag; + UINT8 Data[DCACHE_BYTES_PER_LINE]; +}; + +struct DCACHE_SetStruct { + struct DCACHE_LineStruct Line[4]; // 4路组相联Cache,每组有4行 +} DCache[DCACHE_SET]; + + +// DCache初始化代码,模拟器启动时,会调用此InitDataCache函数 +void InitDataCache() +{ + // *********** 你需要在下面书写代码 *********** + for (int i = 0; i < DCACHE_SET; i++) + { + for (int j = 0; j < 4; j++) + { + DCache[i].Line[j].Valid = 0; + DCache[i].Line[j].Age = j; + DCache[i].Line[j].Dirty = j; + DCache[i].Line[j].Tag = 0; + } + } + // *********** 你需要在上面书写代码 *********** +} + +// 在第Set组中,从4路中,找到需要替换的Cache行 +// 如果4行中,有某行的Valid=0,则返回该行行号 +// 否则,返回Age=0的行的行号 +int GetReplaceLine(UINT32 Set) +{ + // *********** 你需要在下面书写代码 *********** + int replace_line = -1; + + for (int i = 0; i < 4; i++) + { + if (!DCache[Set].Line[i].Valid) + { + return i; + } + if (DCache[Set].Line[i].Age == 0) + { + replace_line = i; + } + } + return replace_line; + // *********** 你需要在上面书写代码 *********** + return -1; +} + +// 更新Age,在第Set组中,将HitLine指定的Cache行的Age设置为0,其他行的Age要相应调整 +// 注意!要确保4行的Age分别为0~3,且唯一 +void UpdateAge(UINT32 Set, int HitLine) +{ + // *********** 你需要在下面书写代码 *********** + UINT8 old_age = DCache[Set].Line[HitLine].Age; + DCache[Set].Line[HitLine].Age = 0; + + for (int i = 0; i < 4; i++) + { + if (i != HitLine && DCache[Set].Line[i].Valid) + { + if (DCache[Set].Line[i].Age < old_age) + { + DCache[Set].Line[i].Age++; + } + } + } + // *********** 你需要在上面书写代码 *********** +} + +// Data Cache访问接口,Cache模拟器会调用此接口,来实现对你的Data Cache访问 +// Address: 访存字节地址 +// Operation: 操作:读操作('L')、写操作('S')、读-修改-写操作('M') +// DataSize: 数据大小:1字节、2字节、4字节、8字节 +// StoreValue: 当执行写操作的时候,需要写入的数据 +// LoadResult: 当执行读操作的时候,从Cache读出的数据 +// 返回值:'M'表示Miss;'H'表示Hit +UINT8 AccessDataCache(UINT64 Address, UINT8 Operation, UINT8 DataSize, + UINT64 StoreValue, UINT64* LoadResult) +{ + UINT32 Set; + UINT8 Block; + UINT64 Tag; + UINT8 MissFlag = 'M'; + UINT64 ReadValue; + int HitLine = 0; + UINT64 HitLineAddress = 0; + + *LoadResult = 0; + + + // Address被划分为 Tag + Set + Block + + // Set Cache的组索引(每组4行) + Set = (Address >> DCACHE_BYTES_PER_LINE_ADDR_BITS) % DCACHE_SET; + Block = Address % DCACHE_BYTES_PER_LINE; + Tag = (Address >> DCACHE_BYTES_PER_LINE_ADDR_BITS) >> DCACHE_SET_ADDR_BITS; // 地址去掉DCACHE_SET、DCACHE_BYTES_PER_LINE,剩下的作为Tag。警告!不能将整个地址作为Tag!! + + // 1. 首先需要检查对应的DCache行,其Valid位是否有效?Tag是否与AddressTag相等? + // 2. 如果Valid有效,且AddressTag相等,则意味着Cache访问“命中” + // 3. 如果“命中”,需要进一步判断是要读('L')还是写('S')或者是修改('M')? + MissFlag = 'M'; + for (int i = 0; i < DCACHE_LINES_PER_SET; i++) + { + if (DCache[Set].Line[i].Valid == 1 && DCache[Set].Line[i].Tag == Tag) + { + MissFlag = 'H'; + HitLine = i; + HitLineAddress = ((DCache[Set].Line[HitLine].Tag << DCACHE_SET_ADDR_BITS) << + DCACHE_BYTES_PER_LINE_ADDR_BITS) | ((UINT64)Set << + DCACHE_BYTES_PER_LINE_ADDR_BITS); // 从Tag中恢复旧的地址 + break; + } + } + if (MissFlag == 'H') + { + UpdateAge(Set, HitLine); + if (Operation == 'L') // 读操作。从DCache对应行中,读取数据,注意数据宽度与对齐要求 + { + ReadValue = 0; + switch (DataSize) + { + case 1: // 1个字节 + ReadValue = DCache[Set].Line[HitLine].Data[Block + 0]; + 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); + 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; + } + // 写穿透:每次更新Cache行,都需要把数据同时写入Memory + //StoreCacheLineToMemory(HitLineAddress, DCache[Set].Line[HitLine].Data, DCACHE_BYTES_PER_LINE); + DCache[Set].Line[HitLine].Dirty = 1; //标记脏数据 + } + UpdateAge(Set, HitLine); + } + else + { + if (debug) + printf("[%s] Address=%016llX Operation=%c DataSize=%u StoreValue=%016llX\n", + __func__, Address, Operation, DataSize, StoreValue); + if (Operation == 'L' || Operation == 'M' || Operation == 'S') // 读操作 + { + // 需要从Memory中读入新的行(真实情况下,这个LoadCacheLineFromMemory需要很长时间的) + int ReLine = GetReplaceLine(Set); + UINT64 ReLineAddress = 0; + + //写回:当被替换的数据为脏数据时,我们需要先将该行数据首先写入到Memory中,再执行替换 + if(DCache[Set].Line[ReLine].Dirty == 1 && DCache[Set].Line[ReLine].Valid == 1) + { + ReLineAddress = (DCache[Set].Line[ReLine].Tag << (DCACHE_SET_ADDR_BITS + DCACHE_BYTES_PER_LINE_ADDR_BITS)) | ((UINT64)Set << DCACHE_BYTES_PER_LINE_ADDR_BITS); // 从Tag中恢复旧的地址 + StoreCacheLineToMemory(ReLineAddress, DCache[Set].Line[ReLine].Data, DCACHE_BYTES_PER_LINE); + } + + //开始替换,加载新数据 + LoadCacheLineFromMemory(Address, DCache[Set].Line[ReLine].Data, DCACHE_BYTES_PER_LINE); + DCache[Set].Line[ReLine].Valid = 1; + DCache[Set].Line[ReLine].Tag = Tag; + DCache[Set].Line[ReLine].Dirty = 0; + UpdateAge(Set, ReLine); + + if(Operation == 'M' || Operation == 'S') + { + switch (DataSize) + { + case 1: // 1个字节 + DCache[Set].Line[ReLine].Data[Block + 0] = StoreValue & 0xFF; + break; + case 2: // 2个字节 + Block = Block & 0xFE; // 需对齐到2字节边界 + DCache[Set].Line[ReLine].Data[Block + 0] = StoreValue & 0xFF; + StoreValue = StoreValue >> 8; + DCache[Set].Line[ReLine].Data[Block + 1] = StoreValue & 0xFF; + break; + case 4: // 4个字节 + Block = Block & 0xFC; // 需对齐到4字节边界 + DCache[Set].Line[ReLine].Data[Block + 0] = StoreValue & 0xFF; + StoreValue = StoreValue >> 8; + DCache[Set].Line[ReLine].Data[Block + 1] = StoreValue & 0xFF; + StoreValue = StoreValue >> 8; + DCache[Set].Line[ReLine].Data[Block + 2] = StoreValue & 0xFF; + StoreValue = StoreValue >> 8; + DCache[Set].Line[ReLine].Data[Block + 3] = StoreValue & 0xFF; + break; + case 8: // 8个字节 + Block = Block & 0xF8; // 需对齐到8字节边界 + DCache[Set].Line[ReLine].Data[Block + 0] = StoreValue & 0xFF; + StoreValue = StoreValue >> 8; + DCache[Set].Line[ReLine].Data[Block + 1] = StoreValue & 0xFF; + StoreValue = StoreValue >> 8; + DCache[Set].Line[ReLine].Data[Block + 2] = StoreValue & 0xFF; + StoreValue = StoreValue >> 8; + DCache[Set].Line[ReLine].Data[Block + 3] = StoreValue & 0xFF; + StoreValue = StoreValue >> 8; + DCache[Set].Line[ReLine].Data[Block + 4] = StoreValue & 0xFF; + StoreValue = StoreValue >> 8; + DCache[Set].Line[ReLine].Data[Block + 5] = StoreValue & 0xFF; + StoreValue = StoreValue >> 8; + DCache[Set].Line[ReLine].Data[Block + 6] = StoreValue & 0xFF; + StoreValue = StoreValue >> 8; + DCache[Set].Line[ReLine].Data[Block + 7] = StoreValue & 0xFF; + break; + } + DCache[Set].Line[ReLine].Dirty = 1; + } + UpdateAge(Set, ReLine); + } + } + return MissFlag; +} + +#ifdef ICACHE_ENABLE + +/* 指令Cache实现部分,可选实现 */ +void InitInstCache(void) +{ + return; +} + +UINT8 AccessInstCache(UINT64 Address, UINT8 Operation, UINT8 InstSize, + UINT64* InstResult) +{ + // ICache只有Operation = 'I'的操作,只会读。不会写 + // 返回值'M' = Miss,'H'=Hit + return 'M'; +} +#endif + +5 +#include +#include "common.h" + +int debug = 0; + +#define DCACHE_SIZE 16384 +#define DCACHE_LINES_PER_SET 4 +#define DCACHE_BYTES_PER_LINE 16 + +#define DCACHE_BYTES_PER_LINE_ADDR_BITS GET_LOG_OF_2(DCACHE_BYTES_PER_LINE) +#define DCACHE_SET (DCACHE_SIZE/(DCACHE_BYTES_PER_LINE*DCACHE_LINES_PER_SET)) +#define DCACHE_SET_ADDR_BITS GET_LOG_OF_2(DCACHE_SET) + +struct DCACHE_LineStruct { + UINT8 Valid; + UINT8 Age; + UINT8 Dirty; + UINT64 Tag; + UINT8 Data[DCACHE_BYTES_PER_LINE]; +}; + +struct DCACHE_SetStruct { + struct DCACHE_LineStruct Line[4]; +} DCache[DCACHE_SET]; + +void InitDataCache() { + for (int s = 0; s < DCACHE_SET; s++) + { + for (int l = 0; l < 4; l++) { + DCache[s].Line[l].Valid = 0; + DCache[s].Line[l].Age = 0; + DCache[s].Line[l].Dirty = 0; + DCache[s].Line[l].Tag = 0; + } + } +} + +int GetReplaceLine(UINT32 Set) { + // 查找无效行 + for (int i = 0; i < 4; i++) { + if (!DCache[Set].Line[i].Valid) return i; + } + + // 查找最大Age行 + int max_age = 0, replace_line = 0; + for (int i = 0; i < 4; i++) { + if (DCache[Set].Line[i].Age > max_age) { + max_age = DCache[Set].Line[i].Age; + replace_line = i; + } + } + return replace_line; +} + +void UpdateAge(UINT32 Set, int HitLine) { + DCache[Set].Line[HitLine].Age = 0; + for (int i = 0; i < 4; i++) { + if (i != HitLine && DCache[Set].Line[i].Valid) { + DCache[Set].Line[i].Age = (DCache[Set].Line[i].Age < 3) ? + DCache[Set].Line[i].Age + 1 : 3; + } + } +} + +UINT8 AccessDataCache(UINT64 Address, UINT8 Operation, UINT8 DataSize, + UINT64 StoreValue, UINT64* LoadResult) +{ + UINT32 Set = (Address >> DCACHE_BYTES_PER_LINE_ADDR_BITS) % DCACHE_SET; + UINT8 Block = Address % DCACHE_BYTES_PER_LINE; + UINT64 Tag = Address >> (DCACHE_BYTES_PER_LINE_ADDR_BITS + DCACHE_SET_ADDR_BITS); + UINT8 MissFlag = 'M'; + int HitLine = -1; + + // 检查命中 + for (int i = 0; i < 4; i++) { + if (DCache[Set].Line[i].Valid && DCache[Set].Line[i].Tag == Tag) { + HitLine = i; + MissFlag = 'H'; + break; + } + } + + if (MissFlag == 'H') { + // 处理数据对齐 + switch (DataSize) { + case 2: Block &= 0xFE; break; + case 4: Block &= 0xFC; break; + case 8: Block &= 0xF8; break; + } + + if (Operation == 'L') { + *LoadResult = 0; + for (int i = DataSize-1; i >= 0; i--) { + *LoadResult = (*LoadResult << 8) | DCache[Set].Line[HitLine].Data[Block+i]; + } + } else { + for (int i = 0; i < DataSize; i++) { + DCache[Set].Line[HitLine].Data[Block+i] = (StoreValue >> (i*8)) & 0xFF; + } + DCache[Set].Line[HitLine].Dirty = 1; + } + } else { + if (Operation == 'L' || Operation == 'S' || Operation == 'M') { + int ReplaceLine = GetReplaceLine(Set); + struct DCACHE_LineStruct *line = &DCache[Set].Line[ReplaceLine]; + + // 写回脏数据 + if (line->Valid && line->Dirty) { + UINT64 old_addr = (line->Tag << (DCACHE_SET_ADDR_BITS + DCACHE_BYTES_PER_LINE_ADDR_BITS)) + | (Set << DCACHE_BYTES_PER_LINE_ADDR_BITS); + StoreCacheLineToMemory(old_addr, line->Data, DCACHE_BYTES_PER_LINE); + } + + // 加载新行 + LoadCacheLineFromMemory(Address, line->Data, DCACHE_BYTES_PER_LINE); + line->Valid = 1; + line->Tag = Tag; + line->Dirty = (Operation == 'L') ? 0 : 1; + + // 更新Age状态 + UpdateAge(Set, ReplaceLine); + + // 处理数据对齐 + switch (DataSize) { + case 2: Block &= 0xFE; break; + case 4: Block &= 0xFC; break; + case 8: Block &= 0xF8; break; + } + + // 处理写操作 + if (Operation != 'L') { + for (int i = 0; i < DataSize; i++) { + line->Data[Block+i] = (StoreValue >> (i*8)) & 0xFF; + } + line->Dirty = 1; + } + + // 返回读数据 + if (Operation == 'L') { + *LoadResult = 0; + for (int i = DataSize-1; i >= 0; i--) { + *LoadResult = (*LoadResult << 8) | line->Data[Block+i]; + } + } + } + } + + return MissFlag; +} + +#ifdef ICACHE_ENABLE +void InitInstCache(void) { return; } +UINT8 AccessInstCache(UINT64 a, UINT8 b, UINT8 c, UINT64* d) { return 'M'; } +#endif + +6 +#include +#include "common.h" + +// debug=1:输出额外调试信息 +int debug = 0; + +// 组相联Data Cache +// 容量为16384字节,4路组相联,每行容纳16个字节 +#define DCACHE_SIZE 16384 +#define DCACHE_LINES_PER_SET 4 +#define DCACHE_BYTES_PER_LINE 16 // 必须是8字节的倍数 + +// 下面参数是自动计算的,你无须需改 +#define DCACHE_BYTES_PER_LINE_ADDR_BITS GET_LOG_OF_2(DCACHE_BYTES_PER_LINE) +#define DCACHE_SET (DCACHE_SIZE/(DCACHE_BYTES_PER_LINE*DCACHE_LINES_PER_SET)) +#define DCACHE_SET_ADDR_BITS GET_LOG_OF_2(DCACHE_SET) + +// Cache行的结构,包括Valid、Age、Dirty、Tag和Data。你所有的状态信息,只能记录在Cache行中! +struct DCACHE_LineStruct +{ + UINT8 Valid; + UINT8 NRU; + UINT8 Dirty; + UINT64 Tag; + UINT8 Data[DCACHE_BYTES_PER_LINE]; +}; + +struct DCACHE_SetStruct { + struct DCACHE_LineStruct Line[4]; // 4路组相联Cache,每组有4行 +} DCache[DCACHE_SET]; + + +// DCache初始化代码,模拟器启动时,会调用此InitDataCache函数 +void InitDataCache() +{ + // *********** 你需要在下面书写代码 *********** + for (int i = 0; i < DCACHE_SET; i++) + { + for (int j = 0; j < 4; j++) + { + DCache[i].Line[j].Valid = 0; + DCache[i].Line[j].NRU = 1; + DCache[i].Line[j].Dirty = 0; + DCache[i].Line[j].Tag = 0; + } + } + // *********** 你需要在上面书写代码 *********** +} + +// 在第Set组中,从4路中,找到需要替换的Cache行 +// 如果4行中,有某行的Valid=0,则返回该行行号 +// 否则,返回NRU位为1的行号 +int GetReplaceLine(UINT32 Set) +{ + // *********** 你需要在下面书写代码 *********** + int replace_line = -1; + + for (int i = 0; i < 4; i++) + { + if (!DCache[Set].Line[i].Valid) + { + return i; + } + if (DCache[Set].Line[i].NRU == 1) + { + replace_line = i; + break; + } + } + + //全部为0时,重置为0并返回首行 + if(replace_line == -1) + { + for(int i = 0; i < 4; i++) + { + DCache[Set].Line[i].NRU = 1; + } + replace_line = 0; + } + return replace_line; + // *********** 你需要在上面书写代码 *********** + return -1; +} + + +// Data Cache访问接口,Cache模拟器会调用此接口,来实现对你的Data Cache访问 +// Address: 访存字节地址 +// Operation: 操作:读操作('L')、写操作('S')、读-修改-写操作('M') +// DataSize: 数据大小:1字节、2字节、4字节、8字节 +// StoreValue: 当执行写操作的时候,需要写入的数据 +// LoadResult: 当执行读操作的时候,从Cache读出的数据 +// 返回值:'M'表示Miss;'H'表示Hit +UINT8 AccessDataCache(UINT64 Address, UINT8 Operation, UINT8 DataSize, + UINT64 StoreValue, UINT64* LoadResult) +{ + UINT32 Set; + UINT8 Block; + UINT64 Tag; + UINT8 MissFlag = 'M'; + UINT64 ReadValue; + int HitLine = 0; + UINT64 HitLineAddress = 0; + + *LoadResult = 0; + + + // Address被划分为 Tag + Set + Block + + // Set Cache的组索引(每组4行) + Set = (Address >> DCACHE_BYTES_PER_LINE_ADDR_BITS) % DCACHE_SET; + Block = Address % DCACHE_BYTES_PER_LINE; + Tag = (Address >> DCACHE_BYTES_PER_LINE_ADDR_BITS) >> DCACHE_SET_ADDR_BITS; // 地址去掉DCACHE_SET、DCACHE_BYTES_PER_LINE,剩下的作为Tag。警告!不能将整个地址作为Tag!! + + // 1. 首先需要检查对应的DCache行,其Valid位是否有效?Tag是否与AddressTag相等? + // 2. 如果Valid有效,且AddressTag相等,则意味着Cache访问“命中” + // 3. 如果“命中”,需要进一步判断是要读('L')还是写('S')或者是修改('M')? + MissFlag = 'M'; + for (int i = 0; i < DCACHE_LINES_PER_SET; i++) + { + if (DCache[Set].Line[i].Valid == 1 && DCache[Set].Line[i].Tag == Tag) + { + MissFlag = 'H'; + HitLine = i; + HitLineAddress = ((DCache[Set].Line[HitLine].Tag << DCACHE_SET_ADDR_BITS) << + DCACHE_BYTES_PER_LINE_ADDR_BITS) | ((UINT64)Set << + DCACHE_BYTES_PER_LINE_ADDR_BITS); // 从Tag中恢复旧的地址 + break; + } + } + if (MissFlag == 'H') + { + //UpdateAge(Set, HitLine); + DCache[Set].Line[HitLine].NRU = 0; + if (Operation == 'L') // 读操作。从DCache对应行中,读取数据,注意数据宽度与对齐要求 + { + ReadValue = 0; + switch (DataSize) + { + case 1: // 1个字节 + ReadValue = DCache[Set].Line[HitLine].Data[Block + 0]; + 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); + 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; + } + // 写穿透:每次更新Cache行,都需要把数据同时写入Memory + //StoreCacheLineToMemory(HitLineAddress, DCache[Set].Line[HitLine].Data, DCACHE_BYTES_PER_LINE); + DCache[Set].Line[HitLine].Dirty = 1; //标记脏数据 + } + //UpdateAge(Set, HitLine); + } + else + { + if (debug) + printf("[%s] Address=%016llX Operation=%c DataSize=%u StoreValue=%016llX\n", + __func__, Address, Operation, DataSize, StoreValue); + if (Operation == 'L' || Operation == 'M' || Operation == 'S') // 读操作 + { + // 需要从Memory中读入新的行(真实情况下,这个LoadCacheLineFromMemory需要很长时间的) + int ReLine = GetReplaceLine(Set); + UINT64 ReLineAddress = 0; + + //写回:当被替换的数据为脏数据时,我们需要先将该行数据首先写入到Memory中,再执行替换 + if(DCache[Set].Line[ReLine].Dirty == 1 && DCache[Set].Line[ReLine].Valid == 1) + { + ReLineAddress = (DCache[Set].Line[ReLine].Tag << (DCACHE_SET_ADDR_BITS + DCACHE_BYTES_PER_LINE_ADDR_BITS)) | ((UINT64)Set << DCACHE_BYTES_PER_LINE_ADDR_BITS); // 从Tag中恢复旧的地址 + StoreCacheLineToMemory(ReLineAddress, DCache[Set].Line[ReLine].Data, DCACHE_BYTES_PER_LINE); + } + + //开始替换,加载新数据 + LoadCacheLineFromMemory(Address, DCache[Set].Line[ReLine].Data, DCACHE_BYTES_PER_LINE); + DCache[Set].Line[ReLine].Valid = 1; + DCache[Set].Line[ReLine].Tag = Tag; + DCache[Set].Line[ReLine].Dirty = 0; + DCache[Set].Line[ReLine].NRU = 0; + //UpdateAge(Set, ReLine); + + if(Operation == 'M' || Operation == 'S') + { + switch (DataSize) + { + case 1: // 1个字节 + DCache[Set].Line[ReLine].Data[Block + 0] = StoreValue & 0xFF; + break; + case 2: // 2个字节 + Block = Block & 0xFE; // 需对齐到2字节边界 + DCache[Set].Line[ReLine].Data[Block + 0] = StoreValue & 0xFF; + StoreValue = StoreValue >> 8; + DCache[Set].Line[ReLine].Data[Block + 1] = StoreValue & 0xFF; + break; + case 4: // 4个字节 + Block = Block & 0xFC; // 需对齐到4字节边界 + DCache[Set].Line[ReLine].Data[Block + 0] = StoreValue & 0xFF; + StoreValue = StoreValue >> 8; + DCache[Set].Line[ReLine].Data[Block + 1] = StoreValue & 0xFF; + StoreValue = StoreValue >> 8; + DCache[Set].Line[ReLine].Data[Block + 2] = StoreValue & 0xFF; + StoreValue = StoreValue >> 8; + DCache[Set].Line[ReLine].Data[Block + 3] = StoreValue & 0xFF; + break; + case 8: // 8个字节 + Block = Block & 0xF8; // 需对齐到8字节边界 + DCache[Set].Line[ReLine].Data[Block + 0] = StoreValue & 0xFF; + StoreValue = StoreValue >> 8; + DCache[Set].Line[ReLine].Data[Block + 1] = StoreValue & 0xFF; + StoreValue = StoreValue >> 8; + DCache[Set].Line[ReLine].Data[Block + 2] = StoreValue & 0xFF; + StoreValue = StoreValue >> 8; + DCache[Set].Line[ReLine].Data[Block + 3] = StoreValue & 0xFF; + StoreValue = StoreValue >> 8; + DCache[Set].Line[ReLine].Data[Block + 4] = StoreValue & 0xFF; + StoreValue = StoreValue >> 8; + DCache[Set].Line[ReLine].Data[Block + 5] = StoreValue & 0xFF; + StoreValue = StoreValue >> 8; + DCache[Set].Line[ReLine].Data[Block + 6] = StoreValue & 0xFF; + StoreValue = StoreValue >> 8; + DCache[Set].Line[ReLine].Data[Block + 7] = StoreValue & 0xFF; + break; + } + DCache[Set].Line[ReLine].Dirty = 1; + } + //UpdateAge(Set, ReLine); + } + } + return MissFlag; +} + +#ifdef ICACHE_ENABLE + +/* 指令Cache实现部分,可选实现 */ +void InitInstCache(void) +{ + return; +} + +UINT8 AccessInstCache(UINT64 Address, UINT8 Operation, UINT8 InstSize, + UINT64* InstResult) +{ + // ICache只有Operation = 'I'的操作,只会读。不会写 + // 返回值'M' = Miss,'H'=Hit + return 'M'; +} +#endif + + +7 +#include +#include "common.h" + +int debug = 0; + +#define DCACHE_SIZE 16384 +#define DCACHE_LINES_PER_SET 4 +#define DCACHE_BYTES_PER_LINE 16 + +#define DCACHE_BYTES_PER_LINE_ADDR_BITS GET_LOG_OF_2(DCACHE_BYTES_PER_LINE) +#define DCACHE_SET (DCACHE_SIZE/(DCACHE_BYTES_PER_LINE*DCACHE_LINES_PER_SET)) +#define DCACHE_SET_ADDR_BITS GET_LOG_OF_2(DCACHE_SET) + +struct DCACHE_LineStruct { + UINT8 Valid; + UINT8 NRU; // 3位RRPV实现 + UINT8 Dirty; + UINT64 Tag; + UINT8 Data[DCACHE_BYTES_PER_LINE]; +}; + +struct DCACHE_SetStruct { + struct DCACHE_LineStruct Line[4]; +} DCache[DCACHE_SET]; + +void InitDataCache() +{ + for (int s = 0; s < DCACHE_SET; s++) + { + for (int l = 0; l < 4; l++) + { + DCache[s].Line[l].Valid = 0; + DCache[s].Line[l].NRU = 6; // 初始化为最大值 + DCache[s].Line[l].Dirty = 0; + DCache[s].Line[l].Tag = 0; + } + } +} + +int GetReplaceLine(UINT32 Set) +{ + // 优先替换无效行 + for (int i = 0; i < 4; i++) { + if (!DCache[Set].Line[i].Valid) return i; + } + + // 查找NRU=7的行 + for (int i = 0; i < 4; i++) { + if (DCache[Set].Line[i].NRU == 7) return i; + } + + while(1) + { + // 所有行NRU加1(最大保持7) + for (int i = 0; i < 4; i++) { + DCache[Set].Line[i].NRU = (DCache[Set].Line[i].NRU < 7) ? + DCache[Set].Line[i].NRU + 1 : 7; + } + + // 再次查找 + for (int i = 0; i < 4; i++) { + if (DCache[Set].Line[i].NRU == 7) return i; + } + } + + return 0; // 保底返回首行 +} + +UINT8 AccessDataCache(UINT64 Address, UINT8 Operation, UINT8 DataSize, + UINT64 StoreValue, UINT64* LoadResult) +{ + UINT32 Set = (Address >> DCACHE_BYTES_PER_LINE_ADDR_BITS) % DCACHE_SET; + UINT8 Block = Address % DCACHE_BYTES_PER_LINE; + UINT64 Tag = Address >> (DCACHE_BYTES_PER_LINE_ADDR_BITS + DCACHE_SET_ADDR_BITS); + UINT8 MissFlag = 'M'; + int HitLine = -1; + + // 命中检测 + for (int i = 0; i < 4; i++) { + if (DCache[Set].Line[i].Valid && DCache[Set].Line[i].Tag == Tag) { + HitLine = i; + MissFlag = 'H'; + DCache[Set].Line[i].NRU = 0; // 命中行NRU置0 + break; + } + } + + if (MissFlag == 'H') { + if (Operation == 'L') { // 读命中 + *LoadResult = 0; + for (int i = DataSize-1; i >= 0; i--) { + *LoadResult = (*LoadResult << 8) | DCache[Set].Line[HitLine].Data[Block+i]; + } + } + else { // 写命中 + for (int i = 0; i < DataSize; i++) { + DCache[Set].Line[HitLine].Data[Block+i] = (StoreValue >> (i*8)) & 0xFF; + } + DCache[Set].Line[HitLine].Dirty = 1; + } + } + else { // 未命中处理 + if (Operation == 'L' || Operation == 'S' || Operation == 'M') { + int ReplaceLine = GetReplaceLine(Set); + struct DCACHE_LineStruct *line = &DCache[Set].Line[ReplaceLine]; + + // 写回脏数据 + if (line->Valid && line->Dirty) { + UINT64 old_addr = (line->Tag << (DCACHE_SET_ADDR_BITS + DCACHE_BYTES_PER_LINE_ADDR_BITS)) + | (Set << DCACHE_BYTES_PER_LINE_ADDR_BITS); + StoreCacheLineToMemory(old_addr, line->Data, DCACHE_BYTES_PER_LINE); + } + + // 加载新行(写分配) + LoadCacheLineFromMemory(Address, line->Data, DCACHE_BYTES_PER_LINE); + line->Valid = 1; + line->Tag = Tag; + line->NRU = 6; // 初始NRU设为6 + line->Dirty = 0; + + // 写操作处理 + if (Operation != 'L') { + for (int i = 0; i < DataSize; i++) { + line->Data[Block+i] = (StoreValue >> (i*8)) & 0xFF; + } + line->Dirty = 1; + } + + // 读操作设置结果 + if (Operation == 'L') { + *LoadResult = 0; + for (int i = DataSize-1; i >= 0; i--) { + *LoadResult = (*LoadResult << 8) | line->Data[Block+i]; + } + } + } + } + return MissFlag; +} + +#ifdef ICACHE_ENABLE +void InitInstCache(void) { return; } +UINT8 AccessInstCache(UINT64 a, UINT8 b, UINT8 c, UINT64* d) { return 'M'; } +#endif + +9 +#include +#include "common.h" + +// debug=1:输出额外调试信息 +int debug = 0; + + +// 全相联Data Cache +// 容量为16384字节,每行容纳16个字节 +#define DCACHE_SIZE 16384 +#define DCACHE_BYTES_PER_LINE 16 // 必须是8字节的倍数 + +// 下面参数是自动计算的,你无须需改 +#define DCACHE_BYTES_PER_LINE_ADDR_BITS GET_LOG_OF_2(DCACHE_BYTES_PER_LINE) +#define DCACHE_LINES (DCACHE_SIZE/DCACHE_BYTES_PER_LINE) + +#define MAX_AGE (DCACHE_LINES-1) // 每1行有一个唯一的Age + +// Cache行的结构,包括Valid、Age、Dirty、Tag和Data。你所有的状态信息,只能记录在Cache行中! +struct DCACHE_LineStruct +{ + UINT8 Valid; + UINT16 Age; + UINT8 Dirty; + UINT64 Tag; + UINT8 Data[DCACHE_BYTES_PER_LINE]; +}; + +struct DCACHE_SetStruct { + struct DCACHE_LineStruct Line[DCACHE_LINES]; +} DCache; + + +// DCache初始化代码,模拟器启动时,会调用此InitDataCache函数 +void InitDataCache() +{ + // *********** 你需要在下面书写代码 *********** + for (int i = 0; i < DCACHE_LINES; i++) + { + DCache.Line[i].Valid = 0; + DCache.Line[i].Age = i; + DCache.Line[i].Tag = 0; + DCache.Line[i].Dirty = 0; + } + // *********** 你需要在上面书写代码 *********** +} + +// 从所有行中,找到需要替换的Cache行 +// 如果有某行的Valid=0,则返回该行行号 +// 否则,返回Age最大的行的行号 +int GetReplaceLine() +{ + // *********** 你需要在下面书写代码 *********** + int replace_line = -1; + UINT8 max_age = 0; + + for (int i = 0; i < DCACHE_LINES; i++) + { + if (!DCache.Line[i].Valid) + { + return i; + } + if (DCache.Line[i].Age > max_age) + { + max_age = DCache.Line[i].Age; + replace_line = i; + } + } + return replace_line; + // *********** 你需要在上面书写代码 *********** + return -1; +} + +// 更新Age,将HitLine指定的Cache行的Age设置为0,其他行的Age要相应调整 +// 注意!要确保所有行的Age分别为0~MAX_AGE,且唯一 +void UpdateAge(int HitLine) +{ + // *********** 你需要在下面书写代码 *********** + UINT8 old_age = DCache.Line[HitLine].Age; + DCache.Line[HitLine].Age = 0; + + for (int i = 0; i < DCACHE_LINES; i++) + { + if (i != HitLine && DCache.Line[i].Valid) + { + if (DCache.Line[i].Age < old_age) + { + DCache.Line[i].Age++; + } + } + } + // *********** 你需要在上面书写代码 *********** +} + +// Data Cache访问接口,Cache模拟器会调用此接口,来实现对你的Data Cache访问 +// Address: 访存字节地址 +// Operation: 操作:读操作('L')、写操作('S')、读-修改-写操作('M') +// DataSize: 数据大小:1字节、2字节、4字节、8字节 +// StoreValue: 当执行写操作的时候,需要写入的数据 +// LoadResult: 当执行读操作的时候,从Cache读出的数据 +// 返回值:'M'表示Miss;'H'表示Hit +UINT8 AccessDataCache(UINT64 Address, UINT8 Operation, UINT8 DataSize, + UINT64 StoreValue, UINT64* LoadResult) +{ + //UINT32 Set; + UINT8 Block; + UINT64 Tag; + UINT8 MissFlag = 'M'; + UINT64 ReadValue; + int HitLine = 0; + UINT64 HitLineAddress = 0; + + *LoadResult = 0; + + + // Address被划分为 Tag + Set + Block + + // Set Cache的组索引(每组4行) + //Set = (Address >> DCACHE_BYTES_PER_LINE_ADDR_BITS) % DCACHE_SET; + Block = Address % DCACHE_BYTES_PER_LINE; + //Tag = (Address >> DCACHE_BYTES_PER_LINE_ADDR_BITS) >> DCACHE_SET_ADDR_BITS; // 地址去掉DCACHE_SET、DCACHE_BYTES_PER_LINE,剩下的作为Tag。警告!不能将整个地址作为Tag!! + Tag = (Address >> DCACHE_BYTES_PER_LINE_ADDR_BITS); + + // 1. 首先需要检查对应的DCache行,其Valid位是否有效?Tag是否与AddressTag相等? + // 2. 如果Valid有效,且AddressTag相等,则意味着Cache访问“命中” + // 3. 如果“命中”,需要进一步判断是要读('L')还是写('S')或者是修改('M')? + MissFlag = 'M'; + for (int i = 0; i < DCACHE_LINES; i++) + { + if (DCache.Line[i].Valid == 1 && DCache.Line[i].Tag == Tag) + { + MissFlag = 'H'; + HitLine = i; + // HitLineAddress = ((DCache.Line[HitLine].Tag << DCACHE_SET_ADDR_BITS) << + // DCACHE_BYTES_PER_LINE_ADDR_BITS) | ((UINT64)Set << + // DCACHE_BYTES_PER_LINE_ADDR_BITS); // 从Tag中恢复旧的地址 + break; + } + } + if (MissFlag == 'H') + { + if (Operation == 'L') // 读操作。从DCache对应行中,读取数据,注意数据宽度与对齐要求 + { + ReadValue = 0; + switch (DataSize) + { + case 1: // 1个字节 + ReadValue = DCache.Line[HitLine].Data[Block + 0]; + break; + case 2: // 2个字节 + Block = Block & 0xFE; // 需对齐到2字节边界 + ReadValue = DCache.Line[HitLine].Data[Block + 1]; + ReadValue = ReadValue << 8; + ReadValue |= DCache.Line[HitLine].Data[Block + 0]; + break; + case 4: // 4个字节 + Block = Block & 0xFC; // 需对齐到4字节边界 + ReadValue = DCache.Line[HitLine].Data[Block + 3]; + ReadValue = ReadValue << 8; + ReadValue |= DCache.Line[HitLine].Data[Block + 2]; + ReadValue = ReadValue << 8; + ReadValue |= DCache.Line[HitLine].Data[Block + 1]; + ReadValue = ReadValue << 8; + ReadValue |= DCache.Line[HitLine].Data[Block + 0]; + break; + case 8: // 8个字节 + Block = Block & 0xF8; // 需对齐到8字节边界 + ReadValue = DCache.Line[HitLine].Data[Block + 7]; + ReadValue = ReadValue << 8; + ReadValue |= DCache.Line[HitLine].Data[Block + 6]; + ReadValue = ReadValue << 8; + ReadValue |= DCache.Line[HitLine].Data[Block + 5]; + ReadValue = ReadValue << 8; + ReadValue |= DCache.Line[HitLine].Data[Block + 4]; + ReadValue = ReadValue << 8; + ReadValue |= DCache.Line[HitLine].Data[Block + 3]; + ReadValue = ReadValue << 8; + ReadValue |= DCache.Line[HitLine].Data[Block + 2]; + ReadValue = ReadValue << 8; + ReadValue |= DCache.Line[HitLine].Data[Block + 1]; + ReadValue = ReadValue << 8; + ReadValue |= DCache.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); + switch (DataSize) + { + case 1: // 1个字节 + DCache.Line[HitLine].Data[Block + 0] = StoreValue & 0xFF; + break; + case 2: // 2个字节 + Block = Block & 0xFE; // 需对齐到2字节边界 + DCache.Line[HitLine].Data[Block + 0] = StoreValue & 0xFF; + StoreValue = StoreValue >> 8; + DCache.Line[HitLine].Data[Block + 1] = StoreValue & 0xFF; + break; + case 4: // 4个字节 + Block = Block & 0xFC; // 需对齐到4字节边界 + DCache.Line[HitLine].Data[Block + 0] = StoreValue & 0xFF; + StoreValue = StoreValue >> 8; + DCache.Line[HitLine].Data[Block + 1] = StoreValue & 0xFF; + StoreValue = StoreValue >> 8; + DCache.Line[HitLine].Data[Block + 2] = StoreValue & 0xFF; + StoreValue = StoreValue >> 8; + DCache.Line[HitLine].Data[Block + 3] = StoreValue & 0xFF; + break; + case 8: // 8个字节 + Block = Block & 0xF8; // 需对齐到8字节边界 + DCache.Line[HitLine].Data[Block + 0] = StoreValue & 0xFF; + StoreValue = StoreValue >> 8; + DCache.Line[HitLine].Data[Block + 1] = StoreValue & 0xFF; + StoreValue = StoreValue >> 8; + DCache.Line[HitLine].Data[Block + 2] = StoreValue & 0xFF; + StoreValue = StoreValue >> 8; + DCache.Line[HitLine].Data[Block + 3] = StoreValue & 0xFF; + StoreValue = StoreValue >> 8; + DCache.Line[HitLine].Data[Block + 4] = StoreValue & 0xFF; + StoreValue = StoreValue >> 8; + DCache.Line[HitLine].Data[Block + 5] = StoreValue & 0xFF; + StoreValue = StoreValue >> 8; + DCache.Line[HitLine].Data[Block + 6] = StoreValue & 0xFF; + StoreValue = StoreValue >> 8; + DCache.Line[HitLine].Data[Block + 7] = StoreValue & 0xFF; + break; + } + // 写穿透:每次更新Cache行,都需要把数据同时写入Memory + //StoreCacheLineToMemory(HitLineAddress, DCache[Set].Line[HitLine].Data, DCACHE_BYTES_PER_LINE); + DCache.Line[HitLine].Dirty = 1; //标记脏数据 + } + UpdateAge(HitLine); + } + else + { + if (debug) + printf("[%s] Address=%016llX Operation=%c DataSize=%u StoreValue=%016llX\n", + __func__, Address, Operation, DataSize, StoreValue); + if (Operation == 'L' || Operation == 'M' || Operation == 'S') // 读操作 + { + // 需要从Memory中读入新的行(真实情况下,这个LoadCacheLineFromMemory需要很长时间的) + int ReLine = GetReplaceLine(); + UINT64 ReLineAddress = 0; + + //写回:当被替换的数据为脏数据时,我们需要先将该行数据首先写入到Memory中,再执行替换 + if(DCache.Line[ReLine].Dirty == 1 && DCache.Line[ReLine].Valid == 1) + { + ReLineAddress = DCache.Line[ReLine].Tag << DCACHE_BYTES_PER_LINE_ADDR_BITS; // 从Tag中恢复旧的地址 + StoreCacheLineToMemory(ReLineAddress, DCache.Line[ReLine].Data, DCACHE_BYTES_PER_LINE); + } + + //开始替换,加载新数据 + LoadCacheLineFromMemory(Address, DCache.Line[ReLine].Data, DCACHE_BYTES_PER_LINE); + DCache.Line[ReLine].Valid = 1; + DCache.Line[ReLine].Tag = Tag; + DCache.Line[ReLine].Dirty = 0; + UpdateAge(ReLine); + + if(Operation == 'M' || Operation == 'S') + { + switch (DataSize) + { + case 1: // 1个字节 + DCache.Line[ReLine].Data[Block + 0] = StoreValue & 0xFF; + break; + case 2: // 2个字节 + Block = Block & 0xFE; // 需对齐到2字节边界 + DCache.Line[ReLine].Data[Block + 0] = StoreValue & 0xFF; + StoreValue = StoreValue >> 8; + DCache.Line[ReLine].Data[Block + 1] = StoreValue & 0xFF; + break; + case 4: // 4个字节 + Block = Block & 0xFC; // 需对齐到4字节边界 + DCache.Line[ReLine].Data[Block + 0] = StoreValue & 0xFF; + StoreValue = StoreValue >> 8; + DCache.Line[ReLine].Data[Block + 1] = StoreValue & 0xFF; + StoreValue = StoreValue >> 8; + DCache.Line[ReLine].Data[Block + 2] = StoreValue & 0xFF; + StoreValue = StoreValue >> 8; + DCache.Line[ReLine].Data[Block + 3] = StoreValue & 0xFF; + break; + case 8: // 8个字节 + Block = Block & 0xF8; // 需对齐到8字节边界 + DCache.Line[ReLine].Data[Block + 0] = StoreValue & 0xFF; + StoreValue = StoreValue >> 8; + DCache.Line[ReLine].Data[Block + 1] = StoreValue & 0xFF; + StoreValue = StoreValue >> 8; + DCache.Line[ReLine].Data[Block + 2] = StoreValue & 0xFF; + StoreValue = StoreValue >> 8; + DCache.Line[ReLine].Data[Block + 3] = StoreValue & 0xFF; + StoreValue = StoreValue >> 8; + DCache.Line[ReLine].Data[Block + 4] = StoreValue & 0xFF; + StoreValue = StoreValue >> 8; + DCache.Line[ReLine].Data[Block + 5] = StoreValue & 0xFF; + StoreValue = StoreValue >> 8; + DCache.Line[ReLine].Data[Block + 6] = StoreValue & 0xFF; + StoreValue = StoreValue >> 8; + DCache.Line[ReLine].Data[Block + 7] = StoreValue & 0xFF; + break; + } + DCache.Line[ReLine].Dirty = 1; + } + //UpdateAge(ReLine); + } + } + return MissFlag; +} + +#ifdef ICACHE_ENABLE + +/* 指令Cache实现部分,可选实现 */ +void InitInstCache(void) +{ + return; +} + +UINT8 AccessInstCache(UINT64 Address, UINT8 Operation, UINT8 InstSize, + UINT64* InstResult) +{ + // ICache只有Operation = 'I'的操作,只会读。不会写 + // 返回值'M' = Miss,'H'=Hit + return 'M'; +} +#endif + diff --git a/cachelab/Cache.bak2 b/cachelab/Cache.bak2 new file mode 100644 index 0000000..316bc9d --- /dev/null +++ b/cachelab/Cache.bak2 @@ -0,0 +1,403 @@ +/////////////////////////////////////////////////////////////////////// +//// Copyright 2022 by mars. // +/////////////////////////////////////////////////////////////////////// + +#include +#include + +#include "common.h" + +#define DEBUG 0 + +#define GET_POWER_OF_2(X) (X == 0x00 ? 0 : \ + X == 0x01 ? 0 : \ + X == 0x02 ? 1 : \ + X == 0x04 ? 2 : \ + X == 0x08 ? 3 : \ + X == 0x10 ? 4 : \ + X == 0x20 ? 5 : \ + X == 0x40 ? 6 : \ + X == 0x80 ? 7 : \ + X == 0x100 ? 8 : \ + X == 0x200 ? 9 : \ + X == 0x400 ? 10 : \ + X == 0x800 ? 11 : \ + X == 0x1000 ? 12 : \ + X == 0x2000 ? 13 : \ + X == 0x4000 ? 14 : \ + X == 0x8000 ? 15 : \ + X == 0x10000 ? 16 : \ + X == 0x20000 ? 17 : \ + X == 0x40000 ? 18 : \ + X == 0x80000 ? 19 : \ + X == 0x100000 ? 20 : \ + X == 0x200000 ? 21 : \ + X == 0x400000 ? 22 : \ + X == 0x800000 ? 23 : \ + X == 0x1000000 ? 24 : \ + X == 0x2000000 ? 25 : \ + X == 0x4000000 ? 26 : \ + X == 0x8000000 ? 27 : \ + X == 0x10000000 ? 28 : \ + X == 0x20000000 ? 29 : \ + X == 0x40000000 ? 30 : \ + X == 0x80000000 ? 31 : \ + X == 0x100000000 ? 32 : 0) + +/* + 全相联Data Cache,16KB大小 + 每行存放64个字节 +*/ +#define DCACHE_SIZE 16384 +#define DCACHE_DATA_PER_LINE 128 // 必须是8字节的倍数 +#define DCACHE_DATA_PER_LINE_ADDR_BITS GET_POWER_OF_2(DCACHE_DATA_PER_LINE) // 必须与上面设置一致,即64字节,需要6位地址 +#define DCACHE_LINES (DCACHE_SIZE/DCACHE_DATA_PER_LINE) +#define MAX_AGE (DCACHE_LINES-1) // 每行有一个唯一的Age + +// Cache行的结构,包括Valid、Age、Dirty、Tag和Data。你所有的状态信息,只能记录在Cache行中! +struct DCACHE_LineStruct +{ + UINT8 Valid; + UINT16 Age; + UINT8 Dirty; + UINT64 Tag; + UINT8 Data[DCACHE_DATA_PER_LINE]; +} DCache[DCACHE_LINES]; + +/* + DCache初始化代码,一般需要把DCache的有效位Valid设置为0 + 模拟器启动时,会调用此InitDataCache函数 +*/ +void InitDataCache() +{ + UINT32 i; + printf("[%s] +-----------------------------------+\n", __func__); + printf("[%s] | 威震天的Data Cache初始化ing.... |\n", __func__); + printf("[%s] +-----------------------------------+\n", __func__); + for (i = 0; i < DCACHE_LINES; i++) { + DCache[i].Valid = 0; + DCache[i].Age = i; + DCache[i].Dirty = 0; + } +} + +/* + 从Memory中读入一行数据到Data Cache中 +*/ +void LoadDataCacheLineFromMemory(UINT64 Address, UINT32 CacheLineIndex) +{ + // 一次性从Memory中将DCACHE_DATA_PER_LINE数据读入某个Data Cache行 + // 提供了一个函数,一次可以读入8个字节 + UINT32 i; + UINT64 ReadData; + UINT64 AlignAddress; + UINT64* pp; + + AlignAddress = Address & ~(DCACHE_DATA_PER_LINE - 1); // 地址必须对齐到DCACHE_DATA_PER_LINE (64)字节边界 + pp = (UINT64*)DCache[CacheLineIndex].Data; + for (i = 0; i < DCACHE_DATA_PER_LINE / 8; i++) + { + ReadData = ReadMemory(AlignAddress + 8LL * i); + if (DEBUG) + printf("[%s] Address=%016llX ReadData=%016llX\n", __func__, AlignAddress + 8LL * i, ReadData); + pp[i] = ReadData; + } +} + +/* + 将Data Cache中的一行数据,写入存储器 +*/ +void StoreDataCacheLineToMemory(UINT64 Address, UINT32 CacheLineIndex) +{ + // 一次性将DCACHE_DATA_PER_LINE数据从某个Data Cache行写入Memory中 + // 提供了一个函数,一次可以写入8个字节 + UINT32 i; + UINT64 WriteData; + UINT64 AlignAddress; + UINT64* pp; + + AlignAddress = Address & ~(DCACHE_DATA_PER_LINE - 1); // 地址必须对齐到DCACHE_DATA_PER_LINE (64)字节边界 + pp = (UINT64*)DCache[CacheLineIndex].Data; + WriteData = 0; + for (i = 0; i < DCACHE_DATA_PER_LINE / 8; i++) + { + WriteData = pp[i]; + WriteMemory(AlignAddress + 8LL * i, WriteData); + if (DEBUG) + printf("[%s] Address=%016llX WriteData=%016llX\n", __func__, AlignAddress + 8LL * i, WriteData); + } +} + +// 从所有行中,找到需要替换的Cache行 +// 如果有某行的Valid=0,则返回该行行号 +// 否则,返回Age最大的行的行号 +int GetReplaceLine() +{ + int replace_line = -1; + UINT16 max_age = 0; + + for (int i = 0; i < DCACHE_LINES; i++) + { + if (!DCache[i].Valid) + { + return i; + } + if (DCache[i].Age > max_age) + { + max_age = DCache[i].Age; + replace_line = i; + } + } + return replace_line; +} + +// 更新Age,将HitLine指定的Cache行的Age设置为0,其他行的Age要相应调整 +// 注意!要确保所有行的Age分别为0~MAX_AGE,且唯一 +// void UpdateAge(int HitLine) +// { +// for (int i = 0; i < DCACHE_LINES; i++) { +// if (i != HitLine && DCache[i].Valid && DCache[i].Age < MAX_AGE) { +// DCache[i].Age++; +// } +// } +// DCache[HitLine].Age = 0; +// } + +void UpdateAge(int HitLine) +{ + UINT16 old_age = DCache[HitLine].Age; + DCache[HitLine].Age = 0; + + for (int i = 0; i < DCACHE_LINES; i++) + { + if (i != HitLine && DCache[i].Valid) + { + if (DCache[i].Age < old_age) + { + DCache[i].Age++; + } + } + } +} + +/* + Data Cache访问接口,系统模拟器会调用此接口,来实现对你的Data Cache访问 + Address: 访存字节地址 + Operation: 操作:读操作('L')、写操作('S')、读-修改-写操作('M') + DataSize: 数据大小:1字节、2字节、4字节、8字节 + StoreValue: 当执行写操作的时候,需要写入的数据 + LoadResult: 当执行读操作的时候,从Cache读出的数据 +*/ +UINT8 AccessDataCache(UINT64 Address, UINT8 Operation, UINT8 DataSize, UINT64 StoreValue, UINT64* LoadResult) +{ + UINT8 BlockOffset; + UINT64 AddressTag; + UINT8 MissFlag = 'M'; + UINT64 ReadValue; + int HitLine = -1; + + *LoadResult = 0; + + // 全相联中,Address被切分为 AddressTag 和 BlockOffset + BlockOffset = Address % DCACHE_DATA_PER_LINE; + AddressTag = Address >> DCACHE_DATA_PER_LINE_ADDR_BITS; + + // 查找是否命中 + for (int i = 0; i < DCACHE_LINES; i++) { + if (DCache[i].Valid == 1 && DCache[i].Tag == AddressTag) { + MissFlag = 'H'; + HitLine = i; + break; + } + } + + if (MissFlag == 'H') // 命中! + { + if (Operation == 'L') // 读操作 + { + ReadValue = 0; + switch (DataSize) + { + case 1: // 1个字节 + ReadValue = DCache[HitLine].Data[BlockOffset + 0]; + break; + case 2: // 2个字节 + BlockOffset = BlockOffset & 0xFE; // 需对齐到2字节边界 + ReadValue = DCache[HitLine].Data[BlockOffset + 1]; ReadValue = ReadValue << 8; + ReadValue |= DCache[HitLine].Data[BlockOffset + 0]; + break; + case 4: // 4个字节 + BlockOffset = BlockOffset & 0xFC; // 需对齐到4字节边界 + ReadValue = DCache[HitLine].Data[BlockOffset + 3]; ReadValue = ReadValue << 8; + ReadValue |= DCache[HitLine].Data[BlockOffset + 2]; ReadValue = ReadValue << 8; + ReadValue |= DCache[HitLine].Data[BlockOffset + 1]; ReadValue = ReadValue << 8; + ReadValue |= DCache[HitLine].Data[BlockOffset + 0]; + break; + case 8: // 8个字节 + BlockOffset = BlockOffset & 0xF8; // 需对齐到8字节边界 + ReadValue = DCache[HitLine].Data[BlockOffset + 7]; ReadValue = ReadValue << 8; + ReadValue |= DCache[HitLine].Data[BlockOffset + 6]; ReadValue = ReadValue << 8; + ReadValue |= DCache[HitLine].Data[BlockOffset + 5]; ReadValue = ReadValue << 8; + ReadValue |= DCache[HitLine].Data[BlockOffset + 4]; ReadValue = ReadValue << 8; + ReadValue |= DCache[HitLine].Data[BlockOffset + 3]; ReadValue = ReadValue << 8; + ReadValue |= DCache[HitLine].Data[BlockOffset + 2]; ReadValue = ReadValue << 8; + ReadValue |= DCache[HitLine].Data[BlockOffset + 1]; ReadValue = ReadValue << 8; + ReadValue |= DCache[HitLine].Data[BlockOffset + 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); + switch (DataSize) + { + case 1: // 1个字节 + DCache[HitLine].Data[BlockOffset + 0] = StoreValue & 0xFF; + break; + case 2: // 2个字节 + BlockOffset = BlockOffset & 0xFE; // 需对齐到2字节边界 + DCache[HitLine].Data[BlockOffset + 0] = StoreValue & 0xFF; StoreValue = StoreValue >> 8; + DCache[HitLine].Data[BlockOffset + 1] = StoreValue & 0xFF; + break; + case 4: // 4个字节 + BlockOffset = BlockOffset & 0xFC; // 需对齐到4字节边界 + DCache[HitLine].Data[BlockOffset + 0] = StoreValue & 0xFF; StoreValue = StoreValue >> 8; + DCache[HitLine].Data[BlockOffset + 1] = StoreValue & 0xFF; StoreValue = StoreValue >> 8; + DCache[HitLine].Data[BlockOffset + 2] = StoreValue & 0xFF; StoreValue = StoreValue >> 8; + DCache[HitLine].Data[BlockOffset + 3] = StoreValue & 0xFF; + break; + case 8: // 8个字节 + BlockOffset = BlockOffset & 0xF8; // 需对齐到8字节边界 + DCache[HitLine].Data[BlockOffset + 0] = StoreValue & 0xFF; StoreValue = StoreValue >> 8; + DCache[HitLine].Data[BlockOffset + 1] = StoreValue & 0xFF; StoreValue = StoreValue >> 8; + DCache[HitLine].Data[BlockOffset + 2] = StoreValue & 0xFF; StoreValue = StoreValue >> 8; + DCache[HitLine].Data[BlockOffset + 3] = StoreValue & 0xFF; StoreValue = StoreValue >> 8; + DCache[HitLine].Data[BlockOffset + 4] = StoreValue & 0xFF; StoreValue = StoreValue >> 8; + DCache[HitLine].Data[BlockOffset + 5] = StoreValue & 0xFF; StoreValue = StoreValue >> 8; + DCache[HitLine].Data[BlockOffset + 6] = StoreValue & 0xFF; StoreValue = StoreValue >> 8; + DCache[HitLine].Data[BlockOffset + 7] = StoreValue & 0xFF; + break; + } + // 写回策略:数据只写入Cache,并设置脏位 + DCache[HitLine].Dirty = 1; + } + // 更新访问时间 + UpdateAge(HitLine); + } + else + { + if (DEBUG) + printf("[%s] Address=%016llX Operation=%c DataSize=%u StoreValue=%016llX\n", __func__, Address, Operation, DataSize, StoreValue); + + // 不命中, 获取要替换的行 + int ReplaceLine = GetReplaceLine(); + + // 如果要替换的行有效且脏,则需要写回到内存 + if (DCache[ReplaceLine].Valid == 1 && DCache[ReplaceLine].Dirty == 1) + { + UINT64 OldAddress = DCache[ReplaceLine].Tag << DCACHE_DATA_PER_LINE_ADDR_BITS; + StoreDataCacheLineToMemory(OldAddress, ReplaceLine); + } + + // 需要从Memory中读入新的行 + LoadDataCacheLineFromMemory(Address, ReplaceLine); + DCache[ReplaceLine].Valid = 1; + DCache[ReplaceLine].Tag = AddressTag; + DCache[ReplaceLine].Dirty = 0; + + if (Operation == 'S' || Operation == 'M') // 写操作(修改操作在此等价于写操作) + { + // 写操作,需要将新的StoreValue更新到CacheLine中 + switch (DataSize) + { + case 1: // 1个字节 + DCache[ReplaceLine].Data[BlockOffset + 0] = StoreValue & 0xFF; + break; + case 2: // 2个字节 + BlockOffset = BlockOffset & 0xFE; // 需对齐到2字节边界 + DCache[ReplaceLine].Data[BlockOffset + 0] = StoreValue & 0xFF; StoreValue = StoreValue >> 8; + DCache[ReplaceLine].Data[BlockOffset + 1] = StoreValue & 0xFF; + break; + case 4: // 4个字节 + BlockOffset = BlockOffset & 0xFC; // 需对齐到4字节边界 + DCache[ReplaceLine].Data[BlockOffset + 0] = StoreValue & 0xFF; StoreValue = StoreValue >> 8; + DCache[ReplaceLine].Data[BlockOffset + 1] = StoreValue & 0xFF; StoreValue = StoreValue >> 8; + DCache[ReplaceLine].Data[BlockOffset + 2] = StoreValue & 0xFF; StoreValue = StoreValue >> 8; + DCache[ReplaceLine].Data[BlockOffset + 3] = StoreValue & 0xFF; + break; + case 8: // 8个字节 + BlockOffset = BlockOffset & 0xF8; // 需对齐到8字节边界 + DCache[ReplaceLine].Data[BlockOffset + 0] = StoreValue & 0xFF; StoreValue = StoreValue >> 8; + DCache[ReplaceLine].Data[BlockOffset + 1] = StoreValue & 0xFF; StoreValue = StoreValue >> 8; + DCache[ReplaceLine].Data[BlockOffset + 2] = StoreValue & 0xFF; StoreValue = StoreValue >> 8; + DCache[ReplaceLine].Data[BlockOffset + 3] = StoreValue & 0xFF; StoreValue = StoreValue >> 8; + DCache[ReplaceLine].Data[BlockOffset + 4] = StoreValue & 0xFF; StoreValue = StoreValue >> 8; + DCache[ReplaceLine].Data[BlockOffset + 5] = StoreValue & 0xFF; StoreValue = StoreValue >> 8; + DCache[ReplaceLine].Data[BlockOffset + 6] = StoreValue & 0xFF; StoreValue = StoreValue >> 8; + DCache[ReplaceLine].Data[BlockOffset + 7] = StoreValue & 0xFF; + break; + } + DCache[ReplaceLine].Dirty = 1; + } + // 更新访问时间 + UpdateAge(ReplaceLine); + + if (Operation == 'L') // 读操作需要返回读取的值 + { + ReadValue = 0; + switch (DataSize) + { + case 1: // 1个字节 + ReadValue = DCache[ReplaceLine].Data[BlockOffset + 0]; + break; + case 2: // 2个字节 + BlockOffset = BlockOffset & 0xFE; // 需对齐到2字节边界 + ReadValue = DCache[ReplaceLine].Data[BlockOffset + 1]; ReadValue = ReadValue << 8; + ReadValue |= DCache[ReplaceLine].Data[BlockOffset + 0]; + break; + case 4: // 4个字节 + BlockOffset = BlockOffset & 0xFC; // 需对齐到4字节边界 + ReadValue = DCache[ReplaceLine].Data[BlockOffset + 3]; ReadValue = ReadValue << 8; + ReadValue |= DCache[ReplaceLine].Data[BlockOffset + 2]; ReadValue = ReadValue << 8; + ReadValue |= DCache[ReplaceLine].Data[BlockOffset + 1]; ReadValue = ReadValue << 8; + ReadValue |= DCache[ReplaceLine].Data[BlockOffset + 0]; + break; + case 8: // 8个字节 + BlockOffset = BlockOffset & 0xF8; // 需对齐到8字节边界 + ReadValue = DCache[ReplaceLine].Data[BlockOffset + 7]; ReadValue = ReadValue << 8; + ReadValue |= DCache[ReplaceLine].Data[BlockOffset + 6]; ReadValue = ReadValue << 8; + ReadValue |= DCache[ReplaceLine].Data[BlockOffset + 5]; ReadValue = ReadValue << 8; + ReadValue |= DCache[ReplaceLine].Data[BlockOffset + 4]; ReadValue = ReadValue << 8; + ReadValue |= DCache[ReplaceLine].Data[BlockOffset + 3]; ReadValue = ReadValue << 8; + ReadValue |= DCache[ReplaceLine].Data[BlockOffset + 2]; ReadValue = ReadValue << 8; + ReadValue |= DCache[ReplaceLine].Data[BlockOffset + 1]; ReadValue = ReadValue << 8; + ReadValue |= DCache[ReplaceLine].Data[BlockOffset + 0]; + break; + } + *LoadResult = ReadValue; + } + } + return MissFlag; +} + +/* 指令Cache实现部分,可选实现 */ +void InitInstCache(void) +{ + return; +} + +void LoadInstCacheLineFromMemory(UINT64 Address, UINT32 CacheLineAddress) +{ + return; +} + +UINT8 AccessInstCache(UINT64 Address, UINT8 Operation, UINT8 InstSize, UINT64* InstResult) +{ + // 返回值'M' = Miss,'H'=Hit + return 'M'; +} + diff --git a/cachelab/Cache.c b/cachelab/Cache.c index 732bb47..316c5f1 100644 --- a/cachelab/Cache.c +++ b/cachelab/Cache.c @@ -1,14 +1,14 @@ -/////////////////////////////////////////////////////////////////////// -//// Copyright 2022 by mars. // -/////////////////////////////////////////////////////////////////////// - +/// +// Copyright 2022 by mars. // +/// + #include #include - + #include "common.h" - + #define DEBUG 0 - + #define GET_POWER_OF_2(X) (X == 0x00 ? 0 : \ X == 0x01 ? 0 : \ X == 0x02 ? 1 : \ @@ -43,87 +43,150 @@ X == 0x40000000 ? 30 : \ X == 0x80000000 ? 31 : \ X == 0x100000000 ? 32 : 0) - + /* - 直接映射Data Cache,16KB大小 - 每行存放64个字节,共256行 + 组相联映射Data Cache,16KB大小 + 每行存放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) +#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位地址 - -// Cache行的结构,包括Valid、Tag和Data。你所有的状态信息,只能记录在Cache行中! + +// DCache行的结构,包括Valid、Tag、Age、Dirty和Data。你所有的状态信息,只能记录在Cache行中! 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]; - + /* DCache初始化代码,一般需要把DCache的有效位Valid设置为0 模拟器启动时,会调用此InitDataCache函数 */ void InitDataCache() { - UINT32 i; + UINT32 i, j; printf("[%s] +-----------------------------------+\n", __func__); - printf("[%s] | 威震天的Data Cache初始化ing.... |\n", __func__); + printf("[%s] | derder的Data Cache初始化ing.... |\n", __func__); printf("[%s] +-----------------------------------+\n", __func__); for (i = 0; i < DCACHE_SET; i++) - DCache[i].Valid = 0; + { + 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; + } + } } - + +// 在第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; +} + +// 更新Age,在第Set组中,将HitLine指定的Cache行的Age设置为0,其他行的Age要相应调整 +// 注意!要确保DCACHE_LINE_PER_SET行的Age分别为0~DCACHE_LINE_PER_SET-1,且唯一 +void UpdateAgeData(UINT32 Set, UINT8 HitLine) +{ + int HitAge = DCache[Set].Line[HitLine].Age; + DCache[Set].Line[HitLine].Age = 0; + for (int i = 0; i < DCACHE_LINE_PER_SET; i++) + { + if (i != HitLine && DCache[Set].Line[i].Age < HitAge) + DCache[Set].Line[i].Age++; + } +} + /* 从Memory中读入一行数据到Data Cache中 */ -void LoadDataCacheLineFromMemory(UINT64 Address, UINT32 CacheLineAddress) +void LoadDataCacheLineFromMemory(UINT64 Address, UINT32 set, UINT8 line) { // 一次性从Memory中将DCACHE_DATA_PER_LINE数据读入某个Data Cache行 // 提供了一个函数,一次可以读入8个字节 - UINT32 i; - UINT64 ReadData; - UINT64 AlignAddress; - UINT64* pp; - - AlignAddress = Address & ~(DCACHE_DATA_PER_LINE - 1); // 地址必须对齐到DCACHE_DATA_PER_LINE (64)字节边界 - pp = (UINT64*)DCache[CacheLineAddress].Data; - for (i = 0; i < DCACHE_DATA_PER_LINE / 8; i++) - { - ReadData = ReadMemory(AlignAddress + 8LL * i); - if (DEBUG) - printf("[%s] Address=%016llX ReadData=%016llX\n", __func__, AlignAddress + 8LL * i, ReadData); - pp[i] = ReadData; - } - + + // 地址对齐到缓存行边界 + 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); + } + } + } - + /* 将Data Cache中的一行数据,写入存储器 */ -void StoreDataCacheLineToMemory(UINT64 Address, UINT32 CacheLineAddress) +void StoreDataCacheLineToMemory(UINT64 Address, UINT32 set,UINT8 line) { // 一次性将DCACHE_DATA_PER_LINE数据从某个Data Cache行写入Memory中 // 提供了一个函数,一次可以写入8个字节 - UINT32 i; - UINT64 WriteData; - UINT64 AlignAddress; - UINT64* pp; - - AlignAddress = Address & ~(DCACHE_DATA_PER_LINE - 1); // 地址必须对齐到DCACHE_DATA_PER_LINE (64)字节边界 - pp = (UINT64*)DCache[CacheLineAddress].Data; - WriteData = 0; - for (i = 0; i < DCACHE_DATA_PER_LINE / 8; i++) - { - WriteData = pp[i]; - WriteMemory(AlignAddress + 8LL * i, WriteData); - if (DEBUG) - printf("[%s] Address=%016llX ReadData=%016llX\n", __func__, AlignAddress + 8LL * i, WriteData); - } + + // 地址对齐到缓存行边界 + 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); + } + } } - + /* Data Cache访问接口,系统模拟器会调用此接口,来实现对你的Data Cache访问 Address: 访存字节地址 @@ -134,57 +197,64 @@ void StoreDataCacheLineToMemory(UINT64 Address, UINT32 CacheLineAddress) */ UINT8 AccessDataCache(UINT64 Address, UINT8 Operation, UINT8 DataSize, UINT64 StoreValue, UINT64* LoadResult) { - UINT32 CacheLineAddress; - UINT8 BlockOffset; - UINT64 AddressTag; + UINT8 Block; + UINT32 Set; + UINT64 Tag; UINT8 MissFlag = 'M'; UINT64 ReadValue; - + UINT8 HitLine; + *LoadResult = 0; - + /* - * 直接映射中,Address被切分为 AddressTag,CacheLineAddress,BlockOffset + * 组相联映射中,Address被切分为 Tag,Set,Block */ - - // CacheLineAddress Cache的行号,在直接映射中,就是组号(每组1行) - CacheLineAddress = (Address >> DCACHE_DATA_PER_LINE_ADDR_BITS) % DCACHE_SET; - BlockOffset = Address % DCACHE_DATA_PER_LINE; - AddressTag = (Address >> DCACHE_DATA_PER_LINE_ADDR_BITS) >> DCACHE_SET_ADDR_BITS; // 地址去掉DCACHE_SET、DCACHE_DATA_PER_LINE,剩下的作为Tag。警告!不能将整个地址作为Tag!! - - if (DCache[CacheLineAddress].Valid == 1 && DCache[CacheLineAddress].Tag == AddressTag) + + Set = (Address >> DCACHE_DATA_PER_LINE_ADDR_BITS) % DCACHE_SET; + Block = Address % DCACHE_DATA_PER_LINE; + Tag = Address >> (DCACHE_DATA_PER_LINE_ADDR_BITS + DCACHE_SET_ADDR_BITS); + + // 检查命中 + 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') { - MissFlag = 'H'; // 命中! - if (Operation == 'L') // 读操作 { ReadValue = 0; switch (DataSize) { case 1: // 1个字节 - ReadValue = DCache[CacheLineAddress].Data[BlockOffset + 0]; + ReadValue = DCache[Set].Line[HitLine].Data[Block + 0]; break; case 2: // 2个字节 - BlockOffset = BlockOffset & 0xFE; // 需对齐到2字节边界 - ReadValue = DCache[CacheLineAddress].Data[BlockOffset + 1]; ReadValue = ReadValue << 8; - ReadValue |= DCache[CacheLineAddress].Data[BlockOffset + 0]; + 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个字节 - BlockOffset = BlockOffset & 0xFC; // 需对齐到4字节边界 - ReadValue = DCache[CacheLineAddress].Data[BlockOffset + 3]; ReadValue = ReadValue << 8; - ReadValue |= DCache[CacheLineAddress].Data[BlockOffset + 2]; ReadValue = ReadValue << 8; - ReadValue |= DCache[CacheLineAddress].Data[BlockOffset + 1]; ReadValue = ReadValue << 8; - ReadValue |= DCache[CacheLineAddress].Data[BlockOffset + 0]; + 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个字节 - BlockOffset = BlockOffset & 0xF8; // 需对齐到8字节边界 - ReadValue = DCache[CacheLineAddress].Data[BlockOffset + 7]; ReadValue = ReadValue << 8; - ReadValue |= DCache[CacheLineAddress].Data[BlockOffset + 6]; ReadValue = ReadValue << 8; - ReadValue |= DCache[CacheLineAddress].Data[BlockOffset + 5]; ReadValue = ReadValue << 8; - ReadValue |= DCache[CacheLineAddress].Data[BlockOffset + 4]; ReadValue = ReadValue << 8; - ReadValue |= DCache[CacheLineAddress].Data[BlockOffset + 3]; ReadValue = ReadValue << 8; - ReadValue |= DCache[CacheLineAddress].Data[BlockOffset + 2]; ReadValue = ReadValue << 8; - ReadValue |= DCache[CacheLineAddress].Data[BlockOffset + 1]; ReadValue = ReadValue << 8; - ReadValue |= DCache[CacheLineAddress].Data[BlockOffset + 0]; + 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; @@ -198,105 +268,347 @@ UINT8 AccessDataCache(UINT64 Address, UINT8 Operation, UINT8 DataSize, UINT64 St switch (DataSize) { case 1: // 1个字节 - DCache[CacheLineAddress].Data[BlockOffset + 0] = StoreValue & 0xFF; + DCache[Set].Line[HitLine].Data[Block + 0] = StoreValue & 0xFF; break; case 2: // 2个字节 - BlockOffset = BlockOffset & 0xFE; // 需对齐到2字节边界 - DCache[CacheLineAddress].Data[BlockOffset + 0] = StoreValue & 0xFF; StoreValue = StoreValue >> 8; - DCache[CacheLineAddress].Data[BlockOffset + 1] = StoreValue & 0xFF; + 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个字节 - BlockOffset = BlockOffset & 0xFC; // 需对齐到4字节边界 - DCache[CacheLineAddress].Data[BlockOffset + 0] = StoreValue & 0xFF; StoreValue = StoreValue >> 8; - DCache[CacheLineAddress].Data[BlockOffset + 1] = StoreValue & 0xFF; StoreValue = StoreValue >> 8; - DCache[CacheLineAddress].Data[BlockOffset + 2] = StoreValue & 0xFF; StoreValue = StoreValue >> 8; - DCache[CacheLineAddress].Data[BlockOffset + 3] = StoreValue & 0xFF; + 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个字节 - BlockOffset = BlockOffset & 0xF8; // 需对齐到8字节边界 - DCache[CacheLineAddress].Data[BlockOffset + 0] = StoreValue & 0xFF; StoreValue = StoreValue >> 8; - DCache[CacheLineAddress].Data[BlockOffset + 1] = StoreValue & 0xFF; StoreValue = StoreValue >> 8; - DCache[CacheLineAddress].Data[BlockOffset + 2] = StoreValue & 0xFF; StoreValue = StoreValue >> 8; - DCache[CacheLineAddress].Data[BlockOffset + 3] = StoreValue & 0xFF; StoreValue = StoreValue >> 8; - DCache[CacheLineAddress].Data[BlockOffset + 4] = StoreValue & 0xFF; StoreValue = StoreValue >> 8; - DCache[CacheLineAddress].Data[BlockOffset + 5] = StoreValue & 0xFF; StoreValue = StoreValue >> 8; - DCache[CacheLineAddress].Data[BlockOffset + 6] = StoreValue & 0xFF; StoreValue = StoreValue >> 8; - DCache[CacheLineAddress].Data[BlockOffset + 7] = StoreValue & 0xFF; + 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; } + UpdateAgeData(Set, HitLine); } - else + else if(MissFlag=='M') { if (DEBUG) printf("[%s] Address=%016llX Operation=%c DataSize=%u StoreValue=%016llX\n", __func__, Address, Operation, DataSize, StoreValue); - MissFlag = 'M'; // 不命中 - if (DCache[CacheLineAddress].Valid == 1) - { - // 淘汰对应的Cache行,如果对应的Cache行有数据,需要写回到Memory中 - UINT64 OldAddress; - // OldAddress = > (Tag,Set,0000) - OldAddress = ((DCache[CacheLineAddress].Tag << DCACHE_SET_ADDR_BITS) << DCACHE_DATA_PER_LINE_ADDR_BITS) | ((UINT64)CacheLineAddress << DCACHE_DATA_PER_LINE_ADDR_BITS); // 从Tag中恢复旧的地址 - StoreDataCacheLineToMemory(OldAddress, CacheLineAddress); - } - // 需要从Memory中读入新的行(真实情况下,这个LoadCacheLineFromMemory需要很长时间的) - LoadDataCacheLineFromMemory(Address, CacheLineAddress); - DCache[CacheLineAddress].Valid = 1; - DCache[CacheLineAddress].Tag = AddressTag; + UINT8 replace_Line = GetReplaceLineData(Set); + if (Operation == 'L') // 读操作 { - // 读操作不需要做事情,因为已经MISS了 + // 写回脏行 + 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); + StoreDataCacheLineToMemory(victim_addr,Set,replace_Line); + } + + // 加载新数据 + LoadDataCacheLineFromMemory(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; + + ReadValue = 0; + switch (DataSize) + { + case 1: // 1个字节 + ReadValue = DCache[Set].Line[replace_Line].Data[Block + 0]; + 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') // 写操作(修改操作在此等价于写操作) { + // 写回脏行 + 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); + StoreDataCacheLineToMemory(victim_addr,Set,replace_Line); + } + + // 加载新数据 + LoadDataCacheLineFromMemory(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[CacheLineAddress].Data[BlockOffset + 0] = StoreValue & 0xFF; + DCache[Set].Line[replace_Line].Data[Block + 0] = StoreValue & 0xFF; break; case 2: // 2个字节 - BlockOffset = BlockOffset & 0xFE; // 需对齐到2字节边界 - DCache[CacheLineAddress].Data[BlockOffset + 0] = StoreValue & 0xFF; StoreValue = StoreValue >> 8; - DCache[CacheLineAddress].Data[BlockOffset + 1] = StoreValue & 0xFF; + 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个字节 - BlockOffset = BlockOffset & 0xFC; // 需对齐到4字节边界 - DCache[CacheLineAddress].Data[BlockOffset + 0] = StoreValue & 0xFF; StoreValue = StoreValue >> 8; - DCache[CacheLineAddress].Data[BlockOffset + 1] = StoreValue & 0xFF; StoreValue = StoreValue >> 8; - DCache[CacheLineAddress].Data[BlockOffset + 2] = StoreValue & 0xFF; StoreValue = StoreValue >> 8; - DCache[CacheLineAddress].Data[BlockOffset + 3] = StoreValue & 0xFF; + 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个字节 - BlockOffset = BlockOffset & 0xF8; // 需对齐到8字节边界 - DCache[CacheLineAddress].Data[BlockOffset + 0] = StoreValue & 0xFF; StoreValue = StoreValue >> 8; - DCache[CacheLineAddress].Data[BlockOffset + 1] = StoreValue & 0xFF; StoreValue = StoreValue >> 8; - DCache[CacheLineAddress].Data[BlockOffset + 2] = StoreValue & 0xFF; StoreValue = StoreValue >> 8; - DCache[CacheLineAddress].Data[BlockOffset + 3] = StoreValue & 0xFF; StoreValue = StoreValue >> 8; - DCache[CacheLineAddress].Data[BlockOffset + 4] = StoreValue & 0xFF; StoreValue = StoreValue >> 8; - DCache[CacheLineAddress].Data[BlockOffset + 5] = StoreValue & 0xFF; StoreValue = StoreValue >> 8; - DCache[CacheLineAddress].Data[BlockOffset + 6] = StoreValue & 0xFF; StoreValue = StoreValue >> 8; - DCache[CacheLineAddress].Data[BlockOffset + 7] = StoreValue & 0xFF; + 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; + UpdateAgeData(Set, replace_Line); } + UpdateAgeData(Set, replace_Line); } return MissFlag; } - -/* 指令Cache实现部分,可选实现 */ + +/* + 组相联映射Instruction Cache,16KB大小 + 每行存放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) + +// 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 InitInstCache(void) { - return; + UINT32 i, j; + printf("[%s] +-----------------------------------+\n", __func__); + printf("[%s] | derder的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; + } + } } - -void LoadInstCacheLineFromMemory(UINT64 Address, UINT32 CacheLineAddress) + +//LRU替换策略 +UINT8 GetReplaceLineInst(UINT32 Set) { - return; + 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要相应调整 +// 注意!要确保ICACHE_LINE_PER_SET行的Age分别为0~ICACHE_LINE_PER_SET-1,且唯一 +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++; + } +} + +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); + } + } + +} + UINT8 AccessInstCache(UINT64 Address, UINT8 Operation, UINT8 InstSize, UINT64* InstResult) { - // 返回值'M' = Miss,'H'=Hit - return 'M'; -} \ No newline at end of file + 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; + + *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); + LoadInstCacheLineFromMemory(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); + } + return MissFlag; +} diff --git a/cachelab/Makefile b/cachelab/Makefile index 799f83a..6228e56 100644 --- a/cachelab/Makefile +++ b/cachelab/Makefile @@ -8,7 +8,7 @@ LDFLAGS += LDLIBS += -lzstd -CPPFLAGS := -O3 -Wall -Wextra -Winline -Winit-self -Wno-sequence-point\ +CPPFLAGS := -Ofast -Wall -Wextra -Winline -Winit-self -Wno-sequence-point\ -Wno-unused-function -Wno-inline -fPIC -W -Wcast-qual -Wpointer-arith -Icbsl/include #CPPFLAGS := -g @@ -19,7 +19,7 @@ objects = Cache.o CacheHelper.o getopt.o cbsl/src/buffer.o cbsl/src/file.o cbsl/ all: $(PROGRAMS) Cache : $(objects) - gcc $(CPPFLAGS) $(LDFLAGS) -o $@ $^ $(LDLIBS) + icx $(CPPFLAGS) $(LDFLAGS) -o $@ $^ $(LDLIBS) rm -f $(objects) clean: diff --git a/perflab/matrix/rowcol.c b/perflab/matrix/rowcol.c new file mode 100644 index 0000000..e69de29 diff --git a/perflab/poly/a.exe b/perflab/poly/a.exe index 305d39b..9e6e928 100755 Binary files a/perflab/poly/a.exe and b/perflab/poly/a.exe differ diff --git a/perflab/poly/poly.c b/perflab/poly/poly.c new file mode 100644 index 0000000..e69de29