base.cc revision 6670
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 54using namespace std; 55 56vector<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 61int maxThreadsPerCPU = 1; 62 63CPUProgressEvent::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 71void 72CPUProgressEvent::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 93const char * 94CPUProgressEvent::description() const 95{ 96 return "CPU Progress"; 97} 98 99#if FULL_SYSTEM 100BaseCPU::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 106BaseCPU::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 211void 212BaseCPU::enableFunctionTrace() 213{ 214 functionTracingEnabled = true; 215} 216 217BaseCPU::~BaseCPU() 218{ 219} 220 221void 222BaseCPU::init() 223{ 224 if (!params()->defer_registration) 225 registerThreadContexts(); 226} 227 228void 229BaseCPU::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 245void 246BaseCPU::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 269Tick 270BaseCPU::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 278Tick 279BaseCPU::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 290void 291BaseCPU::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 315int 316BaseCPU::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 326void 327BaseCPU::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 336void 337BaseCPU::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 393BaseCPU::ProfileEvent::ProfileEvent(BaseCPU *_cpu, Tick _interval) 394 : cpu(_cpu), interval(_interval) 395{ } 396 397void 398BaseCPU::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 409void 410BaseCPU::serialize(std::ostream &os) 411{ 412 SERIALIZE_SCALAR(instCnt); 413 interrupts->serialize(os); 414} 415 416void 417BaseCPU::unserialize(Checkpoint *cp, const std::string §ion) 418{ 419 UNSERIALIZE_SCALAR(instCnt); 420 interrupts->unserialize(cp, section); 421} 422 423#endif // FULL_SYSTEM 424 425void 426BaseCPU::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} 451