atomic.cc revision 9448:569d1e8f74e4
1/* 2 * Copyright (c) 2012 ARM Limited 3 * All rights reserved. 4 * 5 * The license below extends only to copyright in the software and shall 6 * not be construed as granting a license to any other intellectual 7 * property including but not limited to intellectual property relating 8 * to a hardware implementation of the functionality of the software 9 * licensed hereunder. You may use the software subject to the license 10 * terms below provided that you ensure that this notice is replicated 11 * unmodified and in its entirety in all distributions of the software, 12 * modified or unmodified, in source code or in binary form. 13 * 14 * Copyright (c) 2002-2005 The Regents of The University of Michigan 15 * All rights reserved. 16 * 17 * Redistribution and use in source and binary forms, with or without 18 * modification, are permitted provided that the following conditions are 19 * met: redistributions of source code must retain the above copyright 20 * notice, this list of conditions and the following disclaimer; 21 * redistributions in binary form must reproduce the above copyright 22 * notice, this list of conditions and the following disclaimer in the 23 * documentation and/or other materials provided with the distribution; 24 * neither the name of the copyright holders nor the names of its 25 * contributors may be used to endorse or promote products derived from 26 * this software without specific prior written permission. 27 * 28 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 29 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 30 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 31 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 32 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 33 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 34 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 35 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 36 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 37 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 38 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 39 * 40 * Authors: Steve Reinhardt 41 */ 42 43#include "arch/locked_mem.hh" 44#include "arch/mmapped_ipr.hh" 45#include "arch/utility.hh" 46#include "base/bigint.hh" 47#include "config/the_isa.hh" 48#include "cpu/simple/atomic.hh" 49#include "cpu/exetrace.hh" 50#include "debug/Drain.hh" 51#include "debug/ExecFaulting.hh" 52#include "debug/SimpleCPU.hh" 53#include "mem/packet.hh" 54#include "mem/packet_access.hh" 55#include "mem/physical.hh" 56#include "params/AtomicSimpleCPU.hh" 57#include "sim/faults.hh" 58#include "sim/system.hh" 59#include "sim/full_system.hh" 60 61using namespace std; 62using namespace TheISA; 63 64AtomicSimpleCPU::TickEvent::TickEvent(AtomicSimpleCPU *c) 65 : Event(CPU_Tick_Pri), cpu(c) 66{ 67} 68 69 70void 71AtomicSimpleCPU::TickEvent::process() 72{ 73 cpu->tick(); 74} 75 76const char * 77AtomicSimpleCPU::TickEvent::description() const 78{ 79 return "AtomicSimpleCPU tick"; 80} 81 82void 83AtomicSimpleCPU::init() 84{ 85 BaseCPU::init(); 86 87 if (!params()->switched_out && 88 system->getMemoryMode() != Enums::atomic) { 89 fatal("The atomic CPU requires the memory system to be in " 90 "'atomic' mode.\n"); 91 } 92 93 // Initialise the ThreadContext's memory proxies 94 tcBase()->initMemProxies(tcBase()); 95 96 if (FullSystem && !params()->switched_out) { 97 ThreadID size = threadContexts.size(); 98 for (ThreadID i = 0; i < size; ++i) { 99 ThreadContext *tc = threadContexts[i]; 100 // initialize CPU, including PC 101 TheISA::initCPU(tc, tc->contextId()); 102 } 103 } 104 105 // Atomic doesn't do MT right now, so contextId == threadId 106 ifetch_req.setThreadContext(_cpuId, 0); // Add thread ID if we add MT 107 data_read_req.setThreadContext(_cpuId, 0); // Add thread ID here too 108 data_write_req.setThreadContext(_cpuId, 0); // Add thread ID here too 109} 110 111AtomicSimpleCPU::AtomicSimpleCPU(AtomicSimpleCPUParams *p) 112 : BaseSimpleCPU(p), tickEvent(this), width(p->width), locked(false), 113 simulate_data_stalls(p->simulate_data_stalls), 114 simulate_inst_stalls(p->simulate_inst_stalls), 115 drain_manager(NULL), 116 icachePort(name() + ".icache_port", this), 117 dcachePort(name() + ".dcache_port", this), 118 fastmem(p->fastmem) 119{ 120 _status = Idle; 121} 122 123 124AtomicSimpleCPU::~AtomicSimpleCPU() 125{ 126 if (tickEvent.scheduled()) { 127 deschedule(tickEvent); 128 } 129} 130 131unsigned int 132AtomicSimpleCPU::drain(DrainManager *dm) 133{ 134 assert(!drain_manager); 135 if (switchedOut()) 136 return 0; 137 138 if (!isDrained()) { 139 DPRINTF(Drain, "Requesting drain: %s\n", pcState()); 140 drain_manager = dm; 141 return 1; 142 } else { 143 if (tickEvent.scheduled()) 144 deschedule(tickEvent); 145 146 DPRINTF(Drain, "Not executing microcode, no need to drain.\n"); 147 return 0; 148 } 149} 150 151void 152AtomicSimpleCPU::drainResume() 153{ 154 assert(!tickEvent.scheduled()); 155 assert(!drain_manager); 156 if (switchedOut()) 157 return; 158 159 DPRINTF(SimpleCPU, "Resume\n"); 160 if (system->getMemoryMode() != Enums::atomic) { 161 fatal("The atomic CPU requires the memory system to be in " 162 "'atomic' mode.\n"); 163 } 164 165 assert(!threadContexts.empty()); 166 if (threadContexts.size() > 1) 167 fatal("The atomic CPU only supports one thread.\n"); 168 169 if (thread->status() == ThreadContext::Active) { 170 schedule(tickEvent, nextCycle()); 171 _status = BaseSimpleCPU::Running; 172 } else { 173 _status = BaseSimpleCPU::Idle; 174 } 175 176 system->totalNumInsts = 0; 177} 178 179bool 180AtomicSimpleCPU::tryCompleteDrain() 181{ 182 if (!drain_manager) 183 return false; 184 185 DPRINTF(Drain, "tryCompleteDrain: %s\n", pcState()); 186 if (!isDrained()) 187 return false; 188 189 DPRINTF(Drain, "CPU done draining, processing drain event\n"); 190 drain_manager->signalDrainDone(); 191 drain_manager = NULL; 192 193 return true; 194} 195 196 197void 198AtomicSimpleCPU::switchOut() 199{ 200 BaseSimpleCPU::switchOut(); 201 202 assert(!tickEvent.scheduled()); 203 assert(_status == BaseSimpleCPU::Running || _status == Idle); 204 assert(isDrained()); 205} 206 207 208void 209AtomicSimpleCPU::takeOverFrom(BaseCPU *oldCPU) 210{ 211 BaseSimpleCPU::takeOverFrom(oldCPU); 212 213 // The tick event should have been descheduled by drain() 214 assert(!tickEvent.scheduled()); 215 216 ifetch_req.setThreadContext(_cpuId, 0); // Add thread ID if we add MT 217 data_read_req.setThreadContext(_cpuId, 0); // Add thread ID here too 218 data_write_req.setThreadContext(_cpuId, 0); // Add thread ID here too 219} 220 221 222void 223AtomicSimpleCPU::activateContext(ThreadID thread_num, Cycles delay) 224{ 225 DPRINTF(SimpleCPU, "ActivateContext %d (%d cycles)\n", thread_num, delay); 226 227 assert(thread_num == 0); 228 assert(thread); 229 230 assert(_status == Idle); 231 assert(!tickEvent.scheduled()); 232 233 notIdleFraction++; 234 numCycles += ticksToCycles(thread->lastActivate - thread->lastSuspend); 235 236 //Make sure ticks are still on multiples of cycles 237 schedule(tickEvent, clockEdge(delay)); 238 _status = BaseSimpleCPU::Running; 239} 240 241 242void 243AtomicSimpleCPU::suspendContext(ThreadID thread_num) 244{ 245 DPRINTF(SimpleCPU, "SuspendContext %d\n", thread_num); 246 247 assert(thread_num == 0); 248 assert(thread); 249 250 if (_status == Idle) 251 return; 252 253 assert(_status == BaseSimpleCPU::Running); 254 255 // tick event may not be scheduled if this gets called from inside 256 // an instruction's execution, e.g. "quiesce" 257 if (tickEvent.scheduled()) 258 deschedule(tickEvent); 259 260 notIdleFraction--; 261 _status = Idle; 262} 263 264 265Fault 266AtomicSimpleCPU::readMem(Addr addr, uint8_t * data, 267 unsigned size, unsigned flags) 268{ 269 // use the CPU's statically allocated read request and packet objects 270 Request *req = &data_read_req; 271 272 if (traceData) { 273 traceData->setAddr(addr); 274 } 275 276 //The block size of our peer. 277 unsigned blockSize = dcachePort.peerBlockSize(); 278 //The size of the data we're trying to read. 279 int fullSize = size; 280 281 //The address of the second part of this access if it needs to be split 282 //across a cache line boundary. 283 Addr secondAddr = roundDown(addr + size - 1, blockSize); 284 285 if (secondAddr > addr) 286 size = secondAddr - addr; 287 288 dcache_latency = 0; 289 290 while (1) { 291 req->setVirt(0, addr, size, flags, dataMasterId(), thread->pcState().instAddr()); 292 293 // translate to physical address 294 Fault fault = thread->dtb->translateAtomic(req, tc, BaseTLB::Read); 295 296 // Now do the access. 297 if (fault == NoFault && !req->getFlags().isSet(Request::NO_ACCESS)) { 298 Packet pkt = Packet(req, 299 req->isLLSC() ? MemCmd::LoadLockedReq : 300 MemCmd::ReadReq); 301 pkt.dataStatic(data); 302 303 if (req->isMmappedIpr()) 304 dcache_latency += TheISA::handleIprRead(thread->getTC(), &pkt); 305 else { 306 if (fastmem && system->isMemAddr(pkt.getAddr())) 307 system->getPhysMem().access(&pkt); 308 else 309 dcache_latency += dcachePort.sendAtomic(&pkt); 310 } 311 dcache_access = true; 312 313 assert(!pkt.isError()); 314 315 if (req->isLLSC()) { 316 TheISA::handleLockedRead(thread, req); 317 } 318 } 319 320 //If there's a fault, return it 321 if (fault != NoFault) { 322 if (req->isPrefetch()) { 323 return NoFault; 324 } else { 325 return fault; 326 } 327 } 328 329 //If we don't need to access a second cache line, stop now. 330 if (secondAddr <= addr) 331 { 332 if (req->isLocked() && fault == NoFault) { 333 assert(!locked); 334 locked = true; 335 } 336 return fault; 337 } 338 339 /* 340 * Set up for accessing the second cache line. 341 */ 342 343 //Move the pointer we're reading into to the correct location. 344 data += size; 345 //Adjust the size to get the remaining bytes. 346 size = addr + fullSize - secondAddr; 347 //And access the right address. 348 addr = secondAddr; 349 } 350} 351 352 353Fault 354AtomicSimpleCPU::writeMem(uint8_t *data, unsigned size, 355 Addr addr, unsigned flags, uint64_t *res) 356{ 357 // use the CPU's statically allocated write request and packet objects 358 Request *req = &data_write_req; 359 360 if (traceData) { 361 traceData->setAddr(addr); 362 } 363 364 //The block size of our peer. 365 unsigned blockSize = dcachePort.peerBlockSize(); 366 //The size of the data we're trying to read. 367 int fullSize = size; 368 369 //The address of the second part of this access if it needs to be split 370 //across a cache line boundary. 371 Addr secondAddr = roundDown(addr + size - 1, blockSize); 372 373 if(secondAddr > addr) 374 size = secondAddr - addr; 375 376 dcache_latency = 0; 377 378 while(1) { 379 req->setVirt(0, addr, size, flags, dataMasterId(), thread->pcState().instAddr()); 380 381 // translate to physical address 382 Fault fault = thread->dtb->translateAtomic(req, tc, BaseTLB::Write); 383 384 // Now do the access. 385 if (fault == NoFault) { 386 MemCmd cmd = MemCmd::WriteReq; // default 387 bool do_access = true; // flag to suppress cache access 388 389 if (req->isLLSC()) { 390 cmd = MemCmd::StoreCondReq; 391 do_access = TheISA::handleLockedWrite(thread, req); 392 } else if (req->isSwap()) { 393 cmd = MemCmd::SwapReq; 394 if (req->isCondSwap()) { 395 assert(res); 396 req->setExtraData(*res); 397 } 398 } 399 400 if (do_access && !req->getFlags().isSet(Request::NO_ACCESS)) { 401 Packet pkt = Packet(req, cmd); 402 pkt.dataStatic(data); 403 404 if (req->isMmappedIpr()) { 405 dcache_latency += 406 TheISA::handleIprWrite(thread->getTC(), &pkt); 407 } else { 408 if (fastmem && system->isMemAddr(pkt.getAddr())) 409 system->getPhysMem().access(&pkt); 410 else 411 dcache_latency += dcachePort.sendAtomic(&pkt); 412 } 413 dcache_access = true; 414 assert(!pkt.isError()); 415 416 if (req->isSwap()) { 417 assert(res); 418 memcpy(res, pkt.getPtr<uint8_t>(), fullSize); 419 } 420 } 421 422 if (res && !req->isSwap()) { 423 *res = req->getExtraData(); 424 } 425 } 426 427 //If there's a fault or we don't need to access a second cache line, 428 //stop now. 429 if (fault != NoFault || secondAddr <= addr) 430 { 431 if (req->isLocked() && fault == NoFault) { 432 assert(locked); 433 locked = false; 434 } 435 if (fault != NoFault && req->isPrefetch()) { 436 return NoFault; 437 } else { 438 return fault; 439 } 440 } 441 442 /* 443 * Set up for accessing the second cache line. 444 */ 445 446 //Move the pointer we're reading into to the correct location. 447 data += size; 448 //Adjust the size to get the remaining bytes. 449 size = addr + fullSize - secondAddr; 450 //And access the right address. 451 addr = secondAddr; 452 } 453} 454 455 456void 457AtomicSimpleCPU::tick() 458{ 459 DPRINTF(SimpleCPU, "Tick\n"); 460 461 Tick latency = 0; 462 463 for (int i = 0; i < width || locked; ++i) { 464 numCycles++; 465 466 if (!curStaticInst || !curStaticInst->isDelayedCommit()) 467 checkForInterrupts(); 468 469 checkPcEventQueue(); 470 // We must have just got suspended by a PC event 471 if (_status == Idle) { 472 tryCompleteDrain(); 473 return; 474 } 475 476 Fault fault = NoFault; 477 478 TheISA::PCState pcState = thread->pcState(); 479 480 bool needToFetch = !isRomMicroPC(pcState.microPC()) && 481 !curMacroStaticInst; 482 if (needToFetch) { 483 setupFetchRequest(&ifetch_req); 484 fault = thread->itb->translateAtomic(&ifetch_req, tc, 485 BaseTLB::Execute); 486 } 487 488 if (fault == NoFault) { 489 Tick icache_latency = 0; 490 bool icache_access = false; 491 dcache_access = false; // assume no dcache access 492 493 if (needToFetch) { 494 // This is commented out because the decoder would act like 495 // a tiny cache otherwise. It wouldn't be flushed when needed 496 // like the I cache. It should be flushed, and when that works 497 // this code should be uncommented. 498 //Fetch more instruction memory if necessary 499 //if(decoder.needMoreBytes()) 500 //{ 501 icache_access = true; 502 Packet ifetch_pkt = Packet(&ifetch_req, MemCmd::ReadReq); 503 ifetch_pkt.dataStatic(&inst); 504 505 if (fastmem && system->isMemAddr(ifetch_pkt.getAddr())) 506 system->getPhysMem().access(&ifetch_pkt); 507 else 508 icache_latency = icachePort.sendAtomic(&ifetch_pkt); 509 510 assert(!ifetch_pkt.isError()); 511 512 // ifetch_req is initialized to read the instruction directly 513 // into the CPU object's inst field. 514 //} 515 } 516 517 preExecute(); 518 519 if (curStaticInst) { 520 fault = curStaticInst->execute(this, traceData); 521 522 // keep an instruction count 523 if (fault == NoFault) 524 countInst(); 525 else if (traceData && !DTRACE(ExecFaulting)) { 526 delete traceData; 527 traceData = NULL; 528 } 529 530 postExecute(); 531 } 532 533 // @todo remove me after debugging with legion done 534 if (curStaticInst && (!curStaticInst->isMicroop() || 535 curStaticInst->isFirstMicroop())) 536 instCnt++; 537 538 Tick stall_ticks = 0; 539 if (simulate_inst_stalls && icache_access) 540 stall_ticks += icache_latency; 541 542 if (simulate_data_stalls && dcache_access) 543 stall_ticks += dcache_latency; 544 545 if (stall_ticks) { 546 // the atomic cpu does its accounting in ticks, so 547 // keep counting in ticks but round to the clock 548 // period 549 latency += divCeil(stall_ticks, clockPeriod()) * 550 clockPeriod(); 551 } 552 553 } 554 if(fault != NoFault || !stayAtPC) 555 advancePC(fault); 556 } 557 558 if (tryCompleteDrain()) 559 return; 560 561 // instruction takes at least one cycle 562 if (latency < clockPeriod()) 563 latency = clockPeriod(); 564 565 if (_status != Idle) 566 schedule(tickEvent, curTick() + latency); 567} 568 569 570void 571AtomicSimpleCPU::printAddr(Addr a) 572{ 573 dcachePort.printAddr(a); 574} 575 576 577//////////////////////////////////////////////////////////////////////// 578// 579// AtomicSimpleCPU Simulation Object 580// 581AtomicSimpleCPU * 582AtomicSimpleCPUParams::create() 583{ 584 numThreads = 1; 585 if (!FullSystem && workload.size() != 1) 586 panic("only one workload allowed"); 587 return new AtomicSimpleCPU(this); 588} 589