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