The Bridge SimObject, which hosts the CDC and SerDes functionality, could be woken up out-of-order when changing frequencies in DVFS. This change ensures there is no packet drop during the transition times. Change-Id: I40240dcb3e957217abd2d7ad22cc4f78809f7b49 Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/44287 Reviewed-by: Jason Lowe-Power <power.jg@gmail.com> Maintainer: Jason Lowe-Power <power.jg@gmail.com> Tested-by: kokoro <noreply+kokoro@google.com>
267 lines
9.1 KiB
C++
267 lines
9.1 KiB
C++
/*
|
|
* 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"
|
|
|
|
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));
|
|
}
|
|
}
|