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