/*
 * Copyright (c) 2022, Technische Universität Kaiserslautern
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *
 * 2. 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.
 *
 * 3. Neither the name of the copyright holder 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 HOLDER
 * 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:
 *    Iron Prando da Silva
 */

#include "TimeDependenciesInfoHBM2.h"

using namespace std;

TimeDependenciesInfoHBM2::TimeDependenciesInfoHBM2(const QJsonObject& memspec, const uint tCK) : DRAMTimeDependenciesBase(memspec, tCK) {
  mInitializeValues();
}

void TimeDependenciesInfoHBM2::mInitializeValues() {
  burstLength = mMemspecJson["memarchitecturespec"].toObject()["burstLength"].toInt();
  dataRate = mMemspecJson["memarchitecturespec"].toObject()["dataRate"].toInt();

  tRP = tCK * mMemspecJson["memtimingspec"].toObject()["RP"].toInt();
  tRAS = tCK * mMemspecJson["memtimingspec"].toObject()["RAS"].toInt();
  tRC = tCK * mMemspecJson["memtimingspec"].toObject()["RC"].toInt();
  tRCDRD = tCK * mMemspecJson["memtimingspec"].toObject()["RCDRD"].toInt();
  tRCDWR = tCK * mMemspecJson["memtimingspec"].toObject()["RCDWR"].toInt();
  tRTP = tCK * mMemspecJson["memtimingspec"].toObject()["RTP"].toInt();
  tRRDS = tCK * mMemspecJson["memtimingspec"].toObject()["RRDS"].toInt();
  tRRDL = tCK * mMemspecJson["memtimingspec"].toObject()["RRDL"].toInt();
  tRL = tCK * mMemspecJson["memtimingspec"].toObject()["RL"].toInt();
  tPL = tCK * mMemspecJson["memtimingspec"].toObject()["PL"].toInt();
  tCCDS = tCK * mMemspecJson["memtimingspec"].toObject()["CCDS"].toInt();
  tCCDL = tCK * mMemspecJson["memtimingspec"].toObject()["CCDL"].toInt();
  tRTW = tCK * mMemspecJson["memtimingspec"].toObject()["RTW"].toInt();
  tWL = tCK * mMemspecJson["memtimingspec"].toObject()["WL"].toInt();
  tWR = tCK * mMemspecJson["memtimingspec"].toObject()["WR"].toInt();
  tWTRS = tCK * mMemspecJson["memtimingspec"].toObject()["WTRS"].toInt();
  tWTRL = tCK * mMemspecJson["memtimingspec"].toObject()["WTRL"].toInt();
  tCKE = tCK * mMemspecJson["memtimingspec"].toObject()["CKE"].toInt();
  tXP = tCK * mMemspecJson["memtimingspec"].toObject()["XP"].toInt();
  tRFC = tCK * mMemspecJson["memtimingspec"].toObject()["RFC"].toInt();
  tRFCSB = tCK * mMemspecJson["memtimingspec"].toObject()["RFCSB"].toInt();
  tRREFD = tCK * mMemspecJson["memtimingspec"].toObject()["RREFD"].toInt();
  tXS = tCK * mMemspecJson["memtimingspec"].toObject()["XS"].toInt();
  tFAW = tCK * mMemspecJson["memtimingspec"].toObject()["FAW"].toInt();

  tPD = tCKE;
  tCKESR = tCKE + tCK;

  tBURST = (uint) (burstLength / (float) dataRate) * tCK;
  tRDPDE = tRL + tPL + tBURST + tCK;
  tRDSRE = tRDPDE;
  tWRPRE = tWL + tBURST + tWR;
  tWRPDE = tWL + tPL + tBURST + tCK + tWR;
  tWRAPDE = tWL + tPL + tBURST + tCK + tWR;
  tWRRDS = tWL + tBURST + tWTRS;
  tWRRDL = tWL + tBURST + tWTRL;

  mPools.insert({
    "RAS_BUS", {
      1, {
        {"ACT", 2*tCK},
        {"PREPB", tCK},
        {"PREAB", tCK},
        {"REFPB", tCK},
        {"REFAB", tCK},
        {"PDEA", tCK},
        {"PDXA", tCK},
        {"PDEP", tCK},
        {"PDXP", tCK},
        {"SREFEN", tCK},
        {"SREFEX", tCK},
      }
    }
  });

  mPools.insert({
    "CAS_BUS", {
      1, {
        {"RD", tCK},
        {"RDA", tCK},
        {"WR", tCK},
        {"WRA", tCK},
        {"PDEA", tCK},
        {"PDXA", tCK},
        {"PDEP", tCK},
        {"PDXP", tCK},
        {"SREFEN", tCK},
        {"SREFEX", tCK},
      }
    }
  });

  mPools.insert({
    "NAW", {
      4, {
        {"ACT", tFAW},
        {"REFPB", tFAW},
      }
    }
  });
  
}

