decode.cc revision 13646:626670cc6da4
16657Snate@binkert.org/*
26657Snate@binkert.org * Copyright (c) 2013-2014 ARM Limited
36657Snate@binkert.org * All rights reserved
46657Snate@binkert.org *
56657Snate@binkert.org * The license below extends only to copyright in the software and shall
66657Snate@binkert.org * not be construed as granting a license to any other intellectual
76657Snate@binkert.org * property including but not limited to intellectual property relating
86657Snate@binkert.org * to a hardware implementation of the functionality of the software
96657Snate@binkert.org * licensed hereunder.  You may use the software subject to the license
106657Snate@binkert.org * terms below provided that you ensure that this notice is replicated
116657Snate@binkert.org * unmodified and in its entirety in all distributions of the software,
126657Snate@binkert.org * modified or unmodified, in source code or in binary form.
136657Snate@binkert.org *
146657Snate@binkert.org * Redistribution and use in source and binary forms, with or without
156657Snate@binkert.org * modification, are permitted provided that the following conditions are
166657Snate@binkert.org * met: redistributions of source code must retain the above copyright
176657Snate@binkert.org * notice, this list of conditions and the following disclaimer;
186657Snate@binkert.org * redistributions in binary form must reproduce the above copyright
196657Snate@binkert.org * notice, this list of conditions and the following disclaimer in the
206657Snate@binkert.org * documentation and/or other materials provided with the distribution;
216657Snate@binkert.org * neither the name of the copyright holders nor the names of its
226657Snate@binkert.org * contributors may be used to endorse or promote products derived from
236657Snate@binkert.org * this software without specific prior written permission.
246657Snate@binkert.org *
256657Snate@binkert.org * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
266657Snate@binkert.org * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
276657Snate@binkert.org * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
286657Snate@binkert.org * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
296657Snate@binkert.org * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
306657Snate@binkert.org * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
316657Snate@binkert.org * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
326657Snate@binkert.org * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
336657Snate@binkert.org * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
346657Snate@binkert.org * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
356657Snate@binkert.org * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
366657Snate@binkert.org *
376657Snate@binkert.org * Authors: Andrew Bardsley
386657Snate@binkert.org */
396657Snate@binkert.org
406657Snate@binkert.org#include "cpu/minor/decode.hh"
416657Snate@binkert.org
426657Snate@binkert.org#include "cpu/minor/pipeline.hh"
436657Snate@binkert.org#include "debug/Decode.hh"
446657Snate@binkert.org
456657Snate@binkert.orgnamespace Minor
466657Snate@binkert.org{
476657Snate@binkert.org
486657Snate@binkert.orgDecode::Decode(const std::string &name,
496657Snate@binkert.org    MinorCPU &cpu_,
506657Snate@binkert.org    MinorCPUParams &params,
516657Snate@binkert.org    Latch<ForwardInstData>::Output inp_,
526657Snate@binkert.org    Latch<ForwardInstData>::Input out_,
536657Snate@binkert.org    std::vector<InputBuffer<ForwardInstData>> &next_stage_input_buffer) :
546657Snate@binkert.org    Named(name),
556657Snate@binkert.org    cpu(cpu_),
566657Snate@binkert.org    inp(inp_),
576657Snate@binkert.org    out(out_),
586657Snate@binkert.org    nextStageReserve(next_stage_input_buffer),
596657Snate@binkert.org    outputWidth(params.executeInputWidth),
606657Snate@binkert.org    processMoreThanOneInput(params.decodeCycleInput),
616657Snate@binkert.org    decodeInfo(params.numThreads),
626657Snate@binkert.org    threadPriority(0)
637839Snilay@cs.wisc.edu{
647839Snilay@cs.wisc.edu    if (outputWidth < 1)
656657Snate@binkert.org        fatal("%s: executeInputWidth must be >= 1 (%d)\n", name, outputWidth);
666657Snate@binkert.org
676657Snate@binkert.org    if (params.decodeInputBufferSize < 1) {
686657Snate@binkert.org        fatal("%s: decodeInputBufferSize must be >= 1 (%d)\n", name,
696657Snate@binkert.org        params.decodeInputBufferSize);
706657Snate@binkert.org    }
716657Snate@binkert.org
726657Snate@binkert.org    /* Per-thread input buffers */
736657Snate@binkert.org    for (ThreadID tid = 0; tid < params.numThreads; tid++) {
746999Snate@binkert.org        inputBuffer.push_back(
756657Snate@binkert.org            InputBuffer<ForwardInstData>(
766657Snate@binkert.org                name + ".inputBuffer" + std::to_string(tid), "insts",
776657Snate@binkert.org                params.decodeInputBufferSize));
786657Snate@binkert.org    }
796657Snate@binkert.org}
806657Snate@binkert.org
816657Snate@binkert.orgconst ForwardInstData *
826657Snate@binkert.orgDecode::getInput(ThreadID tid)
836657Snate@binkert.org{
846657Snate@binkert.org    /* Get insts from the inputBuffer to work with */
856657Snate@binkert.org    if (!inputBuffer[tid].empty()) {
867055Snate@binkert.org        const ForwardInstData &head = inputBuffer[tid].front();
876657Snate@binkert.org
886657Snate@binkert.org        return (head.isBubble() ? NULL : &(inputBuffer[tid].front()));
896657Snate@binkert.org    } else {
906657Snate@binkert.org        return NULL;
916657Snate@binkert.org    }
927839Snilay@cs.wisc.edu}
937839Snilay@cs.wisc.edu
946657Snate@binkert.orgvoid
956657Snate@binkert.orgDecode::popInput(ThreadID tid)
966657Snate@binkert.org{
976657Snate@binkert.org    if (!inputBuffer[tid].empty())
986657Snate@binkert.org        inputBuffer[tid].pop();
996657Snate@binkert.org
1006657Snate@binkert.org    decodeInfo[tid].inputIndex = 0;
1016657Snate@binkert.org    decodeInfo[tid].inMacroop = false;
1026657Snate@binkert.org}
1037007Snate@binkert.org
1047007Snate@binkert.org#if TRACING_ON
1056657Snate@binkert.org/** Add the tracing data to an instruction.  This originates in
1066657Snate@binkert.org *  decode because this is the first place that execSeqNums are known
1076657Snate@binkert.org *  (these are used as the 'FetchSeq' in tracing data) */
1086657Snate@binkert.orgstatic void
1096657Snate@binkert.orgdynInstAddTracing(MinorDynInstPtr inst, StaticInstPtr static_inst,
1106657Snate@binkert.org    MinorCPU &cpu)
1116657Snate@binkert.org{
112    inst->traceData = cpu.getTracer()->getInstRecord(curTick(),
113        cpu.getContext(inst->id.threadId),
114        inst->staticInst, inst->pc, static_inst);
115
116    /* Use the execSeqNum as the fetch sequence number as this most closely
117     *  matches the other processor models' idea of fetch sequence */
118    if (inst->traceData)
119        inst->traceData->setFetchSeq(inst->id.execSeqNum);
120}
121#endif
122
123void
124Decode::evaluate()
125{
126    /* Push input onto appropriate input buffer */
127    if (!inp.outputWire->isBubble())
128        inputBuffer[inp.outputWire->threadId].setTail(*inp.outputWire);
129
130    ForwardInstData &insts_out = *out.inputWire;
131
132    assert(insts_out.isBubble());
133
134    for (ThreadID tid = 0; tid < cpu.numThreads; tid++)
135        decodeInfo[tid].blocked = !nextStageReserve[tid].canReserve();
136
137    ThreadID tid = getScheduledThread();
138
139    if (tid != InvalidThreadID) {
140        DecodeThreadInfo &decode_info = decodeInfo[tid];
141        const ForwardInstData *insts_in = getInput(tid);
142
143        unsigned int output_index = 0;
144
145        /* Pack instructions into the output while we can.  This may involve
146         * using more than one input line */
147        while (insts_in &&
148           decode_info.inputIndex < insts_in->width() && /* Still more input */
149           output_index < outputWidth /* Still more output to fill */)
150        {
151            MinorDynInstPtr inst = insts_in->insts[decode_info.inputIndex];
152
153            if (inst->isBubble()) {
154                /* Skip */
155                decode_info.inputIndex++;
156                decode_info.inMacroop = false;
157            } else {
158                StaticInstPtr static_inst = inst->staticInst;
159                /* Static inst of a macro-op above the output_inst */
160                StaticInstPtr parent_static_inst = NULL;
161                MinorDynInstPtr output_inst = inst;
162
163                if (inst->isFault()) {
164                    DPRINTF(Decode, "Fault being passed: %d\n",
165                        inst->fault->name());
166
167                    decode_info.inputIndex++;
168                    decode_info.inMacroop = false;
169                } else if (static_inst->isMacroop()) {
170                    /* Generate a new micro-op */
171                    StaticInstPtr static_micro_inst;
172
173                    /* Set up PC for the next micro-op emitted */
174                    if (!decode_info.inMacroop) {
175                        decode_info.microopPC = inst->pc;
176                        decode_info.inMacroop = true;
177                    }
178
179                    /* Get the micro-op static instruction from the
180                     * static_inst. */
181                    static_micro_inst =
182                        static_inst->fetchMicroop(
183                                decode_info.microopPC.microPC());
184
185                    output_inst = new MinorDynInst(inst->id);
186                    output_inst->pc = decode_info.microopPC;
187                    output_inst->staticInst = static_micro_inst;
188                    output_inst->fault = NoFault;
189
190                    /* Allow a predicted next address only on the last
191                     *  microop */
192                    if (static_micro_inst->isLastMicroop()) {
193                        output_inst->predictedTaken = inst->predictedTaken;
194                        output_inst->predictedTarget = inst->predictedTarget;
195                    }
196
197                    DPRINTF(Decode, "Microop decomposition inputIndex:"
198                        " %d output_index: %d lastMicroop: %s microopPC:"
199                        " %d.%d inst: %d\n",
200                        decode_info.inputIndex, output_index,
201                        (static_micro_inst->isLastMicroop() ?
202                            "true" : "false"),
203                        decode_info.microopPC.instAddr(),
204                        decode_info.microopPC.microPC(),
205                        *output_inst);
206
207                    /* Acknowledge that the static_inst isn't mine, it's my
208                     * parent macro-op's */
209                    parent_static_inst = static_inst;
210
211                    static_micro_inst->advancePC(decode_info.microopPC);
212
213                    /* Step input if this is the last micro-op */
214                    if (static_micro_inst->isLastMicroop()) {
215                        decode_info.inputIndex++;
216                        decode_info.inMacroop = false;
217                    }
218                } else {
219                    /* Doesn't need decomposing, pass on instruction */
220                    DPRINTF(Decode, "Passing on inst: %s inputIndex:"
221                        " %d output_index: %d\n",
222                        *output_inst, decode_info.inputIndex, output_index);
223
224                    parent_static_inst = static_inst;
225
226                    /* Step input */
227                    decode_info.inputIndex++;
228                    decode_info.inMacroop = false;
229                }
230
231                /* Set execSeqNum of output_inst */
232                output_inst->id.execSeqNum = decode_info.execSeqNum;
233                /* Add tracing */
234#if TRACING_ON
235                dynInstAddTracing(output_inst, parent_static_inst, cpu);
236#endif
237
238                /* Step to next sequence number */
239                decode_info.execSeqNum++;
240
241                /* Correctly size the output before writing */
242                if (output_index == 0) insts_out.resize(outputWidth);
243                /* Push into output */
244                insts_out.insts[output_index] = output_inst;
245                output_index++;
246            }
247
248            /* Have we finished with the input? */
249            if (decode_info.inputIndex == insts_in->width()) {
250                /* If we have just been producing micro-ops, we *must* have
251                 * got to the end of that for inputIndex to be pushed past
252                 * insts_in->width() */
253                assert(!decode_info.inMacroop);
254                popInput(tid);
255                insts_in = NULL;
256
257                if (processMoreThanOneInput) {
258                    DPRINTF(Decode, "Wrapping\n");
259                    insts_in = getInput(tid);
260                }
261            }
262        }
263
264        /* The rest of the output (if any) should already have been packed
265         *  with bubble instructions by insts_out's initialisation
266         *
267         *  for (; output_index < outputWidth; output_index++)
268         *      assert(insts_out.insts[output_index]->isBubble());
269         */
270    }
271
272    /* If we generated output, reserve space for the result in the next stage
273     *  and mark the stage as being active this cycle */
274    if (!insts_out.isBubble()) {
275        /* Note activity of following buffer */
276        cpu.activityRecorder->activity();
277        insts_out.threadId = tid;
278        nextStageReserve[tid].reserve();
279    }
280
281    /* If we still have input to process and somewhere to put it,
282     *  mark stage as active */
283    for (ThreadID i = 0; i < cpu.numThreads; i++)
284    {
285        if (getInput(i) && nextStageReserve[i].canReserve()) {
286            cpu.activityRecorder->activateStage(Pipeline::DecodeStageId);
287            break;
288        }
289    }
290
291    /* Make sure the input (if any left) is pushed */
292    if (!inp.outputWire->isBubble())
293        inputBuffer[inp.outputWire->threadId].pushTail();
294}
295
296inline ThreadID
297Decode::getScheduledThread()
298{
299    /* Select thread via policy. */
300    std::vector<ThreadID> priority_list;
301
302    switch (cpu.threadPolicy) {
303      case Enums::SingleThreaded:
304        priority_list.push_back(0);
305        break;
306      case Enums::RoundRobin:
307        priority_list = cpu.roundRobinPriority(threadPriority);
308        break;
309      case Enums::Random:
310        priority_list = cpu.randomPriority();
311        break;
312      default:
313        panic("Unknown fetch policy");
314    }
315
316    for (auto tid : priority_list) {
317        if (cpu.getContext(tid)->status() == ThreadContext::Active &&
318            getInput(tid) &&
319            !decodeInfo[tid].blocked) {
320            threadPriority = tid;
321            return tid;
322        }
323    }
324
325   return InvalidThreadID;
326}
327
328bool
329Decode::isDrained()
330{
331    for (const auto &buffer : inputBuffer) {
332        if (!buffer.empty())
333            return false;
334    }
335
336    return (*inp.outputWire).isBubble();
337}
338
339void
340Decode::minorTrace() const
341{
342    std::ostringstream data;
343
344    if (decodeInfo[0].blocked)
345        data << 'B';
346    else
347        (*out.inputWire).reportData(data);
348
349    MINORTRACE("insts=%s\n", data.str());
350    inputBuffer[0].minorTrace();
351}
352
353}
354