m5: merge inorder updates
[gem5.git] / src / cpu / base.cc
1 /*
2 * Copyright (c) 2002-2005 The Regents of The University of Michigan
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
7 * met: redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer;
9 * redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution;
12 * neither the name of the copyright holders nor the names of its
13 * contributors may be used to endorse or promote products derived from
14 * this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 *
28 * Authors: Steve Reinhardt
29 * Nathan Binkert
30 */
31
32 #include <iostream>
33 #include <string>
34 #include <sstream>
35
36 #include "base/cprintf.hh"
37 #include "base/loader/symtab.hh"
38 #include "base/misc.hh"
39 #include "base/output.hh"
40 #include "base/trace.hh"
41 #include "cpu/base.hh"
42 #include "cpu/cpuevent.hh"
43 #include "cpu/thread_context.hh"
44 #include "cpu/profile.hh"
45 #include "params/BaseCPU.hh"
46 #include "sim/sim_exit.hh"
47 #include "sim/process.hh"
48 #include "sim/sim_events.hh"
49 #include "sim/system.hh"
50
51 // Hack
52 #include "sim/stat_control.hh"
53
54 using namespace std;
55
56 vector<BaseCPU *> BaseCPU::cpuList;
57
58 // This variable reflects the max number of threads in any CPU. Be
59 // careful to only use it once all the CPUs that you care about have
60 // been initialized
61 int maxThreadsPerCPU = 1;
62
63 CPUProgressEvent::CPUProgressEvent(BaseCPU *_cpu, Tick ival)
64 : Event(Event::Progress_Event_Pri), _interval(ival), lastNumInst(0),
65 cpu(_cpu), _repeatEvent(true)
66 {
67 if (_interval)
68 cpu->schedule(this, curTick + _interval);
69 }
70
71 void
72 CPUProgressEvent::process()
73 {
74 Counter temp = cpu->totalInstructions();
75 #ifndef NDEBUG
76 double ipc = double(temp - lastNumInst) / (_interval / cpu->ticks(1));
77
78 DPRINTFN("%s progress event, total committed:%i, progress insts committed: "
79 "%lli, IPC: %0.8d\n", cpu->name(), temp, temp - lastNumInst,
80 ipc);
81 ipc = 0.0;
82 #else
83 cprintf("%lli: %s progress event, total committed:%i, progress insts "
84 "committed: %lli\n", curTick, cpu->name(), temp,
85 temp - lastNumInst);
86 #endif
87 lastNumInst = temp;
88
89 if (_repeatEvent)
90 cpu->schedule(this, curTick + _interval);
91 }
92
93 const char *
94 CPUProgressEvent::description() const
95 {
96 return "CPU Progress";
97 }
98
99 #if FULL_SYSTEM
100 BaseCPU::BaseCPU(Params *p)
101 : MemObject(p), clock(p->clock), instCnt(0), _cpuId(p->cpu_id),
102 interrupts(p->interrupts),
103 numThreads(p->numThreads), system(p->system),
104 phase(p->phase)
105 #else
106 BaseCPU::BaseCPU(Params *p)
107 : MemObject(p), clock(p->clock), _cpuId(p->cpu_id),
108 numThreads(p->numThreads), system(p->system),
109 phase(p->phase)
110 #endif
111 {
112 // currentTick = curTick;
113
114 // if Python did not provide a valid ID, do it here
115 if (_cpuId == -1 ) {
116 _cpuId = cpuList.size();
117 }
118
119 // add self to global list of CPUs
120 cpuList.push_back(this);
121
122 DPRINTF(SyscallVerbose, "Constructing CPU with id %d\n", _cpuId);
123
124 if (numThreads > maxThreadsPerCPU)
125 maxThreadsPerCPU = numThreads;
126
127 // allocate per-thread instruction-based event queues
128 comInstEventQueue = new EventQueue *[numThreads];
129 for (ThreadID tid = 0; tid < numThreads; ++tid)
130 comInstEventQueue[tid] =
131 new EventQueue("instruction-based event queue");
132
133 //
134 // set up instruction-count-based termination events, if any
135 //
136 if (p->max_insts_any_thread != 0) {
137 const char *cause = "a thread reached the max instruction count";
138 for (ThreadID tid = 0; tid < numThreads; ++tid) {
139 Event *event = new SimLoopExitEvent(cause, 0);
140 comInstEventQueue[tid]->schedule(event, p->max_insts_any_thread);
141 }
142 }
143
144 if (p->max_insts_all_threads != 0) {
145 const char *cause = "all threads reached the max instruction count";
146
147 // allocate & initialize shared downcounter: each event will
148 // decrement this when triggered; simulation will terminate
149 // when counter reaches 0
150 int *counter = new int;
151 *counter = numThreads;
152 for (ThreadID tid = 0; tid < numThreads; ++tid) {
153 Event *event = new CountedExitEvent(cause, *counter);
154 comInstEventQueue[tid]->schedule(event, p->max_insts_all_threads);
155 }
156 }
157
158 // allocate per-thread load-based event queues
159 comLoadEventQueue = new EventQueue *[numThreads];
160 for (ThreadID tid = 0; tid < numThreads; ++tid)
161 comLoadEventQueue[tid] = new EventQueue("load-based event queue");
162
163 //
164 // set up instruction-count-based termination events, if any
165 //
166 if (p->max_loads_any_thread != 0) {
167 const char *cause = "a thread reached the max load count";
168 for (ThreadID tid = 0; tid < numThreads; ++tid) {
169 Event *event = new SimLoopExitEvent(cause, 0);
170 comLoadEventQueue[tid]->schedule(event, p->max_loads_any_thread);
171 }
172 }
173
174 if (p->max_loads_all_threads != 0) {
175 const char *cause = "all threads reached the max load count";
176 // allocate & initialize shared downcounter: each event will
177 // decrement this when triggered; simulation will terminate
178 // when counter reaches 0
179 int *counter = new int;
180 *counter = numThreads;
181 for (ThreadID tid = 0; tid < numThreads; ++tid) {
182 Event *event = new CountedExitEvent(cause, *counter);
183 comLoadEventQueue[tid]->schedule(event, p->max_loads_all_threads);
184 }
185 }
186
187 functionTracingEnabled = false;
188 if (p->function_trace) {
189 functionTraceStream = simout.find(csprintf("ftrace.%s", name()));
190 currentFunctionStart = currentFunctionEnd = 0;
191 functionEntryTick = p->function_trace_start;
192
193 if (p->function_trace_start == 0) {
194 functionTracingEnabled = true;
195 } else {
196 typedef EventWrapper<BaseCPU, &BaseCPU::enableFunctionTrace> wrap;
197 Event *event = new wrap(this, true);
198 schedule(event, p->function_trace_start);
199 }
200 }
201 #if FULL_SYSTEM
202 interrupts->setCPU(this);
203
204 profileEvent = NULL;
205 if (params()->profile)
206 profileEvent = new ProfileEvent(this, params()->profile);
207 #endif
208 tracer = params()->tracer;
209 }
210
211 void
212 BaseCPU::enableFunctionTrace()
213 {
214 functionTracingEnabled = true;
215 }
216
217 BaseCPU::~BaseCPU()
218 {
219 }
220
221 void
222 BaseCPU::init()
223 {
224 if (!params()->defer_registration)
225 registerThreadContexts();
226 }
227
228 void
229 BaseCPU::startup()
230 {
231 #if FULL_SYSTEM
232 if (!params()->defer_registration && profileEvent)
233 schedule(profileEvent, curTick);
234 #endif
235
236 if (params()->progress_interval) {
237 Tick num_ticks = ticks(params()->progress_interval);
238
239 Event *event;
240 event = new CPUProgressEvent(this, num_ticks);
241 }
242 }
243
244
245 void
246 BaseCPU::regStats()
247 {
248 using namespace Stats;
249
250 numCycles
251 .name(name() + ".numCycles")
252 .desc("number of cpu cycles simulated")
253 ;
254
255 int size = threadContexts.size();
256 if (size > 1) {
257 for (int i = 0; i < size; ++i) {
258 stringstream namestr;
259 ccprintf(namestr, "%s.ctx%d", name(), i);
260 threadContexts[i]->regStats(namestr.str());
261 }
262 } else if (size == 1)
263 threadContexts[0]->regStats(name());
264
265 #if FULL_SYSTEM
266 #endif
267 }
268
269 Tick
270 BaseCPU::nextCycle()
271 {
272 Tick next_tick = curTick - phase + clock - 1;
273 next_tick -= (next_tick % clock);
274 next_tick += phase;
275 return next_tick;
276 }
277
278 Tick
279 BaseCPU::nextCycle(Tick begin_tick)
280 {
281 Tick next_tick = begin_tick;
282 if (next_tick % clock != 0)
283 next_tick = next_tick - (next_tick % clock) + clock;
284 next_tick += phase;
285
286 assert(next_tick >= curTick);
287 return next_tick;
288 }
289
290 void
291 BaseCPU::registerThreadContexts()
292 {
293 ThreadID size = threadContexts.size();
294 for (ThreadID tid = 0; tid < size; ++tid) {
295 ThreadContext *tc = threadContexts[tid];
296
297 /** This is so that contextId and cpuId match where there is a
298 * 1cpu:1context relationship. Otherwise, the order of registration
299 * could affect the assignment and cpu 1 could have context id 3, for
300 * example. We may even want to do something like this for SMT so that
301 * cpu 0 has the lowest thread contexts and cpu N has the highest, but
302 * I'll just do this for now
303 */
304 if (numThreads == 1)
305 tc->setContextId(system->registerThreadContext(tc, _cpuId));
306 else
307 tc->setContextId(system->registerThreadContext(tc));
308 #if !FULL_SYSTEM
309 tc->getProcessPtr()->assignThreadContext(tc->contextId());
310 #endif
311 }
312 }
313
314
315 int
316 BaseCPU::findContext(ThreadContext *tc)
317 {
318 ThreadID size = threadContexts.size();
319 for (ThreadID tid = 0; tid < size; ++tid) {
320 if (tc == threadContexts[tid])
321 return tid;
322 }
323 return 0;
324 }
325
326 void
327 BaseCPU::switchOut()
328 {
329 // panic("This CPU doesn't support sampling!");
330 #if FULL_SYSTEM
331 if (profileEvent && profileEvent->scheduled())
332 deschedule(profileEvent);
333 #endif
334 }
335
336 void
337 BaseCPU::takeOverFrom(BaseCPU *oldCPU, Port *ic, Port *dc)
338 {
339 assert(threadContexts.size() == oldCPU->threadContexts.size());
340
341 _cpuId = oldCPU->cpuId();
342
343 ThreadID size = threadContexts.size();
344 for (ThreadID i = 0; i < size; ++i) {
345 ThreadContext *newTC = threadContexts[i];
346 ThreadContext *oldTC = oldCPU->threadContexts[i];
347
348 newTC->takeOverFrom(oldTC);
349
350 CpuEvent::replaceThreadContext(oldTC, newTC);
351
352 assert(newTC->contextId() == oldTC->contextId());
353 assert(newTC->threadId() == oldTC->threadId());
354 system->replaceThreadContext(newTC, newTC->contextId());
355
356 /* This code no longer works since the zero register (e.g.,
357 * r31 on Alpha) doesn't necessarily contain zero at this
358 * point.
359 if (DTRACE(Context))
360 ThreadContext::compare(oldTC, newTC);
361 */
362 }
363
364 #if FULL_SYSTEM
365 interrupts = oldCPU->interrupts;
366 interrupts->setCPU(this);
367
368 for (ThreadID i = 0; i < size; ++i)
369 threadContexts[i]->profileClear();
370
371 if (profileEvent)
372 schedule(profileEvent, curTick);
373 #endif
374
375 // Connect new CPU to old CPU's memory only if new CPU isn't
376 // connected to anything. Also connect old CPU's memory to new
377 // CPU.
378 if (!ic->isConnected()) {
379 Port *peer = oldCPU->getPort("icache_port")->getPeer();
380 ic->setPeer(peer);
381 peer->setPeer(ic);
382 }
383
384 if (!dc->isConnected()) {
385 Port *peer = oldCPU->getPort("dcache_port")->getPeer();
386 dc->setPeer(peer);
387 peer->setPeer(dc);
388 }
389 }
390
391
392 #if FULL_SYSTEM
393 BaseCPU::ProfileEvent::ProfileEvent(BaseCPU *_cpu, Tick _interval)
394 : cpu(_cpu), interval(_interval)
395 { }
396
397 void
398 BaseCPU::ProfileEvent::process()
399 {
400 ThreadID size = cpu->threadContexts.size();
401 for (ThreadID i = 0; i < size; ++i) {
402 ThreadContext *tc = cpu->threadContexts[i];
403 tc->profileSample();
404 }
405
406 cpu->schedule(this, curTick + interval);
407 }
408
409 void
410 BaseCPU::serialize(std::ostream &os)
411 {
412 SERIALIZE_SCALAR(instCnt);
413 interrupts->serialize(os);
414 }
415
416 void
417 BaseCPU::unserialize(Checkpoint *cp, const std::string &section)
418 {
419 UNSERIALIZE_SCALAR(instCnt);
420 interrupts->unserialize(cp, section);
421 }
422
423 #endif // FULL_SYSTEM
424
425 void
426 BaseCPU::traceFunctionsInternal(Addr pc)
427 {
428 if (!debugSymbolTable)
429 return;
430
431 // if pc enters different function, print new function symbol and
432 // update saved range. Otherwise do nothing.
433 if (pc < currentFunctionStart || pc >= currentFunctionEnd) {
434 string sym_str;
435 bool found = debugSymbolTable->findNearestSymbol(pc, sym_str,
436 currentFunctionStart,
437 currentFunctionEnd);
438
439 if (!found) {
440 // no symbol found: use addr as label
441 sym_str = csprintf("0x%x", pc);
442 currentFunctionStart = pc;
443 currentFunctionEnd = pc + 1;
444 }
445
446 ccprintf(*functionTraceStream, " (%d)\n%d: %s",
447 curTick - functionEntryTick, curTick, sym_str);
448 functionEntryTick = curTick;
449 }
450 }