r4000.cpp: partially implement secondary cache tag manipulation (#9923)

This commit is contained in:
Brice Onken 2022-06-12 17:31:28 -07:00 committed by GitHub
parent c93d24fb23
commit 084c103df0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 196 additions and 73 deletions

View File

@ -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<u32 []>((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<u32>(ADDR(m_r[RSREG], s16(op)),
@ -1263,6 +1200,128 @@ void r4000_base_device::cpu_sdr(u32 const op)
store<u64, false>(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<u32[]>(m_scache_tag_size);
}
else
m_cp0[CP0_Config] |= CONFIG_SC;
}

View File

@ -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<u32[]> m_icache_tag;
std::unique_ptr<u32[]> 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<u32[]> 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)
{
}
};