ruby: message buffers: significant changes
[gem5.git] / src / mem / ruby / network / garnet / flexible-pipeline / NetworkInterface.cc
1 /*
2 * Copyright (c) 2008 Princeton University
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
7 * met: redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer;
9 * redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution;
12 * neither the name of the copyright holders nor the names of its
13 * contributors may be used to endorse or promote products derived from
14 * this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 *
28 * Authors: Niket Agarwal
29 */
30
31 #include <cassert>
32 #include <cmath>
33
34 #include "base/cast.hh"
35 #include "base/stl_helpers.hh"
36 #include "debug/RubyNetwork.hh"
37 #include "mem/ruby/network/MessageBuffer.hh"
38 #include "mem/ruby/network/garnet/flexible-pipeline/NetworkInterface.hh"
39 #include "mem/ruby/network/garnet/flexible-pipeline/flitBuffer.hh"
40 #include "mem/ruby/slicc_interface/NetworkMessage.hh"
41
42 using namespace std;
43 using m5::stl_helpers::deletePointers;
44
45 NetworkInterface::NetworkInterface(const Params *p)
46 : ClockedObject(p), FlexibleConsumer(this)
47 {
48 m_id = p->id;
49 m_virtual_networks = p->virt_nets;
50 m_vc_per_vnet = p->vcs_per_vnet;
51 m_num_vcs = m_vc_per_vnet*m_virtual_networks;
52 m_vc_round_robin = 0;
53
54 // instantiating the NI flit buffers
55 m_ni_buffers.resize(m_num_vcs);
56 for (int i =0; i < m_num_vcs; i++)
57 m_ni_buffers[i] = new flitBuffer();
58
59 m_vc_allocator.resize(m_virtual_networks);
60 for (int i = 0; i < m_virtual_networks; i++) {
61 m_vc_allocator[i] = 0;
62 }
63
64 for (int i = 0; i < m_num_vcs; i++) {
65 m_out_vc_state.push_back(new OutVcState(i));
66 }
67 }
68
69 NetworkInterface::~NetworkInterface()
70 {
71 deletePointers(m_out_vc_state);
72 deletePointers(m_ni_buffers);
73 delete outSrcQueue;
74 }
75
76 void
77 NetworkInterface::addInPort(NetworkLink *in_link)
78 {
79 inNetLink = in_link;
80 in_link->setLinkConsumer(this);
81 }
82
83 void
84 NetworkInterface::addOutPort(NetworkLink *out_link)
85 {
86 outNetLink = out_link;
87 outSrcQueue = new flitBuffer();
88 out_link->setSourceQueue(outSrcQueue);
89 out_link->setSource(this);
90 }
91
92 void
93 NetworkInterface::addNode(map<int, MessageBuffer*>& in,
94 map<int, MessageBuffer*>& out)
95 {
96 inNode_ptr = in;
97 outNode_ptr = out;
98
99 for (auto& it: in) {
100 // the protocol injects messages into the NI
101 it.second->setConsumer(this);
102 it.second->setReceiver(this);
103 }
104
105 for (auto& it : out) {
106 it.second->setSender(this);
107 }
108 }
109
110 void
111 NetworkInterface::request_vc(int in_vc, int in_port, NetDest destination,
112 Cycles request_time)
113 {
114 inNetLink->grant_vc_link(in_vc, request_time);
115 }
116
117 bool
118 NetworkInterface::flitisizeMessage(MsgPtr msg_ptr, int vnet)
119 {
120 NetworkMessage *net_msg_ptr = safe_cast<NetworkMessage *>(msg_ptr.get());
121 NetDest net_msg_dest = net_msg_ptr->getInternalDestination();
122
123 // get all the destinations associated with this message.
124 vector<NodeID> dest_nodes = net_msg_dest.getAllDest();
125
126 // Number of flits is dependent on the link bandwidth available.
127 // This is expressed in terms of bytes/cycle or the flit size
128
129 int num_flits = (int) ceil((double) m_net_ptr->MessageSizeType_to_int(
130 net_msg_ptr->getMessageSize())/m_net_ptr->getNiFlitSize());
131
132 // loop to convert all multicast messages into unicast messages
133 for (int ctr = 0; ctr < dest_nodes.size(); ctr++) {
134 int vc = calculateVC(vnet); // this will return a free output vc
135
136 if (vc == -1) {
137 // did not find a free output vc
138 return false ;
139 }
140 MsgPtr new_msg_ptr = msg_ptr->clone();
141 NodeID destID = dest_nodes[ctr];
142
143 NetworkMessage *new_net_msg_ptr =
144 safe_cast<NetworkMessage *>(new_msg_ptr.get());
145 if (dest_nodes.size() > 1) {
146 NetDest personal_dest;
147 for (int m = 0; m < (int) MachineType_NUM; m++) {
148 if ((destID >= MachineType_base_number((MachineType) m)) &&
149 destID < MachineType_base_number((MachineType) (m+1))) {
150 // calculating the NetDest associated with this destID
151 personal_dest.clear();
152 personal_dest.add((MachineID) {(MachineType) m, (destID -
153 MachineType_base_number((MachineType) m))});
154 new_net_msg_ptr->getInternalDestination() = personal_dest;
155 break;
156 }
157 }
158 net_msg_dest.removeNetDest(personal_dest);
159
160 // removing the destination from the original message to reflect
161 // that a message with this particular destination has been
162 // flitisized and an output vc is acquired
163 net_msg_ptr->getInternalDestination().removeNetDest(personal_dest);
164 }
165 for (int i = 0; i < num_flits; i++) {
166 m_net_ptr->increment_injected_flits(vnet);
167 flit *fl = new flit(i, vc, vnet, num_flits, new_msg_ptr,
168 curCycle());
169 fl->set_delay(curCycle() - ticksToCycles(msg_ptr->getTime()));
170 m_ni_buffers[vc]->insert(fl);
171 }
172
173 m_out_vc_state[vc]->setState(VC_AB_, curCycle());
174
175 // setting an output vc request for the next hop.
176 // This flit will be ready to traverse the link and into the next hop
177 // only when an output vc is acquired at the next hop
178 outNetLink->request_vc_link(
179 vc, new_net_msg_ptr->getInternalDestination(), curCycle());
180 }
181
182 return true ;
183 }
184
185 // An output vc has been granted at the next hop to one of the vc's.
186 // We have to update the state of the vc to reflect this
187 void
188 NetworkInterface::grant_vc(int out_port, int vc, Cycles grant_time)
189 {
190 assert(m_out_vc_state[vc]->isInState(VC_AB_, grant_time));
191 m_out_vc_state[vc]->grant_vc(grant_time);
192 scheduleEvent(Cycles(1));
193 }
194
195 // The tail flit corresponding to this vc has been buffered at the next hop
196 // and thus this vc is now free
197 void
198 NetworkInterface::release_vc(int out_port, int vc, Cycles release_time)
199 {
200 assert(m_out_vc_state[vc]->isInState(ACTIVE_, release_time));
201 m_out_vc_state[vc]->setState(IDLE_, release_time);
202 scheduleEvent(Cycles(1));
203 }
204
205 // Looking for a free output vc
206 int
207 NetworkInterface::calculateVC(int vnet)
208 {
209 int vc_per_vnet;
210 if (m_net_ptr->isVNetOrdered(vnet))
211 vc_per_vnet = 1;
212 else
213 vc_per_vnet = m_vc_per_vnet;
214
215 for (int i = 0; i < vc_per_vnet; i++) {
216 int delta = m_vc_allocator[vnet];
217 m_vc_allocator[vnet]++;
218 if (m_vc_allocator[vnet] == vc_per_vnet)
219 m_vc_allocator[vnet] = 0;
220
221 if (m_out_vc_state[(vnet*m_vc_per_vnet) + delta]->
222 isInState(IDLE_, curCycle())) {
223 return ((vnet*m_vc_per_vnet) + delta);
224 }
225 }
226 return -1;
227 }
228
229 /*
230 * The NI wakeup checks whether there are any ready messages in the protocol
231 * buffer. If yes, it picks that up, flitisizes it into a number of flits and
232 * puts it into an output buffer and schedules the output link.
233 * On a wakeup it also checks whether there are flits in the input link.
234 * If yes, it picks them up and if the flit is a tail, the NI inserts the
235 * corresponding message into the protocol buffer.
236 */
237
238 void
239 NetworkInterface::wakeup()
240 {
241 MsgPtr msg_ptr;
242
243 //Checking for messages coming from the protocol
244 // can pick up a message/cycle for each virtual net
245 for (auto it = inNode_ptr.begin(); it != inNode_ptr.end(); ++it) {
246 int vnet = (*it).first;
247 MessageBuffer *b = (*it).second;
248
249 while (b->isReady()) { // Is there a message waiting
250 msg_ptr = b->peekMsgPtr();
251 if (flitisizeMessage(msg_ptr, vnet)) {
252 b->dequeue();
253 } else {
254 break;
255 }
256 }
257 }
258
259 scheduleOutputLink();
260 checkReschedule();
261
262 /*********** Picking messages destined for this NI **********/
263
264 if (inNetLink->isReady()) {
265 flit *t_flit = inNetLink->consumeLink();
266 if (t_flit->get_type() == TAIL_ || t_flit->get_type() == HEAD_TAIL_) {
267 DPRINTF(RubyNetwork, "m_id: %d, Message delivered at time: %lld\n",
268 m_id, curCycle());
269
270 outNode_ptr[t_flit->get_vnet()]->enqueue(
271 t_flit->get_msg_ptr(), Cycles(1));
272
273 // signal the upstream router that this vc can be freed now
274 inNetLink->release_vc_link(t_flit->get_vc(),
275 curCycle() + Cycles(1));
276 }
277
278 int vnet = t_flit->get_vnet();
279 m_net_ptr->increment_received_flits(vnet);
280 Cycles network_delay = curCycle() - t_flit->get_enqueue_time();
281 Cycles queueing_delay = t_flit->get_delay();
282
283 m_net_ptr->increment_network_latency(network_delay, vnet);
284 m_net_ptr->increment_queueing_latency(queueing_delay, vnet);
285 delete t_flit;
286 }
287 }
288
289 /* This function looks at the NI buffers and if some buffer has flits which
290 * are ready to traverse the link in the next cycle and also the downstream
291 * output vc associated with this flit has buffers left, the link is scheduled
292 * for the next cycle
293 */
294
295 void
296 NetworkInterface::scheduleOutputLink()
297 {
298 int vc = m_vc_round_robin;
299 m_vc_round_robin++;
300 if (m_vc_round_robin == m_num_vcs)
301 m_vc_round_robin = 0;
302
303 for (int i = 0; i < m_num_vcs; i++) {
304 vc++;
305 if (vc == m_num_vcs)
306 vc = 0;
307 if (m_ni_buffers[vc]->isReady(curCycle())) {
308 if (m_out_vc_state[vc]->isInState(ACTIVE_, curCycle()) &&
309 outNetLink->isBufferNotFull_link(vc)) { // buffer backpressure
310
311 // Just removing the flit
312 flit *t_flit = m_ni_buffers[vc]->getTopFlit();
313 t_flit->set_time(curCycle() + Cycles(1));
314 outSrcQueue->insert(t_flit);
315
316 // schedule the out link
317 outNetLink->
318 scheduleEventAbsolute(clockEdge(Cycles(1)));
319 return;
320 }
321 }
322 }
323 }
324
325 void
326 NetworkInterface::checkReschedule()
327 {
328 for (const auto& it : inNode_ptr) {
329 MessageBuffer *b = it.second;
330
331 while (b->isReady()) { // Is there a message waiting
332 scheduleEvent(Cycles(1));
333 return;
334 }
335 }
336
337 for (int vc = 0; vc < m_num_vcs; vc++) {
338 if (m_ni_buffers[vc]->isReady(curCycle() + Cycles(1))) {
339 scheduleEvent(Cycles(1));
340 return;
341 }
342 }
343 }
344
345 bool
346 NetworkInterface::functionalRead(Packet *pkt)
347 {
348 // Go through the internal buffers
349 for (unsigned int i = 0; i < m_ni_buffers.size(); ++i) {
350 if (m_ni_buffers[i]->functionalRead(pkt)) {
351 return true;
352 }
353 }
354
355 // Go through the buffer between this network interface and the router
356 if (outSrcQueue->functionalRead(pkt)) {
357 return true;
358 }
359
360 return false;
361 }
362
363 uint32_t
364 NetworkInterface::functionalWrite(Packet *pkt)
365 {
366 uint32_t num_functional_writes = 0;
367 for (unsigned int i = 0; i < m_ni_buffers.size(); ++i) {
368 num_functional_writes += m_ni_buffers[i]->functionalWrite(pkt);
369 }
370
371 num_functional_writes += outSrcQueue->functionalWrite(pkt);
372 return num_functional_writes;
373 }
374
375 void
376 NetworkInterface::print(std::ostream& out) const
377 {
378 out << "[Network Interface]";
379 }
380
381 NetworkInterface *
382 GarnetNetworkInterfaceParams::create()
383 {
384 return new NetworkInterface(this);
385 }