Commit | Line | Data |
---|---|---|
920dae64 AT |
1 | /* |
2 | * ========== Copyright Header Begin ========================================== | |
3 | * | |
4 | * OpenSPARC T2 Processor File: workerthread.h | |
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 | /* standard includes */ | |
24 | #include <pthread.h> | |
25 | #include <synch.h> | |
26 | #include <signal.h> | |
27 | #include <stdio.h> | |
28 | #include <stdlib.h> | |
29 | #include <errno.h> | |
30 | #include <assert.h> | |
31 | #include <list> | |
32 | ||
33 | /* project includes */ | |
34 | #include "types.h" | |
35 | #include "blaze_globals.h" | |
36 | #include "ui.h" | |
37 | #include "system.h" | |
38 | #include "system_impl.h" | |
39 | #include "atomic.h" | |
40 | #include "tracemod.h" | |
41 | #include "prioque.h" | |
42 | #include "cpu_interface.h" | |
43 | #include "vtracer_async_queue.h" | |
44 | ||
45 | ||
46 | extern "C" { | |
47 | static inline void * system_worker_thread (void * arg); | |
48 | }; | |
49 | ||
50 | class WorkerThread { | |
51 | ||
52 | static Vcpu * first_cpu; | |
53 | ||
54 | static volatile uint64_t GlobalTimeUsecs; | |
55 | // | |
56 | // each workerthread maintains a local copy time variable, which is | |
57 | // used for comparison in doEventQueueCallbacks() | |
58 | // | |
59 | volatile uint64_t simTime; | |
60 | ||
61 | ||
62 | // approximate tick count per cpu/strand. only for "mips" ui-cmd, | |
63 | // this is currently Broken ~!~ | |
64 | static volatile int64_t GlobalTicks; | |
65 | ||
66 | ||
67 | static WorkerThread * wrkthds; // worker thread array | |
68 | static volatile int numThds; // total number of workerthreads | |
69 | ||
70 | pthread_t tid; // the real worker thread | |
71 | int worker_id; // 0..numthds-1 | |
72 | int num_cpus; // # of vcpu's assigned to this workerthread | |
73 | Vcpu *cpus[MAX_MP]; // pointers to vcpu's assigned to this thread, | |
74 | // index = [0 ... num_cpus - 1] | |
75 | ||
76 | EventQue * eq; // per worker thread event queue | |
77 | ||
78 | static pthread_key_t key; // to bind the eq to workerthread's private data | |
79 | ||
80 | int64_t done_x_1024[MAX_MP]; | |
81 | ||
82 | WorkerThread(){ | |
83 | called_from_stept = false; | |
84 | KillThread = false; | |
85 | Nusecs = Ninstrs = Ncycles = 0; | |
86 | sema_init (&KICK,0, USYNC_THREAD, NULL); | |
87 | assert (pthread_create (&tid,NULL, &system_worker_thread, (void*)this) == 0); | |
88 | eq = new EventQue(); | |
89 | /////assert (pthread_setspecific(key,(void *) this) == 0); | |
90 | simTime = GlobalTimeUsecs; | |
91 | } | |
92 | ||
93 | ~WorkerThread(){ | |
94 | sema_destroy (&KICK); | |
95 | delete eq; | |
96 | } | |
97 | ||
98 | ||
99 | sema_t KICK; // <--- KICK semaphores | |
100 | bool KillThread; | |
101 | static int64_t Nusecs; /* stept microseconds */ | |
102 | static int64_t Ninstrs; /* stepi instruction count */ | |
103 | static int64_t Ncycles; /* for exec-driven mode */ | |
104 | ||
105 | static volatile int BarrierCount; | |
106 | static volatile int BarrierLock; | |
107 | static volatile int BarrierTemp; | |
108 | static volatile uint64_t step_remainder; /* see note in .cc for explanation */ | |
109 | ||
110 | static volatile uint64_t stick_incr; | |
111 | // to allow for stick frequencies that are not a multiple of 10^6 | |
112 | static volatile uint64_t stick_remainder; | |
113 | ||
114 | void stepi(uint64_t n); | |
115 | void stept(uint64_t usecs); | |
116 | void stepc(int64_t ncycles); | |
117 | int barrier(); | |
118 | static void kill_worker_threads (); | |
119 | ||
120 | bool called_from_stept; | |
121 | ||
122 | void killThread(){ | |
123 | KillThread = true; | |
124 | sema_post (&KICK); | |
125 | } | |
126 | ||
127 | void doEventqueCallbacks (){ | |
128 | Event_t event; | |
129 | ||
130 | while (! eq->empty() | |
131 | && eq->top_time() <= simTime ) { | |
132 | ||
133 | eq->get_top (&event); | |
134 | (*(EventFunc_T*)event.pq_cbfunc) (event.pq_cbarg1, event.pq_cbarg2); | |
135 | } | |
136 | } | |
137 | ||
138 | ||
139 | void doTrace() { | |
140 | if (cpus[0] == first_cpu) { | |
141 | while (trace_async_queue->getsize()) { | |
142 | VCPU_AsyncData async_rec; | |
143 | trace_async_queue->dequeue(&async_rec); | |
144 | first_cpu->sys_intf.vtrace->async(&async_rec); | |
145 | } // if this is the first cpu in the system | |
146 | } | |
147 | ||
148 | // write sync records to all cpus. Use GlobalTimeUsecs as a possible seq # | |
149 | vtracer_syncinfo syncinfo; | |
150 | syncinfo.synctype = vtracer_syncinfo::synctype_LOCAL; | |
151 | syncinfo.syncid = GlobalTimeUsecs; | |
152 | syncinfo.data = 0; | |
153 | int i; | |
154 | for (i=0; i<num_cpus; i++) { | |
155 | syncinfo.cpuid = cpus[i]->id(); | |
156 | cpus[i]->sys_intf.vtrace->sync(&syncinfo); | |
157 | } | |
158 | ||
159 | } | |
160 | ||
161 | ||
162 | public: | |
163 | // external interface exported by workerthreads | |
164 | ||
165 | void run_workerThread(){ | |
166 | assert (pthread_setspecific(key,(void *) this) == 0); | |
167 | for (;;) { | |
168 | sema_wait (&KICK); // --------------------------------- STANDBY !!! | |
169 | ||
170 | if (KillThread) { | |
171 | pthread_exit (0); | |
172 | tid = 0; | |
173 | } | |
174 | if (Nusecs) | |
175 | stept(Nusecs); | |
176 | else if(Ninstrs) | |
177 | stepi(Ninstrs); | |
178 | else if (Ncycles) | |
179 | stepc(Ncycles); | |
180 | } | |
181 | } | |
182 | static void kick_stepi(uint64_t n){ | |
183 | // initialize the stick related variabled | |
184 | stick_incr = the_arch.stick_freq/1000000ull; | |
185 | ||
186 | if(!wrkthds) | |
187 | create_worker_threads (SYSTEM_get_ncpu(),SYSTEM_get_cpus_per_thread(),\ | |
188 | SYSTEM_get_numthreads()); | |
189 | Ninstrs = n; Nusecs = 0; Ncycles = 0; | |
190 | for (int i = 0; i < numThds; i++) | |
191 | sema_post (&wrkthds[i].KICK); | |
192 | } | |
193 | ||
194 | static void kick_stept(uint64_t usecs){ | |
195 | // initialize the stick related variabled | |
196 | stick_incr = the_arch.stick_freq/1000000ull; | |
197 | ||
198 | if(!wrkthds) | |
199 | create_worker_threads (SYSTEM_get_ncpu(),SYSTEM_get_cpus_per_thread(), \ | |
200 | SYSTEM_get_numthreads()); | |
201 | Nusecs = usecs; Ninstrs = 0; Ncycles = 0; | |
202 | for (int i = 0; i < numThds; i++) | |
203 | sema_post (&wrkthds[i].KICK); | |
204 | } | |
205 | ||
206 | static void kick_stepc(int64_t ncycles) { | |
207 | stick_incr = the_arch.stick_freq/1000000ull; | |
208 | if (!wrkthds) | |
209 | create_worker_threads(SYSTEM_get_ncpu(), SYSTEM_get_ncpu(), 1); | |
210 | ||
211 | Ncycles = ncycles; Nusecs = Ninstrs = 0; | |
212 | sema_post(&wrkthds[0].KICK); | |
213 | } | |
214 | ||
215 | void info(){ | |
216 | ui->verbose("sid = {"); | |
217 | for (int j=0; j < num_cpus; j++) | |
218 | { | |
219 | if (j>0) ui->verbose (","); | |
220 | ui->verbose ("%d", cpus[j]->id()); | |
221 | } | |
222 | ui->verbose ("} \n"); | |
223 | } | |
224 | ||
225 | ||
226 | ||
227 | static void registerEvent (uint64_t stime, | |
228 | EventFunc_T* callbackfunc, void* arg1, void* arg2, | |
229 | UnloadFunc_T* unloadfunc, | |
230 | const char * debugstring) | |
231 | { | |
232 | WorkerThread * wt = (WorkerThread*) pthread_getspecific (key); | |
233 | EventQue * myeq = wt ? wt->eq : 0; | |
234 | if (!myeq) { | |
235 | ||
236 | if (!wrkthds) { | |
237 | ||
238 | create_worker_threads(SYSTEM_get_ncpu(), | |
239 | SYSTEM_get_cpus_per_thread(), | |
240 | SYSTEM_get_numthreads()); | |
241 | } | |
242 | myeq = wrkthds[0].eq; | |
243 | } | |
244 | ||
245 | myeq->insert_callback ( stime, (void*)callbackfunc, arg1, arg2, | |
246 | (void*)unloadfunc, debugstring, | |
247 | (wt ? wt->worker_id : -1));/*more debug*/ | |
248 | } | |
249 | ||
250 | ||
251 | static void doEventqueUnloads () | |
252 | { | |
253 | for (int i = 0; i < numThds; i++) | |
254 | wrkthds[i].eq->unload(); | |
255 | } | |
256 | ||
257 | ||
258 | static void doEventquePrint () | |
259 | { | |
260 | for (int i = 0; i < numThds; i++) | |
261 | wrkthds[i].eq->print (); | |
262 | } | |
263 | ||
264 | ||
265 | ||
266 | ||
267 | static void create_worker_threads (int NumCpus, int cpusPerThread, int numThreads); | |
268 | ||
269 | // store any needed parameter in the form of NAME VALUE pair in fp | |
270 | static void dump(FILE * fp); | |
271 | // restore parameters stored in the form of NAME VALUE pair from fp | |
272 | static int restore(char * line); | |
273 | ||
274 | ||
275 | static uint64_t get_time(){ | |
276 | WorkerThread * wt = (WorkerThread*) pthread_getspecific(key); | |
277 | ||
278 | return (wt ? wt->simTime : GlobalTimeUsecs); | |
279 | } | |
280 | ||
281 | ||
282 | static int64_t get_ticks(){ return GlobalTicks;} | |
283 | ||
284 | ||
285 | static volatile int64_t u_instrs; // for "mips" ui command | |
286 | static volatile int64_t k_instrs; | |
287 | ||
288 | static volatile int64_t u_intervals; // ditto | |
289 | static volatile int64_t k_intervals; | |
290 | }; | |
291 | ||
292 | extern "C" { | |
293 | static inline void * system_worker_thread (void * arg){ | |
294 | WorkerThread * sp = (WorkerThread*) arg; | |
295 | sp->run_workerThread(); | |
296 | return 0; | |
297 | } | |
298 | }; | |
299 |