422 lines
18 KiB
C++
422 lines
18 KiB
C++
/*
|
|
* 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
|
|
*/
|
|
|
|
#include "Dram.h"
|
|
|
|
#include <sys/mman.h>
|
|
#include <tlm>
|
|
#include <systemc>
|
|
#include <tlm_utils/simple_target_socket.h>
|
|
#include <vector>
|
|
#include <array>
|
|
#include <cassert>
|
|
#include <cstdint>
|
|
#include <stdlib.h>
|
|
#include "../common/DebugManager.h"
|
|
#include "../common/dramExtensions.h"
|
|
#include "../controller/Controller.h"
|
|
#include "../controller/core/timingCalculations.h"
|
|
#include "../controller/core/configuration/Configuration.h"
|
|
#include "../common/protocol.h"
|
|
#include "../common/utils.h"
|
|
#include "../common/third_party/DRAMPower/src/libdrampower/LibDRAMPower.h"
|
|
|
|
using namespace std;
|
|
using namespace tlm;
|
|
using namespace Data;
|
|
|
|
Dram::Dram(sc_module_name) : tSocket("socket")
|
|
{
|
|
// Adjust number of bytes per burst dynamically to the selected ecc controller
|
|
bytesPerBurst = Configuration::getInstance()
|
|
.adjustNumBytesAfterECC(bytesPerBurst);
|
|
|
|
tSocket.register_nb_transport_fw(this, &Dram::nb_transport_fw);
|
|
tSocket.register_transport_dbg(this, &Dram::transport_dbg);
|
|
|
|
// Parameters for DRAMPower
|
|
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);
|
|
}
|
|
|
|
Dram::~Dram()
|
|
{
|
|
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;
|
|
|
|
if (Configuration::getInstance().UseMalloc)
|
|
free(memory);
|
|
}
|
|
|
|
tlm_sync_enum Dram::nb_transport_fw(tlm_generic_payload &payload,
|
|
tlm_phase &phase, sc_time &delay)
|
|
{
|
|
MemSpec *memSpec = Configuration::getInstance().memSpec;
|
|
|
|
unsigned int bank = DramExtension::getExtension(payload).getBank().ID();
|
|
|
|
// This is only needed for power simulation:
|
|
unsigned long long cycle = sc_time_stamp().value() / memSpec->clk.value();
|
|
|
|
if (phase == BEGIN_PREB)
|
|
{
|
|
DRAMPower->doCommand(MemCommand::PREB, bank, cycle);
|
|
sendToController(payload, END_PREB, delay + memSpec->getExecutionTime(Command::PreB,
|
|
payload));
|
|
}
|
|
else if (phase == BEGIN_PRE)
|
|
{
|
|
DRAMPower->doCommand(MemCommand::PRE, bank, cycle);
|
|
sendToController(payload, END_PRE, delay + memSpec->getExecutionTime(Command::Precharge,
|
|
payload));
|
|
}
|
|
else if (phase == BEGIN_PRE_ALL)
|
|
{
|
|
DRAMPower->doCommand(MemCommand::PREA, bank, cycle);
|
|
sendToController(payload, END_PRE_ALL,
|
|
delay + memSpec->getExecutionTime(Command::PrechargeAll, payload));
|
|
}
|
|
else if (phase == BEGIN_ACTB)
|
|
{
|
|
DRAMPower->doCommand(MemCommand::ACTB, bank, cycle);
|
|
sendToController(payload, END_ACTB, delay + memSpec->getExecutionTime(Command::ActB,
|
|
payload));
|
|
}
|
|
else if (phase == BEGIN_ACT)
|
|
{
|
|
DRAMPower->doCommand(MemCommand::ACT, bank, cycle);
|
|
sendToController(payload, END_ACT, delay + memSpec->getExecutionTime(Command::Activate,
|
|
payload));
|
|
}
|
|
else if (phase == BEGIN_WR)
|
|
{
|
|
DRAMPower->doCommand(MemCommand::WR, bank, cycle);
|
|
// save data:
|
|
if (StoreMode == StorageMode::Store) // Use Storage
|
|
{
|
|
unsigned char *phyAddr = memory + payload.get_address();
|
|
memcpy(phyAddr, payload.get_data_ptr(), payload.get_data_length());
|
|
}
|
|
sendToController(payload, END_WR, delay + memSpec->getExecutionTime(Command::Write,
|
|
payload));
|
|
}
|
|
else if (phase == BEGIN_RD)
|
|
{
|
|
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());
|
|
}
|
|
sendToController(payload, END_RD, delay + memSpec->getExecutionTime(Command::Read,
|
|
payload));
|
|
}
|
|
else if (phase == BEGIN_WRA)
|
|
{
|
|
DRAMPower->doCommand(MemCommand::WRA, bank, cycle);
|
|
// save data:
|
|
if (StoreMode == StorageMode::Store) // Use Storage
|
|
{
|
|
unsigned char *phyAddr = memory + payload.get_address();
|
|
memcpy(phyAddr, payload.get_data_ptr(), payload.get_data_length());
|
|
}
|
|
sendToController(payload, END_WRA, delay + memSpec->getExecutionTime(Command::WriteA,
|
|
payload));
|
|
}
|
|
else if (phase == BEGIN_RDA)
|
|
{
|
|
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());
|
|
}
|
|
sendToController(payload, END_RDA, delay + memSpec->getExecutionTime(Command::ReadA,
|
|
payload));
|
|
}
|
|
else if (phase == BEGIN_REFA)
|
|
{
|
|
DRAMPower->doCommand(MemCommand::REF, bank, cycle);
|
|
sendToController(payload, END_REFA,
|
|
delay + memSpec->getExecutionTime(Command::AutoRefresh, payload));
|
|
}
|
|
else if (phase == BEGIN_REFB)
|
|
{
|
|
DRAMPower->doCommand(MemCommand::REFB, bank, cycle);
|
|
sendToController(payload, END_REFB,
|
|
delay + memSpec->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)
|
|
{
|
|
DRAMPower->doCommand(MemCommand::PDN_S_ACT, bank, cycle);
|
|
}
|
|
else if (phase == END_PDNA)
|
|
{
|
|
DRAMPower->doCommand(MemCommand::PUP_ACT, bank, cycle);
|
|
}
|
|
else if (phase == BEGIN_PDNAB)
|
|
{
|
|
SC_REPORT_FATAL("DRAM", "Power calculation for bankwise logic not supported");
|
|
}
|
|
else if (phase == END_PDNAB)
|
|
{
|
|
SC_REPORT_FATAL("DRAM", "Power calculation for bankwise logic not supported");
|
|
}
|
|
else if (phase == BEGIN_PDNP)
|
|
{
|
|
DRAMPower->doCommand(MemCommand::PDN_S_PRE, bank, cycle);
|
|
}
|
|
else if (phase == END_PDNP)
|
|
{
|
|
DRAMPower->doCommand(MemCommand::PUP_PRE, bank, cycle);
|
|
}
|
|
else if (phase == BEGIN_PDNPB)
|
|
{
|
|
SC_REPORT_FATAL("DRAM", "Power calculation for bankwise logic not supported");
|
|
}
|
|
else if (phase == END_PDNPB)
|
|
{
|
|
SC_REPORT_FATAL("DRAM", "Power calculation for bankwise logic not supported");
|
|
}
|
|
else if (phase == BEGIN_SREF)
|
|
{
|
|
DRAMPower->doCommand(MemCommand::SREN, bank, cycle);
|
|
}
|
|
else if (phase == END_SREF)
|
|
{
|
|
DRAMPower->doCommand(MemCommand::SREX, bank, cycle);
|
|
}
|
|
else if (phase == BEGIN_SREFB)
|
|
{
|
|
SC_REPORT_FATAL("DRAM", "Power calculation for bankwise logic not supported");
|
|
}
|
|
else if (phase == END_SREFB)
|
|
{
|
|
SC_REPORT_FATAL("DRAM", "Power calculation for bankwise logic not supported");
|
|
}
|
|
else
|
|
{
|
|
SC_REPORT_FATAL("DRAM", "DRAM PEQ was called with unknown phase");
|
|
}
|
|
|
|
return TLM_ACCEPTED;
|
|
}
|
|
|
|
unsigned int Dram::transport_dbg(tlm_generic_payload &trans)
|
|
{
|
|
printDebugMessage("transport_dgb");
|
|
|
|
// TODO: This part is not tested yet, neither with traceplayers nor with GEM5 coupling
|
|
if (StoreMode == StorageMode::NoStorage)
|
|
{
|
|
SC_REPORT_FATAL("DRAM",
|
|
"Debug Transport is used in combination with NoStorage");
|
|
}
|
|
else
|
|
{
|
|
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_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_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 Dram::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 Dram::printDebugMessage(string message)
|
|
{
|
|
DebugManager::getInstance().printDebugMessage(name(), message);
|
|
}
|