* ========== Copyright Header Begin ==========================================
* OpenSPARC T2 Processor File: control.c
* Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES.
* The above named program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
* License version 2 as published by the Free Software Foundation.
* The above named program is distributed in the hope that it will be
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
* You should have received a copy of the GNU General Public
* License along with this work; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
* ========== Copyright Header End ============================================
* Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
#pragma ident "@(#)control.c 1.34 06/10/24 SMI"
* This file contains the control and execution infrastructure for the
* Legion is multi-threaded to achieve high performance including a single
* control thread and along with a number of exec-threads. The exact
* number of exec-threads being determined by the -t command line parameter
* or the number of simulated CPUs.
* List of all the scheduler threads the simulator uses
LIST_DEF(exec_thread_list
, exec_thread_t
);
static int nexec_threads
;
simcpu_list_t simcpu_list
;
barrier_busy_t sync_busy_barrier
;
void init_simstatus(void)
simstatus
.sp
.mode
= false;
simstatus
.sp
.poweron
= false;
simstatus
.running
= false;
simstatus
.initialized
= false;
sp
= LIST_ADD(simcpu_list
, simcpu_t
);
sp
->error_enabled
= false;
sp
->errorp
= Xcalloc(1, error_t
);
sp
->errorp
->errlistp
= NULL
;
sp
->nextp
= (simcpu_t
*)0;
/* Pointer to the exec_thread this simcpu is on */
sp
->etp
= (exec_thread_t
*)0;
/* Head of the list of simcpus for each exec_thread */
sp
->headp
= (simcpu_t
*)0;
sp
->xic_miss_addrp
= NULL
;
sp
->specificp
= specificp
;
#if ERROR_TRAP_GEN /* { */
sp
->error_cycle
= UINT64_MAX
;
sp
->error_pending
= false;
sp
->error_cycle_reached
= false;
#endif /* ERROR_TRAP_GEN } */
sp
->cycle_quantum_start
= sp
->cycle
;
* flush the xdcache, but the support functions
* need to be supplied by the caller
* clear the breakpoint cache for this cpu
static void *exec_thread_init(void *argp
)
exec_thread_t
*etp
= (exec_thread_t
*)argp
;
/* wait until we start running */
barrier_wait(&run_barrier
);
* All threads are now running, including the ctrl-thread.
* We call into exec_loop(), passing it the first simcpu in
* the list to start the execution. The exec_loop will take
* care of switching all simcpus in it's list to ensure that
* they all make equal progress.
DBGEXECLOOP(lprintf(etp
->id
,
"exec_thread%d calling exec_loop for simcpu%d "
etp
->id
, etp
->allp
->gid
, etp
->nsimcpus
););
ASSERT(etp
->allp
!= NULL
);
if (etp
->allp
->config_procp
->proc_typep
->debug_hookp
!= NULL
)
/* We never return to here */
static void exec_thread_alloc(void)
simcpu_t
*prev_sp
= NULL
;
etidx
= exec_thread_list
.count
;
etp
= LIST_ADD(exec_thread_list
, exec_thread_t
);
* We need to figure out how many simcpus this
* etp needs to have assigned.
n
= simcpu_list
.count
/ nexec_threads
;
r
= simcpu_list
.count
% nexec_threads
;
/* create a liked list of simcpus for each exec_thread */
for (i
= 0; i
< n
; i
++) {
if (simidx
< simcpu_list
.count
) {
simcpu_t
*sp
= LIST_ENTRY(simcpu_list
, simidx
);
* Save pointer of first simcpu (head) into etp
* Update the nextp in the previous simcpu of this
/* so all simcpus can find the head of the list */
/* so all simcpus can find the exec_thread */
printf("\nERROR:: exec_thread_alloc: "
"simidx < simcpu_list.count (0x%x < 0x%x)",
simidx
, simcpu_list
.count
);
/* create the execution thread */
create_thread(exec_thread_init
, (void *)etp
, &id
);
static void *ctrl_thread_main(void *argp
)
while (!simstatus
.running
) {
* spin until it's time to go. this is useful when
* we're not auto-starting.
for (i
= 0; i
< simcpu_list
.count
; i
++) {
sp
= LIST_ENTRY(simcpu_list
, i
);
sp
->config_procp
->proc_typep
->exec_setup(sp
);
* after the run barrier everyone, including the
* exec-threads are running
barrier_wait(&run_barrier
);
* now that everybody's running, now wait for everyone to
barrier_wait(&stop_barrier
);
/* we've come to a "stop" */
ASSERT(!simstatus
.running
);
* the exec-threads should be waiting (at the run_barrier)
* until we start running again. meanwhile process the
for (i
= 0; i
< simcpu_list
.count
; i
++) {
sp
= LIST_ENTRY(simcpu_list
, i
);
sp
->config_procp
->proc_typep
->exec_cleanup(sp
);
/* we're stopped, call stop callbacks */
/* if we've hit a breakpoint, call breakpoint callbacks */
if (breakpoint_any_reached()) {
callback_fire(CB_Breakpoint
);
static void ctrl_thread_alloc(void)
create_thread(ctrl_thread_main
, NULL
, &id
);
* support functions for finding devices etc
find_domain_address(domain_t
*domainp
, tpaddr_t pa
)
for (addrp
= domainp
->address
.listp
;
addrp
!= NULL
&& addrp
->baseaddr
<= pa
; addrp
= addrp
->nextp
) {
if ((addrp
->baseaddr
<= pa
) && (addrp
->topaddr
> pa
)) {
return (addrp
); /* bingo */
* The function below should never be called because the cycle_target
* it relates to should never match the execution counter
* If such a match occurs, we ensure an error is reported and the
simcore_cycle_match_error(simcpu_t
*sp
)
fatal("Illegal cycle match error");
cycle_target_off(simcpu_t
*sp
)
sp
->cycle_target
= UINT64_MAX
;
sp
->cycle_target_match
= simcore_cycle_match_error
;
#if !defined(NDEBUG) /* { */
* Used in various control interfaces around the simulator to
simcore_update_debug_bits(uint64_t newval
)
if (debug_bits
== newval
) {
lprintf(-1, "debug_bits 0x%llx -> 0x%llx\n", debug_bits
, newval
);
/* FIXME: Kludge - clobber the decodes incase we turned tracing on ! */
if ((debug_bits
& (DBG_EL
| DBG_EL_MIN
)) == 0 &&
(newval
& (DBG_EL
| DBG_EL_MIN
)) != 0) {
for (i
= 0; i
< simcpu_list
.count
; i
++) {
simcpu_t
*sp
= LIST_ENTRY(simcpu_list
, i
);
sp
->xicache_instn_flush_pending
= true;
nexec_threads
= (options
.threads
> simcpu_list
.count
)
? simcpu_list
.count
: options
.threads
;
* Default behavior for quantum is divide DFT_EXEC_QUANTUM
* by the maximum number of CPUs on an exec thread.
if (options
.quantum
== 0) {
options
.quantum
= (DFT_EXEC_QUANTUM
* nexec_threads
) /
PRINTF(("Using %d execution thread(s) quantum=%u\n", nexec_threads
,
barrier_init(&stop_barrier
, nexec_threads
+ 1);
barrier_init(&run_barrier
, nexec_threads
+ 1);
barrier_busy_init(&sync_busy_barrier
, nexec_threads
);
for (i
= 0; i
< nexec_threads
; i
++) {
* Once we get here, legion has completed all initialization
* and is about to jump to the first instruction. Anything
* that was waiting on the initialization to complete can
simstatus
.initialized
= true;
* If running in service processor mode, we want to wait
* here until the service processor tells us to start.
* Each processor can determine how the SP signals the
* simulation to start, but once the signal is received,
* setting simstatus.sp.poweron to true will cause the
* simulator to start running.
printf("\nWaiting on Serivce Processor POWERON interrupt\n");
while (simstatus
.sp
.poweron
!= true) {
printf("\nReceived Serivce Processor POWERON interrupt\n");
* signal to all exec threads that it's ok to start
* executing instructions.
simstatus
.running
= true;
simstatus
.running
= false;
for (i
= 0; i
< simcpu_list
.count
; i
++) {
simcpu_t
*sp
= LIST_ENTRY(simcpu_list
, i
);
static void simcore_cpu_state_updated(simcpu_t
*sp
)
if (DISABLED(sp
) || PARKED(sp
))
simcore_cpu_enable(simcpu_t
*sp
)
simcore_cpu_state_updated(sp
);
simcore_cpu_state_unpark(simcpu_t
*sp
)
simcore_cpu_state_updated(sp
);
simcore_cpu_disable(simcpu_t
*sp
)
simcore_cpu_state_updated(sp
);
simcore_cpu_state_park(simcpu_t
*sp
)
simcore_cpu_state_updated(sp
);