From 69f2911e04ffb1b19eef1fafb8c040af271f656e Mon Sep 17 00:00:00 2001 From: Tor Aamodt Date: Thu, 15 Jul 2010 18:09:46 -0800 Subject: creating branch for adding support for CUDA 3.x and Fermi [git-p4: depot-paths = "//depot/gpgpu_sim_research/fermi/distribution/": change = 6829] --- src/intersim/Makefile | 119 +++ src/intersim/allocator.cpp | 429 +++++++++++ src/intersim/allocator.hpp | 122 +++ src/intersim/arbiter.cpp | 143 ++++ src/intersim/arbiter.hpp | 51 ++ src/intersim/booksim.hpp | 13 + src/intersim/booksim_config.cpp | 140 ++++ src/intersim/booksim_config.hpp | 11 + src/intersim/buffer_state.cpp | 145 ++++ src/intersim/buffer_state.hpp | 43 ++ src/intersim/config.l | 51 ++ src/intersim/config.y | 33 + src/intersim/config_utils.cpp | 176 +++++ src/intersim/config_utils.hpp | 45 ++ src/intersim/credit.cpp | 16 + src/intersim/credit.hpp | 15 + src/intersim/doc/fancyhdr.sty | 329 ++++++++ src/intersim/doc/manual.pdf | Bin 0 -> 113212 bytes src/intersim/doc/manual.tex | 687 +++++++++++++++++ src/intersim/event_router.cpp | 923 +++++++++++++++++++++++ src/intersim/event_router.hpp | 151 ++++ src/intersim/examples/fly26_age | 41 + src/intersim/examples/mesh | 40 + src/intersim/examples/mesh2 | 41 + src/intersim/examples/mesh4 | 41 + src/intersim/examples/mesh88_lat | 40 + src/intersim/examples/single | 20 + src/intersim/examples/torus88 | 14 + src/intersim/flit.cpp | 12 + src/intersim/flit.hpp | 46 ++ src/intersim/fly.cpp | 133 ++++ src/intersim/fly.hpp | 26 + src/intersim/injection.cpp | 100 +++ src/intersim/injection.hpp | 12 + src/intersim/interconnect_interface.cpp | 574 ++++++++++++++ src/intersim/interconnect_interface.h | 35 + src/intersim/iq_router.cpp | 633 ++++++++++++++++ src/intersim/iq_router.hpp | 75 ++ src/intersim/islip.cpp | 185 +++++ src/intersim/islip.hpp | 22 + src/intersim/kncube.cpp | 253 +++++++ src/intersim/kncube.hpp | 33 + src/intersim/loa.cpp | 102 +++ src/intersim/loa.hpp | 23 + src/intersim/maxsize.cpp | 181 +++++ src/intersim/maxsize.hpp | 22 + src/intersim/misc_utils.cpp | 25 + src/intersim/misc_utils.hpp | 7 + src/intersim/module.cpp | 63 ++ src/intersim/module.hpp | 33 + src/intersim/network.cpp | 156 ++++ src/intersim/network.hpp | 72 ++ src/intersim/outputset.cpp | 134 ++++ src/intersim/outputset.hpp | 36 + src/intersim/pim.cpp | 98 +++ src/intersim/pim.hpp | 20 + src/intersim/pipefifo.hpp | 76 ++ src/intersim/random_utils.cpp | 25 + src/intersim/random_utils.hpp | 21 + src/intersim/rng.cpp | 111 +++ src/intersim/rng.hpp | 10 + src/intersim/rng_double.cpp | 115 +++ src/intersim/rng_double_wrapper.cpp | 9 + src/intersim/rng_wrapper.cpp | 9 + src/intersim/routefunc.cpp | 1045 ++++++++++++++++++++++++++ src/intersim/routefunc.hpp | 14 + src/intersim/router.cpp | 113 +++ src/intersim/router.hpp | 63 ++ src/intersim/selalloc.cpp | 207 +++++ src/intersim/selalloc.hpp | 22 + src/intersim/singlenet.cpp | 43 ++ src/intersim/singlenet.hpp | 17 + src/intersim/stats.cpp | 119 +++ src/intersim/stats.hpp | 39 + src/intersim/statwraper.cpp | 64 ++ src/intersim/statwraper.h | 13 + src/intersim/traffic.cpp | 303 ++++++++ src/intersim/traffic.hpp | 15 + src/intersim/trafficmanager.cpp | 1249 +++++++++++++++++++++++++++++++ src/intersim/trafficmanager.hpp | 173 +++++ src/intersim/vc.cpp | 175 +++++ src/intersim/vc.hpp | 79 ++ src/intersim/wavefront.cpp | 61 ++ src/intersim/wavefront.hpp | 19 + 84 files changed, 11199 insertions(+) create mode 100644 src/intersim/Makefile create mode 100644 src/intersim/allocator.cpp create mode 100644 src/intersim/allocator.hpp create mode 100644 src/intersim/arbiter.cpp create mode 100644 src/intersim/arbiter.hpp create mode 100644 src/intersim/booksim.hpp create mode 100644 src/intersim/booksim_config.cpp create mode 100644 src/intersim/booksim_config.hpp create mode 100644 src/intersim/buffer_state.cpp create mode 100644 src/intersim/buffer_state.hpp create mode 100644 src/intersim/config.l create mode 100644 src/intersim/config.y create mode 100644 src/intersim/config_utils.cpp create mode 100644 src/intersim/config_utils.hpp create mode 100644 src/intersim/credit.cpp create mode 100644 src/intersim/credit.hpp create mode 100644 src/intersim/doc/fancyhdr.sty create mode 100644 src/intersim/doc/manual.pdf create mode 100644 src/intersim/doc/manual.tex create mode 100644 src/intersim/event_router.cpp create mode 100644 src/intersim/event_router.hpp create mode 100644 src/intersim/examples/fly26_age create mode 100644 src/intersim/examples/mesh create mode 100644 src/intersim/examples/mesh2 create mode 100644 src/intersim/examples/mesh4 create mode 100644 src/intersim/examples/mesh88_lat create mode 100644 src/intersim/examples/single create mode 100644 src/intersim/examples/torus88 create mode 100644 src/intersim/flit.cpp create mode 100644 src/intersim/flit.hpp create mode 100644 src/intersim/fly.cpp create mode 100644 src/intersim/fly.hpp create mode 100644 src/intersim/injection.cpp create mode 100644 src/intersim/injection.hpp create mode 100644 src/intersim/interconnect_interface.cpp create mode 100644 src/intersim/interconnect_interface.h create mode 100644 src/intersim/iq_router.cpp create mode 100644 src/intersim/iq_router.hpp create mode 100644 src/intersim/islip.cpp create mode 100644 src/intersim/islip.hpp create mode 100644 src/intersim/kncube.cpp create mode 100644 src/intersim/kncube.hpp create mode 100644 src/intersim/loa.cpp create mode 100644 src/intersim/loa.hpp create mode 100644 src/intersim/maxsize.cpp create mode 100644 src/intersim/maxsize.hpp create mode 100644 src/intersim/misc_utils.cpp create mode 100644 src/intersim/misc_utils.hpp create mode 100644 src/intersim/module.cpp create mode 100644 src/intersim/module.hpp create mode 100644 src/intersim/network.cpp create mode 100644 src/intersim/network.hpp create mode 100644 src/intersim/outputset.cpp create mode 100644 src/intersim/outputset.hpp create mode 100644 src/intersim/pim.cpp create mode 100644 src/intersim/pim.hpp create mode 100644 src/intersim/pipefifo.hpp create mode 100644 src/intersim/random_utils.cpp create mode 100644 src/intersim/random_utils.hpp create mode 100644 src/intersim/rng.cpp create mode 100644 src/intersim/rng.hpp create mode 100644 src/intersim/rng_double.cpp create mode 100644 src/intersim/rng_double_wrapper.cpp create mode 100644 src/intersim/rng_wrapper.cpp create mode 100644 src/intersim/routefunc.cpp create mode 100644 src/intersim/routefunc.hpp create mode 100644 src/intersim/router.cpp create mode 100644 src/intersim/router.hpp create mode 100644 src/intersim/selalloc.cpp create mode 100644 src/intersim/selalloc.hpp create mode 100644 src/intersim/singlenet.cpp create mode 100644 src/intersim/singlenet.hpp create mode 100644 src/intersim/stats.cpp create mode 100644 src/intersim/stats.hpp create mode 100644 src/intersim/statwraper.cpp create mode 100644 src/intersim/statwraper.h create mode 100644 src/intersim/traffic.cpp create mode 100644 src/intersim/traffic.hpp create mode 100644 src/intersim/trafficmanager.cpp create mode 100644 src/intersim/trafficmanager.hpp create mode 100644 src/intersim/vc.cpp create mode 100644 src/intersim/vc.hpp create mode 100644 src/intersim/wavefront.cpp create mode 100644 src/intersim/wavefront.hpp (limited to 'src/intersim') diff --git a/src/intersim/Makefile b/src/intersim/Makefile new file mode 100644 index 0000000..41525fd --- /dev/null +++ b/src/intersim/Makefile @@ -0,0 +1,119 @@ +CREATESHAREDLIB ?=0 +CPP = g++ $(SNOW) +CC = gcc $(SNOW) + +ifeq ($(INTEL),1) + CPP = icpc + CC = icc +endif + + +YACC = bison -d +LEX = flex +PURIFY = /usr/bin/purify +QUANT = /usr/bin/quantify + + +INTERFACE = interconnect_interface.cpp +DEBUG = 0 +CPPFLAGS = -g -Wall +ifneq ($(DEBUG),1) +CPPFLAGS = -O3 -g +else +CPPFLAGS += -D_GLIBCXX_DEBUG -DGLIBCXX_DEBUG_PEDANTIC +endif + +TEST = -DUNIT_TEST +ifneq ($(UNIT_TEST),1) +TEST = +endif + + CPPFLAGS += -fPIC + +PROG = intersim + +CPP_SRCS = $(INTERFACE) \ + config_utils.cpp \ + booksim_config.cpp \ + module.cpp \ + router.cpp \ + iq_router.cpp \ + event_router.cpp \ + vc.cpp \ + routefunc.cpp \ + traffic.cpp \ + allocator.cpp \ + maxsize.cpp \ + network.cpp \ + singlenet.cpp \ + kncube.cpp \ + fly.cpp \ + trafficmanager.cpp \ + random_utils.cpp \ + buffer_state.cpp \ + stats.cpp \ + pim.cpp \ + islip.cpp \ + loa.cpp \ + wavefront.cpp \ + misc_utils.cpp \ + credit.cpp \ + outputset.cpp \ + flit.cpp \ + selalloc.cpp \ + arbiter.cpp \ + injection.cpp \ + rng_wrapper.cpp \ + rng_double_wrapper.cpp \ + statwraper.cpp + +LEX_OBJS = configlex.o +YACC_OBJS = config_tab.o + +#--- Make rules --- + +OBJS = $(CPP_SRCS:.cpp=.o) $(LEX_OBJS) $(YACC_OBJS) + +.PHONY: clean +.PRECIOUS: %_tab.cpp %_tab.hpp %lex.cpp + +lib$(PROG).a: $(OBJS) + ar rcs lib$(PROG).a $(OBJS) + +purify: $(OBJS) + $(PURIFY) -always-use-cache-dir $(CPP) $(OBJS) -o $(PROG) -L/usr/lib + +quantify: $(OBJS) + $(QUANT) -always-use-cache-dir $(CPP) $(OBJS) -o $(PROG) -L/usr/lib + +%lex.o: %lex.cpp %_tab.hpp + $(CPP) $(CPPFLAGS) -c $< -o $@ + +%.o: %.cpp + $(CPP) $(EXTRA) $(CPPFLAGS) $(TEST) -c $< -o $@ + +%.o: %.c + $(CPP) $(CPPFLAGS) $(TEST) $(VCSFLAGS) -c $< -o $@ + +%_tab.cpp: %.y + $(YACC) -b$* -p$* $< + cp -f $*.tab.c $*_tab.cpp + +%_tab.hpp: %_tab.cpp + cp -f $*.tab.h $*_tab.hpp + +%lex.cpp: %.l + $(LEX) -P$* -o$@ $< + cp configlex.cpp configlex.cpp.orig + awk '/configlineno = 1/ {if (line == 0) {line = 1; print} next;} //{print}' configlex.cpp.orig > configlex.cpp + +clean: + rm -f $(OBJS) *_tab.cpp *_tab.hpp *.tab.c *.tab.h *lex.cpp + rm -f $(PROG) + rm -f lib$(PROG).a + rm -f lib$(PROG).so + +interconnect_interface.o: ../cuda-sim/ptx.tab.h + +../cuda-sim/ptx.tab.h: + make -C ../cuda-sim/ ptx.tab.c diff --git a/src/intersim/allocator.cpp b/src/intersim/allocator.cpp new file mode 100644 index 0000000..b7aae5d --- /dev/null +++ b/src/intersim/allocator.cpp @@ -0,0 +1,429 @@ +#include "booksim.hpp" +#include +#include + +#include "allocator.hpp" +#include "maxsize.hpp" +#include "pim.hpp" +#include "islip.hpp" +#include "loa.hpp" +#include "wavefront.hpp" +#include "selalloc.hpp" + +//================================================== +// Allocator base class +//================================================== + +Allocator::Allocator( const Configuration &config, + Module *parent, const string& name, + int inputs, int outputs ) : +Module( parent, name ), _inputs( inputs ), _outputs( outputs ) +{ + _inmatch = new int [_inputs]; + _outmatch = new int [_outputs]; + _outmask = new int [_outputs]; + + for ( int out = 0; out < _outputs; ++out ) { + _outmask[out] = 0; // active + } +} + +Allocator::~Allocator( ) +{ + delete [] _inmatch; + delete [] _outmatch; + delete [] _outmask; +} + +void Allocator::_ClearMatching( ) +{ + for ( int i = 0; i < _inputs; ++i ) { + _inmatch[i] = -1; + } + + for ( int j = 0; j < _outputs; ++j ) { + _outmatch[j] = -1; + } +} + +int Allocator::OutputAssigned( int in ) const +{ + assert( ( in >= 0 ) && ( in < _inputs ) ); + + return _inmatch[in]; +} + +int Allocator::InputAssigned( int out ) const +{ + assert( ( out >= 0 ) && ( out < _outputs ) ); + + return _outmatch[out]; +} + +void Allocator::MaskOutput( int out, int mask ) +{ + assert( ( out >= 0 ) && ( out < _outputs ) ); + _outmask[out] = mask; +} + +//================================================== +// DenseAllocator +//================================================== + +DenseAllocator::DenseAllocator( const Configuration &config, + Module *parent, const string& name, + int inputs, int outputs ) : +Allocator( config, parent, name, inputs, outputs ) +{ + _request = new sRequest * [_inputs]; + + for ( int i = 0; i < _inputs; ++i ) { + _request[i] = new sRequest [_outputs]; + } + + Clear( ); +} + +DenseAllocator::~DenseAllocator( ) +{ + for ( int i = 0; i < _inputs; ++i ) { + delete [] _request[i]; + } + + delete [] _request; +} + +void DenseAllocator::Clear( ) +{ + for ( int i = 0; i < _inputs; ++i ) { + for ( int j = 0; j < _outputs; ++j ) { + _request[i][j].label = -1; + } + } +} + +int DenseAllocator::ReadRequest( int in, int out ) const +{ + assert( ( in >= 0 ) && ( in < _inputs ) && + ( out >= 0 ) && ( out < _outputs ) ); + + return _request[in][out].label; +} + +bool DenseAllocator::ReadRequest( sRequest &req, int in, int out ) const +{ + assert( ( in >= 0 ) && ( in < _inputs ) && + ( out >= 0 ) && ( out < _outputs ) ); + + req = _request[in][out]; + + return( req.label != -1 ); +} + +void DenseAllocator::AddRequest( int in, int out, int label, + int in_pri, int out_pri ) +{ + assert( ( in >= 0 ) && ( in < _inputs ) && + ( out >= 0 ) && ( out < _outputs ) ); + + _request[in][out].label = label; + _request[in][out].in_pri = in_pri; + _request[in][out].out_pri = out_pri; +} + +void DenseAllocator::RemoveRequest( int in, int out, int label ) +{ + assert( ( in >= 0 ) && ( in < _inputs ) && + ( out >= 0 ) && ( out < _outputs ) ); + + _request[in][out].label = -1; +} + +void DenseAllocator::PrintRequests( ) const +{ + cout << "requests for " << _fullname << endl; + for ( int i = 0; i < _inputs; ++i ) { + for ( int j = 0; j < _outputs; ++j ) { + cout << ( _request[i][j].label != -1 ) << " "; + } + cout << endl; + } + cout << endl; +} + +//================================================== +// SparseAllocator +//================================================== + +SparseAllocator::SparseAllocator( const Configuration &config, + Module *parent, const string& name, + int inputs, int outputs ) : +Allocator( config, parent, name, inputs, outputs ) +{ + _in_req = new list [_inputs]; + _out_req = new list [_outputs]; +} + + +SparseAllocator::~SparseAllocator( ) +{ + delete [] _in_req; + delete [] _out_req; +} + +void SparseAllocator::Clear( ) +{ + for ( int i = 0; i < _inputs; ++i ) { + _in_req[i].clear( ); + } + + for ( int j = 0; j < _outputs; ++j ) { + _out_req[j].clear( ); + } + + _in_occ.clear( ); + _out_occ.clear( ); +} + +int SparseAllocator::ReadRequest( int in, int out ) const +{ + sRequest r; + + if ( ! ReadRequest( r, in, out ) ) { + r.label = -1; + } + + return r.label; +} + +bool SparseAllocator::ReadRequest( sRequest &req, int in, int out ) const +{ + bool found; + + assert( ( in >= 0 ) && ( in < _inputs ) && + ( out >= 0 ) && ( out < _outputs ) ); + + list::const_iterator match; + + match = _in_req[in].begin( ); + while ( ( match != _in_req[in].end( ) ) && + ( match->port != out ) ) { + match++; + } + + if ( match != _in_req[in].end( ) ) { + req = *match; + found = true; + } else { + found = false; + } + + return found; +} + +void SparseAllocator::AddRequest( int in, int out, int label, + int in_pri, int out_pri ) +{ + assert( ( in >= 0 ) && ( in < _inputs ) && + ( out >= 0 ) && ( out < _outputs ) ); + + list::iterator insert_point; + list::iterator occ_insert; + sRequest req; + + // insert into occupied inputs list if + // input is currently empty + if ( _in_req[in].empty( ) ) { + occ_insert = _in_occ.begin( ); + while ( ( occ_insert != _in_occ.end( ) ) && + ( *occ_insert < in ) ) { + occ_insert++; + } + assert( ( occ_insert == _in_occ.end( ) ) || + ( *occ_insert != in ) ); + + _in_occ.insert( occ_insert, in ); + } + + // similarly for the output + if ( _out_req[out].empty( ) ) { + occ_insert = _out_occ.begin( ); + while ( ( occ_insert != _out_occ.end( ) ) && + ( *occ_insert < out ) ) { + occ_insert++; + } + assert( ( occ_insert == _out_occ.end( ) ) || + ( *occ_insert != out ) ); + + _out_occ.insert( occ_insert, out ); + } + + // insert input request in order of it's output + insert_point = _in_req[in].begin( ); + while ( ( insert_point != _in_req[in].end( ) ) && + ( insert_point->port < out ) ) { + insert_point++; + } + + req.port = out; + req.label = label; + req.in_pri = in_pri; + req.out_pri = out_pri; + + bool del = false; + bool add = true; + + // For consistent behavior, delete the existing request + // if it is for the same output and has a higher + // priority + + if ( ( insert_point != _in_req[in].end( ) ) && + ( insert_point->port == out ) ) { + if ( insert_point->in_pri < in_pri ) { + del = true; + } else { + add = false; + } + } + + if ( add ) { + _in_req[in].insert( insert_point, req ); + } + + if ( del ) { + _in_req[in].erase( insert_point ); + } + + insert_point = _out_req[out].begin( ); + while ( ( insert_point != _out_req[out].end( ) ) && + ( insert_point->port < in ) ) { + insert_point++; + } + + req.port = in; + req.label = label; + + if ( add ) { + _out_req[out].insert( insert_point, req ); + } + + if ( del ) { + // This should be consistent, but check for sanity + if ( ( insert_point == _out_req[out].end( ) ) || + ( insert_point->port != in ) ) { + Error( "Internal allocator error --- input and output requests non consistent" ); + } + _out_req[out].erase( insert_point ); + } +} + +void SparseAllocator::RemoveRequest( int in, int out, int label ) +{ + assert( ( in >= 0 ) && ( in < _inputs ) && + ( out >= 0 ) && ( out < _outputs ) ); + + list::iterator erase_point; + list::iterator occ_remove; + + // insert input request in order of it's output + erase_point = _in_req[in].begin( ); + while ( ( erase_point != _in_req[in].end( ) ) && + ( erase_point->port != out ) ) { + erase_point++; + } + + assert( erase_point != _in_req[in].end( ) ); + _in_req[in].erase( erase_point ); + + // remove from occupied inputs list if + // input is now empty + if ( _in_req[in].empty( ) ) { + occ_remove = _in_occ.begin( ); + while ( ( occ_remove != _in_occ.end( ) ) && + ( *occ_remove != in ) ) { + occ_remove++; + } + + assert( occ_remove != _in_occ.end( ) ); + _in_occ.erase( occ_remove ); + } + + // similarly for the output + erase_point = _out_req[out].begin( ); + while ( ( erase_point != _out_req[out].end( ) ) && + ( erase_point->port != in ) ) { + erase_point++; + } + + assert( erase_point != _out_req[out].end( ) ); + _out_req[out].erase( erase_point ); + + if ( _out_req[out].empty( ) ) { + occ_remove = _out_occ.begin( ); + while ( ( occ_remove != _out_occ.end( ) ) && + ( *occ_remove != out ) ) { + occ_remove++; + } + + assert( occ_remove != _out_occ.end( ) ); + _out_occ.erase( occ_remove ); + } +} + +void SparseAllocator::PrintRequests( ) const +{ + list::const_iterator iter; + + cout << "input requests:" << endl; + for ( int input = 0; input < _inputs; ++input ) { + cout << " input " << input << " : "; + for ( iter = _in_req[input].begin( ); + iter != _in_req[input].end( ); iter++ ) { + cout << iter->port << " "; + } + cout << endl; + } + + cout << "output requests:" << endl; + for ( int output = 0; output < _outputs; ++output ) { + cout << " output " << output << " : "; + if ( _outmask[output] == 0 ) { + for ( iter = _out_req[output].begin( ); + iter != _out_req[output].end( ); iter++ ) { + cout << iter->port << " "; + } + cout << endl; + } else { + cout << "masked" << endl; + } + } +} + +//================================================== +// Global allocator allocation function +//================================================== + +Allocator *Allocator::NewAllocator( const Configuration &config, + Module *parent, const string& name, + const string &alloc_type, + int inputs, int input_speedup, + int outputs, int output_speedup ) +{ + Allocator *a = 0; + + if ( alloc_type == "max_size" ) { + a = new MaxSizeMatch( config, parent, name, inputs, outputs ); + } else if ( alloc_type == "pim" ) { + a = new PIM( config, parent, name, inputs, outputs ); + } else if ( alloc_type == "islip" ) { + a = new iSLIP_Sparse( config, parent, name, inputs, outputs ); + } else if ( alloc_type == "loa" ) { + a = new LOA( config, parent, name, inputs, input_speedup, outputs, output_speedup ); + } else if ( alloc_type == "wavefront" ) { + a = new Wavefront( config, parent, name, inputs, outputs ); + } else if ( alloc_type == "select" ) { + a = new SelAlloc( config, parent, name, inputs, outputs ); + } + + return a; +} diff --git a/src/intersim/allocator.hpp b/src/intersim/allocator.hpp new file mode 100644 index 0000000..ac77a3a --- /dev/null +++ b/src/intersim/allocator.hpp @@ -0,0 +1,122 @@ +#ifndef _ALLOCATOR_HPP_ +#define _ALLOCATOR_HPP_ + +#include +#include + +#include "module.hpp" +#include "config_utils.hpp" + +class Allocator : public Module { +protected: + const int _inputs; + const int _outputs; + + int *_inmatch; + int *_outmatch; + + int *_outmask; + + void _ClearMatching( ); +public: + + struct sRequest { + int port; + int label; + int in_pri; + int out_pri; + }; + + Allocator( const Configuration &config, + Module *parent, const string& name, + int inputs, int outputs ); + virtual ~Allocator( ); + + virtual void Clear( ) = 0; + + virtual int ReadRequest( int in, int out ) const = 0; + virtual bool ReadRequest( sRequest &req, int in, int out ) const = 0; + + virtual void AddRequest( int in, int out, int label = 1, + int in_pri = 0, int out_pri = 0 ) = 0; + virtual void RemoveRequest( int in, int out, int label = 1 ) = 0; + + virtual void Allocate( ) = 0; + + void MaskOutput( int out, int mask = 1 ); + + int OutputAssigned( int in ) const; + int InputAssigned( int out ) const; + + virtual void PrintRequests( ) const = 0; + + static Allocator *NewAllocator( const Configuration &config, + Module *parent, const string& name, + const string &alloc_type, + int inputs, int input_speedup, + int outputs, int output_speedup ); +}; + +//================================================== +// A dense allocator stores the entire request +// matrix. +//================================================== + +class DenseAllocator : public Allocator { +protected: + sRequest **_request; + +public: + DenseAllocator( const Configuration &config, + Module *parent, const string& name, + int inputs, int outputs ); + virtual ~DenseAllocator( ); + + void Clear( ); + + int ReadRequest( int in, int out ) const; + bool ReadRequest( sRequest &req, int in, int out ) const; + + void AddRequest( int in, int out, int label = 1, + int in_pri = 0, int out_pri = 0 ); + void RemoveRequest( int in, int out, int label = 1 ); + + virtual void Allocate( ) = 0; + + void PrintRequests( ) const; +}; + +//================================================== +// A sparse allocator only stores the requests +// (allows for a more efficient implementation). +//================================================== + +class SparseAllocator : public Allocator { +protected: + list _in_occ; + list _out_occ; + + list *_in_req; + list *_out_req; + +public: + SparseAllocator( const Configuration &config, + Module *parent, const string& name, + int inputs, int outputs ); + virtual ~SparseAllocator( ); + + void Clear( ); + + int ReadRequest( int in, int out ) const; + bool ReadRequest( sRequest &req, int in, int out ) const; + + void AddRequest( int in, int out, int label = 1, + int in_pri = 0, int out_pri = 0 ); + void RemoveRequest( int in, int out, int label = 1 ); + + virtual void Allocate( ) = 0; + + void PrintRequests( ) const; +}; + +#endif diff --git a/src/intersim/arbiter.cpp b/src/intersim/arbiter.cpp new file mode 100644 index 0000000..7225b9b --- /dev/null +++ b/src/intersim/arbiter.cpp @@ -0,0 +1,143 @@ +#include "booksim.hpp" +#include + +#include "arbiter.hpp" + + +Arbiter::Arbiter( const Configuration &, + Module *parent, const string& name, + int inputs ) +: Module( parent, name ), _inputs( inputs ) +{ +} + +Arbiter::~Arbiter( ) +{ +} + +void Arbiter::Clear( ) +{ + _requests.clear( ); +} + +void Arbiter::AddRequest( int in, int label, int pri ) +{ + sRequest r; + list::iterator insert_point; + + r.in = in; r.label = label; r.pri = pri; + + insert_point = _requests.begin( ); + while ( ( insert_point != _requests.end( ) ) && + ( insert_point->in < in ) ) { + insert_point++; + } + + bool del = false; + bool add = true; + + // For consistant behavior, delete the existing request + // if it is for the same input and has a higher + // priority + + if ( ( insert_point != _requests.end( ) ) && + ( insert_point->in == in ) ) { + if ( insert_point->pri < pri ) { + del = true; + } else { + add = false; + } + } + + if ( add ) { + _requests.insert( insert_point, r ); + } + + if ( del ) { + _requests.erase( insert_point ); + } +} + +void Arbiter::RemoveRequest( int in, int label ) +{ + list::iterator erase_point; + + erase_point = _requests.begin( ); + while ( ( erase_point != _requests.end( ) ) && + ( erase_point->in < in ) ) { + erase_point++; + } + + assert( erase_point != _requests.end( ) ); + _requests.erase( erase_point ); +} + +int Arbiter::Match( ) const +{ + return _match; +} + +//================================================== +// PriorityArbiter +//================================================== + +PriorityArbiter::PriorityArbiter( const Configuration &config, + Module *parent, const string& name, + int inputs ) +: Arbiter( config, parent, name, inputs ) +{ + _rr_ptr = 0; +} + +PriorityArbiter::~PriorityArbiter( ) +{ +} + +void PriorityArbiter::Arbitrate( ) +{ + list::iterator p; + + int max_index, max_pri; + bool wrapped; + + if ( _requests.begin( ) != _requests.end( ) ) { + // A round-robin arbiter between input requests + p = _requests.begin( ); + while ( ( p != _requests.end( ) ) && + ( p->in < _rr_ptr ) ) { + p++; + } + + max_index = -1; + max_pri = 0; + + wrapped = false; + while ( (!wrapped) || ( p->in < _rr_ptr ) ) { + if ( p == _requests.end( ) ) { + if ( wrapped ) { + break; + } + // p is valid here because empty lists + // are skipped (above) + p = _requests.begin( ); + wrapped = true; + } + + // check if request is the highest priority so far + if ( ( p->pri > max_pri ) || ( max_index == -1 ) ) { + max_pri = p->pri; + max_index = p->in; + } + + p++; + } + + _match = max_index; // -1 for no match + if ( _match != -1 ) { + _rr_ptr = ( _match + 1 ) % _inputs; + } + + } else { + _match = -1; + } +} diff --git a/src/intersim/arbiter.hpp b/src/intersim/arbiter.hpp new file mode 100644 index 0000000..23b5232 --- /dev/null +++ b/src/intersim/arbiter.hpp @@ -0,0 +1,51 @@ +#ifndef _ARBITER_HPP_ +#define _ARBITER_HPP_ + +#include + +#include "module.hpp" +#include "config_utils.hpp" + +class Arbiter : public Module { +protected: + const int _inputs; + + struct sRequest { + int in; + int label; + int pri; + }; + + list _requests; + + int _match; + +public: + Arbiter( const Configuration &, + Module *parent, const string& name, + int inputs ); + virtual ~Arbiter( ); + + void Clear( ); + + void AddRequest( int in, int label = 0, int pri = 0 ); + void RemoveRequest( int in, int label = 0 ); + + virtual void Arbitrate( ) = 0; + + int Match( ) const; +}; + +class PriorityArbiter : public Arbiter { + int _rr_ptr; + +public: + PriorityArbiter( const Configuration &config, + Module *parent, const string& name, + int inputs ); + ~PriorityArbiter( ); + + void Arbitrate( ); +}; + +#endif diff --git a/src/intersim/booksim.hpp b/src/intersim/booksim.hpp new file mode 100644 index 0000000..3f0a77e --- /dev/null +++ b/src/intersim/booksim.hpp @@ -0,0 +1,13 @@ +#ifndef _BOOKSIM_HPP_ +#define _BOOKSIM_HPP_ + +#include + +#ifdef _WIN32_ + #pragma warning (disable: 4786) + #include +#endif + +using namespace std; + +#endif diff --git a/src/intersim/booksim_config.cpp b/src/intersim/booksim_config.cpp new file mode 100644 index 0000000..da3bd08 --- /dev/null +++ b/src/intersim/booksim_config.cpp @@ -0,0 +1,140 @@ +#include "booksim.hpp" +#include "booksim_config.hpp" + +BookSimConfig::BookSimConfig( ) +{ + _int_map["perfect_icnt"] = 0; // if set overrides fixed_lat_per_hop setting + _int_map["fixed_lat_per_hop"] = 0; // if set icnt is NOT simulated instead packets are sent into destination based on a fixed_lat_per_hop + _int_map["network_count"] = 2; // number of independent interconnection networks (if it is set to 2 then 2 identical networks are created: sh2mem and mem2shd ) + + _int_map["output_extra_latency"] = 0; + + _int_map["use_map"] = 1; + + _int_map["flit_size"] = 32; + //stats + _int_map["enable_link_stats"] = 0; // show output link and VC utilization stats + + _int_map["MATLAB_OUTPUT"] = 0; // output data in MATLAB friendly format + _int_map["DISPLAY_LAT_DIST"] = 0; // distribution of packet latencies + _int_map["DISPLAY_HOP_DIST"] = 0; // distribution of hop counts + _int_map["DISPLAY_PAIR_LATENCY"] = 0; // avg. latency for each s-d pair + + _int_map["input_buf_size"] = 0; + _int_map["ejection_buf_size"] = 0; // if left zero the simulator will use the vc_buf_size instead + _int_map["boundary_buf_size"] = 16; + + //======================================================== + // Network options + //======================================================== + + //==== Multi-node topology options ======================= + + AddStrField( "topology", "torus" ); + + _int_map["k"] = 8; + _int_map["n"] = 2; + + AddStrField( "routing_function", "none" ); + AddStrField( "selection_function", "random" ); + + _int_map["link_failures"] = 0; + _int_map["fail_seed"] = 0; + + _int_map["wire_delay"] = 0; + + //==== Single-node options =============================== + + _int_map["in_ports"] = 5; + _int_map["out_ports"] = 5; + + _int_map["voq"] = 0; + + //======================================================== + // Router options + //======================================================== + + //==== General options =================================== + + AddStrField( "router", "iq" ); + + _int_map["output_delay"] = 0; + _int_map["credit_delay"] = 0; + _float_map["internal_speedup"] = 1.0; + + //==== Input-queued ====================================== + + _int_map["num_vcs"] = 1; + _int_map["vc_buf_size"] = 4; + _int_map["vc_buffer_pool"] = 0; + + _int_map["wait_for_tail_credit"] = 1; // reallocate a VC before a tail credit? + + _int_map["hold_switch_for_packet"] = 0; // hold a switch config for the entire packet + + _int_map["input_speedup"] = 1; // expansion of input ports into crossbar + _int_map["output_speedup"] = 1; // expansion of output ports into crossbar + + _int_map["routing_delay"] = 0; + _int_map["vc_alloc_delay"] = 0; + _int_map["sw_alloc_delay"] = 0; + _int_map["st_prepare_delay"] = 0; + _int_map["st_final_delay"] = 0; + + //==== Event-driven ===================================== + + _int_map["vct"] = 0; + + //==== Allocators ======================================== + + AddStrField( "vc_allocator", "max_size" ); + AddStrField( "sw_allocator", "max_size" ); + + _int_map["alloc_iters"] = 1; + + //==== Traffic ======================================== + + AddStrField( "traffic", "uniform" ); + + _int_map["perm_seed"] = 0; // seed value for random perms + + _float_map["injection_rate"] = 0.2; + _int_map["const_flits_per_packet"] = 1; + + AddStrField( "injection_process", "bernoulli" ); + + _float_map["burst_alpha"] = 0.5; // burst interval + _float_map["burst_beta"] = 0.5; // burst length + + AddStrField( "priority", "age" ); // message priorities + + //==== Simulation parameters ========================== + + // types: + // latency - average + latency distribution for a particular injection rate + // throughput - sustained throughput for a particular injection rate + + AddStrField( "sim_type", "latency" ); + + _int_map["warmup_periods"] = 0; // number of samples periods to "warm-up" the simulation + + _int_map["sample_period"] = 1000; // how long between measurements + _int_map["max_samples"] = 10; // maximum number of sample periods in a simulation + + _float_map["latency_thres"] = 1000.0; // if avg. latency exceeds the threshold, assume unstable + + _int_map["sim_count"] = 1; // number of simulations to perform + + _int_map["auto_periods"] = 1; // non-zero for the simulator to automatically + // control the length of warm-up and the + // total length of the simulation + + _int_map["include_queuing"] = 1; // non-zero includes source queuing latency + + _int_map["reorder"] = 0; + + _int_map["flit_timing"] = 0; // know what you're doing + _int_map["split_packets"] = 0; + + _int_map["seed"] = 0; +} diff --git a/src/intersim/booksim_config.hpp b/src/intersim/booksim_config.hpp new file mode 100644 index 0000000..0e86fab --- /dev/null +++ b/src/intersim/booksim_config.hpp @@ -0,0 +1,11 @@ +#ifndef _BOOKSIM_CONFIG_HPP_ +#define _BOOKSIM_CONFIG_HPP_ + +#include "config_utils.hpp" + +class BookSimConfig : public Configuration { +public: + BookSimConfig( ); +}; + +#endif diff --git a/src/intersim/buffer_state.cpp b/src/intersim/buffer_state.cpp new file mode 100644 index 0000000..72a327d --- /dev/null +++ b/src/intersim/buffer_state.cpp @@ -0,0 +1,145 @@ +#include "booksim.hpp" +#include +#include +#include + +#include "buffer_state.hpp" +#include "random_utils.hpp" + +void BufferState::init( const Configuration& config ) +{ + _Init( config ); +} + +BufferState::BufferState( const Configuration& config, + Module *parent, const string& name ) : +Module( parent, name ) +{ + _Init( config ); +} + +void BufferState::_Init( const Configuration& config ) +{ + _buf_size = config.GetInt( "vc_buf_size" ); + _vcs = config.GetInt( "num_vcs" ); + + _wait_for_tail_credit = config.GetInt( "wait_for_tail_credit" ); + + _in_use = new bool [_vcs]; + _tail_sent = new bool [_vcs]; + _cur_occupied = new int [_vcs]; + + _last_avail = 0; + + for ( int v = 0; v < _vcs; ++v ) { + _in_use[v] = false; + _tail_sent[v] = false; + _cur_occupied[v] = 0; + } +} + +BufferState::~BufferState( ) +{ + delete [] _in_use; + delete [] _tail_sent; + delete [] _cur_occupied; +} + +void BufferState::ProcessCredit( Credit *c ) +{ + assert( c ); + + for ( int v = 0; v < c->vc_cnt; ++v ) { + assert( ( c->vc[v] >= 0 ) && ( c->vc[v] < _vcs ) ); + + if ( ( _wait_for_tail_credit ) && + ( !_in_use[c->vc[v]] ) ) { + Error( "Received credit for idle buffer" ); + } + + if ( _cur_occupied[c->vc[v]] > 0 ) { + --_cur_occupied[c->vc[v]]; + + if ( ( _cur_occupied[c->vc[v]] == 0 ) && + ( _tail_sent[c->vc[v]] ) ) { + _in_use[c->vc[v]] = false; + } + } else { + cout << "VC = " << c->vc[v] << endl; + Error( "Buffer occupancy fell below zero" ); + } + } +} + +void BufferState::SendingFlit( Flit *f ) +{ + assert( f && ( f->vc >= 0 ) && ( f->vc < _vcs ) ); + + if ( _cur_occupied[f->vc] < _buf_size ) { + ++_cur_occupied[f->vc]; + + if ( f->tail ) { + _tail_sent[f->vc] = true; + + if ( !_wait_for_tail_credit ) { + _in_use[f->vc] = false; + } + } + } else { + Error( "Flit sent to full buffer" ); + } +} + +void BufferState::TakeBuffer( int vc ) +{ + assert( ( vc >= 0 ) && ( vc < _vcs ) ); + + if ( _in_use[vc] ) { + Error( "Buffer taken while in use" ); + } + + _in_use[vc] = true; + _tail_sent[vc] = false; +} + +bool BufferState::IsFullFor( int vc ) const +{ + assert( ( vc >= 0 ) && ( vc < _vcs ) ); + return( _cur_occupied[vc] == _buf_size ) ? true : false; +} + +bool BufferState::IsAvailableFor( int vc ) const +{ + assert( ( vc >= 0 ) && ( vc < _vcs ) ); + return !_in_use[vc]; +} + +int BufferState::FindAvailable( ) +{ + int available_vc = -1; + int vc; + + _last_avail = RandomInt( _vcs - 1 ); + + for ( int v = 0; v < _vcs; ++v ) { + vc = ( v + _last_avail + 1 ) % _vcs; // Round-robin + + if ( IsAvailableFor( vc ) ) { + available_vc = vc; + _last_avail = vc; + break; + } + } + + return available_vc; +} + +void BufferState::Display( ) const +{ + cout << _fullname << " :" << endl; + for ( int v = 0; v < _vcs; ++v ) { + cout << " buffer class " << v << endl; + cout << " in_use = " << _in_use[v] << " tail_sent = " << _tail_sent[v] << endl; + cout << " occupied = " << _cur_occupied[v] << endl; + } +} diff --git a/src/intersim/buffer_state.hpp b/src/intersim/buffer_state.hpp new file mode 100644 index 0000000..8418f02 --- /dev/null +++ b/src/intersim/buffer_state.hpp @@ -0,0 +1,43 @@ +#ifndef _BUFFER_STATE_HPP_ +#define _BUFFER_STATE_HPP_ + +#include "module.hpp" +#include "flit.hpp" +#include "credit.hpp" +#include "config_utils.hpp" + +class BufferState : public Module { + + int _wait_for_tail_credit; + int _buf_size; + int _vcs; + + int _last_avail; + + bool *_in_use; + bool *_tail_sent; + int *_cur_occupied; + + void _Init( const Configuration& config ); + +public: + BufferState() : Module( ) {} + void init( const Configuration& config ); + BufferState( const Configuration& config, + Module *parent, const string& name ); + ~BufferState( ); + + void ProcessCredit( Credit *c ); + void SendingFlit( Flit *f ); + + void TakeBuffer( int vc = 0 ); + + bool IsFullFor( int vc = 0 ) const; + bool IsAvailableFor( int vc = 0 ) const; + + int FindAvailable( ); + + void Display( ) const; +}; + +#endif diff --git a/src/intersim/config.l b/src/intersim/config.l new file mode 100644 index 0000000..a39b2b1 --- /dev/null +++ b/src/intersim/config.l @@ -0,0 +1,51 @@ + +%{ + +#include "booksim.hpp" +#include +#include +#include + +#include "config_tab.hpp" +#include "config_utils.hpp" + +void configerror(string msg); +extern "C" int configwrap() { return 1; } + +extern int config_input(char *, int); +#undef YY_INPUT +#define YY_INPUT(b, r, ms) (r = config_input(b, ms)) + +int configlineno = 1; + +%} + +%option nounput + +%% + + /* Ignore comments and all spaces */ + +\/\/[^\n]* ; +[ \t\r]* ; + +\n { configlineno++; } + + /* Commands */ + +[A-Za-z_][A-Za-z0-9_]* { configlval.name = strdup( yytext ); return STR; } + +[0-9]+ { configlval.num = strtoul( yytext, 0, 10 ); return NUM; } + +[0-9]+\.[0-9]+ { configlval.fnum = strtod( yytext, 0 ); return FNUM; } + +. { return yytext[0]; } + +%% + +void configerror( string msg ) +{ + Configuration::GetTheConfig( )->ParseError( msg, configlineno ); +} + + diff --git a/src/intersim/config.y b/src/intersim/config.y new file mode 100644 index 0000000..d2388c9 --- /dev/null +++ b/src/intersim/config.y @@ -0,0 +1,33 @@ +%{ + +#include "booksim.hpp" +#include +#include "config_utils.hpp" + +int configlex(void); +void configerror(string msg); + +%} + +%union { + char *name; + unsigned int num; + double fnum; +} + +%token STR +%token NUM +%token FNUM + +%% + +commands : commands command + | command +; + +command : STR '=' STR ';' { Configuration::GetTheConfig()->Assign( $1, $3 ); free( $1 ); free( $3 ); } + | STR '=' NUM ';' { Configuration::GetTheConfig()->Assign( $1, $3 ); free( $1 ); } + | STR '=' FNUM ';' { Configuration::GetTheConfig()->Assign( $1, $3 ); free( $1 ); } +; + +%% diff --git a/src/intersim/config_utils.cpp b/src/intersim/config_utils.cpp new file mode 100644 index 0000000..a62ea05 --- /dev/null +++ b/src/intersim/config_utils.cpp @@ -0,0 +1,176 @@ +#include "booksim.hpp" +#include +#include +#include + +#include "config_utils.hpp" + +Configuration *Configuration::theConfig = 0; + +Configuration::Configuration( ) +{ + theConfig = this; + _config_file = 0; +} + +void Configuration::AddStrField( const string &field, const string &value ) +{ + _str_map[field] = strdup( value.c_str( ) ); +} + +void Configuration::Assign( const string &field, const string &value ) +{ + map::const_iterator match; + + match = _str_map.find( field ); + if ( match != _str_map.end( ) ) { + free( _str_map[field] ); + _str_map[field] = strdup( value.c_str( ) ); + } else { + string errmsg = "Unknown field "; + errmsg += field; + + ParseError( errmsg, 0 ); + } +} + +void Configuration::Assign( const string &field, unsigned int value ) +{ + map::const_iterator match; + + match = _int_map.find( field ); + if ( match != _int_map.end( ) ) { + _int_map[field] = value; + } else { + string errmsg = "Unknown field "; + errmsg += field; + + ParseError( errmsg, 0 ); + } +} + +void Configuration::Assign( const string &field, double value ) +{ + map::const_iterator match; + + match = _float_map.find( field ); + if ( match != _float_map.end( ) ) { + _float_map[field] = value; + } else { + string errmsg = "Unknown field "; + errmsg += field; + + ParseError( errmsg, 0 ); + } +} + +void Configuration::GetStr( const string &field, string &value, const string &def ) const +{ + map::const_iterator match; + + match = _str_map.find( field ); + if ( match != _str_map.end( ) ) { + value = match->second; + } else { + value = def; + } +} + +unsigned int Configuration::GetInt( const string &field, unsigned int def ) const +{ + map::const_iterator match; + unsigned int r = def; + + match = _int_map.find( field ); + if ( match != _int_map.end( ) ) { + r = match->second; + } + + return r; +} + +double Configuration::GetFloat( const string &field, double def ) const +{ + map::const_iterator match; + double r = def; + + match = _float_map.find( field ); + if ( match != _float_map.end( ) ) { + r = match->second; + } + + return r; +} + +void Configuration::Parse( const string& filename ) +{ + if ( ( _config_file = fopen( filename.c_str( ), "r" ) ) == 0 ) { + cerr << "Could not open configuration file " << filename << endl; + exit( -1 ); + } + + configparse( ); + + fclose( _config_file ); + _config_file = 0; +} + +void Configuration::Parse( const char* filename ) +{ + if ( ( _config_file = fopen( filename , "r" ) ) == 0 ) { + cerr << "Could not open configuration file " << filename << endl; + exit( -1 ); + } + + configparse( ); + + fclose( _config_file ); + _config_file = 0; +} + + +int Configuration::Input( char *line, int max_size ) +{ + int length = 0; + + if ( _config_file ) { + length = fread( line, 1, max_size, _config_file ); + } + + return length; +} + +void Configuration::ParseError( const string &msg, unsigned int lineno ) const +{ + if ( lineno ) { + cerr << "Parse error on line " << lineno << " : " << msg << endl; + } else { + cerr << "Parse error : " << msg << endl; + } + + exit( -1 ); +} + +Configuration *Configuration::GetTheConfig( ) +{ + return theConfig; +} + +//============================================================ + +int config_input( char *line, int max_size ) +{ + return Configuration::GetTheConfig( )->Input( line, max_size ); +} + +bool ParseArgs( Configuration *cf, int argc, char **argv ) +{ + bool rc = false; + + if ( argc > 1 ) { + cf->Parse( argv[1] ); + rc = true; + } + + return rc; +} diff --git a/src/intersim/config_utils.hpp b/src/intersim/config_utils.hpp new file mode 100644 index 0000000..86cc98e --- /dev/null +++ b/src/intersim/config_utils.hpp @@ -0,0 +1,45 @@ +#ifndef _CONFIG_UTILS_HPP_ +#define _CONFIG_UTILS_HPP_ + +#include +#include +#include + +extern int configparse( ); + +class Configuration { + static Configuration *theConfig; + FILE *_config_file; + +protected: + map _str_map; + map _int_map; + map _float_map; + +public: + Configuration( ); + + void AddStrField( const string &field, const string &value ); + + void Assign( const string &field, const string &value ); + void Assign( const string &field, unsigned int value ); + void Assign( const string &field, double value ); + + void GetStr( const string &field, string &value, const string &def = "" ) const; + unsigned int GetInt( const string &field, unsigned int def = 0 ) const; + double GetFloat( const string &field, double def = 0.0 ) const; + + void Parse( const string& filename ); + void Parse( const char* filename ); + + int Input( char *line, int max_size ); + void ParseError( const string &msg, unsigned int lineno ) const; + + static Configuration *GetTheConfig( ); +}; + +bool ParseArgs( Configuration *cf, int argc, char **argv ); + +#endif + + diff --git a/src/intersim/credit.cpp b/src/intersim/credit.cpp new file mode 100644 index 0000000..5090943 --- /dev/null +++ b/src/intersim/credit.cpp @@ -0,0 +1,16 @@ +#include "booksim.hpp" +#include "credit.hpp" + +Credit::Credit( int max_vcs ) +{ + vc = new int [max_vcs]; + vc_cnt = 0; + + tail = false; + id = -1; +} + +Credit::~Credit( ) +{ + delete [] vc; +} diff --git a/src/intersim/credit.hpp b/src/intersim/credit.hpp new file mode 100644 index 0000000..260e0d7 --- /dev/null +++ b/src/intersim/credit.hpp @@ -0,0 +1,15 @@ +#ifndef _CREDIT_HPP_ +#define _CREDIT_HPP_ + +class Credit { +public: + Credit( int max_vcs = 1 ); + ~Credit( ); + + int *vc; + int vc_cnt; + bool head, tail; + int id; +}; + +#endif diff --git a/src/intersim/doc/fancyhdr.sty b/src/intersim/doc/fancyhdr.sty new file mode 100644 index 0000000..8e12237 --- /dev/null +++ b/src/intersim/doc/fancyhdr.sty @@ -0,0 +1,329 @@ +% fancyhdr.sty version 1.99d +% Fancy headers and footers for LaTeX. +% Piet van Oostrum, Dept of Computer Science, University of Utrecht +% Padualaan 14, P.O. Box 80.089, 3508 TB Utrecht, The Netherlands +% Telephone: +31 30 2532180. Email: piet@cs.ruu.nl +% ======================================================================== +% LICENCE: This is free software. You are allowed to use and distribute +% this software in any way you like. You are also allowed to make modified +% versions of it, but you can distribute a modified version only if you +% clearly indicate that it is a modified version and the person(s) who +% modified it. This indication should be in a prominent place, e.g. in the +% top of the file. If possible a contact address, preferably by email, +% should be given for these persons. If that is feasible the modifications +% should be indicated in the source code. +% ======================================================================== +% MODIFICATION HISTORY: +% Sep 16, 1994 +% version 1.4: Correction for use with \reversemargin +% Sep 29, 1994: +% version 1.5: Added the \iftopfloat, \ifbotfloat and \iffloatpage commands +% Oct 4, 1994: +% version 1.6: Reset single spacing in headers/footers for use with +% setspace.sty or doublespace.sty +% Oct 4, 1994: +% version 1.7: changed \let\@mkboth\markboth to +% \def\@mkboth{\protect\markboth} to make it more robust +% Dec 5, 1994: +% version 1.8: corrections for amsbook/amsart: define \@chapapp and (more +% importantly) use the \chapter/sectionmark definitions from ps@headings if +% they exist (which should be true for all standard classes). +% May 31, 1995: +% version 1.9: The proposed \renewcommand{\headrulewidth}{\iffloatpage... +% construction in the doc did not work properly with the fancyplain style. +% June 1, 1995: +% version 1.91: The definition of \@mkboth wasn't restored on subsequent +% \pagestyle{fancy}'s. +% June 1, 1995: +% version 1.92: The sequence \pagestyle{fancyplain} \pagestyle{plain} +% \pagestyle{fancy} would erroneously select the plain version. +% June 1, 1995: +% version 1.93: \fancypagestyle command added. +% Dec 11, 1995: +% version 1.94: suggested by Conrad Hughes +% CJCH, Dec 11, 1995: added \footruleskip to allow control over footrule +% position (old hardcoded value of .3\normalbaselineskip is far too high +% when used with very small footer fonts). +% Jan 31, 1996: +% version 1.95: call \@normalsize in the reset code if that is defined, +% otherwise \normalsize. +% this is to solve a problem with ucthesis.cls, as this doesn't +% define \@currsize. Unfortunately for latex209 calling \normalsize doesn't +% work as this is optimized to do very little, so there \@normalsize should +% be called. Hopefully this code works for all versions of LaTeX known to +% mankind. +% April 25, 1996: +% version 1.96: initialize \headwidth to a magic (negative) value to catch +% most common cases that people change it before calling \pagestyle{fancy}. +% Note it can't be initialized when reading in this file, because +% \textwidth could be changed afterwards. This is quite probable. +% We also switch to \MakeUppercase rather than \uppercase and introduce a +% \nouppercase command for use in headers. and footers. +% May 3, 1996: +% version 1.97: Two changes: +% 1. Undo the change in version 1.8 (using the pagestyle{headings} defaults +% for the chapter and section marks. The current version of amsbook and +% amsart classes don't seem to need them anymore. Moreover the standard +% latex classes don't use \markboth if twoside isn't selected, and this is +% confusing as \leftmark doesn't work as expected. +% 2. include a call to \ps@empty in ps@@fancy. This is to solve a problem +% in the amsbook and amsart classes, that make global changes to \topskip, +% which are reset in \ps@empty. Hopefully this doesn't break other things. +% May 7, 1996: +% version 1.98: +% Added % after the line \def\nouppercase +% May 7, 1996: +% version 1.99: This is the alpha version of fancyhdr 2.0 +% Introduced the new commands \fancyhead, \fancyfoot, and \fancyhf. +% Changed \headrulewidth, \footrulewidth, \footruleskip to +% macros rather than length parameters, In this way they can be +% conditionalized and they don't consume length registers. There is no need +% to have them as length registers unless you want to do calculations with +% them, which is unlikely. Note that this may make some uses of them +% incompatible (i.e. if you have a file that uses \setlength or \xxxx=) +% May 10, 1996: +% version 1.99a: +% Added a few more % signs +% May 10, 1996: +% version 1.99b: +% Changed the syntax of \f@nfor to be resistent to catcode changes of := +% Removed the [1] from the defs of \lhead etc. because the parameter is +% consumed by the \@[xy]lhead etc. macros. +% June 24, 1997: +% version 1.99c: +% corrected \nouppercase to also include the protected form of \MakeUppercase +% \global added to manipulation of \headwidth. +% \iffootnote command added. +% Some comments added about \@fancyhead and \@fancyfoot. +% Aug 24, 1998 +% version 1.99d +% Changed the default \ps@empty to \ps@@empty in order to allow +% \fancypagestyle{empty} redefinition. + +\let\fancy@def\gdef + +\def\if@mpty#1#2#3{\def\temp@ty{#1}\ifx\@empty\temp@ty #2\else#3\fi} + +% Usage: \@forc \var{charstring}{command to be executed for each char} +% This is similar to LaTeX's \@tfor, but expands the charstring. + +\def\@forc#1#2#3{\expandafter\f@rc\expandafter#1\expandafter{#2}{#3}} +\def\f@rc#1#2#3{\def\temp@ty{#2}\ifx\@empty\temp@ty\else + \f@@rc#1#2\f@@rc{#3}\fi} +\def\f@@rc#1#2#3\f@@rc#4{\def#1{#2}#4\f@rc#1{#3}{#4}} + +% Usage: \f@nfor\name:=list\do{body} +% Like LaTeX's \@for but an empty list is treated as a list with an empty +% element + +\newcommand{\f@nfor}[3]{\edef\@fortmp{#2}% + \expandafter\@forloop#2,\@nil,\@nil\@@#1{#3}} + +% Usage: \def@ult \cs{defaults}{argument} +% sets \cs to the characters from defaults appearing in argument +% or defaults if it would be empty. All characters are lowercased. + +\newcommand\def@ult[3]{% + \edef\temp@a{\lowercase{\edef\noexpand\temp@a{#3}}}\temp@a + \def#1{}% + \@forc\tmpf@ra{#2}% + {\expandafter\if@in\tmpf@ra\temp@a{\edef#1{#1\tmpf@ra}}{}}% + \ifx\@empty#1\def#1{#2}\fi} +% +% \if@in +% +\newcommand{\if@in}[4]{% + \edef\temp@a{#2}\def\temp@b##1#1##2\temp@b{\def\temp@b{##1}}% + \expandafter\temp@b#2#1\temp@b\ifx\temp@a\temp@b #4\else #3\fi} + +\newcommand{\fancyhead}{\@ifnextchar[{\f@ncyhf h}{\f@ncyhf h[]}} +\newcommand{\fancyfoot}{\@ifnextchar[{\f@ncyhf f}{\f@ncyhf f[]}} +\newcommand{\fancyhf}{\@ifnextchar[{\f@ncyhf {}}{\f@ncyhf {}[]}} + +% The header and footer fields are stored in command sequences with +% names of the form: \f@ncy with for [eo], form [lcr] +% and from [hf]. + +\def\f@ncyhf#1[#2]#3{% + \def\temp@c{}% + \@forc\tmpf@ra{#2}% + {\expandafter\if@in\tmpf@ra{eolcrhf,EOLCRHF}% + {}{\edef\temp@c{\temp@c\tmpf@ra}}}% + \ifx\@empty\temp@c\else + \ifx\PackageError\undefined + \errmessage{Illegal char `\temp@c' in fancyhdr argument: + [#2]}\else + \PackageError{Fancyhdr}{Illegal char `\temp@c' in fancyhdr argument: + [#2]}{}\fi + \fi + \f@nfor\temp@c{#2}% + {\def@ult\f@@@eo{eo}\temp@c + \def@ult\f@@@lcr{lcr}\temp@c + \def@ult\f@@@hf{hf}{#1\temp@c}% + \@forc\f@@eo\f@@@eo + {\@forc\f@@lcr\f@@@lcr + {\@forc\f@@hf\f@@@hf + {\expandafter\fancy@def\csname + f@ncy\f@@eo\f@@lcr\f@@hf\endcsname + {#3}}}}}} + +% Fancyheadings version 1 commands. These are more or less deprecated, +% but they continue to work. + +\newcommand{\lhead}{\@ifnextchar[{\@xlhead}{\@ylhead}} +\def\@xlhead[#1]#2{\fancy@def\f@ncyelh{#1}\fancy@def\f@ncyolh{#2}} +\def\@ylhead#1{\fancy@def\f@ncyelh{#1}\fancy@def\f@ncyolh{#1}} + +\newcommand{\chead}{\@ifnextchar[{\@xchead}{\@ychead}} +\def\@xchead[#1]#2{\fancy@def\f@ncyech{#1}\fancy@def\f@ncyoch{#2}} +\def\@ychead#1{\fancy@def\f@ncyech{#1}\fancy@def\f@ncyoch{#1}} + +\newcommand{\rhead}{\@ifnextchar[{\@xrhead}{\@yrhead}} +\def\@xrhead[#1]#2{\fancy@def\f@ncyerh{#1}\fancy@def\f@ncyorh{#2}} +\def\@yrhead#1{\fancy@def\f@ncyerh{#1}\fancy@def\f@ncyorh{#1}} + +\newcommand{\lfoot}{\@ifnextchar[{\@xlfoot}{\@ylfoot}} +\def\@xlfoot[#1]#2{\fancy@def\f@ncyelf{#1}\fancy@def\f@ncyolf{#2}} +\def\@ylfoot#1{\fancy@def\f@ncyelf{#1}\fancy@def\f@ncyolf{#1}} + +\newcommand{\cfoot}{\@ifnextchar[{\@xcfoot}{\@ycfoot}} +\def\@xcfoot[#1]#2{\fancy@def\f@ncyecf{#1}\fancy@def\f@ncyocf{#2}} +\def\@ycfoot#1{\fancy@def\f@ncyecf{#1}\fancy@def\f@ncyocf{#1}} + +\newcommand{\rfoot}{\@ifnextchar[{\@xrfoot}{\@yrfoot}} +\def\@xrfoot[#1]#2{\fancy@def\f@ncyerf{#1}\fancy@def\f@ncyorf{#2}} +\def\@yrfoot#1{\fancy@def\f@ncyerf{#1}\fancy@def\f@ncyorf{#1}} + +\newdimen\headwidth +\newcommand{\headrulewidth}{0.4pt} +\newcommand{\footrulewidth}{\z@skip} +\newcommand{\footruleskip}{.3\normalbaselineskip} + +% Fancyplain stuff shouldn't be used anymore (rather +% \fancypagestyle{plain} should be used), but it must be present for +% compatibility reasons. + +\newcommand{\plainheadrulewidth}{\z@skip} +\newcommand{\plainfootrulewidth}{\z@skip} +\newif\if@fancyplain \@fancyplainfalse +\def\fancyplain#1#2{\if@fancyplain#1\else#2\fi} + +\headwidth=-123456789sp %magic constant + +% Command to reset various things in the headers: +% a.o. single spacing (taken from setspace.sty) +% and the catcode of ^^M (so that epsf files in the header work if a +% verbatim crosses a page boundary) +% It also defines a \nouppercase command that disables \uppercase and +% \Makeuppercase. It can only be used in the headers and footers. +\def\fancy@reset{\restorecr + \def\baselinestretch{1}% + \def\nouppercase##1{{\let\uppercase\relax\let\MakeUppercase\relax + \expandafter\let\csname MakeUppercase \endcsname\relax##1}}% + \ifx\undefined\@newbaseline% NFSS not present; 2.09 or 2e + \ifx\@normalsize\undefined \normalsize % for ucthesis.cls + \else \@normalsize \fi + \else% NFSS (2.09) present + \@newbaseline% + \fi} + +% Initialization of the head and foot text. + +% The default values still contain \fancyplain for compatibility. +\fancyhf{} % clear all +% lefthead empty on ``plain'' pages, \rightmark on even, \leftmark on odd pages +% evenhead empty on ``plain'' pages, \leftmark on even, \rightmark on odd pages +\fancyhead[el,or]{\fancyplain{}{\sl\rightmark}} +\fancyhead[er,ol]{\fancyplain{}{\sl\leftmark}} +\fancyfoot[c]{\rm\thepage} % page number + +% Put together a header or footer given the left, center and +% right text, fillers at left and right and a rule. +% The \lap commands put the text into an hbox of zero size, +% so overlapping text does not generate an errormessage. +% These macros have 5 parameters: +% 1. \@lodd or \@rodd % This determines at which side the header will stick +% out. +% 2. \f@ncyolh, \f@ncyelh, \f@ncyolf or \f@ncyelf. This is the left component. +% 3. \f@ncyoch, \f@ncyech, \f@ncyocf or \f@ncyecf. This is the middle comp. +% 4. \f@ncyorh, \f@ncyerh, \f@ncyorf or \f@ncyerf. This is the right component. +% 5. \@lodd or \@rodd % This determines at which side the header will stick +% out. This is the reverse of parameter nr. 1. One of them is always +% \relax and the other one is \hss (after expansion). + +\def\@fancyhead#1#2#3#4#5{#1\hbox to\headwidth{\fancy@reset\vbox{\hbox +{\rlap{\parbox[b]{\headwidth}{\raggedright#2\strut}}\hfill +\parbox[b]{\headwidth}{\centering#3\strut}\hfill +\llap{\parbox[b]{\headwidth}{\raggedleft#4\strut}}}\headrule}}#5} + +\def\@fancyfoot#1#2#3#4#5{#1\hbox to\headwidth{\fancy@reset\vbox{\footrule +\hbox{\rlap{\parbox[t]{\headwidth}{\raggedright#2\strut}}\hfill +\parbox[t]{\headwidth}{\centering#3\strut}\hfill +\llap{\parbox[t]{\headwidth}{\raggedleft#4\strut}}}}}#5} + +\def\headrule{{\if@fancyplain\let\headrulewidth\plainheadrulewidth\fi +\hrule\@height\headrulewidth\@width\headwidth \vskip-\headrulewidth}} + +\def\footrule{{\if@fancyplain\let\footrulewidth\plainfootrulewidth\fi +\vskip-\footruleskip\vskip-\footrulewidth +\hrule\@width\headwidth\@height\footrulewidth\vskip\footruleskip}} + +\def\ps@fancy{% +\@ifundefined{@chapapp}{\let\@chapapp\chaptername}{}%for amsbook +% +% Define \MakeUppercase for old LaTeXen. +% Note: we used \def rather than \let, so that \let\uppercase\relax (from +% the version 1 documentation) will still work. +% +\@ifundefined{MakeUppercase}{\def\MakeUppercase{\uppercase}}{}% +\@ifundefined{chapter}{\def\sectionmark##1{\markboth +{\MakeUppercase{\ifnum \c@secnumdepth>\z@ + \thesection\hskip 1em\relax \fi ##1}}{}}% +\def\subsectionmark##1{\markright {\ifnum \c@secnumdepth >\@ne + \thesubsection\hskip 1em\relax \fi ##1}}}% +{\def\chaptermark##1{\markboth {\MakeUppercase{\ifnum \c@secnumdepth>\m@ne + \@chapapp\ \thechapter. \ \fi ##1}}{}}% +\def\sectionmark##1{\markright{\MakeUppercase{\ifnum \c@secnumdepth >\z@ + \thesection. \ \fi ##1}}}}% +%\csname ps@headings\endcsname % use \ps@headings defaults if they exist +\ps@@fancy +\gdef\ps@fancy{\@fancyplainfalse\ps@@fancy}% +% Initialize \headwidth if the user didn't +% +\ifdim\headwidth<0sp +% +% This catches the case that \headwidth hasn't been initialized and the +% case that the user added something to \headwidth in the expectation that +% it was initialized to \textwidth. We compensate this now. This loses if +% the user intended to multiply it by a factor. But that case is more +% likely done by saying something like \headwidth=1.2\textwidth. +% The doc says you have to change \headwidth after the first call to +% \pagestyle{fancy}. This code is just to catch the most common cases were +% that requirement is violated. +% + \global\advance\headwidth123456789sp\global\advance\headwidth\textwidth +\fi} +\def\ps@fancyplain{\ps@fancy \let\ps@plain\ps@plain@fancy} +\def\ps@plain@fancy{\@fancyplaintrue\ps@@fancy} +\let\ps@@empty\ps@empty +\def\ps@@fancy{% +\ps@@empty % This is for amsbook/amsart, which do strange things with \topskip +\def\@mkboth{\protect\markboth}% +\def\@oddhead{\@fancyhead\@lodd\f@ncyolh\f@ncyoch\f@ncyorh\@rodd}% +\def\@oddfoot{\@fancyfoot\@lodd\f@ncyolf\f@ncyocf\f@ncyorf\@rodd}% +\def\@evenhead{\@fancyhead\@rodd\f@ncyelh\f@ncyech\f@ncyerh\@lodd}% +\def\@evenfoot{\@fancyfoot\@rodd\f@ncyelf\f@ncyecf\f@ncyerf\@lodd}% +} +\def\@lodd{\if@reversemargin\hss\else\relax\fi} +\def\@rodd{\if@reversemargin\relax\else\hss\fi} + +\newif\iffootnote +\let\latex@makecol\@makecol +\def\@makecol{\ifvoid\footins\footnotetrue\else\footnotefalse\fi +\let\topfloat\@toplist\let\botfloat\@botlist\latex@makecol} +\def\iftopfloat#1#2{\ifx\topfloat\empty #2\else #1\fi} +\def\ifbotfloat#1#2{\ifx\botfloat\empty #2\else #1\fi} +\def\iffloatpage#1#2{\if@fcolmade #1\else #2\fi} + +\newcommand{\fancypagestyle}[2]{% + \@namedef{ps@#1}{\let\fancy@def\def#2\relax\ps@fancy}} diff --git a/src/intersim/doc/manual.pdf b/src/intersim/doc/manual.pdf new file mode 100644 index 0000000..c12aa4a Binary files /dev/null and b/src/intersim/doc/manual.pdf differ diff --git a/src/intersim/doc/manual.tex b/src/intersim/doc/manual.tex new file mode 100644 index 0000000..2ec6726 --- /dev/null +++ b/src/intersim/doc/manual.tex @@ -0,0 +1,687 @@ +\documentclass[11pt]{article} +\usepackage{fancyhdr} +\usepackage[dvips]{graphicx} +\usepackage{amsmath,amssymb} +\usepackage{epsfig} +\usepackage{calc} + +\newcommand{\simname}{BookSim~} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +% Setup the margin sizes. + +\evensidemargin = 0in +\oddsidemargin = 0in +\textwidth = 6.5in + +\topmargin = -0.5in +\textheight = 9in + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\author{Brian Towles and William J. Dally} +\title{\simname 1.0 User's Guide} + +\begin{document} + +\maketitle +\tableofcontents + +\pagestyle{fancy} +%\renewcommand{\chaptermark}[1]{\markboth{#1}{}} +\renewcommand{\sectionmark}[1]{\markright{\thesection\ #1}} +\fancyhf{} % delete current setting for header and footer +\fancyhead[LE,RO]{\bfseries\thepage} +\fancyhead[LO]{\bfseries\rightmark} +\fancyhead[RE]{\bfseries\leftmark} +\renewcommand{\headrulewidth}{0.5pt} +\renewcommand{\footrulewidth}{0.5pt} +\addtolength{\headheight}{0.5pt} % make space for the rule +\cfoot{\small\today} +\fancypagestyle{plain}{% + \fancyhf{} % get rid of headers on plain pages + \renewcommand{\headrulewidth}{0pt} % and the line + \renewcommand{\footrulewidth}{0pt} % and the line +} + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\newenvironment{opt_list}[1]{\begin{list}{}{\renewcommand{\makelabel}[1]% +{\texttt{##1}\hfil}\settowidth{\labelwidth}{\texttt{#1}}\setlength{\leftmargin}% +{\labelwidth+\labelsep}}}{\end{list}} + +\section{Introduction} + +This document describes the use of the \simname interconnection +network simulator. The simulator is designed as a companion to the +textbook ``Principles and Practices of Interconnection Networks'' +(PPIN) published by Morgan Kaufmann (ISBN: 0122007514) and it is +assumed that is reader is familiar with the material covered in that +text. + +This user guide is fairly brief as, with most simulators, the best way +to learn and {\it understand} the simulator is to study the code. +Most of the simulator's components are designed to be modular so tasks +such as adding a new routing algorithm, topology, or router +microarchitecture should not require a complete redesign of the code. +Once you have downloaded the code, compiled it, and run a simple +example (Section~\ref{sec:get_started}), the more detailed examples of +Section~\ref{sec:examples} give a good overview of the capabilities of +the simulator. A list of configuration options is provided in +Section~\ref{sec:config_params} for reference. + +\section{Getting started} +\label{sec:get_started} + +\subsection{Downloading and building the simulator} +\label{sec:download} + +The latest version of the simulator is available from +\texttt{http://cva.stanford.edu} as a compressed tar archive. UNIX/Linux +users can extract this archive using the tar utility +\begin{verbatim} + tar xvfz booksim-1.0.tar.gz +\end{verbatim} +Windows users can use a compression program such as WinZip to extract +the archive. + +The simulator itself is written in C++ and has been specifically +tested with GNU's G++ compiler (version $\ge3$). In addition, both a +LEX and YACC tool (also known as FLEX and BISON) are needed to create +the configuration parser. These are standard tools in any UNIX/Linux +development environment. It is suggested that Windows users download +the CYGWIN versions (\texttt{http://www.cygwin.com}) of these UNIX +development tools to simplify their compilation process. The +\texttt{Makefile} should be edited so that the first lines give the +paths to the tools. At Stanford, for example, the compiler, YACC, and +LEX are stored in the \texttt{/usr/pubsw/bin} directory. The +\texttt{Makefile} reflects this: +\begin{verbatim} +CPP = /usr/pubsw/bin/g++ +YACC = /usr/pubsw/bin/byacc -d +LEX = /usr/pubsw/bin/flex +\end{verbatim} +Then, the simulator can be compiled by running \texttt{make} in the +directory that contains the \texttt{Makefile}. + +\subsection{Running a simulation} +\label{sec:run_example} + +The syntax of the simulator is simply +\begin{verbatim} + booksim [configfile] +\end{verbatim} +The optional parameter \texttt{configfile} is a file that contains +configuration information for the simulator. So, for example, to +simulate the performance of a simple $8 \times 8$ torus (8-ary 2-cube) +network on uniform traffic, a configuration such as the one shown in +Figure~\ref{fig:config_example} could be used. This particular +configuration is stored in \texttt{examples/torus88}. + +\begin{figure} +\begin{verbatim} + // Topology + topology = torus; + k = 8; + n = 2; + + // Routing + routing_function = dim_order; + + // Flow control + num_vcs = 2; + + // Traffic + traffic = uniform; + injection_rate = 0.15; +\end{verbatim} +\caption{Example configuration file for simulating a 8-ary 2-cube +network.} +\label{fig:config_example} +\end{figure} + +In addition to specifying the topology, the configuration file also +contains basic information about the routing algorithm, flow control, +and traffic. This simple example uses dimension-order routing and, to +ensure deadlock-freedom of this routing function in the torus, two +virtual channels are required. The \texttt{injection\_rate} parameter +is added to tell the simulator to inject 0.15 flits per simulation +cycle per node. Because the simulator operates at the flit level, +most parameters are specified in units of flits as is the case with +the \texttt{injection\_rate}. Also, any line of the configuration +that begins with \texttt{//} is treated as a comment and ignored by +the simulator. A detailed list of configuration parameters is given in +Section~\ref{sec:config_params}. + +\subsection{Simulation output} + +Continuing our example, running the torus simulation produces the +output shown in Figure~\ref{fig:sim_output}. Each simulation has +three basic phases: warm up, measurement, and drain. The length of +the warm up and measurement phases is a multiple of a basic sample +period (defined by \texttt{sample\_period} in the configuration). As +shown in the figure, the current latency and throughput (rate of +accepted packets) for the simulation is printed after each sample +period. The overall throughput is determined by the lowest throughput +of all the destination in the network, but the average throughput is +also displayed. + +\begin{figure} +\begin{verbatim} +%================================= +% Average latency = 6.02008 +% Accepted packets = 0.11 at node 52 (avg = 0.147094) +% latency change = 1 +% throughput change = 1 + +... + +% Warmed up ... +%================================= +% Average latency = 6.0796 +% Accepted packets = 0.119 at node 5 (avg = 0.148266) +% latency change = 0.00562457 +% throughput change = 0.00379387 + +... + +% Draining all recorded packets ... +% Draining remaining packets ... +====== Traffic class 0 ====== +Overall average latency = 6.09083 (1 samples) +Overall average accepted rate = 0.149475 (1 samples) +Overall min accepted rate = 0.138551 (1 samples) +\end{verbatim} +\caption{Simulator output from running the \texttt{examples/torus88} +configuration file.} +\label{fig:sim_output} +\end{figure} + +After the warm up periods have passed, the simulator prints the +``\texttt{Warmed up}'' message and resets all the simulation statistics. +Then, the measurement phase begins and statistics continue to be +reported after each sample period. Once the measurement periods have +passed, all the measurement packets are drained from the network +before final latency and throughput numbers are reported. Details of +the configuration parameters used to control the length of the +simulation phases are covered in Section~\ref{sec:sim_params}. + +\section{Examples} +\label{sec:examples} + +One of the most basic performance measures of any interconnection +network is its latency versus offered load. +Figure~\ref{fig:lat_vs_load} shows a simple configuration file for +making this measurement in a 8-ary 2-mesh network under the transpose +traffic pattern. This configuration was used to generate Figure 25.2 +in PPIN. The particular configuration accounts for some small delays +and pipelining of the input-queued router and also introduces a small +input speedup to account for any inefficiencies in allocation. By +running simulations for many increments of \texttt{injection\_rate}, +the average latency curve can be found. Then, to compare the +performance of dimension-order routing against several other routing +algorithms, for example, the \texttt{routing\_function} option can be +changed. + +\begin{figure} +\begin{verbatim} +// Topology + +topology = mesh; +k = 8; +n = 2; + +// Routing + +routing_function = dim_order; + +// Flow control + +num_vcs = 8; +vc_buf_size = 8; + +wait_for_tail_credit = 1; + +// Router architecture + +vc_allocator = islip; +sw_allocator = islip; +alloc_iters = 1; + +credit_delay = 2; +routing_delay = 1; +vc_alloc_delay = 1; + +input_speedup = 2; +output_speedup = 1; +internal_speedup = 1.0; + +// Traffic + +traffic = transpose; +const_flits_per_packet = 20; + +// Simulation + +sim_type = latency; +injection_rate = 0.1; +\end{verbatim} +\caption{A typical configuration file (\texttt{examples/mesh88\_lat}) +for creating a latency versus offered load curve for a 8-ary 2-mesh +network.} +\label{fig:lat_vs_load} +\end{figure} + +Figure~\ref{fig:fly_dist} shows a configuration file that can be used +to determine the distribution of packet latencies in a 2-ary 6-fly +network that uses age-based arbitration. Note the use of the +\texttt{priority} configuration parameter along with the +\texttt{select} allocators that account for packet priorities. The +simulator does not output latency distributions by default, but by +editing \texttt{trafficmanager.cpp}, setting the configuration +variable \texttt{DISPLAY\_LAT\_DIST} to true, and recompiling, the +distribution will be displayed at the end of the simulation. This +technique was used to produced the distribution shown in Figure 25.12 +of PPIN. + +\begin{figure} +\begin{verbatim} +// Topology + +topology = fly; +k = 2; +n = 6; + +// Routing + +routing_function = dest_tag; + +// Flow control + +num_vcs = 8; +vc_buf_size = 8; + +wait_for_tail_credit = 1; + +// Router architecture + +vc_allocator = select; +sw_allocator = select; +alloc_iters = 1; + +credit_delay = 2; +routing_delay = 1; +vc_alloc_delay = 1; + +input_speedup = 2; +output_speedup = 1; +internal_speedup = 1.0; + +// Traffic + +traffic = uniform; +const_flits_per_packet = 20; +priority = age; + +// Simulation + +sim_type = latency; +injection_rate = 0.1; +\end{verbatim} +\caption{A configuration file (\texttt{examples/fly26\_age}) for +finding the distribution of packet latencies using age-based +arbitration.} +\label{fig:fly_dist} +\end{figure} + +As a final example, Figure~\ref{fig:single} shows the use of the +special single-node topology to test the performance of a switch +allocator --- in this case, the iSLIP allocator. The +\texttt{in\_ports} and \texttt{out\_ports} options set up a simulation +of an $8\times 8$ crossbar. + +\begin{figure} +\begin{verbatim} +// Topology + +topology = single; +in_ports = 8; +out_ports = 8; + +// Routing + +routing_function = single; + +// Flow control + +vc_allocator = islip; +sw_allocator = islip; +alloc_iters = 2; + +num_vcs = 8; +vc_buf_size = 1000; + +wait_for_tail_credit = 0; + +// Simulation + +sim_type = latency; +injection_rate = 0.1; +\end{verbatim} +\caption{A single-node configuration file (\texttt{examples/single}) +for testing the performance of a switch allocator.} +\label{fig:single} +\end{figure} + +\section{Configuration parameters} +\label{sec:config_params} + +All information used to configure a simulation is passed through a +configuration file as illustrated by the example in +Section~\ref{sec:run_example}. This section lists the existing +configuration parameters --- a user can incorporate additional options +by changing the \texttt{booksim\_config.cpp} file. + +\subsection{Topologies} +\label{sec:topos} + +The \texttt{topology} parameter determines the underlying topology of the +network and the simulator supports four basic topologies: +\begin{opt_list}{single} +\item[fly] A $k$-ary $n$-fly (butterfly) topology. The \texttt{k} +parameter determines the network's radix and the \texttt{n} parameter +determines the network's dimension. + +\item[mesh] A $k$-ary $n$-mesh (mesh) topology. The \texttt{k} +parameter determines the network's radix and the \texttt{n} parameter determines +the network's dimension. + +\item[single] A network with a single node, used for testing single +router performance. The number of input and output ports for the node +is determined by the \texttt{in\_ports} and \texttt{out\_ports} parameters, +respectively. + +\item[torus] A $k$-ary $n$-cube (torus) topology. The \texttt{k} +parameter determines the network's radix and the \texttt{n} parameter determines +the network's dimension. +\end{opt_list} + +Both the \texttt{mesh} and \texttt{torus} topologies support the +addition of random link failures with the \texttt{link\_failures} +parameter. The value of \texttt{link\_failures} determines the number +of channels that are randomly removed from the topology and are thus +no longer available for forwarding packets. Moreover, the +randomization for failed channels is controlled by selecting an +integer value for the \texttt{fail\_seed} parameter --- a fixed seed +gives a fixed set of failed channels, independent of other +randomization in the simulation. Also, note that only certain routing +functions support this feature (see Section~\ref{sec:routing_algs}). + +\subsection{Routing algorithms} +\label{sec:routing_algs} + +The \texttt{routing\_function} parameter selects a routing algorithm +for the topology. Many routing algorithms need multiple virtual +channels for deadlock freedom (VCDF). + +\begin{opt_list}{dim\_order\_bal} + +\item[dim\_order] Dimension-order routing. Works for the +\texttt{mesh} topology (1 VCDF) and for the \texttt{torus} topology (2 +VCDF). + +\item[dim\_order\_bal] Dimension-order routing for the +\texttt{torus} topology with a more balanced use of VCs to +avoid deadlock (2 VCDF). + +\item[dim\_order\_ni] A non-interfering version of +dimension-order routing. Works on the \texttt{torus} or \texttt{mesh} +topology and requires one VC per network terminal. + +\item[min\_adapt] A minimal adaptive routing algorithm for +the \texttt{mesh} topology (2 VCDF) and for the \texttt{torus} +topology (3 VCDF). + +\item[planar\_adapt] Planar-adaptive routing for the +\texttt{mesh} topology (2 VCDF). Supports routing around failed channels. + +\item[romm] ROMM routing for the \texttt{mesh} (2 VCDF). +Load is balanced by routing in two phases: one from the source to a +random intermediate node in the minimal quadrant and a second from the +intermediate to the destination. + +\item[romm\_ni] A non-interfering version of ROMM routing for +the \texttt{mesh} that requires one VC per network terminal. + +\item[single] A dummy routing function used for the +\texttt{single} topology. + +\item[valiant] Valiant's randomized routing algorithm for the +\texttt{mesh} (2 VCDF) and \texttt{torus} (4 VCDF) topology. + +\item[valiant\_ni] A non-interfering version of Valiant's algorithm +for the \texttt{torus} that requires 4 VCs per network terminal. + +\end{opt_list} + +Also, the simulator code is structured so that additional routing +algorithms can be added with minimal changes to the overall simulator +(see the \texttt{routefunc.cpp} file in the simulator's source code). + +\subsection{Flow control} + +The simulator supports basic virtual-channel flow control with +credit-based backpressure. + +\begin{opt_list}{wait\_for\_tail\_credit} + +\item[num\_vcs] The number of virtual channels per physical channel. + +\item[vc\_buf\_size] The depth of each virtual in flits. + +\item[voq] If non-zero, use virtual-output queuing. With virtual +output queuing, a separate virtual channel is assigned to each +destination in the network. This option is most useful when used with +a non-interfering routing algorithm (Section~\ref{sec:routing_algs}). + +\item[wait\_for\_tail\_credit] If non-zero, do not reallocate a virtual +channel until the tail flit has left that virtual channel. This +conservative approach prevents a dependency from being formed between +two packets sharing the same virtual channel in succession. +\end{opt_list} + +\subsection{Router organizations} + +The simulator also supports two different router microarchitectures. +The input-queued router follows the general organization described in +PPIN while the event-driven router is modeled after the router used in +the Avici TSR and described in U.S. Patent 6,370,145. The +microarchitecture is selected using the \texttt{router} option. Also, +both routers share a small set of options. + +\begin{opt_list}{internal\_speedup} +\item[credit\_delay] The processing delay (in cycles) for a credit. +Does not include the wire delay for transmitting the credit. + +\item[internal\_speedup] An arbitrary speedup of the internals of the +routers over the channel transmission rate. For example, a speedup +1.5 means that, on average, 1.5 flits can be forwarded by the router +in the time required for a single flit to be transmitted across a +channel. Also, the configuration parser expects a floating point +number for this field, so integer speedups should also include a +decimal point (e.g. ``2.0''). + +\item[output\_delay] The processing delay incurred in the output queue +of a router. +\end{opt_list} + +\subsubsection{The input-queued router} +\label{sec:iq_router} + +The input-queued router (\texttt{router = iq}) follows the pipeline +described in PPIN of route computation, virtual-channel allocation, +switch allocation, and switch traversal. There are several options +specific to the input-queued router. + +\begin{opt_list}{st\_prepare\_delay} + +\item[input\_speedup] An integer speedup of the input ports in space. +A speedup of 2, for example, gives each input two input ports into the +crossbar. Access to these ports is statically allocated based on the +virtual channel number: virtual channel $v$ at input $i$ is connected +to port $i \cdot s + (v \mod s)$ for an input speedup of $s$. + +\item[output\_speedup] An integer speedup of the output ports in +space. Similar to \texttt{input\_speedup} + +\item[routing\_delay] The delay (in cycles) of route computation. + +\item[sw\_allocator] The type of allocator used for switch allocation. +See Section~\ref{sec:alloc} for a list of the possible allocators. + +\item[sw\_alloc\_delay] The delay (in cycles) of switch allocation. + +\item[vc\_allocator] The type of allocator used for virtual-channel +allocation. See Section~\ref{sec:alloc} for a list of the possible +allocators. + +\item[vc\_alloc\_delay] The delay (in cycles) of virtual-channel +allocation. + +\end{opt_list} + +\subsubsection{The event-driven router} +\label{sec:event_router} + +The event-driven router (\texttt{router = event}) is a +microarchitecture designed specifically to support a large number of +virtual channels (VCs) efficiently. Instead of continuously polling +the state of the virtual channels, as in the input-queued router, only +changes in VC state are tracked. The efficiency then comes from the +fact that the number of state changes per cycle is constant and +independent of the number of VCs. + +\subsection{Allocators} +\label{sec:alloc} + +Many of the allocators used in the simulator are configurable (see +the input-queued router in Section~\ref{sec:iq_router}) and several +allocation algorithms are available. +\begin{opt_list}{wavefront} + +\item[max\_size] Maximum-size matching. +\item[islip] iSLIP separable allocator. +\item[pim] Parallel iterative matching separable allocator. +\item[loa] Lonely output allocator. +\item[wavefront] Wavefront matching. +\item[select] Priority-based allocator. Allocation is performed as in +iSLIP, but with preference towards higher priority packets (see +\texttt{priority} option in Section~\ref{sec:traffic}). + +\end{opt_list} + +Allocation can also be improved by performing multiple iterations of +the algorithm and the number of iterations is controlled by the +\texttt{alloc\_iters} parameter. + +\subsection{Traffic} +\label{sec:traffic} + +The rate at which flits are injected into the simulator is set using +the \texttt{injection\_rate} option. The simulator's cycle time is a +flit cycle, the time it takes a single flit to be injected at a +source, and the injection rate is specified in flits per flit cycle. +For example, setting \texttt{injection\_rate = 0.25} means that each +source injects a new flit one of every four simulator cycles. The +injection process can also be specified as either Bernoulli +(\texttt{injection\_process = bernoulli}) or an on-off process +(\texttt{injection\_process = on\_off}). The burstiness of the latter +injection process is controlled via the \texttt{burst\_alpha} and +\texttt{burst\_beta} parameter. See PPIN Section 24.2.2 for a +description of the on-off process and its parameters. + +The unit of injection is packets, which may be comprised of many +flits. The number of flits per packet is set using the +\texttt{const\_flits\_per\_packet} option. Each packet may also have an +associated priority, either age-based (\texttt{age}) or none +(\texttt{none}), as specified by the \texttt{priority} option. + +The simulator also supports several different traffic patterns that +are specified using the \texttt{traffic} option. To describe these +patterns, we use the same notation of PPIN Section 3.2: $s_i$ ($d_i$) +denotes the $i^\textrm{th}$ bit of the source (destination) address +whereas $s_x$ ($d_x$) denotes the $x^\textrm{th}$ radix-$k$ digit of +the source (destination) address. The bit length of an address is $b += \log_2 N$, where $N$ is the number of nodes in the network. + +\begin{opt_list}{transpose} +\item[uniform] Each source sends an equal amount of traffic to each +destination (\texttt{traffic = uniform}). +\item[bitcomp] Bit complement. $d_i = \neg s_i$. +\item[bitrev] Bit reverse. $d_i = s_{b-i-1}$. +\item[shuffle] $d_i = s_{i-1 \mod b}$. +\item[transpose] $d_i = s_{i+b/2 \mod b}$. +\item[tornado] $d_x = s_x + \lceil k/2 \rceil - 1 \mod k$. +\item[neighbor] $d_x = s_x + 1 \mod k$. +\item[randperm] Random permutation. A fixed permutation traffic +pattern is chosen uniformly at random from the set of all +permutations. The seed used to generate this permutation is set by +the \texttt{perm\_seed} option. So, randomly selecting values for +\texttt{perm\_seed} gives a random sampling of permutation while a +fixed value of \texttt{perm\_seed} allows the same permutation to be +used for several experiments. +\end{opt_list} + +\subsection{Simulation parameters} +\label{sec:sim_params} + +The duration and other aspects of a simulation are controlled using +the set of simulation parameters. + +\begin{opt_list}{warmup\_periods} + +\item[sim\_type] A simulation can either focus on +\texttt{throughput} or \texttt{latency}. The key difference between +these two types is that a \texttt{latency} simulation will wait for +all measurement packets to drain before ending the simulation to +ensure an accurate latency measurement. In \texttt{throughput} +simulations, this final drain step is eliminated to allow simulation +of networks operating beyond their saturation point. + +\item[sample\_period] The sample period is expressed in simulator +cycles and is used as a multiplier when specifying the warm-up length +of a simulation and the maximum number of samples. Also, intermediate +statistics are displayed once every \texttt{sample\_period} cycles. + +\item[warmup\_periods] The length of the simulator warm up expressed +as a multiple of the \texttt{sample\_period}. After warming up, all +statistics counters are reset. + +\item[max\_samples] The total length of simulation expressed as a +multiple of the \texttt{sample\_period}. + +\item[latency\_thres] If the sampled latency of the current simulation +exceeds \texttt{latency\_thres}, the simulation is immediately ended. + +\item[sim\_count] The number of back-to-back simulations to run for the +given configuration. Useful for creating ensemble averages of +particular statistics. + +\item[seed] A random seed for the simulation. + +\item[reorder] A non-zero value indicates that packet order should be +maintained and reordering time is accounted for in the overall latency. + +\end{opt_list} + +\appendix + +\section{Random number generation} + +The simulator uses Knuth's integer and floating point pseudorandom +number generators. These algorithms and their explanations appear in +``The Art of Computer Programming: Seminumerical Algorithms''. + +\end{document} \ No newline at end of file diff --git a/src/intersim/event_router.cpp b/src/intersim/event_router.cpp new file mode 100644 index 0000000..09172cd --- /dev/null +++ b/src/intersim/event_router.cpp @@ -0,0 +1,923 @@ +#include +#include +#include +#include +#include + +#include "event_router.hpp" +#include "stats.hpp" + +EventRouter::EventRouter( const Configuration& config, + Module *parent, string name, int id, + int inputs, int outputs ) +: Router( config, + parent, name, + id, + inputs, outputs ) +{ + ostringstream module_name; + + _vcs = config.GetInt( "num_vcs" ); + _vc_size = config.GetInt( "vc_buf_size" ); + + // Cut-through mode --- packets are not broken + // up and input buffers are assumed to be + // expressed in units of maximum size packets. + + _vct = config.GetInt( "vct" ); + + // Routing + + _rf = GetRoutingFunction( config ); + + // Alloc VC's + + _vc = new VC * [_inputs]; + + for ( int i = 0; i < _inputs; ++i ) { + _vc[i] = new VC [_vcs]; + for( int j=0; j < _vcs; ++j ) { + _vc[i][j].init( config, _outputs ); + } + + for ( int v = 0; v < _vcs; ++v ) { // Name the vc modules + module_name << "vc_i" << i << "_v" << v; + _vc[i][v].SetName( this, module_name.str( ) ); + module_name.seekp( 0, ios::beg ); + } + } + + // Alloc next VCs' state + + _output_state = new EventNextVCState [_outputs]; + for( int j=0; j < _outputs; ++j ) { + _output_state[j].init( config ); + } + + for ( int o = 0; o < _outputs; ++o ) { + module_name << "output" << o << "_vc_state"; + _output_state[o].SetName( this, module_name.str( ) ); + module_name.seekp( 0, ios::beg ); + } + + // Alloc arbiters + + _arrival_arbiter = new PriorityArbiter * [_outputs]; + + for ( int o = 0; o < _outputs; ++o ) { + module_name << "arrival_arb_output" << o; + _arrival_arbiter[o] = + new PriorityArbiter( config, this, module_name.str( ), _inputs ); + module_name.seekp( 0, ios::beg ); + } + + _transport_arbiter = new PriorityArbiter * [_inputs]; + + for ( int i = 0; i < _inputs; ++i ) { + module_name << "transport_arb_input" << i; + _transport_arbiter[i] = + new PriorityArbiter( config, this, module_name.str( ), _outputs ); + module_name.seekp( 0, ios::beg ); + } + + // Alloc pipelines (to simulate processing/transmission delays) + + _crossbar_pipe = + new PipelineFIFO( this, "crossbar_pipeline", _outputs, + _st_prepare_delay + _st_final_delay ); + + _credit_pipe = + new PipelineFIFO( this, "credit_pipeline", _inputs, + _credit_delay ); + + _arrival_pipe = + new PipelineFIFO( this, "arrival_pipeline", _inputs, + 0 /* FIX THIS EVENTUALLY */); + + // Queues + + _input_buffer = new queue [_inputs]; + _output_buffer = new queue [_outputs]; + + _in_cred_buffer = new queue [_inputs]; + _out_cred_buffer = new queue [_outputs]; + + _arrival_queue = new queue [_inputs]; + _transport_queue = new queue [_outputs]; + + // Misc. + + _transport_free = new bool [_inputs]; + _transport_match = new int [_inputs]; + + for ( int i = 0; i < _inputs; ++i ) { + _transport_free[i] = true; + _transport_match[i] = -1; + } +} + +EventRouter::~EventRouter( ) +{ + for ( int i = 0; i < _inputs; ++i ) { + delete [] _vc[i]; + } + + delete [] _vc; + delete [] _output_state; + + for ( int o = 0; o < _outputs; ++o ) { + delete _arrival_arbiter[o]; + } + + for ( int i = 0; i < _inputs; ++i ) { + delete _transport_arbiter[i]; + } + + delete [] _arrival_arbiter; + delete [] _transport_arbiter; + + delete _crossbar_pipe; + delete _credit_pipe; + delete _arrival_pipe; + + delete [] _input_buffer; + delete [] _output_buffer; + + delete [] _in_cred_buffer; + delete [] _out_cred_buffer; + + delete [] _arrival_queue; + delete [] _transport_queue; + + delete [] _transport_free; + delete [] _transport_match; +} + +void EventRouter::ReadInputs( ) +{ + _ReceiveFlits( ); + _ReceiveCredits( ); +} + +void EventRouter::InternalStep( ) +{ + // Receive incoming flits + _IncomingFlits( ); + + // The input pipe simulates routing delay + _arrival_pipe->Advance( ); + + // Clear output requests + for ( int output = 0; output < _outputs; ++output ) { + _arrival_arbiter[output]->Clear( ); + } + + // Check input arrival queues and generate + // requests for the outputs + for ( int input = 0; input < _inputs; ++input ) { + _ArrivalRequests( input ); + } + + // Arbitrate between requests at outputs + for ( int output = 0; output < _outputs; ++output ) { + _ArrivalArb( output ); + } + + for ( int input = 0; input < _inputs; ++input ) { + _transport_arbiter[input]->Clear( ); + } + + _crossbar_pipe->WriteAll( 0 ); + _credit_pipe->WriteAll( 0 ); + + // Generate transport events and their + // requests for the inputs + for ( int output = 0; output < _outputs; ++output ) { + _TransportRequests( output ); + } + + // Arbitrate between requests at inputs + for ( int input = 0; input < _inputs; ++input ) { + _TransportArb( input ); + } + + _crossbar_pipe->Advance( ); + _credit_pipe->Advance( ); + + _OutputQueuing( ); +} + +void EventRouter::WriteOutputs( ) +{ + _SendFlits( ); + _SendCredits( ); +} + +void EventRouter::_ReceiveFlits( ) +{ + Flit *f; + + for ( int input = 0; input < _inputs; ++input ) { + f = *((*_input_channels)[input]); + + if ( f ) { + _input_buffer[input].push( f ); + } + } +} + +void EventRouter::_ReceiveCredits( ) +{ + Credit *c; + + for ( int output = 0; output < _outputs; ++output ) { + c = *((*_output_credits)[output]); + + if ( c ) { + _out_cred_buffer[output].push( c ); + } + } +} + +void EventRouter::_ProcessWaiting( int output, int out_vc ) +{ + // out_vc just sent the transport event for out_vc, + // check if any events are queued on that vc. if so, + // generate another transport event and set the + // owner of the vc, otherwise set the vc to idle. + + int credits; + + tTransportEvent *tevt; + + EventNextVCState::tWaiting *w; + + if ( _output_state[output].IsWaiting( out_vc ) ) { + + // State remains as busy, but the waiting VC takes over + w = _output_state[output].PopWaiting( out_vc ); + + _output_state[output].SetState( out_vc, EventNextVCState::busy ); + _output_state[output].SetInput( out_vc, w->input ); + _output_state[output].SetInputVC( out_vc, w->vc ); + + if ( w->watch ) { + cout << "Dequeuing waiting arrival event at " << _fullname + << " for flit " << w->id << endl; + } + + credits = _output_state[output].GetCredits( out_vc ); + + // Try to queue a transmit event for a waiting packet + if ( credits > 0 ) { + tevt = new tTransportEvent; + tevt->src_vc = w->vc; + tevt->dst_vc = out_vc; + tevt->input = w->input; + tevt->watch = w->watch; // just to have something here + tevt->id = w->id; + + _transport_queue[output].push( tevt ); + + if ( tevt->watch ) { + cout << "Injecting transport event at " << _fullname + << " for flit " << tevt->id << endl; + } + + credits--; + _output_state[output].SetCredits( out_vc, credits ); + _output_state[output].SetPresence( out_vc, w->pres - 1 ); + + } else { + // No credits available, just store presence + _output_state[output].SetPresence( out_vc, w->pres ); + } + + delete w; + + } else { + // Tail sent, none waiting => VC is idle + _output_state[output].SetState( out_vc, EventNextVCState::idle ); + } +} + +void EventRouter::_IncomingFlits( ) +{ + Flit *f; + VC *cur_vc; + + tArrivalEvent *aevt; + + _arrival_pipe->WriteAll( 0 ); + + for ( int input = 0; input < _inputs; ++input ) { + if ( !_input_buffer[input].empty( ) ) { + f = _input_buffer[input].front( ); + _input_buffer[input].pop( ); + + cur_vc = &_vc[input][f->vc]; + + if ( !cur_vc->AddFlit( f ) ) { + cout << "Error processing flit:" << endl << *f; + Error( "VC buffer overflow" ); + } + + // Head flit arriving at idle VC + if ( cur_vc->GetState( ) == VC::idle ) { + + if ( !f->head ) { + cout << "Non-head flit:" << endl; + cout << *f; + Error( "Received non-head flit at idle VC" ); + } + + const OutputSet *route_set; + int out_vc, out_port; + + cur_vc->Route( _rf, this, f, input ); + route_set = cur_vc->GetRouteSet( ); + + if ( !route_set->GetPortVC( &out_port, &out_vc ) ) { + Error( "The event-driven router requires routing functions with a single (port,vc) output" ); + } + + cur_vc->SetOutput( out_port, out_vc ); + cur_vc->SetState( VC::active ); + } else { + if ( f->head ) { + cout << *f; + Error( "Received head flit at non-idle VC." ); + } + } + + if ( f->watch ) { + cout << "Received flit at " << _fullname << ". Output port = " + << cur_vc->GetOutputPort( ) << ", output VC = " + << cur_vc->GetOutputVC( ) << endl; + cout << *f; + } + + // In cut-through mode, only head flits generate arrivals, + // otherwise all flits generate + + if ( ( !_vct ) || ( _vct && f->head ) ) { + // Add the arrival event to a delay pipeline to + // account for routing/decoding time + + aevt = new tArrivalEvent; + + aevt->input = input; + aevt->output = cur_vc->GetOutputPort( ); + aevt->src_vc = f->vc; + aevt->dst_vc = cur_vc->GetOutputVC( ); + aevt->head = f->head; + aevt->tail = f->tail; + + //if ( f->head && f->tail ) { + // Error( "Head/tail packets not supported." ); + //} + + aevt->watch = f->watch; + aevt->id = f->id; + + _arrival_pipe->Write( aevt, input ); + + if ( aevt->watch ) { + cout << "Injected arrival event at " << _fullname + << " for flit " << aevt->id << endl; + } + } + } + } +} + +void EventRouter::_ArrivalRequests( int input ) +{ + tArrivalEvent *aevt; + + aevt = _arrival_pipe->Read( input ); + if ( aevt ) { + _arrival_queue[input].push( aevt ); + } + + if ( !_arrival_queue[input].empty( ) ) { + aevt = _arrival_queue[input].front( ); + _arrival_arbiter[aevt->output]->AddRequest( input ); + } +} + +void EventRouter::_SendTransport( int input, int output, tArrivalEvent *aevt ) +{ + // Try to send a transport event + + tTransportEvent *tevt; + + int credits; + int pres; + + credits = _output_state[output].GetCredits( aevt->dst_vc ); + + if ( credits > 0 ) { + // Take a credit and queue a transport event + credits--; + _output_state[output].SetCredits( aevt->dst_vc, credits ); + + tevt = new tTransportEvent; + tevt->src_vc = aevt->src_vc; + tevt->dst_vc = aevt->dst_vc; + tevt->input = input; + tevt->watch = aevt->watch; + tevt->id = aevt->id; + + _transport_queue[output].push( tevt ); + + if ( tevt->watch ) { + cout << "Injecting transport event at " << _fullname + << " for flit " << tevt->id << endl; + } + } else { + if ( aevt->watch ) { + cout << "No credits available at " << _fullname + << " for flit " << aevt->id << " storing presence." << endl; + } + + // No credits available, just store presence + pres = _output_state[output].GetPresence( aevt->dst_vc ); + _output_state[output].SetPresence( aevt->dst_vc, pres + 1 ); + } +} + +void EventRouter::_ArrivalArb( int output ) +{ + tArrivalEvent *aevt; + tTransportEvent *tevt; + Credit *c; + + EventNextVCState::tWaiting *w; + + int input; + int credits; + int pres; + + // Incoming credits can produce or enable + // transport events --- process them first + + if ( !_out_cred_buffer[output].empty( ) ) { + c = _out_cred_buffer[output].front( ); + _out_cred_buffer[output].pop( ); + + if ( c->vc_cnt != 1 ) { + Error( "Code can't handle credit counts not equal to 1." ); + } + + EventNextVCState::eNextVCState state = + _output_state[output].GetState( c->vc[0] ); + + credits = _output_state[output].GetCredits( c->vc[0] ); + pres = _output_state[output].GetPresence( c->vc[0] ); + + if ( _vct ) { + // In cut-through mode, only head credits indicate a change in + // channel state. + + if ( c->head ) { + credits++; + _output_state[output].SetCredits( c->vc[0], credits ); + _ProcessWaiting( output, c->vc[0] ); + } + } else { + credits++; + _output_state[output].SetCredits( c->vc[0], credits ); + + if ( c->tail ) { // tail flit -- recycle VC + if ( state != EventNextVCState::busy ) { + Error( "Received tail credit at non-busy output VC" ); + } + + _ProcessWaiting( output, c->vc[0] ); + } else if ( ( state == EventNextVCState::busy ) && ( pres > 0 ) ) { + // Flit is present => generate transport event + + tevt = new tTransportEvent; + tevt->input = _output_state[output].GetInput( c->vc[0] ); + tevt->src_vc = _output_state[output].GetInputVC( c->vc[0] ); + tevt->dst_vc = c->vc[0]; + tevt->watch = false; + tevt->id = -1; + + _transport_queue[output].push( tevt ); + + pres--; + credits--; + _output_state[output].SetPresence( c->vc[0], pres ); + _output_state[output].SetCredits( c->vc[0], credits ); + } + } + + delete c; + } + + // Now process arrival events + + _arrival_arbiter[output]->Arbitrate( ); + input = _arrival_arbiter[output]->Match( ); + + if ( input != -1 ) { + // Winning arrival event gets access to output + + aevt = _arrival_queue[input].front( ); + _arrival_queue[input].pop( ); + + if ( aevt->watch ) { + cout << "Processing arrival event at " << _fullname + << " for flit " << aevt->id << endl; + } + + EventNextVCState::eNextVCState state = + _output_state[output].GetState( aevt->dst_vc ); + + if ( aevt->head ) { // Head flits + if ( state == EventNextVCState::idle ) { + // Allocate the output VC and queue a transport event + _output_state[output].SetState( aevt->dst_vc, EventNextVCState::busy ); + _output_state[output].SetInput( aevt->dst_vc, input ); + _output_state[output].SetInputVC( aevt->dst_vc, aevt->src_vc ); + + _SendTransport( input, output, aevt ); + } else { + // VC busy => queue a waiting event + + w = new EventNextVCState::tWaiting; + + w->input = input; + w->vc = aevt->src_vc; + w->id = aevt->id; + w->watch = aevt->watch; + w->pres = 1; + + _output_state[output].PushWaiting( aevt->dst_vc, w ); + } + } else { + if ( _vct ) { + Error( "Received arrival event for non-head flit in cut-through mode" ); + } + + if ( state != EventNextVCState::busy ) { + cout << "flit id = " << aevt->id << endl; + Error( "Received a body flit at a non-busy output VC" ); + } + + if ( ( !_output_state[output].IsInputWaiting( aevt->dst_vc, input, aevt->src_vc ) ) && + ( input == _output_state[output].GetInput( aevt->dst_vc ) ) && + ( aevt->src_vc == _output_state[output].GetInputVC( aevt->dst_vc ) ) ) { + // Body flit part of the current active VC => queue transport event + // (the weird IsInputWaiting call handles a body flit waiting in addition + // to a head flit) + + _SendTransport( input, output, aevt ); + } else { + + // VC busy with a differnet transaction => update waiting event + _output_state[output].IncrWaiting( aevt->dst_vc, input, aevt->src_vc ); + } + } + + delete aevt; + } +} + +void EventRouter::_TransportRequests( int output ) +{ + tTransportEvent *tevt; + + if ( !_transport_queue[output].empty( ) ) { + tevt = _transport_queue[output].front( ); + _transport_arbiter[tevt->input]->AddRequest( output ); + } +} + +void EventRouter::_TransportArb( int input ) +{ + tTransportEvent *tevt; + + int output; + VC *cur_vc; + Flit *f; + Credit *c; + + if ( _transport_free[input] ) { + _transport_arbiter[input]->Arbitrate( ); + output = _transport_arbiter[input]->Match( ); + } else { + output = _transport_match[input]; + } + + if ( output != -1 ) { + // This completes the match from input to output => + // one flit can be transferred + + tevt = _transport_queue[output].front( ); + + if ( tevt->watch ) { + cout << "Processing transport event at " << _fullname + << " for flit " << tevt->id << endl; + } + + cur_vc = &_vc[input][tevt->src_vc]; + + // Some sanity checking first + + if ( ( cur_vc->GetState( ) != VC::active ) ) { + Error( "Non-active VC received grant." ); + } + + if ( cur_vc->Empty( ) ) { + return; //Error( "Empty VC received grant." ); + } + + if ( tevt->dst_vc != cur_vc->GetOutputVC( ) ) { + Error( "Transport event's VC does not match input's destination VC." ); + } + + f = cur_vc->RemoveFlit( ); + + if ( _vct ) { + if ( f->tail ) { + _transport_free[input] = true; + _transport_match[input] = -1; + + _transport_queue[output].pop( ); + delete tevt; + + cur_vc->SetState( VC::idle ); + } else { + _transport_free[input] = false; + _transport_match[input] = output; + } + } else { + _transport_free[input] = true; + _transport_match[input] = -1; + + _transport_queue[output].pop( ); + delete tevt; + + if ( f->tail ) { + cur_vc->SetState( VC::idle ); + } + } + + c = _NewCredit( ); + c->vc[c->vc_cnt] = f->vc; + c->head = f->head; + c->tail = f->tail; + c->vc_cnt++; + c->id = f->id; + _credit_pipe->Write( c, input ); + + if ( f->watch && c->tail ) { + cout << _fullname << " sending tail credit back for flit " << f->id << endl; + } + + // Update and forward the flit to the crossbar + + f->hops++; + f->vc = cur_vc->GetOutputVC( ); + _crossbar_pipe->Write( f, output ); + + if ( f->watch ) { + cout << "Forwarding flit through crossbar at " << _fullname << ":" << endl; + cout << *f; + } + } +} + +void EventRouter::_OutputQueuing( ) +{ + Flit *f; + Credit *c; + + for ( int output = 0; output < _outputs; ++output ) { + f = _crossbar_pipe->Read( output ); + + if ( f ) { + _output_buffer[output].push( f ); + } + } + + for ( int input = 0; input < _inputs; ++input ) { + c = _credit_pipe->Read( input ); + + if ( c ) { + _in_cred_buffer[input].push( c ); + } + } +} + +void EventRouter::_SendFlits( ) +{ + Flit *f; + + for ( int output = 0; output < _outputs; ++output ) { + if ( !_output_buffer[output].empty( ) ) { + f = _output_buffer[output].front( ); + _output_buffer[output].pop( ); + } else { + f = 0; + } + + *(*_output_channels)[output] = f; + } +} + +void EventRouter::_SendCredits( ) +{ + Credit *c; + + for ( int input = 0; input < _inputs; ++input ) { + if ( !_in_cred_buffer[input].empty( ) ) { + c = _in_cred_buffer[input].front( ); + _in_cred_buffer[input].pop( ); + } else { + c = 0; + } + + *(*_input_credits)[input] = c; + } +} + +void EventRouter::Display( ) const +{ + for ( int input = 0; input < _inputs; ++input ) { + for ( int v = 0; v < _vcs; ++v ) { + _vc[input][v].Display( ); + } + } +} + +void EventNextVCState::init( const Configuration& config ) +{ + _Init( config ); +} + +EventNextVCState::EventNextVCState( const Configuration& config, + Module *parent, const string& name ) : +Module( parent, name ) +{ + _Init( config ); +} + +void EventNextVCState::_Init( const Configuration& config ) +{ + _buf_size = config.GetInt( "vc_buf_size" ); + _vcs = config.GetInt( "num_vcs" ); + + _credits = new int [_vcs]; + _presence = new int [_vcs]; + _input = new int [_vcs]; + _inputVC = new int [_vcs]; + _waiting = new list [_vcs]; + _state = new eNextVCState [_vcs]; + + for ( int vc = 0; vc < _vcs; ++vc ) { + _presence[vc] = 0; + _credits[vc] = _buf_size; + _state[vc] = idle; + } +} + +EventNextVCState::~EventNextVCState( ) +{ + delete [] _credits; + delete [] _presence; + delete [] _input; + delete [] _inputVC; + delete [] _waiting; + delete [] _state; +} + +EventNextVCState::eNextVCState EventNextVCState::GetState( int vc ) const +{ + assert( ( vc >= 0 ) && ( vc < _vcs ) ); + return _state[vc]; +} + +int EventNextVCState::GetPresence( int vc ) const +{ + assert( ( vc >= 0 ) && ( vc < _vcs ) ); + return _presence[vc]; +} + +int EventNextVCState::GetCredits( int vc ) const +{ + assert( ( vc >= 0 ) && ( vc < _vcs ) ); + return _credits[vc]; +} + +int EventNextVCState::GetInput( int vc ) const +{ + assert( ( vc >= 0 ) && ( vc < _vcs ) ); + return _input[vc]; +} + +int EventNextVCState::GetInputVC( int vc ) const +{ + assert( ( vc >= 0 ) && ( vc < _vcs ) ); + return _inputVC[vc]; +} + +bool EventNextVCState::IsWaiting( int vc ) const +{ + assert( ( vc >= 0 ) && ( vc < _vcs ) ); + return !_waiting[vc].empty( ); +} + +void EventNextVCState::PushWaiting( int vc, tWaiting *w ) +{ + assert( ( vc >= 0 ) && ( vc < _vcs ) ); + + if ( w->watch ) { + cout << _fullname << " pushing flit " << w->id + << " onto a waiting queue of length " << _waiting[vc].size( ) << endl; + } + + _waiting[vc].push_back( w ); +} + +void EventNextVCState::IncrWaiting( int vc, int w_input, int w_vc ) +{ + list::iterator match; + + // search for match + for ( match = _waiting[vc].begin( ); match != _waiting[vc].end( ); match++ ) { + if ( ( (*match)->input == w_input ) && + ( (*match)->vc == w_vc ) ) break; + } + + if ( match != _waiting[vc].end( ) ) { + (*match)->pres++; + } else { + Error( "Did not find match in IncrWaiting" ); + } +} + +bool EventNextVCState::IsInputWaiting( int vc, int w_input, int w_vc ) const +{ + list::const_iterator match; + bool r; + + // search for match + for ( match = _waiting[vc].begin( ); match != _waiting[vc].end( ); match++ ) { + if ( ( (*match)->input == w_input ) && + ( (*match)->vc == w_vc ) ) break; + } + + if ( match != _waiting[vc].end( ) ) { + r = true; + } else { + r = false; + } + + return r; +} + +EventNextVCState::tWaiting *EventNextVCState::PopWaiting( int vc ) +{ + tWaiting *w; + + assert( ( vc >= 0 ) && ( vc < _vcs ) ); + + w = _waiting[vc].front( ); + _waiting[vc].pop_front( ); + + return w; +} + +void EventNextVCState::SetState( int vc, eNextVCState state ) +{ + assert( ( vc >= 0 ) && ( vc < _vcs ) ); + _state[vc] = state; +} + +void EventNextVCState::SetCredits( int vc, int value ) +{ + assert( ( vc >= 0 ) && ( vc < _vcs ) ); + _credits[vc] = value; +} + +void EventNextVCState::SetPresence( int vc, int value ) +{ + assert( ( vc >= 0 ) && ( vc < _vcs ) ); + _presence[vc] = value; +} + +void EventNextVCState::SetInput( int vc, int input ) +{ + assert( ( vc >= 0 ) && ( vc < _vcs ) ); + _input[vc] = input; +} + +void EventNextVCState::SetInputVC( int vc, int in_vc ) +{ + assert( ( vc >= 0 ) && ( vc < _vcs ) ); + _inputVC[vc] = in_vc; +} diff --git a/src/intersim/event_router.hpp b/src/intersim/event_router.hpp new file mode 100644 index 0000000..91bc005 --- /dev/null +++ b/src/intersim/event_router.hpp @@ -0,0 +1,151 @@ +#ifndef _EVENT_ROUTER_HPP_ +#define _EVENT_ROUTER_HPP_ + +#include +#include + +#include "module.hpp" +#include "router.hpp" +#include "vc.hpp" +#include "arbiter.hpp" +#include "routefunc.hpp" +#include "outputset.hpp" +#include "pipefifo.hpp" + +class EventNextVCState : public Module { +public: + enum eNextVCState { + idle, busy, tail_pending + }; + + struct tWaiting { + int input; + int vc; + int id; + int pres; + bool watch; + }; + +private: + int _buf_size; + int _vcs; + + int *_credits; + int *_presence; + int *_input; + int *_inputVC; + + list *_waiting; + + eNextVCState *_state; + + void _Init( const Configuration& config ); + +public: + EventNextVCState() : Module() {} + void init( const Configuration& config ); + EventNextVCState( const Configuration& config, + Module *parent, const string& name ); + ~EventNextVCState( ); + + eNextVCState GetState( int vc ) const; + int GetPresence( int vc ) const; + int GetCredits( int vc ) const; + int GetInput( int vc ) const; + int GetInputVC( int vc ) const; + + bool IsWaiting( int vc ) const; + bool IsInputWaiting( int vc, int w_input, int w_vc ) const; + + void PushWaiting( int vc, tWaiting *w ); + void IncrWaiting( int vc, int w_input, int w_vc ); + tWaiting *PopWaiting( int vc ); + + void SetState( int vc, eNextVCState state ); + void SetCredits( int vc, int value ); + void SetPresence( int vc, int value ); + void SetInput( int vc, int input ); + void SetInputVC( int vc, int in_vc ); +}; + +class EventRouter : public Router { + int _vcs; + int _vc_size; + + int _vct; + + VC **_vc; + + tRoutingFunction _rf; + + EventNextVCState *_output_state; + + PipelineFIFO *_crossbar_pipe; + PipelineFIFO *_credit_pipe; + + queue *_input_buffer; + queue *_output_buffer; + + queue *_in_cred_buffer; + queue *_out_cred_buffer; + + struct tArrivalEvent { + int input; + int output; + int src_vc; + int dst_vc; + bool head; + bool tail; + + int id; // debug + bool watch; // debug + }; + + PipelineFIFO *_arrival_pipe; + queue *_arrival_queue; + PriorityArbiter **_arrival_arbiter; + + struct tTransportEvent { + int input; + int src_vc; + int dst_vc; + + int id; // debug + bool watch; // debug + }; + + queue *_transport_queue; + PriorityArbiter **_transport_arbiter; + + bool *_transport_free; + int *_transport_match; + + void _ReceiveFlits( ); + void _ReceiveCredits( ); + + void _IncomingFlits( ); + void _ArrivalRequests( int input ); + void _ArrivalArb( int output ); + void _SendTransport( int input, int output, tArrivalEvent *aevt ); + void _ProcessWaiting( int output, int out_vc ); + void _TransportRequests( int output ); + void _TransportArb( int input ); + void _OutputQueuing( ); + + void _SendFlits( ); + void _SendCredits( ); + +public: + EventRouter( const Configuration& config, + Module *parent, string name, int id, + int inputs, int outputs ); + virtual ~EventRouter( ); + + virtual void ReadInputs( ); + virtual void InternalStep( ); + virtual void WriteOutputs( ); + + void Display( ) const; +}; + +#endif diff --git a/src/intersim/examples/fly26_age b/src/intersim/examples/fly26_age new file mode 100644 index 0000000..1abff5d --- /dev/null +++ b/src/intersim/examples/fly26_age @@ -0,0 +1,41 @@ +// Topology + +topology = fly; +k = 2; +n = 6; + +// Routing + +routing_function = dest_tag; + +// Flow control + +num_vcs = 8; +vc_buf_size = 8; + +wait_for_tail_credit = 1; + +// Router architecture + +vc_allocator = select; +sw_allocator = select; +alloc_iters = 1; + +credit_delay = 2; +routing_delay = 1; +vc_alloc_delay = 1; + +input_speedup = 2; +output_speedup = 1; +internal_speedup = 1.0; + +// Traffic + +traffic = uniform; +const_flits_per_packet = 20; +priority = age; + +// Simulation + +sim_type = latency; +injection_rate = 0.1; \ No newline at end of file diff --git a/src/intersim/examples/mesh b/src/intersim/examples/mesh new file mode 100644 index 0000000..b374981 --- /dev/null +++ b/src/intersim/examples/mesh @@ -0,0 +1,40 @@ +// Topology + +topology = mesh; +k = 2; +n = 2; + +// Routing + +routing_function = dim_order; + +// Flow control + +num_vcs = 8; +vc_buf_size = 8; + +wait_for_tail_credit = 1; + +// Router architecture + +vc_allocator = islip; +sw_allocator = islip; +alloc_iters = 1; + +credit_delay = 1; +routing_delay = 1; +vc_alloc_delay = 1; + +input_speedup = 2; +output_speedup = 1; +internal_speedup = 1.0; + +// Traffic + +traffic = transpose; +const_flits_per_packet = 20; + +// Simulation + +sim_type = latency; +injection_rate = 0.1; diff --git a/src/intersim/examples/mesh2 b/src/intersim/examples/mesh2 new file mode 100644 index 0000000..8eb0521 --- /dev/null +++ b/src/intersim/examples/mesh2 @@ -0,0 +1,41 @@ +// Topology + +topology = mesh; +k = 2; +n = 2; + +// Routing + +routing_function = dim_order; + +// Flow control + +num_vcs = 8; +vc_buf_size = 8; + +wait_for_tail_credit = 1; + +// Router architecture + +vc_allocator = islip; +sw_allocator = islip; +alloc_iters = 1; + +credit_delay = 1; +routing_delay = 1; +vc_alloc_delay = 1; + +input_speedup = 2; +output_speedup = 1; +internal_speedup = 1.0; + +// Traffic + +traffic = gpgpusim; +const_flits_per_packet = 3; + +injection_process = gpgpu_injector; +// Simulation + +sim_type = latency; +injection_rate = 0.1; diff --git a/src/intersim/examples/mesh4 b/src/intersim/examples/mesh4 new file mode 100644 index 0000000..8492df6 --- /dev/null +++ b/src/intersim/examples/mesh4 @@ -0,0 +1,41 @@ +// Topology + +topology = mesh; +k = 2; +n = 1; + +// Routing + +routing_function = dim_order; + +// Flow control + +num_vcs = 1; +vc_buf_size = 1; + +wait_for_tail_credit = 1; + +// Router architecture + +vc_allocator = islip; +sw_allocator = islip; +alloc_iters = 1; + +credit_delay = 1; +routing_delay = 1; +vc_alloc_delay = 1; + +input_speedup = 1; +output_speedup = 1; +internal_speedup = 1.0; + +// Traffic + +traffic = gpgpusim; +const_flits_per_packet = 3; + +injection_process = gpgpu_injector; +// Simulation + +sim_type = latency; +injection_rate = 0.1; diff --git a/src/intersim/examples/mesh88_lat b/src/intersim/examples/mesh88_lat new file mode 100644 index 0000000..fca1fb4 --- /dev/null +++ b/src/intersim/examples/mesh88_lat @@ -0,0 +1,40 @@ +// Topology + +topology = mesh; +k = 8; +n = 2; + +// Routing + +routing_function = dim_order; + +// Flow control + +num_vcs = 8; +vc_buf_size = 8; + +wait_for_tail_credit = 1; + +// Router architecture + +vc_allocator = islip; +sw_allocator = islip; +alloc_iters = 1; + +credit_delay = 2; +routing_delay = 1; +vc_alloc_delay = 1; + +input_speedup = 2; +output_speedup = 1; +internal_speedup = 1.0; + +// Traffic + +traffic = transpose; +const_flits_per_packet = 20; + +// Simulation + +sim_type = latency; +injection_rate = 0.1; \ No newline at end of file diff --git a/src/intersim/examples/single b/src/intersim/examples/single new file mode 100644 index 0000000..addb549 --- /dev/null +++ b/src/intersim/examples/single @@ -0,0 +1,20 @@ +// Topology + +topology = single; +in_ports = 8; +out_ports = 8; + +// Routing + +routing_function = single; + +// Flow control + +vc_allocator = islip; +sw_allocator = islip; +alloc_iters = 2; + +num_vcs = 8; +vc_buf_size = 1000; + +wait_for_tail_credit = 0; \ No newline at end of file diff --git a/src/intersim/examples/torus88 b/src/intersim/examples/torus88 new file mode 100644 index 0000000..723e3ac --- /dev/null +++ b/src/intersim/examples/torus88 @@ -0,0 +1,14 @@ +// Topology +topology = torus; +k = 8; +n = 2; + +// Routing +routing_function = dim_order; + +// Flow control +num_vcs = 2; + +// Traffic +traffic = uniform; +injection_rate = 0.15; diff --git a/src/intersim/flit.cpp b/src/intersim/flit.cpp new file mode 100644 index 0000000..8ec248b --- /dev/null +++ b/src/intersim/flit.cpp @@ -0,0 +1,12 @@ +#include "booksim.hpp" +#include "flit.hpp" + +ostream& operator<<( ostream& os, const Flit& f ) +{ + os << " Flit ID: " << f.id << " (" << &f << ")" + << " Head: " << f.head << " Tail: " << f.tail << endl; + os << " Source : " << f.src << " Dest : " << f.dest << endl; + os << " Injection time : " << f.time << endl; + + return os; +} diff --git a/src/intersim/flit.hpp b/src/intersim/flit.hpp new file mode 100644 index 0000000..d22e531 --- /dev/null +++ b/src/intersim/flit.hpp @@ -0,0 +1,46 @@ +#ifndef _FLIT_HPP_ +#define _FLIT_HPP_ + +#include "booksim.hpp" + +#include + +struct Flit { + void* data; + int net_num; // which network is this flit in (we might have several icnt networks) + + int vc; + + bool head; + bool tail; + bool true_tail; + + int time; + + int sn; + int rob_time; + + int id; + bool record; + + int src; + int dest; + + int pri; + + int hops; + bool watch; + + // Fields for multi-phase algorithms + mutable int intm; + mutable int ph; + + mutable int dr; + + // Which VC parition to use for deadlock avoidance in a ring + mutable int ring_par; +}; + +ostream& operator<<( ostream& os, const Flit& f ); + +#endif diff --git a/src/intersim/fly.cpp b/src/intersim/fly.cpp new file mode 100644 index 0000000..1793cc2 --- /dev/null +++ b/src/intersim/fly.cpp @@ -0,0 +1,133 @@ +#include "booksim.hpp" +#include +#include + +#include "fly.hpp" +#include "misc_utils.hpp" + +//#define DEBUG_FLY + +KNFly::KNFly( const Configuration &config ) : +Network( config ) +{ + _ComputeSize( config ); + _Alloc( ); + _BuildNet( config ); +} + +void KNFly::_ComputeSize( const Configuration &config ) +{ + _k = config.GetInt( "k" ); + _n = config.GetInt( "n" ); + + gK = _k; gN = _n; + + _sources = powi( _k, _n ); + _dests = powi( _k, _n ); + + // n stages of k^(n-1) k x k switches + _size = _n*powi( _k, _n-1 ); + + // n-1 sets of wiring between the stages + _channels = (_n-1)*_sources; +} + +void KNFly::_BuildNet( const Configuration &config ) +{ + ostringstream router_name; + + int per_stage = powi( _k, _n-1 ); + + int node = 0; + int c; + + for ( int stage = 0; stage < _n; ++stage ) { + for ( int addr = 0; addr < per_stage; ++addr ) { + + router_name << "router_" << stage << "_" << addr; + _routers[node] = Router::NewRouter( config, this, router_name.str( ), + node, _k, _k ); + router_name.seekp( 0, ios::beg ); + +#ifdef DEBUG_FLY + cout << "connecting node " << node << " to:" << endl; +#endif + + for ( int port = 0; port < _k; ++port ) { + // Input connections + if ( stage == 0 ) { + c = addr*_k + port; + _routers[node]->AddInputChannel( &_inject[c], &_inject_cred[c] ); +#ifdef DEBUG_FLY + cout << " injection channel " << c << endl; +#endif + } else { + c = _InChannel( stage, addr, port ); + _routers[node]->AddInputChannel( &_chan[c], &_chan_cred[c] ); +#ifdef DEBUG_FLY + cout << " input channel " << c << endl; +#endif + } + + // Output connections + if ( stage == _n - 1 ) { + c = addr*_k + port; + _routers[node]->AddOutputChannel( &_eject[c], &_eject_cred[c] ); +#ifdef DEBUG_FLY + cout << " ejection channel " << c << endl; +#endif + } else { + c = _OutChannel( stage, addr, port ); + _routers[node]->AddOutputChannel( &_chan[c], &_chan_cred[c] ); +#ifdef DEBUG_FLY + cout << " output channel " << c << endl; +#endif + } + } + + ++node; + } + } +} + +int KNFly::_OutChannel( int stage, int addr, int port ) const +{ + return stage*_sources + addr*_k + port; +} + +int KNFly::_InChannel( int stage, int addr, int port ) const +{ + int in_addr; + int in_port; + + // Channels are between {node,port} + // { d_{n-1} ... d_{n-stage} ... d_0 } and + // { d_{n-1} ... d_0 ... d_{n-stage} } + + int shift = powi( _k, _n-stage-1 ); + + int last_digit = port; + int zero_digit = ( addr / shift ) % _k; + + // swap zero and last digit to get first node's address + in_addr = addr - zero_digit*shift + last_digit*shift; + in_port = zero_digit; + + return(stage-1)*_sources + in_addr*_k + in_port; +} + +int KNFly::GetN( ) const +{ + return _n; +} + +int KNFly::GetK( ) const +{ + return _k; +} + +double KNFly::Capacity( ) const +{ + return 1.0; +} + diff --git a/src/intersim/fly.hpp b/src/intersim/fly.hpp new file mode 100644 index 0000000..76ebb12 --- /dev/null +++ b/src/intersim/fly.hpp @@ -0,0 +1,26 @@ +#ifndef _FLY_HPP_ +#define _FLY_HPP_ + +#include "network.hpp" + +class KNFly : public Network { + + int _k; + int _n; + + void _ComputeSize( const Configuration &config ); + void _BuildNet( const Configuration &config ); + + int _OutChannel( int stage, int addr, int port ) const; + int _InChannel( int stage, int addr, int port ) const; + +public: + KNFly( const Configuration &config ); + + int GetN( ) const; + int GetK( ) const; + + double Capacity( ) const; +}; + +#endif diff --git a/src/intersim/injection.cpp b/src/intersim/injection.cpp new file mode 100644 index 0000000..68483cb --- /dev/null +++ b/src/intersim/injection.cpp @@ -0,0 +1,100 @@ +#include "booksim.hpp" +#include +#include +#include +#include "injection.hpp" +#include "network.hpp" +#include "random_utils.hpp" +#include "misc_utils.hpp" + +map gInjectionProcessMap; + +double gBurstAlpha; +double gBurstBeta; + +int gConstPacketSize; + +int *gNodeStates = 0; +//============================================================= + +int bernoulli( int /*source*/, double rate ) +{ + return( RandomFloat( ) < ( rate / (double)gConstPacketSize ) ) ? + gConstPacketSize : 0; +} + +//============================================================= + +int on_off( int source, double rate ) +{ + double r1; + bool issue; + + assert( ( source >= 0 ) && ( source < gNodes ) ); + + if ( !gNodeStates ) { + gNodeStates = new int [gNodes]; + + for ( int n = 0; n < gNodes; ++n ) { + gNodeStates[n] = 0; + } + } + + // advance state + + if ( gNodeStates[source] == 0 ) { + if ( RandomFloat( ) < gBurstAlpha ) { // from off to on + gNodeStates[source] = 1; + } + } else if ( RandomFloat( ) < gBurstBeta ) { // from on to off + gNodeStates[source] = 0; + } + + // generate packet + + issue = false; + if ( gNodeStates[source] ) { // on? + r1 = rate * ( 1.0 + gBurstBeta / gBurstAlpha ) / + (double)gConstPacketSize; + + if ( RandomFloat( ) < r1 ) { + issue = true; + } + } + + return issue ? gConstPacketSize : 0; +} + +//============================================================= + +void InitializeInjectionMap( ) +{ + /* Register injection processes functions here */ + + gInjectionProcessMap["bernoulli"] = &bernoulli; + gInjectionProcessMap["on_off"] = &on_off; +} + +tInjectionProcess GetInjectionProcess( const Configuration& config ) +{ + map::const_iterator match; + tInjectionProcess ip; + + string fn; + + config.GetStr( "injection_process", fn ); + match = gInjectionProcessMap.find( fn ); + + if ( match != gInjectionProcessMap.end( ) ) { + ip = match->second; + } else { + cout << "Error: Undefined injection process '" << fn << "'." << endl; + exit(-1); + } + + gConstPacketSize = config.GetInt( "const_flits_per_packet" ); + gBurstAlpha = config.GetFloat( "burst_alpha" ); + gBurstBeta = config.GetFloat( "burst_beta" ); + + return ip; +} diff --git a/src/intersim/injection.hpp b/src/intersim/injection.hpp new file mode 100644 index 0000000..60a017a --- /dev/null +++ b/src/intersim/injection.hpp @@ -0,0 +1,12 @@ +#ifndef _INJECTION_HPP_ +#define _INJECTION_HPP_ + +#include "config_utils.hpp" + +typedef int (*tInjectionProcess)( int, double ); + +void InitializeInjectionMap( ); + +tInjectionProcess GetInjectionProcess( const Configuration& config ); + +#endif diff --git a/src/intersim/interconnect_interface.cpp b/src/intersim/interconnect_interface.cpp new file mode 100644 index 0000000..bb30f81 --- /dev/null +++ b/src/intersim/interconnect_interface.cpp @@ -0,0 +1,574 @@ +#include "booksim.hpp" +#include +#include +#include +#include +#include + +#include "routefunc.hpp" +#include "traffic.hpp" +#include "booksim_config.hpp" +#include "trafficmanager.hpp" +#include "random_utils.hpp" +#include "network.hpp" +#include "singlenet.hpp" +#include "kncube.hpp" +#include "fly.hpp" +#include "injection.hpp" +#include "interconnect_interface.h" +#include "../gpgpu-sim/mem_fetch.h" +#include + +extern unsigned long long gpu_sim_cycle; +int _flit_size ; +extern unsigned int warp_size; + +bool doub_net = false; //double networks disabled by default + +BookSimConfig icnt_config; +TrafficManager** traffic; +unsigned int g_num_vcs; //number of virtual channels +queue ** ejection_buf; +vector round_robin_turn; //keep track of boundary_buf last used in icnt_pop +unsigned int ejection_buffer_capacity ; +unsigned int boundary_buf_capacity ; + +unsigned int input_buffer_capacity ; + +class boundary_buf{ +private: + queue buf; + queue tail_flag; + int packet_n; + unsigned capacity; +public: + boundary_buf(){ + capacity = boundary_buf_capacity; //maximum flits the buffer can hold + packet_n=0; + } + bool is_full(void){ + return (buf.size()>=capacity); + } + bool has_packet() { + return (packet_n); + } + void * pop_packet(){ + assert (packet_n); + void * data = NULL; + void * temp_d = buf.front(); + while (data==NULL) { + if (tail_flag.front()) { + data = buf.front(); + packet_n--; + } + assert(temp_d == buf.front()); //all flits must belong to the same packet + buf.pop(); + tail_flag.pop(); + } + return data; + } + void push_flit_data(void* data,bool is_tail) { + buf.push(data); + tail_flag.push(is_tail); + if (is_tail) { + packet_n++; + } + } +}; + +boundary_buf** clock_boundary_buf; + +class mycomparison { +public: + bool operator() (const void* lhs, const void* rhs) const + { + return( ((mem_fetch_t *)lhs)->icnt_receive_time > ((mem_fetch_t *) rhs)->icnt_receive_time); + } +}; + +bool perfect_icnt = 0; +int fixed_lat_icnt = 0; + +priority_queue, mycomparison> * out_buf_fixedlat_buf; + + +//perfect icnt stats: +unsigned int* max_fixedlat_buf_size; + +static unsigned int net_c; //number of inetrconnection networks + +static unsigned int _n_shader = 0; +static unsigned int _n_mem = 0; + +static int * node_map; //deviceID to mesh location map + //deviceID : Starts from 0 for shaders and then continues until mem nodes + // which starts at location n_shader and then continues to n_shader+n_mem (last device) +static int * reverse_map; + +void map_gen(int dim,int memcount, int memnodes[]) +{ + int k = 0; + int i=0 ; + int j=0 ; + int memfound=0; + for (i = 0; i < dim*dim ; i++) { + memfound=0; + for (j = 0; jShowStats(); + if (icnt_config.GetInt("enable_link_stats")) { + cout << "%=================================" << endl; + cout <<"Traffic "<_net->Display(); + } + } + } else { + //show max queue sizes + cout<<"Max Fixed Latency ICNT queue size for"<ShowOveralStat(); + } + } +} + +void icnt_init_grid (){ + for (unsigned i=0; iIcntInitPerGrid(0/*_time*/); //initialization before gpu grid start + } +} + +int interconnect_has_buffer(unsigned int input_node, + unsigned int *size) +{ + + unsigned int input = node_map[input_node]; + int has_buffer; + int tot_req_size = 0; + for (unsigned int i=0; i<(_n_mem+_n_shader);i++ ) { + if (size[i]) { + tot_req_size+= size[i]; + } + } + unsigned int n_flits = tot_req_size / _flit_size + ((tot_req_size % _flit_size)? 1:0); + if (!(fixed_lat_icnt || perfect_icnt)) { + has_buffer = (traffic[0]->_partial_packets[input][0].size() + n_flits) <= input_buffer_capacity; + if ((net_c>1) && is_mem(input)) { + has_buffer = (traffic[1]->_partial_packets[input][0].size() + n_flits) <= input_buffer_capacity; + } + } else { + has_buffer = 1; + } + return has_buffer; +} + +void interconnect_push ( unsigned int input_node, unsigned int output_node, + void* data, unsigned int size) +{ + int output = node_map[output_node]; + int input = node_map[input_node]; + +#if 0 + cout<<"Call interconnect push input: "<icnt_receive_time = gpu_sim_cycle + fixed_latency(input,output); + out_buf_fixedlat_buf[output].push(data); //deliver the whole packet to destination in zero cycles + if (out_buf_fixedlat_buf[output].size() > max_fixedlat_buf_size[output]) { + max_fixedlat_buf_size[output]= out_buf_fixedlat_buf[output].size(); + } + } else { + + unsigned int n_flits = size / _flit_size + ((size % _flit_size)? 1:0); + int nc; + if (!doub_net) { + nc=0; + } else //doub_net enabled + if (is_shd(input) ) { + nc=0; + } else { + nc=1; + } + traffic[nc]->_GeneratePacket( input, n_flits, 0 /*class*/, traffic[nc]->_time, data, output); +#if DOUB + cout <<"Traffic[" << nc << "] (mapped) sending form "<< input << " to " << output <icnt_receive_time <= gpu_sim_cycle) { + data = out_buf_fixedlat_buf[output].top(); + out_buf_fixedlat_buf[output].pop(); + assert (((mem_fetch_t *)data)->icnt_receive_time); + } + } + } else { + unsigned vc; + unsigned turn = round_robin_turn[output]; + for (vc=0;(vc2) { + cout <InsertRandomFaults( icnt_config ); + } + + traffic[i] = new TrafficManager ( icnt_config, net[i], i/*id*/ ); + } + + fixed_lat_icnt = icnt_config.GetInt( "fixed_lat_per_hop" ); + + if (icnt_config.GetInt( "perfect_icnt" )) { + perfect_icnt = true; + fixed_lat_icnt = 1; + } + _flit_size = icnt_config.GetInt( "flit_size" ); + if (icnt_config.GetInt("ejection_buf_size")) { + ejection_buffer_capacity = icnt_config.GetInt( "ejection_buf_size" ) ; + } else { + ejection_buffer_capacity = icnt_config.GetInt( "vc_buf_size" ); + } + boundary_buf_capacity = icnt_config.GetInt( "boundary_buf_size" ) ; + if (icnt_config.GetInt("input_buf_size")) { + input_buffer_capacity = icnt_config.GetInt("input_buf_size"); + } else { + if (gpgpu_cache_dl1_opt && !gpgpu_no_dl1) { + int l1cache_linesize = 32; + sscanf(gpgpu_cache_dl1_opt,"%*d:%d:%*d:%*c", &l1cache_linesize); + input_buffer_capacity = gpu_n_thread_per_shader*(l1cache_linesize/_flit_size+(int)ceil(8.0f/_flit_size)); + } else { + input_buffer_capacity = gpu_n_thread_per_shader*((int)ceil(8.0f/_flit_size)); + } + } + create_buf(traffic[0]->_dests,warp_size,icnt_config.GetInt( "num_vcs" )); + MATLAB_OUTPUT = icnt_config.GetInt("MATLAB_OUTPUT"); + DISPLAY_LAT_DIST = icnt_config.GetInt("DISPLAY_LAT_DIST"); + DISPLAY_HOP_DIST = icnt_config.GetInt("DISPLAY_HOP_DIST"); + DISPLAY_PAIR_LATENCY = icnt_config.GetInt("DISPLAY_PAIR_LATENCY"); + create_node_map(n_shader,n_mem,traffic[0]->_dests, icnt_config.GetInt("use_map")); + for (unsigned i=0;i_FirstStep(); + } +} + +void advance_interconnect () +{ + if (!fixed_lat_icnt) { + for (unsigned i=0;i_Step( ); + } + } +} + +unsigned interconnect_busy() +{ + unsigned i,j; + for(i=0; i_measured_in_flight) { + return 1; + } + } + for ( i=0 ;i<(_n_shader+_n_mem);i++ ) { + if ( !traffic[0]->_partial_packets[i] [0].empty() ) { + return 1; + } + if ( doub_net && !traffic[1]->_partial_packets[i] [0].empty() ) { + return 1; + } + for ( j=0;j* [src_n]; + clock_boundary_buf = new boundary_buf* [src_n]; + round_robin_turn.resize( src_n ); + for (i=0;i[vc_n]; + clock_boundary_buf[i]= new boundary_buf[vc_n]; + round_robin_turn[vc_n-1]; + } + if (fixed_lat_icnt) { + out_buf_fixedlat_buf = new priority_queue, mycomparison> [src_n]; + max_fixedlat_buf_size = new unsigned int [src_n]; + for (i=0;ivc; + assert (ejection_buf[output][vc].size() < ejection_buffer_capacity); + ejection_buf[output][vc].push(flit); +} + +void transfer2boundary_buf(int output) { + Flit* flit; + unsigned vc; + for (vc=0; vcdata, flit->tail); + traffic[flit->net_num]->credit_return_queue[output].push(flit); //will send back credit + if ( flit->head ) { + assert (flit->dest == output); + } +#if DOUB + cout <<"Traffic " <write? mf->request_uid : mf->mshr->insts[0].uid; + long int cycle = gpu_sim_cycle + gpu_tot_sim_cycle; + int req_type = mf->write? WT_REQ : RD_REQ; + if (is_mem(input)) { + time_vector_update( uid, MR_2SH_ICNT_INJECTED, cycle, req_type ); + } else { + time_vector_update( uid, MR_ICNT_INJECTED, cycle,req_type ); + } +} diff --git a/src/intersim/interconnect_interface.h b/src/intersim/interconnect_interface.h new file mode 100644 index 0000000..541f992 --- /dev/null +++ b/src/intersim/interconnect_interface.h @@ -0,0 +1,35 @@ +#ifndef INTERCONNECT_INTERFACE_H +#define INTERCONNECT_INTERFACE_H + +#include "flit.hpp" +#include "trafficmanager.hpp" + +struct glue_buf { + int flit_c; // flit count + void *data; + int net_num; // which network is this flit in (we might have several icnt networks) + int src; + int dest; +}; + +//node side functions +int interconnect_has_buffer(unsigned int input, unsigned int *size); +void interconnect_push ( unsigned int input, unsigned int output, + void* data, unsigned int size); +void* interconnect_pop(unsigned int output); +void init_interconnect (char* config_file, + unsigned int n_shader, unsigned int n_mem); +void advance_interconnect(); +unsigned interconnect_busy(); +void interconnect_stats() ; + +//interconnect side functions +void create_buf(int src_n,int warp_n, int vc_n); +int in_map (int input) ; +void write_out_buf(int output, Flit * data); +void transfer2boundary_buf(int output); +void time_vector_update_icnt_injected(void* mf, int input); + +#endif + + diff --git a/src/intersim/iq_router.cpp b/src/intersim/iq_router.cpp new file mode 100644 index 0000000..137c0bb --- /dev/null +++ b/src/intersim/iq_router.cpp @@ -0,0 +1,633 @@ +#include +#include +#include +#include +#include + +#include "iq_router.hpp" + +IQRouter::IQRouter( const Configuration& config, + Module *parent, string name, int id, + int inputs, int outputs ) +: Router( config, + parent, name, + id, + inputs, outputs ) +{ + string alloc_type; + ostringstream vc_name; + + _vcs = config.GetInt( "num_vcs" ); + _vc_size = config.GetInt( "vc_buf_size" ); + + _iq_time = 0; + + _output_extra_latency = config.GetInt( "output_extra_latency" ); + + // Routing + + _rf = GetRoutingFunction( config ); + + // Alloc VC's + + _vc = new VC * [_inputs]; + + for ( int i = 0; i < _inputs; ++i ) { + _vc[i] = new VC [_vcs]; + for( int j=0; j < _vcs; j++ ) + _vc[i][j].init( config, _outputs ); + + for ( int v = 0; v < _vcs; ++v ) { // Name the vc modules + vc_name << "vc_i" << i << "_v" << v; + _vc[i][v].SetName( this, vc_name.str( ) ); + vc_name.seekp( 0, ios::beg ); + } + } + + // Alloc next VCs' buffer state + + _next_vcs = new BufferState [_outputs]; + for( int j=0; j < _outputs; j++ ) { + _next_vcs[j].init( config ); + } + + for ( int o = 0; o < _outputs; ++o ) { + vc_name << "next_vc_o" << o; + _next_vcs[o].SetName( this, vc_name.str( ) ); + vc_name.seekp( 0, ios::beg ); + } + + // Alloc allocators + + config.GetStr( "vc_allocator", alloc_type ); + _vc_allocator = Allocator::NewAllocator( config, + this, "vc_allocator", + alloc_type, + _vcs*_inputs, 1, + _vcs*_outputs, 1 ); + + if ( !_vc_allocator ) { + cout << "ERROR: Unknown vc_allocator type " << alloc_type << endl; + exit(-1); + } + + config.GetStr( "sw_allocator", alloc_type ); + _sw_allocator = Allocator::NewAllocator( config, + this, "sw_allocator", + alloc_type, + _inputs*_input_speedup, _input_speedup, + _outputs*_output_speedup, _output_speedup ); + + if ( !_sw_allocator ) { + cout << "ERROR: Unknown sw_allocator type " << alloc_type << endl; + exit(-1); + } + + _sw_rr_offset = new int [_inputs*_input_speedup]; + for ( int i = 0; i < _inputs*_input_speedup; ++i ) { + _sw_rr_offset[i] = 0; + } + + // Alloc pipelines (to simulate processing/transmission delays) + + _crossbar_pipe = + new PipelineFIFO( this, "crossbar_pipeline", _outputs*_output_speedup, + _st_prepare_delay + _st_final_delay ); + + _credit_pipe = + new PipelineFIFO( this, "credit_pipeline", _inputs, + _credit_delay ); + + // Input and output queues + + _input_buffer = new queue [_inputs]; + _output_buffer = new queue > [_outputs]; + + _in_cred_buffer = new queue [_inputs]; + _out_cred_buffer = new queue [_outputs]; + + // Switch configuration (when held for multiple cycles) + + _hold_switch_for_packet = config.GetInt( "hold_switch_for_packet" ); + _switch_hold_in = new int [_inputs*_input_speedup]; + _switch_hold_out = new int [_outputs*_output_speedup]; + _switch_hold_vc = new int [_inputs*_input_speedup]; + + for ( int i = 0; i < _inputs*_input_speedup; ++i ) { + _switch_hold_in[i] = -1; + _switch_hold_vc[i] = -1; + } + + for ( int i = 0; i < _outputs*_output_speedup; ++i ) { + _switch_hold_out[i] = -1; + } +} + +IQRouter::~IQRouter( ) +{ + for ( int i = 0; i < _inputs; ++i ) { + delete [] _vc[i]; + } + + delete [] _vc; + delete [] _next_vcs; + + delete _vc_allocator; + delete _sw_allocator; + + delete [] _sw_rr_offset; + + delete _crossbar_pipe; + delete _credit_pipe; + + delete [] _input_buffer; + delete [] _output_buffer; + + delete [] _in_cred_buffer; + delete [] _out_cred_buffer; + + delete [] _switch_hold_in; + delete [] _switch_hold_vc; + delete [] _switch_hold_out; +} + +void IQRouter::ReadInputs( ) +{ + _ReceiveFlits( ); + _ReceiveCredits( ); +} + +void IQRouter::InternalStep( ) +{ + _InputQueuing( ); + _Route( ); + _VCAlloc( ); + _SWAlloc( ); + + for ( int input = 0; input < _inputs; ++input ) { + for ( int vc = 0; vc < _vcs; ++vc ) { + _vc[input][vc].AdvanceTime( ); + } + } + + _crossbar_pipe->Advance( ); + _credit_pipe->Advance( ); + ++_iq_time; + + _OutputQueuing( ); +} + +#include "interconnect_interface.h" +void IQRouter::WriteOutputs( ) +{ +// Flit *f; +// for ( int output = 0; output < _outputs; ++output ) { +// if ( !_output_buffer[output].empty( ) ) { + // f = _output_buffer[output].front( ); + // if ( out_buf_has_space (f->dest,f->data, f->head , f->tail) ){ + _SendFlits( ); + _SendCredits( ); + //} + //} + // } +} + +void IQRouter::_ReceiveFlits( ) +{ + Flit *f; + + for ( int input = 0; input < _inputs; ++input ) { + f = *((*_input_channels)[input]); + + if ( f ) { + _input_buffer[input].push( f ); + } + } +} + +void IQRouter::_ReceiveCredits( ) +{ + Credit *c; + + for ( int output = 0; output < _outputs; ++output ) { + c = *((*_output_credits)[output]); + + if ( c ) { + _out_cred_buffer[output].push( c ); + } + } +} + +void IQRouter::_InputQueuing( ) +{ + Flit *f; + Credit *c; + VC *cur_vc; + + for ( int input = 0; input < _inputs; ++input ) { + if ( !_input_buffer[input].empty( ) ) { + f = _input_buffer[input].front( ); + _input_buffer[input].pop( ); + + cur_vc = &_vc[input][f->vc]; + + if ( !cur_vc->AddFlit( f ) ) { + Error( "VC buffer overflow" ); + } + + if ( f->watch ) { + cout << "Received flit at " << _fullname << endl; + cout << *f; + } + } + } + + for ( int input = 0; input < _inputs; ++input ) { + for ( int vc = 0; vc < _vcs; ++vc ) { + + cur_vc = &_vc[input][vc]; + + if ( cur_vc->GetState( ) == VC::idle ) { + f = cur_vc->FrontFlit( ); + + if ( f ) { + if ( !f->head ) { + Error( "Received non-head flit at idle VC" ); + } + + cur_vc->Route( _rf, this, f, input ); + cur_vc->SetState( VC::routing ); + } + } + } + } + + for ( int output = 0; output < _outputs; ++output ) { + if ( !_out_cred_buffer[output].empty( ) ) { + c = _out_cred_buffer[output].front( ); + _out_cred_buffer[output].pop( ); + + _next_vcs[output].ProcessCredit( c ); + delete c; + } + } +} + +void IQRouter::_Route( ) +{ + VC *cur_vc; + + for ( int input = 0; input < _inputs; ++input ) { + for ( int vc = 0; vc < _vcs; ++vc ) { + + cur_vc = &_vc[input][vc]; + + if ( ( cur_vc->GetState( ) == VC::routing ) && + ( cur_vc->GetStateTime( ) >= _routing_delay ) ) { + + cur_vc->SetState( VC::vc_alloc ); + } + } + } +} + +void IQRouter::_AddVCRequests( VC* cur_vc, int input_index, bool watch ) +{ + const OutputSet *route_set; + BufferState *dest_vc; + int vc_cnt, out_vc; + int in_priority, out_priority; + + route_set = cur_vc->GetRouteSet( ); + out_priority = cur_vc->GetPriority( ); + + for ( int output = 0; output < _outputs; ++output ) { + vc_cnt = route_set->NumVCs( output ); + dest_vc = &_next_vcs[output]; + + for ( int vc_index = 0; vc_index < vc_cnt; ++vc_index ) { + out_vc = route_set->GetVC( output, vc_index, &in_priority ); + + if ( watch ) { + cout << " trying vc " << out_vc << " (out = " << output << ") ... "; + } + + // 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, + // 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_vc->IsAvailableFor( out_vc ) ) { + _vc_allocator->AddRequest( input_index, output*_vcs + out_vc, 1, + in_priority, out_priority ); + if ( watch ) { + cout << "available" << endl; + } + } else if ( watch ) { + cout << "busy" << endl; + } + } + } +} + +void IQRouter::_VCAlloc( ) +{ + VC *cur_vc; + BufferState *dest_vc; + int input_and_vc; + int match_input; + int match_vc; + + Flit *f; + bool watched; + + _vc_allocator->Clear( ); + watched = false; + + for ( int input = 0; input < _inputs; ++input ) { + for ( int vc = 0; vc < _vcs; ++vc ) { + + cur_vc = &_vc[input][vc]; + + if ( ( cur_vc->GetState( ) == VC::vc_alloc ) && + ( cur_vc->GetStateTime( ) >= _vc_alloc_delay ) ) { + + f = cur_vc->FrontFlit( ); + if ( f->watch ) { + cout << "VC requesting allocation at " << _fullname << endl; + cout << " input_index = " << input*_vcs + vc << endl; + cout << *f; + watched = true; + } + + _AddVCRequests( cur_vc, input*_vcs + vc, f->watch ); + } + } + } + + /*if ( watched ) { + _vc_allocator->PrintRequests( ); + }*/ + + _vc_allocator->Allocate( ); + + // Winning flits get a VC + + for ( int output = 0; output < _outputs; ++output ) { + for ( int vc = 0; vc < _vcs; ++vc ) { + input_and_vc = _vc_allocator->InputAssigned( output*_vcs + vc ); + + if ( input_and_vc != -1 ) { + match_input = input_and_vc / _vcs; + match_vc = input_and_vc - match_input*_vcs; + + cur_vc = &_vc[match_input][match_vc]; + dest_vc = &_next_vcs[output]; + + cur_vc->SetState( VC::active ); + cur_vc->SetOutput( output, vc ); + dest_vc->TakeBuffer( vc ); + + f = cur_vc->FrontFlit( ); + + if ( f->watch ) { + cout << "Granted VC allocation at " << _fullname + << " (input index " << input_and_vc << " )" << endl; + cout << *f; + } + } + } + } +} + +void IQRouter::_SWAlloc( ) +{ + Flit *f; + Credit *c; + + VC *cur_vc; + BufferState *dest_vc; + + int input; + int output; + int vc; + + int expanded_input; + int expanded_output; + + _sw_allocator->Clear( ); + + for ( input = 0; input < _inputs; ++input ) { + for ( int s = 0; s < _input_speedup; ++s ) { + expanded_input = s*_inputs + input; + + // Arbitrate (round-robin) between multiple + // requesting VCs at the same input (handles + // the case when multiple VC's are requesting + // the same output port) + vc = _sw_rr_offset[ expanded_input ]; + + for ( int v = 0; v < _vcs; ++v ) { + + // This continue acounts for the interleaving of + // VCs when input speedup is used + if ( ( vc % _input_speedup ) != s ) { + vc = ( vc + 1 ) % _vcs; + continue; + } + + cur_vc = &_vc[input][vc]; + + if ( ( cur_vc->GetState( ) == VC::active ) && + ( !cur_vc->Empty( ) ) ) { + + dest_vc = &_next_vcs[cur_vc->GetOutputPort( )]; + + if ( !dest_vc->IsFullFor( cur_vc->GetOutputVC( ) ) ) { + + // 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. + + assert( expanded_input == (vc%_input_speedup)*_inputs + input ); + expanded_output = (input%_output_speedup)*_outputs + cur_vc->GetOutputPort( ); + + if ( ( _switch_hold_in[expanded_input] == -1 ) && + ( _switch_hold_out[expanded_output] == -1 ) ) { + + // We could have requested this same input-output pair in a previous + // iteration, only replace the previous request if the current + // request has a higher priority (this is default behavior of the + // allocators). Switch allocation priorities are strictly + // determined by the packet priorities. + + _sw_allocator->AddRequest( expanded_input, expanded_output, vc, + cur_vc->GetPriority( ), + cur_vc->GetPriority( ) ); + } + } + } + + vc = ( vc + 1 ) % _vcs; + } + } + } + + _sw_allocator->Allocate( ); + + // Winning flits cross the switch + + _crossbar_pipe->WriteAll( 0 ); + + for ( int input = 0; input < _inputs; ++input ) { + c = 0; + + for ( int s = 0; s < _input_speedup; ++s ) { + + expanded_input = s*_inputs + input; + + if ( _switch_hold_in[expanded_input] != -1 ) { + expanded_output = _switch_hold_in[expanded_input]; + vc = _switch_hold_vc[expanded_input]; + cur_vc = &_vc[input][vc]; + + if ( cur_vc->Empty( ) ) { // Cancel held match if VC is empty + expanded_output = -1; + } + } else { + expanded_output = _sw_allocator->OutputAssigned( expanded_input ); + } + + if ( expanded_output >= 0 ) { + output = expanded_output % _outputs; + + if ( _switch_hold_in[expanded_input] == -1 ) { + vc = _sw_allocator->ReadRequest( expanded_input, expanded_output ); + cur_vc = &_vc[input][vc]; + } + + if ( _hold_switch_for_packet ) { + _switch_hold_in[expanded_input] = expanded_output; + _switch_hold_vc[expanded_input] = vc; + _switch_hold_out[expanded_output] = expanded_input; + } + + assert( ( cur_vc->GetState( ) == VC::active ) && + ( !cur_vc->Empty( ) ) && + ( cur_vc->GetOutputPort( ) == ( expanded_output % _outputs ) ) ); + + dest_vc = &_next_vcs[cur_vc->GetOutputPort( )]; + + assert( !dest_vc->IsFullFor( cur_vc->GetOutputVC( ) ) ); + + // Forward flit to crossbar and send credit back + f = cur_vc->RemoveFlit( ); + + f->hops++; + + if ( f->watch ) { + cout << "Forwarding flit through crossbar at " << _fullname << ":" << endl; + cout << *f; + } + + if ( !c ) { + c = _NewCredit( _vcs ); + } + + c->vc[c->vc_cnt] = f->vc; + c->vc_cnt++; + + f->vc = cur_vc->GetOutputVC( ); + dest_vc->SendingFlit( f ); + + _crossbar_pipe->Write( f, expanded_output ); + + if ( f->tail ) { + cur_vc->SetState( VC::idle ); + + _switch_hold_in[expanded_input] = -1; + _switch_hold_vc[expanded_input] = -1; + _switch_hold_out[expanded_output] = -1; + } + + _sw_rr_offset[expanded_input] = ( f->vc + 1 ) % _vcs; + } + } + + _credit_pipe->Write( c, input ); + } +} + +void IQRouter::_OutputQueuing( ) +{ + Flit *f; + Credit *c; + int expanded_output; + + for ( int output = 0; output < _outputs; ++output ) { + for ( int t = 0; t < _output_speedup; ++t ) { + expanded_output = _outputs*t + output; + f = _crossbar_pipe->Read( expanded_output ); + + if ( f ) { + _output_buffer[output].push( make_pair(f,_iq_time) ); + } + } + } + + for ( int input = 0; input < _inputs; ++input ) { + c = _credit_pipe->Read( input ); + + if ( c ) { + _in_cred_buffer[input].push( c ); + } + } +} + +void IQRouter::_SendFlits( ) +{ + Flit *f; + + for ( int output = 0; output < _outputs; ++output ) { + + f = NULL; + + if ( !_output_buffer[output].empty( ) ) { + if ((_iq_time - _output_buffer[output].front().second) >= _output_extra_latency) { + f = _output_buffer[output].front( ).first; + _output_buffer[output].pop( ); + } + } + + *(*_output_channels)[output] = f; + } +} + +void IQRouter::_SendCredits( ) +{ + Credit *c; + + for ( int input = 0; input < _inputs; ++input ) { + if ( !_in_cred_buffer[input].empty( ) ) { + c = _in_cred_buffer[input].front( ); + _in_cred_buffer[input].pop( ); + } else { + c = 0; + } + + *(*_input_credits)[input] = c; + } +} + +void IQRouter::Display( ) const +{ + for ( int input = 0; input < _inputs; ++input ) { + for ( int v = 0; v < _vcs; ++v ) { + _vc[input][v].Display( ); + } + } +} diff --git a/src/intersim/iq_router.hpp b/src/intersim/iq_router.hpp new file mode 100644 index 0000000..47db1fa --- /dev/null +++ b/src/intersim/iq_router.hpp @@ -0,0 +1,75 @@ +#ifndef _IQ_ROUTER_HPP_ +#define _IQ_ROUTER_HPP_ + +#include +#include + +#include "module.hpp" +#include "router.hpp" +#include "vc.hpp" +#include "allocator.hpp" +#include "routefunc.hpp" +#include "outputset.hpp" +#include "buffer_state.hpp" +#include "pipefifo.hpp" + +class IQRouter : public Router { + int _vcs; + int _vc_size; + + int _iq_time; + int _output_extra_latency; + + VC **_vc; + BufferState *_next_vcs; + + Allocator *_vc_allocator; + Allocator *_sw_allocator; + + int *_sw_rr_offset; + + tRoutingFunction _rf; + + PipelineFIFO *_crossbar_pipe; + PipelineFIFO *_credit_pipe; + + queue *_input_buffer; + queue > *_output_buffer; + + queue *_in_cred_buffer; + queue *_out_cred_buffer; + + int _hold_switch_for_packet; + int *_switch_hold_in; + int *_switch_hold_out; + int *_switch_hold_vc; + + void _ReceiveFlits( ); + void _ReceiveCredits( ); + + void _InputQueuing( ); + void _Route( ); + void _VCAlloc( ); + void _SWAlloc( ); + void _OutputQueuing( ); + + void _SendFlits( ); + void _SendCredits( ); + + void _AddVCRequests( VC* cur_vc, int input_index, bool watch = false ); + +public: + IQRouter( const Configuration& config, + Module *parent, string name, int id, + int inputs, int outputs ); + + virtual ~IQRouter( ); + + virtual void ReadInputs( ); + virtual void InternalStep( ); + virtual void WriteOutputs( ); + + void Display( ) const; +}; + +#endif diff --git a/src/intersim/islip.cpp b/src/intersim/islip.cpp new file mode 100644 index 0000000..1aafacc --- /dev/null +++ b/src/intersim/islip.cpp @@ -0,0 +1,185 @@ +#include "booksim.hpp" +#include + +#include "islip.hpp" +#include "random_utils.hpp" + +//#define DEBUG_ISLIP + +iSLIP_Sparse::iSLIP_Sparse( const Configuration &config, + Module *parent, const string& name, + int inputs, int outputs ) : +SparseAllocator( config, parent, name, inputs, outputs ) +{ + _iSLIP_iter = config.GetInt( "alloc_iters" ); + + _grants = new int [_outputs]; + _gptrs = new int [_outputs]; + _aptrs = new int [_inputs]; + + for ( int i = 0; i < _inputs; ++i ) { + _aptrs[i] = 0; + } + for ( int j = 0; j < _outputs; ++j ) { + _gptrs[j] = 0; + } +} + +iSLIP_Sparse::~iSLIP_Sparse( ) +{ + delete [] _grants; + delete [] _gptrs; + delete [] _aptrs; +} + +void iSLIP_Sparse::Allocate( ) +{ + int input; + int output; + + int input_offset; + int output_offset; + + list::iterator p; + bool wrapped; + + _ClearMatching( ); + + for ( int iter = 0; iter < _iSLIP_iter; ++iter ) { + // Grant phase + + for ( output = 0; output < _outputs; ++output ) { + _grants[output] = -1; + + // Skip loop if there are no requests + // or the output is already matched + if ( ( _out_req[output].empty( ) ) || + ( _outmatch[output] != -1 ) ) { + continue; + } + + // A round-robin arbiter between input requests + input_offset = _gptrs[output]; + + p = _out_req[output].begin( ); + while ( ( p != _out_req[output].end( ) ) && + ( p->port < input_offset ) ) { + p++; + } + + wrapped = false; + while ( (!wrapped) || ( p->port < input_offset ) ) { + if ( p == _out_req[output].end( ) ) { + if ( wrapped ) { + break; + } + // p is valid here because empty lists + // are skipped (above) + p = _out_req[output].begin( ); + wrapped = true; + } + + input = p->port; + + // we know the output is free (above) and + // if the input is free, grant request + if ( _inmatch[input] == -1 ) { + _grants[output] = input; + break; + } + + p++; + } + } + +#ifdef DEBUG_ISLIP + cout << "grants: "; + for ( int i = 0; i < _outputs; ++i ) { + cout << _grants[i] << " "; + } + cout << endl; + + cout << "aptrs: "; + for ( int i = 0; i < _inputs; ++i ) { + cout << _aptrs[i] << " "; + } + cout << endl; +#endif + + // Accept phase + + for ( input = 0; input < _inputs; ++input ) { + + if ( _in_req[input].empty( ) ) { + continue; + } + + // A round-robin arbiter between output grants + output_offset = _aptrs[input]; + + p = _in_req[input].begin( ); + while ( ( p != _in_req[input].end( ) ) && + ( p->port < output_offset ) ) { + p++; + } + + wrapped = false; + int flag ; + if ( p != _in_req[input].end( ) ) { + flag= (p->port < output_offset) ; + } else { + flag = true; + } + while ( (!wrapped) || (flag) ) { + if ( p == _in_req[input].end( ) ) { + if ( wrapped ) { + break; + } + // p is valid here because empty lists + // are skipped (above) + p = _in_req[input].begin( ); + wrapped = true; + } + + output = p->port; + + // we know the output is free (above) and + // if the input is free, grant request + if ( _grants[output] == input ) { + // Accept + _inmatch[input] = output; + _outmatch[output] = input; + + // Only update pointers if accepted during the 1st iteration + if ( iter == 0 ) { + _gptrs[output] = ( input + 1 ) % _inputs; + _aptrs[input] = ( output + 1 ) % _outputs; + } + + break; + } + + p++; + if ( p != _in_req[input].end( ) ) { + flag= (p->port < output_offset) ; + } else { + flag = true; + } + } + } + } + +#ifdef DEBUG_ISLIP + cout << "input match: "; + for ( int i = 0; i < _inputs; ++i ) { + cout << _inmatch[i] << " "; + } + cout << endl; + + cout << "output match: "; + for ( int j = 0; j < _outputs; ++j ) { + cout << _outmatch[j] << " "; + } + cout << endl; +#endif +} diff --git a/src/intersim/islip.hpp b/src/intersim/islip.hpp new file mode 100644 index 0000000..a7b5242 --- /dev/null +++ b/src/intersim/islip.hpp @@ -0,0 +1,22 @@ +#ifndef _ISLIP_HPP_ +#define _ISLIP_HPP_ + +#include "allocator.hpp" + +class iSLIP_Sparse : public SparseAllocator { + int _iSLIP_iter; + + int *_grants; + int *_gptrs; + int *_aptrs; + +public: + iSLIP_Sparse( const Configuration &config, + Module *parent, const string& name, + int inputs, int outputs ); + ~iSLIP_Sparse( ); + + void Allocate( ); +}; + +#endif diff --git a/src/intersim/kncube.cpp b/src/intersim/kncube.cpp new file mode 100644 index 0000000..f4e1c71 --- /dev/null +++ b/src/intersim/kncube.cpp @@ -0,0 +1,253 @@ +#include "booksim.hpp" +#include +#include + +#include "kncube.hpp" +#include "random_utils.hpp" +#include "misc_utils.hpp" + +KNCube::KNCube( const Configuration &config, bool mesh ) : +Network( config ) +{ + _mesh = mesh; + + _ComputeSize( config ); + _Alloc( ); + _BuildNet( config ); +} + +void KNCube::_ComputeSize( const Configuration &config ) +{ + _k = config.GetInt( "k" ); + _n = config.GetInt( "n" ); + + gK = _k; gN = _n; + + _size = powi( _k, _n ); + _channels = 2*_n*_size; + + _sources = _size; + _dests = _size; +} + +void KNCube::_BuildNet( const Configuration &config ) +{ + int left_node; + int right_node; + + int right_input; + int left_input; + + int right_output; + int left_output; + + ostringstream router_name; + + for ( int node = 0; node < _size; ++node ) { + + router_name << "router"; + + for ( int dim_offset = _size / _k; dim_offset >= 1; dim_offset /= _k ) { + router_name << "_" << ( node / dim_offset ) % _k; + } + + _routers[node] = Router::NewRouter( config, this, router_name.str( ), + node, 2*_n + 1, 2*_n + 1 ); + + router_name.seekp( 0, ios::beg ); + + for ( int dim = 0; dim < _n; ++dim ) { + + left_node = _LeftNode( node, dim ); + right_node = _RightNode( node, dim ); + + // + // Current (N)ode + // (L)eft node + // (R)ight node + // + // L--->N<---R + // L<---N--->R + // + + right_input = _LeftChannel( right_node, dim ); + left_input = _RightChannel( left_node, dim ); + + _routers[node]->AddInputChannel( &_chan[right_input], &_chan_cred[right_input] ); + _routers[node]->AddInputChannel( &_chan[left_input], &_chan_cred[left_input] ); + + right_output = _RightChannel( node, dim ); + left_output = _LeftChannel( node, dim ); + + _routers[node]->AddOutputChannel( &_chan[right_output], &_chan_cred[right_output] ); + _routers[node]->AddOutputChannel( &_chan[left_output], &_chan_cred[left_output] ); + } + + _routers[node]->AddInputChannel( &_inject[node], &_inject_cred[node] ); + _routers[node]->AddOutputChannel( &_eject[node], &_eject_cred[node] ); + } +} + +int KNCube::_LeftChannel( int node, int dim ) +{ + // The base channel for a node is 2*_n*node + int base = 2*_n*node; + + // The offset for a left channel is 2*dim + 1 + int off = 2*dim + 1; + + return( base + off ); +} + +int KNCube::_RightChannel( int node, int dim ) +{ + // The base channel for a node is 2*_n*node + int base = 2*_n*node; + + // The offset for a right channel is 2*dim + int off = 2*dim; + + return( base + off ); +} + +int KNCube::_LeftNode( int node, int dim ) +{ + int k_to_dim = powi( _k, dim ); + int loc_in_dim = ( node / k_to_dim ) % _k; + int left_node; + + // if at the left edge of the dimension, wraparound + if ( loc_in_dim == 0 ) { + left_node = node + (_k-1)*k_to_dim; + } else { + left_node = node - k_to_dim; + } + + return left_node; +} + +int KNCube::_RightNode( int node, int dim ) +{ + int k_to_dim = powi( _k, dim ); + int loc_in_dim = ( node / k_to_dim ) % _k; + int right_node; + + // if at the right edge of the dimension, wraparound + if ( loc_in_dim == ( _k-1 ) ) { + right_node = node - (_k-1)*k_to_dim; + } else { + right_node = node + k_to_dim; + } + + return right_node; +} + +int KNCube::GetN( ) const +{ + return _n; +} + +int KNCube::GetK( ) const +{ + return _k; +} + +void KNCube::InsertRandomFaults( const Configuration &config ) +{ + int num_fails; + unsigned long prev_seed; + + int node, chan; + int i, j, t, n, c; + bool *fail_nodes; + bool available; + + bool edge; + + num_fails = config.GetInt( "link_failures" ); + + if ( num_fails ) { + prev_seed = RandomIntLong( ); + RandomSeed( config.GetInt( "fail_seed" ) ); + + fail_nodes = new bool [_size]; + + for ( i = 0; i < _size; ++i ) { + node = i; + + // edge test + edge = false; + for ( n = 0; n < _n; ++n ) { + if ( ( ( node % _k ) == 0 ) || + ( ( node % _k ) == _k - 1 ) ) { + edge = true; + } + node /= _k; + } + + if ( edge ) { + fail_nodes[i] = true; + } else { + fail_nodes[i] = false; + } + } + + for ( i = 0; i < num_fails; ++i ) { + j = RandomInt( _size - 1 ); + available = false; + + for ( t = 0; ( t < _size ) && (!available); ++t ) { + node = ( j + t ) % _size; + + if ( !fail_nodes[node] ) { + // check neighbors + c = RandomInt( 2*_n - 1 ); + + for ( n = 0; ( n < 2*_n ) && (!available); ++n ) { + chan = ( n + c ) % 2*_n; + + if ( chan % 1 ) { + available = fail_nodes[_LeftNode( node, chan/2 )]; + } else { + available = fail_nodes[_RightNode( node, chan/2 )]; + } + } + } + + if ( !available ) { + cout << "skipping " << node << endl; + } + } + + if ( t == _size ) { + Error( "Could not find another possible fault channel" ); + } + + + OutChannelFault( node, chan ); + fail_nodes[node] = true; + + for ( n = 0; ( n < _n ) && available ; ++n ) { + fail_nodes[_LeftNode( node, n )] = true; + fail_nodes[_RightNode( node, n )] = true; + } + + cout << "failure at node " << node << ", channel " + << chan << endl; + } + + delete [] fail_nodes; + + RandomSeed( prev_seed ); + } +} + +double KNCube::Capacity( ) const +{ + if ( _mesh ) { + return(double)_k / 4.0; + } else { + return(double)_k / 8.0; + } +} + diff --git a/src/intersim/kncube.hpp b/src/intersim/kncube.hpp new file mode 100644 index 0000000..5aba886 --- /dev/null +++ b/src/intersim/kncube.hpp @@ -0,0 +1,33 @@ +#ifndef _KNCUBE_HPP_ +#define _KNCUBE_HPP_ + +#include "network.hpp" + +class KNCube : public Network { + + bool _mesh; + + int _k; + int _n; + + void _ComputeSize( const Configuration &config ); + void _BuildNet( const Configuration &config ); + + int _LeftChannel( int node, int dim ); + int _RightChannel( int node, int dim ); + + int _LeftNode( int node, int dim ); + int _RightNode( int node, int dim ); + +public: + KNCube( const Configuration &config, bool mesh ); + + int GetN( ) const; + int GetK( ) const; + + double Capacity( ) const; + + void InsertRandomFaults( const Configuration &config ); +}; + +#endif diff --git a/src/intersim/loa.cpp b/src/intersim/loa.cpp new file mode 100644 index 0000000..c7501f3 --- /dev/null +++ b/src/intersim/loa.cpp @@ -0,0 +1,102 @@ +#include "booksim.hpp" +#include + +#include "loa.hpp" +#include "random_utils.hpp" + +LOA::LOA( const Configuration &config, + Module *parent, const string& name, + int inputs, int input_speedup, + int outputs, int output_speedup ) : +DenseAllocator( config, parent, name, inputs, outputs ) +{ + _req = new int [_inputs]; + _counts = new int [_outputs]; + + _rptr = new int [_inputs]; + _gptr = new int [_outputs]; +} + +LOA::~LOA( ) +{ + delete [] _req; + delete [] _counts; + delete [] _rptr; + delete [] _gptr; +} + +void LOA::Allocate( ) +{ + int input; + int output; + + int input_offset; + int output_offset; + + int lonely; + int lonely_cnt; + + // Clear matching + + // Count phase --- the number of requests + // per output is counted + + for ( int i = 0; i < _inputs; ++i ) { + _inmatch[i] = -1; + } + for ( int j = 0; j < _outputs; ++j ) { + _outmatch[j] = -1; + _counts[j] = 0; + + for ( int i = 0; i < _inputs; ++i ) { + _counts[j] += ( _request[i][j].label != -1 ) ? 1 : 0; + } + } + + // Request phase + for ( input = 0; input < _inputs; ++input ) { + + // Find the lonely output + output_offset = _rptr[input]; + lonely = -1; + lonely_cnt = _inputs + 1; + + for ( int o = 0; o < _outputs; ++o ) { + output = ( o + output_offset ) % _outputs; + + if ( ( _request[input][output].label != -1 ) && + ( _counts[output] < lonely_cnt ) ) { + lonely = output; + lonely_cnt = _counts[output]; + } + } + + // Request the lonely output (-1 for no request) + _req[input] = lonely; + } + + // Grant phase + for ( output = 0; output < _outputs; ++output ) { + input_offset = _gptr[output]; + + for ( int i = 0; i < _inputs; ++i ) { + input = ( i + input_offset ) % _inputs; + + if ( _req[input] == output ) { + // Grant! + + _inmatch[input] = output; + _outmatch[output] = input; + + _rptr[input] = ( _rptr[input] + 1 ) % _outputs; + _gptr[output] = ( _gptr[output] + 1 ) % _inputs; + + break; + } + } + } + + +} + + diff --git a/src/intersim/loa.hpp b/src/intersim/loa.hpp new file mode 100644 index 0000000..1adb4f9 --- /dev/null +++ b/src/intersim/loa.hpp @@ -0,0 +1,23 @@ +#ifndef _LOA_HPP_ +#define _LOA_HPP_ + +#include "allocator.hpp" + +class LOA : public DenseAllocator { + int *_counts; + int *_req; + + int *_rptr; + int *_gptr; + +public: + LOA( const Configuration &config, + Module *parent, const string& name, + int inputs, int input_speedup, + int outputs, int output_speedup ); + ~LOA( ); + + void Allocate( ); +}; + +#endif diff --git a/src/intersim/maxsize.cpp b/src/intersim/maxsize.cpp new file mode 100644 index 0000000..3d1349a --- /dev/null +++ b/src/intersim/maxsize.cpp @@ -0,0 +1,181 @@ +#include "booksim.hpp" +#include + +#include "maxsize.hpp" + +// shortest augmenting path: +// +// for all unmatched left nodes, +// push node onto work stack +// end +// +// for all j, +// from[j] = undefined +// end +// +// do, +// +// while( !stack.empty ), +// +// nl = stack.pop +// for each edge (nl,j), +// if ( ( lmatch[nl] != j ) && ( from[j] == undefined ) ), +// if ( rmatch[j] == undefined ), +// stop // augmenting path found +// else +// from[j] = nl +// newstack.push( rmatch[j] ) +// end +// end +// end +// end +// +// stack = newstack +// end +// + +//#define DEBUG_MAXSIZE +//#define PRINT_MATCHING + +MaxSizeMatch::MaxSizeMatch( const Configuration &config, + Module *parent, const string& name, + int inputs, int outputs ) : +DenseAllocator( config, parent, name, inputs, outputs ) +{ + _from = new int [_outputs]; + _s = new int [_inputs]; + _ns = new int [_inputs]; +} + +MaxSizeMatch::~MaxSizeMatch( ) +{ + delete [] _from; + delete [] _s; + delete [] _ns; +} + +void MaxSizeMatch::Allocate( ) +{ + // clear matching + for ( int i = 0; i < _inputs; ++i ) { + _inmatch[i] = -1; + } + for ( int j = 0; j < _outputs; ++j ) { + _outmatch[j] = -1; + } + + // augment as many times as possible + // (this is an O(N^3) maximum-size matching algorithm) + while ( _ShortestAugmenting( ) ); +} + + +bool MaxSizeMatch::_ShortestAugmenting( ) +{ + int i, j, jn; + int slen, nslen; + int *t; + + slen = 0; + for ( i = 0; i < _inputs; ++i ) { + if ( _inmatch[i] == -1 ) { // start with unmatched left nodes + _s[slen] = i; + slen++; + } + _from[i] = -1; + } + + for ( int iter = 0; iter < _inputs; iter++ ) { + nslen = 0; + + for ( int e = 0; e < slen; ++e ) { + i = _s[e]; + + for ( j = 0; j < _outputs; ++j ) { + if ( ( _request[i][j].label != -1 ) && // edge (i,j) exists + ( _inmatch[i] != j ) && // (i,j) is not contained in the current matching + ( _from[j] == -1 ) ) { // no shorter path to j exists + + _from[j] = i; // how did we get to j? + +#ifdef DEBUG_MAXSIZE + cout << " got to " << j << " from " << i << endl; +#endif + if ( _outmatch[j] == -1 ) { // j is unmatched -- augmenting path found + goto found_augmenting; + } else { // j is matched + _ns[nslen] = _outmatch[j]; // add the destination of this edge to the leaf nodes + nslen++; + +#ifdef DEBUG_MAXSIZE + cout << " adding " << _outmatch[j] << endl; +#endif + } + } + } + } + + // no augmenting path found yet, swap stacks + t = _s; + _s = _ns; + _ns = t; + slen = nslen; + } + + return false; // no augmenting paths + + found_augmenting: + + // the augmenting path ends at node j on the right + +#ifdef DEBUG_MAXSIZE + cout << "Found path: " << j << "c <- "; +#endif + + i = _from[j]; + _outmatch[j] = i; + +#ifdef DEBUG_MAXSIZE + cout << i; +#endif + + while ( _inmatch[i] != -1 ) { // loop until the end of the path + jn = _inmatch[i]; // remove previous edge (i,jn) and add (i,j) + _inmatch[i] = j; + +#ifdef DEBUG_MAXSIZE + cout << " <- " << j << "c <- "; +#endif + + j = jn; // add edge from (jn,in) + i = _from[j]; + _outmatch[j] = i; + +#ifdef DEBUG_MAXSIZE + cout << i; +#endif + } + +#ifdef DEBUG_MAXSIZE + cout << endl; +#endif + + _inmatch[i] = j; + +#ifdef PRINT_MATCHING + cout << "left matching: "; + + for ( i = 0; i < _inputs; i++ ) { + cout << _inmatch[i] << " "; + } + cout << endl; + + cout << "right matching: "; + for ( i = 0; i < _outputs; i++ ) { + cout << _outmatch[i] << " "; + } + cout << endl; +#endif + + return true; +} diff --git a/src/intersim/maxsize.hpp b/src/intersim/maxsize.hpp new file mode 100644 index 0000000..dcd4b39 --- /dev/null +++ b/src/intersim/maxsize.hpp @@ -0,0 +1,22 @@ +#ifndef _MAXSIZE_HPP_ +#define _MAXSIZE_HPP_ + +#include "allocator.hpp" + +class MaxSizeMatch : public DenseAllocator { + int *_from; // array to hold breadth-first tree + int *_s; // stack of leaf nodes in tree + int *_ns; // next stack + + bool _ShortestAugmenting( ); + +public: + MaxSizeMatch( const Configuration &config, + Module *parent, const string& name, + int inputs, int ouputs ); + ~MaxSizeMatch( ); + + void Allocate( ); +}; + +#endif diff --git a/src/intersim/misc_utils.cpp b/src/intersim/misc_utils.cpp new file mode 100644 index 0000000..6ba6a20 --- /dev/null +++ b/src/intersim/misc_utils.cpp @@ -0,0 +1,25 @@ +#include "booksim.hpp" +#include "misc_utils.hpp" + +int powi( int x, int y ) // compute x to the y +{ + int r = 1; + + for ( int i = 0; i < y; ++i ) { + r *= x; + } + + return r; +} + +int log_two( int x ) +{ + int r = 0; + + x >>= 1; + while ( x ) { + r++; x >>= 1; + } + + return r; +} diff --git a/src/intersim/misc_utils.hpp b/src/intersim/misc_utils.hpp new file mode 100644 index 0000000..856cbb7 --- /dev/null +++ b/src/intersim/misc_utils.hpp @@ -0,0 +1,7 @@ +#ifndef _MISC_UTILS_HPP_ +#define _MISC_UTILS_HPP_ + +int log_two( int x ); +int powi( int x, int y ); + +#endif diff --git a/src/intersim/module.cpp b/src/intersim/module.cpp new file mode 100644 index 0000000..e0a2289 --- /dev/null +++ b/src/intersim/module.cpp @@ -0,0 +1,63 @@ +#include "booksim.hpp" +#include +#include + +#include "module.hpp" + +Module::Module( ) +{ +} + +Module::Module( Module *parent, const string& name ) +{ + SetName( parent, name ); +} + +void Module::_AddChild( Module *child ) +{ + _children.push_back( child ); +} + +void Module::SetName( Module *parent, const string& name ) +{ + _name = name; + + if ( parent ) { + parent->_AddChild( this ); + _fullname = parent->_fullname + "/" + name; + } else { + _fullname = name; + } +} + +void Module::DisplayHierarchy( int level ) const +{ + vector::const_iterator mod_iter; + + for ( int l = 0; l < level; l++ ) { + cout << " "; + } + + cout << _name << endl; + + for ( mod_iter = _children.begin( ); + mod_iter != _children.end( ); mod_iter++ ) { + (*mod_iter)->DisplayHierarchy( level + 1 ); + } +} + +void Module::Error( const string& msg ) const +{ + cout << "Error in " << _fullname << " : " << msg << endl; + exit( -1 ); +} + +void Module::Debug( const string& msg ) const +{ + cout << "Debug (" << _fullname << ") : " << msg << endl; +} + +void Module::Display( ) const +{ + cout << "Display method not implemented for " << _fullname << endl; +} diff --git a/src/intersim/module.hpp b/src/intersim/module.hpp new file mode 100644 index 0000000..5acf0a2 --- /dev/null +++ b/src/intersim/module.hpp @@ -0,0 +1,33 @@ +#ifndef _MODULE_HPP_ +#define _MODULE_HPP_ + +#include "booksim.hpp" + +#include +#include + +class Module { +protected: + string _name; + string _fullname; + + vector _children; + + void _AddChild( Module *child ); + +public: + Module( ); + Module( Module *parent, const string& name ); + virtual ~Module( ) {} + + void SetName( Module *parent, const string& name ); + + void DisplayHierarchy( int level = 0 ) const; + + void Error( const string& msg ) const; + void Debug( const string& msg ) const; + + virtual void Display( ) const; +}; + +#endif diff --git a/src/intersim/network.cpp b/src/intersim/network.cpp new file mode 100644 index 0000000..48c07b4 --- /dev/null +++ b/src/intersim/network.cpp @@ -0,0 +1,156 @@ +#include "booksim.hpp" +#include "interconnect_interface.h" +#include + +#include "network.hpp" + +int gK = 0; +int gN = 0; +int gNodes = 0; + +Network::Network( const Configuration &config ) : +Module( 0, "network" ) +{ + _size = -1; + _sources = -1; + _dests = -1; + _channels = -1; +} + +Network::~Network( ) +{ + for ( int r = 0; r < _size; ++r ) { + delete _routers[r]; + } + + delete [] _routers; + + delete [] _inject; + delete [] _eject; + delete [] _chan; + + delete [] _chan_use; + + delete [] _inject_cred; + delete [] _eject_cred; + delete [] _chan_cred; +} + +void Network::_Alloc( ) +{ + assert( ( _size != -1 ) && + ( _sources != -1 ) && + ( _dests != -1 ) && + ( _channels != -1 ) ); + + _routers = new Router * [_size]; + gNodes = _sources; + + _inject = new Flit * [_sources]; + _eject = new Flit * [_dests]; + _chan = new Flit * [_channels]; + + _chan_use = new int [_channels]; + + for ( int i = 0; i < _channels; ++i ) { + _chan_use[i] = 0; + } + + _chan_use_cycles = 0; + + _inject_cred = new Credit * [_sources]; + _eject_cred = new Credit * [_dests]; + _chan_cred = new Credit * [_channels]; +} + +int Network::NumSources( ) const +{ + return _sources; +} + +int Network::NumDests( ) const +{ + return _dests; +} + +void Network::ReadInputs( ) +{ + for ( int r = 0; r < _size; ++r ) { + _routers[r]->ReadInputs( ); + } +} + +void Network::InternalStep( ) +{ + for ( int r = 0; r < _size; ++r ) { + _routers[r]->InternalStep( ); + } +} + +void Network::WriteOutputs( ) +{ + for ( int r = 0; r < _size; ++r ) { + _routers[r]->WriteOutputs( ); + } + + for ( int c = 0; c < _channels; ++c ) { + if ( _chan[c] ) { + _chan_use[c]++; + } + } + _chan_use_cycles++; +} + +void Network::WriteFlit( Flit *f, int source ) +{ + assert( ( source >= 0 ) && ( source < _sources ) ); + _inject[source] = f; +} + +Flit *Network::ReadFlit( int dest ) +{ + assert( ( dest >= 0 ) && ( dest < _dests ) ); + return _eject[dest]; +} + +void Network::WriteCredit( Credit *c, int dest ) +{ + assert( ( dest >= 0 ) && ( dest < _dests ) ); + _eject_cred[dest] = c; +} + +Credit *Network::ReadCredit( int source ) +{ + assert( ( source >= 0 ) && ( source < _sources ) ); + return _inject_cred[source]; +} + +void Network::InsertRandomFaults( const Configuration &config ) +{ + Error( "InsertRandomFaults not implemented for this topology!" ); +} + +void Network::OutChannelFault( int r, int c, bool fault ) +{ + assert( ( r >= 0 ) && ( r < _size ) ); + _routers[r]->OutChannelFault( c, fault ); +} + +double Network::Capacity( ) const +{ + return 1.0; +} + +void Network::Display( ) const +{ + for ( int r = 0; r < _size; ++r ) { + _routers[r]->Display( ); + } +// if (icnt_config.GetInt("blah") ) { + for ( int c = 0; c < _channels; ++c ) { + cout << "channel " << c << " used " + << 100.0 * (double)_chan_use[c] / (double)_chan_use_cycles + << "% of the time" << endl; + } + // } +} diff --git a/src/intersim/network.hpp b/src/intersim/network.hpp new file mode 100644 index 0000000..601ac7a --- /dev/null +++ b/src/intersim/network.hpp @@ -0,0 +1,72 @@ +#ifndef _NETWORK_HPP_ +#define _NETWORK_HPP_ + +#include + +#include "module.hpp" +#include "flit.hpp" +#include "credit.hpp" +#include "router.hpp" +#include "module.hpp" + +#include "config_utils.hpp" + +extern int gN; +extern int gK; + +extern int gNodes; + +class Network : public Module { +protected: + + int _size; + int _sources; + int _dests; + int _channels; + + Router **_routers; + + Flit **_inject; + Credit **_inject_cred; + + Flit **_eject; + Credit **_eject_cred; + + Flit **_chan; + Credit **_chan_cred; + + int *_chan_use; + int _chan_use_cycles; + + virtual void _ComputeSize( const Configuration &config ) = 0; + virtual void _BuildNet( const Configuration &config ) = 0; + + void _Alloc( ); + +public: + Network( const Configuration &config ); + virtual ~Network( ); + + void WriteFlit( Flit *f, int source ); + Flit *ReadFlit( int dest ); + + void WriteCredit( Credit *c, int dest ); + Credit *ReadCredit( int source ); + + int NumSources( ) const; + int NumDests( ) const; + + virtual void InsertRandomFaults( const Configuration &config ); + void OutChannelFault( int r, int c, bool fault = true ); + + virtual double Capacity( ) const; + + void ReadInputs( ); + void InternalStep( ); + void WriteOutputs( ); + + void Display( ) const; +}; + +#endif + diff --git a/src/intersim/outputset.cpp b/src/intersim/outputset.cpp new file mode 100644 index 0000000..b3970dd --- /dev/null +++ b/src/intersim/outputset.cpp @@ -0,0 +1,134 @@ +#include "booksim.hpp" +#include + +#include "outputset.hpp" + +OutputSet::OutputSet( int num_outputs ) +{ + _num_outputs = num_outputs; + + _outputs = new list [_num_outputs]; +} + +OutputSet::~OutputSet( ) +{ + delete [] _outputs; +} + +void OutputSet::Clear( ) +{ + for ( int i = 0; i < _num_outputs; ++i ) { + _outputs[i].clear( ); + } +} + +void OutputSet::Add( int output_port, int vc, int pri ) +{ + AddRange( output_port, vc, vc, pri ); +} + +void OutputSet::AddRange( int output_port, int vc_start, int vc_end, int pri ) +{ + assert( ( output_port >= 0 ) && + ( output_port < _num_outputs ) && + ( vc_start <= vc_end ) ); + + sSetElement s; + + s.vc_start = vc_start; + s.vc_end = vc_end; + s.pri = pri; + + _outputs[output_port].push_back( s ); +} + +int OutputSet::Size( ) const +{ + return _num_outputs; +} + +bool OutputSet::OutputEmpty( int output_port ) const +{ + assert( ( output_port >= 0 ) && + ( output_port < _num_outputs ) ); + + return _outputs[output_port].empty( ); +} + +int OutputSet::NumVCs( int output_port ) const +{ + assert( ( output_port >= 0 ) && + ( output_port < _num_outputs ) ); + + int total = 0; + + for ( list::const_iterator i = _outputs[output_port].begin( ); + i != _outputs[output_port].end( ); i++ ) { + total += i->vc_end - i->vc_start + 1; + } + + return total; +} + +int OutputSet::GetVC( int output_port, int vc_index, int *pri ) const +{ + assert( ( output_port >= 0 ) && + ( output_port < _num_outputs ) ); + + int range; + int remaining = vc_index; + int vc = -1; + + if ( pri ) { + *pri = -1; + } + + for ( list::const_iterator i = _outputs[output_port].begin( ); + i != _outputs[output_port].end( ); i++ ) { + + range = i->vc_end - i->vc_start + 1; + if ( remaining >= range ) { + remaining -= range; + } else { + vc = i->vc_start + remaining; + if ( pri ) { + *pri = i->pri; + } + break; + } + } + + return vc; +} + +bool OutputSet::GetPortVC( int *out_port, int *out_vc ) const +{ + bool single_output = false; + int used_outputs = 0; + + for ( int output = 0; output < _num_outputs; ++output ) { + + list::const_iterator i = _outputs[output].begin( ); + + if ( i != _outputs[output].end( ) ) { + ++used_outputs; + + if ( i->vc_start == i->vc_end ) { + *out_vc = i->vc_start; + *out_port = output; + single_output = true; + } else { + // multiple vc's selected + break; + } + } + + if ( used_outputs > 1 ) { + // multiple outputs selected + single_output = false; + break; + } + } + + return single_output; +} diff --git a/src/intersim/outputset.hpp b/src/intersim/outputset.hpp new file mode 100644 index 0000000..ad8d2ad --- /dev/null +++ b/src/intersim/outputset.hpp @@ -0,0 +1,36 @@ +#ifndef _OUTPUTSET_HPP_ +#define _OUTPUTSET_HPP_ + +#include +#include + +class OutputSet { + int _num_outputs; + + struct sSetElement { + int vc_start; + int vc_end; + int pri; + }; + + list *_outputs; + +public: + OutputSet( int num_outputs ); + ~OutputSet( ); + + void Clear( ); + void Add( int output_port, int vc, int pri = 0 ); + void AddRange( int output_port, int vc_start, int vc_end, int pri = 0 ); + + int Size( ) const; + bool OutputEmpty( int output_port ) const; + int NumVCs( int output_port ) const; + + int GetVC( int output_port, int vc_index, int *pri = 0 ) const; + bool GetPortVC( int *out_port, int *out_vc ) const; +}; + +#endif + + diff --git a/src/intersim/pim.cpp b/src/intersim/pim.cpp new file mode 100644 index 0000000..be5872c --- /dev/null +++ b/src/intersim/pim.cpp @@ -0,0 +1,98 @@ +#include "booksim.hpp" +#include + +#include "pim.hpp" +#include "random_utils.hpp" + +//#define DEBUG_PIM + +PIM::PIM( const Configuration &config, + Module *parent, const string& name, + int inputs, int outputs ) : +DenseAllocator( config, parent, name, inputs, outputs ) +{ + _PIM_iter = config.GetInt( "alloc_iters" ); + + _grants = new int [_outputs]; +} + +PIM::~PIM( ) +{ + delete [] _grants; +} + +void PIM::Allocate( ) +{ + int input; + int output; + + int input_offset; + int output_offset; + + _ClearMatching( ); + + for ( int iter = 0; iter < _PIM_iter; ++iter ) { + // Grant phase --- outputs randomly choose + // between one of their requests + + for ( output = 0; output < _outputs; ++output ) { + _grants[output] = -1; + + // A random arbiter between input requests + input_offset = RandomInt( _inputs - 1 ); + + for ( int i = 0; i < _inputs; ++i ) { + input = ( i + input_offset ) % _inputs; + + if ( ( _request[input][output].label != -1 ) && + ( _inmatch[input] == -1 ) && + ( _outmatch[output] == -1 ) ) { + + // Grant + _grants[output] = input; + break; + } + } + } + + // Accept phase -- inputs randomly choose + // between input_speedup of their grants + + for ( input = 0; input < _inputs; ++input ) { + + // A random arbiter between output grants + output_offset = RandomInt( _outputs - 1 ); + + for ( int o = 0; o < _outputs; ++o ) { + output = ( o + output_offset ) % _outputs; + + if ( _grants[output] == input ) { + + // Accept + _inmatch[input] = output; + _outmatch[output] = input; + + break; + } + } + } + } + +#ifdef DEBUG_PIM + if ( _outputs == 8 ) { + cout << "input match: " << endl; + for ( int i = 0; i < _inputs; ++i ) { + cout << " from " << i << " to " << _inmatch[i] << endl; + } + cout << endl; + } + + cout << "output match: "; + for ( int j = 0; j < _outputs; ++j ) { + cout << _outmatch[j] << " "; + } + cout << endl; +#endif +} + + diff --git a/src/intersim/pim.hpp b/src/intersim/pim.hpp new file mode 100644 index 0000000..1ce5535 --- /dev/null +++ b/src/intersim/pim.hpp @@ -0,0 +1,20 @@ +#ifndef _PIM_HPP_ +#define _PIM_HPP_ + +#include "allocator.hpp" + +class PIM : public DenseAllocator { + int _PIM_iter; + + int *_grants; +public: + PIM( const Configuration &config, + Module *parent, const string& name, + int inputs, int outputs ); + + ~PIM( ); + + void Allocate( ); +}; + +#endif diff --git a/src/intersim/pipefifo.hpp b/src/intersim/pipefifo.hpp new file mode 100644 index 0000000..8d434d8 --- /dev/null +++ b/src/intersim/pipefifo.hpp @@ -0,0 +1,76 @@ +#ifndef _PIPEFIFO_HPP_ +#define _PIPEFIFO_HPP_ + +#include "module.hpp" + +template class PipelineFIFO : public Module { + int _lanes; + int _depth; + + int _pipe_len; + int _pipe_ptr; + + T ***_data; + +public: + PipelineFIFO( Module *parent, const string& name, int lanes, int depth ); + ~PipelineFIFO( ); + + void Write( T* val, int lane = 0 ); + void WriteAll( T* val ); + + T* Read( int lane = 0 ); + + void Advance( ); +}; + +template PipelineFIFO::PipelineFIFO( Module *parent, + const string& name, + int lanes, int depth ) : +Module( parent, name ), +_lanes( lanes ), _depth( depth ) +{ + _pipe_len = depth + 1; + _pipe_ptr = 0; + + _data = new T ** [_lanes]; + for ( int l = 0; l < _lanes; ++l ) { + _data[l] = new T * [_pipe_len]; + + for ( int d = 0; d < _pipe_len; ++d ) { + _data[l][d] = 0; + } + } +} + +template PipelineFIFO::~PipelineFIFO( ) +{ + for ( int l = 0; l < _lanes; ++l ) { + delete [] _data[l]; + } + delete [] _data; +} + +template void PipelineFIFO::Write( T* val, int lane ) +{ + _data[lane][_pipe_ptr] = val; +} + +template void PipelineFIFO::WriteAll( T* val ) +{ + for ( int l = 0; l < _lanes; ++l ) { + _data[l][_pipe_ptr] = val; + } +} + +template T* PipelineFIFO::Read( int lane ) +{ + return _data[lane][_pipe_ptr]; +} + +template void PipelineFIFO::Advance( ) +{ + _pipe_ptr = ( _pipe_ptr + 1 ) % _pipe_len; +} + +#endif diff --git a/src/intersim/random_utils.cpp b/src/intersim/random_utils.cpp new file mode 100644 index 0000000..94a0ad5 --- /dev/null +++ b/src/intersim/random_utils.cpp @@ -0,0 +1,25 @@ +#include "booksim.hpp" +#include "random_utils.hpp" + +void RandomSeed( long seed ) +{ + ran_start( seed ); + ranf_start( seed ); +} + +int RandomInt( int max ) +// Returns a random integer in the range [0,max] +{ + return( ran_next( ) % (max+1) ); +} + +unsigned long RandomIntLong( ) +{ + return ran_next( ); +} + +float RandomFloat( float max ) +// Returns a random floating-point value in the rage [0,max] +{ + return( ranf_next( ) * max ); +} diff --git a/src/intersim/random_utils.hpp b/src/intersim/random_utils.hpp new file mode 100644 index 0000000..290953d --- /dev/null +++ b/src/intersim/random_utils.hpp @@ -0,0 +1,21 @@ +#ifndef _RANDOM_UTILS_HPP_ +#define _RANDOM_UTILS_HPP_ + +#include "rng.hpp" + +#ifdef USE_TWISTER +extern unsigned long int_genrand( ); +extern void int_lsgenrand( unsigned long seed_array[] ); +extern void int_sgenrand( unsigned long seed ); + +extern double float_genrand( ); +extern void float_lsgenrand( unsigned long seed_array[] ); +extern void float_sgenrand( unsigned long seed ); +#endif + +void RandomSeed( long seed ); +int RandomInt( int max ) ; +float RandomFloat( float max = 1.0 ); +unsigned long RandomIntLong( ); + +#endif diff --git a/src/intersim/rng.cpp b/src/intersim/rng.cpp new file mode 100644 index 0000000..47ddda3 --- /dev/null +++ b/src/intersim/rng.cpp @@ -0,0 +1,111 @@ +/* This program by D E Knuth is in the public domain and freely copyable + * AS LONG AS YOU MAKE ABSOLUTELY NO CHANGES! + * It is explained in Seminumerical Algorithms, 3rd edition, Section 3.6 + * (or in the errata to the 2nd edition --- see + * http://www-cs-faculty.stanford.edu/~knuth/taocp.html + * in the changes to Volume 2 on pages 171 and following). */ + +/* N.B. The MODIFICATIONS introduced in the 9th printing (2002) are + included here; there's no backwards compatibility with the original. */ + +/* This version also adopts Brendan McKay's suggestion to + accommodate naive users who forget to call ran_start(seed). */ + +/* If you find any bugs, please report them immediately to + * taocp@cs.stanford.edu + * (and you will be rewarded if the bug is genuine). Thanks! */ + +/************ see the book for explanations and caveats! *******************/ +/************ in particular, you need two's complement arithmetic **********/ + +#define KK 100 /* the long lag */ +#define LL 37 /* the short lag */ +#define MM (1L<<30) /* the modulus */ +#define mod_diff(x,y) (((x)-(y))&(MM-1)) /* subtraction mod MM */ + +long ran_x[KK]; /* the generator state */ + +#ifdef __STDC__ +void ran_array(long aa[],int n) +#else +void ran_array(aa,n) /* put n new random numbers in aa */ +long *aa; /* destination */ +int n; /* array length (must be at least KK) */ +#endif +{ + register int i,j; + for (j=0;j=MM) ss-=MM-2; /* cyclic shift 29 bits */ + } + x[1]++; /* make x[1] (and only x[1]) odd */ + for (ss=seed&(MM-1),t=TT-1; t; ) { + for (j=KK-1;j>0;j--) x[j+j]=x[j], x[j+j-1]=0; /* "square" */ + for (j=KK+KK-2;j>=KK;j--) + x[j-(KK-LL)]=mod_diff(x[j-(KK-LL)],x[j]), + x[j-KK]=mod_diff(x[j-KK],x[j]); + if (is_odd(ss)) { /* "multiply by z" */ + for (j=KK;j>0;j--) x[j]=x[j-1]; + x[0]=x[KK]; /* shift the buffer cyclically */ + x[LL]=mod_diff(x[LL],x[KK]); + } + if (ss) ss>>=1; + else t--; + } + for (j=0;j=0? *ran_arr_ptr++: ran_arr_cycle()) +long ran_arr_cycle() +{ + if (ran_arr_ptr==&ran_arr_dummy) + ran_start(314159L); /* the user forgot to initialize */ + ran_array(ran_arr_buf,QUALITY); + ran_arr_buf[100]=-1; + ran_arr_ptr=ran_arr_buf+1; + return ran_arr_buf[0]; +} + +#include +int main() +{ + register int m; long a[2009]; + ran_start(310952L); + for (m=0;m<=2009;m++) ran_array(a,1009); + printf("%ld\n", a[0]); /* 995235265 */ + ran_start(310952L); + for (m=0;m<=1009;m++) ran_array(a,2009); + printf("%ld\n", a[0]); /* 995235265 */ + return 0; +} + diff --git a/src/intersim/rng.hpp b/src/intersim/rng.hpp new file mode 100644 index 0000000..db09172 --- /dev/null +++ b/src/intersim/rng.hpp @@ -0,0 +1,10 @@ +#ifndef _RNG_HPP_ +#define _RNG_HPP_ + +void ran_start(long seed); +long ran_next( ); + +void ranf_start(long seed); +double ranf_next( ); + +#endif diff --git a/src/intersim/rng_double.cpp b/src/intersim/rng_double.cpp new file mode 100644 index 0000000..58a3c2c --- /dev/null +++ b/src/intersim/rng_double.cpp @@ -0,0 +1,115 @@ +/* This program by D E Knuth is in the public domain and freely copyable +* AS LONG AS YOU MAKE ABSOLUTELY NO CHANGES! +* It is explained in Seminumerical Algorithms, 3rd edition, Section 3.6 +* (or in the errata to the 2nd edition --- see +* http://www-cs-faculty.stanford.edu/~knuth/taocp.html +* in the changes to Volume 2 on pages 171 and following). */ + +/* N.B. The MODIFICATIONS introduced in the 9th printing (2002) are + included here; there's no backwards compatibility with the original. */ + +/* This version also adopts Brendan McKay's suggestion to + accommodate naive users who forget to call ranf_start(seed). */ + +/* If you find any bugs, please report them immediately to + * taocp@cs.stanford.edu + * (and you will be rewarded if the bug is genuine). Thanks! */ + +/************ see the book for explanations and caveats! *******************/ +/************ in particular, you need two's complement arithmetic **********/ + +#define KK 100 /* the long lag */ +#define LL 37 /* the short lag */ +#define mod_sum(x,y) (((x)+(y))-(int)((x)+(y))) /* (x+y) mod 1.0 */ + +double ran_u[KK]; /* the generator state */ + +#ifdef __STDC__ +void ranf_array(double aa[], int n) +#else +void ranf_array(aa,n) /* put n new random fractions in aa */ +double *aa; /* destination */ +int n; /* array length (must be at least KK) */ +#endif +{ + register int i,j; + for (j=0;j=1.0) ss-=1.0-2*ulp; /* cyclic shift of 51 bits */ + } + u[1]+=ulp; /* make u[1] (and only u[1]) "odd" */ + for (s=seed&0x3fffffff,t=TT-1; t; ) { + for (j=KK-1;j>0;j--) + u[j+j]=u[j],u[j+j-1]=0.0; /* "square" */ + for (j=KK+KK-2;j>=KK;j--) { + u[j-(KK-LL)]=mod_sum(u[j-(KK-LL)],u[j]); + u[j-KK]=mod_sum(u[j-KK],u[j]); + } + if (is_odd(s)) { /* "multiply by z" */ + for (j=KK;j>0;j--) u[j]=u[j-1]; + u[0]=u[KK]; /* shift the buffer cyclically */ + u[LL]=mod_sum(u[LL],u[KK]); + } + if (s) s>>=1; + else t--; + } + for (j=0;j=0? *ranf_arr_ptr++: ranf_arr_cycle()) +double ranf_arr_cycle() +{ + if (ranf_arr_ptr==&ranf_arr_dummy) + ranf_start(314159L); /* the user forgot to initialize */ + ranf_array(ranf_arr_buf,QUALITY); + ranf_arr_buf[100]=-1; + ranf_arr_ptr=ranf_arr_buf+1; + return ranf_arr_buf[0]; +} + +#include +int main() +{ + register int m; double a[2009]; /* a rudimentary test */ + ranf_start(310952); + for (m=0;m<2009;m++) ranf_array(a,1009); + printf("%.20f\n", ran_u[0]); /* 0.36410514377569680455 */ + /* beware of buggy printf routines that do not give full accuracy here! */ + ranf_start(310952); + for (m=0;m<1009;m++) ranf_array(a,2009); + printf("%.20f\n", ran_u[0]); /* 0.36410514377569680455 */ + return 0; +} + diff --git a/src/intersim/rng_double_wrapper.cpp b/src/intersim/rng_double_wrapper.cpp new file mode 100644 index 0000000..d54ca59 --- /dev/null +++ b/src/intersim/rng_double_wrapper.cpp @@ -0,0 +1,9 @@ +#include "rng.hpp" + +#define main rng_double_main +#include "rng_double.cpp" + +double ranf_next( ) +{ + return ranf_arr_next( ); +} diff --git a/src/intersim/rng_wrapper.cpp b/src/intersim/rng_wrapper.cpp new file mode 100644 index 0000000..0bfc669 --- /dev/null +++ b/src/intersim/rng_wrapper.cpp @@ -0,0 +1,9 @@ +#include "rng.hpp" + +#define main rng_main +#include "rng.cpp" + +long ran_next( ) +{ + return ran_arr_next( ); +} diff --git a/src/intersim/routefunc.cpp b/src/intersim/routefunc.cpp new file mode 100644 index 0000000..055baf8 --- /dev/null +++ b/src/intersim/routefunc.cpp @@ -0,0 +1,1045 @@ +#include "booksim.hpp" + +#include +#include +#include + +#include "routefunc.hpp" +#include "kncube.hpp" +#include "random_utils.hpp" + +map gRoutingFunctionMap; + +/* Global information used by routing functions */ + +int gNumVCS; + +/* Add routing functions here */ + +//============================================================= + +void singlerf( const Router *, const Flit *f, int, OutputSet *outputs, bool inject ) +{ + outputs->Clear( ); + outputs->Add( f->dest, f->dest % gNumVCS ); // VOQing +} + +//============================================================= + +int dor_next_mesh( int cur, int dest ) +{ + int dim_left; + int out_port; + + for ( dim_left = 0; dim_left < gN; ++dim_left ) { + if ( ( cur % gK ) != ( dest % gK ) ) { + break; + } + cur /= gK; dest /= gK; + } + + if ( dim_left < gN ) { + cur %= gK; dest %= gK; + + if ( cur < dest ) { + out_port = 2*dim_left; // Right + } else { + out_port = 2*dim_left + 1; // Left + } + } else { + out_port = 2*gN; // Eject + } + + return out_port; +} + +//============================================================= + +void dor_next_torus( int cur, int dest, int in_port, + int *out_port, int *partition, + bool balance = false ) +{ + int dim_left; + int dir; + int dist2; + + for ( dim_left = 0; dim_left < gN; ++dim_left ) { + if ( ( cur % gK ) != ( dest % gK ) ) { + break; + } + cur /= gK; dest /= gK; + } + + if ( dim_left < gN ) { + + if ( (in_port/2) != dim_left ) { + // Turning into a new dimension + + cur %= gK; dest %= gK; + dist2 = gK - 2 * ( ( dest - cur + gK ) % gK ); + + if ( ( dist2 > 0 ) || + ( ( dist2 == 0 ) && ( RandomInt( 1 ) ) ) ) { + *out_port = 2*dim_left; // Right + dir = 0; + } else { + *out_port = 2*dim_left + 1; // Left + dir = 1; + } + + if ( balance ) { + // Cray's "Partition" allocation + // Two datelines: one between k-1 and 0 which forces VC 1 + // another between ((k-1)/2) and ((k-1)/2 + 1) which forces VC 0 + // otherwise any VC can be used + + if ( ( ( dir == 0 ) && ( cur > dest ) ) || + ( ( dir == 1 ) && ( cur < dest ) ) ) { + *partition = 1; + } else if ( ( ( dir == 0 ) && ( cur <= (gK-1)/2 ) && ( dest > (gK-1)/2 ) ) || + ( ( dir == 1 ) && ( cur > (gK-1)/2 ) && ( dest <= (gK-1)/2 ) ) ) { + *partition = 0; + } else { + *partition = RandomInt( 1 ); // use either VC set + } + } else { + // Deterministic, fixed dateline between nodes k-1 and 0 + + if ( ( ( dir == 0 ) && ( cur > dest ) ) || + ( ( dir == 1 ) && ( dest < cur ) ) ) { + *partition = 1; + } else { + *partition = 0; + } + } + } else { + // Inverting the least significant bit keeps + // the packet moving in the same direction + *out_port = in_port ^ 0x1; + } + + } else { + *out_port = 2*gN; // Eject + } +} + +//============================================================= + +void dim_order_mesh( const Router *r, const Flit *f, int in_channel, OutputSet *outputs, bool inject ) +{ + int out_port; + + outputs->Clear( ); + + if ( inject ) { // use any VC for injection + outputs->AddRange( 0, 0, gNumVCS - 1 ); + } else { + out_port = dor_next_mesh( r->GetID( ), f->dest ); + + if ( f->watch ) { + cout << "flit " << f->id << " (" << f << ") at " << r->GetID( ) << " destined to " + << f->dest << " using channel " << out_port << ", vc range = [" + << 0 << "," << gNumVCS - 1 << "] (in_channel is " << in_channel << ")" << endl; + } + + outputs->AddRange( out_port, 0, gNumVCS - 1 ); + } +} + +//============================================================= + +void dim_order_ni_mesh( const Router *r, const Flit *f, int in_channel, OutputSet *outputs, bool inject ) +{ + int out_port; + int vcs_per_dest = gNumVCS / gNodes; + + outputs->Clear( ); + out_port = dor_next_mesh( r->GetID( ), f->dest ); + + if ( f->watch ) { + cout << "flit " << f->id << " (" << f << ") at " << r->GetID( ) << " destined to " + << f->dest << " using channel " << out_port << ", vc range = [" + << f->dest*vcs_per_dest << "," << (f->dest+1)*vcs_per_dest - 1 + << "] (in_channel is " << in_channel << ")" << endl; + } + + outputs->AddRange( out_port, f->dest*vcs_per_dest, (f->dest+1)*vcs_per_dest - 1 ); +} + +//============================================================= + +// Random intermediate in the minimal quadrant defined +// by the source and destination +int rand_min_intr_mesh( int src, int dest ) +{ + int dist; + + int intm = 0; + int offset = 1; + + for ( int n = 0; n < gN; ++n ) { + dist = ( dest % gK ) - ( src % gK ); + + if ( dist > 0 ) { + intm += offset * ( ( src % gK ) + RandomInt( dist ) ); + } else { + intm += offset * ( ( dest % gK ) + RandomInt( -dist ) ); + } + + offset *= gK; + dest /= gK; src /= gK; + } + + return intm; +} + +//============================================================= + +void romm_mesh( const Router *r, const Flit *f, int in_channel, OutputSet *outputs, bool inject ) +{ + int out_port; + int vc_min, vc_max; + + outputs->Clear( ); + + if ( in_channel == 2*gN ) { + f->ph = 1; // Phase 1 + f->intm = rand_min_intr_mesh( f->src, f->dest ); + } + + if ( ( f->ph == 1 ) && ( r->GetID( ) == f->intm ) ) { + f->ph = 2; // Go to phase 2 + } + + if ( f->ph == 1 ) { // In phase 1 + out_port = dor_next_mesh( r->GetID( ), f->intm ); + vc_min = 0; + vc_max = gNumVCS/2 - 1; + } else { // In phase 2 + out_port = dor_next_mesh( r->GetID( ), f->dest ); + vc_min = gNumVCS/2; + vc_max = gNumVCS - 1; + } + + outputs->AddRange( out_port, vc_min, vc_max ); +} + +//============================================================= + +void romm_ni_mesh( const Router *r, const Flit *f, int in_channel, OutputSet *outputs, bool inject ) +{ + int out_port; + int vcs_per_dest = gNumVCS / gNodes; + + outputs->Clear( ); + + if ( in_channel == 2*gN ) { + f->ph = 1; // Phase 1 + f->intm = rand_min_intr_mesh( f->src, f->dest ); + } + + if ( ( f->ph == 1 ) && ( r->GetID( ) == f->intm ) ) { + f->ph = 2; // Go to phase 2 + } + + if ( f->ph == 1 ) { // In phase 1 + out_port = dor_next_mesh( r->GetID( ), f->intm ); + } else { // In phase 2 + out_port = dor_next_mesh( r->GetID( ), f->dest ); + } + + outputs->AddRange( out_port, f->dest*vcs_per_dest, (f->dest+1)*vcs_per_dest - 1 ); +} + +//============================================================= + +void min_adapt_mesh( const Router *r, const Flit *f, int in_channel, OutputSet *outputs, bool inject ) +{ + int out_port; + int cur, dest; + int in_vc; + + outputs->Clear( ); + + if ( in_channel == 2*gN ) { + in_vc = gNumVCS - 1; // ignore the injection VC + } else { + in_vc = f->vc; + } + + // DOR for the escape channel (VC 0), low priority + out_port = dor_next_mesh( r->GetID( ), f->dest ); + outputs->AddRange( out_port, 0, 0, 0 ); + + if ( f->watch ) { + cout << "flit " << f->id << " (" << f << ") at " << r->GetID( ) << " destined to " + << f->dest << " using channel " << out_port << ", vc range = [" + << 0 << "," << gNumVCS - 1 << "] (in_channel is " << in_channel << ")" << endl; + } + + if ( in_vc != 0 ) { // If not in the escape VC + // Minimal adaptive for all other channels + cur = r->GetID( ); dest = f->dest; + + for ( int n = 0; n < gN; ++n ) { + if ( ( cur % gK ) != ( dest % gK ) ) { + // Add minimal direction in dimension 'n' + if ( ( cur % gK ) < ( dest % gK ) ) { // Right + outputs->AddRange( 2*n, 1, gNumVCS - 1, 1 ); + } else { // Left + outputs->AddRange( 2*n + 1, 1, gNumVCS - 1, 1 ); + } + } + cur /= gK; + dest /= gK; + } + } +} + +//============================================================= + +void planar_adapt_mesh( const Router *r, const Flit *f, int in_channel, OutputSet *outputs, bool inject ) +{ + int cur, dest; + int vc_mult; + int vc_min, vc_max; + int d1_min_c; + int in_vc; + int n; + + bool increase; + bool fault; + bool atedge; + + outputs->Clear( ); + + cur = r->GetID( ); + dest = f->dest; + in_vc = f->vc; + vc_mult = gNumVCS / 3; + + if ( cur != dest ) { + + // Find the first unmatched dimension -- except + // for when we're in the first dimension because + // of misrouting in the last adaptive plane. + // In this case, go to the last dimension instead. + + for ( n = 0; n < gN; ++n ) { + if ( ( ( cur % gK ) != ( dest % gK ) ) && + !( ( in_channel/2 == 0 ) && + ( n == 0 ) && + ( in_vc < 2*vc_mult ) ) ) { + break; + } + + cur /= gK; + dest /= gK; + } + + assert( n < gN ); + + if ( f->watch ) { + cout << "PLANAR ADAPTIVE: flit " << f->id + << " in adaptive plane " << n << " at " << r->GetID( ) << endl; + } + + // We're in adaptive plane n + + // Can route productively in d_{i,2} + if ( ( cur % gK ) < ( dest % gK ) ) { // Increasing + increase = true; + if ( !r->IsFaultyOutput( 2*n ) ) { + outputs->AddRange( 2*n, 2*vc_mult, gNumVCS - 1 ); + fault = false; + + if ( f->watch ) { + cout << "PLANAR ADAPTIVE: increasing in dimension " << n << endl; + } + } else { + fault = true; + } + } else { // Decreasing + increase = false; + if ( !r->IsFaultyOutput( 2*n + 1 ) ) { + outputs->AddRange( 2*n + 1, 2*vc_mult, gNumVCS - 1 ); + fault = false; + + if ( f->watch ) { + cout << "PLANAR ADAPTIVE: decreasing in dimension " << n << endl; + } + } else { + fault = true; + } + } + + n = ( n + 1 ) % gN; + cur /= gK; + dest /= gK; + + if ( increase ) { + vc_min = 0; + vc_max = vc_mult - 1; + } else { + vc_min = vc_mult; + vc_max = 2*vc_mult - 1; + } + + if ( ( cur % gK ) < ( dest % gK ) ) { // Increasing in d_{i+1} + d1_min_c = 2*n; + } else if ( ( cur % gK ) != ( dest % gK ) ) { // Decreasing in d_{i+1} + d1_min_c = 2*n + 1; + } else { + d1_min_c = -1; + } + + // do we want to 180? if so, the last + // route was a misroute in this dimension, + // if there is no fault in d_i, just ignore + // this dimension, otherwise continue to misroute + if ( d1_min_c == in_channel ) { + if ( fault ) { + d1_min_c = in_channel ^ 1; + } else { + d1_min_c = -1; + } + + if ( f->watch ) { + cout << "PLANAR ADAPTIVE: avoiding 180 in dimension " << n << endl; + } + } + + if ( d1_min_c != -1 ) { + if ( !r->IsFaultyOutput( d1_min_c ) ) { + outputs->AddRange( d1_min_c, vc_min, vc_max ); + } else if ( fault ) { + // major problem ... fault in d_i and d_{i+1} + r->Error( "There seem to be faults in d_i and d_{i+1}" ); + } + } else if ( fault ) { // need to misroute! + if ( cur % gK == 0 ) { + d1_min_c = 2*n; + atedge = true; + } else if ( cur % gK == gK - 1 ) { + d1_min_c = 2*n + 1; + atedge = true; + } else { + d1_min_c = 2*n + RandomInt( 1 ); // random misroute + + if ( d1_min_c == in_channel ) { // don't 180 + d1_min_c = in_channel ^ 1; + } + atedge = false; + } + + if ( !r->IsFaultyOutput( d1_min_c ) ) { + outputs->AddRange( d1_min_c, vc_min, vc_max ); + } else if ( !atedge && !r->IsFaultyOutput( d1_min_c ^ 1 ) ) { + outputs->AddRange( d1_min_c ^ 1, vc_min, vc_max ); + } else { + // major problem ... fault in d_i and d_{i+1} + r->Error( "There seem to be faults in d_i and d_{i+1}" ); + } + } + } else { + outputs->AddRange( 2*gN, 0, gNumVCS - 1 ); + } +} + +//============================================================= + +void limited_adapt_mesh_old( const Router *r, const Flit *f, int in_channel, OutputSet *outputs, bool inject ) +{ + int in_vc; + int in_dim; + + int min_port; + + bool dor_dim; + bool equal; + + int cur, dest; + + outputs->Clear( ); + + if ( inject ) { + outputs->AddRange( 0, 0, gNumVCS - 1 ); + f->ph = 0; // zero dimension reversals + } else { + + cur = r->GetID( ); dest = f->dest; + if ( cur != dest ) { + + if ( f->ph == 0 ) { + f->ph = 1; + + in_vc = 0; + in_dim = 0; + } else { + in_vc = f->vc; + in_dim = in_channel/2; + } + + // The first remaining is the DOR escape path + dor_dim = true; + + for ( int n = 0; n < gN; ++n ) { + if ( ( cur % gK ) != ( dest % gK ) ) { + if ( ( cur % gK ) < ( dest % gK ) ) { + min_port = 2*n; // Right + } else { + min_port = 2*n + 1; // Left + } + + if ( dor_dim ) { + // Low priority escape path + outputs->AddRange( min_port, gNumVCS - 1, gNumVCS - 1, 0 ); + dor_dim = false; + } + + equal = false; + } else { + equal = true; + min_port = 2*n; + } + + if ( in_vc < gNumVCS - 1 ) { // adaptive VC's left? + if ( n < in_dim ) { + // Productive (minimal) direction, with reversal + if ( in_vc == gNumVCS - 2 ) { + outputs->AddRange( min_port, in_vc + 1, in_vc + 1, equal ? 1 : 2 ); + } else { + outputs->AddRange( min_port, in_vc + 1, gNumVCS - 2, equal ? 1 : 2 ); + } + + // Unproductive (non-minimal) direction, with reversal + if ( in_vc < gNumVCS - 2 ) { + if ( in_vc == gNumVCS - 3 ) { + outputs->AddRange( min_port ^ 0x1, in_vc + 1, in_vc + 1, 1 ); + } else { + outputs->AddRange( min_port ^ 0x1, in_vc + 1, gNumVCS - 3, 1 ); + } + } + } else if ( n == in_dim ) { + if ( !equal ) { + // Productive (minimal) direction, no reversal + outputs->AddRange( min_port, in_vc, gNumVCS - 2, 4 ); + } + } else { + // Productive (minimal) direction, no reversal + outputs->AddRange( min_port, in_vc, gNumVCS - 2, equal ? 1 : 3 ); + // Unproductive (non-minimal) direction, no reversal + if ( in_vc < gNumVCS - 2 ) { + outputs->AddRange( min_port ^ 0x1, in_vc, gNumVCS - 2, 1 ); + } + } + } + + cur /= gK; + dest /= gK; + } + } else { // at destination + outputs->AddRange( 2*gN, 0, gNumVCS - 1 ); + } + } +} + +void limited_adapt_mesh( const Router *r, const Flit *f, int in_channel, OutputSet *outputs, bool inject ) +{ + int min_port; + + int cur, dest; + + outputs->Clear( ); + + if ( inject ) { + outputs->AddRange( 0, 0, gNumVCS - 2 ); + f->dr = 0; // zero dimension reversals + } else { + cur = r->GetID( ); dest = f->dest; + + if ( cur != dest ) { + if ( ( f->vc != gNumVCS - 1 ) && + ( f->dr != gNumVCS - 2 ) ) { + + for ( int n = 0; n < gN; ++n ) { + if ( ( cur % gK ) != ( dest % gK ) ) { + if ( ( cur % gK ) < ( dest % gK ) ) { + min_port = 2*n; // Right + } else { + min_port = 2*n + 1; // Left + } + + // Go in a productive direction with high priority + outputs->AddRange( min_port, 0, gNumVCS - 2, 2 ); + + // Go in the non-productive direction with low priority + outputs->AddRange( min_port ^ 0x1, 0, gNumVCS - 2, 1 ); + } else { + // Both directions are non-productive + outputs->AddRange( 2*n, 0, gNumVCS - 2, 1 ); + outputs->AddRange( 2*n+1, 0, gNumVCS - 2, 1 ); + } + + cur /= gK; + dest /= gK; + } + + } else { + outputs->AddRange( dor_next_mesh( cur, dest ), + gNumVCS - 1, gNumVCS - 1, 0 ); + } + + } else { // at destination + outputs->AddRange( 2*gN, 0, gNumVCS - 1 ); + } + } +} + +//============================================================= + +void valiant_mesh( const Router *r, const Flit *f, int in_channel, OutputSet *outputs, bool inject ) +{ + int out_port; + int vc_min, vc_max; + + outputs->Clear( ); + + + if ( in_channel == 2*gN ) { + f->ph = 1; // Phase 1 + f->intm = RandomInt( gNodes - 1 ); + } + + if ( ( f->ph == 1 ) && ( r->GetID( ) == f->intm ) ) { + f->ph = 2; // Go to phase 2 + } + + if ( f->ph == 1 ) { // In phase 1 + out_port = dor_next_mesh( r->GetID( ), f->intm ); + vc_min = 0; + vc_max = gNumVCS/2 - 1; + } else { // In phase 2 + out_port = dor_next_mesh( r->GetID( ), f->dest ); + vc_min = gNumVCS/2; + vc_max = gNumVCS - 1; + } + + outputs->AddRange( out_port, vc_min, vc_max ); +} + +//============================================================= + +void valiant_torus( const Router *r, const Flit *f, int in_channel, OutputSet *outputs, bool inject ) +{ + int out_port; + int vc_min, vc_max; + + outputs->Clear( ); + + if ( in_channel == 2*gN ) { + f->ph = 1; // Phase 1 + f->intm = RandomInt( gNodes - 1 ); + } + + if ( ( f->ph == 1 ) && ( r->GetID( ) == f->intm ) ) { + f->ph = 2; // Go to phase 2 + in_channel = 2*gN; // ensures correct vc selection at the beginning of phase 2 + } + + if ( f->ph == 1 ) { // In phase 1 + dor_next_torus( r->GetID( ), f->intm, in_channel, + &out_port, &f->ring_par, false ); + + if ( f->ring_par == 0 ) { + vc_min = 0; + vc_max = gNumVCS/4 - 1; + } else { + vc_min = gNumVCS/4; + vc_max = gNumVCS/2 - 1; + } + } else { // In phase 2 + dor_next_torus( r->GetID( ), f->dest, in_channel, + &out_port, &f->ring_par, false ); + + if ( f->ring_par == 0 ) { + vc_min = gNumVCS/2; + vc_max = (3*gNumVCS)/4 - 1; + } else { + vc_min = (3*gNumVCS)/4; + vc_max = gNumVCS - 1; + } + } + + outputs->AddRange( out_port, vc_min, vc_max ); +} + +//============================================================= + +void valiant_ni_torus( const Router *r, const Flit *f, int in_channel, + OutputSet *outputs, bool inject ) +{ + int out_port; + int vc_min, vc_max; + + outputs->Clear( ); + + if ( in_channel == 2*gN ) { + f->ph = 1; // Phase 1 + f->intm = RandomInt( gNodes - 1 ); + } + + if ( ( f->ph == 1 ) && ( r->GetID( ) == f->intm ) ) { + f->ph = 2; // Go to phase 2 + in_channel = 2*gN; // ensures correct vc selection at the beginning of phase 2 + } + + if ( f->ph == 1 ) { // In phase 1 + dor_next_torus( r->GetID( ), f->intm, in_channel, + &out_port, &f->ring_par, false ); + + if ( f->ring_par == 0 ) { + vc_min = f->dest; + vc_max = f->dest; + } else { + vc_min = f->dest + gNodes; + vc_max = f->dest + gNodes; + } + + } else { // In phase 2 + dor_next_torus( r->GetID( ), f->dest, in_channel, + &out_port, &f->ring_par, false ); + + if ( f->ring_par == 0 ) { + vc_min = f->dest + 2*gNodes; + vc_max = f->dest + 2*gNodes; + } else { + vc_min = f->dest + 3*gNodes; + vc_max = f->dest + 3*gNodes; + } + } + + if ( f->watch ) { + cout << "flit " << f->id << " (" << f << ") at " << r->GetID( ) << " destined to " + << f->dest << " using channel " << out_port << ", vc range = [" + << vc_min << "," << vc_max + << "] (in_channel is " << in_channel << ")" << endl; + } + + outputs->AddRange( out_port, vc_min, vc_max ); +} + +//============================================================= + +void dim_order_torus( const Router *r, const Flit *f, int in_channel, + OutputSet *outputs, bool inject ) +{ + int cur; + int dest; + + int out_port; + int vc_min, vc_max; + + outputs->Clear( ); + + cur = r->GetID( ); + dest = f->dest; + + dor_next_torus( cur, dest, in_channel, + &out_port, &f->ring_par, false ); + + if ( f->ring_par == 0 ) { + vc_min = 0; + vc_max = gNumVCS/2 - 1; + } else { + vc_min = gNumVCS/2; + vc_max = gNumVCS - 1; + } + + if ( f->watch ) { + cout << "flit " << f->id << " (" << f << ") at " << r->GetID( ) << " destined to " + << f->dest << " using channel " << out_port << ", vc range = [" + << vc_min << "," << vc_max << "] (in_channel is " << in_channel << ")" << endl; + } + + outputs->AddRange( out_port, vc_min, vc_max ); +} + +//============================================================= + +void dim_order_ni_torus( const Router *r, const Flit *f, int in_channel, + OutputSet *outputs, bool inject ) +{ + int cur; + int dest; + + int out_port; + int vcs_per_dest = gNumVCS / gNodes; + + outputs->Clear( ); + + cur = r->GetID( ); + dest = f->dest; + + outputs->Clear( ); + dor_next_torus( cur, dest, in_channel, + &out_port, &f->ring_par, false ); + + if ( f->watch ) { + cout << "flit " << f->id << " (" << f << ") at " << r->GetID( ) << " destined to " + << f->dest << " using channel " << out_port << ", vc range = [" + << f->dest*vcs_per_dest << "," << (f->dest+1)*vcs_per_dest - 1 + << "] (in_channel is " << in_channel << ")" << endl; + } + + outputs->AddRange( out_port, f->dest*vcs_per_dest, (f->dest+1)*vcs_per_dest - 1 ); +} + +//============================================================= + +void dim_order_bal_torus( const Router *r, const Flit *f, int in_channel, + OutputSet *outputs, bool inject ) +{ + int cur; + int dest; + + int out_port; + int vc_min, vc_max; + + outputs->Clear( ); + + cur = r->GetID( ); + dest = f->dest; + + dor_next_torus( cur, dest, in_channel, + &out_port, &f->ring_par, true ); + + if ( f->ring_par == 0 ) { + vc_min = 0; + vc_max = gNumVCS/2 - 1; + } else { + vc_min = gNumVCS/2; + vc_max = gNumVCS - 1; + } + + if ( f->watch ) { + cout << "flit " << f->id << " (" << f << ") at " << r->GetID( ) << " destined to " + << f->dest << " using channel " << out_port << ", vc range = [" + << vc_min << "," << vc_max << "] (in_channel is " << in_channel << ")" << endl; + } + + outputs->AddRange( out_port, vc_min, vc_max ); +} + +//============================================================= + +void min_adapt_torus( const Router *r, const Flit *f, int in_channel, OutputSet *outputs, bool inject ) +{ + int cur, dest, dist2; + int in_vc; + int out_port; + + outputs->Clear( ); + + if ( in_channel == 2*gN ) { + in_vc = gNumVCS - 1; // ignore the injection VC + } else { + in_vc = f->vc; + } + + if ( in_vc > 1 ) { // If not in the escape VCs + // Minimal adaptive for all other channels + cur = r->GetID( ); dest = f->dest; + + for ( int n = 0; n < gN; ++n ) { + if ( ( cur % gK ) != ( dest % gK ) ) { + dist2 = gK - 2 * ( ( ( dest % gK ) - ( cur % gK ) + gK ) % gK ); + + if ( dist2 > 0 ) { /*) || + ( ( dist2 == 0 ) && ( RandomInt( 1 ) ) ) ) {*/ + outputs->AddRange( 2*n, 3, 3, 1 ); // Right + } else { + outputs->AddRange( 2*n + 1, 3, 3, 1 ); // Left + } + } + + cur /= gK; + dest /= gK; + } + + // DOR for the escape channel (VCs 0-1), low priority --- + // trick the algorithm with the in channel. want VC assignment + // as if we had injected at this node + dor_next_torus( r->GetID( ), f->dest, 2*gN, + &out_port, &f->ring_par, false ); + } else { + // DOR for the escape channel (VCs 0-1), low priority + dor_next_torus( r->GetID( ), f->dest, in_channel, + &out_port, &f->ring_par, false ); + } + + if ( f->ring_par == 0 ) { + outputs->AddRange( out_port, 0, 0, 0 ); + } else { + outputs->AddRange( out_port, 1, 1, 0 ); + } + + if ( f->watch ) { + cout << "flit " << f->id << " (" << f << ") at " << r->GetID( ) << " destined to " + << f->dest << " using channel " << out_port << ", vc range = [" + << 0 << "," << gNumVCS - 1 << "] (in_channel is " << in_channel << ")" << endl; + } + + +} + +//============================================================= + +void dest_tag( const Router *r, const Flit *f, int in_channel, + OutputSet *outputs, bool inject ) +{ + outputs->Clear( ); + + int stage = ( r->GetID( ) * gK ) / gNodes; + int dest = f->dest; + + while ( stage < ( gN - 1 ) ) { + dest /= gK; + ++stage; + } + + int out_port = dest % gK; + + outputs->AddRange( out_port, 0, gNumVCS - 1 ); +} + +//============================================================= + +void chaos_torus( const Router *r, const Flit *f, + int in_channel, OutputSet *outputs, bool inject ) +{ + int cur, dest; + int dist2; + + outputs->Clear( ); + + cur = r->GetID( ); dest = f->dest; + + if ( cur != dest ) { + for ( int n = 0; n < gN; ++n ) { + + if ( ( cur % gK ) != ( dest % gK ) ) { + dist2 = gK - 2 * ( ( ( dest % gK ) - ( cur % gK ) + gK ) % gK ); + + if ( dist2 >= 0 ) { + outputs->AddRange( 2*n, 0, 0 ); // Right + } + + if ( dist2 <= 0 ) { + outputs->AddRange( 2*n + 1, 0, 0 ); // Left + } + } + + cur /= gK; + dest /= gK; + } + } else { + outputs->AddRange( 2*gN, 0, 0 ); + } +} + + +//============================================================= + +void chaos_mesh( const Router *r, const Flit *f, + int in_channel, OutputSet *outputs, bool inject ) +{ + int cur, dest; + + outputs->Clear( ); + + cur = r->GetID( ); dest = f->dest; + + if ( cur != dest ) { + for ( int n = 0; n < gN; ++n ) { + if ( ( cur % gK ) != ( dest % gK ) ) { + // Add minimal direction in dimension 'n' + if ( ( cur % gK ) < ( dest % gK ) ) { // Right + outputs->AddRange( 2*n, 0, 0 ); + } else { // Left + outputs->AddRange( 2*n + 1, 0, 0 ); + } + } + cur /= gK; + dest /= gK; + } + } else { + outputs->AddRange( 2*gN, 0, 0 ); + } +} + +//============================================================= + +void InitializeRoutingMap( ) +{ + /* Register routing functions here */ + + gRoutingFunctionMap["single_single"] = &singlerf; + + gRoutingFunctionMap["dim_order_mesh"] = &dim_order_mesh; + gRoutingFunctionMap["dim_order_ni_mesh"] = &dim_order_ni_mesh; + gRoutingFunctionMap["dim_order_torus"] = &dim_order_torus; + gRoutingFunctionMap["dim_order_ni_torus"] = &dim_order_ni_torus; + gRoutingFunctionMap["dim_order_bal_torus"] = &dim_order_bal_torus; + + gRoutingFunctionMap["romm_mesh"] = &romm_mesh; + gRoutingFunctionMap["romm_ni_mesh"] = &romm_ni_mesh; + + gRoutingFunctionMap["min_adapt_mesh"] = &min_adapt_mesh; + gRoutingFunctionMap["min_adapt_torus"] = &min_adapt_torus; + + gRoutingFunctionMap["planar_adapt_mesh"] = &planar_adapt_mesh; + + gRoutingFunctionMap["limited_adapt_mesh"] = &limited_adapt_mesh; + + gRoutingFunctionMap["valiant_mesh"] = &valiant_mesh; + gRoutingFunctionMap["valiant_torus"] = &valiant_torus; + gRoutingFunctionMap["valiant_ni_torus"] = &valiant_ni_torus; + + gRoutingFunctionMap["dest_tag_fly"] = &dest_tag; + + gRoutingFunctionMap["chaos_mesh"] = &chaos_mesh; + gRoutingFunctionMap["chaos_torus"] = &chaos_torus; +} + +tRoutingFunction GetRoutingFunction( const Configuration& config ) +{ + map::const_iterator match; + tRoutingFunction rf; + + string fn, topo, fn_topo; + + gNumVCS = config.GetInt( "num_vcs" ); + + config.GetStr( "topology", topo ); + + config.GetStr( "routing_function", fn, "none" ); + fn_topo = fn + "_" + topo; + match = gRoutingFunctionMap.find( fn_topo ); + + if ( match != gRoutingFunctionMap.end( ) ) { + rf = match->second; + } else { + if ( fn == "none" ) { + cout << "Error: No routing function specified in configuration." << endl; + } else { + cout << "Error: Undefined routing function '" << fn << "' for the topology '" + << topo << "'." << endl; + } + exit(-1); + } + + return rf; +} + + diff --git a/src/intersim/routefunc.hpp b/src/intersim/routefunc.hpp new file mode 100644 index 0000000..e82c105 --- /dev/null +++ b/src/intersim/routefunc.hpp @@ -0,0 +1,14 @@ +#ifndef _ROUTEFUNC_HPP_ +#define _ROUTEFUNC_HPP_ + +#include "flit.hpp" +#include "router.hpp" +#include "outputset.hpp" +#include "config_utils.hpp" + +typedef void (*tRoutingFunction)( const Router *, const Flit *, int in_channel, OutputSet *, bool ); + +void InitializeRoutingMap( ); +tRoutingFunction GetRoutingFunction( const Configuration& config ); + +#endif diff --git a/src/intersim/router.cpp b/src/intersim/router.cpp new file mode 100644 index 0000000..6a750a6 --- /dev/null +++ b/src/intersim/router.cpp @@ -0,0 +1,113 @@ +#include "booksim.hpp" + +#include +#include + +#include "router.hpp" +#include "iq_router.hpp" +#include "event_router.hpp" + +Router::Router( const Configuration& config, + Module *parent, string name, int id, + int inputs, int outputs ) : +Module( parent, name ), +_id( id ), +_inputs( inputs ), +_outputs( outputs ) +{ + _routing_delay = config.GetInt( "routing_delay" ); + _vc_alloc_delay = config.GetInt( "vc_alloc_delay" ); + _sw_alloc_delay = config.GetInt( "sw_alloc_delay" ); + _st_prepare_delay = config.GetInt( "st_prepare_delay" ); + _st_final_delay = config.GetInt( "st_final_delay" ); + _credit_delay = config.GetInt( "credit_delay" ); + _input_speedup = config.GetInt( "input_speedup" ); + _output_speedup = config.GetInt( "output_speedup" ); + + _input_channels = new vector; + _input_credits = new vector; + + _output_channels = new vector; + _output_credits = new vector; + + _channel_faults = new vector; +} + +Router::~Router( ) +{ + delete _input_channels; + delete _input_credits; + delete _output_channels; + delete _output_credits; + delete _channel_faults; +} + +Credit *Router::_NewCredit( int vcs ) +{ + Credit *c; + + c = new Credit( vcs ); + return c; +} + +void Router::_RetireCredit( Credit *c ) +{ + delete c; +} + +void Router::AddInputChannel( Flit **channel, Credit **backchannel ) +{ + _input_channels->push_back( channel ); + _input_credits->push_back( backchannel ); +} + +void Router::AddOutputChannel( Flit **channel, Credit **backchannel ) +{ + _output_channels->push_back( channel ); + _output_credits->push_back( backchannel ); + _channel_faults->push_back( false ); +} + +int Router::GetID( ) const +{ + return _id; +} + +void Router::OutChannelFault( int c, bool fault ) +{ + assert( ( c >= 0 ) && ( c < (int)_channel_faults->size( ) ) ); + + (*_channel_faults)[c] = fault; +} + +bool Router::IsFaultyOutput( int c ) const +{ + assert( ( c >= 0 ) && ( c < (int)_channel_faults->size( ) ) ); + + return(*_channel_faults)[c]; +} + +Router *Router::NewRouter( const Configuration& config, + Module *parent, string name, int id, + int inputs, int outputs ) +{ + Router *r; + string type; + + config.GetStr( "router", type ); + + if ( type == "iq" ) { + r = new IQRouter( config, parent, name, id, inputs, outputs ); + } else if ( type == "event" ) { + r = new EventRouter( config, parent, name, id, inputs, outputs ); + } else { + cout << "Unknown router type " << type << endl; + } + + return r; +} + + + + + diff --git a/src/intersim/router.hpp b/src/intersim/router.hpp new file mode 100644 index 0000000..d2dff13 --- /dev/null +++ b/src/intersim/router.hpp @@ -0,0 +1,63 @@ +#ifndef _ROUTER_HPP_ +#define _ROUTER_HPP_ + +#include +#include + +#include "module.hpp" +#include "flit.hpp" +#include "credit.hpp" +#include "config_utils.hpp" + +class Router : public Module { +protected: + int _id; + + int _inputs; + int _outputs; + + int _input_speedup; + int _output_speedup; + + int _routing_delay; + int _vc_alloc_delay; + int _sw_alloc_delay; + int _st_prepare_delay; + int _st_final_delay; + + int _credit_delay; + + vector *_input_channels; + vector *_input_credits; + vector *_output_channels; + vector *_output_credits; + vector *_channel_faults; + + Credit *_NewCredit( int vcs = 1 ); + void _RetireCredit( Credit *c ); + +public: + Router( const Configuration& config, + Module *parent, string name, int id, + int inputs, int outputs ); + + virtual ~Router( ); + + static Router *NewRouter( const Configuration& config, + Module *parent, string name, int id, + int inputs, int outputs ); + + void AddInputChannel( Flit **channel, Credit **backchannel ); + void AddOutputChannel( Flit **channel, Credit **backchannel ); + + virtual void ReadInputs( ) = 0; + virtual void InternalStep( ) = 0; + virtual void WriteOutputs( ) = 0; + + void OutChannelFault( int c, bool fault = true ); + bool IsFaultyOutput( int c ) const; + + int GetID( ) const; +}; + +#endif diff --git a/src/intersim/selalloc.cpp b/src/intersim/selalloc.cpp new file mode 100644 index 0000000..53d31fe --- /dev/null +++ b/src/intersim/selalloc.cpp @@ -0,0 +1,207 @@ +#include "booksim.hpp" +#include + +#include "selalloc.hpp" +#include "random_utils.hpp" + +//#define DEBUG_SELALLOC + +SelAlloc::SelAlloc( const Configuration &config, + Module *parent, const string& name, + int inputs, int outputs ) : +SparseAllocator( config, parent, name, inputs, outputs ) +{ + _iter = config.GetInt( "alloc_iters" ); + + _grants = new int [_outputs]; + _gptrs = new int [_outputs]; + _aptrs = new int [_inputs]; + + for ( int i = 0; i < _inputs; ++i ) { + _aptrs[i] = 0; + } + for ( int j = 0; j < _outputs; ++j ) { + _gptrs[j] = 0; + } +} + +SelAlloc::~SelAlloc( ) +{ + delete [] _grants; + delete [] _aptrs; + delete [] _gptrs; +} + +void SelAlloc::Allocate( ) +{ + int input; + int output; + + int input_offset; + int output_offset; + + list::iterator p; + list::iterator outer_iter; + bool wrapped; + + int max_index; + int max_pri; + + _ClearMatching( ); + + for ( int i = 0; i < _outputs; ++i ) { + _grants[i] = -1; + } + + for ( int iter = 0; iter < _iter; ++iter ) { + // Grant phase + + for ( outer_iter = _out_occ.begin( ); + outer_iter != _out_occ.end( ); ++outer_iter ) { + output = *outer_iter; + + // Skip loop if there are no requests + // or the output is already matched or + // the output is masked + if ( ( _out_req[output].empty( ) ) || + ( _outmatch[output] != -1 ) || + ( _outmask[output] != 0 ) ) { + continue; + } + + // A round-robin arbiter between input requests + input_offset = _gptrs[output]; + + p = _out_req[output].begin( ); + while ( ( p != _out_req[output].end( ) ) && + ( p->port < input_offset ) ) { + p++; + } + + max_index = -1; + max_pri = 0; + + wrapped = false; + while ( (!wrapped) || ( p->port < input_offset ) ) { + if ( p == _out_req[output].end( ) ) { + if ( wrapped ) { + break; + } + // p is valid here because empty lists + // are skipped (above) + p = _out_req[output].begin( ); + wrapped = true; + } + + input = p->port; + + // we know the output is free (above) and + // if the input is free, check if request is the + // highest priority so far + if ( ( _inmatch[input] == -1 ) && + ( ( p->out_pri > max_pri ) || ( max_index == -1 ) ) ) { + max_pri = p->out_pri; + max_index = input; + } + + p++; + } + + if ( max_index != -1 ) { // grant + _grants[output] = max_index; + } + } + +#ifdef DEBUG_SELALLOC + cout << "grants: "; + for ( int i = 0; i < _outputs; ++i ) { + cout << _grants[i] << " "; + } + cout << endl; + + cout << "aptrs: "; + for ( int i = 0; i < _inputs; ++i ) { + cout << _aptrs[i] << " "; + } + cout << endl; +#endif + + // Accept phase + + for ( outer_iter = _in_occ.begin( ); + outer_iter != _in_occ.end( ); ++outer_iter ) { + input = *outer_iter; + + if ( _in_req[input].empty( ) ) { + continue; + } + + // A round-robin arbiter between output grants + output_offset = _aptrs[input]; + + p = _in_req[input].begin( ); + while ( ( p != _in_req[input].end( ) ) && + ( p->port < output_offset ) ) { + p++; + } + + max_index = -1; + max_pri = 0; + + wrapped = false; + while ( (!wrapped) || ( p->port < output_offset ) ) { + if ( p == _in_req[input].end( ) ) { + if ( wrapped ) { + break; + } + // p is valid here because empty lists + // are skipped (above) + p = _in_req[input].begin( ); + wrapped = true; + } + + output = p->port; + + // we know the output is free (above) and + // if the input is free, check if the highest + // priroity + if ( ( _grants[output] == input ) && + ( !_out_req[output].empty( ) ) && + ( ( p->in_pri > max_pri ) || ( max_index == -1 ) ) ) { + max_pri = p->in_pri; + max_index = output; + } + + p++; + } + + if ( max_index != -1 ) { + // Accept + output = max_index; + + _inmatch[input] = output; + _outmatch[output] = input; + + // Only update pointers if accepted during the 1st iteration + if ( iter == 0 ) { + _gptrs[output] = ( input + 1 ) % _inputs; + _aptrs[input] = ( output + 1 ) % _outputs; + } + } + } + } + +#ifdef DEBUG_SELALLOC + cout << "input match: "; + for ( int i = 0; i < _inputs; ++i ) { + cout << _inmatch[i] << " "; + } + cout << endl; + + cout << "output match: "; + for ( int j = 0; j < _outputs; ++j ) { + cout << _outmatch[j] << " "; + } + cout << endl; +#endif +} diff --git a/src/intersim/selalloc.hpp b/src/intersim/selalloc.hpp new file mode 100644 index 0000000..8e2fb84 --- /dev/null +++ b/src/intersim/selalloc.hpp @@ -0,0 +1,22 @@ +#ifndef _SELALLOC_HPP_ +#define _SELALLOC_HPP_ + +#include "allocator.hpp" + +class SelAlloc : public SparseAllocator { + int _iter; + + int *_grants; + int *_aptrs; + int *_gptrs; + +public: + SelAlloc( const Configuration &config, + Module *parent, const string& name, + int inputs, int outputs ); + ~SelAlloc( ); + + void Allocate( ); +}; + +#endif diff --git a/src/intersim/singlenet.cpp b/src/intersim/singlenet.cpp new file mode 100644 index 0000000..ec68fb4 --- /dev/null +++ b/src/intersim/singlenet.cpp @@ -0,0 +1,43 @@ +#include "booksim.hpp" +#include + +#include "singlenet.hpp" + +SingleNet::SingleNet( const Configuration &config ) : +Network( config ) +{ + _ComputeSize( config ); + _Alloc( ); + _BuildNet( config ); +} + +void SingleNet::_ComputeSize( const Configuration &config ) +{ + _sources = config.GetInt( "in_ports" ); + _dests = config.GetInt( "out_ports" ); + + _size = 1; + _channels = 0; +} + +void SingleNet::_BuildNet( const Configuration &config ) +{ + int i; + + _routers[0] = Router::NewRouter( config, this, "router", 0, + _sources, _dests ); + + for ( i = 0; i < _sources; ++i ) { + _routers[0]->AddInputChannel( &_inject[i], &_inject_cred[i] ); + } + + for ( i = 0; i < _dests; ++i ) { + _routers[0]->AddOutputChannel( &_eject[i], &_eject_cred[i] ); + } +} + +void SingleNet::Display( ) const +{ + _routers[0]->Display( ); +} + diff --git a/src/intersim/singlenet.hpp b/src/intersim/singlenet.hpp new file mode 100644 index 0000000..f664c1c --- /dev/null +++ b/src/intersim/singlenet.hpp @@ -0,0 +1,17 @@ +#ifndef _SINGLENET_HPP_ +#define _SINGLENET_HPP_ + +#include "network.hpp" + +class SingleNet : public Network { + + void _ComputeSize( const Configuration &config ); + void _BuildNet( const Configuration &config ); + +public: + SingleNet( const Configuration &config ); + + void Display( ) const; +}; + +#endif diff --git a/src/intersim/stats.cpp b/src/intersim/stats.cpp new file mode 100644 index 0000000..50cecce --- /dev/null +++ b/src/intersim/stats.cpp @@ -0,0 +1,119 @@ +#include "booksim.hpp" +#include +#include +#include + +#include "stats.hpp" + +Stats::Stats( Module *parent, const string &name, + double bin_size, int num_bins ) : +Module( parent, name ), +_num_bins( num_bins ), _bin_size( bin_size ) +{ + _hist = new int [_num_bins]; + + Clear( ); +} + +Stats::~Stats( ) +{ + delete [] _hist; +} + +void Stats::Clear( ) +{ + _num_samples = 0; + _sample_sum = 0.0; + + for ( int b = 0; b < _num_bins; ++b ) { + _hist[b] = 0; + } + + _reset = true; +} + +double Stats::Average( ) const +{ + return _sample_sum / (double)_num_samples; +} + +double Stats::Min( ) const +{ + return _min; +} + +double Stats::Max( ) const +{ + return _max; +} + +int Stats::NumSamples( ) const +{ + return _num_samples; +} + +void Stats::AddSample( double val ) +{ + int b; + + _num_samples++; + _sample_sum += val; + + if ( _reset ) { + _reset = false; + _max = val; + _min = val; + } else { + if ( val > _max ) { + _max = val; + } + if ( val < _min ) { + _min = val; + } + } + + b = (int)floor( val / _bin_size ); + + if ( b < 0 ) { + b = 0; + } else if ( b >= _num_bins ) { + b = _num_bins - 1; + } + + _hist[b]++; +} + +void Stats::AddSample( int val ) +{ + AddSample( (double)val ); +} + +void Stats::Display( ) const +{ + int b; + + if (_bin_size != 1.0 ) { + cout<<_fullname<<"_"; + printf("bins = [ "); + for ( b = 0; b < _num_bins; ++b ) { + printf("%d ", b* (unsigned)_bin_size); + } + printf("];\n"); + } + + cout<<_fullname<<"_"; + printf("freq = [ "); + for ( b = 0; b < _num_bins; ++b ) { + printf("%d ", (unsigned) _hist[b]); + } + printf("];\n"); +} + +bool Stats::NeverUsed() const +{ + if ( _reset ) { + return true; + } else { + return false; + } +} diff --git a/src/intersim/stats.hpp b/src/intersim/stats.hpp new file mode 100644 index 0000000..7c2a398 --- /dev/null +++ b/src/intersim/stats.hpp @@ -0,0 +1,39 @@ +#ifndef _STATS_HPP_ +#define _STATS_HPP_ + +#include "module.hpp" + +class Stats : public Module { + int _num_samples; + double _sample_sum; + + bool _reset; + double _min; + double _max; + + int _num_bins; + double _bin_size; + + int *_hist; + +public: + Stats( Module *parent, const string &name, + double bin_size = 1.0, int num_bins = 10 ); + ~Stats( ); + + void Clear( ); + + double Average( ) const; + double Max( ) const; + double Min( ) const; + int NumSamples( ) const; + + void AddSample( double val ); + void AddSample( int val ); + + + void Display( ) const; + bool NeverUsed() const; +}; + +#endif diff --git a/src/intersim/statwraper.cpp b/src/intersim/statwraper.cpp new file mode 100644 index 0000000..9590f6f --- /dev/null +++ b/src/intersim/statwraper.cpp @@ -0,0 +1,64 @@ +//a Wraper function for stats class +#include "stats.hpp" +#include + +void* StatCreate (const char * name, double bin_size, int num_bins) { + Stats* newstat = new Stats(NULL,name,bin_size,num_bins); + newstat->Clear (); + return(void *) newstat; +} + +void StatClear(void * st) +{ + ((Stats *)st)->Clear(); +} + +void StatAddSample (void * st, int val) +{ + ((Stats *)st)->AddSample(val); +} + +double StatAverage(void * st) +{ + return((Stats *)st)->Average(); +} + +double StatMax(void * st) +{ + return((Stats *)st)->Max(); +} + +double StatMin(void * st) +{ + return((Stats *)st)->Min(); +} + +void StatDisp (void * st) +{ + printf ("Stats for "); + ((Stats *)st)->DisplayHierarchy(); + if (((Stats *)st)->NeverUsed()) { + printf (" was never updated!\n"); + } else { + printf("Min %f Max %f Average %f \n",((Stats *)st)->Min(),((Stats *)st)->Max(),StatAverage(st)); + ((Stats *)st)->Display(); + } +} + +void StatDumptofile (void * st, FILE *f) +{ + +} + +#if 0 +int main () +{ + void * mytest = StatCreate("Test",1,5); + StatAddSample(mytest,4); + StatAddSample(mytest,4);StatAddSample(mytest,4); + StatAddSample(mytest,2); + StatDisp(mytest); +} +#endif + + diff --git a/src/intersim/statwraper.h b/src/intersim/statwraper.h new file mode 100644 index 0000000..35ca6de --- /dev/null +++ b/src/intersim/statwraper.h @@ -0,0 +1,13 @@ +#ifndef STAT_WRAPER_H +#define STAT_WRAPER_H + +void* StatCreate (const char * name, double bin_size, int num_bins) ; +void StatClear(void * st); +void StatAddSample (void * st, int val); +double StatAverage(void * st) ; +double StatMax(void * st) ; +double StatMin(void * st) ; +void StatDisp (void * st); +void StatDumptofile (void * st, FILE f); + +#endif diff --git a/src/intersim/traffic.cpp b/src/intersim/traffic.cpp new file mode 100644 index 0000000..62bc2b4 --- /dev/null +++ b/src/intersim/traffic.cpp @@ -0,0 +1,303 @@ +#include "booksim.hpp" +#include +#include + +#include "traffic.hpp" +#include "network.hpp" +#include "random_utils.hpp" +#include "misc_utils.hpp" + +map gTrafficFunctionMap; + +int gResetTraffic = 0; +int gStepTraffic = 0; + +void src_dest_bin( int source, int dest, int lg ) +{ + int b, t; + + cout << "from: "; + t = source; + for ( b = 0; b < lg; ++b ) { + cout << ( ( t >> ( lg - b - 1 ) ) & 0x1 ); + } + + cout << " to "; + t = dest; + for ( b = 0; b < lg; ++b ) { + cout << ( ( t >> ( lg - b - 1 ) ) & 0x1 ); + } + cout << endl; +} + +//============================================================= + +int uniform( int source, int total_nodes ) +{ + return RandomInt( total_nodes - 1 ); +} + +//============================================================= + +int bitcomp( int source, int total_nodes ) +{ + int lg = log_two( total_nodes ); + int mask = total_nodes - 1; + int dest; + + if ( ( 1 << lg ) != total_nodes ) { + cout << "Error: The 'bitcomp' traffic pattern requires the number of" + << " nodes to be a power of two!" << endl; + exit(-1); + } + + dest = ( ~source ) & mask; + + return dest; +} + +//============================================================= + +int transpose( int source, int total_nodes ) +{ + int lg = log_two( total_nodes ); + int mask_lo = (1 << (lg/2)) - 1; + int mask_hi = mask_lo << (lg/2); + int dest; + + if ( ( ( 1 << lg ) != total_nodes ) || ( lg & 0x1 ) ) { + cout << "Error: The 'transpose' traffic pattern requires the number of" + << " nodes to be an even power of two!" << endl; + exit(-1); + } + + dest = ( ( source >> (lg/2) ) & mask_lo ) | + ( ( source << (lg/2) ) & mask_hi ); + + return dest; +} + +//============================================================= + +int bitrev( int source, int total_nodes ) +{ + int lg = log_two( total_nodes ); + int dest; + + if ( ( 1 << lg ) != total_nodes ) { + cout << "Error: The 'bitrev' traffic pattern requires the number of" + << " nodes to be a power of two!" << endl; + exit(-1); + } + + // If you were fancy you could do this in O(log log total_nodes) + // instructions, but I'm not + + dest = 0; + for ( int b = 0; b < lg; ++b ) { + dest |= ( ( source >> b ) & 0x1 ) << ( lg - b - 1 ); + } + + return dest; +} + +//============================================================= + +int shuffle( int source, int total_nodes ) +{ + int lg = log_two( total_nodes ); + int dest; + + if ( ( 1 << lg ) != total_nodes ) { + cout << "Error: The 'shuffle' traffic pattern requires the number of" + << " nodes to be a power of two!" << endl; + exit(-1); + } + + dest = ( ( source << 1 ) & ( total_nodes - 1 ) ) | + ( ( source >> ( lg - 1 ) ) & 0x1 ); + + return dest; +} + +//============================================================= + +int tornado( int source, int total_nodes ) +{ + int offset = 1; + int dest = 0; + + for ( int n = 0; n < gN; ++n ) { + dest += offset * + ( ( ( source / offset ) % gK + ( gK/2 - 1 ) ) % gK ); + offset *= gK; + } + + return dest; +} + +//============================================================= + +int neighbor( int source, int total_nodes ) +{ + int offset = 1; + int dest = 0; + + for ( int n = 0; n < gN; ++n ) { + dest += offset * + ( ( ( source / offset ) % gK + 1 ) % gK ); + offset *= gK; + } + + return dest; +} + +//============================================================= + +int *gPerm = 0; +int gPermSeed; + +void GenerateRandomPerm( int total_nodes ) +{ + int ind; + int i,j; + int cnt; + unsigned long prev_rand; + + prev_rand = RandomIntLong( ); + RandomSeed( gPermSeed ); + + if ( !gPerm ) { + gPerm = new int [total_nodes]; + } + + for ( i = 0; i < total_nodes; ++i ) { + gPerm[i] = -1; + } + + for ( i = 0; i < total_nodes; ++i ) { + ind = RandomInt( total_nodes - 1 - i ); + + j = 0; + cnt = 0; + while ( ( cnt < ind ) || + ( gPerm[j] != -1 ) ) { + if ( gPerm[j] == -1 ) { + ++cnt; + } + ++j; + + if ( j >= total_nodes ) { + cout << "ERROR: GenerateRandomPerm( ) internal error" << endl; + exit(-1); + } + } + + gPerm[j] = i; + } + + RandomSeed( prev_rand ); +} + +int randperm( int source, int total_nodes ) +{ + if ( gResetTraffic || !gPerm ) { + GenerateRandomPerm( total_nodes ); + gResetTraffic = 0; + } + + return gPerm[source]; +} + +//============================================================= + +int diagonal( int source, int total_nodes ) +{ + int t = RandomInt( 2 ); + int d; + + // 2/3 of traffic goes from source->source + // 1/3 of traffic goes from source->(source+1)%total_nodes + + if ( t == 0 ) { + d = ( source + 1 ) % total_nodes; + } else { + d = source; + } + + return d; +} + +//============================================================= + +int asymmetric( int source, int total_nodes ) +{ + int d; + int half = total_nodes / 2; + + d = ( source % half ) + RandomInt( 1 ) * half; + + return d; +} + +//============================================================= + +void InitializeTrafficMap( ) +{ + /* Register Traffic functions here */ + + gTrafficFunctionMap["uniform"] = &uniform; + + // "Bit" patterns + + gTrafficFunctionMap["bitcomp"] = &bitcomp; + gTrafficFunctionMap["bitrev"] = &bitrev; + gTrafficFunctionMap["transpose"] = &transpose; + gTrafficFunctionMap["shuffle"] = &shuffle; + + // "Digit" patterns + + gTrafficFunctionMap["tornado"] = &tornado; + gTrafficFunctionMap["neighbor"] = &neighbor; + + // Other patterns + + gTrafficFunctionMap["randperm"] = &randperm; + + gTrafficFunctionMap["diagonal"] = &diagonal; + gTrafficFunctionMap["asymmetric"] = &asymmetric; +} + +void ResetTrafficFunction( ) +{ + gResetTraffic++; +} + +void StepTrafficFunction( ) +{ + gStepTraffic++; +} + +tTrafficFunction GetTrafficFunction( const Configuration& config ) +{ + map::const_iterator match; + tTrafficFunction tf; + + string fn; + + config.GetStr( "traffic", fn, "none" ); + match = gTrafficFunctionMap.find( fn ); + + if ( match != gTrafficFunctionMap.end( ) ) { + tf = match->second; + } else { + cout << "Error: Undefined traffic pattern '" << fn << "'." << endl; + exit(-1); + } + + gPermSeed = config.GetInt( "perm_seed" ); + + return tf; +} + + diff --git a/src/intersim/traffic.hpp b/src/intersim/traffic.hpp new file mode 100644 index 0000000..c0a796c --- /dev/null +++ b/src/intersim/traffic.hpp @@ -0,0 +1,15 @@ +#ifndef _TRAFFIC_HPP_ +#define _TRAFFIC_HPP_ + +#include "config_utils.hpp" + +typedef int (*tTrafficFunction)( int, int ); + +void InitializeTrafficMap( ); + +void ResetTraffic( ); +void StepTrafficFunctions( ); + +tTrafficFunction GetTrafficFunction( const Configuration& config ); + +#endif diff --git a/src/intersim/trafficmanager.cpp b/src/intersim/trafficmanager.cpp new file mode 100644 index 0000000..3a633b8 --- /dev/null +++ b/src/intersim/trafficmanager.cpp @@ -0,0 +1,1249 @@ +#include "booksim.hpp" +#include +#include +#include + +#include "trafficmanager.hpp" +#include "random_utils.hpp" +#include "interconnect_interface.h" + +//Turns on flip tracking! +//#ifndef DEBUG +#define DEBUG 0 +//#endif + +int MATLAB_OUTPUT = 0; // output data in MATLAB friendly format +int DISPLAY_LAT_DIST = 1; // distribution of packet latencies +int DISPLAY_HOP_DIST = 1; // distribution of hop counts +int DISPLAY_PAIR_LATENCY = 0; // avg. latency for each s-d pair + +TrafficManager::TrafficManager( const Configuration &config, Network *net , int u_id) +: Module( 0, "traffic_manager" ) +{ + int s; + ostringstream tmp_name; + string sim_type, priority; + + uid = u_id; + _net = net; + _cur_id = 0; + + _sources = _net->NumSources( ); + _dests = _net->NumDests( ); + + // ============ Message priorities ============ + + config.GetStr( "priority", priority ); + + _classes = 1; + + if ( priority == "class" ) { + _classes = 2; + _pri_type = class_based; + } else if ( priority == "age" ) { + _pri_type = age_based; + } else if ( priority == "none" ) { + _pri_type = none; + } else { + Error( "Unknown priority " + priority ); + } + + // ============ Injection VC states ============ + + _buf_states = new BufferState * [_sources]; + + for ( s = 0; s < _sources; ++s ) { + tmp_name << "buf_state_" << s; + _buf_states[s] = new BufferState( config, this, tmp_name.str( ) ); + tmp_name.seekp( 0, ios::beg ); + } + + // ============ Injection queues ============ + + _voqing = config.GetInt( "voq" ); + + if ( _voqing ) { + _use_lagging = false; + } else { + _use_lagging = true; + } + + _time = 0; + _warmup_time = -1; + _drain_time = -1; + _empty_network = false; + + _measured_in_flight = 0; + _total_in_flight = 0; + + if ( _use_lagging ) { + _qtime = new int * [_sources]; + _qdrained = new bool * [_sources]; + } + + if ( _voqing ) { + _voq = new list * [_sources]; + _active_list = new list [_sources]; + _active_vc = new bool * [_sources]; + } + + _partial_packets = new list * [_sources]; + + for ( s = 0; s < _sources; ++s ) { + if ( _use_lagging ) { + _qtime[s] = new int [_classes]; + _qdrained[s] = new bool [_classes]; + } + + if ( _voqing ) { + _voq[s] = new list [_dests]; + _active_vc[s] = new bool [_dests]; + } + + _partial_packets[s] = new list [_classes]; + } + + _split_packets = config.GetInt( "split_packets" ); + + credit_return_queue = new queue [_sources]; + + // ============ Reorder queues ============ + + _reorder = config.GetInt( "reorder" ) ? true : false; + + if ( _reorder ) { + _inject_sqn = new int * [_sources]; + _rob_sqn = new int * [_sources]; + _rob_sqn_max = new int * [_sources]; + _rob = new priority_queue, flitp_compare> * [_sources]; + + for ( int i = 0; i < _sources; ++i ) { + _inject_sqn[i] = new int [_dests]; + _rob_sqn[i] = new int [_dests]; + _rob_sqn_max[i] = new int [_dests]; + _rob[i] = new priority_queue, flitp_compare> [_dests]; + + for ( int j = 0; j < _dests; ++j ) { + _inject_sqn[i][j] = 0; + _rob_sqn[i][j] = 0; + _rob_sqn_max[i][j] = 0; + } + } + + _rob_pri = new int [_dests]; + + for ( int i = 0; i < _dests; ++i ) { + _rob_pri[i] = 0; + } + } + + // ============ Statistics ============ + + _latency_stats = new Stats * [_classes]; + _overall_latency = new Stats * [_classes]; + + for ( int c = 0; c < _classes; ++c ) { + tmp_name << "latency_stat_" << c; + _latency_stats[c] = new Stats( this, tmp_name.str( ), 1.0, 1000 ); + tmp_name.seekp( 0, ios::beg ); + + tmp_name << "overall_latency_stat_" << c; + _overall_latency[c] = new Stats( this, tmp_name.str( ), 1.0, 1000 ); + tmp_name.seekp( 0, ios::beg ); + } + + _pair_latency = new Stats * [_dests]; + _accepted_packets = new Stats * [_dests]; + + for ( int i = 0; i < _dests; ++i ) { + tmp_name << "pair_stat_" << i; + _pair_latency[i] = new Stats( this, tmp_name.str( ), 1.0, 250 ); + tmp_name.seekp( 0, ios::beg ); + + tmp_name << "accepted_stat_" << i; + _accepted_packets[i] = new Stats( this, tmp_name.str( ) ); + tmp_name.seekp( 0, ios::beg ); + } + + _hop_stats = new Stats( this, "hop_stats", 1.0, 20 );; + _overall_accepted = new Stats( this, "overall_acceptance" ); + _overall_accepted_min = new Stats( this, "overall_min_acceptance" ); + + if ( _reorder ) { + _rob_latency = new Stats( this, "rob_latency", 1.0, 1000 ); + _rob_size = new Stats( this, "rob_size", 1.0, 250 ); + } + + _flit_timing = config.GetInt( "flit_timing" ); + + // ============ Simulation parameters ============ + + _load = config.GetFloat( "injection_rate" ); + _packet_size = config.GetInt( "const_flits_per_packet" ); + + _total_sims = config.GetInt( "sim_count" ); + + _internal_speedup = config.GetFloat( "internal_speedup" ); + _partial_internal_cycles = 0.0; + + _traffic_function = NULL; // GetTrafficFunction( config ); // Not used by gpgpusim + _routing_function = GetRoutingFunction( config ); + _injection_process = NULL; // GetInjectionProcess( config ); // Not used by gpgpusim + + config.GetStr( "sim_type", sim_type ); + + if ( sim_type == "latency" ) { + _sim_mode = latency; + } else if ( sim_type == "throughput" ) { + _sim_mode = throughput; + } else { + Error( "Unknown sim_type " + sim_type ); + } + + _sample_period = config.GetInt( "sample_period" ); + _max_samples = config.GetInt( "max_samples" ); + _warmup_periods = config.GetInt( "warmup_periods" ); + _latency_thres = config.GetFloat( "latency_thres" ); + _include_queuing = config.GetInt( "include_queuing" ); +} + +TrafficManager::~TrafficManager( ) +{ + for ( int s = 0; s < _sources; ++s ) { + if ( _use_lagging ) { + delete [] _qtime[s]; + delete [] _qdrained[s]; + } + if ( _voqing ) { + delete [] _voq[s]; + delete [] _active_vc[s]; + } + delete [] _partial_packets[s]; + delete _buf_states[s]; + } + + if ( _use_lagging ) { + delete [] _qtime; + delete [] _qdrained; + } + + if ( _voqing ) { + delete [] _voq; + delete [] _active_vc; + } + + if ( _reorder ) { + for ( int i = 0; i < _sources; ++i ) { + delete [] _inject_sqn[i]; + delete [] _rob_sqn[i]; + delete [] _rob_sqn_max[i]; + delete [] _rob[i]; + } + + delete [] _inject_sqn; + delete [] _rob_sqn; + delete [] _rob_sqn_max; + delete [] _rob; + delete [] _rob_pri; + + delete _rob_latency; + delete _rob_size; + } + + delete [] _buf_states; + delete [] _partial_packets; + + for ( int c = 0; c < _classes; ++c ) { + delete _latency_stats[c]; + delete _overall_latency[c]; + } + + delete [] _latency_stats; + delete [] _overall_latency; + + delete _hop_stats; + delete _overall_accepted; + delete _overall_accepted_min; + + for ( int i = 0; i < _dests; ++i ) { + delete _accepted_packets[i]; + delete _pair_latency[i]; + } + + delete [] _accepted_packets; + delete [] _pair_latency; +} + +Flit *TrafficManager::_NewFlit( ) +{ + Flit *f; + f = new Flit; + + f->id = _cur_id; + f->hops = 0; + f->watch = false; + + // Add specific packet watches for debugging + if (DEBUG || f->id == -1 ) { + f->watch = true; + } + + _in_flight[_cur_id] = true; + ++_cur_id; + return f; +} + +void TrafficManager::_RetireFlit( Flit *f, int dest ) +{ + static int sample_num = 0; + + map::iterator match; + + match = _in_flight.find( f->id ); + + if ( match != _in_flight.end( ) ) { + if ( f->watch ) { + cout << "Matched flit ID = " << f->id << endl; + } + _in_flight.erase( match ); + } else { + cout << "Unmatched flit! ID = " << f->id << endl; + Error( "" ); + } + + if ( f->watch ) { + cout << "Ejecting flit " << f->id + << ", lat = " << _time - f->time + << ", src = " << f->src + << ", dest = " << f->dest << endl; + } + + // Only record statistics once per packet (at true tails) + // unless flit-level timing is on + if ( f->tail || _flit_timing ) { + _total_in_flight--; + if ( _total_in_flight < 0 ) { + Error( "Total in flight count dropped below zero!" ); + } + + if ( ( _sim_state == warming_up ) || f->record ) { + if ( f->true_tail || _flit_timing ) { + _hop_stats->AddSample( f->hops ); + assert( (_time - f->time)>0 ); + switch ( _pri_type ) { + case class_based: + _latency_stats[f->pri]->AddSample( (_time - f->time) ); + break; + case age_based: // fall through + case none: + _latency_stats[0]->AddSample( (_time - f->time) ); + break; + } + + if ( _reorder ) { + _rob_latency->AddSample( (_time - f->rob_time )); + } + + if ( f->src == 0 ) { + _pair_latency[dest]->AddSample( (_time - f->time ) ); + } + } + + if ( f->record ) { + + _measured_in_flight--; + if ( _measured_in_flight < 0 ) { + Error( "Measured in flight count dropped below zero!" ); + } + } + + ++sample_num; + } + } + + delete f; +} + +//never called in gpgpusim +int TrafficManager::_IssuePacket( int source, int cl ) const +{ + float class_load; + if ( _pri_type == class_based ) { + if ( cl == 0 ) { + class_load = 0.9 * _load; + } else { + class_load = 0.1 * _load; + } + } else { + class_load = _load; + } + //gppgusim_injector ignores second parameter! + return _injection_process( source, class_load ); +} + +void TrafficManager::_GeneratePacket( int source, int psize /*# of flits*/ , + int cl, int time, void* data, int dest ) +{ + Flit *f; + bool record; + bool split_head; + bool split_tail; + + if ( ( _sim_state == running ) || + ( ( _sim_state == draining ) && ( time < _drain_time ) ) ) { + record = true; + } else { + record = false; + } + + for ( int i = 0; i < psize; ++i ) { + f = _NewFlit( ); + + split_head = false; + split_tail = false; + + if ( _split_packets > 0 ) { + if ( ( i % _split_packets ) == 0 ) { + split_head = true; + } + + if ( ( i % _split_packets ) == ( _split_packets - 1 ) ) { + split_tail = true; + } + } + + f->src = source; + f->time = time; + f->record = record; + f->data = data; + f->net_num = uid; + if ( ( i == 0 ) || ( split_head ) ) { // Head flit + f->head = true; + f->dest = dest; + } else { + f->head = false; + f->dest = -1; + } + + f->true_tail = false; + if ( ( i == ( psize - 1 ) ) || ( split_tail ) ) { // Tail flit + f->tail = true; + + if ( i == ( psize - 1 ) ) { + f->true_tail = true; + } + } else { + f->tail = false; + } + + if ( _reorder ) { + f->sn = _inject_sqn[source][dest]; + _inject_sqn[source][dest]++; + } + + switch ( _pri_type ) { + case class_based: + f->pri = cl; break; + case age_based: + f->pri = -time; break; + case none: + f->pri = 0; break; + } + + f->vc = -1; + + if ( f->watch ) { + cout << "Generating flit at time " << time << endl; + cout << *f; + } + + if ( f->tail || _flit_timing ) { + if ( record ) { + ++_measured_in_flight; + } + ++_total_in_flight; + } + + if ( _flit_timing ) { + time++; + } + + _partial_packets[source][cl].push_back( f ); + } +} + +void TrafficManager::_FirstStep( ) +{ + // Ensure that all outputs are defined before starting simulation + + _net->WriteOutputs( ); + + for ( int output = 0; output < _net->NumDests( ); ++output ) { + _net->WriteCredit( 0, output ); + } +} + +void TrafficManager::_ClassInject( ) +{ + Flit *f, *nf; + Credit *cred; + + // Receive credits and inject new traffic + for ( int input = 0; input < _net->NumSources( ); ++input ) { + + cred = _net->ReadCredit( input ); + if ( cred ) { + _buf_states[input]->ProcessCredit( cred ); + delete cred; + } + + bool write_flit = false; + int highest_class = 0; + bool generated; + + for ( int c = 0; c < _classes; ++c ) { + // Potentially generate packets for any (input,class) + // that is currently empty + if ( _partial_packets[input][c].empty( ) ) { + generated = false; + + if ( !_empty_network ) { + if ( ( _sim_state == draining ) && + ( _qtime[input][c] > _drain_time ) ) { + _qdrained[input][c] = true; + } + } + + if ( generated ) { + highest_class = c; + } + + } else { + highest_class = c; + } + } + + // Now, check partially issued packet to + // see if it can be issued + if ( !_partial_packets[input][highest_class].empty( ) ) { + f = _partial_packets[input][highest_class].front( ); + + if ( f->head && ( f->vc == -1 ) ) { // Find first available VC + f->vc = _buf_states[input]->FindAvailable( ); + + if ( f->vc != -1 ) { + _buf_states[input]->TakeBuffer( f->vc ); + } + } + + if ( f->vc != -1 ) { + if ( !_buf_states[input]->IsFullFor( f->vc ) ) { + + _partial_packets[input][highest_class].pop_front( ); + _buf_states[input]->SendingFlit( f ); + time_vector_update_icnt_injected(f->data, input); + write_flit = true; + + // Pass VC "back" + if ( !_partial_packets[input][highest_class].empty( ) && !f->tail ) { + nf = _partial_packets[input][highest_class].front( ); + nf->vc = f->vc; + } + } + if ( f->watch ) { + cout << "Flit " << f->id << " written into injection port at time " << _time << endl; + } + } else { + if ( f->watch ) { + cout << "Flit " << f->id << " stalled at injection waiting for available VC at time " << _time << endl; + } + } + } + + _net->WriteFlit( write_flit ? f : 0, input ); + } +} + +void TrafficManager::_VOQInject( ) +{ + Flit *f; + Credit *cred; + + int vc; + int dest; + + for ( int input = 0; input < _net->NumSources( ); ++input ) { + + // Receive credits + cred = _net->ReadCredit( input ); + if ( cred ) { + _buf_states[input]->ProcessCredit( cred ); + + for ( int i = 0; i < cred->vc_cnt; i++ ) { + vc = cred->vc[i]; + + // If this credit enables a VC that has packets waiting, + // set the VC to active (append it to the active list) + + if ( !_voq[input][vc].empty( ) && !_active_vc[input][vc] ) { + f = _voq[input][vc].front( ); + + if ( ( f->head && _buf_states[input]->IsAvailableFor( vc ) ) || + ( !f->head && !_buf_states[input]->IsFullFor( vc ) ) ) { + _active_list[input].push_back( vc ); + _active_vc[input][vc] = true; + } + } + } + + delete cred; + } +/* + if ( !_empty_network ) { + // Inject packets + psize = _IssuePacket( input, 0 ); + } else { + psize = 0; + } +*/ + if ( !_partial_packets[input][0].empty( )/*was psize */) { + //_GeneratePacket( input, psize, 0, _time ); //already generated in interconnect_push + dest = -1; + + bool wasempty = false; + + // Move a generated packet to the appropriate VOQ + while ( !_partial_packets[input][0].empty( ) ) { + f = _partial_packets[input][0].front( ); + _partial_packets[input][0].pop_front( ); + time_vector_update_icnt_injected(f->data, input); + + if ( f->head ) { + dest = f->dest; + wasempty = _voq[input][dest].empty( ); + } + + if ( dest == -1 ) { + Error( "Didn't see head flit in VOQ injection" ); + } + + f->dest = dest; + f->vc = dest; + + _voq[input][dest].push_back( f ); + } + + // If this packet enables a VC, + // set the VC to active (append it to the active list) + if ( wasempty && + ( !_active_vc[input][dest] ) && + ( _buf_states[input]->IsAvailableFor( dest ) ) ) { + _active_list[input].push_back( dest ); + _active_vc[input][dest] = true; + } + } + + // Write packets to the network + if ( !_active_list[input].empty( ) ) { + + dest = _active_list[input].front( ); + _active_list[input].pop_front( ); + + if ( _voq[input][dest].empty( ) ) { + Error( "VOQ marked as active, but empty" ); + } + + f = _voq[input][dest].front( ); + _voq[input][dest].pop_front( ); + + if ( f->head ) { + _buf_states[input]->TakeBuffer( dest ); + } + + _buf_states[input]->SendingFlit( f ); + _net->WriteFlit( f, input ); + + // Inactivate VC if it can't accept any more flits or + // no more flits are available to be sent + if ( ( f->tail && _buf_states[input]->IsAvailableFor( dest ) ) || + ( !f->tail && !_buf_states[input]->IsFullFor( dest ) ) ) { + _active_list[input].push_back( dest ); + } else { + _active_vc[input][dest] = false; + } + + } else { + _net->WriteFlit( 0, input ); + } + } +} + +Flit *TrafficManager::_ReadROB( int dest ) +{ + int src; + Flit *f; + + src = _rob_pri[dest]; + f = 0; + + for ( int i = 0; i < _sources; ++i ) { + + if ( !_rob[src][dest].empty( ) ) { + f = _rob[src][dest].top( ); + + if ( f->sn == _rob_sqn[src][dest] ) { + _rob[src][dest].pop( ); + _rob_sqn[src][dest]++; + _rob_pri[dest] = ( src + 1 ) % _sources; + break; + } else { + f = 0; + } + } + + src = ( src + 1 ) % _sources; + } + + return f; +} + +void TrafficManager::_Step( ) +{ + Flit *f; + Credit *cred; + + // Inject traffic + if ( _voqing ) { + _VOQInject( ); + } else { + _ClassInject( ); + } + + // Advance network + + _net->ReadInputs( ); + + _partial_internal_cycles += _internal_speedup; + while ( _partial_internal_cycles >= 1.0 ) { + _net->InternalStep( ); + _partial_internal_cycles -= 1.0; + } + + _net->WriteOutputs( ); + + ++_time; + + // Eject traffic and send credits + Flit *last_valid_flit; //= new Flit; + for ( int output = 0; output < _dests; ++output ) { + f = _net->ReadFlit( output ); + + if ( f ) { + if (1 || f->tail) { + write_out_buf(output, f); // it should have space! + if ( f->watch ) { + cout << "Sent flit " << f->id << " to output buffer " << output << endl; + cout << " Not sending the credit yet! " <watch ) { + cout << "ejected flit " << f->id << " at output " << output << endl; + cout << "sending credit for " << f->vc << endl; + } + + if ( _reorder ) { + if ( f->watch ) { + cout << "adding flit " << f->id << " to reorder buffer" << endl; + cout << "flit's SN is " << f->sn << " buffer's SN is " + << _rob_sqn[f->src][f->dest] << endl; + } + + if ( f->sn > _rob_sqn_max[f->src][f->dest] ) { + _rob_sqn_max[f->src][f->dest] = f->sn; + } + + if ( f->head ) { + _rob_size->AddSample( f->sn - _rob_sqn[f->src][f->dest] ); + } + + f->rob_time = _time; + _rob[f->src][output].push( f ); + } else { + _RetireFlit( f, output ); + if ( !_empty_network ) { + _accepted_packets[output]->AddSample( 1 ); + } + } + } + } + transfer2boundary_buf( output ); + if (!credit_return_queue[output].empty()) { + last_valid_flit = credit_return_queue[output].front(); + credit_return_queue[output].pop(); + } else { + last_valid_flit=NULL; + } + if (last_valid_flit) { + + + cred = new Credit( 1 ); + cred->vc[0] =last_valid_flit->vc; + cred->vc_cnt = 1; + cred->head = last_valid_flit->head; + cred->tail =last_valid_flit->tail; + + _net->WriteCredit( cred, output ); + if (last_valid_flit->watch) { + cout <<"WE WROTE A CREDIT for flit "<id<<"To output "<AddSample( 1 ); + } + } else { + _net->WriteCredit( 0, output ); + + if ( !_reorder && !_empty_network) { + _accepted_packets[output]->AddSample( 0 ); + } + } + + if ( _reorder ) { + f = _ReadROB( output ); + + if ( f ) { + if ( f->watch ) { + cout << "flit " << f->id << " removed from ROB at output " << output << endl; + } + + _RetireFlit( f, output ); + if ( !_empty_network ) { + _accepted_packets[output]->AddSample( 1 ); + } + } else { + if ( !_empty_network ) { + _accepted_packets[output]->AddSample( 0 ); + } + } + } + } +} + +bool TrafficManager::_PacketsOutstanding( ) const +{ + bool outstanding; + + if ( _measured_in_flight == 0 ) { + outstanding = false; + + if ( _use_lagging ) { + for ( int c = 0; c < _classes; ++c ) { + for ( int s = 0; s < _sources; ++s ) { + if ( !_qdrained[s][c] ) { +#ifdef DEBUG_DRAIN + cout << "waiting on queue " << s << " class " << c; + cout << ", time = " << _time << " qtime = " << _qtime[s][c] << endl; +#endif + outstanding = true; + break; + } + } + if ( outstanding ) { + break; + } + } + } + } else { +#ifdef DEBUG_DRAIN + cout << "in flight = " << _measured_in_flight << endl; +#endif + outstanding = true; + } + + return outstanding; +} + +void TrafficManager::_ClearStats( ) +{ + for ( int c = 0; c < _classes; ++c ) { + _latency_stats[c]->Clear( ); + } + + for ( int i = 0; i < _dests; ++i ) { + _accepted_packets[i]->Clear( ); + _pair_latency[i]->Clear( ); + } + + if ( _reorder ) { + _rob_latency->Clear( ); + _rob_size->Clear( ); + } +} + +int TrafficManager::_ComputeAccepted( double *avg, double *min ) const +{ + int dmin; + + *min = 1.0; + *avg = 0.0; + + for ( int d = 0; d < _dests; ++d ) { + if ( _accepted_packets[d]->Average( ) < *min ) { + *min = _accepted_packets[d]->Average( ); + dmin = d; + } + *avg += _accepted_packets[d]->Average( ); + } + + *avg /= (double)_dests; + + return dmin; +} + +void TrafficManager::_DisplayRemaining( ) const +{ + map::const_iterator iter; + int i; + + cout << "Remaining flits (" << _measured_in_flight << " measurement packets) : "; + for ( iter = _in_flight.begin( ), i = 0; + ( iter != _in_flight.end( ) ) && ( i < 20 ); + iter++, i++ ) { + cout << iter->first << " "; + } + cout << endl; +} + +//special initilization each tiem a new GPU grid is started +void TrafficManager::IcntInitPerGrid (int time) +{ //some initialization parts of _SingleSim for gpgpgusim + _time = time ; + if ( _use_lagging ) { + for ( int s = 0; s < _sources; ++s ) { + for ( int c = 0; c < _classes; ++c ) { + _qtime[s][c] = _time; // Was Zero + _qdrained[s][c] = false; + } + } + } + + if ( _voqing ) { + for ( int s = 0; s < _sources; ++s ) { + for ( int d = 0; d < _dests; ++d ) { + _active_vc[s][d] = false; + } + } + } + _sim_state = running; + _ClearStats( ); +} + +bool TrafficManager::_SingleSim( ) +{ + int iter; + int total_phases; + int converged; + int max_outstanding; + int empty_steps; + + double cur_latency; + double prev_latency; + + double cur_accepted; + double prev_accepted; + + double warmup_threshold; + double stopping_threshold; + double acc_stopping_threshold; + + double min, avg; + + bool clear_last; + + _time = 0; + + if ( _use_lagging ) { + for ( int s = 0; s < _sources; ++s ) { + for ( int c = 0; c < _classes; ++c ) { + _qtime[s][c] = 0; + _qdrained[s][c] = false; + } + } + } + + if ( _voqing ) { + for ( int s = 0; s < _sources; ++s ) { + for ( int d = 0; d < _dests; ++d ) { + _active_vc[s][d] = false; + } + } + } + + stopping_threshold = 0.01; + acc_stopping_threshold = 0.01; + warmup_threshold = 0.05; + iter = 0; + converged = 0; + max_outstanding = 0; + total_phases = 0; + + // warm-up ... + // reset stats, all packets after warmup_time marked + // converge + // draing, wait until all packets finish + + _sim_state = warming_up; + total_phases = 0; + prev_latency = 0; + prev_accepted = 0; + + _ClearStats( ); + clear_last = false; + + while ( ( total_phases < _max_samples ) && + ( ( _sim_state != running ) || + ( converged < 3 ) ) ) { + + if ( clear_last || ( ( _sim_state == warming_up ) && ( (total_phases & 0x1) == 0 ) ) ) { + clear_last = false; + _ClearStats( ); + } + + for ( iter = 0; iter < _sample_period; ++iter ) { + _Step( ); + } + + cout << "%=================================" << endl; + + int dmin; + + cur_latency = _latency_stats[0]->Average( ); + dmin = _ComputeAccepted( &avg, &min ); + cur_accepted = avg; + + cout << "% Average latency = " << cur_latency << endl; + + if ( _reorder ) { + cout << "% Reorder latency = " << _rob_latency->Average( ) << endl; + cout << "% Reorder size = " << _rob_size->Average( ) << endl; + } + + cout << "% Accepted packets = " << min << " at node " << dmin << " (avg = " << avg << ")" << endl; + + if ( MATLAB_OUTPUT ) { + cout << "lat(" << total_phases + 1 << ") = " << cur_latency << ";" << endl; + cout << "thru(" << total_phases + 1 << ",:) = [ "; + for ( int d = 0; d < _dests; ++d ) { + cout << _accepted_packets[d]->Average( ) << " "; + } + cout << "];" << endl; + } + + // Fail safe + if ( ( _sim_mode == latency ) && ( cur_latency >_latency_thres ) ) { + cout << "Average latency is getting huge" << endl; + converged = 0; + _sim_state = warming_up; + break; + } + + cout << "% latency change = " << fabs( ( cur_latency - prev_latency ) / cur_latency ) << endl; + cout << "% throughput change = " << fabs( ( cur_accepted - prev_accepted ) / cur_accepted ) << endl; + + if ( _sim_state == warming_up ) { + + if ( _warmup_periods == 0 ) { + if ( _sim_mode == latency ) { + if ( ( fabs( ( cur_latency - prev_latency ) / cur_latency ) < warmup_threshold ) && + ( fabs( ( cur_accepted - prev_accepted ) / cur_accepted ) < warmup_threshold ) ) { + cout << "% Warmed up ..." << endl; + clear_last = true; + _sim_state = running; + } + } else { + if ( fabs( ( cur_accepted - prev_accepted ) / cur_accepted ) < warmup_threshold ) { + cout << "% Warmed up ..." << endl; + clear_last = true; + _sim_state = running; + } + } + } else { + if ( total_phases + 1 >= _warmup_periods ) { + cout << "% Warmed up ..." << endl; + clear_last = true; + _sim_state = running; + } + } + } else if ( _sim_state == running ) { + if ( _sim_mode == latency ) { + if ( ( fabs( ( cur_latency - prev_latency ) / cur_latency ) < stopping_threshold ) && + ( fabs( ( cur_accepted - prev_accepted ) / cur_accepted ) < acc_stopping_threshold ) ) { + ++converged; + } else { + converged = 0; + } + } else { + if ( fabs( ( cur_accepted - prev_accepted ) / cur_accepted ) > acc_stopping_threshold ) { + converged = 0; + } + } + } + + prev_latency = cur_latency; + prev_accepted = cur_accepted; + + ++total_phases; + } + + if ( _sim_state == running ) { + ++converged; + + if ( _sim_mode == latency ) { + cout << "% Draining all recorded packets ..." << endl; + _sim_state = draining; + _drain_time = _time; + empty_steps = 0; + while ( _PacketsOutstanding( ) ) { + _Step( ); + ++empty_steps; + + if ( empty_steps % 1000 == 0 ) { + _DisplayRemaining( ); + } + } + } + } else { + cout << "Too many sample periods needed to converge" << endl; + } + + // Empty any remaining packets + cout << "% Draining remaining packets ..." << endl; + _empty_network = true; + empty_steps = 0; + while ( _total_in_flight > 0 ) { + _Step( ); + ++empty_steps; + + if ( empty_steps % 1000 == 0 ) { + _DisplayRemaining( ); + } + } + _empty_network = false; + + return( converged > 0 ); +} + +void TrafficManager::SetDrainState( ) +{ + _sim_state = draining; + _drain_time = _time; + +} + +void TrafficManager::ShowOveralStat( ) +{ + int c; + + for ( c = 0; c < _classes; ++c ) { + cout << "=======Traffic["<NumSamples( ) << " samples)" << endl; + + cout << "Traffic["<NumSamples( ) << " samples)" << endl; + + cout << "Traffic["<NumSamples( ) << " samples)" << endl; + + if ( DISPLAY_LAT_DIST ) { + _latency_stats[c]->Display( ); + } + } + + if ( _reorder ) { + cout << "Traffic["<Average( ) << endl; + + if ( DISPLAY_LAT_DIST ) { + _rob_latency->Display( ); + _rob_size->Display( ); + } + } + + if ( DISPLAY_HOP_DIST ) { + cout << "Traffic["<NumSamples( ) << " samples)" << endl; + + _hop_stats->Display( ); + } + + if ( DISPLAY_PAIR_LATENCY ) { + for ( int i = 0; i < _dests; ++i ) { + cout << "Traffic["<NumSamples( ) << " samples)" << endl; + _pair_latency[i]->Display( ); + } + } +} + +void TrafficManager::ShowStats() +{ + double min, avg; + + static int total_phases; + + double cur_latency; + static double prev_latency; + + double cur_accepted; + static double prev_accepted; + + //from step + cout << "%=================================" << endl; + + cur_latency = _latency_stats[0]->Average( ); + int dmin = _ComputeAccepted( &avg, &min ); + cur_accepted = avg; + + cout << "% Average latency = " << cur_latency << endl; + + if ( _reorder ) { + cout << "% Reorder latency = " << _rob_latency->Average( ) << endl; + cout << "% Reorder size = " << _rob_size->Average( ) << endl; + } + + cout << "% Accepted packets = " << min << " at node " << dmin << " (avg = " << avg << ")" << endl; + + if ( MATLAB_OUTPUT ) { + cout << "lat(" << total_phases + 1 << ") = " << cur_latency << ";" << endl; + cout << "thru(" << total_phases + 1 << ",:) = [ "; + for ( int d = 0; d < _dests; ++d ) { + cout << _accepted_packets[d]->Average( ) << " "; + } + cout << "];" << endl; + } + + cout << "% latency change = " << fabs( ( cur_latency - prev_latency ) / cur_latency ) << endl; + cout << "% throughput change = " << fabs( ( cur_accepted - prev_accepted ) / cur_accepted ) << endl; + + prev_latency = cur_latency; + prev_accepted = cur_accepted; + total_phases++; + + + //from Run + //save last Grid's stats + for ( int c = 0; c < _classes; ++c ) { + _overall_latency[c]->AddSample( _latency_stats[c]->Average( ) ); + } + + //_ComputeAccepted( &avg, &min ); + _overall_accepted->AddSample( avg ); + _overall_accepted_min->AddSample( min ); +/* moved to interconnect_stats function in intreconnect_interface + cout << "%=================================" << endl; + cout << "Link utilizations:" << endl; + _net->Display(); +*/ +} diff --git a/src/intersim/trafficmanager.hpp b/src/intersim/trafficmanager.hpp new file mode 100644 index 0000000..105d73c --- /dev/null +++ b/src/intersim/trafficmanager.hpp @@ -0,0 +1,173 @@ +#ifndef _TRAFFICMANAGER_HPP_ +#define _TRAFFICMANAGER_HPP_ + +#include +#include +#include + +#include "module.hpp" +#include "config_utils.hpp" +#include "network.hpp" +#include "flit.hpp" +#include "buffer_state.hpp" +#include "stats.hpp" +#include "traffic.hpp" +#include "routefunc.hpp" +#include "outputset.hpp" +#include "injection.hpp" + +class flitp_compare { +public: + bool operator()( const Flit *a, const Flit *b ) const { + return( a->sn > b->sn ); + } +}; + +class TrafficManager : public Module { +public: + int _sources; + int _dests; + + Network *_net; + + // ============ Message priorities ============ + + enum ePriority { + class_based, age_based, none + }; + + ePriority _pri_type; + int _classes; + + // ============ Injection VC states ============ + + BufferState **_buf_states; + + // ============ Injection queues ============ + + int _voqing; + int **_qtime; + bool **_qdrained; + list **_partial_packets; + + bool _use_lagging; + + list **_voq; + list *_active_list; + bool **_active_vc; + + int _measured_in_flight; + int _total_in_flight; + map _in_flight; + bool _empty_network; + + int _split_packets; + + queue * credit_return_queue; //keeps flits that their corresponding credits are not sent yet + // ============ Reorder queues ============ + + bool _reorder; + + int **_inject_sqn; + int **_rob_sqn; + int **_rob_sqn_max; + int *_rob_pri; + + priority_queue, flitp_compare> **_rob; + + // ============ Statistics ============ + + Stats **_latency_stats; + Stats **_overall_latency; + Stats *_rob_latency; + Stats *_rob_size; + + Stats **_pair_latency; + Stats *_hop_stats; + + Stats **_accepted_packets; + Stats *_overall_accepted; + Stats *_overall_accepted_min; + + int **_latest_packet; + + bool _flit_timing; + + // ============ Simulation parameters ============ + + enum eSimState { + warming_up, running, draining, done + }; + eSimState _sim_state; + + enum eSimMode { + latency, throughput + }; + eSimMode _sim_mode; + + int _warmup_time; + int _drain_time; + + float _load; + int _packet_size; + + int _total_sims; + int _sample_period; + int _max_samples; + int _warmup_periods; + + int _include_queuing; + + double _latency_thres; + + float _internal_speedup; + float _partial_internal_cycles; + + int _cur_id; + int _time; + + list _used_flits; + list _free_flits; + + tTrafficFunction _traffic_function; + tRoutingFunction _routing_function; + tInjectionProcess _injection_process; + + // ============ Internal methods ============ + + Flit *_NewFlit( ); + void _RetireFlit( Flit *f, int dest ); + + void _FirstStep( ); + void _Step( ); + + Flit *_ReadROB( int dest ); + + bool _PacketsOutstanding( ) const; + + int _IssuePacket( int source, int cl ) const; + void _GeneratePacket( int source, int size, int cl, int time, void* data, int dest ); + + void _ClassInject( ); + void _VOQInject( ); + + void _ClearStats( ); + + int _ComputeAccepted( double *avg, double *min ) const; + + bool _SingleSim( ); + + void _DisplayRemaining( ) const; + +public: + int uid; // this traffic manger's ID useful when we have more than 1 traffic objects + TrafficManager( const Configuration &config, Network *net,int uid ); + ~TrafficManager( ); + void IcntInitPerGrid (int time); + void SetDrainState( ); + void ShowStats(); + void ShowOveralStat( ); + void IcntInitPerGrid(); +}; + +#endif diff --git a/src/intersim/vc.cpp b/src/intersim/vc.cpp new file mode 100644 index 0000000..a1a4ff6 --- /dev/null +++ b/src/intersim/vc.cpp @@ -0,0 +1,175 @@ +#include "booksim.hpp" +#include "vc.hpp" + +void VC::init( const Configuration& config, int outputs ) +{ + _Init( config, outputs ); +} + +VC::VC( const Configuration& config, int outputs, + Module *parent, const string& name ) : +Module( parent, name ) +{ + _Init( config, outputs ); +} + +VC::~VC( ) +{ +} + +void VC::_Init( const Configuration& config, int outputs ) +{ + _state = idle; + _state_time = 0; + + _size = int( config.GetInt( "vc_buf_size" ) ); + + _route_set = new OutputSet( outputs ); + + _occupied_cnt = 0; + + _total_cycles = 0; + _vc_alloc_cycles = 0; + _active_cycles = 0; + _idle_cycles = 0; + + _pri = 0; + + _watched = false; +} + +bool VC::AddFlit( Flit *f ) +{ + bool success = false; + + if ( (int)_buffer.size( ) != _size ) { + _buffer.push( f ); + success = true; + } + + return success; +} + +Flit *VC::FrontFlit( ) +{ + Flit *f; + + if ( !_buffer.empty( ) ) { + f = _buffer.front( ); + } else { + f = 0; + } + + return f; +} + +Flit *VC::RemoveFlit( ) +{ + Flit *f; + + if ( !_buffer.empty( ) ) { + f = _buffer.front( ); + _buffer.pop( ); + } else { + f = 0; + } + + return f; +} + +bool VC::Empty( ) const +{ + return _buffer.empty( ); +} + +VC::eVCState VC::GetState( ) const +{ + return _state; +} + +int VC::GetStateTime( ) const +{ + return _state_time; +} + +void VC::SetState( eVCState s ) +{ + _state = s; + _state_time = 0; + + if ( s == active ) { + Flit *f; + + f = FrontFlit( ); + if ( f ) { + _pri = f->pri; + } + + _occupied_cnt++; + } +} + +const OutputSet *VC::GetRouteSet( ) const +{ + return _route_set; +} + +void VC::SetOutput( int port, int vc ) +{ + _out_port = port; + _out_vc = vc; +} + +int VC::GetOutputPort( ) const +{ + return _out_port; +} + +int VC::GetOutputVC( ) const +{ + return _out_vc; +} + +int VC::GetPriority( ) const +{ + return _pri; +} + +void VC::Route( tRoutingFunction rf, const Router* router, const Flit* f, int in_channel ) +{ + rf( router, f, in_channel, _route_set, false ); +} + +void VC::AdvanceTime( ) +{ + _state_time++; + + _total_cycles++; + switch ( _state ) { + case idle : _idle_cycles++; break; + case active : _active_cycles++; break; + case vc_alloc : _vc_alloc_cycles++; break; + case routing : break; + } +} + +// ==== Debug functions ==== + +void VC::SetWatch( bool watch ) +{ + _watched = watch; +} + +bool VC::IsWatched( ) const +{ + return _watched; +} + +void VC::Display( ) const +{ + cout << _fullname << " : " + << "idle " << 100.0 * (double)_idle_cycles / (double)_total_cycles << "% " + << "vc_alloc " << 100.0 * (double)_vc_alloc_cycles / (double)_total_cycles << "% " + << "active " << 100.0 * (double)_active_cycles / (double)_total_cycles << "% " + << endl; +} diff --git a/src/intersim/vc.hpp b/src/intersim/vc.hpp new file mode 100644 index 0000000..ecb8c2d --- /dev/null +++ b/src/intersim/vc.hpp @@ -0,0 +1,79 @@ +#ifndef _VC_HPP_ +#define _VC_HPP_ + +#include + +#include "flit.hpp" +#include "outputset.hpp" +#include "routefunc.hpp" +#include "config_utils.hpp" + +class VCRouter; + +class VC : public Module { +public: + enum eVCState { + idle, routing, vc_alloc, active + }; + +private: + int _size; + + queue _buffer; + + eVCState _state; + int _state_time; + + OutputSet *_route_set; + int _out_port, _out_vc; + + int _occupied_cnt; + int _total_cycles; + int _vc_alloc_cycles; + int _active_cycles; + int _idle_cycles; + + int _pri; + + void _Init( const Configuration& config, int outputs ); + + bool _watched; + +public: + VC() : Module() {} + void init( const Configuration& config, int outputs ); + VC( const Configuration& config, int outputs, + Module *parent, const string& name ); + ~VC( ); + + bool AddFlit( Flit *f ); + Flit *FrontFlit( ); + Flit *RemoveFlit( ); + + bool Empty( ) const; + + eVCState GetState( ) const; + int GetStateTime( ) const; + void SetState( eVCState s ); + + const OutputSet *GetRouteSet( ) const; + + void SetOutput( int port, int vc ); + int GetOutputPort( ) const; + int GetOutputVC( ) const; + + int GetPriority( ) const; + + void Route( tRoutingFunction rf, const Router* router, const Flit* f, int in_channel ); + + void AdvanceTime( ); + + // ==== Debug functions ==== + + void SetWatch( bool watch = true ); + bool IsWatched( ) const; + + void Display( ) const; +}; + +#endif diff --git a/src/intersim/wavefront.cpp b/src/intersim/wavefront.cpp new file mode 100644 index 0000000..c9fab7a --- /dev/null +++ b/src/intersim/wavefront.cpp @@ -0,0 +1,61 @@ +#include "booksim.hpp" +#include + +#include "wavefront.hpp" +#include "random_utils.hpp" + +Wavefront::Wavefront( const Configuration &config, + Module *parent, const string& name, + int inputs, int outputs ) : +DenseAllocator( config, parent, name, inputs, outputs ) +{ + // We need a square wavefront allocator, so take the max dimension + _square = ( _inputs > _outputs ) ? _inputs : _outputs; + + // The diagonal with priority + _pri = 0; +} + +Wavefront::~Wavefront( ) +{ +} + +void Wavefront::Allocate( ) +{ + int input; + int output; + + // Clear matching + + for ( int i = 0; i < _inputs; ++i ) { + _inmatch[i] = -1; + } + for ( int j = 0; j < _outputs; ++j ) { + _outmatch[j] = -1; + } + + // Loop through diagonals of request matrix + + for ( int p = 0; p < _square; ++p ) { + output = ( _pri + p ) % _square; + + // Step through the current diagonal + for ( input = 0; input < _inputs; ++input ) { + if ( ( output < _outputs ) && + ( _inmatch[input] == -1 ) && + ( _outmatch[output] == -1 ) && + ( _request[input][output].label != -1 ) ) { + // Grant! + _inmatch[input] = output; + _outmatch[output] = input; + } + + output = ( output + 1 ) % _square; + } + } + + // Round-robin the priority diagonal + _pri = ( _pri + 1 ) % _square; +} + + diff --git a/src/intersim/wavefront.hpp b/src/intersim/wavefront.hpp new file mode 100644 index 0000000..d0c9ba6 --- /dev/null +++ b/src/intersim/wavefront.hpp @@ -0,0 +1,19 @@ +#ifndef _WAVEFRONT_HPP_ +#define _WAVEFRONT_HPP_ + +#include "allocator.hpp" + +class Wavefront : public DenseAllocator { + int _square; + int _pri; + +public: + Wavefront( const Configuration &config, + Module *parent, const string& name, + int inputs, int outputs ); + ~Wavefront( ); + + void Allocate( ); +}; + +#endif -- cgit v1.3