NetworkInterface.cc revision 11666:10d59d546ea2
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{
55    m_router_id = -1;
56    m_vc_round_robin = 0;
57    m_ni_out_vcs.resize(m_num_vcs);
58    m_ni_out_vcs_enqueue_time.resize(m_num_vcs);
59    outCreditQueue = new flitBuffer();
60
61    // instantiating the NI flit buffers
62    for (int i = 0; i < m_num_vcs; i++) {
63        m_ni_out_vcs[i] = new flitBuffer();
64        m_ni_out_vcs_enqueue_time[i] = Cycles(INFINITE_);
65    }
66
67    m_vc_allocator.resize(m_virtual_networks); // 1 allocator per vnet
68    for (int i = 0; i < m_virtual_networks; i++) {
69        m_vc_allocator[i] = 0;
70    }
71}
72
73void
74NetworkInterface::init()
75{
76    for (int i = 0; i < m_num_vcs; i++) {
77        m_out_vc_state.push_back(new OutVcState(i, m_net_ptr));
78    }
79}
80
81NetworkInterface::~NetworkInterface()
82{
83    deletePointers(m_out_vc_state);
84    deletePointers(m_ni_out_vcs);
85    delete outCreditQueue;
86    delete outFlitQueue;
87}
88
89void
90NetworkInterface::addInPort(NetworkLink *in_link,
91                              CreditLink *credit_link)
92{
93    inNetLink = in_link;
94    in_link->setLinkConsumer(this);
95    outCreditLink = credit_link;
96    credit_link->setSourceQueue(outCreditQueue);
97}
98
99void
100NetworkInterface::addOutPort(NetworkLink *out_link,
101                             CreditLink *credit_link,
102                             SwitchID router_id)
103{
104    inCreditLink = credit_link;
105    credit_link->setLinkConsumer(this);
106
107    outNetLink = out_link;
108    outFlitQueue = new flitBuffer();
109    out_link->setSourceQueue(outFlitQueue);
110
111    m_router_id = router_id;
112}
113
114void
115NetworkInterface::addNode(vector<MessageBuffer *>& in,
116                            vector<MessageBuffer *>& out)
117{
118    inNode_ptr = in;
119    outNode_ptr = out;
120
121    for (auto& it : in) {
122        if (it != nullptr) {
123            it->setConsumer(this);
124        }
125    }
126}
127
128
129/*
130 * The NI wakeup checks whether there are any ready messages in the protocol
131 * buffer. If yes, it picks that up, flitisizes it into a number of flits and
132 * puts it into an output buffer and schedules the output link. On a wakeup
133 * it also checks whether there are flits in the input link. If yes, it picks
134 * them up and if the flit is a tail, the NI inserts the corresponding message
135 * into the protocol buffer. It also checks for credits being sent by the
136 * downstream router.
137 */
138
139void
140NetworkInterface::wakeup()
141{
142    DPRINTF(RubyNetwork, "Network Interface %d connected to router %d "
143            "woke up at time: %lld\n", m_id, m_router_id, curCycle());
144
145    MsgPtr msg_ptr;
146    Tick curTime = clockEdge();
147
148    // Checking for messages coming from the protocol
149    // can pick up a message/cycle for each virtual net
150    for (int vnet = 0; vnet < inNode_ptr.size(); ++vnet) {
151        MessageBuffer *b = inNode_ptr[vnet];
152        if (b == nullptr) {
153            continue;
154        }
155
156        if (b->isReady(curTime)) { // Is there a message waiting
157            msg_ptr = b->peekMsgPtr();
158            if (flitisizeMessage(msg_ptr, vnet)) {
159                b->dequeue(curTime);
160            } else {
161                break;
162            }
163        }
164    }
165
166    scheduleOutputLink();
167    checkReschedule();
168
169    /*********** Check the incoming flit link **********/
170
171    if (inNetLink->isReady(curCycle())) {
172        flit *t_flit = inNetLink->consumeLink();
173        bool free_signal = false;
174        if (t_flit->get_type() == TAIL_ || t_flit->get_type() == HEAD_TAIL_) {
175            free_signal = true;
176
177            // enqueue into the protocol buffers
178            outNode_ptr[t_flit->get_vnet()]->enqueue(
179                t_flit->get_msg_ptr(), curTime, cyclesToTicks(Cycles(1)));
180        }
181        // Simply send a credit back since we are not buffering
182        // this flit in the NI
183        Credit *t_credit = new Credit(t_flit->get_vc(), free_signal,
184                                         curCycle());
185        outCreditQueue->insert(t_credit);
186        outCreditLink->
187            scheduleEventAbsolute(clockEdge(Cycles(1)));
188
189        int vnet = t_flit->get_vnet();
190
191        // Update Stats
192
193        // Latency
194        m_net_ptr->increment_received_flits(vnet);
195        Cycles network_delay = curCycle() - t_flit->get_enqueue_time();
196        Cycles queueing_delay = t_flit->get_src_delay();
197
198        m_net_ptr->increment_flit_network_latency(network_delay, vnet);
199        m_net_ptr->increment_flit_queueing_latency(queueing_delay, vnet);
200
201        if (t_flit->get_type() == TAIL_ || t_flit->get_type() == HEAD_TAIL_) {
202            m_net_ptr->increment_received_packets(vnet);
203            m_net_ptr->increment_packet_network_latency(network_delay, vnet);
204            m_net_ptr->increment_packet_queueing_latency(queueing_delay, vnet);
205        }
206
207        // Hops
208        m_net_ptr->increment_total_hops(t_flit->get_route().hops_traversed);
209
210        delete t_flit;
211    }
212
213    /****************** Check the incoming credit link *******/
214
215    if (inCreditLink->isReady(curCycle())) {
216        Credit *t_credit = (Credit*) inCreditLink->consumeLink();
217        m_out_vc_state[t_credit->get_vc()]->increment_credit();
218        if (t_credit->is_free_signal()) {
219            m_out_vc_state[t_credit->get_vc()]->setState(IDLE_, curCycle());
220        }
221        delete t_credit;
222    }
223}
224
225
226// Embed the protocol message into flits
227bool
228NetworkInterface::flitisizeMessage(MsgPtr msg_ptr, int vnet)
229{
230    Message *net_msg_ptr = msg_ptr.get();
231    NetDest net_msg_dest = net_msg_ptr->getDestination();
232
233    // gets all the destinations associated with this message.
234    vector<NodeID> dest_nodes = net_msg_dest.getAllDest();
235
236    // Number of flits is dependent on the link bandwidth available.
237    // This is expressed in terms of bytes/cycle or the flit size
238    int num_flits = (int) ceil((double) m_net_ptr->MessageSizeType_to_int(
239        net_msg_ptr->getMessageSize())/m_net_ptr->getNiFlitSize());
240
241    // loop to convert all multicast messages into unicast messages
242    for (int ctr = 0; ctr < dest_nodes.size(); ctr++) {
243
244        // this will return a free output virtual channel
245        int vc = calculateVC(vnet);
246
247        if (vc == -1) {
248            return false ;
249        }
250        MsgPtr new_msg_ptr = msg_ptr->clone();
251        NodeID destID = dest_nodes[ctr];
252
253        Message *new_net_msg_ptr = new_msg_ptr.get();
254        if (dest_nodes.size() > 1) {
255            NetDest personal_dest;
256            for (int m = 0; m < (int) MachineType_NUM; m++) {
257                if ((destID >= MachineType_base_number((MachineType) m)) &&
258                    destID < MachineType_base_number((MachineType) (m+1))) {
259                    // calculating the NetDest associated with this destID
260                    personal_dest.clear();
261                    personal_dest.add((MachineID) {(MachineType) m, (destID -
262                        MachineType_base_number((MachineType) m))});
263                    new_net_msg_ptr->getDestination() = personal_dest;
264                    break;
265                }
266            }
267            net_msg_dest.removeNetDest(personal_dest);
268            // removing the destination from the original message to reflect
269            // that a message with this particular destination has been
270            // flitisized and an output vc is acquired
271            net_msg_ptr->getDestination().removeNetDest(personal_dest);
272        }
273
274        // Embed Route into the flits
275        // NetDest format is used by the routing table
276        // Custom routing algorithms just need destID
277        RouteInfo route;
278        route.vnet = vnet;
279        route.net_dest = new_net_msg_ptr->getDestination();
280        route.src_ni = m_id;
281        route.src_router = m_router_id;
282        route.dest_ni = destID;
283        route.dest_router = m_net_ptr->get_router_id(destID);
284
285        // initialize hops_traversed to -1
286        // so that the first router increments it to 0
287        route.hops_traversed = -1;
288
289        m_net_ptr->increment_injected_packets(vnet);
290        for (int i = 0; i < num_flits; i++) {
291            m_net_ptr->increment_injected_flits(vnet);
292            flit *fl = new flit(i, vc, vnet, route, num_flits, new_msg_ptr,
293                curCycle());
294
295            fl->set_src_delay(curCycle() - ticksToCycles(msg_ptr->getTime()));
296            m_ni_out_vcs[vc]->insert(fl);
297        }
298
299        m_ni_out_vcs_enqueue_time[vc] = curCycle();
300        m_out_vc_state[vc]->setState(ACTIVE_, curCycle());
301    }
302    return true ;
303}
304
305// Looking for a free output vc
306int
307NetworkInterface::calculateVC(int vnet)
308{
309    for (int i = 0; i < m_vc_per_vnet; i++) {
310        int delta = m_vc_allocator[vnet];
311        m_vc_allocator[vnet]++;
312        if (m_vc_allocator[vnet] == m_vc_per_vnet)
313            m_vc_allocator[vnet] = 0;
314
315        if (m_out_vc_state[(vnet*m_vc_per_vnet) + delta]->isInState(
316                    IDLE_, curCycle())) {
317            return ((vnet*m_vc_per_vnet) + delta);
318        }
319    }
320    return -1;
321}
322
323
324/** This function looks at the NI buffers
325 *  if some buffer has flits which are ready to traverse the link in the next
326 *  cycle, and the downstream output vc associated with this flit has buffers
327 *  left, the link is scheduled for the next cycle
328 */
329
330void
331NetworkInterface::scheduleOutputLink()
332{
333    int vc = m_vc_round_robin;
334    m_vc_round_robin++;
335    if (m_vc_round_robin == m_num_vcs)
336        m_vc_round_robin = 0;
337
338    for (int i = 0; i < m_num_vcs; i++) {
339        vc++;
340        if (vc == m_num_vcs)
341            vc = 0;
342
343        // model buffer backpressure
344        if (m_ni_out_vcs[vc]->isReady(curCycle()) &&
345            m_out_vc_state[vc]->has_credit()) {
346
347            bool is_candidate_vc = true;
348            int t_vnet = get_vnet(vc);
349            int vc_base = t_vnet * m_vc_per_vnet;
350
351            if (m_net_ptr->isVNetOrdered(t_vnet)) {
352                for (int vc_offset = 0; vc_offset < m_vc_per_vnet;
353                     vc_offset++) {
354                    int t_vc = vc_base + vc_offset;
355                    if (m_ni_out_vcs[t_vc]->isReady(curCycle())) {
356                        if (m_ni_out_vcs_enqueue_time[t_vc] <
357                            m_ni_out_vcs_enqueue_time[vc]) {
358                            is_candidate_vc = false;
359                            break;
360                        }
361                    }
362                }
363            }
364            if (!is_candidate_vc)
365                continue;
366
367            m_out_vc_state[vc]->decrement_credit();
368            // Just removing the flit
369            flit *t_flit = m_ni_out_vcs[vc]->getTopFlit();
370            t_flit->set_time(curCycle() + Cycles(1));
371            outFlitQueue->insert(t_flit);
372            // schedule the out link
373            outNetLink->scheduleEventAbsolute(clockEdge(Cycles(1)));
374
375            if (t_flit->get_type() == TAIL_ ||
376               t_flit->get_type() == HEAD_TAIL_) {
377                m_ni_out_vcs_enqueue_time[vc] = Cycles(INFINITE_);
378            }
379            return;
380        }
381    }
382}
383
384int
385NetworkInterface::get_vnet(int vc)
386{
387    for (int i = 0; i < m_virtual_networks; i++) {
388        if (vc >= (i*m_vc_per_vnet) && vc < ((i+1)*m_vc_per_vnet)) {
389            return i;
390        }
391    }
392    fatal("Could not determine vc");
393}
394
395
396// Wakeup the NI in the next cycle if there are waiting
397// messages in the protocol buffer, or waiting flits in the
398// output VC buffer
399void
400NetworkInterface::checkReschedule()
401{
402    for (const auto& it : inNode_ptr) {
403        if (it == nullptr) {
404            continue;
405        }
406
407        while (it->isReady(clockEdge())) { // Is there a message waiting
408            scheduleEvent(Cycles(1));
409            return;
410        }
411    }
412
413    for (int vc = 0; vc < m_num_vcs; vc++) {
414        if (m_ni_out_vcs[vc]->isReady(curCycle() + Cycles(1))) {
415            scheduleEvent(Cycles(1));
416            return;
417        }
418    }
419}
420
421void
422NetworkInterface::print(std::ostream& out) const
423{
424    out << "[Network Interface]";
425}
426
427uint32_t
428NetworkInterface::functionalWrite(Packet *pkt)
429{
430    uint32_t num_functional_writes = 0;
431    for (unsigned int i  = 0; i < m_num_vcs; ++i) {
432        num_functional_writes += m_ni_out_vcs[i]->functionalWrite(pkt);
433    }
434
435    num_functional_writes += outFlitQueue->functionalWrite(pkt);
436    return num_functional_writes;
437}
438
439NetworkInterface *
440GarnetNetworkInterfaceParams::create()
441{
442    return new NetworkInterface(this);
443}
444