text: /*
   * Copyright (c) 2012 ARM Limited
   * All rights reserved
   *
   * The license below extends only to copyright in the software and shall
   * not be construed as granting a license to any other intellectual
   * property including but not limited to intellectual property relating
   * to a hardware implementation of the functionality of the software
   * licensed hereunder.  You may use the software subject to the license
   * terms below provided that you ensure that this notice is replicated
   * unmodified and in its entirety in all distributions of the software,
   * modified or unmodified, in source code or in binary form.
   *
   * Copyright (c) 2007 MIPS Technologies, 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 "cpu/inorder/resources/resource_list.hh"
  #include "cpu/inorder/resource_pool.hh"
  
  using namespace std;
  using namespace ThePipeline;
  
  ResourcePool::ResourcePool(InOrderCPU *_cpu, InOrderCPUParams *params)
      : cpu(_cpu), instUnit(NULL), dataUnit(NULL)
  {
      //@todo: use this function to instantiate the resources in resource pool. This will help in the
      //auto-generation of this pipeline model.
      //ThePipeline::addResources(resources, memObjects);
  
      // Declare Resource Objects
      // name - id - bandwidth - latency - CPU - Parameters
      // --------------------------------------------------
      resources.push_back(new FetchSeqUnit("fetch_seq_unit", FetchSeq,
              StageWidth * 2, 0, _cpu, params));
  
      resources.push_back(new TLBUnit("itlb", ITLB, StageWidth, 0, _cpu, params));
  
  
      // Keep track of the instruction fetch unit so we can easily
      // provide a pointer to it in the CPU.
      instUnit = new FetchUnit("icache_port", ICache,
                               StageWidth * MaxThreads, 0, _cpu,
                               params);
      resources.push_back(instUnit);
  
      resources.push_back(new DecodeUnit("decode_unit", Decode, StageWidth, 0,
              _cpu, params));
  
      resources.push_back(new BranchPredictor("branch_predictor", BPred,
              StageWidth, 0, _cpu, params));
  
      for (int i = 0; i < params->numberOfThreads; i++) {
          char fbuff_name[20];
          sprintf(fbuff_name, "fetch_buffer_t%i", i);
          resources.push_back(new InstBuffer(fbuff_name, FetchBuff + i, 4, 0,
                  _cpu, params));
      }
  
      resources.push_back(new UseDefUnit("regfile_manager", RegManager,
              StageWidth * MaxThreads, 0, _cpu, params));
  
      resources.push_back(new AGENUnit("agen_unit", AGEN, StageWidth, 0, _cpu,
              params));
  
      resources.push_back(new ExecutionUnit("execution_unit", ExecUnit,
              StageWidth, 0, _cpu, params));
  
      resources.push_back(new MultDivUnit("mult_div_unit", MDU, 5, 0, _cpu,
              params));
  
      resources.push_back(new TLBUnit("dtlb", DTLB, StageWidth, 0, _cpu, params));
  
      // Keep track of the data load/store unit so we can easily provide
      // a pointer to it in the CPU.
      dataUnit = new CacheUnit("dcache_port", DCache,
                               StageWidth * MaxThreads, 0, _cpu,
                               params);
      resources.push_back(dataUnit);
  
      resources.push_back(new GraduationUnit("graduation_unit", Grad,
              StageWidth * MaxThreads, 0, _cpu, params));
  }
  
  void
  ResourcePool::init()
  {
      for (int i=0; i < resources.size(); i++) {
          resources[i]->init();
      }
  }
  
  string
  ResourcePool::name()
  {
      return cpu->name() + ".ResourcePool";
  }
  
  
  void
  ResourcePool::regStats()
  {
      DPRINTF(Resource, "Registering Stats Throughout Resource Pool.\n");
  
      int num_resources = resources.size();
  
      for (int idx = 0; idx < num_resources; idx++) {
          resources[idx]->regStats();
      }
  }
  
  ResReqPtr
  ResourcePool::request(int res_idx, DynInstPtr inst)
  {
      //Make Sure This is a valid resource ID
      assert(res_idx >= 0 && res_idx < resources.size());
  
      return resources[res_idx]->request(inst);
  }
  
  void
  ResourcePool::squash(DynInstPtr inst, int res_idx, InstSeqNum done_seq_num,
                       ThreadID tid)
  {
      resources[res_idx]->squash(inst, ThePipeline::NumStages-1, done_seq_num, tid);
  }
  
  int
  ResourcePool::slotsAvail(int res_idx)
  {
      return resources[res_idx]->slotsAvail();
  }
  
  int
  ResourcePool::slotsInUse(int res_idx)
  {
      return resources[res_idx]->slotsInUse();
  }
  
  void
  ResourcePool::scheduleEvent(InOrderCPU::CPUEventType e_type, DynInstPtr inst,
                              int delay,  int res_idx, ThreadID tid)
  {
      assert(delay >= 0);
  
      ResPoolEvent *res_pool_event = new ResPoolEvent(this);
  
      switch (e_type)
      {
        case InOrderCPU::ActivateThread:
          {
              DPRINTF(Resource, "Scheduling Activate Thread Resource Pool Event for tick %i.\n",
                      curTick() + delay);
              res_pool_event->setEvent(e_type,
                                       inst,
                                       inst->squashingStage,
                                       inst->squashSeqNum,
                                       inst->readTid());
              res_pool_event->schedule(curTick() + cpu->cycles(delay));
  
          }
          break;
  
        case InOrderCPU::SuspendThread:
        case InOrderCPU::DeallocateThread:
          {
              DPRINTF(Resource, "Scheduling Deactivate Thread Resource Pool Event for tick %i.\n",
                      curTick() + delay);
  
              res_pool_event->setEvent(e_type,
                                       inst,
                                       inst->squashingStage,
                                       inst->squashSeqNum,
                                       tid);
  
              res_pool_event->schedule(curTick() + cpu->cycles(delay));
  
          }
          break;
  
        case ResourcePool::InstGraduated:
          {
              DPRINTF(Resource, "Scheduling Inst-Graduated Resource Pool Event for tick %i.\n",
                      curTick() + delay);
  
              res_pool_event->setEvent(e_type,
                                       inst,
                                       inst->squashingStage,
                                       inst->seqNum,
                                       inst->readTid());
              res_pool_event->schedule(curTick() + cpu->cycles(delay));
  
          }
          break;
  
        case ResourcePool::SquashAll:
          {
              DPRINTF(Resource, "Scheduling Squash Resource Pool Event for tick %i.\n",
                      curTick() + delay);
              res_pool_event->setEvent(e_type,
                                       inst,
                                       inst->squashingStage,
                                       inst->squashSeqNum,
                                       inst->readTid());
              res_pool_event->schedule(curTick() + cpu->cycles(delay));
  
          }
          break;
  
        default:
          DPRINTF(Resource, "Ignoring Unrecognized CPU Event Type #%i.\n", e_type);
          ; // If Resource Pool doesnt recognize event, we ignore it.
      }
  }
  
  void
  ResourcePool::unscheduleEvent(int res_idx, DynInstPtr inst)
  {
      resources[res_idx]->unscheduleEvent(inst);
  }
  
  void
  ResourcePool::squashAll(DynInstPtr inst, int stage_num,
                          InstSeqNum done_seq_num, ThreadID tid)
  {
      DPRINTF(Resource, "[tid:%i] Stage %i squashing all instructions above [sn:%i].\n",
              stage_num, tid, done_seq_num);
  
      int num_resources = resources.size();
  
      for (int idx = 0; idx < num_resources; idx++) {
          resources[idx]->squash(inst, stage_num, done_seq_num, tid);
      }
  }
  
  void
  ResourcePool::activateAll(ThreadID tid)
  {
      DPRINTF(Resource, "[tid:%i] Broadcasting Thread Activation to all resources.\n",
              tid);
  
      int num_resources = resources.size();
  
      for (int idx = 0; idx < num_resources; idx++) {
          resources[idx]->activateThread(tid);
      }
  }
  
  void
  ResourcePool::deactivateAll(ThreadID tid)
  {
      DPRINTF(Resource, "[tid:%i] Broadcasting Thread Deactivation to all resources.\n",
              tid);
  
      int num_resources = resources.size();
  
      for (int idx = 0; idx < num_resources; idx++) {
          resources[idx]->deactivateThread(tid);
      }
  }
  
  void
  ResourcePool::instGraduated(InstSeqNum seq_num, ThreadID tid)
  {
      DPRINTF(Resource, "[tid:%i] Broadcasting [sn:%i] graduation to all resources.\n",
              tid, seq_num);
  
      int num_resources = resources.size();
  
      for (int idx = 0; idx < num_resources; idx++) {
          resources[idx]->instGraduated(seq_num, tid);
      }
  }
  
  ResourcePool::ResPoolEvent::ResPoolEvent(ResourcePool *_resPool)
      : Event(&mainEventQueue, CPU_Tick_Pri),
        resPool(_resPool)
  { eventType = (InOrderCPU::CPUEventType) Default; }
  
  void
  ResourcePool::ResPoolEvent::process()
  {
      switch (eventType)
      {
        case InOrderCPU::ActivateThread:
          resPool->activateAll(tid);
          break;
  
        case InOrderCPU::SuspendThread:
        case InOrderCPU::DeallocateThread:
          resPool->deactivateAll(tid);
          break;
  
        case ResourcePool::InstGraduated:
          resPool->instGraduated(seqNum, tid);
          break;
  
        case ResourcePool::SquashAll:
          resPool->squashAll(inst, stageNum, seqNum, tid);
          break;
  
        default:
          fatal("Unrecognized Event Type");
      }
  
      resPool->cpu->cpuEventRemoveList.push(this);
  }
  
  
  const char *
  ResourcePool::ResPoolEvent::description()
  {
      return "Resource Pool event";
  }
  
  /** Schedule resource event, regardless of its current state. */
  void
  ResourcePool::ResPoolEvent::scheduleEvent(int delay)
  {
      if (squashed())
          reschedule(curTick() + resPool->cpu->cycles(delay));
      else if (!scheduled())
          schedule(curTick() + resPool->cpu->cycles(delay));
  }
  
  /** Unschedule resource event, regardless of its current state. */
  void
  ResourcePool::ResPoolEvent::unscheduleEvent()
  {
      if (scheduled())
          squash();
  }
  