ruby: Check MessageBuffer space in garnet NetworkInterface
authorMatthew Poremba <matthew.poremba@amd.com>
Thu, 19 Jan 2017 16:59:10 +0000 (11:59 -0500)
committerMatthew Poremba <matthew.poremba@amd.com>
Thu, 19 Jan 2017 16:59:10 +0000 (11:59 -0500)
Garnet's NetworkInterface does not consider the size of MessageBuffers when
ejecting a Message from the network. Add a size check for the MessageBuffer
and only enqueue if space is available. If space is not available, the
message if placed in a queue and the credit is held. A callback from the
MessageBuffer is implemented to wake the NetworkInterface. If there are
messages in the stalled queue, they are processed first, in a FIFO manner
and if succesfully ejected, the credit is finally sent back upstream. The
maximum size of the stall queue is equal to the number of valid VNETs
with MessageBuffers attached.

src/mem/ruby/network/MessageBuffer.cc
src/mem/ruby/network/MessageBuffer.hh
src/mem/ruby/network/garnet2.0/NetworkInterface.cc
src/mem/ruby/network/garnet2.0/NetworkInterface.hh
src/mem/ruby/network/garnet2.0/flit.cc
src/mem/ruby/network/garnet2.0/flit.hh
src/mem/ruby/network/garnet2.0/flitBuffer.hh

index 573d8833a27b8f8e5c1a49fb798767c8f9587142..67bc7f72d4a5d9e2c27364daaf95279a4d4e593d 100644 (file)
@@ -60,6 +60,8 @@ MessageBuffer::MessageBuffer(const Params *p)
 
     m_buf_msgs = 0;
     m_stall_time = 0;
+
+    m_dequeue_callback = nullptr;
 }
 
 unsigned int
@@ -241,9 +243,26 @@ MessageBuffer::dequeue(Tick current_time, bool decrement_messages)
         m_buf_msgs--;
     }
 
+    // if a dequeue callback was requested, call it now
+    if (m_dequeue_callback) {
+        m_dequeue_callback();
+    }
+
     return delay;
 }
 
+void
+MessageBuffer::registerDequeueCallback(std::function<void()> callback)
+{
+    m_dequeue_callback = callback;
+}
+
+void
+MessageBuffer::unregisterDequeueCallback()
+{
+    m_dequeue_callback = nullptr;
+}
+
 void
 MessageBuffer::clear()
 {
index fc8feb03e6a6fc4224bcde813b06cdd6824e455a..e6ec5ac4b11803f007662a6480ae5492409aa841 100644 (file)
@@ -102,6 +102,9 @@ class MessageBuffer : public SimObject
     //! removes it from the queue and returns its total delay.
     Tick dequeue(Tick current_time, bool decrement_messages = true);
 
+    void registerDequeueCallback(std::function<void()> callback);
+    void unregisterDequeueCallback();
+
     void recycle(Tick current_time, Tick recycle_latency);
     bool isEmpty() const { return m_prio_heap.size() == 0; }
     bool isStallMapEmpty() { return m_stall_msg_map.size() == 0; }
@@ -133,6 +136,8 @@ class MessageBuffer : public SimObject
     Consumer* m_consumer;
     std::vector<MsgPtr> m_prio_heap;
 
+    std::function<void()> m_dequeue_callback;
+
     // use a std::map for the stalled messages as this container is
     // sorted and ensures a well-defined iteration order
     typedef std::map<Addr, std::list<MsgPtr> > StallMsgMapType;
index fe9f1b87eff3763a7b34b12a051f7132ec286c96..b3d89cab8ce6bbbb3f9e3e1242545f45580ffdd5 100644 (file)
@@ -70,6 +70,8 @@ NetworkInterface::NetworkInterface(const Params *p)
     for (int i = 0; i < m_virtual_networks; i++) {
         m_vc_allocator[i] = 0;
     }
+
+    m_stall_count.resize(m_virtual_networks);
 }
 
 void
@@ -127,6 +129,41 @@ NetworkInterface::addNode(vector<MessageBuffer *>& in,
     }
 }
 
+void
+NetworkInterface::dequeueCallback()
+{
+    // An output MessageBuffer has dequeued something this cycle and there
+    // is now space to enqueue a stalled message. However, we cannot wake
+    // on the same cycle as the dequeue. Schedule a wake at the soonest
+    // possible time (next cycle).
+    scheduleEventAbsolute(clockEdge(Cycles(1)));
+}
+
+void
+NetworkInterface::incrementStats(flit *t_flit)
+{
+    int vnet = t_flit->get_vnet();
+
+    // Latency
+    m_net_ptr->increment_received_flits(vnet);
+    Cycles network_delay =
+        t_flit->get_dequeue_time() - t_flit->get_enqueue_time() - Cycles(1);
+    Cycles src_queueing_delay = t_flit->get_src_delay();
+    Cycles dest_queueing_delay = (curCycle() - t_flit->get_dequeue_time());
+    Cycles queueing_delay = src_queueing_delay + dest_queueing_delay;
+
+    m_net_ptr->increment_flit_network_latency(network_delay, vnet);
+    m_net_ptr->increment_flit_queueing_latency(queueing_delay, vnet);
+
+    if (t_flit->get_type() == TAIL_ || t_flit->get_type() == HEAD_TAIL_) {
+        m_net_ptr->increment_received_packets(vnet);
+        m_net_ptr->increment_packet_network_latency(network_delay, vnet);
+        m_net_ptr->increment_packet_queueing_latency(queueing_delay, vnet);
+    }
+
+    // Hops
+    m_net_ptr->increment_total_hops(t_flit->get_route().hops_traversed);
+}
 
 /*
  * The NI wakeup checks whether there are any ready messages in the protocol
@@ -166,48 +203,50 @@ NetworkInterface::wakeup()
     scheduleOutputLink();
     checkReschedule();
 
-    /*********** Check the incoming flit link **********/
+    // Check if there are flits stalling a virtual channel. Track if a
+    // message is enqueued to restrict ejection to one message per cycle.
+    bool messageEnqueuedThisCycle = checkStallQueue();
 
+    /*********** Check the incoming flit link **********/
     if (inNetLink->isReady(curCycle())) {
         flit *t_flit = inNetLink->consumeLink();
-        bool free_signal = false;
-        if (t_flit->get_type() == TAIL_ || t_flit->get_type() == HEAD_TAIL_) {
-            free_signal = true;
-
-            // enqueue into the protocol buffers
-            outNode_ptr[t_flit->get_vnet()]->enqueue(
-                t_flit->get_msg_ptr(), curTime, cyclesToTicks(Cycles(1)));
-        }
-        // Simply send a credit back since we are not buffering
-        // this flit in the NI
-        Credit *t_credit = new Credit(t_flit->get_vc(), free_signal,
-                                         curCycle());
-        outCreditQueue->insert(t_credit);
-        outCreditLink->
-            scheduleEventAbsolute(clockEdge(Cycles(1)));
-
         int vnet = t_flit->get_vnet();
+        t_flit->set_dequeue_time(curCycle());
 
-        // Update Stats
-
-        // Latency
-        m_net_ptr->increment_received_flits(vnet);
-        Cycles network_delay = curCycle() - t_flit->get_enqueue_time();
-        Cycles queueing_delay = t_flit->get_src_delay();
-
-        m_net_ptr->increment_flit_network_latency(network_delay, vnet);
-        m_net_ptr->increment_flit_queueing_latency(queueing_delay, vnet);
-
+        // If a tail flit is received, enqueue into the protocol buffers if
+        // space is available. Otherwise, exchange non-tail flits for credits.
         if (t_flit->get_type() == TAIL_ || t_flit->get_type() == HEAD_TAIL_) {
-            m_net_ptr->increment_received_packets(vnet);
-            m_net_ptr->increment_packet_network_latency(network_delay, vnet);
-            m_net_ptr->increment_packet_queueing_latency(queueing_delay, vnet);
-        }
-
-        // Hops
-        m_net_ptr->increment_total_hops(t_flit->get_route().hops_traversed);
+            if (!messageEnqueuedThisCycle &&
+                outNode_ptr[vnet]->areNSlotsAvailable(1, curTime)) {
+                // Space is available. Enqueue to protocol buffer.
+                outNode_ptr[vnet]->enqueue(t_flit->get_msg_ptr(), curTime,
+                                           cyclesToTicks(Cycles(1)));
+
+                // Simply send a credit back since we are not buffering
+                // this flit in the NI
+                sendCredit(t_flit, true);
+
+                // Update stats and delete flit pointer
+                incrementStats(t_flit);
+                delete t_flit;
+            } else {
+                // No space available- Place tail flit in stall queue and set
+                // up a callback for when protocol buffer is dequeued. Stat
+                // update and flit pointer deletion will occur upon unstall.
+                m_stall_queue.push_back(t_flit);
+                m_stall_count[vnet]++;
+
+                auto cb = std::bind(&NetworkInterface::dequeueCallback, this);
+                outNode_ptr[vnet]->registerDequeueCallback(cb);
+            }
+        } else {
+            // Non-tail flit. Send back a credit but not VC free signal.
+            sendCredit(t_flit, false);
 
-        delete t_flit;
+            // Update stats and delete flit pointer.
+            incrementStats(t_flit);
+            delete t_flit;
+        }
     }
 
     /****************** Check the incoming credit link *******/
@@ -220,8 +259,68 @@ NetworkInterface::wakeup()
         }
         delete t_credit;
     }
+
+
+    // It is possible to enqueue multiple outgoing credit flits if a message
+    // was unstalled in the same cycle as a new message arrives. In this
+    // case, we should schedule another wakeup to ensure the credit is sent
+    // back.
+    if (outCreditQueue->getSize() > 0) {
+        outCreditLink->scheduleEventAbsolute(clockEdge(Cycles(1)));
+    }
+}
+
+void
+NetworkInterface::sendCredit(flit *t_flit, bool is_free)
+{
+    Credit *credit_flit = new Credit(t_flit->get_vc(), is_free, curCycle());
+    outCreditQueue->insert(credit_flit);
 }
 
+bool
+NetworkInterface::checkStallQueue()
+{
+    bool messageEnqueuedThisCycle = false;
+    Tick curTime = clockEdge();
+
+    if (!m_stall_queue.empty()) {
+        for (auto stallIter = m_stall_queue.begin();
+             stallIter != m_stall_queue.end(); ) {
+            flit *stallFlit = *stallIter;
+            int vnet = stallFlit->get_vnet();
+
+            // If we can now eject to the protocol buffer, send back credits
+            if (outNode_ptr[vnet]->areNSlotsAvailable(1, curTime)) {
+                outNode_ptr[vnet]->enqueue(stallFlit->get_msg_ptr(), curTime,
+                                           cyclesToTicks(Cycles(1)));
+
+                // Send back a credit with free signal now that the VC is no
+                // longer stalled.
+                sendCredit(stallFlit, true);
+
+                // Update Stats
+                incrementStats(stallFlit);
+
+                // Flit can now safely be deleted and removed from stall queue
+                delete stallFlit;
+                m_stall_queue.erase(stallIter);
+                m_stall_count[vnet]--;
+
+                // If there are no more stalled messages for this vnet, the
+                // callback on it's MessageBuffer is not needed.
+                if (m_stall_count[vnet] == 0)
+                    outNode_ptr[vnet]->unregisterDequeueCallback();
+
+                messageEnqueuedThisCycle = true;
+                break;
+            } else {
+                ++stallIter;
+            }
+        }
+    }
+
+    return messageEnqueuedThisCycle;
+}
 
 // Embed the protocol message into flits
 bool
index f1d1fd50507e87538ad08d36b4afd5fe4d29b608..2dfb2d2878e0a782007eb1dd4b4f1e51d319f61d 100644 (file)
@@ -62,6 +62,7 @@ class NetworkInterface : public ClockedObject, public Consumer
     void addOutPort(NetworkLink *out_link, CreditLink *credit_link,
         SwitchID router_id);
 
+    void dequeueCallback();
     void wakeup();
     void addNode(std::vector<MessageBuffer *> &inNode,
                  std::vector<MessageBuffer *> &outNode);
@@ -90,6 +91,10 @@ class NetworkInterface : public ClockedObject, public Consumer
     CreditLink *inCreditLink;
     CreditLink *outCreditLink;
 
+    // Queue for stalled flits
+    std::deque<flit *> m_stall_queue;
+    std::vector<int> m_stall_count;
+
     // Input Flit Buffers
     // The flit buffers which will serve the Consumer
     std::vector<flitBuffer *>  m_ni_out_vcs;
@@ -102,10 +107,15 @@ class NetworkInterface : public ClockedObject, public Consumer
     // When a vc stays busy for a long time, it indicates a deadlock
     std::vector<int> vc_busy_counter;
 
+    bool checkStallQueue();
     bool flitisizeMessage(MsgPtr msg_ptr, int vnet);
     int calculateVC(int vnet);
+
     void scheduleOutputLink();
     void checkReschedule();
+    void sendCredit(flit *t_flit, bool is_free);
+
+    void incrementStats(flit *t_flit);
 };
 
 #endif // __MEM_RUBY_NETWORK_GARNET_NETWORK_INTERFACE_HH__
index e507ea4422cc5b9b38500a4934eb7e5175422cc7..60f1082f0a98db3e61a4f11ffe1fb950adc40973 100644 (file)
@@ -40,6 +40,7 @@ flit::flit(int id, int  vc, int vnet, RouteInfo route, int size,
     m_size = size;
     m_msg_ptr = msg_ptr;
     m_enqueue_time = curTime;
+    m_dequeue_time = curTime;
     m_time = curTime;
     m_id = id;
     m_vnet = vnet;
index 3e30ec98cdc4020b7b673de3392e8ed8a4fd20ce..419450b1c256a03a28b31c58719ebbd858bc5bf1 100644 (file)
@@ -51,6 +51,7 @@ class flit
     int get_outport() {return m_outport; }
     int get_size() { return m_size; }
     Cycles get_enqueue_time() { return m_enqueue_time; }
+    Cycles get_dequeue_time() { return m_dequeue_time; }
     int get_id() { return m_id; }
     Cycles get_time() { return m_time; }
     int get_vnet() { return m_vnet; }
@@ -66,6 +67,7 @@ class flit
     void set_vc(int vc) { m_vc = vc; }
     void set_route(RouteInfo route) { m_route = route; }
     void set_src_delay(Cycles delay) { src_delay = delay; }
+    void set_dequeue_time(Cycles time) { m_dequeue_time = time; }
 
     void increment_hops() { m_route.hops_traversed++; }
     void print(std::ostream& out) const;
@@ -103,7 +105,7 @@ class flit
     int m_vc;
     RouteInfo m_route;
     int m_size;
-    Cycles m_enqueue_time, m_time;
+    Cycles m_enqueue_time, m_dequeue_time, m_time;
     flit_type m_type;
     MsgPtr m_msg_ptr;
     int m_outport;
index eb6ad616730490bde802e6a6c229433f58b9112d..d98fe8b4fdc5cbb498dd7cbc821290fe69cd833a 100644 (file)
@@ -52,6 +52,7 @@ class flitBuffer
     void print(std::ostream& out) const;
     bool isFull();
     void setMaxSize(int maximum);
+    int getSize() const { return m_buffer.size(); }
 
     flit *
     getTopFlit()