From be63d893cd7d9c0f948bda84cd6e4c4f3fe53271 Mon Sep 17 00:00:00 2001 From: Connor McLaughlin Date: Thu, 29 Oct 2020 23:04:36 +1000 Subject: [PATCH] CPU: Use partial icache fills for non-line-aligned addresses --- src/core/bus.cpp | 51 ++++++++++++++++++++----------------- src/core/cpu_code_cache.cpp | 1 - src/core/cpu_core.h | 9 ++----- src/core/cpu_core_private.h | 19 +++++++++++--- 4 files changed, 46 insertions(+), 34 deletions(-) diff --git a/src/core/bus.cpp b/src/core/bus.cpp index 5c09bc97c..7b2b8f09a 100644 --- a/src/core/bus.cpp +++ b/src/core/bus.cpp @@ -1006,11 +1006,12 @@ TickCount GetICacheFillTicks(VirtualMemoryAddress address) if (address < RAM_MIRROR_END) { - return 1 * (ICACHE_LINE_SIZE / sizeof(u32)); + return 1 * ((ICACHE_LINE_SIZE - (address & (ICACHE_LINE_SIZE - 1))) / sizeof(u32)); } else if (address >= BIOS_BASE && address < (BIOS_BASE + BIOS_SIZE)) { - return m_bios_access_time[static_cast(MemoryAccessSize::Word)] * (ICACHE_LINE_SIZE / sizeof(u32)); + return m_bios_access_time[static_cast(MemoryAccessSize::Word)] * + ((ICACHE_LINE_SIZE - (address & (ICACHE_LINE_SIZE - 1))) / sizeof(u32)); } else { @@ -1046,9 +1047,29 @@ void CheckAndUpdateICacheTags(u32 line_count, TickCount uncached_ticks) u32 FillICache(VirtualMemoryAddress address) { const u32 line = GetICacheLine(address); - g_state.icache_tags[line] = GetICacheTagForAddress(address); u8* line_data = &g_state.icache_data[line * ICACHE_LINE_SIZE]; - DoInstructionRead(address & ~(ICACHE_LINE_SIZE - 1u), line_data); + u32 line_tag; + switch ((address >> 2) & 0x03u) + { + case 0: + DoInstructionRead(address & ~(ICACHE_LINE_SIZE - 1u), line_data); + line_tag = GetICacheTagForAddress(address); + break; + case 1: + DoInstructionRead(address & (~(ICACHE_LINE_SIZE - 1u) | 0x4), line_data + 0x4); + line_tag = GetICacheTagForAddress(address) | 0x1; + break; + case 2: + DoInstructionRead(address & (~(ICACHE_LINE_SIZE - 1u) | 0x8), line_data + 0x8); + line_tag = GetICacheTagForAddress(address) | 0x3; + break; + case 3: + default: + DoInstructionRead(address & (~(ICACHE_LINE_SIZE - 1u) | 0xC), line_data + 0xC); + line_tag = GetICacheTagForAddress(address) | 0x7; + break; + } + g_state.icache_tags[line] = line_tag; const u32 offset = GetICacheLineOffset(address); u32 result; @@ -1059,7 +1080,7 @@ u32 FillICache(VirtualMemoryAddress address) void ClearICache() { std::memset(g_state.icache_data.data(), 0, ICACHE_SIZE); - g_state.icache_tags.fill(ICACHE_INVALD_BIT | ICACHE_DISABLED_BIT); + g_state.icache_tags.fill(ICACHE_INVALID_BITS); } ALWAYS_INLINE_RELEASE static u32 ReadICache(VirtualMemoryAddress address) @@ -1076,7 +1097,7 @@ ALWAYS_INLINE_RELEASE static void WriteICache(VirtualMemoryAddress address, u32 { const u32 line = GetICacheLine(address); const u32 offset = GetICacheLineOffset(address); - g_state.icache_tags[line] = GetICacheTagForAddress(address) | ICACHE_INVALD_BIT; + g_state.icache_tags[line] = GetICacheTagForAddress(address) | ICACHE_INVALID_BITS; std::memcpy(&g_state.icache_data[line * ICACHE_LINE_SIZE + offset], &value, sizeof(value)); } @@ -1086,19 +1107,6 @@ static void WriteCacheControl(u32 value) CacheControl changed_bits{g_state.cache_control.bits ^ value}; g_state.cache_control.bits = value; - if (changed_bits.icache_enable) - { - if (g_state.cache_control.icache_enable) - { - for (u32 i = 0; i < ICACHE_LINES; i++) - g_state.icache_tags[i] &= ~ICACHE_DISABLED_BIT; - } - else - { - for (u32 i = 0; i < ICACHE_LINES; i++) - g_state.icache_tags[i] |= ICACHE_DISABLED_BIT; - } - } } template @@ -1323,15 +1331,12 @@ bool FetchInstruction() case 0x04: // KSEG0 - physical memory cached { #if 0 - // TODO: icache - TickCount cycles; - DoInstructionRead(address, cycles, g_state.next_instruction.bits); + DoInstructionRead(address, &g_state.next_instruction.bits); #else if (CompareICacheTag(address)) g_state.next_instruction.bits = ReadICache(address); else g_state.next_instruction.bits = FillICache(address); - #endif } break; diff --git a/src/core/cpu_code_cache.cpp b/src/core/cpu_code_cache.cpp index cd5359565..7f311cc9d 100644 --- a/src/core/cpu_code_cache.cpp +++ b/src/core/cpu_code_cache.cpp @@ -490,7 +490,6 @@ bool CompileBlock(CodeBlock* block) if (icache_line != last_cache_line) { block->icache_line_count++; - block->icache_line_count = GetICacheFillTicks(pc); last_cache_line = icache_line; } block->uncached_fetch_ticks += GetInstructionReadTicks(pc); diff --git a/src/core/cpu_core.h b/src/core/cpu_core.h index 5a59c1d3d..edff8a2aa 100644 --- a/src/core/cpu_core.h +++ b/src/core/cpu_core.h @@ -25,13 +25,8 @@ enum : PhysicalMemoryAddress ICACHE_LINE_SIZE = 16, ICACHE_LINES = ICACHE_SIZE / ICACHE_LINE_SIZE, ICACHE_SLOTS_PER_LINE = ICACHE_SLOTS / ICACHE_LINES, - ICACHE_TAG_ADDRESS_MASK = 0xFFFFFFF0u -}; - -enum : u32 -{ - ICACHE_DISABLED_BIT = 0x01, - ICACHE_INVALD_BIT = 0x02, + ICACHE_TAG_ADDRESS_MASK = 0xFFFFFFF0u, + ICACHE_INVALID_BITS = 0x0Fu, }; union CacheControl diff --git a/src/core/cpu_core_private.h b/src/core/cpu_core_private.h index caff0de60..2b7da61ef 100644 --- a/src/core/cpu_core_private.h +++ b/src/core/cpu_core_private.h @@ -1,6 +1,6 @@ #pragma once -#include "cpu_core.h" #include "bus.h" +#include "cpu_core.h" namespace CPU { @@ -11,7 +11,7 @@ void RaiseException(u32 CAUSE_bits, u32 EPC); ALWAYS_INLINE bool HasPendingInterrupt() { return g_state.cop0_regs.sr.IEc && - (((g_state.cop0_regs.cause.bits & g_state.cop0_regs.sr.bits) & (UINT32_C(0xFF) << 8)) != 0); + (((g_state.cop0_regs.cause.bits & g_state.cop0_regs.sr.bits) & (UINT32_C(0xFF) << 8)) != 0); } ALWAYS_INLINE void CheckForPendingInterrupt() @@ -40,10 +40,23 @@ ALWAYS_INLINE u32 GetICacheTagForAddress(VirtualMemoryAddress address) { return (address & ICACHE_TAG_ADDRESS_MASK); } +ALWAYS_INLINE u32 GetICacheFillTagForAddress(VirtualMemoryAddress address) +{ + static const u32 invalid_bits[4] = {0, 1, 3, 7}; + return GetICacheTagForAddress(address) | invalid_bits[(address >> 2) & 0x03u]; +} +ALWAYS_INLINE u32 GetICacheTagMaskForAddress(VirtualMemoryAddress address) +{ + const u32 offset = (address >> 2) & 0x03u; + static const u32 mask[4] = {ICACHE_TAG_ADDRESS_MASK | 1, ICACHE_TAG_ADDRESS_MASK | 2, ICACHE_TAG_ADDRESS_MASK | 4, + ICACHE_TAG_ADDRESS_MASK | 8}; + return mask[(address >> 2) & 0x03u]; +} + ALWAYS_INLINE bool CompareICacheTag(VirtualMemoryAddress address) { const u32 line = GetICacheLine(address); - return (g_state.icache_tags[line] == GetICacheTagForAddress(address)); + return ((g_state.icache_tags[line] & GetICacheTagMaskForAddress(address)) == GetICacheTagForAddress(address)); } TickCount GetInstructionReadTicks(VirtualMemoryAddress address);