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 42#include "cpu/minor/pipeline.hh" 43#include "debug/Decode.hh" 44 45namespace Minor 46{ 47 48Decode::Decode(const std::string &name, 49 MinorCPU &cpu_, 50 MinorCPUParams ¶ms, 51 Latch<ForwardInstData>::Output inp_, 52 Latch<ForwardInstData>::Input out_, 53 std::vector<InputBuffer<ForwardInstData>> &next_stage_input_buffer) : 54 Named(name), 55 cpu(cpu_), 56 inp(inp_), 57 out(out_), 58 nextStageReserve(next_stage_input_buffer), 59 outputWidth(params.executeInputWidth), 60 processMoreThanOneInput(params.decodeCycleInput), 61 decodeInfo(params.numThreads), 62 threadPriority(0) 63{ 64 if (outputWidth < 1) 65 fatal("%s: executeInputWidth must be >= 1 (%d)\n", name, outputWidth); 66 67 if (params.decodeInputBufferSize < 1) { 68 fatal("%s: decodeInputBufferSize must be >= 1 (%d)\n", name, 69 params.decodeInputBufferSize); 70 } 71 72 /* Per-thread input buffers */ 73 for (ThreadID tid = 0; tid < params.numThreads; tid++) { 74 inputBuffer.push_back( 75 InputBuffer<ForwardInstData>( 76 name + ".inputBuffer" + std::to_string(tid), "insts", 77 params.decodeInputBufferSize)); 78 } 79} 80 81const ForwardInstData * 82Decode::getInput(ThreadID tid) 83{ 84 /* Get insts from the inputBuffer to work with */ 85 if (!inputBuffer[tid].empty()) { 86 const ForwardInstData &head = inputBuffer[tid].front(); 87 88 return (head.isBubble() ? NULL : &(inputBuffer[tid].front())); 89 } else { 90 return NULL; 91 } 92} 93 94void 95Decode::popInput(ThreadID tid) 96{ 97 if (!inputBuffer[tid].empty()) 98 inputBuffer[tid].pop(); 99 100 decodeInfo[tid].inputIndex = 0; 101 decodeInfo[tid].inMacroop = false; 102} 103 104#if TRACING_ON 105/** Add the tracing data to an instruction. This originates in 106 * decode because this is the first place that execSeqNums are known 107 * (these are used as the 'FetchSeq' in tracing data) */ 108static void 109dynInstAddTracing(MinorDynInstPtr inst, StaticInstPtr static_inst, 110 MinorCPU &cpu) 111{ 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 (getInput(tid) && !decodeInfo[tid].blocked) { 318 threadPriority = tid; 319 return tid; 320 } 321 } 322 323 return InvalidThreadID; 324} 325 326bool 327Decode::isDrained() 328{ 329 for (const auto &buffer : inputBuffer) { 330 if (!buffer.empty()) 331 return false; 332 } 333 334 return (*inp.outputWire).isBubble(); 335} 336 337void 338Decode::minorTrace() const 339{ 340 std::ostringstream data; 341 342 if (decodeInfo[0].blocked) 343 data << 'B'; 344 else 345 (*out.inputWire).reportData(data); 346 347 MINORTRACE("insts=%s\n", data.str()); 348 inputBuffer[0].minorTrace(); 349} 350 351} 352