Files
DRAMSys/DRAMSys/library/src/simulation/Arbiter.cpp
2020-03-26 15:45:00 +01:00

246 lines
11 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
* Eder F. Zulian
*/
#include "Arbiter.h"
using namespace std;
using namespace tlm;
Arbiter::Arbiter(sc_module_name name) :
sc_module(name), payloadEventQueue(this, &Arbiter::peqCallback)
{
// The arbiter communicates with one or more memory unity through one or more sockets (one or more memory channels).
// Each of the arbiter's initiator sockets is bound to a memory controller's target socket.
// Anytime an transaction comes from a memory unity to the arbiter the "bw" callback is called.
iSocket.register_nb_transport_bw(this, &Arbiter::nb_transport_bw);
for (size_t i = 0; i < Configuration::getInstance().numberOfMemChannels; ++i)
{
channelIsFree.push_back(true);
pendingRequests.push_back(queue<tlm_generic_payload *>());
nextPayloadID.push_back(0);
}
// One or more devices can accesss all the memory units through the arbiter.
// Devices' initiator sockets are bound to arbiter's target sockets.
// As soon the arbiter receives a request in any of its target sockets it should treat and forward it to the proper memory channel.
tSocket.register_nb_transport_fw(this, &Arbiter::nb_transport_fw);
tSocket.register_transport_dbg(this, &Arbiter::transport_dbg);
}
// Initiated by initiator side
// This function is called when an arbiter's target socket receives a transaction from a device
tlm_sync_enum Arbiter::nb_transport_fw(int id, tlm_generic_payload &payload,
tlm_phase &phase, sc_time &fwDelay)
{
sc_time notDelay = clkAlign(sc_time_stamp() + fwDelay) -
(sc_time_stamp() + fwDelay);
if (phase == BEGIN_REQ)
{
// adjust address offset:
payload.set_address(payload.get_address() -
Configuration::getInstance().addressOffset);
// In the begin request phase the socket ID is appended to the payload.
// It will extracted from the payload and used later.
appendDramExtension(id, payload);
payload.acquire();
}
else if (phase == END_RESP)
{
notDelay += Configuration::getInstance().memSpec->clk;
payload.release();
}
PRINTDEBUGMESSAGE(name(), "[fw] " + phaseNameToString(phase) + " notification in " +
notDelay.to_string());
payloadEventQueue.notify(payload, phase, notDelay);
return TLM_ACCEPTED;
}
// Initiated by dram side
// This function is called when an arbiter's initiator socket receives a transaction from a memory controller
tlm_sync_enum Arbiter::nb_transport_bw(int channelId, tlm_generic_payload &payload,
tlm_phase &phase, sc_time &bwDelay)
{
// Check channel ID
if ((unsigned int)channelId != DramExtension::getExtension(payload).getChannel().ID())
SC_REPORT_FATAL("Arbiter", "Payload extension was corrupted");
PRINTDEBUGMESSAGE(name(), "[bw] " + phaseNameToString(phase) + " notification in " +
bwDelay.to_string());
payloadEventQueue.notify(payload, phase, bwDelay);
return TLM_ACCEPTED;
}
unsigned int Arbiter::transport_dbg(int /*id*/, tlm::tlm_generic_payload &trans)
{
// adjust address offset:
trans.set_address(trans.get_address() -
Configuration::getInstance().addressOffset);
DecodedAddress decodedAddress = AddressDecoder::getInstance().decodeAddress(
trans.get_address());
return iSocket[decodedAddress.channel]->transport_dbg(trans);
}
void Arbiter::peqCallback(tlm_generic_payload &payload, const tlm_phase &phase)
{
unsigned int initiatorSocket = DramExtension::getExtension(
payload).getThread().ID();
unsigned int channelId = DramExtension::getExtension(payload).getChannel().ID();
// Check the valid range of initiatorSocket ID and channel Id
// TODO: initiatorSocket ID not checked
assert(channelId < Configuration::getInstance().numberOfMemChannels);
// Phases initiated by the intiator side from arbiter's point of view (devices performing memory requests to the arbiter)
if (phase == BEGIN_REQ)
{
if (channelIsFree[channelId]) {
// This channel was available. Forward the new transaction to the memory controller.
channelIsFree[channelId] = false;
sendToChannel(channelId, payload, phase, SC_ZERO_TIME);
} else {
// This channel is busy. Enqueue the new transaction which phase is BEGIN_REQ.
pendingRequests[channelId].push(&payload);
}
}
// Phases initiated by the target side from arbiter's point of view (memory side)
else if (phase == END_REQ) {
channelIsFree[channelId] = true;
// The arbiter receives a transaction which phase is END_REQ from memory controller and forwards it to the requester device.
sendToInitiator(initiatorSocket, payload, phase, SC_ZERO_TIME);
// This channel is now free! Dispatch a new transaction (phase is BEGIN_REQ) from the queue, if any. Send it to the memory controller.
if (!pendingRequests[channelId].empty()) {
tlm_generic_payload *payloadToSend = pendingRequests[channelId].front();
pendingRequests[channelId].pop();
// Send ONE of the enqueued new transactions (phase is BEGIN_REQ) through this channel.
sendToChannel(channelId, *payloadToSend, BEGIN_REQ, SC_ZERO_TIME);
// Mark the channel as busy again.
channelIsFree[channelId] = false;
}
}
else if (phase == BEGIN_RESP)
{
// The arbiter receives a transaction in BEGIN_RESP phase
// (that came from the memory side) and forwards it to the requester
// device
if (receivedResponses[initiatorSocket].empty()) {
sendToInitiator(initiatorSocket, payload, phase, SC_ZERO_TIME);
}
// Enqueue the transaction in BEGIN_RESP phase until the initiator
// device acknowledge it (phase changes to END_RESP).
receivedResponses[initiatorSocket].push(&payload);
}
else if (phase == END_RESP)
{
// Send the END_RESP message to the memory
sendToChannel(channelId, payload, phase, SC_ZERO_TIME);
// Drop one element of the queue of BEGIN_RESP from memory to this device
receivedResponses[initiatorSocket].pop();
// Check if there are queued transactoins with phase BEGIN_RESP from memory to this device
if (!receivedResponses[initiatorSocket].empty()) {
// The queue is not empty.
tlm_generic_payload *payloadToSend = receivedResponses[initiatorSocket].front();
// Send ONE extra BEGIN_RESP to the device
sendToInitiator(initiatorSocket, *payloadToSend, BEGIN_RESP, SC_ZERO_TIME);
}
}
else
SC_REPORT_FATAL(0,
"Payload event queue in arbiter was triggered with unknown phase");
}
void Arbiter::sendToChannel(unsigned int channelId, tlm_generic_payload &payload,
const tlm_phase &phase, const sc_time &delay)
{
tlm_phase TPhase = phase;
sc_time TDelay = delay;
iSocket[channelId]->nb_transport_fw(payload, TPhase, TDelay);
}
void Arbiter::sendToInitiator(unsigned int id, tlm_generic_payload &payload,
const tlm_phase &phase, const sc_time &delay)
{
tlm_phase TPhase = phase;
sc_time TDelay = delay;
tSocket[id]->nb_transport_bw(payload, TPhase, TDelay);
}
void Arbiter::appendDramExtension(int socketId, tlm_generic_payload &payload)
{
// Append Generation Extension
GenerationExtension *genExtension = new GenerationExtension(sc_time_stamp());
payload.set_auto_extension(genExtension);
unsigned int burstlength = payload.get_streaming_width();
DecodedAddress decodedAddress = AddressDecoder::getInstance().decodeAddress(payload.get_address());
// Check the valid range of decodedAddress
if (addressIsValid(decodedAddress)) {
DramExtension *extension = new DramExtension(Thread(socketId),
Channel(decodedAddress.channel), Rank(decodedAddress.rank),
BankGroup(decodedAddress.bankgroup), Bank(decodedAddress.bank),
Row(decodedAddress.row), Column(decodedAddress.column),
burstlength, nextPayloadID[decodedAddress.channel]++);
payload.set_auto_extension(extension);
} else {
SC_REPORT_FATAL("Arbiter", "Decoded Address is not inside the valid range");
}
}
bool Arbiter::addressIsValid(DecodedAddress &decodedAddress)
{
if (decodedAddress.channel >= AddressDecoder::getInstance().amount.channel)
return false;
if (decodedAddress.rank >= AddressDecoder::getInstance().amount.rank)
return false;
if (decodedAddress.bankgroup >= AddressDecoder::getInstance().amount.bankgroup)
return false;
if (decodedAddress.bank >= AddressDecoder::getInstance().amount.bank)
return false;
if (decodedAddress.row >= AddressDecoder::getInstance().amount.row)
return false;
if (decodedAddress.column >= AddressDecoder::getInstance().amount.column)
return false;
return true;
}