const std::vector<QString> TimeDependenciesInfoHBM2::getPossiblePhases() {
  return {
    "ACT",
    "RD",
    "WR",
    "PREPB",
    "RDA",
    "WRA",
    "REFPB",
    "REFAB",
    "PREAB",
    "PDEP",
    "PDXP",
    "SREFEN",
    "SREFEX",
    "PDEA",
    "PDXA",
  };
}

DependencyMap TimeDependenciesInfoHBM2::mSpecializedGetDependencies() const {
  DependencyMap dmap;

  dmap.emplace(
    piecewise_construct,
    forward_as_tuple("ACT"),
    forward_as_tuple(
      initializer_list<TimeDependency>{
        {tRC, "ACT", DependencyType::IntraBank, "tRC"},
        {tRRDL, "ACT", DependencyType::IntraBankGroup, "tRRDL"},
        {tRRDS, "ACT", DependencyType::IntraRank, "tRRDS"},
        {tRTP + tRP - tCK, "RDA", DependencyType::IntraBank, "tRTP + tRP - tCK"},
        {tWRPRE + tRP - tCK, "WRA", DependencyType::IntraBank, "tWRPRE + tRP - tCK"},
        {tRP - tCK, "PREPB", DependencyType::IntraBank, "tRP - tCK"},
        {tRP - tCK, "PREAB", DependencyType::IntraRank, "tRP - tCK"},
        {tXP - tCK, "PDXA", DependencyType::IntraRank, "tXP - tCK"},
        {tXP - tCK, "PDXP", DependencyType::IntraRank, "tXP - tCK"},
        {tRFC - tCK, "REFAB", DependencyType::IntraRank, "tRFC - tCK"},
        {tRFCSB - tCK, "REFPB", DependencyType::IntraBank, "tRFCSB - tCK"},
        {tRREFD - tCK, "REFPB", DependencyType::IntraBankGroup, "tRREFD - tCK"},
        {tRREFD - tCK, "REFPB", DependencyType::IntraRank, "tRREFD - tCK"},
        {tXS - tCK, "SREFEX", DependencyType::IntraRank, "tXS - tCK"},
        {2 * tCK, "RAS_BUS", DependencyType::InterRank, "2 * tCK"},
        {tFAW, "NAW", DependencyType::IntraRank, "tFAW"},
      }
    )
  );

  dmap.emplace(
    piecewise_construct,
    forward_as_tuple("RD"),
    forward_as_tuple(
      initializer_list<TimeDependency>{
        {tRCDRD + tCK, "ACT", DependencyType::IntraBank, "tRCDRD + tCK"},
        {tCCDL, "RD", DependencyType::IntraBank, "tCCDL"},
        {tCCDL, "RD", DependencyType::IntraBankGroup, "tCCDL"},
        {tCCDS, "RD", DependencyType::IntraRank, "tCCDS"},
        {tCCDL, "RDA", DependencyType::IntraBankGroup, "tCCDL"},
        {tCCDS, "RDA", DependencyType::IntraRank, "tCCDS"},
        {tWRRDL, "WR", DependencyType::IntraBank, "tWRRDL"},
        {tWRRDL, "WR", DependencyType::IntraBankGroup, "tWRRDL"},
        {tWRRDS, "WR", DependencyType::IntraRank, "tWRRDS"},
        {tWRRDL, "WRA", DependencyType::IntraBankGroup, "tWRRDL"},
        {tWRRDS, "WRA", DependencyType::IntraRank, "tWRRDS"},
        {tXP, "PDXA", DependencyType::IntraRank, "tXP"},
        {tCK, "CAS_BUS", DependencyType::InterRank, "tCK"},
      }
    )
  );

  dmap.emplace(
    piecewise_construct,
    forward_as_tuple("WR"),
    forward_as_tuple(
      initializer_list<TimeDependency>{
        {tRCDWR + tCK, "ACT", DependencyType::IntraBank, "tRCDWR + tCK"},
        {tRTW, "RD", DependencyType::IntraBank, "tRTW"},
        {tRTW, "RD", DependencyType::IntraBankGroup, "tRTW"},
        {tRTW, "RD", DependencyType::IntraRank, "tRTW"},
        {tRTW, "RDA", DependencyType::IntraBankGroup, "tRTW"},
        {tRTW, "RDA", DependencyType::IntraRank, "tRTW"},
        {tCCDL, "WR", DependencyType::IntraBank, "tCCDL"},
        {tCCDL, "WR", DependencyType::IntraBankGroup, "tCCDL"},
        {tCCDS, "WR", DependencyType::IntraRank, "tCCDS"},
        {tCCDL, "WRA", DependencyType::IntraBankGroup, "tCCDL"},
        {tCCDS, "WRA", DependencyType::IntraRank, "tCCDS"},
        {tXP, "PDXA", DependencyType::IntraRank, "tXP"},
        {tCK, "CAS_BUS", DependencyType::InterRank, "tCK"},
      }
    )
  );

  dmap.emplace(
    piecewise_construct,
    forward_as_tuple("PREPB"),
    forward_as_tuple(
      initializer_list<TimeDependency>{
        {tRAS + tCK, "ACT", DependencyType::IntraBank, "tRAS + tCK"},
        {tRTP, "RD", DependencyType::IntraBank, "tRTP"},
        {tWRPRE, "WR", DependencyType::IntraBank, "tWRPRE"},
        {tXP, "PDXA", DependencyType::IntraRank, "tXP"},
        {tCK, "RAS_BUS", DependencyType::InterRank, "tCK"},
      }
    )
  );

  dmap.emplace(
    piecewise_construct,
    forward_as_tuple("RDA"),
    forward_as_tuple(
      initializer_list<TimeDependency>{
        {tRCDRD + tCK, "ACT", DependencyType::IntraBank, "tRCDRD + tCK"},
        {tCCDL, "RD", DependencyType::IntraBank, "tCCDL"},
        {tCCDL, "RD", DependencyType::IntraBankGroup, "tCCDL"},
        {tCCDS, "RD", DependencyType::IntraRank, "tCCDS"},
        {tCCDL, "RDA", DependencyType::IntraBankGroup, "tCCDL"},
        {tCCDS, "RDA", DependencyType::IntraRank, "tCCDS"},
        {tWL + tBURST + max({tWR - tRTP, tWTRL}), "WR", DependencyType::IntraBank, "tWL + tBURST + max(tWR - tRTP, tWTRL)"},
        {tWRRDL, "WR", DependencyType::IntraBankGroup, "tWRRDL"},
        {tWRRDS, "WR", DependencyType::IntraRank, "tWRRDS"},
        {tWRRDL, "WRA", DependencyType::IntraBankGroup, "tWRRDL"},
        {tWRRDS, "WRA", DependencyType::IntraRank, "tWRRDS"},
        {tXP, "PDXA", DependencyType::IntraRank, "tXP"},
        {tCK, "CAS_BUS", DependencyType::InterRank, "tCK"},
      }
    )
  );

  dmap.emplace(
    piecewise_construct,
    forward_as_tuple("WRA"),
    forward_as_tuple(
      initializer_list<TimeDependency>{
        {tRCDWR + tCK, "ACT", DependencyType::IntraBank, "tRCDWR + tCK"},
        {tRTW, "RD", DependencyType::IntraBank, "tRTW"},
        {tRTW, "RD", DependencyType::IntraBankGroup, "tRTW"},
        {tRTW, "RD", DependencyType::IntraRank, "tRTW"},
        {tRTW, "RDA", DependencyType::IntraBankGroup, "tRTW"},
        {tRTW, "RDA", DependencyType::IntraRank, "tRTW"},
        {tCCDL, "WR", DependencyType::IntraBank, "tCCDL"},
        {tCCDL, "WR", DependencyType::IntraBankGroup, "tCCDL"},
        {tCCDS, "WR", DependencyType::IntraRank, "tCCDS"},
        {tCCDL, "WRA", DependencyType::IntraBankGroup, "tCCDL"},
        {tCCDS, "WRA", DependencyType::IntraRank, "tCCDS"},
        {tXP, "PDXA", DependencyType::IntraRank, "tXP"},
        {tCK, "CAS_BUS", DependencyType::InterRank, "tCK"},
      }
    )
  );

  dmap.emplace(
    piecewise_construct,
    forward_as_tuple("REFPB"),
    forward_as_tuple(
      initializer_list<TimeDependency>{
        {tRC + tCK, "ACT", DependencyType::IntraBank, "tRC + tCK"},
        {tRRDL + tCK, "ACT", DependencyType::IntraBankGroup, "tRRDL + tCK"},
        {tRRDS + tCK, "ACT", DependencyType::IntraRank, "tRRDS + tCK"},
        {tRTP + tRP, "RDA", DependencyType::IntraBank, "tRTP + tRP"},
        {tWRPRE + tRP, "WRA", DependencyType::IntraBank, "tWRPRE + tRP"},
        {tRP, "PREPB", DependencyType::IntraBank, "tRP"},
        {tRP, "PREAB", DependencyType::IntraRank, "tRP"},
        {tXP, "PDXA", DependencyType::IntraRank, "tXP"},
        {tXP, "PDXP", DependencyType::IntraRank, "tXP"},
        {tRFC, "REFAB", DependencyType::IntraRank, "tRFC"},
        {tRFCSB, "REFPB", DependencyType::IntraBank, "tRFCSB"},
        {tRREFD, "REFPB", DependencyType::IntraBankGroup, "tRREFD"},
        {tRREFD, "REFPB", DependencyType::IntraRank, "tRREFD"},
        {tXS, "SREFEX", DependencyType::IntraRank, "tXS"},
        {tCK, "RAS_BUS", DependencyType::InterRank, "tCK"},
        {tFAW, "NAW", DependencyType::IntraRank, "tFAW"},
      }
    )
  );

  dmap.emplace(
    piecewise_construct,
    forward_as_tuple("REFAB"),
    forward_as_tuple(
      initializer_list<TimeDependency>{
        {tRC + tCK, "ACT", DependencyType::IntraRank, "tRC + tCK"},
        {tRTP + tRP, "RDA", DependencyType::IntraRank, "tRTP + tRP"},
        {tWRPRE + tRP, "WRA", DependencyType::IntraRank, "tWRPRE + tRP"},
        {tRP, "PREPB", DependencyType::IntraRank, "tRP"},
        {tRP, "PREAB", DependencyType::IntraRank, "tRP"},
        {tXP, "PDXP", DependencyType::IntraRank, "tXP"},
        {tRFC, "REFAB", DependencyType::IntraRank, "tRFC"},
        {tRFCSB, "REFPB", DependencyType::IntraRank, "tRFCSB"},
        {tXS, "SREFEX", DependencyType::IntraRank, "tXS"},
        {tCK, "RAS_BUS", DependencyType::InterRank, "tCK"},
      }
    )
  );

  dmap.emplace(
    piecewise_construct,
    forward_as_tuple("PREAB"),
    forward_as_tuple(
      initializer_list<TimeDependency>{
        {tRAS + tCK, "ACT", DependencyType::IntraRank, "tRAS + tCK"},
        {tRTP, "RD", DependencyType::IntraRank, "tRTP"},
        {tRTP, "RDA", DependencyType::IntraRank, "tRTP"},
        {tWRPRE, "WR", DependencyType::IntraRank, "tWRPRE"},
        {tWRPRE, "WRA", DependencyType::IntraRank, "tWRPRE"},
        {tXP, "PDXA", DependencyType::IntraRank, "tXP"},
        {tRFCSB, "REFPB", DependencyType::IntraRank, "tRFCSB"},
        {tCK, "RAS_BUS", DependencyType::InterRank, "tCK"},
      }
    )
  );

  dmap.emplace(
    piecewise_construct,
    forward_as_tuple("PDEP"),
    forward_as_tuple(
      initializer_list<TimeDependency>{
        {tRDPDE, "RD", DependencyType::IntraRank, "tRDPDE"},
        {tRDPDE, "RDA", DependencyType::IntraRank, "tRDPDE"},
        {tWRAPDE, "WRA", DependencyType::IntraRank, "tWRAPDE"},
        {tCKE, "PDXP", DependencyType::IntraRank, "tCKE"},
        {tXS, "SREFEX", DependencyType::IntraRank, "tXS"},
        {tCK, "RAS_BUS", DependencyType::InterRank, "tCK"},
        {tCK, "CAS_BUS", DependencyType::InterRank, "tCK"},
      }
    )
  );

  dmap.emplace(
    piecewise_construct,
    forward_as_tuple("PDXP"),
    forward_as_tuple(
      initializer_list<TimeDependency>{
        {tPD, "PDEP", DependencyType::IntraRank, "tPD"},
        {tCK, "RAS_BUS", DependencyType::InterRank, "tCK"},
        {tCK, "CAS_BUS", DependencyType::InterRank, "tCK"},
      }
    )
  );

  dmap.emplace(
    piecewise_construct,
    forward_as_tuple("SREFEN"),
    forward_as_tuple(
      initializer_list<TimeDependency>{
        {tRC + tCK, "ACT", DependencyType::IntraRank, "tRC + tCK"},
        {max({tRTP + tRP, tRDSRE}), "RDA", DependencyType::IntraRank, "max(tRTP + tRP, tRDSRE)"},
        {tWRPRE + tRP, "WRA", DependencyType::IntraRank, "tWRPRE + tRP"},
        {tRP, "PREPB", DependencyType::IntraRank, "tRP"},
        {tRP, "PREAB", DependencyType::IntraRank, "tRP"},
        {tXP, "PDXP", DependencyType::IntraRank, "tXP"},
        {tRFC, "REFAB", DependencyType::IntraRank, "tRFC"},
        {tRFCSB, "REFPB", DependencyType::IntraRank, "tRFCSB"},
        {tXS, "SREFEX", DependencyType::IntraRank, "tXS"},
        {tCK, "RAS_BUS", DependencyType::InterRank, "tCK"},
        {tCK, "CAS_BUS", DependencyType::InterRank, "tCK"},
      }
    )
  );

  dmap.emplace(
    piecewise_construct,
    forward_as_tuple("SREFEX"),
    forward_as_tuple(
      initializer_list<TimeDependency>{
        {tCKESR, "SREFEN", DependencyType::IntraRank, "tCKESR"},
        {tCK, "RAS_BUS", DependencyType::InterRank, "tCK"},
        {tCK, "CAS_BUS", DependencyType::InterRank, "tCK"},
      }
    )
  );

  dmap.emplace(
    piecewise_construct,
    forward_as_tuple("PDEA"),
    forward_as_tuple(
      initializer_list<TimeDependency>{
        {tRDPDE, "RD", DependencyType::IntraRank, "tRDPDE"},
        {tRDPDE, "RDA", DependencyType::IntraRank, "tRDPDE"},
        {tWRPDE, "WR", DependencyType::IntraRank, "tWRPDE"},
        {tWRAPDE, "WRA", DependencyType::IntraRank, "tWRAPDE"},
        {tCKE, "PDXA", DependencyType::IntraRank, "tCKE"},
        {tCK, "RAS_BUS", DependencyType::InterRank, "tCK"},
        {tCK, "CAS_BUS", DependencyType::InterRank, "tCK"},
      }
    )
  );

  dmap.emplace(
    piecewise_construct,
    forward_as_tuple("PDXA"),
    forward_as_tuple(
      initializer_list<TimeDependency>{
        {tPD, "PDEA", DependencyType::IntraRank, "tPD"},
        {tCK, "RAS_BUS", DependencyType::InterRank, "tCK"},
        {tCK, "CAS_BUS", DependencyType::InterRank, "tCK"},
      }
    )
  );

  return dmap;
}
