Commit | Line | Data |
---|---|---|
86530b38 AT |
1 | // ========== Copyright Header Begin ========================================== |
2 | // | |
3 | // OpenSPARC T2 Processor File: sparcBenchUtils.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 | #include <std_display_defines.vri> | |
37 | #include <std_display_class.vrh> | |
38 | #include <plusArgMacros.vri> | |
39 | #include <defines.vri> | |
40 | ||
41 | #include <cmp.vri> | |
42 | #include <globals.vri> | |
43 | ||
44 | #ifndef FC_BENCH | |
45 | #ifndef FC_SCAN_BENCH | |
46 | #include <verilog_tasks_ncu.vri> | |
47 | #endif | |
48 | #endif | |
49 | ||
50 | // mem slam, reg slam | |
51 | #include <verilog_tasks_misc.vri> | |
52 | ||
53 | #include <std_display_class.vrh> | |
54 | #include <baseParamsClass.vrh> | |
55 | #include <sparcParams.vrh> | |
56 | #include <baseUtilsClass.vrh> | |
57 | #include <memArray.vrh> | |
58 | ||
59 | #ifndef CCM_BENCH | |
60 | #ifndef FC_BENCH | |
61 | #ifndef FC_SCAN_BENCH | |
62 | #include <ccx_tag_class.vrh> | |
63 | #endif | |
64 | #endif | |
65 | #endif | |
66 | ||
67 | #include <sparcBenchUtils_if.vrh> | |
68 | ||
69 | #define CLASSNAME SparcBenchUtils | |
70 | #define CLASSNAMEQ "SparcBenchUtils" | |
71 | ||
72 | class CLASSNAME extends BaseUtils { | |
73 | ||
74 | local string className = "SparcBenchUtils"; | |
75 | local StandardDisplay dbg; | |
76 | local reg noNCUcheck = 0; | |
77 | local reg failNCUcheck = 0; | |
78 | ||
79 | // rands | |
80 | local randc reg [5:0] startThread; | |
81 | local randc reg [2:0] randCID; | |
82 | local rand integer parkWait; | |
83 | // end rands | |
84 | ||
85 | // constraints | |
86 | constraint parkWait_c { | |
87 | parkWait <= gParam.por_delay_max; | |
88 | parkWait >= gParam.por_delay_min; | |
89 | } | |
90 | // end constraints | |
91 | ||
92 | ||
93 | task new(StandardDisplay dbgHndl, integer clockPeriod = 100); | |
94 | ||
95 | task resetDut(); | |
96 | ||
97 | function reg ioSpaceAccess(reg [63:0] addr, // full addr of request | |
98 | var reg [127:0] data, // r/w data | |
99 | reg read = 1, // request is read, else write | |
100 | reg [7:0] size = 8'hff, // write byte mask | |
101 | integer thread = 99, // please provide | |
102 | integer myPort = 99); // optional for messaging | |
103 | ||
104 | function reg [127:0] copyDataByte (reg [127:0] data, | |
105 | reg [7:0] size, | |
106 | reg [3:0] offset); | |
107 | ||
108 | function reg [63:0] evictAddr (reg [7:0] core_enable, | |
109 | var reg [2:0] cidUsed, | |
110 | reg [3:0] cid = 4'hf, | |
111 | integer dCacheWeight = 60); | |
112 | ||
113 | function reg [127:0] evictVector (reg [7:0] core_enable, | |
114 | reg [63:0] evict_pa, | |
115 | reg [2:0] cpuId, | |
116 | var reg [7:0] targets); | |
117 | ||
118 | function reg [63:0] getThreadEnables(); | |
119 | function reg [63:0] getRunStatus(); | |
120 | function reg [2:0] whichBank(reg [63:0] addr); | |
121 | } | |
122 | ||
123 | ||
124 | //---------------------------------------------------------- | |
125 | // Do not call randomize() in this classes new() task. | |
126 | // The final class extention of this class must (does) do that in its new()! | |
127 | // Go ahead an assume that the first set of random numbers are available. | |
128 | task CLASSNAME::new(StandardDisplay dbgHndl, integer clockPeriod = 100) { | |
129 | super.new(dbgHndl, clockPeriod); | |
130 | this.dbg = dbgHndl; | |
131 | if (mChkPlusarg(noNCUcheck)) noNCUcheck = 1; | |
132 | if (mChkPlusarg(failNCUcheck)) failNCUcheck = 1; | |
133 | } | |
134 | ||
135 | ||
136 | // call AFTER time zero | |
137 | function reg [63:0] CLASSNAME::getThreadEnables() { | |
138 | //getThreadEnables = sparcBenchUtils_if.th_check_enable; // -vcs_run_args=+thread=1 | |
139 | getThreadEnables = gParam.finishMask; // this is better | |
140 | } | |
141 | ||
142 | // call AFTER time zero. gets core_running_status. | |
143 | function reg [63:0] CLASSNAME::getRunStatus() { | |
144 | getRunStatus = sparcBenchUtils_if.core_running_status; | |
145 | } | |
146 | ||
147 | ||
148 | // return which bank this address goes to. | |
149 | // for partial bank mode | |
150 | function reg [2:0] CLASSNAME::whichBank(reg [63:0] addr) { | |
151 | ||
152 | case (gParam.bank_set_mask) { | |
153 | 1: whichBank = addr[6]; | |
154 | 2: whichBank = {2'b01,addr[6]}; | |
155 | 3: whichBank = {1'b0,addr[7:6]}; | |
156 | 4: whichBank = {2'b10,addr[6]}; | |
157 | 5: whichBank = {addr[7],1'b0,addr[6]}; | |
158 | 6: whichBank = {addr[7],~addr[7],addr[6]}; | |
159 | 8: whichBank = {2'b11,addr[6]}; | |
160 | 9: whichBank = {addr[7],addr[7],addr[6]}; | |
161 | 10: whichBank = {addr[7],1'b1,addr[6]}; | |
162 | 12: whichBank = {1'b1,addr[7],addr[6]}; | |
163 | 15: whichBank = {addr[8],addr[7],addr[6]}; | |
164 | default: error("bank_set_mask is illegal\n"); | |
165 | } | |
166 | ||
167 | } | |
168 | ||
169 | ||
170 | ||
171 | //---------------------------------------------------------- | |
172 | // start threads | |
173 | // | |
174 | // rules: | |
175 | // 1 - always start with the lowest core, after that randomize core selection. | |
176 | // 2 - in a core, always start the lowest thread first. | |
177 | // 3 - after each core has had its lowest thread started, randomize the | |
178 | // rest of the threads. | |
179 | // | |
180 | ||
181 | task CLASSNAME::resetDut () { | |
182 | ||
183 | ||
184 | //reg [31:0] resetThis; | |
185 | reg [63:0] threadEnables; | |
186 | integer core, tid, tmp; | |
187 | ||
188 | integer lowCore; // lowest core from threadEnables | |
189 | integer lowThread [8]; // lowest thread in each core | |
190 | reg [63:0] doneThreads; // have been started already | |
191 | reg [7:0] doneCores; // have been started already | |
192 | ||
193 | ||
194 | #ifndef FC_BENCH | |
195 | #ifndef FC_SCAN_BENCH | |
196 | ||
197 | for (tid=0; tid<8; tid++) lowThread[tid] = 32'hx; | |
198 | ||
199 | void = this.rand_mode(OFF, "startThread"); | |
200 | ||
201 | // do not unpark too early. | |
202 | if (sparcBenchUtils_if.core_cycle_cnt < 12) | |
203 | repeat (12 - sparcBenchUtils_if.core_cycle_cnt) | |
204 | @(negedge sparcBenchUtils_if.clk); | |
205 | ||
206 | // range is 28-48 (38 ideal) cycles from time zero if +forcePORstate | |
207 | if (gParam.forcePORstate) { | |
208 | if (sparcBenchUtils_if.core_cycle_cnt < 38) | |
209 | repeat (38 - sparcBenchUtils_if.core_cycle_cnt) | |
210 | @(negedge sparcBenchUtils_if.clk); | |
211 | } | |
212 | ||
213 | threadEnables = sparcBenchUtils_if.th_check_enable; // -vcs_run_args=+thread=1 | |
214 | if (! threadEnables) { | |
215 | PR_ALWAYS(className, MON_ALWAYS, | |
216 | psprintf("Unparking threads: th_check_enable = 0, Will not unpark ANY threads!!!")); | |
217 | return; | |
218 | } | |
219 | PR_ALWAYS(className, MON_ALWAYS, | |
220 | psprintf("Unparking threads: th_check_enable = %h",threadEnables)); | |
221 | ||
222 | // find lowest core | |
223 | for (core=0; core<8; core++) { | |
224 | if (|threadEnables[7+(core*8):core*8]) { | |
225 | lowCore = core; | |
226 | break; | |
227 | } else doneCores[core] = 1; | |
228 | } | |
229 | ||
230 | // find lowest thread in each core | |
231 | for (core=lowCore; core<8; core++) { | |
232 | for (tid=0; tid<8; tid++) { | |
233 | if (threadEnables[tid+(8*core)] == 1) { | |
234 | lowThread[core] = tid; | |
235 | break; | |
236 | } | |
237 | } | |
238 | } | |
239 | ||
240 | doneThreads = ~threadEnables; // call unused tids done for starters | |
241 | ||
242 | PR_ALWAYS(className, MON_ALWAYS, | |
243 | psprintf("Unparking: low core = %0d, low threads (0-07): %1d,%1d,%1d,%1d,%1d,%1d,%1d,%1d",lowCore,lowThread[7],lowThread[6],lowThread[5],lowThread[4],lowThread[3],lowThread[2],lowThread[1],lowThread[0])); | |
244 | PR_ALWAYS(className, MON_ALWAYS, | |
245 | psprintf("Unparking: low core = %0d, low threads (0-63): %2d,%2d,%2d,%2d,%2d,%2d,%2d,%2d",lowCore,lowThread[7]+56,lowThread[6]+48,lowThread[5]+40,lowThread[4]+32,lowThread[3]+24,lowThread[2]+16,lowThread[1]+8,lowThread[0])); | |
246 | ||
247 | // POR threads in random orders after lowest core lowest tid | |
248 | // but lowest first for each core. | |
249 | // this is lowest first... | |
250 | tmp = (lowCore*8)+lowThread[lowCore]; | |
251 | verilog_write_cmp_reg({ASI_CMP_CORE,ASI_CMP_CORE_RUNNING_W1S}, | |
252 | 1 << tmp, tmp); | |
253 | ||
254 | doneThreads[tmp] = 1; // mark it done | |
255 | doneCores[lowCore] = &doneThreads[7+(lowCore*8):lowCore*8]; // core done? | |
256 | ||
257 | PR_ALWAYS(className, MON_ALWAYS, | |
258 | psprintf("T%0d unparked (lowest tid in lowest core)\n",tmp)); | |
259 | ||
260 | // quick check | |
261 | if (doneThreads !== 64'hffffffffffffffff) { | |
262 | ||
263 | // now the rest | |
264 | ||
265 | // start the lowest thread on each core (except lowest core). | |
266 | // they could be chosen at random or sequential. | |
267 | repeat (8) { | |
268 | core = randCID; | |
269 | void = this.randomize(); | |
270 | if (core == lowCore || doneCores[core]) continue; | |
271 | if (lowThread[core] !== 32'hx) { | |
272 | tmp = (core*8)+lowThread[core]; | |
273 | // parkWait = urandom_range(gParam.por_delay_max,gParam.por_delay_min); | |
274 | // PR_DEBUG (className, MON_DEBUG, | |
275 | // psprintf("T%0d post unpark delay %d\n",get_cycle(),resetThis,del)); | |
276 | repeat (parkWait) @(posedge sparcBenchUtils_if.clk); | |
277 | verilog_write_cmp_reg({ASI_CMP_CORE,ASI_CMP_CORE_RUNNING_W1S}, | |
278 | 1 << tmp, tmp); | |
279 | PR_ALWAYS(className, MON_ALWAYS, | |
280 | psprintf("T%0d unparked (lowest tid in core)\n",tmp)); | |
281 | doneThreads[tmp] = 1; // mark it done | |
282 | doneCores[core] = &doneThreads[7+(core*8):core*8]; // core done? | |
283 | } | |
284 | } | |
285 | ||
286 | // start the remaining threads at random | |
287 | repeat (64) { | |
288 | if (doneThreads[startThread] == 0) { | |
289 | void = this.randomize(); | |
290 | // parkWait = urandom_range(gParam.por_delay_max,gParam.por_delay_min); | |
291 | // PR_DEBUG (className, MON_DEBUG, | |
292 | // psprintf("T%0d post unpark delay %d\n",get_cycle(),resetThis,del)); | |
293 | repeat (parkWait) @(posedge sparcBenchUtils_if.clk); | |
294 | ||
295 | verilog_write_cmp_reg({ASI_CMP_CORE,ASI_CMP_CORE_RUNNING_W1S}, | |
296 | 1 << startThread, | |
297 | startThread); | |
298 | PR_ALWAYS(className, MON_ALWAYS, | |
299 | psprintf("T%0d unparked (remaining non-lowest tids)\n",startThread)); | |
300 | doneThreads[startThread] = 1; // mark it done | |
301 | ||
302 | } | |
303 | void = this.rand_mode(ON, "startThread"); | |
304 | void = this.randomize(); | |
305 | void = this.rand_mode(OFF, "startThread"); | |
306 | } | |
307 | ||
308 | } | |
309 | ||
310 | #endif | |
311 | #endif | |
312 | } | |
313 | ||
314 | /* | |
315 | task CLASSNAME::resetDut () { | |
316 | ||
317 | ||
318 | reg [31:0] tid, resetThis; | |
319 | reg [63:0] threadEnables; | |
320 | integer lowThread, del; | |
321 | ||
322 | #ifndef FC_BENCH | |
323 | ||
324 | void = this.rand_mode(OFF, "startThread"); | |
325 | ||
326 | repeat (5) @(negedge sparcBenchUtils_if.clk); | |
327 | ||
328 | threadEnables = sparcBenchUtils_if.th_check_enable; // -vcs_run_args=+thread=1 | |
329 | ||
330 | PR_ALWAYS(className, MON_ALWAYS, | |
331 | psprintf("Unparking threads: th_check_enable = %h",threadEnables)); | |
332 | ||
333 | // find lowest thread | |
334 | for (tid=0; tid<=63; tid++) { | |
335 | if (threadEnables[tid] == 1) { | |
336 | lowThread = tid; | |
337 | break; | |
338 | } | |
339 | } | |
340 | ||
341 | //POR threads in random orders after lowest | |
342 | verilog_write_cmp_reg({ASI_CMP_CORE,ASI_CMP_CORE_RUNNING_W1S}, | |
343 | 1<<lowThread, | |
344 | lowThread); | |
345 | PR_ALWAYS(className, MON_ALWAYS, | |
346 | psprintf("T%0d unparked (first)\n",lowThread)); | |
347 | ||
348 | repeat (64) { | |
349 | if (threadEnables[startThread] == 1 && startThread !== lowThread) { | |
350 | del = urandom_range(gParam.por_delay_max,gParam.por_delay_min); | |
351 | // PR_DEBUG (className, MON_DEBUG, | |
352 | // psprintf("T%0d post unpark delay %d\n",get_cycle(),resetThis,del)); | |
353 | repeat (del) @(posedge sparcBenchUtils_if.clk); | |
354 | ||
355 | verilog_write_cmp_reg({ASI_CMP_CORE,ASI_CMP_CORE_RUNNING_W1S}, | |
356 | 1<<startThread, | |
357 | startThread); | |
358 | PR_ALWAYS(className, MON_ALWAYS, | |
359 | psprintf("T%0d unparked\n",startThread)); | |
360 | ||
361 | } | |
362 | void = this.rand_mode(ON, "startThread"); | |
363 | void = this.randomize(); | |
364 | void = this.rand_mode(OFF, "startThread"); | |
365 | } | |
366 | ||
367 | #endif | |
368 | ||
369 | } | |
370 | */ | |
371 | ||
372 | ||
373 | ||
374 | ||
375 | ||
376 | //---------------------------------------------------------- | |
377 | // Is address allowed at NCU for a read or write? Returns true if the | |
378 | // r/w was OK. If the return is false, the response packet MUST set the | |
379 | // error indication just as a real NCU would. The caller has to do | |
380 | // this!!! Also returns the data for the address to the caller. Zeros | |
381 | // will be returned for addresses that have never been written (except | |
382 | // 0x82 RNG). | |
383 | // | |
384 | // Certain ASI registers (CMP) may be implemented in verilog. | |
385 | // We will detect that here and do the right thing. | |
386 | // | |
387 | // Any write to any address that isn't explicitly defined will be | |
388 | // silently dropped. That includes reserved and not supported regions. | |
389 | // It also includes unimplemented addresses within a region. (For | |
390 | // example, the 0x90 region is for IO mapped ASI registers, but there | |
391 | // aren't very many. Any write to an address that isn't mapped to a | |
392 | // register will get dropped by NCU RTL, *BUT* this function allows the | |
393 | ||
394 | // | |
395 | // Real world store example: | |
396 | // * The LSU sends the NCU a store. | |
397 | // * The NCU acks the store - always. | |
398 | // * Since the store is ack'ed, software sees it as done. | |
399 | // * The NCU trys to figure out where the write should go. | |
400 | // If it can't figure it out, it drops it. | |
401 | // * The SOC units themselves could also drop it if the NCU | |
402 | // forwarded them a request that they don't support. | |
403 | // | |
404 | // This function will use the following table to define it's behavior. | |
405 | // Any range identified with ERROR *must* cause a load error response packet | |
406 | // to be returned to the core. THE USER/CALLER MUST DO THIS!!! This will | |
407 | // cause a trap to be taken by the core. In order for nas to take the | |
408 | // same trap, we need to add this case to the current interrupt sync | |
409 | // mechanism. Reads from 0x82 will return random data. Currently, only | |
410 | // 0xFF will be considered 100% read only. | |
411 | // | |
412 | // OLD Address [39:32] behavior | |
413 | // OLD --------------- -------- | |
414 | // OLD 0x80 NCU R/W allowed | |
415 | // OLD 0x81 NIU R/W allowed | |
416 | // OLD * 0x82 RNG (Random Number Generator) IGNORE writes | |
417 | // OLD * 0x83 CCU R/W allowed | |
418 | // OLD 0x84 MCUs R/W allowed | |
419 | // OLD 0x85 TCU (JTAG/TAP. NCU/Core will not initiate) ERROR on read | |
420 | // OLD * 0x86 TAP to ASI (not supported) ERROR on read | |
421 | // OLD * 0x87 TAP to L2 CSR (not supported) ERROR on read | |
422 | // OLD 0x88 DMUCSR R/W allowed | |
423 | // OLD 0x89 RST R/W allowed | |
424 | // OLD 0x8A-0x8F Reserved ERROR on read | |
425 | // OLD 0x90 ASI CPU shared registers R/W allowed | |
426 | // OLD 0x91-0x9F Reserved ERROR on read | |
427 | // OLD 0xA0-0xBF L2 CSR (never comes to NCU) ERROR on read | |
428 | // OLD 0xC0-0xCF PCIE (64GB) / DMUPIO R/W allowed | |
429 | // OLD 0xD0-0xFE Reserved ERROR on read | |
430 | // OLD 0xFF SSI (boot ROM) IGNORE writes | |
431 | // | |
432 | // Address [39:32] behavior | |
433 | // --------------- -------- | |
434 | // 0x80 NCU R/W allowed | |
435 | // 0x81 NIU R/W allowed | |
436 | // 0x82 Reserved ERROR on read | |
437 | // 0x83 CCU (+0x30 is Random Number Generator) R/W allowed | |
438 | // 0x84 MCUs R/W allowed | |
439 | // 0x85 TCU (JTAG/TAP) R/W allowed | |
440 | // 0x86 Debug R/W allowed | |
441 | // 0x87 Reserved ERROR on read | |
442 | // 0x88 DMU R/W allowed | |
443 | // 0x89 RST R/W allowed | |
444 | // 0x8A-0x8F Reserved ERROR on read | |
445 | // 0x90 ASI CPU shared registers R/W allowed | |
446 | // 0x91-0x9F Reserved ERROR on read | |
447 | // 0xA0-0xBF L2 CSR (never goes to NCU) ERROR on read | |
448 | // 0xC0-0xCF PCIE (64GB) / DMUPIO R/W allowed | |
449 | // 0xD0-0xFE Reserved ERROR on read | |
450 | // 0xFF SSI (boot ROM) IGNORE writes | |
451 | // | |
452 | // To disable the CPX pkt error response and allow full R/W of ALL | |
453 | // addresses by the NCU model (except writes to 0xFF) +noNCUcheck can be | |
454 | // used. Until Riesling/nas is updated with interrupt sync, and a | |
455 | // specific knowledge of the I/O map, +noNCUcheck could possibly be used. | |
456 | // The plus arg +noNCUcheck is normally not recommended because it | |
457 | // presents a behavior that IS NOT at all like a real N2 (so forget I | |
458 | // even mentioned it)! | |
459 | // | |
460 | // To always fail the simulation on error ("ERROR on read" space was | |
461 | // accessed or 0xFF was written), use +failNCUcheck. This would be a good | |
462 | // way to find "bad" diags. | |
463 | // | |
464 | // | |
465 | function reg CLASSNAME::ioSpaceAccess(reg [63:0] addr, // full addr of request | |
466 | var reg [127:0] data, // r/w data | |
467 | reg read = 1, // request is read, else write | |
468 | reg [7:0] size = 8'hff, // write mask, read size | |
469 | integer thread = 99, | |
470 | integer myPort = 99) // optional for messaging | |
471 | { | |
472 | ||
473 | reg [63:0] tmp64; | |
474 | ||
475 | #ifndef NCURTL | |
476 | #ifndef FC_BENCH | |
477 | #ifndef FC_SCAN_BENCH | |
478 | ||
479 | PR_INFO ("ioaccess", MON_INFO, | |
480 | psprintf("addr = %h data = %h R/W = %b size = %h thread = %0d myPort = %0d", | |
481 | addr,data,read,size,thread,myPort)); | |
482 | ||
483 | ioSpaceAccess = 1; // no error | |
484 | addr = addr & 64'hfffffffffffffff8; | |
485 | if (read) data = 128'hdeaddeaddeaddeaddeaddeaddeaddead; | |
486 | ||
487 | // detect special registers implemented in verilog. | |
488 | if ( | |
489 | (addr[IO_ASI_ADDR_NCU] == 8'h90 && | |
490 | (addr[IO_ASI_ADDR_REG] == ASI_CMP_CORE || | |
491 | addr[IO_ASI_ADDR_ADR] == ASI_WMR_VEC_MASK_ADR || | |
492 | addr[IO_ASI_ADDR_ADR] == ASI_L2_IDX_HASH_EN_ADR || | |
493 | addr[IO_ASI_ADDR_ADR] == ASI_CMP_TICK_ENABLE_ADR)) || | |
494 | (addr == ASI_RESET_STAT) | |
495 | ) { | |
496 | ||
497 | if (thread == 99) thread = addr[IO_ASI_ADDR_CT]; | |
498 | if (read == 0) | |
499 | verilog_write_cmp_reg(addr,data[63:0],thread); | |
500 | else | |
501 | { | |
502 | verilog_read_cmp_reg(addr,tmp64,thread); | |
503 | data = {tmp64,tmp64}; | |
504 | } | |
505 | ||
506 | PR_INFO ("ioaccess", MON_INFO, | |
507 | psprintf("err = %b Special IO Register Access (i.e. CMP reg, etc)", | |
508 | ioSpaceAccess)); | |
509 | return; | |
510 | } | |
511 | ||
512 | ||
513 | case (addr[IO_ASI_ADDR_NCU]) { | |
514 | 8'h80, 8'h81, 8'h83, 8'h84, 8'h85, 8'h86, 8'h88, 8'h89: { | |
515 | // 8'h83 +0x30 is Random Number Generator | |
516 | if (addr[39:0] == 40'h83_0000_0030) { | |
517 | if (read) { | |
518 | data[63:0] = {urandom(),urandom()}; | |
519 | // review getting socket errors | |
520 | #ifndef GATESIM | |
521 | verilog_mem_slam(addr[39:0], | |
522 | data[63:0], | |
523 | 8'hff, | |
524 | "NCU BFM"); | |
525 | #endif | |
526 | } | |
527 | } else { | |
528 | if (read) data = gMem.read128(addr,myPort, 1); | |
529 | else gMem.writeBM(addr, data[63:0], size, myPort); | |
530 | } | |
531 | } | |
532 | 8'h82, 8'h87, 8'h8a, 8'h8b, 8'h8c, 8'h8d, 8'h8e, 8'h8f: { // reserved | |
533 | ioSpaceAccess = 0; | |
534 | } | |
535 | } // case | |
536 | ||
537 | case (addr[39:36]) { | |
538 | 4'h9: {// 0x90 ASI CPU shared registers | |
539 | if (addr[IO_ASI_ADDR_NCU] == 8'h90) { // 0x90 ASI CPU shared registers | |
540 | // Certain ASI registers (CMP) may be implemented in verilog. | |
541 | // The code that calls this function MUST filter those out | |
542 | // and do the right thing. We will not attempt that here!!! | |
543 | if (read) data = gMem.read128(addr,myPort, 1); | |
544 | else gMem.writeBM(addr, data[63:0], size, myPort); | |
545 | } else {// 0x91-0x9F Reserved ERROR | |
546 | ioSpaceAccess = 0; | |
547 | } | |
548 | } | |
549 | 4'hA: {// 0xA0-0xBF L2 CSR (handled by CCX directly and does not come to NCU) ERROR | |
550 | PR_ERROR (CLASSNAMEQ, MON_ERR, psprintf ("T%d Bench ERROR FAIL: - L2 CSR address seen at NCU, this is bad! (addr=%0h (%0h),data=%0h,mask=%0h,read=%0h",thread,addr,addr[39:32],data,size,read)); | |
551 | } | |
552 | 4'hB: {// 0xA0-0xBF L2 CSR (handled by CCX directly and does not come to NCU) ERROR | |
553 | PR_ERROR (CLASSNAMEQ, MON_ERR, psprintf ("T%d Bench ERROR FAIL: - L2 CSR address seen at NCU, this is bad! (addr=%0h (%0h),data=%0h,mask=%0h,read=%0h",thread,addr,addr[39:32],data,size,read)); | |
554 | ||
555 | } | |
556 | 4'hC: {// 0xC0-0xCF PCIE (64GB) / DMUPIO | |
557 | if (read) data = gMem.read128(addr,myPort, 1); | |
558 | else gMem.writeBM(addr, data[63:0], size, myPort); | |
559 | } | |
560 | 4'hD: {// 0xD0-0xFE Reserved ERROR | |
561 | ioSpaceAccess = 0; | |
562 | } | |
563 | 4'hE: {// 0xD0-0xFE Reserved ERROR | |
564 | ioSpaceAccess = 0; | |
565 | } | |
566 | 4'hF: {// 0xD0-0xFE Reserved ERROR | |
567 | if (addr[39:32] == 8'hFF) {// 0xFF SSI (boot ROM) ERROR on write | |
568 | if (read) { | |
569 | if (read) data = gMem.read128(addr,myPort, 1); | |
570 | } else ioSpaceAccess = 0; | |
571 | } else {// 0xD0-0xFE Reserved ERROR | |
572 | ioSpaceAccess = 0; | |
573 | } | |
574 | } | |
575 | } // case | |
576 | ||
577 | ||
578 | // if +failNCUcheck fail the sim right now! | |
579 | if (ioSpaceAccess == 0 && failNCUcheck == 1) | |
580 | PR_ERROR (CLASSNAMEQ, MON_ERR, psprintf ("T%d Bench ERROR FAIL: - Attempt to access I/O address/ASI in a way that is NOT allowed. (addr=%0h (%0h),data=%0h,mask=%0h,read=%0h",thread,addr,addr[39:32],data,size,read)); | |
581 | ||
582 | // if +noNCUcheck, always return a happy value | |
583 | // and (almost) always do the access | |
584 | if (noNCUcheck) { | |
585 | ioSpaceAccess = 1; | |
586 | if (read == 1 && addr[39:32] !== 8'h82) | |
587 | data = gMem.read128(addr,myPort, 1); | |
588 | if (read == 0 && addr[39:32] !== 8'hFF) | |
589 | gMem.writeBM(addr, data[63:0], size, myPort); | |
590 | } | |
591 | ||
592 | PR_INFO ("ioaccess", MON_INFO, | |
593 | psprintf("err = %b Normal IO Register Access", | |
594 | ioSpaceAccess)); | |
595 | ||
596 | ||
597 | #endif | |
598 | #endif | |
599 | #endif | |
600 | } | |
601 | ||
602 | //---------------------------------------------------------- | |
603 | // replicate data bytes across all data bytes depending on size. | |
604 | // big endian, so the high bit bytes replicate from high bit | |
605 | // end to low bit end. | |
606 | function reg[127:0] CLASSNAME::copyDataByte (reg [127:0] data, | |
607 | reg [7:0] size, | |
608 | reg [3:0] offset) | |
609 | { | |
610 | ||
611 | reg [127:0] tmpData = 0; | |
612 | ||
613 | // start with the addressed byte at byte 0 | |
614 | data = data << (8*offset); | |
615 | ||
616 | case (size) { | |
617 | 8'h0: {// 1 byte | |
618 | repeat (16) { | |
619 | tmpData = tmpData >> 8; // seems backwards but works | |
620 | tmpData[127:120] = data[127:120]; | |
621 | } | |
622 | } | |
623 | 8'h1: {// 2 bytes | |
624 | repeat (8) { | |
625 | tmpData = tmpData >> 16; | |
626 | tmpData[127:112] = data[127:112]; | |
627 | } | |
628 | } | |
629 | 8'h2: {// 4 bytes | |
630 | repeat (4) { | |
631 | tmpData = tmpData >> 32; | |
632 | tmpData[127:96] = data[127:96]; | |
633 | } | |
634 | } | |
635 | 8'h3: {// 8 bytes | |
636 | repeat (2) { | |
637 | tmpData = tmpData >> 64; | |
638 | tmpData[127:64] = data[127:64]; | |
639 | } | |
640 | } | |
641 | 8'h4: {// 16 bytes | |
642 | tmpData = data; | |
643 | } | |
644 | ||
645 | default: PR_ERROR("copyDataByte", MON_ERR, psprintf ("ERROR bad size=%b passed in.",size)); | |
646 | ||
647 | } | |
648 | ||
649 | copyDataByte = tmpData; | |
650 | } | |
651 | ||
652 | ||
653 | ||
654 | ||
655 | // Evictions use 2 functions, evictAddr & evictVector. | |
656 | // "Eviction loop" code will call evictAddr to get address, then BFM will call | |
657 | // evictVector to get the vector and modify the shadow tag. | |
658 | // "Eviction loop" code will call task enqueueEvict in the correct bfm to | |
659 | // request the invalidate after evictAddr has been called. | |
660 | // The evict User Event that takes PA, will call task enqueueEvict in the | |
661 | // correct bfm to request the invalidate. | |
662 | ||
663 | ||
664 | //-------------------- | |
665 | // Pick a random core and search for a line with a valid entry. | |
666 | // mem_index - itag 0..63, dtag 0..127 | |
667 | // evict_index is normalized index between 0..31 | |
668 | // used to search for tag in all lines in the group (aka L2 cache line) | |
669 | // returns a valid address. | |
670 | ||
671 | // Inputs: | |
672 | // core_enable = 8 bit vector, 1 bit per core | |
673 | // cid = N, to limit search to that core. | |
674 | // dCacheWeight = N to weight I/D caches. 100 % based | |
675 | // | |
676 | // Outputs: | |
677 | // found address if any, all F's otherwise. | |
678 | // cidUsed = a core that had the address | |
679 | function reg [63:0] CLASSNAME::evictAddr(reg [7:0] core_enable, | |
680 | var reg [2:0] cidUsed, | |
681 | reg [3:0] cid = 4'hf, | |
682 | integer dCacheWeight = 60) // 100 % based | |
683 | { | |
684 | reg evict=0; // =1, if valid entry found in the table | |
685 | reg [6:0] mem_index=0; // raw index from tag table search | |
686 | reg [28:0] evict_tag=0; // tag that is being evicted | |
687 | reg [4:0] evict_index=0; // Normalized between 0..31 | |
688 | reg junk; | |
689 | reg dCacheAddr = 0; | |
690 | ||
691 | #ifndef CCM_BENCH | |
692 | #ifndef FC_BENCH | |
693 | #ifndef FC_SCAN_BENCH | |
694 | ||
695 | evictAddr = 64'hFFFFFFFFFFFFFFFF; | |
696 | cidUsed = 0; | |
697 | ||
698 | if (cid == 4'hf) { | |
699 | // Pick cid out of the enabled cores. | |
700 | // This runs in zero time so randCID is safe from other calls to randomize(). | |
701 | while (!core_enable[randCID]) { | |
702 | void = this.randomize(); | |
703 | } | |
704 | cidUsed = randCID; | |
705 | } else { | |
706 | // check the cid | |
707 | cidUsed = cid; | |
708 | if (!core_enable[cidUsed]) | |
709 | PR_ERROR("evict", MON_ERR, | |
710 | psprintf (" BENCH ERROR - selected core not available!")); | |
711 | } | |
712 | ||
713 | randcase { | |
714 | 100-dCacheWeight: { | |
715 | // Get raw index that has a valid entry | |
716 | itag[cidUsed].search_tagmem(evict,mem_index); // set evict=1, if valid entry found | |
717 | // found nothing | |
718 | if (! evict) return; | |
719 | // Normalize between 0..31 | |
720 | evict_index = mem_index / 2; | |
721 | // Get the tag that will be evicted | |
722 | itag[cidUsed].get_tag(mem_index,junk,evict_tag); | |
723 | } | |
724 | dCacheWeight: { | |
725 | // Get raw index that has a valid entry | |
726 | dtag[cidUsed].search_tagmem(evict,mem_index); // set evict=1, if valid entry found | |
727 | // found nothing | |
728 | if (! evict) return; | |
729 | // Normalize between 0..31 | |
730 | evict_index = mem_index / 4; | |
731 | // Get the tag that will be evicted | |
732 | dtag[cidUsed].get_tag(mem_index,junk,evict_tag); | |
733 | dCacheAddr = 1; | |
734 | } | |
735 | } | |
736 | ||
737 | evictAddr = {evict_tag,evict_index,6'b0}; | |
738 | ||
739 | PR_INFO ("evict", MON_INFO, | |
740 | psprintf("EVICTION evictAddr PA{[39:6],6'b0}=%h evict_tag(pa[39:11])=%h evict_index(pa[10:6])=%h core=%0d dCacheAddr=%0d", | |
741 | evictAddr,evict_tag,evict_index,cidUsed,dCacheAddr)); | |
742 | ||
743 | #endif | |
744 | #endif | |
745 | #endif | |
746 | ||
747 | } | |
748 | ||
749 | ||
750 | //---------------------------------------------------------- | |
751 | // Evict a PA from the L1 cache in all cores that are enabled. | |
752 | // The value that is returned as evictVector has the format as defined | |
753 | // in the CCX packet spec as Vinv. | |
754 | // | |
755 | // Pass in address. Will check all enabled cores for hit at that address and return | |
756 | // an eviction vector for all matching/hitting cores. | |
757 | // | |
758 | // Inputs: | |
759 | // core_enable = 8 bit vector, 1 bit per core | |
760 | // evict_pa = address to evict | |
761 | // cpuId = CPU associated with evict_pa being cached | |
762 | // Output: | |
763 | // evictVector = 0, no entries to evict. | |
764 | // targets = cores that will be the target of the evict packet. | |
765 | function reg [127:0] CLASSNAME::evictVector(reg [7:0] core_enable, | |
766 | reg [63:0] evict_pa, | |
767 | reg [2:0] cpuId, | |
768 | var reg [7:0] targets) | |
769 | { | |
770 | reg evict=0; // 1, if valid entry found in the table | |
771 | reg [6:0] mem_index=0; // raw index from tag table search | |
772 | reg [28:0] evict_tag=0; // tag that is being evicted | |
773 | reg [4:0] evict_index=0; // Normalized between 0..31 | |
774 | reg [3:0] dmatch=0; | |
775 | reg [3:0] imatch=0,i; | |
776 | reg [111:0] i_vect=0,d_vect=0,e_vect=0; | |
777 | ||
778 | #ifndef CCM_BENCH | |
779 | #ifndef FC_BENCH | |
780 | #ifndef FC_SCAN_BENCH | |
781 | ||
782 | targets = 0; | |
783 | evictVector = 0; | |
784 | ||
785 | // Get index & tag from User argument | |
786 | evict_tag = evict_pa[39:11]; | |
787 | evict_index = evict_pa[10:6]; | |
788 | mem_index = evict_pa[10:4]; // tag class array index | |
789 | ||
790 | ||
791 | //-------------------- | |
792 | // If valid tag found in tag table, evict it in both itag & dtag | |
793 | ||
794 | PR_INFO ("evict", MON_INFO, | |
795 | psprintf("EVICTION evictVector PA{[39:6],6'b0}=%h evict_tag(pa[39:11])=%h evict_index(pa[10:6])=%h core=%0d mem_index(pa[10:4])=%h", | |
796 | evict_pa,evict_tag,evict_index,cpuId,mem_index)); | |
797 | ||
798 | //-------------------- | |
799 | // If valid tag found in tag table, evict it in both itag & dtag | |
800 | i_vect = 0; | |
801 | d_vect = 0; | |
802 | e_vect = 0; | |
803 | ||
804 | // evict the pa in all cores L1 cache | |
805 | for (i=0; i<=7; i=i+1) { | |
806 | if (core_enable[i]) { | |
807 | itag[i].evict_group(evict_tag, evict_index, i_vect); | |
808 | dtag[i].evict_group(evict_tag, evict_index, d_vect); | |
809 | ||
810 | if ((i_vect!=0)|(d_vect!=0)) { | |
811 | targets[i] = 1'b1; | |
812 | } | |
813 | ||
814 | // Create invalidation vector that is returned in CPX pkt | |
815 | e_vect = e_vect | i_vect | d_vect; | |
816 | } | |
817 | } | |
818 | ||
819 | ||
820 | if (e_vect) { | |
821 | //-------------------- | |
822 | // Debug messages | |
823 | ||
824 | PR_INFO ("evict", MON_INFO, | |
825 | psprintf("EVICTION evictVector PA[39:0]=%h tag=%h evict_index(pa[10:6])=%h mem_index(pa[10:4])=%h+",evict_pa,evict_tag,evict_index,mem_index)); | |
826 | PR_INFO ("evict", MON_INFO, | |
827 | psprintf("EVICTION \taddr: 0x%12h 0x%12h 0x%12h 0x%12h", evict_pa+48,evict_pa+32, evict_pa+16, evict_pa)); | |
828 | PR_INFO ("evict", MON_INFO, | |
829 | psprintf("EVICTION \titag: Vinv[111:88] = %h [87:56] = %h [55:32] = %h [31:0] = %h ", | |
830 | i_vect[111:88],i_vect[87:56],i_vect[55:32],i_vect[31:0])); | |
831 | PR_INFO ("evict", MON_INFO, | |
832 | psprintf("EVICTION \tdtag: Vinv[111:88] = %h [87:56] = %h [55:32] = %h [31:0] = %h ", | |
833 | d_vect[111:88],d_vect[87:56],d_vect[55:32],d_vect[31:0])); | |
834 | PR_INFO ("evict", MON_INFO, | |
835 | psprintf("EVICTION \t Vinv[111:88] = %h [87:56] = %h [55:32] = %h [31:0] = %h ", | |
836 | e_vect[111:88],e_vect[87:56],e_vect[55:32],e_vect[31:0])); | |
837 | ||
838 | ||
839 | // Create Vinv as defined in CCX packet spec | |
840 | evictVector[127:117] = 11'b0; | |
841 | evictVector[116:112] = evict_index; | |
842 | evictVector[111:0] = e_vect; | |
843 | ||
844 | } else { | |
845 | // not an error because time elapsed and the L1 tags have changed! | |
846 | } | |
847 | ||
848 | #endif | |
849 | #endif | |
850 | #endif | |
851 | ||
852 | } | |
853 |