📄 scache.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. * *//***************************************************************** * scache.c * Secondary cache model * * $Author: bosch $ * $Date: 1998/02/10 00:28:04 $ *****************************************************************/#include <assert.h>#include <malloc.h>#include "syslimits.h"#include "simutil.h"#include "sim_error.h"#include "scache.h"#include "memsys.h"#include "cpu_interface.h"#include "hw_events.h"#include "cpu_stats.h"#ifdef HWBCOPY# include "hw_bcopy.h"#endif#include "arch_specifics.h"/* #define CC_CHECKER */#ifdef CC_CHECKERstatic int CacheChecker(PA paddr, int cpuNum, int isExcl);#endif#if defined(SIM_MIPS32) || defined(SIM_MIPS64)extern int sim_magic_OSPC_access(int cpuNum, uint VA, byte *data);extern void sim_magic_OSPC_stalled(int cpuNum, uint VA, int stalled);/*#define DEBUG_OSPC_STALL*/#endif/* * Possible return status from the miss handling table. * SMHTSUCCESS - Entry allocated everything looks good. * SMHTMERGE - This request merged with another. Returned other. * SMHTFULL - Entry not allocated because table was full. * SMHTCONFLICT - Entry not allocated because it conflicted with one already * allocated. */typedef enum { SMHTSUCCESS = 0, SMHTFULL, SMHTMERGE, SMHTCONFLICT} SMHTStatus;/* The second level caches for all of the processors */SCache *SCACHE;/* Local Functions */static void SCacheHitEvent(int cpuNum,EventCallbackHdr *event, void *arg);static SMHTStatus AllocSMHT(int cpuNum, MCMD mcmd, PA pAddr, int lru, int *smhtind);static void FreeSMHT(int cpuNum, int entryNum);static void SCacheUpdate(int cpuNum, SMHT *smht, int mode, int status, int result, byte *data);static FlushStatus SCacheFlush(int cpuNum, int writeback, int retain, PA pAddr, int size, byte *data);static SCResult SCacheUpgradeMiss(int cpuNum, VA vAddr, PA pAddr, SCacheCmd cmd, int mhtind, int way, struct SCacheSet *set, SimCounter *missCntPtr);static SCResult SCacheMiss(int cpuNum, VA vAddr, PA pAddr, SCacheCmd cmd, int mhtind, struct SCacheSet* set, SimCounter *missCntPtr);static void SCacheLRUInit(uint *lruword);static void SCacheLRUMake(uint *lruword, int set);static int SCacheLRU(uint lruword);static void SCacheLRUTouch(uint *lruword,int set);#ifdef DATA_HANDLINGstatic void DumpCacheLine(byte *data, int length);#endif/* Some shared functions for cache manipulation */int SameCacheLine(PA addr1, PA addr2, unsigned cacheLineSize);PA QuadWordAlign(PA offset);#define CURRENT_PC(_cpu) (CPUVec.CurrentPC(_cpu))/***************************************************************** * InitSCaches *****************************************************************/voidInitSCaches(void){ int i,j,k;#ifdef DATA_HANDLING char *pool;#endif SCACHE = (SCache *)calloc(TOTAL_CPUS,sizeof(SCache)); CPUPrint("SCACHE: Assoc = %d Size = %#x Line = %d HitTime %d\n", SCACHE_ASSOC, SCACHE_SIZE, SCACHE_LINE_SIZE, SCACHE_HIT_TIME); for (j=0; j < TOTAL_CPUS; j++) { SCACHE[j].set = (struct SCacheSet *) calloc(SCACHE_INDEX, sizeof(struct SCacheSet)); if (SCACHE[j].set == NULL) { CPUError("calloc failed on scache allocation (%d bytes)\n", SCACHE_INDEX*sizeof(struct SCacheSet)); }#ifdef DATA_HANDLING pool = (char *) calloc(SCACHE_LINE_SIZE * SCACHE_INDEX * SCACHE_ASSOC, sizeof(char));#endif for (i=0; i < SCACHE_INDEX; i++) { for (k=0; k < SCACHE_ASSOC; k++) { SCACHE[j].set[i].tags[k] = INVALID_TAG;#ifdef DATA_HANDLING { if (pool == NULL) { CPUError("calloc failed on scache pool allocation\n"); } /* Allocate the actual memory for the cache line's data */ SCACHE[j].set[i].data[k] = pool; pool += SCACHE_LINE_SIZE; }#endif } SCacheLRUInit(&SCACHE[j].set[i].LRU); } for (i=0; i < SMHT_SIZE; i++) { SCACHE[j].SMHT[i].inuse = FALSE; } SCACHE[j].SMHTnumInuse = 0; }}/***************************************************************** * SCacheFetch * * Process a first level cache miss. *****************************************************************/SCResultSCacheFetch(int cpuNum, VA vAddr, PA pAddr, SCacheCmd cmd, int mhtind){ struct SCacheSet* set; SimCounter *missCntPtr; int way; int cacheNum = GET_CACHE_NUM(cpuNum); int scacheNum = GET_SCACHE_NUM(cpuNum); uint ind = SCACHE_INDEXOF(pAddr); PA tag = SCACHE_TAG(pAddr); if (interest(pAddr)) { LogEntry("fetch",cpuNum,"pA=0%08x state=%d tag=0x%x foo=0x%x\n",pAddr, MSCacheState(cpuNum, pAddr),tag,SCACHE[scacheNum].set[ind].tags[0]); } switch (cmd) { case SC_IGET: SCACHE[scacheNum].stats.Igets++; missCntPtr = &SCACHE[scacheNum].stats.IgetMisses; break; case SC_DGET: case SC_DLLGET: SCACHE[scacheNum].stats.Dgets++; missCntPtr = &SCACHE[scacheNum].stats.DgetMisses;#ifdef HWBCOPY if ((SimConfigGetBool("Hwbcopy.Streaming")) && (SCACHE[scacheNum].get.last_index + 1) == ind) { int run = ++SCACHE[scacheNum].get.consecutive; int range = SimConfigGetInt("Hwbcopy.StreamRange"); int depth = SimConfigGetInt("Hwbcopy.StreamDepth"); int i; for (i = 1; i < depth; i++) { if ((run >= range*i) && (run <= range*(i+1))) { PA pfAddr = pAddr + (i * SCACHE_LINE_SIZE); VA vfAddr = vAddr + (i * SCACHE_LINE_SIZE); if (IS_VALID_PA(M_FROM_CPU(cpuNum), pfAddr)) { SCACHE[scacheNum].stats.StreamGets[i]++; SCachePrefetch(cpuNum, vfAddr, pfAddr, MEMSYS_GET); } } } if (run >= range*depth) { PA pfAddr = pAddr + (depth * SCACHE_LINE_SIZE); VA vfAddr = vAddr + (depth * SCACHE_LINE_SIZE); if (IS_VALID_PA(M_FROM_CPU(cpuNum), pfAddr)) { SCACHE[scacheNum].stats.StreamGets[depth]++; SCachePrefetch(cpuNum, vfAddr, pfAddr, MEMSYS_GET); } } } else if ((SCACHE[scacheNum].get.last_index) != ind) SCACHE[scacheNum].get.consecutive = 0; SCACHE[scacheNum].get.last_index = ind;#endif break; case SC_DGETX: case SC_DLLGETX: SCACHE[scacheNum].stats.DgetXs++; missCntPtr = &SCACHE[scacheNum].stats.DgetXMisses;#ifdef HWBCOPY if ((SimConfigGetBool("Hwbcopy.Streaming")) && (SCACHE[scacheNum].getx.last_index + 1) == ind) { int run = ++SCACHE[scacheNum].getx.consecutive; int range = SimConfigGetInt("Hwbcopy.StreamRange"); int depth = SimConfigGetInt("Hwbcopy.StreamDepth"); int i; for (i = 1; i < depth; i++) { if ((run >= range*i) && (run <= range*(i+1))) { PA pfAddr = pAddr + (i * SCACHE_LINE_SIZE); VA vfAddr = vAddr + (i * SCACHE_LINE_SIZE); if (IS_VALID_PA(M_FROM_CPU(cpuNum), pfAddr)) { SCACHE[scacheNum].stats.StreamGetXs[i]++; SCachePrefetch(cpuNum, vfAddr, pfAddr, MEMSYS_GETX); } } } if (run >= range*depth) { PA pfAddr = pAddr + (depth * SCACHE_LINE_SIZE); VA vfAddr = vAddr + (depth * SCACHE_LINE_SIZE); if (IS_VALID_PA(M_FROM_CPU(cpuNum), pfAddr)) { SCACHE[scacheNum].stats.StreamGetXs[depth]++; SCachePrefetch(cpuNum, vfAddr, pfAddr, MEMSYS_GETX); } } } else if ((SCACHE[scacheNum].getx.last_index) != ind) SCACHE[scacheNum].getx.consecutive = 0; SCACHE[scacheNum].getx.last_index = ind;#endif break; case SC_DUGETX: case SC_DSCUGETX: SCACHE[scacheNum].stats.Dupgrades++; missCntPtr = &SCACHE[scacheNum].stats.DupgradeMisses; break; default: CPUError("Unknown SC command 0x%x\n", cmd); missCntPtr = 0; break; }/* pAddr = pAddr & ~(PA)(SCACHE_LINE_SIZE-1);*/ set = & SCACHE[scacheNum].set[ind]; for( way=0;way<SCACHE_ASSOC;++way ) { if((set->tags[way] & ~EXCLUSIVE_TAG) == tag) { break; } } if (way==SCACHE_ASSOC) { /* A miss */ return SCacheMiss(cpuNum, vAddr, pAddr, cmd, mhtind, set, missCntPtr); } if ((cmd & EXCLUSIVE_CMD) && !(set->tags[way] & EXCLUSIVE_TAG)) { if ((cmd == SC_DGETX) || (cmd == SC_DLLGETX)) { /* A first level getx which requires an upgrade in the scache -- * * change the statistics to record this as an upgrade, not a getx */ SCACHE[scacheNum].stats.DgetXs--; SCACHE[scacheNum].stats.Dupgrades++; missCntPtr = &SCACHE[scacheNum].stats.DupgradeMisses; } return SCacheUpgradeMiss(cpuNum, vAddr, pAddr, cmd, mhtind, way, set, missCntPtr); } if (VERBOSE_DEBUG) { CPUPrint("%d: SCacheFetch: hit for %8.8lx\n",cpuNum,pAddr); } /* A hit in the second level, update LRU and PUT the line in the first * level cache. */ SCacheLRUTouch(&SCACHE[scacheNum].set[ind].LRU, way); /* * Return the line to the first level after the specified delay. */ CACHE[cacheNum].MHT[mhtind].smhtEntry = SECOND_LEVEL_HIT_ENTRY; if (set->tags[way] & EXCLUSIVE_TAG) { CACHE[cacheNum].MHT[mhtind].mode = MEMSYS_EXCLUSIVE; } else { CACHE[cacheNum].MHT[mhtind].mode = MEMSYS_SHARED; } if (memsysVec.NoMemoryDelay || SCACHE_HIT_TIME == 0 || IN_MAGIC_SECTION(cpuNum)) { MHTReqDone(cpuNum, CACHE[cacheNum].MHT + mhtind, CACHE[cacheNum].MHT[mhtind].mode, MEMSYS_STATUS_SUCCESS); return SCSUCCESS; } else { EventDoCallback(cpuNum, SCacheHitEvent, (EventCallbackHdr *)(CACHE[cacheNum].MHT + mhtind), (void *)NULL, SCACHE_HIT_TIME); return SCSTALL; }} /***************************************************************** * SCacheMiss * 2nd level miss, forward to memory system. Start by allocating * space in the SMHT. *****************************************************************/SCResultSCacheMiss(int cpuNum, VA vAddr, PA pAddr, SCacheCmd cmd, int mhtind, struct SCacheSet* set, SimCounter *missCntPtr){ int cacheNum = GET_CACHE_NUM(cpuNum); int scacheNum = GET_SCACHE_NUM(cpuNum); PA replacePaddr; int writeback; MCMD mcmd; int way; int replace; int waiter; SMHTStatus ret; Result mret; int smhtind; /* Quad-word align the miss */ pAddr = QuadWordAlign(pAddr);#ifndef SOLO ASSERT (IS_VALID_PA(M_FROM_CPU(cpuNum), pAddr));#endif /* Check if there are any lines marked INVALID and replace them * rather than the LRU line */ for (replace=0; replace<SCACHE_ASSOC; ++replace) { if (set->tags[replace] & INVALID_TAG ) { break; } } if (replace == SCACHE_ASSOC) { way = SCacheLRU(set->LRU); } else { way = replace; } mcmd = (cmd & EXCLUSIVE_CMD) ? MEMSYS_GETX : MEMSYS_GET; if ((cmd == SC_DLLGET) || (cmd == SC_DLLGETX)) { mcmd |= MEMSYS_LLFLAVOR; } if (cmd == SC_IGET) { mcmd |= MEMSYS_IFFLAVOR; } ret = AllocSMHT(cpuNum, mcmd, pAddr, way, &smhtind); if ((ret == SMHTFULL) || (ret == SMHTCONFLICT)) { /*We've overrun or conflicted in the SMHT. Return * a retry so we will try again. */ if (VERBOSE_DEBUG) { CPUPrint("%d: SCacheMiss: VA: %8.8lx PA: %8.8lx RETRY\n", cpuNum, vAddr, pAddr); } /* NOTE: I've already counted this reference as a L2 Ref. To make the stats work, I need a count of these gets that have to retry since the retry doesn't show up as an L1 Miss. */ switch (cmd) { case SC_IGET: SCACHE[scacheNum].stats.IGetMHTRetries++; break; case SC_DGET: case SC_DLLGET: SCACHE[scacheNum].stats.DGetMHTRetries++; break; case SC_DGETX: case SC_DLLGETX: SCACHE[scacheNum].stats.DGetXMHTRetries++; break; case SC_DUGETX: case SC_DSCUGETX: SCACHE[scacheNum].stats.DUGetXMHTRetries++; break; default: CPUWarning("Bad cmd found at SMHT conflict\n"); ASSERT(0); } return SCRETRY; } ASSERT((ret == SMHTSUCCESS) || (ret == SMHTMERGE)); /* * Add the MHT entry to the lists of entries waiting * for the line to be filled or upgraded. */ ASSERT(SCACHE[scacheNum].SMHT[smhtind].numMHTwait < MHT_SIZE); waiter = SCACHE[scacheNum].SMHT[smhtind].numMHTwait++; SCACHE[scacheNum].SMHT[smhtind].mhtInd[waiter] = mhtind; SCACHE[scacheNum].SMHT[smhtind].vAddr = vAddr; CACHE[cacheNum].MHT[mhtind].smhtEntry = smhtind; if (ret == SMHTMERGE) { /* * This line already has an outstanding miss of the right * type. Merge into it. */ if (VERBOSE_DEBUG) { CPUPrint("%d: SCacheMiss: VA: %8.8lx PA: %8.8lx MERGE\n", cpuNum, vAddr, pAddr); } SCACHE[scacheNum].stats.MergeMisses++; return SCSTALL; } /* Now we need to allocate clear out space in the cache * for it. If the entry we choose is valid we flush * it from the primary cache(s). We pass the replaced * address to the memory system so it can simulated writebacks * and replacement hints, etc. */ if (VERBOSE_DEBUG) { CPUPrint("%d: SCacheMiss: VA: %8.8lx PA: %8.8lx\n",cpuNum, vAddr, pAddr); } if (!(set->tags[way] & INVALID_TAG)) { replacePaddr = STAG_TO_PA(set->tags[way],set-SCACHE[scacheNum].set); writeback = ((set->tags[way] & EXCLUSIVE_TAG) != 0);#ifndef SOLO ASSERT (IS_VALID_PA(M_FROM_CPU(cpuNum), replacePaddr));
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -