atomic.cc revision 13954:2f400a5f2627
112771Sqtt2@cornell.edu/* 212771Sqtt2@cornell.edu * Copyright 2014 Google, Inc. 312771Sqtt2@cornell.edu * Copyright (c) 2012-2013,2015,2017-2018 ARM Limited 412771Sqtt2@cornell.edu * All rights reserved. 512771Sqtt2@cornell.edu * 612771Sqtt2@cornell.edu * The license below extends only to copyright in the software and shall 712771Sqtt2@cornell.edu * not be construed as granting a license to any other intellectual 812771Sqtt2@cornell.edu * property including but not limited to intellectual property relating 912771Sqtt2@cornell.edu * to a hardware implementation of the functionality of the software 1012771Sqtt2@cornell.edu * licensed hereunder. You may use the software subject to the license 1112771Sqtt2@cornell.edu * terms below provided that you ensure that this notice is replicated 1212771Sqtt2@cornell.edu * unmodified and in its entirety in all distributions of the software, 1312771Sqtt2@cornell.edu * modified or unmodified, in source code or in binary form. 1412771Sqtt2@cornell.edu * 1512771Sqtt2@cornell.edu * Copyright (c) 2002-2005 The Regents of The University of Michigan 1612771Sqtt2@cornell.edu * All rights reserved. 1712771Sqtt2@cornell.edu * 1812771Sqtt2@cornell.edu * Redistribution and use in source and binary forms, with or without 1912771Sqtt2@cornell.edu * modification, are permitted provided that the following conditions are 2012771Sqtt2@cornell.edu * met: redistributions of source code must retain the above copyright 2112771Sqtt2@cornell.edu * notice, this list of conditions and the following disclaimer; 2212771Sqtt2@cornell.edu * redistributions in binary form must reproduce the above copyright 2312771Sqtt2@cornell.edu * notice, this list of conditions and the following disclaimer in the 2412771Sqtt2@cornell.edu * documentation and/or other materials provided with the distribution; 2512771Sqtt2@cornell.edu * neither the name of the copyright holders nor the names of its 2612771Sqtt2@cornell.edu * contributors may be used to endorse or promote products derived from 2712771Sqtt2@cornell.edu * this software without specific prior written permission. 2812771Sqtt2@cornell.edu * 2912771Sqtt2@cornell.edu * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 3012771Sqtt2@cornell.edu * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 3112771Sqtt2@cornell.edu * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 3212771Sqtt2@cornell.edu * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 3312771Sqtt2@cornell.edu * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 3412771Sqtt2@cornell.edu * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 3512771Sqtt2@cornell.edu * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 3612771Sqtt2@cornell.edu * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 3712771Sqtt2@cornell.edu * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 3812771Sqtt2@cornell.edu * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 3912771Sqtt2@cornell.edu * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 4012771Sqtt2@cornell.edu * 4112771Sqtt2@cornell.edu * Authors: Steve Reinhardt 4212771Sqtt2@cornell.edu */ 4312771Sqtt2@cornell.edu 4412771Sqtt2@cornell.edu#include "cpu/simple/atomic.hh" 4512771Sqtt2@cornell.edu 4612771Sqtt2@cornell.edu#include "arch/locked_mem.hh" 4712771Sqtt2@cornell.edu#include "arch/mmapped_ipr.hh" 4812771Sqtt2@cornell.edu#include "arch/utility.hh" 4912771Sqtt2@cornell.edu#include "base/output.hh" 5012771Sqtt2@cornell.edu#include "config/the_isa.hh" 5112771Sqtt2@cornell.edu#include "cpu/exetrace.hh" 5212771Sqtt2@cornell.edu#include "cpu/utils.hh" 5312771Sqtt2@cornell.edu#include "debug/Drain.hh" 5412771Sqtt2@cornell.edu#include "debug/ExecFaulting.hh" 5512771Sqtt2@cornell.edu#include "debug/SimpleCPU.hh" 5612771Sqtt2@cornell.edu#include "mem/packet.hh" 5712771Sqtt2@cornell.edu#include "mem/packet_access.hh" 5812771Sqtt2@cornell.edu#include "mem/physical.hh" 5912771Sqtt2@cornell.edu#include "params/AtomicSimpleCPU.hh" 6012771Sqtt2@cornell.edu#include "sim/faults.hh" 6112771Sqtt2@cornell.edu#include "sim/full_system.hh" 6212771Sqtt2@cornell.edu#include "sim/system.hh" 6312771Sqtt2@cornell.edu 6412771Sqtt2@cornell.eduusing namespace std; 6512771Sqtt2@cornell.eduusing namespace TheISA; 6612771Sqtt2@cornell.edu 6712771Sqtt2@cornell.eduvoid 6812771Sqtt2@cornell.eduAtomicSimpleCPU::init() 6912771Sqtt2@cornell.edu{ 7012771Sqtt2@cornell.edu BaseSimpleCPU::init(); 7112771Sqtt2@cornell.edu 7212771Sqtt2@cornell.edu int cid = threadContexts[0]->contextId(); 7312771Sqtt2@cornell.edu ifetch_req->setContext(cid); 7412771Sqtt2@cornell.edu data_read_req->setContext(cid); 7512771Sqtt2@cornell.edu data_write_req->setContext(cid); 7612771Sqtt2@cornell.edu data_amo_req->setContext(cid); 7712771Sqtt2@cornell.edu} 7812771Sqtt2@cornell.edu 7912771Sqtt2@cornell.eduAtomicSimpleCPU::AtomicSimpleCPU(AtomicSimpleCPUParams *p) 8012771Sqtt2@cornell.edu : BaseSimpleCPU(p), 8112771Sqtt2@cornell.edu tickEvent([this]{ tick(); }, "AtomicSimpleCPU tick", 8212771Sqtt2@cornell.edu false, Event::CPU_Tick_Pri), 8312771Sqtt2@cornell.edu width(p->width), locked(false), 8412771Sqtt2@cornell.edu simulate_data_stalls(p->simulate_data_stalls), 8512771Sqtt2@cornell.edu simulate_inst_stalls(p->simulate_inst_stalls), 8612771Sqtt2@cornell.edu icachePort(name() + ".icache_port", this), 8712771Sqtt2@cornell.edu dcachePort(name() + ".dcache_port", this), 8812771Sqtt2@cornell.edu dcache_access(false), dcache_latency(0), 8912771Sqtt2@cornell.edu ppCommit(nullptr) 9012771Sqtt2@cornell.edu{ 9112771Sqtt2@cornell.edu _status = Idle; 9212771Sqtt2@cornell.edu ifetch_req = std::make_shared<Request>(); 9312771Sqtt2@cornell.edu data_read_req = std::make_shared<Request>(); 9412771Sqtt2@cornell.edu data_write_req = std::make_shared<Request>(); 9512771Sqtt2@cornell.edu data_amo_req = std::make_shared<Request>(); 9612771Sqtt2@cornell.edu} 9712771Sqtt2@cornell.edu 9812771Sqtt2@cornell.edu 9912771Sqtt2@cornell.eduAtomicSimpleCPU::~AtomicSimpleCPU() 10012771Sqtt2@cornell.edu{ 10112771Sqtt2@cornell.edu if (tickEvent.scheduled()) { 10212771Sqtt2@cornell.edu deschedule(tickEvent); 10312771Sqtt2@cornell.edu } 10412771Sqtt2@cornell.edu} 10512771Sqtt2@cornell.edu 10612771Sqtt2@cornell.eduDrainState 10712771Sqtt2@cornell.eduAtomicSimpleCPU::drain() 10812771Sqtt2@cornell.edu{ 10912771Sqtt2@cornell.edu // Deschedule any power gating event (if any) 11012771Sqtt2@cornell.edu deschedulePowerGatingEvent(); 11112771Sqtt2@cornell.edu 11212771Sqtt2@cornell.edu if (switchedOut()) 11312771Sqtt2@cornell.edu return DrainState::Drained; 11412771Sqtt2@cornell.edu 11512771Sqtt2@cornell.edu if (!isDrained()) { 11612771Sqtt2@cornell.edu DPRINTF(Drain, "Requesting drain.\n"); 11712771Sqtt2@cornell.edu return DrainState::Draining; 11812771Sqtt2@cornell.edu } else { 11912771Sqtt2@cornell.edu if (tickEvent.scheduled()) 12012771Sqtt2@cornell.edu deschedule(tickEvent); 12112771Sqtt2@cornell.edu 12212771Sqtt2@cornell.edu activeThreads.clear(); 12312771Sqtt2@cornell.edu DPRINTF(Drain, "Not executing microcode, no need to drain.\n"); 12412771Sqtt2@cornell.edu return DrainState::Drained; 12512771Sqtt2@cornell.edu } 12612771Sqtt2@cornell.edu} 12712771Sqtt2@cornell.edu 12812771Sqtt2@cornell.eduvoid 12912771Sqtt2@cornell.eduAtomicSimpleCPU::threadSnoop(PacketPtr pkt, ThreadID sender) 13012771Sqtt2@cornell.edu{ 13112771Sqtt2@cornell.edu DPRINTF(SimpleCPU, "received snoop pkt for addr:%#x %s\n", pkt->getAddr(), 13212771Sqtt2@cornell.edu pkt->cmdString()); 13312771Sqtt2@cornell.edu 13412771Sqtt2@cornell.edu for (ThreadID tid = 0; tid < numThreads; tid++) { 13512771Sqtt2@cornell.edu if (tid != sender) { 13612771Sqtt2@cornell.edu if (getCpuAddrMonitor(tid)->doMonitor(pkt)) { 13712771Sqtt2@cornell.edu wakeup(tid); 13812771Sqtt2@cornell.edu } 13912771Sqtt2@cornell.edu 14012771Sqtt2@cornell.edu TheISA::handleLockedSnoop(threadInfo[tid]->thread, 14112771Sqtt2@cornell.edu pkt, dcachePort.cacheBlockMask); 14212771Sqtt2@cornell.edu } 14312771Sqtt2@cornell.edu } 14412771Sqtt2@cornell.edu} 14512771Sqtt2@cornell.edu 14612771Sqtt2@cornell.eduvoid 14712771Sqtt2@cornell.eduAtomicSimpleCPU::drainResume() 14812771Sqtt2@cornell.edu{ 14912771Sqtt2@cornell.edu assert(!tickEvent.scheduled()); 15012771Sqtt2@cornell.edu if (switchedOut()) 15112771Sqtt2@cornell.edu return; 15212771Sqtt2@cornell.edu 15312771Sqtt2@cornell.edu DPRINTF(SimpleCPU, "Resume\n"); 15412771Sqtt2@cornell.edu verifyMemoryMode(); 15512771Sqtt2@cornell.edu 15612771Sqtt2@cornell.edu assert(!threadContexts.empty()); 15712771Sqtt2@cornell.edu 15812771Sqtt2@cornell.edu _status = BaseSimpleCPU::Idle; 15912771Sqtt2@cornell.edu 16012771Sqtt2@cornell.edu for (ThreadID tid = 0; tid < numThreads; tid++) { 16112771Sqtt2@cornell.edu if (threadInfo[tid]->thread->status() == ThreadContext::Active) { 16212771Sqtt2@cornell.edu threadInfo[tid]->notIdleFraction = 1; 16312771Sqtt2@cornell.edu activeThreads.push_back(tid); 16412771Sqtt2@cornell.edu _status = BaseSimpleCPU::Running; 16512771Sqtt2@cornell.edu 16612771Sqtt2@cornell.edu // Tick if any threads active 16712771Sqtt2@cornell.edu if (!tickEvent.scheduled()) { 16812771Sqtt2@cornell.edu schedule(tickEvent, nextCycle()); 16912771Sqtt2@cornell.edu } 17012771Sqtt2@cornell.edu } else { 17112771Sqtt2@cornell.edu threadInfo[tid]->notIdleFraction = 0; 17212771Sqtt2@cornell.edu } 17312771Sqtt2@cornell.edu } 17412771Sqtt2@cornell.edu 17512771Sqtt2@cornell.edu // Reschedule any power gating event (if any) 17612771Sqtt2@cornell.edu schedulePowerGatingEvent(); 17712771Sqtt2@cornell.edu} 17812771Sqtt2@cornell.edu 17912771Sqtt2@cornell.edubool 18012771Sqtt2@cornell.eduAtomicSimpleCPU::tryCompleteDrain() 18112771Sqtt2@cornell.edu{ 18212771Sqtt2@cornell.edu if (drainState() != DrainState::Draining) 18312771Sqtt2@cornell.edu return false; 18412771Sqtt2@cornell.edu 18512771Sqtt2@cornell.edu DPRINTF(Drain, "tryCompleteDrain.\n"); 18612771Sqtt2@cornell.edu if (!isDrained()) 18712771Sqtt2@cornell.edu return false; 18812771Sqtt2@cornell.edu 18912771Sqtt2@cornell.edu DPRINTF(Drain, "CPU done draining, processing drain event\n"); 19012771Sqtt2@cornell.edu signalDrainDone(); 19112771Sqtt2@cornell.edu 19212771Sqtt2@cornell.edu return true; 19312771Sqtt2@cornell.edu} 19412771Sqtt2@cornell.edu 195 196void 197AtomicSimpleCPU::switchOut() 198{ 199 BaseSimpleCPU::switchOut(); 200 201 assert(!tickEvent.scheduled()); 202 assert(_status == BaseSimpleCPU::Running || _status == Idle); 203 assert(isDrained()); 204} 205 206 207void 208AtomicSimpleCPU::takeOverFrom(BaseCPU *oldCPU) 209{ 210 BaseSimpleCPU::takeOverFrom(oldCPU); 211 212 // The tick event should have been descheduled by drain() 213 assert(!tickEvent.scheduled()); 214} 215 216void 217AtomicSimpleCPU::verifyMemoryMode() const 218{ 219 if (!system->isAtomicMode()) { 220 fatal("The atomic CPU requires the memory system to be in " 221 "'atomic' mode.\n"); 222 } 223} 224 225void 226AtomicSimpleCPU::activateContext(ThreadID thread_num) 227{ 228 DPRINTF(SimpleCPU, "ActivateContext %d\n", thread_num); 229 230 assert(thread_num < numThreads); 231 232 threadInfo[thread_num]->notIdleFraction = 1; 233 Cycles delta = ticksToCycles(threadInfo[thread_num]->thread->lastActivate - 234 threadInfo[thread_num]->thread->lastSuspend); 235 numCycles += delta; 236 237 if (!tickEvent.scheduled()) { 238 //Make sure ticks are still on multiples of cycles 239 schedule(tickEvent, clockEdge(Cycles(0))); 240 } 241 _status = BaseSimpleCPU::Running; 242 if (std::find(activeThreads.begin(), activeThreads.end(), thread_num) 243 == activeThreads.end()) { 244 activeThreads.push_back(thread_num); 245 } 246 247 BaseCPU::activateContext(thread_num); 248} 249 250 251void 252AtomicSimpleCPU::suspendContext(ThreadID thread_num) 253{ 254 DPRINTF(SimpleCPU, "SuspendContext %d\n", thread_num); 255 256 assert(thread_num < numThreads); 257 activeThreads.remove(thread_num); 258 259 if (_status == Idle) 260 return; 261 262 assert(_status == BaseSimpleCPU::Running); 263 264 threadInfo[thread_num]->notIdleFraction = 0; 265 266 if (activeThreads.empty()) { 267 _status = Idle; 268 269 if (tickEvent.scheduled()) { 270 deschedule(tickEvent); 271 } 272 } 273 274 BaseCPU::suspendContext(thread_num); 275} 276 277Tick 278AtomicSimpleCPU::sendPacket(MasterPort &port, const PacketPtr &pkt) 279{ 280 return port.sendAtomic(pkt); 281} 282 283Tick 284AtomicSimpleCPU::AtomicCPUDPort::recvAtomicSnoop(PacketPtr pkt) 285{ 286 DPRINTF(SimpleCPU, "received snoop pkt for addr:%#x %s\n", pkt->getAddr(), 287 pkt->cmdString()); 288 289 // X86 ISA: Snooping an invalidation for monitor/mwait 290 AtomicSimpleCPU *cpu = (AtomicSimpleCPU *)(&owner); 291 292 for (ThreadID tid = 0; tid < cpu->numThreads; tid++) { 293 if (cpu->getCpuAddrMonitor(tid)->doMonitor(pkt)) { 294 cpu->wakeup(tid); 295 } 296 } 297 298 // if snoop invalidates, release any associated locks 299 // When run without caches, Invalidation packets will not be received 300 // hence we must check if the incoming packets are writes and wakeup 301 // the processor accordingly 302 if (pkt->isInvalidate() || pkt->isWrite()) { 303 DPRINTF(SimpleCPU, "received invalidation for addr:%#x\n", 304 pkt->getAddr()); 305 for (auto &t_info : cpu->threadInfo) { 306 TheISA::handleLockedSnoop(t_info->thread, pkt, cacheBlockMask); 307 } 308 } 309 310 return 0; 311} 312 313void 314AtomicSimpleCPU::AtomicCPUDPort::recvFunctionalSnoop(PacketPtr pkt) 315{ 316 DPRINTF(SimpleCPU, "received snoop pkt for addr:%#x %s\n", pkt->getAddr(), 317 pkt->cmdString()); 318 319 // X86 ISA: Snooping an invalidation for monitor/mwait 320 AtomicSimpleCPU *cpu = (AtomicSimpleCPU *)(&owner); 321 for (ThreadID tid = 0; tid < cpu->numThreads; tid++) { 322 if (cpu->getCpuAddrMonitor(tid)->doMonitor(pkt)) { 323 cpu->wakeup(tid); 324 } 325 } 326 327 // if snoop invalidates, release any associated locks 328 if (pkt->isInvalidate()) { 329 DPRINTF(SimpleCPU, "received invalidation for addr:%#x\n", 330 pkt->getAddr()); 331 for (auto &t_info : cpu->threadInfo) { 332 TheISA::handleLockedSnoop(t_info->thread, pkt, cacheBlockMask); 333 } 334 } 335} 336 337bool 338AtomicSimpleCPU::genMemFragmentRequest(const RequestPtr& req, Addr frag_addr, 339 int size, Request::Flags flags, 340 const std::vector<bool>& byte_enable, 341 int& frag_size, int& size_left) const 342{ 343 bool predicate = true; 344 Addr inst_addr = threadInfo[curThread]->thread->pcState().instAddr(); 345 346 frag_size = std::min( 347 cacheLineSize() - addrBlockOffset(frag_addr, cacheLineSize()), 348 (Addr) size_left); 349 size_left -= frag_size; 350 351 if (!byte_enable.empty()) { 352 // Set up byte-enable mask for the current fragment 353 auto it_start = byte_enable.begin() + (size - (frag_size + size_left)); 354 auto it_end = byte_enable.begin() + (size - size_left); 355 if (isAnyActiveElement(it_start, it_end)) { 356 req->setVirt(0, frag_addr, frag_size, flags, dataMasterId(), 357 inst_addr); 358 req->setByteEnable(std::vector<bool>(it_start, it_end)); 359 } else { 360 predicate = false; 361 } 362 } else { 363 req->setVirt(0, frag_addr, frag_size, flags, dataMasterId(), 364 inst_addr); 365 } 366 367 return predicate; 368} 369 370Fault 371AtomicSimpleCPU::readMem(Addr addr, uint8_t * data, unsigned size, 372 Request::Flags flags, 373 const std::vector<bool>& byteEnable) 374{ 375 SimpleExecContext& t_info = *threadInfo[curThread]; 376 SimpleThread* thread = t_info.thread; 377 378 // use the CPU's statically allocated read request and packet objects 379 const RequestPtr &req = data_read_req; 380 381 if (traceData) 382 traceData->setMem(addr, size, flags); 383 384 dcache_latency = 0; 385 386 req->taskId(taskId()); 387 388 Addr frag_addr = addr; 389 int frag_size = 0; 390 int size_left = size; 391 bool predicate; 392 Fault fault = NoFault; 393 394 while (1) { 395 predicate = genMemFragmentRequest(req, frag_addr, size, flags, 396 byteEnable, frag_size, size_left); 397 398 // translate to physical address 399 if (predicate) { 400 fault = thread->dtb->translateAtomic(req, thread->getTC(), 401 BaseTLB::Read); 402 } 403 404 // Now do the access. 405 if (predicate && fault == NoFault && 406 !req->getFlags().isSet(Request::NO_ACCESS)) { 407 Packet pkt(req, Packet::makeReadCmd(req)); 408 pkt.dataStatic(data); 409 410 if (req->isMmappedIpr()) { 411 dcache_latency += TheISA::handleIprRead(thread->getTC(), &pkt); 412 } else { 413 dcache_latency += sendPacket(dcachePort, &pkt); 414 } 415 dcache_access = true; 416 417 assert(!pkt.isError()); 418 419 if (req->isLLSC()) { 420 TheISA::handleLockedRead(thread, req); 421 } 422 } 423 424 //If there's a fault, return it 425 if (fault != NoFault) { 426 if (req->isPrefetch()) { 427 return NoFault; 428 } else { 429 return fault; 430 } 431 } 432 433 // If we don't need to access further cache lines, stop now. 434 if (size_left == 0) { 435 if (req->isLockedRMW() && fault == NoFault) { 436 assert(!locked); 437 locked = true; 438 } 439 return fault; 440 } 441 442 /* 443 * Set up for accessing the next cache line. 444 */ 445 frag_addr += frag_size; 446 447 //Move the pointer we're reading into to the correct location. 448 data += frag_size; 449 } 450} 451 452Fault 453AtomicSimpleCPU::writeMem(uint8_t *data, unsigned size, Addr addr, 454 Request::Flags flags, uint64_t *res, 455 const std::vector<bool>& byteEnable) 456{ 457 SimpleExecContext& t_info = *threadInfo[curThread]; 458 SimpleThread* thread = t_info.thread; 459 static uint8_t zero_array[64] = {}; 460 461 if (data == NULL) { 462 assert(size <= 64); 463 assert(flags & Request::STORE_NO_DATA); 464 // This must be a cache block cleaning request 465 data = zero_array; 466 } 467 468 // use the CPU's statically allocated write request and packet objects 469 const RequestPtr &req = data_write_req; 470 471 if (traceData) 472 traceData->setMem(addr, size, flags); 473 474 dcache_latency = 0; 475 476 req->taskId(taskId()); 477 478 Addr frag_addr = addr; 479 int frag_size = 0; 480 int size_left = size; 481 int curr_frag_id = 0; 482 bool predicate; 483 Fault fault = NoFault; 484 485 while (1) { 486 predicate = genMemFragmentRequest(req, frag_addr, size, flags, 487 byteEnable, frag_size, size_left); 488 489 // translate to physical address 490 if (predicate) 491 fault = thread->dtb->translateAtomic(req, thread->getTC(), 492 BaseTLB::Write); 493 494 // Now do the access. 495 if (predicate && fault == NoFault) { 496 bool do_access = true; // flag to suppress cache access 497 498 if (req->isLLSC()) { 499 assert(curr_frag_id == 0); 500 do_access = 501 TheISA::handleLockedWrite(thread, req, 502 dcachePort.cacheBlockMask); 503 } else if (req->isSwap()) { 504 assert(curr_frag_id == 0); 505 if (req->isCondSwap()) { 506 assert(res); 507 req->setExtraData(*res); 508 } 509 } 510 511 if (do_access && !req->getFlags().isSet(Request::NO_ACCESS)) { 512 Packet pkt(req, Packet::makeWriteCmd(req)); 513 pkt.dataStatic(data); 514 515 if (req->isMmappedIpr()) { 516 dcache_latency += 517 TheISA::handleIprWrite(thread->getTC(), &pkt); 518 } else { 519 dcache_latency += sendPacket(dcachePort, &pkt); 520 521 // Notify other threads on this CPU of write 522 threadSnoop(&pkt, curThread); 523 } 524 dcache_access = true; 525 assert(!pkt.isError()); 526 527 if (req->isSwap()) { 528 assert(res && curr_frag_id == 0); 529 memcpy(res, pkt.getConstPtr<uint8_t>(), size); 530 } 531 } 532 533 if (res && !req->isSwap()) { 534 *res = req->getExtraData(); 535 } 536 } 537 538 //If there's a fault or we don't need to access a second cache line, 539 //stop now. 540 if (fault != NoFault || size_left == 0) 541 { 542 if (req->isLockedRMW() && fault == NoFault) { 543 assert(byteEnable.empty()); 544 assert(locked && curr_frag_id == 0); 545 locked = false; 546 } 547 548 if (fault != NoFault && req->isPrefetch()) { 549 return NoFault; 550 } else { 551 return fault; 552 } 553 } 554 555 /* 556 * Set up for accessing the next cache line. 557 */ 558 frag_addr += frag_size; 559 560 //Move the pointer we're reading into to the correct location. 561 data += frag_size; 562 563 curr_frag_id++; 564 } 565} 566 567Fault 568AtomicSimpleCPU::amoMem(Addr addr, uint8_t* data, unsigned size, 569 Request::Flags flags, AtomicOpFunctor *amo_op) 570{ 571 SimpleExecContext& t_info = *threadInfo[curThread]; 572 SimpleThread* thread = t_info.thread; 573 574 // use the CPU's statically allocated amo request and packet objects 575 const RequestPtr &req = data_amo_req; 576 577 if (traceData) 578 traceData->setMem(addr, size, flags); 579 580 //The address of the second part of this access if it needs to be split 581 //across a cache line boundary. 582 Addr secondAddr = roundDown(addr + size - 1, cacheLineSize()); 583 584 // AMO requests that access across a cache line boundary are not 585 // allowed since the cache does not guarantee AMO ops to be executed 586 // atomically in two cache lines 587 // For ISAs such as x86 that requires AMO operations to work on 588 // accesses that cross cache-line boundaries, the cache needs to be 589 // modified to support locking both cache lines to guarantee the 590 // atomicity. 591 if (secondAddr > addr) { 592 panic("AMO request should not access across a cache line boundary\n"); 593 } 594 595 dcache_latency = 0; 596 597 req->taskId(taskId()); 598 req->setVirt(0, addr, size, flags, dataMasterId(), 599 thread->pcState().instAddr(), amo_op); 600 601 // translate to physical address 602 Fault fault = thread->dtb->translateAtomic(req, thread->getTC(), 603 BaseTLB::Write); 604 605 // Now do the access. 606 if (fault == NoFault && !req->getFlags().isSet(Request::NO_ACCESS)) { 607 // We treat AMO accesses as Write accesses with SwapReq command 608 // data will hold the return data of the AMO access 609 Packet pkt(req, Packet::makeWriteCmd(req)); 610 pkt.dataStatic(data); 611 612 if (req->isMmappedIpr()) 613 dcache_latency += TheISA::handleIprRead(thread->getTC(), &pkt); 614 else { 615 dcache_latency += sendPacket(dcachePort, &pkt); 616 } 617 618 dcache_access = true; 619 620 assert(!pkt.isError()); 621 assert(!req->isLLSC()); 622 } 623 624 if (fault != NoFault && req->isPrefetch()) { 625 return NoFault; 626 } 627 628 //If there's a fault and we're not doing prefetch, return it 629 return fault; 630} 631 632void 633AtomicSimpleCPU::tick() 634{ 635 DPRINTF(SimpleCPU, "Tick\n"); 636 637 // Change thread if multi-threaded 638 swapActiveThread(); 639 640 // Set memroy request ids to current thread 641 if (numThreads > 1) { 642 ContextID cid = threadContexts[curThread]->contextId(); 643 644 ifetch_req->setContext(cid); 645 data_read_req->setContext(cid); 646 data_write_req->setContext(cid); 647 data_amo_req->setContext(cid); 648 } 649 650 SimpleExecContext& t_info = *threadInfo[curThread]; 651 SimpleThread* thread = t_info.thread; 652 653 Tick latency = 0; 654 655 for (int i = 0; i < width || locked; ++i) { 656 numCycles++; 657 updateCycleCounters(BaseCPU::CPU_STATE_ON); 658 659 if (!curStaticInst || !curStaticInst->isDelayedCommit()) { 660 checkForInterrupts(); 661 checkPcEventQueue(); 662 } 663 664 // We must have just got suspended by a PC event 665 if (_status == Idle) { 666 tryCompleteDrain(); 667 return; 668 } 669 670 Fault fault = NoFault; 671 672 TheISA::PCState pcState = thread->pcState(); 673 674 bool needToFetch = !isRomMicroPC(pcState.microPC()) && 675 !curMacroStaticInst; 676 if (needToFetch) { 677 ifetch_req->taskId(taskId()); 678 setupFetchRequest(ifetch_req); 679 fault = thread->itb->translateAtomic(ifetch_req, thread->getTC(), 680 BaseTLB::Execute); 681 } 682 683 if (fault == NoFault) { 684 Tick icache_latency = 0; 685 bool icache_access = false; 686 dcache_access = false; // assume no dcache access 687 688 if (needToFetch) { 689 // This is commented out because the decoder would act like 690 // a tiny cache otherwise. It wouldn't be flushed when needed 691 // like the I cache. It should be flushed, and when that works 692 // this code should be uncommented. 693 //Fetch more instruction memory if necessary 694 //if (decoder.needMoreBytes()) 695 //{ 696 icache_access = true; 697 Packet ifetch_pkt = Packet(ifetch_req, MemCmd::ReadReq); 698 ifetch_pkt.dataStatic(&inst); 699 700 icache_latency = sendPacket(icachePort, &ifetch_pkt); 701 702 assert(!ifetch_pkt.isError()); 703 704 // ifetch_req is initialized to read the instruction directly 705 // into the CPU object's inst field. 706 //} 707 } 708 709 preExecute(); 710 711 Tick stall_ticks = 0; 712 if (curStaticInst) { 713 fault = curStaticInst->execute(&t_info, traceData); 714 715 // keep an instruction count 716 if (fault == NoFault) { 717 countInst(); 718 ppCommit->notify(std::make_pair(thread, curStaticInst)); 719 } 720 else if (traceData && !DTRACE(ExecFaulting)) { 721 delete traceData; 722 traceData = NULL; 723 } 724 725 if (fault != NoFault && 726 dynamic_pointer_cast<SyscallRetryFault>(fault)) { 727 // Retry execution of system calls after a delay. 728 // Prevents immediate re-execution since conditions which 729 // caused the retry are unlikely to change every tick. 730 stall_ticks += clockEdge(syscallRetryLatency) - curTick(); 731 } 732 733 postExecute(); 734 } 735 736 // @todo remove me after debugging with legion done 737 if (curStaticInst && (!curStaticInst->isMicroop() || 738 curStaticInst->isFirstMicroop())) 739 instCnt++; 740 741 if (simulate_inst_stalls && icache_access) 742 stall_ticks += icache_latency; 743 744 if (simulate_data_stalls && dcache_access) 745 stall_ticks += dcache_latency; 746 747 if (stall_ticks) { 748 // the atomic cpu does its accounting in ticks, so 749 // keep counting in ticks but round to the clock 750 // period 751 latency += divCeil(stall_ticks, clockPeriod()) * 752 clockPeriod(); 753 } 754 755 } 756 if (fault != NoFault || !t_info.stayAtPC) 757 advancePC(fault); 758 } 759 760 if (tryCompleteDrain()) 761 return; 762 763 // instruction takes at least one cycle 764 if (latency < clockPeriod()) 765 latency = clockPeriod(); 766 767 if (_status != Idle) 768 reschedule(tickEvent, curTick() + latency, true); 769} 770 771void 772AtomicSimpleCPU::regProbePoints() 773{ 774 BaseCPU::regProbePoints(); 775 776 ppCommit = new ProbePointArg<pair<SimpleThread*, const StaticInstPtr>> 777 (getProbeManager(), "Commit"); 778} 779 780void 781AtomicSimpleCPU::printAddr(Addr a) 782{ 783 dcachePort.printAddr(a); 784} 785 786//////////////////////////////////////////////////////////////////////// 787// 788// AtomicSimpleCPU Simulation Object 789// 790AtomicSimpleCPU * 791AtomicSimpleCPUParams::create() 792{ 793 return new AtomicSimpleCPU(this); 794} 795