📄 r4k_cp0.c
字号:
IndexReg index; /* I'm going to assume that the index register is in the correct range. */ index = P->CP0[C0_INX]; P->CP0[C0_PGMASK] = P->tlbEntry[GET_IDX(index)].PgMsk; P->CP0[C0_TLBHI] = P->tlbEntry[GET_IDX(index)].Hi & ~P->tlbEntry[GET_IDX(index)].PgMsk & ~TLBHI_G; P->CP0[C0_TLBLO_0] = P->tlbEntry[GET_IDX(index)].Lo0; P->CP0[C0_TLBLO_1] = P->tlbEntry[GET_IDX(index)].Lo1; if (IS_GLOBAL_HI(P->tlbEntry[GET_IDX(index)].Hi)){ P->CP0[C0_TLBLO_0] |= TLBLO_G; P->CP0[C0_TLBLO_1] |= TLBLO_G; } else { P->CP0[C0_TLBLO_0] &= ~TLBLO_G; P->CP0[C0_TLBLO_1] &= ~TLBLO_G; } /* Reading into TLBHI could change the ASID */ P->pcVPNcache = 0; TraceCheckASID(P); return;}/***************************************************************** * Do_TLB_Write * Write the indexed TLB entry. The TLB entry pointed at by the * contents of the TLB index register is loaded with the contents * of the Hi, Lo0, Lo1, and PgMsk registers. Since both Lo0 and Lo1 * are written, the C0_TLBLO_1 and _0 must be in a consistent * state before this instruction is called. * * R4000 CHANGES >> G bit of tlbhi written as AND of G bits * in EntryLo0 and EntryLo1. *****************************************************************/static Result DoTLBWrite(CPUState *P, int idx){ int hashNum; Reg frameMask; /* remove old entry from hash table */ if( P->indexList[idx].onList ) { List_Remove( &(P->indexList[idx].links)); P->indexList[idx].onList = 0; } /* if we are doing an indexed write into a segment which should not * be using the TLB we do NOTHING, and issue a mipsyswarning * that this has been called. */ if (IS_R10000(P)) { frameMask = (P->CP0[C0_FRAMEMASK] << TLBFRAMESHIFT); } else { frameMask = 0; } P->tlbEntry[idx].PgMsk = P->CP0[C0_PGMASK]; P->tlbEntry[idx].Hi = P->CP0[C0_TLBHI] & ~P->CP0[C0_PGMASK]; P->tlbEntry[idx].Lo0 = (P->CP0[C0_TLBLO_0] & ~TLBLO_G) & ~frameMask; P->tlbEntry[idx].Lo1 = (P->CP0[C0_TLBLO_1] & ~TLBLO_G) & ~frameMask; P->tlbEntrySize[idx] = ComputeTlbEntrySize(P->tlbEntry[idx].PgMsk); if (IS_GLOBAL_LO(P->CP0[C0_TLBLO_0]) && IS_GLOBAL_LO(P->CP0[C0_TLBLO_1])) { P->tlbEntry[idx].Hi |= TLBHI_G; } /* To check which segment it is, I retrieve the VPN2 and shift up * by 1 to get the original vpn. Since all VPN2's are floored, and * segs are on even boundaries, this should work. Then this number up * to account for the page offset so that the MIPSY_IS_KSEG0 test will * work. This will change when PageMask register is used. */ if (!IS_UNMAPPED_TLBHI(P->tlbEntry[idx].Hi)) { /* update hash table Insert all global entries under ASID 0 */ if (IS_GLOBAL_HI( P->tlbEntry[idx].Hi)) { hashNum = TLBHash(GET_VPN2(P->tlbEntry[idx].Hi), GET_REGION(P->tlbEntry[idx].Hi), 0); } else { hashNum = TLBHash(GET_VPN2(P->tlbEntry[idx].Hi), GET_REGION(P->tlbEntry[idx].Hi), GET_ASID(P->tlbEntry[idx].Hi)); if(GET_ASID(P->tlbEntry[idx].Hi) == 0) { if (!((P->tlbEntry[idx].Hi == 0) && !(TLBLO_V & P->tlbEntry[idx].Lo0) && !(TLBLO_V & P->tlbEntry[idx].Lo1))) { /* IRIX 6.2 seems to like to slam all zeros into * the TLB. Don't print a message if this happens. */#ifndef TORNADO/* common operation in Tornado; should be nothing wrong with it */#if 0 /* just as common in Topsy -- PZ */ CPUWarning("Non-global ASID 0 entry written to TLB @%#lx RA %#lx\n", (long)P->PC, (long)P->R[31]); /* got rid of the evil %llx -- PZ */#endif#else#endif /* TORNADO */ } } } List_Insert(&(P->indexList[idx].links), LIST_ATFRONT(&(P->tlbIndexHeaders[hashNum]))); P->indexList[idx].onList = 1; if (!IS_CACHEABLE(P->tlbEntry[idx].Lo0) || !IS_CACHEABLE(P->tlbEntry[idx].Lo1)) { CPUWarning("Entering uncached TLB Entry at PC %#x\n", P->PC); } } /* END IF KSEG0 || KSEG1 */ P->pcVPNcache = 0; TraceCheckASID(P); return SUCCESS;}/***************************************************************** * WriteTLBEntry - * Write the indexed TLB entry. The TLB entry pointed at by the * contents of the TLB index register is loaded with the contents * of the Hi and Lo registers. *****************************************************************/static ResultWriteTLBEntry(CPUState *P){ IndexReg index; /* I'm going to assume that the index register is in the correct range. */ index = P->CP0[C0_INX]; return DoTLBWrite(P, GET_IDX(index));}/***************************************************************** * WriteRandomTLBEntry - * Write random TLB entry. The TLB entry pointed at by the contents * of the TLB Random register is loaded with the contents of Hi * and Lo. * * Technically, the value of the Random register is decremented on * each machine clock cycle and ranges between NWIREDENTRIES * and NTLBENTRIES. To save time, I'll just read the clock. * TO_DO NUMWIREDENTRIES should be based on the register. it * is, I keep the randomReg unimplemented and hueristically done as before. * * R4000 CHANGES >> No changes. *****************************************************************/#ifdef DETERMINISTIC_TLBint currentRandom = 0;#endifstatic ResultWriteRandomTLBEntry(CPUState *P){ unsigned randomReg; unsigned numRandEntries = P->numTlbEntries - P->CP0[C0_TLBWIRED];#ifdef DETERMINISTIC_TLB randomReg = (currentRandom++ % numRandEntries);#else randomReg = (MipsyReadTime(P->myNum) % numRandEntries);#endif return DoTLBWrite(P, randomReg + P->CP0[C0_TLBWIRED]);}/***************************************************************** * EXCEPTION - * This is sort of a major routine. When an exception is raised, * execution is suspended and the processor enters kernel mode. * The CPU should abort the instruction that caused the exception * along with any others in the pipeline. * * The CPU loads the Exception Program Counter (EPC) with a * restart location - either the instruction, or it's predecessor * if it's in the branch delay slot. I check if it's in a branch * delay slot by seeing if the difference between PC and nPC is * greater than one instruction. * * R4000 STATUS REGISTER UPDATE * 1.) The handler needs to know the mode(user or supervisor) * so only do not change mode. * 2.) disable interrupts * 3.) Are we inside another Exception, if EXL == 1 we are, * and do not set PC or nPC. * 4.) on start cpu loads EPC with restart location... * 5.) check BEV bit and goto vector. * * The current kernel/user mode and interrupt enable status is NOT saved. * Currently does not set ce bit in status register. ****************************************************************/static bool isREFILLException = FALSE; void EXCEPTION(CPUState *P, int code){ StatusReg statusReg; CauseReg causeReg; uint prettyCode = code >> CAUSE_EXCSHIFT; VA exceptionBase; TraceException(P, code); SIM_DEBUG_DETAIL(('i', "INTR", P->myNum, "EXC code: 0x%x)\tintrbits: 0x%x\tPC: 0x%llx, RA: 0x%llx bad=0x%llx\n", code, P->intrBitsPtr[0], (uint64)P->PC, (uint64) P->R[31],(uint64)P->CP0[C0_BADVADDR])); statusReg.ts_data = P->CP0[C0_SR]; /* no exceptions when ERL is asserted */ if (statusReg.s32.ts_erl) return; /* Clear any cached instruction in case this exception is terminating * a miss stall */ P->stalledInst = 0; /* Set the BD bit, CE, IP, and ExcCode fields of the Cause * register. Along with the BD bit, set the Exception Program * Counter (either to current PC or PC - 4 if BD slot). */ causeReg.tc_data = P->CP0[C0_CAUSE]; causeReg.s32.tc_exccode = code >> CAUSE_EXCSHIFT; /* The code comes in already shifted to the left 2 bits */ if ((code == EXC_RMISS) || (code == EXC_WMISS)) { STATS_INC(P->myNum, numFaults, 1); } if (code == EXC_INT) { STATS_INC(P->myNum, numInterrupts, 1); } STATS_INC(P->myNum, causeCount[prettyCode], 1); /* EPC must point to the preceeding branch or delay instruction */ if (P->branchStatus == BranchStatus_bd) { /* The inst causing the exception is in a BD slot */ causeReg.s32.tc_bd = TRUE; if (statusReg.s32.ts_exl == 0){ P->CP0[C0_EPC] = P->PC-INST_SIZE; statusReg.s32.ts_exl = 1; } } else { causeReg.s32.tc_bd = FALSE; if (statusReg.s32.ts_exl == 0){ P->CP0[C0_EPC] = P->PC; statusReg.s32.ts_exl = 1; } } P->CP0[C0_SR] = statusReg.ts_data; P->CP0[C0_CAUSE] = causeReg.tc_data; UpdateCPUMode(P); EXC_STALL_EVENT(P->myNum, P->PC, 1); exceptionBase = (statusReg.s32.ts_bev == 0) ? EXC_VEC_BASE_0 : EXC_VEC_BASE_1; if (isREFILLException){#if defined(SIM_MIPS64) Reg32 sr_reg = statusReg.ts_data; int region; XContextReg xctxt; xctxt.tc_data = P->CP0[C0_XCTXT]; region = xctxt.s64.tc_region; if (((region == 0) && (sr_reg & SR_UX)) || ((region == 3) && (sr_reg & SR_KX)) || ((region == 1) && (sr_reg & SR_SX))) { P->PC = exceptionBase + XUT_VEC_OFFSET; P->nPC = exceptionBase + XUT_VEC_OFFSET + 4; } else { #else {#endif P->PC = exceptionBase; P->nPC = exceptionBase+4; } } else { P->PC = exceptionBase + E_VEC_OFFSET; P->nPC = exceptionBase + E_VEC_OFFSET + 4; } if (isREFILLException) { UTLB_EVENT(); } else { EXC_EVENT(prettyCode); }}/***************************************************************** * UTLB_EXCEPTION * * R4000 MIPS uses 1 special vector for any * TLB refill exception. * note:: this used to be called utlb_exception. ****************************************************************/static void UTLB_EXCEPTION(CPUState *P, int code){ /* This will be counted here and in the EXC_WMISS and */ /* EXC_RMISS. Subtract out if needed. */ STATS_INC(P->myNum, utlbCount[code >> CAUSE_EXCSHIFT], 1); isREFILLException = TRUE; EXCEPTION(P, code); isREFILLException = FALSE;}/* UNTOUCHED MXS */#ifdef MIPSY_MXS/* * PrintException - Print the specified exception - used for debugging. */void PrintException(struct s_cpu_state *st, int exnum) { CPUState *P = (CPUState *) (st->mipsyPtr); if (exnum >= 0) CPUPrint("MXS: CPU%d: EXC @ 0x%x cause 0x%x badvaddr 0x%x time %lld\n", P->myNum, P->CP0[C0_EPC], P->exception[exnum].cause, P->exception[exnum].badaddr, (uint64)MipsyReadTime(P->myNum)); }/* * HandleException - Modify the register state of the machine * to reflect an exception happening. */void HandleException(struct s_cpu_state *st, int exnum, int in_delay) { CPUState *P = (CPUState *) (st->mipsyPtr); StatusReg statusReg; statusReg.ts_data = P->CP0[C0_SR]; CopyFromMXS (P); if (in_delay) { P->branchStatus = BranchStatus_bd; } else { P->branchStatus = BranchStatus_none; } if (exnum < 0) { if (exnum == -1) { /* Coherency exception - Restart from point of exception */ if (in_delay) { P->PC -= 4; P->nPC = P->PC+4; } CopyToMXS (P); /* Resets the MXS instruction window */ CPUPrint("MXS: Coherency exception at 0x%x time %lld\n", P->PC, (uint64) MipsyReadTime(P->myNum)); } else if (exnum == -2) { /* Switch back to mipsy */ P->inMXS = 0; P->switchToMIPSY = 0; CopyToMXS(P); /* This will cause MXS to be reset */ CPUPrint("MXS: Switching to MIPSY on CPU%d at 0x%x time %lld\n", P->myNum, P->PC, (uint64)MipsyReadTime(P->myNum)); } else if (exnum == -3) { CPUPrint("MXS: About to ProcEXIT cpu %d at 0x%x time %lld\n", P->myNum, P->PC, (uint64)MipsyReadTime(P->myNum)); /* MakeProcessExit(P->myNum); */ CPUError("MakeProcessExit doesn't work\n"); CopyToMXS(P); /* This will cause MXS to be reset */ } else if (exnum == -4) { CPUPrint("MXS: About to EXIT cpu %d at 0x%x time %lld\n", P->myNum, P->PC, (uint64)MipsyReadTime(P->myNum)); CopyToMXS(P); /* This will cause MXS to be reset */ MipsyExit(BASE); } return; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -