/////////////////////////////////////////////////////////////////////// //// 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'; }