// ========== Copyright Header Begin ========================================== // // OpenSPARC T2 Processor File: sparcBenchUtils.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 #include #include #include #include #include #include #ifndef FC_BENCH #ifndef FC_SCAN_BENCH #include #endif #endif // mem slam, reg slam #include #include #include #include #include #include #ifndef CCM_BENCH #ifndef FC_BENCH #ifndef FC_SCAN_BENCH #include #endif #endif #endif #include #define CLASSNAME SparcBenchUtils #define CLASSNAMEQ "SparcBenchUtils" class CLASSNAME extends BaseUtils { local string className = "SparcBenchUtils"; local StandardDisplay dbg; local reg noNCUcheck = 0; local reg failNCUcheck = 0; // rands local randc reg [5:0] startThread; local randc reg [2:0] randCID; local rand integer parkWait; // end rands // constraints constraint parkWait_c { parkWait <= gParam.por_delay_max; parkWait >= gParam.por_delay_min; } // end constraints task new(StandardDisplay dbgHndl, integer clockPeriod = 100); task resetDut(); function reg ioSpaceAccess(reg [63:0] addr, // full addr of request var reg [127:0] data, // r/w data reg read = 1, // request is read, else write reg [7:0] size = 8'hff, // write byte mask integer thread = 99, // please provide integer myPort = 99); // optional for messaging function reg [127:0] copyDataByte (reg [127:0] data, reg [7:0] size, reg [3:0] offset); function reg [63:0] evictAddr (reg [7:0] core_enable, var reg [2:0] cidUsed, reg [3:0] cid = 4'hf, integer dCacheWeight = 60); function reg [127:0] evictVector (reg [7:0] core_enable, reg [63:0] evict_pa, reg [2:0] cpuId, var reg [7:0] targets); function reg [63:0] getThreadEnables(); function reg [63:0] getRunStatus(); function reg [2:0] whichBank(reg [63:0] addr); } //---------------------------------------------------------- // Do not call randomize() in this classes new() task. // The final class extention of this class must (does) do that in its new()! // Go ahead an assume that the first set of random numbers are available. task CLASSNAME::new(StandardDisplay dbgHndl, integer clockPeriod = 100) { super.new(dbgHndl, clockPeriod); this.dbg = dbgHndl; if (mChkPlusarg(noNCUcheck)) noNCUcheck = 1; if (mChkPlusarg(failNCUcheck)) failNCUcheck = 1; } // call AFTER time zero function reg [63:0] CLASSNAME::getThreadEnables() { //getThreadEnables = sparcBenchUtils_if.th_check_enable; // -vcs_run_args=+thread=1 getThreadEnables = gParam.finishMask; // this is better } // call AFTER time zero. gets core_running_status. function reg [63:0] CLASSNAME::getRunStatus() { getRunStatus = sparcBenchUtils_if.core_running_status; } // return which bank this address goes to. // for partial bank mode function reg [2:0] CLASSNAME::whichBank(reg [63:0] addr) { case (gParam.bank_set_mask) { 1: whichBank = addr[6]; 2: whichBank = {2'b01,addr[6]}; 3: whichBank = {1'b0,addr[7:6]}; 4: whichBank = {2'b10,addr[6]}; 5: whichBank = {addr[7],1'b0,addr[6]}; 6: whichBank = {addr[7],~addr[7],addr[6]}; 8: whichBank = {2'b11,addr[6]}; 9: whichBank = {addr[7],addr[7],addr[6]}; 10: whichBank = {addr[7],1'b1,addr[6]}; 12: whichBank = {1'b1,addr[7],addr[6]}; 15: whichBank = {addr[8],addr[7],addr[6]}; default: error("bank_set_mask is illegal\n"); } } //---------------------------------------------------------- // start threads // // rules: // 1 - always start with the lowest core, after that randomize core selection. // 2 - in a core, always start the lowest thread first. // 3 - after each core has had its lowest thread started, randomize the // rest of the threads. // task CLASSNAME::resetDut () { //reg [31:0] resetThis; reg [63:0] threadEnables; integer core, tid, tmp; integer lowCore; // lowest core from threadEnables integer lowThread [8]; // lowest thread in each core reg [63:0] doneThreads; // have been started already reg [7:0] doneCores; // have been started already #ifndef FC_BENCH #ifndef FC_SCAN_BENCH for (tid=0; tid<8; tid++) lowThread[tid] = 32'hx; void = this.rand_mode(OFF, "startThread"); // do not unpark too early. if (sparcBenchUtils_if.core_cycle_cnt < 12) repeat (12 - sparcBenchUtils_if.core_cycle_cnt) @(negedge sparcBenchUtils_if.clk); // range is 28-48 (38 ideal) cycles from time zero if +forcePORstate if (gParam.forcePORstate) { if (sparcBenchUtils_if.core_cycle_cnt < 38) repeat (38 - sparcBenchUtils_if.core_cycle_cnt) @(negedge sparcBenchUtils_if.clk); } threadEnables = sparcBenchUtils_if.th_check_enable; // -vcs_run_args=+thread=1 if (! threadEnables) { PR_ALWAYS(className, MON_ALWAYS, psprintf("Unparking threads: th_check_enable = 0, Will not unpark ANY threads!!!")); return; } PR_ALWAYS(className, MON_ALWAYS, psprintf("Unparking threads: th_check_enable = %h",threadEnables)); // find lowest core for (core=0; core<8; core++) { if (|threadEnables[7+(core*8):core*8]) { lowCore = core; break; } else doneCores[core] = 1; } // find lowest thread in each core for (core=lowCore; core<8; core++) { for (tid=0; tid<8; tid++) { if (threadEnables[tid+(8*core)] == 1) { lowThread[core] = tid; break; } } } doneThreads = ~threadEnables; // call unused tids done for starters PR_ALWAYS(className, MON_ALWAYS, psprintf("Unparking: low core = %0d, low threads (0-07): %1d,%1d,%1d,%1d,%1d,%1d,%1d,%1d",lowCore,lowThread[7],lowThread[6],lowThread[5],lowThread[4],lowThread[3],lowThread[2],lowThread[1],lowThread[0])); PR_ALWAYS(className, MON_ALWAYS, psprintf("Unparking: low core = %0d, low threads (0-63): %2d,%2d,%2d,%2d,%2d,%2d,%2d,%2d",lowCore,lowThread[7]+56,lowThread[6]+48,lowThread[5]+40,lowThread[4]+32,lowThread[3]+24,lowThread[2]+16,lowThread[1]+8,lowThread[0])); // POR threads in random orders after lowest core lowest tid // but lowest first for each core. // this is lowest first... tmp = (lowCore*8)+lowThread[lowCore]; verilog_write_cmp_reg({ASI_CMP_CORE,ASI_CMP_CORE_RUNNING_W1S}, 1 << tmp, tmp); doneThreads[tmp] = 1; // mark it done doneCores[lowCore] = &doneThreads[7+(lowCore*8):lowCore*8]; // core done? PR_ALWAYS(className, MON_ALWAYS, psprintf("T%0d unparked (lowest tid in lowest core)\n",tmp)); // quick check if (doneThreads !== 64'hffffffffffffffff) { // now the rest // start the lowest thread on each core (except lowest core). // they could be chosen at random or sequential. repeat (8) { core = randCID; void = this.randomize(); if (core == lowCore || doneCores[core]) continue; if (lowThread[core] !== 32'hx) { tmp = (core*8)+lowThread[core]; // parkWait = urandom_range(gParam.por_delay_max,gParam.por_delay_min); // PR_DEBUG (className, MON_DEBUG, // psprintf("T%0d post unpark delay %d\n",get_cycle(),resetThis,del)); repeat (parkWait) @(posedge sparcBenchUtils_if.clk); verilog_write_cmp_reg({ASI_CMP_CORE,ASI_CMP_CORE_RUNNING_W1S}, 1 << tmp, tmp); PR_ALWAYS(className, MON_ALWAYS, psprintf("T%0d unparked (lowest tid in core)\n",tmp)); doneThreads[tmp] = 1; // mark it done doneCores[core] = &doneThreads[7+(core*8):core*8]; // core done? } } // start the remaining threads at random repeat (64) { if (doneThreads[startThread] == 0) { void = this.randomize(); // parkWait = urandom_range(gParam.por_delay_max,gParam.por_delay_min); // PR_DEBUG (className, MON_DEBUG, // psprintf("T%0d post unpark delay %d\n",get_cycle(),resetThis,del)); repeat (parkWait) @(posedge sparcBenchUtils_if.clk); verilog_write_cmp_reg({ASI_CMP_CORE,ASI_CMP_CORE_RUNNING_W1S}, 1 << startThread, startThread); PR_ALWAYS(className, MON_ALWAYS, psprintf("T%0d unparked (remaining non-lowest tids)\n",startThread)); doneThreads[startThread] = 1; // mark it done } void = this.rand_mode(ON, "startThread"); void = this.randomize(); void = this.rand_mode(OFF, "startThread"); } } #endif #endif } /* task CLASSNAME::resetDut () { reg [31:0] tid, resetThis; reg [63:0] threadEnables; integer lowThread, del; #ifndef FC_BENCH void = this.rand_mode(OFF, "startThread"); repeat (5) @(negedge sparcBenchUtils_if.clk); threadEnables = sparcBenchUtils_if.th_check_enable; // -vcs_run_args=+thread=1 PR_ALWAYS(className, MON_ALWAYS, psprintf("Unparking threads: th_check_enable = %h",threadEnables)); // find lowest thread for (tid=0; tid<=63; tid++) { if (threadEnables[tid] == 1) { lowThread = tid; break; } } //POR threads in random orders after lowest verilog_write_cmp_reg({ASI_CMP_CORE,ASI_CMP_CORE_RUNNING_W1S}, 1<> 8; // seems backwards but works tmpData[127:120] = data[127:120]; } } 8'h1: {// 2 bytes repeat (8) { tmpData = tmpData >> 16; tmpData[127:112] = data[127:112]; } } 8'h2: {// 4 bytes repeat (4) { tmpData = tmpData >> 32; tmpData[127:96] = data[127:96]; } } 8'h3: {// 8 bytes repeat (2) { tmpData = tmpData >> 64; tmpData[127:64] = data[127:64]; } } 8'h4: {// 16 bytes tmpData = data; } default: PR_ERROR("copyDataByte", MON_ERR, psprintf ("ERROR bad size=%b passed in.",size)); } copyDataByte = tmpData; } // Evictions use 2 functions, evictAddr & evictVector. // "Eviction loop" code will call evictAddr to get address, then BFM will call // evictVector to get the vector and modify the shadow tag. // "Eviction loop" code will call task enqueueEvict in the correct bfm to // request the invalidate after evictAddr has been called. // The evict User Event that takes PA, will call task enqueueEvict in the // correct bfm to request the invalidate. //-------------------- // Pick a random core and search for a line with a valid entry. // mem_index - itag 0..63, dtag 0..127 // evict_index is normalized index between 0..31 // used to search for tag in all lines in the group (aka L2 cache line) // returns a valid address. // Inputs: // core_enable = 8 bit vector, 1 bit per core // cid = N, to limit search to that core. // dCacheWeight = N to weight I/D caches. 100 % based // // Outputs: // found address if any, all F's otherwise. // cidUsed = a core that had the address function reg [63:0] CLASSNAME::evictAddr(reg [7:0] core_enable, var reg [2:0] cidUsed, reg [3:0] cid = 4'hf, integer dCacheWeight = 60) // 100 % based { reg evict=0; // =1, if valid entry found in the table reg [6:0] mem_index=0; // raw index from tag table search reg [28:0] evict_tag=0; // tag that is being evicted reg [4:0] evict_index=0; // Normalized between 0..31 reg junk; reg dCacheAddr = 0; #ifndef CCM_BENCH #ifndef FC_BENCH #ifndef FC_SCAN_BENCH evictAddr = 64'hFFFFFFFFFFFFFFFF; cidUsed = 0; if (cid == 4'hf) { // Pick cid out of the enabled cores. // This runs in zero time so randCID is safe from other calls to randomize(). while (!core_enable[randCID]) { void = this.randomize(); } cidUsed = randCID; } else { // check the cid cidUsed = cid; if (!core_enable[cidUsed]) PR_ERROR("evict", MON_ERR, psprintf (" BENCH ERROR - selected core not available!")); } randcase { 100-dCacheWeight: { // Get raw index that has a valid entry itag[cidUsed].search_tagmem(evict,mem_index); // set evict=1, if valid entry found // found nothing if (! evict) return; // Normalize between 0..31 evict_index = mem_index / 2; // Get the tag that will be evicted itag[cidUsed].get_tag(mem_index,junk,evict_tag); } dCacheWeight: { // Get raw index that has a valid entry dtag[cidUsed].search_tagmem(evict,mem_index); // set evict=1, if valid entry found // found nothing if (! evict) return; // Normalize between 0..31 evict_index = mem_index / 4; // Get the tag that will be evicted dtag[cidUsed].get_tag(mem_index,junk,evict_tag); dCacheAddr = 1; } } evictAddr = {evict_tag,evict_index,6'b0}; PR_INFO ("evict", MON_INFO, psprintf("EVICTION evictAddr PA{[39:6],6'b0}=%h evict_tag(pa[39:11])=%h evict_index(pa[10:6])=%h core=%0d dCacheAddr=%0d", evictAddr,evict_tag,evict_index,cidUsed,dCacheAddr)); #endif #endif #endif } //---------------------------------------------------------- // Evict a PA from the L1 cache in all cores that are enabled. // The value that is returned as evictVector has the format as defined // in the CCX packet spec as Vinv. // // Pass in address. Will check all enabled cores for hit at that address and return // an eviction vector for all matching/hitting cores. // // Inputs: // core_enable = 8 bit vector, 1 bit per core // evict_pa = address to evict // cpuId = CPU associated with evict_pa being cached // Output: // evictVector = 0, no entries to evict. // targets = cores that will be the target of the evict packet. function reg [127:0] CLASSNAME::evictVector(reg [7:0] core_enable, reg [63:0] evict_pa, reg [2:0] cpuId, var reg [7:0] targets) { reg evict=0; // 1, if valid entry found in the table reg [6:0] mem_index=0; // raw index from tag table search reg [28:0] evict_tag=0; // tag that is being evicted reg [4:0] evict_index=0; // Normalized between 0..31 reg [3:0] dmatch=0; reg [3:0] imatch=0,i; reg [111:0] i_vect=0,d_vect=0,e_vect=0; #ifndef CCM_BENCH #ifndef FC_BENCH #ifndef FC_SCAN_BENCH targets = 0; evictVector = 0; // Get index & tag from User argument evict_tag = evict_pa[39:11]; evict_index = evict_pa[10:6]; mem_index = evict_pa[10:4]; // tag class array index //-------------------- // If valid tag found in tag table, evict it in both itag & dtag PR_INFO ("evict", MON_INFO, psprintf("EVICTION evictVector PA{[39:6],6'b0}=%h evict_tag(pa[39:11])=%h evict_index(pa[10:6])=%h core=%0d mem_index(pa[10:4])=%h", evict_pa,evict_tag,evict_index,cpuId,mem_index)); //-------------------- // If valid tag found in tag table, evict it in both itag & dtag i_vect = 0; d_vect = 0; e_vect = 0; // evict the pa in all cores L1 cache for (i=0; i<=7; i=i+1) { if (core_enable[i]) { itag[i].evict_group(evict_tag, evict_index, i_vect); dtag[i].evict_group(evict_tag, evict_index, d_vect); if ((i_vect!=0)|(d_vect!=0)) { targets[i] = 1'b1; } // Create invalidation vector that is returned in CPX pkt e_vect = e_vect | i_vect | d_vect; } } if (e_vect) { //-------------------- // Debug messages PR_INFO ("evict", MON_INFO, psprintf("EVICTION evictVector PA[39:0]=%h tag=%h evict_index(pa[10:6])=%h mem_index(pa[10:4])=%h+",evict_pa,evict_tag,evict_index,mem_index)); PR_INFO ("evict", MON_INFO, psprintf("EVICTION \taddr: 0x%12h 0x%12h 0x%12h 0x%12h", evict_pa+48,evict_pa+32, evict_pa+16, evict_pa)); PR_INFO ("evict", MON_INFO, psprintf("EVICTION \titag: Vinv[111:88] = %h [87:56] = %h [55:32] = %h [31:0] = %h ", i_vect[111:88],i_vect[87:56],i_vect[55:32],i_vect[31:0])); PR_INFO ("evict", MON_INFO, psprintf("EVICTION \tdtag: Vinv[111:88] = %h [87:56] = %h [55:32] = %h [31:0] = %h ", d_vect[111:88],d_vect[87:56],d_vect[55:32],d_vect[31:0])); PR_INFO ("evict", MON_INFO, psprintf("EVICTION \t Vinv[111:88] = %h [87:56] = %h [55:32] = %h [31:0] = %h ", e_vect[111:88],e_vect[87:56],e_vect[55:32],e_vect[31:0])); // Create Vinv as defined in CCX packet spec evictVector[127:117] = 11'b0; evictVector[116:112] = evict_index; evictVector[111:0] = e_vect; } else { // not an error because time elapsed and the L1 tags have changed! } #endif #endif #endif }