/*
 * Copyright (c) 2013-2014 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.
 *
 * 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: Andrew Bardsley
 */

/**
 * @file
 *
 *  Execute function unit descriptions and pipeline implementations.
 */

#ifndef __CPU_MINOR_FUNC_UNIT_HH__
#define __CPU_MINOR_FUNC_UNIT_HH__

#include "cpu/minor/buffers.hh"
#include "cpu/minor/dyn_inst.hh"
#include "cpu/func_unit.hh"
#include "cpu/timing_expr.hh"
#include "params/MinorFU.hh"
#include "params/MinorFUPool.hh"
#include "params/MinorOpClass.hh"
#include "params/MinorOpClassSet.hh"
#include "sim/clocked_object.hh"

/** Boxing for MinorOpClass to get around a build problem with C++11 but
 *  also allow for future additions to op class checking */
class MinorOpClass : public SimObject
{
  public:
    OpClass opClass;

  public:
    MinorOpClass(const MinorOpClassParams *params) :
        SimObject(params),
        opClass(params->opClass)
    { }
};

/** Wrapper for a matchable set of op classes */
class MinorOpClassSet : public SimObject
{
  public:
    std::vector<MinorOpClass *> opClasses;

    /** Convenience packing of opClasses into a bit vector for easier
     *  testing */
    std::vector<bool> capabilityList;

  public:
    MinorOpClassSet(const MinorOpClassSetParams *params);

  public:
    /** Does this set support the given op class */
    bool provides(OpClass op_class) { return capabilityList[op_class]; }
};

/** Extra timing capability to allow individual ops to have their source
 *  register dependency latencies tweaked based on the ExtMachInst of the
 *  source instruction.
 */
class MinorFUTiming: public SimObject
{
  public:
    /** Mask off the ExtMachInst of an instruction before comparing with
     *  match */
    uint64_t mask;
    uint64_t match;

    /** Textual description of the decode's purpose */
    std::string description;

    /** If true, instructions matching this mask/match should *not* be
     *  issued in this FU */
    bool suppress;

    /** Extra latency that the instruction should spend at the end of
     *  the pipeline */
    Cycles extraCommitLat;
    TimingExpr *extraCommitLatExpr;

    /** Extra delay that results should show in the scoreboard after
     *  leaving the pipeline.  If set to Cycles(0) for memory references,
     *  an 'unpredictable' return time will be set in the scoreboard
     *  blocking following dependent instructions from issuing */
    Cycles extraAssumedLat;

    /** Cycle offsets from the scoreboard delivery times of register values
     *  for each of this instruction's source registers (in srcRegs order).
     *  The offsets are subtracted from the scoreboard returnCycle times.
     *  For example, for an instruction type with 3 source registers,
     *  [2, 1, 2] will allow the instruction to issue upto 2 cycles early
     *  for dependencies on the 1st and 3rd register and upto 1 cycle early
     *  on the 2nd. */
    std::vector<Cycles> srcRegsRelativeLats;

    /** Extra opClasses check (after the FU one) */
    MinorOpClassSet *opClasses;

  public:
    MinorFUTiming(const MinorFUTimingParams *params);

  public:
    /** Does the extra decode in this object support the given op class */
    bool provides(OpClass op_class) { return opClasses->provides(op_class); }
};

/** A functional unit that can execute any of opClasses operations with a
 *  single op(eration)Lat(ency) and issueLat(ency) associated with the unit
 *  rather than each operation (as in src/FuncUnit).
 *
 *  This is very similar to cpu/func_unit but replicated here to allow
 *  the Minor functional units to change without having to disturb the common
 *  definition.
 */
class MinorFU : public SimObject
{
  public:
    MinorOpClassSet *opClasses;

    /** Delay from issuing the operation, to it reaching the
     *  end of the associated pipeline */
    Cycles opLat;

    /** Delay after issuing an operation before the next
     *  operation can be issued */
    Cycles issueLat;

    /** FUs which this pipeline can't receive a forwarded (i.e. relative
     *  latency != 0) result from */
    std::vector<unsigned int> cantForwardFromFUIndices;

    /** Extra timing info to give timings to individual ops */
    std::vector<MinorFUTiming *> timings;

  public:
    MinorFU(const MinorFUParams *params) :
        SimObject(params),
        opClasses(params->opClasses),
        opLat(params->opLat),
        issueLat(params->issueLat),
        cantForwardFromFUIndices(params->cantForwardFromFUIndices),
        timings(params->timings)
    { }
};

/** A collection of MinorFUs */
class MinorFUPool : public SimObject
{
  public:
    std::vector<MinorFU *> funcUnits;

  public:
    MinorFUPool(const MinorFUPoolParams *params) :
        SimObject(params),
        funcUnits(params->funcUnits)
    { }
};

namespace Minor
{

/** Container class to box instructions in the FUs to make those
 *  queues have correct bubble behaviour when stepped */
class QueuedInst
{
  public:
    MinorDynInstPtr inst;

  public:
    QueuedInst(MinorDynInstPtr inst_ = MinorDynInst::bubble()) :
        inst(inst_)
    { }

  public:
    /** Report and bubble interfaces */
    void reportData(std::ostream &os) const;
    bool isBubble() const { return inst->isBubble(); }

    static QueuedInst bubble()
    { return QueuedInst(MinorDynInst::bubble()); }
};

/** Functional units have pipelines which stall when an inst gets to
 *  their ends allowing Execute::commit to pick up timing-completed insts
 *  when it feels like it */
typedef SelfStallingPipeline<QueuedInst,
    ReportTraitsAdaptor<QueuedInst> > FUPipelineBase;

/** A functional unit configured from a MinorFU object */
class FUPipeline : public FUPipelineBase, public FuncUnit
{
  public:
    /** Functional unit description that this pipeline implements */
    const MinorFU &description;

    /** An FUPipeline needs access to curCycle, use this timing source */
    ClockedObject &timeSource;

    /** Set of operation classes supported by this FU */
    std::bitset<Num_OpClasses> capabilityList;

    /** FUs which this pipeline can't receive a forwarded (i.e. relative
     *  latency != 0) result from */
    std::vector<bool> cantForwardFromFUIndices;

  public:
    /** When can a new instruction be inserted into the pipeline?  This is
     *  an absolute cycle time unless it is 0 in which case the an
     *  instruction can be pushed straightaway */
    Cycles nextInsertCycle;

  public:
    FUPipeline(const std::string &name, const MinorFU &description_,
        ClockedObject &timeSource_);

  public:
    /** How many cycles must from curCycle before insertion into the
     *  pipeline is allowed */
    Cycles cyclesBeforeInsert();

    /** Can an instruction be inserted now? */
    bool canInsert() const;

    /** Find the extra timing information for this instruction.  Returns
     *  NULL if no decode info. is found */
    MinorFUTiming *findTiming(const StaticInstPtr &inst);

    /** Step the pipeline.  Allow multiple steps? */
    void advance();
};

}

#endif /* __CPU_MINOR_FUNC_UNIT_HH__ */