decode.cc revision 11568:91e95eb78191
1/* 2 * Copyright (c) 2013-2014 ARM Limited 3 * All rights reserved 4 * 5 * The license below extends only to copyright in the software and shall 6 * not be construed as granting a license to any other intellectual 7 * property including but not limited to intellectual property relating 8 * to a hardware implementation of the functionality of the software 9 * licensed hereunder. You may use the software subject to the license 10 * terms below provided that you ensure that this notice is replicated 11 * unmodified and in its entirety in all distributions of the software, 12 * modified or unmodified, in source code or in binary form. 13 * 14 * Redistribution and use in source and binary forms, with or without 15 * modification, are permitted provided that the following conditions are 16 * met: redistributions of source code must retain the above copyright 17 * notice, this list of conditions and the following disclaimer; 18 * redistributions in binary form must reproduce the above copyright 19 * notice, this list of conditions and the following disclaimer in the 20 * documentation and/or other materials provided with the distribution; 21 * neither the name of the copyright holders nor the names of its 22 * contributors may be used to endorse or promote products derived from 23 * this software without specific prior written permission. 24 * 25 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 26 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 27 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 28 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 29 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 30 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 31 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 32 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 33 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 34 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 35 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 36 * 37 * Authors: Andrew Bardsley 38 */ 39 40#include "cpu/minor/decode.hh" 41#include "cpu/minor/pipeline.hh" 42#include "debug/Decode.hh" 43 44namespace Minor 45{ 46 47Decode::Decode(const std::string &name, 48 MinorCPU &cpu_, 49 MinorCPUParams ¶ms, 50 Latch<ForwardInstData>::Output inp_, 51 Latch<ForwardInstData>::Input out_, 52 std::vector<InputBuffer<ForwardInstData>> &next_stage_input_buffer) : 53 Named(name), 54 cpu(cpu_), 55 inp(inp_), 56 out(out_), 57 nextStageReserve(next_stage_input_buffer), 58 outputWidth(params.executeInputWidth), 59 processMoreThanOneInput(params.decodeCycleInput), 60 decodeInfo(params.numThreads), 61 threadPriority(0) 62{ 63 if (outputWidth < 1) 64 fatal("%s: executeInputWidth must be >= 1 (%d)\n", name, outputWidth); 65 66 if (params.decodeInputBufferSize < 1) { 67 fatal("%s: decodeInputBufferSize must be >= 1 (%d)\n", name, 68 params.decodeInputBufferSize); 69 } 70 71 /* Per-thread input buffers */ 72 for (ThreadID tid = 0; tid < params.numThreads; tid++) { 73 inputBuffer.push_back( 74 InputBuffer<ForwardInstData>( 75 name + ".inputBuffer" + std::to_string(tid), "insts", 76 params.decodeInputBufferSize)); 77 } 78} 79 80const ForwardInstData * 81Decode::getInput(ThreadID tid) 82{ 83 /* Get insts from the inputBuffer to work with */ 84 if (!inputBuffer[tid].empty()) { 85 const ForwardInstData &head = inputBuffer[tid].front(); 86 87 return (head.isBubble() ? NULL : &(inputBuffer[tid].front())); 88 } else { 89 return NULL; 90 } 91} 92 93void 94Decode::popInput(ThreadID tid) 95{ 96 if (!inputBuffer[tid].empty()) 97 inputBuffer[tid].pop(); 98 99 decodeInfo[tid].inputIndex = 0; 100 decodeInfo[tid].inMacroop = false; 101} 102 103#if TRACING_ON 104/** Add the tracing data to an instruction. This originates in 105 * decode because this is the first place that execSeqNums are known 106 * (these are used as the 'FetchSeq' in tracing data) */ 107static void 108dynInstAddTracing(MinorDynInstPtr inst, StaticInstPtr static_inst, 109 MinorCPU &cpu) 110{ 111 inst->traceData = cpu.getTracer()->getInstRecord(curTick(), 112 cpu.getContext(inst->id.threadId), 113 inst->staticInst, inst->pc, static_inst); 114 115 /* Use the execSeqNum as the fetch sequence number as this most closely 116 * matches the other processor models' idea of fetch sequence */ 117 if (inst->traceData) 118 inst->traceData->setFetchSeq(inst->id.execSeqNum); 119} 120#endif 121 122void 123Decode::evaluate() 124{ 125 /* Push input onto appropriate input buffer */ 126 if (!inp.outputWire->isBubble()) 127 inputBuffer[inp.outputWire->threadId].setTail(*inp.outputWire); 128 129 ForwardInstData &insts_out = *out.inputWire; 130 131 assert(insts_out.isBubble()); 132 133 for (ThreadID tid = 0; tid < cpu.numThreads; tid++) 134 decodeInfo[tid].blocked = !nextStageReserve[tid].canReserve(); 135 136 ThreadID tid = getScheduledThread(); 137 138 if (tid != InvalidThreadID) { 139 DecodeThreadInfo &decode_info = decodeInfo[tid]; 140 const ForwardInstData *insts_in = getInput(tid); 141 142 unsigned int output_index = 0; 143 144 /* Pack instructions into the output while we can. This may involve 145 * using more than one input line */ 146 while (insts_in && 147 decode_info.inputIndex < insts_in->width() && /* Still more input */ 148 output_index < outputWidth /* Still more output to fill */) 149 { 150 MinorDynInstPtr inst = insts_in->insts[decode_info.inputIndex]; 151 152 if (inst->isBubble()) { 153 /* Skip */ 154 decode_info.inputIndex++; 155 decode_info.inMacroop = false; 156 } else { 157 StaticInstPtr static_inst = inst->staticInst; 158 /* Static inst of a macro-op above the output_inst */ 159 StaticInstPtr parent_static_inst = NULL; 160 MinorDynInstPtr output_inst = inst; 161 162 if (inst->isFault()) { 163 DPRINTF(Decode, "Fault being passed: %d\n", 164 inst->fault->name()); 165 166 decode_info.inputIndex++; 167 decode_info.inMacroop = false; 168 } else if (static_inst->isMacroop()) { 169 /* Generate a new micro-op */ 170 StaticInstPtr static_micro_inst; 171 172 /* Set up PC for the next micro-op emitted */ 173 if (!decode_info.inMacroop) { 174 decode_info.microopPC = inst->pc; 175 decode_info.inMacroop = true; 176 } 177 178 /* Get the micro-op static instruction from the 179 * static_inst. */ 180 static_micro_inst = 181 static_inst->fetchMicroop( 182 decode_info.microopPC.microPC()); 183 184 output_inst = new MinorDynInst(inst->id); 185 output_inst->pc = decode_info.microopPC; 186 output_inst->staticInst = static_micro_inst; 187 output_inst->fault = NoFault; 188 189 /* Allow a predicted next address only on the last 190 * microop */ 191 if (static_micro_inst->isLastMicroop()) { 192 output_inst->predictedTaken = inst->predictedTaken; 193 output_inst->predictedTarget = inst->predictedTarget; 194 } 195 196 DPRINTF(Decode, "Microop decomposition inputIndex:" 197 " %d output_index: %d lastMicroop: %s microopPC:" 198 " %d.%d inst: %d\n", 199 decode_info.inputIndex, output_index, 200 (static_micro_inst->isLastMicroop() ? 201 "true" : "false"), 202 decode_info.microopPC.instAddr(), 203 decode_info.microopPC.microPC(), 204 *output_inst); 205 206 /* Acknowledge that the static_inst isn't mine, it's my 207 * parent macro-op's */ 208 parent_static_inst = static_inst; 209 210 static_micro_inst->advancePC(decode_info.microopPC); 211 212 /* Step input if this is the last micro-op */ 213 if (static_micro_inst->isLastMicroop()) { 214 decode_info.inputIndex++; 215 decode_info.inMacroop = false; 216 } 217 } else { 218 /* Doesn't need decomposing, pass on instruction */ 219 DPRINTF(Decode, "Passing on inst: %s inputIndex:" 220 " %d output_index: %d\n", 221 *output_inst, decode_info.inputIndex, output_index); 222 223 parent_static_inst = static_inst; 224 225 /* Step input */ 226 decode_info.inputIndex++; 227 decode_info.inMacroop = false; 228 } 229 230 /* Set execSeqNum of output_inst */ 231 output_inst->id.execSeqNum = decode_info.execSeqNum; 232 /* Add tracing */ 233#if TRACING_ON 234 dynInstAddTracing(output_inst, parent_static_inst, cpu); 235#endif 236 237 /* Step to next sequence number */ 238 decode_info.execSeqNum++; 239 240 /* Correctly size the output before writing */ 241 if (output_index == 0) insts_out.resize(outputWidth); 242 /* Push into output */ 243 insts_out.insts[output_index] = output_inst; 244 output_index++; 245 } 246 247 /* Have we finished with the input? */ 248 if (decode_info.inputIndex == insts_in->width()) { 249 /* If we have just been producing micro-ops, we *must* have 250 * got to the end of that for inputIndex to be pushed past 251 * insts_in->width() */ 252 assert(!decode_info.inMacroop); 253 popInput(tid); 254 insts_in = NULL; 255 256 if (processMoreThanOneInput) { 257 DPRINTF(Decode, "Wrapping\n"); 258 insts_in = getInput(tid); 259 } 260 } 261 } 262 263 /* The rest of the output (if any) should already have been packed 264 * with bubble instructions by insts_out's initialisation 265 * 266 * for (; output_index < outputWidth; output_index++) 267 * assert(insts_out.insts[output_index]->isBubble()); 268 */ 269 } 270 271 /* If we generated output, reserve space for the result in the next stage 272 * and mark the stage as being active this cycle */ 273 if (!insts_out.isBubble()) { 274 /* Note activity of following buffer */ 275 cpu.activityRecorder->activity(); 276 insts_out.threadId = tid; 277 nextStageReserve[tid].reserve(); 278 } 279 280 /* If we still have input to process and somewhere to put it, 281 * mark stage as active */ 282 for (ThreadID i = 0; i < cpu.numThreads; i++) 283 { 284 if (getInput(i) && nextStageReserve[i].canReserve()) { 285 cpu.activityRecorder->activateStage(Pipeline::DecodeStageId); 286 break; 287 } 288 } 289 290 /* Make sure the input (if any left) is pushed */ 291 if (!inp.outputWire->isBubble()) 292 inputBuffer[inp.outputWire->threadId].pushTail(); 293} 294 295inline ThreadID 296Decode::getScheduledThread() 297{ 298 /* Select thread via policy. */ 299 std::vector<ThreadID> priority_list; 300 301 switch (cpu.threadPolicy) { 302 case Enums::SingleThreaded: 303 priority_list.push_back(0); 304 break; 305 case Enums::RoundRobin: 306 priority_list = cpu.roundRobinPriority(threadPriority); 307 break; 308 case Enums::Random: 309 priority_list = cpu.randomPriority(); 310 break; 311 default: 312 panic("Unknown fetch policy"); 313 } 314 315 for (auto tid : priority_list) { 316 if (getInput(tid) && !decodeInfo[tid].blocked) { 317 threadPriority = tid; 318 return tid; 319 } 320 } 321 322 return InvalidThreadID; 323} 324 325bool 326Decode::isDrained() 327{ 328 for (const auto &buffer : inputBuffer) { 329 if (!buffer.empty()) 330 return false; 331 } 332 333 return (*inp.outputWire).isBubble(); 334} 335 336void 337Decode::minorTrace() const 338{ 339 std::ostringstream data; 340 341 if (decodeInfo[0].blocked) 342 data << 'B'; 343 else 344 (*out.inputWire).reportData(data); 345 346 MINORTRACE("insts=%s\n", data.str()); 347 inputBuffer[0].minorTrace(); 348} 349 350} 351