Initial commit of OpenSPARC T2 architecture model.
[OpenSPARC-T2-SAM] / legion / src / simcore / control.c
/*
* ========== 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"
#include <stdlib.h>
#include <pthread.h>
#include <errno.h>
#include <unistd.h>
#include "basics.h"
#include "options.h"
#include "fatal.h"
#include "allocate.h"
#include "simcore.h"
#include "config.h"
#include "createthr.h"
#include "xicache.h"
#include "barrier.h"
#include "callback.h"
#include "breakpoint.h"
/*
* This file contains the control and execution infrastructure for the
* simulator.
*
* 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
* for execution.
*/
LIST_DEF(exec_thread_list, exec_thread_t);
static int nexec_threads;
simcpu_list_t simcpu_list;
barrier_t stop_barrier;
barrier_t run_barrier;
barrier_busy_t sync_busy_barrier;
simstatus_t simstatus;
void init_simstatus(void)
{
simstatus.sp.mode = false;
simstatus.sp.poweron = false;
simstatus.running = false;
simstatus.initialized = false;
}
simcpu_t *sim_cpu_alloc(
config_proc_t *cfp,
void *specificp)
{
simcpu_t *sp;
int gid;
gid = simcpu_list.count;
sp = LIST_ADD(simcpu_list, simcpu_t);
sp->gid = gid;
sp->state_bits = 0;
sp->attention = 0;
#if ERROR_INJECTION
sp->error_enabled = false;
sp->error_check = false;
sp->error_priv = NULL;
sp->errorp = Xcalloc(1, error_t);
sp->errorp->errlistp = NULL;
#endif
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->decodemep = NULL;
sp->xic_miss = NULL;
sp->xic_miss_addrp = NULL;
sp->config_procp = cfp;
sp->specificp = specificp;
#if ERROR_TRAP_GEN /* { */
sp->error_cycle = UINT64_MAX;
sp->error_pending = false;
sp->error_cycle_reached = false;
sp->eep = NULL;
#endif /* ERROR_TRAP_GEN } */
sp->cycle = 0LL;
sp->cycle_quantum_start = sp->cycle;
cycle_target_off(sp);
sp->total_instr = 0LL;
/*
* More misc stuff ..
* flush the xdcache, but the support functions
* need to be supplied by the caller
*/
xdcache_flush(sp);
sp->xdc.miss = NULL;
/*
* clear the breakpoint cache for this cpu
*/
sp->bp_infop = NULL;
return (sp);
}
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 "
"nsimcpus=%d\n",
etp->id, etp->allp->gid, etp->nsimcpus););
ASSERT(etp->allp != NULL);
if (etp->allp->config_procp->proc_typep->debug_hookp != NULL)
exec_loop_dh(etp);
else
exec_loop(etp);
/* We never return to here */
return ((void*)0);
}
static void exec_thread_alloc(void)
{
exec_thread_t *etp;
pthread_t id;
int i, n, r, etidx;
simcpu_t *prev_sp = NULL;
static int simidx = 0;
etidx = exec_thread_list.count;
etp = LIST_ADD(exec_thread_list, exec_thread_t);
etp->id = etidx;
/*
* 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;
if (etp->id < r)
n += 1;
/* 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
*/
if (etp->allp == NULL)
etp->allp = sp;
/*
* Update the nextp in the previous simcpu of this
* list
*/
if (prev_sp != NULL)
prev_sp->nextp = sp;
/* so all simcpus can find the head of the list */
sp->headp = etp->allp;
/* so all simcpus can find the exec_thread */
sp->etp = etp;
prev_sp = sp;
etp->nsimcpus++;
simidx++;
} else {
printf("\nERROR:: exec_thread_alloc: "
"simidx < simcpu_list.count (0x%x < 0x%x)",
simidx, simcpu_list.count);
fatal("DIE");
}
}
/* create the execution thread */
create_thread(exec_thread_init, (void *)etp, &id);
}
static void *ctrl_thread_main(void *argp)
{
simcpu_t *sp;
int i, j;
for (;;) {
while (!simstatus.running) {
/*
* spin until it's time to go. this is useful when
* we're not auto-starting.
*/
}
/* pre-exec setup */
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
* stop again
*/
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
* stopped callbacks
*/
/* post-exec cleanup */
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 */
callback_fire(CB_Stop);
/* if we've hit a breakpoint, call breakpoint callbacks */
if (breakpoint_any_reached()) {
callback_fire(CB_Breakpoint);
}
}
}
static void ctrl_thread_alloc(void)
{
pthread_t id;
create_thread(ctrl_thread_main, NULL, &id);
}
/*
* support functions for finding devices etc
*/
config_addr_t *
find_domain_address(domain_t *domainp, tpaddr_t pa)
{
config_addr_t *addrp;
for (addrp = domainp->address.listp;
addrp != NULL && addrp->baseaddr <= pa; addrp = addrp->nextp) {
if ((addrp->baseaddr <= pa) && (addrp->topaddr > pa)) {
return (addrp); /* bingo */
}
}
return (NULL);
}
/*
* 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
* simulator stops.
*/
void
simcore_cycle_match_error(simcpu_t *sp)
{
fatal("Illegal cycle match error");
}
void
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
* modify the debug modes
*/
void
simcore_update_debug_bits(uint64_t newval)
{
int i;
if (debug_bits == newval) {
return;
}
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;
set_sync_pending(sp);
}
}
/* end of Kludge */
debug_bits = newval;
}
#endif /* } */
void
init_threads(void)
{
int i;
callback_init();
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) /
simcpu_list.count;
}
PRINTF(("Using %d execution thread(s) quantum=%u\n", nexec_threads,
options.quantum));
barrier_init(&stop_barrier, nexec_threads + 1);
barrier_init(&run_barrier, nexec_threads + 1);
barrier_busy_init(&sync_busy_barrier, nexec_threads);
ctrl_thread_alloc();
for (i = 0; i < nexec_threads; i++) {
exec_thread_alloc();
}
}
void
simcore_start(void)
{
/*
* 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
* start now.
*/
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.
*/
if (simstatus.sp.mode) {
printf("\nWaiting on Serivce Processor POWERON interrupt\n");
while (simstatus.sp.poweron != true) {
sleep(1);
}
printf("\nReceived Serivce Processor POWERON interrupt\n");
}
/*
* signal to all exec threads that it's ok to start
* executing instructions.
*/
simstatus.running = true;
}
void
simcore_stop(void)
{
if (simstatus.running) {
int i;
simstatus.running = false;
for (i = 0; i < simcpu_list.count; i++) {
simcpu_t *sp = LIST_ENTRY(simcpu_list, i);
set_sync_pending(sp);
}
}
}
static void simcore_cpu_state_updated(simcpu_t *sp)
{
if (DISABLED(sp) || PARKED(sp))
set_sync_pending(sp);
}
void
simcore_cpu_enable(simcpu_t *sp)
{
ASSERT(DISABLED(sp));
CLR_DISABLED(sp);
simcore_cpu_state_updated(sp);
}
void
simcore_cpu_state_unpark(simcpu_t *sp)
{
ASSERT(PARKED(sp));
CLR_PARKED(sp);
simcore_cpu_state_updated(sp);
}
void
simcore_cpu_disable(simcpu_t *sp)
{
ASSERT(!DISABLED(sp));
SET_DISABLED(sp);
simcore_cpu_state_updated(sp);
}
void
simcore_cpu_state_park(simcpu_t *sp)
{
ASSERT(!PARKED(sp));
SET_PARKED(sp);
simcore_cpu_state_updated(sp);
}