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 &params)
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