diff options
author | 2008-06-16 03:12:49 +0000 | |
---|---|---|
committer | 2008-06-16 03:12:49 +0000 | |
commit | 5deab27d6d41791498aac45246d3b7cc5ea6281f (patch) | |
tree | 56c1910008a5e3df2cc6ea0034b409044765d041 /src/emu/cpu/mips/mips3com.c | |
parent | cd0703d80ab10309a691929f66efd792b5fff6ca (diff) |
Created common virtual TLB managment module.
Updated MIPS and PowerPC code to make use of it.
Diffstat (limited to 'src/emu/cpu/mips/mips3com.c')
-rw-r--r-- | src/emu/cpu/mips/mips3com.c | 333 |
1 files changed, 171 insertions, 162 deletions
diff --git a/src/emu/cpu/mips/mips3com.c b/src/emu/cpu/mips/mips3com.c index cdf8e69bf67..74d1a8f8bac 100644 --- a/src/emu/cpu/mips/mips3com.c +++ b/src/emu/cpu/mips/mips3com.c @@ -13,7 +13,7 @@ DEBUGGING ***************************************************************************/ -#define PRINTF_TLB (1) +#define PRINTF_TLB (0) @@ -26,8 +26,36 @@ static TIMER_CALLBACK( compare_int_callback ); static UINT32 compute_config_register(const mips3_state *mips); static UINT32 compute_prid_register(const mips3_state *mips); -static void tlb_write_common(mips3_state *mips, int index); -static void tlb_entry_log_half(mips3_tlb_entry *tlbent, int index, int which); +static void tlb_map_entry(mips3_state *mips, int tlbindex); +static void tlb_write_common(mips3_state *mips, int tlbindex); +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); +} @@ -40,14 +68,8 @@ static void tlb_entry_log_half(mips3_tlb_entry *tlbent, int index, int which); structure based on the configured type -------------------------------------------------*/ -size_t mips3com_init(mips3_state *mips, mips3_flavor flavor, int bigendian, int index, int clock, const mips3_config *config, int (*irqcallback)(int), void *memory) +void mips3com_init(mips3_state *mips, mips3_flavor flavor, int bigendian, int index, int clock, const mips3_config *config, int (*irqcallback)(int)) { - int tlbindex; - - /* if no memory, return the amount needed */ - if (memory == NULL) - return sizeof(mips->tlb_table[0]) * (1 << (MIPS3_MAX_PADDR_SHIFT - MIPS3_MIN_PAGE_SHIFT)); - /* initialize based on the config */ memset(mips, 0, sizeof(*mips)); mips->flavor = flavor; @@ -61,25 +83,25 @@ size_t mips3com_init(mips3_state *mips, mips3_flavor flavor, int bigendian, int /* set up the endianness */ mips->memory = *memory_get_accessors(ADDRESS_SPACE_PROGRAM, 32, mips->bigendian ? CPU_IS_BE : CPU_IS_LE); - /* allocate memory */ - mips->tlb_table = memory; - - /* initialize the TLB state */ - for (tlbindex = 0; tlbindex < ARRAY_LENGTH(mips->tlb); tlbindex++) - { - mips3_tlb_entry *tlbent = &mips->tlb[tlbindex]; - tlbent->page_mask = 0; - tlbent->entry_hi = 0xffffffff; - tlbent->entry_lo[0] = 0xfffffff8; - tlbent->entry_lo[1] = 0xfffffff8; - } + /* allocate the virtual TLB */ + mips->vtlb = vtlb_alloc(cpu_getactivecpu(), ADDRESS_SPACE_PROGRAM, 2 * MIPS3_TLB_ENTRIES + 2, 0); /* allocate a timer for the compare interrupt */ mips->compare_int_timer = timer_alloc(compare_int_callback, NULL); /* reset the state */ mips3com_reset(mips); - return 0; +} + + +/*------------------------------------------------- + mips3com_exit - common cleanup/exit +-------------------------------------------------*/ + +void mips3com_exit(mips3_state *mips) +{ + if (mips->vtlb != NULL) + vtlb_free(mips->vtlb); } @@ -90,6 +112,8 @@ size_t mips3com_init(mips3_state *mips, mips3_flavor flavor, int bigendian, int void mips3com_reset(mips3_state *mips) { + int tlbindex; + /* initialize the state */ mips->pc = 0xbfc00000; mips->cpr[0][COP0_Status] = SR_BEV | SR_ERL; @@ -100,8 +124,21 @@ void mips3com_reset(mips3_state *mips) mips->cpr[0][COP0_PRId] = compute_prid_register(mips); mips->count_zero_time = activecpu_gettotalcycles(); - /* recompute the TLB table */ - mips3com_recompute_tlb_table(mips); + /* initialize the TLB state */ + for (tlbindex = 0; tlbindex < ARRAY_LENGTH(mips->tlb); tlbindex++) + { + mips3_tlb_entry *entry = &mips->tlb[tlbindex]; + entry->page_mask = 0; + entry->entry_hi = 0xffffffff; + entry->entry_lo[0] = 0xfffffff8; + entry->entry_lo[1] = 0xfffffff8; + vtlb_load(mips->vtlb, 2 * tlbindex + 0, 0, 0, 0); + vtlb_load(mips->vtlb, 2 * tlbindex + 1, 0, 0, 0); + } + + /* load the fixed TLB range */ + vtlb_load(mips->vtlb, 2 * MIPS3_TLB_ENTRIES + 0, (0xa0000000 - 0x80000000) >> MIPS3_MIN_PAGE_SHIFT, 0x80000000, 0x00000000 | VTLB_READ_ALLOWED | VTLB_WRITE_ALLOWED | VTLB_FETCH_ALLOWED | VTLB_FLAG_VALID); + vtlb_load(mips->vtlb, 2 * MIPS3_TLB_ENTRIES + 1, (0xc0000000 - 0xa0000000) >> MIPS3_MIN_PAGE_SHIFT, 0xa0000000, 0x00000000 | VTLB_READ_ALLOWED | VTLB_WRITE_ALLOWED | VTLB_FETCH_ALLOWED | VTLB_FLAG_VALID); } @@ -154,99 +191,18 @@ void mips3com_update_cycle_counting(mips3_state *mips) ***************************************************************************/ /*------------------------------------------------- - mips3com_map_tlb_entries - map entries from the - TLB into the tlb_table --------------------------------------------------*/ - -void mips3com_map_tlb_entries(mips3_state *mips) -{ - int valid_asid = mips->cpr[0][COP0_EntryHi] & 0xff; - int index; - - /* iterate over all TLB entries */ - for (index = 0; index < ARRAY_LENGTH(mips->tlb); index++) - { - mips3_tlb_entry *tlbent = &mips->tlb[index]; - - /* only process if the ASID matches or if global */ - if (valid_asid == (tlbent->entry_hi & 0xff) || ((tlbent->entry_lo[0] & tlbent->entry_lo[1]) & TLB_GLOBAL)) - { - UINT32 count = (tlbent->page_mask >> 13) & 0x00fff; - UINT32 vpn = ((tlbent->entry_hi >> 13) & 0x07ffffff) << 1; - int which, i; - - /* ignore if the virtual address is beyond 32 bits */ - if (vpn < (1 << (MIPS3_MAX_PADDR_SHIFT - MIPS3_MIN_PAGE_SHIFT))) - - /* loop over both the even and odd pages */ - for (which = 0; which < 2; which++) - { - UINT64 lo = tlbent->entry_lo[which]; - UINT32 pfn = (lo >> 6) & 0x00ffffff; - UINT32 wp = (lo & 7) | TLB_PRESENT; - - /* ensure that if the valid bit is clear, the dirty bit is as well */ - /* this way we can always test dirty for writes, and valid for reads */ - if (!(wp & TLB_VALID)) - wp &= ~TLB_DIRTY; - - for (i = 0; i <= count; i++, vpn++, pfn++) - if (vpn < 0x80000 || vpn >= 0xc0000) - mips->tlb_table[vpn] = (pfn << MIPS3_MIN_PAGE_SHIFT) | wp; - } - } - } -} - - -/*------------------------------------------------- - mips3com_unmap_tlb_entries - unmap entries from - the TLB into the tlb_table + mips3com_asid_changed - remap all non-global + TLB entries -------------------------------------------------*/ -void mips3com_unmap_tlb_entries(mips3_state *mips) +void mips3com_asid_changed(mips3_state *mips) { - int index; - - /* iterate over all TLB entries */ - for (index = 0; index < ARRAY_LENGTH(mips->tlb); index++) - { - mips3_tlb_entry *tlbent = &mips->tlb[index]; - UINT32 count = (tlbent->page_mask >> 13) & 0x00fff; - UINT32 vpn = ((tlbent->entry_hi >> 13) & 0x07ffffff) << 1; - int which, i; - - /* ignore if the virtual address is beyond 32 bits */ - if (vpn < (1 << (MIPS3_MAX_PADDR_SHIFT - MIPS3_MIN_PAGE_SHIFT))) - - /* loop over both the even and odd pages */ - for (which = 0; which < 2; which++) - for (i = 0; i <= count; i++, vpn++) - if (vpn < 0x80000 || vpn >= 0xc0000) - mips->tlb_table[vpn] = 0; - } -} - - -/*------------------------------------------------- - mips3com_recompute_tlb_table - recompute the TLB - table from scratch --------------------------------------------------*/ - -void mips3com_recompute_tlb_table(mips3_state *mips) -{ - UINT32 addr; - - /* map in the hard-coded spaces */ - for (addr = 0x80000000; addr < 0xc0000000; addr += MIPS3_MIN_PAGE_SIZE) - mips->tlb_table[addr >> MIPS3_MIN_PAGE_SHIFT] = (addr & 0x1ffff000) | TLB_PRESENT | TLB_DIRTY | TLB_VALID | TLB_GLOBAL; - - /* reset everything else to unmapped */ - memset(&mips->tlb_table[0x00000000 >> MIPS3_MIN_PAGE_SHIFT], 0, sizeof(mips->tlb_table[0]) * (0x80000000 >> MIPS3_MIN_PAGE_SHIFT)); - memset(&mips->tlb_table[0xc0000000 >> MIPS3_MIN_PAGE_SHIFT], 0, sizeof(mips->tlb_table[0]) * (0x40000000 >> MIPS3_MIN_PAGE_SHIFT)); + int tlbindex; - /* remap all the entries in the TLB */ - mips3com_map_tlb_entries(mips); + /* iterate over all non-global TLB entries and remap them */ + for (tlbindex = 0; tlbindex < ARRAY_LENGTH(mips->tlb); tlbindex++) + if (!tlb_entry_is_global(&mips->tlb[tlbindex])) + tlb_map_entry(mips, tlbindex); } @@ -260,10 +216,11 @@ int mips3com_translate_address(mips3_state *mips, int space, int intention, offs /* only applies to the program address space */ if (space == ADDRESS_SPACE_PROGRAM) { - UINT32 result = mips->tlb_table[*address >> MIPS3_MIN_PAGE_SHIFT]; - if (!(result & TLB_PRESENT) || !(result & TLB_VALID)) + const vtlb_entry *table = vtlb_table(mips->vtlb); + vtlb_entry entry = table[*address >> MIPS3_MIN_PAGE_SHIFT]; + if ((entry & (1 << (intention & (TRANSLATE_TYPE_MASK | TRANSLATE_USER_MASK)))) == 0) return FALSE; - *address = (result & ~MIPS3_MIN_PAGE_MASK) | (*address & MIPS3_MIN_PAGE_MASK); + *address = (entry & ~MIPS3_MIN_PAGE_MASK) | (*address & MIPS3_MIN_PAGE_MASK); } return TRUE; } @@ -275,18 +232,18 @@ int mips3com_translate_address(mips3_state *mips, int space, int intention, offs void mips3com_tlbr(mips3_state *mips) { - UINT32 index = mips->cpr[0][COP0_Index] & 0x3f; + UINT32 tlbindex = mips->cpr[0][COP0_Index] & 0x3f; /* only handle entries within the TLB */ - if (index < ARRAY_LENGTH(mips->tlb)) + if (tlbindex < ARRAY_LENGTH(mips->tlb)) { - mips3_tlb_entry *tlbent = &mips->tlb[index]; + mips3_tlb_entry *entry = &mips->tlb[tlbindex]; /* copy data from the TLB entry into the COP0 registers */ - mips->cpr[0][COP0_PageMask] = tlbent->page_mask; - mips->cpr[0][COP0_EntryHi] = tlbent->entry_hi; - mips->cpr[0][COP0_EntryLo0] = tlbent->entry_lo[0]; - mips->cpr[0][COP0_EntryLo1] = tlbent->entry_lo[1]; + mips->cpr[0][COP0_PageMask] = entry->page_mask; + mips->cpr[0][COP0_EntryHi] = entry->entry_hi; + mips->cpr[0][COP0_EntryLo0] = entry->entry_lo[0]; + mips->cpr[0][COP0_EntryLo1] = entry->entry_lo[1]; } } @@ -310,14 +267,14 @@ void mips3com_tlbwr(mips3_state *mips) { UINT32 wired = mips->cpr[0][COP0_Wired] & 0x3f; UINT32 unwired = ARRAY_LENGTH(mips->tlb) - wired; - UINT32 index = ARRAY_LENGTH(mips->tlb) - 1; + UINT32 tlbindex = ARRAY_LENGTH(mips->tlb) - 1; /* "random" is based off of the current cycle counting through the non-wired pages */ if (unwired > 0) - index = ((activecpu_gettotalcycles() - mips->count_zero_time) % unwired + wired) & 0x3f; + tlbindex = ((activecpu_gettotalcycles() - mips->count_zero_time) % unwired + wired) & 0x3f; - /* use the common handler to write to this index */ - tlb_write_common(mips, index); + /* use the common handler to write to this tlbindex */ + tlb_write_common(mips, tlbindex); } @@ -327,35 +284,29 @@ void mips3com_tlbwr(mips3_state *mips) void mips3com_tlbp(mips3_state *mips) { - UINT32 index; + UINT32 tlbindex; UINT64 vpn; /* iterate over TLB entries */ - for (index = 0; index < ARRAY_LENGTH(mips->tlb); index++) + for (tlbindex = 0; tlbindex < ARRAY_LENGTH(mips->tlb); tlbindex++) { - mips3_tlb_entry *tlbent = &mips->tlb[index]; - UINT64 mask = ~((tlbent->page_mask >> 13) & 0xfff) << 13; + mips3_tlb_entry *entry = &mips->tlb[tlbindex]; + UINT64 mask = ~((entry->page_mask >> 13) & 0xfff) << 13; /* if the relevant bits of EntryHi match the relevant bits of the TLB */ - if ((tlbent->entry_hi & mask) == (mips->cpr[0][COP0_EntryHi] & mask)) + if ((entry->entry_hi & mask) == (mips->cpr[0][COP0_EntryHi] & mask)) /* and if we are either global or matching the current ASID, then stop */ - if ((tlbent->entry_hi & 0xff) == (mips->cpr[0][COP0_EntryHi] & 0xff) || ((tlbent->entry_lo[0] & tlbent->entry_lo[1]) & TLB_GLOBAL)) + if ((entry->entry_hi & 0xff) == (mips->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 = ((mips->cpr[0][COP0_EntryHi] >> 13) & 0x07ffffff) << 1; - if (index != ARRAY_LENGTH(mips->tlb)) - { - assert(mips->tlb_table[vpn & 0xfffff] & TLB_PRESENT); - mips->cpr[0][COP0_Index] = index; - } + if (tlbindex != ARRAY_LENGTH(mips->tlb)) + mips->cpr[0][COP0_Index] = tlbindex; else - { - assert(!(mips->tlb_table[vpn & 0xfffff] & TLB_PRESENT)); mips->cpr[0][COP0_Index] = 0x80000000; - } } @@ -828,32 +779,90 @@ static UINT32 compute_prid_register(const mips3_state *mips) /*------------------------------------------------- + tlb_map_entry - map a single TLB + entry +-------------------------------------------------*/ + +static void tlb_map_entry(mips3_state *mips, int tlbindex) +{ + int current_asid = mips->cpr[0][COP0_EntryHi] & 0xff; + mips3_tlb_entry *entry = &mips->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(mips->vtlb, 2 * tlbindex + 0, 0, 0, 0); + vtlb_load(mips->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(mips->vtlb, 2 * tlbindex + 0, 0, 0, 0); + vtlb_load(mips->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 = (lo >> 6) & 0x00ffffff; + UINT32 flags = 0; + + /* 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(mips->vtlb, 2 * tlbindex + which, count, effvpn << MIPS3_MIN_PAGE_SHIFT, (pfn << MIPS3_MIN_PAGE_SHIFT) | flags); + } +} + + +/*------------------------------------------------- tlb_write_common - common routine for writing a TLB entry -------------------------------------------------*/ -static void tlb_write_common(mips3_state *mips, int index) +static void tlb_write_common(mips3_state *mips, int tlbindex) { /* only handle entries within the TLB */ - if (index < ARRAY_LENGTH(mips->tlb)) + if (tlbindex < ARRAY_LENGTH(mips->tlb)) { - mips3_tlb_entry *tlbent = &mips->tlb[index]; - - /* unmap what we have */ - mips3com_unmap_tlb_entries(mips); + mips3_tlb_entry *entry = &mips->tlb[tlbindex]; /* fill in the new TLB entry from the COP0 registers */ - tlbent->page_mask = mips->cpr[0][COP0_PageMask]; - tlbent->entry_hi = mips->cpr[0][COP0_EntryHi] & ~(tlbent->page_mask & U64(0x0000000001ffe000)); - tlbent->entry_lo[0] = mips->cpr[0][COP0_EntryLo0]; - tlbent->entry_lo[1] = mips->cpr[0][COP0_EntryLo1]; + entry->page_mask = mips->cpr[0][COP0_PageMask]; + entry->entry_hi = mips->cpr[0][COP0_EntryHi] & ~(entry->page_mask & U64(0x0000000001ffe000)); + entry->entry_lo[0] = mips->cpr[0][COP0_EntryLo0]; + entry->entry_lo[1] = mips->cpr[0][COP0_EntryLo1]; - /* remap the TLB */ - mips3com_map_tlb_entries(mips); + /* remap this TLB entry */ + tlb_map_entry(mips, tlbindex); /* log the two halves once they are in */ - tlb_entry_log_half(tlbent, index, 0); - tlb_entry_log_half(tlbent, index, 1); + tlb_entry_log_half(entry, tlbindex, 0); + tlb_entry_log_half(entry, tlbindex, 1); } } @@ -863,24 +872,24 @@ static void tlb_write_common(mips3_state *mips, int index) entry -------------------------------------------------*/ -static void tlb_entry_log_half(mips3_tlb_entry *tlbent, int index, int which) +static void tlb_entry_log_half(mips3_tlb_entry *entry, int tlbindex, int which) { #if PRINTF_TLB - UINT64 hi = tlbent->entry_hi; - UINT64 lo = tlbent->entry_lo[which]; + 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 = (((tlbent->page_mask >> 13) & 0xfff) + 1) << MIPS3_MIN_PAGE_SHIFT; + 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; - mame_printf_debug("index=%08X pagesize=%08X vaddr=%08X%08X paddr=%08X%08X asid=%02X r=%X c=%X dvg=%c%c%c\n", - index, pagesize, (UINT32)(vaddr >> 32), (UINT32)vaddr, (UINT32)(paddr >> 32), (UINT32)paddr, + 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' : '.'); #endif } |