commit_impl.hh revision 2632:1bb2f91485ea
1/* 2 * Copyright (c) 2004-2005 The Regents of The University of Michigan 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are 7 * met: redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer; 9 * redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution; 12 * neither the name of the copyright holders nor the names of its 13 * contributors may be used to endorse or promote products derived from 14 * this software without specific prior written permission. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 17 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 18 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 19 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 20 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 21 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 22 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29#include "base/timebuf.hh" 30#include "cpu/o3/commit.hh" 31#include "cpu/exetrace.hh" 32 33template <class Impl> 34SimpleCommit<Impl>::SimpleCommit(Params ¶ms) 35 : dcacheInterface(params.dcacheInterface), 36 iewToCommitDelay(params.iewToCommitDelay), 37 renameToROBDelay(params.renameToROBDelay), 38 renameWidth(params.renameWidth), 39 iewWidth(params.executeWidth), 40 commitWidth(params.commitWidth) 41{ 42 _status = Idle; 43} 44 45template <class Impl> 46void 47SimpleCommit<Impl>::regStats() 48{ 49 commitCommittedInsts 50 .name(name() + ".commitCommittedInsts") 51 .desc("The number of committed instructions") 52 .prereq(commitCommittedInsts); 53 commitSquashedInsts 54 .name(name() + ".commitSquashedInsts") 55 .desc("The number of squashed insts skipped by commit") 56 .prereq(commitSquashedInsts); 57 commitSquashEvents 58 .name(name() + ".commitSquashEvents") 59 .desc("The number of times commit is told to squash") 60 .prereq(commitSquashEvents); 61 commitNonSpecStalls 62 .name(name() + ".commitNonSpecStalls") 63 .desc("The number of times commit has been forced to stall to " 64 "communicate backwards") 65 .prereq(commitNonSpecStalls); 66 commitCommittedBranches 67 .name(name() + ".commitCommittedBranches") 68 .desc("The number of committed branches") 69 .prereq(commitCommittedBranches); 70 commitCommittedLoads 71 .name(name() + ".commitCommittedLoads") 72 .desc("The number of committed loads") 73 .prereq(commitCommittedLoads); 74 commitCommittedMemRefs 75 .name(name() + ".commitCommittedMemRefs") 76 .desc("The number of committed memory references") 77 .prereq(commitCommittedMemRefs); 78 branchMispredicts 79 .name(name() + ".branchMispredicts") 80 .desc("The number of times a branch was mispredicted") 81 .prereq(branchMispredicts); 82 n_committed_dist 83 .init(0,commitWidth,1) 84 .name(name() + ".COM:committed_per_cycle") 85 .desc("Number of insts commited each cycle") 86 .flags(Stats::pdf) 87 ; 88} 89 90template <class Impl> 91void 92SimpleCommit<Impl>::setCPU(FullCPU *cpu_ptr) 93{ 94 DPRINTF(Commit, "Commit: Setting CPU pointer.\n"); 95 cpu = cpu_ptr; 96} 97 98template <class Impl> 99void 100SimpleCommit<Impl>::setTimeBuffer(TimeBuffer<TimeStruct> *tb_ptr) 101{ 102 DPRINTF(Commit, "Commit: Setting time buffer pointer.\n"); 103 timeBuffer = tb_ptr; 104 105 // Setup wire to send information back to IEW. 106 toIEW = timeBuffer->getWire(0); 107 108 // Setup wire to read data from IEW (for the ROB). 109 robInfoFromIEW = timeBuffer->getWire(-iewToCommitDelay); 110} 111 112template <class Impl> 113void 114SimpleCommit<Impl>::setRenameQueue(TimeBuffer<RenameStruct> *rq_ptr) 115{ 116 DPRINTF(Commit, "Commit: Setting rename queue pointer.\n"); 117 renameQueue = rq_ptr; 118 119 // Setup wire to get instructions from rename (for the ROB). 120 fromRename = renameQueue->getWire(-renameToROBDelay); 121} 122 123template <class Impl> 124void 125SimpleCommit<Impl>::setIEWQueue(TimeBuffer<IEWStruct> *iq_ptr) 126{ 127 DPRINTF(Commit, "Commit: Setting IEW queue pointer.\n"); 128 iewQueue = iq_ptr; 129 130 // Setup wire to get instructions from IEW. 131 fromIEW = iewQueue->getWire(-iewToCommitDelay); 132} 133 134template <class Impl> 135void 136SimpleCommit<Impl>::setROB(ROB *rob_ptr) 137{ 138 DPRINTF(Commit, "Commit: Setting ROB pointer.\n"); 139 rob = rob_ptr; 140} 141 142template <class Impl> 143void 144SimpleCommit<Impl>::tick() 145{ 146 // If the ROB is currently in its squash sequence, then continue 147 // to squash. In this case, commit does not do anything. Otherwise 148 // run commit. 149 if (_status == ROBSquashing) { 150 if (rob->isDoneSquashing()) { 151 _status = Running; 152 } else { 153 rob->doSquash(); 154 155 // Send back sequence number of tail of ROB, so other stages 156 // can squash younger instructions. Note that really the only 157 // stage that this is important for is the IEW stage; other 158 // stages can just clear all their state as long as selective 159 // replay isn't used. 160 toIEW->commitInfo.doneSeqNum = rob->readTailSeqNum(); 161 toIEW->commitInfo.robSquashing = true; 162 } 163 } else { 164 commit(); 165 } 166 167 markCompletedInsts(); 168 169 // Writeback number of free ROB entries here. 170 DPRINTF(Commit, "Commit: ROB has %d free entries.\n", 171 rob->numFreeEntries()); 172 toIEW->commitInfo.freeROBEntries = rob->numFreeEntries(); 173} 174 175template <class Impl> 176void 177SimpleCommit<Impl>::commit() 178{ 179 ////////////////////////////////////// 180 // Check for interrupts 181 ////////////////////////////////////// 182 183 // Process interrupts if interrupts are enabled and not in PAL mode. 184 // Take the PC from commit and write it to the IPR, then squash. The 185 // interrupt completing will take care of restoring the PC from that value 186 // in the IPR. Look at IPR[EXC_ADDR]; 187 // hwrei() is what resets the PC to the place where instruction execution 188 // beings again. 189#if FULL_SYSTEM 190 if (//checkInterrupts && 191 cpu->check_interrupts() && 192 !cpu->inPalMode(readCommitPC())) { 193 // Will need to squash all instructions currently in flight and have 194 // the interrupt handler restart at the last non-committed inst. 195 // Most of that can be handled through the trap() function. The 196 // processInterrupts() function really just checks for interrupts 197 // and then calls trap() if there is an interrupt present. 198 199 // CPU will handle implementation of the interrupt. 200 cpu->processInterrupts(); 201 } 202#endif // FULL_SYSTEM 203 204 //////////////////////////////////// 205 // Check for squash signal, handle that first 206 //////////////////////////////////// 207 208 // Want to mainly check if the IEW stage is telling the ROB to squash. 209 // Should I also check if the commit stage is telling the ROB to squah? 210 // This might be necessary to keep the same timing between the IQ and 211 // the ROB... 212 if (fromIEW->squash) { 213 DPRINTF(Commit, "Commit: Squashing instructions in the ROB.\n"); 214 215 _status = ROBSquashing; 216 217 InstSeqNum squashed_inst = fromIEW->squashedSeqNum; 218 219 rob->squash(squashed_inst); 220 221 // Send back the sequence number of the squashed instruction. 222 toIEW->commitInfo.doneSeqNum = squashed_inst; 223 224 // Send back the squash signal to tell stages that they should squash. 225 toIEW->commitInfo.squash = true; 226 227 // Send back the rob squashing signal so other stages know that the 228 // ROB is in the process of squashing. 229 toIEW->commitInfo.robSquashing = true; 230 231 toIEW->commitInfo.branchMispredict = fromIEW->branchMispredict; 232 233 toIEW->commitInfo.branchTaken = fromIEW->branchTaken; 234 235 toIEW->commitInfo.nextPC = fromIEW->nextPC; 236 237 toIEW->commitInfo.mispredPC = fromIEW->mispredPC; 238 239 if (toIEW->commitInfo.branchMispredict) { 240 ++branchMispredicts; 241 } 242 } 243 244 if (_status != ROBSquashing) { 245 // If we're not currently squashing, then get instructions. 246 getInsts(); 247 248 // Try to commit any instructions. 249 commitInsts(); 250 } 251 252 // If the ROB is empty, we can set this stage to idle. Use this 253 // in the future when the Idle status will actually be utilized. 254#if 0 255 if (rob->isEmpty()) { 256 DPRINTF(Commit, "Commit: ROB is empty. Status changed to idle.\n"); 257 _status = Idle; 258 // Schedule an event so that commit will actually wake up 259 // once something gets put in the ROB. 260 } 261#endif 262} 263 264// Loop that goes through as many instructions in the ROB as possible and 265// tries to commit them. The actual work for committing is done by the 266// commitHead() function. 267template <class Impl> 268void 269SimpleCommit<Impl>::commitInsts() 270{ 271 //////////////////////////////////// 272 // Handle commit 273 // Note that commit will be handled prior to the ROB so that the ROB 274 // only tries to commit instructions it has in this current cycle, and 275 // not instructions it is writing in during this cycle. 276 // Can't commit and squash things at the same time... 277 //////////////////////////////////// 278 279 if (rob->isEmpty()) 280 return; 281 282 DynInstPtr head_inst = rob->readHeadInst(); 283 284 unsigned num_committed = 0; 285 286 // Commit as many instructions as possible until the commit bandwidth 287 // limit is reached, or it becomes impossible to commit any more. 288 while (!rob->isEmpty() && 289 head_inst->readyToCommit() && 290 num_committed < commitWidth) 291 { 292 DPRINTF(Commit, "Commit: Trying to commit head instruction.\n"); 293 294 // If the head instruction is squashed, it is ready to retire at any 295 // time. However, we need to avoid updating any other state 296 // incorrectly if it's already been squashed. 297 if (head_inst->isSquashed()) { 298 299 DPRINTF(Commit, "Commit: Retiring squashed instruction from " 300 "ROB.\n"); 301 302 // Tell ROB to retire head instruction. This retires the head 303 // inst in the ROB without affecting any other stages. 304 rob->retireHead(); 305 306 ++commitSquashedInsts; 307 308 } else { 309 // Increment the total number of non-speculative instructions 310 // executed. 311 // Hack for now: it really shouldn't happen until after the 312 // commit is deemed to be successful, but this count is needed 313 // for syscalls. 314 cpu->funcExeInst++; 315 316 // Try to commit the head instruction. 317 bool commit_success = commitHead(head_inst, num_committed); 318 319 // Update what instruction we are looking at if the commit worked. 320 if (commit_success) { 321 ++num_committed; 322 323 // Send back which instruction has been committed. 324 // @todo: Update this later when a wider pipeline is used. 325 // Hmm, can't really give a pointer here...perhaps the 326 // sequence number instead (copy). 327 toIEW->commitInfo.doneSeqNum = head_inst->seqNum; 328 329 ++commitCommittedInsts; 330 331 if (!head_inst->isNop()) { 332 cpu->instDone(); 333 } 334 } else { 335 break; 336 } 337 } 338 339 // Update the pointer to read the next instruction in the ROB. 340 head_inst = rob->readHeadInst(); 341 } 342 343 DPRINTF(CommitRate, "%i\n", num_committed); 344 n_committed_dist.sample(num_committed); 345} 346 347template <class Impl> 348bool 349SimpleCommit<Impl>::commitHead(DynInstPtr &head_inst, unsigned inst_num) 350{ 351 // Make sure instruction is valid 352 assert(head_inst); 353 354 // If the instruction is not executed yet, then it is a non-speculative 355 // or store inst. Signal backwards that it should be executed. 356 if (!head_inst->isExecuted()) { 357 // Keep this number correct. We have not yet actually executed 358 // and committed this instruction. 359 cpu->funcExeInst--; 360 361 if (head_inst->isNonSpeculative()) { 362 DPRINTF(Commit, "Commit: Encountered a store or non-speculative " 363 "instruction at the head of the ROB, PC %#x.\n", 364 head_inst->readPC()); 365 366 toIEW->commitInfo.nonSpecSeqNum = head_inst->seqNum; 367 368 // Change the instruction so it won't try to commit again until 369 // it is executed. 370 head_inst->clearCanCommit(); 371 372 ++commitNonSpecStalls; 373 374 return false; 375 } else { 376 panic("Commit: Trying to commit un-executed instruction " 377 "of unknown type!\n"); 378 } 379 } 380 381 // Now check if it's one of the special trap or barrier or 382 // serializing instructions. 383 if (head_inst->isThreadSync() || 384 head_inst->isSerializing() || 385 head_inst->isMemBarrier() || 386 head_inst->isWriteBarrier() ) 387 { 388 // Not handled for now. Mem barriers and write barriers are safe 389 // to simply let commit as memory accesses only happen once they 390 // reach the head of commit. Not sure about the other two. 391 panic("Serializing or barrier instructions" 392 " are not handled yet.\n"); 393 } 394 395 // Check if the instruction caused a fault. If so, trap. 396 Fault inst_fault = head_inst->getFault(); 397 398 if (inst_fault != NoFault) { 399 if (!head_inst->isNop()) { 400#if FULL_SYSTEM 401 cpu->trap(inst_fault); 402#else // !FULL_SYSTEM 403 panic("fault (%d) detected @ PC %08p", inst_fault, 404 head_inst->PC); 405#endif // FULL_SYSTEM 406 } 407 } 408 409 // Check if we're really ready to commit. If not then return false. 410 // I'm pretty sure all instructions should be able to commit if they've 411 // reached this far. For now leave this in as a check. 412 if (!rob->isHeadReady()) { 413 panic("Commit: Unable to commit head instruction!\n"); 414 return false; 415 } 416 417 // If it's a branch, then send back branch prediction update info 418 // to the fetch stage. 419 // This should be handled in the iew stage if a mispredict happens... 420 421 if (head_inst->isControl()) { 422 423#if 0 424 toIEW->nextPC = head_inst->readPC(); 425 //Maybe switch over to BTB incorrect. 426 toIEW->btbMissed = head_inst->btbMiss(); 427 toIEW->target = head_inst->nextPC; 428 //Maybe also include global history information. 429 //This simple version will have no branch prediction however. 430#endif 431 432 ++commitCommittedBranches; 433 } 434 435 // Now that the instruction is going to be committed, finalize its 436 // trace data. 437 if (head_inst->traceData) { 438 head_inst->traceData->finalize(); 439 } 440 441 //Finally clear the head ROB entry. 442 rob->retireHead(); 443 444 // Return true to indicate that we have committed an instruction. 445 return true; 446} 447 448template <class Impl> 449void 450SimpleCommit<Impl>::getInsts() 451{ 452 ////////////////////////////////////// 453 // Handle ROB functions 454 ////////////////////////////////////// 455 456 // Read any issued instructions and place them into the ROB. Do this 457 // prior to squashing to avoid having instructions in the ROB that 458 // don't get squashed properly. 459 int insts_to_process = min((int)renameWidth, fromRename->size); 460 461 for (int inst_num = 0; 462 inst_num < insts_to_process; 463 ++inst_num) 464 { 465 if (!fromRename->insts[inst_num]->isSquashed()) { 466 DPRINTF(Commit, "Commit: Inserting PC %#x into ROB.\n", 467 fromRename->insts[inst_num]->readPC()); 468 rob->insertInst(fromRename->insts[inst_num]); 469 } else { 470 DPRINTF(Commit, "Commit: Instruction %i PC %#x was " 471 "squashed, skipping.\n", 472 fromRename->insts[inst_num]->seqNum, 473 fromRename->insts[inst_num]->readPC()); 474 } 475 } 476} 477 478template <class Impl> 479void 480SimpleCommit<Impl>::markCompletedInsts() 481{ 482 // Grab completed insts out of the IEW instruction queue, and mark 483 // instructions completed within the ROB. 484 for (int inst_num = 0; 485 inst_num < fromIEW->size && fromIEW->insts[inst_num]; 486 ++inst_num) 487 { 488 DPRINTF(Commit, "Commit: Marking PC %#x, SN %i ready within ROB.\n", 489 fromIEW->insts[inst_num]->readPC(), 490 fromIEW->insts[inst_num]->seqNum); 491 492 // Mark the instruction as ready to commit. 493 fromIEW->insts[inst_num]->setCanCommit(); 494 } 495} 496 497template <class Impl> 498uint64_t 499SimpleCommit<Impl>::readCommitPC() 500{ 501 return rob->readHeadPC(); 502} 503