📄 cache.c
字号:
L2_LINE_TRANS_EVENT(cpuNum, pAddr, type, EMP[cpuNum].PC, 0, IS_KUSEG(EMP[cpuNum].PC)); L2_IMISS_EVENT( EmbraCpuCycleCount(cpuNum) + miss_handling_time, cpuNum, EMP[cpuNum].PC, pcPAddr, miss_handling_time, type | E_I); miss_handling_time += MEM_CYCLE_TIME; CACHE_INC( cpuNum, CURRENT_MODE(&EMP[cpuNum]), i_miss, d_miss, MEM_I_SHARED ); /* Set the data QC to inaccessible */ set_qc_state( cpuNum, ADDR2SLINE(vAddr), pline, MEM_INVALID ); /* No need to CLEAR_BD */ /* Set the instr QC to accessible */ set_qc_state( cpuNum, ADDR2SLINE(CLEAR_BD(EMP[cpuNum].PC)), ADDR2SLINE(pcPAddr), MEM_I_SHARED ); EMP[cpuNum].cache_tag[line_no] = CACHE_SET_SHARED( ADDR2SLINE(pcPAddr) ); } else { /* Set the new line's quick check to the proper, accesible state */ /* Note that this invalidates phys_info[pline].virt_line */ /* Since the new entry could be the same as the old ( like on an */ /* upgrade the invalidate happens first */ set_qc_state( cpuNum, ADDR2SLINE(vAddr), pline, state ); /* Update Mshade tags */ if( VQC_SHARED( state ) ) { EMP[cpuNum].cache_tag[line_no] = CACHE_SET_SHARED( pline ); /* RG here are the transitions... */ } else { ASSERT( VQC_EXCL( state ) ); EMP[cpuNum].cache_tag[line_no] = CACHE_SET_EXCL( pline ); /* RG here are the transitions... */ } } EMP[cpuNum].cycleCountdown -= miss_handling_time; CACHE_INC_BY(cpuNum, CURRENT_MODE(&EMP[cpuNum]), i_stall_cyc, d_stall_cyc, state, miss_handling_time );}void MPinUPCache_Ref( int cpuNum, PA pAddr, VA vAddr, EmVQCMemState state ){ uint pline = ADDR2SLINE(pAddr); uint line_no = SCACHE_INDEXOF(pAddr); int smht_flags = 0; int miss_handling_time = 0; MA maPCAddr = 0; if (interest(pAddr)) { LogEntry("ref",cpuNum,"pAddr=%08x state=%x msstate=%d \n",pAddr,state, MSCacheState(cpuNum,pAddr)); } /* no sc in delay slot */ if( !IN_BD( EMP[cpuNum].PC ) ){ maPCAddr = K0_TO_MEMADDR(M_FROM_CPU(cpuNum), non_excepting_tv(cpuNum,EMP[cpuNum].PC)); ASSERT(maPCAddr); if( MAJOR_OPCODE(*(uint*)maPCAddr) == sc_op ) { smht_flags |= SMHT_SC; } } else { smht_flags |= SMHT_BD; } if( pline == CACHE_PLINE( EMP[cpuNum].cache_tag[line_no] ) && CACHE_VALID( EMP[cpuNum].cache_tag[line_no] ) ) { /* If permissions match, then this is a real cache hit, and a qc */ /* miss */ /* By above test cache_tag is valid, so only need to check excl */ /* VASSERT( !VQC_SHARED( state ) || CACHE_VALID( EMP[cpuNum].cache_tag[line_no] ), ("State %d, line_no %d\n", state, line_no) ); */ if( VQC_SHARED( state ) || ( VQC_EXCL( state) && CACHE_EXCL( EMP[cpuNum].cache_tag[line_no] ) ) ) { /* Cache hit */ /* Update qc to present access status. I could do this based on the cache tag value, but why bother? */ set_qc_state( cpuNum, ADDR2SLINE(vAddr), pline, state ); if (interest(pAddr)) { LogEntry("ref_qc",cpuNum,"pAddr=%08x state=%x msstate=%d\n",pAddr,state, MSCacheState(cpuNum,pAddr)); } return; } else { /* Upgrade */ smht_flags |= SMHT_UPGRADE; if (interest(pAddr)) { LogEntry("ref_upg",cpuNum,"pAddr=%08x state=%x msstate=%d\n",pAddr,state, MSCacheState(cpuNum,pAddr)); } /* Remove xfer time */ miss_handling_time += UPGRADE_TIME; goto accountingDone; } } miss_handling_time += MEM_CYCLE_TIME;accountingDone:#ifdef notdef CPUPrint("R %d %-4lld 0x%-8x VA 0x%-8x PA 0x%-8x 0x%-2x\n", cpuNum, EmbraCpuInstrCount(cpuNum), EMP[cpuNum].PC, vAddr,pAddr, state);#endif iCount = EmbraCpuInstrCount(cpuNum); ASSERT( iCount >= cachePrevInstrCount[cpuNum]); cachePrevInstrCount[cpuNum] = iCount;/* * XXX Emmett has this commented out, but told me to execute it. (ed) *//* do we need this ? */#ifdef notdef if( !iCount ) { /* Update vQC and cache tags */ Cache_CommitRef( cpuNum, pAddr, vAddr, 0, state, smht_flags); if( interest(pAddr)) { LogEntry("cc_ref_0",cpuNum,"pAddr=%08x state=%x msstate=%d\n",pAddr,state, MSCacheState(cpuNum,pAddr)); } }#endif if( emSMHT[cpuNum].iCount == iCount ) { /* Commit the previous reference */#ifdef notdef CPUPut("EMBRA SMHT %d %-4lld 0x%-8x VA 0x%-8x PA 0x%-8x 0x%-2x 0x%-8x PA 0x%-8x 0x%-2x\n", cpuNum, emSMHT[cpuNum].iCount, EMP[cpuNum].PC, emSMHT[cpuNum].vAddr, emSMHT[cpuNum].pAddr, emSMHT[cpuNum].state, vAddr, pAddr, state);#endif if( !( VQC_INST( emSMHT[cpuNum].state ) && VQC_DATA(state)) ) { /* ASSERT(!iCount || state == emSMHT[cpuNum].state);*/ if (emSMHT[cpuNum].pAddr != INVALID_TAG) { Cache_CommitRef(cpuNum, emSMHT[cpuNum].pAddr, emSMHT[cpuNum].vAddr, emSMHT[cpuNum].pcPAddr, (EmVQCMemState)emSMHT[cpuNum].state, emSMHT[cpuNum].smht_flags); /* * We used the MHT, now get rid of it (ed) */ if (interest(emSMHT[cpuNum].pAddr)) { LogEntry("cc_ref_1",cpuNum,"pAddr=%08x state=%x msstate=%d\n",pAddr,state, MSCacheState(cpuNum,emSMHT[cpuNum].pAddr)); } emSMHT[cpuNum].pAddr = INVALID_TAG; } if( VQC_DATA(state) ) { if( !(smht_flags & SMHT_BD) ){ if( NOT_LAST_IN_SLINE(EMP[cpuNum].PC) ){ /* not in bd and NLISL means not last on page */ PA pPCAddr = MEMADDR_TO_PHYS(M_FROM_CPU(cpuNum), maPCAddr) + INST_SIZE; uint tag = EMP[cpuNum].cache_tag[SCACHE_INDEXOF(pPCAddr)]; /* if( line_no == SCACHE_INDEXOF(MEMADDR_TO_PHYS(maPCAddr)) ){ */ if( !CACHE_VALID(tag) || CACHE_PLINE(tag) != ADDR2SLINE(pPCAddr) ) { EMP[cpuNum].cycleCountdown -= MEM_CYCLE_TIME; smht_flags = 0; if( MAJOR_OPCODE(*(uint*)(maPCAddr + INST_SIZE)) == sc_op ) smht_flags = SMHT_SC; Cache_CommitRef( cpuNum, pPCAddr, /* dref completed and we are not in bd */ EMP[cpuNum].PC + INST_SIZE, 0, MEM_I_SHARED, smht_flags); emSMHT[cpuNum].iCount++; emSMHT[cpuNum].state = MEM_I_SHARED; if (interest(EMP[cpuNum].PC + INST_SIZE)) { LogEntry("cc_ref_2",cpuNum,"pAddr=%08x state=%x msstate=%d\n", EMP[cpuNum].PC + INST_SIZE ,state, MSCacheState(cpuNum,EMP[cpuNum].PC + INST_SIZE)); } } } } } if (interest(pAddr)) { LogEntry("refret",cpuNum,"pAddr=%08x state=%x msstate=%d\n",pAddr,state, MSCacheState(cpuNum,pAddr)); } return; } } EMP[cpuNum].cycleCountdown -= miss_handling_time; if( (EMP[cpuNum].blockCycleCountdown - miss_handling_time) <= 0 ) { if( !(smht_flags & SMHT_BD) ) { emSMHT[cpuNum].pAddr = pAddr; emSMHT[cpuNum].vAddr = vAddr; emSMHT[cpuNum].pcPAddr = 0; emSMHT[cpuNum].state = state; emSMHT[cpuNum].smht_flags = smht_flags; emSMHT[cpuNum].iCount = iCount; EMP[cpuNum].jumpPC = (uint)continue_run_without_chaining; if (interest(pAddr)) { LogEntry("ref_tc",cpuNum,"pAddr=%08x state=%x msstate=%d\n",pAddr,state, MSCacheState(cpuNum,pAddr)); } ReenterTC_CX(&EMP[cpuNum]); /* NOT REACHED */ } } /* Update vQC and cache tags */ Cache_CommitRef( cpuNum, pAddr, vAddr, 0, state, smht_flags); emSMHT[cpuNum].iCount = iCount; if (interest(pAddr)) { LogEntry("refend",cpuNum,"pAddr=%08x state=%x msstate=%d \n",pAddr,state, MSCacheState(cpuNum,pAddr)); }}#ifdef OLD_VERSION_EBvoid MPinUPCache_Ref_old( int cpuNum, PA pAddr, VA vAddr, EmVQCMemState state ){ uint line_no = SCACHE_INDEXOF( pAddr ); uint pline = ADDR2SLINE(pAddr); VA vpc = CLEAR_BD(EMP[cpuNum].PC); K0A pcK0Addr = non_excepting_tv(cpuNum, CLEAR_BD(EMP[cpuNum].PC)); PA pcPAddr; int smht_flags = 0; int miss_handling_time = 0; int cx_me = 0; /* Otherwise our PC is not mapped! */ ASSUME( pcK0Addr ); if( pcK0Addr ) { if( MAJOR_OPCODE( *(uint*)K0_TO_MEMADDR( M_FROM_CPU(cpuNum), pcK0Addr ) ) == sc_op ) { smht_flags |= SMHT_SC; } pcPAddr = K0_TO_PHYS_REMAP( pcK0Addr, cpuNum ); if( ( !VQC_INST(state) ) && (line_no == SCACHE_INDEXOF( pcPAddr ) ) ) { /* PC and data ref map to same cache line */ smht_flags |= SMHT_PCCONFLICT; } } if( pline == CACHE_PLINE( EMP[cpuNum].cache_tag[line_no] ) && CACHE_VALID( EMP[cpuNum].cache_tag[line_no] ) ) { /* If permissions match, then this is a real cache hit, and a qc */ /* miss */ /* By above test cache_tag is valid, so only need to check excl */ /* VASSERT( !VQC_SHARED( state ) || CACHE_VALID( EMP[cpuNum].cache_tag[line_no] ), ("State %d, line_no %d\n", state, line_no) ); */ if( VQC_SHARED( state ) || ( VQC_EXCL( state) && CACHE_EXCL( EMP[cpuNum].cache_tag[line_no] ) ) ) { /* Cache hit */ /* Update qc to present access status. I could do this based on the cache tag value, but why bother? */ set_qc_state( cpuNum, ADDR2SLINE(vAddr), pline, state ); return; } else { /* Upgrade */ smht_flags |= SMHT_UPGRADE; miss_handling_time += UPGRADE_TIME; goto accountingDone; } } miss_handling_time += MEM_CYCLE_TIME;accountingDone: /* XXX - Lasciate ogni speranza If the cost of handling the miss, plus the rest of the instructions in the block is larger than the current quantum, then we should context switch and let more time pass. There are a couple of exceptions 1. Translator/pc_tc lookup can't deal with delay slot instructions 2. Give priority to sc that can succeed, otherwise livelock 3. Livelock can still occur. Livelock is detected by multiple misses without the instruction count increasing. There are two cases--an intra-cpu conflict or an inter-cpu conflict. Notes: cx - processor context switch ret - return to emitted code, allowing reference to succeed, independent of vQC state I assume that processor timeQuantum < MemCycleTime XXX - I assume direct mapped cache a. Intra-cpu conflict--this is a pc/data conflict. Note: pc/data conflicts in the delay slot or on the last instruction of a cache line are not conflicts. Reality: data miss, then the instr miss on the next instruction (if that is in the same line as the data ref, and we are not in a delay slot). Simulator (format--action, cache simulator response): data miss cx instr miss (guaranteed b/c of cx) part I charge & set QC only if !BD(pc) && NOT_LAST_IN_SLINE(pc) & ret data miss part II no charge, don't set QC & ret Detection: part I data then instr miss part II instr then data miss Problem: If a line branches back to itself, we do not register the miss. This is solved by having lines branch back to their icache check. b. Inter-cpu conflict--e.g. two cpus want the same line, at least one of them exclusively Reality: p1 data miss, p2's data miss, p1 succeeds/advances pc Simulator: p1 i|d shared miss cx p2 data excl miss cx p1 (i|d) shared miss no charge, set QC, ret Detection: last_miss_state == this_miss_state Note, p1's first reference could be data, and its second could be instr (indicating both an intra and inter cpu conflict), in that case, intra-cpu processing is still correct 4. Final wrinkle--Intra-cpu conflicts that are possibly successful sc instructions can't cx the processor Reality: see 3a Simulator: data miss charge data, if !BD(pc) && !NOT_LAST_IN_SLINE charge instr, set QC to instr ret */ iCount = EmbraCpuInstrCount(cpuNum); if( lastInstrCount[cpuNum] != iCount ) { if( (EMP[cpuNum].blockCycleCountdown - miss_handling_time) < 0 && !IN_BD(EMP[cpuNum].PC) ) { if( !(smht_flags & SMHT_SC) || EMP[cpuNum].LLAddr == 0){ cx_me = 1; } else { if( smht_flags & SMHT_PCCONFLICT ) { /* ASSERT( !IN_BD(EMP[cpuNum].PC ); */ if( NOT_LAST_IN_SLINE(EMP[cpuNum].PC) ){ /* This is to catch case 4 */ smht_flags |= SMHT_DOUBLECOUNT; miss_handling_time += MEM_CYCLE_TIME; } } } } } else { if( lastMissState[cpuNum] == state ) { /* Inter-cpu conflict */ miss_handling_time = 0; } else { if( VQC_DATA(lastMissState[cpuNum]) && VQC_INST(state) ) { /* Intra-cpu conflict part I (possibly inter-cpu conflict) */ uint pc = EMP[cpuNum].PC; if( IN_BD(pc) || !NOT_LAST_IN_SLINE(pc) ){ /* Spurious callout--no ireference in reality */ lastMissState[cpuNum] = MEM_INVALID; return; } } else { if( VQC_INST(lastMissState[cpuNum]) && VQC_DATA(state) ) { /* Intra-cpu conflict part II */ /* Spurious callout--no dreference in reality */ lastMissState[cpuNum] = MEM_INVALID; return; } } } } /* If this is a real miss, count it and charge stall time */ if( miss_handling_time ) { uint type = E_L2; if (VQC_SHARED(state)) { type |= E_READ; } else { type |= E_WRITE; } EMP[cpuNum].cycleCountdown -= miss_handling_time; if( smht_flags & SMHT_UPGRADE ) { CACHE_SINC( cpuNum, CURRENT_MODE(&EMP[cpuNum]), upgrades );
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -