Commit | Line | Data |
---|---|---|
86530b38 AT |
1 | // ========== Copyright Header Begin ========================================== |
2 | // | |
3 | // OpenSPARC T2 Processor File: ccxDevMemBFM.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 | #include <ccxDevicesDefines.vri> | |
43 | #include <cmp.vri> | |
44 | #include <std_display_defines.vri> | |
45 | ||
46 | #include <std_display_class.vrh> | |
47 | #include <basePktClass.vrh> | |
48 | #include <cpxPktClass.vrh> | |
49 | #include <pcxPktClass.vrh> | |
50 | #include <baseParamsClass.vrh> | |
51 | #include <sparcParams.vrh> | |
52 | #include <ccxDevBaseBFM.vrh> | |
53 | #include <memArray.vrh> | |
54 | #include <baseUtilsClass.vrh> | |
55 | #include <sparcBenchUtils.vrh> | |
56 | #include <ccx_tag_class.vrh> | |
57 | ||
58 | // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! | |
59 | // To use this class, you must have in your bench a class that extends | |
60 | // sparcBenchUtils.vr. It must be in files named utilsClass.vr/utilsClass.vrh | |
61 | // AND have a global handle called gUtil. | |
62 | #include <utilsClass.vrh> | |
63 | ||
64 | // uncomment to debug | |
65 | //#define CCXDEVMEMBFM_DEBUG | |
66 | ||
67 | ||
68 | // #define MON_CCXPKT 24 | |
69 | // #define GNT_ATTEMPTS 20 | |
70 | ||
71 | #define CLASSNAME CcxDevMemBFM | |
72 | #define CLASSNAMEQ "CcxDevMemBFM" | |
73 | ||
74 | class CLASSNAME extends CcxDevBaseBFM { | |
75 | ||
76 | local reg [63:0] cas1Data[64], cas1Addr[64]; | |
77 | local reg cas_swap [64]; | |
78 | // local reg [31:0] invVectorCAS; | |
79 | local reg cacheOff = 0; | |
80 | local integer reqId; | |
81 | local integer ldstSyncLock; | |
82 | local integer stalling; | |
83 | static reg burstSync; | |
84 | ||
85 | // methods | |
86 | task new(integer instatnce, reg passiveIn=0, | |
87 | reg cacheoff = 0, reg flagUnexpected=0, reg ccxOnly=0); | |
88 | ||
89 | // virtual in base | |
90 | task recv(BasePkt pktHndl); | |
91 | task cancelRecv(BasePkt pktHndl); | |
92 | ||
93 | local task slave(); | |
94 | local task respond(PcxPkt reqPkt); | |
95 | // local task monitor(); | |
96 | local function reg [3:0] lineState(PcxPkt reqPkt, | |
97 | string why="debug lineState:", | |
98 | reg quiet=1); | |
99 | ||
100 | task sendIntr(reg [5:0] tid, | |
101 | reg [1:0] type, | |
102 | reg [5:0] vect); | |
103 | ||
104 | local task updateItag(PcxPkt reqPkt, CpxPkt rspPkt, CpxPkt rspPkt2 = null, | |
105 | reg [2:0] cpuId, integer how = TAG_VAL); | |
106 | local task updateDtag(PcxPkt reqPkt, CpxPkt rspPkt, | |
107 | reg [2:0] cpuId, integer how = TAG_VAL); | |
108 | local function reg data_equal(reg [63:0] data1, | |
109 | reg [63:0] data2, | |
110 | reg[7:0] size); | |
111 | // local task create_vector(reg table, reg [3:0] way, | |
112 | // var reg [31:0] vect, reg [2:0] core_num); | |
113 | local function reg [31:0] getInvalVector(integer type, | |
114 | PcxPkt reqPkt, | |
115 | reg [2:0] cpuId); | |
116 | ||
117 | local task ldstSync(reg [2:0] cpuId, CpxPkt rspPkt); | |
118 | public task enqueueEvict(reg [7:0] coreEnable, | |
119 | reg [39:0] evictPA = 40'hffffffffff, | |
120 | reg [3:0] cid = 4'hf, | |
121 | reg all_cores = 0, | |
122 | integer dCacheWeight = 60); | |
123 | ||
124 | local task burstResp(integer amount); | |
125 | ||
126 | } | |
127 | ||
128 | ||
129 | task CLASSNAME::new(integer instatnce, reg passiveIn=0, | |
130 | reg cacheoff = 0, reg flagUnexpected=0, reg ccxOnly=0) { | |
131 | ||
132 | super.new(instatnce, passiveIn, CLASSNAMEQ); | |
133 | super.ccxOnly = ccxOnly; // only testing ccx | |
134 | super.flagUnexpected = flagUnexpected; | |
135 | srandom(gSeed,this); | |
136 | ||
137 | cacheOff = cacheoff; | |
138 | stalling = 0; | |
139 | reqId = 0; | |
140 | burstSync = 0; | |
141 | ldstSyncLock = alloc( SEMAPHORE, 0, 1, 1 ); // this may not be needed anymore | |
142 | ||
143 | // in base | |
144 | super.stallStart = gParam.stallStart; | |
145 | super.stallStop = gParam.stallStop; | |
146 | ||
147 | if (myPort == DEV_NCU) lineHash[0] = 0; | |
148 | ||
149 | if (!passiveIn) { | |
150 | // Initialize Outputs | |
151 | gPcxPort[myPort].$stall <= 0; | |
152 | gCpxPort[myPort].$req <= 0; | |
153 | // gCpxPort[myPort].$datao <= 0; | |
154 | if (myPort !== DEV_NCU) gCpxPort[myPort].$atmo <= 0; | |
155 | } | |
156 | ||
157 | fork { | |
158 | @(posedge gPcxPort[myPort].$clk); | |
159 | gCpxPort[myPort].$gnt == 0; | |
160 | gPcxPort[myPort].$rdy == 0; | |
161 | gPcxPort[myPort].$datai == 0; | |
162 | if (myPort !== DEV_NCU) gPcxPort[myPort].$atmi == 0; | |
163 | } join none | |
164 | ||
165 | fork slave(); | |
166 | join none | |
167 | ||
168 | // service mailboxes, send packets | |
169 | fork super.serviceSends2(PP_CPX); | |
170 | join none | |
171 | ||
172 | if (gParam.burstAmount) { | |
173 | fork burstResp(gParam.burstAmount); | |
174 | join none | |
175 | } | |
176 | ||
177 | } | |
178 | ||
179 | ||
180 | ||
181 | // Wait for data from PCX, we are a cache/IO BFM | |
182 | task CLASSNAME::slave() { | |
183 | ||
184 | ccxPort portVar = gPcxPort[myPort]; | |
185 | reg [145:0] tmpVec; | |
186 | ||
187 | if (!passive) { | |
188 | ||
189 | fork | |
190 | { // wait for packets. | |
191 | // we can get back to back packets... | |
192 | while (1) { | |
193 | ||
194 | if (!portVar.$rdy) { | |
195 | @(portVar.$rdy); | |
196 | } | |
197 | ||
198 | if (portVar.$rdy && stalling) { | |
199 | error("ERROR FAIL: should not get rdy when stalled\n"); | |
200 | } | |
201 | ||
202 | { // keep block! {} | |
203 | PcxPkt reqPkt = new(); | |
204 | // need to fork to handle back to back reqs | |
205 | // and delayed responses | |
206 | fork { | |
207 | outstandingReqs++; | |
208 | reqPkt.ccxSourced = 1; | |
209 | @(negedge portVar.$clk); | |
210 | reqPkt.loadPkt(portVar.$datai, myPort); | |
211 | ||
212 | // if on L2 port, look at portVar.$atmi and save value | |
213 | if (myPort !== DEV_NCU) reqPkt.atm_wire = portVar.$atmi; | |
214 | ||
215 | #ifdef CCXDEVMEMBFM_DEBUG | |
216 | printf("%0d: CcxDevMemBFM[%2d]::slave: got req packet, outstandingReqs++=%0d, atm=%0d, ccxSourced=%0d, vec=%h\n",get_time(LO),myPort,outstandingReqs,reqPkt.atm_wire,reqPkt.ccxSourced,reqPkt.getVector()); | |
217 | #endif | |
218 | ||
219 | // for CCX testing, anyone waiting for this packet? | |
220 | // was it expected? should we auto respond? | |
221 | if (ccxOnly) { | |
222 | tmpVec = reqPkt.makeSignature(); | |
223 | if (assoc_index(CHECK, expectedSig, tmpVec)) { | |
224 | expectedSig[tmpVec].loadPkt(reqPkt.getVector(), myPort); | |
225 | expectedSig[tmpVec].arrivalTime = get_cycle(); | |
226 | expectedSig[tmpVec].pktArrived = ~expectedSig[tmpVec].pktArrived; | |
227 | } else if (flagUnexpected) { | |
228 | reqPkt.print(myPort); | |
229 | PR_ERROR(CLASSNAMEQ, MON_ERROR, | |
230 | psprintf ("Unexpected packet on port %0d, vector=%h", | |
231 | myPort,reqPkt.getVector())); | |
232 | } | |
233 | } else { | |
234 | // check how/if we should respond and do it | |
235 | suspend_thread(); // do as last thing in this time slot. | |
236 | if (!ccxOnly && reqPkt.valid) { | |
237 | fork respond(reqPkt); | |
238 | join none | |
239 | } else { | |
240 | // drop this packet | |
241 | outstandingReqs--; | |
242 | printf("%0d: CcxDevMemBFM[%2d]::slave: got invalid req packet, outstandingReqs++=%0d, atm=%0d, ccxSourced=%0d, vec=%h\n",get_time(LO),myPort,outstandingReqs,reqPkt.atm_wire,reqPkt.ccxSourced,reqPkt.getVector()); | |
243 | reqPkt.print(myPort); | |
244 | reqPkt = null; | |
245 | } | |
246 | } | |
247 | ||
248 | } join none | |
249 | } | |
250 | ||
251 | @(negedge portVar.$clk); | |
252 | ||
253 | } // while | |
254 | } | |
255 | ||
256 | ||
257 | { // stall signal management | |
258 | while (1) { | |
259 | wait_var(outstandingReqs); | |
260 | //printf("%0d: wait_var at stall check outstandingReqs=%0d\n", get_time(LO), outstandingReqs); | |
261 | // stall? Takes 3 clocks for the CCX to actually stall | |
262 | if (outstandingReqs >= stallStart && !stalling) { | |
263 | fork { | |
264 | portVar.$stall = 1; | |
265 | //printf("%0d: CcxDevMemBFM[%2d]::stalling, outstandingReqs=%0d\n", get_time(LO),myPort, outstandingReqs); | |
266 | repeat (2) @(negedge portVar.$clk); | |
267 | stalling = 1; | |
268 | } join none | |
269 | } | |
270 | if (outstandingReqs <= stallStop && stalling) { | |
271 | fork { | |
272 | portVar.$stall = 0; | |
273 | @(negedge portVar.$clk); | |
274 | stalling = 0; | |
275 | //printf("%0d: CcxDevMemBFM[%2d]::un-stalling, outstandingReqs=%0d\n", get_time(LO),myPort, outstandingReqs); | |
276 | @(negedge portVar.$clk); | |
277 | stalling = 0; // yes, twice, in case we started stalling above | |
278 | //printf("%0d: CcxDevMemBFM[%2d]::done un-stalling, outstandingReqs=%0d\n", get_time(LO),myPort, outstandingReqs); | |
279 | } join none | |
280 | } | |
281 | ||
282 | } | |
283 | } | |
284 | join all | |
285 | ||
286 | } // ! passive | |
287 | } | |
288 | ||
289 | ||
290 | task CLASSNAME::sendIntr(reg [5:0] tid, | |
291 | reg [1:0] type, | |
292 | reg [5:0] vect) | |
293 | { | |
294 | CpxPkt reqPkt; | |
295 | ||
296 | reqPkt = new(); | |
297 | reqPkt.createIntr(tid,type,vect); // INTR_RESET,INTR_POR | |
298 | reqPkt.sendPorts = 1 << myPort; | |
299 | reqPkt.targetPorts = 1 << tid[5:3]; // not multicast | |
300 | reqPkt.send(1); | |
301 | } | |
302 | ||
303 | ||
304 | ||
305 | // check how/if we should respond and do it. | |
306 | // check response signature and use user response if hit (review) | |
307 | // else return a built in response. | |
308 | task CLASSNAME::respond(PcxPkt reqPkt) { | |
309 | ||
310 | CpxPkt rspPkt, rspPkt2, rspPkt3; | |
311 | reg [7:0] cas1size, targetCores; | |
312 | reg [127:0] tmpData; | |
313 | reg [31:0] invVector = 0; | |
314 | reg [31:0] tmpInvVect = 0; | |
315 | reg [1:0] invField = 0; | |
316 | reg [63:0] tmp64; | |
317 | integer i; | |
318 | reg [5:0] thread; | |
319 | ccxPort portVar = gCpxPort[myPort]; | |
320 | reg [63:0] tmpAddr; | |
321 | reg [39:0] tmpPa; | |
322 | ||
323 | // do not drive on this port | |
324 | if (passive) return; | |
325 | ||
326 | thread = {reqPkt.cpuId,reqPkt.tid}; | |
327 | ||
328 | // check response signature & use user pkt if hit | |
329 | // else return built in response pkt based on request pkt fields. | |
330 | // use fast resp mailbox. | |
331 | ||
332 | // Some may be illegal for L2 port, check port number and address! | |
333 | if (myPort !== DEV_NCU && reqPkt.addr[39] == 1 && | |
334 | (reqPkt.addr[39:32] < 8'hA0 || reqPkt.addr[39:32] > 8'hBF)) { | |
335 | reqPkt.print(myPort); | |
336 | PR_ERROR (CLASSNAMEQ, MON_ERR, | |
337 | psprintf ("T%d Port=%0d request type [128:124]=%b. ERROR FAIL: Illegal I/O on non-NCU port!",thread,myPort,reqPkt.rqtyp)); | |
338 | return; | |
339 | } | |
340 | ||
341 | // create response packets | |
342 | rspPkt = new(reqPkt); | |
343 | rspPkt.sendPorts = 1 << myPort; | |
344 | rspPkt.addr = reqPkt.addr; | |
345 | ||
346 | #ifdef CCXDEVMEMBFM_DEBUG | |
347 | reqPkt.reqId = reqId; | |
348 | rspPkt.reqId = reqId; | |
349 | reqId++; | |
350 | if (reqId == 10000) reqId = 0; | |
351 | reqPkt.reqTime = get_time(LO); | |
352 | rspPkt.reqTime = reqPkt.reqTime; | |
353 | // state of tag for line at this time | |
354 | reqPkt.lineWay = lineState(reqPkt, *, 1); | |
355 | reqPkt.print(myPort); | |
356 | #endif | |
357 | ||
358 | // pick a response | |
359 | case (reqPkt.rqtyp) { | |
360 | PCX_LD, | |
361 | PCX_PREF, | |
362 | PCX_PREF_ICE, | |
363 | PCX_DIAG_LD, | |
364 | PCX_D_INVAL: | |
365 | { | |
366 | case ({reqPkt.inv,reqPkt.pf}) { | |
367 | 0: { | |
368 | if (myPort !== DEV_NCU) { | |
369 | // can't tell diag load from load so both are load! | |
370 | rspPkt.rtntyp = CPX_LD; // code is done | |
371 | rspPkt.rtntypU = U_CPX_LD; | |
372 | } else { | |
373 | // can't tell diag load from load so both are load! | |
374 | rspPkt.rtntyp = CPX_NCU_LD; // code is done | |
375 | rspPkt.rtntypU = U_CPX_NCU_LD; | |
376 | } | |
377 | } | |
378 | 1: { | |
379 | if (reqPkt.nc) { | |
380 | rspPkt.rtntyp = CPX_PREF; | |
381 | rspPkt.rtntypU = U_CPX_PREF; | |
382 | } else { | |
383 | error(""); | |
384 | } | |
385 | } | |
386 | 2: { | |
387 | if (!reqPkt.nc) { | |
388 | rspPkt.rtntyp = CPX_D_INVAL; | |
389 | rspPkt.rtntypU = U_CPX_D_INVAL; | |
390 | invField = D_INVAL; | |
391 | } else { | |
392 | error(""); | |
393 | } | |
394 | } | |
395 | 3: { | |
396 | if (reqPkt.nc) { | |
397 | rspPkt.rtntyp = CPX_PREF_ICE; | |
398 | rspPkt.rtntypU = U_CPX_PREF_ICE; | |
399 | } else { | |
400 | error(""); | |
401 | } | |
402 | ||
403 | } | |
404 | } // case | |
405 | } // case | |
406 | ||
407 | PCX_ST, | |
408 | PCX_BLK_ST, | |
409 | PCX_BLK_INIT_ST, | |
410 | PCX_DIAG_ST : { | |
411 | if (reqPkt.l1wayBis && !reqPkt.pf) { | |
412 | rspPkt.rtntyp = CPX_ST; // code is done | |
413 | rspPkt.rtntypU = U_CPX_BIS; | |
414 | } else if (reqPkt.l1wayBis && reqPkt.pf) { | |
415 | rspPkt.rtntyp = CPX_ST; // code is done | |
416 | rspPkt.rtntypU = U_CPX_BLK_ST; | |
417 | } else { | |
418 | // can't tell diag store from store so both are store! | |
419 | rspPkt.rtntyp = CPX_ST; // code is done | |
420 | rspPkt.rtntypU = U_CPX_ST; | |
421 | } | |
422 | } | |
423 | ||
424 | PCX_CAS1: { | |
425 | rspPkt.rtntyp = CPX_CAS_RTN; | |
426 | rspPkt.rtntypU = U_CPX_CAS_RTN; | |
427 | } | |
428 | ||
429 | PCX_CAS2: { | |
430 | rspPkt.rtntyp = CPX_CAS_ACK; | |
431 | rspPkt.rtntypU = U_CPX_CAS_ACK; | |
432 | } | |
433 | ||
434 | PCX_STR_LD: { | |
435 | rspPkt.rtntyp = CPX_STR_LD; // code is done | |
436 | rspPkt.rtntypU = U_CPX_STR_LD; | |
437 | } | |
438 | ||
439 | PCX_STR_ST: { | |
440 | rspPkt.rtntyp = CPX_STR_ST; // code is done | |
441 | rspPkt.rtntypU = U_CPX_STR_ST; | |
442 | } | |
443 | ||
444 | PCX_SWAP: { | |
445 | rspPkt.rtntyp = CPX_SWAP_RTN; // will do ack as well. | |
446 | rspPkt.rtntypU = U_CPX_SWAP_RTN; // will do U_CPX_SWAP_ACK as well. | |
447 | } | |
448 | ||
449 | PCX_MMU_LD: { | |
450 | rspPkt.rtntyp = CPX_MMU_RTN; // code is done | |
451 | rspPkt.rtntypU = U_CPX_MMU_RTN; | |
452 | } | |
453 | ||
454 | PCX_IFILL: { | |
455 | if (reqPkt.inv == 0) { | |
456 | if (myPort !== DEV_NCU) { | |
457 | rspPkt.rtntyp = CPX_IFILL; // code is done | |
458 | rspPkt.rtntypU = U_CPX_IFILL; | |
459 | } else { | |
460 | rspPkt.rtntyp = CPX_NCU_IFILL; // code is done | |
461 | rspPkt.rtntypU = U_CPX_NCU_IFILL; | |
462 | } | |
463 | } else { | |
464 | rspPkt.rtntyp = CPX_I_INVAL; | |
465 | rspPkt.rtntypU = U_CPX_I_INVAL; | |
466 | invField = I_INVAL; | |
467 | } | |
468 | } | |
469 | ||
470 | default : { | |
471 | reqPkt.print(myPort); | |
472 | PR_ERROR (CLASSNAMEQ, MON_ERR, | |
473 | psprintf ("T%d Port=%0d request type [128:124]=%b. ERROR FAIL: Unsupported rqtyp",thread,myPort,reqPkt.rqtyp)); | |
474 | reqPkt = null; rspPkt2 = null; | |
475 | } | |
476 | } // case | |
477 | ||
478 | ||
479 | // finalize random values for response times | |
480 | if (myPort == DEV_NCU) | |
481 | void = rspPkt.randomize() with {hit == 1;}; | |
482 | else | |
483 | void = rspPkt.randomize(); | |
484 | ||
485 | ||
486 | // do the response | |
487 | case (rspPkt.rtntypU) { | |
488 | ||
489 | U_CPX_LD, U_CPX_DIAG_LD, | |
490 | U_CPX_STR_LD, U_CPX_MMU_RTN, U_CPX_PREF, | |
491 | U_CPX_PREF_ICE, U_CPX_NCU_LD: { | |
492 | ||
493 | integer i; | |
494 | ||
495 | repeat (ordering(rspPkt, "LD RTN")) @(posedge portVar.$clk); | |
496 | // get semaphore for this clock | |
497 | semaphore_get(WAIT, ldstSyncLock, 1); | |
498 | ||
499 | if (reqPkt.addr[39] && myPort == DEV_NCU) { | |
500 | ||
501 | // is it a special address for us? | |
502 | // if (reqPkt.addr[IO_ASI_ADDR_NCU] == IO_ASI_CPU || | |
503 | // reqPkt.addr[IO_ASI_ADDR_NCU] == IO_ASI_NCU) { | |
504 | rspPkt.err[1] = ! gUtil.ioSpaceAccess(reqPkt.addr, | |
505 | tmpData, *, | |
506 | reqPkt.size, | |
507 | thread, myPort); | |
508 | ||
509 | if (reqPkt.size == 4) | |
510 | PR_ERROR(CLASSNAMEQ, MON_ERROR, psprintf("TID %0d is doing a 16 byte load to I/O addr %0h!",reqPkt.tid,reqPkt.addr)); | |
511 | ||
512 | rspPkt.data = gUtil.copyDataByte(tmpData,reqPkt.size,reqPkt.addr[3:0]); | |
513 | // } | |
514 | // | |
515 | // } else if (reqPkt.addr[39] && myPort !== DEV_NCU && | |
516 | // (reqPkt.addr[IO_ASI_ADDR_NCU] < 8'hA0 || | |
517 | // reqPkt.addr[IO_ASI_ADDR_NCU] > 8'hBF) ) { | |
518 | // // I/O but not L2 CSRs | |
519 | // error("I/O LD to L2, but is not L2 CSRs"); | |
520 | } else { // LD from non-NCU space or L2 CSRs | |
521 | ||
522 | // Address in req is quad word aligned. | |
523 | // Access memory at double word boundaries. | |
524 | tmpAddr = {reqPkt.addr[39:4],4'b0000}; | |
525 | rspPkt.data = gMem.read128(tmpAddr,myPort,1); | |
526 | if (gParam.mcuMemPrint[READ]) { | |
527 | printf("\n%7d00: dumpMem: dumping memory related to this request pkt:",get_cycle()); | |
528 | reqPkt.print(myPort); | |
529 | gMem.dumpMem(reqPkt.addr[39:0] & 40'hFFFFFFFFC0, 8); | |
530 | } | |
531 | ||
532 | if (rspPkt.rtntypU == U_CPX_MMU_RTN) | |
533 | rspPkt.wayMMUid = reqPkt.l1wayMMUid; | |
534 | ||
535 | if (rspPkt.rtntypU == U_CPX_PREF) { | |
536 | rspPkt.nc = 1; | |
537 | rspPkt.pf = 1; | |
538 | // prefetch data is irrelevant since data does not allocate in L1 | |
539 | rspPkt.data = {urandom(),urandom(),urandom(),urandom()}; | |
540 | ||
541 | // does this imply the the L1 does not have this line? | |
542 | ||
543 | //invVector = getInvalVector(rspPkt.rtntypU, reqPkt, reqPkt.cpuId); | |
544 | } | |
545 | ||
546 | // evict line from L1 & L2. | |
547 | // send invalidates to other cores for this address. | |
548 | // do not send response to initiating core!!! | |
549 | // Used by SW to flush lines in L2 based on an index and a | |
550 | // way specified as part of the Physical Address in the instruction | |
551 | // itself. Bits [39:37] of the PA has to be driven as 3'b011 by SW and | |
552 | // the way,index,bank information would be on PA[21:18], PA[17:9] and | |
553 | // PA[8:6] respectively. | |
554 | if (rspPkt.rtntypU == U_CPX_PREF_ICE) { | |
555 | ||
556 | // the BFM is not going to support this because the "address" | |
557 | // only makes sense to L2 RTL. Can't properly handle this. | |
558 | printf("\n\n%7d: WARNING, L2 BFM does not support prefetchICE, ignoring!\n\n\n"); | |
559 | return; | |
560 | ||
561 | // create a single EVICTION packet and send it to every core needing it | |
562 | rspPkt.targetPorts[8:0] = gParam.coreEnable; | |
563 | rspPkt.rtntypU = U_CPX_EVICT; | |
564 | rspPkt.rtntyp = CPX_EVICT; | |
565 | rspPkt.l2miss = 0; | |
566 | rspPkt.err = 0; | |
567 | rspPkt.nc = 0; | |
568 | rspPkt.pf = 0; | |
569 | ||
570 | tmpPa = reqPkt.addr[39:0]; | |
571 | ||
572 | // gets evict vector and invals dup tags. | |
573 | rspPkt.data = gUtil.evictVector(gParam.coreEnable, | |
574 | tmpPa, | |
575 | reqPkt.cpuId, | |
576 | targetCores); // return val for target cores | |
577 | ||
578 | rspPkt.targetPorts = targetCores; | |
579 | ||
580 | ||
581 | // inval line from L1 & L2. | |
582 | // send invalidates to other cores for this address. | |
583 | // do not send response to initiating core!!! | |
584 | if (rspPkt.data) { | |
585 | // notify LDST sync just once when doing U_CPX_PREF_ICE | |
586 | // no matter what targets get invalidated. | |
587 | ldstSync(reqPkt.cpuId,rspPkt); | |
588 | rspPkt.send(1); | |
589 | } | |
590 | ||
591 | @(posedge portVar.$clk); | |
592 | semaphore_put(ldstSyncLock, 1 ); | |
593 | ||
594 | return; // due to U_CPX_PREF_ICE | |
595 | ||
596 | } // U_CPX_PREF_ICE | |
597 | ||
598 | } // l2 ld | |
599 | ||
600 | ||
601 | // Plusargs to dump LD/ST to logfile | |
602 | if (gParam.show_load) { | |
603 | if (rspPkt.rtntypU == U_CPX_STR_LD) | |
604 | PR_NORMAL(CLASSNAMEQ, MON_NORMAL, psprintf("Tx LOAD PA = %h DATA = %h TYPE = %0d",reqPkt.addr,rspPkt.data,rspPkt.rtntypU)); | |
605 | else | |
606 | PR_NORMAL(CLASSNAMEQ, MON_NORMAL, psprintf("T%d LOAD PA = %h DATA = %h TYPE = %0d",reqPkt.tid,reqPkt.addr,rspPkt.data,rspPkt.rtntypU)); | |
607 | } | |
608 | ||
609 | // notify LDST sync | |
610 | ldstSync(reqPkt.cpuId,rspPkt); | |
611 | ||
612 | // Update D$ tag table | |
613 | if (reqPkt.nc == 0 && rspPkt.rtntypU !== U_CPX_PREF_ICE) | |
614 | updateDtag(reqPkt, rspPkt, reqPkt.cpuId, TAG_VAL); | |
615 | ||
616 | #ifdef CCXDEVMEMBFM_DEBUG | |
617 | // state of tag for line at this time | |
618 | rspPkt.lineWay = lineState(reqPkt, *, 1); | |
619 | #endif | |
620 | ||
621 | // queue packet for delivery, in this case, this BFM will drive it. | |
622 | rspPkt.send(1); | |
623 | ||
624 | @(posedge portVar.$clk); | |
625 | semaphore_put(ldstSyncLock, 1 ); | |
626 | ||
627 | ||
628 | } // LD | |
629 | ||
630 | ||
631 | U_CPX_ST, U_CPX_DIAG_ST, | |
632 | U_CPX_STR_ST, U_CPX_BIS, U_CPX_BLK_ST: { | |
633 | ||
634 | ||
635 | repeat (ordering(rspPkt, "ST RTN")) @(posedge portVar.$clk); | |
636 | // get semaphore for this clock | |
637 | semaphore_get(WAIT, ldstSyncLock, 1); | |
638 | ||
639 | if (reqPkt.addr[39] && myPort == DEV_NCU) { // I/O ? | |
640 | ||
641 | // is it a special address for us? | |
642 | // if (reqPkt.addr[IO_ASI_ADDR_NCU] == IO_ASI_CPU || | |
643 | // reqPkt.addr[IO_ASI_ADDR_NCU] == IO_ASI_NCU) { | |
644 | if (reqPkt.addr[IO_ASI_ADDR_REG] == ASI_SWVR_UDB_INTR_W) { | |
645 | // Send Store Ack but don't store to anything | |
646 | // Send INTERRUPT packet behind it | |
647 | rspPkt2 = new(reqPkt); | |
648 | rspPkt2 = rspPkt.object_copy(); | |
649 | ||
650 | // second packets must not have this set because it will | |
651 | // cause outstandingRequests to decrement twice. | |
652 | rspPkt2.ccxSourced = 0; | |
653 | rspPkt2.sendPorts = 1 << myPort; | |
654 | rspPkt2.createIntr(reqPkt.data[13:8],reqPkt.data[15:14],reqPkt.data[5:0]); | |
655 | } else { | |
656 | tmpData[63:0] = reqPkt.data; | |
657 | void = gUtil.ioSpaceAccess(reqPkt.addr, tmpData, 0, | |
658 | reqPkt.size, thread, myPort); | |
659 | // } | |
660 | } | |
661 | // } else if (reqPkt.addr[39] && myPort !== DEV_NCU && | |
662 | // (reqPkt.addr[39:32] < 8'hA0 || reqPkt.addr[39:32] > 8'hBF) ) { | |
663 | // // I/O but not L2 CSRs | |
664 | // error("I/O ST to L2, but it is not a L2 CSR!"); | |
665 | } else { // not NCU space, L2 or L2 CSR | |
666 | // this only gets done on hit. nas never does this so we wont either. | |
667 | // if (rspPkt.rtntypU == U_CPX_BIS) | |
668 | // gMem.write512({reqPkt.addr[39:6],6'b000000}, 0, myPort); | |
669 | ||
670 | // do not write if inv is set! BIS does not init w/ zeros. | |
671 | if (reqPkt.inv == 0) { | |
672 | gMem.writeBM({reqPkt.addr[39:3],3'b000}, reqPkt.data, reqPkt.size, myPort); | |
673 | if (gParam.mcuMemPrint[WRITE]) { | |
674 | printf("\n%7d00: dumpMem: dumping memory related to this request pkt:",get_cycle()); | |
675 | reqPkt.print(myPort); | |
676 | gMem.dumpMem(reqPkt.addr & 40'hFFFFFFFFC0, 8); | |
677 | } | |
678 | } | |
679 | ||
680 | } | |
681 | ||
682 | // Plusargs to dump LD/ST to logfile | |
683 | if (gParam.show_store) { | |
684 | if (rspPkt.rtntypU == U_CPX_STR_ST) | |
685 | PR_NORMAL(CLASSNAMEQ, MON_NORMAL, | |
686 | psprintf("Tx STORE PA = %h DATA = %h BYTE_MASK = %b TYPE = %0d", | |
687 | reqPkt.addr,reqPkt.data,reqPkt.size,rspPkt.rtntypU)); | |
688 | else | |
689 | PR_NORMAL(CLASSNAMEQ, MON_NORMAL, | |
690 | psprintf("T%d STORE PA = %h DATA = %h BYTE_MASK = %b TYPE = %0d", | |
691 | reqPkt.tid,reqPkt.addr,reqPkt.data,reqPkt.size,rspPkt.rtntypU)); | |
692 | } | |
693 | ||
694 | // L1 cache tags, get inv vec and invalidate all our dupe tags as needed. ST | |
695 | invVector = 0; | |
696 | targetCores = 0; | |
697 | // always target the requester | |
698 | targetCores[reqPkt.cpuId] = 1; | |
699 | ||
700 | for (i=0;i<=gParam.coreMax;i++) { | |
701 | tmpInvVect = getInvalVector(rspPkt.rtntypU, reqPkt, i); | |
702 | if (tmpInvVect) targetCores[i] = 1; | |
703 | invVector = invVector | tmpInvVect; | |
704 | } | |
705 | ||
706 | invField = 0; | |
707 | rspPkt.data = {2'b0, reqPkt.l1wayBis, | |
708 | invField, reqPkt.addr[5:4], reqPkt.cpuId[2:0], | |
709 | reqPkt.addr[11:6],7'b0, reqPkt.addr[3], | |
710 | reqPkt.size[7:0],invVector, reqPkt.data[63:0]}; | |
711 | ||
712 | if (rspPkt.rtntypU == U_CPX_STR_ST) | |
713 | rspPkt.l2miss = 0; | |
714 | ||
715 | #ifdef CCXDEVMEMBFM_DEBUG | |
716 | // state of tag for line at this time | |
717 | rspPkt.lineWay = lineState(reqPkt, *, 1); | |
718 | #endif | |
719 | ||
720 | // queue packet(s) for delivery, in this case, this BFM will drive it. | |
721 | if ((reqPkt.addr[IO_ASI_ADDR_NCU] == IO_ASI_CPU || | |
722 | reqPkt.addr[IO_ASI_ADDR_NCU] == IO_ASI_NCU) && | |
723 | reqPkt.addr[IO_ASI_ADDR_REG] == ASI_SWVR_UDB_INTR_W) { | |
724 | rspPkt.send(1); | |
725 | repeat (rspPkt.pkt2Delay) @(posedge portVar.$clk); | |
726 | // special interrupt "reflection" | |
727 | rspPkt2.l2miss = 0; | |
728 | rspPkt2.send(1); | |
729 | } else { | |
730 | // need to invalidate other cores on store/blkSt/BIS! | |
731 | rspPkt.targetPorts = targetCores; | |
732 | rspPkt.send(1); | |
733 | ||
734 | } | |
735 | ||
736 | // notify LDST sync | |
737 | ldstSync(reqPkt.cpuId,rspPkt); | |
738 | ||
739 | @(posedge portVar.$clk); | |
740 | semaphore_put(ldstSyncLock, 1 ); | |
741 | ||
742 | } // U_CPX_ST, U_CPX_DIAG_ST, U_CPX_STR_ST, U_CPX_BIS | |
743 | ||
744 | ||
745 | U_CPX_D_INVAL, U_CPX_I_INVAL: { | |
746 | ||
747 | integer i; | |
748 | ||
749 | repeat (ordering(rspPkt, "INVAL")) @(posedge portVar.$clk); | |
750 | // get semaphore for this clock | |
751 | semaphore_get(WAIT, ldstSyncLock, 1); | |
752 | ||
753 | // Core wants to invalidate all entries in the cache line | |
754 | if (invField == D_INVAL) { | |
755 | for (i=0; i<=3; i=i+1) { | |
756 | dtag[reqPkt.cpuId].write_tag(i,reqPkt.addr[10:4],29'b0,TAG_INVAL); | |
757 | } | |
758 | } else { | |
759 | for (i=0; i<=7; i=i+1) { | |
760 | itag[reqPkt.cpuId].write_tag(i,{1'b0,reqPkt.addr[10:5]},29'b0,TAG_INVAL); | |
761 | } | |
762 | } | |
763 | ||
764 | invVector = 0; | |
765 | rspPkt.data = {2'b0,reqPkt.l1wayBis,invField,reqPkt.addr[5:4],reqPkt.cpuId, | |
766 | reqPkt.addr[11:6],7'b0,reqPkt.addr[3],reqPkt.size,invVector,reqPkt.data[63:0]}; | |
767 | ||
768 | ||
769 | #ifdef CCXDEVMEMBFM_DEBUG | |
770 | // state of tag for line at this time | |
771 | rspPkt.lineWay = lineState(reqPkt, *, 1); | |
772 | #endif | |
773 | ||
774 | // queue packet(s) for delivery, in this case, this BFM will drive it. | |
775 | rspPkt.send(1); | |
776 | ||
777 | // notify LDST sync | |
778 | ldstSync(reqPkt.cpuId,rspPkt); | |
779 | ||
780 | @(posedge portVar.$clk); | |
781 | semaphore_put(ldstSyncLock, 1 ); | |
782 | ||
783 | ||
784 | } // U_CPX_D_INVAL, U_CPX_I_INVAL | |
785 | ||
786 | ||
787 | ||
788 | U_CPX_CAS_RTN: | |
789 | { | |
790 | ||
791 | if (myPort == DEV_NCU) error("CAS not allowed at NCU"); // I/O | |
792 | ||
793 | repeat (ordering(rspPkt, "CAS RTN")) @(posedge portVar.$clk); | |
794 | // get semaphore for this clock | |
795 | semaphore_get(WAIT, ldstSyncLock, 1); | |
796 | ||
797 | // save away cas1Data, cas1Addr. | |
798 | // hold until next packet (cas2) | |
799 | cas1Addr[thread] = reqPkt.addr; | |
800 | cas1Data[thread] = reqPkt.data; | |
801 | tmpAddr = reqPkt.addr[39:0]; | |
802 | tmpData = gMem.read_mem(tmpAddr,myPort); | |
803 | ||
804 | // remember to swap on following CAS2 pkt. | |
805 | cas_swap[thread] = data_equal(cas1Data[thread],tmpData,reqPkt.size); | |
806 | // for ldst sync | |
807 | rspPkt.CASstore = cas_swap[thread]; | |
808 | ||
809 | // always return the LOAD data at cas1Addr in first response pkt. | |
810 | rspPkt.data = gMem.read128(tmpAddr,myPort,1); | |
811 | if (gParam.mcuMemPrint[READ]) { | |
812 | printf("\n%7d00: dumpMem: dumping memory related to this request pkt:",get_cycle()); | |
813 | reqPkt.print(myPort); | |
814 | gMem.dumpMem(tmpAddr & 40'hFFFFFFFFC0, 8); | |
815 | } | |
816 | ||
817 | // rspPkt.recvPort = reqPkt.cpuId; | |
818 | // rspPkt.recvPorts = 1 << reqPkt.cpuId; | |
819 | rspPkt.nc = 1; | |
820 | rspPkt.tid = reqPkt.tid; | |
821 | rspPkt.atmIf2 = 1; | |
822 | rspPkt.wv = 0; | |
823 | ||
824 | if (gParam.show_load) { | |
825 | if (rspPkt.CASstore) | |
826 | PR_NORMAL(CLASSNAMEQ, MON_NORMAL, | |
827 | psprintf("T%d LOAD PA = %h DATA = %h TYPE = CAS swap will be true", reqPkt.tid,reqPkt.addr,tmpData)); | |
828 | else | |
829 | PR_NORMAL(CLASSNAMEQ, MON_NORMAL, | |
830 | psprintf("T%d LOAD PA = %h DATA = %h TYPE = CAS swap will be false", reqPkt.tid,reqPkt.addr,tmpData)); | |
831 | } | |
832 | ||
833 | ||
834 | #ifdef CCXDEVMEMBFM_DEBUG | |
835 | // state of tag for line at this time | |
836 | rspPkt.lineWay = lineState(reqPkt, *, 1); | |
837 | #endif | |
838 | ||
839 | rspPkt.send(1); | |
840 | ||
841 | // notify LDST sync | |
842 | ldstSync(reqPkt.cpuId,rspPkt); | |
843 | ||
844 | @(posedge portVar.$clk); | |
845 | semaphore_put(ldstSyncLock, 1 ); | |
846 | ||
847 | } // U_CPX_CAS_RTN | |
848 | ||
849 | ||
850 | U_CPX_CAS_ACK: { | |
851 | ||
852 | if (myPort == DEV_NCU) error("CAS not allowed at NCU"); // I/O | |
853 | ||
854 | repeat (ordering(rspPkt, "CAS ACK")) @(posedge portVar.$clk); | |
855 | // get semaphore for this clock | |
856 | semaphore_get(WAIT, ldstSyncLock, 1); | |
857 | ||
858 | // do the swap here on the second pkt. | |
859 | ||
860 | // compare the data at cas1Addr to the data cas1Data. | |
861 | // if ==, swap cas2 data with the data at cas1Addr. | |
862 | // always return the data at cas1Addr in first response pkt. | |
863 | // | |
864 | // cas1Data has rs2 data. | |
865 | // cas1Addr has rs1 addr. | |
866 | // | |
867 | // cas2 data has rd. | |
868 | ||
869 | // if cas_swap true from previous packet, write the rd/cas2 data to mem. | |
870 | if (cas_swap[thread]) { | |
871 | tmpAddr = cas1Addr[thread]; | |
872 | gMem.writeBM(tmpAddr[39:0], reqPkt.data, reqPkt.size, myPort); | |
873 | if (gParam.mcuMemPrint[WRITE]) { | |
874 | printf("\n%7d00: dumpMem: dumping memory related to this request pkt:",get_cycle()); | |
875 | reqPkt.print(myPort); | |
876 | gMem.dumpMem(reqPkt.addr & 40'hFFFFFFFFC0, 8); | |
877 | } | |
878 | ||
879 | // for ldst sync | |
880 | rspPkt.CASstore = 1; | |
881 | ||
882 | // Plusargs to dump LD/ST to logfile | |
883 | if (gParam.show_store && cas_swap[thread]) | |
884 | PR_NORMAL(CLASSNAMEQ, MON_NORMAL, | |
885 | psprintf("T%d STORE PA = %h DATA = %h BYTE_MASK = %b TYPE = CAS swap true", | |
886 | reqPkt.tid,reqPkt.addr,reqPkt.data,reqPkt.size)); | |
887 | cas_swap[thread] = 0; | |
888 | } | |
889 | ||
890 | // ack pkt | |
891 | rspPkt.nc = 1; | |
892 | rspPkt.tid = reqPkt.tid; | |
893 | rspPkt.atmIf2 = 1; | |
894 | rspPkt.wv = 0; | |
895 | ||
896 | // L1 cache tags, get vec and invalidate all duplicate tags as needed. CAS | |
897 | invVector = 0; | |
898 | targetCores = 0; | |
899 | // always target the requester | |
900 | targetCores[reqPkt.cpuId] = 1; | |
901 | ||
902 | for (i=0;i<=gParam.coreMax;i++) { | |
903 | tmpInvVect = getInvalVector(rspPkt.rtntypU, reqPkt, i); | |
904 | if (tmpInvVect) targetCores[i] = 1; | |
905 | invVector = invVector | tmpInvVect; | |
906 | } | |
907 | ||
908 | invField = 0; | |
909 | rspPkt.data = {2'b0,reqPkt.l1wayBis,invField,reqPkt.addr[5:4],reqPkt.cpuId, | |
910 | reqPkt.addr[11:6],7'b0,reqPkt.addr[3],reqPkt.size,invVector,reqPkt.data[63:0]}; | |
911 | ||
912 | ||
913 | #ifdef CCXDEVMEMBFM_DEBUG | |
914 | // state of tag for line at this time | |
915 | rspPkt.lineWay = lineState(reqPkt, *, 1); | |
916 | #endif | |
917 | ||
918 | // queue packet for delivery, in this case, this BFM will drive it. | |
919 | rspPkt.targetPorts = targetCores; | |
920 | rspPkt.send(1); | |
921 | ||
922 | // notify LDST sync | |
923 | ldstSync(reqPkt.cpuId,rspPkt); | |
924 | ||
925 | @(posedge portVar.$clk); | |
926 | semaphore_put(ldstSyncLock, 1 ); | |
927 | ||
928 | } // U_CPX_CAS_ACK | |
929 | ||
930 | ||
931 | U_CPX_SWAP_RTN, U_CPX_SWAP_ACK: | |
932 | // do the swap, the return pkt, and the ack pkt here. | |
933 | // we get addr and 32 bit swap data. Use size mask. | |
934 | { | |
935 | if (myPort == DEV_NCU) error("SWAP not allowed at NCU"); // I/O | |
936 | ||
937 | fork { | |
938 | ||
939 | repeat (ordering(rspPkt, "SWAP RTN")) @(posedge portVar.$clk); | |
940 | // get semaphore for this clock | |
941 | semaphore_get(WAIT, ldstSyncLock, 1); | |
942 | ||
943 | // load | |
944 | tmpAddr = {reqPkt.addr[39:4],4'b0000}; | |
945 | rspPkt.data = gMem.read128(tmpAddr,myPort,1); | |
946 | if (gParam.mcuMemPrint[READ]) { | |
947 | printf("\n%7d00: dumpMem: dumping memory related to this request pkt:",get_cycle()); | |
948 | reqPkt.print(myPort); | |
949 | gMem.dumpMem(tmpAddr & 40'hFFFFFFFFC0, 8); | |
950 | } | |
951 | ||
952 | // Plusargs to dump LD/ST to logfile | |
953 | if (gParam.show_load) | |
954 | PR_NORMAL(CLASSNAMEQ, MON_NORMAL, psprintf("T%d LOAD PA = %h DATA = %h TYPE = SWAP", | |
955 | reqPkt.tid,reqPkt.addr,rspPkt.data)); | |
956 | ||
957 | // return pkt | |
958 | // | |
959 | rspPkt.nc = 1; | |
960 | rspPkt.tid = reqPkt.tid; | |
961 | rspPkt.atmIf2 = 1; | |
962 | rspPkt.wv = 0; | |
963 | // for ldst sync | |
964 | rspPkt.CASstore = 1; | |
965 | ||
966 | // used by fork 2 when it proceeds | |
967 | rspPkt2 = new(reqPkt); | |
968 | rspPkt2 = rspPkt.object_copy(); | |
969 | ||
970 | #ifdef CCXDEVMEMBFM_DEBUG | |
971 | // state of tag for line at this time | |
972 | rspPkt.lineWay = lineState(reqPkt, *, 1); | |
973 | #endif | |
974 | ||
975 | // send load data | |
976 | rspPkt.send(1); | |
977 | ||
978 | // notify LDST sync | |
979 | ldstSync(reqPkt.cpuId,rspPkt); | |
980 | ||
981 | @(posedge portVar.$clk); | |
982 | semaphore_put(ldstSyncLock, 1 ); | |
983 | ||
984 | } // fork 1 | |
985 | ||
986 | ///////////////////////////////// | |
987 | ||
988 | { // fork 2 | |
989 | ||
990 | delay(1); | |
991 | ||
992 | // wait | |
993 | repeat (ordering(rspPkt, "SWAP ACK")) @(posedge portVar.$clk); | |
994 | // get semaphore for this clock | |
995 | semaphore_get(WAIT, ldstSyncLock, 1); | |
996 | ||
997 | // ack pkt | |
998 | // | |
999 | gMem.writeBM({reqPkt.addr[39:3],3'b000}, reqPkt.data, reqPkt.size, myPort); | |
1000 | if (gParam.mcuMemPrint[WRITE]) { | |
1001 | printf("\n%7d00: dumpMem: dumping memory related to this request pkt:",get_cycle()); | |
1002 | reqPkt.print(myPort); | |
1003 | gMem.dumpMem(reqPkt.addr & 40'hFFFFFFFFC0, 8); | |
1004 | } | |
1005 | ||
1006 | rspPkt2.ccxSourced2 = reqPkt.ccxSourced; | |
1007 | // second packet must not have ccxSourced set because it will | |
1008 | // cause outstandingRequests to decrement twice. | |
1009 | rspPkt2.ccxSourced = 0; | |
1010 | rspPkt2.rtntyp = CPX_SWAP_ACK; | |
1011 | rspPkt2.rtntypU = U_CPX_SWAP_ACK; | |
1012 | ||
1013 | // L1 cache tags, get vec and invalidate all duplicate tags as needed. SW | |
1014 | invVector = 0; | |
1015 | targetCores = 0; | |
1016 | // always target the requester | |
1017 | targetCores[reqPkt.cpuId] = 1; | |
1018 | ||
1019 | for (i=0;i<=gParam.coreMax;i++) { | |
1020 | tmpInvVect = getInvalVector(rspPkt2.rtntypU, reqPkt, i); | |
1021 | if (tmpInvVect) targetCores[i] = 1; | |
1022 | invVector = invVector | tmpInvVect; | |
1023 | } | |
1024 | ||
1025 | // Vack | |
1026 | invField = 0; | |
1027 | rspPkt2.data = {2'b0,reqPkt.l1wayBis,invField,reqPkt.addr[5:4],reqPkt.cpuId, | |
1028 | reqPkt.addr[11:6],7'b0,reqPkt.addr[3],reqPkt.size,invVector,reqPkt.data[63:0]}; | |
1029 | ||
1030 | ||
1031 | // Plusargs to dump LD/ST to logfile | |
1032 | if (gParam.show_store) | |
1033 | PR_NORMAL(CLASSNAMEQ, MON_NORMAL, psprintf("T%d STORE PA = %h DATA = %h BYTE_MASK = %b TYPE = SWAP",reqPkt.tid,reqPkt.addr,reqPkt.data,reqPkt.size)); | |
1034 | ||
1035 | #ifdef CCXDEVMEMBFM_DEBUG | |
1036 | // state of tag for line at this time | |
1037 | rspPkt2.lineWay = lineState(reqPkt, *, 1); | |
1038 | #endif | |
1039 | ||
1040 | // queue packet for delivery, in this case, this BFM will drive it. | |
1041 | rspPkt2.targetPorts = targetCores; | |
1042 | rspPkt2.send(1); | |
1043 | ||
1044 | // notify LDST sync | |
1045 | ldstSync(reqPkt.cpuId,rspPkt2); | |
1046 | ||
1047 | @(posedge portVar.$clk); | |
1048 | semaphore_put(ldstSyncLock, 1 ); | |
1049 | ||
1050 | } join none // all | |
1051 | ||
1052 | } // PCX_SWAP_RTN | |
1053 | ||
1054 | ||
1055 | U_CPX_IFILL, U_CPX_NCU_IFILL: { | |
1056 | ||
1057 | repeat (ordering(rspPkt, "IFILL")) @(posedge portVar.$clk); | |
1058 | // get semaphore for this clock | |
1059 | semaphore_get(WAIT, ldstSyncLock, 1); | |
1060 | ||
1061 | tmpAddr = reqPkt.addr; | |
1062 | ||
1063 | // are we NCU? return 1 packet, not 2 | |
1064 | if (myPort == DEV_NCU) { | |
1065 | // Set error bit on fetch to non-boot I/O to match NCU behavior | |
1066 | if (tmpAddr[39] == 1 && tmpAddr[39:32] != 8'hff) | |
1067 | rspPkt.err = 2'b10; // uncorrectable error | |
1068 | rspPkt.wayf4b = 1; // 4 byte fill | |
1069 | tmpAddr = {reqPkt.addr[39:4],4'b0}; // 4 byte addressing!!! | |
1070 | rspPkt.data = gMem.read128(tmpAddr,myPort,1); | |
1071 | if (gParam.mcuMemPrint[READ]) { | |
1072 | printf("\n%7d00: dumpMem: dumping memory related to this request pkt:",get_cycle()); | |
1073 | reqPkt.print(myPort); | |
1074 | gMem.dumpMem(tmpAddr & 40'hFFFFFFFFC0, 8); | |
1075 | } | |
1076 | ||
1077 | //printf("%0d: CcxDevMemBFM[%2d]::respond: T%d read @ reqPkt/tmp %0h/%0h\n", get_time(LO),myPort,reqPkt.tid,reqPkt.addr,tmpAddr); | |
1078 | //printf("%0d: CcxDevMemBFM[%2d]::respond: T%d read data %0h\n", get_time(LO),myPort,reqPkt.tid,rspPkt.data); | |
1079 | ||
1080 | // queue packet for delivery, this BFM instance will end up driving it. | |
1081 | rspPkt.send(1); | |
1082 | ||
1083 | } else { // L2$ | |
1084 | rspPkt2 = new(reqPkt); | |
1085 | rspPkt2 = rspPkt.object_copy(); | |
1086 | ||
1087 | tmpAddr = {reqPkt.addr[39:5],5'b0}; | |
1088 | rspPkt.data = gMem.read128(tmpAddr,myPort, 1); | |
1089 | if (gParam.mcuMemPrint[READ]) { | |
1090 | printf("\n%7d00: dumpMem: dumping memory related to this request pkt:",get_cycle()); | |
1091 | reqPkt.print(myPort); | |
1092 | gMem.dumpMem(tmpAddr & 40'hFFFFFFFFC0, 8); | |
1093 | } | |
1094 | ||
1095 | // second packets must not have this set because it will | |
1096 | // cause outstandingRequests to decrement twice. | |
1097 | rspPkt2.ccxSourced = 0; | |
1098 | ||
1099 | tmpAddr = tmpAddr + 16; //5'b10000; | |
1100 | rspPkt2.atmIf2 = 1; | |
1101 | rspPkt2.l2miss = 0; | |
1102 | rspPkt2.data = gMem.read128(tmpAddr,myPort, 1); | |
1103 | ||
1104 | #ifdef CCXDEVMEMBFM_DEBUG | |
1105 | // state of tag for line at this time | |
1106 | rspPkt.lineWay = lineState(reqPkt, *, 1); | |
1107 | #endif | |
1108 | // L1$ tag update. | |
1109 | updateItag(reqPkt, rspPkt, rspPkt2, reqPkt.cpuId, TAG_VAL); | |
1110 | ||
1111 | ||
1112 | // queue packets for delivery. | |
1113 | // | |
1114 | // Atomics must stay in order. The CPX rtl will hold the first | |
1115 | // pkt until the second pkt arrives. They will arrive at the core | |
1116 | // back to back. Putting time between pkt1 and pkt2 tests the CCX only, | |
1117 | // the core never sees time between them. The 2 pkts MUST go into the | |
1118 | // mailbox back to back to avoid another pkt from this port getting between | |
1119 | // them. | |
1120 | rspPkt.send(1); | |
1121 | rspPkt2.send(1); | |
1122 | ||
1123 | } | |
1124 | ||
1125 | // notify LDST sync | |
1126 | // ldstSync(reqPkt.cpuId,rspPkt); | |
1127 | ||
1128 | @(posedge portVar.$clk); | |
1129 | semaphore_put(ldstSyncLock, 1 ); | |
1130 | ||
1131 | } | |
1132 | ||
1133 | ||
1134 | default : { | |
1135 | rspPkt.print(myPort); | |
1136 | PR_ERROR (CLASSNAMEQ, MON_ERR, | |
1137 | psprintf ("T%d Port=%0d rtntyp=%b. Unsupported rtntyp for CCX MEM device BFM.",thread,myPort, rspPkt.rtntyp)); | |
1138 | } | |
1139 | } // case | |
1140 | ||
1141 | } | |
1142 | ||
1143 | ||
1144 | task CLASSNAME::updateDtag(PcxPkt reqPkt, CpxPkt rspPkt, reg [2:0] cpuId, integer how = TAG_VAL) | |
1145 | { | |
1146 | reg [3:0] match; | |
1147 | ||
1148 | if (myPort == DEV_NCU || cacheOff || reqPkt.nc == 1) return; | |
1149 | ||
1150 | // Update D$ tag table | |
1151 | dtag[cpuId].write_tag (reqPkt.l1wayMMUid, | |
1152 | reqPkt.addr[10:4], | |
1153 | reqPkt.addr[39:11],how); | |
1154 | ||
1155 | // Invalidate the other (I) tag table | |
1156 | match = itag[cpuId].get_way ("D, chk 4 hit in I:", | |
1157 | {1'b0,reqPkt.addr[10:5]}, | |
1158 | reqPkt.addr[39:11]); | |
1159 | if (match != 4'b0) { | |
1160 | itag[cpuId].write_tag(match[3:1],{1'b0,reqPkt.addr[10:5]},29'b0,TAG_INVAL); | |
1161 | rspPkt.wv = 1'b1; | |
1162 | rspPkt.wayMMUid = match[3:2]; | |
1163 | rspPkt.wayf4b = match[1]; | |
1164 | } | |
1165 | ||
1166 | } | |
1167 | ||
1168 | ||
1169 | ||
1170 | task CLASSNAME::updateItag(PcxPkt reqPkt, CpxPkt rspPkt, | |
1171 | CpxPkt rspPkt2 = null, reg [2:0] cpuId, integer how = TAG_VAL) | |
1172 | { | |
1173 | reg [3:0] match; | |
1174 | ||
1175 | if (myPort == DEV_NCU || cacheOff) return; | |
1176 | ||
1177 | if (!reqPkt.nc) { | |
1178 | ||
1179 | if (match != 4'b0) | |
1180 | PR_ERROR(CLASSNAMEQ, MON_ERR, | |
1181 | psprintf ("ERROR itag: DUT request to L2$ 0x%0h should have hit in the L1$.\n\n",reqPkt.addr)); | |
1182 | ||
1183 | // Update I$ tag table | |
1184 | itag[cpuId].write_tag ({reqPkt.l1wayBis,reqPkt.l1wayMMUid}, | |
1185 | {1'b0,reqPkt.addr[10:5]}, | |
1186 | reqPkt.addr[39:11],how,1); | |
1187 | } | |
1188 | ||
1189 | // Invalidate the other (D) tag table (whether nc=0|1) | |
1190 | match = dtag[cpuId].get_way ("I, chk 4 hit in D:", | |
1191 | reqPkt.addr[10:4], | |
1192 | reqPkt.addr[39:11]); | |
1193 | if (match != 4'b0) { | |
1194 | dtag[cpuId].write_tag({1'b0,match[3:2]},reqPkt.addr[10:4],29'b0,TAG_INVAL); | |
1195 | rspPkt.wv = 1'b1; | |
1196 | rspPkt.wayMMUid = match[3:2]; | |
1197 | } | |
1198 | // ifill covers 2 D$ lines | |
1199 | if (rspPkt2 !== null) { | |
1200 | match = dtag[cpuId].get_way ("I, chk 4 hit in D:", | |
1201 | reqPkt.addr[10:4]+1, | |
1202 | reqPkt.addr[39:11]); | |
1203 | if (match != 4'b0) { | |
1204 | dtag[cpuId].write_tag({1'b0,match[3:2]},reqPkt.addr[10:4]+1,29'b0,TAG_INVAL); | |
1205 | rspPkt2.wv = 1'b1; | |
1206 | rspPkt2.wayMMUid = match[3:2]; | |
1207 | } | |
1208 | } | |
1209 | ||
1210 | } | |
1211 | ||
1212 | ||
1213 | ||
1214 | // use to receive an expected packet. | |
1215 | // mainly for CCX testing. | |
1216 | // | |
1217 | // user passes in a packet whos fields are set to match the | |
1218 | // packet that should show up at this port. When it does, the | |
1219 | // caller is notified (toggle a var in the passed in packet) and | |
1220 | // the passed in packet will be populated with what showed up at the | |
1221 | // destinatin port. Unexpected (not registered via a call to this task) | |
1222 | // packets will cause failure. | |
1223 | task CLASSNAME::recv(BasePkt pktHndl) { | |
1224 | ||
1225 | // PcxPkt pcxPkt; | |
1226 | ||
1227 | // assign/cast pktHndl to be of PcxPkt type rather than base | |
1228 | //cast_assign(pcxPkt,pktHndl); | |
1229 | ||
1230 | // load signature hash. key is signature and data is clk count at call time. | |
1231 | // expectedSig[pcxPkt.getVector()] = pktHndl; | |
1232 | expectedSig[pktHndl.getVector()] = pktHndl; | |
1233 | ||
1234 | } | |
1235 | ||
1236 | // Mainly for CCX testing. Call when a pkt should no longer arrive. | |
1237 | // For CCX, a pkt should never intentionally get dropped so this may not get used. | |
1238 | task CLASSNAME::cancelRecv(BasePkt pktHndl) { | |
1239 | ||
1240 | // PcxPkt pcxPkt; | |
1241 | ||
1242 | // assign/cast pktHndl to be of PcxPkt type rather than base | |
1243 | //cast_assign(pcxPkt,pktHndl); | |
1244 | ||
1245 | // clear signature hash. | |
1246 | // void = assoc_index(DELETE,expectedSig,pcxPkt.getVector()); | |
1247 | void = assoc_index(DELETE,expectedSig,pktHndl.getVector()); | |
1248 | ||
1249 | } | |
1250 | ||
1251 | ||
1252 | ||
1253 | //---------------------------------------------------------- | |
1254 | // Compare 2 data vectors using size as a mask | |
1255 | function reg CLASSNAME::data_equal (reg [63:0] data1, | |
1256 | reg [63:0] data2, | |
1257 | reg[7:0] size) { | |
1258 | ||
1259 | reg eq0,eq1,eq2,eq3,eq4,eq5,eq6,eq7; | |
1260 | ||
1261 | eq7 = !size[7] | (data1[63:56]==data2[63:56]); | |
1262 | eq6 = !size[6] | (data1[55:48]==data2[55:48]); | |
1263 | eq5 = !size[5] | (data1[47:40]==data2[47:40]); | |
1264 | eq4 = !size[4] | (data1[39:32]==data2[39:32]); | |
1265 | eq3 = !size[3] | (data1[31:24]==data2[31:24]); | |
1266 | eq2 = !size[2] | (data1[23:16]==data2[23:16]); | |
1267 | eq1 = !size[1] | (data1[15: 8]==data2[15: 8]); | |
1268 | eq0 = !size[0] | (data1[ 7: 0]==data2[ 7: 0]); | |
1269 | ||
1270 | data_equal = eq7 & eq6 & eq5 & eq4 & eq3 & eq2 & eq1 & eq0; | |
1271 | ||
1272 | } | |
1273 | ||
1274 | ||
1275 | ||
1276 | // hit indication, what L2 thinks the core L1 has. | |
1277 | // returns invalidation vector for the indicated core. | |
1278 | // Does the invalidate of our duplicate tags. | |
1279 | function reg [31:0] CLASSNAME::getInvalVector(integer type, | |
1280 | PcxPkt reqPkt, | |
1281 | reg [2:0] cpuId) | |
1282 | { | |
1283 | ||
1284 | reg [31:0] invVect_d, invVect_i; | |
1285 | reg [3:0] dmatch, imatch; | |
1286 | ||
1287 | if (myPort == DEV_NCU || cacheOff) { | |
1288 | getInvalVector = 0; | |
1289 | return; | |
1290 | } | |
1291 | ||
1292 | #ifdef CCXDEVMEMBFM_DEBUG | |
1293 | // state of tag for line at this time | |
1294 | dtag[cpuId].dump_line("getInvalVector D1:", | |
1295 | reqPkt.addr[10:4], | |
1296 | MON_ALWAYS); | |
1297 | #endif | |
1298 | ||
1299 | // Check if store hit the D$ of given cpuId | |
1300 | dmatch = dtag[cpuId].get_way("getInvalVector:", | |
1301 | reqPkt.addr[10:4], | |
1302 | reqPkt.addr[39:11]); | |
1303 | ||
1304 | //create_vector (INSTR_TAG,dmatch,invVect_d,cpuId); | |
1305 | invVect_d = dmatch << (cpuId * 4); | |
1306 | ||
1307 | // Invalidate entry in given cpuId (all cpu's) D$ if found... | |
1308 | // Do this for Stream ST, Atomics (CAS/SWAP), Block *ST. | |
1309 | if ((type == U_CPX_STR_ST || | |
1310 | type == U_CPX_BIS || | |
1311 | type == U_CPX_CAS_ACK || | |
1312 | type == U_CPX_CAS_RTN || | |
1313 | type == U_CPX_BLK_ST || | |
1314 | type == U_CPX_SWAP_RTN || | |
1315 | type == U_CPX_SWAP_ACK || | |
1316 | reqPkt.cpuId !== cpuId) && // hitting core is not requesting core | |
1317 | dmatch) | |
1318 | dtag[cpuId].write_tag({1'b0,dmatch[3:2]}, | |
1319 | reqPkt.addr[10:4], | |
1320 | 29'b0,TAG_INVAL); | |
1321 | ||
1322 | // Check if store hit the I$ | |
1323 | imatch = itag[cpuId].get_way("getInvalVector:", | |
1324 | {1'b0,reqPkt.addr[10:5]}, | |
1325 | reqPkt.addr[39:11]); | |
1326 | ||
1327 | // Invalidate entry in I$ if found | |
1328 | if (imatch) itag[cpuId].write_tag(imatch[3:1], | |
1329 | {1'b0,reqPkt.addr[10:5]}, | |
1330 | 29'b0,TAG_INVAL); | |
1331 | ||
1332 | //create_vector (INSTR_TAG,imatch,invVect_i,cpuId); | |
1333 | invVect_i = imatch << (cpuId * 4); | |
1334 | ||
1335 | // Per spec, you cannot get match in both I$ & D$ | |
1336 | if ((imatch!=0)&(dmatch!=0)) { | |
1337 | PR_ERROR(CLASSNAMEQ, MON_ERR, | |
1338 | psprintf ("found match in both D$ and I$ for core %0d. D$=%b, I$=%b",cpuId,dmatch,imatch)); | |
1339 | } | |
1340 | ||
1341 | getInvalVector = invVect_d | invVect_i; | |
1342 | ||
1343 | ||
1344 | #ifdef CCXDEVMEMBFM_DEBUG | |
1345 | // state of tag for line at this time | |
1346 | dtag[cpuId].dump_line("getInvalVector D2:", | |
1347 | reqPkt.addr[10:4], | |
1348 | MON_ALWAYS); | |
1349 | #endif | |
1350 | ||
1351 | } | |
1352 | ||
1353 | ||
1354 | task CLASSNAME::ldstSync(reg [2:0] cpuId, CpxPkt rspPkt) { | |
1355 | ||
1356 | if (rspPkt.ccxSourced || rspPkt.ccxSourced2 || rspPkt.rtntyp == CPX_EVICT) { | |
1357 | // notify nas LD/ST Sync for all but these types. | |
1358 | // CAS that writes to L2 (CASstore), gntTarget, pa, pkt. | |
1359 | if (myPort > 7 && | |
1360 | rspPkt.rtntypU !== U_CPX_STR_LD && | |
1361 | rspPkt.rtntypU !== U_CPX_MMU_RTN && | |
1362 | rspPkt.rtntypU !== U_CPX_PREF) { | |
1363 | // these are NR0 interface types (return to zero) | |
1364 | gLdStSyncPort[myPort].$cid <= cpuId; | |
1365 | gLdStSyncPort[myPort].$ctrue <= rspPkt.CASstore; | |
1366 | //gLdStSyncPort[myPort].$swap <= (rspPkt.rtntypU == U_CPX_SWAP_ACK || rspPkt.rtntypU == U_CPX_SWAP_RTN); // swap; | |
1367 | gLdStSyncPort[myPort].$swap <= 0; | |
1368 | gLdStSyncPort[myPort].$pa <= rspPkt.addr; | |
1369 | gLdStSyncPort[myPort].$pkt <= rspPkt.getVector(); | |
1370 | } | |
1371 | } | |
1372 | ||
1373 | } | |
1374 | ||
1375 | ||
1376 | // special task to potentially create an eviction packet to | |
1377 | // send to some cores. This task will make sure that LDST sync | |
1378 | // gets notified correctly. It also makes sure that | |
1379 | // duplicate tag updating is ordered and sane. This "psudo request" | |
1380 | // is ordered like any other. | |
1381 | // | |
1382 | // Caller can specify a target address to evict. Will do nothing if address | |
1383 | // is not cached. Otherwise, we find an address. | |
1384 | task CcxDevMemBFM::enqueueEvict(reg [7:0] coreEnable, | |
1385 | reg [39:0] evictPA = 40'hffffffffff, | |
1386 | reg [3:0] cid = 4'hf, | |
1387 | reg all_cores = 0, | |
1388 | integer dCacheWeight = 60) { | |
1389 | ||
1390 | ccxPort portVar = gCpxPort[myPort]; | |
1391 | reg [7:0] targets; | |
1392 | CpxPkt pkt; | |
1393 | reg [127:0] vect; | |
1394 | ||
1395 | ||
1396 | pkt = new(); | |
1397 | //pkt.randomize(); | |
1398 | //pkt.responseDelay = pkt.pkt2Delay; | |
1399 | pkt.responseDelay = 2; | |
1400 | pkt.tid = cid; | |
1401 | pkt.sendPorts = 1 << myPort; | |
1402 | pkt.rtntyp = CPX_EVICT; | |
1403 | pkt.rtntypU = U_CPX_EVICT; | |
1404 | pkt.addr = evictPA; | |
1405 | pkt.l2miss = 0; | |
1406 | ||
1407 | repeat (ordering(pkt, "XEVICT")) @(posedge portVar.$clk); | |
1408 | // get semaphore for this clock | |
1409 | semaphore_get(WAIT, ldstSyncLock, 1); | |
1410 | ||
1411 | // get vector and update L1 dup tags | |
1412 | vect = gUtil.evictVector (coreEnable, | |
1413 | evictPA, | |
1414 | cid, | |
1415 | targets); | |
1416 | ||
1417 | // vect = gUtil.evictVinv(coreEnable, | |
1418 | // targets, | |
1419 | // evictPA, | |
1420 | // cid, | |
1421 | // all_cores, | |
1422 | // dCacheWeight); | |
1423 | ||
1424 | #ifdef CCXDEVMEMBFM_DEBUG | |
1425 | if (! targets) printf("EVICTION gUtil.evictVector did not return any targets!!!\n"); | |
1426 | else printf("EVICTION gUtil.evictVector return targets = %b vec = %h\n", targets,vect); | |
1427 | #endif | |
1428 | ||
1429 | if (vect && targets) { | |
1430 | pkt.targetPorts = targets; | |
1431 | pkt.addr = evictPA; | |
1432 | pkt.tid = cid; | |
1433 | pkt.data = vect; | |
1434 | ldstSync(0,pkt); // notify | |
1435 | pkt.send(1); // doit | |
1436 | ||
1437 | PR_NORMAL(CLASSNAMEQ, MON_NORMAL, | |
1438 | psprintf ("Sending EVICTION pkt to cores targets=0x%h, a=0x%h, vec=0x%h", | |
1439 | pkt.targetPorts,pkt.addr,pkt.data)); | |
1440 | } else pkt = null; | |
1441 | ||
1442 | @(posedge portVar.$clk); | |
1443 | semaphore_put(ldstSyncLock, 1 ); | |
1444 | } | |
1445 | ||
1446 | ||
1447 | // used for debug only | |
1448 | function reg [3:0] CLASSNAME::lineState(PcxPkt reqPkt, | |
1449 | string why="debug lineState:", | |
1450 | reg quiet=1) | |
1451 | { | |
1452 | ||
1453 | if (reqPkt.rqtypU == U_PCX_IFILL || reqPkt.rqtyp == PCX_IFILL) { | |
1454 | lineState = itag[reqPkt.cpuId].get_way(why, | |
1455 | {1'b0,reqPkt.addr[10:5]}, | |
1456 | reqPkt.addr[39:11]); | |
1457 | if (!quiet) | |
1458 | itag[reqPkt.cpuId].dump_line(why, reqPkt.addr[10:4],MON_INFO); | |
1459 | } | |
1460 | else { | |
1461 | lineState = dtag[reqPkt.cpuId].get_way(why, | |
1462 | reqPkt.addr[10:4], | |
1463 | reqPkt.addr[39:11]); | |
1464 | if (!quiet) | |
1465 | dtag[reqPkt.cpuId].dump_line(why, reqPkt.addr[10:4],MON_INFO); | |
1466 | } | |
1467 | ||
1468 | ||
1469 | } | |
1470 | ||
1471 | ||
1472 | // hold off the responses until outstandingReqs reaches amount. | |
1473 | // this will clump responses into bursts periodically. | |
1474 | task CLASSNAME::burstResp(integer amount) | |
1475 | { | |
1476 | reg [63:0] tmp; | |
1477 | reg iSync = 0; | |
1478 | integer wait, reason; | |
1479 | ccxPort portVar = gCpxPort[myPort]; | |
1480 | ||
1481 | if (gParam.burstSync == myPort-8 || gParam.burstSync == myPort) iSync = 1; | |
1482 | wait = gParam.burstHoldoff; | |
1483 | repeat (10) @(negedge gPcxPort[myPort].$clk); | |
1484 | tmp = gUtil.getThreadEnables(); | |
1485 | while (tmp !== gOutOfBoot) wait_var(gOutOfBoot); // all threads out of boot | |
1486 | ||
1487 | // make stall agreeable so we do not stall cores requests when trying to | |
1488 | // build up a burst | |
1489 | stallStart = amount + 6; | |
1490 | PR_ALWAYS(CLASSNAMEQ, MON_ALWAYS, | |
1491 | psprintf("Port %2d, stallStart changed to %0d due to burst option.",myPort, stallStart)); | |
1492 | while (1) { | |
1493 | if (outstandingReqs < amount) { // just drain naturally if too many in queue | |
1494 | ||
1495 | @(posedge portVar.$clk); | |
1496 | semaphore_get(WAIT, ldstSyncLock, 1); // stop responding | |
1497 | ||
1498 | // start respoding after amount or burstSync or x clocks | |
1499 | fork | |
1500 | { | |
1501 | while (outstandingReqs < amount) wait_var(outstandingReqs); // wait | |
1502 | reason = 1; | |
1503 | } | |
1504 | { | |
1505 | repeat (wait) @(posedge gPcxPort[myPort].$clk); // but not too long | |
1506 | reason = 2; | |
1507 | } | |
1508 | { | |
1509 | wait_var(burstSync); // burst on sync when in use | |
1510 | reason = 3; | |
1511 | } | |
1512 | join any | |
1513 | terminate; // kill remaining forks | |
1514 | ||
1515 | if (iSync) burstSync = ~burstSync; // signal the other banks | |
1516 | ||
1517 | if (outstandingReqs >= 2) | |
1518 | PR_INFO(CLASSNAMEQ, MON_INFO, | |
1519 | psprintf("Port %2d, Letting %0d packets burst through (reason=%0d)",myPort, outstandingReqs, reason)); | |
1520 | ||
1521 | @(posedge portVar.$clk); | |
1522 | semaphore_put(ldstSyncLock, 1 ); // allow normal responses | |
1523 | } | |
1524 | repeat (5) @(posedge gPcxPort[myPort].$clk); // give a little break | |
1525 | } // rinse and repeat | |
1526 | } |