// ========== Copyright Header Begin ========================================== // // OpenSPARC T2 Processor File: ccxDevBaseBFM.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 // uncomment to debug //#define CCXDEVBASEBFM_DEBUG //#define CCXDEVBASEBFM_DEBUG2 #define CLASSNAME CcxDevBaseBFM virtual class CLASSNAME { // Keep track of load requests to same L2 cache line. // Line is 64 bytes ([5:0] = 0, addr[63:6]). // Index to this array will be addr[63:6]. // Array holds: // count in [63:32], number of requests active for this line. // cycle (time) in [31:0], most recent request for the line will go out at this time. // Next response for line will have to be AFTER that cycle/time. // // On request, query lineHash: // If hit, fullDelay = lineHash[LINECYCLE] + randDelay. // Always, lineHash[addr[63:6]] = {count++,fullDelay} // // On our non-dropped response: // lineHash[addr[63:6]] = {count--,fullDelay} // if !count, delete lineHash[addr[63:6]] protected reg [63:0] lineHash[]; protected reg ccxOnly; // only testing ccx protected integer myPort; protected string name; // protected reg [145:0] userRespSig []; // signature hash of packets that require // // a specific user provided response, not auto. protected BasePkt expectedSig []; // signature hash of packets that should // arrive at this port. idx = sig, data = pkt. protected reg passive; // we are watching port only, HW is driving. protected integer outstandingReqs; // mailboxes, one per CCX destination protected integer outBox, bypassBox; // bypassBox for fast response. // box conters protected integer outBoxCnt, bypassBoxCnt; protected integer boxLock; protected reg [145:0] idleData; protected reg flagUnexpected; protected integer stallStart; protected integer stallStop; task new(integer instatnce, reg passiveIn=0, string nameIn); task send(BasePkt pktHndl, integer fastResp=0); function reg dataEqual (reg [63:0] data1, reg [63:0] data2, reg [7:0] size); // protected task serviceSends(reg type); protected task serviceSends2(reg type); virtual task recv(BasePkt pktHndl); virtual task cancelRecv(BasePkt pktHndl); local task popQ(var reg [1:0] bufCount[9], var BasePkt slots[3][9], integer gntTarget, integer qSize, integer dropTarget); protected function integer manyHot(reg [63:0] vec); protected function integer whichHot(reg [63:0] vec, reg check=0); protected function integer ordering(BasePkt basePkt, string text); } task CLASSNAME::new(integer instatnce, reg passiveIn=0, string nameIn) { integer i; name = nameIn; passive = passiveIn; myPort = instatnce; printf("%7d %s::new creating BFM on port %0d (passive=%0b)\n",get_time(LO),nameIn,instatnce,passiveIn); outstandingReqs = 0; boxLock = alloc( SEMAPHORE, 0, 1, 1 ); if (!passive) { outBoxCnt = 0; bypassBoxCnt =0; outBox = alloc (MAILBOX,0,1); bypassBox = alloc (MAILBOX,0,1); } // review idleData = {urandom(),urandom(),urandom(),urandom(),urandom()}; } // Queue pkt for service task CLASSNAME::send(BasePkt pktHndl, integer fastResp) { // do not drive on this port if (passive) { printf("ERROR FAIL: attempt to send packet on passive port %d!!!\n", myPort); error(""); return; } semaphore_get(WAIT, boxLock, 1 ); if (fastResp) { mailbox_put(bypassBox,pktHndl); bypassBoxCnt++; } else { mailbox_put(outBox,pktHndl); outBoxCnt++; } #ifdef CCXDEVBASEBFM_DEBUG2 printf("%0d: CcxDevBaseBFM[%2d]::send mailbox_put: bypassBoxCnt=%0d outBoxCnt=%0d vec=%0h\n", get_time(LO),myPort,bypassBoxCnt,outBoxCnt,pktHndl.getVector()); #endif semaphore_put(boxLock, 1 ); } // Compare 2 data DWs using size as a 8 bit mask function reg CLASSNAME::dataEqual (reg [63:0] data1, reg [63:0] data2, reg [7:0] size) { integer i; dataEqual = 1; for (i=0; i<8; i++) { if ((data1[(i*8)+7:i*8] !== data2[(i*8)+7:i*8]) && size[i]) { dataEqual = 0; break; } } } // it is possible to have data asserted accross 8 clocks if all cores are // getting a packet. could send to all 8 targets in 9 clocks! // grant tells us that the CCX has pulled from the buffer and an entry // is now free no matter how long it takes. // It is the responsibility of the source to keep track of the number of // entries free in the FIFO. The PCX returns a grant signal to indicate // that access to the target was granted. Because the grant signal // arrives AT LEAST one (two) cycles after the request, some requests may be // speculative. If a grant is not received on the cycle after the // speculative request, that means the request was not accepted and the // packet was dropped. In this case, the sender must cancel any action taken // when the packet was issued to the PCX and retry the request later. // atomic request should never be dropped. They are sent only when // there is room for two entries in the CCX fifo (from the SPC side). // atomic responses (IFILL) must go back to back but the CCX fifo need // not be empty. if ifill #2 gets dropped, the retry only asserts req, // not atomic. For atomics, there is 1 req for both packets (unless ifill // #2 gets dropped), but there are 2 gnts. // When broadcasting invalidations, every target fifo targeted must not be // full. Will have to wait for fifo space when broadcasting. No speculating! review // Service mailboxes and drive pins of port. Fast resp box has priority. // This task is forked off in the extended classes. // task CLASSNAME::serviceSends(reg type) { // // BasePkt sndPkt; // CpxPkt cpxSndPkt; // ccxPort portVar; // reg casAtomicWait = 0; // reg gotGrant = 0; // integer start = 0; // integer dropBit = 0; // integer valid = 0; // integer dstPort; // will be 0-9 // integer i, j; // integer offset; // integer targets; // 8 or 9 ports // integer slot=0; // integer recvTarget=0; // integer gntTarget=0; // integer dropTarget=99; // target dropped. 99 means none dropped this clk // integer dropTargetIF2 = 99; // dropped IFILL #2 pkts accross targets (target id). // // // keep state of CCX 2 entry queue // reg [8:0] dropped = 0; // accumulated dropped pkts accross targets. Can be many hot. // reg [1:0] count [9] = {0,0,0,0,0,0,0,0,0}; // BasePkt dropPkt [9]; // BasePkt reqedPkt; // // integer qSize = 3; // BasePkt slots [3] [9]; // x packets, over 8 or 9 ports // // index, assuming we are streaming. May not get past 0 if !back2back pkts. // // 0: pkt from 2 reqs back // // 1: pkt from 1 reqs back // // 2: pkt driven this clk // // reg multicastWait = 0; // waiting/spinning for all target ports to be not-full // // // if (passive) return; // // // tmp holder for cast_assign // cpxSndPkt = new(); // // for (i=0; i 1200 && myPort == 8) vera_plot("vera_plot",DEBUSSY, "this.*", 1); // // //// block for sending req and data. //// // // give priority to any previously dropped packets. // { // // // if reqedPkt not null, previous clk did the req for this pkt // // so we must send it now. // if (reqedPkt !== null) { // // review for multicast // recvTarget = 0; // while(reqedPkt.recvPorts[recvTarget] !== 1) recvTarget++; // //recvTarget = reqedPkt.recvPorts - offset; // stay below 9 // // portVar.$datao <= reqedPkt.getVector(); // // #ifdef CCXDEVBASEBFM_DEBUG // printf("%0d: CcxDevBaseBFM[%2d]::serviceSends drive data port/target/tid:%0d/%0d/%0d COUNT now is=%0d vec=%0h\n",get_time(LO),myPort,myPort,recvTarget,reqedPkt.tid,count[recvTarget],reqedPkt.getVector()); // { integer x; // for (x=0;x= 10328) breakpoint; // // //if (myPort == 11 && count[dropTarget] > 3) breakpoint; // // popQ(count, // // slots, // // dropTarget, // // qSize, // // dropTarget); // // } else { // // // will send the dropped pkt later. // dropTargetIF2 = dropTarget; // used later by pkt send block // } // else { // // will send the dropped pkt later. // dropped[8:0] = 1 << dropTarget; // used later by pkt send block // // // #ifdef CCXDEVBASEBFM_DEBUG // printf("%0d: CcxDevBaseBFM[%2d]::serviceSends pop, will have DROPPED pkt for this req port/targets/tid:%0d/%h/%0d COUNT-- now vec=%0h\n",get_time(LO),myPort,myPort,dropPkt[dropTarget].recvPorts,dropPkt[dropTarget].tid,count[dropTarget]-1,dropPkt[dropTarget].getVector()); // //dropPkt[dropTarget].printPkt(); // #endif // // // // pull it from Q, since pkt not in RTL Q // // for (slot=0; slot 2) // count[dropTarget]--; // // // pull it from Q, since pkt not in RTL Q // slots[count[dropTarget]][dropTarget] = null; // // #ifdef CCXDEVBASEBFM_DEBUG // {integer x; // for (x=0;x 15) { // fork // { // if (count[0] == 0 && count[1] == 0 && count[2] == 0 && count[3] == 0 && // count[4] == 0 && count[5] == 0 && count[6] == 0 && count[7] == 0 && // count[8] == 0 && dropped == 0 && reqedPkt == null) // wait_var(bypassBoxCnt,outBoxCnt); // } // { // @(posedge portVar.$gnt); // } // join any // // if (portVar.$clk) @(negedge portVar.$clk); // //if (myPort == 16) printf("%0d: port %0d looping...\n", get_time(LO),myPort); // } // // } // while 1 // } task CLASSNAME::popQ(var reg [1:0] bufCount[9], var BasePkt slots[3][9], integer gntTarget, integer qSize, integer dropTarget) { integer slot = 0; integer x = 0; reg [31:0] respTime = 0; reg [63:0] cnt = 0, tmp64; reg [31:0] line; #ifdef CCXDEVMEMBFM_DEBUG slots[0][gntTarget].print(myPort); #endif bufCount[gntTarget]--; #ifdef CCXDEVBASEBFM_DEBUG printf("%0d: CcxDevBaseBFM[%2d]::serviceSends pop, BUFCOUNT-- for target %0d is now %0d.\n",get_time(LO),myPort,gntTarget,bufCount[gntTarget]); { integer x; for (x=0;x= 6167 && myPort == 9) breakpoint; line = slots[0][gntTarget].addr; line = line & CACHE_LINE_MASK; // if (get_cycle() >= 6167 && myPort == 9 && // slots[0][gntTarget].tid == 0 && line == 8'h40) breakpoint; // update ordering hash for CPX pkts. // for multicast packets, ONLY call this for the lowest target. if (gntTarget == whichHot(slots[0][gntTarget].targetPorts)) void = ordering(slots[0][gntTarget], "UPDATE"); } } // was grant from our response to a SPC request? // look at oldest ungranted packet if (slots[0][gntTarget].ccxSourced && slots[0][gntTarget].decGntTarget == gntTarget) { // we have successfully responded to a ccx sourced request pkt. // if pkt was a multicast, dont dec outstandingReqs on each gnt, // just one of them. Will use the lowest target as the one to trigger // the decrement. outstandingReqs--; #ifdef CCXDEVBASEBFM_DEBUG printf("%0d: CcxDevBaseBFM[%2d]::serviceSends pop, got grant port/target/tid:%0d/%0d/%0d outstandingReqs--=%0d, vec=%0h\n",get_time(LO),myPort,myPort,gntTarget,slots[0][gntTarget].tid,outstandingReqs,slots[0][gntTarget].getVector()); #endif if (outstandingReqs < 0 || outstandingReqs > (stallStart*3)) { printf("failing packet vvvvvvvvvvvvvv\n"); slots[0][gntTarget].print(myPort); printf("%0d: CcxDevBaseBFM[%2d]::serviceSends pop ERROR FAIL: outstandingReqs count too high/low after above pkt sent (OR=%0d, stallStart=%0d, burst=%0d)\n",get_time(LO),myPort,outstandingReqs,stallStart,gParam.burstAmount); printf ("%0d: CcxDevBaseBFM[%2d]::serviceSends pop will delay exit by 2 clks\n",get_time(LO),myPort); repeat (2) @(posedge CLOCK); error("outstandingReqs not right!\n"); } } // if ccxSourced // shift for Q pop for (slot=0; slot>= 1; } } // which bit set? if return is 99, there was 0 or > 1 hot. // returns the lowest bit number set. function integer CLASSNAME::whichHot(reg [63:0] vec, reg check=1) { whichHot = 0; while(vec[whichHot] !== 1) whichHot++; // none hot if (check && vec == 0) whichHot=99; // >1 hot if (check && manyHot(vec) > 1) whichHot=99; } // returns wait count for response. Updates cache line hash that // keeps track of when the latest response for a L2 cache line will go out. // // If we are a NCU, everything is in order received! function integer CLASSNAME::ordering(BasePkt basePkt, string text) { CpxPkt rspPkt; reg [31:0] wait, respTime=0, curTime, tmp; reg [63:0] count=0, tmp64; reg [31:0] line; reg ifill = 0; reg exist = 0; cast_assign(rspPkt, basePkt); curTime = get_cycle(); // this case prevents all possibility of reordering. // if (gParam.respDelayMax[myPort] == gParam.respDelayMin[myPort]) { // ordering = gParam.respDelayMax[myPort]; // // #ifdef CCXDEVMEMBFM_DEBUG // printf("%0d: CcxDevMemBFM[%2d]::ordering %s: addr=0x%0h, curTime=%0d, wait=%0d\n",get_time(LO),myPort,text,line,curTime,ordering); // #endif // return; // } if (myPort == DEV_NCU) { // just keep track of tha latest response time in [0] only respTime = lineHash[0]; // get time/cycle // #ifdef CCXDEVBASEBFM_DEBUG // printf("%0d: CcxDevMemBFM[%2d]::ordering %s: existing addr=0x%0h, respTime=%0d\n",get_time(LO),myPort,text,rspPkt.addr,lineHash[0]); // #endif // if current time is < latest resp then we need to add the difference to // a random time. (respTime - curTime) else just delay a random time. // wait = urandom_range(gParam.respDelayMax[myPort], // gParam.respDelayMin[myPort]); wait = rspPkt.responseDelay; if (curTime < respTime) wait = wait + respTime - curTime; // update entry. lineHash[0] = curTime + wait; #ifdef CCXDEVBASEBFM_DEBUG printf("%0d: CcxDevBaseBFM[%2d]::ordering %9s: NCU addr=0x%h, desired respTime=%0d\n",get_time(LO),myPort,text,rspPkt.addr,lineHash[0]); #endif } else { line = rspPkt.addr[31:0]; line = line & CACHE_LINE_MASK; if (text == "UPDATE") { // this HAS to be at the end of the time tick // to get the accurate count value fork { suspend_thread(); // this HAS to be at the end of the time tick if (assoc_index(CHECK,lineHash,line)) { tmp64 = lineHash[line]; respTime = tmp64[31:0]; // get time/cycle count = tmp64[63:32]; // get count if (count == 1) { #ifdef CCXDEVBASEBFM_DEBUG printf("%0d: CcxDevBaseBFM[%2d]::ordering DELETE: existing line=0x%5h, tid=%0d, vec=%h\n",get_time(LO),myPort,line,basePkt.tid,rspPkt.getVector()); rspPkt.print(myPort); #endif // this HAS to be at the end of the time tick // or it will be deleted when other threads still need to see it. void = assoc_index(DELETE,lineHash,line); } else { count--; lineHash[line] = {count[31:0],respTime[31:0]}; #ifdef CCXDEVBASEBFM_DEBUG printf("%0d: CcxDevBaseBFM[%2d]::ordering DECREMNT: existing line=0x%5h, tid=%0d, count=%0d, vec=%h\n",get_time(LO),myPort,line,basePkt.tid,count,rspPkt.getVector()); rspPkt.print(myPort); #endif } } } join none return; } if (text == "IFILL") ifill = 1; // already responding to this line? if (assoc_index(CHECK,lineHash,line)) { tmp64 = lineHash[line]; respTime = tmp64[31:0]; // get time/cycle count = tmp64[63:32]; // get count exist = 1; } // if current time is < latest resp for this line // then we need to add the difference to a random time. (respTime - curTime) // else just delay a random time. // wait = urandom_range(gParam.respDelayMax[myPort], // gParam.respDelayMin[myPort]); // need a shorter time for CAS and SWAP second packets. if (text == "SWAP ACK" || text == "CAS ACK") { wait = rspPkt.pkt2Delay; // 1-3 #ifdef CCXDEVBASEBFM_DEBUG printf("%0d: CcxDevBaseBFM[%2d]::ordering %9s: wait changed to %0d\n",get_time(LO), myPort, text, wait); #endif } else { wait = rspPkt.responseDelay; } if (curTime < respTime) { wait = wait + (respTime - curTime); #ifdef CCXDEVBASEBFM_DEBUG printf("%0d: CcxDevBaseBFM[%2d]::ordering %9s: wait fixed to be %0d\n",get_time(LO), myPort, text, wait); #endif } count++; // update/create entry. ifill response has 2 pkts so second // will go out 1 clock later. Record that extra clock. respTime = curTime + wait + ifill; lineHash[line] = {count[31:0],respTime[31:0]}; #ifdef CCXDEVBASEBFM_DEBUG if (exist) printf("%0d: CcxDevBaseBFM[%2d]::ordering %9s: existing line=0x%5h, tid=%0d, count=%0d, respTime=%0d, vec=%h\n",get_time(LO),myPort,text,line,rspPkt.tid,count,respTime,basePkt.getVector()); else printf("%0d: CcxDevBaseBFM[%2d]::ordering %9s: initial line=0x%5h, tid=%0d, count=%0d, desired respTime=%0d, vec=%h\n",get_time(LO),myPort,text,line,rspPkt.tid,count,respTime,basePkt.getVector()); #endif } ordering = wait; } /////////////////////////////////////////////////////////////////////////////// // it is possible to have data asserted accross 8 clocks if all cores are // getting a packet. could send to all 8 targets in 9 clocks! // grant tells us that the CCX has pulled from the buffer and an entry // is now free no matter how long it takes. // It is the responsibility of the source to keep track of the number of // entries free in the FIFO. The PCX returns a grant signal to indicate // that access to the target was granted. Because the grant signal // arrives AT LEAST one (two) cycles after the request, some requests may be // speculative. If a grant is not received on the cycle after the // speculative request, that means the request was not accepted and the // packet was dropped. In this case, the sender must cancel any action taken // when the packet was issued to the PCX and retry the request later. // atomic request should never be dropped. They are sent only when // there is room for two entries in the CCX fifo (from the SPC side). // atomic responses (IFILL) must go back to back but the CCX fifo need // not be empty. if ifill #2 gets dropped, the retry only asserts req, // not atomic. For atomics, there is 1 req for both packets (unless ifill // #2 gets dropped), but there are 2 gnts. // When broadcasting invalidations, every target fifo targeted must not be // full. Will have to wait for fifo space when broadcasting. No speculating! review // Service mailboxes and drive pins of port. Fast resp box has priority. // This task is forked off in the extended classes. task CLASSNAME::serviceSends2(reg type) { BasePkt sndPkt; CpxPkt cpxSndPkt; BasePkt reqedPkt; ccxPort portVar; reg gotGrant = 0; integer start = 0; integer valid = 0; integer dstPort; // will be 0-9 integer i, j; reg [8:0] tmp9; integer offset; integer targetsAvial; // 8 or 9 ports integer slot=0; integer recvTarget=0; reg [8:0] recvTargets=0; // targets to request, from pkt integer gntTarget=0; integer tmpTarget=0; integer dropTarget=99; // target dropped. 99 means none dropped this clk reg dropped = 0; // have dropped pkt BasePkt dropPkt; integer dropTargetIF2 = 99; // dropped IFILL #2 pkts accross targets (target id). // keep state of CCX 2 entry queue reg [1:0] bufCount [9] = {0,0,0,0,0,0,0,0,0}; integer qSize = 3; BasePkt slots [3] [9]; // x packets, over 8 or 9 ports // index, assuming we are streaming. May not get past 0 if !back2back pkts. // 0: pkt from 2 reqs back // 1: pkt from 1 reqs back // 2: pkt driven this clk reg [8:0] casAtomicWait = 0; // waiting/spinning for target ports to be empty reg [8:0] fullBufferWait = 0; // waiting/spinning for target ports to be not-full reg noSpeculation = 0; // debug if (passive) return; if (myPort == DEV_NCU) noSpeculation = 1; // tmp holder for cast_assign cpxSndPkt = new(); for (i=0; i 1200 && myPort == 8) vera_plot("vera_plot",DEBUSSY, "this.*", 1); //// block for sending req and data. //// // give priority to any previously dropped packet. { // if reqedPkt not null, previous clk did the req for this pkt // so we must send it now. if (reqedPkt !== null) { recvTargets = reqedPkt.targetPorts; recvTarget = 0; while(recvTargets[recvTarget] !== 1) recvTarget++; reqedPkt.decGntTarget = recvTarget; // for multicast portVar.$datao <= reqedPkt.getVector(); #ifdef CCXDEVBASEBFM_DEBUG printf("%0d: CcxDevBaseBFM[%2d]::serviceSends drive data port/targets/tid:%0d/%0d/%0d COUNT now is=%0d vec=%0h\n",get_time(LO),myPort,myPort,recvTargets,reqedPkt.tid,bufCount[recvTarget],reqedPkt.getVector()); { integer x , y; for (y=0;y 1) { // multicasting // check every target for buffer space //printf("%0d: CcxDevBaseBFM[%2d]::serviceSends multicast seen on peek!\n",get_time(LO),myPort); tmp9 = sndPkt.targetPorts; for (i=0;i 1) { fullBufferWait[i] = 1; // not all buffers have space, this target //printf("%0d: CcxDevBaseBFM[%2d]::serviceSends multicast fullBufferWait[%0d] set (%b)\n",get_time(LO),myPort,i,fullBufferWait); } } // multicast // speculation turned off if (noSpeculation && bufCount[whichHot(sndPkt.targetPorts)] > 1) fullBufferWait[whichHot(sndPkt.targetPorts)] = 1; // get a new pkt to send if not waiting if (!casAtomicWait && !fullBufferWait) { if (bypassBoxCnt) { valid = mailbox_get(NO_WAIT,bypassBox,sndPkt); bypassBoxCnt--; #ifdef CCXDEVBASEBFM_DEBUG printf("%0d: CcxDevBaseBFM[%2d]::serviceSends: got packet from bypassBox: bypassBoxCnt=%0d outBoxCnt=%0d latency=%0d vec=%0h\n",get_time(LO),myPort,bypassBoxCnt,outBoxCnt,get_time(LO)-sndPkt.reqTime,sndPkt.getVector()); #endif } else { valid = mailbox_get(NO_WAIT,outBox,sndPkt); outBoxCnt--; #ifdef CCXDEVBASEBFM_DEBUG printf("%0d: CcxDevBaseBFM[%2d]::serviceSends: got packet from outBox: bypassBoxCnt=%0d outBoxCnt=%0d latency=%0d vec=%0h\n",get_time(LO),myPort,bypassBoxCnt,outBoxCnt,get_time(LO)-sndPkt.reqTime,sndPkt.getVector()); #endif } recvTargets = sndPkt.targetPorts; recvTarget = 0; while(recvTargets[recvTarget] !== 1) recvTarget++; sndPkt.decGntTarget = recvTarget; // for multicast // now drive chosen pkt req to chosen target // and store chosen pkt into reqedPkt for data send on next clk. // Second pkt of atomic pair does not req. if (sndPkt.atomic == 2) { portVar.$req <= 0; portVar.$atmo <= 0; } else { portVar.$req <= recvTargets; // 1 << recvTarget; if (sndPkt.atomic == 1 && myPort !== DEV_NCU) portVar.$atmo <= 1; // if (manyHot(recvTargets) > 1) printf("%0d: CcxDevBaseBFM[%2d]::serviceSends multicast seen on req!\n",get_time(LO),myPort); } // data to send next clk, doing req this clk reqedPkt = sndPkt; // push pkt. for (i=0;i 2) bufCount[dropTarget]--; // pull it from Q, since pkt not in RTL Q slots[bufCount[dropTarget]][dropTarget] = null; #ifdef CCXDEVBASEBFM_DEBUG {integer x; for (x=0;x 15) { fork { if (bufCount[0] == 0 && bufCount[1] == 0 && bufCount[2] == 0 && bufCount[3] == 0 && bufCount[4] == 0 && bufCount[5] == 0 && bufCount[6] == 0 && bufCount[7] == 0 && bufCount[8] == 0 && dropped == 0 && reqedPkt == null) wait_var(bypassBoxCnt,outBoxCnt); } { @(posedge portVar.$gnt); } join any if (portVar.$clk) @(negedge portVar.$clk); //if (myPort == 16) printf("%0d: port %0d looping...\n", get_time(LO),myPort); } } // while 1 }