/*
 * Copyright (c) 2015, University of 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:
 *    Robert Gernhardt
 *    Matthias Jung
 *    Peter Ehses
 *    Eder F. Zulian
 *    Felipe S. Prado
 */

#ifndef DRAM_H_
#define DRAM_H_

#include <sys/mman.h>
#include <tlm>
#include <systemc>
#include <tlm_utils/peq_with_cb_and_phase.h>
#include <tlm_utils/simple_target_socket.h>
#include <vector>
#include <array>
#include <cassert>
#include <cstdint>
#include <stdlib.h>
#include "../common/DebugManager.h"
#include "../common/dramExtension.h"
#include "../controller/Controller.h"
#include "../controller/core/TimingCalculation.h"
#include "../controller/core/configuration/Configuration.h"
#include "../common/protocol.h"
#include "../common/Utils.h"
#include "../common/third_party/DRAMPower/src/libdrampower/LibDRAMPower.h"
#include "../error/errormodel.h"

using namespace std;
using namespace tlm;
using namespace Data;

struct Dram : sc_module {
    unsigned int bytesPerBurst = Configuration::getInstance().getBytesPerBurst();

    // TLM Related:
    tlm_utils::simple_target_socket<Dram> tSocket;

    // Power Model related
    bool powerAnalysis = Configuration::getInstance().PowerAnalysis;
    libDRAMPower *DRAMPower;

    // Bandwidth realted:
    unsigned long long int numberOfTransactionsServed;
    sc_time firstAccess;
    sc_time lastAccess;

    // Error Model related:
    StorageMode StoreMode = Configuration::getInstance().StoreMode;
    std::vector<errorModel *> ememory;

    // Data Storage:
    unsigned char *memory;

    Controller *dramController;

    SC_CTOR(Dram) : tSocket("socket")
    {
        // Adjust number of bytes per burst dynamically to the selected ecc controller
        bytesPerBurst = Configuration::getInstance().adjustNumBytesAfterECC(
                            bytesPerBurst);
        dramController = NULL;

        std::uint64_t memorySize = Configuration::getInstance().getSimMemSizeInBytes();
        if (Configuration::getInstance().UseMalloc) {
            memory = (unsigned char *)malloc(memorySize);
            if (!memory) {
                SC_REPORT_FATAL(this->name(), "Memory allocation failed");
            }
        } else {
            // allocate and model storage of one DRAM channel using memory map
            memory = (unsigned char *)mmap(NULL, memorySize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON | MAP_NORESERVE, -1, 0);
        }

        tSocket.register_nb_transport_fw(this, &Dram::nb_transport_fw);
        tSocket.register_transport_dbg(this, &Dram::transport_dbg);

        if (powerAnalysis == true) {
            sc_time clk = Configuration::getInstance().memSpec.clk;

            MemArchitectureSpec memArchSpec;
            memArchSpec.burstLength       =
                Configuration::getInstance().memSpec.BurstLength;
            memArchSpec.dataRate          = Configuration::getInstance().memSpec.DataRate;
            memArchSpec.nbrOfRows         =
                Configuration::getInstance().memSpec.NumberOfRows;
            memArchSpec.nbrOfBanks        =
                Configuration::getInstance().memSpec.NumberOfBanks;
            memArchSpec.nbrOfColumns      =
                Configuration::getInstance().memSpec.NumberOfColumns;
            memArchSpec.nbrOfRanks        =
                Configuration::getInstance().memSpec.NumberOfRanks;
            memArchSpec.width             = Configuration::getInstance().memSpec.bitWidth;
            memArchSpec.nbrOfBankGroups   =
                Configuration::getInstance().memSpec.NumberOfBankGroups;
            memArchSpec.twoVoltageDomains = (Configuration::getInstance().memSpec.vDD2 == 0
                                             ? false : true);
            memArchSpec.dll               = Configuration::getInstance().memSpec.DLL;

            MemTimingSpec memTimingSpec;
            memTimingSpec.FAWB    = Configuration::getInstance().tfawbclk;
            memTimingSpec.RASB    = Configuration::getInstance().trasbclk;
            memTimingSpec.RCB     = Configuration::getInstance().trcbclk;
            memTimingSpec.RPB     = Configuration::getInstance().trpbclk;
            memTimingSpec.RRDB    = Configuration::getInstance().trrdblclk;
            memTimingSpec.RRDB_L  = Configuration::getInstance().trrdblclk;
            memTimingSpec.RRDB_S  = Configuration::getInstance().trrdblclk;
            memTimingSpec.AL     = Configuration::getInstance().memSpec.tAL / clk;
            memTimingSpec.CCD    = Configuration::getInstance().memSpec.tCCD_S / clk;
            memTimingSpec.CCD_L  = Configuration::getInstance().memSpec.tCCD_L / clk;
            memTimingSpec.CCD_S  = Configuration::getInstance().memSpec.tCCD_S / clk;
            memTimingSpec.CKE    = Configuration::getInstance().memSpec.tCKE / clk;
            memTimingSpec.CKESR  = Configuration::getInstance().memSpec.tCKESR / clk;
            memTimingSpec.clkMhz = Configuration::getInstance().memSpec.clkMHz;
            // See also MemTimingSpec.cc in DRAMPower
            memTimingSpec.clkPeriod = 1000.0 / Configuration::getInstance().memSpec.clkMHz;
            memTimingSpec.DQSCK  = Configuration::getInstance().memSpec.tDQSCK / clk;
            memTimingSpec.FAW    = Configuration::getInstance().memSpec.tNAW / clk;
            memTimingSpec.RAS    = Configuration::getInstance().memSpec.tRAS / clk;
            memTimingSpec.RC     = Configuration::getInstance().memSpec.tRC / clk;
            memTimingSpec.RCD    = Configuration::getInstance().memSpec.tRCD / clk;
            memTimingSpec.REFI   = Configuration::getInstance().memSpec.tREFI / clk;
            auto m = Configuration::getInstance().getRefMode();
            if (m == 4)
                memTimingSpec.RFC = Configuration::getInstance().memSpec.tRFC4 / clk;
            else if (m == 2)
                memTimingSpec.RFC = Configuration::getInstance().memSpec.tRFC2 / clk;
            else
                memTimingSpec.RFC = Configuration::getInstance().memSpec.tRFC / clk;
            memTimingSpec.RL     = Configuration::getInstance().memSpec.tRL / clk;
            memTimingSpec.RP     = Configuration::getInstance().memSpec.tRP / clk;
            memTimingSpec.RRD    = Configuration::getInstance().memSpec.tRRD_S / clk;
            memTimingSpec.RRD_L  = Configuration::getInstance().memSpec.tRRD_L / clk;
            memTimingSpec.RRD_S  = Configuration::getInstance().memSpec.tRRD_S / clk;
            memTimingSpec.RTP    = Configuration::getInstance().memSpec.tRTP / clk;
            memTimingSpec.TAW    = Configuration::getInstance().memSpec.tNAW / clk;
            memTimingSpec.WL     = Configuration::getInstance().memSpec.tWL / clk;
            memTimingSpec.WR     = Configuration::getInstance().memSpec.tWR / clk;
            memTimingSpec.WTR    = Configuration::getInstance().memSpec.tWTR_S / clk;
            memTimingSpec.WTR_L  = Configuration::getInstance().memSpec.tWTR_L / clk;
            memTimingSpec.WTR_S  = Configuration::getInstance().memSpec.tWTR_S / clk;
            memTimingSpec.XP     = Configuration::getInstance().memSpec.tXP / clk;
            memTimingSpec.XPDLL  = Configuration::getInstance().memSpec.tXPDLL / clk;
            memTimingSpec.XS     = Configuration::getInstance().memSpec.tXSR / clk;
            memTimingSpec.XSDLL  = Configuration::getInstance().memSpec.tXSRDLL / clk;

            MemPowerSpec  memPowerSpec;
            memPowerSpec.idd0    = Configuration::getInstance().memSpec.iDD0;
            memPowerSpec.idd02   = Configuration::getInstance().memSpec.iDD02;
            memPowerSpec.idd2p0  = Configuration::getInstance().memSpec.iDD2P0;
            memPowerSpec.idd2p02 = Configuration::getInstance().memSpec.iDD2P02;
            memPowerSpec.idd2p1  = Configuration::getInstance().memSpec.iDD2P1;
            memPowerSpec.idd2p12 = Configuration::getInstance().memSpec.iDD2P12;
            memPowerSpec.idd2n   = Configuration::getInstance().memSpec.iDD2N;
            memPowerSpec.idd2n2  = Configuration::getInstance().memSpec.iDD2N2;
            memPowerSpec.idd3p0  = Configuration::getInstance().memSpec.iDD3P0;
            memPowerSpec.idd3p02 = Configuration::getInstance().memSpec.iDD3P02;
            memPowerSpec.idd3p1  = Configuration::getInstance().memSpec.iDD3P1;
            memPowerSpec.idd3p12 = Configuration::getInstance().memSpec.iDD3P12;
            memPowerSpec.idd3n   = Configuration::getInstance().memSpec.iDD3N;
            memPowerSpec.idd3n2  = Configuration::getInstance().memSpec.iDD3N2;
            memPowerSpec.idd4r   = Configuration::getInstance().memSpec.iDD4R;
            memPowerSpec.idd4r2  = Configuration::getInstance().memSpec.iDD4R2;
            memPowerSpec.idd4w   = Configuration::getInstance().memSpec.iDD4W;
            memPowerSpec.idd4w2  = Configuration::getInstance().memSpec.iDD4W2;
            memPowerSpec.idd5    = Configuration::getInstance().memSpec.iDD5;
            memPowerSpec.idd52   = Configuration::getInstance().memSpec.iDD52;
            memPowerSpec.idd6    = Configuration::getInstance().memSpec.iDD6;
            memPowerSpec.idd62   = Configuration::getInstance().memSpec.iDD62;
            memPowerSpec.vdd     = Configuration::getInstance().memSpec.vDD;
            memPowerSpec.vdd2    = Configuration::getInstance().memSpec.vDD2;

            MemorySpecification memSpec;
            memSpec.id = Configuration::getInstance().memSpec.MemoryId;
            memSpec.memoryType = Configuration::getInstance().memSpec.MemoryType;
            memSpec.memTimingSpec = memTimingSpec;
            memSpec.memPowerSpec  = memPowerSpec;
            memSpec.memArchSpec   = memArchSpec;

            DRAMPower = new libDRAMPower( memSpec, 0 );
        }

        // Bandwidth Calculation:
        numberOfTransactionsServed = 0;
        firstAccess = SC_ZERO_TIME;
        lastAccess = SC_ZERO_TIME;

        // For each bank in a channel a error Model is created:
        if (StoreMode == StorageMode::ErrorModel) {
            for (unsigned i = 0; i < Configuration::getInstance().memSpec.NumberOfBanks;
                    i++) {
                errorModel *em;
                std::string errorModelStr = "errorModel_bank" + std::to_string(i);
                if (powerAnalysis == true) {
                    em = new errorModel(errorModelStr.c_str(), DRAMPower);
                } else {
                    em = new errorModel(errorModelStr.c_str());
                }
                ememory.push_back(em);
            }
        }
    }

    virtual void end_of_simulation()
    {
    }

    ~Dram()
    {
        if (powerAnalysis == true) {
            if (!Configuration::getInstance().DatabaseRecording)
                DRAMPower->calcEnergy();

            // Print the final total energy and the average power for
            // the simulation:
            cout << name() << string("  Total Energy:   ")
                 << fixed << std::setprecision( 2 )
                 << DRAMPower->getEnergy().total_energy
                 * Configuration::getInstance().NumberOfDevicesOnDIMM
                 << string(" pJ")
                 << endl;

            cout << name() << string("  Average Power:  ")
                 << fixed << std::setprecision( 2 )
                 << DRAMPower->getPower().average_power
                 * Configuration::getInstance().NumberOfDevicesOnDIMM
                 << string(" mW") << endl;
        }

        // Bandwidth:

        sc_time activeTime = numberOfTransactionsServed
                             * Configuration::getInstance().memSpec.BurstLength
                             / Configuration::getInstance().memSpec.DataRate
                             * Configuration::getInstance().memSpec.clk;

        sc_time idleTime    = dramController->getIdleTime();
        sc_time endTime     = dramController->getEndTime();
        sc_time startTime   = dramController->getStartTime();

        double bandwidth = (activeTime / (endTime - startTime) * 100);
        double bandwidth_IDLE = ((activeTime) / (endTime - startTime - idleTime) * 100);

        double maxBandwidth = (
                                  // clk in Mhz e.g. 800 [MHz]:
                                  (1000000 / Configuration::getInstance().memSpec.clk.to_double())
                                  // DataRate e.g. 2
                                  * Configuration::getInstance().memSpec.DataRate
                                  // BusWidth e.g. 8 or 64
                                  * Configuration::getInstance().memSpec.bitWidth
                                  //   Number of devices on a DIMM e.g. 8
                                  * Configuration::getInstance().NumberOfDevicesOnDIMM ) / ( 1024 );

        cout << name() << string("  Total Time:     ")
             << (endTime - startTime).to_string()
             << endl;
        cout << name() << string("  AVG BW:         ")
             << std::fixed << std::setprecision(2)
             << ((bandwidth / 100)*maxBandwidth)
             << " Gibit/s (" << bandwidth << " %)"
             << endl;
        cout << name() << string("  AVG BW/IDLE:    ")
             << std::fixed << std::setprecision(2)
             << ((bandwidth_IDLE / 100)*maxBandwidth)
             << " Gibit/s (" << (bandwidth_IDLE) << " %)"
             << endl;
        cout << name() << string("  MAX BW:         ")
             << std::fixed << std::setprecision(2)
             << maxBandwidth << " Gibit/s"
             << endl;
        // Clean up:
        for (auto e : ememory) {
            delete e;
        }

        if (Configuration::getInstance().UseMalloc) {
            free(memory);
        }
    }

    virtual tlm::tlm_sync_enum nb_transport_fw(tlm::tlm_generic_payload &payload,
                                               tlm::tlm_phase &phase, sc_time &delay)
    {
        if (numberOfTransactionsServed == 0) {
            firstAccess = sc_time_stamp();
        } else {
            lastAccess = sc_time_stamp();
        }

        unsigned int bank = DramExtension::getExtension(payload).getBank().ID();

        // This is only needed for power simulation:
        unsigned long long cycle = 0;
        if (powerAnalysis == true) {
            cycle = sc_time_stamp().value() /
                    Configuration::getInstance().memSpec.clk.value();
        }
        if (phase == BEGIN_PREB) {
            if (powerAnalysis == true) {
                DRAMPower->doCommand(MemCommand::PREB, bank, cycle);
            }
            sendToController(payload, END_PREB, delay + getExecutionTime(Command::PreB,
                                                                         payload));
        } else if (phase == BEGIN_PRE) {
            if (powerAnalysis == true) {
                DRAMPower->doCommand(MemCommand::PRE, bank, cycle);
            }
            sendToController(payload, END_PRE, delay + getExecutionTime(Command::Precharge,
                                                                        payload));
        } else if (phase == BEGIN_PRE_ALL) {
            if (powerAnalysis == true) {
                DRAMPower->doCommand(MemCommand::PREA, bank, cycle);
            }
            sendToController(payload, END_PRE_ALL,
                             delay +  getExecutionTime(Command::PrechargeAll, payload));
        } else if (phase == BEGIN_ACTB) {
            if (powerAnalysis == true) {
                DRAMPower->doCommand(MemCommand::ACTB, bank, cycle);
            }
            sendToController(payload, END_ACTB, delay + getExecutionTime(Command::ActB,
                                                                         payload));
            unsigned int row = DramExtension::getExtension(payload).getRow().ID();
            if (StoreMode == StorageMode::ErrorModel) {
                ememory[bank]->activate(row);
            }
        } else if (phase == BEGIN_ACT) {
            if (powerAnalysis == true) {
                DRAMPower->doCommand(MemCommand::ACT, bank, cycle);
            }
            sendToController(payload, END_ACT, delay +  getExecutionTime(Command::Activate,
                                                                         payload));
            unsigned int row = DramExtension::getExtension(payload).getRow().ID();

            if (StoreMode == StorageMode::ErrorModel) {
                ememory[bank]->activate(row);
            }
        } else if (phase == BEGIN_WR) {
#if !defined (DRAMSYS_PCT) && !defined (DRAMSYS_GEM5)
            assert(payload.get_data_length() == bytesPerBurst);
#endif

            if (powerAnalysis == true) {
                DRAMPower->doCommand(MemCommand::WR, bank, cycle);
            }
            numberOfTransactionsServed++;

            //save data:
            if (StoreMode == StorageMode::NoStorage) {
                // Don't store data
            } else if (StoreMode == StorageMode::Store) { // Use Storage
                unsigned char *phyAddr = memory + payload.get_address();
                memcpy(phyAddr, payload.get_data_ptr(), payload.get_data_length());
            } else { // == 2 Use Storage with Error Model
                ememory[bank]->store(payload);
            }
            sendToController(payload, END_WR, delay +  getExecutionTime(Command::Write,
                                                                        payload));
        } else if (phase == BEGIN_RD) {
#if !defined (DRAMSYS_PCT) && !defined (DRAMSYS_GEM5)
            assert(payload.get_data_length() == bytesPerBurst);
#endif

            numberOfTransactionsServed++;
            if (powerAnalysis == true) {
                DRAMPower->doCommand(MemCommand::RD, bank, cycle);
            }

            // Load data:
            if (StoreMode == StorageMode::Store) {  //use StorageMode
                unsigned char *phyAddr = memory + payload.get_address();
                memcpy(payload.get_data_ptr(), phyAddr, payload.get_data_length());
            } else if (StoreMode ==
                       StorageMode::ErrorModel) { // use StorageMode with errormodel
                ememory[bank]->load(payload);
            }

            sendToController(payload, END_RD, delay +  getExecutionTime(Command::Read,
                                                                        payload));
        } else if (phase == BEGIN_WRA) {
            numberOfTransactionsServed++;
            if (powerAnalysis == true) {
                DRAMPower->doCommand(MemCommand::WRA, bank, cycle);
            }

            //save data:
            if (StoreMode == StorageMode::NoStorage) {
                // Don't store data
            } else if (StoreMode == StorageMode::Store) { // Use Storage
                unsigned char *phyAddr = memory + payload.get_address();
                memcpy(phyAddr, payload.get_data_ptr(), payload.get_data_length());
            } else { // == 2 Use Storage with Error Model
                ememory[bank]->store(payload);
            }
            sendToController(payload, END_WRA, delay +  getExecutionTime(Command::WriteA,
                                                                         payload));
        } else if (phase == BEGIN_RDA) {
            numberOfTransactionsServed++;
            if (powerAnalysis == true) {
                DRAMPower->doCommand(MemCommand::RDA, bank, cycle);
            }

            // Load data:
            if (StoreMode == StorageMode::Store) {  //use StorageMode
                unsigned char *phyAddr = memory + payload.get_address();
                memcpy(payload.get_data_ptr(), phyAddr, payload.get_data_length());
            } else if (StoreMode ==
                       StorageMode::ErrorModel) { // use StorageMode with errormodel
                ememory[bank]->load(payload);
            }

            sendToController(payload, END_RDA, delay +  getExecutionTime(Command::ReadA,
                                                                         payload));
        } else if (phase == BEGIN_REFA) {
            if (powerAnalysis == true) {
                DRAMPower->doCommand(MemCommand::REF, bank, cycle);
            }
            sendToController(payload, END_REFA,
                             delay +  getExecutionTime(Command::AutoRefresh, payload));
            unsigned int row = DramExtension::getExtension(payload).getRow().ID();

            if (StoreMode == StorageMode::ErrorModel) {
                ememory[bank]->refresh(row);
            }
        }

        else if (phase == BEGIN_REFB) {
            if (powerAnalysis == true) {
                DRAMPower->doCommand(MemCommand::REFB, bank, cycle);
            }
            sendToController(payload, END_REFB,
                             delay +  getExecutionTime(Command::AutoRefresh, payload));
        }

        //Powerdown phases have to be started and ended by the controller, because they do not have a fixed length
        else if (phase == BEGIN_PDNA) {
            if (powerAnalysis == true) {
                DRAMPower->doCommand(MemCommand::PDN_S_ACT, bank, cycle);
            }
        } else if (phase == END_PDNA) {
            if (powerAnalysis == true) {
                DRAMPower->doCommand(MemCommand::PUP_ACT, bank, cycle);
            }
        } else if (phase == BEGIN_PDNAB) {
            if (powerAnalysis == true) {
                SC_REPORT_FATAL("DRAM", "Power calculation for bankwise logic not supported");
            }
        } else if (phase == END_PDNAB) {
            if (powerAnalysis == true) {
                SC_REPORT_FATAL("DRAM", "Power calculation for bankwise logic not supported");
            }
        } else if (phase == BEGIN_PDNP) {
            if (powerAnalysis == true) {
                DRAMPower->doCommand(MemCommand::PDN_S_PRE, bank, cycle);
            }
        } else if (phase == END_PDNP) {
            if (powerAnalysis == true) {
                DRAMPower->doCommand(MemCommand::PUP_PRE, bank, cycle);
            }
        } else if (phase == BEGIN_PDNPB) {
            if (powerAnalysis == true) {
                SC_REPORT_FATAL("DRAM", "Power calculation for bankwise logic not supported");
            }
        } else if (phase == END_PDNPB) {
            if (powerAnalysis == true) {
                SC_REPORT_FATAL("DRAM", "Power calculation for bankwise logic not supported");
            }
        } else if (phase == BEGIN_SREF) {
            if (powerAnalysis == true) {
                DRAMPower->doCommand(MemCommand::SREN, bank, cycle);
            }
        } else if (phase == END_SREF) {
            if (powerAnalysis == true) {
                DRAMPower->doCommand(MemCommand::SREX, bank, cycle);
            }
        } else if (phase == BEGIN_SREFB) {
            if (powerAnalysis == true) {
                SC_REPORT_FATAL("DRAM", "Power calculation for bankwise logic not supported");
            }
        } else if (phase == END_SREFB) {
            if (powerAnalysis == true) {
                SC_REPORT_FATAL("DRAM", "Power calculation for bankwise logic not supported");
            }
        } else {
            if (powerAnalysis == true) {
                SC_REPORT_FATAL("DRAM", "DRAM PEQ was called with unknown phase");
            }
        }

        return tlm::TLM_ACCEPTED;
    }

    virtual unsigned int transport_dbg(tlm::tlm_generic_payload &trans)
    {
        printDebugMessage("transport_dgb");

        // TODO: This part is not tested yet, neither with traceplayers neither with GEM5 coupling
        if (StoreMode == StorageMode::NoStorage) {
            SC_REPORT_FATAL("DRAM",
                            "Debug Transport is used in combination with NoStorage");
        } else {
            tlm::tlm_command cmd = trans.get_command();
            //sc_dt::uint64    adr = trans.get_address(); // TODO: - offset;
            unsigned char   *ptr = trans.get_data_ptr();
            unsigned int     len = trans.get_data_length();
            //unsigned int bank    = DramExtension::getExtension(trans).getBank().ID();

            //cout << "cmd " << (cmd ? "write" : "read") << " adr " << hex << adr << " len " << len << endl;

            if ( cmd == tlm::TLM_READ_COMMAND ) {
                if (StoreMode == StorageMode::Store) { // Use Storage
                    unsigned char *phyAddr = memory + trans.get_address();
                    memcpy(ptr, phyAddr, trans.get_data_length());
                } else {
                    //ememory[bank]->load(trans);
                    SC_REPORT_FATAL("DRAM", "Debug transport not supported with error model yet.");
                }
            } else if ( cmd == tlm::TLM_WRITE_COMMAND ) {

                if (StoreMode == StorageMode::Store) { // Use Storage
                    unsigned char *phyAddr = memory + trans.get_address();
                    memcpy(phyAddr, ptr, trans.get_data_length());
                } else {
                    //ememory[bank]->store(trans);
                    SC_REPORT_FATAL("DRAM", "Debug transport not supported with error model yet.");
                }

            }
            return len;
        }
        return 0;
    }

    void sendToController(tlm_generic_payload &payload, const tlm_phase &phase,
                          const sc_time &delay)
    {
        tlm_phase TPhase = phase;
        sc_time TDelay = delay;
        tSocket->nb_transport_bw(payload, TPhase, TDelay);
    }

    void printDebugMessage(string message)
    {
        DebugManager::getInstance().printDebugMessage(name(), message);
    }

    void setDramController(Controller *contr)
    {
        dramController = contr;
    }
};

#endif /* DRAM_H_ */

