Initial commit of OpenSPARC T2 architecture model.
[OpenSPARC-T2-SAM] / sam-t2 / sam / system / blaze / include / workerthread.h
/*
* ========== Copyright Header Begin ==========================================
*
* OpenSPARC T2 Processor File: workerthread.h
* 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 ============================================
*/
/* standard includes */
#include <pthread.h>
#include <synch.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <assert.h>
#include <list>
/* project includes */
#include "types.h"
#include "blaze_globals.h"
#include "ui.h"
#include "system.h"
#include "system_impl.h"
#include "atomic.h"
#include "tracemod.h"
#include "prioque.h"
#include "cpu_interface.h"
#include "vtracer_async_queue.h"
extern "C" {
static inline void * system_worker_thread (void * arg);
};
class WorkerThread {
static Vcpu * first_cpu;
static volatile uint64_t GlobalTimeUsecs;
//
// each workerthread maintains a local copy time variable, which is
// used for comparison in doEventQueueCallbacks()
//
volatile uint64_t simTime;
// approximate tick count per cpu/strand. only for "mips" ui-cmd,
// this is currently Broken ~!~
static volatile int64_t GlobalTicks;
static WorkerThread * wrkthds; // worker thread array
static volatile int numThds; // total number of workerthreads
pthread_t tid; // the real worker thread
int worker_id; // 0..numthds-1
int num_cpus; // # of vcpu's assigned to this workerthread
Vcpu *cpus[MAX_MP]; // pointers to vcpu's assigned to this thread,
// index = [0 ... num_cpus - 1]
EventQue * eq; // per worker thread event queue
static pthread_key_t key; // to bind the eq to workerthread's private data
int64_t done_x_1024[MAX_MP];
WorkerThread(){
called_from_stept = false;
KillThread = false;
Nusecs = Ninstrs = Ncycles = 0;
sema_init (&KICK,0, USYNC_THREAD, NULL);
assert (pthread_create (&tid,NULL, &system_worker_thread, (void*)this) == 0);
eq = new EventQue();
/////assert (pthread_setspecific(key,(void *) this) == 0);
simTime = GlobalTimeUsecs;
}
~WorkerThread(){
sema_destroy (&KICK);
delete eq;
}
sema_t KICK; // <--- KICK semaphores
bool KillThread;
static int64_t Nusecs; /* stept microseconds */
static int64_t Ninstrs; /* stepi instruction count */
static int64_t Ncycles; /* for exec-driven mode */
static volatile int BarrierCount;
static volatile int BarrierLock;
static volatile int BarrierTemp;
static volatile uint64_t step_remainder; /* see note in .cc for explanation */
static volatile uint64_t stick_incr;
// to allow for stick frequencies that are not a multiple of 10^6
static volatile uint64_t stick_remainder;
void stepi(uint64_t n);
void stept(uint64_t usecs);
void stepc(int64_t ncycles);
int barrier();
static void kill_worker_threads ();
bool called_from_stept;
void killThread(){
KillThread = true;
sema_post (&KICK);
}
void doEventqueCallbacks (){
Event_t event;
while (! eq->empty()
&& eq->top_time() <= simTime ) {
eq->get_top (&event);
(*(EventFunc_T*)event.pq_cbfunc) (event.pq_cbarg1, event.pq_cbarg2);
}
}
void doTrace() {
if (cpus[0] == first_cpu) {
while (trace_async_queue->getsize()) {
VCPU_AsyncData async_rec;
trace_async_queue->dequeue(&async_rec);
first_cpu->sys_intf.vtrace->async(&async_rec);
} // if this is the first cpu in the system
}
// write sync records to all cpus. Use GlobalTimeUsecs as a possible seq #
vtracer_syncinfo syncinfo;
syncinfo.synctype = vtracer_syncinfo::synctype_LOCAL;
syncinfo.syncid = GlobalTimeUsecs;
syncinfo.data = 0;
int i;
for (i=0; i<num_cpus; i++) {
syncinfo.cpuid = cpus[i]->id();
cpus[i]->sys_intf.vtrace->sync(&syncinfo);
}
}
public:
// external interface exported by workerthreads
void run_workerThread(){
assert (pthread_setspecific(key,(void *) this) == 0);
for (;;) {
sema_wait (&KICK); // --------------------------------- STANDBY !!!
if (KillThread) {
pthread_exit (0);
tid = 0;
}
if (Nusecs)
stept(Nusecs);
else if(Ninstrs)
stepi(Ninstrs);
else if (Ncycles)
stepc(Ncycles);
}
}
static void kick_stepi(uint64_t n){
// initialize the stick related variabled
stick_incr = the_arch.stick_freq/1000000ull;
if(!wrkthds)
create_worker_threads (SYSTEM_get_ncpu(),SYSTEM_get_cpus_per_thread(),\
SYSTEM_get_numthreads());
Ninstrs = n; Nusecs = 0; Ncycles = 0;
for (int i = 0; i < numThds; i++)
sema_post (&wrkthds[i].KICK);
}
static void kick_stept(uint64_t usecs){
// initialize the stick related variabled
stick_incr = the_arch.stick_freq/1000000ull;
if(!wrkthds)
create_worker_threads (SYSTEM_get_ncpu(),SYSTEM_get_cpus_per_thread(), \
SYSTEM_get_numthreads());
Nusecs = usecs; Ninstrs = 0; Ncycles = 0;
for (int i = 0; i < numThds; i++)
sema_post (&wrkthds[i].KICK);
}
static void kick_stepc(int64_t ncycles) {
stick_incr = the_arch.stick_freq/1000000ull;
if (!wrkthds)
create_worker_threads(SYSTEM_get_ncpu(), SYSTEM_get_ncpu(), 1);
Ncycles = ncycles; Nusecs = Ninstrs = 0;
sema_post(&wrkthds[0].KICK);
}
void info(){
ui->verbose("sid = {");
for (int j=0; j < num_cpus; j++)
{
if (j>0) ui->verbose (",");
ui->verbose ("%d", cpus[j]->id());
}
ui->verbose ("} \n");
}
static void registerEvent (uint64_t stime,
EventFunc_T* callbackfunc, void* arg1, void* arg2,
UnloadFunc_T* unloadfunc,
const char * debugstring)
{
WorkerThread * wt = (WorkerThread*) pthread_getspecific (key);
EventQue * myeq = wt ? wt->eq : 0;
if (!myeq) {
if (!wrkthds) {
create_worker_threads(SYSTEM_get_ncpu(),
SYSTEM_get_cpus_per_thread(),
SYSTEM_get_numthreads());
}
myeq = wrkthds[0].eq;
}
myeq->insert_callback ( stime, (void*)callbackfunc, arg1, arg2,
(void*)unloadfunc, debugstring,
(wt ? wt->worker_id : -1));/*more debug*/
}
static void doEventqueUnloads ()
{
for (int i = 0; i < numThds; i++)
wrkthds[i].eq->unload();
}
static void doEventquePrint ()
{
for (int i = 0; i < numThds; i++)
wrkthds[i].eq->print ();
}
static void create_worker_threads (int NumCpus, int cpusPerThread, int numThreads);
// store any needed parameter in the form of NAME VALUE pair in fp
static void dump(FILE * fp);
// restore parameters stored in the form of NAME VALUE pair from fp
static int restore(char * line);
static uint64_t get_time(){
WorkerThread * wt = (WorkerThread*) pthread_getspecific(key);
return (wt ? wt->simTime : GlobalTimeUsecs);
}
static int64_t get_ticks(){ return GlobalTicks;}
static volatile int64_t u_instrs; // for "mips" ui command
static volatile int64_t k_instrs;
static volatile int64_t u_intervals; // ditto
static volatile int64_t k_intervals;
};
extern "C" {
static inline void * system_worker_thread (void * arg){
WorkerThread * sp = (WorkerThread*) arg;
sp->run_workerThread();
return 0;
}
};