📄 cache.c
字号:
/* * Copyright (C) 1996-1998 by the Board of Trustees * of Leland Stanford Junior University. * * This file is part of the SimOS distribution. * See LICENSE file for terms of the license. * *//**************************************************************** * cache.c * * $Author: bosch $ * $Date: 1998/02/10 00:30:09 $ *****************************************************************/#include <bstring.h>#include <sys/types.h>#include <sys/mman.h>#include <unistd.h>#include <stdlib.h>#include "annotations.h"#include "embra.h"#include "cache.h"#include "directory.h"#include "decoder.h"#include "mem_control.h"#include "driver.h"#include "main_run.h"#include "qc.h"#include "translator.h"#include "clock.h"#include "callout.h"#include "stats.h"#include "hw_events.h"#include "simutil.h"#include "addr_layout.h"#ifdef MEM_ANNOTATIONS#include "annotations.h"#include "clock.h"#endif#include "addr_layout.h"#define NOT_LAST_IN_SLINE(_Addr) (((uint)(_Addr)+INST_SIZE) & (SCACHE_LINE_SIZE - 1 ))#define SCACHE_TAG_BASE_ADDR(_cpu) ((PLN*) \(CACHE_TAG_START + ((_cpu) * CACHE_TAG_SIZE) ) ) /* Replacement Hints are no longer implemented, but with Directory_Elimitate they could be *//* NOTE: these can not be used in parallel cache simulation *//*--------------------------------------*//* cache & qc_p/Physarray debug support *//* log all cache misses *//* #define LOG_MISS_ALL *//* log all misses on cacheline xxx, stop at PC, CC=EMP.cycleCount*//* #define LOG_MISS #define STOP_PC 0x60007078 #define STOP_CC 305448 *//* count misses for every cacheline *//* #define DEBUG_PA *//* insert cache consistency checks *//* #define DEBUG_CACHE *//*--------------------------------------*/static void Cache_CommitRef(int cpuNum, PA pAddr, VA vAddr, PA pcPAddr, EmVQCMemState state, int flags);void cache_consistency_check( int cpuNum );/* Data Structures */static SimTime cachePrevInstrCount[SIM_MAXCPUS];#ifdef gone_cleanupstatic int lastMissState[SIM_MAXCPUS];static int64 lastInstrCount[SIM_MAXCPUS];#endifstatic int64 iCount;/* Miss handing table allocated in shared memory. Only need 1 per CPU */EmSMHT *emSMHT;#ifdef DEBUG_PAint *misscount;#endifvoid Cache_Init( int cpuNum ){ int i; if( embra.emode == EMBRA_PAGE ) { return; } /* Clear the cache */ if( embra.sequential ) { int cpu; for( cpu = 0; cpu<TOTAL_CPUS; cpu++) { /* To bootstrap the cache miss strategy MPinUP */ emSMHT[cpu].state = MEM_I_SHARED; emSMHT[cpu].pAddr = INVALID_TAG; if (EMP[cpu].cache_tag ) continue; EMP[cpu].cache_tag = (PLN*) ZALLOC_PERM(CACHE_TAG_SIZE,"EmbraTags");#ifdef DEBUG_PA misscount = (int *) ZALLOC_PERM((sizeof(int)*LINES_PER_CACHE),"EmbraTags");#endif CPUPrint("0x%x D CACHE_TAG_BASE_%d 0x%x\n", EMP[cpu].cache_tag, cpu, (uint)EMP[cpu].cache_tag + CACHE_TAG_SIZE ); for( i = 0; i < LINES_PER_CACHE; i++ ) { EMP[cpu].cache_tag[i] = INVALID_TAG;#ifdef DEBUG_PA misscount[i]=0;#endif } } } else { ASSERT (0); }}/* Called when another processor steals a line *//* or called by EmbraDMAInval on a DMA transfer *//* Pass the directory entry, so we only clobber possible conflicts *//* Invalidate cache then pQC, then vQC because we have the directory lock & therefore avoid race conditions */void Cache_Clobber( int machine, int cpuNum, PA pAddr, Dir_Entry cpu_bits, EmVQCMemState state ){ uint type = E_L2; int i; if (!VQC_EXCL(state) ) { type |= E_DOWNGRADE; } ASSERT (!(cpu_bits>>cpuNum & 0x1)); for(i = FIRST_CPU(machine); i <= LAST_CPU(machine); i++ ) { /* * No transition on invalidating CPU and on downgrades! */ if (i!=cpuNum && !(type&E_DOWNGRADE)) { if (CACHE_PLINE( EMP[i].cache_tag[SCACHE_INDEXOF(pAddr)] ) == ADDR2SLINE( pAddr )) { /* pAddr is in cache */ ASSERT ((cpu_bits>>i) & 0x1); if (CACHE_SHARED(EMP[i].cache_tag[SCACHE_INDEXOF(pAddr)])) { L2_LINE_TRANS_EVENT(i, pAddr,type | E_EXTERNAL | E_FLUSH_CLEAN,0, 0,IS_KUSEG(EMP[i].PC)); } else { L2_LINE_TRANS_EVENT(i, pAddr,type | E_EXTERNAL | E_WRITEBACK,0, 0,IS_KUSEG(EMP[i].PC)); } } else { #if should_work_does_not ASSERT (!((cpu_bits>>i)&0x1));#endif L2_LINE_TRANS_EVENT(i, pAddr,type | E_FAKE_EXTERNAL,0,0, IS_KUSEG(EMP[i].PC)); } } if( (cpu_bits>>i) & 0x1 ) { if ( CACHE_PLINE( EMP[i].cache_tag[SCACHE_INDEXOF(pAddr)] ) == ADDR2SLINE( pAddr ) ){ /* pAddr is in cache */ if( VQC_EXCL(state) ) { EMP[i].cache_tag[SCACHE_INDEXOF(pAddr)] = INVALID_TAG; /* If the intervener is getting exclusive control, then fail the subsequent sc */ if( ADDR2SLINE(EMP[i].LLAddr) == ADDR2SLINE(PHYS_TO_MEMADDR(M_FROM_CPU(i), pAddr)) ) EMP[i].LLAddr = 0; { PA pPCAddr; uint pc = EMP[i].PC; pc = IN_BD(pc)?CLEAR_BD(pc)+INST_SIZE:pc; pPCAddr = K0_TO_PHYS( non_excepting_tv(i,pc) ); if( ADDR2SLINE(pPCAddr) == ADDR2SLINE(pAddr) ) { /* Ballsy */ EMP[i].jumpPC = (uint) continue_run_without_chaining; } } } else { /* Downgrade doesn't invalidate chance of SC suceeding */ /* RG: external line transition to shared. Is downgrade ok if its state is already shared? I'll double check just to make sure */ EMP[i].cache_tag[SCACHE_INDEXOF(pAddr)] = CACHE_SET_SHARED(CACHE_PLINE(EMP[i].cache_tag[SCACHE_INDEXOF(pAddr)])); } qc_clobber( pAddr, i, state ); } } } }/* This is called on a QC miss. It drives all cache state transitions */void UPCache_Ref( int cpuNum, PA pAddr, VA vAddr, EmVQCMemState state ){ uint line_no = SCACHE_INDEXOF( pAddr ); uint pline = ADDR2SLINE(pAddr); K0A pcK0Addr = non_excepting_tv(cpuNum, CLEAR_BD(EMP[cpuNum].PC)); PA pcPAddr; int pcConflict = 0; int miss_handling_time = 0; uint type = E_L2; if (VQC_SHARED(state)) { type |= E_READ; } else { type |= E_WRITE; } ASSERT( NUM_CPUS(M_FROM_CPU(cpuNum)) == 1 ); /* Otherwise our PC is not mapped! */ ASSUME( pcK0Addr ); if( pcK0Addr ) { /* We have to take action on a conflict if our PC is mapped data reference and our PC map to the same line */ pcPAddr = K0_TO_PHYS_REMAP( pcK0Addr, cpuNum ); pcConflict = ( !VQC_INST(state) ) && (line_no == SCACHE_INDEXOF( pcPAddr ) ); }#ifdef DEBUG_CACHE static int miss_count; miss_count++; if( miss_count > 100000 && (miss_count % 10000) == 0 ) { cache_consistency_check( cpuNum ); }#endif /*******************/#ifdef COMMENTOUT CPUPrint("Cache Entry PC 0x%x st 0x%x vAddr 0x%x pAddr 0x%x pline 0x%x ln %d ctag 0x%x\n", EMP[cpuNum].PC, state, vAddr, pAddr, pline, line_no EMP[cpuNum].cache_tag[line_no] );#endif if( pline == CACHE_PLINE( EMP[cpuNum].cache_tag[line_no] ) && CACHE_VALID( EMP[cpuNum].cache_tag[line_no] ) ) { /* If permissions match, then its a cache hit, and vQC 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 0x%x, line_no %d pline 0x%x\n", state, line_no, pline) ); if( VQC_SHARED( state ) || ( VQC_EXCL( state) && CACHE_EXCL( EMP[cpuNum].cache_tag[line_no] ) ) ) { /* real cache hit, vQC miss */ set_qc_state( cpuNum, ADDR2SLINE(vAddr), pline, state ); return; } else { /* Upgrade--Free on a uniprocessor */ /* RG upgrading from shared to exclusive. For a UP, this is simply a transition.... */ CACHE_SINC( cpuNum, CURRENT_MODE(EMP), upgrades ); miss_handling_time += 0;#ifdef LOG_MISS if (line_no == LOG_MISS){ CPUPrint("Cache MISS-upgrade on %d , PC = %x, cycleCount = %lld\n",LOG_MISS,EMP[cpuNum].PC,EMP[cpuNum].cycleCount); }#endif goto accountingDone; } } miss_handling_time += MEM_CYCLE_TIME; ASSERT (MEM_CYCLE_TIME); CACHE_INC( cpuNum, CURRENT_MODE(&EMP[cpuNum]), i_miss, d_miss, state );#ifdef LOG_MISS if (line_no == LOG_MISS) CPUPrint("Cache MISS on %d , PC = %x, cycleCount = %lld\n",LOG_MISS,EMP[cpuNum].PC,EMP[cpuNum].cycleCount); if ((EMP[cpuNum].PC==STOP_PC)&&(EmbraCpuCycleCount(cpuNum)==STOP_CC)) ASSERT(0);#endif { #ifdef DEBUG_PA misscount[line_no]++;#endif#ifdef LOG_MISS_ALL CPUPrint("Cache MISS on %x , PC = %x, cycleCount = %lld\n",line_no,EMP[cpuNum].PC,EmbraCpuCycleCount(cpuNum)); if ((EMP[cpuNum].PC==STOP_PC)&&(EmbraCpuCycleCount(cpuNum)==STOP_CC)) ASSERT(0);#endif /* RG Record the miss after the transition */ if( CACHE_VALID( EMP[cpuNum].cache_tag[line_no] ) ) { /* RG: The current entry is booted, regardless of if it's correct or not. */ uint type = E_L2; uint oldAddr; if (CACHE_EXCL( EMP[cpuNum].cache_tag[line_no])) { type |= E_WRITEBACK; } else { type |= E_FLUSH_CLEAN; } /* Kick out previous line. Need to do a transition here */ oldAddr = SLINE2ADDR(CACHE_PLINE(EMP[cpuNum].cache_tag[line_no])); L2_LINE_TRANS_EVENT(cpuNum,oldAddr,type, vAddr, 0, IS_KUSEG(EMP[cpuNum].PC)); } if (VQC_INST(state)) { L2_IMISS_EVENT( EmbraCpuCycleCount(cpuNum), cpuNum, vAddr, pAddr, miss_handling_time, type | E_I); } else { VA pc = CLEAR_BD(EMP[cpuNum].PC); L2_DMISS_EVENT( EmbraCpuCycleCount(cpuNum), cpuNum, pc, vAddr, pAddr, miss_handling_time, type | E_D, 0); } }accountingDone: /* If there is a previous entry in this line, kick it out */ if( CACHE_VALID( EMP[cpuNum].cache_tag[line_no] ) ) { /* This could be an upgrade which means we are kicking out the shared entry, but that is just redundant work, not incorrect */ if (embra.useVQC){ set_qc_state( cpuNum, PQC_VLINE( EMP[cpuNum].qc_p[ CACHE_PLINE( EMP[cpuNum].cache_tag[line_no])]), CACHE_PLINE( EMP[cpuNum].cache_tag[line_no] ), MEM_INVALID ); } else { /* !embra.useVQC */ set_qc_state( cpuNum,0, CACHE_PLINE( EMP[cpuNum].cache_tag[line_no] ), MEM_INVALID ); } } /* Set the qc entry and the cache tags. */ /* If we had a conflict then set the qc and tags to the I entry */ /* XXX - This means that UP MUST return the */ /* address so the QC does not rewind (that will infinite loop) */ if( pcConflict && !IN_BD( EMP[cpuNum].PC ) && NOT_LAST_IN_SLINE( EMP[cpuNum].PC ) ) { /* We have detected a conflict. Between the PC and the miss address. */ /* What should happen here is 1. PC line is present in cache 2. l/s is executed, cache miss and PC line evicted 3. l/s completes even though PC line is not in cache 4. If l/s in delay slot then l/s owns line, else Imiss, and PC owns line However, we don't emit an icache check after every l/s for performance. Therefore, just count the I miss, and assign the qc to the proper party */ /* Data miss was charged earlier, charge I miss */ /* Going with what emmett says--charge I miss now. Use original miss_handling time--don't charge twice. */ /* Transition to evict vaddr (data), IMiss needs to bring in next pc +4.. */ if ( VQC_EXCL(state)) { type = E_L2 | E_WRITEBACK; } else { type = E_L2 | E_FLUSH_CLEAN; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -