/*
 * 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 "TimeDependenciesInfoLPDDR4.h"

using namespace std;

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

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

  tRRD = tCK * mMemspecJson["memtimingspec"].toObject()["RRD"].toInt();
  tRCD = tCK * mMemspecJson["memtimingspec"].toObject()["RCD"].toInt();
  tRAS = tCK * mMemspecJson["memtimingspec"].toObject()["RAS"].toInt();
  tFAW = tCK * mMemspecJson["memtimingspec"].toObject()["FAW"].toInt();
  tRPpb = tCK * mMemspecJson["memtimingspec"].toObject()["RPpb"].toInt();
  tRPab = tCK * mMemspecJson["memtimingspec"].toObject()["RPab"].toInt();
  tRCpb = tCK * mMemspecJson["memtimingspec"].toObject()["RCpb"].toInt();
  tRCab = tCK * mMemspecJson["memtimingspec"].toObject()["RCab"].toInt();
  tCCD = tCK * mMemspecJson["memtimingspec"].toObject()["CCD"].toInt();
  tRTP = tCK * mMemspecJson["memtimingspec"].toObject()["RTP"].toInt();
  tWR = tCK * mMemspecJson["memtimingspec"].toObject()["WR"].toInt();
  tWTR = tCK * mMemspecJson["memtimingspec"].toObject()["WTR"].toInt();
  tPPD = tCK * mMemspecJson["memtimingspec"].toObject()["PPD"].toInt();
  tWPRE = tCK * mMemspecJson["memtimingspec"].toObject()["WPRE"].toInt();
  tRPST = tCK * mMemspecJson["memtimingspec"].toObject()["RPST"].toInt();
  tDQSCK = tCK * mMemspecJson["memtimingspec"].toObject()["DQSCK"].toInt();
  tDQSS = tCK * mMemspecJson["memtimingspec"].toObject()["DQSS"].toInt();
  tDQS2DQ = tCK * mMemspecJson["memtimingspec"].toObject()["DQS2DQ"].toInt();
  tRL = tCK * mMemspecJson["memtimingspec"].toObject()["RL"].toInt();
  tWL = tCK * mMemspecJson["memtimingspec"].toObject()["WL"].toInt();
  tRFCab = tCK * mMemspecJson["memtimingspec"].toObject()["RFCab"].toInt();
  tRFCpb = tCK * mMemspecJson["memtimingspec"].toObject()["RFCpb"].toInt();
  tESCKE = tCK * mMemspecJson["memtimingspec"].toObject()["ESCKE"].toInt();
  tSR = tCK * mMemspecJson["memtimingspec"].toObject()["SR"].toInt();
  tXSR = tCK * mMemspecJson["memtimingspec"].toObject()["XSR"].toInt();
  tXP = tCK * mMemspecJson["memtimingspec"].toObject()["XP"].toInt();
  tCKE = tCK * mMemspecJson["memtimingspec"].toObject()["CKE"].toInt();
  tCMDCKE = tCK * mMemspecJson["memtimingspec"].toObject()["CMDCKE"].toInt();
  tRTRS = tCK * mMemspecJson["memtimingspec"].toObject()["RTRS"].toInt();

  tBURST = (uint) (burstLength / (float) dataRate) * tCK;
  tRDWR = tRL + tDQSCK + tBURST - tWL + tWPRE + tRPST;
  tRDWR_R = tRL + tBURST + tRTRS - tWL;
  tWRRD = tWL + tCK + tBURST + tWTR;
  tWRRD_R = tWL + tBURST + tRTRS - tRL;
  tRDPRE = tRTP + tBURST - 6 * tCK;
  tRDAACT = tRTP + tRPpb + tBURST - 8 * tCK;
  tWRPRE = 2 * tCK + tWL + tCK + tBURST + tWR;
  tWRAACT = tWL + tBURST + tWR + tCK + tRPpb;
  tACTPDEN = 3 * tCK + tCMDCKE;
  tPRPDEN = tCK + tCMDCKE;
  tRDPDEN = 3 * tCK + tRL + tDQSCK + tBURST + tRPST;
  tWRPDEN = 3 * tCK + tWL + (ceil((uint) (tDQSS / (float) tCK)) + ceil((uint) (tDQS2DQ / (float) tCK))) * tCK + tBURST + tWR;
  tWRAPDEN = 3 * tCK + tWL + (ceil((uint) (tDQSS / (float) tCK)) + ceil((uint) (tDQS2DQ / (float) tCK))) * tCK + tBURST + tWR + 2 * tCK;
  tREFPDEN = tCK + tCMDCKE;
  tSREFPDEN = tCK + tESCKE;

  mPools.insert({
    "CMD_BUS", {
      1, {
        {"ACT", 4 * tCK},
        {"RD", 4 * tCK},
        {"WR", 4 * tCK},
        {"RDA", 4 * tCK},
        {"WRA", 4 * tCK},
        {"PREPB", 2 * tCK},
        {"PREAB", 2 * tCK},
        {"REFAB", 2 * tCK},
        {"SREFEN", 2 * tCK},
        {"SREFEX", 2 * tCK},
        {"REFPB", 2 * tCK},
      }
    }
  });

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

}

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

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

  dmap.emplace(
    piecewise_construct,
    forward_as_tuple("ACT"),
    forward_as_tuple(
      initializer_list<TimeDependency>{
        {tRCpb, "ACT", DependencyType::IntraBank, "tRCpb"},
        {tRRD, "ACT", DependencyType::IntraRank, "tRRD"},
        {tRDAACT, "RDA", DependencyType::IntraBank, "tRDAACT"},
        {tWRAACT, "WRA", DependencyType::IntraBank, "tWRAACT"},
        {tRPpb - 2 * tCK, "PREPB", DependencyType::IntraBank, "tRPpb - 2 * tCK"},
        {tRPab - 2 * tCK, "PREAB", DependencyType::IntraRank, "tRPab - 2 * tCK"},
        {tXP, "PDXA", DependencyType::IntraRank, "tXP"},
        {tXP, "PDXP", DependencyType::IntraRank, "tXP"},
        {tRFCab - 2 * tCK, "REFAB", DependencyType::IntraRank, "tRFCab - 2 * tCK"},
        {tRFCpb - 2 * tCK, "REFPB", DependencyType::IntraBank, "tRFCpb - 2 * tCK"},
        {tRRD - 2 * tCK, "REFPB", DependencyType::IntraRank, "tRRD - 2 * tCK"},
        {tXSR - 2 * tCK, "SREFEX", DependencyType::IntraRank, "tXSR - 2 * tCK"},
        {4 * tCK, "CMD_BUS", DependencyType::InterRank, "CommandBus"},
        {tFAW, "NAW", DependencyType::IntraRank, "tFAW"},
      }
    )
  );

  dmap.emplace(
    piecewise_construct,
    forward_as_tuple("RD"),
    forward_as_tuple(
      initializer_list<TimeDependency>{
        {tRCD, "ACT", DependencyType::IntraBank, "tRCD"},
        {tCCD, "RD", DependencyType::IntraBank, "tCCD"}, //
        {tCCD, "RD", DependencyType::IntraRank, "tCCD"},
        {tBURST + tRTRS, "RD", DependencyType::InterRank, "tBURST + tRTRS"},
        {tCCD, "RDA", DependencyType::IntraRank, "tCCD"},
        {tBURST + tRTRS, "RDA", DependencyType::InterRank, "tBURST + tRTRS"},
        {tWRRD, "WR", DependencyType::IntraBank, "tWRRD"},
        {tWRRD, "WR", DependencyType::IntraRank, "tWRRD"},
        {tWRRD_R, "WR", DependencyType::InterRank, "tWRRD_R"},
        {tWRRD, "WRA", DependencyType::IntraRank, "tWRRD"},
        {tWRRD_R, "WRA", DependencyType::InterRank, "tWRRD_R"},
        {tXP, "PDXA", DependencyType::IntraRank, "tXP"},
        {4 * tCK, "CMD_BUS", DependencyType::InterRank, "CommandBus"},
      }
    )
  );

  dmap.emplace(
    piecewise_construct,
    forward_as_tuple("WR"),
    forward_as_tuple(
      initializer_list<TimeDependency>{
        {tRCD, "ACT", DependencyType::IntraBank, "tRCD"},
        {tRDWR, "RD", DependencyType::IntraBank, "tRDWR"}, //
        {tRDWR, "RD", DependencyType::IntraRank, "tRDWR"},
        {tRDWR_R, "RD", DependencyType::InterRank, "tRDWR_R"},
        {tRDWR, "RDA", DependencyType::IntraRank, "tRDWR"},
        {tRDWR_R, "RDA", DependencyType::InterRank, "tRDWR_R"},
        {tCCD, "WR", DependencyType::IntraBank, "tCCD"},
        {tCCD, "WR", DependencyType::IntraRank, "tCCD"},
        {tBURST + tRTRS, "WR", DependencyType::InterRank, "tBURST + tRTRS"},
        {tCCD, "WRA", DependencyType::IntraRank, "tCCD"},
        {tBURST + tRTRS, "WRA", DependencyType::InterRank, "tBURST + tRTRS"},
        {tXP, "PDXA", DependencyType::IntraRank, "tXP"},
        {4 * tCK, "CMD_BUS", DependencyType::InterRank, "CommandBus"},
      }
    )
  );

  dmap.emplace(
    piecewise_construct,
    forward_as_tuple("PREPB"),
    forward_as_tuple(
      initializer_list<TimeDependency>{
        {tRAS + 2 * tCK, "ACT", DependencyType::IntraBank, "tRAS + 2 * tCK"},
        {tRDPRE, "RD", DependencyType::IntraBank, "tRDPRE"},
        {tWRPRE, "WR", DependencyType::IntraBank, "tWRPRE"},
        {tPPD, "PREPB", DependencyType::IntraRank, "tPPD"},
        {tXP, "PDXA", DependencyType::IntraRank, "tXP"},
        {2 * tCK, "CMD_BUS", DependencyType::InterRank, "CommandBus"},
      }
    )
  );

  dmap.emplace(
    piecewise_construct,
    forward_as_tuple("RDA"),
    forward_as_tuple(
      initializer_list<TimeDependency>{
        {tRCD, "ACT", DependencyType::IntraBank, "tRCD"},
        {tCCD, "RD", DependencyType::IntraBank, "tCCD"},
        {tCCD, "RD", DependencyType::IntraRank, "tCCD"},
        {tBURST + tRTRS, "RD", DependencyType::InterRank, "tBURST + tRTRS"},
        {tCCD, "RDA", DependencyType::IntraRank, "tCCD"},
        {tBURST + tRTRS, "RDA", DependencyType::InterRank, "tBURST + tRTRS"},
        {max({tWRRD, tWRPRE - tRDPRE}), "WR", DependencyType::IntraBank, "max(tWRRD, tWRPRE - tRDPRE)"},
        {tWRRD, "WR", DependencyType::IntraRank, "tWRRD"},
        {tWRRD_R, "WR", DependencyType::InterRank, "tWRRD_R"},
        {tWRRD, "WRA", DependencyType::IntraRank, "tWRRD"},
        {tWRRD_R, "WRA", DependencyType::InterRank, "tWRRD_R"},
        {tXP, "PDXA", DependencyType::IntraRank, "tXP"},
        {4 * tCK, "CMD_BUS", DependencyType::InterRank, "CommandBus"},
      }
    )
  );

  dmap.emplace(
    piecewise_construct,
    forward_as_tuple("WRA"),
    forward_as_tuple(
      initializer_list<TimeDependency>{
        {tRCD, "ACT", DependencyType::IntraBank, "tRCD"},
        {tRDWR, "RD", DependencyType::IntraBank, "tRDWR"},
        {tRDWR, "RD", DependencyType::IntraRank, "tRDWR"},
        {tRDWR_R, "RD", DependencyType::InterRank, "tRDWR_R"},
        {tRDWR, "RDA", DependencyType::IntraRank, "tRDWR"},
        {tRDWR_R, "RDA", DependencyType::InterRank, "tRDWR_R"},
        {tCCD, "WR", DependencyType::IntraBank, "tCCD"},
        {tCCD, "WR", DependencyType::IntraRank, "tCCD"},
        {tBURST + tRTRS, "WR", DependencyType::InterRank, "tBURST + tRTRS"},
        {tCCD, "WRA", DependencyType::IntraRank, "tCCD"},
        {tBURST + tRTRS, "WRA", DependencyType::InterRank, "tBURST + tRTRS"},
        {tXP, "PDXA", DependencyType::IntraRank, "tXP"},
        {4 * tCK, "CMD_BUS", DependencyType::InterRank, "CommandBus"},
      }
    )
  );

  dmap.emplace(
    piecewise_construct,
    forward_as_tuple("REFPB"),
    forward_as_tuple(
      initializer_list<TimeDependency>{
        {tRCpb + 2 * tCK, "ACT", DependencyType::IntraBank, "tRCpb + 2 * tCK"},
        {tRRD + 2 * tCK, "ACT", DependencyType::IntraRank, "tRRD + 2 * tCK"},
        {tRDPRE + tRPpb, "RDA", DependencyType::IntraBank, "tRDPRE + tRPpb"},
        {tWRPRE + tRPpb, "WRA", DependencyType::IntraBank, "tWRPRE + tRPpb"},
        {tRPpb, "PREPB", DependencyType::IntraBank, "tRPpb"},
        {tRPab, "PREAB", DependencyType::IntraRank, "tRPab"},
        {tXP, "PDXP", DependencyType::IntraRank, "tXP"},
        {tXP, "PDXA", DependencyType::IntraRank, "tXP"},
        {tRFCab, "REFAB", DependencyType::IntraRank, "tRFCab"},
        {tRFCpb, "REFPB", DependencyType::IntraBank, "tRFCpb"}, //
        {tRFCpb, "REFPB", DependencyType::IntraRank, "tRFCpb"},
        {tXSR, "SREFEX", DependencyType::IntraRank, "tXSR"},
        {2 * tCK, "CMD_BUS", DependencyType::InterRank, "CommandBus"},
        {tFAW, "NAW", DependencyType::IntraRank, "tFAW"},
      }
    )
  );

  dmap.emplace(
    piecewise_construct,
    forward_as_tuple("REFAB"),
    forward_as_tuple(
      initializer_list<TimeDependency>{
        {tRCpb + 2 * tCK, "ACT", DependencyType::IntraRank, "tRCpb + 2 * tCK"},
        {tRDPRE + tRPpb, "RDA", DependencyType::IntraRank, "tRDPRE + tRPpb"},
        {tWRPRE + tRPpb, "WRA", DependencyType::IntraRank, "tWRPRE + tRPpb"},
        {tRPpb, "PREPB", DependencyType::IntraRank, "tRPpb"},
        {tRPab, "PREAB", DependencyType::IntraRank, "tRPab"},
        {tXP, "PDXP", DependencyType::IntraRank, "tXP"},
        {tRFCab, "REFAB", DependencyType::IntraRank, "tRFCab"},
        {tRFCpb, "REFPB", DependencyType::IntraRank, "tRFCpb"},
        {tXSR, "SREFEX", DependencyType::IntraRank, "tXSR"},
        {2 * tCK, "CMD_BUS", DependencyType::InterRank, "CommandBus"},
      }
    )
  );

  dmap.emplace(
    piecewise_construct,
    forward_as_tuple("PREAB"),
    forward_as_tuple(
      initializer_list<TimeDependency>{
        {tRAS + 2 * tCK, "ACT", DependencyType::IntraRank, "tRAS + 2 * tCK"},
        {tRDPRE, "RD", DependencyType::IntraRank, "tRDPRE"},
        {tRDPRE, "RDA", DependencyType::IntraRank, "tRDPRE"},
        {tWRPRE, "WR", DependencyType::IntraRank, "tWRPRE"},
        {tWRPRE, "WRA", DependencyType::IntraRank, "tWRPRE"},
        {tPPD, "PREPB", DependencyType::IntraRank, "tPPD"},
        {tXP, "PDXA", DependencyType::IntraRank, "tXP"},
        {tRFCpb, "REFPB", DependencyType::IntraRank, "tRFCpb"},
        {2 * tCK, "CMD_BUS", DependencyType::InterRank, "CommandBus"},
      }
    )
  );

  dmap.emplace(
    piecewise_construct,
    forward_as_tuple("PDEP"),
    forward_as_tuple(
      initializer_list<TimeDependency>{
        {tRDPDEN, "RD", DependencyType::IntraRank, "tRDPDEN"},
        {tRDPDEN, "RDA", DependencyType::IntraRank, "tRDPDEN"},
        {tWRAPDEN, "WRA", DependencyType::IntraRank, "tWRAPDEN"},
        {tPRPDEN, "PREPB", DependencyType::IntraRank, "tPRPDEN"},
        {tPRPDEN, "PREAB", DependencyType::IntraRank, "tPRPDEN"},
        {tCKE, "PDXP", DependencyType::IntraRank, "tCKE"},
        {tREFPDEN, "REFAB", DependencyType::IntraRank, "tREFPDEN"},
        {tREFPDEN, "REFPB", DependencyType::IntraRank, "tREFPDEN"},
        {tXSR, "SREFEX", DependencyType::IntraRank, "tXSR"},
      }
    )
  );

  dmap.emplace(
    piecewise_construct,
    forward_as_tuple("PDXP"),
    forward_as_tuple(
      initializer_list<TimeDependency>{
        {tCKE, "PDEP", DependencyType::IntraRank, "tCKE"},
      }
    )
  );

  dmap.emplace(
    piecewise_construct,
    forward_as_tuple("SREFEN"),
    forward_as_tuple(
      initializer_list<TimeDependency>{
        {tRCpb + 2 * tCK, "ACT", DependencyType::IntraRank, "tRCpb + 2 * tCK"},
        {max({tRDPDEN, tRDPRE + tRPpb}), "RDA", DependencyType::IntraRank, "max(tRDPDEN, tRDPRE + tRPpb)"},
        {max({tWRAPDEN, tWRPRE + tRPpb}), "WRA", DependencyType::IntraRank, "max(tWRAPDEN, tWRPRE + tRPpb)"},
        {tRPpb, "PREPB", DependencyType::IntraRank, "tRPpb"},
        {tRPab, "PREAB", DependencyType::IntraRank, "tRPab"},
        {tXP, "PDXP", DependencyType::IntraRank, "tXP"},
        {tRFCab, "REFAB", DependencyType::IntraRank, "tRFCab"},
        {tRFCpb, "REFPB", DependencyType::IntraRank, "tRFCpb"},
        {tXSR, "SREFEX", DependencyType::IntraRank, "tXSR"},
        {2 * tCK, "CMD_BUS", DependencyType::InterRank, "CommandBus"},
      }
    )
  );

  dmap.emplace(
    piecewise_construct,
    forward_as_tuple("SREFEX"),
    forward_as_tuple(
      initializer_list<TimeDependency>{
        {tSR, "SREFEN", DependencyType::IntraRank, "tSR"},
        {tXP, "SRPDEX", DependencyType::IntraRank, "tXP"}, //
        {2 * tCK, "CMD_BUS", DependencyType::InterRank, "CommandBus"},
      }
    )
  );

  dmap.emplace(
    piecewise_construct,
    forward_as_tuple("PDEA"),
    forward_as_tuple(
      initializer_list<TimeDependency>{
        {tACTPDEN, "ACT", DependencyType::IntraRank, "tACTPDEN"},
        {tRDPDEN, "RD", DependencyType::IntraRank, "tRDPDEN"},
        {tRDPDEN, "RDA", DependencyType::IntraRank, "tRDPDEN"},
        {tWRPDEN, "WR", DependencyType::IntraRank, "tWRPDEN"},
        {tWRAPDEN, "WRA", DependencyType::IntraRank, "tWRAPDEN"},
        {tPRPDEN, "PREPB", DependencyType::IntraRank, "tPRPDEN"},
        {tCKE, "PDXA", DependencyType::IntraRank, "tCKE"},
        {tREFPDEN, "REFPB", DependencyType::IntraRank, "tREFPDEN"},
      }
    )
  );

  dmap.emplace(
    piecewise_construct,
    forward_as_tuple("PDXA"),
    forward_as_tuple(
      initializer_list<TimeDependency>{
        {tCKE, "PDEA", DependencyType::IntraRank, "tCKE"},
      }
    )
  );

  dmap.emplace(
    piecewise_construct,
    forward_as_tuple("SRPDEN"),
    forward_as_tuple(
      initializer_list<TimeDependency>{
        {tSREFPDEN, "SREFEN", DependencyType::IntraRank, "tSREFPDEN"},
      }
    )
  );

  dmap.emplace(
    piecewise_construct,
    forward_as_tuple("SRPDEX"),
    forward_as_tuple(
      initializer_list<TimeDependency>{
        {tCKE, "SRPDEN", DependencyType::IntraRank, "tCKE"},
      }
    )
  );

  return dmap;
}
