blob: 98a05fddda601944232b1d93509270f445d4d6b6 [file] [log] [blame]
/*
* Copyright (c) 2020 Advanced Micro Devices, Inc.
* All rights reserved.
*
* For use for simulation and test purposes only
*
* 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.
*/
#include "mem/ruby/network/garnet/NetworkBridge.hh"
#include <cmath>
#include "debug/RubyNetwork.hh"
#include "params/GarnetIntLink.hh"
namespace gem5
{
namespace ruby
{
namespace garnet
{
NetworkBridge::NetworkBridge(const Params &p)
:CreditLink(p)
{
enCdc = true;
enSerDes = true;
mType = p.vtype;
cdcLatency = p.cdc_latency;
serDesLatency = p.serdes_latency;
lastScheduledAt = 0;
nLink = p.link;
if (mType == enums::LINK_OBJECT) {
nLink->setLinkConsumer(this);
setSourceQueue(nLink->getBuffer(), nLink);
} else if (mType == enums::OBJECT_LINK) {
nLink->setSourceQueue(&linkBuffer, this);
setLinkConsumer(nLink);
} else {
// CDC type must be set
panic("CDC type must be set");
}
}
void
NetworkBridge::setVcsPerVnet(uint32_t consumerVcs)
{
DPRINTF(RubyNetwork, "VcsPerVnet VC: %d\n", consumerVcs);
NetworkLink::setVcsPerVnet(consumerVcs);
lenBuffer.resize(consumerVcs * m_virt_nets);
sizeSent.resize(consumerVcs * m_virt_nets);
flitsSent.resize(consumerVcs * m_virt_nets);
extraCredit.resize(consumerVcs * m_virt_nets);
nLink->setVcsPerVnet(consumerVcs);
}
void
NetworkBridge::initBridge(NetworkBridge *coBrid, bool cdc_en, bool serdes_en)
{
coBridge = coBrid;
enCdc = cdc_en;
enSerDes = serdes_en;
}
NetworkBridge::~NetworkBridge()
{
}
void
NetworkBridge::scheduleFlit(flit *t_flit, Cycles latency)
{
Cycles totLatency = latency;
if (enCdc) {
// Add the CDC latency
totLatency = latency + cdcLatency;
}
Tick sendTime = link_consumer->getObject()->clockEdge(totLatency);
Tick nextAvailTick = lastScheduledAt + link_consumer->getObject()->\
cyclesToTicks(Cycles(1));
sendTime = std::max(nextAvailTick, sendTime);
t_flit->set_time(sendTime);
lastScheduledAt = sendTime;
linkBuffer.insert(t_flit);
link_consumer->scheduleEventAbsolute(sendTime);
}
void
NetworkBridge::neutralize(int vc, int eCredit)
{
extraCredit[vc].push(eCredit);
}
void
NetworkBridge::flitisizeAndSend(flit *t_flit)
{
// Serialize-Deserialize only if it is enabled
if (enSerDes) {
// Calculate the target-width
int target_width = bitWidth;
int cur_width = nLink->bitWidth;
if (mType == enums::OBJECT_LINK) {
target_width = nLink->bitWidth;
cur_width = bitWidth;
}
DPRINTF(RubyNetwork, "Target width: %d Current: %d\n",
target_width, cur_width);
assert(target_width != cur_width);
int vc = t_flit->get_vc();
if (target_width > cur_width) {
// Deserialize
// This deserializer combines flits from the
// same message together
int num_flits = 0;
int flitPossible = 0;
if (t_flit->get_type() == CREDIT_) {
lenBuffer[vc]++;
assert(extraCredit[vc].front());
if (lenBuffer[vc] == extraCredit[vc].front()) {
flitPossible = 1;
extraCredit[vc].pop();
lenBuffer[vc] = 0;
}
} else if (t_flit->get_type() == TAIL_ ||
t_flit->get_type() == HEAD_TAIL_) {
// If its the end of packet, then send whatever
// is available.
int sizeAvail = (t_flit->msgSize - sizeSent[vc]);
flitPossible = ceil((float)sizeAvail/(float)target_width);
assert (flitPossible < 2);
num_flits = (t_flit->get_id() + 1) - flitsSent[vc];
// Stop tracking the packet.
flitsSent[vc] = 0;
sizeSent[vc] = 0;
} else {
// If we are yet to receive the complete packet
// track the size recieved and flits deserialized.
int sizeAvail =
((t_flit->get_id() + 1)*cur_width) - sizeSent[vc];
flitPossible = floor((float)sizeAvail/(float)target_width);
assert (flitPossible < 2);
num_flits = (t_flit->get_id() + 1) - flitsSent[vc];
if (flitPossible) {
sizeSent[vc] += target_width;
flitsSent[vc] = t_flit->get_id() + 1;
}
}
DPRINTF(RubyNetwork, "Deserialize :%dB -----> %dB "
" vc:%d\n", cur_width, target_width, vc);
flit *fl = NULL;
if (flitPossible) {
fl = t_flit->deserialize(lenBuffer[vc], num_flits,
target_width);
}
// Inform the credit serializer about the number
// of flits that were generated.
if (t_flit->get_type() != CREDIT_ && fl) {
coBridge->neutralize(vc, num_flits);
}
// Schedule only if we are done deserializing
if (fl) {
DPRINTF(RubyNetwork, "Scheduling a flit\n");
lenBuffer[vc] = 0;
scheduleFlit(fl, serDesLatency);
}
// Delete this flit, new flit is sent in any case
delete t_flit;
} else {
// Serialize
DPRINTF(RubyNetwork, "Serializing flit :%d -----> %d "
"(vc:%d, Original Message Size: %d)\n",
cur_width, target_width, vc, t_flit->msgSize);
int flitPossible = 0;
if (t_flit->get_type() == CREDIT_) {
// We store the deserialization ratio and then
// access it when serializing credits in the
// oppposite direction.
assert(extraCredit[vc].front());
flitPossible = extraCredit[vc].front();
extraCredit[vc].pop();
} else if (t_flit->get_type() == HEAD_ ||
t_flit->get_type() == BODY_) {
int sizeAvail =
((t_flit->get_id() + 1)*cur_width) - sizeSent[vc];
flitPossible = floor((float)sizeAvail/(float)target_width);
if (flitPossible) {
sizeSent[vc] += flitPossible*target_width;
flitsSent[vc] += flitPossible;
}
} else {
int sizeAvail = t_flit->msgSize - sizeSent[vc];
flitPossible = ceil((float)sizeAvail/(float)target_width);
sizeSent[vc] = 0;
flitsSent[vc] = 0;
}
assert(flitPossible > 0);
// Schedule all the flits
// num_flits could be zero for credits
for (int i = 0; i < flitPossible; i++) {
// Ignore neutralized credits
flit *fl = t_flit->serialize(i, flitPossible, target_width);
scheduleFlit(fl, serDesLatency);
DPRINTF(RubyNetwork, "Serialized to flit[%d of %d parts]:"
" %s\n", i+1, flitPossible, *fl);
}
if (t_flit->get_type() != CREDIT_) {
coBridge->neutralize(vc, flitPossible);
}
// Delete this flit, new flit is sent in any case
delete t_flit;
}
return;
}
// If only CDC is enabled schedule it
scheduleFlit(t_flit, Cycles(0));
}
void
NetworkBridge::wakeup()
{
flit *t_flit;
if (link_srcQueue->isReady(curTick())) {
t_flit = link_srcQueue->getTopFlit();
DPRINTF(RubyNetwork, "Recieved flit %s\n", *t_flit);
flitisizeAndSend(t_flit);
}
// Reschedule in case there is a waiting flit.
if (!link_srcQueue->isEmpty()) {
scheduleEvent(Cycles(1));
}
}
} // namespace garnet
} // namespace ruby
} // namespace gem5