execute.cc revision 11419:9c7b55faea5d
1/* 2 * Copyright (c) 2013-2014 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 * Redistribution and use in source and binary forms, with or without 15 * modification, are permitted provided that the following conditions are 16 * met: redistributions of source code must retain the above copyright 17 * notice, this list of conditions and the following disclaimer; 18 * redistributions in binary form must reproduce the above copyright 19 * notice, this list of conditions and the following disclaimer in the 20 * documentation and/or other materials provided with the distribution; 21 * neither the name of the copyright holders nor the names of its 22 * contributors may be used to endorse or promote products derived from 23 * this software without specific prior written permission. 24 * 25 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 26 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 27 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 28 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 29 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 30 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 31 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 32 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 33 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 34 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 35 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 36 * 37 * Authors: Andrew Bardsley 38 */ 39 40#include "arch/locked_mem.hh" 41#include "arch/registers.hh" 42#include "arch/utility.hh" 43#include "cpu/minor/cpu.hh" 44#include "cpu/minor/exec_context.hh" 45#include "cpu/minor/execute.hh" 46#include "cpu/minor/fetch1.hh" 47#include "cpu/minor/lsq.hh" 48#include "cpu/op_class.hh" 49#include "debug/Activity.hh" 50#include "debug/Branch.hh" 51#include "debug/Drain.hh" 52#include "debug/MinorExecute.hh" 53#include "debug/MinorInterrupt.hh" 54#include "debug/MinorMem.hh" 55#include "debug/MinorTrace.hh" 56#include "debug/PCEvent.hh" 57 58namespace Minor 59{ 60 61Execute::Execute(const std::string &name_, 62 MinorCPU &cpu_, 63 MinorCPUParams ¶ms, 64 Latch<ForwardInstData>::Output inp_, 65 Latch<BranchData>::Input out_) : 66 Named(name_), 67 inp(inp_), 68 out(out_), 69 cpu(cpu_), 70 issueLimit(params.executeIssueLimit), 71 memoryIssueLimit(params.executeMemoryIssueLimit), 72 commitLimit(params.executeCommitLimit), 73 memoryCommitLimit(params.executeMemoryCommitLimit), 74 processMoreThanOneInput(params.executeCycleInput), 75 fuDescriptions(*params.executeFuncUnits), 76 numFuncUnits(fuDescriptions.funcUnits.size()), 77 setTraceTimeOnCommit(params.executeSetTraceTimeOnCommit), 78 setTraceTimeOnIssue(params.executeSetTraceTimeOnIssue), 79 allowEarlyMemIssue(params.executeAllowEarlyMemoryIssue), 80 noCostFUIndex(fuDescriptions.funcUnits.size() + 1), 81 lsq(name_ + ".lsq", name_ + ".dcache_port", 82 cpu_, *this, 83 params.executeMaxAccessesInMemory, 84 params.executeMemoryWidth, 85 params.executeLSQRequestsQueueSize, 86 params.executeLSQTransfersQueueSize, 87 params.executeLSQStoreBufferSize, 88 params.executeLSQMaxStoreBufferStoresPerCycle), 89 scoreboard(name_ + ".scoreboard"), 90 inputBuffer(name_ + ".inputBuffer", "insts", 91 params.executeInputBufferSize), 92 inputIndex(0), 93 lastCommitWasEndOfMacroop(true), 94 instsBeingCommitted(params.executeCommitLimit), 95 streamSeqNum(InstId::firstStreamSeqNum), 96 lastPredictionSeqNum(InstId::firstPredictionSeqNum), 97 drainState(NotDraining) 98{ 99 if (commitLimit < 1) { 100 fatal("%s: executeCommitLimit must be >= 1 (%d)\n", name_, 101 commitLimit); 102 } 103 104 if (issueLimit < 1) { 105 fatal("%s: executeCommitLimit must be >= 1 (%d)\n", name_, 106 issueLimit); 107 } 108 109 if (memoryIssueLimit < 1) { 110 fatal("%s: executeMemoryIssueLimit must be >= 1 (%d)\n", name_, 111 memoryIssueLimit); 112 } 113 114 if (memoryCommitLimit > commitLimit) { 115 fatal("%s: executeMemoryCommitLimit (%d) must be <=" 116 " executeCommitLimit (%d)\n", 117 name_, memoryCommitLimit, commitLimit); 118 } 119 120 if (params.executeInputBufferSize < 1) { 121 fatal("%s: executeInputBufferSize must be >= 1 (%d)\n", name_, 122 params.executeInputBufferSize); 123 } 124 125 if (params.executeInputBufferSize < 1) { 126 fatal("%s: executeInputBufferSize must be >= 1 (%d)\n", name_, 127 params.executeInputBufferSize); 128 } 129 130 /* This should be large enough to count all the in-FU instructions 131 * which need to be accounted for in the inFlightInsts 132 * queue */ 133 unsigned int total_slots = 0; 134 135 /* Make FUPipelines for each MinorFU */ 136 for (unsigned int i = 0; i < numFuncUnits; i++) { 137 std::ostringstream fu_name; 138 MinorFU *fu_description = fuDescriptions.funcUnits[i]; 139 140 /* Note the total number of instruction slots (for sizing 141 * the inFlightInst queue) and the maximum latency of any FU 142 * (for sizing the activity recorder) */ 143 total_slots += fu_description->opLat; 144 145 fu_name << name_ << ".fu." << i; 146 147 FUPipeline *fu = new FUPipeline(fu_name.str(), *fu_description, cpu); 148 149 funcUnits.push_back(fu); 150 } 151 152 /** Check that there is a functional unit for all operation classes */ 153 for (int op_class = No_OpClass + 1; op_class < Num_OpClasses; op_class++) { 154 bool found_fu = false; 155 unsigned int fu_index = 0; 156 157 while (fu_index < numFuncUnits && !found_fu) 158 { 159 if (funcUnits[fu_index]->provides( 160 static_cast<OpClass>(op_class))) 161 { 162 found_fu = true; 163 } 164 fu_index++; 165 } 166 167 if (!found_fu) { 168 warn("No functional unit for OpClass %s\n", 169 Enums::OpClassStrings[op_class]); 170 } 171 } 172 173 inFlightInsts = new Queue<QueuedInst, 174 ReportTraitsAdaptor<QueuedInst> >( 175 name_ + ".inFlightInsts", "insts", total_slots); 176 177 inFUMemInsts = new Queue<QueuedInst, 178 ReportTraitsAdaptor<QueuedInst> >( 179 name_ + ".inFUMemInsts", "insts", total_slots); 180} 181 182const ForwardInstData * 183Execute::getInput() 184{ 185 /* Get a line from the inputBuffer to work with */ 186 if (!inputBuffer.empty()) { 187 const ForwardInstData &head = inputBuffer.front(); 188 189 return (head.isBubble() ? NULL : &(inputBuffer.front())); 190 } else { 191 return NULL; 192 } 193} 194 195void 196Execute::popInput() 197{ 198 if (!inputBuffer.empty()) 199 inputBuffer.pop(); 200 201 inputIndex = 0; 202} 203 204void 205Execute::tryToBranch(MinorDynInstPtr inst, Fault fault, BranchData &branch) 206{ 207 ThreadContext *thread = cpu.getContext(inst->id.threadId); 208 const TheISA::PCState &pc_before = inst->pc; 209 TheISA::PCState target = thread->pcState(); 210 211 /* Force a branch for SerializeAfter instructions at the end of micro-op 212 * sequence when we're not suspended */ 213 bool force_branch = thread->status() != ThreadContext::Suspended && 214 !inst->isFault() && 215 inst->isLastOpInInst() && 216 (inst->staticInst->isSerializeAfter() || 217 inst->staticInst->isIprAccess()); 218 219 DPRINTF(Branch, "tryToBranch before: %s after: %s%s\n", 220 pc_before, target, (force_branch ? " (forcing)" : "")); 221 222 /* Will we change the PC to something other than the next instruction? */ 223 bool must_branch = pc_before != target || 224 fault != NoFault || 225 force_branch; 226 227 /* The reason for the branch data we're about to generate, set below */ 228 BranchData::Reason reason = BranchData::NoBranch; 229 230 if (fault == NoFault) 231 { 232 TheISA::advancePC(target, inst->staticInst); 233 thread->pcState(target); 234 235 DPRINTF(Branch, "Advancing current PC from: %s to: %s\n", 236 pc_before, target); 237 } 238 239 if (inst->predictedTaken && !force_branch) { 240 /* Predicted to branch */ 241 if (!must_branch) { 242 /* No branch was taken, change stream to get us back to the 243 * intended PC value */ 244 DPRINTF(Branch, "Predicted a branch from 0x%x to 0x%x but" 245 " none happened inst: %s\n", 246 inst->pc.instAddr(), inst->predictedTarget.instAddr(), *inst); 247 248 reason = BranchData::BadlyPredictedBranch; 249 } else if (inst->predictedTarget == target) { 250 /* Branch prediction got the right target, kill the branch and 251 * carry on. 252 * Note that this information to the branch predictor might get 253 * overwritten by a "real" branch during this cycle */ 254 DPRINTF(Branch, "Predicted a branch from 0x%x to 0x%x correctly" 255 " inst: %s\n", 256 inst->pc.instAddr(), inst->predictedTarget.instAddr(), *inst); 257 258 reason = BranchData::CorrectlyPredictedBranch; 259 } else { 260 /* Branch prediction got the wrong target */ 261 DPRINTF(Branch, "Predicted a branch from 0x%x to 0x%x" 262 " but got the wrong target (actual: 0x%x) inst: %s\n", 263 inst->pc.instAddr(), inst->predictedTarget.instAddr(), 264 target.instAddr(), *inst); 265 266 reason = BranchData::BadlyPredictedBranchTarget; 267 } 268 } else if (must_branch) { 269 /* Unpredicted branch */ 270 DPRINTF(Branch, "Unpredicted branch from 0x%x to 0x%x inst: %s\n", 271 inst->pc.instAddr(), target.instAddr(), *inst); 272 273 reason = BranchData::UnpredictedBranch; 274 } else { 275 /* No branch at all */ 276 reason = BranchData::NoBranch; 277 } 278 279 updateBranchData(reason, inst, target, branch); 280} 281 282void 283Execute::updateBranchData( 284 BranchData::Reason reason, 285 MinorDynInstPtr inst, const TheISA::PCState &target, 286 BranchData &branch) 287{ 288 if (reason != BranchData::NoBranch) { 289 /* Bump up the stream sequence number on a real branch*/ 290 if (BranchData::isStreamChange(reason)) 291 streamSeqNum++; 292 293 /* Branches (even mis-predictions) don't change the predictionSeqNum, 294 * just the streamSeqNum */ 295 branch = BranchData(reason, streamSeqNum, 296 /* Maintaining predictionSeqNum if there's no inst is just a 297 * courtesy and looks better on minorview */ 298 (inst->isBubble() ? lastPredictionSeqNum 299 : inst->id.predictionSeqNum), 300 target, inst); 301 302 DPRINTF(Branch, "Branch data signalled: %s\n", branch); 303 } 304} 305 306void 307Execute::handleMemResponse(MinorDynInstPtr inst, 308 LSQ::LSQRequestPtr response, BranchData &branch, Fault &fault) 309{ 310 ThreadID thread_id = inst->id.threadId; 311 ThreadContext *thread = cpu.getContext(thread_id); 312 313 ExecContext context(cpu, *cpu.threads[thread_id], *this, inst); 314 315 PacketPtr packet = response->packet; 316 317 bool is_load = inst->staticInst->isLoad(); 318 bool is_store = inst->staticInst->isStore(); 319 bool is_prefetch = inst->staticInst->isDataPrefetch(); 320 321 /* If true, the trace's predicate value will be taken from the exec 322 * context predicate, otherwise, it will be set to false */ 323 bool use_context_predicate = true; 324 325 if (response->fault != NoFault) { 326 /* Invoke memory faults. */ 327 DPRINTF(MinorMem, "Completing fault from DTLB access: %s\n", 328 response->fault->name()); 329 330 if (inst->staticInst->isPrefetch()) { 331 DPRINTF(MinorMem, "Not taking fault on prefetch: %s\n", 332 response->fault->name()); 333 334 /* Don't assign to fault */ 335 } else { 336 /* Take the fault raised during the TLB/memory access */ 337 fault = response->fault; 338 339 fault->invoke(thread, inst->staticInst); 340 } 341 } else if (!packet) { 342 DPRINTF(MinorMem, "Completing failed request inst: %s\n", 343 *inst); 344 use_context_predicate = false; 345 } else if (packet->isError()) { 346 DPRINTF(MinorMem, "Trying to commit error response: %s\n", 347 *inst); 348 349 fatal("Received error response packet for inst: %s\n", *inst); 350 } else if (is_store || is_load || is_prefetch) { 351 assert(packet); 352 353 DPRINTF(MinorMem, "Memory response inst: %s addr: 0x%x size: %d\n", 354 *inst, packet->getAddr(), packet->getSize()); 355 356 if (is_load && packet->getSize() > 0) { 357 DPRINTF(MinorMem, "Memory data[0]: 0x%x\n", 358 static_cast<unsigned int>(packet->getConstPtr<uint8_t>()[0])); 359 } 360 361 /* Complete the memory access instruction */ 362 fault = inst->staticInst->completeAcc(packet, &context, 363 inst->traceData); 364 365 if (fault != NoFault) { 366 /* Invoke fault created by instruction completion */ 367 DPRINTF(MinorMem, "Fault in memory completeAcc: %s\n", 368 fault->name()); 369 fault->invoke(thread, inst->staticInst); 370 } else { 371 /* Stores need to be pushed into the store buffer to finish 372 * them off */ 373 if (response->needsToBeSentToStoreBuffer()) 374 lsq.sendStoreToStoreBuffer(response); 375 } 376 } else { 377 fatal("There should only ever be reads, " 378 "writes or faults at this point\n"); 379 } 380 381 lsq.popResponse(response); 382 383 if (inst->traceData) { 384 inst->traceData->setPredicate((use_context_predicate ? 385 context.readPredicate() : false)); 386 } 387 388 doInstCommitAccounting(inst); 389 390 /* Generate output to account for branches */ 391 tryToBranch(inst, fault, branch); 392} 393 394bool 395Execute::isInterrupted(ThreadID thread_id) const 396{ 397 return cpu.checkInterrupts(cpu.getContext(thread_id)); 398} 399 400bool 401Execute::takeInterrupt(ThreadID thread_id, BranchData &branch) 402{ 403 DPRINTF(MinorInterrupt, "Considering interrupt status from PC: %s\n", 404 cpu.getContext(thread_id)->pcState()); 405 406 Fault interrupt = cpu.getInterruptController(thread_id)->getInterrupt 407 (cpu.getContext(thread_id)); 408 409 if (interrupt != NoFault) { 410 /* The interrupt *must* set pcState */ 411 cpu.getInterruptController(thread_id)->updateIntrInfo 412 (cpu.getContext(thread_id)); 413 interrupt->invoke(cpu.getContext(thread_id)); 414 415 assert(!lsq.accessesInFlight()); 416 417 DPRINTF(MinorInterrupt, "Invoking interrupt: %s to PC: %s\n", 418 interrupt->name(), cpu.getContext(thread_id)->pcState()); 419 420 /* Assume that an interrupt *must* cause a branch. Assert this? */ 421 422 updateBranchData(BranchData::Interrupt, MinorDynInst::bubble(), 423 cpu.getContext(thread_id)->pcState(), branch); 424 } 425 426 return interrupt != NoFault; 427} 428 429bool 430Execute::executeMemRefInst(MinorDynInstPtr inst, BranchData &branch, 431 bool &passed_predicate, Fault &fault) 432{ 433 bool issued = false; 434 435 /* Set to true if the mem op. is issued and sent to the mem system */ 436 passed_predicate = false; 437 438 if (!lsq.canRequest()) { 439 /* Not acting on instruction yet as the memory 440 * queues are full */ 441 issued = false; 442 } else { 443 ThreadContext *thread = cpu.getContext(inst->id.threadId); 444 TheISA::PCState old_pc = thread->pcState(); 445 446 ExecContext context(cpu, *cpu.threads[inst->id.threadId], 447 *this, inst); 448 449 DPRINTF(MinorExecute, "Initiating memRef inst: %s\n", *inst); 450 451 Fault init_fault = inst->staticInst->initiateAcc(&context, 452 inst->traceData); 453 454 if (init_fault != NoFault) { 455 DPRINTF(MinorExecute, "Fault on memory inst: %s" 456 " initiateAcc: %s\n", *inst, init_fault->name()); 457 fault = init_fault; 458 } else { 459 /* Only set this if the instruction passed its 460 * predicate */ 461 passed_predicate = context.readPredicate(); 462 463 /* Set predicate in tracing */ 464 if (inst->traceData) 465 inst->traceData->setPredicate(passed_predicate); 466 467 /* If the instruction didn't pass its predicate (and so will not 468 * progress from here) Try to branch to correct and branch 469 * mis-prediction. */ 470 if (!passed_predicate) { 471 /* Leave it up to commit to handle the fault */ 472 lsq.pushFailedRequest(inst); 473 } 474 } 475 476 /* Restore thread PC */ 477 thread->pcState(old_pc); 478 issued = true; 479 } 480 481 return issued; 482} 483 484/** Increment a cyclic buffer index for indices [0, cycle_size-1] */ 485inline unsigned int 486cyclicIndexInc(unsigned int index, unsigned int cycle_size) 487{ 488 unsigned int ret = index + 1; 489 490 if (ret == cycle_size) 491 ret = 0; 492 493 return ret; 494} 495 496/** Decrement a cyclic buffer index for indices [0, cycle_size-1] */ 497inline unsigned int 498cyclicIndexDec(unsigned int index, unsigned int cycle_size) 499{ 500 int ret = index - 1; 501 502 if (ret < 0) 503 ret = cycle_size - 1; 504 505 return ret; 506} 507 508unsigned int 509Execute::issue(bool only_issue_microops) 510{ 511 const ForwardInstData *insts_in = getInput(); 512 513 /* Early termination if we have no instructions */ 514 if (!insts_in) 515 return 0; 516 517 /* Start from the first FU */ 518 unsigned int fu_index = 0; 519 520 /* Remains true while instructions are still being issued. If any 521 * instruction fails to issue, this is set to false and we exit issue. 522 * This strictly enforces in-order issue. For other issue behaviours, 523 * a more complicated test in the outer while loop below is needed. */ 524 bool issued = true; 525 526 /* Number of insts issues this cycle to check for issueLimit */ 527 unsigned num_insts_issued = 0; 528 529 /* Number of memory ops issues this cycle to check for memoryIssueLimit */ 530 unsigned num_mem_insts_issued = 0; 531 532 /* Number of instructions discarded this cycle in order to enforce a 533 * discardLimit. @todo, add that parameter? */ 534 unsigned num_insts_discarded = 0; 535 536 do { 537 MinorDynInstPtr inst = insts_in->insts[inputIndex]; 538 ThreadID thread_id = inst->id.threadId; 539 Fault fault = inst->fault; 540 bool discarded = false; 541 bool issued_mem_ref = false; 542 543 if (inst->isBubble()) { 544 /* Skip */ 545 issued = true; 546 } else if (cpu.getContext(thread_id)->status() == 547 ThreadContext::Suspended) 548 { 549 DPRINTF(MinorExecute, "Not issuing inst: %s from suspended" 550 " thread\n", *inst); 551 552 issued = false; 553 } else if (inst->id.streamSeqNum != streamSeqNum) { 554 DPRINTF(MinorExecute, "Discarding inst: %s as its stream" 555 " state was unexpected, expected: %d\n", 556 *inst, streamSeqNum); 557 issued = true; 558 discarded = true; 559 } else if (fault == NoFault && only_issue_microops && 560 /* Is this anything other than a non-first microop */ 561 (!inst->staticInst->isMicroop() || 562 !inst->staticInst->isFirstMicroop())) 563 { 564 DPRINTF(MinorExecute, "Not issuing new non-microop inst: %s\n", 565 *inst); 566 567 issued = false; 568 } else { 569 /* Try and issue an instruction into an FU, assume we didn't and 570 * fix that in the loop */ 571 issued = false; 572 573 /* Try FU from 0 each instruction */ 574 fu_index = 0; 575 576 /* Try and issue a single instruction stepping through the 577 * available FUs */ 578 do { 579 FUPipeline *fu = funcUnits[fu_index]; 580 581 DPRINTF(MinorExecute, "Trying to issue inst: %s to FU: %d\n", 582 *inst, fu_index); 583 584 /* Does the examined fu have the OpClass-related capability 585 * needed to execute this instruction? Faults can always 586 * issue to any FU but probably should just 'live' in the 587 * inFlightInsts queue rather than having an FU. */ 588 bool fu_is_capable = (!inst->isFault() ? 589 fu->provides(inst->staticInst->opClass()) : true); 590 591 if (inst->isNoCostInst()) { 592 /* Issue free insts. to a fake numbered FU */ 593 fu_index = noCostFUIndex; 594 595 /* And start the countdown on activity to allow 596 * this instruction to get to the end of its FU */ 597 cpu.activityRecorder->activity(); 598 599 /* Mark the destinations for this instruction as 600 * busy */ 601 scoreboard.markupInstDests(inst, cpu.curCycle() + 602 Cycles(0), cpu.getContext(thread_id), false); 603 604 inst->fuIndex = noCostFUIndex; 605 inst->extraCommitDelay = Cycles(0); 606 inst->extraCommitDelayExpr = NULL; 607 608 /* Push the instruction onto the inFlight queue so 609 * it can be committed in order */ 610 QueuedInst fu_inst(inst); 611 inFlightInsts->push(fu_inst); 612 613 issued = true; 614 615 } else if (!fu_is_capable || fu->alreadyPushed()) { 616 /* Skip */ 617 if (!fu_is_capable) { 618 DPRINTF(MinorExecute, "Can't issue as FU: %d isn't" 619 " capable\n", fu_index); 620 } else { 621 DPRINTF(MinorExecute, "Can't issue as FU: %d is" 622 " already busy\n", fu_index); 623 } 624 } else if (fu->stalled) { 625 DPRINTF(MinorExecute, "Can't issue inst: %s into FU: %d," 626 " it's stalled\n", 627 *inst, fu_index); 628 } else if (!fu->canInsert()) { 629 DPRINTF(MinorExecute, "Can't issue inst: %s to busy FU" 630 " for another: %d cycles\n", 631 *inst, fu->cyclesBeforeInsert()); 632 } else { 633 MinorFUTiming *timing = (!inst->isFault() ? 634 fu->findTiming(inst->staticInst) : NULL); 635 636 const std::vector<Cycles> *src_latencies = 637 (timing ? &(timing->srcRegsRelativeLats) 638 : NULL); 639 640 const std::vector<bool> *cant_forward_from_fu_indices = 641 &(fu->cantForwardFromFUIndices); 642 643 if (timing && timing->suppress) { 644 DPRINTF(MinorExecute, "Can't issue inst: %s as extra" 645 " decoding is suppressing it\n", 646 *inst); 647 } else if (!scoreboard.canInstIssue(inst, src_latencies, 648 cant_forward_from_fu_indices, 649 cpu.curCycle(), cpu.getContext(thread_id))) 650 { 651 DPRINTF(MinorExecute, "Can't issue inst: %s yet\n", 652 *inst); 653 } else { 654 /* Can insert the instruction into this FU */ 655 DPRINTF(MinorExecute, "Issuing inst: %s" 656 " into FU %d\n", *inst, 657 fu_index); 658 659 Cycles extra_dest_retire_lat = Cycles(0); 660 TimingExpr *extra_dest_retire_lat_expr = NULL; 661 Cycles extra_assumed_lat = Cycles(0); 662 663 /* Add the extraCommitDelay and extraAssumeLat to 664 * the FU pipeline timings */ 665 if (timing) { 666 extra_dest_retire_lat = 667 timing->extraCommitLat; 668 extra_dest_retire_lat_expr = 669 timing->extraCommitLatExpr; 670 extra_assumed_lat = 671 timing->extraAssumedLat; 672 } 673 674 issued_mem_ref = inst->isMemRef(); 675 676 QueuedInst fu_inst(inst); 677 678 /* Decorate the inst with FU details */ 679 inst->fuIndex = fu_index; 680 inst->extraCommitDelay = extra_dest_retire_lat; 681 inst->extraCommitDelayExpr = 682 extra_dest_retire_lat_expr; 683 684 if (issued_mem_ref) { 685 /* Remember which instruction this memory op 686 * depends on so that initiateAcc can be called 687 * early */ 688 if (allowEarlyMemIssue) { 689 inst->instToWaitFor = 690 scoreboard.execSeqNumToWaitFor(inst, 691 cpu.getContext(thread_id)); 692 693 if (lsq.getLastMemBarrier() > 694 inst->instToWaitFor) 695 { 696 DPRINTF(MinorExecute, "A barrier will" 697 " cause a delay in mem ref issue of" 698 " inst: %s until after inst" 699 " %d(exec)\n", *inst, 700 lsq.getLastMemBarrier()); 701 702 inst->instToWaitFor = 703 lsq.getLastMemBarrier(); 704 } else { 705 DPRINTF(MinorExecute, "Memory ref inst:" 706 " %s must wait for inst %d(exec)" 707 " before issuing\n", 708 *inst, inst->instToWaitFor); 709 } 710 711 inst->canEarlyIssue = true; 712 } 713 /* Also queue this instruction in the memory ref 714 * queue to ensure in-order issue to the LSQ */ 715 DPRINTF(MinorExecute, "Pushing mem inst: %s\n", 716 *inst); 717 inFUMemInsts->push(fu_inst); 718 } 719 720 /* Issue to FU */ 721 fu->push(fu_inst); 722 /* And start the countdown on activity to allow 723 * this instruction to get to the end of its FU */ 724 cpu.activityRecorder->activity(); 725 726 /* Mark the destinations for this instruction as 727 * busy */ 728 scoreboard.markupInstDests(inst, cpu.curCycle() + 729 fu->description.opLat + 730 extra_dest_retire_lat + 731 extra_assumed_lat, 732 cpu.getContext(thread_id), 733 issued_mem_ref && extra_assumed_lat == Cycles(0)); 734 735 /* Push the instruction onto the inFlight queue so 736 * it can be committed in order */ 737 inFlightInsts->push(fu_inst); 738 739 issued = true; 740 } 741 } 742 743 fu_index++; 744 } while (fu_index != numFuncUnits && !issued); 745 746 if (!issued) 747 DPRINTF(MinorExecute, "Didn't issue inst: %s\n", *inst); 748 } 749 750 if (issued) { 751 /* Generate MinorTrace's MinorInst lines. Do this at commit 752 * to allow better instruction annotation? */ 753 if (DTRACE(MinorTrace) && !inst->isBubble()) 754 inst->minorTraceInst(*this); 755 756 /* Mark up barriers in the LSQ */ 757 if (!discarded && inst->isInst() && 758 inst->staticInst->isMemBarrier()) 759 { 760 DPRINTF(MinorMem, "Issuing memory barrier inst: %s\n", *inst); 761 lsq.issuedMemBarrierInst(inst); 762 } 763 764 if (inst->traceData && setTraceTimeOnIssue) { 765 inst->traceData->setWhen(curTick()); 766 } 767 768 if (issued_mem_ref) 769 num_mem_insts_issued++; 770 771 if (discarded) { 772 num_insts_discarded++; 773 } else if (!inst->isBubble()) { 774 num_insts_issued++; 775 776 if (num_insts_issued == issueLimit) 777 DPRINTF(MinorExecute, "Reached inst issue limit\n"); 778 } 779 780 inputIndex++; 781 DPRINTF(MinorExecute, "Stepping to next inst inputIndex: %d\n", 782 inputIndex); 783 } 784 785 /* Got to the end of a line */ 786 if (inputIndex == insts_in->width()) { 787 popInput(); 788 /* Set insts_in to null to force us to leave the surrounding 789 * loop */ 790 insts_in = NULL; 791 792 if (processMoreThanOneInput) { 793 DPRINTF(MinorExecute, "Wrapping\n"); 794 insts_in = getInput(); 795 } 796 } 797 } while (insts_in && inputIndex < insts_in->width() && 798 /* We still have instructions */ 799 fu_index != numFuncUnits && /* Not visited all FUs */ 800 issued && /* We've not yet failed to issue an instruction */ 801 num_insts_issued != issueLimit && /* Still allowed to issue */ 802 num_mem_insts_issued != memoryIssueLimit); 803 804 return num_insts_issued; 805} 806 807bool 808Execute::tryPCEvents() 809{ 810 ThreadContext *thread = cpu.getContext(0); 811 unsigned int num_pc_event_checks = 0; 812 813 /* Handle PC events on instructions */ 814 Addr oldPC; 815 do { 816 oldPC = thread->instAddr(); 817 cpu.system->pcEventQueue.service(thread); 818 num_pc_event_checks++; 819 } while (oldPC != thread->instAddr()); 820 821 if (num_pc_event_checks > 1) { 822 DPRINTF(PCEvent, "Acting on PC Event to PC: %s\n", 823 thread->pcState()); 824 } 825 826 return num_pc_event_checks > 1; 827} 828 829void 830Execute::doInstCommitAccounting(MinorDynInstPtr inst) 831{ 832 assert(!inst->isFault()); 833 834 MinorThread *thread = cpu.threads[inst->id.threadId]; 835 836 /* Increment the many and various inst and op counts in the 837 * thread and system */ 838 if (!inst->staticInst->isMicroop() || inst->staticInst->isLastMicroop()) 839 { 840 thread->numInst++; 841 thread->numInsts++; 842 cpu.stats.numInsts++; 843 cpu.system->totalNumInsts++; 844 845 /* Act on events related to instruction counts */ 846 cpu.comInstEventQueue[inst->id.threadId]->serviceEvents(thread->numInst); 847 cpu.system->instEventQueue.serviceEvents(cpu.system->totalNumInsts); 848 } 849 thread->numOp++; 850 thread->numOps++; 851 cpu.stats.numOps++; 852 cpu.stats.committedInstType[inst->id.threadId] 853 [inst->staticInst->opClass()]++; 854 855 /* Set the CP SeqNum to the numOps commit number */ 856 if (inst->traceData) 857 inst->traceData->setCPSeq(thread->numOp); 858 859 cpu.probeInstCommit(inst->staticInst); 860} 861 862bool 863Execute::commitInst(MinorDynInstPtr inst, bool early_memory_issue, 864 BranchData &branch, Fault &fault, bool &committed, 865 bool &completed_mem_issue) 866{ 867 ThreadID thread_id = inst->id.threadId; 868 ThreadContext *thread = cpu.getContext(thread_id); 869 870 bool completed_inst = true; 871 fault = NoFault; 872 873 /* Is the thread for this instruction suspended? In that case, just 874 * stall as long as there are no pending interrupts */ 875 if (thread->status() == ThreadContext::Suspended && 876 !isInterrupted(thread_id)) 877 { 878 DPRINTF(MinorExecute, "Not committing inst from suspended thread" 879 " inst: %s\n", *inst); 880 completed_inst = false; 881 } else if (inst->isFault()) { 882 ExecContext context(cpu, *cpu.threads[thread_id], *this, inst); 883 884 DPRINTF(MinorExecute, "Fault inst reached Execute: %s\n", 885 inst->fault->name()); 886 887 fault = inst->fault; 888 inst->fault->invoke(thread, NULL); 889 890 tryToBranch(inst, fault, branch); 891 } else if (inst->staticInst->isMemRef()) { 892 /* Memory accesses are executed in two parts: 893 * executeMemRefInst -- calculates the EA and issues the access 894 * to memory. This is done here. 895 * handleMemResponse -- handles the response packet, done by 896 * Execute::commit 897 * 898 * While the memory access is in its FU, the EA is being 899 * calculated. At the end of the FU, when it is ready to 900 * 'commit' (in this function), the access is presented to the 901 * memory queues. When a response comes back from memory, 902 * Execute::commit will commit it. 903 */ 904 bool predicate_passed = false; 905 bool completed_mem_inst = executeMemRefInst(inst, branch, 906 predicate_passed, fault); 907 908 if (completed_mem_inst && fault != NoFault) { 909 if (early_memory_issue) { 910 DPRINTF(MinorExecute, "Fault in early executing inst: %s\n", 911 fault->name()); 912 /* Don't execute the fault, just stall the instruction 913 * until it gets to the head of inFlightInsts */ 914 inst->canEarlyIssue = false; 915 /* Not completed as we'll come here again to pick up 916 * the fault when we get to the end of the FU */ 917 completed_inst = false; 918 } else { 919 DPRINTF(MinorExecute, "Fault in execute: %s\n", 920 fault->name()); 921 fault->invoke(thread, NULL); 922 923 tryToBranch(inst, fault, branch); 924 completed_inst = true; 925 } 926 } else { 927 completed_inst = completed_mem_inst; 928 } 929 completed_mem_issue = completed_inst; 930 } else if (inst->isInst() && inst->staticInst->isMemBarrier() && 931 !lsq.canPushIntoStoreBuffer()) 932 { 933 DPRINTF(MinorExecute, "Can't commit data barrier inst: %s yet as" 934 " there isn't space in the store buffer\n", *inst); 935 936 completed_inst = false; 937 } else { 938 ExecContext context(cpu, *cpu.threads[thread_id], *this, inst); 939 940 DPRINTF(MinorExecute, "Committing inst: %s\n", *inst); 941 942 fault = inst->staticInst->execute(&context, 943 inst->traceData); 944 945 /* Set the predicate for tracing and dump */ 946 if (inst->traceData) 947 inst->traceData->setPredicate(context.readPredicate()); 948 949 committed = true; 950 951 if (fault != NoFault) { 952 DPRINTF(MinorExecute, "Fault in execute of inst: %s fault: %s\n", 953 *inst, fault->name()); 954 fault->invoke(thread, inst->staticInst); 955 } 956 957 doInstCommitAccounting(inst); 958 tryToBranch(inst, fault, branch); 959 } 960 961 if (completed_inst) { 962 /* Keep a copy of this instruction's predictionSeqNum just in case 963 * we need to issue a branch without an instruction (such as an 964 * interrupt) */ 965 lastPredictionSeqNum = inst->id.predictionSeqNum; 966 967 /* Check to see if this instruction suspended the current thread. */ 968 if (!inst->isFault() && 969 thread->status() == ThreadContext::Suspended && 970 branch.isBubble() && /* It didn't branch too */ 971 !isInterrupted(thread_id)) /* Don't suspend if we have 972 interrupts */ 973 { 974 TheISA::PCState resume_pc = cpu.getContext(0)->pcState(); 975 976 assert(resume_pc.microPC() == 0); 977 978 DPRINTF(MinorInterrupt, "Suspending thread: %d from Execute" 979 " inst: %s\n", inst->id.threadId, *inst); 980 981 cpu.stats.numFetchSuspends++; 982 983 updateBranchData(BranchData::SuspendThread, inst, resume_pc, 984 branch); 985 } 986 } 987 988 return completed_inst; 989} 990 991void 992Execute::commit(bool only_commit_microops, bool discard, BranchData &branch) 993{ 994 Fault fault = NoFault; 995 Cycles now = cpu.curCycle(); 996 997 /** 998 * Try and execute as many instructions from the end of FU pipelines as 999 * possible. This *doesn't* include actually advancing the pipelines. 1000 * 1001 * We do this by looping on the front of the inFlightInsts queue for as 1002 * long as we can find the desired instruction at the end of the 1003 * functional unit it was issued to without seeing a branch or a fault. 1004 * In this function, these terms are used: 1005 * complete -- The instruction has finished its passage through 1006 * its functional unit and its fate has been decided 1007 * (committed, discarded, issued to the memory system) 1008 * commit -- The instruction is complete(d), not discarded and has 1009 * its effects applied to the CPU state 1010 * discard(ed) -- The instruction is complete but not committed 1011 * as its streamSeqNum disagrees with the current 1012 * Execute::streamSeqNum 1013 * 1014 * Commits are also possible from two other places: 1015 * 1016 * 1) Responses returning from the LSQ 1017 * 2) Mem ops issued to the LSQ ('committed' from the FUs) earlier 1018 * than their position in the inFlightInsts queue, but after all 1019 * their dependencies are resolved. 1020 */ 1021 1022 /* Has an instruction been completed? Once this becomes false, we stop 1023 * trying to complete instructions. */ 1024 bool completed_inst = true; 1025 1026 /* Number of insts committed this cycle to check against commitLimit */ 1027 unsigned int num_insts_committed = 0; 1028 1029 /* Number of memory access instructions committed to check against 1030 * memCommitLimit */ 1031 unsigned int num_mem_refs_committed = 0; 1032 1033 if (only_commit_microops && !inFlightInsts->empty()) { 1034 DPRINTF(MinorInterrupt, "Only commit microops %s %d\n", 1035 *(inFlightInsts->front().inst), 1036 lastCommitWasEndOfMacroop); 1037 } 1038 1039 while (!inFlightInsts->empty() && /* Some more instructions to process */ 1040 !branch.isStreamChange() && /* No real branch */ 1041 fault == NoFault && /* No faults */ 1042 completed_inst && /* Still finding instructions to execute */ 1043 num_insts_committed != commitLimit /* Not reached commit limit */ 1044 ) 1045 { 1046 if (only_commit_microops) { 1047 DPRINTF(MinorInterrupt, "Committing tail of insts before" 1048 " interrupt: %s\n", 1049 *(inFlightInsts->front().inst)); 1050 } 1051 1052 QueuedInst *head_inflight_inst = &(inFlightInsts->front()); 1053 1054 InstSeqNum head_exec_seq_num = 1055 head_inflight_inst->inst->id.execSeqNum; 1056 1057 /* The instruction we actually process if completed_inst 1058 * remains true to the end of the loop body. 1059 * Start by considering the the head of the in flight insts queue */ 1060 MinorDynInstPtr inst = head_inflight_inst->inst; 1061 1062 bool committed_inst = false; 1063 bool discard_inst = false; 1064 bool completed_mem_ref = false; 1065 bool issued_mem_ref = false; 1066 bool early_memory_issue = false; 1067 1068 /* Must set this again to go around the loop */ 1069 completed_inst = false; 1070 1071 /* If we're just completing a macroop before an interrupt or drain, 1072 * can we stil commit another microop (rather than a memory response) 1073 * without crosing into the next full instruction? */ 1074 bool can_commit_insts = !inFlightInsts->empty() && 1075 !(only_commit_microops && lastCommitWasEndOfMacroop); 1076 1077 /* Can we find a mem response for this inst */ 1078 LSQ::LSQRequestPtr mem_response = 1079 (inst->inLSQ ? lsq.findResponse(inst) : NULL); 1080 1081 DPRINTF(MinorExecute, "Trying to commit canCommitInsts: %d\n", 1082 can_commit_insts); 1083 1084 /* Test for PC events after every instruction */ 1085 if (isInbetweenInsts() && tryPCEvents()) { 1086 ThreadContext *thread = cpu.getContext(0); 1087 1088 /* Branch as there was a change in PC */ 1089 updateBranchData(BranchData::UnpredictedBranch, 1090 MinorDynInst::bubble(), thread->pcState(), branch); 1091 } else if (mem_response && 1092 num_mem_refs_committed < memoryCommitLimit) 1093 { 1094 /* Try to commit from the memory responses next */ 1095 discard_inst = inst->id.streamSeqNum != streamSeqNum || 1096 discard; 1097 1098 DPRINTF(MinorExecute, "Trying to commit mem response: %s\n", 1099 *inst); 1100 1101 /* Complete or discard the response */ 1102 if (discard_inst) { 1103 DPRINTF(MinorExecute, "Discarding mem inst: %s as its" 1104 " stream state was unexpected, expected: %d\n", 1105 *inst, streamSeqNum); 1106 1107 lsq.popResponse(mem_response); 1108 } else { 1109 handleMemResponse(inst, mem_response, branch, fault); 1110 committed_inst = true; 1111 } 1112 1113 completed_mem_ref = true; 1114 completed_inst = true; 1115 } else if (can_commit_insts) { 1116 /* If true, this instruction will, subject to timing tweaks, 1117 * be considered for completion. try_to_commit flattens 1118 * the `if' tree a bit and allows other tests for inst 1119 * commit to be inserted here. */ 1120 bool try_to_commit = false; 1121 1122 /* Try and issue memory ops early if they: 1123 * - Can push a request into the LSQ 1124 * - Have reached the end of their FUs 1125 * - Have had all their dependencies satisfied 1126 * - Are from the right stream 1127 * 1128 * For any other case, leave it to the normal instruction 1129 * issue below to handle them. 1130 */ 1131 if (!inFUMemInsts->empty() && lsq.canRequest()) { 1132 DPRINTF(MinorExecute, "Trying to commit from mem FUs\n"); 1133 1134 const MinorDynInstPtr head_mem_ref_inst = 1135 inFUMemInsts->front().inst; 1136 FUPipeline *fu = funcUnits[head_mem_ref_inst->fuIndex]; 1137 const MinorDynInstPtr &fu_inst = fu->front().inst; 1138 1139 /* Use this, possibly out of order, inst as the one 1140 * to 'commit'/send to the LSQ */ 1141 if (!fu_inst->isBubble() && 1142 !fu_inst->inLSQ && 1143 fu_inst->canEarlyIssue && 1144 streamSeqNum == fu_inst->id.streamSeqNum && 1145 head_exec_seq_num > fu_inst->instToWaitFor) 1146 { 1147 DPRINTF(MinorExecute, "Issuing mem ref early" 1148 " inst: %s instToWaitFor: %d\n", 1149 *(fu_inst), fu_inst->instToWaitFor); 1150 1151 inst = fu_inst; 1152 try_to_commit = true; 1153 early_memory_issue = true; 1154 completed_inst = true; 1155 } 1156 } 1157 1158 /* Try and commit FU-less insts */ 1159 if (!completed_inst && inst->isNoCostInst()) { 1160 DPRINTF(MinorExecute, "Committing no cost inst: %s", *inst); 1161 1162 try_to_commit = true; 1163 completed_inst = true; 1164 } 1165 1166 /* Try to issue from the ends of FUs and the inFlightInsts 1167 * queue */ 1168 if (!completed_inst && !inst->inLSQ) { 1169 DPRINTF(MinorExecute, "Trying to commit from FUs\n"); 1170 1171 /* Try to commit from a functional unit */ 1172 /* Is the head inst of the expected inst's FU actually the 1173 * expected inst? */ 1174 QueuedInst &fu_inst = 1175 funcUnits[inst->fuIndex]->front(); 1176 InstSeqNum fu_inst_seq_num = fu_inst.inst->id.execSeqNum; 1177 1178 if (fu_inst.inst->isBubble()) { 1179 /* No instruction ready */ 1180 completed_inst = false; 1181 } else if (fu_inst_seq_num != head_exec_seq_num) { 1182 /* Past instruction: we must have already executed it 1183 * in the same cycle and so the head inst isn't 1184 * actually at the end of its pipeline 1185 * Future instruction: handled above and only for 1186 * mem refs on their way to the LSQ */ 1187 } else /* if (fu_inst_seq_num == head_exec_seq_num) */ { 1188 /* All instructions can be committed if they have the 1189 * right execSeqNum and there are no in-flight 1190 * mem insts before us */ 1191 try_to_commit = true; 1192 completed_inst = true; 1193 } 1194 } 1195 1196 if (try_to_commit) { 1197 discard_inst = inst->id.streamSeqNum != streamSeqNum || 1198 discard; 1199 1200 /* Is this instruction discardable as its streamSeqNum 1201 * doesn't match? */ 1202 if (!discard_inst) { 1203 /* Try to commit or discard a non-memory instruction. 1204 * Memory ops are actually 'committed' from this FUs 1205 * and 'issued' into the memory system so we need to 1206 * account for them later (commit_was_mem_issue gets 1207 * set) */ 1208 if (inst->extraCommitDelayExpr) { 1209 DPRINTF(MinorExecute, "Evaluating expression for" 1210 " extra commit delay inst: %s\n", *inst); 1211 1212 ThreadContext *thread = 1213 cpu.getContext(inst->id.threadId); 1214 1215 TimingExprEvalContext context(inst->staticInst, 1216 thread, NULL); 1217 1218 uint64_t extra_delay = inst->extraCommitDelayExpr-> 1219 eval(context); 1220 1221 DPRINTF(MinorExecute, "Extra commit delay expr" 1222 " result: %d\n", extra_delay); 1223 1224 if (extra_delay < 128) { 1225 inst->extraCommitDelay += Cycles(extra_delay); 1226 } else { 1227 DPRINTF(MinorExecute, "Extra commit delay was" 1228 " very long: %d\n", extra_delay); 1229 } 1230 inst->extraCommitDelayExpr = NULL; 1231 } 1232 1233 /* Move the extraCommitDelay from the instruction 1234 * into the minimumCommitCycle */ 1235 if (inst->extraCommitDelay != Cycles(0)) { 1236 inst->minimumCommitCycle = cpu.curCycle() + 1237 inst->extraCommitDelay; 1238 inst->extraCommitDelay = Cycles(0); 1239 } 1240 1241 /* @todo Think about making lastMemBarrier be 1242 * MAX_UINT_64 to avoid using 0 as a marker value */ 1243 if (!inst->isFault() && inst->isMemRef() && 1244 lsq.getLastMemBarrier() < 1245 inst->id.execSeqNum && 1246 lsq.getLastMemBarrier() != 0) 1247 { 1248 DPRINTF(MinorExecute, "Not committing inst: %s yet" 1249 " as there are incomplete barriers in flight\n", 1250 *inst); 1251 completed_inst = false; 1252 } else if (inst->minimumCommitCycle > now) { 1253 DPRINTF(MinorExecute, "Not committing inst: %s yet" 1254 " as it wants to be stalled for %d more cycles\n", 1255 *inst, inst->minimumCommitCycle - now); 1256 completed_inst = false; 1257 } else { 1258 completed_inst = commitInst(inst, 1259 early_memory_issue, branch, fault, 1260 committed_inst, issued_mem_ref); 1261 } 1262 } else { 1263 /* Discard instruction */ 1264 completed_inst = true; 1265 } 1266 1267 if (completed_inst) { 1268 /* Allow the pipeline to advance. If the FU head 1269 * instruction wasn't the inFlightInsts head 1270 * but had already been committed, it would have 1271 * unstalled the pipeline before here */ 1272 if (inst->fuIndex != noCostFUIndex) 1273 funcUnits[inst->fuIndex]->stalled = false; 1274 } 1275 } 1276 } else { 1277 DPRINTF(MinorExecute, "No instructions to commit\n"); 1278 completed_inst = false; 1279 } 1280 1281 /* All discardable instructions must also be 'completed' by now */ 1282 assert(!(discard_inst && !completed_inst)); 1283 1284 /* Instruction committed but was discarded due to streamSeqNum 1285 * mismatch */ 1286 if (discard_inst) { 1287 DPRINTF(MinorExecute, "Discarding inst: %s as its stream" 1288 " state was unexpected, expected: %d\n", 1289 *inst, streamSeqNum); 1290 1291 if (fault == NoFault) 1292 cpu.stats.numDiscardedOps++; 1293 } 1294 1295 /* Mark the mem inst as being in the LSQ */ 1296 if (issued_mem_ref) { 1297 inst->fuIndex = 0; 1298 inst->inLSQ = true; 1299 } 1300 1301 /* Pop issued (to LSQ) and discarded mem refs from the inFUMemInsts 1302 * as they've *definitely* exited the FUs */ 1303 if (completed_inst && inst->isMemRef()) { 1304 /* The MemRef could have been discarded from the FU or the memory 1305 * queue, so just check an FU instruction */ 1306 if (!inFUMemInsts->empty() && 1307 inFUMemInsts->front().inst == inst) 1308 { 1309 inFUMemInsts->pop(); 1310 } 1311 } 1312 1313 if (completed_inst && !(issued_mem_ref && fault == NoFault)) { 1314 /* Note that this includes discarded insts */ 1315 DPRINTF(MinorExecute, "Completed inst: %s\n", *inst); 1316 1317 /* Got to the end of a full instruction? */ 1318 lastCommitWasEndOfMacroop = inst->isFault() || 1319 inst->isLastOpInInst(); 1320 1321 /* lastPredictionSeqNum is kept as a convenience to prevent its 1322 * value from changing too much on the minorview display */ 1323 lastPredictionSeqNum = inst->id.predictionSeqNum; 1324 1325 /* Finished with the inst, remove it from the inst queue and 1326 * clear its dependencies */ 1327 inFlightInsts->pop(); 1328 1329 /* Complete barriers in the LSQ/move to store buffer */ 1330 if (inst->isInst() && inst->staticInst->isMemBarrier()) { 1331 DPRINTF(MinorMem, "Completing memory barrier" 1332 " inst: %s committed: %d\n", *inst, committed_inst); 1333 lsq.completeMemBarrierInst(inst, committed_inst); 1334 } 1335 1336 scoreboard.clearInstDests(inst, inst->isMemRef()); 1337 } 1338 1339 /* Handle per-cycle instruction counting */ 1340 if (committed_inst) { 1341 bool is_no_cost_inst = inst->isNoCostInst(); 1342 1343 /* Don't show no cost instructions as having taken a commit 1344 * slot */ 1345 if (DTRACE(MinorTrace) && !is_no_cost_inst) 1346 instsBeingCommitted.insts[num_insts_committed] = inst; 1347 1348 if (!is_no_cost_inst) 1349 num_insts_committed++; 1350 1351 if (num_insts_committed == commitLimit) 1352 DPRINTF(MinorExecute, "Reached inst commit limit\n"); 1353 1354 /* Re-set the time of the instruction if that's required for 1355 * tracing */ 1356 if (inst->traceData) { 1357 if (setTraceTimeOnCommit) 1358 inst->traceData->setWhen(curTick()); 1359 inst->traceData->dump(); 1360 } 1361 1362 if (completed_mem_ref) 1363 num_mem_refs_committed++; 1364 1365 if (num_mem_refs_committed == memoryCommitLimit) 1366 DPRINTF(MinorExecute, "Reached mem ref commit limit\n"); 1367 } 1368 } 1369} 1370 1371bool 1372Execute::isInbetweenInsts() const 1373{ 1374 return lastCommitWasEndOfMacroop && 1375 !lsq.accessesInFlight(); 1376} 1377 1378void 1379Execute::evaluate() 1380{ 1381 inputBuffer.setTail(*inp.outputWire); 1382 BranchData &branch = *out.inputWire; 1383 1384 const ForwardInstData *insts_in = getInput(); 1385 1386 /* Do all the cycle-wise activities for dcachePort here to potentially 1387 * free up input spaces in the LSQ's requests queue */ 1388 lsq.step(); 1389 1390 /* Has an interrupt been signalled? This may not be acted on 1391 * straighaway so this is different from took_interrupt below */ 1392 bool interrupted = false; 1393 /* If there was an interrupt signalled, was it acted on now? */ 1394 bool took_interrupt = false; 1395 1396 if (cpu.getInterruptController(0)) { 1397 /* This is here because it seems that after drainResume the 1398 * interrupt controller isn't always set */ 1399 interrupted = drainState == NotDraining && isInterrupted(0); 1400 } else { 1401 DPRINTF(MinorInterrupt, "No interrupt controller\n"); 1402 } 1403 1404 unsigned int num_issued = 0; 1405 1406 if (DTRACE(MinorTrace)) { 1407 /* Empty the instsBeingCommitted for MinorTrace */ 1408 instsBeingCommitted.bubbleFill(); 1409 } 1410 1411 /* THREAD threadId on isInterrupted */ 1412 /* Act on interrupts */ 1413 if (interrupted && isInbetweenInsts()) { 1414 took_interrupt = takeInterrupt(0, branch); 1415 /* Clear interrupted if no interrupt was actually waiting */ 1416 interrupted = took_interrupt; 1417 } 1418 1419 if (took_interrupt) { 1420 /* Do no commit/issue this cycle */ 1421 } else if (!branch.isBubble()) { 1422 /* It's important that this is here to carry Fetch1 wakeups to Fetch1 1423 * without overwriting them */ 1424 DPRINTF(MinorInterrupt, "Execute skipping a cycle to allow old" 1425 " branch to complete\n"); 1426 } else { 1427 if (interrupted) { 1428 if (inFlightInsts->empty()) { 1429 DPRINTF(MinorInterrupt, "Waiting but no insts\n"); 1430 } else { 1431 DPRINTF(MinorInterrupt, "Waiting for end of inst before" 1432 " signalling interrupt\n"); 1433 } 1434 } 1435 1436 /* commit can set stalled flags observable to issue and so *must* be 1437 * called first */ 1438 if (drainState != NotDraining) { 1439 if (drainState == DrainCurrentInst) { 1440 /* Commit only micro-ops, don't kill anything else */ 1441 commit(true, false, branch); 1442 1443 if (isInbetweenInsts()) 1444 setDrainState(DrainHaltFetch); 1445 1446 /* Discard any generated branch */ 1447 branch = BranchData::bubble(); 1448 } else if (drainState == DrainAllInsts) { 1449 /* Kill all instructions */ 1450 while (getInput()) 1451 popInput(); 1452 commit(false, true, branch); 1453 } 1454 } else { 1455 /* Commit micro-ops only if interrupted. Otherwise, commit 1456 * anything you like */ 1457 commit(interrupted, false, branch); 1458 } 1459 1460 /* This will issue merrily even when interrupted in the sure and 1461 * certain knowledge that the interrupt with change the stream */ 1462 if (insts_in) 1463 num_issued = issue(false); 1464 } 1465 1466 /* Halt fetch, but don't do it until we have the current instruction in 1467 * the bag */ 1468 if (drainState == DrainHaltFetch) { 1469 updateBranchData(BranchData::HaltFetch, MinorDynInst::bubble(), 1470 TheISA::PCState(0), branch); 1471 1472 cpu.wakeupOnEvent(Pipeline::ExecuteStageId); 1473 setDrainState(DrainAllInsts); 1474 } 1475 1476 MinorDynInstPtr next_issuable_inst = NULL; 1477 bool can_issue_next = false; 1478 1479 /* Find the next issuable instruction and see if it can be issued */ 1480 if (getInput()) { 1481 MinorDynInstPtr inst = getInput()->insts[inputIndex]; 1482 1483 if (inst->isFault()) { 1484 can_issue_next = true; 1485 } else if (!inst->isBubble()) { 1486 if (cpu.getContext(inst->id.threadId)->status() != 1487 ThreadContext::Suspended) 1488 { 1489 next_issuable_inst = inst; 1490 } 1491 } 1492 } 1493 1494 bool becoming_stalled = true; 1495 1496 /* Advance the pipelines and note whether they still need to be 1497 * advanced */ 1498 for (unsigned int i = 0; i < numFuncUnits; i++) { 1499 FUPipeline *fu = funcUnits[i]; 1500 1501 fu->advance(); 1502 1503 /* If we need to go again, the pipeline will have been left or set 1504 * to be unstalled */ 1505 if (fu->occupancy != 0 && !fu->stalled) 1506 becoming_stalled = false; 1507 1508 /* Could we possibly issue the next instruction? This is quite 1509 * an expensive test */ 1510 if (next_issuable_inst && !fu->stalled && 1511 scoreboard.canInstIssue(next_issuable_inst, 1512 NULL, NULL, cpu.curCycle() + Cycles(1), 1513 cpu.getContext(next_issuable_inst->id.threadId)) && 1514 fu->provides(next_issuable_inst->staticInst->opClass())) 1515 { 1516 can_issue_next = true; 1517 } 1518 } 1519 1520 bool head_inst_might_commit = false; 1521 1522 /* Could the head in flight insts be committed */ 1523 if (!inFlightInsts->empty()) { 1524 const QueuedInst &head_inst = inFlightInsts->front(); 1525 1526 if (head_inst.inst->isNoCostInst()) { 1527 head_inst_might_commit = true; 1528 } else { 1529 FUPipeline *fu = funcUnits[head_inst.inst->fuIndex]; 1530 1531 /* Head inst is commitable */ 1532 if ((fu->stalled && 1533 fu->front().inst->id == head_inst.inst->id) || 1534 lsq.findResponse(head_inst.inst)) 1535 { 1536 head_inst_might_commit = true; 1537 } 1538 } 1539 } 1540 1541 DPRINTF(Activity, "Need to tick num issued insts: %s%s%s%s%s%s\n", 1542 (num_issued != 0 ? " (issued some insts)" : ""), 1543 (becoming_stalled ? " (becoming stalled)" : "(not becoming stalled)"), 1544 (can_issue_next ? " (can issued next inst)" : ""), 1545 (head_inst_might_commit ? "(head inst might commit)" : ""), 1546 (lsq.needsToTick() ? " (LSQ needs to tick)" : ""), 1547 (interrupted ? " (interrupted)" : "")); 1548 1549 bool need_to_tick = 1550 num_issued != 0 || /* Issued some insts this cycle */ 1551 !becoming_stalled || /* Some FU pipelines can still move */ 1552 can_issue_next || /* Can still issue a new inst */ 1553 head_inst_might_commit || /* Could possible commit the next inst */ 1554 lsq.needsToTick() || /* Must step the dcache port */ 1555 interrupted; /* There are pending interrupts */ 1556 1557 if (!need_to_tick) { 1558 DPRINTF(Activity, "The next cycle might be skippable as there are no" 1559 " advanceable FUs\n"); 1560 } 1561 1562 /* Wake up if we need to tick again */ 1563 if (need_to_tick) 1564 cpu.wakeupOnEvent(Pipeline::ExecuteStageId); 1565 1566 /* Note activity of following buffer */ 1567 if (!branch.isBubble()) 1568 cpu.activityRecorder->activity(); 1569 1570 /* Make sure the input (if any left) is pushed */ 1571 inputBuffer.pushTail(); 1572} 1573 1574void 1575Execute::wakeupFetch(BranchData::Reason reason) 1576{ 1577 BranchData branch; 1578 assert(branch.isBubble()); 1579 1580 /* THREAD thread id */ 1581 ThreadContext *thread = cpu.getContext(0); 1582 1583 /* Force a branch to the current PC (which should be the next inst.) to 1584 * wake up Fetch1 */ 1585 if (!branch.isStreamChange() /* No real branch already happened */) { 1586 DPRINTF(MinorInterrupt, "Waking up Fetch (via Execute) by issuing" 1587 " a branch: %s\n", thread->pcState()); 1588 1589 assert(thread->pcState().microPC() == 0); 1590 1591 updateBranchData(reason, 1592 MinorDynInst::bubble(), thread->pcState(), branch); 1593 } else { 1594 DPRINTF(MinorInterrupt, "Already branching, no need for wakeup\n"); 1595 } 1596 1597 *out.inputWire = branch; 1598 1599 /* Make sure we get ticked */ 1600 cpu.wakeupOnEvent(Pipeline::ExecuteStageId); 1601} 1602 1603void 1604Execute::minorTrace() const 1605{ 1606 std::ostringstream insts; 1607 std::ostringstream stalled; 1608 1609 instsBeingCommitted.reportData(insts); 1610 lsq.minorTrace(); 1611 inputBuffer.minorTrace(); 1612 scoreboard.minorTrace(); 1613 1614 /* Report functional unit stalling in one string */ 1615 unsigned int i = 0; 1616 while (i < numFuncUnits) 1617 { 1618 stalled << (funcUnits[i]->stalled ? '1' : 'E'); 1619 i++; 1620 if (i != numFuncUnits) 1621 stalled << ','; 1622 } 1623 1624 MINORTRACE("insts=%s inputIndex=%d streamSeqNum=%d" 1625 " stalled=%s drainState=%d isInbetweenInsts=%d\n", 1626 insts.str(), inputIndex, streamSeqNum, stalled.str(), drainState, 1627 isInbetweenInsts()); 1628 1629 std::for_each(funcUnits.begin(), funcUnits.end(), 1630 std::mem_fun(&FUPipeline::minorTrace)); 1631 1632 inFlightInsts->minorTrace(); 1633 inFUMemInsts->minorTrace(); 1634} 1635 1636void 1637Execute::drainResume() 1638{ 1639 DPRINTF(Drain, "MinorExecute drainResume\n"); 1640 1641 setDrainState(NotDraining); 1642 1643 /* Wakeup fetch and keep the pipeline running until that branch takes 1644 * effect */ 1645 wakeupFetch(BranchData::WakeupFetch); 1646 cpu.wakeupOnEvent(Pipeline::ExecuteStageId); 1647} 1648 1649std::ostream &operator <<(std::ostream &os, Execute::DrainState state) 1650{ 1651 switch (state) 1652 { 1653 case Execute::NotDraining: 1654 os << "NotDraining"; 1655 break; 1656 case Execute::DrainCurrentInst: 1657 os << "DrainCurrentInst"; 1658 break; 1659 case Execute::DrainHaltFetch: 1660 os << "DrainHaltFetch"; 1661 break; 1662 case Execute::DrainAllInsts: 1663 os << "DrainAllInsts"; 1664 break; 1665 default: 1666 os << "Drain-" << static_cast<int>(state); 1667 break; 1668 } 1669 1670 return os; 1671} 1672 1673void 1674Execute::setDrainState(DrainState state) 1675{ 1676 DPRINTF(Drain, "setDrainState: %s\n", state); 1677 drainState = state; 1678} 1679 1680unsigned int 1681Execute::drain() 1682{ 1683 DPRINTF(Drain, "MinorExecute drain\n"); 1684 1685 if (drainState == NotDraining) { 1686 cpu.wakeupOnEvent(Pipeline::ExecuteStageId); 1687 1688 /* Go to DrainCurrentInst if we're between microops 1689 * or waiting on an unbufferable memory operation. 1690 * Otherwise we can go straight to DrainHaltFetch 1691 */ 1692 if (isInbetweenInsts()) 1693 setDrainState(DrainHaltFetch); 1694 else 1695 setDrainState(DrainCurrentInst); 1696 } 1697 1698 return (isDrained() ? 0 : 1); 1699} 1700 1701bool 1702Execute::isDrained() 1703{ 1704 return drainState == DrainAllInsts && 1705 inputBuffer.empty() && 1706 inFlightInsts->empty() && 1707 lsq.isDrained(); 1708} 1709 1710Execute::~Execute() 1711{ 1712 for (unsigned int i = 0; i < numFuncUnits; i++) 1713 delete funcUnits[i]; 1714 1715 delete inFlightInsts; 1716} 1717 1718bool 1719Execute::instIsRightStream(MinorDynInstPtr inst) 1720{ 1721 return inst->id.streamSeqNum == streamSeqNum; 1722} 1723 1724bool 1725Execute::instIsHeadInst(MinorDynInstPtr inst) 1726{ 1727 bool ret = false; 1728 1729 if (!inFlightInsts->empty()) 1730 ret = inFlightInsts->front().inst->id == inst->id; 1731 1732 return ret; 1733} 1734 1735MinorCPU::MinorCPUPort & 1736Execute::getDcachePort() 1737{ 1738 return lsq.getDcachePort(); 1739} 1740 1741} 1742