atomic.cc revision 14297:b4519e586f5e
15217Ssaidi@eecs.umich.edu/* 29428SAndreas.Sandberg@ARM.com * Copyright 2014 Google, Inc. 39428SAndreas.Sandberg@ARM.com * Copyright (c) 2012-2013,2015,2017-2019 ARM Limited 49428SAndreas.Sandberg@ARM.com * All rights reserved. 59428SAndreas.Sandberg@ARM.com * 69428SAndreas.Sandberg@ARM.com * The license below extends only to copyright in the software and shall 79428SAndreas.Sandberg@ARM.com * not be construed as granting a license to any other intellectual 89428SAndreas.Sandberg@ARM.com * property including but not limited to intellectual property relating 99428SAndreas.Sandberg@ARM.com * to a hardware implementation of the functionality of the software 109428SAndreas.Sandberg@ARM.com * licensed hereunder. You may use the software subject to the license 119428SAndreas.Sandberg@ARM.com * terms below provided that you ensure that this notice is replicated 129428SAndreas.Sandberg@ARM.com * unmodified and in its entirety in all distributions of the software, 139428SAndreas.Sandberg@ARM.com * modified or unmodified, in source code or in binary form. 145217Ssaidi@eecs.umich.edu * 155217Ssaidi@eecs.umich.edu * Copyright (c) 2002-2005 The Regents of The University of Michigan 165217Ssaidi@eecs.umich.edu * All rights reserved. 175217Ssaidi@eecs.umich.edu * 185217Ssaidi@eecs.umich.edu * Redistribution and use in source and binary forms, with or without 195217Ssaidi@eecs.umich.edu * modification, are permitted provided that the following conditions are 205217Ssaidi@eecs.umich.edu * met: redistributions of source code must retain the above copyright 215217Ssaidi@eecs.umich.edu * notice, this list of conditions and the following disclaimer; 225217Ssaidi@eecs.umich.edu * redistributions in binary form must reproduce the above copyright 235217Ssaidi@eecs.umich.edu * notice, this list of conditions and the following disclaimer in the 245217Ssaidi@eecs.umich.edu * documentation and/or other materials provided with the distribution; 255217Ssaidi@eecs.umich.edu * neither the name of the copyright holders nor the names of its 265217Ssaidi@eecs.umich.edu * contributors may be used to endorse or promote products derived from 275217Ssaidi@eecs.umich.edu * this software without specific prior written permission. 285217Ssaidi@eecs.umich.edu * 295217Ssaidi@eecs.umich.edu * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 305217Ssaidi@eecs.umich.edu * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 315217Ssaidi@eecs.umich.edu * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 325217Ssaidi@eecs.umich.edu * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 335217Ssaidi@eecs.umich.edu * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 345217Ssaidi@eecs.umich.edu * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 355217Ssaidi@eecs.umich.edu * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 365217Ssaidi@eecs.umich.edu * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 375217Ssaidi@eecs.umich.edu * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 385217Ssaidi@eecs.umich.edu * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 395217Ssaidi@eecs.umich.edu * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 405217Ssaidi@eecs.umich.edu * 415217Ssaidi@eecs.umich.edu * Authors: Steve Reinhardt 425217Ssaidi@eecs.umich.edu */ 435217Ssaidi@eecs.umich.edu 445217Ssaidi@eecs.umich.edu#include "cpu/simple/atomic.hh" 456658Snate@binkert.org 465217Ssaidi@eecs.umich.edu#include "arch/locked_mem.hh" 478232Snate@binkert.org#include "arch/mmapped_ipr.hh" 485217Ssaidi@eecs.umich.edu#include "arch/utility.hh" 495217Ssaidi@eecs.umich.edu#include "base/output.hh" 505217Ssaidi@eecs.umich.edu#include "config/the_isa.hh" 515217Ssaidi@eecs.umich.edu#include "cpu/exetrace.hh" 525217Ssaidi@eecs.umich.edu#include "cpu/utils.hh" 535217Ssaidi@eecs.umich.edu#include "debug/Drain.hh" 545217Ssaidi@eecs.umich.edu#include "debug/ExecFaulting.hh" 555217Ssaidi@eecs.umich.edu#include "debug/SimpleCPU.hh" 565217Ssaidi@eecs.umich.edu#include "mem/packet.hh" 575217Ssaidi@eecs.umich.edu#include "mem/packet_access.hh" 585217Ssaidi@eecs.umich.edu#include "mem/physical.hh" 595217Ssaidi@eecs.umich.edu#include "params/AtomicSimpleCPU.hh" 605217Ssaidi@eecs.umich.edu#include "sim/faults.hh" 615217Ssaidi@eecs.umich.edu#include "sim/full_system.hh" 625217Ssaidi@eecs.umich.edu#include "sim/system.hh" 635217Ssaidi@eecs.umich.edu 645217Ssaidi@eecs.umich.eduusing namespace std; 655217Ssaidi@eecs.umich.eduusing namespace TheISA; 665217Ssaidi@eecs.umich.edu 675217Ssaidi@eecs.umich.eduvoid 685217Ssaidi@eecs.umich.eduAtomicSimpleCPU::init() 695217Ssaidi@eecs.umich.edu{ 705217Ssaidi@eecs.umich.edu BaseSimpleCPU::init(); 715217Ssaidi@eecs.umich.edu 725217Ssaidi@eecs.umich.edu int cid = threadContexts[0]->contextId(); 735217Ssaidi@eecs.umich.edu ifetch_req->setContext(cid); 745217Ssaidi@eecs.umich.edu data_read_req->setContext(cid); 755217Ssaidi@eecs.umich.edu data_write_req->setContext(cid); 765217Ssaidi@eecs.umich.edu data_amo_req->setContext(cid); 775217Ssaidi@eecs.umich.edu} 785217Ssaidi@eecs.umich.edu 797720Sgblack@eecs.umich.eduAtomicSimpleCPU::AtomicSimpleCPU(AtomicSimpleCPUParams *p) 807720Sgblack@eecs.umich.edu : BaseSimpleCPU(p), 815712Shsul@eecs.umich.edu tickEvent([this]{ tick(); }, "AtomicSimpleCPU tick", 825712Shsul@eecs.umich.edu false, Event::CPU_Tick_Pri), 835217Ssaidi@eecs.umich.edu width(p->width), locked(false), 845217Ssaidi@eecs.umich.edu simulate_data_stalls(p->simulate_data_stalls), 855714Shsul@eecs.umich.edu simulate_inst_stalls(p->simulate_inst_stalls), 865714Shsul@eecs.umich.edu icachePort(name() + ".icache_port", this), 875714Shsul@eecs.umich.edu dcachePort(name() + ".dcache_port", this), 885714Shsul@eecs.umich.edu dcache_access(false), dcache_latency(0), 895714Shsul@eecs.umich.edu ppCommit(nullptr) 905714Shsul@eecs.umich.edu{ 915714Shsul@eecs.umich.edu _status = Idle; 925217Ssaidi@eecs.umich.edu ifetch_req = std::make_shared<Request>(); 939428SAndreas.Sandberg@ARM.com data_read_req = std::make_shared<Request>(); 949428SAndreas.Sandberg@ARM.com data_write_req = std::make_shared<Request>(); 959428SAndreas.Sandberg@ARM.com data_amo_req = std::make_shared<Request>(); 969428SAndreas.Sandberg@ARM.com} 979428SAndreas.Sandberg@ARM.com 989428SAndreas.Sandberg@ARM.com 999428SAndreas.Sandberg@ARM.comAtomicSimpleCPU::~AtomicSimpleCPU() 1009428SAndreas.Sandberg@ARM.com{ 1019428SAndreas.Sandberg@ARM.com if (tickEvent.scheduled()) { 1029428SAndreas.Sandberg@ARM.com deschedule(tickEvent); 1039428SAndreas.Sandberg@ARM.com } 1049428SAndreas.Sandberg@ARM.com} 1059428SAndreas.Sandberg@ARM.com 1069428SAndreas.Sandberg@ARM.comDrainState 1079428SAndreas.Sandberg@ARM.comAtomicSimpleCPU::drain() 1089428SAndreas.Sandberg@ARM.com{ 1099428SAndreas.Sandberg@ARM.com // Deschedule any power gating event (if any) 1109428SAndreas.Sandberg@ARM.com deschedulePowerGatingEvent(); 1119428SAndreas.Sandberg@ARM.com 1129428SAndreas.Sandberg@ARM.com if (switchedOut()) 1139428SAndreas.Sandberg@ARM.com return DrainState::Drained; 1149428SAndreas.Sandberg@ARM.com 1159428SAndreas.Sandberg@ARM.com if (!isCpuDrained()) { 1169428SAndreas.Sandberg@ARM.com DPRINTF(Drain, "Requesting drain.\n"); 1179428SAndreas.Sandberg@ARM.com return DrainState::Draining; 1189428SAndreas.Sandberg@ARM.com } else { 1199428SAndreas.Sandberg@ARM.com if (tickEvent.scheduled()) 1209428SAndreas.Sandberg@ARM.com deschedule(tickEvent); 1219428SAndreas.Sandberg@ARM.com 1229428SAndreas.Sandberg@ARM.com activeThreads.clear(); 1239428SAndreas.Sandberg@ARM.com DPRINTF(Drain, "Not executing microcode, no need to drain.\n"); 1249428SAndreas.Sandberg@ARM.com return DrainState::Drained; 1259428SAndreas.Sandberg@ARM.com } 1269428SAndreas.Sandberg@ARM.com} 1279428SAndreas.Sandberg@ARM.com 1289428SAndreas.Sandberg@ARM.comvoid 1299428SAndreas.Sandberg@ARM.comAtomicSimpleCPU::threadSnoop(PacketPtr pkt, ThreadID sender) 1309428SAndreas.Sandberg@ARM.com{ 1319428SAndreas.Sandberg@ARM.com DPRINTF(SimpleCPU, "received snoop pkt for addr:%#x %s\n", pkt->getAddr(), 1329428SAndreas.Sandberg@ARM.com pkt->cmdString()); 1339428SAndreas.Sandberg@ARM.com 1349428SAndreas.Sandberg@ARM.com for (ThreadID tid = 0; tid < numThreads; tid++) { 1359428SAndreas.Sandberg@ARM.com if (tid != sender) { 1369428SAndreas.Sandberg@ARM.com if (getCpuAddrMonitor(tid)->doMonitor(pkt)) { 1379428SAndreas.Sandberg@ARM.com wakeup(tid); 1389428SAndreas.Sandberg@ARM.com } 139 140 TheISA::handleLockedSnoop(threadInfo[tid]->thread, 141 pkt, dcachePort.cacheBlockMask); 142 } 143 } 144} 145 146void 147AtomicSimpleCPU::drainResume() 148{ 149 assert(!tickEvent.scheduled()); 150 if (switchedOut()) 151 return; 152 153 DPRINTF(SimpleCPU, "Resume\n"); 154 verifyMemoryMode(); 155 156 assert(!threadContexts.empty()); 157 158 _status = BaseSimpleCPU::Idle; 159 160 for (ThreadID tid = 0; tid < numThreads; tid++) { 161 if (threadInfo[tid]->thread->status() == ThreadContext::Active) { 162 threadInfo[tid]->notIdleFraction = 1; 163 activeThreads.push_back(tid); 164 _status = BaseSimpleCPU::Running; 165 166 // Tick if any threads active 167 if (!tickEvent.scheduled()) { 168 schedule(tickEvent, nextCycle()); 169 } 170 } else { 171 threadInfo[tid]->notIdleFraction = 0; 172 } 173 } 174 175 // Reschedule any power gating event (if any) 176 schedulePowerGatingEvent(); 177} 178 179bool 180AtomicSimpleCPU::tryCompleteDrain() 181{ 182 if (drainState() != DrainState::Draining) 183 return false; 184 185 DPRINTF(Drain, "tryCompleteDrain.\n"); 186 if (!isCpuDrained()) 187 return false; 188 189 DPRINTF(Drain, "CPU done draining, processing drain event\n"); 190 signalDrainDone(); 191 192 return true; 193} 194 195 196void 197AtomicSimpleCPU::switchOut() 198{ 199 BaseSimpleCPU::switchOut(); 200 201 assert(!tickEvent.scheduled()); 202 assert(_status == BaseSimpleCPU::Running || _status == Idle); 203 assert(isCpuDrained()); 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 req->setByteEnable(std::vector<bool>()); 366 } 367 368 return predicate; 369} 370 371Fault 372AtomicSimpleCPU::readMem(Addr addr, uint8_t * data, unsigned size, 373 Request::Flags flags, 374 const std::vector<bool>& byteEnable) 375{ 376 SimpleExecContext& t_info = *threadInfo[curThread]; 377 SimpleThread* thread = t_info.thread; 378 379 // use the CPU's statically allocated read request and packet objects 380 const RequestPtr &req = data_read_req; 381 382 if (traceData) 383 traceData->setMem(addr, size, flags); 384 385 dcache_latency = 0; 386 387 req->taskId(taskId()); 388 389 Addr frag_addr = addr; 390 int frag_size = 0; 391 int size_left = size; 392 bool predicate; 393 Fault fault = NoFault; 394 395 while (1) { 396 predicate = genMemFragmentRequest(req, frag_addr, size, flags, 397 byteEnable, frag_size, size_left); 398 399 // translate to physical address 400 if (predicate) { 401 fault = thread->dtb->translateAtomic(req, thread->getTC(), 402 BaseTLB::Read); 403 } 404 405 // Now do the access. 406 if (predicate && fault == NoFault && 407 !req->getFlags().isSet(Request::NO_ACCESS)) { 408 Packet pkt(req, Packet::makeReadCmd(req)); 409 pkt.dataStatic(data); 410 411 if (req->isMmappedIpr()) { 412 dcache_latency += TheISA::handleIprRead(thread->getTC(), &pkt); 413 } else { 414 dcache_latency += sendPacket(dcachePort, &pkt); 415 } 416 dcache_access = true; 417 418 assert(!pkt.isError()); 419 420 if (req->isLLSC()) { 421 TheISA::handleLockedRead(thread, req); 422 } 423 } 424 425 //If there's a fault, return it 426 if (fault != NoFault) { 427 if (req->isPrefetch()) { 428 return NoFault; 429 } else { 430 return fault; 431 } 432 } 433 434 // If we don't need to access further cache lines, stop now. 435 if (size_left == 0) { 436 if (req->isLockedRMW() && fault == NoFault) { 437 assert(!locked); 438 locked = true; 439 } 440 return fault; 441 } 442 443 /* 444 * Set up for accessing the next cache line. 445 */ 446 frag_addr += frag_size; 447 448 //Move the pointer we're reading into to the correct location. 449 data += frag_size; 450 } 451} 452 453Fault 454AtomicSimpleCPU::writeMem(uint8_t *data, unsigned size, Addr addr, 455 Request::Flags flags, uint64_t *res, 456 const std::vector<bool>& byteEnable) 457{ 458 SimpleExecContext& t_info = *threadInfo[curThread]; 459 SimpleThread* thread = t_info.thread; 460 static uint8_t zero_array[64] = {}; 461 462 if (data == NULL) { 463 assert(size <= 64); 464 assert(flags & Request::STORE_NO_DATA); 465 // This must be a cache block cleaning request 466 data = zero_array; 467 } 468 469 // use the CPU's statically allocated write request and packet objects 470 const RequestPtr &req = data_write_req; 471 472 if (traceData) 473 traceData->setMem(addr, size, flags); 474 475 dcache_latency = 0; 476 477 req->taskId(taskId()); 478 479 Addr frag_addr = addr; 480 int frag_size = 0; 481 int size_left = size; 482 int curr_frag_id = 0; 483 bool predicate; 484 Fault fault = NoFault; 485 486 while (1) { 487 predicate = genMemFragmentRequest(req, frag_addr, size, flags, 488 byteEnable, frag_size, size_left); 489 490 // translate to physical address 491 if (predicate) 492 fault = thread->dtb->translateAtomic(req, thread->getTC(), 493 BaseTLB::Write); 494 495 // Now do the access. 496 if (predicate && fault == NoFault) { 497 bool do_access = true; // flag to suppress cache access 498 499 if (req->isLLSC()) { 500 assert(curr_frag_id == 0); 501 do_access = 502 TheISA::handleLockedWrite(thread, req, 503 dcachePort.cacheBlockMask); 504 } else if (req->isSwap()) { 505 assert(curr_frag_id == 0); 506 if (req->isCondSwap()) { 507 assert(res); 508 req->setExtraData(*res); 509 } 510 } 511 512 if (do_access && !req->getFlags().isSet(Request::NO_ACCESS)) { 513 Packet pkt(req, Packet::makeWriteCmd(req)); 514 pkt.dataStatic(data); 515 516 if (req->isMmappedIpr()) { 517 dcache_latency += 518 TheISA::handleIprWrite(thread->getTC(), &pkt); 519 } else { 520 dcache_latency += sendPacket(dcachePort, &pkt); 521 522 // Notify other threads on this CPU of write 523 threadSnoop(&pkt, curThread); 524 } 525 dcache_access = true; 526 assert(!pkt.isError()); 527 528 if (req->isSwap()) { 529 assert(res && curr_frag_id == 0); 530 memcpy(res, pkt.getConstPtr<uint8_t>(), size); 531 } 532 } 533 534 if (res && !req->isSwap()) { 535 *res = req->getExtraData(); 536 } 537 } 538 539 //If there's a fault or we don't need to access a second cache line, 540 //stop now. 541 if (fault != NoFault || size_left == 0) 542 { 543 if (req->isLockedRMW() && fault == NoFault) { 544 assert(byteEnable.empty()); 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, AtomicOpFunctorPtr 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(), std::move(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