From 084c103df0f3bfeec996848aeab4f2731a2de525 Mon Sep 17 00:00:00 2001 From: Brice Onken <32155223+briceonk@users.noreply.github.com> Date: Sun, 12 Jun 2022 17:31:28 -0700 Subject: [PATCH] r4000.cpp: partially implement secondary cache tag manipulation (#9923) --- src/devices/cpu/mips/r4000.cpp | 240 +++++++++++++++++++++++---------- src/devices/cpu/mips/r4000.h | 29 +++- 2 files changed, 196 insertions(+), 73 deletions(-) diff --git a/src/devices/cpu/mips/r4000.cpp b/src/devices/cpu/mips/r4000.cpp index 1cf885379cc..15f161b32c3 100644 --- a/src/devices/cpu/mips/r4000.cpp +++ b/src/devices/cpu/mips/r4000.cpp @@ -55,6 +55,8 @@ // experimental primary instruction cache #define ICACHE 0 +#define SCACHE !(m_cp0[CP0_Config] & CONFIG_SC) + #include "logmacro.h" #define USE_ABI_REG_NAMES 1 @@ -197,6 +199,9 @@ void r4000_base_device::device_start() m_icache_data = std::make_unique((0x1000U << config_ic) >> 2); R4000_ENDIAN_LE_BE(accessors(m_le), accessors(m_be)); + + if (SCACHE) + save_pointer(NAME(m_scache_tag), m_scache_tag_size); } void r4000_base_device::device_reset() @@ -945,75 +950,7 @@ void r4000_base_device::cpu_execute(u32 const op) cpu_swr(op); break; case 0x2f: // CACHE - if ((SR & SR_KSU) && !(SR & SR_CU0) && !(SR & (SR_EXL | SR_ERL))) - { - cpu_exception(EXCEPTION_CP0); - break; - } - - switch ((op >> 16) & 0x1f) - { - case 0x00: // index invalidate (I) - if (ICACHE) - { - m_icache_tag[(ADDR(m_r[RSREG], s16(op)) & m_icache_mask_hi) >> m_icache_shift] &= ~ICACHE_V; - break; - } - [[fallthrough]]; - case 0x04: // index load tag (I) - if (ICACHE) - { - u32 const tag = m_icache_tag[(ADDR(m_r[RSREG], s16(op)) & m_icache_mask_hi) >> m_icache_shift]; - - m_cp0[CP0_TagLo] = ((tag & ICACHE_PTAG) << 8) | ((tag & ICACHE_V) >> 18) | ((tag & ICACHE_P) >> 25); - m_cp0[CP0_ECC] = 0; // data ecc or parity - - break; - } - [[fallthrough]]; - case 0x08: // index store tag (I) - if (ICACHE) - { - // FIXME: compute parity - m_icache_tag[(ADDR(m_r[RSREG], s16(op)) & m_icache_mask_hi) >> m_icache_shift] = - (m_cp0[CP0_TagLo] & TAGLO_PTAGLO) >> 8 | (m_cp0[CP0_TagLo] & TAGLO_PSTATE) << 18; - - break; - } - [[fallthrough]]; - case 0x01: // index writeback invalidate (D) - case 0x02: // index invalidate (SI) - case 0x03: // index writeback invalidate (SD) - - case 0x05: // index load tag (D) - case 0x06: // index load tag (SI) - case 0x07: // index load tag (SI) - - case 0x09: // index store tag (D) - case 0x0a: // index store tag (SI) - case 0x0b: // index store tag (SD) - - case 0x0d: // create dirty exclusive (D) - case 0x0f: // create dirty exclusive (SD) - - case 0x10: // hit invalidate (I) - case 0x11: // hit invalidate (D) - case 0x12: // hit invalidate (SI) - case 0x13: // hit invalidate (SD) - - case 0x14: // fill (I) - case 0x15: // hit writeback invalidate (D) - case 0x17: // hit writeback invalidate (SD) - - case 0x18: // hit writeback (I) - case 0x19: // hit writeback (D) - case 0x1b: // hit writeback (SD) - - case 0x1e: // hit set virtual (SI) - case 0x1f: // hit set virtual (SD) - //LOGMASKED(LOG_CACHE, "cache 0x%08x unimplemented (%s)\n", op, machine().describe_context()); - break; - } + cp0_cache(op); break; case 0x30: // LL load_linked(ADDR(m_r[RSREG], s16(op)), @@ -1263,6 +1200,128 @@ void r4000_base_device::cpu_sdr(u32 const op) store(offset, m_r[RTREG] << shift, ~u64(0) << shift); } +void r4000_base_device::cp0_cache(u32 const op) +{ + if ((SR & SR_KSU) && !(SR & SR_CU0) && !(SR & (SR_EXL | SR_ERL))) + { + cpu_exception(EXCEPTION_CP0); + return; + } + + switch ((op >> 16) & 0x1f) + { + case 0x00: // index invalidate (I) + if (ICACHE) + { + m_icache_tag[(ADDR(m_r[RSREG], s16(op)) & m_icache_mask_hi) >> m_icache_shift] &= ~ICACHE_V; + break; + } + [[fallthrough]]; + case 0x04: // index load tag (I) + if (ICACHE) + { + u32 const tag = m_icache_tag[(ADDR(m_r[RSREG], s16(op)) & m_icache_mask_hi) >> m_icache_shift]; + + m_cp0[CP0_TagLo] = ((tag & ICACHE_PTAG) << 8) | ((tag & ICACHE_V) >> 18) | ((tag & ICACHE_P) >> 25); + m_cp0[CP0_ECC] = 0; // data ecc or parity + + break; + } + [[fallthrough]]; + case 0x08: // index store tag (I) + if (ICACHE) + { + // FIXME: compute parity + m_icache_tag[(ADDR(m_r[RSREG], s16(op)) & m_icache_mask_hi) >> m_icache_shift] = + (m_cp0[CP0_TagLo] & TAGLO_PTAGLO) >> 8 | (m_cp0[CP0_TagLo] & TAGLO_PSTATE) << 18; + break; + } + [[fallthrough]]; + case 0x01: // index writeback invalidate (D) + case 0x02: // index invalidate (SI) + case 0x03: // index writeback invalidate (SD) + case 0x05: // index load tag (D) + //LOGMASKED(LOG_CACHE, "cache 0x%08x unimplemented (%s)\n", op, machine().describe_context()); + break; + case 0x06: // index load tag (SI) + case 0x07: // index load tag (SD) + if (SCACHE) + { + // TODO: translation type for CACHE instruction? Read seems reasonable since only the tag is changing here + u64 physical_address = ADDR(m_r[RSREG], s16(op)); + translate_result const t = translate(TRANSLATE_READ, physical_address); + if (t == ERROR || t == MISS) + return; + + u32 const index = (physical_address & m_scache_tag_mask) >> m_scache_line_index; + if (index < m_scache_tag_size) + { + // TODO: Load the ECC register here + u32 const tag = m_scache_tag[index]; + u32 const cs = (tag & SCACHE_CS) >> 22; + u32 const stag = tag & SCACHE_STAG; + u32 const pidx = (tag & SCACHE_PIDX) >> 19; + m_cp0[CP0_TagLo] = (stag << 13) | (cs << 10) | (pidx << 7); + } + else + fatalerror("r4000 scache load tag index out of range!"); + } + else + LOGMASKED(LOG_CACHE, "cache 0x%08x called without scache enabled (%s)\n", op, machine().describe_context()); + break; + case 0x09: // index store tag (D) + //LOGMASKED(LOG_CACHE, "cache 0x%08x unimplemented (%s)\n", op, machine().describe_context()); + break; + case 0x0a: // index store tag (SI) + case 0x0b: // index store tag (SD) + if (SCACHE) + { + // TODO: translation type for CACHE instruction? Read seems reasonable since only the tag is changing here + u64 const virtual_address = ADDR(m_r[RSREG], s16(op)); + u64 physical_address = virtual_address; + translate_result const t = translate(TRANSLATE_READ, physical_address); + if (t == ERROR || t == MISS) + return; + + u64 const index = (physical_address & m_scache_tag_mask) >> m_scache_line_index; + if (index < m_scache_tag_size) + { + // TODO: Calculate ECC bits here + u64 const tag_lo = m_cp0[CP0_TagLo]; + u32 const cs = (tag_lo & TAGLO_CS) >> 10; + u32 const stag = (tag_lo & TAGLO_STAG) >> 13; + u32 const pidx = (virtual_address & 0x7000) >> 12; + m_scache_tag[index] = cs << 22 | pidx << 19 | stag; + } + else + fatalerror("r4000 scache store tag index out of range!"); + } + else + LOGMASKED(LOG_CACHE, "cache 0x%08x called without scache enabled (%s)\n", op, machine().describe_context()); + break; + case 0x0d: // create dirty exclusive (D) + case 0x0f: // create dirty exclusive (SD) + + case 0x10: // hit invalidate (I) + case 0x11: // hit invalidate (D) + case 0x12: // hit invalidate (SI) + case 0x13: // hit invalidate (SD) + + case 0x14: // fill (I) + case 0x15: // hit writeback invalidate (D) + case 0x17: // hit writeback invalidate (SD) + + case 0x18: // hit writeback (I) + case 0x19: // hit writeback (D) + case 0x1b: // hit writeback (SD) + + case 0x1e: // hit set virtual (SI) + case 0x1f: // hit set virtual (SD) + //LOGMASKED(LOG_CACHE, "cache 0x%08x unimplemented (%s)\n", op, machine().describe_context()); + break; + } +} + void r4000_base_device::cp0_execute(u32 const op) { if ((SR & SR_KSU) && !(SR & SR_CU0) && !(SR & (SR_EXL | SR_ERL))) @@ -4021,3 +4080,46 @@ std::string r4000_base_device::debug_unicode_string(u64 unicode_string_pointer) return utf8_from_wstring(result); } + +void r4000_base_device::configure_scache() +{ + if (m_scache_size > 0) + { + /* + * Secondary cache tag size depends on the cache line size + * (how many bytes are transferred with one cache operation) and the + * size of the cache itself. + * For example, the Sony NEWS NWS-5000X has a 1MB secondary cache + * and a cache line size of 16 words. So, the slice of the physical + * address used to index into the cache is bits 19:6. + * See chapter 11 of the R4000 user manual for more details. + */ + if (m_scache_line_size == 0) + fatalerror("SCACHE size set but line size was not set!"); + + if (m_scache_line_size <= 0x10) + m_scache_line_index = 4; + else if (m_scache_line_size <= 0x20) + { + m_scache_line_index = 5; + m_cp0[CP0_Config] |= 1 << 22; + } + else if (m_scache_line_size <= 0x40) + { + m_scache_line_index = 6; + m_cp0[CP0_Config] |= 2 << 22; + } + else + { + m_scache_line_index = 7; + m_cp0[CP0_Config] |= 3 << 22; + } + + m_scache_tag_size = m_scache_size >> m_scache_line_index; + m_scache_tag_mask = m_scache_size - 1; + + m_scache_tag = std::make_unique(m_scache_tag_size); + } + else + m_cp0[CP0_Config] |= CONFIG_SC; +} diff --git a/src/devices/cpu/mips/r4000.h b/src/devices/cpu/mips/r4000.h index f4a015e9c77..adc9ccd1f3d 100644 --- a/src/devices/cpu/mips/r4000.h +++ b/src/devices/cpu/mips/r4000.h @@ -291,6 +291,8 @@ protected: TAGLO_PTAGLO = 0xffffff00, // physical adddress bits 35:12 TAGLO_PSTATE = 0x000000c0, // primary cache state TAGLO_P = 0x00000001, // primary tag even parity + TAGLO_CS = 0x00001c00, // scache status + TAGLO_STAG = 0xffffe000, // scache tag }; enum icache_mask : u32 { @@ -306,6 +308,12 @@ protected: DCACHE_W = 0x02000000, // write-back DCACHE_WP = 0x02000000, // even parity for write-back }; + enum scache_mask : u32 + { + SCACHE_CS = 0x01c00000, // cache state + SCACHE_STAG = 0x0007ffff, // physical tag + SCACHE_PIDX = 0x00380000, // primary cache index + }; // device_t overrides virtual void device_start() override; @@ -342,6 +350,7 @@ protected: void cpu_sdr(u32 const op); // cp0 implementation + void cp0_cache(u32 const op); void cp0_execute(u32 const op); u64 cp0_get(unsigned const reg); void cp0_set(unsigned const reg, u64 const data); @@ -383,6 +392,9 @@ protected: std::string debug_string_array(u64 array_pointer); std::string debug_unicode_string(u64 unicode_string_pointer); + // configuration helpers + void configure_scache(); + // device configuration state address_space_config m_program_config_le; address_space_config m_program_config_be; @@ -458,6 +470,14 @@ protected: std::unique_ptr m_icache_tag; std::unique_ptr m_icache_data; + // experimental scache state + u32 m_scache_size; // Size in bytes + u8 m_scache_line_size; + u32 m_scache_line_index; // Secondary cache line shift + u32 m_scache_tag_mask; // Mask for extracting the tag from a physical address + u32 m_scache_tag_size; + std::unique_ptr m_scache_tag; + // statistics u64 m_tlb_scans; u64 m_tlb_loops; @@ -485,15 +505,16 @@ public: class r4400_device : public r4000_base_device { public: - r4400_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock, bool timer_interrupt_disabled) + r4400_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock, bool timer_interrupt_disabled, u32 scache_size, u8 scache_line_size) : r4000_base_device(mconfig, R4400, tag, owner, clock, 0x0440, 0x0500, CACHE_16K, CACHE_16K, 10, 20, 69, 133, timer_interrupt_disabled) { - // no secondary cache - m_cp0[CP0_Config] |= CONFIG_SC; + m_scache_size = scache_size; + m_scache_line_size = scache_line_size; + configure_scache(); } r4400_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) - : r4400_device(mconfig, tag, owner, clock, false) + : r4400_device(mconfig, tag, owner, clock, false, 0, 0) { } };