#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