text: /*
   * Copyright (c) 2007 MIPS Technologies, Inc.
   * Copyright (c) 2013 Advanced Micro Devices, Inc.
   * All rights reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions are
   * met: redistributions of source code must retain the above copyright
   * notice, this list of conditions and the following disclaimer;
   * redistributions in binary form must reproduce the above copyright
   * notice, this list of conditions and the following disclaimer in the
   * documentation and/or other materials provided with the distribution;
   * neither the name of the copyright holders nor the names of its
   * contributors may be used to endorse or promote products derived from
   * this software without specific prior written permission.
   *
   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
   * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
   * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
   * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
   * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
   * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
   * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
   * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
   * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   *
   * Authors: Korey Sewell
   *
   */
  
  #include <list>
  #include <vector>
  
  #include "arch/isa_traits.hh"
  #include "config/the_isa.hh"
  #include "cpu/inorder/resources/use_def.hh"
  #include "cpu/inorder/cpu.hh"
  #include "cpu/inorder/pipeline_traits.hh"
  #include "debug/InOrderStall.hh"
  #include "debug/InOrderUseDef.hh"
  
  using namespace std;
  using namespace TheISA;
  using namespace ThePipeline;
  
  UseDefUnit::UseDefUnit(string res_name, int res_id, int res_width,
                         Cycles res_latency, InOrderCPU *_cpu,
                         ThePipeline::Params *params)
      : Resource(res_name, res_id, res_width, res_latency, _cpu)
  {
      for (ThreadID tid = 0; tid < ThePipeline::MaxThreads; tid++) {
          nonSpecInstActive[tid] = &cpu->nonSpecInstActive[tid];
          nonSpecSeqNum[tid] = &cpu->nonSpecSeqNum[tid];
          serializeOnNextInst[tid] =  false;
          serializeAfterSeqNum[tid] = 0;
          regDepMap[tid] = &cpu->archRegDepMap[tid];
      }
  
  }
  
  void
  UseDefUnit::regStats()
  {
      uniqueRegsPerSwitch
          .name(name() + ".uniqueRegsPerSwitch")
          .desc("Number of Unique Registers Needed Per Context Switch")
          .prereq(uniqueRegsPerSwitch);
  
      intRegFileReads
          .name(name() + ".intRegFileReads")
          .desc("Number of Reads from Int. Register File");
  
      intRegFileWrites
          .name(name() + ".intRegFileWrites")
          .desc("Number of Writes to Int. Register File");
  
      intRegFileAccs
          .name(name() + ".intRegFileAccesses")
          .desc("Total Accesses (Read+Write) to the Int. Register File");
      intRegFileAccs = intRegFileReads + intRegFileWrites;
  
      floatRegFileReads
          .name(name() + ".floatRegFileReads")
          .desc("Number of Reads from FP Register File");
  
      floatRegFileWrites
          .name(name() + ".floatRegFileWrites")
          .desc("Number of Writes to FP Register File");
  
      floatRegFileAccs
          .name(name() + ".floatRegFileAccesses")
          .desc("Total Accesses (Read+Write) to the FP Register File");
      floatRegFileAccs = floatRegFileReads + floatRegFileWrites;
  
      //@todo: add miscreg reads/writes
      //       add forwarding by type???
  
      regForwards
          .name(name() + ".regForwards")
          .desc("Number of Registers Read Through Forwarding Logic");
      
      Resource::regStats();
  }
  
  void
  UseDefUnit::init()
  {
      // Set Up Resource Events to Appropriate Resource BandWidth
      if (latency > Cycles(0)) {
          resourceEvent = new ResourceEvent[width];
      } else {
          resourceEvent = NULL;
      }
  
      for (int i = 0; i < width; i++) {
          reqs[i] = new UseDefRequest(this);
      }
  
      initSlots();
  }
  
  ResReqPtr
  UseDefUnit::getRequest(DynInstPtr inst, int stage_num, int res_idx,
                       int slot_num, unsigned cmd)
  {
      UseDefRequest *ud_req = dynamic_cast<UseDefRequest*>(reqs[slot_num]);
      ud_req->setRequest(inst, stage_num, id, slot_num, cmd,
                         inst->curSkedEntry->idx);
      return ud_req;
  }
  
  
  ResReqPtr
  UseDefUnit::findRequest(DynInstPtr inst)
  {
      for (int i = 0; i < width; i++) {
          UseDefRequest* ud_req =
              dynamic_cast<UseDefRequest*>(reqs[i]);
          assert(ud_req);
  
          if (ud_req->valid &&
              ud_req->getInst() == inst &&
              ud_req->cmd == inst->curSkedEntry->cmd &&
              ud_req->useDefIdx == inst->curSkedEntry->idx) {
              return ud_req;
          }
      }
  
      return NULL;
  }
  
  void
  UseDefUnit::execute(int slot_idx)
  {
      UseDefRequest* ud_req = dynamic_cast<UseDefRequest*>(reqs[slot_idx]);
      DynInstPtr inst = ud_req->inst;
      ThreadID tid = inst->readTid();
      InstSeqNum seq_num = inst->seqNum;
      int ud_idx = ud_req->useDefIdx;
  
      if (serializeOnNextInst[tid] &&
          seq_num > serializeAfterSeqNum[tid]) {
          inst->setSerializeBefore();
          serializeOnNextInst[tid] = false;
      }
  
      if ((inst->isIprAccess() || inst->isSerializeBefore()) &&
          cpu->instList[tid].front() != inst) {
          DPRINTF(InOrderUseDef, "[tid:%i]: [sn:%i] Serialize before instruction encountered."
                  " Blocking until pipeline is clear.\n", tid, seq_num);
          ud_req->done(false);
          return;
      } else if (inst->isStoreConditional() || inst->isSerializeAfter()) {
          DPRINTF(InOrderUseDef, "[tid:%i]: [sn:%i] Serialize after instruction encountered."
                  " Blocking until pipeline is clear.\n", tid, seq_num);
          serializeOnNextInst[tid] = true;
          serializeAfterSeqNum[tid] = seq_num;
      }
  
      if (inst->fault != NoFault) {
          DPRINTF(InOrderUseDef,
                  "[tid:%i]: [sn:%i]: Detected %s fault @ %x. Forwarding to "
                  "next stage.\n", inst->readTid(), inst->seqNum, inst->fault->name(),
                  inst->pcState());
          ud_req->done();
          return;
      }
  
      // If there is a non-speculative instruction
      // in the pipeline then stall instructions here
      // ---
      if (*nonSpecInstActive[tid] == true && seq_num > *nonSpecSeqNum[tid]) {
          DPRINTF(InOrderUseDef, "[tid:%i]: [sn:%i] cannot execute because"
                  "there is non-speculative instruction [sn:%i] has not "
                  "graduated.\n", tid, seq_num, *nonSpecSeqNum[tid]);
          ud_req->done(false);
          return;
      } else if (inst->isNonSpeculative()) {
          *nonSpecInstActive[tid] = true;
          *nonSpecSeqNum[tid] = seq_num;
      }
  
      switch (ud_req->cmd)
      {
        case ReadSrcReg:
          {
              RegClass reg_type;
              RegIndex reg_idx = inst->_srcRegIdx[ud_idx];
              RegIndex flat_idx = cpu->flattenRegIdx(reg_idx, reg_type, tid);
              inst->flattenSrcReg(ud_idx, flat_idx);
              
              if (flat_idx == TheISA::ZeroReg && reg_type == IntRegClass) {
                  DPRINTF(InOrderUseDef, "[tid:%i]: [sn:%i]: Ignoring Reading of ISA-ZeroReg "
                          "(Int. Reg %i).\n", tid, inst->seqNum, flat_idx);
                  ud_req->done();
                  return;
              } else {
                  DPRINTF(InOrderUseDef, "[tid:%i]: [sn:%i]: Attempting to read source "
                          "register idx %i (reg #%i, flat#%i).\n",
                          tid, seq_num, ud_idx, reg_idx, flat_idx);
              }
  
              if (regDepMap[tid]->canRead(reg_type, flat_idx, inst)) {
                  switch (reg_type)
                  {
                    case IntRegClass:
                      {
                          uniqueIntRegMap[flat_idx] = true;
  
                          DPRINTF(InOrderUseDef, "[tid:%i]: [sn:%i]: Reading Int Reg %i"
                                  " (%i) from Register File:0x%x.\n",
                                  tid, seq_num,
                                  reg_idx, flat_idx,
                                  cpu->readIntReg(flat_idx,inst->readTid()));
                          inst->setIntSrc(ud_idx,
                                          cpu->readIntReg(flat_idx,
                                                          inst->readTid()));
                          intRegFileReads++;
                      }
                      break;
  
                    case FloatRegClass:
                      {
                          uniqueFloatRegMap[flat_idx] = true;
                          DPRINTF(InOrderUseDef, "[tid:%i]: [sn:%i]: Reading Float Reg %i"
                                  " (%i) from Register File:%x (%08f).\n",
                                  tid, seq_num,
                                  reg_idx - FP_Reg_Base, flat_idx,
                                  cpu->readFloatRegBits(flat_idx,
                                                        inst->readTid()),
                                  cpu->readFloatReg(flat_idx,
                                                    inst->readTid()));
  
                          inst->setFloatSrc(ud_idx,
                                            cpu->readFloatReg(flat_idx,
                                                              inst->readTid()));
                          inst->setFloatRegBitsSrc(ud_idx,
                                                   cpu->readFloatRegBits(flat_idx,
                                                                         inst->readTid()));
                          floatRegFileReads++;
                      }
                      break;
  
                    case MiscRegClass:
                      {
                          uniqueMiscRegMap[flat_idx] = true;
                          DPRINTF(InOrderUseDef, "[tid:%i]: [sn:%i]: Reading Misc Reg %i "
                                  " (%i) from Register File:0x%x.\n",
                                  tid, seq_num,
                                  reg_idx - Misc_Reg_Base, flat_idx,
                                  cpu->readMiscReg(flat_idx,
                                  inst->readTid()));
                          inst->setIntSrc(ud_idx,
                                          cpu->readMiscReg(flat_idx,
                                                           inst->readTid()));
                      }
                      break;
  
                    default:
                      panic("Invalid Register Type: %i", reg_type);
                  }
  
                  ud_req->done();
              } else {
                  // Look for forwarding opportunities
                  DynInstPtr forward_inst = regDepMap[tid]->canForward(reg_type,
                                                                       flat_idx,
                                                                       inst);
  
                  if (forward_inst) {
                      int dest_reg_idx =
                          forward_inst->getDestIdxNum(flat_idx);
  
                      switch (reg_type)
                      {
                        case IntRegClass:
                          {
                              DPRINTF(InOrderUseDef, "[tid:%i]: Forwarding dest."
                                      " reg %i (%i), value 0x%x from "
                                      "[sn:%i] to [sn:%i] source #%x.\n",
                                      tid, reg_idx, flat_idx,
                                      forward_inst->readIntResult(dest_reg_idx),
                                      forward_inst->seqNum, 
                                      inst->seqNum, ud_idx);
                              inst->setIntSrc(ud_idx, 
                                              forward_inst->
                                              readIntResult(dest_reg_idx));
                          }
                          break;
  
                        case FloatRegClass:
                          {
                              DPRINTF(InOrderUseDef, "[tid:%i]: Forwarding dest."
                                      " reg %i (%i) value 0x%x from "
                                      "[sn:%i] to [sn:%i] source #%i.\n",
                                      tid, reg_idx - FP_Reg_Base, flat_idx,
                                      forward_inst->readFloatResult(dest_reg_idx),
                                      forward_inst->seqNum, inst->seqNum, ud_idx);
                              inst->setFloatSrc(ud_idx,
                                                forward_inst->
                                                readFloatResult(dest_reg_idx));
                          }
                          break;
  
                        case MiscRegClass:
                          {
                              DPRINTF(InOrderUseDef, "[tid:%i]: Forwarding dest."
                                      " reg %i (%i) value 0x%x from "
                                      "[sn:%i] to [sn:%i] source #%i.\n",
                                      tid, reg_idx - Misc_Reg_Base, flat_idx,
                                      forward_inst->readIntResult(dest_reg_idx),
                                      forward_inst->seqNum, 
                                      inst->seqNum, ud_idx);
                              inst->setIntSrc(ud_idx, 
                                              forward_inst->
                                              readIntResult(dest_reg_idx));
                          }
                          break;
  
                        default:
                          panic("Invalid Register Type: %i", reg_type);
                      }
  
                      regForwards++;
                      ud_req->done();
                  } else {
                      DPRINTF(InOrderUseDef, "[tid:%i]: Source register idx: %i "
                              "is not ready to read.\n",
                              tid, reg_idx);
                      DPRINTF(InOrderStall, "STALL: [tid:%i]: waiting to read "
                              "register (idx=%i)\n",
                              tid, reg_idx);
                      ud_req->done(false);
                  }
              }
          }
          break;
  
        case WriteDestReg:
          {
              RegClass reg_type;
              RegIndex reg_idx = inst->_destRegIdx[ud_idx];
              RegIndex flat_idx = cpu->flattenRegIdx(reg_idx, reg_type, tid);
  
              if (flat_idx == TheISA::ZeroReg && reg_type == IntRegClass) {
                  DPRINTF(IntRegs, "[tid:%i]: Ignoring Writing of ISA-ZeroReg "
                          "(Int. Reg %i)\n", tid, flat_idx);
                  ud_req->done();
                  return;
              }
  
              if (regDepMap[tid]->canWrite(reg_type, flat_idx, inst)) {
                  DPRINTF(InOrderUseDef, "[tid:%i]: [sn:%i]: Flattening register idx %i "
                          "(%i) and Attempting to write to Register File.\n",
                          tid, seq_num, reg_idx, flat_idx);
  
                  switch (reg_type)
                  {
                    case IntRegClass:
                      {
                          uniqueIntRegMap[flat_idx] = true;
  
                          DPRINTF(InOrderUseDef, "[tid:%i]: [sn:%i]: Writing Int. Result "
                                  "0x%x to register idx %i (%i).\n",
                                  tid, seq_num, inst->readIntResult(ud_idx),
                                  reg_idx, flat_idx);
  
                          // Remove Dependencies
                          regDepMap[tid]->removeFront(reg_type, flat_idx, inst);
  
                          cpu->setIntReg(flat_idx,
                                         inst->readIntResult(ud_idx),
                                         inst->readTid());
                          intRegFileWrites++;
                      }
                      break;
  
                    case FloatRegClass:
                      {
                          uniqueFloatRegMap[flat_idx] = true;
  
                          // Remove Reg. Dependecny Block on this Register
                          regDepMap[tid]->removeFront(reg_type, flat_idx, inst);
  
                          if (inst->resultType(ud_idx) == 
                              InOrderDynInst::FloatBits) {
                              DPRINTF(InOrderUseDef, "[tid:%i]: [sn:%i]: Writing FP-Bits "
                                      "Result %08f (bits:0x%x) to register "
                                      "idx %i (%i).\n",
                                      tid, seq_num,
                                      inst->readFloatResult(ud_idx), 
                                      inst->readFloatBitsResult(ud_idx),
                                      reg_idx - FP_Reg_Base, flat_idx);
  
                              // Check for FloatRegBits Here
                              cpu->setFloatRegBits(flat_idx,
                                                   inst->readFloatBitsResult(ud_idx),
                                                   inst->readTid());
                          } else if (inst->resultType(ud_idx) == 
                                     InOrderDynInst::Float) {
                              DPRINTF(InOrderUseDef, "[tid:%i]: [sn:%i]: Writing Float "
                                      "Result %08f (bits:0x%x) to register "
                                      "idx %i (%i).\n",
                                      tid, seq_num, inst->readFloatResult(ud_idx),
                                      inst->readIntResult(ud_idx), 
                                      reg_idx - FP_Reg_Base, flat_idx);
  
                              cpu->setFloatReg(flat_idx,
                                               inst->readFloatResult(ud_idx),
                                               inst->readTid());
                          } else if (inst->resultType(ud_idx) == 
                                     InOrderDynInst::Double) {
                              DPRINTF(InOrderUseDef, "[tid:%i]: [sn:%i]: Writing Double "
                                      "Result %08f (bits:0x%x) to register "
                                      "idx %i (%i).\n",
                                      tid, seq_num,
                                      inst->readFloatResult(ud_idx), 
                                      inst->readIntResult(ud_idx), 
                                      reg_idx - FP_Reg_Base, flat_idx);
  
                              cpu->setFloatReg(flat_idx,
                                               inst->readFloatResult(ud_idx),
                                               inst->readTid());
                          } else {
                              panic("Result Type Not Set For [sn:%i] %s.\n", 
                                    inst->seqNum, inst->instName());
                          }
  
                          floatRegFileWrites++;
                      }
                      break;
  
                    case MiscRegClass:
                      {
                          uniqueMiscRegMap[flat_idx] = true;
  
                          DPRINTF(InOrderUseDef, "[tid:%i]: Writing Misc. 0x%x "
                                  "to register idx %i.\n",
                                  tid, inst->readIntResult(ud_idx), reg_idx - Misc_Reg_Base);
  
                          // Remove Dependencies
                          regDepMap[tid]->removeFront(reg_type, flat_idx, inst);
  
                          cpu->setMiscReg(flat_idx,
                                      inst->readIntResult(ud_idx),
                                          inst->readTid());
                      }
                      break;
  
                    default:
                      panic("Invalid Register Type: %i", reg_type);
                  }
  
                  ud_req->done();
              } else {
                  DPRINTF(InOrderUseDef, "[tid:%i]: [sn:%i]: Dest. register idx: %i is "
                          "not ready to write.\n",
                          tid, seq_num, reg_idx);
                  DPRINTF(InOrderStall, "STALL: [tid:%i]: waiting to write "
                          "register (idx=%i)\n",
                          tid, reg_idx);
                  ud_req->done(false);
              }
          }
          break;
  
        case MarkDestRegs:
          {
              regDepMap[tid]->insert(inst);
              ud_req->done();
          }
          break;
  
        default:
          fatal("Unrecognized command to %s", resName);
      }
  
  }
  
  void
  UseDefUnit::updateAfterContextSwitch(DynInstPtr inst, ThreadID tid)
  {
      uniqueRegsPerSwitch = uniqueIntRegMap.size() + uniqueFloatRegMap.size()
          + uniqueMiscRegMap.size();
      uniqueIntRegMap.clear();
      uniqueFloatRegMap.clear();
      uniqueMiscRegMap.clear();
  }
  