mirror of
https://github.com/holub/mame
synced 2025-04-19 15:11:37 +03:00
r4000.cpp: partially implement secondary cache tag manipulation (#9923)
This commit is contained in:
parent
c93d24fb23
commit
084c103df0
@ -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;
|
||||
}
|
||||
|
@ -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)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user