Commit | Line | Data |
---|---|---|
920dae64 AT |
1 | /* |
2 | * ========== Copyright Header Begin ========================================== | |
3 | * | |
4 | * OpenSPARC T2 Processor File: control.c | |
5 | * Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved. | |
6 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES. | |
7 | * | |
8 | * The above named program is free software; you can redistribute it and/or | |
9 | * modify it under the terms of the GNU General Public | |
10 | * License version 2 as published by the Free Software Foundation. | |
11 | * | |
12 | * The above named program is distributed in the hope that it will be | |
13 | * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
15 | * General Public License for more details. | |
16 | * | |
17 | * You should have received a copy of the GNU General Public | |
18 | * License along with this work; if not, write to the Free Software | |
19 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. | |
20 | * | |
21 | * ========== Copyright Header End ============================================ | |
22 | */ | |
23 | /* | |
24 | * Copyright 2006 Sun Microsystems, Inc. All rights reserved. | |
25 | * Use is subject to license terms. | |
26 | */ | |
27 | #pragma ident "@(#)control.c 1.34 06/10/24 SMI" | |
28 | ||
29 | #include <stdlib.h> | |
30 | #include <pthread.h> | |
31 | #include <errno.h> | |
32 | #include <unistd.h> | |
33 | ||
34 | #include "basics.h" | |
35 | #include "options.h" | |
36 | #include "fatal.h" | |
37 | #include "allocate.h" | |
38 | #include "simcore.h" | |
39 | #include "config.h" | |
40 | #include "createthr.h" | |
41 | #include "xicache.h" | |
42 | #include "barrier.h" | |
43 | #include "callback.h" | |
44 | #include "breakpoint.h" | |
45 | ||
46 | /* | |
47 | * This file contains the control and execution infrastructure for the | |
48 | * simulator. | |
49 | * | |
50 | * Legion is multi-threaded to achieve high performance including a single | |
51 | * control thread and along with a number of exec-threads. The exact | |
52 | * number of exec-threads being determined by the -t command line parameter | |
53 | * or the number of simulated CPUs. | |
54 | */ | |
55 | ||
56 | /* | |
57 | * List of all the scheduler threads the simulator uses | |
58 | * for execution. | |
59 | */ | |
60 | ||
61 | LIST_DEF(exec_thread_list, exec_thread_t); | |
62 | static int nexec_threads; | |
63 | ||
64 | simcpu_list_t simcpu_list; | |
65 | ||
66 | barrier_t stop_barrier; | |
67 | barrier_t run_barrier; | |
68 | barrier_busy_t sync_busy_barrier; | |
69 | ||
70 | simstatus_t simstatus; | |
71 | ||
72 | void init_simstatus(void) | |
73 | { | |
74 | simstatus.sp.mode = false; | |
75 | simstatus.sp.poweron = false; | |
76 | simstatus.running = false; | |
77 | simstatus.initialized = false; | |
78 | } | |
79 | ||
80 | simcpu_t *sim_cpu_alloc( | |
81 | config_proc_t *cfp, | |
82 | void *specificp) | |
83 | { | |
84 | simcpu_t *sp; | |
85 | int gid; | |
86 | ||
87 | ||
88 | gid = simcpu_list.count; | |
89 | sp = LIST_ADD(simcpu_list, simcpu_t); | |
90 | sp->gid = gid; | |
91 | ||
92 | sp->state_bits = 0; | |
93 | ||
94 | sp->attention = 0; | |
95 | ||
96 | #if ERROR_INJECTION | |
97 | sp->error_enabled = false; | |
98 | sp->error_check = false; | |
99 | sp->error_priv = NULL; | |
100 | sp->errorp = Xcalloc(1, error_t); | |
101 | sp->errorp->errlistp = NULL; | |
102 | #endif | |
103 | ||
104 | sp->nextp = (simcpu_t *)0; | |
105 | ||
106 | /* Pointer to the exec_thread this simcpu is on */ | |
107 | sp->etp = (exec_thread_t *)0; | |
108 | ||
109 | /* Head of the list of simcpus for each exec_thread */ | |
110 | sp->headp = (simcpu_t *)0; | |
111 | ||
112 | sp->decodemep = NULL; | |
113 | sp->xic_miss = NULL; | |
114 | sp->xic_miss_addrp = NULL; | |
115 | sp->config_procp = cfp; | |
116 | sp->specificp = specificp; | |
117 | ||
118 | #if ERROR_TRAP_GEN /* { */ | |
119 | sp->error_cycle = UINT64_MAX; | |
120 | sp->error_pending = false; | |
121 | sp->error_cycle_reached = false; | |
122 | sp->eep = NULL; | |
123 | #endif /* ERROR_TRAP_GEN } */ | |
124 | ||
125 | sp->cycle = 0LL; | |
126 | sp->cycle_quantum_start = sp->cycle; | |
127 | cycle_target_off(sp); | |
128 | sp->total_instr = 0LL; | |
129 | ||
130 | /* | |
131 | * More misc stuff .. | |
132 | * flush the xdcache, but the support functions | |
133 | * need to be supplied by the caller | |
134 | */ | |
135 | xdcache_flush(sp); | |
136 | sp->xdc.miss = NULL; | |
137 | ||
138 | /* | |
139 | * clear the breakpoint cache for this cpu | |
140 | */ | |
141 | sp->bp_infop = NULL; | |
142 | ||
143 | return (sp); | |
144 | } | |
145 | ||
146 | static void *exec_thread_init(void *argp) | |
147 | { | |
148 | exec_thread_t *etp = (exec_thread_t *)argp; | |
149 | ||
150 | /* wait until we start running */ | |
151 | ||
152 | barrier_wait(&run_barrier); | |
153 | ||
154 | ||
155 | /* | |
156 | * All threads are now running, including the ctrl-thread. | |
157 | * | |
158 | * We call into exec_loop(), passing it the first simcpu in | |
159 | * the list to start the execution. The exec_loop will take | |
160 | * care of switching all simcpus in it's list to ensure that | |
161 | * they all make equal progress. | |
162 | */ | |
163 | DBGEXECLOOP(lprintf(etp->id, | |
164 | "exec_thread%d calling exec_loop for simcpu%d " | |
165 | "nsimcpus=%d\n", | |
166 | etp->id, etp->allp->gid, etp->nsimcpus);); | |
167 | ||
168 | ASSERT(etp->allp != NULL); | |
169 | if (etp->allp->config_procp->proc_typep->debug_hookp != NULL) | |
170 | exec_loop_dh(etp); | |
171 | else | |
172 | exec_loop(etp); | |
173 | ||
174 | /* We never return to here */ | |
175 | return ((void*)0); | |
176 | } | |
177 | ||
178 | static void exec_thread_alloc(void) | |
179 | { | |
180 | exec_thread_t *etp; | |
181 | pthread_t id; | |
182 | int i, n, r, etidx; | |
183 | simcpu_t *prev_sp = NULL; | |
184 | static int simidx = 0; | |
185 | ||
186 | etidx = exec_thread_list.count; | |
187 | etp = LIST_ADD(exec_thread_list, exec_thread_t); | |
188 | etp->id = etidx; | |
189 | ||
190 | /* | |
191 | * We need to figure out how many simcpus this | |
192 | * etp needs to have assigned. | |
193 | */ | |
194 | n = simcpu_list.count / nexec_threads; | |
195 | r = simcpu_list.count % nexec_threads; | |
196 | ||
197 | if (etp->id < r) | |
198 | n += 1; | |
199 | ||
200 | /* create a liked list of simcpus for each exec_thread */ | |
201 | for (i = 0; i < n; i++) { | |
202 | if (simidx < simcpu_list.count) { | |
203 | simcpu_t *sp = LIST_ENTRY(simcpu_list, simidx); | |
204 | ||
205 | /* | |
206 | * Save pointer of first simcpu (head) into etp | |
207 | */ | |
208 | if (etp->allp == NULL) | |
209 | etp->allp = sp; | |
210 | ||
211 | /* | |
212 | * Update the nextp in the previous simcpu of this | |
213 | * list | |
214 | */ | |
215 | if (prev_sp != NULL) | |
216 | prev_sp->nextp = sp; | |
217 | ||
218 | /* so all simcpus can find the head of the list */ | |
219 | sp->headp = etp->allp; | |
220 | ||
221 | /* so all simcpus can find the exec_thread */ | |
222 | sp->etp = etp; | |
223 | ||
224 | prev_sp = sp; | |
225 | etp->nsimcpus++; | |
226 | simidx++; | |
227 | } else { | |
228 | printf("\nERROR:: exec_thread_alloc: " | |
229 | "simidx < simcpu_list.count (0x%x < 0x%x)", | |
230 | simidx, simcpu_list.count); | |
231 | fatal("DIE"); | |
232 | } | |
233 | } | |
234 | ||
235 | /* create the execution thread */ | |
236 | create_thread(exec_thread_init, (void *)etp, &id); | |
237 | } | |
238 | ||
239 | static void *ctrl_thread_main(void *argp) | |
240 | { | |
241 | simcpu_t *sp; | |
242 | int i, j; | |
243 | ||
244 | for (;;) { | |
245 | while (!simstatus.running) { | |
246 | /* | |
247 | * spin until it's time to go. this is useful when | |
248 | * we're not auto-starting. | |
249 | */ | |
250 | } | |
251 | ||
252 | /* pre-exec setup */ | |
253 | for (i = 0; i < simcpu_list.count; i++) { | |
254 | sp = LIST_ENTRY(simcpu_list, i); | |
255 | sp->config_procp->proc_typep->exec_setup(sp); | |
256 | } | |
257 | ||
258 | /* | |
259 | * after the run barrier everyone, including the | |
260 | * exec-threads are running | |
261 | */ | |
262 | ||
263 | barrier_wait(&run_barrier); | |
264 | ||
265 | /* | |
266 | * now that everybody's running, now wait for everyone to | |
267 | * stop again | |
268 | */ | |
269 | ||
270 | barrier_wait(&stop_barrier); | |
271 | ||
272 | /* we've come to a "stop" */ | |
273 | ||
274 | ASSERT(!simstatus.running); | |
275 | ||
276 | /* | |
277 | * the exec-threads should be waiting (at the run_barrier) | |
278 | * until we start running again. meanwhile process the | |
279 | * stopped callbacks | |
280 | */ | |
281 | ||
282 | /* post-exec cleanup */ | |
283 | for (i = 0; i < simcpu_list.count; i++) { | |
284 | sp = LIST_ENTRY(simcpu_list, i); | |
285 | sp->config_procp->proc_typep->exec_cleanup(sp); | |
286 | } | |
287 | ||
288 | /* we're stopped, call stop callbacks */ | |
289 | callback_fire(CB_Stop); | |
290 | ||
291 | /* if we've hit a breakpoint, call breakpoint callbacks */ | |
292 | if (breakpoint_any_reached()) { | |
293 | callback_fire(CB_Breakpoint); | |
294 | } | |
295 | } | |
296 | } | |
297 | ||
298 | static void ctrl_thread_alloc(void) | |
299 | { | |
300 | pthread_t id; | |
301 | ||
302 | create_thread(ctrl_thread_main, NULL, &id); | |
303 | } | |
304 | ||
305 | /* | |
306 | * support functions for finding devices etc | |
307 | */ | |
308 | config_addr_t * | |
309 | find_domain_address(domain_t *domainp, tpaddr_t pa) | |
310 | { | |
311 | config_addr_t *addrp; | |
312 | ||
313 | for (addrp = domainp->address.listp; | |
314 | addrp != NULL && addrp->baseaddr <= pa; addrp = addrp->nextp) { | |
315 | if ((addrp->baseaddr <= pa) && (addrp->topaddr > pa)) { | |
316 | return (addrp); /* bingo */ | |
317 | } | |
318 | } | |
319 | return (NULL); | |
320 | } | |
321 | ||
322 | ||
323 | /* | |
324 | * The function below should never be called because the cycle_target | |
325 | * it relates to should never match the execution counter | |
326 | * If such a match occurs, we ensure an error is reported and the | |
327 | * simulator stops. | |
328 | */ | |
329 | void | |
330 | simcore_cycle_match_error(simcpu_t *sp) | |
331 | { | |
332 | fatal("Illegal cycle match error"); | |
333 | } | |
334 | ||
335 | void | |
336 | cycle_target_off(simcpu_t *sp) | |
337 | { | |
338 | sp->cycle_target = UINT64_MAX; | |
339 | ||
340 | sp->cycle_target_match = simcore_cycle_match_error; | |
341 | } | |
342 | ||
343 | ||
344 | #if !defined(NDEBUG) /* { */ | |
345 | ||
346 | /* | |
347 | * Used in various control interfaces around the simulator to | |
348 | * modify the debug modes | |
349 | */ | |
350 | void | |
351 | simcore_update_debug_bits(uint64_t newval) | |
352 | { | |
353 | int i; | |
354 | ||
355 | if (debug_bits == newval) { | |
356 | return; | |
357 | } | |
358 | ||
359 | lprintf(-1, "debug_bits 0x%llx -> 0x%llx\n", debug_bits, newval); | |
360 | ||
361 | /* FIXME: Kludge - clobber the decodes incase we turned tracing on ! */ | |
362 | ||
363 | if ((debug_bits & (DBG_EL | DBG_EL_MIN)) == 0 && | |
364 | (newval & (DBG_EL | DBG_EL_MIN)) != 0) { | |
365 | for (i = 0; i < simcpu_list.count; i++) { | |
366 | simcpu_t *sp = LIST_ENTRY(simcpu_list, i); | |
367 | ||
368 | sp->xicache_instn_flush_pending = true; | |
369 | set_sync_pending(sp); | |
370 | } | |
371 | } | |
372 | /* end of Kludge */ | |
373 | ||
374 | debug_bits = newval; | |
375 | } | |
376 | #endif /* } */ | |
377 | ||
378 | void | |
379 | init_threads(void) | |
380 | { | |
381 | int i; | |
382 | ||
383 | callback_init(); | |
384 | ||
385 | nexec_threads = (options.threads > simcpu_list.count) | |
386 | ? simcpu_list.count : options.threads; | |
387 | /* | |
388 | * Default behavior for quantum is divide DFT_EXEC_QUANTUM | |
389 | * by the maximum number of CPUs on an exec thread. | |
390 | */ | |
391 | if (options.quantum == 0) { | |
392 | options.quantum = (DFT_EXEC_QUANTUM * nexec_threads) / | |
393 | simcpu_list.count; | |
394 | } | |
395 | PRINTF(("Using %d execution thread(s) quantum=%u\n", nexec_threads, | |
396 | options.quantum)); | |
397 | ||
398 | barrier_init(&stop_barrier, nexec_threads + 1); | |
399 | barrier_init(&run_barrier, nexec_threads + 1); | |
400 | barrier_busy_init(&sync_busy_barrier, nexec_threads); | |
401 | ||
402 | ctrl_thread_alloc(); | |
403 | ||
404 | for (i = 0; i < nexec_threads; i++) { | |
405 | exec_thread_alloc(); | |
406 | } | |
407 | } | |
408 | ||
409 | void | |
410 | simcore_start(void) | |
411 | { | |
412 | /* | |
413 | * Once we get here, legion has completed all initialization | |
414 | * and is about to jump to the first instruction. Anything | |
415 | * that was waiting on the initialization to complete can | |
416 | * start now. | |
417 | */ | |
418 | simstatus.initialized = true; | |
419 | ||
420 | /* | |
421 | * If running in service processor mode, we want to wait | |
422 | * here until the service processor tells us to start. | |
423 | * Each processor can determine how the SP signals the | |
424 | * simulation to start, but once the signal is received, | |
425 | * setting simstatus.sp.poweron to true will cause the | |
426 | * simulator to start running. | |
427 | */ | |
428 | if (simstatus.sp.mode) { | |
429 | printf("\nWaiting on Serivce Processor POWERON interrupt\n"); | |
430 | while (simstatus.sp.poweron != true) { | |
431 | sleep(1); | |
432 | } | |
433 | printf("\nReceived Serivce Processor POWERON interrupt\n"); | |
434 | } | |
435 | ||
436 | /* | |
437 | * signal to all exec threads that it's ok to start | |
438 | * executing instructions. | |
439 | */ | |
440 | simstatus.running = true; | |
441 | } | |
442 | ||
443 | void | |
444 | simcore_stop(void) | |
445 | { | |
446 | if (simstatus.running) { | |
447 | int i; | |
448 | ||
449 | simstatus.running = false; | |
450 | ||
451 | for (i = 0; i < simcpu_list.count; i++) { | |
452 | simcpu_t *sp = LIST_ENTRY(simcpu_list, i); | |
453 | ||
454 | set_sync_pending(sp); | |
455 | } | |
456 | } | |
457 | } | |
458 | ||
459 | static void simcore_cpu_state_updated(simcpu_t *sp) | |
460 | { | |
461 | if (DISABLED(sp) || PARKED(sp)) | |
462 | set_sync_pending(sp); | |
463 | } | |
464 | ||
465 | void | |
466 | simcore_cpu_enable(simcpu_t *sp) | |
467 | { | |
468 | ASSERT(DISABLED(sp)); | |
469 | CLR_DISABLED(sp); | |
470 | ||
471 | simcore_cpu_state_updated(sp); | |
472 | } | |
473 | ||
474 | void | |
475 | simcore_cpu_state_unpark(simcpu_t *sp) | |
476 | { | |
477 | ASSERT(PARKED(sp)); | |
478 | CLR_PARKED(sp); | |
479 | ||
480 | simcore_cpu_state_updated(sp); | |
481 | } | |
482 | ||
483 | void | |
484 | simcore_cpu_disable(simcpu_t *sp) | |
485 | { | |
486 | ASSERT(!DISABLED(sp)); | |
487 | SET_DISABLED(sp); | |
488 | ||
489 | simcore_cpu_state_updated(sp); | |
490 | } | |
491 | ||
492 | void | |
493 | simcore_cpu_state_park(simcpu_t *sp) | |
494 | { | |
495 | ASSERT(!PARKED(sp)); | |
496 | SET_PARKED(sp); | |
497 | ||
498 | simcore_cpu_state_updated(sp); | |
499 | } |