diff options
Diffstat (limited to 'src/devices/cpu/mips/mips3com.c')
-rw-r--r-- | src/devices/cpu/mips/mips3com.c | 433 |
1 files changed, 433 insertions, 0 deletions
diff --git a/src/devices/cpu/mips/mips3com.c b/src/devices/cpu/mips/mips3com.c new file mode 100644 index 00000000000..0409014eeba --- /dev/null +++ b/src/devices/cpu/mips/mips3com.c @@ -0,0 +1,433 @@ +// license:BSD-3-Clause +// copyright-holders:Aaron Giles +/*************************************************************************** + + mips3com.c + + Common MIPS III/IV definitions and functions + +***************************************************************************/ + +#include "emu.h" +#include "mips3com.h" + + +/*************************************************************************** + FUNCTION PROTOTYPES +***************************************************************************/ + +static void tlb_entry_log_half(mips3_tlb_entry *entry, int tlbindex, int which); + + + +/*************************************************************************** + INLINE FUNCTIONS +***************************************************************************/ + +/*------------------------------------------------- + tlb_entry_matches_asid - TRUE if the given + TLB entry matches the provided ASID +-------------------------------------------------*/ + +INLINE int tlb_entry_matches_asid(const mips3_tlb_entry *entry, UINT8 asid) +{ + return (entry->entry_hi & 0xff) == asid; +} + + +/*------------------------------------------------- + tlb_entry_is_global - TRUE if the given + TLB entry is global +-------------------------------------------------*/ + +INLINE int tlb_entry_is_global(const mips3_tlb_entry *entry) +{ + return (entry->entry_lo[0] & entry->entry_lo[1] & TLB_GLOBAL); +} + + +void mips3_device::execute_set_input(int inputnum, int state) +{ + if (inputnum >= MIPS3_IRQ0 && inputnum <= MIPS3_IRQ5) + { + if (state != CLEAR_LINE) + m_core->cpr[0][COP0_Cause] |= 0x400 << inputnum; + else + m_core->cpr[0][COP0_Cause] &= ~(0x400 << inputnum); + } +} + + +/*************************************************************************** + INITIALIZATION AND SHUTDOWN +***************************************************************************/ + +/*------------------------------------------------- + mips3com_update_cycle_counting - update cycle + counts and the timers +-------------------------------------------------*/ + +void mips3_device::mips3com_update_cycle_counting() +{ + /* modify the timer to go off */ + if (m_core->compare_armed && (m_core->cpr[0][COP0_Status] & SR_IMEX5)) + { + UINT32 count = (total_cycles() - m_core->count_zero_time) / 2; + UINT32 compare = m_core->cpr[0][COP0_Compare]; + UINT32 delta = compare - count; + attotime newtime = cycles_to_attotime((UINT64)delta * 2); + m_compare_int_timer->adjust(newtime); + return; + } + m_compare_int_timer->adjust(attotime::never); +} + + + +/*************************************************************************** + TLB HANDLING +***************************************************************************/ + +/*------------------------------------------------- + mips3com_asid_changed - remap all non-global + TLB entries +-------------------------------------------------*/ + +void mips3_device::mips3com_asid_changed() +{ + int tlbindex; + + /* iterate over all non-global TLB entries and remap them */ + for (tlbindex = 0; tlbindex < m_tlbentries; tlbindex++) + if (!tlb_entry_is_global(&m_tlb[tlbindex])) + tlb_map_entry(tlbindex); +} + + +/*------------------------------------------------- + mips3com_tlbr - execute the tlbr instruction +-------------------------------------------------*/ + +void mips3_device::mips3com_tlbr() +{ + UINT32 tlbindex = m_core->cpr[0][COP0_Index] & 0x3f; + + /* only handle entries within the TLB */ + if (tlbindex < m_tlbentries) + { + mips3_tlb_entry *entry = &m_tlb[tlbindex]; + + /* copy data from the TLB entry into the COP0 registers */ + m_core->cpr[0][COP0_PageMask] = entry->page_mask; + m_core->cpr[0][COP0_EntryHi] = entry->entry_hi; + m_core->cpr[0][COP0_EntryLo0] = entry->entry_lo[0]; + m_core->cpr[0][COP0_EntryLo1] = entry->entry_lo[1]; + } +} + + +/*------------------------------------------------- + mips3com_tlbwi - execute the tlbwi instruction +-------------------------------------------------*/ + +void mips3_device::mips3com_tlbwi() +{ + /* use the common handler and write based off the COP0 Index register */ + tlb_write_common(m_core->cpr[0][COP0_Index] & 0x3f); +} + + +/*------------------------------------------------- + mips3com_tlbwr - execute the tlbwr instruction +-------------------------------------------------*/ + +void mips3_device::mips3com_tlbwr() +{ + UINT32 wired = m_core->cpr[0][COP0_Wired] & 0x3f; + UINT32 unwired = m_tlbentries - wired; + UINT32 tlbindex = m_tlbentries - 1; + + /* "random" is based off of the current cycle counting through the non-wired pages */ + if (unwired > 0) + tlbindex = ((total_cycles() - m_core->count_zero_time) % unwired + wired) & 0x3f; + + /* use the common handler to write to this tlbindex */ + tlb_write_common(tlbindex); +} + + +/*------------------------------------------------- + mips3com_tlbp - execute the tlbp instruction +-------------------------------------------------*/ + +void mips3_device::mips3com_tlbp() +{ + UINT32 tlbindex; + + /* iterate over TLB entries */ + for (tlbindex = 0; tlbindex < m_tlbentries; tlbindex++) + { + mips3_tlb_entry *entry = &m_tlb[tlbindex]; + UINT64 mask = ~((entry->page_mask >> 13) & 0xfff) << 13; + + /* if the relevant bits of EntryHi match the relevant bits of the TLB */ + if ((entry->entry_hi & mask) == (m_core->cpr[0][COP0_EntryHi] & mask)) + + /* and if we are either global or matching the current ASID, then stop */ + if ((entry->entry_hi & 0xff) == (m_core->cpr[0][COP0_EntryHi] & 0xff) || ((entry->entry_lo[0] & entry->entry_lo[1]) & TLB_GLOBAL)) + break; + } + + /* validate that our tlb_table was in sync */ +// vpn = ((m_cores->cpr[0][COP0_EntryHi] >> 13) & 0x07ffffff) << 1; + if (tlbindex != m_tlbentries) + m_core->cpr[0][COP0_Index] = tlbindex; + else + m_core->cpr[0][COP0_Index] = 0x80000000; +} + + + +/*************************************************************************** + INTERNAL HELPERS +***************************************************************************/ + +/*------------------------------------------------- + compare_int_callback - callback that fires + whenever a compare interrupt is generated +-------------------------------------------------*/ + +TIMER_CALLBACK_MEMBER( mips3_device::compare_int_callback ) +{ + set_input_line(MIPS3_IRQ5, ASSERT_LINE); +} + + +/*------------------------------------------------- + compute_config_register - compute the value + of the config register +-------------------------------------------------*/ + +UINT32 mips3_device::compute_config_register() +{ + /* set the cache line size to 32 bytes */ + UINT32 configreg = 0x00026030; + int divisor; + + // NEC VR series does not use a 100% compatible COP0/TLB implementation + if (m_flavor == MIPS3_TYPE_VR4300) + { + /* + For VR43xx, Config is as follows: + bit 31 = always 0 + bits 28-30 = EC + bits 24-27 = EP + bits 16-23 = always b0000010 + bit 15 = endian indicator as standard MIPS III + bits 4-14 = always b11001000110 + bit 3 = CU + bits 0-2 = K0 ("Coherency algorithm of kseg0") + */ + + configreg = 0x6460; + } + else + { + /* set the data cache size */ + if (c_icache_size <= 0x01000) configreg |= 0 << 6; + else if (c_icache_size <= 0x02000) configreg |= 1 << 6; + else if (c_icache_size <= 0x04000) configreg |= 2 << 6; + else if (c_icache_size <= 0x08000) configreg |= 3 << 6; + else if (c_icache_size <= 0x10000) configreg |= 4 << 6; + else if (c_icache_size <= 0x20000) configreg |= 5 << 6; + else if (c_icache_size <= 0x40000) configreg |= 6 << 6; + else configreg |= 7 << 6; + + /* set the instruction cache size */ + if (c_icache_size <= 0x01000) configreg |= 0 << 9; + else if (c_icache_size <= 0x02000) configreg |= 1 << 9; + else if (c_icache_size <= 0x04000) configreg |= 2 << 9; + else if (c_icache_size <= 0x08000) configreg |= 3 << 9; + else if (c_icache_size <= 0x10000) configreg |= 4 << 9; + else if (c_icache_size <= 0x20000) configreg |= 5 << 9; + else if (c_icache_size <= 0x40000) configreg |= 6 << 9; + else configreg |= 7 << 9; + + + /* set the system clock divider */ + divisor = 2; + if (c_system_clock != 0) + { + divisor = m_cpu_clock / c_system_clock; + if (c_system_clock * divisor != m_cpu_clock) + { + configreg |= 0x80000000; + divisor = m_cpu_clock * 2 / c_system_clock; + } + } + configreg |= (((divisor < 2) ? 2 : (divisor > 8) ? 8 : divisor) - 2) << 28; + } + + /* set the endianness bit */ + if (m_bigendian) + configreg |= 0x00008000; + + return configreg; +} + + +/*------------------------------------------------- + compute_prid_register - compute the value + of the PRId register +-------------------------------------------------*/ + +UINT32 mips3_device::compute_prid_register() +{ + switch (m_flavor) + { + case MIPS3_TYPE_VR4300: + return 0x0b00; + + case MIPS3_TYPE_R4600: + case MIPS3_TYPE_R4650: + return 0x2000; + + case MIPS3_TYPE_R4700: + return 0x2100; + + case MIPS3_TYPE_R5000: + case MIPS3_TYPE_QED5271: + return 0x2300; + + case MIPS3_TYPE_RM7000: + return 0x2700; + + default: + fatalerror("Unknown MIPS flavor specified\n"); + } + // never executed + //return 0x2000; +} + + +/*------------------------------------------------- + tlb_map_entry - map a single TLB + entry +-------------------------------------------------*/ + +void mips3_device::tlb_map_entry(int tlbindex) +{ + int current_asid = m_core->cpr[0][COP0_EntryHi] & 0xff; + mips3_tlb_entry *entry = &m_tlb[tlbindex]; + UINT32 count, vpn; + int which; + + /* the ASID doesn't match the current ASID, and if the page isn't global, unmap it from the TLB */ + if (!tlb_entry_matches_asid(entry, current_asid) && !tlb_entry_is_global(entry)) + { + vtlb_load(m_vtlb, 2 * tlbindex + 0, 0, 0, 0); + vtlb_load(m_vtlb, 2 * tlbindex + 1, 0, 0, 0); + return; + } + + /* extract the VPN index; ignore if the virtual address is beyond 32 bits */ + vpn = ((entry->entry_hi >> 13) & 0x07ffffff) << 1; + if (vpn >= (1 << (MIPS3_MAX_PADDR_SHIFT - MIPS3_MIN_PAGE_SHIFT))) + { + vtlb_load(m_vtlb, 2 * tlbindex + 0, 0, 0, 0); + vtlb_load(m_vtlb, 2 * tlbindex + 1, 0, 0, 0); + return; + } + + /* get the number of pages from the page mask */ + count = ((entry->page_mask >> 13) & 0x00fff) + 1; + + /* loop over both the even and odd pages */ + for (which = 0; which < 2; which++) + { + UINT32 effvpn = vpn + count * which; + UINT64 lo = entry->entry_lo[which]; + UINT32 pfn; + UINT32 flags = 0; + + /* compute physical page index */ + pfn = (lo >> 6) & m_pfnmask; + + /* valid? */ + if ((lo & 2) != 0) + { + flags |= VTLB_FLAG_VALID | VTLB_READ_ALLOWED | VTLB_FETCH_ALLOWED; + + /* writable? */ + if ((lo & 4) != 0) + flags |= VTLB_WRITE_ALLOWED; + + /* mirror the flags for user mode if the VPN is in user space */ + if (effvpn < (0x80000000 >> MIPS3_MIN_PAGE_SHIFT)) + flags |= (flags << 4) & (VTLB_USER_READ_ALLOWED | VTLB_USER_WRITE_ALLOWED | VTLB_USER_FETCH_ALLOWED); + } + + /* load the virtual TLB with the corresponding entries */ + if ((effvpn + count) <= (0x80000000 >> MIPS3_MIN_PAGE_SHIFT) || effvpn >= (0xc0000000 >> MIPS3_MIN_PAGE_SHIFT)) + vtlb_load(m_vtlb, 2 * tlbindex + which, count, effvpn << MIPS3_MIN_PAGE_SHIFT, (pfn << MIPS3_MIN_PAGE_SHIFT) | flags); + else + vtlb_load(m_vtlb, 2 * tlbindex + which, 0, 0, 0); + } +} + + +/*------------------------------------------------- + tlb_write_common - common routine for writing + a TLB entry +-------------------------------------------------*/ + +void mips3_device::tlb_write_common(int tlbindex) +{ + /* only handle entries within the TLB */ + if (tlbindex < m_tlbentries) + { + mips3_tlb_entry *entry = &m_tlb[tlbindex]; + + /* fill in the new TLB entry from the COP0 registers */ + entry->page_mask = m_core->cpr[0][COP0_PageMask]; + entry->entry_hi = m_core->cpr[0][COP0_EntryHi] & ~(entry->page_mask & U64(0x0000000001ffe000)); + entry->entry_lo[0] = m_core->cpr[0][COP0_EntryLo0]; + entry->entry_lo[1] = m_core->cpr[0][COP0_EntryLo1]; + + /* remap this TLB entry */ + tlb_map_entry(tlbindex); + + /* log the two halves once they are in */ + tlb_entry_log_half(entry, tlbindex, 0); + tlb_entry_log_half(entry, tlbindex, 1); + } +} + + +/*------------------------------------------------- + tlb_entry_log_half - log half of a single TLB + entry +-------------------------------------------------*/ + +static void tlb_entry_log_half(mips3_tlb_entry *entry, int tlbindex, int which) +{ +if (PRINTF_TLB) +{ + UINT64 hi = entry->entry_hi; + UINT64 lo = entry->entry_lo[which]; + UINT32 vpn = (((hi >> 13) & 0x07ffffff) << 1); + UINT32 asid = hi & 0xff; + UINT32 r = (hi >> 62) & 3; + UINT32 pfn = (lo >> 6) & 0x00ffffff; + UINT32 c = (lo >> 3) & 7; + UINT32 pagesize = (((entry->page_mask >> 13) & 0xfff) + 1) << MIPS3_MIN_PAGE_SHIFT; + UINT64 vaddr = (UINT64)vpn * MIPS3_MIN_PAGE_SIZE; + UINT64 paddr = (UINT64)pfn * MIPS3_MIN_PAGE_SIZE; + + vaddr += pagesize * which; + + printf("index=%08X pagesize=%08X vaddr=%08X%08X paddr=%08X%08X asid=%02X r=%X c=%X dvg=%c%c%c\n", + tlbindex, pagesize, (UINT32)(vaddr >> 32), (UINT32)vaddr, (UINT32)(paddr >> 32), (UINT32)paddr, + asid, r, c, (lo & 4) ? 'd' : '.', (lo & 2) ? 'v' : '.', (lo & 1) ? 'g' : '.'); +} +} |