base.cc revision 6144:e330f7bc22ef
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 number_of_threads(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 number_of_threads(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 (number_of_threads > maxThreadsPerCPU) 125 maxThreadsPerCPU = number_of_threads; 126 127 // allocate per-thread instruction-based event queues 128 comInstEventQueue = new EventQueue *[number_of_threads]; 129 for (int i = 0; i < number_of_threads; ++i) 130 comInstEventQueue[i] = new EventQueue("instruction-based event queue"); 131 132 // 133 // set up instruction-count-based termination events, if any 134 // 135 if (p->max_insts_any_thread != 0) { 136 const char *cause = "a thread reached the max instruction count"; 137 for (int i = 0; i < number_of_threads; ++i) { 138 Event *event = new SimLoopExitEvent(cause, 0); 139 comInstEventQueue[i]->schedule(event, p->max_insts_any_thread); 140 } 141 } 142 143 if (p->max_insts_all_threads != 0) { 144 const char *cause = "all threads reached the max instruction count"; 145 146 // allocate & initialize shared downcounter: each event will 147 // decrement this when triggered; simulation will terminate 148 // when counter reaches 0 149 int *counter = new int; 150 *counter = number_of_threads; 151 for (int i = 0; i < number_of_threads; ++i) { 152 Event *event = new CountedExitEvent(cause, *counter); 153 comInstEventQueue[i]->schedule(event, p->max_insts_any_thread); 154 } 155 } 156 157 // allocate per-thread load-based event queues 158 comLoadEventQueue = new EventQueue *[number_of_threads]; 159 for (int i = 0; i < number_of_threads; ++i) 160 comLoadEventQueue[i] = new EventQueue("load-based event queue"); 161 162 // 163 // set up instruction-count-based termination events, if any 164 // 165 if (p->max_loads_any_thread != 0) { 166 const char *cause = "a thread reached the max load count"; 167 for (int i = 0; i < number_of_threads; ++i) { 168 Event *event = new SimLoopExitEvent(cause, 0); 169 comLoadEventQueue[i]->schedule(event, p->max_loads_any_thread); 170 } 171 } 172 173 if (p->max_loads_all_threads != 0) { 174 const char *cause = "all threads reached the max load count"; 175 // allocate & initialize shared downcounter: each event will 176 // decrement this when triggered; simulation will terminate 177 // when counter reaches 0 178 int *counter = new int; 179 *counter = number_of_threads; 180 for (int i = 0; i < number_of_threads; ++i) { 181 Event *event = new CountedExitEvent(cause, *counter); 182 comLoadEventQueue[i]->schedule(event, p->max_loads_all_threads); 183 } 184 } 185 186 functionTracingEnabled = false; 187 if (p->function_trace) { 188 functionTraceStream = simout.find(csprintf("ftrace.%s", name())); 189 currentFunctionStart = currentFunctionEnd = 0; 190 functionEntryTick = p->function_trace_start; 191 192 if (p->function_trace_start == 0) { 193 functionTracingEnabled = true; 194 } else { 195 typedef EventWrapper<BaseCPU, &BaseCPU::enableFunctionTrace> wrap; 196 Event *event = new wrap(this, true); 197 schedule(event, p->function_trace_start); 198 } 199 } 200#if FULL_SYSTEM 201 interrupts->setCPU(this); 202 203 profileEvent = NULL; 204 if (params()->profile) 205 profileEvent = new ProfileEvent(this, params()->profile); 206#endif 207 tracer = params()->tracer; 208} 209 210void 211BaseCPU::enableFunctionTrace() 212{ 213 functionTracingEnabled = true; 214} 215 216BaseCPU::~BaseCPU() 217{ 218} 219 220void 221BaseCPU::init() 222{ 223 if (!params()->defer_registration) 224 registerThreadContexts(); 225} 226 227void 228BaseCPU::startup() 229{ 230#if FULL_SYSTEM 231 if (!params()->defer_registration && profileEvent) 232 schedule(profileEvent, curTick); 233#endif 234 235 if (params()->progress_interval) { 236 Tick num_ticks = ticks(params()->progress_interval); 237 238 Event *event; 239 event = new CPUProgressEvent(this, num_ticks); 240 } 241} 242 243 244void 245BaseCPU::regStats() 246{ 247 using namespace Stats; 248 249 numCycles 250 .name(name() + ".numCycles") 251 .desc("number of cpu cycles simulated") 252 ; 253 254 int size = threadContexts.size(); 255 if (size > 1) { 256 for (int i = 0; i < size; ++i) { 257 stringstream namestr; 258 ccprintf(namestr, "%s.ctx%d", name(), i); 259 threadContexts[i]->regStats(namestr.str()); 260 } 261 } else if (size == 1) 262 threadContexts[0]->regStats(name()); 263 264#if FULL_SYSTEM 265#endif 266} 267 268Tick 269BaseCPU::nextCycle() 270{ 271 Tick next_tick = curTick - phase + clock - 1; 272 next_tick -= (next_tick % clock); 273 next_tick += phase; 274 return next_tick; 275} 276 277Tick 278BaseCPU::nextCycle(Tick begin_tick) 279{ 280 Tick next_tick = begin_tick; 281 if (next_tick % clock != 0) 282 next_tick = next_tick - (next_tick % clock) + clock; 283 next_tick += phase; 284 285 assert(next_tick >= curTick); 286 return next_tick; 287} 288 289void 290BaseCPU::registerThreadContexts() 291{ 292 for (int i = 0; i < threadContexts.size(); ++i) { 293 ThreadContext *tc = threadContexts[i]; 294 295 /** This is so that contextId and cpuId match where there is a 296 * 1cpu:1context relationship. Otherwise, the order of registration 297 * could affect the assignment and cpu 1 could have context id 3, for 298 * example. We may even want to do something like this for SMT so that 299 * cpu 0 has the lowest thread contexts and cpu N has the highest, but 300 * I'll just do this for now 301 */ 302 if (number_of_threads == 1) 303 tc->setContextId(system->registerThreadContext(tc, _cpuId)); 304 else 305 tc->setContextId(system->registerThreadContext(tc)); 306#if !FULL_SYSTEM 307 tc->getProcessPtr()->assignThreadContext(tc->contextId()); 308#endif 309 } 310} 311 312 313int 314BaseCPU::findContext(ThreadContext *tc) 315{ 316 for (int i = 0; i < threadContexts.size(); ++i) { 317 if (tc == threadContexts[i]) 318 return i; 319 } 320 return 0; 321} 322 323void 324BaseCPU::switchOut() 325{ 326// panic("This CPU doesn't support sampling!"); 327#if FULL_SYSTEM 328 if (profileEvent && profileEvent->scheduled()) 329 deschedule(profileEvent); 330#endif 331} 332 333void 334BaseCPU::takeOverFrom(BaseCPU *oldCPU, Port *ic, Port *dc) 335{ 336 assert(threadContexts.size() == oldCPU->threadContexts.size()); 337 338 _cpuId = oldCPU->cpuId(); 339 340 for (int i = 0; i < threadContexts.size(); ++i) { 341 ThreadContext *newTC = threadContexts[i]; 342 ThreadContext *oldTC = oldCPU->threadContexts[i]; 343 344 newTC->takeOverFrom(oldTC); 345 346 CpuEvent::replaceThreadContext(oldTC, newTC); 347 348 assert(newTC->contextId() == oldTC->contextId()); 349 assert(newTC->threadId() == oldTC->threadId()); 350 system->replaceThreadContext(newTC, newTC->contextId()); 351 352 /* This code no longer works since the zero register (e.g., 353 * r31 on Alpha) doesn't necessarily contain zero at this 354 * point. 355 if (DTRACE(Context)) 356 ThreadContext::compare(oldTC, newTC); 357 */ 358 } 359 360#if FULL_SYSTEM 361 interrupts = oldCPU->interrupts; 362 interrupts->setCPU(this); 363 364 for (int i = 0; i < threadContexts.size(); ++i) 365 threadContexts[i]->profileClear(); 366 367 if (profileEvent) 368 schedule(profileEvent, curTick); 369#endif 370 371 // Connect new CPU to old CPU's memory only if new CPU isn't 372 // connected to anything. Also connect old CPU's memory to new 373 // CPU. 374 if (!ic->isConnected()) { 375 Port *peer = oldCPU->getPort("icache_port")->getPeer(); 376 ic->setPeer(peer); 377 peer->setPeer(ic); 378 } 379 380 if (!dc->isConnected()) { 381 Port *peer = oldCPU->getPort("dcache_port")->getPeer(); 382 dc->setPeer(peer); 383 peer->setPeer(dc); 384 } 385} 386 387 388#if FULL_SYSTEM 389BaseCPU::ProfileEvent::ProfileEvent(BaseCPU *_cpu, Tick _interval) 390 : cpu(_cpu), interval(_interval) 391{ } 392 393void 394BaseCPU::ProfileEvent::process() 395{ 396 for (int i = 0, size = cpu->threadContexts.size(); i < size; ++i) { 397 ThreadContext *tc = cpu->threadContexts[i]; 398 tc->profileSample(); 399 } 400 401 cpu->schedule(this, curTick + interval); 402} 403 404void 405BaseCPU::serialize(std::ostream &os) 406{ 407 SERIALIZE_SCALAR(instCnt); 408 interrupts->serialize(os); 409} 410 411void 412BaseCPU::unserialize(Checkpoint *cp, const std::string §ion) 413{ 414 UNSERIALIZE_SCALAR(instCnt); 415 interrupts->unserialize(cp, section); 416} 417 418#endif // FULL_SYSTEM 419 420void 421BaseCPU::traceFunctionsInternal(Addr pc) 422{ 423 if (!debugSymbolTable) 424 return; 425 426 // if pc enters different function, print new function symbol and 427 // update saved range. Otherwise do nothing. 428 if (pc < currentFunctionStart || pc >= currentFunctionEnd) { 429 string sym_str; 430 bool found = debugSymbolTable->findNearestSymbol(pc, sym_str, 431 currentFunctionStart, 432 currentFunctionEnd); 433 434 if (!found) { 435 // no symbol found: use addr as label 436 sym_str = csprintf("0x%x", pc); 437 currentFunctionStart = pc; 438 currentFunctionEnd = pc + 1; 439 } 440 441 ccprintf(*functionTraceStream, " (%d)\n%d: %s", 442 curTick - functionEntryTick, curTick, sym_str); 443 functionEntryTick = curTick; 444 } 445} 446