Commit | Line | Data |
---|---|---|
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 | ||
61 | virtual 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 | ||
127 | task 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 | |
153 | task 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 | |
182 | function 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 | ||
638 | task 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? | |
735 | function 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. | |
745 | function 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! | |
763 | function 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. | |
934 | task 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 | } |