// ========== Copyright Header Begin ========================================== // // OpenSPARC T2 Processor File: ccxDevMemBFM.vr // Copyright (C) 1995-2007 Sun Microsystems, Inc. All Rights Reserved // 4150 Network Circle, Santa Clara, California 95054, U.S.A. // // * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; version 2 of the License. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // For the avoidance of doubt, and except that if any non-GPL license // choice is available it will apply instead, Sun elects to use only // the General Public License version 2 (GPLv2) at this time for any // software where a choice of GPL license versions is made // available with the language indicating that GPLv2 or any later version // may be used, or where a choice of which version of the GPL is applied is // otherwise unspecified. // // Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, // CA 95054 USA or visit www.sun.com if you need additional information or // have any questions. // // ========== Copyright Header End ============================================ #include // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! // To use this class, you must have in your bench a files called globals.vri // that has all global extern declerations in it. #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! // To use this class, you must have in your bench a class that extends // sparcBenchUtils.vr. It must be in files named utilsClass.vr/utilsClass.vrh // AND have a global handle called gUtil. #include // uncomment to debug //#define CCXDEVMEMBFM_DEBUG // #define MON_CCXPKT 24 // #define GNT_ATTEMPTS 20 #define CLASSNAME CcxDevMemBFM #define CLASSNAMEQ "CcxDevMemBFM" class CLASSNAME extends CcxDevBaseBFM { local reg [63:0] cas1Data[64], cas1Addr[64]; local reg cas_swap [64]; // local reg [31:0] invVectorCAS; local reg cacheOff = 0; local integer reqId; local integer ldstSyncLock; local integer stalling; static reg burstSync; // methods task new(integer instatnce, reg passiveIn=0, reg cacheoff = 0, reg flagUnexpected=0, reg ccxOnly=0); // virtual in base task recv(BasePkt pktHndl); task cancelRecv(BasePkt pktHndl); local task slave(); local task respond(PcxPkt reqPkt); // local task monitor(); local function reg [3:0] lineState(PcxPkt reqPkt, string why="debug lineState:", reg quiet=1); task sendIntr(reg [5:0] tid, reg [1:0] type, reg [5:0] vect); local task updateItag(PcxPkt reqPkt, CpxPkt rspPkt, CpxPkt rspPkt2 = null, reg [2:0] cpuId, integer how = TAG_VAL); local task updateDtag(PcxPkt reqPkt, CpxPkt rspPkt, reg [2:0] cpuId, integer how = TAG_VAL); local function reg data_equal(reg [63:0] data1, reg [63:0] data2, reg[7:0] size); // local task create_vector(reg table, reg [3:0] way, // var reg [31:0] vect, reg [2:0] core_num); local function reg [31:0] getInvalVector(integer type, PcxPkt reqPkt, reg [2:0] cpuId); local task ldstSync(reg [2:0] cpuId, CpxPkt rspPkt); public task enqueueEvict(reg [7:0] coreEnable, reg [39:0] evictPA = 40'hffffffffff, reg [3:0] cid = 4'hf, reg all_cores = 0, integer dCacheWeight = 60); local task burstResp(integer amount); } task CLASSNAME::new(integer instatnce, reg passiveIn=0, reg cacheoff = 0, reg flagUnexpected=0, reg ccxOnly=0) { super.new(instatnce, passiveIn, CLASSNAMEQ); super.ccxOnly = ccxOnly; // only testing ccx super.flagUnexpected = flagUnexpected; srandom(gSeed,this); cacheOff = cacheoff; stalling = 0; reqId = 0; burstSync = 0; ldstSyncLock = alloc( SEMAPHORE, 0, 1, 1 ); // this may not be needed anymore // in base super.stallStart = gParam.stallStart; super.stallStop = gParam.stallStop; if (myPort == DEV_NCU) lineHash[0] = 0; if (!passiveIn) { // Initialize Outputs gPcxPort[myPort].$stall <= 0; gCpxPort[myPort].$req <= 0; // gCpxPort[myPort].$datao <= 0; if (myPort !== DEV_NCU) gCpxPort[myPort].$atmo <= 0; } fork { @(posedge gPcxPort[myPort].$clk); gCpxPort[myPort].$gnt == 0; gPcxPort[myPort].$rdy == 0; gPcxPort[myPort].$datai == 0; if (myPort !== DEV_NCU) gPcxPort[myPort].$atmi == 0; } join none fork slave(); join none // service mailboxes, send packets fork super.serviceSends2(PP_CPX); join none if (gParam.burstAmount) { fork burstResp(gParam.burstAmount); join none } } // Wait for data from PCX, we are a cache/IO BFM task CLASSNAME::slave() { ccxPort portVar = gPcxPort[myPort]; reg [145:0] tmpVec; if (!passive) { fork { // wait for packets. // we can get back to back packets... while (1) { if (!portVar.$rdy) { @(portVar.$rdy); } if (portVar.$rdy && stalling) { error("ERROR FAIL: should not get rdy when stalled\n"); } { // keep block! {} PcxPkt reqPkt = new(); // need to fork to handle back to back reqs // and delayed responses fork { outstandingReqs++; reqPkt.ccxSourced = 1; @(negedge portVar.$clk); reqPkt.loadPkt(portVar.$datai, myPort); // if on L2 port, look at portVar.$atmi and save value if (myPort !== DEV_NCU) reqPkt.atm_wire = portVar.$atmi; #ifdef CCXDEVMEMBFM_DEBUG printf("%0d: CcxDevMemBFM[%2d]::slave: got req packet, outstandingReqs++=%0d, atm=%0d, ccxSourced=%0d, vec=%h\n",get_time(LO),myPort,outstandingReqs,reqPkt.atm_wire,reqPkt.ccxSourced,reqPkt.getVector()); #endif // for CCX testing, anyone waiting for this packet? // was it expected? should we auto respond? if (ccxOnly) { tmpVec = reqPkt.makeSignature(); if (assoc_index(CHECK, expectedSig, tmpVec)) { expectedSig[tmpVec].loadPkt(reqPkt.getVector(), myPort); expectedSig[tmpVec].arrivalTime = get_cycle(); expectedSig[tmpVec].pktArrived = ~expectedSig[tmpVec].pktArrived; } else if (flagUnexpected) { reqPkt.print(myPort); PR_ERROR(CLASSNAMEQ, MON_ERROR, psprintf ("Unexpected packet on port %0d, vector=%h", myPort,reqPkt.getVector())); } } else { // check how/if we should respond and do it suspend_thread(); // do as last thing in this time slot. if (!ccxOnly && reqPkt.valid) { fork respond(reqPkt); join none } else { // drop this packet outstandingReqs--; printf("%0d: CcxDevMemBFM[%2d]::slave: got invalid req packet, outstandingReqs++=%0d, atm=%0d, ccxSourced=%0d, vec=%h\n",get_time(LO),myPort,outstandingReqs,reqPkt.atm_wire,reqPkt.ccxSourced,reqPkt.getVector()); reqPkt.print(myPort); reqPkt = null; } } } join none } @(negedge portVar.$clk); } // while } { // stall signal management while (1) { wait_var(outstandingReqs); //printf("%0d: wait_var at stall check outstandingReqs=%0d\n", get_time(LO), outstandingReqs); // stall? Takes 3 clocks for the CCX to actually stall if (outstandingReqs >= stallStart && !stalling) { fork { portVar.$stall = 1; //printf("%0d: CcxDevMemBFM[%2d]::stalling, outstandingReqs=%0d\n", get_time(LO),myPort, outstandingReqs); repeat (2) @(negedge portVar.$clk); stalling = 1; } join none } if (outstandingReqs <= stallStop && stalling) { fork { portVar.$stall = 0; @(negedge portVar.$clk); stalling = 0; //printf("%0d: CcxDevMemBFM[%2d]::un-stalling, outstandingReqs=%0d\n", get_time(LO),myPort, outstandingReqs); @(negedge portVar.$clk); stalling = 0; // yes, twice, in case we started stalling above //printf("%0d: CcxDevMemBFM[%2d]::done un-stalling, outstandingReqs=%0d\n", get_time(LO),myPort, outstandingReqs); } join none } } } join all } // ! passive } task CLASSNAME::sendIntr(reg [5:0] tid, reg [1:0] type, reg [5:0] vect) { CpxPkt reqPkt; reqPkt = new(); reqPkt.createIntr(tid,type,vect); // INTR_RESET,INTR_POR reqPkt.sendPorts = 1 << myPort; reqPkt.targetPorts = 1 << tid[5:3]; // not multicast reqPkt.send(1); } // check how/if we should respond and do it. // check response signature and use user response if hit (review) // else return a built in response. task CLASSNAME::respond(PcxPkt reqPkt) { CpxPkt rspPkt, rspPkt2, rspPkt3; reg [7:0] cas1size, targetCores; reg [127:0] tmpData; reg [31:0] invVector = 0; reg [31:0] tmpInvVect = 0; reg [1:0] invField = 0; reg [63:0] tmp64; integer i; reg [5:0] thread; ccxPort portVar = gCpxPort[myPort]; reg [63:0] tmpAddr; reg [39:0] tmpPa; // do not drive on this port if (passive) return; thread = {reqPkt.cpuId,reqPkt.tid}; // check response signature & use user pkt if hit // else return built in response pkt based on request pkt fields. // use fast resp mailbox. // Some may be illegal for L2 port, check port number and address! if (myPort !== DEV_NCU && reqPkt.addr[39] == 1 && (reqPkt.addr[39:32] < 8'hA0 || reqPkt.addr[39:32] > 8'hBF)) { reqPkt.print(myPort); PR_ERROR (CLASSNAMEQ, MON_ERR, psprintf ("T%d Port=%0d request type [128:124]=%b. ERROR FAIL: Illegal I/O on non-NCU port!",thread,myPort,reqPkt.rqtyp)); return; } // create response packets rspPkt = new(reqPkt); rspPkt.sendPorts = 1 << myPort; rspPkt.addr = reqPkt.addr; #ifdef CCXDEVMEMBFM_DEBUG reqPkt.reqId = reqId; rspPkt.reqId = reqId; reqId++; if (reqId == 10000) reqId = 0; reqPkt.reqTime = get_time(LO); rspPkt.reqTime = reqPkt.reqTime; // state of tag for line at this time reqPkt.lineWay = lineState(reqPkt, *, 1); reqPkt.print(myPort); #endif // pick a response case (reqPkt.rqtyp) { PCX_LD, PCX_PREF, PCX_PREF_ICE, PCX_DIAG_LD, PCX_D_INVAL: { case ({reqPkt.inv,reqPkt.pf}) { 0: { if (myPort !== DEV_NCU) { // can't tell diag load from load so both are load! rspPkt.rtntyp = CPX_LD; // code is done rspPkt.rtntypU = U_CPX_LD; } else { // can't tell diag load from load so both are load! rspPkt.rtntyp = CPX_NCU_LD; // code is done rspPkt.rtntypU = U_CPX_NCU_LD; } } 1: { if (reqPkt.nc) { rspPkt.rtntyp = CPX_PREF; rspPkt.rtntypU = U_CPX_PREF; } else { error(""); } } 2: { if (!reqPkt.nc) { rspPkt.rtntyp = CPX_D_INVAL; rspPkt.rtntypU = U_CPX_D_INVAL; invField = D_INVAL; } else { error(""); } } 3: { if (reqPkt.nc) { rspPkt.rtntyp = CPX_PREF_ICE; rspPkt.rtntypU = U_CPX_PREF_ICE; } else { error(""); } } } // case } // case PCX_ST, PCX_BLK_ST, PCX_BLK_INIT_ST, PCX_DIAG_ST : { if (reqPkt.l1wayBis && !reqPkt.pf) { rspPkt.rtntyp = CPX_ST; // code is done rspPkt.rtntypU = U_CPX_BIS; } else if (reqPkt.l1wayBis && reqPkt.pf) { rspPkt.rtntyp = CPX_ST; // code is done rspPkt.rtntypU = U_CPX_BLK_ST; } else { // can't tell diag store from store so both are store! rspPkt.rtntyp = CPX_ST; // code is done rspPkt.rtntypU = U_CPX_ST; } } PCX_CAS1: { rspPkt.rtntyp = CPX_CAS_RTN; rspPkt.rtntypU = U_CPX_CAS_RTN; } PCX_CAS2: { rspPkt.rtntyp = CPX_CAS_ACK; rspPkt.rtntypU = U_CPX_CAS_ACK; } PCX_STR_LD: { rspPkt.rtntyp = CPX_STR_LD; // code is done rspPkt.rtntypU = U_CPX_STR_LD; } PCX_STR_ST: { rspPkt.rtntyp = CPX_STR_ST; // code is done rspPkt.rtntypU = U_CPX_STR_ST; } PCX_SWAP: { rspPkt.rtntyp = CPX_SWAP_RTN; // will do ack as well. rspPkt.rtntypU = U_CPX_SWAP_RTN; // will do U_CPX_SWAP_ACK as well. } PCX_MMU_LD: { rspPkt.rtntyp = CPX_MMU_RTN; // code is done rspPkt.rtntypU = U_CPX_MMU_RTN; } PCX_IFILL: { if (reqPkt.inv == 0) { if (myPort !== DEV_NCU) { rspPkt.rtntyp = CPX_IFILL; // code is done rspPkt.rtntypU = U_CPX_IFILL; } else { rspPkt.rtntyp = CPX_NCU_IFILL; // code is done rspPkt.rtntypU = U_CPX_NCU_IFILL; } } else { rspPkt.rtntyp = CPX_I_INVAL; rspPkt.rtntypU = U_CPX_I_INVAL; invField = I_INVAL; } } default : { reqPkt.print(myPort); PR_ERROR (CLASSNAMEQ, MON_ERR, psprintf ("T%d Port=%0d request type [128:124]=%b. ERROR FAIL: Unsupported rqtyp",thread,myPort,reqPkt.rqtyp)); reqPkt = null; rspPkt2 = null; } } // case // finalize random values for response times if (myPort == DEV_NCU) void = rspPkt.randomize() with {hit == 1;}; else void = rspPkt.randomize(); // do the response case (rspPkt.rtntypU) { U_CPX_LD, U_CPX_DIAG_LD, U_CPX_STR_LD, U_CPX_MMU_RTN, U_CPX_PREF, U_CPX_PREF_ICE, U_CPX_NCU_LD: { integer i; repeat (ordering(rspPkt, "LD RTN")) @(posedge portVar.$clk); // get semaphore for this clock semaphore_get(WAIT, ldstSyncLock, 1); if (reqPkt.addr[39] && myPort == DEV_NCU) { // is it a special address for us? // if (reqPkt.addr[IO_ASI_ADDR_NCU] == IO_ASI_CPU || // reqPkt.addr[IO_ASI_ADDR_NCU] == IO_ASI_NCU) { rspPkt.err[1] = ! gUtil.ioSpaceAccess(reqPkt.addr, tmpData, *, reqPkt.size, thread, myPort); if (reqPkt.size == 4) PR_ERROR(CLASSNAMEQ, MON_ERROR, psprintf("TID %0d is doing a 16 byte load to I/O addr %0h!",reqPkt.tid,reqPkt.addr)); rspPkt.data = gUtil.copyDataByte(tmpData,reqPkt.size,reqPkt.addr[3:0]); // } // // } else if (reqPkt.addr[39] && myPort !== DEV_NCU && // (reqPkt.addr[IO_ASI_ADDR_NCU] < 8'hA0 || // reqPkt.addr[IO_ASI_ADDR_NCU] > 8'hBF) ) { // // I/O but not L2 CSRs // error("I/O LD to L2, but is not L2 CSRs"); } else { // LD from non-NCU space or L2 CSRs // Address in req is quad word aligned. // Access memory at double word boundaries. tmpAddr = {reqPkt.addr[39:4],4'b0000}; rspPkt.data = gMem.read128(tmpAddr,myPort,1); if (gParam.mcuMemPrint[READ]) { printf("\n%7d00: dumpMem: dumping memory related to this request pkt:",get_cycle()); reqPkt.print(myPort); gMem.dumpMem(reqPkt.addr[39:0] & 40'hFFFFFFFFC0, 8); } if (rspPkt.rtntypU == U_CPX_MMU_RTN) rspPkt.wayMMUid = reqPkt.l1wayMMUid; if (rspPkt.rtntypU == U_CPX_PREF) { rspPkt.nc = 1; rspPkt.pf = 1; // prefetch data is irrelevant since data does not allocate in L1 rspPkt.data = {urandom(),urandom(),urandom(),urandom()}; // does this imply the the L1 does not have this line? //invVector = getInvalVector(rspPkt.rtntypU, reqPkt, reqPkt.cpuId); } // evict line from L1 & L2. // send invalidates to other cores for this address. // do not send response to initiating core!!! // Used by SW to flush lines in L2 based on an index and a // way specified as part of the Physical Address in the instruction // itself. Bits [39:37] of the PA has to be driven as 3'b011 by SW and // the way,index,bank information would be on PA[21:18], PA[17:9] and // PA[8:6] respectively. if (rspPkt.rtntypU == U_CPX_PREF_ICE) { // the BFM is not going to support this because the "address" // only makes sense to L2 RTL. Can't properly handle this. printf("\n\n%7d: WARNING, L2 BFM does not support prefetchICE, ignoring!\n\n\n"); return; // create a single EVICTION packet and send it to every core needing it rspPkt.targetPorts[8:0] = gParam.coreEnable; rspPkt.rtntypU = U_CPX_EVICT; rspPkt.rtntyp = CPX_EVICT; rspPkt.l2miss = 0; rspPkt.err = 0; rspPkt.nc = 0; rspPkt.pf = 0; tmpPa = reqPkt.addr[39:0]; // gets evict vector and invals dup tags. rspPkt.data = gUtil.evictVector(gParam.coreEnable, tmpPa, reqPkt.cpuId, targetCores); // return val for target cores rspPkt.targetPorts = targetCores; // inval line from L1 & L2. // send invalidates to other cores for this address. // do not send response to initiating core!!! if (rspPkt.data) { // notify LDST sync just once when doing U_CPX_PREF_ICE // no matter what targets get invalidated. ldstSync(reqPkt.cpuId,rspPkt); rspPkt.send(1); } @(posedge portVar.$clk); semaphore_put(ldstSyncLock, 1 ); return; // due to U_CPX_PREF_ICE } // U_CPX_PREF_ICE } // l2 ld // Plusargs to dump LD/ST to logfile if (gParam.show_load) { if (rspPkt.rtntypU == U_CPX_STR_LD) PR_NORMAL(CLASSNAMEQ, MON_NORMAL, psprintf("Tx LOAD PA = %h DATA = %h TYPE = %0d",reqPkt.addr,rspPkt.data,rspPkt.rtntypU)); else PR_NORMAL(CLASSNAMEQ, MON_NORMAL, psprintf("T%d LOAD PA = %h DATA = %h TYPE = %0d",reqPkt.tid,reqPkt.addr,rspPkt.data,rspPkt.rtntypU)); } // notify LDST sync ldstSync(reqPkt.cpuId,rspPkt); // Update D$ tag table if (reqPkt.nc == 0 && rspPkt.rtntypU !== U_CPX_PREF_ICE) updateDtag(reqPkt, rspPkt, reqPkt.cpuId, TAG_VAL); #ifdef CCXDEVMEMBFM_DEBUG // state of tag for line at this time rspPkt.lineWay = lineState(reqPkt, *, 1); #endif // queue packet for delivery, in this case, this BFM will drive it. rspPkt.send(1); @(posedge portVar.$clk); semaphore_put(ldstSyncLock, 1 ); } // LD U_CPX_ST, U_CPX_DIAG_ST, U_CPX_STR_ST, U_CPX_BIS, U_CPX_BLK_ST: { repeat (ordering(rspPkt, "ST RTN")) @(posedge portVar.$clk); // get semaphore for this clock semaphore_get(WAIT, ldstSyncLock, 1); if (reqPkt.addr[39] && myPort == DEV_NCU) { // I/O ? // is it a special address for us? // if (reqPkt.addr[IO_ASI_ADDR_NCU] == IO_ASI_CPU || // reqPkt.addr[IO_ASI_ADDR_NCU] == IO_ASI_NCU) { if (reqPkt.addr[IO_ASI_ADDR_REG] == ASI_SWVR_UDB_INTR_W) { // Send Store Ack but don't store to anything // Send INTERRUPT packet behind it rspPkt2 = new(reqPkt); rspPkt2 = rspPkt.object_copy(); // second packets must not have this set because it will // cause outstandingRequests to decrement twice. rspPkt2.ccxSourced = 0; rspPkt2.sendPorts = 1 << myPort; rspPkt2.createIntr(reqPkt.data[13:8],reqPkt.data[15:14],reqPkt.data[5:0]); } else { tmpData[63:0] = reqPkt.data; void = gUtil.ioSpaceAccess(reqPkt.addr, tmpData, 0, reqPkt.size, thread, myPort); // } } // } else if (reqPkt.addr[39] && myPort !== DEV_NCU && // (reqPkt.addr[39:32] < 8'hA0 || reqPkt.addr[39:32] > 8'hBF) ) { // // I/O but not L2 CSRs // error("I/O ST to L2, but it is not a L2 CSR!"); } else { // not NCU space, L2 or L2 CSR // this only gets done on hit. nas never does this so we wont either. // if (rspPkt.rtntypU == U_CPX_BIS) // gMem.write512({reqPkt.addr[39:6],6'b000000}, 0, myPort); // do not write if inv is set! BIS does not init w/ zeros. if (reqPkt.inv == 0) { gMem.writeBM({reqPkt.addr[39:3],3'b000}, reqPkt.data, reqPkt.size, myPort); if (gParam.mcuMemPrint[WRITE]) { printf("\n%7d00: dumpMem: dumping memory related to this request pkt:",get_cycle()); reqPkt.print(myPort); gMem.dumpMem(reqPkt.addr & 40'hFFFFFFFFC0, 8); } } } // Plusargs to dump LD/ST to logfile if (gParam.show_store) { if (rspPkt.rtntypU == U_CPX_STR_ST) PR_NORMAL(CLASSNAMEQ, MON_NORMAL, psprintf("Tx STORE PA = %h DATA = %h BYTE_MASK = %b TYPE = %0d", reqPkt.addr,reqPkt.data,reqPkt.size,rspPkt.rtntypU)); else PR_NORMAL(CLASSNAMEQ, MON_NORMAL, psprintf("T%d STORE PA = %h DATA = %h BYTE_MASK = %b TYPE = %0d", reqPkt.tid,reqPkt.addr,reqPkt.data,reqPkt.size,rspPkt.rtntypU)); } // L1 cache tags, get inv vec and invalidate all our dupe tags as needed. ST invVector = 0; targetCores = 0; // always target the requester targetCores[reqPkt.cpuId] = 1; for (i=0;i<=gParam.coreMax;i++) { tmpInvVect = getInvalVector(rspPkt.rtntypU, reqPkt, i); if (tmpInvVect) targetCores[i] = 1; invVector = invVector | tmpInvVect; } invField = 0; rspPkt.data = {2'b0, reqPkt.l1wayBis, invField, reqPkt.addr[5:4], reqPkt.cpuId[2:0], reqPkt.addr[11:6],7'b0, reqPkt.addr[3], reqPkt.size[7:0],invVector, reqPkt.data[63:0]}; if (rspPkt.rtntypU == U_CPX_STR_ST) rspPkt.l2miss = 0; #ifdef CCXDEVMEMBFM_DEBUG // state of tag for line at this time rspPkt.lineWay = lineState(reqPkt, *, 1); #endif // queue packet(s) for delivery, in this case, this BFM will drive it. if ((reqPkt.addr[IO_ASI_ADDR_NCU] == IO_ASI_CPU || reqPkt.addr[IO_ASI_ADDR_NCU] == IO_ASI_NCU) && reqPkt.addr[IO_ASI_ADDR_REG] == ASI_SWVR_UDB_INTR_W) { rspPkt.send(1); repeat (rspPkt.pkt2Delay) @(posedge portVar.$clk); // special interrupt "reflection" rspPkt2.l2miss = 0; rspPkt2.send(1); } else { // need to invalidate other cores on store/blkSt/BIS! rspPkt.targetPorts = targetCores; rspPkt.send(1); } // notify LDST sync ldstSync(reqPkt.cpuId,rspPkt); @(posedge portVar.$clk); semaphore_put(ldstSyncLock, 1 ); } // U_CPX_ST, U_CPX_DIAG_ST, U_CPX_STR_ST, U_CPX_BIS U_CPX_D_INVAL, U_CPX_I_INVAL: { integer i; repeat (ordering(rspPkt, "INVAL")) @(posedge portVar.$clk); // get semaphore for this clock semaphore_get(WAIT, ldstSyncLock, 1); // Core wants to invalidate all entries in the cache line if (invField == D_INVAL) { for (i=0; i<=3; i=i+1) { dtag[reqPkt.cpuId].write_tag(i,reqPkt.addr[10:4],29'b0,TAG_INVAL); } } else { for (i=0; i<=7; i=i+1) { itag[reqPkt.cpuId].write_tag(i,{1'b0,reqPkt.addr[10:5]},29'b0,TAG_INVAL); } } invVector = 0; rspPkt.data = {2'b0,reqPkt.l1wayBis,invField,reqPkt.addr[5:4],reqPkt.cpuId, reqPkt.addr[11:6],7'b0,reqPkt.addr[3],reqPkt.size,invVector,reqPkt.data[63:0]}; #ifdef CCXDEVMEMBFM_DEBUG // state of tag for line at this time rspPkt.lineWay = lineState(reqPkt, *, 1); #endif // queue packet(s) for delivery, in this case, this BFM will drive it. rspPkt.send(1); // notify LDST sync ldstSync(reqPkt.cpuId,rspPkt); @(posedge portVar.$clk); semaphore_put(ldstSyncLock, 1 ); } // U_CPX_D_INVAL, U_CPX_I_INVAL U_CPX_CAS_RTN: { if (myPort == DEV_NCU) error("CAS not allowed at NCU"); // I/O repeat (ordering(rspPkt, "CAS RTN")) @(posedge portVar.$clk); // get semaphore for this clock semaphore_get(WAIT, ldstSyncLock, 1); // save away cas1Data, cas1Addr. // hold until next packet (cas2) cas1Addr[thread] = reqPkt.addr; cas1Data[thread] = reqPkt.data; tmpAddr = reqPkt.addr[39:0]; tmpData = gMem.read_mem(tmpAddr,myPort); // remember to swap on following CAS2 pkt. cas_swap[thread] = data_equal(cas1Data[thread],tmpData,reqPkt.size); // for ldst sync rspPkt.CASstore = cas_swap[thread]; // always return the LOAD data at cas1Addr in first response pkt. rspPkt.data = gMem.read128(tmpAddr,myPort,1); if (gParam.mcuMemPrint[READ]) { printf("\n%7d00: dumpMem: dumping memory related to this request pkt:",get_cycle()); reqPkt.print(myPort); gMem.dumpMem(tmpAddr & 40'hFFFFFFFFC0, 8); } // rspPkt.recvPort = reqPkt.cpuId; // rspPkt.recvPorts = 1 << reqPkt.cpuId; rspPkt.nc = 1; rspPkt.tid = reqPkt.tid; rspPkt.atmIf2 = 1; rspPkt.wv = 0; if (gParam.show_load) { if (rspPkt.CASstore) PR_NORMAL(CLASSNAMEQ, MON_NORMAL, psprintf("T%d LOAD PA = %h DATA = %h TYPE = CAS swap will be true", reqPkt.tid,reqPkt.addr,tmpData)); else PR_NORMAL(CLASSNAMEQ, MON_NORMAL, psprintf("T%d LOAD PA = %h DATA = %h TYPE = CAS swap will be false", reqPkt.tid,reqPkt.addr,tmpData)); } #ifdef CCXDEVMEMBFM_DEBUG // state of tag for line at this time rspPkt.lineWay = lineState(reqPkt, *, 1); #endif rspPkt.send(1); // notify LDST sync ldstSync(reqPkt.cpuId,rspPkt); @(posedge portVar.$clk); semaphore_put(ldstSyncLock, 1 ); } // U_CPX_CAS_RTN U_CPX_CAS_ACK: { if (myPort == DEV_NCU) error("CAS not allowed at NCU"); // I/O repeat (ordering(rspPkt, "CAS ACK")) @(posedge portVar.$clk); // get semaphore for this clock semaphore_get(WAIT, ldstSyncLock, 1); // do the swap here on the second pkt. // compare the data at cas1Addr to the data cas1Data. // if ==, swap cas2 data with the data at cas1Addr. // always return the data at cas1Addr in first response pkt. // // cas1Data has rs2 data. // cas1Addr has rs1 addr. // // cas2 data has rd. // if cas_swap true from previous packet, write the rd/cas2 data to mem. if (cas_swap[thread]) { tmpAddr = cas1Addr[thread]; gMem.writeBM(tmpAddr[39:0], reqPkt.data, reqPkt.size, myPort); if (gParam.mcuMemPrint[WRITE]) { printf("\n%7d00: dumpMem: dumping memory related to this request pkt:",get_cycle()); reqPkt.print(myPort); gMem.dumpMem(reqPkt.addr & 40'hFFFFFFFFC0, 8); } // for ldst sync rspPkt.CASstore = 1; // Plusargs to dump LD/ST to logfile if (gParam.show_store && cas_swap[thread]) PR_NORMAL(CLASSNAMEQ, MON_NORMAL, psprintf("T%d STORE PA = %h DATA = %h BYTE_MASK = %b TYPE = CAS swap true", reqPkt.tid,reqPkt.addr,reqPkt.data,reqPkt.size)); cas_swap[thread] = 0; } // ack pkt rspPkt.nc = 1; rspPkt.tid = reqPkt.tid; rspPkt.atmIf2 = 1; rspPkt.wv = 0; // L1 cache tags, get vec and invalidate all duplicate tags as needed. CAS invVector = 0; targetCores = 0; // always target the requester targetCores[reqPkt.cpuId] = 1; for (i=0;i<=gParam.coreMax;i++) { tmpInvVect = getInvalVector(rspPkt.rtntypU, reqPkt, i); if (tmpInvVect) targetCores[i] = 1; invVector = invVector | tmpInvVect; } invField = 0; rspPkt.data = {2'b0,reqPkt.l1wayBis,invField,reqPkt.addr[5:4],reqPkt.cpuId, reqPkt.addr[11:6],7'b0,reqPkt.addr[3],reqPkt.size,invVector,reqPkt.data[63:0]}; #ifdef CCXDEVMEMBFM_DEBUG // state of tag for line at this time rspPkt.lineWay = lineState(reqPkt, *, 1); #endif // queue packet for delivery, in this case, this BFM will drive it. rspPkt.targetPorts = targetCores; rspPkt.send(1); // notify LDST sync ldstSync(reqPkt.cpuId,rspPkt); @(posedge portVar.$clk); semaphore_put(ldstSyncLock, 1 ); } // U_CPX_CAS_ACK U_CPX_SWAP_RTN, U_CPX_SWAP_ACK: // do the swap, the return pkt, and the ack pkt here. // we get addr and 32 bit swap data. Use size mask. { if (myPort == DEV_NCU) error("SWAP not allowed at NCU"); // I/O fork { repeat (ordering(rspPkt, "SWAP RTN")) @(posedge portVar.$clk); // get semaphore for this clock semaphore_get(WAIT, ldstSyncLock, 1); // load tmpAddr = {reqPkt.addr[39:4],4'b0000}; rspPkt.data = gMem.read128(tmpAddr,myPort,1); if (gParam.mcuMemPrint[READ]) { printf("\n%7d00: dumpMem: dumping memory related to this request pkt:",get_cycle()); reqPkt.print(myPort); gMem.dumpMem(tmpAddr & 40'hFFFFFFFFC0, 8); } // Plusargs to dump LD/ST to logfile if (gParam.show_load) PR_NORMAL(CLASSNAMEQ, MON_NORMAL, psprintf("T%d LOAD PA = %h DATA = %h TYPE = SWAP", reqPkt.tid,reqPkt.addr,rspPkt.data)); // return pkt // rspPkt.nc = 1; rspPkt.tid = reqPkt.tid; rspPkt.atmIf2 = 1; rspPkt.wv = 0; // for ldst sync rspPkt.CASstore = 1; // used by fork 2 when it proceeds rspPkt2 = new(reqPkt); rspPkt2 = rspPkt.object_copy(); #ifdef CCXDEVMEMBFM_DEBUG // state of tag for line at this time rspPkt.lineWay = lineState(reqPkt, *, 1); #endif // send load data rspPkt.send(1); // notify LDST sync ldstSync(reqPkt.cpuId,rspPkt); @(posedge portVar.$clk); semaphore_put(ldstSyncLock, 1 ); } // fork 1 ///////////////////////////////// { // fork 2 delay(1); // wait repeat (ordering(rspPkt, "SWAP ACK")) @(posedge portVar.$clk); // get semaphore for this clock semaphore_get(WAIT, ldstSyncLock, 1); // ack pkt // gMem.writeBM({reqPkt.addr[39:3],3'b000}, reqPkt.data, reqPkt.size, myPort); if (gParam.mcuMemPrint[WRITE]) { printf("\n%7d00: dumpMem: dumping memory related to this request pkt:",get_cycle()); reqPkt.print(myPort); gMem.dumpMem(reqPkt.addr & 40'hFFFFFFFFC0, 8); } rspPkt2.ccxSourced2 = reqPkt.ccxSourced; // second packet must not have ccxSourced set because it will // cause outstandingRequests to decrement twice. rspPkt2.ccxSourced = 0; rspPkt2.rtntyp = CPX_SWAP_ACK; rspPkt2.rtntypU = U_CPX_SWAP_ACK; // L1 cache tags, get vec and invalidate all duplicate tags as needed. SW invVector = 0; targetCores = 0; // always target the requester targetCores[reqPkt.cpuId] = 1; for (i=0;i<=gParam.coreMax;i++) { tmpInvVect = getInvalVector(rspPkt2.rtntypU, reqPkt, i); if (tmpInvVect) targetCores[i] = 1; invVector = invVector | tmpInvVect; } // Vack invField = 0; rspPkt2.data = {2'b0,reqPkt.l1wayBis,invField,reqPkt.addr[5:4],reqPkt.cpuId, reqPkt.addr[11:6],7'b0,reqPkt.addr[3],reqPkt.size,invVector,reqPkt.data[63:0]}; // Plusargs to dump LD/ST to logfile if (gParam.show_store) PR_NORMAL(CLASSNAMEQ, MON_NORMAL, psprintf("T%d STORE PA = %h DATA = %h BYTE_MASK = %b TYPE = SWAP",reqPkt.tid,reqPkt.addr,reqPkt.data,reqPkt.size)); #ifdef CCXDEVMEMBFM_DEBUG // state of tag for line at this time rspPkt2.lineWay = lineState(reqPkt, *, 1); #endif // queue packet for delivery, in this case, this BFM will drive it. rspPkt2.targetPorts = targetCores; rspPkt2.send(1); // notify LDST sync ldstSync(reqPkt.cpuId,rspPkt2); @(posedge portVar.$clk); semaphore_put(ldstSyncLock, 1 ); } join none // all } // PCX_SWAP_RTN U_CPX_IFILL, U_CPX_NCU_IFILL: { repeat (ordering(rspPkt, "IFILL")) @(posedge portVar.$clk); // get semaphore for this clock semaphore_get(WAIT, ldstSyncLock, 1); tmpAddr = reqPkt.addr; // are we NCU? return 1 packet, not 2 if (myPort == DEV_NCU) { // Set error bit on fetch to non-boot I/O to match NCU behavior if (tmpAddr[39] == 1 && tmpAddr[39:32] != 8'hff) rspPkt.err = 2'b10; // uncorrectable error rspPkt.wayf4b = 1; // 4 byte fill tmpAddr = {reqPkt.addr[39:4],4'b0}; // 4 byte addressing!!! rspPkt.data = gMem.read128(tmpAddr,myPort,1); if (gParam.mcuMemPrint[READ]) { printf("\n%7d00: dumpMem: dumping memory related to this request pkt:",get_cycle()); reqPkt.print(myPort); gMem.dumpMem(tmpAddr & 40'hFFFFFFFFC0, 8); } //printf("%0d: CcxDevMemBFM[%2d]::respond: T%d read @ reqPkt/tmp %0h/%0h\n", get_time(LO),myPort,reqPkt.tid,reqPkt.addr,tmpAddr); //printf("%0d: CcxDevMemBFM[%2d]::respond: T%d read data %0h\n", get_time(LO),myPort,reqPkt.tid,rspPkt.data); // queue packet for delivery, this BFM instance will end up driving it. rspPkt.send(1); } else { // L2$ rspPkt2 = new(reqPkt); rspPkt2 = rspPkt.object_copy(); tmpAddr = {reqPkt.addr[39:5],5'b0}; rspPkt.data = gMem.read128(tmpAddr,myPort, 1); if (gParam.mcuMemPrint[READ]) { printf("\n%7d00: dumpMem: dumping memory related to this request pkt:",get_cycle()); reqPkt.print(myPort); gMem.dumpMem(tmpAddr & 40'hFFFFFFFFC0, 8); } // second packets must not have this set because it will // cause outstandingRequests to decrement twice. rspPkt2.ccxSourced = 0; tmpAddr = tmpAddr + 16; //5'b10000; rspPkt2.atmIf2 = 1; rspPkt2.l2miss = 0; rspPkt2.data = gMem.read128(tmpAddr,myPort, 1); #ifdef CCXDEVMEMBFM_DEBUG // state of tag for line at this time rspPkt.lineWay = lineState(reqPkt, *, 1); #endif // L1$ tag update. updateItag(reqPkt, rspPkt, rspPkt2, reqPkt.cpuId, TAG_VAL); // queue packets for delivery. // // Atomics must stay in order. The CPX rtl will hold the first // pkt until the second pkt arrives. They will arrive at the core // back to back. Putting time between pkt1 and pkt2 tests the CCX only, // the core never sees time between them. The 2 pkts MUST go into the // mailbox back to back to avoid another pkt from this port getting between // them. rspPkt.send(1); rspPkt2.send(1); } // notify LDST sync // ldstSync(reqPkt.cpuId,rspPkt); @(posedge portVar.$clk); semaphore_put(ldstSyncLock, 1 ); } default : { rspPkt.print(myPort); PR_ERROR (CLASSNAMEQ, MON_ERR, psprintf ("T%d Port=%0d rtntyp=%b. Unsupported rtntyp for CCX MEM device BFM.",thread,myPort, rspPkt.rtntyp)); } } // case } task CLASSNAME::updateDtag(PcxPkt reqPkt, CpxPkt rspPkt, reg [2:0] cpuId, integer how = TAG_VAL) { reg [3:0] match; if (myPort == DEV_NCU || cacheOff || reqPkt.nc == 1) return; // Update D$ tag table dtag[cpuId].write_tag (reqPkt.l1wayMMUid, reqPkt.addr[10:4], reqPkt.addr[39:11],how); // Invalidate the other (I) tag table match = itag[cpuId].get_way ("D, chk 4 hit in I:", {1'b0,reqPkt.addr[10:5]}, reqPkt.addr[39:11]); if (match != 4'b0) { itag[cpuId].write_tag(match[3:1],{1'b0,reqPkt.addr[10:5]},29'b0,TAG_INVAL); rspPkt.wv = 1'b1; rspPkt.wayMMUid = match[3:2]; rspPkt.wayf4b = match[1]; } } task CLASSNAME::updateItag(PcxPkt reqPkt, CpxPkt rspPkt, CpxPkt rspPkt2 = null, reg [2:0] cpuId, integer how = TAG_VAL) { reg [3:0] match; if (myPort == DEV_NCU || cacheOff) return; if (!reqPkt.nc) { if (match != 4'b0) PR_ERROR(CLASSNAMEQ, MON_ERR, psprintf ("ERROR itag: DUT request to L2$ 0x%0h should have hit in the L1$.\n\n",reqPkt.addr)); // Update I$ tag table itag[cpuId].write_tag ({reqPkt.l1wayBis,reqPkt.l1wayMMUid}, {1'b0,reqPkt.addr[10:5]}, reqPkt.addr[39:11],how,1); } // Invalidate the other (D) tag table (whether nc=0|1) match = dtag[cpuId].get_way ("I, chk 4 hit in D:", reqPkt.addr[10:4], reqPkt.addr[39:11]); if (match != 4'b0) { dtag[cpuId].write_tag({1'b0,match[3:2]},reqPkt.addr[10:4],29'b0,TAG_INVAL); rspPkt.wv = 1'b1; rspPkt.wayMMUid = match[3:2]; } // ifill covers 2 D$ lines if (rspPkt2 !== null) { match = dtag[cpuId].get_way ("I, chk 4 hit in D:", reqPkt.addr[10:4]+1, reqPkt.addr[39:11]); if (match != 4'b0) { dtag[cpuId].write_tag({1'b0,match[3:2]},reqPkt.addr[10:4]+1,29'b0,TAG_INVAL); rspPkt2.wv = 1'b1; rspPkt2.wayMMUid = match[3:2]; } } } // use to receive an expected packet. // mainly for CCX testing. // // user passes in a packet whos fields are set to match the // packet that should show up at this port. When it does, the // caller is notified (toggle a var in the passed in packet) and // the passed in packet will be populated with what showed up at the // destinatin port. Unexpected (not registered via a call to this task) // packets will cause failure. task CLASSNAME::recv(BasePkt pktHndl) { // PcxPkt pcxPkt; // assign/cast pktHndl to be of PcxPkt type rather than base //cast_assign(pcxPkt,pktHndl); // load signature hash. key is signature and data is clk count at call time. // expectedSig[pcxPkt.getVector()] = pktHndl; expectedSig[pktHndl.getVector()] = pktHndl; } // Mainly for CCX testing. Call when a pkt should no longer arrive. // For CCX, a pkt should never intentionally get dropped so this may not get used. task CLASSNAME::cancelRecv(BasePkt pktHndl) { // PcxPkt pcxPkt; // assign/cast pktHndl to be of PcxPkt type rather than base //cast_assign(pcxPkt,pktHndl); // clear signature hash. // void = assoc_index(DELETE,expectedSig,pcxPkt.getVector()); void = assoc_index(DELETE,expectedSig,pktHndl.getVector()); } //---------------------------------------------------------- // Compare 2 data vectors using size as a mask function reg CLASSNAME::data_equal (reg [63:0] data1, reg [63:0] data2, reg[7:0] size) { reg eq0,eq1,eq2,eq3,eq4,eq5,eq6,eq7; eq7 = !size[7] | (data1[63:56]==data2[63:56]); eq6 = !size[6] | (data1[55:48]==data2[55:48]); eq5 = !size[5] | (data1[47:40]==data2[47:40]); eq4 = !size[4] | (data1[39:32]==data2[39:32]); eq3 = !size[3] | (data1[31:24]==data2[31:24]); eq2 = !size[2] | (data1[23:16]==data2[23:16]); eq1 = !size[1] | (data1[15: 8]==data2[15: 8]); eq0 = !size[0] | (data1[ 7: 0]==data2[ 7: 0]); data_equal = eq7 & eq6 & eq5 & eq4 & eq3 & eq2 & eq1 & eq0; } // hit indication, what L2 thinks the core L1 has. // returns invalidation vector for the indicated core. // Does the invalidate of our duplicate tags. function reg [31:0] CLASSNAME::getInvalVector(integer type, PcxPkt reqPkt, reg [2:0] cpuId) { reg [31:0] invVect_d, invVect_i; reg [3:0] dmatch, imatch; if (myPort == DEV_NCU || cacheOff) { getInvalVector = 0; return; } #ifdef CCXDEVMEMBFM_DEBUG // state of tag for line at this time dtag[cpuId].dump_line("getInvalVector D1:", reqPkt.addr[10:4], MON_ALWAYS); #endif // Check if store hit the D$ of given cpuId dmatch = dtag[cpuId].get_way("getInvalVector:", reqPkt.addr[10:4], reqPkt.addr[39:11]); //create_vector (INSTR_TAG,dmatch,invVect_d,cpuId); invVect_d = dmatch << (cpuId * 4); // Invalidate entry in given cpuId (all cpu's) D$ if found... // Do this for Stream ST, Atomics (CAS/SWAP), Block *ST. if ((type == U_CPX_STR_ST || type == U_CPX_BIS || type == U_CPX_CAS_ACK || type == U_CPX_CAS_RTN || type == U_CPX_BLK_ST || type == U_CPX_SWAP_RTN || type == U_CPX_SWAP_ACK || reqPkt.cpuId !== cpuId) && // hitting core is not requesting core dmatch) dtag[cpuId].write_tag({1'b0,dmatch[3:2]}, reqPkt.addr[10:4], 29'b0,TAG_INVAL); // Check if store hit the I$ imatch = itag[cpuId].get_way("getInvalVector:", {1'b0,reqPkt.addr[10:5]}, reqPkt.addr[39:11]); // Invalidate entry in I$ if found if (imatch) itag[cpuId].write_tag(imatch[3:1], {1'b0,reqPkt.addr[10:5]}, 29'b0,TAG_INVAL); //create_vector (INSTR_TAG,imatch,invVect_i,cpuId); invVect_i = imatch << (cpuId * 4); // Per spec, you cannot get match in both I$ & D$ if ((imatch!=0)&(dmatch!=0)) { PR_ERROR(CLASSNAMEQ, MON_ERR, psprintf ("found match in both D$ and I$ for core %0d. D$=%b, I$=%b",cpuId,dmatch,imatch)); } getInvalVector = invVect_d | invVect_i; #ifdef CCXDEVMEMBFM_DEBUG // state of tag for line at this time dtag[cpuId].dump_line("getInvalVector D2:", reqPkt.addr[10:4], MON_ALWAYS); #endif } task CLASSNAME::ldstSync(reg [2:0] cpuId, CpxPkt rspPkt) { if (rspPkt.ccxSourced || rspPkt.ccxSourced2 || rspPkt.rtntyp == CPX_EVICT) { // notify nas LD/ST Sync for all but these types. // CAS that writes to L2 (CASstore), gntTarget, pa, pkt. if (myPort > 7 && rspPkt.rtntypU !== U_CPX_STR_LD && rspPkt.rtntypU !== U_CPX_MMU_RTN && rspPkt.rtntypU !== U_CPX_PREF) { // these are NR0 interface types (return to zero) gLdStSyncPort[myPort].$cid <= cpuId; gLdStSyncPort[myPort].$ctrue <= rspPkt.CASstore; //gLdStSyncPort[myPort].$swap <= (rspPkt.rtntypU == U_CPX_SWAP_ACK || rspPkt.rtntypU == U_CPX_SWAP_RTN); // swap; gLdStSyncPort[myPort].$swap <= 0; gLdStSyncPort[myPort].$pa <= rspPkt.addr; gLdStSyncPort[myPort].$pkt <= rspPkt.getVector(); } } } // special task to potentially create an eviction packet to // send to some cores. This task will make sure that LDST sync // gets notified correctly. It also makes sure that // duplicate tag updating is ordered and sane. This "psudo request" // is ordered like any other. // // Caller can specify a target address to evict. Will do nothing if address // is not cached. Otherwise, we find an address. task CcxDevMemBFM::enqueueEvict(reg [7:0] coreEnable, reg [39:0] evictPA = 40'hffffffffff, reg [3:0] cid = 4'hf, reg all_cores = 0, integer dCacheWeight = 60) { ccxPort portVar = gCpxPort[myPort]; reg [7:0] targets; CpxPkt pkt; reg [127:0] vect; pkt = new(); //pkt.randomize(); //pkt.responseDelay = pkt.pkt2Delay; pkt.responseDelay = 2; pkt.tid = cid; pkt.sendPorts = 1 << myPort; pkt.rtntyp = CPX_EVICT; pkt.rtntypU = U_CPX_EVICT; pkt.addr = evictPA; pkt.l2miss = 0; repeat (ordering(pkt, "XEVICT")) @(posedge portVar.$clk); // get semaphore for this clock semaphore_get(WAIT, ldstSyncLock, 1); // get vector and update L1 dup tags vect = gUtil.evictVector (coreEnable, evictPA, cid, targets); // vect = gUtil.evictVinv(coreEnable, // targets, // evictPA, // cid, // all_cores, // dCacheWeight); #ifdef CCXDEVMEMBFM_DEBUG if (! targets) printf("EVICTION gUtil.evictVector did not return any targets!!!\n"); else printf("EVICTION gUtil.evictVector return targets = %b vec = %h\n", targets,vect); #endif if (vect && targets) { pkt.targetPorts = targets; pkt.addr = evictPA; pkt.tid = cid; pkt.data = vect; ldstSync(0,pkt); // notify pkt.send(1); // doit PR_NORMAL(CLASSNAMEQ, MON_NORMAL, psprintf ("Sending EVICTION pkt to cores targets=0x%h, a=0x%h, vec=0x%h", pkt.targetPorts,pkt.addr,pkt.data)); } else pkt = null; @(posedge portVar.$clk); semaphore_put(ldstSyncLock, 1 ); } // used for debug only function reg [3:0] CLASSNAME::lineState(PcxPkt reqPkt, string why="debug lineState:", reg quiet=1) { if (reqPkt.rqtypU == U_PCX_IFILL || reqPkt.rqtyp == PCX_IFILL) { lineState = itag[reqPkt.cpuId].get_way(why, {1'b0,reqPkt.addr[10:5]}, reqPkt.addr[39:11]); if (!quiet) itag[reqPkt.cpuId].dump_line(why, reqPkt.addr[10:4],MON_INFO); } else { lineState = dtag[reqPkt.cpuId].get_way(why, reqPkt.addr[10:4], reqPkt.addr[39:11]); if (!quiet) dtag[reqPkt.cpuId].dump_line(why, reqPkt.addr[10:4],MON_INFO); } } // hold off the responses until outstandingReqs reaches amount. // this will clump responses into bursts periodically. task CLASSNAME::burstResp(integer amount) { reg [63:0] tmp; reg iSync = 0; integer wait, reason; ccxPort portVar = gCpxPort[myPort]; if (gParam.burstSync == myPort-8 || gParam.burstSync == myPort) iSync = 1; wait = gParam.burstHoldoff; repeat (10) @(negedge gPcxPort[myPort].$clk); tmp = gUtil.getThreadEnables(); while (tmp !== gOutOfBoot) wait_var(gOutOfBoot); // all threads out of boot // make stall agreeable so we do not stall cores requests when trying to // build up a burst stallStart = amount + 6; PR_ALWAYS(CLASSNAMEQ, MON_ALWAYS, psprintf("Port %2d, stallStart changed to %0d due to burst option.",myPort, stallStart)); while (1) { if (outstandingReqs < amount) { // just drain naturally if too many in queue @(posedge portVar.$clk); semaphore_get(WAIT, ldstSyncLock, 1); // stop responding // start respoding after amount or burstSync or x clocks fork { while (outstandingReqs < amount) wait_var(outstandingReqs); // wait reason = 1; } { repeat (wait) @(posedge gPcxPort[myPort].$clk); // but not too long reason = 2; } { wait_var(burstSync); // burst on sync when in use reason = 3; } join any terminate; // kill remaining forks if (iSync) burstSync = ~burstSync; // signal the other banks if (outstandingReqs >= 2) PR_INFO(CLASSNAMEQ, MON_INFO, psprintf("Port %2d, Letting %0d packets burst through (reason=%0d)",myPort, outstandingReqs, reason)); @(posedge portVar.$clk); semaphore_put(ldstSyncLock, 1 ); // allow normal responses } repeat (5) @(posedge gPcxPort[myPort].$clk); // give a little break } // rinse and repeat }