atomic.cc revision 8850
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 */ 30 31#include "arch/locked_mem.hh" 32#include "arch/mmapped_ipr.hh" 33#include "arch/utility.hh" 34#include "base/bigint.hh" 35#include "config/the_isa.hh" 36#include "cpu/simple/atomic.hh" 37#include "cpu/exetrace.hh" 38#include "debug/ExecFaulting.hh" 39#include "debug/SimpleCPU.hh" 40#include "mem/packet.hh" 41#include "mem/packet_access.hh" 42#include "params/AtomicSimpleCPU.hh" 43#include "sim/faults.hh" 44#include "sim/system.hh" 45#include "sim/full_system.hh" 46 47using namespace std; 48using namespace TheISA; 49 50AtomicSimpleCPU::TickEvent::TickEvent(AtomicSimpleCPU *c) 51 : Event(CPU_Tick_Pri), cpu(c) 52{ 53} 54 55 56void 57AtomicSimpleCPU::TickEvent::process() 58{ 59 cpu->tick(); 60} 61 62const char * 63AtomicSimpleCPU::TickEvent::description() const 64{ 65 return "AtomicSimpleCPU tick"; 66} 67 68Port * 69AtomicSimpleCPU::getPort(const string &if_name, int idx) 70{ 71 if (if_name == "physmem_port") { 72 hasPhysMemPort = true; 73 return &physmemPort; 74 } else { 75 return BaseCPU::getPort(if_name, idx); 76 } 77} 78 79void 80AtomicSimpleCPU::init() 81{ 82 BaseCPU::init(); 83 if (FullSystem) { 84 ThreadID size = threadContexts.size(); 85 for (ThreadID i = 0; i < size; ++i) { 86 ThreadContext *tc = threadContexts[i]; 87 // initialize CPU, including PC 88 TheISA::initCPU(tc, tc->contextId()); 89 } 90 } 91 92 // Initialise the ThreadContext's memory proxies 93 tcBase()->initMemProxies(tcBase()); 94 95 if (hasPhysMemPort) { 96 AddrRangeList pmAddrList = physmemPort.getPeer()->getAddrRanges(); 97 physMemAddr = *pmAddrList.begin(); 98 } 99 // Atomic doesn't do MT right now, so contextId == threadId 100 ifetch_req.setThreadContext(_cpuId, 0); // Add thread ID if we add MT 101 data_read_req.setThreadContext(_cpuId, 0); // Add thread ID here too 102 data_write_req.setThreadContext(_cpuId, 0); // Add thread ID here too 103} 104 105AtomicSimpleCPU::AtomicSimpleCPU(AtomicSimpleCPUParams *p) 106 : BaseSimpleCPU(p), tickEvent(this), width(p->width), locked(false), 107 simulate_data_stalls(p->simulate_data_stalls), 108 simulate_inst_stalls(p->simulate_inst_stalls), 109 icachePort(name() + "-iport", this), dcachePort(name() + "-iport", this), 110 physmemPort(name() + "-iport", this), hasPhysMemPort(false) 111{ 112 _status = Idle; 113} 114 115 116AtomicSimpleCPU::~AtomicSimpleCPU() 117{ 118 if (tickEvent.scheduled()) { 119 deschedule(tickEvent); 120 } 121} 122 123void 124AtomicSimpleCPU::serialize(ostream &os) 125{ 126 SimObject::State so_state = SimObject::getState(); 127 SERIALIZE_ENUM(so_state); 128 SERIALIZE_SCALAR(locked); 129 BaseSimpleCPU::serialize(os); 130 nameOut(os, csprintf("%s.tickEvent", name())); 131 tickEvent.serialize(os); 132} 133 134void 135AtomicSimpleCPU::unserialize(Checkpoint *cp, const string §ion) 136{ 137 SimObject::State so_state; 138 UNSERIALIZE_ENUM(so_state); 139 UNSERIALIZE_SCALAR(locked); 140 BaseSimpleCPU::unserialize(cp, section); 141 tickEvent.unserialize(cp, csprintf("%s.tickEvent", section)); 142} 143 144void 145AtomicSimpleCPU::resume() 146{ 147 if (_status == Idle || _status == SwitchedOut) 148 return; 149 150 DPRINTF(SimpleCPU, "Resume\n"); 151 assert(system->getMemoryMode() == Enums::atomic); 152 153 changeState(SimObject::Running); 154 if (thread->status() == ThreadContext::Active) { 155 if (!tickEvent.scheduled()) 156 schedule(tickEvent, nextCycle()); 157 } 158 system->totalNumInsts = 0; 159} 160 161void 162AtomicSimpleCPU::switchOut() 163{ 164 assert(_status == Running || _status == Idle); 165 _status = SwitchedOut; 166 167 tickEvent.squash(); 168} 169 170 171void 172AtomicSimpleCPU::takeOverFrom(BaseCPU *oldCPU) 173{ 174 BaseCPU::takeOverFrom(oldCPU); 175 176 assert(!tickEvent.scheduled()); 177 178 // if any of this CPU's ThreadContexts are active, mark the CPU as 179 // running and schedule its tick event. 180 ThreadID size = threadContexts.size(); 181 for (ThreadID i = 0; i < size; ++i) { 182 ThreadContext *tc = threadContexts[i]; 183 if (tc->status() == ThreadContext::Active && _status != Running) { 184 _status = Running; 185 schedule(tickEvent, nextCycle()); 186 break; 187 } 188 } 189 if (_status != Running) { 190 _status = Idle; 191 } 192 assert(threadContexts.size() == 1); 193 ifetch_req.setThreadContext(_cpuId, 0); // Add thread ID if we add MT 194 data_read_req.setThreadContext(_cpuId, 0); // Add thread ID here too 195 data_write_req.setThreadContext(_cpuId, 0); // Add thread ID here too 196} 197 198 199void 200AtomicSimpleCPU::activateContext(ThreadID thread_num, int delay) 201{ 202 DPRINTF(SimpleCPU, "ActivateContext %d (%d cycles)\n", thread_num, delay); 203 204 assert(thread_num == 0); 205 assert(thread); 206 207 assert(_status == Idle); 208 assert(!tickEvent.scheduled()); 209 210 notIdleFraction++; 211 numCycles += tickToCycles(thread->lastActivate - thread->lastSuspend); 212 213 //Make sure ticks are still on multiples of cycles 214 schedule(tickEvent, nextCycle(curTick() + ticks(delay))); 215 _status = Running; 216} 217 218 219void 220AtomicSimpleCPU::suspendContext(ThreadID thread_num) 221{ 222 DPRINTF(SimpleCPU, "SuspendContext %d\n", thread_num); 223 224 assert(thread_num == 0); 225 assert(thread); 226 227 if (_status == Idle) 228 return; 229 230 assert(_status == Running); 231 232 // tick event may not be scheduled if this gets called from inside 233 // an instruction's execution, e.g. "quiesce" 234 if (tickEvent.scheduled()) 235 deschedule(tickEvent); 236 237 notIdleFraction--; 238 _status = Idle; 239} 240 241 242Fault 243AtomicSimpleCPU::readMem(Addr addr, uint8_t * data, 244 unsigned size, unsigned flags) 245{ 246 // use the CPU's statically allocated read request and packet objects 247 Request *req = &data_read_req; 248 249 if (traceData) { 250 traceData->setAddr(addr); 251 } 252 253 //The block size of our peer. 254 unsigned blockSize = dcachePort.peerBlockSize(); 255 //The size of the data we're trying to read. 256 int fullSize = size; 257 258 //The address of the second part of this access if it needs to be split 259 //across a cache line boundary. 260 Addr secondAddr = roundDown(addr + size - 1, blockSize); 261 262 if (secondAddr > addr) 263 size = secondAddr - addr; 264 265 dcache_latency = 0; 266 267 while (1) { 268 req->setVirt(0, addr, size, flags, dataMasterId(), thread->pcState().instAddr()); 269 270 // translate to physical address 271 Fault fault = thread->dtb->translateAtomic(req, tc, BaseTLB::Read); 272 273 // Now do the access. 274 if (fault == NoFault && !req->getFlags().isSet(Request::NO_ACCESS)) { 275 Packet pkt = Packet(req, 276 req->isLLSC() ? MemCmd::LoadLockedReq : MemCmd::ReadReq, 277 Packet::Broadcast); 278 pkt.dataStatic(data); 279 280 if (req->isMmappedIpr()) 281 dcache_latency += TheISA::handleIprRead(thread->getTC(), &pkt); 282 else { 283 if (hasPhysMemPort && pkt.getAddr() == physMemAddr) 284 dcache_latency += physmemPort.sendAtomic(&pkt); 285 else 286 dcache_latency += dcachePort.sendAtomic(&pkt); 287 } 288 dcache_access = true; 289 290 assert(!pkt.isError()); 291 292 if (req->isLLSC()) { 293 TheISA::handleLockedRead(thread, req); 294 } 295 } 296 297 //If there's a fault, return it 298 if (fault != NoFault) { 299 if (req->isPrefetch()) { 300 return NoFault; 301 } else { 302 return fault; 303 } 304 } 305 306 //If we don't need to access a second cache line, stop now. 307 if (secondAddr <= addr) 308 { 309 if (req->isLocked() && fault == NoFault) { 310 assert(!locked); 311 locked = true; 312 } 313 return fault; 314 } 315 316 /* 317 * Set up for accessing the second cache line. 318 */ 319 320 //Move the pointer we're reading into to the correct location. 321 data += size; 322 //Adjust the size to get the remaining bytes. 323 size = addr + fullSize - secondAddr; 324 //And access the right address. 325 addr = secondAddr; 326 } 327} 328 329 330Fault 331AtomicSimpleCPU::writeMem(uint8_t *data, unsigned size, 332 Addr addr, unsigned flags, uint64_t *res) 333{ 334 // use the CPU's statically allocated write request and packet objects 335 Request *req = &data_write_req; 336 337 if (traceData) { 338 traceData->setAddr(addr); 339 } 340 341 //The block size of our peer. 342 unsigned blockSize = dcachePort.peerBlockSize(); 343 //The size of the data we're trying to read. 344 int fullSize = size; 345 346 //The address of the second part of this access if it needs to be split 347 //across a cache line boundary. 348 Addr secondAddr = roundDown(addr + size - 1, blockSize); 349 350 if(secondAddr > addr) 351 size = secondAddr - addr; 352 353 dcache_latency = 0; 354 355 while(1) { 356 req->setVirt(0, addr, size, flags, dataMasterId(), thread->pcState().instAddr()); 357 358 // translate to physical address 359 Fault fault = thread->dtb->translateAtomic(req, tc, BaseTLB::Write); 360 361 // Now do the access. 362 if (fault == NoFault) { 363 MemCmd cmd = MemCmd::WriteReq; // default 364 bool do_access = true; // flag to suppress cache access 365 366 if (req->isLLSC()) { 367 cmd = MemCmd::StoreCondReq; 368 do_access = TheISA::handleLockedWrite(thread, req); 369 } else if (req->isSwap()) { 370 cmd = MemCmd::SwapReq; 371 if (req->isCondSwap()) { 372 assert(res); 373 req->setExtraData(*res); 374 } 375 } 376 377 if (do_access && !req->getFlags().isSet(Request::NO_ACCESS)) { 378 Packet pkt = Packet(req, cmd, Packet::Broadcast); 379 pkt.dataStatic(data); 380 381 if (req->isMmappedIpr()) { 382 dcache_latency += 383 TheISA::handleIprWrite(thread->getTC(), &pkt); 384 } else { 385 if (hasPhysMemPort && pkt.getAddr() == physMemAddr) 386 dcache_latency += physmemPort.sendAtomic(&pkt); 387 else 388 dcache_latency += dcachePort.sendAtomic(&pkt); 389 } 390 dcache_access = true; 391 assert(!pkt.isError()); 392 393 if (req->isSwap()) { 394 assert(res); 395 memcpy(res, pkt.getPtr<uint8_t>(), fullSize); 396 } 397 } 398 399 if (res && !req->isSwap()) { 400 *res = req->getExtraData(); 401 } 402 } 403 404 //If there's a fault or we don't need to access a second cache line, 405 //stop now. 406 if (fault != NoFault || secondAddr <= addr) 407 { 408 if (req->isLocked() && fault == NoFault) { 409 assert(locked); 410 locked = false; 411 } 412 if (fault != NoFault && req->isPrefetch()) { 413 return NoFault; 414 } else { 415 return fault; 416 } 417 } 418 419 /* 420 * Set up for accessing the second cache line. 421 */ 422 423 //Move the pointer we're reading into to the correct location. 424 data += size; 425 //Adjust the size to get the remaining bytes. 426 size = addr + fullSize - secondAddr; 427 //And access the right address. 428 addr = secondAddr; 429 } 430} 431 432 433void 434AtomicSimpleCPU::tick() 435{ 436 DPRINTF(SimpleCPU, "Tick\n"); 437 438 Tick latency = 0; 439 440 for (int i = 0; i < width || locked; ++i) { 441 numCycles++; 442 443 if (!curStaticInst || !curStaticInst->isDelayedCommit()) 444 checkForInterrupts(); 445 446 checkPcEventQueue(); 447 // We must have just got suspended by a PC event 448 if (_status == Idle) 449 return; 450 451 Fault fault = NoFault; 452 453 TheISA::PCState pcState = thread->pcState(); 454 455 bool needToFetch = !isRomMicroPC(pcState.microPC()) && 456 !curMacroStaticInst; 457 if (needToFetch) { 458 setupFetchRequest(&ifetch_req); 459 fault = thread->itb->translateAtomic(&ifetch_req, tc, 460 BaseTLB::Execute); 461 } 462 463 if (fault == NoFault) { 464 Tick icache_latency = 0; 465 bool icache_access = false; 466 dcache_access = false; // assume no dcache access 467 468 if (needToFetch) { 469 // This is commented out because the predecoder would act like 470 // a tiny cache otherwise. It wouldn't be flushed when needed 471 // like the I cache. It should be flushed, and when that works 472 // this code should be uncommented. 473 //Fetch more instruction memory if necessary 474 //if(predecoder.needMoreBytes()) 475 //{ 476 icache_access = true; 477 Packet ifetch_pkt = Packet(&ifetch_req, MemCmd::ReadReq, 478 Packet::Broadcast); 479 ifetch_pkt.dataStatic(&inst); 480 481 if (hasPhysMemPort && ifetch_pkt.getAddr() == physMemAddr) 482 icache_latency = physmemPort.sendAtomic(&ifetch_pkt); 483 else 484 icache_latency = icachePort.sendAtomic(&ifetch_pkt); 485 486 assert(!ifetch_pkt.isError()); 487 488 // ifetch_req is initialized to read the instruction directly 489 // into the CPU object's inst field. 490 //} 491 } 492 493 preExecute(); 494 495 if (curStaticInst) { 496 fault = curStaticInst->execute(this, traceData); 497 498 // keep an instruction count 499 if (fault == NoFault) 500 countInst(); 501 else if (traceData && !DTRACE(ExecFaulting)) { 502 delete traceData; 503 traceData = NULL; 504 } 505 506 postExecute(); 507 } 508 509 // @todo remove me after debugging with legion done 510 if (curStaticInst && (!curStaticInst->isMicroop() || 511 curStaticInst->isFirstMicroop())) 512 instCnt++; 513 514 Tick stall_ticks = 0; 515 if (simulate_inst_stalls && icache_access) 516 stall_ticks += icache_latency; 517 518 if (simulate_data_stalls && dcache_access) 519 stall_ticks += dcache_latency; 520 521 if (stall_ticks) { 522 Tick stall_cycles = stall_ticks / ticks(1); 523 Tick aligned_stall_ticks = ticks(stall_cycles); 524 525 if (aligned_stall_ticks < stall_ticks) 526 aligned_stall_ticks += 1; 527 528 latency += aligned_stall_ticks; 529 } 530 531 } 532 if(fault != NoFault || !stayAtPC) 533 advancePC(fault); 534 } 535 536 // instruction takes at least one cycle 537 if (latency < ticks(1)) 538 latency = ticks(1); 539 540 if (_status != Idle) 541 schedule(tickEvent, curTick() + latency); 542} 543 544 545void 546AtomicSimpleCPU::printAddr(Addr a) 547{ 548 dcachePort.printAddr(a); 549} 550 551 552//////////////////////////////////////////////////////////////////////// 553// 554// AtomicSimpleCPU Simulation Object 555// 556AtomicSimpleCPU * 557AtomicSimpleCPUParams::create() 558{ 559 numThreads = 1; 560 if (!FullSystem && workload.size() != 1) 561 panic("only one workload allowed"); 562 return new AtomicSimpleCPU(this); 563} 564