commit_impl.hh revision 1061
112855Sgabeblack@google.com// @todo: Bug when something reaches execute, and mispredicts, but is never 212855Sgabeblack@google.com// put into the ROB because the ROB is full. Need rename stage to predict 312855Sgabeblack@google.com// the free ROB entries better. 412855Sgabeblack@google.com 512855Sgabeblack@google.com#ifndef __COMMIT_IMPL_HH__ 612855Sgabeblack@google.com#define __COMMIT_IMPL_HH__ 712855Sgabeblack@google.com 812855Sgabeblack@google.com#include "base/timebuf.hh" 912855Sgabeblack@google.com#include "cpu/beta_cpu/commit.hh" 1012855Sgabeblack@google.com#include "cpu/exetrace.hh" 1112855Sgabeblack@google.com 1212855Sgabeblack@google.comtemplate <class Impl> 1312855Sgabeblack@google.comSimpleCommit<Impl>::SimpleCommit(Params ¶ms) 1412855Sgabeblack@google.com : dcacheInterface(params.dcacheInterface), 1512855Sgabeblack@google.com iewToCommitDelay(params.iewToCommitDelay), 1612855Sgabeblack@google.com renameToROBDelay(params.renameToROBDelay), 1712855Sgabeblack@google.com renameWidth(params.renameWidth), 1812855Sgabeblack@google.com iewWidth(params.executeWidth), 1912855Sgabeblack@google.com commitWidth(params.commitWidth) 2012855Sgabeblack@google.com{ 2112855Sgabeblack@google.com _status = Idle; 2212855Sgabeblack@google.com} 2312855Sgabeblack@google.com 2412855Sgabeblack@google.comtemplate <class Impl> 2512855Sgabeblack@google.comvoid 2612855Sgabeblack@google.comSimpleCommit<Impl>::setCPU(FullCPU *cpu_ptr) 2712855Sgabeblack@google.com{ 2812855Sgabeblack@google.com DPRINTF(Commit, "Commit: Setting CPU pointer.\n"); 2912855Sgabeblack@google.com cpu = cpu_ptr; 3012855Sgabeblack@google.com} 3112855Sgabeblack@google.com 3212855Sgabeblack@google.comtemplate <class Impl> 3312855Sgabeblack@google.comvoid 3412855Sgabeblack@google.comSimpleCommit<Impl>::setTimeBuffer(TimeBuffer<TimeStruct> *tb_ptr) 3512855Sgabeblack@google.com{ 3612855Sgabeblack@google.com DPRINTF(Commit, "Commit: Setting time buffer pointer.\n"); 3712855Sgabeblack@google.com timeBuffer = tb_ptr; 3812855Sgabeblack@google.com 3912855Sgabeblack@google.com // Setup wire to send information back to IEW. 4012855Sgabeblack@google.com toIEW = timeBuffer->getWire(0); 4112855Sgabeblack@google.com 4212855Sgabeblack@google.com // Setup wire to read data from IEW (for the ROB). 4312855Sgabeblack@google.com robInfoFromIEW = timeBuffer->getWire(-iewToCommitDelay); 4412855Sgabeblack@google.com} 4512855Sgabeblack@google.com 4612855Sgabeblack@google.comtemplate <class Impl> 4712855Sgabeblack@google.comvoid 4812855Sgabeblack@google.comSimpleCommit<Impl>::setRenameQueue(TimeBuffer<RenameStruct> *rq_ptr) 4912855Sgabeblack@google.com{ 5012855Sgabeblack@google.com DPRINTF(Commit, "Commit: Setting rename queue pointer.\n"); 5112855Sgabeblack@google.com renameQueue = rq_ptr; 5212855Sgabeblack@google.com 5312855Sgabeblack@google.com // Setup wire to get instructions from rename (for the ROB). 5412855Sgabeblack@google.com fromRename = renameQueue->getWire(-renameToROBDelay); 5512855Sgabeblack@google.com} 5612855Sgabeblack@google.com 5712855Sgabeblack@google.comtemplate <class Impl> 5812855Sgabeblack@google.comvoid 5912855Sgabeblack@google.comSimpleCommit<Impl>::setIEWQueue(TimeBuffer<IEWStruct> *iq_ptr) 6012855Sgabeblack@google.com{ 6112855Sgabeblack@google.com DPRINTF(Commit, "Commit: Setting IEW queue pointer.\n"); 6212855Sgabeblack@google.com iewQueue = iq_ptr; 6312855Sgabeblack@google.com 6412855Sgabeblack@google.com // Setup wire to get instructions from IEW. 6512855Sgabeblack@google.com fromIEW = iewQueue->getWire(-iewToCommitDelay); 6612855Sgabeblack@google.com} 6712855Sgabeblack@google.com 6812855Sgabeblack@google.comtemplate <class Impl> 6912855Sgabeblack@google.comvoid 7012855Sgabeblack@google.comSimpleCommit<Impl>::setROB(ROB *rob_ptr) 7112855Sgabeblack@google.com{ 7212855Sgabeblack@google.com DPRINTF(Commit, "Commit: Setting ROB pointer.\n"); 7312855Sgabeblack@google.com rob = rob_ptr; 7412855Sgabeblack@google.com} 7512855Sgabeblack@google.com 7612855Sgabeblack@google.comtemplate <class Impl> 7712855Sgabeblack@google.comvoid 7812855Sgabeblack@google.comSimpleCommit<Impl>::tick() 7912855Sgabeblack@google.com{ 8012855Sgabeblack@google.com // If the ROB is currently in its squash sequence, then continue 8112855Sgabeblack@google.com // to squash. In this case, commit does not do anything. Otherwise 8212855Sgabeblack@google.com // run commit. 8312855Sgabeblack@google.com if (_status == ROBSquashing) { 8412855Sgabeblack@google.com if (rob->isDoneSquashing()) { 8512855Sgabeblack@google.com _status = Running; 8612855Sgabeblack@google.com } else { 8712855Sgabeblack@google.com rob->doSquash(); 8812855Sgabeblack@google.com 8912855Sgabeblack@google.com // Send back sequence number of tail of ROB, so other stages 9012855Sgabeblack@google.com // can squash younger instructions. Note that really the only 9112855Sgabeblack@google.com // stage that this is important for is the IEW stage; other 9212855Sgabeblack@google.com // stages can just clear all their state as long as selective 9312855Sgabeblack@google.com // replay isn't used. 9412855Sgabeblack@google.com toIEW->commitInfo.doneSeqNum = rob->readTailSeqNum(); 9512855Sgabeblack@google.com toIEW->commitInfo.robSquashing = true; 9612855Sgabeblack@google.com } 9712855Sgabeblack@google.com } else { 9812855Sgabeblack@google.com commit(); 9912855Sgabeblack@google.com } 10012855Sgabeblack@google.com 10112855Sgabeblack@google.com markCompletedInsts(); 10212855Sgabeblack@google.com 10312855Sgabeblack@google.com // Writeback number of free ROB entries here. 10412855Sgabeblack@google.com DPRINTF(Commit, "Commit: ROB has %d free entries.\n", 10512855Sgabeblack@google.com rob->numFreeEntries()); 10612855Sgabeblack@google.com toIEW->commitInfo.freeROBEntries = rob->numFreeEntries(); 10712855Sgabeblack@google.com} 10812855Sgabeblack@google.com 10912855Sgabeblack@google.comtemplate <class Impl> 11012855Sgabeblack@google.comvoid 11112855Sgabeblack@google.comSimpleCommit<Impl>::commit() 11212855Sgabeblack@google.com{ 11312855Sgabeblack@google.com ////////////////////////////////////// 11412855Sgabeblack@google.com // Check for interrupts 11512855Sgabeblack@google.com ////////////////////////////////////// 11612855Sgabeblack@google.com 11712855Sgabeblack@google.com // Process interrupts if interrupts are enabled and not in PAL mode. 11812855Sgabeblack@google.com // Take the PC from commit and write it to the IPR, then squash. The 11912855Sgabeblack@google.com // interrupt completing will take care of restoring the PC from that value 12012855Sgabeblack@google.com // in the IPR. Look at IPR[EXC_ADDR]; 12112855Sgabeblack@google.com // hwrei() is what resets the PC to the place where instruction execution 12212855Sgabeblack@google.com // beings again. 12312855Sgabeblack@google.com#ifdef FULL_SYSTEM 12412855Sgabeblack@google.com if (ISA::check_interrupts && 12512855Sgabeblack@google.com cpu->check_interrupts() && 12612855Sgabeblack@google.com !xc->inPalMode()) { 12712855Sgabeblack@google.com // Will need to squash all instructions currently in flight and have 12812855Sgabeblack@google.com // the interrupt handler restart at the last non-committed inst. 12912855Sgabeblack@google.com // Most of that can be handled through the trap() function. The 13012855Sgabeblack@google.com // processInterrupts() function really just checks for interrupts 13112855Sgabeblack@google.com // and then calls trap() if there is an interrupt present. 13212855Sgabeblack@google.com 13312855Sgabeblack@google.com // CPU will handle implementation of the interrupt. 13412855Sgabeblack@google.com cpu->processInterrupts(); 135 } 136#endif // FULL_SYSTEM 137 138 //////////////////////////////////// 139 // Check for squash signal, handle that first 140 //////////////////////////////////// 141 142 // Want to mainly check if the IEW stage is telling the ROB to squash. 143 // Should I also check if the commit stage is telling the ROB to squah? 144 // This might be necessary to keep the same timing between the IQ and 145 // the ROB... 146 if (robInfoFromIEW->iewInfo.squash) { 147 DPRINTF(Commit, "Commit: Squashing instructions in the ROB.\n"); 148 149 _status = ROBSquashing; 150 151 InstSeqNum squashed_inst = robInfoFromIEW->iewInfo.squashedSeqNum; 152 153 rob->squash(squashed_inst); 154 155 // Send back the sequence number of the squashed instruction. 156 toIEW->commitInfo.doneSeqNum = squashed_inst; 157 158 // Send back the squash signal to tell stages that they should squash. 159 toIEW->commitInfo.squash = true; 160 161 // Send back the rob squashing signal so other stages know that the 162 // ROB is in the process of squashing. 163 toIEW->commitInfo.robSquashing = true; 164 165 toIEW->commitInfo.branchMispredict = 166 robInfoFromIEW->iewInfo.branchMispredict; 167 168 toIEW->commitInfo.branchTaken = 169 robInfoFromIEW->iewInfo.branchTaken; 170 171 toIEW->commitInfo.nextPC = robInfoFromIEW->iewInfo.nextPC; 172 173 toIEW->commitInfo.mispredPC = robInfoFromIEW->iewInfo.mispredPC; 174 } 175 176 if (_status != ROBSquashing) { 177 // If we're not currently squashing, then get instructions. 178 getInsts(); 179 180 // Try to commit any instructions. 181 commitInsts(); 182 } 183 184 // If the ROB is empty, we can set this stage to idle. Use this 185 // in the future when the Idle status will actually be utilized. 186#if 0 187 if (rob->isEmpty()) { 188 DPRINTF(Commit, "Commit: ROB is empty. Status changed to idle.\n"); 189 _status = Idle; 190 // Schedule an event so that commit will actually wake up 191 // once something gets put in the ROB. 192 } 193#endif 194} 195 196// Loop that goes through as many instructions in the ROB as possible and 197// tries to commit them. The actual work for committing is done by the 198// commitHead() function. 199template <class Impl> 200void 201SimpleCommit<Impl>::commitInsts() 202{ 203 //////////////////////////////////// 204 // Handle commit 205 // Note that commit will be handled prior to the ROB so that the ROB 206 // only tries to commit instructions it has in this current cycle, and 207 // not instructions it is writing in during this cycle. 208 // Can't commit and squash things at the same time... 209 //////////////////////////////////// 210 211 DynInstPtr head_inst = rob->readHeadInst(); 212 213 unsigned num_committed = 0; 214 215 // Commit as many instructions as possible until the commit bandwidth 216 // limit is reached, or it becomes impossible to commit any more. 217 while (!rob->isEmpty() && 218 head_inst->readyToCommit() && 219 num_committed < commitWidth) 220 { 221 DPRINTF(Commit, "Commit: Trying to commit head instruction.\n"); 222 223 // If the head instruction is squashed, it is ready to retire at any 224 // time. However, we need to avoid updating any other state 225 // incorrectly if it's already been squashed. 226 if (head_inst->isSquashed()) { 227 // Hack to avoid the instruction being retired (and deleted) if 228 // it hasn't been through the IEW stage yet. 229 if (!head_inst->isExecuted()) { 230 break; 231 } 232 233 DPRINTF(Commit, "Commit: Retiring squashed instruction from " 234 "ROB.\n"); 235 236 // Tell ROB to retire head instruction. This retires the head 237 // inst in the ROB without affecting any other stages. 238 rob->retireHead(); 239 240 } else { 241 // Increment the total number of non-speculative instructions 242 // executed. 243 // Hack for now: it really shouldn't happen until after the 244 // commit is deemed to be successful, but this count is needed 245 // for syscalls. 246 cpu->funcExeInst++; 247 248 // Try to commit the head instruction. 249 bool commit_success = commitHead(head_inst, num_committed); 250 251 // Update what instruction we are looking at if the commit worked. 252 if(commit_success) { 253 ++num_committed; 254 255 // Send back which instruction has been committed. 256 // @todo: Update this later when a wider pipeline is used. 257 // Hmm, can't really give a pointer here...perhaps the 258 // sequence number instead (copy). 259 toIEW->commitInfo.doneSeqNum = head_inst->seqNum; 260 261 cpu->instDone(); 262 } else { 263 break; 264 } 265 } 266 267 // Update the pointer to read the next instruction in the ROB. 268 head_inst = rob->readHeadInst(); 269 } 270} 271 272template <class Impl> 273bool 274SimpleCommit<Impl>::commitHead(DynInstPtr &head_inst, unsigned inst_num) 275{ 276 // Make sure instruction is valid 277 assert(head_inst); 278 279 Fault fault = No_Fault; 280 281 // If the head instruction is a store or a load, then execute it 282 // because this simple model does no speculative memory access. 283 // Hopefully this covers all memory references. 284 // Also check if it's nonspeculative. Or a nop. Then it will be 285 // executed only when it reaches the head of the ROB. Actually 286 // executing a nop is a bit overkill... 287 if (!head_inst->isExecuted()) { 288 // Keep this number correct. We have not yet actually executed 289 // and committed this instruction. 290 cpu->funcExeInst--; 291 if (head_inst->isStore() || head_inst->isNonSpeculative()) { 292 DPRINTF(Commit, "Commit: Encountered a store or non-speculative " 293 "instruction at the head of the ROB, PC %#x.\n", 294 head_inst->readPC()); 295 296 toIEW->commitInfo.nonSpecSeqNum = head_inst->seqNum; 297 298 // Change the instruction so it won't try to commit again until 299 // it is executed. 300 head_inst->clearCanCommit(); 301 302 return false; 303 } else { 304 panic("Commit: Trying to commit un-executed instruction " 305 "of unknown type!\n"); 306 } 307 } 308 309 // Check if memory access was successful. 310 if (fault != No_Fault) { 311 // Handle data cache miss here. In the future, set the status 312 // to data cache miss, then exit the stage. Have an event 313 // that handles commiting the head instruction, then setting 314 // the stage back to running, when the event is run. (just 315 // make sure that event is commit's run for that cycle) 316 panic("Commit: Load/store instruction failed, not sure what " 317 "to do.\n"); 318 // Also will want to clear the instruction's fault after being 319 // handled here so it's not handled again below. 320 } 321 322 // Now check if it's one of the special trap or barrier or 323 // serializing instructions. 324 if (head_inst->isThreadSync() || 325 head_inst->isSerializing() || 326 head_inst->isMemBarrier() || 327 head_inst->isWriteBarrier() ) 328 { 329 // Not handled for now. Mem barriers and write barriers are safe 330 // to simply let commit as memory accesses only happen once they 331 // reach the head of commit. Not sure about the other two. 332 panic("Serializing or barrier instructions" 333 " are not handled yet.\n"); 334 } 335 336 // Check if the instruction caused a fault. If so, trap. 337 if (head_inst->getFault() != No_Fault) { 338#ifdef FULL_SYSTEM 339 cpu->trap(fault); 340#else // !FULL_SYSTEM 341 if (!head_inst->isNop()) { 342 panic("fault (%d) detected @ PC %08p", head_inst->getFault(), 343 head_inst->PC); 344 } 345#endif // FULL_SYSTEM 346 } 347 348 // Check if we're really ready to commit. If not then return false. 349 // I'm pretty sure all instructions should be able to commit if they've 350 // reached this far. For now leave this in as a check. 351 if(!rob->isHeadReady()) { 352 DPRINTF(Commit, "Commit: Unable to commit head instruction!\n"); 353 return false; 354 } 355 356 // If it's a branch, then send back branch prediction update info 357 // to the fetch stage. 358 // This should be handled in the iew stage if a mispredict happens... 359#if 0 360 if (head_inst->isControl()) { 361 362 toIEW->nextPC = head_inst->readPC(); 363 //Maybe switch over to BTB incorrect. 364 toIEW->btbMissed = head_inst->btbMiss(); 365 toIEW->target = head_inst->nextPC; 366 //Maybe also include global history information. 367 //This simple version will have no branch prediction however. 368 } 369#endif 370 371#if 0 372 // Check if the instruction has a destination register. 373 // If so add the previous physical register of its logical register's 374 // destination to the free list through the time buffer. 375 for (int i = 0; i < head_inst->numDestRegs(); i++) 376 { 377 toIEW->commitInfo.freeRegs.push_back(head_inst->prevDestRegIdx(i)); 378 } 379#endif 380 381 // Explicit communication back to the LDSTQ that a load has been committed 382 // and can be removed from the LDSTQ. Stores don't need this because 383 // the LDSTQ will already have been told that a store has reached the head 384 // of the ROB. Consider including communication if it's a store as well 385 // to keep things orthagonal. 386 if (head_inst->isLoad()) { 387 toIEW->commitInfo.commitIsLoad = true; 388 } 389 390 // Now that the instruction is going to be committed, finalize its 391 // trace data. 392 if (head_inst->traceData) { 393 head_inst->traceData->finalize(); 394 } 395 396 //Finally clear the head ROB entry. 397 rob->retireHead(); 398 399 // Return true to indicate that we have committed an instruction. 400 return true; 401} 402 403template <class Impl> 404void 405SimpleCommit<Impl>::getInsts() 406{ 407 ////////////////////////////////////// 408 // Handle ROB functions 409 ////////////////////////////////////// 410 411 // Read any issued instructions and place them into the ROB. Do this 412 // prior to squashing to avoid having instructions in the ROB that 413 // don't get squashed properly. 414 int insts_to_process = min((int)renameWidth, fromRename->size); 415 416 for (int inst_num = 0; 417 inst_num < insts_to_process; 418 ++inst_num) 419 { 420 if (!fromRename->insts[inst_num]->isSquashed()) { 421 DPRINTF(Commit, "Commit: Inserting PC %#x into ROB.\n", 422 fromRename->insts[inst_num]->readPC()); 423 rob->insertInst(fromRename->insts[inst_num]); 424 } else { 425 DPRINTF(Commit, "Commit: Instruction %i PC %#x was " 426 "squashed, skipping.\n", 427 fromRename->insts[inst_num]->seqNum, 428 fromRename->insts[inst_num]->readPC()); 429 } 430 } 431} 432 433template <class Impl> 434void 435SimpleCommit<Impl>::markCompletedInsts() 436{ 437 // Grab completed insts out of the IEW instruction queue, and mark 438 // instructions completed within the ROB. 439 for (int inst_num = 0; 440 inst_num < iewWidth && fromIEW->insts[inst_num]; 441 ++inst_num) 442 { 443 DPRINTF(Commit, "Commit: Marking PC %#x, SN %i ready within ROB.\n", 444 fromIEW->insts[inst_num]->readPC(), 445 fromIEW->insts[inst_num]->seqNum); 446 447 // Mark the instruction as ready to commit. 448 fromIEW->insts[inst_num]->setCanCommit(); 449 } 450} 451 452template <class Impl> 453uint64_t 454SimpleCommit<Impl>::readCommitPC() 455{ 456 return rob->readHeadPC(); 457} 458 459#endif // __COMMIT_IMPL_HH__ 460