decode_impl.hh revision 1060
1#ifndef __SIMPLE_DECODE_CC__
2#define __SIMPLE_DECODE_CC__
3
4#include "cpu/beta_cpu/decode.hh"
5
6template<class Impl>
7SimpleDecode<Impl>::SimpleDecode(Params &params)
8    : renameToDecodeDelay(params.renameToDecodeDelay),
9      iewToDecodeDelay(params.iewToDecodeDelay),
10      commitToDecodeDelay(params.commitToDecodeDelay),
11      fetchToDecodeDelay(params.fetchToDecodeDelay),
12      decodeWidth(params.decodeWidth)
13{
14    DPRINTF(Decode, "Decode: decodeWidth=%i.\n", decodeWidth);
15    _status = Idle;
16}
17
18template<class Impl>
19void
20SimpleDecode<Impl>::setCPU(FullCPU *cpu_ptr)
21{
22    DPRINTF(Decode, "Decode: Setting CPU pointer.\n");
23    cpu = cpu_ptr;
24}
25
26template<class Impl>
27void
28SimpleDecode<Impl>::setTimeBuffer(TimeBuffer<TimeStruct> *tb_ptr)
29{
30    DPRINTF(Decode, "Decode: Setting time buffer pointer.\n");
31    timeBuffer = tb_ptr;
32
33    // Setup wire to write information back to fetch.
34    toFetch = timeBuffer->getWire(0);
35
36    // Create wires to get information from proper places in time buffer.
37    fromRename = timeBuffer->getWire(-renameToDecodeDelay);
38    fromIEW = timeBuffer->getWire(-iewToDecodeDelay);
39    fromCommit = timeBuffer->getWire(-commitToDecodeDelay);
40}
41
42template<class Impl>
43void
44SimpleDecode<Impl>::setDecodeQueue(TimeBuffer<DecodeStruct> *dq_ptr)
45{
46    DPRINTF(Decode, "Decode: Setting decode queue pointer.\n");
47    decodeQueue = dq_ptr;
48
49    // Setup wire to write information to proper place in decode queue.
50    toRename = decodeQueue->getWire(0);
51}
52
53template<class Impl>
54void
55SimpleDecode<Impl>::setFetchQueue(TimeBuffer<FetchStruct> *fq_ptr)
56{
57    DPRINTF(Decode, "Decode: Setting fetch queue pointer.\n");
58    fetchQueue = fq_ptr;
59
60    // Setup wire to read information from fetch queue.
61    fromFetch = fetchQueue->getWire(-fetchToDecodeDelay);
62}
63
64template<class Impl>
65void
66SimpleDecode<Impl>::block()
67{
68    DPRINTF(Decode, "Decode: Blocking.\n");
69
70    // Set the status to Blocked.
71    _status = Blocked;
72
73    // Add the current inputs to the skid buffer so they can be
74    // reprocessed when this stage unblocks.
75    skidBuffer.push(*fromFetch);
76
77    // Note that this stage only signals previous stages to stall when
78    // it is the cause of the stall originates at this stage.  Otherwise
79    // the previous stages are expected to check all possible stall signals.
80}
81
82template<class Impl>
83inline void
84SimpleDecode<Impl>::unblock()
85{
86    DPRINTF(Decode, "Decode: Unblocking, going to remove "
87            "instructions from skid buffer.\n");
88    // Remove the now processed instructions from the skid buffer.
89    skidBuffer.pop();
90
91    // If there's still information in the skid buffer, then
92    // continue to tell previous stages to stall.  They will be
93            // able to restart once the skid buffer is empty.
94    if (!skidBuffer.empty()) {
95        toFetch->decodeInfo.stall = true;
96    } else {
97        DPRINTF(Decode, "Decode: Finished unblocking.\n");
98        _status = Running;
99    }
100}
101
102// This squash is specifically for when Decode detects a PC-relative branch
103// was predicted incorrectly.
104template<class Impl>
105void
106SimpleDecode<Impl>::squash(DynInst *inst)
107{
108    DPRINTF(Decode, "Decode: Squashing due to incorrect branch prediction "
109                    "detected at decode.\n");
110    Addr new_PC = inst->nextPC;
111
112    toFetch->decodeInfo.predIncorrect = true;
113    toFetch->decodeInfo.squash = true;
114    toFetch->decodeInfo.nextPC = new_PC;
115
116    // Set status to squashing.
117    _status = Squashing;
118
119    // Maybe advance the time buffer?  Not sure what to do in the normal
120    // case.
121
122    // Clear the skid buffer in case it has any data in it.
123    while (!skidBuffer.empty())
124    {
125        skidBuffer.pop();
126    }
127}
128
129template<class Impl>
130void
131SimpleDecode<Impl>::squash()
132{
133    DPRINTF(Decode, "Decode: Squashing.\n");
134    // Set status to squashing.
135    _status = Squashing;
136
137    // Maybe advance the time buffer?  Not sure what to do in the normal
138    // case.
139
140    // Clear the skid buffer in case it has any data in it.
141    while (!skidBuffer.empty())
142    {
143        skidBuffer.pop();
144    }
145}
146
147template<class Impl>
148void
149SimpleDecode<Impl>::tick()
150{
151    // Decode should try to execute as many instructions as its bandwidth
152    // will allow, as long as it is not currently blocked.
153    if (_status != Blocked && _status != Squashing) {
154        DPRINTF(Decode, "Decode: Not blocked, so attempting to run "
155                        "stage.\n");
156        // Make sure that the skid buffer has something in it if the
157        // status is unblocking.
158        assert(_status == Unblocking ? !skidBuffer.empty() : 1);
159
160        decode();
161
162        // If the status was unblocking, then instructions from the skid
163        // buffer were used.  Remove those instructions and handle
164        // the rest of unblocking.
165        if (_status == Unblocking) {
166            unblock();
167        }
168    } else if (_status == Blocked) {
169        if (fromFetch->insts[0] != NULL) {
170            block();
171        }
172
173        if (!fromRename->renameInfo.stall &&
174                   !fromIEW->iewInfo.stall &&
175                   !fromCommit->commitInfo.stall) {
176            DPRINTF(Decode, "Decode: Stall signals cleared, going to "
177                    "unblock.\n");
178            _status = Unblocking;
179
180            // Continue to tell previous stage to block until this
181            // stage is done unblocking.
182            toFetch->decodeInfo.stall = true;
183        } else {
184            DPRINTF(Decode, "Decode: Still blocked.\n");
185            toFetch->decodeInfo.stall = true;
186        }
187
188        if (fromCommit->commitInfo.squash ||
189            fromCommit->commitInfo.robSquashing) {
190            squash();
191        }
192    } else if (_status == Squashing) {
193        if (!fromCommit->commitInfo.squash &&
194            !fromCommit->commitInfo.robSquashing) {
195            _status = Running;
196        } else if (fromCommit->commitInfo.squash) {
197            squash();
198        }
199    }
200}
201
202template<class Impl>
203void
204SimpleDecode<Impl>::decode()
205{
206    // Check time buffer if being told to squash.
207    if (/* fromRename->renameInfo.squash || */
208        /* fromIEW->iewInfo.squash || */
209        fromCommit->commitInfo.squash) {
210        squash();
211        return;
212    }
213
214    // Check time buffer if being told to stall.
215    if (fromRename->renameInfo.stall ||
216        fromIEW->iewInfo.stall ||
217        fromCommit->commitInfo.stall)
218    {
219        block();
220        return;
221    }
222
223    // Check fetch queue to see if instructions are available.
224    // If no available instructions, do nothing, unless this stage is
225    // currently unblocking.
226    if (fromFetch->insts[0] == NULL && _status != Unblocking) {
227        DPRINTF(Decode, "Decode: Nothing to do, breaking out early.\n");
228        // Should I change the status to idle?
229        return;
230    }
231
232    DynInst *inst;
233    // Instead have a class member variable that records which instruction
234    // was the last one that was ended on.  At the tick() stage, it can
235    // check if that's equal to 0.  If not, then don't pop stuff off.
236    unsigned num_inst = 0;
237    bool insts_available = _status == Unblocking ?
238        skidBuffer.front().insts[num_inst] != NULL :
239        fromFetch->insts[num_inst] != NULL;
240
241    // Debug block...
242#if 0
243    if (insts_available) {
244        DPRINTF(Decode, "Decode: Instructions available.\n");
245    } else {
246        if (_status == Unblocking && skidBuffer.empty()) {
247            DPRINTF(Decode, "Decode: No instructions available, skid buffer "
248                    "empty.\n");
249        } else if (_status != Unblocking &&
250                   fromFetch->insts[0] == NULL) {
251            DPRINTF(Decode, "Decode: No instructions available, fetch queue "
252                    "empty.\n");
253        } else {
254            panic("Decode: No instructions available, unexpected condition!"
255                  "\n");
256        }
257    }
258#endif
259
260    // Check to make sure that instructions coming from fetch are valid.
261    // Normally at this stage the branch target of PC-relative branches
262    // should be computed here.  However in this simple model all
263    // computation will take place at execute.  Hence doneTargCalc()
264    // will always be false.
265     while (num_inst < decodeWidth &&
266            insts_available)
267     {
268        DPRINTF(Decode, "Decode: Sending instruction to rename.\n");
269        // Might create some sort of accessor to get an instruction
270        // on a per thread basis.  Or might be faster to just get
271        // a pointer to an array or list of instructions and use that
272        // within this code.
273        inst = _status == Unblocking ? skidBuffer.front().insts[num_inst] :
274               fromFetch->insts[num_inst];
275        DPRINTF(Decode, "Decode: Processing instruction %i with PC %#x\n",
276                inst, inst->readPC());
277
278        // This current instruction is valid, so add it into the decode
279        // queue.  The next instruction may not be valid, so check to
280        // see if branches were predicted correctly.
281        toRename->insts[num_inst] = inst;
282
283        // Ensure that if it was predicted as a branch, it really is a
284        // branch.  This case should never happen in this model.
285        if (inst->predTaken() && !inst->isControl()) {
286            panic("Instruction predicted as a branch!");
287
288            // Might want to set some sort of boolean and just do
289            // a check at the end
290            squash(inst);
291            break;
292        }
293
294        // Ensure that the predicted branch target is the actual branch
295        // target if possible (branches that are PC relative).
296        if (inst->isControl() && inst->doneTargCalc()) {
297            if (inst->mispredicted()) {
298                // Might want to set some sort of boolean and just do
299                // a check at the end
300                squash(inst);
301                break;
302            }
303        }
304
305        // Also check if instructions have no source registers.  Mark
306        // them as ready to issue at any time.  Not sure if this check
307        // should exist here or at a later stage; however it doesn't matter
308        // too much for function correctness.
309        if (inst->numSrcRegs() == 0) {
310            inst->setCanIssue();
311        }
312
313        // Increment which instruction we're looking at.
314        ++num_inst;
315
316        // Check whether or not there are instructions available.
317        // Either need to check within the skid buffer, or the fetch
318        // queue, depending if this stage is unblocking or not.
319        insts_available = _status == Unblocking ?
320                           skidBuffer.front().insts[num_inst] == NULL :
321                           fromFetch->insts[num_inst] == NULL;
322    }
323}
324
325#endif // __SIMPLE_DECODE_CC__
326