Initial commit of OpenSPARC T2 architecture model.
[OpenSPARC-T2-SAM] / legion / src / simcore / control.c
CommitLineData
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
61LIST_DEF(exec_thread_list, exec_thread_t);
62static int nexec_threads;
63
64simcpu_list_t simcpu_list;
65
66barrier_t stop_barrier;
67barrier_t run_barrier;
68barrier_busy_t sync_busy_barrier;
69
70simstatus_t simstatus;
71
72void init_simstatus(void)
73{
74 simstatus.sp.mode = false;
75 simstatus.sp.poweron = false;
76 simstatus.running = false;
77 simstatus.initialized = false;
78}
79
80simcpu_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
146static 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
178static 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
239static 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
298static 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 */
308config_addr_t *
309find_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 */
329void
330simcore_cycle_match_error(simcpu_t *sp)
331{
332 fatal("Illegal cycle match error");
333}
334
335void
336cycle_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 */
350void
351simcore_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
378void
379init_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
409void
410simcore_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
443void
444simcore_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
459static void simcore_cpu_state_updated(simcpu_t *sp)
460{
461 if (DISABLED(sp) || PARKED(sp))
462 set_sync_pending(sp);
463}
464
465void
466simcore_cpu_enable(simcpu_t *sp)
467{
468 ASSERT(DISABLED(sp));
469 CLR_DISABLED(sp);
470
471 simcore_cpu_state_updated(sp);
472}
473
474void
475simcore_cpu_state_unpark(simcpu_t *sp)
476{
477 ASSERT(PARKED(sp));
478 CLR_PARKED(sp);
479
480 simcore_cpu_state_updated(sp);
481}
482
483void
484simcore_cpu_disable(simcpu_t *sp)
485{
486 ASSERT(!DISABLED(sp));
487 SET_DISABLED(sp);
488
489 simcore_cpu_state_updated(sp);
490}
491
492void
493simcore_cpu_state_park(simcpu_t *sp)
494{
495 ASSERT(!PARKED(sp));
496 SET_PARKED(sp);
497
498 simcore_cpu_state_updated(sp);
499}