MIPS TLB performance improvements, by Daniel Jacobowitz.
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@2220 c046a42c-6fe2-441c-8c8c-71466251a162
This commit is contained in:
parent
ec2309289d
commit
814b9a4749
@ -94,7 +94,8 @@ struct CPUMIPSState {
|
|||||||
|
|
||||||
#endif
|
#endif
|
||||||
#if defined(MIPS_USES_R4K_TLB)
|
#if defined(MIPS_USES_R4K_TLB)
|
||||||
tlb_t tlb[MIPS_TLB_NB];
|
tlb_t tlb[MIPS_TLB_MAX];
|
||||||
|
uint32_t tlb_in_use;
|
||||||
#endif
|
#endif
|
||||||
uint32_t CP0_index;
|
uint32_t CP0_index;
|
||||||
uint32_t CP0_random;
|
uint32_t CP0_random;
|
||||||
|
@ -115,5 +115,6 @@ uint32_t cpu_mips_get_count (CPUState *env);
|
|||||||
void cpu_mips_store_count (CPUState *env, uint32_t value);
|
void cpu_mips_store_count (CPUState *env, uint32_t value);
|
||||||
void cpu_mips_store_compare (CPUState *env, uint32_t value);
|
void cpu_mips_store_compare (CPUState *env, uint32_t value);
|
||||||
void cpu_mips_clock_init (CPUState *env);
|
void cpu_mips_clock_init (CPUState *env);
|
||||||
|
void cpu_mips_tlb_flush (CPUState *env, int flush_global);
|
||||||
|
|
||||||
#endif /* !defined(__QEMU_MIPS_EXEC_H__) */
|
#endif /* !defined(__QEMU_MIPS_EXEC_H__) */
|
||||||
|
@ -46,7 +46,7 @@ static int map_address (CPUState *env, target_ulong *physical, int *prot,
|
|||||||
tlb_t *tlb;
|
tlb_t *tlb;
|
||||||
int i, n;
|
int i, n;
|
||||||
|
|
||||||
for (i = 0; i < MIPS_TLB_NB; i++) {
|
for (i = 0; i < env->tlb_in_use; i++) {
|
||||||
tlb = &env->tlb[i];
|
tlb = &env->tlb[i];
|
||||||
/* Check ASID, virtual page number & size */
|
/* Check ASID, virtual page number & size */
|
||||||
if ((tlb->G == 1 || tlb->ASID == ASID) &&
|
if ((tlb->G == 1 || tlb->ASID == ASID) &&
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
/* Uses MIPS R4Kc TLB model */
|
/* Uses MIPS R4Kc TLB model */
|
||||||
#define MIPS_USES_R4K_TLB
|
#define MIPS_USES_R4K_TLB
|
||||||
#define MIPS_TLB_NB 16
|
#define MIPS_TLB_NB 16
|
||||||
|
#define MIPS_TLB_MAX 128
|
||||||
/* basic FPU register support */
|
/* basic FPU register support */
|
||||||
#define MIPS_USES_FPU 1
|
#define MIPS_USES_FPU 1
|
||||||
/* Define a implementation number of 1.
|
/* Define a implementation number of 1.
|
||||||
|
@ -367,7 +367,7 @@ void do_mtc0 (int reg, int sel)
|
|||||||
env->CP0_EntryHi = val;
|
env->CP0_EntryHi = val;
|
||||||
/* If the ASID changes, flush qemu's TLB. */
|
/* If the ASID changes, flush qemu's TLB. */
|
||||||
if ((old & 0xFF) != (val & 0xFF))
|
if ((old & 0xFF) != (val & 0xFF))
|
||||||
tlb_flush (env, 1);
|
cpu_mips_tlb_flush (env, 1);
|
||||||
rn = "EntryHi";
|
rn = "EntryHi";
|
||||||
break;
|
break;
|
||||||
case 11:
|
case 11:
|
||||||
@ -568,7 +568,14 @@ void fpu_handle_exception(void)
|
|||||||
|
|
||||||
/* TLB management */
|
/* TLB management */
|
||||||
#if defined(MIPS_USES_R4K_TLB)
|
#if defined(MIPS_USES_R4K_TLB)
|
||||||
static void invalidate_tlb (int idx)
|
void cpu_mips_tlb_flush (CPUState *env, int flush_global)
|
||||||
|
{
|
||||||
|
/* Flush qemu's TLB and discard all shadowed entries. */
|
||||||
|
tlb_flush (env, flush_global);
|
||||||
|
env->tlb_in_use = MIPS_TLB_NB;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void invalidate_tlb (int idx, int use_extra)
|
||||||
{
|
{
|
||||||
tlb_t *tlb;
|
tlb_t *tlb;
|
||||||
target_ulong addr;
|
target_ulong addr;
|
||||||
@ -583,6 +590,15 @@ static void invalidate_tlb (int idx)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (use_extra && env->tlb_in_use < MIPS_TLB_MAX) {
|
||||||
|
/* For tlbwr, we can shadow the discarded entry into
|
||||||
|
a new (fake) TLB entry, as long as the guest can not
|
||||||
|
tell that it's there. */
|
||||||
|
env->tlb[env->tlb_in_use] = *tlb;
|
||||||
|
env->tlb_in_use++;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (tlb->V0) {
|
if (tlb->V0) {
|
||||||
tb_invalidate_page_range(tlb->PFN[0], tlb->end - tlb->VPN);
|
tb_invalidate_page_range(tlb->PFN[0], tlb->end - tlb->VPN);
|
||||||
addr = tlb->VPN;
|
addr = tlb->VPN;
|
||||||
@ -601,6 +617,14 @@ static void invalidate_tlb (int idx)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void mips_tlb_flush_extra (CPUState *env, int first)
|
||||||
|
{
|
||||||
|
/* Discard entries from env->tlb[first] onwards. */
|
||||||
|
while (env->tlb_in_use > first) {
|
||||||
|
invalidate_tlb(--env->tlb_in_use, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void fill_tlb (int idx)
|
static void fill_tlb (int idx)
|
||||||
{
|
{
|
||||||
tlb_t *tlb;
|
tlb_t *tlb;
|
||||||
@ -627,9 +651,14 @@ static void fill_tlb (int idx)
|
|||||||
|
|
||||||
void do_tlbwi (void)
|
void do_tlbwi (void)
|
||||||
{
|
{
|
||||||
|
/* Discard cached TLB entries. We could avoid doing this if the
|
||||||
|
tlbwi is just upgrading access permissions on the current entry;
|
||||||
|
that might be a further win. */
|
||||||
|
mips_tlb_flush_extra (env, MIPS_TLB_NB);
|
||||||
|
|
||||||
/* Wildly undefined effects for CP0_index containing a too high value and
|
/* Wildly undefined effects for CP0_index containing a too high value and
|
||||||
MIPS_TLB_NB not being a power of two. But so does real silicon. */
|
MIPS_TLB_NB not being a power of two. But so does real silicon. */
|
||||||
invalidate_tlb(env->CP0_index & (MIPS_TLB_NB - 1));
|
invalidate_tlb(env->CP0_index & (MIPS_TLB_NB - 1), 0);
|
||||||
fill_tlb(env->CP0_index & (MIPS_TLB_NB - 1));
|
fill_tlb(env->CP0_index & (MIPS_TLB_NB - 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -637,7 +666,7 @@ void do_tlbwr (void)
|
|||||||
{
|
{
|
||||||
int r = cpu_mips_get_random(env);
|
int r = cpu_mips_get_random(env);
|
||||||
|
|
||||||
invalidate_tlb(r);
|
invalidate_tlb(r, 1);
|
||||||
fill_tlb(r);
|
fill_tlb(r);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -660,6 +689,17 @@ void do_tlbp (void)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (i == MIPS_TLB_NB) {
|
if (i == MIPS_TLB_NB) {
|
||||||
|
/* No match. Discard any shadow entries, if any of them match. */
|
||||||
|
for (i = MIPS_TLB_NB; i < env->tlb_in_use; i++) {
|
||||||
|
tlb = &env->tlb[i];
|
||||||
|
|
||||||
|
/* Check ASID, virtual page number & size */
|
||||||
|
if ((tlb->G == 1 || tlb->ASID == ASID) && tlb->VPN == tag) {
|
||||||
|
mips_tlb_flush_extra (env, i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
env->CP0_index |= 0x80000000;
|
env->CP0_index |= 0x80000000;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -674,8 +714,10 @@ void do_tlbr (void)
|
|||||||
tlb = &env->tlb[env->CP0_index & (MIPS_TLB_NB - 1)];
|
tlb = &env->tlb[env->CP0_index & (MIPS_TLB_NB - 1)];
|
||||||
|
|
||||||
/* If this will change the current ASID, flush qemu's TLB. */
|
/* If this will change the current ASID, flush qemu's TLB. */
|
||||||
if (ASID != tlb->ASID && tlb->G != 1)
|
if (ASID != tlb->ASID)
|
||||||
tlb_flush (env, 1);
|
cpu_mips_tlb_flush (env, 1);
|
||||||
|
|
||||||
|
mips_tlb_flush_extra(env, MIPS_TLB_NB);
|
||||||
|
|
||||||
env->CP0_EntryHi = tlb->VPN | tlb->ASID;
|
env->CP0_EntryHi = tlb->VPN | tlb->ASID;
|
||||||
size = (tlb->end - tlb->VPN) >> 12;
|
size = (tlb->end - tlb->VPN) >> 12;
|
||||||
|
@ -2430,6 +2430,7 @@ CPUMIPSState *cpu_mips_init (void)
|
|||||||
env->PC = 0xBFC00000;
|
env->PC = 0xBFC00000;
|
||||||
#if defined (MIPS_USES_R4K_TLB)
|
#if defined (MIPS_USES_R4K_TLB)
|
||||||
env->CP0_random = MIPS_TLB_NB - 1;
|
env->CP0_random = MIPS_TLB_NB - 1;
|
||||||
|
env->tlb_in_use = MIPS_TLB_NB;
|
||||||
#endif
|
#endif
|
||||||
env->CP0_Wired = 0;
|
env->CP0_Wired = 0;
|
||||||
env->CP0_Config0 = MIPS_CONFIG0;
|
env->CP0_Config0 = MIPS_CONFIG0;
|
||||||
|
Loading…
Reference in New Issue
Block a user