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