1/*
2 * Copyright (c) 2008 Princeton University
3 * Copyright (c) 2016 Georgia Institute of Technology
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are
8 * met: redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer;
10 * redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution;
13 * neither the name of the copyright holders nor the names of its
14 * contributors may be used to endorse or promote products derived from
15 * this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 *
29 * Authors: Niket Agarwal
30 *          Tushar Krishna
31 */
32
33
34#include "mem/ruby/network/garnet2.0/NetworkInterface.hh"
35
36#include <cassert>
37#include <cmath>
38
39#include "base/cast.hh"
40#include "base/stl_helpers.hh"
41#include "debug/RubyNetwork.hh"
42#include "mem/ruby/network/MessageBuffer.hh"
43#include "mem/ruby/network/garnet2.0/Credit.hh"
44#include "mem/ruby/network/garnet2.0/flitBuffer.hh"
45#include "mem/ruby/slicc_interface/Message.hh"
46
47using namespace std;
48using m5::stl_helpers::deletePointers;
49
50NetworkInterface::NetworkInterface(const Params *p)
51    : ClockedObject(p), Consumer(this), m_id(p->id),
52      m_virtual_networks(p->virt_nets), m_vc_per_vnet(p->vcs_per_vnet),
53      m_num_vcs(m_vc_per_vnet * m_virtual_networks),
54      m_deadlock_threshold(p->garnet_deadlock_threshold),
55      vc_busy_counter(m_virtual_networks, 0)
56{
57    m_router_id = -1;
58    m_vc_round_robin = 0;
59    m_ni_out_vcs.resize(m_num_vcs);
60    m_ni_out_vcs_enqueue_time.resize(m_num_vcs);
61    outCreditQueue = new flitBuffer();
62
63    // instantiating the NI flit buffers
64    for (int i = 0; i < m_num_vcs; i++) {
65        m_ni_out_vcs[i] = new flitBuffer();
66        m_ni_out_vcs_enqueue_time[i] = Cycles(INFINITE_);
67    }
68
69    m_vc_allocator.resize(m_virtual_networks); // 1 allocator per vnet
70    for (int i = 0; i < m_virtual_networks; i++) {
71        m_vc_allocator[i] = 0;
72    }
73
74    m_stall_count.resize(m_virtual_networks);
75}
76
77void
78NetworkInterface::init()
79{
80    for (int i = 0; i < m_num_vcs; i++) {
81        m_out_vc_state.push_back(new OutVcState(i, m_net_ptr));
82    }
83}
84
85NetworkInterface::~NetworkInterface()
86{
87    deletePointers(m_out_vc_state);
88    deletePointers(m_ni_out_vcs);
89    delete outCreditQueue;
90    delete outFlitQueue;
91}
92
93void
94NetworkInterface::addInPort(NetworkLink *in_link,
95                              CreditLink *credit_link)
96{
97    inNetLink = in_link;
98    in_link->setLinkConsumer(this);
99    outCreditLink = credit_link;
100    credit_link->setSourceQueue(outCreditQueue);
101}
102
103void
104NetworkInterface::addOutPort(NetworkLink *out_link,
105                             CreditLink *credit_link,
106                             SwitchID router_id)
107{
108    inCreditLink = credit_link;
109    credit_link->setLinkConsumer(this);
110
111    outNetLink = out_link;
112    outFlitQueue = new flitBuffer();
113    out_link->setSourceQueue(outFlitQueue);
114
115    m_router_id = router_id;
116}
117
118void
119NetworkInterface::addNode(vector<MessageBuffer *>& in,
120                            vector<MessageBuffer *>& out)
121{
122    inNode_ptr = in;
123    outNode_ptr = out;
124
125    for (auto& it : in) {
126        if (it != nullptr) {
127            it->setConsumer(this);
128        }
129    }
130}
131
132void
133NetworkInterface::dequeueCallback()
134{
135    // An output MessageBuffer has dequeued something this cycle and there
136    // is now space to enqueue a stalled message. However, we cannot wake
137    // on the same cycle as the dequeue. Schedule a wake at the soonest
138    // possible time (next cycle).
139    scheduleEventAbsolute(clockEdge(Cycles(1)));
140}
141
142void
143NetworkInterface::incrementStats(flit *t_flit)
144{
145    int vnet = t_flit->get_vnet();
146
147    // Latency
148    m_net_ptr->increment_received_flits(vnet);
149    Cycles network_delay =
150        t_flit->get_dequeue_time() - t_flit->get_enqueue_time() - Cycles(1);
151    Cycles src_queueing_delay = t_flit->get_src_delay();
152    Cycles dest_queueing_delay = (curCycle() - t_flit->get_dequeue_time());
153    Cycles queueing_delay = src_queueing_delay + dest_queueing_delay;
154
155    m_net_ptr->increment_flit_network_latency(network_delay, vnet);
156    m_net_ptr->increment_flit_queueing_latency(queueing_delay, vnet);
157
158    if (t_flit->get_type() == TAIL_ || t_flit->get_type() == HEAD_TAIL_) {
159        m_net_ptr->increment_received_packets(vnet);
160        m_net_ptr->increment_packet_network_latency(network_delay, vnet);
161        m_net_ptr->increment_packet_queueing_latency(queueing_delay, vnet);
162    }
163
164    // Hops
165    m_net_ptr->increment_total_hops(t_flit->get_route().hops_traversed);
166}
167
168/*
169 * The NI wakeup checks whether there are any ready messages in the protocol
170 * buffer. If yes, it picks that up, flitisizes it into a number of flits and
171 * puts it into an output buffer and schedules the output link. On a wakeup
172 * it also checks whether there are flits in the input link. If yes, it picks
173 * them up and if the flit is a tail, the NI inserts the corresponding message
174 * into the protocol buffer. It also checks for credits being sent by the
175 * downstream router.
176 */
177
178void
179NetworkInterface::wakeup()
180{
181    DPRINTF(RubyNetwork, "Network Interface %d connected to router %d "
182            "woke up at time: %lld\n", m_id, m_router_id, curCycle());
183
184    MsgPtr msg_ptr;
185    Tick curTime = clockEdge();
186
187    // Checking for messages coming from the protocol
188    // can pick up a message/cycle for each virtual net
189    for (int vnet = 0; vnet < inNode_ptr.size(); ++vnet) {
190        MessageBuffer *b = inNode_ptr[vnet];
191        if (b == nullptr) {
192            continue;
193        }
194
195        if (b->isReady(curTime)) { // Is there a message waiting
196            msg_ptr = b->peekMsgPtr();
197            if (flitisizeMessage(msg_ptr, vnet)) {
198                b->dequeue(curTime);
199            }
200        }
201    }
202
203    scheduleOutputLink();
204    checkReschedule();
205
206    // Check if there are flits stalling a virtual channel. Track if a
207    // message is enqueued to restrict ejection to one message per cycle.
208    bool messageEnqueuedThisCycle = checkStallQueue();
209
210    /*********** Check the incoming flit link **********/
211    if (inNetLink->isReady(curCycle())) {
212        flit *t_flit = inNetLink->consumeLink();
213        int vnet = t_flit->get_vnet();
214        t_flit->set_dequeue_time(curCycle());
215
216        // If a tail flit is received, enqueue into the protocol buffers if
217        // space is available. Otherwise, exchange non-tail flits for credits.
218        if (t_flit->get_type() == TAIL_ || t_flit->get_type() == HEAD_TAIL_) {
219            if (!messageEnqueuedThisCycle &&
220                outNode_ptr[vnet]->areNSlotsAvailable(1, curTime)) {
221                // Space is available. Enqueue to protocol buffer.
222                outNode_ptr[vnet]->enqueue(t_flit->get_msg_ptr(), curTime,
223                                           cyclesToTicks(Cycles(1)));
224
225                // Simply send a credit back since we are not buffering
226                // this flit in the NI
227                sendCredit(t_flit, true);
228
229                // Update stats and delete flit pointer
230                incrementStats(t_flit);
231                delete t_flit;
232            } else {
233                // No space available- Place tail flit in stall queue and set
234                // up a callback for when protocol buffer is dequeued. Stat
235                // update and flit pointer deletion will occur upon unstall.
236                m_stall_queue.push_back(t_flit);
237                m_stall_count[vnet]++;
238
239                auto cb = std::bind(&NetworkInterface::dequeueCallback, this);
240                outNode_ptr[vnet]->registerDequeueCallback(cb);
241            }
242        } else {
243            // Non-tail flit. Send back a credit but not VC free signal.
244            sendCredit(t_flit, false);
245
246            // Update stats and delete flit pointer.
247            incrementStats(t_flit);
248            delete t_flit;
249        }
250    }
251
252    /****************** Check the incoming credit link *******/
253
254    if (inCreditLink->isReady(curCycle())) {
255        Credit *t_credit = (Credit*) inCreditLink->consumeLink();
256        m_out_vc_state[t_credit->get_vc()]->increment_credit();
257        if (t_credit->is_free_signal()) {
258            m_out_vc_state[t_credit->get_vc()]->setState(IDLE_, curCycle());
259        }
260        delete t_credit;
261    }
262
263
264    // It is possible to enqueue multiple outgoing credit flits if a message
265    // was unstalled in the same cycle as a new message arrives. In this
266    // case, we should schedule another wakeup to ensure the credit is sent
267    // back.
268    if (outCreditQueue->getSize() > 0) {
269        outCreditLink->scheduleEventAbsolute(clockEdge(Cycles(1)));
270    }
271}
272
273void
274NetworkInterface::sendCredit(flit *t_flit, bool is_free)
275{
276    Credit *credit_flit = new Credit(t_flit->get_vc(), is_free, curCycle());
277    outCreditQueue->insert(credit_flit);
278}
279
280bool
281NetworkInterface::checkStallQueue()
282{
283    bool messageEnqueuedThisCycle = false;
284    Tick curTime = clockEdge();
285
286    if (!m_stall_queue.empty()) {
287        for (auto stallIter = m_stall_queue.begin();
288             stallIter != m_stall_queue.end(); ) {
289            flit *stallFlit = *stallIter;
290            int vnet = stallFlit->get_vnet();
291
292            // If we can now eject to the protocol buffer, send back credits
293            if (outNode_ptr[vnet]->areNSlotsAvailable(1, curTime)) {
294                outNode_ptr[vnet]->enqueue(stallFlit->get_msg_ptr(), curTime,
295                                           cyclesToTicks(Cycles(1)));
296
297                // Send back a credit with free signal now that the VC is no
298                // longer stalled.
299                sendCredit(stallFlit, true);
300
301                // Update Stats
302                incrementStats(stallFlit);
303
304                // Flit can now safely be deleted and removed from stall queue
305                delete stallFlit;
306                m_stall_queue.erase(stallIter);
307                m_stall_count[vnet]--;
308
309                // If there are no more stalled messages for this vnet, the
310                // callback on it's MessageBuffer is not needed.
311                if (m_stall_count[vnet] == 0)
312                    outNode_ptr[vnet]->unregisterDequeueCallback();
313
314                messageEnqueuedThisCycle = true;
315                break;
316            } else {
317                ++stallIter;
318            }
319        }
320    }
321
322    return messageEnqueuedThisCycle;
323}
324
325// Embed the protocol message into flits
326bool
327NetworkInterface::flitisizeMessage(MsgPtr msg_ptr, int vnet)
328{
329    Message *net_msg_ptr = msg_ptr.get();
330    NetDest net_msg_dest = net_msg_ptr->getDestination();
331
332    // gets all the destinations associated with this message.
333    vector<NodeID> dest_nodes = net_msg_dest.getAllDest();
334
335    // Number of flits is dependent on the link bandwidth available.
336    // This is expressed in terms of bytes/cycle or the flit size
337    int num_flits = (int) ceil((double) m_net_ptr->MessageSizeType_to_int(
338        net_msg_ptr->getMessageSize())/m_net_ptr->getNiFlitSize());
339
340    // loop to convert all multicast messages into unicast messages
341    for (int ctr = 0; ctr < dest_nodes.size(); ctr++) {
342
343        // this will return a free output virtual channel
344        int vc = calculateVC(vnet);
345
346        if (vc == -1) {
347            return false ;
348        }
349        MsgPtr new_msg_ptr = msg_ptr->clone();
350        NodeID destID = dest_nodes[ctr];
351
352        Message *new_net_msg_ptr = new_msg_ptr.get();
353        if (dest_nodes.size() > 1) {
354            NetDest personal_dest;
355            for (int m = 0; m < (int) MachineType_NUM; m++) {
356                if ((destID >= MachineType_base_number((MachineType) m)) &&
357                    destID < MachineType_base_number((MachineType) (m+1))) {
358                    // calculating the NetDest associated with this destID
359                    personal_dest.clear();
360                    personal_dest.add((MachineID) {(MachineType) m, (destID -
361                        MachineType_base_number((MachineType) m))});
362                    new_net_msg_ptr->getDestination() = personal_dest;
363                    break;
364                }
365            }
366            net_msg_dest.removeNetDest(personal_dest);
367            // removing the destination from the original message to reflect
368            // that a message with this particular destination has been
369            // flitisized and an output vc is acquired
370            net_msg_ptr->getDestination().removeNetDest(personal_dest);
371        }
372
373        // Embed Route into the flits
374        // NetDest format is used by the routing table
375        // Custom routing algorithms just need destID
376        RouteInfo route;
377        route.vnet = vnet;
378        route.net_dest = new_net_msg_ptr->getDestination();
379        route.src_ni = m_id;
380        route.src_router = m_router_id;
381        route.dest_ni = destID;
382        route.dest_router = m_net_ptr->get_router_id(destID);
383
384        // initialize hops_traversed to -1
385        // so that the first router increments it to 0
386        route.hops_traversed = -1;
387
388        m_net_ptr->increment_injected_packets(vnet);
389        for (int i = 0; i < num_flits; i++) {
390            m_net_ptr->increment_injected_flits(vnet);
391            flit *fl = new flit(i, vc, vnet, route, num_flits, new_msg_ptr,
392                curCycle());
393
394            fl->set_src_delay(curCycle() - ticksToCycles(msg_ptr->getTime()));
395            m_ni_out_vcs[vc]->insert(fl);
396        }
397
398        m_ni_out_vcs_enqueue_time[vc] = curCycle();
399        m_out_vc_state[vc]->setState(ACTIVE_, curCycle());
400    }
401    return true ;
402}
403
404// Looking for a free output vc
405int
406NetworkInterface::calculateVC(int vnet)
407{
408    for (int i = 0; i < m_vc_per_vnet; i++) {
409        int delta = m_vc_allocator[vnet];
410        m_vc_allocator[vnet]++;
411        if (m_vc_allocator[vnet] == m_vc_per_vnet)
412            m_vc_allocator[vnet] = 0;
413
414        if (m_out_vc_state[(vnet*m_vc_per_vnet) + delta]->isInState(
415                    IDLE_, curCycle())) {
416            vc_busy_counter[vnet] = 0;
417            return ((vnet*m_vc_per_vnet) + delta);
418        }
419    }
420
421    vc_busy_counter[vnet] += 1;
422    panic_if(vc_busy_counter[vnet] > m_deadlock_threshold,
423        "%s: Possible network deadlock in vnet: %d at time: %llu \n",
424        name(), vnet, curTick());
425
426    return -1;
427}
428
429
430/** This function looks at the NI buffers
431 *  if some buffer has flits which are ready to traverse the link in the next
432 *  cycle, and the downstream output vc associated with this flit has buffers
433 *  left, the link is scheduled for the next cycle
434 */
435
436void
437NetworkInterface::scheduleOutputLink()
438{
439    int vc = m_vc_round_robin;
440
441    for (int i = 0; i < m_num_vcs; i++) {
442        vc++;
443        if (vc == m_num_vcs)
444            vc = 0;
445
446        // model buffer backpressure
447        if (m_ni_out_vcs[vc]->isReady(curCycle()) &&
448            m_out_vc_state[vc]->has_credit()) {
449
450            bool is_candidate_vc = true;
451            int t_vnet = get_vnet(vc);
452            int vc_base = t_vnet * m_vc_per_vnet;
453
454            if (m_net_ptr->isVNetOrdered(t_vnet)) {
455                for (int vc_offset = 0; vc_offset < m_vc_per_vnet;
456                     vc_offset++) {
457                    int t_vc = vc_base + vc_offset;
458                    if (m_ni_out_vcs[t_vc]->isReady(curCycle())) {
459                        if (m_ni_out_vcs_enqueue_time[t_vc] <
460                            m_ni_out_vcs_enqueue_time[vc]) {
461                            is_candidate_vc = false;
462                            break;
463                        }
464                    }
465                }
466            }
467            if (!is_candidate_vc)
468                continue;
469
470            m_vc_round_robin = vc;
471
472            m_out_vc_state[vc]->decrement_credit();
473            // Just removing the flit
474            flit *t_flit = m_ni_out_vcs[vc]->getTopFlit();
475            t_flit->set_time(curCycle() + Cycles(1));
476            outFlitQueue->insert(t_flit);
477            // schedule the out link
478            outNetLink->scheduleEventAbsolute(clockEdge(Cycles(1)));
479
480            if (t_flit->get_type() == TAIL_ ||
481               t_flit->get_type() == HEAD_TAIL_) {
482                m_ni_out_vcs_enqueue_time[vc] = Cycles(INFINITE_);
483            }
484            return;
485        }
486    }
487}
488
489int
490NetworkInterface::get_vnet(int vc)
491{
492    for (int i = 0; i < m_virtual_networks; i++) {
493        if (vc >= (i*m_vc_per_vnet) && vc < ((i+1)*m_vc_per_vnet)) {
494            return i;
495        }
496    }
497    fatal("Could not determine vc");
498}
499
500
501// Wakeup the NI in the next cycle if there are waiting
502// messages in the protocol buffer, or waiting flits in the
503// output VC buffer
504void
505NetworkInterface::checkReschedule()
506{
507    for (const auto& it : inNode_ptr) {
508        if (it == nullptr) {
509            continue;
510        }
511
512        while (it->isReady(clockEdge())) { // Is there a message waiting
513            scheduleEvent(Cycles(1));
514            return;
515        }
516    }
517
518    for (int vc = 0; vc < m_num_vcs; vc++) {
519        if (m_ni_out_vcs[vc]->isReady(curCycle() + Cycles(1))) {
520            scheduleEvent(Cycles(1));
521            return;
522        }
523    }
524}
525
526void
527NetworkInterface::print(std::ostream& out) const
528{
529    out << "[Network Interface]";
530}
531
532uint32_t
533NetworkInterface::functionalWrite(Packet *pkt)
534{
535    uint32_t num_functional_writes = 0;
536    for (unsigned int i  = 0; i < m_num_vcs; ++i) {
537        num_functional_writes += m_ni_out_vcs[i]->functionalWrite(pkt);
538    }
539
540    num_functional_writes += outFlitQueue->functionalWrite(pkt);
541    return num_functional_writes;
542}
543
544NetworkInterface *
545GarnetNetworkInterfaceParams::create()
546{
547    return new NetworkInterface(this);
548}
549