Initial commit of OpenSPARC T2 design and verification files.
[OpenSPARC-T2-DV] / verif / env / common / vera / ccxDevices / ccxDevBaseBFM.vr
CommitLineData
86530b38
AT
1// ========== Copyright Header Begin ==========================================
2//
3// OpenSPARC T2 Processor File: ccxDevBaseBFM.vr
4// Copyright (C) 1995-2007 Sun Microsystems, Inc. All Rights Reserved
5// 4150 Network Circle, Santa Clara, California 95054, U.S.A.
6//
7// * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
8//
9// This program is free software; you can redistribute it and/or modify
10// it under the terms of the GNU General Public License as published by
11// the Free Software Foundation; version 2 of the License.
12//
13// This program is distributed in the hope that it will be useful,
14// but WITHOUT ANY WARRANTY; without even the implied warranty of
15// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16// GNU General Public License for more details.
17//
18// You should have received a copy of the GNU General Public License
19// along with this program; if not, write to the Free Software
20// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21//
22// For the avoidance of doubt, and except that if any non-GPL license
23// choice is available it will apply instead, Sun elects to use only
24// the General Public License version 2 (GPLv2) at this time for any
25// software where a choice of GPL license versions is made
26// available with the language indicating that GPLv2 or any later version
27// may be used, or where a choice of which version of the GPL is applied is
28// otherwise unspecified.
29//
30// Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
31// CA 95054 USA or visit www.sun.com if you need additional information or
32// have any questions.
33//
34// ========== Copyright Header End ============================================
35#include <vera_defines.vrh>
36
37// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
38// To use this class, you must have in your bench a files called globals.vri
39// that has all global extern declerations in it.
40#include <globals.vri>
41
42
43#include <ccxDevicesDefines.vri>
44// #include <defines.vri>
45#include <std_display_defines.vri>
46
47#include <std_display_class.vrh>
48#include <basePktClass.vrh>
49#include <cpxPktClass.vrh>
50#include <pcxPktClass.vrh>
51#include <baseParamsClass.vrh>
52#include <sparcParams.vrh>
53
54
55// uncomment to debug
56//#define CCXDEVBASEBFM_DEBUG
57//#define CCXDEVBASEBFM_DEBUG2
58
59#define CLASSNAME CcxDevBaseBFM
60
61virtual class CLASSNAME {
62
63 // Keep track of load requests to same L2 cache line.
64 // Line is 64 bytes ([5:0] = 0, addr[63:6]).
65 // Index to this array will be addr[63:6].
66 // Array holds:
67 // count in [63:32], number of requests active for this line.
68 // cycle (time) in [31:0], most recent request for the line will go out at this time.
69 // Next response for line will have to be AFTER that cycle/time.
70 //
71 // On request, query lineHash:
72 // If hit, fullDelay = lineHash[LINECYCLE] + randDelay.
73 // Always, lineHash[addr[63:6]] = {count++,fullDelay}
74 //
75 // On our non-dropped response:
76 // lineHash[addr[63:6]] = {count--,fullDelay}
77 // if !count, delete lineHash[addr[63:6]]
78 protected reg [63:0] lineHash[];
79
80 protected reg ccxOnly; // only testing ccx
81 protected integer myPort;
82 protected string name;
83 // protected reg [145:0] userRespSig []; // signature hash of packets that require
84 // // a specific user provided response, not auto.
85
86 protected BasePkt expectedSig []; // signature hash of packets that should
87 // arrive at this port. idx = sig, data = pkt.
88
89 protected reg passive; // we are watching port only, HW is driving.
90 protected integer outstandingReqs;
91
92 // mailboxes, one per CCX destination
93 protected integer outBox, bypassBox; // bypassBox for fast response.
94 // box conters
95 protected integer outBoxCnt, bypassBoxCnt;
96 protected integer boxLock;
97
98 protected reg [145:0] idleData;
99
100 protected reg flagUnexpected;
101
102 protected integer stallStart;
103 protected integer stallStop;
104
105 task new(integer instatnce, reg passiveIn=0, string nameIn);
106 task send(BasePkt pktHndl, integer fastResp=0);
107 function reg dataEqual (reg [63:0] data1,
108 reg [63:0] data2,
109 reg [7:0] size);
110
111// protected task serviceSends(reg type);
112 protected task serviceSends2(reg type);
113
114 virtual task recv(BasePkt pktHndl);
115 virtual task cancelRecv(BasePkt pktHndl);
116
117 local task popQ(var reg [1:0] bufCount[9],
118 var BasePkt slots[3][9],
119 integer gntTarget,
120 integer qSize,
121 integer dropTarget);
122 protected function integer manyHot(reg [63:0] vec);
123 protected function integer whichHot(reg [63:0] vec, reg check=0);
124 protected function integer ordering(BasePkt basePkt, string text);
125}
126
127task CLASSNAME::new(integer instatnce, reg passiveIn=0, string nameIn) {
128
129 integer i;
130
131 name = nameIn;
132 passive = passiveIn;
133 myPort = instatnce;
134
135 printf("%7d %s::new creating BFM on port %0d (passive=%0b)\n",get_time(LO),nameIn,instatnce,passiveIn);
136
137 outstandingReqs = 0;
138 boxLock = alloc( SEMAPHORE, 0, 1, 1 );
139
140 if (!passive) {
141 outBoxCnt = 0;
142 bypassBoxCnt =0;
143 outBox = alloc (MAILBOX,0,1);
144 bypassBox = alloc (MAILBOX,0,1);
145 }
146
147 // review
148 idleData = {urandom(),urandom(),urandom(),urandom(),urandom()};
149
150}
151
152// Queue pkt for service
153task CLASSNAME::send(BasePkt pktHndl, integer fastResp) {
154
155 // do not drive on this port
156 if (passive) {
157 printf("ERROR FAIL: attempt to send packet on passive port %d!!!\n", myPort);
158 error("");
159 return;
160 }
161
162 semaphore_get(WAIT, boxLock, 1 );
163
164 if (fastResp) {
165 mailbox_put(bypassBox,pktHndl);
166 bypassBoxCnt++;
167 } else {
168 mailbox_put(outBox,pktHndl);
169 outBoxCnt++;
170 }
171
172#ifdef CCXDEVBASEBFM_DEBUG2
173 printf("%0d: CcxDevBaseBFM[%2d]::send mailbox_put: bypassBoxCnt=%0d outBoxCnt=%0d vec=%0h\n", get_time(LO),myPort,bypassBoxCnt,outBoxCnt,pktHndl.getVector());
174#endif
175
176 semaphore_put(boxLock, 1 );
177}
178
179
180
181// Compare 2 data DWs using size as a 8 bit mask
182function reg CLASSNAME::dataEqual (reg [63:0] data1,
183 reg [63:0] data2,
184 reg [7:0] size) {
185 integer i;
186 dataEqual = 1;
187
188 for (i=0; i<8; i++) {
189 if ((data1[(i*8)+7:i*8] !== data2[(i*8)+7:i*8]) && size[i]) {
190 dataEqual = 0;
191 break;
192 }
193 }
194}
195
196
197// it is possible to have data asserted accross 8 clocks if all cores are
198// getting a packet. could send to all 8 targets in 9 clocks!
199
200// grant tells us that the CCX has pulled from the buffer and an entry
201// is now free no matter how long it takes.
202
203// It is the responsibility of the source to keep track of the number of
204// entries free in the FIFO. The PCX returns a grant signal to indicate
205// that access to the target was granted. Because the grant signal
206// arrives AT LEAST one (two) cycles after the request, some requests may be
207// speculative. If a grant is not received on the cycle after the
208// speculative request, that means the request was not accepted and the
209// packet was dropped. In this case, the sender must cancel any action taken
210// when the packet was issued to the PCX and retry the request later.
211
212// atomic request should never be dropped. They are sent only when
213// there is room for two entries in the CCX fifo (from the SPC side).
214
215// atomic responses (IFILL) must go back to back but the CCX fifo need
216// not be empty. if ifill #2 gets dropped, the retry only asserts req,
217// not atomic. For atomics, there is 1 req for both packets (unless ifill
218// #2 gets dropped), but there are 2 gnts.
219
220// When broadcasting invalidations, every target fifo targeted must not be
221// full. Will have to wait for fifo space when broadcasting. No speculating! review
222
223// Service mailboxes and drive pins of port. Fast resp box has priority.
224// This task is forked off in the extended classes.
225// task CLASSNAME::serviceSends(reg type) {
226//
227// BasePkt sndPkt;
228// CpxPkt cpxSndPkt;
229// ccxPort portVar;
230// reg casAtomicWait = 0;
231// reg gotGrant = 0;
232// integer start = 0;
233// integer dropBit = 0;
234// integer valid = 0;
235// integer dstPort; // will be 0-9
236// integer i, j;
237// integer offset;
238// integer targets; // 8 or 9 ports
239// integer slot=0;
240// integer recvTarget=0;
241// integer gntTarget=0;
242// integer dropTarget=99; // target dropped. 99 means none dropped this clk
243// integer dropTargetIF2 = 99; // dropped IFILL #2 pkts accross targets (target id).
244//
245// // keep state of CCX 2 entry queue
246// reg [8:0] dropped = 0; // accumulated dropped pkts accross targets. Can be many hot.
247// reg [1:0] count [9] = {0,0,0,0,0,0,0,0,0};
248// BasePkt dropPkt [9];
249// BasePkt reqedPkt;
250//
251// integer qSize = 3;
252// BasePkt slots [3] [9]; // x packets, over 8 or 9 ports
253// // index, assuming we are streaming. May not get past 0 if !back2back pkts.
254// // 0: pkt from 2 reqs back
255// // 1: pkt from 1 reqs back
256// // 2: pkt driven this clk
257//
258// reg multicastWait = 0; // waiting/spinning for all target ports to be not-full
259//
260//
261// if (passive) return;
262//
263// // tmp holder for cast_assign
264// cpxSndPkt = new();
265//
266// for (i=0; i<qSize; i++)
267// for (j=0; j<9; j++)
268// slots[i][j] = null;
269//
270// if (type == PP_PCX) {
271// portVar = gPcxPort[myPort];
272// offset = 8; // target ports are 9-17
273// targets = 9;
274// }
275// else {
276// portVar = gCpxPort[myPort];
277// offset = 0; // target ports are 0-7
278// targets = 8;
279// }
280//
281// @(negedge portVar.$clk);
282//
283//
284// while (1) {
285// // if (get_cycle() > 1200 && myPort == 8) vera_plot("vera_plot",DEBUSSY, "this.*", 1);
286//
287// //// block for sending req and data. ////
288// // give priority to any previously dropped packets.
289// {
290//
291// // if reqedPkt not null, previous clk did the req for this pkt
292// // so we must send it now.
293// if (reqedPkt !== null) {
294// // review for multicast
295// recvTarget = 0;
296// while(reqedPkt.recvPorts[recvTarget] !== 1) recvTarget++;
297// //recvTarget = reqedPkt.recvPorts - offset; // stay below 9
298//
299// portVar.$datao <= reqedPkt.getVector();
300//
301// #ifdef CCXDEVBASEBFM_DEBUG
302// 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());
303// { integer x;
304// for (x=0;x<qSize;x++)
305// if (slots[x][recvTarget] !== null) printf("%0d: CcxDevBaseBFM[%2d]::serviceSends drive data dump port/targets/tid:%0d/%h/%0d vec[%2d]=%0h\n",get_time(LO),myPort,myPort,slots[x][recvTarget].recvPorts,slots[x][recvTarget].tid,x,slots[x][recvTarget].getVector());}
306// #endif
307//
308// // lastly
309// reqedPkt = null;
310// } else {
311// portVar.$datao <= IDLE_DATA;
312// }
313//
314//
315// // what packet will be next? need to *req* it 1 cycle before data.
316// //
317// // any dropped packets to send?
318// // if a target has a dropped pkt, send it rather than a new pkt.
319// if (dropTargetIF2 !== 99) { // need to hold IFILL #2 pkt on wires until taken
320// dropBit = dropTargetIF2;
321// reqedPkt = dropPkt[dropBit]; // current dropped IFILL #2 packet
322// portVar.$datao <= reqedPkt.getVector();
323//
324// #ifdef CCXDEVBASEBFM_DEBUG
325// printf("%0d: CcxDevBaseBFM[%2d]::serviceSends: holding IFILL #2 on wire until taken: targets=%h vec=%0h\n",get_time(LO),myPort,reqedPkt.recvPorts,reqedPkt.getVector());
326// #endif
327//
328// } else if (dropped[8:0]) {
329// // pick a dropped packet target at random by picking a random
330// // port to start a circular check at.
331// start = urandom_range(targets-1,0);
332// while (dropped[start%targets] == 0) {
333// // printf("%0d: start = %0d dropped[%2d]=%0d\n", get_time(LO),start,start%targets,dropped[start%targets]);
334// start++;
335// }
336// dropBit = start%targets;
337//
338// // printf("%0d: start = %0d dropped[%2d]=%0d dropBit=%0d\n", get_time(LO),start,start%targets,dropped[start%targets],dropBit);
339//
340// // now drive chosen pkt req to chosen target
341// // and store chosen pkt into reqedPkt for data send on next clk.
342// portVar.$req <= 1 << dropBit;
343//
344// // drive atomic on ifill pkt #1 retrys only, not pkt #2
345// if (dropPkt[dropBit].atomic == 1 && myPort !== DEV_NCU)
346// portVar.$atmo <= 1; // << dropBit;
347//
348// // data to send next clk, doing req this clk
349// reqedPkt = dropPkt[dropBit]; // previously dropped packet
350//
351// // push pkt.
352// slots[count[recvTarget]][recvTarget] = reqedPkt;
353//
354// // CCX Q is based on req being set, not data
355// count[dropBit]++;
356//
357// // printf("%0d: CcxDevBaseBFM[%2d]::serviceSends dropped req, COUNT++ for target %0d is %0d.\n",get_time(LO),myPort,recvTarget,count[recvTarget]);
358//
359// dropPkt[dropBit] = null;
360//
361// #ifdef CCXDEVBASEBFM_DEBUG
362// printf("%0d: CcxDevBaseBFM[%2d]::serviceSends: next clks pkt will be a DROP re-send: targets=%h dropped=%b vec=%0h\n",get_time(LO),myPort,reqedPkt.recvPorts,dropped,reqedPkt.getVector());
363// #endif
364// // packet for target no longer dropped (unless dropped again)
365// dropped[dropBit] = 0;
366//
367// }
368// // no dropped pkt to send, not waiting to multi cast, GET NEW PKT
369// else if ((bypassBoxCnt || outBoxCnt) && !multicastWait) {
370// // new pkt in mailbox
371// semaphore_get(WAIT, boxLock, 1 );
372//
373// // peek ahead for atomics (CAS). If the CCX target Q is not empty,
374// // we will have to wait for it to be. SPC sourced atomics must be together.
375// // this could be more effecient later...
376// if (myPort < 8) {
377// if (bypassBoxCnt) {
378// void = mailbox_get(COPY_NO_WAIT,bypassBox,sndPkt);
379// if (count[sndPkt.recvPort]) {
380// cast_assign(cpxSndPkt,sndPkt);
381// if (cpxSndPkt.rtntyp !== CPX_IFILL) casAtomicWait = cpxSndPkt.atmIf2;
382// }
383// } else if (outBoxCnt) {
384// void = mailbox_get(COPY_NO_WAIT,outBox,sndPkt);
385// if (count[sndPkt.recvPort]) {
386// cast_assign(cpxSndPkt,sndPkt);
387// if (cpxSndPkt.rtntyp !== CPX_IFILL) casAtomicWait = cpxSndPkt.atmIf2;
388// }
389// }
390// }
391//
392//
393// // get a new pkt to send
394// if (!casAtomicWait) {
395// if (bypassBoxCnt) {
396// valid = mailbox_get(NO_WAIT,bypassBox,sndPkt);
397// bypassBoxCnt--;
398// // review for multicast
399// recvTarget = 0;
400// while(sndPkt.recvPorts[recvTarget] !== 1) recvTarget++;
401// // recvTarget = sndPkt.recvPort - offset; // stay below 9;
402//
403// #ifdef CCXDEVBASEBFM_DEBUG
404// 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());
405// #endif
406// } else {
407// valid = mailbox_get(NO_WAIT,outBox,sndPkt);
408// outBoxCnt--;
409// // review for multicast
410// recvTarget = 0;
411// while(sndPkt.recvPorts[recvTarget] !== 1) recvTarget++;
412// // recvTarget = sndPkt.recvPort - offset; // stay below 9;
413//
414// #ifdef CCXDEVBASEBFM_DEBUG
415// 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());
416// #endif
417// }
418//
419// semaphore_put(boxLock, 1 );
420//
421// // now drive chosen pkt req to chosen target
422// // and store chosen pkt into reqedPkt for data send on next clk.
423// // Second pkt of atomic pair does not req.
424// if (sndPkt.atomic == 2) {
425// portVar.$req <= 0;
426// portVar.$atmo <= 0;
427// } else {
428// // review for multicast
429// portVar.$req <= 1 << recvTarget; // sndPkt.recvPorts; // 1 << recvTarget;
430// if (sndPkt.atomic == 1 && myPort !== DEV_NCU)
431// portVar.$atmo <= 1; // << recvTarget; // sndPkt.recvPorts; // 1 << recvTarget;
432// }
433//
434// // data to send next clk, doing req this clk
435// reqedPkt = sndPkt;
436//
437// // push pkt.
438// slots[count[recvTarget]][recvTarget] = reqedPkt;
439//
440// // CCX Q is based on req being set, not data
441// count[recvTarget]++;
442//
443// // printf("%0d: CcxDevBaseBFM[%2d]::serviceSends normal req, COUNT++ for target %0d is now %0d.\n",get_time(LO),myPort,recvTarget,count[recvTarget]);
444//
445// } // if !casAtomicWait
446// } else {
447// // no new pkt for next cycle
448// portVar.$req <= 0;
449// if (myPort !== DEV_NCU) portVar.$atmo <= 0;
450// reqedPkt = null;
451// }
452//
453// } // block for sending req and data
454//
455//
456//
457//
458// //// check grant block ////
459// {
460//
461// // #ifdef CCXDEVBASEBFM_DEBUG
462// // // only one port can drive these at a time
463// // if (myPort == 11) {
464// // probe_if.count0 = count[0] soft;
465// // probe_if.count1 = count[1] soft;
466// // probe_if.count2 = count[2] soft;
467// // probe_if.count3 = count[3] soft;
468// // probe_if.count4 = count[4] soft;
469// // probe_if.count5 = count[5] soft;
470// // probe_if.count6 = count[6] soft;
471// // probe_if.count7 = count[7] soft;
472// // }
473// // #endif
474//
475// // any grants in this cycle?
476// gotGrant = 0;
477// for (gntTarget=0;gntTarget<targets;gntTarget++) {
478// case (count[gntTarget]) {
479// 0: { // Q empty
480// if (portVar.$gnt[gntTarget]) {
481// error("%0d: CcxDevBaseBFM[%2d]::serviceSends ERROR FAIL port/target:%0d/%0d bad pop or unexpected grant on port (count was 0)!\n",get_time(LO),myPort,myPort,gntTarget);
482// }
483// }
484// 1: { // Q half full
485// if (portVar.$gnt[gntTarget]) {
486// gotGrant = 1;
487// }
488// }
489// 2: { // Q full
490// if (portVar.$gnt[gntTarget]) {
491// gotGrant = 1;
492// }
493// }
494// 3: { // did speculative send succeed?
495// // if Q already full, must get a grant in same cycle as our req or dropped
496// if (portVar.$gnt[gntTarget]) {
497// gotGrant = 1;
498// dropTarget = 99;
499// dropTargetIF2 = 99;
500// // printf("%0d: CcxDevBaseBFM[%2d]::serviceSends gotGrant, speculation SUCCESS, count for target %0d was %0d.\n",get_time(LO),myPort,gntTarget,count[gntTarget]);
501// } else {
502// // speculation failed
503// dropTarget = gntTarget;
504// // printf("%0d: CcxDevBaseBFM[%2d]::serviceSends gotGrant, NO grant, speculation FAIL, count for target %0d was %0d.\n",get_time(LO),myPort,gntTarget,count[gntTarget]);
505// }
506// }
507// default: {
508// error("%0d: CcxDevBaseBFM[%2d]::serviceSends: ERROR FAIL: port %0d Q count of %0d not right!\n",get_time(LO),myPort,myPort,count[gntTarget]);
509// }
510// }//case
511//
512//
513// /// ///
514// /// pop Q, packet made it out ///
515// /// ///
516// if (gotGrant) {
517//
518// // reset
519// gotGrant = 0;
520// popQ(count,
521// slots,
522// gntTarget,
523// qSize,
524// dropTarget);
525//
526//
527//
528// // if Q empty
529// if (!count[gntTarget]) casAtomicWait = 0;
530//
531// } // if (gotGrant)
532// } // for (gntTarget=0;gntTarget<targets;gntTarget++)
533// } // check grant blk
534//
535//
536//
537// //// block to handle dropped pkts. ////
538// // save off dropped pkt as last thing after data sends.
539// // deals with "dropTarget".
540// {
541// if (dropTarget !== 99) {
542// dropPkt[dropTarget] = slots[2][dropTarget];
543//
544//
545// // Special Case
546// // if dropped pkt was second ifill pkt (CAS2 never dropped)
547// // then keep driving packet data until we get a grant.
548// if (dropPkt[dropTarget].atomic == 2) {
549//
550// // if not seeing gnt now, need to hold this packet (reqedPkt) on wire
551// // for another clock, or more w/o setting req first.
552//
553// #ifdef CCXDEVBASEBFM_DEBUG
554// printf("%0d: CcxDevBaseBFM[%2d]::serviceSends DROPPED IFILL 2 waiting for grant port/targets/tid:%0d/%h/%0d vec=%0h\n",get_time(LO),myPort,myPort,dropPkt[dropTarget].recvPorts,dropPkt[dropTarget].tid,dropPkt[dropTarget].getVector());
555// #endif
556//
557// // if (!portVar.$gnt[dropTarget])
558// // @ (posedge portVar.$gnt[dropTarget]);
559// //
560
561// // if (myPort == 11 && get_cycle() >= 10328) breakpoint;
562// // //if (myPort == 11 && count[dropTarget] > 3) breakpoint;
563// // popQ(count,
564// // slots,
565// // dropTarget,
566// // qSize,
567// // dropTarget);
568// // } else {
569//
570// // will send the dropped pkt later.
571// dropTargetIF2 = dropTarget; // used later by pkt send block
572// }
573// else {
574// // will send the dropped pkt later.
575// dropped[8:0] = 1 << dropTarget; // used later by pkt send block
576//
577//
578// #ifdef CCXDEVBASEBFM_DEBUG
579// 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());
580// //dropPkt[dropTarget].printPkt();
581// #endif
582//
583// // // pull it from Q, since pkt not in RTL Q
584// // for (slot=0; slot<qSize-1; slot++) {
585// // slots[slot][dropTarget] = slots[slot+1][dropTarget];
586// // slots[slot+1][dropTarget] = null;
587// // }
588//
589// // dec count since dropped pkt not in Q (3 -> 2)
590// count[dropTarget]--;
591//
592// // pull it from Q, since pkt not in RTL Q
593// slots[count[dropTarget]][dropTarget] = null;
594//
595// #ifdef CCXDEVBASEBFM_DEBUG
596// {integer x;
597// for (x=0;x<qSize;x++)
598// if (slots[x][dropTarget] !== null) printf("%0d: CcxDevBaseBFM[%2d]::serviceSends post dropped pop dump port/target/tid:%0d/%0d/%0d vec[%2d]=%0h\n",get_time(LO),myPort,myPort,dropTarget,slots[x][dropTarget].tid,x,slots[x][dropTarget].getVector());}
599// #endif
600//
601// // reset
602// dropTarget = 99;
603//
604// }
605// }
606//
607// } // block to handle dropped pkts.
608//
609//
610// @(negedge portVar.$clk);
611//
612// // Block here on no box count, no gnt expected, no dropped pkt, etc
613// // Are we idle? If so, wake up on mailbox having a packet. Only makes
614// // sense for IOB since it has long periods of inactivity (about 85%-90%).
615// // Downside is that we will miss unexpected grants so watch for that too.
616// if (myPort < 8 && myPort > 15) {
617// fork
618// {
619// if (count[0] == 0 && count[1] == 0 && count[2] == 0 && count[3] == 0 &&
620// count[4] == 0 && count[5] == 0 && count[6] == 0 && count[7] == 0 &&
621// count[8] == 0 && dropped == 0 && reqedPkt == null)
622// wait_var(bypassBoxCnt,outBoxCnt);
623// }
624// {
625// @(posedge portVar.$gnt);
626// }
627// join any
628//
629// if (portVar.$clk) @(negedge portVar.$clk);
630// //if (myPort == 16) printf("%0d: port %0d looping...\n", get_time(LO),myPort);
631// }
632//
633// } // while 1
634// }
635
636
637
638task CLASSNAME::popQ(var reg [1:0] bufCount[9],
639 var BasePkt slots[3][9],
640 integer gntTarget,
641 integer qSize,
642 integer dropTarget)
643{
644
645 integer slot = 0;
646 integer x = 0;
647 reg [31:0] respTime = 0;
648 reg [63:0] cnt = 0, tmp64;
649 reg [31:0] line;
650
651#ifdef CCXDEVMEMBFM_DEBUG
652 slots[0][gntTarget].print(myPort);
653#endif
654
655 bufCount[gntTarget]--;
656
657#ifdef CCXDEVBASEBFM_DEBUG
658 printf("%0d: CcxDevBaseBFM[%2d]::serviceSends pop, BUFCOUNT-- for target %0d is now %0d.\n",get_time(LO),myPort,gntTarget,bufCount[gntTarget]);
659 { integer x;
660 for (x=0;x<qSize;x++)
661 if (slots[x][gntTarget] !== null) printf("%0d: CcxDevBaseBFM[%2d]::serviceSends pop dump port/targets/tid:%0d/%h/%0d vec[%2d]=%0h\n",get_time(LO),myPort,myPort,slots[x][gntTarget].targetPorts,slots[x][gntTarget].tid,x,slots[x][gntTarget].getVector());}
662#endif
663
664
665#ifdef CCXDEVBASEBFM_DEBUG
666 printf("%0d: CcxDevBaseBFM[%2d]::serviceSends pop, got grant port/target/tid:%0d/%0d/%0d bufCount=%0d<-%0d, dropped=%0d, ccxSourced=%0d, outstandingReqs=%0d, vec=%0h\n",get_time(LO),myPort,myPort,gntTarget,slots[0][gntTarget].tid,bufCount[gntTarget],bufCount[gntTarget]+1,dropTarget != 99 ? 1:0,slots[0][gntTarget].ccxSourced,outstandingReqs,slots[0][gntTarget].getVector());
667 //slots[0][gntTarget].print(myPort);
668#endif
669
670
671 if (slots[0][gntTarget].ccxSourced || slots[0][gntTarget].ccxSourced2) {
672
673 // Keep track of (order) requests to same L2 cache line.
674 // If a request is satisfied, we can forget about it.
675 if (myPort !== DEV_NCU) {
676//if (get_cycle() >= 6167 && myPort == 9) breakpoint;
677
678 line = slots[0][gntTarget].addr;
679 line = line & CACHE_LINE_MASK;
680// if (get_cycle() >= 6167 && myPort == 9 &&
681// slots[0][gntTarget].tid == 0 && line == 8'h40) breakpoint;
682
683 // update ordering hash for CPX pkts.
684 // for multicast packets, ONLY call this for the lowest target.
685 if (gntTarget == whichHot(slots[0][gntTarget].targetPorts))
686 void = ordering(slots[0][gntTarget], "UPDATE");
687
688 }
689 }
690
691 // was grant from our response to a SPC request?
692 // look at oldest ungranted packet
693 if (slots[0][gntTarget].ccxSourced && slots[0][gntTarget].decGntTarget == gntTarget) {
694 // we have successfully responded to a ccx sourced request pkt.
695 // if pkt was a multicast, dont dec outstandingReqs on each gnt,
696 // just one of them. Will use the lowest target as the one to trigger
697 // the decrement.
698 outstandingReqs--;
699
700#ifdef CCXDEVBASEBFM_DEBUG
701 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());
702#endif
703
704 if (outstandingReqs < 0 || outstandingReqs > (stallStart*3)) {
705 printf("failing packet vvvvvvvvvvvvvv\n");
706 slots[0][gntTarget].print(myPort);
707 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);
708 printf ("%0d: CcxDevBaseBFM[%2d]::serviceSends pop will delay exit by 2 clks\n",get_time(LO),myPort);
709 repeat (2) @(posedge CLOCK);
710 error("outstandingReqs not right!\n");
711 }
712
713 } // if ccxSourced
714
715
716 // shift for Q pop
717 for (slot=0; slot<qSize-1; slot++) {
718 slots[slot][gntTarget] = slots[slot+1][gntTarget];
719 slots[slot+1][gntTarget] = null;
720 }
721
722#ifdef CCXDEVBASEBFM_DEBUG
723 {
724 integer x;
725 for (x=0;x<qSize;x++)
726 if (slots[x][gntTarget] !== null) printf("%0d: CcxDevBaseBFM[%2d]::serviceSends pop dump port/targets/tid:%0d/%h/%0d vec[%2d]=%0h\n",get_time(LO),myPort,myPort,slots[x][gntTarget].targetPorts,slots[x][gntTarget].tid,x,slots[x][gntTarget].getVector());
727 }
728#endif
729 /// end pop ///
730
731}
732
733
734// how many bits set?
735function integer CLASSNAME::manyHot(reg [63:0] vec) {
736 manyHot = 0;
737 while (vec) {
738 manyHot += vec[0];
739 vec >>= 1;
740 }
741}
742
743// which bit set? if return is 99, there was 0 or > 1 hot.
744// returns the lowest bit number set.
745function integer CLASSNAME::whichHot(reg [63:0] vec, reg check=1) {
746 whichHot = 0;
747
748 while(vec[whichHot] !== 1) whichHot++;
749
750 // none hot
751 if (check && vec == 0) whichHot=99;
752 // >1 hot
753 if (check && manyHot(vec) > 1) whichHot=99;
754
755}
756
757
758
759// returns wait count for response. Updates cache line hash that
760// keeps track of when the latest response for a L2 cache line will go out.
761//
762// If we are a NCU, everything is in order received!
763function integer CLASSNAME::ordering(BasePkt basePkt, string text)
764
765{
766 CpxPkt rspPkt;
767 reg [31:0] wait, respTime=0, curTime, tmp;
768 reg [63:0] count=0, tmp64;
769 reg [31:0] line;
770 reg ifill = 0;
771 reg exist = 0;
772
773
774 cast_assign(rspPkt, basePkt);
775
776 curTime = get_cycle();
777
778
779 // this case prevents all possibility of reordering.
780// if (gParam.respDelayMax[myPort] == gParam.respDelayMin[myPort]) {
781// ordering = gParam.respDelayMax[myPort];
782//
783// #ifdef CCXDEVMEMBFM_DEBUG
784// printf("%0d: CcxDevMemBFM[%2d]::ordering %s: addr=0x%0h, curTime=%0d, wait=%0d\n",get_time(LO),myPort,text,line,curTime,ordering);
785// #endif
786// return;
787// }
788
789
790 if (myPort == DEV_NCU) {
791
792 // just keep track of tha latest response time in [0] only
793 respTime = lineHash[0]; // get time/cycle
794
795// #ifdef CCXDEVBASEBFM_DEBUG
796// printf("%0d: CcxDevMemBFM[%2d]::ordering %s: existing addr=0x%0h, respTime=%0d\n",get_time(LO),myPort,text,rspPkt.addr,lineHash[0]);
797// #endif
798
799 // if current time is < latest resp then we need to add the difference to
800 // a random time. (respTime - curTime) else just delay a random time.
801 // wait = urandom_range(gParam.respDelayMax[myPort],
802 // gParam.respDelayMin[myPort]);
803 wait = rspPkt.responseDelay;
804 if (curTime < respTime) wait = wait + respTime - curTime;
805
806 // update entry.
807 lineHash[0] = curTime + wait;
808
809#ifdef CCXDEVBASEBFM_DEBUG
810 printf("%0d: CcxDevBaseBFM[%2d]::ordering %9s: NCU addr=0x%h, desired respTime=%0d\n",get_time(LO),myPort,text,rspPkt.addr,lineHash[0]);
811#endif
812
813 } else {
814
815 line = rspPkt.addr[31:0];
816 line = line & CACHE_LINE_MASK;
817
818 if (text == "UPDATE") {
819 // this HAS to be at the end of the time tick
820 // to get the accurate count value
821 fork {
822 suspend_thread(); // this HAS to be at the end of the time tick
823 if (assoc_index(CHECK,lineHash,line)) {
824 tmp64 = lineHash[line];
825 respTime = tmp64[31:0]; // get time/cycle
826 count = tmp64[63:32]; // get count
827 if (count == 1) {
828#ifdef CCXDEVBASEBFM_DEBUG
829 printf("%0d: CcxDevBaseBFM[%2d]::ordering DELETE: existing line=0x%5h, tid=%0d, vec=%h\n",get_time(LO),myPort,line,basePkt.tid,rspPkt.getVector());
830 rspPkt.print(myPort);
831#endif
832 // this HAS to be at the end of the time tick
833 // or it will be deleted when other threads still need to see it.
834 void = assoc_index(DELETE,lineHash,line);
835
836 } else {
837 count--;
838 lineHash[line] = {count[31:0],respTime[31:0]};
839#ifdef CCXDEVBASEBFM_DEBUG
840 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());
841 rspPkt.print(myPort);
842#endif
843 }
844 }
845 } join none
846
847 return;
848 }
849
850
851 if (text == "IFILL") ifill = 1;
852
853 // already responding to this line?
854 if (assoc_index(CHECK,lineHash,line)) {
855 tmp64 = lineHash[line];
856 respTime = tmp64[31:0]; // get time/cycle
857 count = tmp64[63:32]; // get count
858 exist = 1;
859 }
860
861 // if current time is < latest resp for this line
862 // then we need to add the difference to a random time. (respTime - curTime)
863 // else just delay a random time.
864 // wait = urandom_range(gParam.respDelayMax[myPort],
865 // gParam.respDelayMin[myPort]);
866
867 // need a shorter time for CAS and SWAP second packets.
868 if (text == "SWAP ACK" || text == "CAS ACK") {
869 wait = rspPkt.pkt2Delay; // 1-3
870#ifdef CCXDEVBASEBFM_DEBUG
871 printf("%0d: CcxDevBaseBFM[%2d]::ordering %9s: wait changed to %0d\n",get_time(LO), myPort, text, wait);
872#endif
873 } else {
874 wait = rspPkt.responseDelay;
875 }
876
877 if (curTime < respTime) {
878 wait = wait + (respTime - curTime);
879#ifdef CCXDEVBASEBFM_DEBUG
880 printf("%0d: CcxDevBaseBFM[%2d]::ordering %9s: wait fixed to be %0d\n",get_time(LO), myPort, text, wait);
881#endif
882 }
883
884 count++;
885
886 // update/create entry. ifill response has 2 pkts so second
887 // will go out 1 clock later. Record that extra clock.
888 respTime = curTime + wait + ifill;
889
890 lineHash[line] = {count[31:0],respTime[31:0]};
891
892#ifdef CCXDEVBASEBFM_DEBUG
893 if (exist)
894 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());
895 else
896 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());
897#endif
898
899 }
900
901 ordering = wait;
902
903}
904
905///////////////////////////////////////////////////////////////////////////////
906// it is possible to have data asserted accross 8 clocks if all cores are
907// getting a packet. could send to all 8 targets in 9 clocks!
908
909// grant tells us that the CCX has pulled from the buffer and an entry
910// is now free no matter how long it takes.
911
912// It is the responsibility of the source to keep track of the number of
913// entries free in the FIFO. The PCX returns a grant signal to indicate
914// that access to the target was granted. Because the grant signal
915// arrives AT LEAST one (two) cycles after the request, some requests may be
916// speculative. If a grant is not received on the cycle after the
917// speculative request, that means the request was not accepted and the
918// packet was dropped. In this case, the sender must cancel any action taken
919// when the packet was issued to the PCX and retry the request later.
920
921// atomic request should never be dropped. They are sent only when
922// there is room for two entries in the CCX fifo (from the SPC side).
923
924// atomic responses (IFILL) must go back to back but the CCX fifo need
925// not be empty. if ifill #2 gets dropped, the retry only asserts req,
926// not atomic. For atomics, there is 1 req for both packets (unless ifill
927// #2 gets dropped), but there are 2 gnts.
928
929// When broadcasting invalidations, every target fifo targeted must not be
930// full. Will have to wait for fifo space when broadcasting. No speculating! review
931
932// Service mailboxes and drive pins of port. Fast resp box has priority.
933// This task is forked off in the extended classes.
934task CLASSNAME::serviceSends2(reg type) {
935
936 BasePkt sndPkt;
937 CpxPkt cpxSndPkt;
938 BasePkt reqedPkt;
939 ccxPort portVar;
940 reg gotGrant = 0;
941 integer start = 0;
942 integer valid = 0;
943 integer dstPort; // will be 0-9
944 integer i, j;
945 reg [8:0] tmp9;
946 integer offset;
947 integer targetsAvial; // 8 or 9 ports
948 integer slot=0;
949 integer recvTarget=0;
950 reg [8:0] recvTargets=0; // targets to request, from pkt
951 integer gntTarget=0;
952 integer tmpTarget=0;
953 integer dropTarget=99; // target dropped. 99 means none dropped this clk
954 reg dropped = 0; // have dropped pkt
955 BasePkt dropPkt;
956 integer dropTargetIF2 = 99; // dropped IFILL #2 pkts accross targets (target id).
957
958
959
960 // keep state of CCX 2 entry queue
961 reg [1:0] bufCount [9] = {0,0,0,0,0,0,0,0,0};
962 integer qSize = 3;
963 BasePkt slots [3] [9]; // x packets, over 8 or 9 ports
964 // index, assuming we are streaming. May not get past 0 if !back2back pkts.
965 // 0: pkt from 2 reqs back
966 // 1: pkt from 1 reqs back
967 // 2: pkt driven this clk
968
969 reg [8:0] casAtomicWait = 0; // waiting/spinning for target ports to be empty
970 reg [8:0] fullBufferWait = 0; // waiting/spinning for target ports to be not-full
971
972 reg noSpeculation = 0; // debug
973
974 if (passive) return;
975
976 if (myPort == DEV_NCU) noSpeculation = 1;
977
978 // tmp holder for cast_assign
979 cpxSndPkt = new();
980
981 for (i=0; i<qSize; i++)
982 for (j=0; j<9; j++)
983 slots[i][j] = null;
984
985 if (type == PP_PCX) {
986 portVar = gPcxPort[myPort];
987 offset = 8; // target ports are 9-17
988 targetsAvial = 9;
989 }
990 else {
991 portVar = gCpxPort[myPort];
992 offset = 0; // target ports are 0-7
993 targetsAvial = 8;
994 }
995
996 @(negedge portVar.$clk);
997
998
999 while (1) {
1000 // if (get_cycle() > 1200 && myPort == 8) vera_plot("vera_plot",DEBUSSY, "this.*", 1);
1001
1002 //// block for sending req and data. ////
1003 // give priority to any previously dropped packet.
1004 {
1005
1006 // if reqedPkt not null, previous clk did the req for this pkt
1007 // so we must send it now.
1008 if (reqedPkt !== null) {
1009
1010 recvTargets = reqedPkt.targetPorts;
1011
1012 recvTarget = 0;
1013 while(recvTargets[recvTarget] !== 1) recvTarget++;
1014 reqedPkt.decGntTarget = recvTarget; // for multicast
1015
1016 portVar.$datao <= reqedPkt.getVector();
1017
1018#ifdef CCXDEVBASEBFM_DEBUG
1019 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());
1020 { integer x , y;
1021 for (y=0;y<targetsAvial;y++) {
1022 if (recvTargets[y]) {
1023 for (x=0;x<qSize;x++)
1024 if (slots[x][y] !== null) printf("%0d: CcxDevBaseBFM[%2d]::serviceSends drive data dump port/targets/tid:%0d/%h/%0d vec[%2d]=%0h\n",get_time(LO),myPort,myPort,slots[x][y].targetPorts,slots[x][y].tid,x,slots[x][y].getVector());
1025 }
1026 }
1027 }
1028#endif
1029
1030 // lastly
1031 reqedPkt = null;
1032 } else {
1033 portVar.$datao <= IDLE_DATA;
1034 }
1035
1036
1037 // what packet will be next? need to *req* it 1 cycle before data.
1038 // or hold previous packet if a dropped IF2.
1039 //
1040 // any dropped packets to send?
1041 // if a target has a dropped pkt, send it rather than a new pkt.
1042 if (dropTargetIF2 !== 99) { // need to hold IFILL #2 pkt on wires until taken
1043 dropTarget = dropTargetIF2;
1044 reqedPkt = dropPkt; // current dropped IFILL #2 packet
1045
1046#ifdef CCXDEVBASEBFM_DEBUG
1047 printf("%0d: CcxDevBaseBFM[%2d]::serviceSends: holding dropped IFILL #2 on wire until taken: targets=%h vec=%0h\n",get_time(LO),myPort,reqedPkt.targetPorts,reqedPkt.getVector());
1048#endif
1049
1050 } else if (dropped) {
1051
1052 // data to send next clk, doing req this clk
1053 reqedPkt = dropPkt; // previously dropped packet
1054
1055 dropTarget = whichHot(reqedPkt.targetPorts);
1056
1057 // now drive chosen pkt req to chosen target
1058 // and store chosen pkt into reqedPkt for data send on next clk.
1059 portVar.$req <= 1 << dropTarget; //dropBit;
1060
1061 // drive atomic on ifill pkt #1 retrys only, not pkt #2
1062 if (reqedPkt.atomic == 1 && myPort !== DEV_NCU)
1063 portVar.$atmo <= 1;
1064
1065
1066 // push pkt.
1067 // multicast pkts are never dropped so we are
1068 // operating on single target pkt here.
1069 slots[bufCount[recvTarget]][recvTarget] = reqedPkt;
1070
1071 // CCX Q is based on req being set, not data
1072 bufCount[dropTarget]++;
1073
1074 // printf("%0d: CcxDevBaseBFM[%2d]::serviceSends dropped req, COUNT++ for target %0d is %0d.\n",get_time(LO),myPort,recvTarget,bufCount[recvTarget]);
1075
1076 dropPkt = null;
1077
1078#ifdef CCXDEVBASEBFM_DEBUG
1079 printf("%0d: CcxDevBaseBFM[%2d]::serviceSends: next clks pkt will be a DROP re-send: targets=%h dropped=%b vec=%0h\n",get_time(LO),myPort,reqedPkt.targetPorts,dropped,reqedPkt.getVector());
1080#endif
1081 // packet for target no longer dropped (unless dropped again)
1082 dropped= 0;
1083
1084 } // else if (dropped)
1085
1086 // no dropped pkt to send, not already waiting for buffer slot(s), GET NEW PKT
1087 else if ((bypassBoxCnt || outBoxCnt) && !fullBufferWait && !casAtomicWait) {
1088 // new pkt in mailbox
1089 semaphore_get(WAIT, boxLock, 1 );
1090
1091 // peek ahead for atomics (CAS). If the CCX target Q is not empty,
1092 // we will have to wait for it to be. SPC sourced atomics must be together.
1093 // this could be more effecient later...
1094 //
1095 // Also peek for multicast packets. Need to have buffer space for
1096 // EVERY target before sending a multicast packet!
1097 //
1098 // If buffer not empty, we should be concerned about the next
1099 // packet being atomic or multicast. Don't want to take it until
1100 // we can deal with it.
1101 if (bypassBoxCnt) {
1102 void = mailbox_get(COPY_NO_WAIT,bypassBox,sndPkt);
1103 } else if (outBoxCnt) {
1104 void = mailbox_get(COPY_NO_WAIT,outBox,sndPkt);
1105 }
1106
1107 //printf("%0d: CcxDevBaseBFM[%2d]::serviceSends packet peek\n",get_time(LO),myPort);
1108 //cast_assign(cpxSndPkt,sndPkt);
1109
1110 // SPC sending atomic
1111 if (sndPkt.rqtyp == PCX_CAS1 && bufCount[whichHot(sndPkt.targetPorts)])
1112 casAtomicWait[whichHot(sndPkt.targetPorts)] = 1;
1113
1114 if (manyHot(sndPkt.targetPorts) > 1) { // multicasting
1115 // check every target for buffer space
1116 //printf("%0d: CcxDevBaseBFM[%2d]::serviceSends multicast seen on peek!\n",get_time(LO),myPort);
1117 tmp9 = sndPkt.targetPorts;
1118 for (i=0;i<targetsAvial;i++)
1119 if (tmp9[i] && bufCount[i] > 1) {
1120 fullBufferWait[i] = 1; // not all buffers have space, this target
1121 //printf("%0d: CcxDevBaseBFM[%2d]::serviceSends multicast fullBufferWait[%0d] set (%b)\n",get_time(LO),myPort,i,fullBufferWait);
1122
1123 }
1124 } // multicast
1125
1126 // speculation turned off
1127 if (noSpeculation && bufCount[whichHot(sndPkt.targetPorts)] > 1)
1128 fullBufferWait[whichHot(sndPkt.targetPorts)] = 1;
1129
1130 // get a new pkt to send if not waiting
1131 if (!casAtomicWait && !fullBufferWait) {
1132 if (bypassBoxCnt) {
1133 valid = mailbox_get(NO_WAIT,bypassBox,sndPkt);
1134 bypassBoxCnt--;
1135
1136#ifdef CCXDEVBASEBFM_DEBUG
1137 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());
1138#endif
1139 } else {
1140 valid = mailbox_get(NO_WAIT,outBox,sndPkt);
1141 outBoxCnt--;
1142
1143#ifdef CCXDEVBASEBFM_DEBUG
1144 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());
1145#endif
1146 }
1147
1148 recvTargets = sndPkt.targetPorts;
1149
1150 recvTarget = 0;
1151 while(recvTargets[recvTarget] !== 1) recvTarget++;
1152 sndPkt.decGntTarget = recvTarget; // for multicast
1153
1154
1155 // now drive chosen pkt req to chosen target
1156 // and store chosen pkt into reqedPkt for data send on next clk.
1157 // Second pkt of atomic pair does not req.
1158 if (sndPkt.atomic == 2) {
1159 portVar.$req <= 0;
1160 portVar.$atmo <= 0;
1161 } else {
1162 portVar.$req <= recvTargets; // 1 << recvTarget;
1163 if (sndPkt.atomic == 1 && myPort !== DEV_NCU)
1164 portVar.$atmo <= 1;
1165
1166 // if (manyHot(recvTargets) > 1) printf("%0d: CcxDevBaseBFM[%2d]::serviceSends multicast seen on req!\n",get_time(LO),myPort);
1167 }
1168
1169 // data to send next clk, doing req this clk
1170 reqedPkt = sndPkt;
1171
1172 // push pkt.
1173 for (i=0;i<targetsAvial;i++) {
1174 if (recvTargets[i]) { // for multicast
1175 slots[bufCount[i]][i] = reqedPkt;
1176 // CCX Q is based on req being set, not data
1177 bufCount[i]++;
1178#ifdef CCXDEVBASEBFM_DEBUG
1179 printf("%0d: CcxDevBaseBFM[%2d]::serviceSends pushing pkt for target %0d, bufCount now %0d\n",get_time(LO),myPort, i, bufCount[i]);
1180#endif
1181 }
1182 }
1183
1184 } else { // if !casAtomicWait && !fullBufferWait
1185 // no new pkt for next cycle
1186 portVar.$req <= 0;
1187 if (myPort !== DEV_NCU) portVar.$atmo <= 0;
1188 reqedPkt = null;
1189 }
1190
1191#ifdef CCXDEVBASEBFM_DEBUG
1192 if (casAtomicWait)
1193 printf("%0d: CcxDevBaseBFM[%2d]::serviceSends casAtomicWait state!\n",
1194 get_time(LO),myPort);
1195 if (fullBufferWait)
1196 printf("%0d: CcxDevBaseBFM[%2d]::serviceSends fullBufferWait state!\n",
1197 get_time(LO),myPort);
1198#endif
1199
1200 semaphore_put(boxLock, 1 );
1201
1202 } else {
1203 // no new pkt for next cycle
1204 portVar.$req <= 0;
1205 if (myPort !== DEV_NCU) portVar.$atmo <= 0;
1206 reqedPkt = null;
1207
1208 //portVar.$datao <= IDLE_DATA;
1209 }
1210
1211
1212 } // block for sending req and data
1213
1214
1215
1216
1217 //// check grant block ////
1218 {
1219
1220 // #ifdef CCXDEVBASEBFM_DEBUG
1221 // // only one port can drive these at a time
1222 // if (myPort == 11) {
1223 // probe_if.count0 = count[0] soft;
1224 // probe_if.count1 = count[1] soft;
1225 // probe_if.count2 = count[2] soft;
1226 // probe_if.count3 = count[3] soft;
1227 // probe_if.count4 = count[4] soft;
1228 // probe_if.count5 = count[5] soft;
1229 // probe_if.count6 = count[6] soft;
1230 // probe_if.count7 = count[7] soft;
1231 // }
1232 // #endif
1233
1234 // any grants in this cycle? check all targets.
1235 gotGrant = 0;
1236 for (gntTarget=0;gntTarget<targetsAvial;gntTarget++) {
1237 case (bufCount[gntTarget]) {
1238 0: { // Q empty
1239 if (portVar.$gnt[gntTarget]) {
1240 error("%0d: CcxDevBaseBFM[%2d]::serviceSends ERROR FAIL port/target:%0d/%0d bad pop or unexpected grant on port (bufCount was 0)!\n",get_time(LO),myPort,myPort,gntTarget);
1241 }
1242 }
1243 1: { // Q half full
1244 if (portVar.$gnt[gntTarget]) {
1245 gotGrant = 1;
1246 }
1247 }
1248 2: { // Q full
1249 if (portVar.$gnt[gntTarget]) {
1250 gotGrant = 1;
1251 }
1252 }
1253 3: { // did speculative send succeed?
1254 // if Q already full, must get a grant in same cycle as our req or dropped
1255 if (portVar.$gnt[gntTarget]) {
1256 gotGrant = 1;
1257 dropTarget = 99;
1258 dropTargetIF2 = 99;
1259 // printf("%0d: CcxDevBaseBFM[%2d]::serviceSends gotGrant, speculation SUCCESS, bufCount for target %0d was %0d.\n",get_time(LO),myPort,gntTarget,bufCount[gntTarget]);
1260 } else {
1261 // speculation failed
1262 dropTarget = gntTarget;
1263 // printf("%0d: CcxDevBaseBFM[%2d]::serviceSends gotGrant, NO grant, speculation FAIL, bufCount for target %0d was %0d.\n",get_time(LO),myPort,gntTarget,bufCount[gntTarget]);
1264 }
1265 }
1266 default: {
1267 error("%0d: CcxDevBaseBFM[%2d]::serviceSends: ERROR FAIL: port %0d Q count of %0d not right!\n",get_time(LO),myPort,myPort,bufCount[gntTarget]);
1268 }
1269 }//case
1270
1271
1272 /// ///
1273 /// pop Q, packet made it out other side ///
1274 /// ///
1275 if (gotGrant) {
1276
1277 // reset
1278 gotGrant = 0;
1279 popQ(bufCount,
1280 slots,
1281 gntTarget,
1282 qSize,
1283 dropTarget);
1284
1285 // Q empty?
1286 if (bufCount[gntTarget] == 0) casAtomicWait[gntTarget] = 0;
1287 // if (fullBufferWait[gntTarget]) printf("%0d: CcxDevBaseBFM[%2d]::serviceSends fullBufferWait[%0d] clear!\n",get_time(LO),myPort,gntTarget);
1288
1289// // delay this for 1 clock to be more like real NCU
1290// if (myPort == DEV_NCU) {
1291// fork {
1292// tmpTarget = gntTarget;
1293// @(negedge portVar.$clk);
1294// if (bufCount[tmpTarget] <= 1) fullBufferWait[tmpTarget] = 0;
1295// } join none
1296// } else {
1297 if (bufCount[gntTarget] <= 1) fullBufferWait[gntTarget] = 0;
1298// }
1299
1300 } // if (gotGrant)
1301 } // for (gntTarget=0;gntTarget<targetsAvial;gntTarget++)
1302 } // check grant blk
1303
1304
1305
1306 //// block to handle dropped pkts. ////
1307 // save off dropped pkt as last thing after data sends.
1308 // deals with "dropTarget". multicast pkts never dropped!
1309 {
1310 if (dropTarget !== 99) {
1311 dropPkt = slots[2][dropTarget];
1312
1313
1314 // Special Case
1315 // if dropped pkt was second ifill pkt (CAS2 never dropped)
1316 // then keep driving packet data until we get a grant.
1317 if (dropPkt.atomic == 2) {
1318
1319 // if not seeing gnt now, need to hold this packet (reqedPkt) on wire
1320 // for another clock, or more w/o setting req first.
1321
1322#ifdef CCXDEVBASEBFM_DEBUG
1323 printf("%0d: CcxDevBaseBFM[%2d]::serviceSends DROPPED IFILL 2 waiting for grant port/targets/tid:%0d/%h/%0d vec=%0h\n",get_time(LO),myPort,myPort,dropPkt.targetPorts,dropPkt.tid,dropPkt.getVector());
1324#endif
1325
1326 // will send the dropped pkt later.
1327 dropTargetIF2 = dropTarget; // used later by pkt send block
1328
1329 } else {
1330 // will send the dropped pkt later.
1331 dropped = 1; // used later by pkt send block
1332
1333#ifdef CCXDEVBASEBFM_DEBUG
1334 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.targetPorts,dropPkt.tid,bufCount[dropTarget]-1,dropPkt.getVector());
1335 //dropPkt.printPkt();
1336#endif
1337
1338 // dec bufCount since dropped pkt not in Q (3 -> 2)
1339 bufCount[dropTarget]--;
1340
1341 // pull it from Q, since pkt not in RTL Q
1342 slots[bufCount[dropTarget]][dropTarget] = null;
1343
1344#ifdef CCXDEVBASEBFM_DEBUG
1345 {integer x;
1346 for (x=0;x<qSize;x++)
1347 if (slots[x][dropTarget] !== null) printf("%0d: CcxDevBaseBFM[%2d]::serviceSends post dropped pop dump port/target/tid:%0d/%0d/%0d vec[%2d]=%0h\n",get_time(LO),myPort,myPort,dropTarget,slots[x][dropTarget].tid,x,slots[x][dropTarget].getVector());}
1348#endif
1349
1350 // reset
1351 dropTarget = 99;
1352
1353 }
1354 }
1355
1356 } // block to handle dropped pkts.
1357
1358
1359 @(negedge portVar.$clk);
1360
1361 // Block/sleep here on no box count, no gnt expected, no dropped pkt, etc
1362 // Are we idle? If so, wake up on mailbox having a packet. Only makes
1363 // sense for IOB since it has long periods of inactivity (about 85%-90%).
1364 // Downside is that we will miss unexpected grants so watch for that too.
1365 if (myPort < 8 && myPort > 15) {
1366 fork
1367 {
1368 if (bufCount[0] == 0 && bufCount[1] == 0 && bufCount[2] == 0 && bufCount[3] == 0 &&
1369 bufCount[4] == 0 && bufCount[5] == 0 && bufCount[6] == 0 && bufCount[7] == 0 &&
1370 bufCount[8] == 0 && dropped == 0 && reqedPkt == null)
1371 wait_var(bypassBoxCnt,outBoxCnt);
1372 }
1373 {
1374 @(posedge portVar.$gnt);
1375 }
1376 join any
1377
1378 if (portVar.$clk) @(negedge portVar.$clk);
1379 //if (myPort == 16) printf("%0d: port %0d looping...\n", get_time(LO),myPort);
1380 }
1381
1382 } // while 1
1383}