| /* | |
| * Copyright (c) 2020 Advanced Micro Devices, Inc. | |
| * 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. | |
| */ | |
| #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 |