diff options
Diffstat (limited to 'src/intersim2/routers/iq_router.cpp')
| -rw-r--r-- | src/intersim2/routers/iq_router.cpp | 2385 |
1 files changed, 2385 insertions, 0 deletions
diff --git a/src/intersim2/routers/iq_router.cpp b/src/intersim2/routers/iq_router.cpp new file mode 100644 index 0000000..d97f485 --- /dev/null +++ b/src/intersim2/routers/iq_router.cpp @@ -0,0 +1,2385 @@ +// $Id: iq_router.cpp 5263 2012-09-20 23:40:33Z dub $ + +/* + Copyright (c) 2007-2012, Trustees of The Leland Stanford Junior University + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + Redistributions in binary form must reproduce the above copyright notice, this + list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "iq_router.hpp" + +#include <string> +#include <sstream> +#include <iostream> +#include <iomanip> +#include <cstdlib> +#include <cassert> +#include <limits> + +#include "globals.hpp" +#include "random_utils.hpp" +#include "vc.hpp" +#include "routefunc.hpp" +#include "outputset.hpp" +#include "buffer.hpp" +#include "buffer_state.hpp" +#include "roundrobin_arb.hpp" +#include "allocator.hpp" +#include "switch_monitor.hpp" +#include "buffer_monitor.hpp" + +IQRouter::IQRouter( Configuration const & config, Module *parent, + string const & name, int id, int inputs, int outputs ) +: Router( config, parent, name, id, inputs, outputs ), _active(false) +{ + _vcs = config.GetInt( "num_vcs" ); + + _vc_busy_when_full = (config.GetInt("vc_busy_when_full") > 0); + _vc_prioritize_empty = (config.GetInt("vc_prioritize_empty") > 0); + _vc_shuffle_requests = (config.GetInt("vc_shuffle_requests") > 0); + + _speculative = (config.GetInt("speculative") > 0); + _spec_check_elig = (config.GetInt("spec_check_elig") > 0); + _spec_check_cred = (config.GetInt("spec_check_cred") > 0); + _spec_mask_by_reqs = (config.GetInt("spec_mask_by_reqs") > 0); + + _routing_delay = config.GetInt( "routing_delay" ); + _vc_alloc_delay = config.GetInt( "vc_alloc_delay" ); + if(!_vc_alloc_delay) { + Error("VC allocator cannot have zero delay."); + } + _sw_alloc_delay = config.GetInt( "sw_alloc_delay" ); + if(!_sw_alloc_delay) { + Error("Switch allocator cannot have zero delay."); + } + + // Routing + string const rf = config.GetStr("routing_function") + "_" + config.GetStr("topology"); + map<string, tRoutingFunction>::const_iterator rf_iter = gRoutingFunctionMap.find(rf); + if(rf_iter == gRoutingFunctionMap.end()) { + Error("Invalid routing function: " + rf); + } + _rf = rf_iter->second; + + // Alloc VC's + _buf.resize(_inputs); + for ( int i = 0; i < _inputs; ++i ) { + ostringstream module_name; + module_name << "buf_" << i; + _buf[i] = new Buffer(config, _outputs, this, module_name.str( ) ); + module_name.str(""); + } + + // Alloc next VCs' buffer state + _next_buf.resize(_outputs); + for (int j = 0; j < _outputs; ++j) { + ostringstream module_name; + module_name << "next_vc_o" << j; + _next_buf[j] = new BufferState( config, this, module_name.str( ) ); + module_name.str(""); + } + + // Alloc allocators + string vc_alloc_type = config.GetStr( "vc_allocator" ); + if(vc_alloc_type == "piggyback") { + if(!_speculative) { + Error("Piggyback VC allocation requires speculative switch allocation to be enabled."); + } + _vc_allocator = NULL; + _vc_rr_offset.resize(_outputs*_classes, -1); + } else { + _vc_allocator = Allocator::NewAllocator( this, "vc_allocator", + vc_alloc_type, + _vcs*_inputs, + _vcs*_outputs ); + + if ( !_vc_allocator ) { + Error("Unknown vc_allocator type: " + vc_alloc_type); + } + } + + string sw_alloc_type = config.GetStr( "sw_allocator" ); + _sw_allocator = Allocator::NewAllocator( this, "sw_allocator", + sw_alloc_type, + _inputs*_input_speedup, + _outputs*_output_speedup ); + + if ( !_sw_allocator ) { + Error("Unknown sw_allocator type: " + sw_alloc_type); + } + + string spec_sw_alloc_type = config.GetStr( "spec_sw_allocator" ); + if ( _speculative && ( spec_sw_alloc_type != "prio" ) ) { + _spec_sw_allocator = Allocator::NewAllocator( this, "spec_sw_allocator", + spec_sw_alloc_type, + _inputs*_input_speedup, + _outputs*_output_speedup ); + if ( !_spec_sw_allocator ) { + Error("Unknown spec_sw_allocator type: " + spec_sw_alloc_type); + } + } else { + _spec_sw_allocator = NULL; + } + + _sw_rr_offset.resize(_inputs*_input_speedup); + for(int i = 0; i < _inputs*_input_speedup; ++i) + _sw_rr_offset[i] = i % _input_speedup; + + _noq = config.GetInt("noq") > 0; + if(_noq) { + if(_routing_delay) { + Error("NOQ requires lookahead routing to be enabled."); + } + if(_vcs < _outputs) { + Error("NOQ requires at least as many VCs as router outputs."); + } + } + _noq_next_output_port.resize(_inputs, vector<int>(_vcs, -1)); + _noq_next_vc_start.resize(_inputs, vector<int>(_vcs, -1)); + _noq_next_vc_end.resize(_inputs, vector<int>(_vcs, -1)); + + // Output queues + _output_buffer_size = config.GetInt("output_buffer_size"); + _output_buffer.resize(_outputs); + _credit_buffer.resize(_inputs); + + // Switch configuration (when held for multiple cycles) + _hold_switch_for_packet = (config.GetInt("hold_switch_for_packet") > 0); + _switch_hold_in.resize(_inputs*_input_speedup, -1); + _switch_hold_out.resize(_outputs*_output_speedup, -1); + _switch_hold_vc.resize(_inputs*_input_speedup, -1); + + _bufferMonitor = new BufferMonitor(inputs, _classes); + _switchMonitor = new SwitchMonitor(inputs, outputs, _classes); + +#ifdef TRACK_FLOWS + for(int c = 0; c < _classes; ++c) { + _stored_flits[c].resize(_inputs, 0); + _active_packets[c].resize(_inputs, 0); + } + _outstanding_classes.resize(_outputs, vector<queue<int> >(_vcs)); +#endif +} + +IQRouter::~IQRouter( ) +{ + + if(gPrintActivity) { + cout << Name() << ".bufferMonitor:" << endl ; + cout << *_bufferMonitor << endl ; + + cout << Name() << ".switchMonitor:" << endl ; + cout << "Inputs=" << _inputs ; + cout << "Outputs=" << _outputs ; + cout << *_switchMonitor << endl ; + } + + for(int i = 0; i < _inputs; ++i) + delete _buf[i]; + + for(int j = 0; j < _outputs; ++j) + delete _next_buf[j]; + + delete _vc_allocator; + delete _sw_allocator; + if(_spec_sw_allocator) + delete _spec_sw_allocator; + + delete _bufferMonitor; + delete _switchMonitor; +} + +void IQRouter::AddOutputChannel(FlitChannel * channel, CreditChannel * backchannel) +{ + int alloc_delay = _speculative ? max(_vc_alloc_delay, _sw_alloc_delay) : (_vc_alloc_delay + _sw_alloc_delay); + int min_latency = 1 + _crossbar_delay + channel->GetLatency() + _routing_delay + alloc_delay + backchannel->GetLatency() + _credit_delay; + _next_buf[_output_channels.size()]->SetMinLatency(min_latency); + Router::AddOutputChannel(channel, backchannel); +} + +void IQRouter::ReadInputs( ) +{ + bool have_flits = _ReceiveFlits( ); + bool have_credits = _ReceiveCredits( ); + _active = _active || have_flits || have_credits; +} + +void IQRouter::_InternalStep( ) +{ + if(!_active) { + return; + } + + _InputQueuing( ); + bool activity = !_proc_credits.empty(); + + if(!_route_vcs.empty()) + _RouteEvaluate( ); + if(_vc_allocator) { + _vc_allocator->Clear(); + if(!_vc_alloc_vcs.empty()) + _VCAllocEvaluate( ); + } + if(_hold_switch_for_packet) { + if(!_sw_hold_vcs.empty()) + _SWHoldEvaluate( ); + } + _sw_allocator->Clear(); + if(_spec_sw_allocator) + _spec_sw_allocator->Clear(); + if(!_sw_alloc_vcs.empty()) + _SWAllocEvaluate( ); + if(!_crossbar_flits.empty()) + _SwitchEvaluate( ); + + if(!_route_vcs.empty()) { + _RouteUpdate( ); + activity = activity || !_route_vcs.empty(); + } + if(!_vc_alloc_vcs.empty()) { + _VCAllocUpdate( ); + activity = activity || !_vc_alloc_vcs.empty(); + } + if(_hold_switch_for_packet) { + if(!_sw_hold_vcs.empty()) { + _SWHoldUpdate( ); + activity = activity || !_sw_hold_vcs.empty(); + } + } + if(!_sw_alloc_vcs.empty()) { + _SWAllocUpdate( ); + activity = activity || !_sw_alloc_vcs.empty(); + } + if(!_crossbar_flits.empty()) { + _SwitchUpdate( ); + activity = activity || !_crossbar_flits.empty(); + } + + _active = activity; + + _OutputQueuing( ); + + _bufferMonitor->cycle( ); + _switchMonitor->cycle( ); +} + +void IQRouter::WriteOutputs( ) +{ + _SendFlits( ); + _SendCredits( ); +} + + +//------------------------------------------------------------------------------ +// read inputs +//------------------------------------------------------------------------------ + +bool IQRouter::_ReceiveFlits( ) +{ + bool activity = false; + for(int input = 0; input < _inputs; ++input) { + Flit * const f = _input_channels[input]->Receive(); + if(f) { + +#ifdef TRACK_FLOWS + ++_received_flits[f->cl][input]; +#endif + + if(f->watch) { + *gWatchOut << GetSimTime() << " | " << FullName() << " | " + << "Received flit " << f->id + << " from channel at input " << input + << "." << endl; + } + _in_queue_flits.insert(make_pair(input, f)); + activity = true; + } + } + return activity; +} + +bool IQRouter::_ReceiveCredits( ) +{ + bool activity = false; + for(int output = 0; output < _outputs; ++output) { + Credit * const c = _output_credits[output]->Receive(); + if(c) { + _proc_credits.push_back(make_pair(GetSimTime() + _credit_delay, + make_pair(c, output))); + activity = true; + } + } + return activity; +} + + +//------------------------------------------------------------------------------ +// input queuing +//------------------------------------------------------------------------------ + +void IQRouter::_InputQueuing( ) +{ + for(map<int, Flit *>::const_iterator iter = _in_queue_flits.begin(); + iter != _in_queue_flits.end(); + ++iter) { + + int const input = iter->first; + assert((input >= 0) && (input < _inputs)); + + Flit * const f = iter->second; + assert(f); + + int const vc = f->vc; + assert((vc >= 0) && (vc < _vcs)); + + Buffer * const cur_buf = _buf[input]; + + if(f->watch) { + *gWatchOut << GetSimTime() << " | " << FullName() << " | " + << "Adding flit " << f->id + << " to VC " << vc + << " at input " << input + << " (state: " << VC::VCSTATE[cur_buf->GetState(vc)]; + if(cur_buf->Empty(vc)) { + *gWatchOut << ", empty"; + } else { + assert(cur_buf->FrontFlit(vc)); + *gWatchOut << ", front: " << cur_buf->FrontFlit(vc)->id; + } + *gWatchOut << ")." << endl; + } + cur_buf->AddFlit(vc, f); + +#ifdef TRACK_FLOWS + ++_stored_flits[f->cl][input]; + if(f->head) ++_active_packets[f->cl][input]; +#endif + + _bufferMonitor->write(input, f) ; + + if(cur_buf->GetState(vc) == VC::idle) { + assert(cur_buf->FrontFlit(vc) == f); + assert(cur_buf->GetOccupancy(vc) == 1); + assert(f->head); + assert(_switch_hold_vc[input*_input_speedup + vc%_input_speedup] != vc); + if(_routing_delay) { + cur_buf->SetState(vc, VC::routing); + _route_vcs.push_back(make_pair(-1, make_pair(input, vc))); + } else { + if(f->watch) { + *gWatchOut << GetSimTime() << " | " << FullName() << " | " + << "Using precomputed lookahead routing information for VC " << vc + << " at input " << input + << " (front: " << f->id + << ")." << endl; + } + cur_buf->SetRouteSet(vc, &f->la_route_set); + cur_buf->SetState(vc, VC::vc_alloc); + if(_speculative) { + _sw_alloc_vcs.push_back(make_pair(-1, make_pair(make_pair(input, vc), + -1))); + } + if(_vc_allocator) { + _vc_alloc_vcs.push_back(make_pair(-1, make_pair(make_pair(input, vc), + -1))); + } + if(_noq) { + _UpdateNOQ(input, vc, f); + } + } + } else if((cur_buf->GetState(vc) == VC::active) && + (cur_buf->FrontFlit(vc) == f)) { + if(_switch_hold_vc[input*_input_speedup + vc%_input_speedup] == vc) { + _sw_hold_vcs.push_back(make_pair(-1, make_pair(make_pair(input, vc), + -1))); + } else { + _sw_alloc_vcs.push_back(make_pair(-1, make_pair(make_pair(input, vc), + -1))); + } + } + } + _in_queue_flits.clear(); + + while(!_proc_credits.empty()) { + + pair<int, pair<Credit *, int> > const & item = _proc_credits.front(); + + int const time = item.first; + if(GetSimTime() < time) { + break; + } + + Credit * const c = item.second.first; + assert(c); + + int const output = item.second.second; + assert((output >= 0) && (output < _outputs)); + + BufferState * const dest_buf = _next_buf[output]; + +#ifdef TRACK_FLOWS + for(set<int>::const_iterator iter = c->vc.begin(); iter != c->vc.end(); ++iter) { + int const vc = *iter; + assert(!_outstanding_classes[output][vc].empty()); + int cl = _outstanding_classes[output][vc].front(); + _outstanding_classes[output][vc].pop(); + assert(_outstanding_credits[cl][output] > 0); + --_outstanding_credits[cl][output]; + } +#endif + + dest_buf->ProcessCredit(c); + c->Free(); + _proc_credits.pop_front(); + } +} + + +//------------------------------------------------------------------------------ +// routing +//------------------------------------------------------------------------------ + +void IQRouter::_RouteEvaluate( ) +{ + assert(_routing_delay); + + for(deque<pair<int, pair<int, int> > >::iterator iter = _route_vcs.begin(); + iter != _route_vcs.end(); + ++iter) { + + int const time = iter->first; + if(time >= 0) { + break; + } + iter->first = GetSimTime() + _routing_delay - 1; + + int const input = iter->second.first; + assert((input >= 0) && (input < _inputs)); + int const vc = iter->second.second; + assert((vc >= 0) && (vc < _vcs)); + + Buffer const * const cur_buf = _buf[input]; + assert(!cur_buf->Empty(vc)); + assert(cur_buf->GetState(vc) == VC::routing); + + Flit const * const f = cur_buf->FrontFlit(vc); + assert(f); + assert(f->vc == vc); + assert(f->head); + + if(f->watch) { + *gWatchOut << GetSimTime() << " | " << FullName() << " | " + << "Beginning routing for VC " << vc + << " at input " << input + << " (front: " << f->id + << ")." << endl; + } + } +} + +void IQRouter::_RouteUpdate( ) +{ + assert(_routing_delay); + + while(!_route_vcs.empty()) { + + pair<int, pair<int, int> > const & item = _route_vcs.front(); + + int const time = item.first; + if((time < 0) || (GetSimTime() < time)) { + break; + } + assert(GetSimTime() == time); + + int const input = item.second.first; + assert((input >= 0) && (input < _inputs)); + int const vc = item.second.second; + assert((vc >= 0) && (vc < _vcs)); + + Buffer * const cur_buf = _buf[input]; + assert(!cur_buf->Empty(vc)); + assert(cur_buf->GetState(vc) == VC::routing); + + Flit * const f = cur_buf->FrontFlit(vc); + assert(f); + assert(f->vc == vc); + assert(f->head); + + if(f->watch) { + *gWatchOut << GetSimTime() << " | " << FullName() << " | " + << "Completed routing for VC " << vc + << " at input " << input + << " (front: " << f->id + << ")." << endl; + } + + cur_buf->Route(vc, _rf, this, f, input); + cur_buf->SetState(vc, VC::vc_alloc); + if(_speculative) { + _sw_alloc_vcs.push_back(make_pair(-1, make_pair(item.second, -1))); + } + if(_vc_allocator) { + _vc_alloc_vcs.push_back(make_pair(-1, make_pair(item.second, -1))); + } + // NOTE: No need to handle NOQ here, as it requires lookahead routing! + _route_vcs.pop_front(); + } +} + + +//------------------------------------------------------------------------------ +// VC allocation +//------------------------------------------------------------------------------ + +void IQRouter::_VCAllocEvaluate( ) +{ + assert(_vc_allocator); + + bool watched = false; + + for(deque<pair<int, pair<pair<int, int>, int> > >::iterator iter = _vc_alloc_vcs.begin(); + iter != _vc_alloc_vcs.end(); + ++iter) { + + int const time = iter->first; + if(time >= 0) { + break; + } + + int const input = iter->second.first.first; + assert((input >= 0) && (input < _inputs)); + int const vc = iter->second.first.second; + assert((vc >= 0) && (vc < _vcs)); + + assert(iter->second.second == -1); + + Buffer const * const cur_buf = _buf[input]; + assert(!cur_buf->Empty(vc)); + assert(cur_buf->GetState(vc) == VC::vc_alloc); + + Flit const * const f = cur_buf->FrontFlit(vc); + assert(f); + assert(f->vc == vc); + assert(f->head); + + if(f->watch) { + *gWatchOut << GetSimTime() << " | " << FullName() << " | " + << "Beginning VC allocation for VC " << vc + << " at input " << input + << " (front: " << f->id + << ")." << endl; + } + + OutputSet const * const route_set = cur_buf->GetRouteSet(vc); + assert(route_set); + + int const out_priority = cur_buf->GetPriority(vc); + set<OutputSet::sSetElement> const setlist = route_set->GetSet(); + + bool elig = false; + bool cred = false; + bool reserved = false; + + assert(!_noq || (setlist.size() == 1)); + + for(set<OutputSet::sSetElement>::const_iterator iset = setlist.begin(); + iset != setlist.end(); + ++iset) { + + int const out_port = iset->output_port; + assert((out_port >= 0) && (out_port < _outputs)); + + BufferState const * const dest_buf = _next_buf[out_port]; + + int vc_start; + int vc_end; + + if(_noq && _noq_next_output_port[input][vc] >= 0) { + assert(!_routing_delay); + vc_start = _noq_next_vc_start[input][vc]; + vc_end = _noq_next_vc_end[input][vc]; + } else { + vc_start = iset->vc_start; + vc_end = iset->vc_end; + } + assert(vc_start >= 0 && vc_start < _vcs); + assert(vc_end >= 0 && vc_end < _vcs); + assert(vc_end >= vc_start); + + for(int out_vc = vc_start; out_vc <= vc_end; ++out_vc) { + assert((out_vc >= 0) && (out_vc < _vcs)); + + int in_priority = iset->pri; + if(_vc_prioritize_empty && !dest_buf->IsEmptyFor(out_vc)) { + assert(in_priority >= 0); + in_priority += numeric_limits<int>::min(); + } + + // On the input input side, a VC might request several output VCs. + // These VCs can be prioritized by the routing function, and this is + // reflected in "in_priority". On the output side, if multiple VCs are + // requesting the same output VC, the priority of VCs is based on the + // actual packet priorities, which is reflected in "out_priority". + + if(!dest_buf->IsAvailableFor(out_vc)) { + if(f->watch) { + int const use_input_and_vc = dest_buf->UsedBy(out_vc); + int const use_input = use_input_and_vc / _vcs; + int const use_vc = use_input_and_vc % _vcs; + *gWatchOut << GetSimTime() << " | " << FullName() << " | " + << " VC " << out_vc + << " at output " << out_port + << " is in use by VC " << use_vc + << " at input " << use_input; + Flit * cf = _buf[use_input]->FrontFlit(use_vc); + if(cf) { + *gWatchOut << " (front flit: " << cf->id << ")"; + } else { + *gWatchOut << " (empty)"; + } + *gWatchOut << "." << endl; + } + } else { + elig = true; + if(_vc_busy_when_full && dest_buf->IsFullFor(out_vc)) { + if(f->watch) + *gWatchOut << GetSimTime() << " | " << FullName() << " | " + << " VC " << out_vc + << " at output " << out_port + << " is full." << endl; + reserved |= !dest_buf->IsFull(); + } else { + cred = true; + if(f->watch){ + *gWatchOut << GetSimTime() << " | " << FullName() << " | " + << " Requesting VC " << out_vc + << " at output " << out_port + << " (in_pri: " << in_priority + << ", out_pri: " << out_priority + << ")." << endl; + watched = true; + } + int const input_and_vc + = _vc_shuffle_requests ? (vc*_inputs + input) : (input*_vcs + vc); + _vc_allocator->AddRequest(input_and_vc, out_port*_vcs + out_vc, + 0, in_priority, out_priority); + } + } + } + } + if(!elig) { + iter->second.second = STALL_BUFFER_BUSY; + } else if(_vc_busy_when_full && !cred) { + iter->second.second = reserved ? STALL_BUFFER_RESERVED : STALL_BUFFER_FULL; + } + } + + if(watched) { + *gWatchOut << GetSimTime() << " | " << _vc_allocator->FullName() << " | "; + _vc_allocator->PrintRequests( gWatchOut ); + } + + _vc_allocator->Allocate(); + + if(watched) { + *gWatchOut << GetSimTime() << " | " << _vc_allocator->FullName() << " | "; + _vc_allocator->PrintGrants( gWatchOut ); + } + + for(deque<pair<int, pair<pair<int, int>, int> > >::iterator iter = _vc_alloc_vcs.begin(); + iter != _vc_alloc_vcs.end(); + ++iter) { + + int const time = iter->first; + if(time >= 0) { + break; + } + iter->first = GetSimTime() + _vc_alloc_delay - 1; + + int const input = iter->second.first.first; + assert((input >= 0) && (input < _inputs)); + int const vc = iter->second.first.second; + assert((vc >= 0) && (vc < _vcs)); + + if(iter->second.second < -1) { + continue; + } + + assert(iter->second.second == -1); + + Buffer const * const cur_buf = _buf[input]; + assert(!cur_buf->Empty(vc)); + assert(cur_buf->GetState(vc) == VC::vc_alloc); + + Flit const * const f = cur_buf->FrontFlit(vc); + assert(f); + assert(f->vc == vc); + assert(f->head); + + int const input_and_vc + = _vc_shuffle_requests ? (vc*_inputs + input) : (input*_vcs + vc); + int const output_and_vc = _vc_allocator->OutputAssigned(input_and_vc); + + if(output_and_vc >= 0) { + + int const match_output = output_and_vc / _vcs; + assert((match_output >= 0) && (match_output < _outputs)); + int const match_vc = output_and_vc % _vcs; + assert((match_vc >= 0) && (match_vc < _vcs)); + + if(f->watch) { + *gWatchOut << GetSimTime() << " | " << FullName() << " | " + << "Assigning VC " << match_vc + << " at output " << match_output + << " to VC " << vc + << " at input " << input + << "." << endl; + } + + iter->second.second = output_and_vc; + + } else { + + if(f->watch) { + *gWatchOut << GetSimTime() << " | " << FullName() << " | " + << "VC allocation failed for VC " << vc + << " at input " << input + << "." << endl; + } + + iter->second.second = STALL_BUFFER_CONFLICT; + + } + } + + if(_vc_alloc_delay <= 1) { + return; + } + + for(deque<pair<int, pair<pair<int, int>, int> > >::iterator iter = _vc_alloc_vcs.begin(); + iter != _vc_alloc_vcs.end(); + ++iter) { + + int const time = iter->first; + assert(time >= 0); + if(GetSimTime() < time) { + break; + } + + assert(iter->second.second != -1); + + int const output_and_vc = iter->second.second; + + if(output_and_vc >= 0) { + + int const match_output = output_and_vc / _vcs; + assert((match_output >= 0) && (match_output < _outputs)); + int const match_vc = output_and_vc % _vcs; + assert((match_vc >= 0) && (match_vc < _vcs)); + + BufferState const * const dest_buf = _next_buf[match_output]; + + int const input = iter->second.first.first; + assert((input >= 0) && (input < _inputs)); + int const vc = iter->second.first.second; + assert((vc >= 0) && (vc < _vcs)); + + Buffer const * const cur_buf = _buf[input]; + assert(!cur_buf->Empty(vc)); + assert(cur_buf->GetState(vc) == VC::vc_alloc); + + Flit const * const f = cur_buf->FrontFlit(vc); + assert(f); + assert(f->vc == vc); + assert(f->head); + + if(!dest_buf->IsAvailableFor(match_vc)) { + if(f->watch) { + *gWatchOut << GetSimTime() << " | " << FullName() << " | " + << " Discarding previously generated grant for VC " << vc + << " at input " << input + << ": VC " << match_vc + << " at output " << match_output + << " is no longer available." << endl; + } + iter->second.second = STALL_BUFFER_BUSY; + } else if(_vc_busy_when_full && dest_buf->IsFullFor(match_vc)) { + if(f->watch) { + *gWatchOut << GetSimTime() << " | " << FullName() << " | " + << " Discarding previously generated grant for VC " << vc + << " at input " << input + << ": VC " << match_vc + << " at output " << match_output + << " has become full." << endl; + } + iter->second.second = dest_buf->IsFull() ? STALL_BUFFER_FULL : STALL_BUFFER_RESERVED; + } + } + } +} + +void IQRouter::_VCAllocUpdate( ) +{ + assert(_vc_allocator); + + while(!_vc_alloc_vcs.empty()) { + + pair<int, pair<pair<int, int>, int> > const & item = _vc_alloc_vcs.front(); + + int const time = item.first; + if((time < 0) || (GetSimTime() < time)) { + break; + } + assert(GetSimTime() == time); + + int const input = item.second.first.first; + assert((input >= 0) && (input < _inputs)); + int const vc = item.second.first.second; + assert((vc >= 0) && (vc < _vcs)); + + assert(item.second.second != -1); + + Buffer * const cur_buf = _buf[input]; + assert(!cur_buf->Empty(vc)); + assert(cur_buf->GetState(vc) == VC::vc_alloc); + + Flit const * const f = cur_buf->FrontFlit(vc); + assert(f); + assert(f->vc == vc); + assert(f->head); + + if(f->watch) { + *gWatchOut << GetSimTime() << " | " << FullName() << " | " + << "Completed VC allocation for VC " << vc + << " at input " << input + << " (front: " << f->id + << ")." << endl; + } + + int const output_and_vc = item.second.second; + + if(output_and_vc >= 0) { + + int const match_output = output_and_vc / _vcs; + assert((match_output >= 0) && (match_output < _outputs)); + int const match_vc = output_and_vc % _vcs; + assert((match_vc >= 0) && (match_vc < _vcs)); + + if(f->watch) { + *gWatchOut << GetSimTime() << " | " << FullName() << " | " + << " Acquiring assigned VC " << match_vc + << " at output " << match_output + << "." << endl; + } + + BufferState * const dest_buf = _next_buf[match_output]; + assert(dest_buf->IsAvailableFor(match_vc)); + + dest_buf->TakeBuffer(match_vc, input*_vcs + vc); + + cur_buf->SetOutput(vc, match_output, match_vc); + cur_buf->SetState(vc, VC::active); + if(!_speculative) { + _sw_alloc_vcs.push_back(make_pair(-1, make_pair(item.second.first, -1))); + } + } else { + if(f->watch) { + *gWatchOut << GetSimTime() << " | " << FullName() << " | " + << " No output VC allocated." << endl; + } + +#ifdef TRACK_STALLS + assert((output_and_vc == STALL_BUFFER_BUSY) || + (output_and_vc == STALL_BUFFER_CONFLICT)); + if(output_and_vc == STALL_BUFFER_BUSY) { + ++_buffer_busy_stalls[f->cl]; + } else if(output_and_vc == STALL_BUFFER_CONFLICT) { + ++_buffer_conflict_stalls[f->cl]; + } +#endif + + _vc_alloc_vcs.push_back(make_pair(-1, make_pair(item.second.first, -1))); + } + _vc_alloc_vcs.pop_front(); + } +} + + +//------------------------------------------------------------------------------ +// switch holding +//------------------------------------------------------------------------------ + +void IQRouter::_SWHoldEvaluate( ) +{ + assert(_hold_switch_for_packet); + + for(deque<pair<int, pair<pair<int, int>, int> > >::iterator iter = _sw_hold_vcs.begin(); + iter != _sw_hold_vcs.end(); + ++iter) { + + int const time = iter->first; + if(time >= 0) { + break; + } + iter->first = GetSimTime(); + + int const input = iter->second.first.first; + assert((input >= 0) && (input < _inputs)); + int const vc = iter->second.first.second; + assert((vc >= 0) && (vc < _vcs)); + + assert(iter->second.second == -1); + + Buffer const * const cur_buf = _buf[input]; + assert(!cur_buf->Empty(vc)); + assert(cur_buf->GetState(vc) == VC::active); + + Flit const * const f = cur_buf->FrontFlit(vc); + assert(f); + assert(f->vc == vc); + + if(f->watch) { + *gWatchOut << GetSimTime() << " | " << FullName() << " | " + << "Beginning held switch allocation for VC " << vc + << " at input " << input + << " (front: " << f->id + << ")." << endl; + } + + int const expanded_input = input * _input_speedup + vc % _input_speedup; + assert(_switch_hold_vc[expanded_input] == vc); + + int const match_port = cur_buf->GetOutputPort(vc); + assert((match_port >= 0) && (match_port < _outputs)); + int const match_vc = cur_buf->GetOutputVC(vc); + assert((match_vc >= 0) && (match_vc < _vcs)); + + int const expanded_output = match_port*_output_speedup + input%_output_speedup; + assert(_switch_hold_in[expanded_input] == expanded_output); + + BufferState const * const dest_buf = _next_buf[match_port]; + + if(dest_buf->IsFullFor(match_vc)) { + if(f->watch) { + *gWatchOut << GetSimTime() << " | " << FullName() << " | " + << " Unable to reuse held connection from input " << input + << "." << (expanded_input % _input_speedup) + << " to output " << match_port + << "." << (expanded_output % _output_speedup) + << ": No credit available." << endl; + } + iter->second.second = dest_buf->IsFull() ? STALL_BUFFER_FULL : STALL_BUFFER_RESERVED; + } else { + if(f->watch) { + *gWatchOut << GetSimTime() << " | " << FullName() << " | " + << " Reusing held connection from input " << input + << "." << (expanded_input % _input_speedup) + << " to output " << match_port + << "." << (expanded_output % _output_speedup) + << "." << endl; + } + iter->second.second = expanded_output; + } + } +} + +void IQRouter::_SWHoldUpdate( ) +{ + assert(_hold_switch_for_packet); + + while(!_sw_hold_vcs.empty()) { + + pair<int, pair<pair<int, int>, int> > const & item = _sw_hold_vcs.front(); + + int const time = item.first; + if(time < 0) { + break; + } + assert(GetSimTime() == time); + + int const input = item.second.first.first; + assert((input >= 0) && (input < _inputs)); + int const vc = item.second.first.second; + assert((vc >= 0) && (vc < _vcs)); + + assert(item.second.second != -1); + + Buffer * const cur_buf = _buf[input]; + assert(!cur_buf->Empty(vc)); + assert(cur_buf->GetState(vc) == VC::active); + + Flit * const f = cur_buf->FrontFlit(vc); + assert(f); + assert(f->vc == vc); + + if(f->watch) { + *gWatchOut << GetSimTime() << " | " << FullName() << " | " + << "Completed held switch allocation for VC " << vc + << " at input " << input + << " (front: " << f->id + << ")." << endl; + } + + int const expanded_input = input * _input_speedup + vc % _input_speedup; + assert(_switch_hold_vc[expanded_input] == vc); + + int const expanded_output = item.second.second; + + if(expanded_output >= 0 && ( _output_buffer_size==-1 || _output_buffer[expanded_output].size()<size_t(_output_buffer_size))) { + + assert(_switch_hold_in[expanded_input] == expanded_output); + assert(_switch_hold_out[expanded_output] == expanded_input); + + int const output = expanded_output / _output_speedup; + assert((output >= 0) && (output < _outputs)); + assert(cur_buf->GetOutputPort(vc) == output); + + int const match_vc = cur_buf->GetOutputVC(vc); + assert((match_vc >= 0) && (match_vc < _vcs)); + + BufferState * const dest_buf = _next_buf[output]; + + if(f->watch) { + *gWatchOut << GetSimTime() << " | " << FullName() << " | " + << " Scheduling switch connection from input " << input + << "." << (vc % _input_speedup) + << " to output " << output + << "." << (expanded_output % _output_speedup) + << "." << endl; + } + + cur_buf->RemoveFlit(vc); + +#ifdef TRACK_FLOWS + --_stored_flits[f->cl][input]; + if(f->tail) --_active_packets[f->cl][input]; +#endif + + _bufferMonitor->read(input, f) ; + + f->hops++; + f->vc = match_vc; + + if(!_routing_delay && f->head) { + const FlitChannel * channel = _output_channels[output]; + const Router * router = channel->GetSink(); + if(router) { + if(_noq) { + if(f->watch) { + *gWatchOut << GetSimTime() << " | " << FullName() << " | " + << "Updating lookahead routing information for flit " << f->id + << " (NOQ)." << endl; + } + int next_output_port = _noq_next_output_port[input][vc]; + assert(next_output_port >= 0); + _noq_next_output_port[input][vc] = -1; + int next_vc_start = _noq_next_vc_start[input][vc]; + assert(next_vc_start >= 0 && next_vc_start < _vcs); + _noq_next_vc_start[input][vc] = -1; + int next_vc_end = _noq_next_vc_end[input][vc]; + assert(next_vc_end >= 0 && next_vc_end < _vcs); + _noq_next_vc_end[input][vc] = -1; + f->la_route_set.Clear(); + f->la_route_set.AddRange(next_output_port, next_vc_start, next_vc_end); + } else { + if(f->watch) { + *gWatchOut << GetSimTime() << " | " << FullName() << " | " + << "Updating lookahead routing information for flit " << f->id + << "." << endl; + } + int in_channel = channel->GetSinkPort(); + _rf(router, f, in_channel, &f->la_route_set, false); + } + } else { + f->la_route_set.Clear(); + } + } + +#ifdef TRACK_FLOWS + ++_outstanding_credits[f->cl][output]; + _outstanding_classes[output][f->vc].push(f->cl); +#endif + + dest_buf->SendingFlit(f); + + _crossbar_flits.push_back(make_pair(-1, make_pair(f, make_pair(expanded_input, expanded_output)))); + + if(_out_queue_credits.count(input) == 0) { + _out_queue_credits.insert(make_pair(input, Credit::New())); + } + _out_queue_credits.find(input)->second->vc.insert(vc); + + if(cur_buf->Empty(vc)) { + if(f->watch) { + *gWatchOut << GetSimTime() << " | " << FullName() << " | " + << " Cancelling held connection from input " << input + << "." << (expanded_input % _input_speedup) + << " to " << output + << "." << (expanded_output % _output_speedup) + << ": No more flits." << endl; + } + _switch_hold_vc[expanded_input] = -1; + _switch_hold_in[expanded_input] = -1; + _switch_hold_out[expanded_output] = -1; + if(f->tail) { + cur_buf->SetState(vc, VC::idle); + } + } else { + Flit * const nf = cur_buf->FrontFlit(vc); + assert(nf); + assert(nf->vc == vc); + if(f->tail) { + assert(nf->head); + if(f->watch) { + *gWatchOut << GetSimTime() << " | " << FullName() << " | " + << " Cancelling held connection from input " << input + << "." << (expanded_input % _input_speedup) + << " to " << output + << "." << (expanded_output % _output_speedup) + << ": End of packet." << endl; + } + _switch_hold_vc[expanded_input] = -1; + _switch_hold_in[expanded_input] = -1; + _switch_hold_out[expanded_output] = -1; + if(_routing_delay) { + cur_buf->SetState(vc, VC::routing); + _route_vcs.push_back(make_pair(-1, item.second.first)); + } else { + if(nf->watch) { + *gWatchOut << GetSimTime() << " | " << FullName() << " | " + << "Using precomputed lookahead routing information for VC " << vc + << " at input " << input + << " (front: " << nf->id + << ")." << endl; + } + cur_buf->SetRouteSet(vc, &nf->la_route_set); + cur_buf->SetState(vc, VC::vc_alloc); + if(_speculative) { + _sw_alloc_vcs.push_back(make_pair(-1, make_pair(item.second.first, + -1))); + } + if(_vc_allocator) { + _vc_alloc_vcs.push_back(make_pair(-1, make_pair(item.second.first, + -1))); + } + if(_noq) { + _UpdateNOQ(input, vc, nf); + } + } + } else { + _sw_hold_vcs.push_back(make_pair(-1, make_pair(item.second.first, + -1))); + } + } + } else { + //when internal speedup >1.0, the buffer stall stats may not be accruate + assert((expanded_output == STALL_BUFFER_FULL) || + (expanded_output == STALL_BUFFER_RESERVED) || !( _output_buffer_size==-1 || _output_buffer[expanded_output].size()<size_t(_output_buffer_size))); + + int const held_expanded_output = _switch_hold_in[expanded_input]; + assert(held_expanded_output >= 0); + + if(f->watch) { + *gWatchOut << GetSimTime() << " | " << FullName() << " | " + << " Cancelling held connection from input " << input + << "." << (expanded_input % _input_speedup) + << " to " << (held_expanded_output / _output_speedup) + << "." << (held_expanded_output % _output_speedup) + << ": Flit not sent." << endl; + } + _switch_hold_vc[expanded_input] = -1; + _switch_hold_in[expanded_input] = -1; + _switch_hold_out[held_expanded_output] = -1; + _sw_alloc_vcs.push_back(make_pair(-1, make_pair(item.second.first, + -1))); + } + _sw_hold_vcs.pop_front(); + } +} + + +//------------------------------------------------------------------------------ +// switch allocation +//------------------------------------------------------------------------------ + +bool IQRouter::_SWAllocAddReq(int input, int vc, int output) +{ + assert(input >= 0 && input < _inputs); + assert(vc >= 0 && vc < _vcs); + assert(output >= 0 && output < _outputs); + + // When input_speedup > 1, the virtual channel buffers are interleaved to + // create multiple input ports to the switch. Similarily, the output ports + // are interleaved based on their originating input when output_speedup > 1. + + int const expanded_input = input * _input_speedup + vc % _input_speedup; + int const expanded_output = output * _output_speedup + input % _output_speedup; + + Buffer const * const cur_buf = _buf[input]; + assert(!cur_buf->Empty(vc)); + assert((cur_buf->GetState(vc) == VC::active) || + (_speculative && (cur_buf->GetState(vc) == VC::vc_alloc))); + + Flit const * const f = cur_buf->FrontFlit(vc); + assert(f); + assert(f->vc == vc); + + if((_switch_hold_in[expanded_input] < 0) && + (_switch_hold_out[expanded_output] < 0)) { + + Allocator * allocator = _sw_allocator; + int prio = cur_buf->GetPriority(vc); + + if(_speculative && (cur_buf->GetState(vc) == VC::vc_alloc)) { + if(_spec_sw_allocator) { + allocator = _spec_sw_allocator; + } else { + assert(prio >= 0); + prio += numeric_limits<int>::min(); + } + } + + Allocator::sRequest req; + + if(allocator->ReadRequest(req, expanded_input, expanded_output)) { + if(RoundRobinArbiter::Supersedes(vc, prio, req.label, req.in_pri, + _sw_rr_offset[expanded_input], _vcs)) { + if(f->watch) { + *gWatchOut << GetSimTime() << " | " << FullName() << " | " + << " Replacing earlier request from VC " << req.label + << " for output " << output + << "." << (expanded_output % _output_speedup) + << " with priority " << req.in_pri + << " (" << ((cur_buf->GetState(vc) == VC::active) ? + "non-spec" : + "spec") + << ", pri: " << prio + << ")." << endl; + } + allocator->RemoveRequest(expanded_input, expanded_output, req.label); + allocator->AddRequest(expanded_input, expanded_output, vc, prio, prio); + return true; + } + if(f->watch) { + *gWatchOut << GetSimTime() << " | " << FullName() << " | " + << " Output " << output + << "." << (expanded_output % _output_speedup) + << " was already requested by VC " << req.label + << " with priority " << req.in_pri + << " (pri: " << prio + << ")." << endl; + } + return false; + } + if(f->watch) { + *gWatchOut << GetSimTime() << " | " << FullName() << " | " + << " Requesting output " << output + << "." << (expanded_output % _output_speedup) + << " (" << ((cur_buf->GetState(vc) == VC::active) ? + "non-spec" : + "spec") + << ", pri: " << prio + << ")." << endl; + } + allocator->AddRequest(expanded_input, expanded_output, vc, prio, prio); + return true; + } + if(f->watch) { + *gWatchOut << GetSimTime() << " | " << FullName() << " | " + << " Ignoring output " << output + << "." << (expanded_output % _output_speedup) + << " due to switch hold ("; + if(_switch_hold_in[expanded_input] >= 0) { + *gWatchOut << "input: " << input + << "." << (expanded_input % _input_speedup); + if(_switch_hold_out[expanded_output] >= 0) { + *gWatchOut << ", "; + } + } + if(_switch_hold_out[expanded_output] >= 0) { + *gWatchOut << "output: " << output + << "." << (expanded_output % _output_speedup); + } + *gWatchOut << ")." << endl; + } + return false; +} + +void IQRouter::_SWAllocEvaluate( ) +{ + bool watched = false; + + for(deque<pair<int, pair<pair<int, int>, int> > >::iterator iter = _sw_alloc_vcs.begin(); + iter != _sw_alloc_vcs.end(); + ++iter) { + + int const time = iter->first; + if(time >= 0) { + break; + } + + int const input = iter->second.first.first; + assert((input >= 0) && (input < _inputs)); + int const vc = iter->second.first.second; + assert((vc >= 0) && (vc < _vcs)); + + assert(iter->second.second == -1); + + assert(_switch_hold_vc[input * _input_speedup + vc % _input_speedup] != vc); + + Buffer const * const cur_buf = _buf[input]; + assert(!cur_buf->Empty(vc)); + assert((cur_buf->GetState(vc) == VC::active) || + (_speculative && (cur_buf->GetState(vc) == VC::vc_alloc))); + + Flit const * const f = cur_buf->FrontFlit(vc); + assert(f); + assert(f->vc == vc); + + if(f->watch) { + *gWatchOut << GetSimTime() << " | " << FullName() << " | " + << "Beginning switch allocation for VC " << vc + << " at input " << input + << " (front: " << f->id + << ")." << endl; + } + + if(cur_buf->GetState(vc) == VC::active) { + + int const dest_output = cur_buf->GetOutputPort(vc); + assert((dest_output >= 0) && (dest_output < _outputs)); + int const dest_vc = cur_buf->GetOutputVC(vc); + assert((dest_vc >= 0) && (dest_vc < _vcs)); + + BufferState const * const dest_buf = _next_buf[dest_output]; + + if(dest_buf->IsFullFor(dest_vc) || ( _output_buffer_size!=-1 && _output_buffer[dest_output].size()>=(size_t)(_output_buffer_size))) { + if(f->watch) { + *gWatchOut << GetSimTime() << " | " << FullName() << " | " + << " VC " << dest_vc + << " at output " << dest_output + << " is full." << endl; + } + iter->second.second = dest_buf->IsFull() ? STALL_BUFFER_FULL : STALL_BUFFER_RESERVED; + continue; + } + bool const requested = _SWAllocAddReq(input, vc, dest_output); + watched |= requested && f->watch; + continue; + } + assert(_speculative && (cur_buf->GetState(vc) == VC::vc_alloc)); + assert(f->head); + + // The following models the speculative VC allocation aspects of the + // pipeline. An input VC with a request in for an egress virtual channel + // will also speculatively bid for the switch regardless of whether the VC + // allocation succeeds. + + OutputSet const * const route_set = cur_buf->GetRouteSet(vc); + assert(route_set); + + set<OutputSet::sSetElement> const setlist = route_set->GetSet(); + + assert(!_noq || (setlist.size() == 1)); + + for(set<OutputSet::sSetElement>::const_iterator iset = setlist.begin(); + iset != setlist.end(); + ++iset) { + + int const dest_output = iset->output_port; + assert((dest_output >= 0) && (dest_output < _outputs)); + + // for lower levels of speculation, ignore credit availability and always + // issue requests for all output ports in route set + + BufferState const * const dest_buf = _next_buf[dest_output]; + + bool elig = false; + bool cred = false; + + if(_spec_check_elig) { + + // for higher levels of speculation, check if at least one suitable VC + // is available at the current output + + int vc_start; + int vc_end; + + if(_noq && _noq_next_output_port[input][vc] >= 0) { + assert(!_routing_delay); + vc_start = _noq_next_vc_start[input][vc]; + vc_end = _noq_next_vc_end[input][vc]; + } else { + vc_start = iset->vc_start; + vc_end = iset->vc_end; + } + assert(vc_start >= 0 && vc_start < _vcs); + assert(vc_end >= 0 && vc_end < _vcs); + assert(vc_end >= vc_start); + + for(int dest_vc = vc_start; dest_vc <= vc_end; ++dest_vc) { + assert((dest_vc >= 0) && (dest_vc < _vcs)); + + if(dest_buf->IsAvailableFor(dest_vc) && ( _output_buffer_size==-1 || _output_buffer[dest_output].size()<(size_t)(_output_buffer_size))) { + elig = true; + if(!_spec_check_cred || !dest_buf->IsFullFor(dest_vc)) { + cred = true; + break; + } + } + } + } + + if(_spec_check_elig && !elig) { + if(f->watch) { + *gWatchOut << GetSimTime() << " | " << FullName() << " | " + << " Output " << dest_output + << " has no suitable VCs available." << endl; + } + iter->second.second = STALL_BUFFER_BUSY; + } else if(_spec_check_cred && !cred) { + if(f->watch) { + *gWatchOut << GetSimTime() << " | " << FullName() << " | " + << " All suitable VCs at output " << dest_output + << " are full." << endl; + } + iter->second.second = dest_buf->IsFull() ? STALL_BUFFER_FULL : STALL_BUFFER_RESERVED; + } else { + bool const requested = _SWAllocAddReq(input, vc, dest_output); + watched |= requested && f->watch; + } + } + } + + if(watched) { + *gWatchOut << GetSimTime() << " | " << _sw_allocator->FullName() << " | "; + _sw_allocator->PrintRequests(gWatchOut); + if(_spec_sw_allocator) { + *gWatchOut << GetSimTime() << " | " << _spec_sw_allocator->FullName() << " | "; + _spec_sw_allocator->PrintRequests(gWatchOut); + } + } + + _sw_allocator->Allocate(); + if(_spec_sw_allocator) + _spec_sw_allocator->Allocate(); + + if(watched) { + *gWatchOut << GetSimTime() << " | " << _sw_allocator->FullName() << " | "; + _sw_allocator->PrintGrants(gWatchOut); + if(_spec_sw_allocator) { + *gWatchOut << GetSimTime() << " | " << _spec_sw_allocator->FullName() << " | "; + _spec_sw_allocator->PrintGrants(gWatchOut); + } + } + + for(deque<pair<int, pair<pair<int, int>, int> > >::iterator iter = _sw_alloc_vcs.begin(); + iter != _sw_alloc_vcs.end(); + ++iter) { + + int const time = iter->first; + if(time >= 0) { + break; + } + iter->first = GetSimTime() + _sw_alloc_delay - 1; + + int const input = iter->second.first.first; + assert((input >= 0) && (input < _inputs)); + int const vc = iter->second.first.second; + assert((vc >= 0) && (vc < _vcs)); + + if(iter->second.second < -1) { + continue; + } + + assert(iter->second.second == -1); + + Buffer const * const cur_buf = _buf[input]; + assert(!cur_buf->Empty(vc)); + assert((cur_buf->GetState(vc) == VC::active) || + (_speculative && (cur_buf->GetState(vc) == VC::vc_alloc))); + + Flit const * const f = cur_buf->FrontFlit(vc); + assert(f); + assert(f->vc == vc); + + int const expanded_input = input * _input_speedup + vc % _input_speedup; + + int expanded_output = _sw_allocator->OutputAssigned(expanded_input); + + if(expanded_output >= 0) { + assert((expanded_output % _output_speedup) == (input % _output_speedup)); + int const granted_vc = _sw_allocator->ReadRequest(expanded_input, expanded_output); + if(granted_vc == vc) { + if(f->watch) { + *gWatchOut << GetSimTime() << " | " << FullName() << " | " + << "Assigning output " << (expanded_output / _output_speedup) + << "." << (expanded_output % _output_speedup) + << " to VC " << vc + << " at input " << input + << "." << (vc % _input_speedup) + << "." << endl; + } + _sw_rr_offset[expanded_input] = (vc + _input_speedup) % _vcs; + iter->second.second = expanded_output; + } else { + if(f->watch) { + *gWatchOut << GetSimTime() << " | " << FullName() << " | " + << "Switch allocation failed for VC " << vc + << " at input " << input + << ": Granted to VC " << granted_vc << "." << endl; + } + iter->second.second = STALL_CROSSBAR_CONFLICT; + } + } else if(_spec_sw_allocator) { + expanded_output = _spec_sw_allocator->OutputAssigned(expanded_input); + if(expanded_output >= 0) { + assert((expanded_output % _output_speedup) == (input % _output_speedup)); + if(_spec_mask_by_reqs && + _sw_allocator->OutputHasRequests(expanded_output)) { + if(f->watch) { + *gWatchOut << GetSimTime() << " | " << FullName() << " | " + << "Discarding speculative grant for VC " << vc + << " at input " << input + << "." << (vc % _input_speedup) + << " because output " << (expanded_output / _output_speedup) + << "." << (expanded_output % _output_speedup) + << " has non-speculative requests." << endl; + } + iter->second.second = STALL_CROSSBAR_CONFLICT; + } else if(!_spec_mask_by_reqs && + (_sw_allocator->InputAssigned(expanded_output) >= 0)) { + if(f->watch) { + *gWatchOut << GetSimTime() << " | " << FullName() << " | " + << "Discarding speculative grant for VC " << vc + << " at input " << input + << "." << (vc % _input_speedup) + << " because output " << (expanded_output / _output_speedup) + << "." << (expanded_output % _output_speedup) + << " has a non-speculative grant." << endl; + } + iter->second.second = STALL_CROSSBAR_CONFLICT; + } else { + int const granted_vc = _spec_sw_allocator->ReadRequest(expanded_input, + expanded_output); + if(granted_vc == vc) { + if(f->watch) { + *gWatchOut << GetSimTime() << " | " << FullName() << " | " + << "Assigning output " << (expanded_output / _output_speedup) + << "." << (expanded_output % _output_speedup) + << " to VC " << vc + << " at input " << input + << "." << (vc % _input_speedup) + << "." << endl; + } + _sw_rr_offset[expanded_input] = (vc + _input_speedup) % _vcs; + iter->second.second = expanded_output; + } else { + if(f->watch) { + *gWatchOut << GetSimTime() << " | " << FullName() << " | " + << "Switch allocation failed for VC " << vc + << " at input " << input + << ": Granted to VC " << granted_vc << "." << endl; + } + iter->second.second = STALL_CROSSBAR_CONFLICT; + } + } + } else { + + if(f->watch) { + *gWatchOut << GetSimTime() << " | " << FullName() << " | " + << "Switch allocation failed for VC " << vc + << " at input " << input + << ": No output granted." << endl; + } + + iter->second.second = STALL_CROSSBAR_CONFLICT; + + } + } else { + + if(f->watch) { + *gWatchOut << GetSimTime() << " | " << FullName() << " | " + << "Switch allocation failed for VC " << vc + << " at input " << input + << ": No output granted." << endl; + } + + iter->second.second = STALL_CROSSBAR_CONFLICT; + + } + } + + if(!_speculative && (_sw_alloc_delay <= 1)) { + return; + } + + for(deque<pair<int, pair<pair<int, int>, int> > >::iterator iter = _sw_alloc_vcs.begin(); + iter != _sw_alloc_vcs.end(); + ++iter) { + + int const time = iter->first; + assert(time >= 0); + if(GetSimTime() < time) { + break; + } + + assert(iter->second.second != -1); + + int const expanded_output = iter->second.second; + + if(expanded_output >= 0) { + + int const output = expanded_output / _output_speedup; + assert((output >= 0) && (output < _outputs)); + + BufferState const * const dest_buf = _next_buf[output]; + + int const input = iter->second.first.first; + assert((input >= 0) && (input < _inputs)); + assert((input % _output_speedup) == (expanded_output % _output_speedup)); + int const vc = iter->second.first.second; + assert((vc >= 0) && (vc < _vcs)); + + int const expanded_input = input * _input_speedup + vc % _input_speedup; + assert(_switch_hold_vc[expanded_input] != vc); + + Buffer const * const cur_buf = _buf[input]; + assert(!cur_buf->Empty(vc)); + assert((cur_buf->GetState(vc) == VC::active) || + (_speculative && (cur_buf->GetState(vc) == VC::vc_alloc))); + + Flit const * const f = cur_buf->FrontFlit(vc); + assert(f); + assert(f->vc == vc); + + if((_switch_hold_in[expanded_input] >= 0) || + (_switch_hold_out[expanded_output] >= 0)) { + if(f->watch) { + *gWatchOut << GetSimTime() << " | " << FullName() << " | " + << "Discarding grant from input " << input + << "." << (vc % _input_speedup) + << " to output " << output + << "." << (expanded_output % _output_speedup) + << " due to conflict with held connection at "; + if(_switch_hold_in[expanded_input] >= 0) { + *gWatchOut << "input"; + } + if((_switch_hold_in[expanded_input] >= 0) && + (_switch_hold_out[expanded_output] >= 0)) { + *gWatchOut << " and "; + } + if(_switch_hold_out[expanded_output] >= 0) { + *gWatchOut << "output"; + } + *gWatchOut << "." << endl; + } + iter->second.second = STALL_CROSSBAR_CONFLICT; + } else if(_speculative && (cur_buf->GetState(vc) == VC::vc_alloc)) { + + assert(f->head); + + if(_vc_allocator) { // separate VC and switch allocators + + int const input_and_vc = + _vc_shuffle_requests ? (vc*_inputs + input) : (input*_vcs + vc); + int const output_and_vc = _vc_allocator->OutputAssigned(input_and_vc); + + if(output_and_vc < 0) { + if(f->watch) { + *gWatchOut << GetSimTime() << " | " << FullName() << " | " + << "Discarding grant from input " << input + << "." << (vc % _input_speedup) + << " to output " << output + << "." << (expanded_output % _output_speedup) + << " due to misspeculation." << endl; + } + iter->second.second = -1; // stall is counted in VC allocation path! + } else if((output_and_vc / _vcs) != output) { + if(f->watch) { + *gWatchOut << GetSimTime() << " | " << FullName() << " | " + << "Discarding grant from input " << input + << "." << (vc % _input_speedup) + << " to output " << output + << "." << (expanded_output % _output_speedup) + << " due to port mismatch between VC and switch allocator." << endl; + } + iter->second.second = STALL_BUFFER_CONFLICT; // count this case as if we had failed allocation + } else if(dest_buf->IsFullFor((output_and_vc % _vcs))) { + if(f->watch) { + *gWatchOut << GetSimTime() << " | " << FullName() << " | " + << "Discarding grant from input " << input + << "." << (vc % _input_speedup) + << " to output " << output + << "." << (expanded_output % _output_speedup) + << " due to lack of credit." << endl; + } + iter->second.second = dest_buf->IsFull() ? STALL_BUFFER_FULL : STALL_BUFFER_RESERVED; + } + + } else { // VC allocation is piggybacked onto switch allocation + + OutputSet const * const route_set = cur_buf->GetRouteSet(vc); + assert(route_set); + + set<OutputSet::sSetElement> const setlist = route_set->GetSet(); + + bool busy = true; + bool full = true; + bool reserved = false; + + assert(!_noq || (setlist.size() == 1)); + + for(set<OutputSet::sSetElement>::const_iterator iset = setlist.begin(); + iset != setlist.end(); + ++iset) { + if(iset->output_port == output) { + + int vc_start; + int vc_end; + + if(_noq && _noq_next_output_port[input][vc] >= 0) { + assert(!_routing_delay); + vc_start = _noq_next_vc_start[input][vc]; + vc_end = _noq_next_vc_end[input][vc]; + } else { + vc_start = iset->vc_start; + vc_end = iset->vc_end; + } + assert(vc_start >= 0 && vc_start < _vcs); + assert(vc_end >= 0 && vc_end < _vcs); + assert(vc_end >= vc_start); + + for(int out_vc = vc_start; out_vc <= vc_end; ++out_vc) { + assert((out_vc >= 0) && (out_vc < _vcs)); + if(dest_buf->IsAvailableFor(out_vc)) { + busy = false; + if(!dest_buf->IsFullFor(out_vc)) { + full = false; + break; + } else if(!dest_buf->IsFull()) { + reserved = true; + } + } + } + if(!full) { + break; + } + } + } + + if(busy) { + if(f->watch) { + *gWatchOut << GetSimTime() << " | " << FullName() << " | " + << "Discarding grant from input " << input + << "." << (vc % _input_speedup) + << " to output " << output + << "." << (expanded_output % _output_speedup) + << " because no suitable output VC for piggyback allocation is available." << endl; + } + iter->second.second = STALL_BUFFER_BUSY; + } else if(full) { + if(f->watch) { + *gWatchOut << GetSimTime() << " | " << FullName() << " | " + << "Discarding grant from input " << input + << "." << (vc % _input_speedup) + << " to output " << output + << "." << (expanded_output % _output_speedup) + << " because all suitable output VCs for piggyback allocation are full." << endl; + } + iter->second.second = reserved ? STALL_BUFFER_RESERVED : STALL_BUFFER_FULL; + } + + } + + } else { + assert(cur_buf->GetOutputPort(vc) == output); + + int const match_vc = cur_buf->GetOutputVC(vc); + assert((match_vc >= 0) && (match_vc < _vcs)); + + if(dest_buf->IsFullFor(match_vc)) { + if(f->watch) { + *gWatchOut << GetSimTime() << " | " << FullName() << " | " + << " Discarding grant from input " << input + << "." << (vc % _input_speedup) + << " to output " << output + << "." << (expanded_output % _output_speedup) + << " due to lack of credit." << endl; + } + iter->second.second = dest_buf->IsFull() ? STALL_BUFFER_FULL : STALL_BUFFER_RESERVED; + } + } + } + } +} + +void IQRouter::_SWAllocUpdate( ) +{ + while(!_sw_alloc_vcs.empty()) { + + pair<int, pair<pair<int, int>, int> > const & item = _sw_alloc_vcs.front(); + + int const time = item.first; + if((time < 0) || (GetSimTime() < time)) { + break; + } + assert(GetSimTime() == time); + + int const input = item.second.first.first; + assert((input >= 0) && (input < _inputs)); + int const vc = item.second.first.second; + assert((vc >= 0) && (vc < _vcs)); + + Buffer * const cur_buf = _buf[input]; + assert(!cur_buf->Empty(vc)); + assert((cur_buf->GetState(vc) == VC::active) || + (_speculative && (cur_buf->GetState(vc) == VC::vc_alloc))); + + Flit * const f = cur_buf->FrontFlit(vc); + assert(f); + assert(f->vc == vc); + + if(f->watch) { + *gWatchOut << GetSimTime() << " | " << FullName() << " | " + << "Completed switch allocation for VC " << vc + << " at input " << input + << " (front: " << f->id + << ")." << endl; + } + + int const expanded_output = item.second.second; + + if(expanded_output >= 0) { + + int const expanded_input = input * _input_speedup + vc % _input_speedup; + assert(_switch_hold_vc[expanded_input] < 0); + assert(_switch_hold_in[expanded_input] < 0); + assert(_switch_hold_out[expanded_output] < 0); + + int const output = expanded_output / _output_speedup; + assert((output >= 0) && (output < _outputs)); + + BufferState * const dest_buf = _next_buf[output]; + + int match_vc; + + if(!_vc_allocator && (cur_buf->GetState(vc) == VC::vc_alloc)) { + + assert(f->head); + + int const cl = f->cl; + assert((cl >= 0) && (cl < _classes)); + + int const vc_offset = _vc_rr_offset[output*_classes+cl]; + + match_vc = -1; + int match_prio = numeric_limits<int>::min(); + + const OutputSet * route_set = cur_buf->GetRouteSet(vc); + set<OutputSet::sSetElement> const setlist = route_set->GetSet(); + + assert(!_noq || (setlist.size() == 1)); + + for(set<OutputSet::sSetElement>::const_iterator iset = setlist.begin(); + iset != setlist.end(); + ++iset) { + if(iset->output_port == output) { + + int vc_start; + int vc_end; + + if(_noq && _noq_next_output_port[input][vc] >= 0) { + assert(!_routing_delay); + vc_start = _noq_next_vc_start[input][vc]; + vc_end = _noq_next_vc_end[input][vc]; + } else { + vc_start = iset->vc_start; + vc_end = iset->vc_end; + } + assert(vc_start >= 0 && vc_start < _vcs); + assert(vc_end >= 0 && vc_end < _vcs); + assert(vc_end >= vc_start); + + for(int out_vc = vc_start; out_vc <= vc_end; ++out_vc) { + assert((out_vc >= 0) && (out_vc < _vcs)); + + int vc_prio = iset->pri; + if(_vc_prioritize_empty && !dest_buf->IsEmptyFor(out_vc)) { + assert(vc_prio >= 0); + vc_prio += numeric_limits<int>::min(); + } + + // FIXME: This check should probably be performed in Evaluate(), + // not Update(), as the latter can cause the outcome to depend on + // the order of evaluation! + if(dest_buf->IsAvailableFor(out_vc) && + !dest_buf->IsFullFor(out_vc) && + ((match_vc < 0) || + RoundRobinArbiter::Supersedes(out_vc, vc_prio, + match_vc, match_prio, + vc_offset, _vcs))) { + match_vc = out_vc; + match_prio = vc_prio; + } + } + } + } + assert(match_vc >= 0); + + if(f->watch) { + *gWatchOut << GetSimTime() << " | " << FullName() << " | " + << " Allocating VC " << match_vc + << " at output " << output + << " via piggyback VC allocation." << endl; + } + + cur_buf->SetState(vc, VC::active); + cur_buf->SetOutput(vc, output, match_vc); + dest_buf->TakeBuffer(match_vc, input*_vcs + vc); + + _vc_rr_offset[output*_classes+cl] = (match_vc + 1) % _vcs; + + } else { + + assert(cur_buf->GetOutputPort(vc) == output); + + match_vc = cur_buf->GetOutputVC(vc); + + } + assert((match_vc >= 0) && (match_vc < _vcs)); + + if(f->watch) { + *gWatchOut << GetSimTime() << " | " << FullName() << " | " + << " Scheduling switch connection from input " << input + << "." << (vc % _input_speedup) + << " to output " << output + << "." << (expanded_output % _output_speedup) + << "." << endl; + } + + cur_buf->RemoveFlit(vc); + +#ifdef TRACK_FLOWS + --_stored_flits[f->cl][input]; + if(f->tail) --_active_packets[f->cl][input]; +#endif + + _bufferMonitor->read(input, f) ; + + f->hops++; + f->vc = match_vc; + + if(!_routing_delay && f->head) { + const FlitChannel * channel = _output_channels[output]; + const Router * router = channel->GetSink(); + if(router) { + if(_noq) { + if(f->watch) { + *gWatchOut << GetSimTime() << " | " << FullName() << " | " + << "Updating lookahead routing information for flit " << f->id + << " (NOQ)." << endl; + } + int next_output_port = _noq_next_output_port[input][vc]; + assert(next_output_port >= 0); + _noq_next_output_port[input][vc] = -1; + int next_vc_start = _noq_next_vc_start[input][vc]; + assert(next_vc_start >= 0 && next_vc_start < _vcs); + _noq_next_vc_start[input][vc] = -1; + int next_vc_end = _noq_next_vc_end[input][vc]; + assert(next_vc_end >= 0 && next_vc_end < _vcs); + _noq_next_vc_end[input][vc] = -1; + f->la_route_set.Clear(); + f->la_route_set.AddRange(next_output_port, next_vc_start, next_vc_end); + } else { + if(f->watch) { + *gWatchOut << GetSimTime() << " | " << FullName() << " | " + << "Updating lookahead routing information for flit " << f->id + << "." << endl; + } + int in_channel = channel->GetSinkPort(); + _rf(router, f, in_channel, &f->la_route_set, false); + } + } else { + f->la_route_set.Clear(); + } + } + +#ifdef TRACK_FLOWS + ++_outstanding_credits[f->cl][output]; + _outstanding_classes[output][f->vc].push(f->cl); +#endif + + dest_buf->SendingFlit(f); + + _crossbar_flits.push_back(make_pair(-1, make_pair(f, make_pair(expanded_input, expanded_output)))); + + if(_out_queue_credits.count(input) == 0) { + _out_queue_credits.insert(make_pair(input, Credit::New())); + } + _out_queue_credits.find(input)->second->vc.insert(vc); + + if(cur_buf->Empty(vc)) { + if(f->tail) { + cur_buf->SetState(vc, VC::idle); + } + } else { + Flit * const nf = cur_buf->FrontFlit(vc); + assert(nf); + assert(nf->vc == vc); + if(f->tail) { + assert(nf->head); + if(_routing_delay) { + cur_buf->SetState(vc, VC::routing); + _route_vcs.push_back(make_pair(-1, item.second.first)); + } else { + if(nf->watch) { + *gWatchOut << GetSimTime() << " | " << FullName() << " | " + << "Using precomputed lookahead routing information for VC " << vc + << " at input " << input + << " (front: " << nf->id + << ")." << endl; + } + cur_buf->SetRouteSet(vc, &nf->la_route_set); + cur_buf->SetState(vc, VC::vc_alloc); + if(_speculative) { + _sw_alloc_vcs.push_back(make_pair(-1, make_pair(item.second.first, + -1))); + } + if(_vc_allocator) { + _vc_alloc_vcs.push_back(make_pair(-1, make_pair(item.second.first, + -1))); + } + if(_noq) { + _UpdateNOQ(input, vc, nf); + } + } + } else { + if(_hold_switch_for_packet) { + if(f->watch) { + *gWatchOut << GetSimTime() << " | " << FullName() << " | " + << "Setting up switch hold for VC " << vc + << " at input " << input + << "." << (expanded_input % _input_speedup) + << " to output " << output + << "." << (expanded_output % _output_speedup) + << "." << endl; + } + _switch_hold_vc[expanded_input] = vc; + _switch_hold_in[expanded_input] = expanded_output; + _switch_hold_out[expanded_output] = expanded_input; + _sw_hold_vcs.push_back(make_pair(-1, make_pair(item.second.first, + -1))); + } else { + _sw_alloc_vcs.push_back(make_pair(-1, make_pair(item.second.first, + -1))); + } + } + } + } else { + if(f->watch) { + *gWatchOut << GetSimTime() << " | " << FullName() << " | " + << " No output port allocated." << endl; + } + +#ifdef TRACK_STALLS + assert((expanded_output == -1) || // for stalls that are accounted for in VC allocation path + (expanded_output == STALL_BUFFER_BUSY) || + (expanded_output == STALL_BUFFER_CONFLICT) || + (expanded_output == STALL_BUFFER_FULL) || + (expanded_output == STALL_BUFFER_RESERVED) || + (expanded_output == STALL_CROSSBAR_CONFLICT)); + if(expanded_output == STALL_BUFFER_BUSY) { + ++_buffer_busy_stalls[f->cl]; + } else if(expanded_output == STALL_BUFFER_CONFLICT) { + ++_buffer_conflict_stalls[f->cl]; + } else if(expanded_output == STALL_BUFFER_FULL) { + ++_buffer_full_stalls[f->cl]; + } else if(expanded_output == STALL_BUFFER_RESERVED) { + ++_buffer_reserved_stalls[f->cl]; + } else if(expanded_output == STALL_CROSSBAR_CONFLICT) { + ++_crossbar_conflict_stalls[f->cl]; + } +#endif + + _sw_alloc_vcs.push_back(make_pair(-1, make_pair(item.second.first, -1))); + } + _sw_alloc_vcs.pop_front(); + } +} + + +//------------------------------------------------------------------------------ +// switch traversal +//------------------------------------------------------------------------------ + +void IQRouter::_SwitchEvaluate( ) +{ + for(deque<pair<int, pair<Flit *, pair<int, int> > > >::iterator iter = _crossbar_flits.begin(); + iter != _crossbar_flits.end(); + ++iter) { + + int const time = iter->first; + if(time >= 0) { + break; + } + iter->first = GetSimTime() + _crossbar_delay - 1; + + Flit const * const f = iter->second.first; + assert(f); + + int const expanded_input = iter->second.second.first; + int const expanded_output = iter->second.second.second; + + if(f->watch) { + *gWatchOut << GetSimTime() << " | " << FullName() << " | " + << "Beginning crossbar traversal for flit " << f->id + << " from input " << (expanded_input / _input_speedup) + << "." << (expanded_input % _input_speedup) + << " to output " << (expanded_output / _output_speedup) + << "." << (expanded_output % _output_speedup) + << "." << endl; + } + } +} + +void IQRouter::_SwitchUpdate( ) +{ + while(!_crossbar_flits.empty()) { + + pair<int, pair<Flit *, pair<int, int> > > const & item = _crossbar_flits.front(); + + int const time = item.first; + if((time < 0) || (GetSimTime() < time)) { + break; + } + assert(GetSimTime() == time); + + Flit * const f = item.second.first; + assert(f); + + int const expanded_input = item.second.second.first; + int const input = expanded_input / _input_speedup; + assert((input >= 0) && (input < _inputs)); + int const expanded_output = item.second.second.second; + int const output = expanded_output / _output_speedup; + assert((output >= 0) && (output < _outputs)); + + if(f->watch) { + *gWatchOut << GetSimTime() << " | " << FullName() << " | " + << "Completed crossbar traversal for flit " << f->id + << " from input " << input + << "." << (expanded_input % _input_speedup) + << " to output " << output + << "." << (expanded_output % _output_speedup) + << "." << endl; + } + _switchMonitor->traversal(input, output, f) ; + + if(f->watch) { + *gWatchOut << GetSimTime() << " | " << FullName() << " | " + << "Buffering flit " << f->id + << " at output " << output + << "." << endl; + } + _output_buffer[output].push(f); + //the output buffer size isn't precise due to flits in flight + //but there is a maximum bound based on output speed up and ST traversal + assert(_output_buffer[output].size()<=(size_t)_output_buffer_size+ _crossbar_delay* _output_speedup+( _output_speedup-1) ||_output_buffer_size==-1); + _crossbar_flits.pop_front(); + } +} + + +//------------------------------------------------------------------------------ +// output queuing +//------------------------------------------------------------------------------ + +void IQRouter::_OutputQueuing( ) +{ + for(map<int, Credit *>::const_iterator iter = _out_queue_credits.begin(); + iter != _out_queue_credits.end(); + ++iter) { + + int const input = iter->first; + assert((input >= 0) && (input < _inputs)); + + Credit * const c = iter->second; + assert(c); + assert(!c->vc.empty()); + + _credit_buffer[input].push(c); + } + _out_queue_credits.clear(); +} + +//------------------------------------------------------------------------------ +// write outputs +//------------------------------------------------------------------------------ + +void IQRouter::_SendFlits( ) +{ + for ( int output = 0; output < _outputs; ++output ) { + if ( !_output_buffer[output].empty( ) ) { + Flit * const f = _output_buffer[output].front( ); + assert(f); + _output_buffer[output].pop( ); + +#ifdef TRACK_FLOWS + ++_sent_flits[f->cl][output]; +#endif + + if(f->watch) + *gWatchOut << GetSimTime() << " | " << FullName() << " | " + << "Sending flit " << f->id + << " to channel at output " << output + << "." << endl; + if(gTrace) { + cout << "Outport " << output << endl << "Stop Mark" << endl; + } + _output_channels[output]->Send( f ); + } + } +} + +void IQRouter::_SendCredits( ) +{ + for ( int input = 0; input < _inputs; ++input ) { + if ( !_credit_buffer[input].empty( ) ) { + Credit * const c = _credit_buffer[input].front( ); + assert(c); + _credit_buffer[input].pop( ); + _input_credits[input]->Send( c ); + } + } +} + + +//------------------------------------------------------------------------------ +// misc. +//------------------------------------------------------------------------------ + +void IQRouter::Display( ostream & os ) const +{ + for ( int input = 0; input < _inputs; ++input ) { + _buf[input]->Display( os ); + } +} + +int IQRouter::GetUsedCredit(int o) const +{ + assert((o >= 0) && (o < _outputs)); + BufferState const * const dest_buf = _next_buf[o]; + return dest_buf->Occupancy(); +} + +int IQRouter::GetBufferOccupancy(int i) const { + assert(i >= 0 && i < _inputs); + return _buf[i]->GetOccupancy(); +} + +#ifdef TRACK_BUFFERS +int IQRouter::GetUsedCreditForClass(int output, int cl) const +{ + assert((output >= 0) && (output < _outputs)); + BufferState const * const dest_buf = _next_buf[output]; + return dest_buf->OccupancyForClass(cl); +} + +int IQRouter::GetBufferOccupancyForClass(int input, int cl) const +{ + assert((input >= 0) && (input < _inputs)); + return _buf[input]->GetOccupancyForClass(cl); +} +#endif + +vector<int> IQRouter::UsedCredits() const +{ + vector<int> result(_outputs*_vcs); + for(int o = 0; o < _outputs; ++o) { + for(int v = 0; v < _vcs; ++v) { + result[o*_vcs+v] = _next_buf[o]->OccupancyFor(v); + } + } + return result; +} + +vector<int> IQRouter::FreeCredits() const +{ + vector<int> result(_outputs*_vcs); + for(int o = 0; o < _outputs; ++o) { + for(int v = 0; v < _vcs; ++v) { + result[o*_vcs+v] = _next_buf[o]->AvailableFor(v); + } + } + return result; +} + +vector<int> IQRouter::MaxCredits() const +{ + vector<int> result(_outputs*_vcs); + for(int o = 0; o < _outputs; ++o) { + for(int v = 0; v < _vcs; ++v) { + result[o*_vcs+v] = _next_buf[o]->LimitFor(v); + } + } + return result; +} + +void IQRouter::_UpdateNOQ(int input, int vc, Flit const * f) { + assert(!_routing_delay); + assert(f); + assert(f->vc == vc); + assert(f->head); + set<OutputSet::sSetElement> sl = f->la_route_set.GetSet(); + assert(sl.size() == 1); + int out_port = sl.begin()->output_port; + const FlitChannel * channel = _output_channels[out_port]; + const Router * router = channel->GetSink(); + if(router) { + int in_channel = channel->GetSinkPort(); + OutputSet nos; + _rf(router, f, in_channel, &nos, false); + sl = nos.GetSet(); + assert(sl.size() == 1); + OutputSet::sSetElement const & se = *sl.begin(); + int next_output_port = se.output_port; + assert(next_output_port >= 0); + assert(_noq_next_output_port[input][vc] < 0); + _noq_next_output_port[input][vc] = next_output_port; + int next_vc_count = (se.vc_end - se.vc_start + 1) / router->NumOutputs(); + int next_vc_start = se.vc_start + next_output_port * next_vc_count; + assert(next_vc_start >= 0 && next_vc_start < _vcs); + assert(_noq_next_vc_start[input][vc] < 0); + _noq_next_vc_start[input][vc] = next_vc_start; + int next_vc_end = se.vc_start + (next_output_port + 1) * next_vc_count - 1; + assert(next_vc_end >= 0 && next_vc_end < _vcs); + assert(_noq_next_vc_end[input][vc] < 0); + _noq_next_vc_end[input][vc] = next_vc_end; + assert(next_vc_start <= next_vc_end); + if(f->watch) { + *gWatchOut << GetSimTime() << " | " << FullName() << " | " + << "Computing lookahead routing information for flit " << f->id + << " (NOQ)." << endl; + } + } +} |
