/////////////////////////////////////////////////////////////////////// //// 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个字节,共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_ADDR_BITS GET_POWER_OF_2(DCACHE_SET) // 必须与上面设置一致,即256行,需要8位地址 // Cache行的结构,包括Valid、Tag和Data。你所有的状态信息,只能记录在Cache行中! struct DCACHE_LineStruct { UINT8 Valid; UINT64 Tag; UINT8 Data[DCACHE_DATA_PER_LINE]; }DCache[DCACHE_SET]; /* 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_SET; i++) DCache[i].Valid = 0; } /* 从Memory中读入一行数据到Data Cache中 */ void LoadDataCacheLineFromMemory(UINT64 Address, UINT32 CacheLineAddress) { // 一次性从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; } } /* 将Data Cache中的一行数据,写入存储器 */ void StoreDataCacheLineToMemory(UINT64 Address, UINT32 CacheLineAddress) { // 一次性将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); } } /* 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) { UINT32 CacheLineAddress; UINT8 BlockOffset; UINT64 AddressTag; UINT8 MissFlag = 'M'; UINT64 ReadValue; *LoadResult = 0; /* * 直接映射中,Address被切分为 AddressTag,CacheLineAddress,BlockOffset */ // 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) { MissFlag = 'H'; // 命中! if (Operation == 'L') // 读操作 { ReadValue = 0; switch (DataSize) { case 1: // 1个字节 ReadValue = DCache[CacheLineAddress].Data[BlockOffset + 0]; break; case 2: // 2个字节 BlockOffset = BlockOffset & 0xFE; // 需对齐到2字节边界 ReadValue = DCache[CacheLineAddress].Data[BlockOffset + 1]; ReadValue = ReadValue << 8; ReadValue |= DCache[CacheLineAddress].Data[BlockOffset + 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]; 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]; 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[CacheLineAddress].Data[BlockOffset + 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; 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; 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; break; } } } else { 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; if (Operation == 'L') // 读操作 { // 读操作不需要做事情,因为已经MISS了 } else if (Operation == 'S' || Operation == 'M') // 写操作(修改操作在此等价于写操作) { // 写操作,需要将新的StoreValue更新到CacheLine中 switch (DataSize) { case 1: // 1个字节 DCache[CacheLineAddress].Data[BlockOffset + 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; 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; 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; break; } } } 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'; }