From 0a07661b5744587196009fd6a2515c66817fe53d Mon Sep 17 00:00:00 2001 From: DesperateProgrammer Date: Wed, 24 Jan 2024 09:51:56 +0100 Subject: [PATCH] Implemented DCache and several CP15 registers --- src/ARM.h | 30 +++- src/CP15.cpp | 355 +++++++++++++++++++++++++++++++++++++++++---- src/MemConstants.h | 13 ++ 3 files changed, 363 insertions(+), 35 deletions(-) diff --git a/src/ARM.h b/src/ARM.h index e9611b81..99ef4baf 100644 --- a/src/ARM.h +++ b/src/ARM.h @@ -311,11 +311,25 @@ public: u32 RandomLineIndex(); void ICacheLookup(u32 addr); + bool IsAddressICachable(u32 addr); + + void ICacheInvalidateAll(); void ICacheInvalidateByAddr(u32 addr); void ICacheInvalidateBySetAndWay(u8 cacheSet, u8 cacheLine); - void ICacheInvalidateAll(); - bool IsAddressICachable(u32 addr); + + void DCacheLookup(u32 addr); + bool IsAddressDCachable(u32 addr); + + void DCacheInvalidateAll(); + void DCacheInvalidateByAddr(u32 addr); + void DCacheInvalidateBySetAndWay(u8 cacheSet, u8 cacheLine); + + void DCacheClearAll(); + void DCacheClearByAddr(u32 addr); + void DCacheClearByASetAndWay(u8 cacheSet, u8 cacheLine); + + void CP15Write(u32 id, u32 val); u32 CP15Read(u32 id) const; @@ -326,6 +340,7 @@ public: u32 DTCMSetting, ITCMSetting; u32 DCacheLockDown, ICacheLockDown; + u32 CacheDebugRegisterIndex; // for aarch64 JIT they need to go up here // to be addressable by a 12-bit immediate @@ -336,9 +351,13 @@ public: u8 ITCM[ITCMPhysicalSize]; u8* DTCM; - u8 ICache[0x2000]; - u32 ICacheTags[64*4]; - u8 ICacheCount[64]; + u8 ICache[ICACHE_SIZE]; + u32 ICacheTags[ICACHE_LINESPERSET*ICACHE_SETS]; + u8 ICacheCount; + + u8 DCache[DCACHE_SIZE]; + u32 DCacheTags[DCACHE_LINESPERSET*DCACHE_SETS]; + u8 DCacheCount; u32 PU_CodeCacheable; u32 PU_DataCacheable; @@ -361,6 +380,7 @@ public: u8 MemTimings[0x100000][4]; u8* CurICacheLine; + u8* CurDCacheLine; bool (*GetMemRegion)(u32 addr, bool write, MemRegion* region); diff --git a/src/CP15.cpp b/src/CP15.cpp index 22285cf7..ea3acddf 100644 --- a/src/CP15.cpp +++ b/src/CP15.cpp @@ -57,10 +57,15 @@ void ARMv5::CP15Reset() ICacheLockDown = 0; DCacheLockDown = 0; + CacheDebugRegisterIndex = 0; memset(ICache, 0, ICACHE_SIZE); ICacheInvalidateAll(); - memset(ICacheCount, 0, ICACHE_LINESPERSET); + ICacheCount = 0; + + memset(DCache, 0, DCACHE_SIZE); + DCacheInvalidateAll(); + DCacheCount = 0; PU_CodeCacheable = 0; PU_DataCacheable = 0; @@ -87,8 +92,17 @@ void ARMv5::CP15DoSavestate(Savestate* file) file->VarArray(ITCM, ITCMPhysicalSize); file->VarArray(DTCM, DTCMPhysicalSize); + file->VarArray(ICache, sizeof(ICache)); + file->VarArray(ICacheTags, sizeof(ICacheTags)); + file->Var8(&ICacheCount); + + file->VarArray(DCache, sizeof(DCache)); + file->VarArray(DCacheTags, sizeof(DCacheTags)); + file->Var8(&DCacheCount); + file->Var32(&DCacheLockDown); file->Var32(&ICacheLockDown); + file->Var32(&CacheDebugRegisterIndex); file->Var32(&PU_CodeCacheable); file->Var32(&PU_DataCacheable); @@ -340,13 +354,13 @@ u32 ARMv5::RandomLineIndex() void ARMv5::ICacheLookup(u32 addr) { - u32 tag = addr & ~(ICACHE_LINESPERSET * ICACHE_LINELENGTH - 1); + u32 tag = (addr & ~(ICACHE_LINELENGTH - 1)) | CACHE_FLAG_VALID; u32 id = (addr >> ICACHE_LINELENGTH_LOG2) & (ICACHE_LINESPERSET-1); id <<= ICACHE_SETS_LOG2; for (int set=0;set>ICACHE_SETS_LOG2]; - ICacheCount[id>>ICACHE_SETS_LOG2] = (line+1) & (ICACHE_SETS-1); + line = ICacheCount; + ICacheCount = (line+1) & (ICACHE_SETS-1); } else { @@ -381,7 +395,7 @@ void ARMv5::ICacheLookup(u32 addr) *(u32*)&ptr[i] = NDS.ARM9Read32(addr+i); } - ICacheTags[line] = tag; + ICacheTags[line] = addr | (line & (ICACHE_SETS-1)) | CACHE_FLAG_VALID; // ouch :/ //printf("cache miss %08X: %d/%d\n", addr, NDS::ARM9MemTimings[addr >> 14][2], NDS::ARM9MemTimings[addr >> 14][3]); @@ -391,18 +405,15 @@ void ARMv5::ICacheLookup(u32 addr) void ARMv5::ICacheInvalidateByAddr(u32 addr) { - u32 tag = addr & ~(ICACHE_LINESPERSET * ICACHE_LINELENGTH - 1); + u32 tag = (addr & ~(ICACHE_LINELENGTH - 1)) | CACHE_FLAG_VALID; u32 id = (addr >> ICACHE_LINELENGTH_LOG2) & (ICACHE_LINESPERSET-1); id <<= ICACHE_SETS_LOG2; for (int set=0;set> 12] & 0x40 ; } +void ARMv5::DCacheLookup(u32 addr) +{ + //Log(LogLevel::Debug,"DCache load @ %08x\n", addr); + addr &= ~3; + u32 tag = (addr & ~(DCACHE_LINELENGTH - 1)) | CACHE_FLAG_VALID; + u32 id = (addr >> DCACHE_LINELENGTH_LOG2) & (DCACHE_LINESPERSET-1); + + id <<= DCACHE_SETS_LOG2; + for (int set=0;set %08lx\n", addr, ((u32 *)CurDCacheLine)[(addr & (DCACHE_LINELENGTH-1)) >> 2]); + return; + } + } + + // cache miss + u32 line; + if (CP15Control & CP15_CACHE_CR_ROUNDROBIN) + { + line = DCacheCount; + DCacheCount = (line+1) & (DCACHE_SETS-1); + } + else + { + line = RandomLineIndex(); + } + + line += id; + + addr &= ~(DCACHE_LINELENGTH-1); + u8* ptr = &DCache[line << DCACHE_LINELENGTH_LOG2]; + + //Log(LogLevel::Debug,"DCache miss, load @ %08x\n", addr); + for (int i = 0; i < DCACHE_LINELENGTH; i+=sizeof(u32)) + { + //DataRead32S(addr+i, (u32*)&ptr[i]); + if (addr+i < ITCMSize) + { + *((u32*)&ptr[i]) = *(u32*)&ITCM[addr & (ITCMPhysicalSize - 1)]; + } else + if (((addr+i) & DTCMMask) == DTCMBase) + { + *((u32*)&ptr[i]) = *(u32*)&DTCM[addr & (DTCMPhysicalSize - 1)]; + } else + { + *((u32*)&ptr[i]) = BusRead32(addr+i); + + } + //Log(LogLevel::Debug,"DCache store @ %08x: %08x\n", addr+i, *(u32*)&ptr[i]); + } + + DCacheTags[line] = addr | (line & (DCACHE_SETS-1)) | CACHE_FLAG_VALID; + + // ouch :/ + //printf("cache miss %08X: %d/%d\n", addr, NDS::ARM9MemTimings[addr >> 14][2], NDS::ARM9MemTimings[addr >> 14][3]); + DataCycles = (NDS.ARM9MemTimings[addr >> 14][2] + (NDS.ARM9MemTimings[addr >> 14][3] * 7)) << NDS.ARM9ClockShift; + CurDCacheLine = ptr; +} + +void ARMv5::DCacheInvalidateByAddr(u32 addr) +{ + u32 tag = (addr & ~(DCACHE_LINELENGTH - 1)) | CACHE_FLAG_VALID; + u32 id = (addr >> DCACHE_LINELENGTH_LOG2) & (DCACHE_LINESPERSET-1); + + id <<= DCACHE_SETS_LOG2; + for (int set=0;set= DCACHE_SETS) + return; + if (cacheLine >= DCACHE_LINESPERSET) + return; + + u32 idx = (cacheLine << DCACHE_SETS_LOG2) + cacheSet; + DCacheTags[idx] &= ~CACHE_FLAG_VALID; ; +} + + +void ARMv5::DCacheInvalidateAll() +{ + for (int i = 0; i < DCACHE_SIZE / DCACHE_LINELENGTH; i++) + DCacheTags[i] &= ~CACHE_FLAG_VALID; ; +} + +void ARMv5::DCacheClearAll() +{ + // TODO: right now any write to cached data goes straight to the + // underlying memory and invalidates the cache line. +} + +void ARMv5::DCacheClearByAddr(u32 addr) +{ + // TODO: right now any write to cached data goes straight to the + // underlying memory and invalidates the cache line. +} + +void ARMv5::DCacheClearByASetAndWay(u8 cacheSet, u8 cacheLine) +{ + // TODO: right now any write to cached data goes straight to the + // underlying memory and invalidates the cache line. +} + +bool ARMv5::IsAddressDCachable(u32 addr) +{ + return PU_Map[addr >> 12] & 0x10 ; +} void ARMv5::CP15Write(u32 id, u32 val) { @@ -623,23 +748,75 @@ void ARMv5::CP15Write(u32 id, u32 val) return; - case 0x761: + case 0x760: + DCacheInvalidateAll(); //printf("inval data cache %08X\n", val); return; - case 0x762: + case 0x761: + DCacheInvalidateByAddr(val); //printf("inval data cache SI\n"); return; + case 0x762: + { + // Cache invalidat by line number and set number + u8 cacheSet = val >> (32 - DCACHE_SETS_LOG2) & (DCACHE_SETS -1); + u8 cacheLine = (val >> DCACHE_LINELENGTH_LOG2) & (DCACHE_LINESPERSET -1); + DCacheInvalidateBySetAndWay(cacheSet, cacheLine); + } + return; + case 0x770: + // invalidate both caches + ICacheInvalidateAll(); + DCacheInvalidateAll(); + break; + + case 0x7A0: + //Log(LogLevel::Debug,"clean data cache\n"); + DCacheClearAll(); + return; case 0x7A1: - //printf("flush data cache %08X\n", val); + //Log(LogLevel::Debug,"clean data cache MVA\n"); + DCacheClearByAddr(val); return; case 0x7A2: - //printf("flush data cache SI\n"); + //Log(LogLevel::Debug,"clean data cache SET/WAY\n"); + { + // Cache invalidat by line number and set number + u8 cacheSet = val >> (32 - DCACHE_SETS_LOG2) & (DCACHE_SETS -1); + u8 cacheLine = (val >> DCACHE_LINELENGTH_LOG2) & (DCACHE_LINESPERSET -1); + DCacheClearByASetAndWay(cacheSet, cacheLine); + } return; case 0x7A3: // Test and clean (optional) // Is not present on the NDS/DSi - return; + return; + + case 0x7D1: + Log(LogLevel::Debug,"Prefetch instruction cache MVA\n"); + break; + + case 0x7E0: + //Log(LogLevel::Debug,"clean & invalidate data cache\n"); + DCacheClearAll(); + DCacheInvalidateAll(); + return; + case 0x7E1: + //Log(LogLevel::Debug,"clean & invalidate data cache MVA\n"); + DCacheClearByAddr(val); + DCacheInvalidateByAddr(val); + return; + case 0x7E2: + //Log(LogLevel::Debug,"clean & invalidate data cache SET/WAY\n"); + { + // Cache invalidat by line number and set number + u8 cacheSet = val >> (32 - DCACHE_SETS_LOG2) & (DCACHE_SETS -1); + u8 cacheLine = (val >> DCACHE_LINELENGTH_LOG2) & (DCACHE_LINESPERSET -1); + DCacheClearByASetAndWay(cacheSet, cacheLine); + DCacheInvalidateBySetAndWay(cacheSet, cacheLine); + } + return; case 0x900: // Cache Lockdown - Format B @@ -648,6 +825,7 @@ void ARMv5::CP15Write(u32 id, u32 val) // The Cache is 4 way associative // But all bits are r/w DCacheLockDown = val ; + Log(LogLevel::Debug,"ICacheLockDown\n"); return; case 0x901: // Cache Lockdown - Format B @@ -656,6 +834,7 @@ void ARMv5::CP15Write(u32 id, u32 val) // The Cache is 4 way associative // But all bits are r/w ICacheLockDown = val; + Log(LogLevel::Debug,"ICacheLockDown\n"); return; case 0x910: @@ -669,16 +848,27 @@ void ARMv5::CP15Write(u32 id, u32 val) return; case 0xF00: - //printf("cache debug index register %08X\n", val); + CacheDebugRegisterIndex = val; return; case 0xF10: - //printf("cache debug instruction tag %08X\n", val); - return; + // instruction cache Tag register + { + uint8_t segment = (CacheDebugRegisterIndex >> (32-ICACHE_SETS_LOG2)) & (ICACHE_SETS-1); + uint8_t wordAddress = (CacheDebugRegisterIndex & (ICACHE_LINELENGTH-1)) >> 2; + uint8_t index = (CacheDebugRegisterIndex >> ICACHE_LINELENGTH_LOG2) & (ICACHE_LINESPERSET-1); + ICacheTags[(index << ICACHE_SETS_LOG2) + segment] = val; + } case 0xF20: - //printf("cache debug data tag %08X\n", val); - return; + // data cache Tag register + { + uint8_t segment = (CacheDebugRegisterIndex >> (32-DCACHE_SETS_LOG2)) & (DCACHE_SETS-1); + uint8_t wordAddress = (CacheDebugRegisterIndex & (DCACHE_LINELENGTH-1)) >> 2; + uint8_t index = (CacheDebugRegisterIndex >> DCACHE_LINELENGTH_LOG2) & (DCACHE_LINESPERSET-1); + DCacheTags[(index << DCACHE_SETS_LOG2) + segment] = val; + } + case 0xF30: //printf("cache debug instruction cache %08X\n", val); @@ -794,6 +984,27 @@ u32 ARMv5::CP15Read(u32 id) const return DTCMSetting; case 0x911: return ITCMSetting; + + case 0xF00: + return CacheDebugRegisterIndex; + case 0xF10: + // instruction cache Tag register + { + uint8_t segment = (CacheDebugRegisterIndex >> (32-ICACHE_SETS_LOG2)) & (ICACHE_SETS-1); + uint8_t wordAddress = (CacheDebugRegisterIndex & (ICACHE_LINELENGTH-1)) >> 2; + uint8_t index = (CacheDebugRegisterIndex >> ICACHE_LINELENGTH_LOG2) & (ICACHE_LINESPERSET-1); + Log(LogLevel::Debug, "Read ICache Tag %08lx -> %08lx\n", CacheDebugRegisterIndex, ICacheTags[(index << ICACHE_SETS_LOG2) + segment]); + return ICacheTags[(index << ICACHE_SETS_LOG2) + segment]; + } + case 0xF20: + // data cache Tag register + { + uint8_t segment = (CacheDebugRegisterIndex >> (32-DCACHE_SETS_LOG2)) & (DCACHE_SETS-1); + uint8_t wordAddress = (CacheDebugRegisterIndex & (DCACHE_LINELENGTH-1)) >> 2; + uint8_t index = (CacheDebugRegisterIndex >> DCACHE_LINELENGTH_LOG2) & (DCACHE_LINESPERSET-1); + Log(LogLevel::Debug, "Read DCache Tag %08lx (%u, %02x, %u) -> %08lx\n", CacheDebugRegisterIndex, segment, index, wordAddress, DCacheTags[(index << DCACHE_SETS_LOG2) + segment]); + return DCacheTags[(index << DCACHE_SETS_LOG2) + segment]; + } } if ((id & 0xF00) == 0xF00) // test/debug shit? @@ -860,6 +1071,19 @@ void ARMv5::DataRead8(u32 addr, u32* val) return; } +#if 1 + if (CP15Control & CP15_CACHE_CR_DCACHEENABLE) + { + if (PU_Map[addr >> 12] & 0x10) + { + DCacheLookup(addr & ~3); + DataCycles = 1; + *val = CurDCacheLine[addr & (DCACHE_LINELENGTH - 1)]; + return; + } + } +#endif + DataRegion = addr; if (addr < ITCMSize) @@ -887,6 +1111,19 @@ void ARMv5::DataRead16(u32 addr, u32* val) return; } +#if 1 + if (CP15Control & CP15_CACHE_CR_DCACHEENABLE) + { + if (PU_Map[addr >> 12] & 0x10) + { + DCacheLookup(addr & ~3); + DataCycles = 1; + *val = *(u16 *)&CurDCacheLine[addr & (DCACHE_LINELENGTH - 2)]; + return; + } + } +#endif + DataRegion = addr; addr &= ~1; @@ -916,6 +1153,19 @@ void ARMv5::DataRead32(u32 addr, u32* val) return; } +#if 1 + if (CP15Control & CP15_CACHE_CR_DCACHEENABLE) + { + if (PU_Map[addr >> 12] & 0x10) + { + DCacheLookup(addr & ~3); + DataCycles = 1; + *val = *(u32 *)&CurDCacheLine[addr & (DCACHE_LINELENGTH - 4)]; + return; + } + } +#endif + DataRegion = addr; addr &= ~3; @@ -941,6 +1191,19 @@ void ARMv5::DataRead32S(u32 addr, u32* val) { addr &= ~3; +#if 1 + if (CP15Control & CP15_CACHE_CR_DCACHEENABLE) + { + if (PU_Map[addr >> 12] & 0x10) + { + DCacheLookup(addr & ~3); + DataCycles = 1; + *val = *(u32 *)&CurDCacheLine[addr & (DCACHE_LINELENGTH - 4)]; + return; + } + } +#endif + if (addr < ITCMSize) { DataCycles += 1; @@ -966,6 +1229,14 @@ void ARMv5::DataWrite8(u32 addr, u8 val) return; } + if (CP15Control & CP15_CACHE_CR_DCACHEENABLE) + { + if (PU_Map[addr >> 12] & 0x10) + { + DCacheInvalidateByAddr(addr); + } + } + DataRegion = addr; if (addr < ITCMSize) @@ -994,6 +1265,14 @@ void ARMv5::DataWrite16(u32 addr, u16 val) return; } + if (CP15Control & CP15_CACHE_CR_DCACHEENABLE) + { + if (PU_Map[addr >> 12] & 0x10) + { + DCacheInvalidateByAddr(addr); + } + } + DataRegion = addr; addr &= ~1; @@ -1024,6 +1303,14 @@ void ARMv5::DataWrite32(u32 addr, u32 val) return; } + if (CP15Control & CP15_CACHE_CR_DCACHEENABLE) + { + if (PU_Map[addr >> 12] & 0x10) + { + DCacheInvalidateByAddr(addr); + } + } + DataRegion = addr; addr &= ~3; @@ -1050,6 +1337,14 @@ void ARMv5::DataWrite32S(u32 addr, u32 val) { addr &= ~3; + if (CP15Control & CP15_CACHE_CR_DCACHEENABLE) + { + if (PU_Map[addr >> 12] & 0x10) + { + DCacheInvalidateByAddr(addr); + } + } + if (addr < ITCMSize) { DataCycles += 1; diff --git a/src/MemConstants.h b/src/MemConstants.h index f06edd96..6b378f7a 100644 --- a/src/MemConstants.h +++ b/src/MemConstants.h @@ -44,6 +44,19 @@ constexpr u32 ICACHE_LINELENGTH_LOG2 = ICACHE_LINELENGTH_ENCODED + 3; constexpr u32 ICACHE_LINELENGTH = 8 * (1 << ICACHE_LINELENGTH_ENCODED); constexpr u32 ICACHE_LINESPERSET = ICACHE_SIZE / (ICACHE_SETS * ICACHE_LINELENGTH); +constexpr u32 DCACHE_SIZE_LOG2 = 12; +constexpr u32 DCACHE_SIZE = 1 << DCACHE_SIZE_LOG2; +constexpr u32 DCACHE_SETS_LOG2 = 2; +constexpr u32 DCACHE_SETS = 1 << DCACHE_SETS_LOG2; +constexpr u32 DCACHE_LINELENGTH_ENCODED = 2; +constexpr u32 DCACHE_LINELENGTH_LOG2 = DCACHE_LINELENGTH_ENCODED + 3; +constexpr u32 DCACHE_LINELENGTH = 8 * (1 << DCACHE_LINELENGTH_ENCODED); +constexpr u32 DCACHE_LINESPERSET = DCACHE_SIZE / (DCACHE_SETS * DCACHE_LINELENGTH); + +constexpr u32 CACHE_FLAG_VALID = (1 << 4); +constexpr u32 CACHE_FLAG_DIRTY_LOWERHALF = (1 << 2); +constexpr u32 CACHE_FLAG_DIRTY_UPPERHALF = (1 << 3); + constexpr u32 CP15_CR_MPUENABLE = (1 << 0); constexpr u32 CP15_CR_BIGENDIAN = (1 << 7); constexpr u32 CP15_CR_HIGHEXCEPTIONBASE = (1 << 13);