diff options
Diffstat (limited to 'src')
143 files changed, 40014 insertions, 0 deletions
diff --git a/src/Makefile b/src/Makefile new file mode 100644 index 0000000..36b695a --- /dev/null +++ b/src/Makefile @@ -0,0 +1,108 @@ +# Copyright (c) 2009 by Tor M. Aamodt, Wilson W. L. Fung, Ali Bakhoda, +# George L. Yuan, Ivan Sham and the +# University of British Columbia +# Vancouver, BC V6T 1Z4 +# All Rights Reserved. +# +# THIS IS A LEGAL DOCUMENT BY DOWNLOADING GPGPU-SIM, YOU ARE AGREEING TO THESE +# TERMS AND CONDITIONS. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNERS OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# +# NOTE: The files libcuda/cuda_runtime_api.c and src/cuda-sim/cuda-math.h +# are derived from the CUDA Toolset available from http://www.nvidia.com/cuda +# (property of NVIDIA). The files benchmarks/BlackScholes/* and +# benchmarks/template/* are derived from the CUDA SDK available from +# http://www.nvidia.com/cuda (also property of NVIDIA). The files +# src/gpgpusim_entrypoint.c and src/simplesim-3.0/* are derived from the +# SimpleScalar Toolset available from http://www.simplescalar.com/ +# (property of SimpleScalar LLC) and the files src/intersim/* are derived +# from Booksim (Simulator provided with the textbook "Principles and +# Practices of Interconnection Networks" available from +# http://cva.stanford.edu/books/ppin/). As such, those files are bound by +# the corresponding legal terms and conditions set forth separately (original +# copyright notices are left in files from these sources and where we have +# modified a file our copyright notice appears before the original copyright +# notice). +# +# Using this version of GPGPU-Sim requires a complete installation of CUDA +# version 1.1, which is distributed seperately by NVIDIA under separate terms +# and conditions. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# 3. Neither the name of the University of British Columbia nor the names of +# its contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# 4. This version of GPGPU-SIM is distributed freely for non-commercial use only. +# +# 5. No nonprofit user may place any restrictions on the use of this software, +# including as modified by the user, by any other authorized user. +# +# 6. GPGPU-SIM was developed primarily by Tor M. Aamodt, Wilson W. L. Fung, +# Ali Bakhoda, George L. Yuan, at the University of British Columbia, +# Vancouver, BC V6T 1Z4 + +# GPGPU-Sim Makefile + +DEBUG=0 + +CXXFLAGS = -Wall -DDEBUG + +GNUC_CPP0X := $(shell gcc --version | perl -ne 'if (/gcc\s+\(.*\)\s+([0-9.]+)/){ if($$1 >= 4.3) {$$n=1} else {$$n=0;} } END { print $$n; }') +ifeq ($(GNUC_CPP0X), 1) + CXXFLAGS += -std=c++0x +endif + +ifneq ($(DEBUG),1) + OPTFLAGS += -O3 +else + CXXFLAGS += -D_GLIBCXX_DEBUG -DGLIBCXX_DEBUG_PEDANTIC +endif + +OPTFLAGS += -g3 -fPIC + +CPP = g++ $(SNOW) +OEXT = o + +SRCS = $(shell ls *.cc) +OBJS = $(SRCS:.cc=.$(OEXT)) + +libgpgpusim.a: $(OBJS) gpu_uarch_simlib + ar rcs libgpgpusim.a $(OBJS) gpgpu-sim/*.o + +gpu_uarch_simlib: + make -C ./gpgpu-sim + +depend: + makedepend $(SRCS) 2> /dev/null + +.cc.$(OEXT): + $(CPP) $(OPTFLAGS) $(CXXFLAGS) -o $*.$(OEXT) -c $*.cc + +clean: + rm -f *.o core *~ *.a + +option_parser.$(OEXT): option_parser.h + +# DO NOT DELETE + diff --git a/src/abstract_hardware_model.h b/src/abstract_hardware_model.h new file mode 100644 index 0000000..39f4b3f --- /dev/null +++ b/src/abstract_hardware_model.h @@ -0,0 +1,12 @@ +#ifndef CORE_T_INCLUDED +#define CORE_T_INCLUDED + +class core_t { +public: + virtual ~core_t() {} + virtual void set_at_barrier( unsigned cta_id, unsigned warp_id ) = 0; + virtual void warp_exit( unsigned warp_id ) = 0; + virtual bool warp_waiting_at_barrier( unsigned warp_id ) = 0; +}; + +#endif diff --git a/src/cuda-sim/Makefile b/src/cuda-sim/Makefile new file mode 100644 index 0000000..c0d245d --- /dev/null +++ b/src/cuda-sim/Makefile @@ -0,0 +1,170 @@ +# Copyright (c) 2009 by Tor M. Aamodt and the University of British Columbia +# Vancouver, BC V6T 1Z4 +# All Rights Reserved. +# +# THIS IS A LEGAL DOCUMENT BY DOWNLOADING GPGPU-SIM, YOU ARE AGREEING TO THESE +# TERMS AND CONDITIONS. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNERS OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# +# NOTE: The files libcuda/cuda_runtime_api.c and src/cuda-sim/cuda-math.h +# are derived from the CUDA Toolset available from http://www.nvidia.com/cuda +# (property of NVIDIA). The files benchmarks/BlackScholes/ and +# benchmarks/template/ are derived from the CUDA SDK available from +# http://www.nvidia.com/cuda (also property of NVIDIA). The files +# src/gpgpusim_entrypoint.c and src/simplesim-3.0/ are derived from the +# SimpleScalar Toolset available from http://www.simplescalar.com/ +# (property of SimpleScalar LLC) and the files src/intersim/ are derived +# from Booksim (Simulator provided with the textbook "Principles and +# Practices of Interconnection Networks" available from +# http://cva.stanford.edu/books/ppin/). As such, those files are bound by +# the corresponding legal terms and conditions set forth separately (original +# copyright notices are left in files from these sources and where we have +# modified a file our copyright notice appears before the original copyright +# notice). +# +# Using this version of GPGPU-Sim requires a complete installation of CUDA +# which is distributed seperately by NVIDIA under separate terms and +# conditions. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# 3. Neither the name of the University of British Columbia nor the names of +# its contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# 4. This version of GPGPU-SIM is distributed freely for non-commercial use only. +# +# 5. No nonprofit user may place any restrictions on the use of this software, +# including as modified by the user, by any other authorized user. +# +# 6. GPGPU-SIM was developed primarily by Tor M. Aamodt, Wilson W. L. Fung, +# Ali Bakhoda, George L. Yuan, at the University of British Columbia, +# Vancouver, BC V6T 1Z4 + +default: libgpgpu_ptx_sim.a + +INTEL=0 +DEBUG?=0 + +CPP = g++ $(SNOW) +CC = gcc $(SNOW) +ifeq ($(INTEL),1) + CPP = icpc + CC = icc +endif + +CC_VERSION := $(shell gcc --version | perl -ne '/gcc\s+\(.*\)\s+([0-9.]+)/ and print $$1;') +GNUC_CPP0X := $(shell gcc --version | perl -ne 'if (/gcc\s+\(.*\)\s+([0-9.]+)/){ if($$1 >= 4.3) {$$n=1} else {$$n=0;} } END { print $$n; }') + +OPT := -O3 -g3 -Wall -Wno-unused-function -Wno-sign-compare +ifeq ($(DEBUG),1) + OPT := -g3 -Wall -Wno-unused-function -Wno-sign-compare +endif +OPT += -I$(CUDAHOME)/include +OPT += -fPIC + +CXX_OPT = $(OPT) +ifeq ($(INTEL),1) + CXX_OPT += -std=c++0x +else +ifeq ($(GNUC_CPP0X),1) + CXX_OPT += -std=c++0x +endif +endif + +OBJS := instructions.o cuda-sim.o ptx_ir.o ptx_sim.o memory.o ptx-stats.o ptx.tab.o lex.ptx_.o ptxinfo.tab.o lex.ptxinfo_.o + +CUDART_VERSION:=$(shell nvcc --version | awk '/release/ {print $$5;}' | sed 's/,//' | sed 's/\./ /' | awk '{printf("%02u%02u", 10*int($$1), 10*$$2);}') + +OPT += -DCUDART_VERSION=$(CUDART_VERSION) + +libgpgpu_ptx_sim.a: $(OBJS) + ar rcs libgpgpu_ptx_sim.a ptx.tab.o lex.ptx_.o ptxinfo.tab.o lex.ptxinfo_.o $(OBJS) + +ptx.tab.o: ptx.tab.c + $(CC) -c $(OPT) -DYYDEBUG ptx.tab.c + +lex.ptx_.o: lex.ptx_.c + $(CC) -c $(OPT) lex.ptx_.c + +ptxinfo.tab.o: ptxinfo.tab.c + $(CC) -c $(OPT) -DYYDEBUG ptxinfo.tab.c + +lex.ptxinfo_.o: lex.ptxinfo_.c + $(CC) -c $(OPT) lex.ptxinfo_.c + +ptx.tab.c: + bison --name-prefix=ptx_ -v -d ptx.y + +ptxinfo.tab.c: + bison --name-prefix=ptxinfo_ -v -d ptxinfo.y + +lex.ptx_.c: + flex ptx.l + +lex.ptxinfo_.c: + flex ptxinfo.l + +clean: + rm -f *~ *.o *.gcda *.gcno *.gcov libgpgpu_ptx_sim.a \ + ptx.tab.h ptx.tab.c ptx.output lex.ptx_.c \ + ptxinfo.tab.h ptxinfo.tab.c ptxinfo.output lex.ptxinfo_.c \ + instructions.h ptx_parser_decode.def directed_tests.log + +.c.o: + $(CC) -c $(OPT) $< -o $*.o +.cc.o: + $(CPP) -c $(CXX_OPT) $< -o $*.o + +instructions.h: instructions.cc + @touch $*.h + @chmod +w $*.h + @echo "// DO NOT EDIT THIS FILE! IT IS AUTOMATICALLY GENERATED BY THE MAKEFILE (see target for instructions.h)" > $*.h + @echo "#include \"ptx_ir.h\"" >> $*.h + @echo "#ifndef instructions_h_included" >> $*.h + @echo "#define instructions_h_included" >> $*.h + @echo "#ifdef __cplusplus" >> $*.h + @echo "extern \"C\" {" >> $*.h + @echo "#endif" >> $*.h + @cat $< | grep "_impl(" | sed 's/{.*//' | sed 's/$$/;/' >> $*.h + @echo "#ifdef __cplusplus" >> $*.h + @echo "}" >> $*.h + @echo "#endif" >> $*.h + @echo "#endif" >> $*.h + @chmod -w $*.h + @echo "created instructions.h" + +ptx_parser_decode.def: ptx.tab.c +ifeq ($(shell uname),Linux) + cat ptx.tab.h | grep "=" | sed 's/^[ ]\+//' | sed 's/[=,]//g' | sed 's/\([_A-Z1-9]\+\)[ ]\+\([0-9]\+\)/\1 \1/' | sed 's/^/DEF(/' | sed 's/ /,"/' | sed 's/$$/")/' > ptx_parser_decode.def +else + cat ptx.tab.h | grep "=" | sed -E 's/^ +//' | sed 's/[=,]//g' | sed -E 's/([_A-Z1-9]+).*/\1 \1/' | sed 's/^/DEF(/' | sed 's/ /,"/' | sed 's/$$/")/' > ptx_parser_decode.def +endif + +depend: + makedepend *.cc *.c 2> /dev/null + +instructions.o: instructions.h ptx.tab.c +ptx_ir.o: ptx.tab.c ptx_parser_decode.def + +# DO NOT DELETE + diff --git a/src/cuda-sim/cuda-math.h b/src/cuda-sim/cuda-math.h new file mode 100644 index 0000000..d495461 --- /dev/null +++ b/src/cuda-sim/cuda-math.h @@ -0,0 +1,81 @@ +// This file created from vector_types.h distributed with CUDA 1.1 +// Changes Copyright 2009, Wilson W. L. Fung and Tor M. Aamodt +// University of British Columbia + +/* + * Copyright 1993-2007 NVIDIA Corporation. All rights reserved. + * + * NOTICE TO USER: + * + * This source code is subject to NVIDIA ownership rights under U.S. and + * international Copyright laws. Users and possessors of this source code + * are hereby granted a nonexclusive, royalty-free license to use this code + * in individual and commercial software. + * + * NVIDIA MAKES NO REPRESENTATION ABOUT THE SUITABILITY OF THIS SOURCE + * CODE FOR ANY PURPOSE. IT IS PROVIDED "AS IS" WITHOUT EXPRESS OR + * IMPLIED WARRANTY OF ANY KIND. NVIDIA DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOURCE CODE, INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE. + * IN NO EVENT SHALL NVIDIA BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL, + * OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS + * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE + * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE + * OR PERFORMANCE OF THIS SOURCE CODE. + * + * U.S. Government End Users. This source code is a "commercial item" as + * that term is defined at 48 C.F.R. 2.101 (OCT 1995), consisting of + * "commercial computer software" and "commercial computer software + * documentation" as such terms are used in 48 C.F.R. 12.212 (SEPT 1995) + * and is provided to the U.S. Government only as a commercial end item. + * Consistent with 48 C.F.R.12.212 and 48 C.F.R. 227.7202-1 through + * 227.7202-4 (JUNE 1995), all U.S. Government End Users acquire the + * source code with only those rights set forth herein. + * + * Any use of this source code in individual and commercial software must + * include, in the user documentation and internal comments to the code, + * the above Disclaimer and U.S. Government End Users Notice. + */ + +#ifndef CUDA_MATH +#define CUDA_MATH + +// cuda math implementations +#undef max +#undef min +namespace cuda_math { +#define __attribute__(a) // to remove warnings inside math_functions.h +#undef INT_MAX + +// DEVICE_BUILTIN + struct int4 { + int x, y, z, w; + }; + struct uint4 { + unsigned int x, y, z, w; + }; + struct float4 { + float x, y, z, w; + }; + struct float2 { + float x, y; + }; + + +// DEVICE_BUILTIN + typedef struct int4 int4; + typedef struct uint4 uint4; + typedef struct float4 float4; + typedef struct float2 float2; + +extern float rsqrtf(float); // CUDA 2.3 beta + +#define CUDA_FLOAT_MATH_FUNCTIONS +#include <device_types.h> +#define __CUDA_INTERNAL_COMPILATION__ +#include <math_functions.h> +#undef __CUDA_INTERNAL_COMPILATION__ +#undef __attribute__ +} + +#endif diff --git a/src/cuda-sim/cuda-sim.cc b/src/cuda-sim/cuda-sim.cc new file mode 100644 index 0000000..d691a64 --- /dev/null +++ b/src/cuda-sim/cuda-sim.cc @@ -0,0 +1,2195 @@ +/* + * Copyright (c) 2009 by Tor M. Aamodt, Ali Bakhoda, Wilson W. L. Fung, + * George L. Yuan, Henry Wong, Dan O'Connor, Zev Weiss and the + * University of British Columbia + * Vancouver, BC V6T 1Z4 + * All Rights Reserved. + * + * THIS IS A LEGAL DOCUMENT BY DOWNLOADING GPGPU-SIM, YOU ARE AGREEING TO THESE + * TERMS AND CONDITIONS. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * NOTE: The files libcuda/cuda_runtime_api.c and src/cuda-sim/cuda-math.h + * are derived from the CUDA Toolset available from http://www.nvidia.com/cuda + * (property of NVIDIA). The files benchmarks/BlackScholes/ and + * benchmarks/template/ are derived from the CUDA SDK available from + * http://www.nvidia.com/cuda (also property of NVIDIA). The files from + * src/intersim/ are derived from Booksim (a simulator provided with the + * textbook "Principles and Practices of Interconnection Networks" available + * from http://cva.stanford.edu/books/ppin/). As such, those files are bound by + * the corresponding legal terms and conditions set forth separately (original + * copyright notices are left in files from these sources and where we have + * modified a file our copyright notice appears before the original copyright + * notice). + * + * Using this version of GPGPU-Sim requires a complete installation of CUDA + * which is distributed seperately by NVIDIA under separate terms and + * conditions. To use this version of GPGPU-Sim with OpenCL requires a + * recent version of NVIDIA's drivers which support OpenCL. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the University of British Columbia nor the names of + * its contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * 4. This version of GPGPU-SIM is distributed freely for non-commercial use only. + * + * 5. No nonprofit user may place any restrictions on the use of this software, + * including as modified by the user, by any other authorized user. + * + * 6. GPGPU-SIM was developed primarily by Tor M. Aamodt, Wilson W. L. Fung, + * Ali Bakhoda, George L. Yuan, at the University of British Columbia, + * Vancouver, BC V6T 1Z4 + */ + +#include "instructions.h" +#include "ptx_ir.h" +#include "ptx_sim.h" +#include <stdio.h> +#include <dirent.h> + +#include "opcodes.h" +#include "../intersim/statwraper.h" +#include "dram_callback.h" +#include <set> +#include <map> +#include "../util.h" +#include "memory.h" +#include "ptx-stats.h" + + +int gpgpu_ptx_instruction_classification=0; +void ** g_inst_classification_stat = NULL; +void ** g_inst_op_classification_stat= NULL; +int g_ptx_kernel_count = -1; // used for classification stat collection purposes +extern "C" int ptx_parse(); +extern "C" int ptx__scan_string(const char*); +extern "C" int ptx_debug; +extern "C" FILE *ptx_in; + +extern "C" const char *g_ptxinfo_filename = NULL; +extern "C" int ptxinfo_parse(); +extern "C" int ptxinfo_debug; +extern "C" FILE *ptxinfo_in; + +int g_debug_execution = 0; +int g_debug_thread_uid = 0; +addr_t g_debug_pc = 0xBEEF1518; +int g_override_embedded_ptx = false; + +const char *g_filename; +bool g_debug_ir_generation = false; +unsigned g_ptx_sim_num_insn = 0; + +extern memory_space *g_global_mem; +extern memory_space *g_param_mem; +extern memory_space *g_tex_mem; +extern memory_space *g_surf_mem; + +std::map<const struct textureReference*,const struct cudaArray*> TextureToArrayMap; // texture bindings +std::map<const struct textureReference*, const struct textureInfo*> TextureToInfoMap; +std::map<std::string, const struct textureReference*> NameToTextureMap; +unsigned int g_texcache_linesize; +int gpgpu_option_spread_blocks_across_cores = 0; +unsigned gpgpu_param_num_shaders = 0; + +void gpgpu_ptx_sim_bindNameToTexture(const char* name, const struct textureReference* texref) +{ + std::string texname(name); + NameToTextureMap[texname] = texref; +} + +const struct textureReference* gpgpu_ptx_sim_accessTextureofName(const char* name) { + std::string texname(name); + return NameToTextureMap[texname]; +} + +const char* gpgpu_ptx_sim_findNamefromTexture(const struct textureReference* texref) +{ + std::map<std::string, const struct textureReference*>::iterator itr = NameToTextureMap.begin(); + while (itr != NameToTextureMap.end()) { + if ((*itr).second == texref) { + const char *p = ((*itr).first).c_str(); + return p; + } + itr++; + } + return NULL; +} + +unsigned int intLOGB2( unsigned int v ) { + unsigned int shift; + unsigned int r; + + r = 0; + + shift = (( v & 0xFFFF0000) != 0 ) << 4; v >>= shift; r |= shift; + shift = (( v & 0xFF00 ) != 0 ) << 3; v >>= shift; r |= shift; + shift = (( v & 0xF0 ) != 0 ) << 2; v >>= shift; r |= shift; + shift = (( v & 0xC ) != 0 ) << 1; v >>= shift; r |= shift; + shift = (( v & 0x2 ) != 0 ) << 0; v >>= shift; r |= shift; + + return r; +} + +void gpgpu_ptx_sim_bindTextureToArray(const struct textureReference* texref, const struct cudaArray* array) +{ + TextureToArrayMap[texref] = array; + unsigned int texel_size_bits = array->desc.w + array->desc.x + array->desc.y + array->desc.z; + unsigned int texel_size = texel_size_bits/8; + unsigned int Tx, Ty; + int r; + + printf("GPGPU-Sim PTX: texel size = %d\n", texel_size); + printf("GPGPU-Sim PTX: texture cache linesize = %d\n", g_texcache_linesize); + //first determine base Tx size for given linesize + switch (g_texcache_linesize) { + case 16: + Tx = 4; + break; + case 32: + Tx = 8; + break; + case 64: + Tx = 8; + break; + case 128: + Tx = 16; + break; + case 256: + Tx = 16; + break; + default: + printf("GPGPU-Sim PTX: Line size of %d bytes currently not supported.\n", g_texcache_linesize); + assert(0); + break; + } + r = texel_size >> 2; + //modify base Tx size to take into account size of each texel in bytes + while (r != 0) { + Tx = Tx >> 1; + r = r >> 2; + } + //by now, got the correct Tx size, calculate correct Ty size + Ty = g_texcache_linesize/(Tx*texel_size); + + printf("GPGPU-Sim PTX: Tx = %d; Ty = %d, Tx_numbits = %d, Ty_numbits = %d\n", Tx, Ty, intLOGB2(Tx), intLOGB2(Ty)); + printf("GPGPU-Sim PTX: Texel size = %d bytes; texel_size_numbits = %d\n", texel_size, intLOGB2(texel_size)); + printf("GPGPU-Sim PTX: Binding texture to array starting at devPtr32 = 0x%x\n", array->devPtr32); + printf("GPGPU-Sim PTX: Texel size = %d bytes\n", texel_size); + struct textureInfo* texInfo = (struct textureInfo*) malloc(sizeof(struct textureInfo)); + texInfo->Tx = Tx; + texInfo->Ty = Ty; + texInfo->Tx_numbits = intLOGB2(Tx); + texInfo->Ty_numbits = intLOGB2(Ty); + texInfo->texel_size = texel_size; + texInfo->texel_size_numbits = intLOGB2(texel_size); + TextureToInfoMap[texref] = texInfo; +} + +const struct cudaArray* gpgpu_ptx_sim_accessArrayofTexture(struct textureReference* texref) { + return TextureToArrayMap[texref]; +} + +int gpgpu_ptx_sim_sizeofTexture(const char* name) +{ + std::string texname(name); + const struct textureReference* texref = NameToTextureMap[texname]; + const struct cudaArray* array = TextureToArrayMap[texref]; + return array->size; +} + +extern unsigned int warp_size; + +int g_warn_literal_operands_two_type_inst; + +std::list<operand_info> check_operands( int opcode, + const std::list<int> &scalar_type, + const std::list<operand_info> &operands ) +{ + if( (opcode == CVT_OP) || (opcode == SET_OP) || (opcode == SLCT_OP) || (opcode == TEX_OP) ) { + // just make sure these do not have have const operands... + if( !g_warn_literal_operands_two_type_inst ) { + std::list<operand_info>::const_iterator o; + for( o = operands.begin(); o != operands.end(); o++ ) { + const operand_info &op = *o; + if( op.is_literal() ) { + printf("GPGPU-Sim PTX: PTX uses two scalar type intruction with literal operand.\n"); + g_warn_literal_operands_two_type_inst = 1; + } + } + } + } else { + assert( scalar_type.size() < 2 ); + if( scalar_type.size() == 1 ) { + std::list<operand_info> result; + int inst_type = scalar_type.front(); + std::list<operand_info>::const_iterator o; + for( o = operands.begin(); o != operands.end(); o++ ) { + const operand_info &op = *o; + if( op.is_literal() ) { + if( (op.get_type() == double_op_t) && (inst_type == F32_TYPE) ) { + ptx_reg_t v = op.get_literal_value(); + float u = (float)v.f64; + operand_info n(u); + result.push_back(n); + } else { + result.push_back(op); + } + } else { + result.push_back(op); + } + } + return result; + } + } + return operands; +} + +ptx_instruction::ptx_instruction( int opcode, + const symbol *pred, + int neg_pred, + symbol *label, + const std::list<operand_info> &operands, + const operand_info &return_var, + const std::list<int> &options, + const std::list<int> &scalar_type, + int space_spec, + const char *file, + unsigned line, + const char *source ) +{ + m_uid = ++g_num_ptx_inst_uid; + m_PC = 0; + m_opcode = opcode; + m_pred = pred; + m_neg_pred = neg_pred; + m_label = label; + const std::list<operand_info> checked_operands = check_operands(opcode,scalar_type,operands); + m_operands.insert(m_operands.begin(), checked_operands.begin(), checked_operands.end() ); + m_return_var = return_var; + m_options = options; + m_wide = false; + m_hi = false; + m_lo = false; + m_uni = false; + m_rounding_mode = RN_OPTION; + m_compare_op = -1; + m_saturation_mode = 0; + m_geom_spec = 0; + m_vector_spec = 0; + m_atomic_spec = 0; + m_warp_size = ::warp_size; + m_membar_level = 0; + + std::list<int>::const_iterator i; + unsigned n=1; + for ( i=options.begin(); i!= options.end(); i++, n++ ) { + int last_ptx_inst_option = *i; + switch ( last_ptx_inst_option ) { + case EQU_OPTION: + case NEU_OPTION: + case LTU_OPTION: + case LEU_OPTION: + case GTU_OPTION: + case GEU_OPTION: + case EQ_OPTION: + case NE_OPTION: + case LT_OPTION: + case LE_OPTION: + case GT_OPTION: + case GE_OPTION: + case LS_OPTION: + case HS_OPTION: + m_compare_op = last_ptx_inst_option; + break; + case NUM_OPTION: + case NAN_OPTION: + m_compare_op = last_ptx_inst_option; + // assert(0); // finish this + break; + case SAT_OPTION: + m_saturation_mode = 1; + break; + case RNI_OPTION: + case RZI_OPTION: + case RMI_OPTION: + case RPI_OPTION: + case RN_OPTION: + case RZ_OPTION: + case RM_OPTION: + case RP_OPTION: + m_rounding_mode = last_ptx_inst_option; + break; + case HI_OPTION: + m_compare_op = last_ptx_inst_option; + m_hi = true; + assert( !m_lo ); + assert( !m_wide ); + break; + case LO_OPTION: + m_compare_op = last_ptx_inst_option; + m_lo = true; + assert( !m_hi ); + assert( !m_wide ); + break; + case WIDE_OPTION: + m_wide = true; + assert( !m_lo ); + assert( !m_hi ); + break; + case UNI_OPTION: + m_uni = true; // don't care... < now we DO care when constructing flowgraph> + break; + case GEOM_MODIFIER_1D: + case GEOM_MODIFIER_2D: + case GEOM_MODIFIER_3D: + m_geom_spec = last_ptx_inst_option; + break; + case V2_TYPE: + case V3_TYPE: + case V4_TYPE: + m_vector_spec = last_ptx_inst_option; + break; + case ATOMIC_AND: + case ATOMIC_OR: + case ATOMIC_XOR: + case ATOMIC_CAS: + case ATOMIC_EXCH: + case ATOMIC_ADD: + case ATOMIC_INC: + case ATOMIC_DEC: + case ATOMIC_MIN: + case ATOMIC_MAX: + m_atomic_spec = last_ptx_inst_option; + break; + case APPROX_OPTION: + break; + case FULL_OPTION: + break; + case ANY_OPTION: + m_vote_mode = vote_any; + break; + case ALL_OPTION: + m_vote_mode = vote_all; + break; + case GLOBAL_OPTION: + m_membar_level = GLOBAL_OPTION; + break; + case CTA_OPTION: + m_membar_level = CTA_OPTION; + break; + case FTZ_OPTION: + break; + + default: + assert(0); + break; + } + } + m_scalar_type = scalar_type; + m_space_spec = space_spec; + m_source_file = file?file:"<unknown>"; + m_source_line = line; + m_source = source; +} + +void ptx_instruction::print_insn() const +{ + print_insn(stdout); + fflush(stdout); +} + +void ptx_instruction::print_insn( FILE *fp ) const +{ + char buf[1024], *p; + snprintf(buf,1024,"%s", m_source.c_str()); + p = strtok(buf,";"); + if( !is_label() ) + fprintf(fp,"PC=%3u [idx=%3u] ", m_PC, m_instr_mem_index ); + else + fprintf(fp," " ); + fprintf(fp,"(%s:%u) %s", m_source_file.c_str(), m_source_line, p ); +} + +unsigned g_assemble_code_next_pc=1; +std::map<unsigned,function_info*> g_pc_to_finfo; +std::vector<ptx_instruction*> function_info::s_g_pc_to_insn; + +void function_info::print_insn( unsigned pc, FILE * fp ) const +{ + unsigned index = pc - m_start_PC; + fprintf(fp,"FUNC[%s]",m_name.c_str() ); + if ( index >= m_instr_mem_size ) { + fprintf(fp, "<past last instruction (max pc=%u)>", m_start_PC + m_instr_mem_size - 1 ); + } else { + if ( m_instr_mem[index] != NULL ) + m_instr_mem[index]->print_insn(fp); + else + fprintf(fp, "<no instruction at pc = %u>", pc ); + } +} + +void function_info::ptx_assemble() +{ + if( m_assembled ) { + return; + } + + // get the instructions into instruction memory... + unsigned num_inst = m_instructions.size(); + m_instr_mem = new ptx_instruction*[ num_inst ]; + m_instr_mem_size = num_inst; + + printf("GPGPU-Sim PTX: instruction assembly for function \'%s\'... ", m_name.c_str() ); + fflush(stdout); + std::list<ptx_instruction*>::iterator i; + addr_t n=0; // offset in m_instr_mem + addr_t PC = g_assemble_code_next_pc; // globally unique address (across functions) + m_start_PC = PC; + s_g_pc_to_insn.reserve(s_g_pc_to_insn.size() + m_instructions.size()); + for ( i=m_instructions.begin(); i != m_instructions.end(); i++ ) { + ptx_instruction *pI = *i; + if ( pI->is_label() ) { + const symbol *l = pI->get_label(); + labels[l->name()] = n; + } else { + g_pc_to_finfo[PC] = this; + m_instr_mem[n] = pI; + s_g_pc_to_insn.push_back(pI); + assert(pI == s_g_pc_to_insn[PC - 1]); + pI->set_m_instr_mem_index(n); + pI->set_PC(PC); + n++; + PC++; + } + } + g_assemble_code_next_pc=PC; + for ( unsigned ii=0; ii < n; ii++ ) { // handle branch instructions + ptx_instruction *pI = m_instr_mem[ii]; + if ( pI->get_opcode() == BRA_OP ) { + operand_info &target = pI->dst(); //get operand, e.g. target name + if ( labels.find(target.name()) == labels.end() ) { + printf("GPGPU-Sim PTX: Loader error (%s:%u): Branch label \"%s\" does not appear in assembly code.", + pI->source_file(),pI->source_line(), target.name().c_str() ); + abort(); + } + unsigned index = labels[ target.name() ]; //determine address from name + unsigned PC = m_instr_mem[index]->get_PC(); + g_current_symbol_table->set_label_address( target.get_symbol(), PC ); + target.set_type(label_t); + } + } + printf(" done.\n"); + fflush(stdout); + + create_basic_blocks(); + connect_basic_blocks(); + if ( g_debug_execution>=2 ) { + print_basic_blocks(); + print_basic_block_links(); + print_basic_block_dot(); + } + find_postdominators(); + find_ipostdominators(); + if ( g_debug_execution>=2 ) { + print_postdominators(); + print_ipostdominators(); + } + m_assembled = true; +} + + + +void gpgpu_ptx_sim_init_memory() +{ + static bool initialized = false; + if ( !initialized ) { + g_global_mem = new memory_space_impl<8192>("global",64*1024); + g_param_mem = new memory_space_impl<64>("param",64*1024); + g_tex_mem = new memory_space_impl<8192>("tex",64*1024); + g_surf_mem = new memory_space_impl<8192>("surf",64*1024); + initialized = true; + } +} + +int load_static_globals( symbol_table *symtab, unsigned min_gaddr, unsigned max_gaddr) +{ + printf( "GPGPU-Sim PTX: loading global with explicit initializers... " ); + fflush(stdout); + int ng_bytes=0; + symbol_table::iterator g=symtab->global_iterator_begin(); + bool below_image=true; + + for ( ; g!=symtab->global_iterator_end(); g++) { + symbol *global = *g; + if ( global->has_initializer() ) { + std::list<operand_info> init_list = global->get_initializer(); + for ( std::list<operand_info>::iterator i=init_list.begin(); i!=init_list.end(); i++ ) { + operand_info op = *i; + ptx_reg_t value = op.get_literal_value(); + int nbytes = 0; + switch ( op.get_type() ) { + case int_t: nbytes = 4; break; + case float_op_t: nbytes = 4; break; + case double_op_t: nbytes = 8; break; + default: + abort(); + } + unsigned addr=global->get_address(); + + assert( below_image && addr+nbytes < min_gaddr ); // min_gaddr is start of "heap" for cudaMalloc + + g_global_mem->write(addr,nbytes,&value); + ng_bytes+=nbytes; + } + } + } + printf( " done.\n"); + fflush(stdout); + return ng_bytes; +} + +int load_constants( symbol_table *symtab ) +{ + printf( "GPGPU-Sim PTX: loading constants with explicit initializers... " ); + fflush(stdout); + int nc_bytes = 0; + symbol_table::iterator g=symtab->const_iterator_begin(); + + for ( ; g!=symtab->const_iterator_end(); g++) { + symbol *constant = *g; + if ( constant->is_const() && constant->has_initializer() ) { + + // get the constant element data size + int basic_type; + size_t num_bits; + type_decode(constant->type()->get_key().scalar_type(),num_bits,basic_type); + + std::list<operand_info> init_list = constant->get_initializer(); + int nbytes_written = 0; + for ( std::list<operand_info>::iterator i=init_list.begin(); i!=init_list.end(); i++ ) { + operand_info op = *i; + ptx_reg_t value = op.get_literal_value(); + int nbytes = num_bits/8; + switch ( op.get_type() ) { + case int_t: assert(nbytes >= 1); break; + case float_op_t: assert(nbytes == 4); break; + case double_op_t: assert(nbytes >= 4); break; // account for double DEMOTING + default: + abort(); + } + unsigned addr=constant->get_address() + nbytes_written; + + g_global_mem->write(addr,nbytes,&value); // assume little endian (so u8 is the first byte in u32) + nc_bytes+=nbytes; + nbytes_written += nbytes; + } + } + } + printf( " done.\n"); + fflush(stdout); + return nc_bytes; +} + +unsigned long long g_dev_malloc=0x10000000; // start allocating from this address (lower values used for allocating globals in .ptx file) + +void* gpgpu_ptx_sim_malloc( size_t size ) +{ + unsigned long long result = g_dev_malloc; + printf("GPGPU-Sim PTX: allocating %zu bytes on GPU starting at address 0x%Lx\n", size, g_dev_malloc ); + fflush(stdout); + g_dev_malloc += size; + if (size%64) g_dev_malloc += (64 - size%64); //align to 64 byte boundaries + return(void*) result; +} + +void* gpgpu_ptx_sim_mallocarray( size_t size ) +{ + unsigned long long result = g_dev_malloc; + printf("GPGPU-Sim PTX: allocating %zu bytes on GPU starting at address 0x%Lx\n", size, g_dev_malloc ); + fflush(stdout); + g_dev_malloc += size; + if (size%64) g_dev_malloc += (64 - size%64); //align to 64 byte boundaries + return(void*) result; +} + + +void gpgpu_ptx_sim_memcpy_to_gpu( size_t dst_start_addr, const void *src, size_t count ) +{ + printf("GPGPU-Sim PTX: copying %zu bytes from CPU[0x%Lx] to GPU[0x%Lx] ... ", count, (unsigned long long) src, (unsigned long long) dst_start_addr ); + fflush(stdout); + char *src_data = (char*)src; + for (unsigned n=0; n < count; n ++ ) + g_global_mem->write(dst_start_addr+n,1, src_data+n); + printf( " done.\n"); + fflush(stdout); +} + +void gpgpu_ptx_sim_memcpy_from_gpu( void *dst, size_t src_start_addr, size_t count ) +{ + printf("GPGPU-Sim PTX: copying %zu bytes from GPU[0x%Lx] to CPU[0x%Lx] ...", count, (unsigned long long) src_start_addr, (unsigned long long) dst ); + fflush(stdout); + unsigned char *dst_data = (unsigned char*)dst; + for (unsigned n=0; n < count; n ++ ) + g_global_mem->read(src_start_addr+n,1,dst_data+n); + printf( " done.\n"); + fflush(stdout); +} + +void gpgpu_ptx_sim_memcpy_gpu_to_gpu( size_t dst, size_t src, size_t count ) +{ + printf("GPGPU-Sim PTX: copying %zu bytes from GPU[0x%Lx] to GPU[0x%Lx] ...", count, + (unsigned long long) src, (unsigned long long) dst ); + fflush(stdout); + for (unsigned n=0; n < count; n ++ ) { + unsigned char tmp; + g_global_mem->read(src+n,1,&tmp); + g_global_mem->write(dst+n,1, &tmp); + } + printf( " done.\n"); + fflush(stdout); +} + +void gpgpu_ptx_sim_memset( size_t dst_start_addr, int c, size_t count ) +{ + printf("GPGPU-Sim PTX: setting %zu bytes of memory to 0x%x starting at 0x%Lx... ", + count, (unsigned char) c, (unsigned long long) dst_start_addr ); + fflush(stdout); + unsigned char c_value = (unsigned char)c; + for (unsigned n=0; n < count; n ++ ) + g_global_mem->write(dst_start_addr+n,1,&c_value); + printf( " done.\n"); + fflush(stdout); +} + +int ptx_thread_done( void *thd ) +{ + ptx_thread_info *the_thread = (ptx_thread_info *) thd; + int result = 0; + result = (the_thread==NULL) || the_thread->is_done(); + return result; +} + +const char * ptx_get_fname( unsigned PC ) +{ + static const char *null_ptr = "<null finfo ptr>"; + std::map<unsigned,function_info*>::iterator f=g_pc_to_finfo.find(PC); + if( f== g_pc_to_finfo.end() ) + return null_ptr; + return f->second->get_name().c_str(); +} + +unsigned ptx_thread_donecycle( void *thr ) +{ + ptx_thread_info *the_thread = (ptx_thread_info *) thr; + if( the_thread == NULL ) + return 0; + return the_thread->donecycle(); +} + +int ptx_thread_get_next_pc( void *thd ) +{ + ptx_thread_info *the_thread = (ptx_thread_info *) thd; + if ( the_thread == NULL ) + return -1; + return the_thread->get_pc(); // PC should already be updatd to next PC at this point (was set in shader_decode() last time thread ran) +} + +void* ptx_thread_get_next_finfo( void *thd ) +{ + ptx_thread_info *the_thread = (ptx_thread_info *) thd; + if ( the_thread == NULL ) + return NULL; + return the_thread->get_finfo(); // finfo should already be updatd to next PC at this point (was set in shader_decode() last time thread ran) +} + +int ptx_thread_at_barrier( void *thd ) +{ + ptx_thread_info *the_thread = (ptx_thread_info *) thd; + if ( the_thread == NULL ) + return 0; + return the_thread->is_at_barrier(); +} + +int ptx_thread_all_at_barrier( void *thd ) +{ + ptx_thread_info *the_thread = (ptx_thread_info *) thd; + if ( the_thread == NULL ) + return 0; + return the_thread->all_at_barrier()?1:0; +} + +unsigned long long ptx_thread_get_cta_uid( void *thd ) +{ + ptx_thread_info *the_thread = (ptx_thread_info *) thd; + if ( the_thread == NULL ) + return 0; + return the_thread->get_cta_uid(); +} + +void ptx_thread_reset_barrier( void *thd ) +{ + ptx_thread_info *the_thread = (ptx_thread_info *) thd; + if ( the_thread == NULL ) + return; + the_thread->clear_barrier(); +} + +void ptx_thread_release_barrier( void *thd ) +{ + ptx_thread_info *the_thread = (ptx_thread_info *) thd; + if ( the_thread == NULL ) + return; + the_thread->release_barrier(); +} + +void ptx_print_insn( address_type pc, FILE *fp ) +{ + std::map<unsigned,function_info*>::iterator f = g_pc_to_finfo.find(pc); + if( f == g_pc_to_finfo.end() ) { + fprintf(fp,"<no instruction at address 0x%x (%u)>", pc, pc ); + return; + } + function_info *finfo = f->second; + assert( finfo ); + finfo->print_insn(pc,fp); +} + +void function_info::ptx_decode_inst( ptx_thread_info *thread, + unsigned *op_type, + int *i1, int *i2, int *i3, int *i4, + int *o1, int *o2, int *o3, int *o4, + int *vectorin, + int *vectorout, + int *arch_reg ) +{ + addr_t pc = thread->get_pc(); + unsigned index = pc - m_start_PC; + assert( index < m_instr_mem_size ); + ptx_instruction *pI = m_instr_mem[index]; //get instruction from m_instr_mem[PC] + + bool has_dst = false ; + int opcode = pI->get_opcode(); //determine the opcode + + switch ( pI->get_opcode() ) { +#define OP_DEF(OP,FUNC,STR,DST,CLASSIFICATION) case OP: has_dst = (DST!=0); break; +#include "opcodes.def" +#undef OP_DEF + default: + printf( "Execution error: Invalid opcode (0x%x)\n", pI->get_opcode() ); + break; + } + + *op_type = ALU_OP; + if ( opcode == LD_OP ) { + *op_type = LOAD_OP; + } else if ( opcode == ST_OP ) { + *op_type = STORE_OP; + } else if ( opcode == BRA_OP ) { + *op_type = BRANCH_OP; + } else if ( opcode == TEX_OP ) { + *op_type = LOAD_OP; + } else if ( opcode == ATOM_OP ) { + *op_type = LOAD_OP; // make atomics behave more like a load. + } else if ( opcode == BAR_OP ) { + *op_type = BARRIER_OP; + } + + int n=0,m=0; + ptx_instruction::const_iterator op=pI->op_iter_begin(); + for ( ; op != pI->op_iter_end(); op++, n++ ) { //process operands + + const operand_info &o = *op; + if ( has_dst && n==0 ) { + if ( o.is_reg() ) { //but is destination an actual register? (seems like it fails if it's a vector) + *o1 = o.reg_num(); + arch_reg[0] = o.arch_reg_num(); + } else if ( o.is_vector() ) { //but is destination an actual register? (seems like it fails if it's a vector) + *vectorin = 1; + *o1 = o.reg1_num(); + *o2 = o.reg2_num(); + *o3 = o.reg3_num(); + *o4 = o.reg4_num(); + for (int i = 0; i < 4; i++) + arch_reg[i] = o.arch_reg_num(i); + } + } else { + if ( o.is_reg() ) { + int reg_num = o.reg_num(); + arch_reg[m + 4] = o.arch_reg_num(); + switch ( m ) { + case 0: *i1 = reg_num; break; + case 1: *i2 = reg_num; break; + case 2: *i3 = reg_num; break; + default: + break; + } + m++; + } else if ( o.is_vector() ) { + assert(m == 0); //only support 1 vector operand (for textures) right now + *vectorout = 1; + *i1 = o.reg1_num(); + *i2 = o.reg2_num(); + *i3 = o.reg3_num(); + *i4 = o.reg4_num(); + for (int i = 0; i < 4; i++) + arch_reg[i + 4] = o.arch_reg_num(i); + m+=4; + } + } + } +} + +unsigned function_info::ptx_get_inst_op( ptx_thread_info *thread ) +{ + addr_t pc = thread->get_pc(); + unsigned index = pc - m_start_PC; + assert( index < m_instr_mem_size ); + ptx_instruction *pI = m_instr_mem[index]; //get instruction from m_instr_mem[PC] + + int opcode = pI->get_opcode(); //determine the opcode + + if ( opcode == LD_OP ) { + if ( pI->get_space() != SHARED_DIRECTIVE ) //treat shared memory access as generic instruction (e.g. no bank conflicts) + return LOAD_OP; + } else if ( opcode == ST_OP ) { + if ( pI->get_space() != SHARED_DIRECTIVE ) + return STORE_OP; + } else if ( opcode == BRA_OP ) { + return BRANCH_OP; + } else if ( opcode == TEX_OP ) { + return LOAD_OP; + } + return ALU_OP; +} + +void function_info::add_param_name_and_type( unsigned index, std::string name, int type ) +{ + unsigned parsed_index; + char buffer[2048]; + snprintf(buffer,2048,"%s_param_%%u", m_name.c_str() ); + int ntokens = sscanf(name.c_str(),buffer,&parsed_index); + if( ntokens == 1 ) { + assert( m_ptx_param_info.find(parsed_index) == m_ptx_param_info.end() ); + m_ptx_param_info[parsed_index] = param_info(parsed_index,name,type); + } else { + assert( m_ptx_param_info.find(index) == m_ptx_param_info.end() ); + m_ptx_param_info[index] = param_info(index,name,type); + } +} + +void function_info::add_param_data( unsigned argn, struct gpgpu_ptx_sim_arg *args ) +{ + const void *data = args->m_start; + param_t tmp; + + if( data ) { + memcpy(&tmp.data,data,args->m_nbytes); + std::map<unsigned,param_info>::iterator i=m_ptx_param_info.find(argn); + if( i != m_ptx_param_info.end()) { + i->second.add_data(tmp); + } else { + // This should only happen for OpenCL: + // + // The LLVM PTX compiler in NVIDIA's driver (version 190.29) + // does not generate an argument in the function declaration + // for __constant arguments. + // + // The associated constant memory space can be allocated in two + // ways. It can be explicitly initialized in the .ptx file where + // it is declared. Or, it can be allocated using the clCreateBuffer + // on the host. In this later case, the .ptx file will contain + // a global declaration of the parameter, but it will have an unknown + // array size. Thus, the symbol's address will not be set and we need + // to set it here before executing the PTX. + + char buffer[2048]; + snprintf(buffer,2048,"%s_param_%u",m_name.c_str(),argn); + + symbol *p = m_symtab->lookup(buffer); + if( p == NULL ) + printf("GPGPU-Sim PTX: ERROR ** could not locate symbol for \'%s\' : cannot bind buffer\n", buffer); + p->set_address((addr_t)*(size_t*)data); + } + } else { + // This should only happen for OpenCL, but doesn't cause problems + } +} + +int ptx_branch_taken( void *thd ) +{ + ptx_thread_info *thread = (ptx_thread_info *) thd; + return (thread != NULL) && thread->branch_taken(); +} + +template<int activate_level> +bool ptx_debug_exec_dump_cond(int thd_uid, addr_t pc) +{ + if (g_debug_execution >= activate_level) { + // check each type of debug dump constraint to filter out dumps + if ( (g_debug_thread_uid != 0) && (thd_uid != (unsigned)g_debug_thread_uid) ) { + return false; + } + if ( (g_debug_pc != 0xBEEF1518) && (pc != g_debug_pc) ) { + return false; + } + + return true; + } + + return false; +} + +unsigned datatype2size( unsigned data_type ) +{ + unsigned data_size; + switch ( data_type ) { + case B8_TYPE: + case S8_TYPE: + case U8_TYPE: + data_size = 1; break; + case B16_TYPE: + case S16_TYPE: + case U16_TYPE: + case F16_TYPE: + data_size = 2; break; + case B32_TYPE: + case S32_TYPE: + case U32_TYPE: + case F32_TYPE: + data_size = 4; break; + case B64_TYPE: + case S64_TYPE: + case U64_TYPE: + case F64_TYPE: + data_size = 8; break; + default: assert(0); break; + } + return data_size; +} + +extern unsigned long long gpu_sim_cycle; +unsigned g_warp_active_mask; + +void function_info::ptx_exec_inst( ptx_thread_info *thread, + addr_t *addr, + unsigned *space, + unsigned *data_size, + dram_callback_t* callback, + unsigned warp_active_mask ) +{ + bool skip = false; + int op_classification = 0; + addr_t pc = thread->next_instr(); + unsigned index = pc - m_start_PC; + assert( index < m_instr_mem_size ); + ptx_instruction *pI = m_instr_mem[index]; + g_current_symbol_table = thread->get_finfo()->get_symtab(); + thread->clearRPC(); + thread->m_last_set_operand_value.u64 = 0; + *space = -1; + *addr = 0xFEEBDAED; + + thread->clear_modifiedregs(); + if( pI->has_pred() ) { + const operand_info &pred = pI->get_pred(); + ptx_reg_t pred_value = thread->get_operand_value(pred); + skip = !pred_value.pred ^ pI->get_pred_neg(); + } + g_warp_active_mask = warp_active_mask; + if( !skip ) { + switch ( pI->get_opcode() ) { +#define OP_DEF(OP,FUNC,STR,DST,CLASSIFICATION) case OP: FUNC(pI,thread); op_classification = CLASSIFICATION; break; +#include "opcodes.def" +#undef OP_DEF + default: + printf( "Execution error: Invalid opcode (0x%x)\n", pI->get_opcode() ); + break; + } + } + + if ( ptx_debug_exec_dump_cond<5>(thread->get_uid(), pc) ) { + dim3 ctaid = thread->get_ctaid(); + dim3 tid = thread->get_tid(); + printf("%u [cyc=%u][thd=%u][i=%u] : ctaid=(%u,%u,%u) tid=(%u,%u,%u) icount=%u [pc=%u] (%s:%u - %s) [0x%llx]\n", + g_ptx_sim_num_insn, + (unsigned)gpu_sim_cycle, + thread->get_uid(), + pI->uid(), ctaid.x,ctaid.y,ctaid.z,tid.x,tid.y,tid.z, + thread->get_icount(), + pc, pI->source_file(), pI->source_line(), pI->get_source(), + thread->m_last_set_operand_value.u64 ); + fflush(stdout); + } + + *data_size = 0; + if ( (pI->get_opcode() == LD_OP || pI->get_opcode() == ST_OP) ) { + *addr = thread->last_eaddr(); + *space = thread->last_space(); + + unsigned to_type = pI->get_type(); + *data_size = datatype2size(to_type); + } + + if ( pI->get_opcode() == ATOM_OP ) { + *addr = thread->last_eaddr(); + *space = thread->last_space(); + callback->function = thread->last_callback().function; + callback->instruction = thread->last_callback().instruction; + callback->thread = thread; + + unsigned to_type = pI->get_type(); + *data_size = datatype2size(to_type); + } else { + // make sure that the callback isn't set + callback->function = NULL; + callback->instruction = NULL; + } + + if (pI->get_opcode() == TEX_OP) { + *addr = thread->last_eaddr(); + *space = thread->last_space(); + + unsigned to_type = pI->get_type(); + *data_size = datatype2size(to_type); + } + + if ( g_debug_execution >= 6 ) { + if ( ptx_debug_exec_dump_cond<6>(thread->get_uid(), pc) ) + thread->dump_modifiedregs(); + } else if ( g_debug_execution >= 10 ) { + if ( ptx_debug_exec_dump_cond<10>(thread->get_uid(), pc) ) + thread->dump_regs(); + } + thread->update_pc(); + g_ptx_sim_num_insn++; + ptx_file_line_stats_add_exec_count(pI); + if ( gpgpu_ptx_instruction_classification ) { + unsigned space = pI->get_space(); + switch ( space ) { + case GLOBAL_DIRECTIVE: space = 10; break; + case LOCAL_DIRECTIVE: space = 11; break; + case TEX_DIRECTIVE: space = 12; break; + case SURF_DIRECTIVE: space = 13; break; + case PARAM_DIRECTIVE: space = 14; break; + case SHARED_DIRECTIVE: space = 15; break; + case CONST_DIRECTIVE: space = 16; break; + default: + space = 0 ; + break; + } + StatAddSample( g_inst_classification_stat[g_ptx_kernel_count], op_classification); + if (space) StatAddSample( g_inst_classification_stat[g_ptx_kernel_count], ( int )space); + StatAddSample( g_inst_op_classification_stat[g_ptx_kernel_count], (int) pI->get_opcode() ); + } + if ( (g_ptx_sim_num_insn % 100000) == 0 ) { + dim3 ctaid = thread->get_ctaid(); + dim3 tid = thread->get_tid(); + printf("%u instructions simulated : ctaid=(%u,%u,%u) tid=(%u,%u,%u)\n", + g_ptx_sim_num_insn, ctaid.x,ctaid.y,ctaid.z,tid.x,tid.y,tid.z ); + fflush(stdout); + } +} + +unsigned g_gx, g_gy, g_gz; + +dim3 g_cudaGridDim, g_cudaBlockDim; + +unsigned g_cta_launch_sid; +std::list<ptx_thread_info *> g_active_threads; +std::map<unsigned,unsigned> g_sm_idx_offset_next; +unsigned g_sm_next_index; +std::map<unsigned,memory_space*> g_shared_memory_lookup; +std::map<unsigned,ptx_cta_info*> g_ptx_cta_lookup; +std::map<unsigned,std::map<unsigned,memory_space*> > g_local_memory_lookup; + +// return number of blocks in grid +unsigned ptx_sim_grid_size() +{ + return g_cudaGridDim.x * g_cudaGridDim.y * g_cudaGridDim.z; +} + +void set_option_gpgpu_spread_blocks_across_cores(int option) +{ + gpgpu_option_spread_blocks_across_cores = option; +} + +void set_param_gpgpu_num_shaders(int num_shaders) +{ + gpgpu_param_num_shaders = num_shaders; +} + +unsigned ptx_sim_cta_size() +{ + return g_cudaBlockDim.x * g_cudaBlockDim.y * g_cudaBlockDim.z; +} + +const struct gpgpu_ptx_sim_kernel_info* ptx_sim_kernel_info() { + return g_entrypoint_func_info->get_kernel_info(); +} + +void ptx_sim_free_sm( void** thread_info ) +{ +} + +unsigned ptx_sim_init_thread( void** thread_info,int sid,unsigned tid,unsigned threads_left,unsigned num_threads, core_t *core, unsigned hw_cta_id, unsigned hw_warp_id ) +{ + if ( *thread_info != NULL ) { + ptx_thread_info *thd = *((ptx_thread_info**)thread_info); + assert( thd->is_done() ); + if ( g_debug_execution==-1 ) { + dim3 ctaid = thd->get_ctaid(); + dim3 tid = thd->get_tid(); + printf("GPGPU-Sim PTX simulator: thread exiting ctaid=(%u,%u,%u) tid=(%u,%u,%u) @ 0x%Lx\n", + ctaid.x,ctaid.y,ctaid.z,tid.x,tid.y,tid.z, (unsigned long long)*((void**)thread_info) ); + fflush(stdout); + } + thd->m_cta_info->assert_barrier_empty(); + thd->m_cta_info->register_deleted_thread(thd); + delete thd; + *((ptx_thread_info**)thread_info) = NULL; + } + + if ( !g_active_threads.empty() ) { //if g_active_threads not empty... + assert( g_active_threads.size() <= threads_left ); + if ( g_cta_launch_sid == (unsigned)-1 ) + g_cta_launch_sid = sid; + assert( g_cta_launch_sid == (unsigned)sid ); + ptx_thread_info *thd = g_active_threads.front(); + g_active_threads.pop_front(); + *((ptx_thread_info**)thread_info) = thd; + thd->set_hw_tid(tid); + thd->set_hw_wid(hw_warp_id); + thd->set_hw_ctaid(hw_cta_id); + thd->set_core(core); + thd->set_hw_sid(sid); + return 1; + } + + if ( g_gx >= g_cudaGridDim.x || g_gy >= g_cudaGridDim.y || g_gz >= g_cudaGridDim.z ) { + return 0; //finished! + } + + if ( threads_left < ptx_sim_cta_size() ) { + return 0; + } + + if ( g_debug_execution==-1 ) { + printf("GPGPU-Sim PTX simulator: STARTING THREAD ALLOCATION --> \n"); + fflush(stdout); + } + + //initializing new CTA + ptx_cta_info *cta_info = NULL; + memory_space *shared_mem = NULL; + + unsigned cta_size = ptx_sim_cta_size(); //blocksize + unsigned sm_offset = g_sm_idx_offset_next[sid]; + unsigned max_cta_per_sm = num_threads/cta_size; // e.g., 256 / 48 = 5 + assert( max_cta_per_sm > 0 ); + + //following sm_idx calculation assumes shaders being filled up depth-first? + unsigned sm_idx = sid*max_cta_per_sm + sm_offset; /* Is this assuming non-even block distribution? */ + sm_idx = max_cta_per_sm*sid + tid/cta_size; + + + if (!gpgpu_option_spread_blocks_across_cores) { + // update offset... + if ( (sm_offset + 1) >= max_cta_per_sm ) { + sm_offset = 0; + } else { + sm_offset++; + } + g_sm_idx_offset_next[sid] = sm_offset; + } else { + sm_idx = (tid/cta_size)*gpgpu_param_num_shaders + sid; + } + + if ( g_shared_memory_lookup.find(sm_idx) == g_shared_memory_lookup.end() ) { + if ( g_debug_execution >= 1 ) { + printf(" <CTA alloc> : sm_idx=%u sid=%u sm_offset=%u max_cta_per_sm=%u\n", + sm_idx, sid, sm_offset, max_cta_per_sm ); + } + char buf[512]; + snprintf(buf,512,"shared_%u", sid); + shared_mem = new memory_space_impl<16*1024>(buf,4); + g_shared_memory_lookup[sm_idx] = shared_mem; + cta_info = new ptx_cta_info(sm_idx); + g_ptx_cta_lookup[sm_idx] = cta_info; + } else { + if ( g_debug_execution >= 1 ) { + printf(" <CTA realloc> : sm_idx=%u sid=%u sm_offset=%u max_cta_per_sm=%u\n", + sm_idx, sid, sm_offset, max_cta_per_sm ); + } + shared_mem = g_shared_memory_lookup[sm_idx]; + cta_info = g_ptx_cta_lookup[sm_idx]; + cta_info->check_cta_thread_status_and_reset(); + } + + std::map<unsigned,memory_space*> &local_mem_lookup = g_local_memory_lookup[sid]; + unsigned new_tid; + for ( unsigned tz=0; tz < g_cudaBlockDim.z; tz++ ) { + for ( unsigned ty=0; ty < g_cudaBlockDim.y; ty++ ) { + for ( unsigned tx=0; tx < g_cudaBlockDim.x; tx++ ) { + new_tid = tx + g_cudaBlockDim.x*ty + g_cudaBlockDim.x*g_cudaBlockDim.y*tz; //unique id but only to a BLOCK + new_tid += tid; + ptx_thread_info *thd = new ptx_thread_info(); + + memory_space *local_mem = NULL; + std::map<unsigned,memory_space*>::iterator l = local_mem_lookup.find(new_tid); + if ( l != local_mem_lookup.end() ) { + local_mem = l->second; + } else { + char buf[512]; + snprintf(buf,512,"local_%u_%u", sid, new_tid); + local_mem = new memory_space_impl<32>(buf,32); + local_mem_lookup[new_tid] = local_mem; + } + thd->set_info(g_entrypoint_symbol_table,g_entrypoint_func_info); + thd->set_nctaid(g_cudaGridDim.x,g_cudaGridDim.y,g_cudaGridDim.z); + thd->set_ntid(g_cudaBlockDim.x,g_cudaBlockDim.y,g_cudaBlockDim.z); + thd->set_ctaid(g_gx,g_gy,g_gz); + thd->set_tid(tx,ty,tz); + thd->set_hw_tid((unsigned)-1); + thd->set_hw_wid((unsigned)-1); + thd->set_hw_ctaid((unsigned)-1); + thd->set_core(NULL); + thd->set_hw_sid((unsigned)-1); + thd->set_valid(); + thd->m_shared_mem = shared_mem; + thd->m_cta_info = cta_info; + cta_info->add_thread(thd); + thd->m_local_mem = local_mem; + if ( g_debug_execution==-1 ) { + printf("GPGPU-Sim PTX simulator: allocating thread ctaid=(%u,%u,%u) tid=(%u,%u,%u) @ 0x%Lx\n", + g_gx,g_gy,g_gz,tx,ty,tz, (unsigned long long)thd ); + fflush(stdout); + } + g_active_threads.push_back(thd); + } + } + } + if ( g_debug_execution==-1 ) { + printf("GPGPU-Sim PTX simulator: <-- FINISHING THREAD ALLOCATION\n"); + fflush(stdout); + } + + g_gx++; + if ( g_gx >= g_cudaGridDim.x ) { + g_gx = 0; + g_gy++; + if ( g_gy >= g_cudaGridDim.y ) { + g_gy = 0; + g_gz++; + } + } + + g_cta_launch_sid = -1; + + assert( g_active_threads.size() <= threads_left ); + + g_cta_launch_sid = sid; + *((ptx_thread_info**)thread_info) = g_active_threads.front(); + (*((ptx_thread_info**)thread_info) )->set_hw_tid(tid); + (*((ptx_thread_info**)thread_info) )->set_hw_wid(hw_warp_id); + (*((ptx_thread_info**)thread_info) )->set_hw_ctaid(hw_cta_id); + (*((ptx_thread_info**)thread_info) )->set_core(core); + (*((ptx_thread_info**)thread_info) )->set_hw_sid(sid); + g_active_threads.pop_front(); + + return 1; +} + +void init_inst_classification_stat() { + char kernelname[256] =""; +#define MAX_CLASS_KER 256 + if (!g_inst_classification_stat) g_inst_classification_stat = (void**)calloc(MAX_CLASS_KER, sizeof(void*)); + snprintf(kernelname, MAX_CLASS_KER, "Kernel %d Classification\n",g_ptx_kernel_count ); + assert( g_ptx_kernel_count < MAX_CLASS_KER ) ; // a static limit on number of kernels increase it if it fails! + g_inst_classification_stat[g_ptx_kernel_count] = StatCreate(kernelname,1,20); + if (!g_inst_op_classification_stat) g_inst_op_classification_stat = (void**)calloc(MAX_CLASS_KER, sizeof(void*)); + snprintf(kernelname, MAX_CLASS_KER, "Kernel %d OP Classification\n",g_ptx_kernel_count ); + g_inst_op_classification_stat[g_ptx_kernel_count] = StatCreate(kernelname,1,100); +} + +unsigned g_max_regs_per_thread = 0; + +std::map<std::string,function_info*> *g_kernel_name_to_function_lookup=NULL; +std::map<std::string,symbol_table*> g_kernel_name_to_symtab_lookup; +std::map<const void*,std::string> *g_host_to_kernel_entrypoint_name_lookup=NULL; +extern unsigned g_ptx_thread_info_uid_next; + +void gpgpu_ptx_sim_init_grid( const char *kernel_key, struct gpgpu_ptx_sim_arg* args, + struct dim3 gridDim, struct dim3 blockDim ) +{ + g_gx=0; + g_gy=0; + g_gz=0; + g_cudaGridDim = gridDim; + g_cudaBlockDim = blockDim; + g_sm_idx_offset_next.clear(); + g_sm_next_index = 0; + + if ( g_host_to_kernel_entrypoint_name_lookup->find(kernel_key) == + g_host_to_kernel_entrypoint_name_lookup->end() ) { + printf("GPGPU-Sim PTX: ERROR ** cannot locate PTX entry point\n" ); + abort(); + } else { + std::string kname = (*g_host_to_kernel_entrypoint_name_lookup)[kernel_key]; + printf("GPGPU-Sim PTX: Launching kernel \'%s\' gridDim= (%u,%u,%u) blockDim = (%u,%u,%u); ntuid=%u\n", + kname.c_str(), g_cudaGridDim.x,g_cudaGridDim.y,g_cudaGridDim.z,g_cudaBlockDim.x,g_cudaBlockDim.y,g_cudaBlockDim.z, + g_ptx_thread_info_uid_next ); + + if ( g_kernel_name_to_function_lookup->find(kname) == + g_kernel_name_to_function_lookup->end() ) { + printf("GPGPU-Sim PTX: ERROR ** function \'%s\' not found in ptx file\n", kname.c_str() ); + abort(); + } + g_entrypoint_func_info = g_func_info = (*g_kernel_name_to_function_lookup)[kname]; + g_entrypoint_symbol_table = g_current_symbol_table = g_kernel_name_to_symtab_lookup[kname]; + } + + unsigned argcount=0; + struct gpgpu_ptx_sim_arg *tmparg = args; + while (tmparg) { + tmparg = tmparg->m_next; + argcount++; + } + + unsigned argn=1; + while (args) { + g_func_info->add_param_data(argcount-argn,args); + args = args->m_next; + argn++; + } + g_func_info->finalize(g_param_mem,g_current_symbol_table); + g_ptx_kernel_count++; + if ( gpgpu_ptx_instruction_classification ) { + init_inst_classification_stat(); + } + fflush(stdout); +} + +const char *g_gpgpusim_version_string = "2.1.1b (beta)"; + +void print_splash() +{ + static int splash_printed=0; + if ( !splash_printed ) { + fprintf(stdout, "\n\n *** GPGPU-Sim version %s ***\n\n\n", g_gpgpusim_version_string ); + splash_printed=1; + } +} + +void gpgpu_ptx_sim_register_kernel(const char *hostFun, const char *deviceFun) +{ + const void* key=hostFun; + print_splash(); + if ( g_host_to_kernel_entrypoint_name_lookup == NULL ) + g_host_to_kernel_entrypoint_name_lookup = new std::map<const void*,std::string>; + if( g_kernel_name_to_function_lookup == NULL ) + g_kernel_name_to_function_lookup = new std::map<std::string,function_info*>; + if ( g_host_to_kernel_entrypoint_name_lookup->find(key) != + g_host_to_kernel_entrypoint_name_lookup->end() ) { + printf("GPGPU-Sim Loader error: Don't know how to identify PTX kernels during cudaLaunch\n" + " for this application.\n"); + abort(); + } + (*g_host_to_kernel_entrypoint_name_lookup)[key] = deviceFun; + if( g_kernel_name_to_function_lookup->find(deviceFun) == + g_kernel_name_to_function_lookup->end() ) { + (*g_kernel_name_to_function_lookup)[deviceFun] = NULL; // we set this later, set keys now for error checking + } + + printf("GPGPU-Sim PTX: __cudaRegisterFunction %s : 0x%Lx\n", deviceFun, (unsigned long long)hostFun); +} + +extern int ptx_lineno; + +void register_ptx_function( const char *name, function_info *impl, symbol_table *symtab ) +{ + printf("GPGPU-Sim PTX: parsing function %s\n", name ); + if( g_kernel_name_to_function_lookup == NULL ) + g_kernel_name_to_function_lookup = new std::map<std::string,function_info*>; + + std::map<std::string,function_info*>::iterator i_kernel = g_kernel_name_to_function_lookup->find(name); + if (i_kernel != g_kernel_name_to_function_lookup->end() && i_kernel->second != NULL) { + printf("GPGPU-Sim PTX: WARNING: Function already parsed once. Overwriting.\n"); + } + (*g_kernel_name_to_function_lookup)[name] = impl; + g_kernel_name_to_symtab_lookup[name] = symtab; +} + +std::map<const void*,std::string> g_const_name_lookup; // indexed by hostVar +std::map<const void*,std::string> g_global_name_lookup; // indexed by hostVar +std::set<std::string> g_globals; +std::set<std::string> g_constants; +extern std::map<std::string,symbol_table*> g_sym_name_to_symbol_table; + +void gpgpu_ptx_sim_register_const_variable(void *hostVar, const char *deviceName, size_t size ) +{ + printf("GPGPU-Sim PTX registering constant %s (%zu bytes) to name mapping\n", deviceName, size ); + g_const_name_lookup[hostVar] = deviceName; + //assert( g_current_symbol_table != NULL ); + //g_sym_name_to_symbol_table[deviceName] = g_current_symbol_table; +} + +void gpgpu_ptx_sim_register_global_variable(void *hostVar, const char *deviceName, size_t size ) +{ + printf("GPGPU-Sim PTX registering global %s hostVar to name mapping\n", deviceName ); + g_global_name_lookup[hostVar] = deviceName; + //assert( g_current_symbol_table != NULL ); + //g_sym_name_to_symbol_table[deviceName] = g_current_symbol_table; +} + +void gpgpu_ptx_sim_memcpy_symbol(const char *hostVar, const void *src, size_t count, size_t offset, int to ) +{ + printf("GPGPU-Sim PTX: starting gpgpu_ptx_sim_memcpy_symbol with hostVar 0x%p\n", hostVar); + bool found_sym = false; + int mem_region = 0; + std::string sym_name; + + std::map<const void*,std::string>::iterator c=g_const_name_lookup.find(hostVar); + if ( c!=g_const_name_lookup.end() ) { + found_sym = true; + sym_name = c->second; + mem_region = CONST_DIRECTIVE; + } + std::map<const void*,std::string>::iterator g=g_global_name_lookup.find(hostVar); + if ( g!=g_global_name_lookup.end() ) { + if ( found_sym ) { + printf("Execution error: PTX symbol \"%s\" w/ hostVar=0x%Lx is declared both const and global?\n", + sym_name.c_str(), (unsigned long long)hostVar ); + abort(); + } + found_sym = true; + sym_name = g->second; + mem_region = GLOBAL_DIRECTIVE; + } + if( g_globals.find(hostVar) != g_globals.end() ) { + found_sym = true; + sym_name = hostVar; + mem_region = GLOBAL_DIRECTIVE; + } + if( g_constants.find(hostVar) != g_constants.end() ) { + found_sym = true; + sym_name = hostVar; + mem_region = CONST_DIRECTIVE; + } + + if ( !found_sym ) { + printf("Execution error: No information for PTX symbol w/ hostVar=0x%Lx\n", (unsigned long long)hostVar ); + abort(); + } else printf("GPGPU-Sim PTX: gpgpu_ptx_sim_memcpy_symbol: Found PTX symbol w/ hostVar=0x%Lx\n", (unsigned long long)hostVar ); + const char *mem_name = NULL; + memory_space *mem = NULL; + + std::map<std::string,symbol_table*>::iterator st = g_sym_name_to_symbol_table.find(sym_name.c_str()); + if( st != g_sym_name_to_symbol_table.end() ) { + g_current_symbol_table = st->second; + } + + symbol *sym = g_current_symbol_table->lookup(sym_name.c_str()); + assert(sym); + unsigned dst = sym->get_address() + offset; + switch (mem_region) { + case CONST_DIRECTIVE: + mem = g_global_mem; + mem_name = "global"; + break; + case GLOBAL_DIRECTIVE: + mem = g_global_mem; + mem_name = "global"; + break; + default: + abort(); + } + printf("GPGPU-Sim PTX: gpgpu_ptx_sim_memcpy_symbol: copying %zu bytes %s symbol %s+%zu @0x%x ...\n", + count, (to?" to ":"from"), sym_name.c_str(), offset, dst ); + for ( unsigned n=0; n < count; n++ ) { + if( to ) mem->write(dst+n,1,((char*)src)+n); + else mem->read(dst+n,1,((char*)src)+n); + } + fflush(stdout); +} + +int g_ptx_sim_mode=0; +// used by libcuda.a if non-zer cudaLaunch() will call gpgpu_ptx_sim_main_func() +// if zero it calls gpgpu_ptx_sim_main_perf() + +#if defined(__APPLE__) +int ptx_file_filter(struct dirent *de ) +#else +int ptx_file_filter(const struct dirent *de ) +#endif +{ + const char *tmp = strstr(de->d_name,".ptx"); + if ( tmp != NULL && tmp[4] == 0 ) { + return 1; + } + return 0; +} + +void read_environment_variables() +{ + ptx_debug = 0; + g_debug_execution = 0; + g_debug_ir_generation = false; + + char *mode = getenv("PTX_SIM_MODE_FUNC"); + if ( mode ) + sscanf(mode,"%u", &g_ptx_sim_mode); + printf("GPGPU-Sim PTX: simulation mode %d (can change with PTX_SIM_MODE_FUNC environment variable:\n", g_ptx_sim_mode); + printf(" 1=functional simulation only, 0=detailed performance simulator)\n"); + g_filename = getenv("PTX_SIM_KERNELFILE"); + char *dbg_level = getenv("PTX_SIM_DEBUG"); + if ( dbg_level && strlen(dbg_level) ) { + printf("GPGPU-Sim PTX: setting debug level to %s\n", dbg_level ); + fflush(stdout); + sscanf(dbg_level,"%d", &g_debug_execution); + } + char *dbg_thread = getenv("PTX_SIM_DEBUG_THREAD_UID"); + if ( dbg_thread && strlen(dbg_thread) ) { + printf("GPGPU-Sim PTX: printing debug information for thread uid %s\n", dbg_thread ); + fflush(stdout); + sscanf(dbg_thread,"%d", &g_debug_thread_uid); + } + char *dbg_pc = getenv("PTX_SIM_DEBUG_PC"); + if ( dbg_pc && strlen(dbg_pc) ) { + printf("GPGPU-Sim PTX: printing debug information for instruction with PC = %s\n", dbg_pc ); + fflush(stdout); + sscanf(dbg_pc,"%d", &g_debug_pc); + } + +#if CUDART_VERSION > 1010 + g_override_embedded_ptx = false; + char *usefile = getenv("PTX_SIM_USE_PTX_FILE"); + if (usefile && strlen(usefile)) { + printf("GPGPU-Sim PTX: overriding embedded ptx with ptx file (PTX_SIM_USE_PTX_FILE is set)\n"); + fflush(stdout); + g_override_embedded_ptx = true; + } +#else + g_override_embedded_ptx = true; +#endif + + if ( g_debug_execution >= 40 ) { + ptx_debug = 1; + } + if ( g_debug_execution >= 30 ) { + g_debug_ir_generation = true; + } +} + +static FILE *open_ptxinfo (const char* ptx_filename) +{ + const int ptx_fnamelen = strlen(ptx_filename); + char *ptxi_fname = new char[ptx_fnamelen+5]; + strcpy (ptxi_fname, ptx_filename); + strcpy (ptxi_fname+ptx_fnamelen, "info"); + + //ptxinfo_debug=1; + g_ptxinfo_filename = ptxi_fname; + FILE *f = fopen (ptxi_fname, "rt"); + return f; +} + +struct ptx_info_t { + char *str; + ptx_info_t *next; +}; + +ptx_info_t *g_ptx_source_array = NULL; +unsigned g_used_embedded_ptx_files; +extern double g_ptx_version; + +void gpgpu_ptx_sim_add_ptxstring( const char *ptx_string ) +{ + ptx_info_t *t = new ptx_info_t; + t->next = NULL; + t->str = strdup(ptx_string); + + // put ptx source into a fifo + if (g_ptx_source_array == NULL) { + // first ptx source + g_ptx_source_array = t; + } else { + // insert subsequent ptx source at the end of queue + ptx_info_t *l_ptx_source = g_ptx_source_array; + while (l_ptx_source->next != NULL) { + l_ptx_source = l_ptx_source->next; + } + l_ptx_source->next = t; + } +} + +const ptx_instruction *ptx_instruction_lookup( const char *filename, unsigned linenumber ); + +void print_ptx_file( const char *p, unsigned source_num, const char *filename ) +{ + printf("\nGPGPU-Sim PTX: file _%u.ptx contents:\n\n", source_num ); + char *s = strdup(p); + char *t = s; + unsigned n=1; + while ( *t != '\0' ) { + char *u = t; + while ( (*u != '\n') && (*u != '\0') ) u++; + unsigned last = (*u == '\0'); + *u = '\0'; + const ptx_instruction *pI = ptx_instruction_lookup(filename,n); + char pc[64]; + if( pI && pI->get_PC() ) + snprintf(pc,64,"%4u", pI->get_PC() ); + else + snprintf(pc,64," "); + printf(" _%u.ptx %4u (pc=%s): %s\n", source_num, n, pc, t ); + if ( last ) break; + t = u+1; + n++; + } + free(s); + fflush(stdout); +} + +extern int g_save_embedded_ptx; + +void gpgpu_ptx_sim_load_ptx_from_string( const char *p, unsigned source_num ) +{ + char buf[1024]; + snprintf(buf,1024,"_%u.ptx", source_num ); + if( g_save_embedded_ptx ) { + FILE *fp = fopen(buf,"w"); + fprintf(fp,"%s",p); + fclose(fp); + } + g_filename = strdup(buf); + init_parser(); + ptx__scan_string(p); + int errors = ptx_parse (); + if ( errors ) { + char fname[1024]; + snprintf(fname,1024,"_ptx_XXXXXX"); + int fd=mkstemp(fname); + close(fd); + printf("GPGPU-Sim PTX: parser error detected, exiting... but first extracting .ptx to \"%s\"\n", fname); + FILE *ptxfile = fopen(fname,"w"); + fprintf(ptxfile,"%s", p ); + fclose(ptxfile); + abort(); + exit(40); + } + + if ( g_debug_execution >= 1 ) + print_ptx_file(p,source_num,g_filename); + + printf("GPGPU-Sim PTX: finished parsing EMBEDDED .ptx file %s\n",g_filename); + + char fname[1024]; + snprintf(fname,1024,"_ptx_XXXXXX"); + int fd=mkstemp(fname); + close(fd); + + printf("GPGPU-Sim PTX: extracting embedded .ptx to temporary file \"%s\"\n", fname); + FILE *ptxfile = fopen(fname,"w"); + fprintf(ptxfile,"%s",p); + fclose(ptxfile); + + char fname2[1024]; + snprintf(fname2,1024,"_ptx2_XXXXXX"); + fd=mkstemp(fname2); + close(fd); + char commandline2[4096]; + snprintf(commandline2,4096,"cat %s | sed 's/.version 1.5/.version 1.4/' | sed 's/, texmode_independent//' | sed 's/\\(\\.extern \\.const\\[1\\] .b8 \\w\\+\\)\\[\\]/\\1\\[1\\]/' | sed 's/const\\[.\\]/const\\[0\\]/g' > %s", fname, fname2); + int result = system(commandline2); + if( result != 0 ) { + printf("GPGPU-Sim PTX: ERROR ** while loading PTX (a) %d\n", result); + printf(" Ensure you have write access to simulation directory\n"); + printf(" and have \'cat\' and \'sed\' in your path.\n"); + exit(1); + } + + char tempfile_ptxinfo[1024]; + snprintf(tempfile_ptxinfo,1024,"%sinfo",fname); + char commandline[1024]; + snprintf(commandline,1024,"ptxas -v %s --output-file /dev/null 2> %s", fname2, tempfile_ptxinfo); + printf("GPGPU-Sim PTX: generating ptxinfo using \"%s\"\n", commandline); + result = system(commandline); + if( result != 0 ) { + printf("GPGPU-Sim PTX: ERROR ** while loading PTX (b) %d\n", result); + printf(" Ensure ptxas is in your path.\n"); + exit(1); + } + + ptxinfo_in = fopen(tempfile_ptxinfo,"r"); + g_ptxinfo_filename = tempfile_ptxinfo; + ptxinfo_parse(); + snprintf(commandline,1024,"rm -f %s %s %s", fname, fname2, tempfile_ptxinfo); + printf("GPGPU-Sim PTX: removing ptxinfo using \"%s\"\n", commandline); + result = system(commandline); + if( result != 0 ) { + printf("GPGPU-Sim PTX: ERROR ** while loading PTX (c) %d\n", result); + exit(1); + } + g_filename = NULL; +} + +void gpgpu_ptx_assemble( std::string kname, void *kinfo ) +{ + function_info *func_info = (function_info *)kinfo; + g_func_info = func_info; + g_current_symbol_table = g_kernel_name_to_symtab_lookup[ kname ]; + if( g_current_symbol_table == NULL ) { + printf("\nGPGPU-Sim PTX: ERROR no information for kernel \'%s\'\n" + " this can happen for kernels contained in CUDA\n" + " libraries (such as CUBLAS, CUFFT, or CUDPP)\n", kname.c_str() ); + exit(1); + } + + func_info->ptx_assemble(); +} + +void gpgpu_ptx_sim_load_gpu_kernels() +{ + ptx_in = NULL; + if ( g_filename ) + ptx_in = fopen( g_filename, "r" ); + static unsigned source_num = 0; + gpgpu_ptx_sim_init_memory(); + if (ptx_in) { + init_parser(); + ptx_parse(); + ptxinfo_in = open_ptxinfo(g_filename); + ptxinfo_parse(); + load_static_globals(g_global_symbol_table,0x10000000,0xFFFFFFFF); + load_constants(g_global_symbol_table); + } else { + if (!g_override_embedded_ptx) { + g_used_embedded_ptx_files=1; + printf("GPGPU-Sim PTX: USING EMBEDDED .ptx files...\n"); + ptx_info_t *s; + for ( s=g_ptx_source_array; s!=NULL; s=s->next ) { + gpgpu_ptx_sim_load_ptx_from_string(s->str, ++source_num); + load_static_globals(g_global_symbol_table,0x10000000,0xFFFFFFFF); + load_constants(g_global_symbol_table); + } + } else { + g_filename = NULL; + struct dirent **namelist; + int n; + + n = scandir(".", &namelist, ptx_file_filter, alphasort); + if (n < 0) + perror("scandir"); + else { + while (n--) { + if ( g_filename != NULL ) { + printf("Loader error: support for multiple .ptx files not yet enabled\n"); + abort(); + } + g_filename = strdup(namelist[n]->d_name); + printf("Parsing %s..\n", g_filename); + ptx_in = fopen( g_filename, "r" ); + free(namelist[n]); + init_parser(); + ptx_parse (); + ptxinfo_in = open_ptxinfo(g_filename); + ptxinfo_parse(); + g_filename = NULL; + load_static_globals(g_global_symbol_table,0x10000000,0xFFFFFFFF); + load_constants(g_global_symbol_table); + } + free(namelist); + } + } + } + + if ( ptx_in == NULL && g_override_embedded_ptx ) { + printf("GPGPU-Sim PTX Simulator error: Could find/open .ptx file for reading\n"); + printf(" This means there are no .ptx files in the current directory.\n"); + printf(" Either place a .ptx file in the current directory, or ensure\n" ); + printf(" the PTX_SIM_KERNELFILE environment variable points to .ptx file.\n"); + printf(" PTX_SIM_KERNELFILE=\"%s\"\n", g_filename ); + exit(1); + } + + if ( g_error_detected ) { + printf( "GPGPU-Sim PTX: Program parsing completed: Errors detected.\n" ); + exit(1); + } else { + printf( "GPGPU-Sim PTX: Program parsing completed (max %u registers used per thread).\n", g_max_regs_per_thread ); + } + + if ( g_kernel_name_to_function_lookup ) { + for ( std::map<std::string,function_info*>::iterator f=g_kernel_name_to_function_lookup->begin(); + f != g_kernel_name_to_function_lookup->end(); f++ ) { + gpgpu_ptx_assemble(f->first,f->second); + } + } +} + +extern time_t simulation_starttime; + +ptx_cta_info *g_func_cta_info = NULL; + +#define MAX(a,b) (((a)>(b))?(a):(b)) + +void gpgpu_ptx_sim_main_func( const char *kernel_key, dim3 gridDim, dim3 blockDim, struct gpgpu_ptx_sim_arg *args) +{ + printf("GPGPU-Sim: Performing Functional Simulation...\n"); + + printf("ERROR: Need to derived core_t for functional simulation, functional simulation no longer operational\n"); + // also: need PDOM stack, etc... for functional simulation + exit(1); + + time_t end_time, elapsed_time, days, hrs, minutes, sec; + int i1, i2, i3, i4, o1, o2, o3, o4; + int vectorin, vectorout; + + gpgpu_ptx_sim_init_grid(kernel_key, args,gridDim,blockDim); + + memory_space *shared_mem = new memory_space_impl<16*1024>("shared",4); + + std::map<unsigned,memory_space*> lm_lookup; + + if ( g_func_cta_info == NULL ) + g_func_cta_info = new ptx_cta_info(0); + + for ( unsigned gx=0; gx < gridDim.x; gx++ ) { + for ( unsigned gy=0; gy < gridDim.y; gy++ ) { + for ( unsigned gz=0; gz < gridDim.z; gz++ ) { + std::list<ptx_thread_info *> active_threads; + std::list<ptx_thread_info *> blocked_threads; + + g_func_cta_info->check_cta_thread_status_and_reset(); + + for ( unsigned tx=0; tx < blockDim.x; tx++ ) { + for ( unsigned ty=0; ty < blockDim.y; ty++ ) { + for ( unsigned tz=0; tz < blockDim.z; tz++ ) { + memory_space *local_mem = NULL; + ptx_thread_info *thd = new ptx_thread_info(); + + unsigned lm_idx = blockDim.x*blockDim.y*tz + blockDim.x * ty + tx; + std::map<unsigned,memory_space*>::iterator lm=lm_lookup.find(lm_idx); + if ( lm == lm_lookup.end() ) { + char buf[1024]; + snprintf(buf,1024,"local_(%u,%u,%u)", tx, ty, tz ); + local_mem = new memory_space_impl<32>(buf,32); + lm_lookup[lm_idx] = local_mem; + } else { + local_mem = lm->second; + } + + + thd->set_info(g_current_symbol_table,g_func_info); + thd->set_nctaid(gridDim.x,gridDim.y,gridDim.z); + thd->set_ntid(blockDim.x, blockDim.y, blockDim.z); + thd->set_ctaid(gx,gy,gz); + thd->set_tid(tx,ty,tz); + thd->set_valid(); + thd->m_shared_mem = shared_mem; + thd->m_local_mem = local_mem; + thd->m_cta_info = g_func_cta_info; + g_func_cta_info->add_thread(thd); + active_threads.push_back(thd); + } + } + } + + while ( !(active_threads.empty() && blocked_threads.empty()) ) { + // while there are still threads left to execute in this CTA + ptx_thread_info *thread = NULL; + + if ( !active_threads.empty() ) { + thread = active_threads.front(); + active_threads.pop_front(); + } else { + active_threads = blocked_threads; + blocked_threads.clear(); + std::list<ptx_thread_info *>::iterator a=active_threads.begin(); + for ( ; a != active_threads.end(); a++ ) { + ptx_thread_info *thd = *a; + thd->clear_barrier(); + } + g_func_cta_info->release_barrier(); + } + + while ( thread != NULL ) { + if ( thread->is_at_barrier() ) { + blocked_threads.push_back(thread); + thread = NULL; + break; + } + if ( thread->is_done() ) { + thread->m_cta_info->register_deleted_thread(thread); + delete thread; + thread = NULL; + break; + } + + unsigned op_type; + addr_t addr; + unsigned space; + int arch_reg[MAX_REG_OPERANDS] = { -1 }; + unsigned data_size; + dram_callback_t callback; + unsigned warp_active_mask = (unsigned)-1; // vote instruction with diverged warps won't execute correctly + // in functional simulation mode + + g_func_info->ptx_decode_inst( thread, &op_type, &i1, &i2, &i3, &i4, &o1, &o2, &o3, &o4, &vectorin, &vectorout, arch_reg ); + g_func_info->ptx_exec_inst( thread, &addr, &space, &data_size, &callback, warp_active_mask ); + } + } + } + } + } + printf( "GPGPU-Sim: Done functional simulation (%u instructions simulated).\n", g_ptx_sim_num_insn ); + if ( gpgpu_ptx_instruction_classification ) { + StatDisp( g_inst_classification_stat[g_ptx_kernel_count]); + StatDisp ( g_inst_op_classification_stat[g_ptx_kernel_count]); + } + end_time = time((time_t *)NULL); + elapsed_time = MAX(end_time - simulation_starttime, 1); + + days = elapsed_time/(3600*24); + hrs = elapsed_time/3600 - 24*days; + minutes = elapsed_time/60 - 60*(hrs + 24*days); + sec = elapsed_time - 60*(minutes + 60*(hrs + 24*days)); + + fflush(stderr); + printf("\n\ngpgpu_simulation_time = %u days, %u hrs, %u min, %u sec (%u sec)\n", + (unsigned)days, (unsigned)hrs, (unsigned)minutes, (unsigned)sec, (unsigned)elapsed_time ); + printf("gpgpu_simulation_rate = %u (inst/sec)\n", (unsigned)(g_ptx_sim_num_insn / elapsed_time) ); + fflush(stdout); +} + +void ptx_decode_inst( void *thd, unsigned *op, int *i1, int *i2, int *i3, int *i4, int *o1, int *o2, int *o3, int *o4, int *vectorin, int *vectorout, int *arch_reg ) +{ + *op = NO_OP; + *o1 = 0; + *o2 = 0; + *o3 = 0; + *o4 = 0; + *i1 = 0; + *i2 = 0; + *i3 = 0; + *i4 = 0; + *vectorin = 0; + *vectorout = 0; + std::fill_n(arch_reg, MAX_REG_OPERANDS, -1); + + if ( thd == NULL ) + return; + + ptx_thread_info *thread = (ptx_thread_info *) thd; + g_func_info = thread->func_info(); + g_func_info->ptx_decode_inst(thread,op,i1,i2,i3,i4,o1,o2,o3,o4,vectorin,vectorout,arch_reg); +} + +unsigned ptx_get_inst_op( void *thd) +{ + if ( thd == NULL ) + return NO_OP; + + ptx_thread_info *thread = (ptx_thread_info *) thd; + return(thread->func_info())->ptx_get_inst_op(thread); +} + +void ptx_exec_inst( void *thd, address_type *addr, unsigned *space, unsigned *data_size, dram_callback_t* callback, unsigned warp_active_mask ) +{ + if ( thd == NULL ) + return; + ptx_thread_info *thread = (ptx_thread_info *) thd; + g_func_info = thread->func_info(); + g_func_info->ptx_exec_inst( thread, addr, space, data_size, callback, warp_active_mask ); +} + +void ptx_dump_regs( void *thd ) +{ + if ( thd == NULL ) + return; + ptx_thread_info *t = (ptx_thread_info *) thd; + t->dump_regs(); +} + +unsigned ptx_set_tex_cache_linesize(unsigned linesize) +{ + g_texcache_linesize = linesize; + return 0; +} + +unsigned ptx_kernel_program_size() +{ + return g_func_info->get_function_size(); +} + +unsigned translate_pc_to_ptxlineno(unsigned pc) +{ + // this function assumes that the kernel fits inside a single PTX file + // function_info *pFunc = g_func_info; // assume that the current kernel is the one in query + const ptx_instruction *pInsn = function_info::pc_to_instruction(pc); + unsigned ptx_line_number = pInsn->source_line(); + + return ptx_line_number; +} + +int g_ptxinfo_error_detected; + + +static char *g_ptxinfo_kname = NULL; +static struct gpgpu_ptx_sim_kernel_info g_ptxinfo_kinfo; + +extern "C" void ptxinfo_function(const char *fname ) +{ + g_ptxinfo_kinfo.regs=0; + g_ptxinfo_kinfo.lmem=0; + g_ptxinfo_kinfo.smem=0; + g_ptxinfo_kinfo.cmem=0; + g_ptxinfo_kname = strdup(fname); +} + +extern "C" void ptxinfo_regs( unsigned nregs ) +{ + g_ptxinfo_kinfo.regs=nregs; +} + +extern "C" void ptxinfo_lmem( unsigned declared, unsigned system ) +{ + g_ptxinfo_kinfo.lmem=declared+system; +} + +extern "C" void ptxinfo_smem( unsigned declared, unsigned system ) +{ + g_ptxinfo_kinfo.smem=declared+system; +} + +extern "C" void ptxinfo_cmem( unsigned nbytes, unsigned bank ) +{ + g_ptxinfo_kinfo.cmem+=nbytes; +} + +extern "C" void ptxinfo_addinfo() +{ + if ( g_kernel_name_to_function_lookup ) { + std::map<std::string,function_info*>::iterator i=g_kernel_name_to_function_lookup->find(g_ptxinfo_kname); + if ( (g_kernel_name_to_function_lookup == NULL) || (i == g_kernel_name_to_function_lookup->end()) ) { + printf ("GPGPU-Sim PTX: Kernel '%s' in %s not found. Ignoring.\n", g_ptxinfo_kname, g_filename); + } else { + printf ("GPGPU-Sim PTX: Kernel %s\n", g_ptxinfo_kname); + function_info *fi = i->second; + fi->set_kernel_info(&g_ptxinfo_kinfo); + } + } else { + printf ("GPGPU-Sim PTX: Kernel '%s' in %s not found (no kernels registered).\n", g_ptxinfo_kname, g_filename); + } + + free(g_ptxinfo_kname); + g_ptxinfo_kname=NULL; + g_ptxinfo_kinfo.regs=0; + g_ptxinfo_kinfo.lmem=0; + g_ptxinfo_kinfo.smem=0; + g_ptxinfo_kinfo.cmem=0; +} + +void dwf_insert_reconv_pt(address_type pc); + +struct rec_pts { + gpgpu_recon_t *s_kernel_recon_points; + int s_num_recon; +}; + +struct std::map<function_info*,rec_pts> g_rpts; + +struct rec_pts find_reconvergence_points( function_info *finfo ) +{ + rec_pts tmp; + std::map<function_info*,rec_pts>::iterator r=g_rpts.find(finfo); + + if( r==g_rpts.end() ) { + int num_recon = finfo->get_num_reconvergence_pairs(); + + gpgpu_recon_t *kernel_recon_points = (struct gpgpu_recon_t*) calloc(num_recon, sizeof(struct gpgpu_recon_t)); + finfo->get_reconvergence_pairs(kernel_recon_points); + printf("Reconvergence Pairs for %s\n", finfo->get_name().c_str() ); + for (int i=0;i<num_recon;i++) + printf("%d\t%d\n", kernel_recon_points[i].source_pc, kernel_recon_points[i].target_pc); + tmp.s_kernel_recon_points = kernel_recon_points; + tmp.s_num_recon = num_recon; + g_rpts[finfo] = tmp; + } else { + tmp = r->second; + } + return tmp; +} + +unsigned int get_converge_point( unsigned int pc, void *thd ) +{ + // the branch could encode the reconvergence point and/or a bit that indicates the + // reconvergence point is the return PC on the call stack in the case the branch has + // no immediate postdominator in the function (i.e., due to multiple return points). + + std::map<unsigned,function_info*>::iterator f=g_pc_to_finfo.find(pc); + assert( f != g_pc_to_finfo.end() ); + function_info *finfo = f->second; + rec_pts tmp = find_reconvergence_points(finfo); + + int i=0; + for (; i < tmp.s_num_recon; ++i) { + if (tmp.s_kernel_recon_points[i].source_pc == pc) { + if( tmp.s_kernel_recon_points[i].target_pc == (unsigned) -2 ) { + // function call return + ptx_thread_info *the_thread = (ptx_thread_info*)thd; + assert( the_thread != NULL ); + return the_thread->get_return_PC(); + } else { + return tmp.s_kernel_recon_points[i].target_pc; + } + } + } + assert(i < tmp.s_num_recon); + abort(); // returning garbage! +} + +void find_reconvergence_points() +{ + find_reconvergence_points(g_func_info); +} + +void dwf_process_reconv_pts() +{ + rec_pts tmp = find_reconvergence_points(g_func_info); + for (int i = 0; i < tmp.s_num_recon; ++i) { + dwf_insert_reconv_pt(tmp.s_kernel_recon_points[i].target_pc); + } +} + +void *gpgpusim_opencl_getkernel_Object( const char *kernel_name ) +{ + std::map<std::string,function_info*>::iterator i=g_kernel_name_to_function_lookup->find(kernel_name); + + if( i == g_kernel_name_to_function_lookup->end() ) { + abort(); + return NULL; + } + return i->second; +} diff --git a/src/cuda-sim/dram_callback.h b/src/cuda-sim/dram_callback.h new file mode 100644 index 0000000..9336ee0 --- /dev/null +++ b/src/cuda-sim/dram_callback.h @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2009 by Tor M. Aamodt, Miles Kaech, Dan O'Connor and the + * University of British Columbia + * Vancouver, BC V6T 1Z4 + * All Rights Reserved. + * + * THIS IS A LEGAL DOCUMENT BY DOWNLOADING GPGPU-SIM, YOU ARE AGREEING TO THESE + * TERMS AND CONDITIONS. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * NOTE: The files libcuda/cuda_runtime_api.c and src/cuda-sim/cuda-math.h + * are derived from the CUDA Toolset available from http://www.nvidia.com/cuda + * (property of NVIDIA). The files benchmarks/BlackScholes/ and + * benchmarks/template/ are derived from the CUDA SDK available from + * http://www.nvidia.com/cuda (also property of NVIDIA). The files from + * src/intersim/ are derived from Booksim (a simulator provided with the + * textbook "Principles and Practices of Interconnection Networks" available + * from http://cva.stanford.edu/books/ppin/). As such, those files are bound by + * the corresponding legal terms and conditions set forth separately (original + * copyright notices are left in files from these sources and where we have + * modified a file our copyright notice appears before the original copyright + * notice). + * + * Using this version of GPGPU-Sim requires a complete installation of CUDA + * which is distributed seperately by NVIDIA under separate terms and + * conditions. To use this version of GPGPU-Sim with OpenCL requires a + * recent version of NVIDIA's drivers which support OpenCL. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the University of British Columbia nor the names of + * its contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * 4. This version of GPGPU-SIM is distributed freely for non-commercial use only. + * + * 5. No nonprofit user may place any restrictions on the use of this software, + * including as modified by the user, by any other authorized user. + * + * 6. GPGPU-SIM was developed primarily by Tor M. Aamodt, Wilson W. L. Fung, + * Ali Bakhoda, George L. Yuan, at the University of British Columbia, + * Vancouver, BC V6T 1Z4 + */ + +////////////////////////////////////////////////////////// +// +// dram_callback.h +// by Miles Kaech +// +// defines a callback mechanism allowing instructions to be functionally executed +// once they arrive at dram. For example, atomic instructions should really be +// functionally simulated only once they hit the dram chips. +// +////////////////////////////////////////////////////////// + +#ifndef __DRAM_CALLBACK_H__ +#define __DRAM_CALLBACK_H__ + +typedef struct { + void (*function)(void* pI, void* gOldGThread); + void* instruction; + void* thread;// callback has to abuse g_thread when executed +}dram_callback_t; + +#endif // #ifndef __DRAM_CALLBACK_H__ diff --git a/src/cuda-sim/instructions.cc b/src/cuda-sim/instructions.cc new file mode 100644 index 0000000..a7a4df6 --- /dev/null +++ b/src/cuda-sim/instructions.cc @@ -0,0 +1,2921 @@ +/* + * Copyright (c) 2009 by Tor M. Aamodt, Ali Bakhoda, Joey Ting, Dan O'Connor, + * Clive Lin, George L. Yuan, Wilson W. L. Fung and the + * University of British Columbia + * Vancouver, BC V6T 1Z4 + * All Rights Reserved. + * + * THIS IS A LEGAL DOCUMENT BY DOWNLOADING GPGPU-SIM, YOU ARE AGREEING TO THESE + * TERMS AND CONDITIONS. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * NOTE: The files libcuda/cuda_runtime_api.c and src/cuda-sim/cuda-math.h + * are derived from the CUDA Toolset available from http://www.nvidia.com/cuda + * (property of NVIDIA). The files benchmarks/BlackScholes/ and + * benchmarks/template/ are derived from the CUDA SDK available from + * http://www.nvidia.com/cuda (also property of NVIDIA). The files from + * src/intersim/ are derived from Booksim (a simulator provided with the + * textbook "Principles and Practices of Interconnection Networks" available + * from http://cva.stanford.edu/books/ppin/). As such, those files are bound by + * the corresponding legal terms and conditions set forth separately (original + * copyright notices are left in files from these sources and where we have + * modified a file our copyright notice appears before the original copyright + * notice). + * + * Using this version of GPGPU-Sim requires a complete installation of CUDA + * which is distributed seperately by NVIDIA under separate terms and + * conditions. To use this version of GPGPU-Sim with OpenCL requires a + * recent version of NVIDIA's drivers which support OpenCL. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the University of British Columbia nor the names of + * its contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * 4. This version of GPGPU-SIM is distributed freely for non-commercial use only. + * + * 5. No nonprofit user may place any restrictions on the use of this software, + * including as modified by the user, by any other authorized user. + * + * 6. GPGPU-SIM was developed primarily by Tor M. Aamodt, Wilson W. L. Fung, + * Ali Bakhoda, George L. Yuan, at the University of British Columbia, + * Vancouver, BC V6T 1Z4 + */ + +#include "instructions.h" +#include "ptx_ir.h" +#include "opcodes.h" +#include "ptx_sim.h" +#include "ptx.tab.h" +#include "dram_callback.h" +#include <stdlib.h> +#include <math.h> +#include <fenv.h> + +#include "cuda-math.h" +#include "../abstract_hardware_model.h" + +unsigned g_num_ptx_inst_uid=0; +unsigned cudasim_n_tex_insn=0; + +const char *g_opcode_string[NUM_OPCODES] = { +#define OP_DEF(OP,FUNC,STR,DST,CLASSIFICATION) STR, +#include "opcodes.def" +#undef OP_DEF +}; + +extern std::map<unsigned,std::string> g_ptx_token_decode; +extern std::map<struct textureReference*,struct cudaArray*> TextureToArrayMap; // texture bindings +extern std::map<struct textureReference*,struct textureInfo*> TextureToInfoMap; // texture bindings +extern std::map<std::string, struct textureReference*> NameToTextureMap; + +memory_space *g_global_mem; +memory_space *g_tex_mem; +memory_space *g_surf_mem; +memory_space *g_param_mem; + +void inst_not_implemented( const ptx_instruction * pI ) ; +unsigned unfound_register_warned = 0; + +ptx_reg_t ptx_thread_info::get_operand_value( const symbol *reg ) +{ + assert( reg->type()->get_key().is_reg() ); + const std::string &name = reg->name(); + std::map<std::string,ptx_reg_t>::iterator regs_iter = m_regs.back().find(name); + if (regs_iter == m_regs.back().end()) { + unsigned call_uid = m_callstack.back().m_call_uid; + ptx_reg_t uninit_reg; + uninit_reg.u32 = 0xDEADBEEF; + set_operand_value(reg, uninit_reg); // give it a value since we are going to warn the user anyway + std::string file_loc = get_location(); + if( !unfound_register_warned ) { + printf("GPGPU-Sim PTX: WARNING (%s) ** reading undefined register \'%s\' (cuid:%u). Setting to 0XDEADBEEF.\n", + file_loc.c_str(), name.c_str(), call_uid ); + unfound_register_warned = 1; + } + regs_iter = m_regs.back().insert(std::make_pair(name, uninit_reg)).first; + } + return regs_iter->second; +} + +ptx_reg_t ptx_thread_info::get_operand_value( const operand_info &op ) +{ + ptx_reg_t result, tmp; + const char *name = NULL; + if ( op.is_reg() ) { + result = get_operand_value( op.get_symbol() ); + } else if ( op.is_builtin()) { + result = get_builtin( op.get_int(), op.get_addr_offset() ); + } else if ( op.is_memory_operand() ) { + // a few options here... + const symbol *sym = op.get_symbol(); + const type_info *type = sym->type(); + const type_info_key &info = type->get_key(); + + if ( info.is_reg() ) { + name = op.name().c_str(); + std::map<std::string,ptx_reg_t>::iterator regs_iter = m_regs.back().find(name); + assert( regs_iter != m_regs.back().end() ); + tmp = regs_iter->second; + result.u64 = tmp.u64 + op.get_addr_offset(); + } else if ( info.is_param() ) { + result = sym->get_address() + op.get_addr_offset(); + } else if ( info.is_global() ) { + assert( op.get_addr_offset() == 0 ); + result = sym->get_address(); + } else if ( info.is_local() ) { + result = sym->get_address() + op.get_addr_offset(); + } else if ( info.is_const() ) { + result = sym->get_address() + op.get_addr_offset(); + } else if ( op.is_shared() ) { + result = op.get_symbol()->get_address() + op.get_addr_offset(); + } else { + assert(0); + } + + } else if ( op.is_literal() ) { + result = op.get_literal_value(); + } else if ( op.is_label() ) { + result = op.get_symbol()->get_address(); + } else if ( op.is_shared() ) { + result = op.get_symbol()->get_address(); + } else if ( op.is_const() ) { + result = op.get_symbol()->get_address(); + } else if ( op.is_global() ) { + result = op.get_symbol()->get_address(); + } else if ( op.is_local() ) { + result = op.get_symbol()->get_address(); + } else { + name = op.name().c_str(); + printf("ERROR! %s is neither local, global, const, shared, label, literal, memory operand, builtin, or register!\n",name); + assert(0); + } + + return result; +} + +unsigned get_operand_nbits( const operand_info &op ) +{ + if ( op.is_reg() ) { + const symbol *sym = op.get_symbol(); + const type_info *typ = sym->type(); + type_info_key t = typ->get_key(); + switch( t.scalar_type() ) { + case PRED_TYPE: + return 1; + case B8_TYPE: case S8_TYPE: case U8_TYPE: + return 8; + case S16_TYPE: case U16_TYPE: case F16_TYPE: case B16_TYPE: + return 16; + case S32_TYPE: case U32_TYPE: case F32_TYPE: case B32_TYPE: + return 32; + case S64_TYPE: case U64_TYPE: case F64_TYPE: case B64_TYPE: + return 64; + default: + printf("ERROR: unknown register type\n"); + fflush(stdout); + abort(); + } + } else { + printf("ERROR: Need to implement get_operand_nbits() for currently unsupported operand_info type\n"); + fflush(stdout); + abort(); + } + + return 0; +} + +void ptx_thread_info::get_vector_operand_values( const operand_info &op, ptx_reg_t* ptx_regs, unsigned num_elements ) +{ + const char *name1, *name2, *name3, *name4 = NULL; + if ( op.is_vector() ) { + if (num_elements > 3) { + name4 = op.vec_name4().c_str(); + assert( m_regs.back().find(name4) != m_regs.back().end() ); + ptx_regs[3] = m_regs.back()[ name4 ]; + } + if (num_elements > 2) { + name3 = op.vec_name3().c_str(); + assert( m_regs.back().find(name3) != m_regs.back().end() ); + ptx_regs[2] = m_regs.back()[ name3 ]; + } + name1 = op.vec_name1().c_str(); + name2 = op.vec_name2().c_str(); + assert( m_regs.back().find(name1) != m_regs.back().end() ); + assert( m_regs.back().find(name2) != m_regs.back().end() ); + ptx_regs[0] = m_regs.back()[ name1 ]; + ptx_regs[1] = m_regs.back()[ name2 ]; + } else { + assert(0); + } +} + +void sign_extend( ptx_reg_t &data, unsigned src_size, const operand_info &dst ) +{ + if( !dst.is_reg() ) + return; + unsigned dst_size = get_operand_nbits( dst ); + if( src_size >= dst_size ) + return; + // src_size < dst_size + unsigned long long mask = 1; + mask <<= (src_size-1); + if( (mask & data.u64) == 0 ) { + // no need to sign extend + return; + } + // need to sign extend + mask = 1; + mask <<= dst_size-src_size; + mask -= 1; + mask <<= src_size; + data.u64 |= mask; +} + +void ptx_thread_info::set_operand_value( const operand_info &dst, const ptx_reg_t &data ) +{ + m_regs.back()[ dst.name() ] = data; + m_debug_trace_regs_modified.back()[ dst.name() ] = data; + m_last_set_operand_value = data; +} + +void ptx_thread_info::set_operand_value( const symbol *dst, const ptx_reg_t &data ) +{ + m_regs.back()[ dst->name() ] = data; + m_debug_trace_regs_modified.back()[ dst->name() ] = data; + m_last_set_operand_value = data; +} + +void ptx_thread_info::set_vector_operand_values( const operand_info &dst, + const ptx_reg_t &data1, + const ptx_reg_t &data2, + const ptx_reg_t &data3, + const ptx_reg_t &data4, + unsigned num_elements ) +{ + m_regs.back()[ dst.vec_name1() ] = data1; + m_debug_trace_regs_modified.back()[ dst.vec_name1() ] = data1; + m_regs.back()[ dst.vec_name2() ] = data2; + m_debug_trace_regs_modified.back()[ dst.vec_name2() ] = data2; + if (num_elements > 2) { + m_regs.back()[ dst.vec_name3() ] = data3; + m_debug_trace_regs_modified.back()[ dst.vec_name3() ] = data3; + if (num_elements > 3) { + m_regs.back()[ dst.vec_name4() ] = data4; + m_debug_trace_regs_modified.back()[ dst.vec_name4() ] = data4; + } + } + m_last_set_operand_value = data1; +} + +#define my_abs(a) (((a)<0)?(-a):(a)) + +#define MY_MAX_I(a,b) (a > b) ? a : b +#define MY_MAX_F(a,b) isNaN(a) ? b : isNaN(b) ? a : (a > b) ? a : b + +#define MY_MIN_I(a,b) (a < b) ? a : b +#define MY_MIN_F(a,b) isNaN(a) ? b : isNaN(b) ? a : (a < b) ? a : b + +#define MY_INC_I(a,b) (a >= b) ? 0 : a+1 +#define MY_DEC_I(a,b) ((a == 0) || (a > b)) ? b : a-1 + +#define MY_CAS_I(a,b,c) (a == b) ? c : a + +#define MY_EXCH(a,b) b + +void abs_impl( const ptx_instruction *pI, ptx_thread_info *thread ) +{ + ptx_reg_t a, d; + const operand_info &dst = pI->dst(); + const operand_info &src1 = pI->src1(); + a = thread->get_operand_value(src1); + + unsigned i_type = pI->get_type(); + switch ( i_type ) { + case S16_TYPE: d.s16 = my_abs(a.s16); break; + case S32_TYPE: d.s32 = my_abs(a.s32); break; + case S64_TYPE: d.s64 = my_abs(a.s64); break; + case F32_TYPE: d.f32 = my_abs(a.f32); break; + case F64_TYPE: d.f64 = my_abs(a.f64); break; + default: + printf("Execution error: type mismatch with instruction\n"); + assert(0); + break; + } + + thread->set_operand_value(dst,d); +} + +void add_impl( const ptx_instruction *pI, ptx_thread_info *thread ) +{ + ptx_reg_t src1_data, src2_data, data; + + const operand_info &dst = pI->dst(); //get operand info of sources and destination + const operand_info &src1 = pI->src1(); //use them to determine that they are of type 'register' + const operand_info &src2 = pI->src2(); + src1_data = thread->get_operand_value(src1); //get values from the operand infos + src2_data = thread->get_operand_value(src2); + + unsigned rounding_mode = pI->rounding_mode(); + int orig_rm = fegetround(); + switch ( rounding_mode ) { + case RN_OPTION: break; + case RZ_OPTION: fesetround( FE_TOWARDZERO ); break; + default: assert(0); break; + } + + unsigned to_type = pI->get_type(); + + switch ( to_type ) { + case S8_TYPE: + case S16_TYPE: + case S32_TYPE: + case S64_TYPE: + case U8_TYPE: + case U16_TYPE: + case U32_TYPE: + case U64_TYPE: + data.s64 = src1_data.s64 + src2_data.s64; break; + case F16_TYPE: assert(0); break; + case F32_TYPE: data.f32 = src1_data.f32 + src2_data.f32; break; + case F64_TYPE: data.f64 = src1_data.f64 + src2_data.f64; break; + default: assert(0); break; + } + fesetround( orig_rm ); + thread->set_operand_value(dst,data); +} + +void addc_impl( const ptx_instruction *pI, ptx_thread_info *thread ) { inst_not_implemented(pI); } + +void and_impl( const ptx_instruction *pI, ptx_thread_info *thread ) +{ + ptx_reg_t src1_data, src2_data, data; + + const operand_info &dst = pI->dst(); + const operand_info &src1 = pI->src1(); + const operand_info &src2 = pI->src2(); + + src1_data = thread->get_operand_value(src1); + src2_data = thread->get_operand_value(src2); + + data.u64 = src1_data.u64 & src2_data.u64; + + thread->set_operand_value(dst,data); +} + +void atom_callback( void* ptx_inst, void* thd ) +{ + ptx_thread_info *thread = (ptx_thread_info*)thd; + ptx_instruction *pI = (ptx_instruction*)ptx_inst; + + // Check state space + assert( pI->get_space()==GLOBAL_DIRECTIVE ); + + // "Decode" the output type + unsigned to_type = pI->get_type(); + size_t size; + int t; + type_decode(to_type, size, t); + + // Set up operand variables + ptx_reg_t data, // d + src1_data, // a + src2_data, // b + op_result; // temp variable to hold operation result + + bool data_ready = false; + + // Get operand info of sources and destination + const operand_info &dst = pI->dst(); // d + const operand_info &src1 = pI->src1(); // a + const operand_info &src2 = pI->src2(); // b + + // Get operand values + src1_data = thread->get_operand_value(src1); // a + src2_data = thread->get_operand_value(src2); // b + + // Copy value pointed to in operand 'a' into register 'd' + // (i.e. copy src1_data to dst) + g_global_mem->read(src1_data.u32,size/8,&data.s64); + thread->set_operand_value(dst, data); // Write value into register 'd' + + // Get the atomic operation to be performed + unsigned m_atomic_spec = pI->get_atomic(); + + switch ( m_atomic_spec ) { + // AND + case ATOMIC_AND: + { + + switch ( to_type ) { + case B32_TYPE: + case U32_TYPE: + op_result.u32 = data.u32 & src2_data.u32; + data_ready = true; + break; + case S32_TYPE: + op_result.s32 = data.s32 & src2_data.s32; + data_ready = true; + break; + default: + printf("Execution error: type mismatch (%x) with instruction\natom.AND only accepts b32\n", to_type); + assert(0); + break; + } + + break; + } + // OR + case ATOMIC_OR: + { + + switch ( to_type ) { + case B32_TYPE: + case U32_TYPE: + op_result.u32 = data.u32 | src2_data.u32; + data_ready = true; + break; + case S32_TYPE: + op_result.s32 = data.s32 | src2_data.s32; + data_ready = true; + break; + default: + printf("Execution error: type mismatch (%x) with instruction\natom.OR only accepts b32\n", to_type); + assert(0); + break; + } + + break; + } + // XOR + case ATOMIC_XOR: + { + + switch ( to_type ) { + case B32_TYPE: + case U32_TYPE: + op_result.u32 = data.u32 ^ src2_data.u32; + data_ready = true; + break; + case S32_TYPE: + op_result.s32 = data.s32 ^ src2_data.s32; + data_ready = true; + break; + default: + printf("Execution error: type mismatch (%x) with instruction\natom.XOR only accepts b32\n", to_type); + assert(0); + break; + } + + break; + } + // CAS + case ATOMIC_CAS: + { + + ptx_reg_t src3_data; + const operand_info &src3 = pI->src3(); + src3_data = thread->get_operand_value(src3); + + switch ( to_type ) { + case B32_TYPE: + case U32_TYPE: + op_result.u32 = MY_CAS_I(data.u32, src2_data.u32, src3_data.u32); + data_ready = true; + break; + case S32_TYPE: + op_result.s32 = MY_CAS_I(data.s32, src2_data.s32, src3_data.s32); + data_ready = true; + break; + default: + printf("Execution error: type mismatch (%x) with instruction\natom.CAS only accepts b32\n", to_type); + assert(0); + break; + } + + break; + } + // EXCH + case ATOMIC_EXCH: + { + switch ( to_type ) { + case B32_TYPE: + case U32_TYPE: + op_result.u32 = MY_EXCH(data.u32, src2_data.u32); + data_ready = true; + break; + case S32_TYPE: + op_result.s32 = MY_EXCH(data.s32, src2_data.s32); + data_ready = true; + break; + default: + printf("Execution error: type mismatch (%x) with instruction\natom.EXCH only accepts b32\n", to_type); + assert(0); + break; + } + + break; + } + // ADD + case ATOMIC_ADD: + { + + switch ( to_type ) { + case U32_TYPE: + op_result.u32 = data.u32 + src2_data.u32; + data_ready = true; + break; + case S32_TYPE: + op_result.s32 = data.s32 + src2_data.s32; + data_ready = true; + break; + default: + printf("Execution error: type mismatch with instruction\natom.ADD only accepts u32 and s32\n"); + assert(0); + break; + } + + break; + } + // INC + case ATOMIC_INC: + { + switch ( to_type ) { + case U32_TYPE: + op_result.u32 = MY_INC_I(data.u32, src2_data.u32); + data_ready = true; + break; + default: + printf("Execution error: type mismatch with instruction\natom.INC only accepts u32 and s32\n"); + assert(0); + break; + } + + break; + } + // DEC + case ATOMIC_DEC: + { + switch ( to_type ) { + case U32_TYPE: + op_result.u32 = MY_DEC_I(data.u32, src2_data.u32); + data_ready = true; + break; + default: + printf("Execution error: type mismatch with instruction\natom.DEC only accepts u32 and s32\n"); + assert(0); + break; + } + + break; + } + // MIN + case ATOMIC_MIN: + { + switch ( to_type ) { + case U32_TYPE: + op_result.u32 = MY_MIN_I(data.u32, src2_data.u32); + data_ready = true; + break; + case S32_TYPE: + op_result.s32 = MY_MIN_I(data.s32, src2_data.s32); + data_ready = true; + break; + default: + printf("Execution error: type mismatch with instruction\natom.MIN only accepts u32 and s32\n"); + assert(0); + break; + } + + break; + } + // MAX + case ATOMIC_MAX: + { + switch ( to_type ) { + case U32_TYPE: + op_result.u32 = MY_MAX_I(data.u32, src2_data.u32); + data_ready = true; + break; + case S32_TYPE: + op_result.s32 = MY_MAX_I(data.s32, src2_data.s32); + data_ready = true; + break; + default: + printf("Execution error: type mismatch with instruction\natom.MAX only accepts u32 and s32\n"); + assert(0); + break; + } + + break; + } + // DEFAULT + default: + { + assert(0); + break; + } + } + + // Write operation result into global memory + // (i.e. copy src1_data to dst) + g_global_mem->write(src1_data.u32,size/8,&op_result.s64); +} + +// atom_impl will now result in a callback being called in mem_ctrl_pop (gpu-sim.c) +void atom_impl( const ptx_instruction *pI, ptx_thread_info *thread ) +{ + // SYNTAX + // atom.space.operation.type d, a, b[, c]; (now read in callback) + + // Check state space + assert( pI->get_space()==GLOBAL_DIRECTIVE ); + + // get the memory address + const operand_info &src1 = pI->src1(); + ptx_reg_t src1_data = thread->get_operand_value(src1); + + unsigned space = pI->get_space(); + + thread->m_last_effective_address = src1_data.u32; + thread->m_last_memory_space = space; + thread->m_last_dram_callback.function = atom_callback; + thread->m_last_dram_callback.instruction = (void*)pI; +} + +void bar_sync_impl( const ptx_instruction *pI, ptx_thread_info *thread ) +{ + const operand_info &dst = pI->dst(); + ptx_reg_t b = thread->get_operand_value(dst); + assert( b.u32 == 0 ); // not clear what should happen if this is not zero +} + +void bra_impl( const ptx_instruction *pI, ptx_thread_info *thread ) +{ + const operand_info &target = pI->dst(); + ptx_reg_t target_pc = thread->get_operand_value(target); + + thread->m_branch_taken = true; + thread->set_npc(target_pc); +} + +void brkpt_impl( const ptx_instruction *pI, ptx_thread_info *thread ) { inst_not_implemented(pI); } + +extern int gpgpu_simd_model; +#define POST_DOMINATOR 1 /* must match enum value in shader.h */ +void get_pdom_stack_top_info( unsigned sid, unsigned tid, unsigned *npc, unsigned *rpc ); + +void call_impl( const ptx_instruction *pI, ptx_thread_info *thread ) +{ + static unsigned call_uid_next = 1; + + const operand_info &target = pI->func_addr(); + assert( target.is_function_address() ); + const symbol *func_addr = target.get_symbol(); + const function_info *target_func = func_addr->get_pc(); + + // check that number of args and return match function requirements + if( pI->has_return() ^ target_func->has_return() ) { + printf("GPGPU-Sim PTX: Execution error - mismatch in number of return values between\n" + " call instruction and function declaration\n"); + abort(); + } + unsigned n_return = target_func->has_return(); + unsigned n_args = target_func->num_args(); + unsigned n_operands = pI->get_num_operands(); + + if( n_operands != (n_return+1+n_args) ) { + printf("GPGPU-Sim PTX: Execution error - mismatch in number of arguements between\n" + " call instruction and function declaration\n"); + abort(); + } + + // read source arguements into register specified in declaration of function + std::list< std::pair<const symbol*, ptx_reg_t> > arg_values; + for( unsigned arg=0; arg < n_args; arg ++ ) { + const operand_info &op_info = pI->operand_lookup(n_return+1+arg); + if( !op_info.is_reg() ) { + printf("GPGPU-Sim PTX: Execution error - no support for non-register opernands for call/return\n"); + abort(); + } + const symbol *dst_reg = target_func->get_arg(arg); + + ptx_reg_t src_value = thread->get_operand_value(op_info); + arg_values.push_back( std::make_pair(dst_reg,src_value) ); + } + + // note register for corresponding return instruction to place result into + const symbol *return_var_src = NULL; + const symbol *return_var_dst = NULL; + if( target_func->has_return() ) { + if( !pI->dst().is_reg() ) { + printf("GPGPU-Sim PTX: Execution error - no support for non-register dst reg for call\n"); + abort(); + } + return_var_dst = pI->dst().get_symbol(); + return_var_src = target_func->get_return_var(); + } + + unsigned sid = thread->get_hw_sid(); + unsigned tid = thread->get_hw_tid(); + unsigned callee_pc=0, callee_rpc=0; + if( gpgpu_simd_model == POST_DOMINATOR ) { + get_pdom_stack_top_info(sid,tid,&callee_pc,&callee_rpc); + assert( callee_pc == thread->get_pc() ); + } + + thread->callstack_push(callee_pc+1,callee_rpc,return_var_src,return_var_dst,call_uid_next++); + + std::list< std::pair<const symbol*, ptx_reg_t> >::iterator a; + for( a=arg_values.begin(); a != arg_values.end(); a++ ) { + const symbol *dst_reg = a->first; + ptx_reg_t value = a->second; + thread->set_operand_value(dst_reg,value); + } + + thread->set_npc(target_func); +} + +void cnot_impl( const ptx_instruction *pI, ptx_thread_info *thread ) +{ + ptx_reg_t a, b, d; + const operand_info &dst = pI->dst(); + const operand_info &src1 = pI->src1(); + a = thread->get_operand_value(src1); + + unsigned i_type = pI->get_type(); + switch ( i_type ) { + case PRED_TYPE: d.pred = (a.pred == 0)?1:0; break; + case B16_TYPE: d.u16 = (a.u16 == 0)?1:0; break; + case B32_TYPE: d.u32 = (a.u32 == 0)?1:0; break; + case B64_TYPE: d.u64 = (a.u64 == 0)?1:0; break; + default: + printf("Execution error: type mismatch with instruction\n"); + assert(0); // TODO: add more typechecking like this + break; + } + + thread->set_operand_value(dst,d); +} + +void cos_impl( const ptx_instruction *pI, ptx_thread_info *thread ) +{ + ptx_reg_t a, d; + const operand_info &dst = pI->dst(); + const operand_info &src1 = pI->src1(); + a = thread->get_operand_value(src1); + + unsigned i_type = pI->get_type(); + switch ( i_type ) { + case F32_TYPE: + d.f32 = cos(a.f32); + break; + default: + printf("Execution error: type mismatch with instruction\n"); + assert(0); + break; + } + + thread->set_operand_value(dst,d); +} + +ptx_reg_t chop( ptx_reg_t x, unsigned from_width, unsigned to_width, int to_sign, int rounding_mode, int saturation_mode ) +{ + switch ( to_width ) { + case 8: x.mask_and(0,0xFF); break; + case 16: x.mask_and(0,0xFFFF); break; + case 32: x.mask_and(0,0xFFFFFFFF); break; + case 64: break; + default: assert(0); + } + return x; +} + +ptx_reg_t sext( ptx_reg_t x, unsigned from_width, unsigned to_width, int to_sign, int rounding_mode, int saturation_mode ) +{ + x=chop(x,0,from_width,0,rounding_mode,saturation_mode); + switch ( from_width ) { + case 8: if ( x.get_bit(7) ) x.mask_or(0xFFFFFFFF,0xFFFFFF00);break; + case 16:if ( x.get_bit(15) ) x.mask_or(0xFFFFFFFF,0xFFFF0000);break; + case 32: if ( x.get_bit(31) ) x.mask_or(0xFFFFFFFF,0x00000000);break; + case 64: break; + default: assert(0); + } + return x; +} + +ptx_reg_t zext( ptx_reg_t x, unsigned from_width, unsigned to_width, int to_sign, int rounding_mode, int saturation_mode ) +{ + return chop(x,0,from_width,0,rounding_mode,saturation_mode); +} + +int saturatei(int a, int max, int min) +{ + if (a > max) a = max; + else if (a < min) a = min; + return a; +} + +unsigned int saturatei(unsigned int a, unsigned int max) +{ + if (a > max) a = max; + return a; +} + +ptx_reg_t f2x( ptx_reg_t x, unsigned from_width, unsigned to_width, int to_sign, int rounding_mode, int saturation_mode ) +{ + assert( from_width == 32); + + enum cuda_math::cudaRoundMode mode = cuda_math::cudaRoundZero; + switch (rounding_mode) { + case RZI_OPTION: mode = cuda_math::cudaRoundZero; break; + case RNI_OPTION: mode = cuda_math::cudaRoundNearest; break; + case RMI_OPTION: mode = cuda_math::cudaRoundMinInf; break; + case RPI_OPTION: mode = cuda_math::cudaRoundPosInf; break; + default: break; + } + + ptx_reg_t y; + if ( to_sign == 1 ) { // convert to 64-bit number first? + int tmp = cuda_math::__internal_float2int(x.f32, mode); + if ((x.u32 & 0x7f800000) == 0) + tmp = 0; // round denorm. FP to 0 + if (saturation_mode && to_width < 32) { + tmp = saturatei(tmp, (1<<to_width) - 1, -(1<<to_width)); + } + switch ( to_width ) { + case 8: y.s8 = (char)tmp; break; + case 16: y.s16 = (short)tmp; break; + case 32: y.s32 = (int)tmp; break; + case 64: y.s64 = (long long)tmp; break; + default: assert(0); break; + } + } else if ( to_sign == 0 ) { + unsigned int tmp = cuda_math::__internal_float2uint(x.f32, mode); + if ((x.u32 & 0x7f800000) == 0) + tmp = 0; // round denorm. FP to 0 + if (saturation_mode && to_width < 32) { + tmp = saturatei(tmp, (1<<to_width) - 1); + } + switch ( to_width ) { + case 8: y.u8 = (unsigned char)tmp; break; + case 16: y.u16 = (unsigned short)tmp; break; + case 32: y.u32 = (unsigned int)tmp; break; + case 64: y.u64 = (unsigned long long)tmp; break; + default: assert(0); break; + } + } else { + switch ( to_width ) { + case 16: assert(0); break; + case 32: assert(0); break; // handled by f2f + case 64: + y.f64 = x.f32; + break; + default: assert(0); break; + } + } + return y; +} + +double saturated2i (double a, double max, double min) { + if (a > max) a = max; + else if (a < min) a = min; + return a; +} + +ptx_reg_t d2x( ptx_reg_t x, unsigned from_width, unsigned to_width, int to_sign, int rounding_mode, int saturation_mode ) +{ + assert( from_width == 64); + + double tmp; + switch (rounding_mode) { + case RZI_OPTION: tmp = trunc(x.f64); break; + case RNI_OPTION: tmp = nearbyint(x.f64); break; + case RMI_OPTION: tmp = floor(x.f64); break; + case RPI_OPTION: tmp = ceil(x.f64); break; + default: tmp = x.f64; break; + } + + ptx_reg_t y; + if ( to_sign == 1 ) { + tmp = saturated2i(tmp, ((1<<(to_width - 1)) - 1), (1<<(to_width - 1)) ); + switch ( to_width ) { + case 8: y.s8 = (char)tmp; break; + case 16: y.s16 = (short)tmp; break; + case 32: y.s32 = (int)tmp; break; + case 64: y.s64 = (long long)tmp; break; + default: assert(0); break; + } + } else if ( to_sign == 0 ) { + tmp = saturated2i(tmp, ((1<<(to_width - 1)) - 1), 0); + switch ( to_width ) { + case 8: y.u8 = (unsigned char)tmp; break; + case 16: y.u16 = (unsigned short)tmp; break; + case 32: y.u32 = (unsigned int)tmp; break; + case 64: y.u64 = (unsigned long long)tmp; break; + default: assert(0); break; + } + } else { + switch ( to_width ) { + case 16: assert(0); break; + case 32: + y.f32 = x.f64; + break; + case 64: + y.f64 = x.f64; // should be handled by d2d + break; + default: assert(0); break; + } + } + return y; +} + +ptx_reg_t s2f( ptx_reg_t x, unsigned from_width, unsigned to_width, int to_sign, int rounding_mode, int saturation_mode ) +{ + ptx_reg_t y; + + if (from_width < 64) { // 32-bit conversion + y = sext(x,from_width,32,0,rounding_mode,saturation_mode); + + switch ( to_width ) { + case 16: assert(0); break; + case 32: + switch (rounding_mode) { + case RZ_OPTION: y.f32 = cuda_math::__int2float_rz(y.s32); break; + case RN_OPTION: y.f32 = cuda_math::__int2float_rn(y.s32); break; + case RM_OPTION: y.f32 = cuda_math::__int2float_rd(y.s32); break; + case RP_OPTION: y.f32 = cuda_math::__int2float_ru(y.s32); break; + default: break; + } + break; + case 64: y.f64 = y.s32; break; // no rounding needed + default: assert(0); break; + } + } else { + switch ( to_width ) { + case 16: assert(0); break; + case 32: + switch (rounding_mode) { + case RZ_OPTION: y.f32 = cuda_math::__ll2float_rn(y.s64); break; + case RN_OPTION: y.f32 = cuda_math::__ll2float_rn(y.s64); break; + case RM_OPTION: y.f32 = cuda_math::__ll2float_rn(y.s64); break; + case RP_OPTION: y.f32 = cuda_math::__ll2float_rn(y.s64); break; + default: break; + } + break; + case 64: y.f64 = y.s64; break; // no internal implementation found + default: assert(0); break; + } + } + + // saturating an integer to 1 or 0? + return y; +} + +ptx_reg_t u2f( ptx_reg_t x, unsigned from_width, unsigned to_width, int to_sign, int rounding_mode, int saturation_mode ) +{ + ptx_reg_t y; + + if (from_width < 64) { // 32-bit conversion + y = zext(x,from_width,32,0,rounding_mode,saturation_mode); + + switch ( to_width ) { + case 16: assert(0); break; + case 32: + switch (rounding_mode) { + case RZ_OPTION: y.f32 = cuda_math::__uint2float_rz(y.u32); break; + case RN_OPTION: y.f32 = cuda_math::__uint2float_rn(y.u32); break; + case RM_OPTION: y.f32 = cuda_math::__uint2float_rd(y.u32); break; + case RP_OPTION: y.f32 = cuda_math::__uint2float_ru(y.u32); break; + default: break; + } + break; + case 64: y.f64 = y.u32; break; // no rounding needed + default: assert(0); break; + } + } else { + switch ( to_width ) { + case 16: assert(0); break; + case 32: + switch (rounding_mode) { + case RZ_OPTION: y.f32 = cuda_math::__ull2float_rn(y.u64); break; + case RN_OPTION: y.f32 = cuda_math::__ull2float_rn(y.u64); break; + case RM_OPTION: y.f32 = cuda_math::__ull2float_rn(y.u64); break; + case RP_OPTION: y.f32 = cuda_math::__ull2float_rn(y.u64); break; + default: break; + } + break; + case 64: y.f64 = y.u64; break; // no internal implementation found + default: assert(0); break; + } + } + + // saturating an integer to 1 or 0? + return y; +} + +ptx_reg_t f2f( ptx_reg_t x, unsigned from_width, unsigned to_width, int to_sign, int rounding_mode, int saturation_mode ) +{ + ptx_reg_t y; + switch ( rounding_mode ) { + case RZI_OPTION: + y.f32 = truncf(x.f32); + break; + case RNI_OPTION: + y.f32 = cuda_math::__internal_nearbyintf(x.f32); + break; + case RMI_OPTION: + if ((x.u32 & 0x7f800000) == 0) { + y.u32 = x.u32 & 0x80000000; // round denorm. FP to 0, keeping sign + } else { + y.f32 = floorf(x.f32); + } + break; + case RPI_OPTION: + if ((x.u32 & 0x7f800000) == 0) { + y.u32 = x.u32 & 0x80000000; // round denorm. FP to 0, keeping sign + } else { + y.f32 = ceilf(x.f32); + } + break; + default: + if ((x.u32 & 0x7f800000) == 0) { + y.u32 = x.u32 & 0x80000000; // round denorm. FP to 0, keeping sign + } else { + y.f32 = x.f32; + } + break; + } + if (cuda_math::__cuda___isnanf(y.f32)) { + y.u32 = 0x7fffffff; + } else if (saturation_mode) { + y.f32 = cuda_math::__saturatef(y.f32); + } + + return y; +} + +ptx_reg_t d2d( ptx_reg_t x, unsigned from_width, unsigned to_width, int to_sign, int rounding_mode, int saturation_mode ) +{ + ptx_reg_t y; + switch ( rounding_mode ) { + case RZI_OPTION: + y.f64 = trunc(x.f64); + break; + case RNI_OPTION: + y.f64 = cuda_math::__internal_nearbyintf(x.f64); + break; + case RMI_OPTION: + y.f64 = floor(x.f64); + break; + case RPI_OPTION: + y.f64 = ceil(x.f64); + break; + default: + y.f64 = x.f64; + break; + } + if (isnan(y.f64)) { + y.u64 = 0xfff8000000000000ull; + } else if (saturation_mode) { + y.f64 = cuda_math::__saturatef(y.f64); + } + return y; +} + +ptx_reg_t (*g_cvt_fn[11][11])( ptx_reg_t x, unsigned from_width, unsigned to_width, int to_sign, + int rounding_mode, int saturation_mode ) = { + { NULL, sext, sext, sext, NULL, sext, sext, sext, s2f, s2f, s2f}, + { chop, NULL, sext, sext, chop, NULL, sext, sext, s2f, s2f, s2f}, + { chop, chop, NULL, sext, chop, chop, NULL, sext, s2f, s2f, s2f}, + { chop, chop, chop, NULL, chop, chop, chop, NULL, s2f, s2f, s2f}, + { NULL, zext, zext, zext, NULL, zext, zext, zext, u2f, u2f, u2f}, + { chop, NULL, zext, zext, chop, NULL, zext, zext, u2f, u2f, u2f}, + { chop, chop, NULL, zext, chop, chop, NULL, zext, u2f, u2f, u2f}, + { chop, chop, chop, NULL, chop, chop, chop, NULL, u2f, u2f, u2f}, + { f2x , f2x , f2x , f2x , f2x , f2x , f2x , f2x , NULL,f2x, f2x}, + { f2x , f2x , f2x , f2x , f2x , f2x , f2x , f2x , f2x, f2f, f2x}, + { d2x , d2x , d2x , d2x , d2x , d2x , d2x , d2x , d2x, d2x, d2d} +}; + +unsigned type_decode( unsigned type, size_t &size, int &basic_type ) +{ + switch ( type ) { + case S8_TYPE: size=8; basic_type=1; return 0; + case S16_TYPE: size=16; basic_type=1; return 1; + case S32_TYPE: size=32; basic_type=1; return 2; + case S64_TYPE: size=64; basic_type=1; return 3; + case U8_TYPE: size=8; basic_type=0; return 4; + case U16_TYPE: size=16; basic_type=0; return 5; + case U32_TYPE: size=32; basic_type=0; return 6; + case U64_TYPE: size=64; basic_type=0; return 7; + case F16_TYPE: size=16; basic_type=-1; return 8; + case F32_TYPE: size=32; basic_type=-1; return 9; + case F64_TYPE: size=64; basic_type=-1; return 10; + case PRED_TYPE: size=1; basic_type=2; return 11; + case B8_TYPE: size=8; basic_type=0; return 12; + case B16_TYPE: size=16; basic_type=0; return 13; + case B32_TYPE: size=32; basic_type=0; return 14; + case B64_TYPE: size=64; basic_type=0; return 15; + default: + printf("ERROR ** type_decode() does not know about \"%s\"\n", g_ptx_token_decode[type].c_str() ); + assert(0); + return 0xDEADBEEF; + } +} + +void ptx_round(ptx_reg_t& data, int rounding_mode, int type) +{ + if (rounding_mode == RN_OPTION) { + return; + } + switch ( rounding_mode ) { + case RZI_OPTION: + switch ( type ) { + case S8_TYPE: + case S16_TYPE: + case S32_TYPE: + case S64_TYPE: + case U8_TYPE: + case U16_TYPE: + case U32_TYPE: + case U64_TYPE: + printf("Trying to round an integer??\n"); assert(0); break; + case F16_TYPE: assert(0); break; + case F32_TYPE: + data.f32 = truncf(data.f32); + break; + case F64_TYPE: + if (data.f64 < 0) data.f64 = ceil(data.f64); //negative + else data.f64 = floor(data.f64); //positive + break; + default: assert(0); break; + } + break; + case RNI_OPTION: + switch ( type ) { + case S8_TYPE: + case S16_TYPE: + case S32_TYPE: + case S64_TYPE: + case U8_TYPE: + case U16_TYPE: + case U32_TYPE: + case U64_TYPE: + printf("Trying to round an integer??\n"); assert(0); break; + case F16_TYPE: assert(0); break; + case F32_TYPE: data.f32 = cuda_math::__cuda_nearbyintf(data.f32); break; + case F64_TYPE: data.f64 = round(data.f64); break; + default: assert(0); break; + } + break; + case RMI_OPTION: + switch ( type ) { + case S8_TYPE: + case S16_TYPE: + case S32_TYPE: + case S64_TYPE: + case U8_TYPE: + case U16_TYPE: + case U32_TYPE: + case U64_TYPE: + printf("Trying to round an integer??\n"); assert(0); break; + case F16_TYPE: assert(0); break; + case F32_TYPE: + data.f32 = floorf(data.f32); + break; + case F64_TYPE: data.f64 = floor(data.f64); break; + default: assert(0); break; + } + break; + case RPI_OPTION: + switch ( type ) { + case S8_TYPE: + case S16_TYPE: + case S32_TYPE: + case S64_TYPE: + case U8_TYPE: + case U16_TYPE: + case U32_TYPE: + case U64_TYPE: + printf("Trying to round an integer??\n"); assert(0); break; + case F16_TYPE: assert(0); break; + case F32_TYPE: data.f32 = ceilf(data.f32); break; + case F64_TYPE: data.f64 = ceil(data.f64); break; + default: assert(0); break; + } + break; + default: break; + } + + if (type == F32_TYPE) { + if (cuda_math::__cuda___isnanf(data.f32)) { + data.u32 = 0x7fffffff; + } + } + if (type == F64_TYPE) { + if (isnan(data.f64)) { + data.u64 = 0xfff8000000000000ull; + } + } +} + +void ptx_saturate(ptx_reg_t& data, int saturation_mode, int type) +{ + if (!saturation_mode) { + return; + } + switch ( type ) { + case S8_TYPE: + case S16_TYPE: + case S32_TYPE: + case S64_TYPE: + case U8_TYPE: + case U16_TYPE: + case U32_TYPE: + case U64_TYPE: + printf("Trying to clamp an integer to 1??\n"); assert(0); break; + case F16_TYPE: assert(0); break; + case F32_TYPE: + if (data.f32 > 1.0f) data.f32 = 1.0f; //negative + if (data.f32 < 0.0f) data.f32 = 0.0f; //positive + break; + case F64_TYPE: + if (data.f64 > 1.0f) data.f64 = 1.0f; //negative + if (data.f64 < 0.0f) data.f64 = 0.0f; //positive + break; + default: assert(0); break; + } + +} + +void cvt_impl( const ptx_instruction *pI, ptx_thread_info *thread ) +{ + const operand_info &dst = pI->dst(); + const operand_info &src1 = pI->src1(); + unsigned to_type = pI->get_type(); + unsigned from_type = pI->get_type2(); + unsigned rounding_mode = pI->rounding_mode(); + unsigned saturation_mode = pI->saturation_mode(); + + if ( to_type == F16_TYPE || from_type == F16_TYPE ) + abort(); + + int to_sign, from_sign; + size_t from_width, to_width; + unsigned src_fmt = type_decode(from_type, from_width, from_sign); + unsigned dst_fmt = type_decode(to_type, to_width, to_sign); + + ptx_reg_t data = thread->get_operand_value(src1); + if ( g_cvt_fn[src_fmt][dst_fmt] != NULL ) { + ptx_reg_t result = g_cvt_fn[src_fmt][dst_fmt](data,from_width,to_width,to_sign, rounding_mode, saturation_mode); + data = result; + } + + thread->set_operand_value(dst,data); +} + +void div_impl( const ptx_instruction *pI, ptx_thread_info *thread ) +{ + ptx_reg_t data; + + const operand_info &dst = pI->dst(); + const operand_info &src1 = pI->src1(); + const operand_info &src2 = pI->src2(); + + ptx_reg_t src1_data = thread->get_operand_value(src1); + ptx_reg_t src2_data = thread->get_operand_value(src2); + + unsigned i_type = pI->get_type(); + + switch ( i_type ) { + case S8_TYPE: + case S16_TYPE: + case S32_TYPE: + case S64_TYPE: + data.s64 = src1_data.s64 / src2_data.s64; break; + case U8_TYPE: + case U16_TYPE: + case U32_TYPE: + case U64_TYPE: + case B8_TYPE: + case B16_TYPE: + case B32_TYPE: + case B64_TYPE: + data.u64 = src1_data.u64 / src2_data.u64; break; + case F16_TYPE: assert(0); break; + case F32_TYPE: data.f32 = src1_data.f32 / src2_data.f32; break; + case F64_TYPE: data.f64 = src1_data.f64 / src2_data.f64; break; + default: assert(0); break; + } + thread->set_operand_value(dst,data); +} + +void ex2_impl( const ptx_instruction *pI, ptx_thread_info *thread ) +{ + ptx_reg_t src1_data, src2_data, data; + const operand_info &dst = pI->dst(); + const operand_info &src1 = pI->src1(); + src1_data = thread->get_operand_value(src1); + + unsigned i_type = pI->get_type(); + switch ( i_type ) { + case F32_TYPE: + data.f32 = cuda_math::__powf(2.0, src1_data.f32); + break; + default: + printf("Execution error: type mismatch with instruction\n"); + assert(0); + break; + } + + thread->set_operand_value(dst,data); +} + +void exit_impl( const ptx_instruction *pI, ptx_thread_info *thread ) +{ + core_t *sc = thread->get_core(); + unsigned warp_id = thread->get_hw_wid(); + sc->warp_exit(warp_id); + + thread->m_cta_info->register_thread_exit(thread); + thread->set_done(); +} + +void ld_impl( const ptx_instruction *pI, ptx_thread_info *thread ) +{ + const operand_info &dst = pI->dst(); + const operand_info &src1 = pI->src1(); + ptx_reg_t src1_data = thread->get_operand_value(src1); + ptx_reg_t data; + unsigned space = pI->get_space(); + unsigned vector_spec = pI->get_vector(); + unsigned type = pI->get_type(); + memory_space *mem = NULL; + + switch ( space ) { + case GLOBAL_DIRECTIVE: mem = g_global_mem; break; + case LOCAL_DIRECTIVE: mem = thread->m_local_mem; break; + case TEX_DIRECTIVE: mem = g_tex_mem; break; + case SURF_DIRECTIVE: mem = g_surf_mem; break; + case PARAM_DIRECTIVE: mem = g_param_mem; break; + case SHARED_DIRECTIVE: mem = thread->m_shared_mem; break; + case CONST_DIRECTIVE: mem = g_global_mem; break; + default: + assert(0); + break; + } + + size_t size; + int t; + data.u64=0; + type_decode(type,size,t); + if (!vector_spec) { + mem->read(src1_data.u32,size/8,&data.s64); + if( type == S16_TYPE || type == S32_TYPE ) + sign_extend(data,size,dst); + thread->set_operand_value(dst,data); + } else { + ptx_reg_t data1, data2, data3, data4; + mem->read(src1_data.u32,size/8,&data1.s64); + mem->read(src1_data.u32+size/8,size/8,&data2.s64); + if (vector_spec != V2_TYPE) { //either V3 or V4 + mem->read(src1_data.u32+2*size/8,size/8,&data3.s64); + if (vector_spec != V3_TYPE) { //v4 + mem->read(src1_data.u32+3*size/8,size/8,&data4.s64); + thread->set_vector_operand_values(dst,data1,data2,data3,data4, 4); + } else //v3 + thread->set_vector_operand_values(dst,data1,data2,data3,data3,3); + } else //v2 + thread->set_vector_operand_values(dst,data1,data2,data2,data2,2); + } + thread->m_last_effective_address = src1_data.u32; + thread->m_last_memory_space = space; +} + +void lg2_impl( const ptx_instruction *pI, ptx_thread_info *thread ) +{ + ptx_reg_t a, d; + const operand_info &dst = pI->dst(); + const operand_info &src1 = pI->src1(); + a = thread->get_operand_value(src1); + + unsigned i_type = pI->get_type(); + switch ( i_type ) { + case F32_TYPE: + d.f32 = log(a.f32)/log(2); + break; + default: + printf("Execution error: type mismatch with instruction\n"); + assert(0); + break; + } + + thread->set_operand_value(dst,d); +} + +void mad24_impl( const ptx_instruction *pI, ptx_thread_info *thread ) +{ + const operand_info &dst = pI->dst(); + const operand_info &src1 = pI->src1(); + const operand_info &src2 = pI->src2(); + const operand_info &src3 = pI->src3(); + ptx_reg_t d, t; + ptx_reg_t a = thread->get_operand_value(src1); + ptx_reg_t b = thread->get_operand_value(src2); + ptx_reg_t c = thread->get_operand_value(src3); + + unsigned i_type = pI->get_type(); + unsigned sat_mode = pI->saturation_mode(); + + assert( !pI->is_wide() ); + + switch ( i_type ) { + case S32_TYPE: + t.s64 = a.s32 * b.s32; + if ( pI->is_hi() ) { + d.s64 = (t.s64>>16) + c.s32; + if ( sat_mode ) { + if ( d.s64 > (int)0x7FFFFFFF ) + d.s64 = (int)0x7FFFFFFF; + else if ( d.s64 < (int)0x80000000 ) + d.s64 = (int)0x80000000; + } + } else if ( pI->is_lo() ) d.s64 = t.s32 + c.s32; + else assert(0); + break; + case U32_TYPE: + t.u64 = a.u32 * b.u32; + if ( pI->is_hi() ) d.u64 = (t.u64>>16) + c.u32; + else if ( pI->is_lo() ) d.u64 = t.u32 + c.u32; + else assert(0); + break; + default: + assert(0); + break; + } + thread->set_operand_value(dst,d); +} + +void mad_impl( const ptx_instruction *pI, ptx_thread_info *thread ) +{ + const operand_info &dst = pI->dst(); + const operand_info &src1 = pI->src1(); + const operand_info &src2 = pI->src2(); + const operand_info &src3 = pI->src3(); + ptx_reg_t d, t; + ptx_reg_t a = thread->get_operand_value(src1); + ptx_reg_t b = thread->get_operand_value(src2); + ptx_reg_t c = thread->get_operand_value(src3); + + unsigned i_type = pI->get_type(); + unsigned rounding_mode = pI->rounding_mode(); + + switch ( i_type ) { + case S16_TYPE: + t.s32 = a.s16 * b.s16; + if ( pI->is_wide() ) d.s32 = t.s32 + c.s32; + else if ( pI->is_hi() ) d.s16 = (t.s32>>16) + c.s16; + else if ( pI->is_lo() ) d.s16 = t.s16 + c.s16; + else assert(0); + break; + case S32_TYPE: + t.s64 = a.s32 * b.s32; + if ( pI->is_wide() ) d.s64 = t.s64 + c.s64; + else if ( pI->is_hi() ) d.s32 = (t.s64>>32) + c.s32; + else if ( pI->is_lo() ) d.s32 = t.s32 + c.s32; + else assert(0); + break; + case S64_TYPE: + t.s64 = a.s64 * b.s64; + assert( !pI->is_wide() ); + assert( !pI->is_hi() ); + if ( pI->is_lo() ) d.s64 = t.s64 + c.s64; + else assert(0); + break; + case U16_TYPE: + t.u32 = a.u16 * b.u16; + if ( pI->is_wide() ) d.u32 = t.u32 + c.u32; + else if ( pI->is_hi() ) d.u16 = (t.u32>>16) + c.u16; + else if ( pI->is_lo() ) d.u16 = t.u16 + c.u16; + else assert(0); + break; + case U32_TYPE: + t.u64 = a.u32 * b.u32; + if ( pI->is_wide() ) d.u64 = t.u64 + c.u64; + else if ( pI->is_hi() ) d.u32 = (t.u64>>32) + c.u32; + else if ( pI->is_lo() ) d.u32 = t.u32 + c.u32; + else assert(0); + break; + case U64_TYPE: + t.u64 = a.u64 * b.u64; + assert( !pI->is_wide() ); + assert( !pI->is_hi() ); + if ( pI->is_lo() ) d.u64 = t.u64 + c.u64; + else assert(0); + break; + case F16_TYPE: + assert(0); + break; + case F32_TYPE: { + int orig_rm = fegetround(); + switch ( rounding_mode ) { + case RN_OPTION: break; + case RZ_OPTION: fesetround( FE_TOWARDZERO ); break; + default: assert(0); break; + } + d.f32 = a.f32 * b.f32 + c.f32; + if ( pI->saturation_mode() ) { + if ( d.f32 < 0 ) d.f32 = 0; + else if ( d.f32 > 1.0f ) d.f32 = 1.0f; + } + fesetround( orig_rm ); + break; + } + case F64_TYPE: { + int orig_rm = fegetround(); + switch ( rounding_mode ) { + case RN_OPTION: break; + case RZ_OPTION: fesetround( FE_TOWARDZERO ); break; + default: assert(0); break; + } + d.f64 = a.f64 * b.f64 + c.f64; + if ( pI->saturation_mode() ) { + if ( d.f64 < 0 ) d.f64 = 0; + else if ( d.f64 > 1.0f ) d.f64 = 1.0; + } + fesetround( orig_rm ); + break; + } + default: + assert(0); + break; + } + thread->set_operand_value(dst,d); +} + +bool isNaN(float x) +{ + return isnan(x); +} + +bool isNaN(double x) +{ + return isnan(x); +} + +void max_impl( const ptx_instruction *pI, ptx_thread_info *thread ) +{ + ptx_reg_t a, b, d; + const operand_info &dst = pI->dst(); + const operand_info &src1 = pI->src1(); + const operand_info &src2 = pI->src2(); + a = thread->get_operand_value(src1); + b = thread->get_operand_value(src2); + + unsigned i_type = pI->get_type(); + switch ( i_type ) { + case U16_TYPE: d.u16 = MY_MAX_I(a.u16,b.u16); break; + case U32_TYPE: d.u32 = MY_MAX_I(a.u32,b.u32); break; + case U64_TYPE: d.u64 = MY_MAX_I(a.u64,b.u64); break; + case S16_TYPE: d.s16 = MY_MAX_I(a.s16,b.s16); break; + case S32_TYPE: d.s32 = MY_MAX_I(a.s32,b.s32); break; + case S64_TYPE: d.s64 = MY_MAX_I(a.s64,b.s64); break; + case F32_TYPE: d.f32 = MY_MAX_F(a.f32,b.f32); break; + case F64_TYPE: d.f64 = MY_MAX_F(a.f64,b.f64); break; + default: + printf("Execution error: type mismatch with instruction\n"); + assert(0); + break; + } + + thread->set_operand_value(dst,d); +} + +void membar_impl( const ptx_instruction *pI, ptx_thread_info *thread ) +{ + /* TODO: add timing model support */ +} + +void min_impl( const ptx_instruction *pI, ptx_thread_info *thread ) +{ + ptx_reg_t a, b, d; + const operand_info &dst = pI->dst(); + const operand_info &src1 = pI->src1(); + const operand_info &src2 = pI->src2(); + a = thread->get_operand_value(src1); + b = thread->get_operand_value(src2); + + unsigned i_type = pI->get_type(); + switch ( i_type ) { + case U16_TYPE: d.u16 = MY_MIN_I(a.u16,b.u16); break; + case U32_TYPE: d.u32 = MY_MIN_I(a.u32,b.u32); break; + case U64_TYPE: d.u64 = MY_MIN_I(a.u64,b.u64); break; + case S16_TYPE: d.s16 = MY_MIN_I(a.s16,b.s16); break; + case S32_TYPE: d.s32 = MY_MIN_I(a.s32,b.s32); break; + case S64_TYPE: d.s64 = MY_MIN_I(a.s64,b.s64); break; + case F32_TYPE: d.f32 = MY_MIN_F(a.f32,b.f32); break; + case F64_TYPE: d.f64 = MY_MIN_F(a.f64,b.f64); break; + default: + printf("Execution error: type mismatch with instruction\n"); + assert(0); + break; + } + + thread->set_operand_value(dst,d); +} + +void mov_impl( const ptx_instruction *pI, ptx_thread_info *thread ) +{ + ptx_reg_t data; + + const operand_info &dst = pI->dst(); + const operand_info &src1 = pI->src1(); + + if( src1.is_vector() || dst.is_vector() ) { + // pack or unpack operation + unsigned nbits_to_move; + ptx_reg_t tmp_bits; + + switch( pI->get_type() ) { + case B16_TYPE: nbits_to_move = 16; break; + case B32_TYPE: nbits_to_move = 32; break; + case B64_TYPE: nbits_to_move = 64; break; + default: printf("Execution error: mov pack/unpack with unsupported type qualifier\n"); assert(0); break; + } + + if( src1.is_vector() ) { + unsigned nelem = src1.get_vect_nelem(); + ptx_reg_t v[4]; + thread->get_vector_operand_values(src1, v, nelem ); + + unsigned bits_per_src_elem = nbits_to_move / nelem; + for( unsigned i=0; i < nelem; i++ ) { + switch(bits_per_src_elem) { + case 8: tmp_bits.u64 |= ((unsigned long long)(v[i].u8) << (8*i)); break; + case 16: tmp_bits.u64 |= ((unsigned long long)(v[i].u16) << (16*i)); break; + case 32: tmp_bits.u64 |= ((unsigned long long)(v[i].u32) << (32*i)); break; + default: printf("Execution error: mov pack/unpack with unsupported source/dst size ratio (src)\n"); assert(0); break; + } + } + } else { + data = thread->get_operand_value(src1); + + switch( pI->get_type() ) { + case B16_TYPE: tmp_bits.u16 = data.u16; break; + case B32_TYPE: tmp_bits.u32 = data.u32; break; + case B64_TYPE: tmp_bits.u64 = data.u64; break; + default: assert(0); break; + } + } + + if( dst.is_vector() ) { + unsigned nelem = dst.get_vect_nelem(); + ptx_reg_t v[4]; + unsigned bits_per_dst_elem = nbits_to_move / nelem; + for( unsigned i=0; i < nelem; i++ ) { + switch(bits_per_dst_elem) { + case 8: v[i].u8 = tmp_bits.u64 & (((unsigned long long) 0xFF) << (8*i)); break; + case 16: v[i].u16 = tmp_bits.u64 & (((unsigned long long) 0xFFFF) << (16*i)); break; + case 32: v[i].u32 = tmp_bits.u64 & (((unsigned long long) 0xFFFFFFFF) << (32*i)); break; + default: + printf("Execution error: mov pack/unpack with unsupported source/dst size ratio (dst)\n"); + assert(0); + break; + } + } + thread->set_vector_operand_values(dst,v[0],v[1],v[2],v[3],nelem); + } else { + thread->set_operand_value(dst,tmp_bits); + } + } else { + data = thread->get_operand_value(src1); + thread->set_operand_value(dst,data); + } +} + +void mul24_impl( const ptx_instruction *pI, ptx_thread_info *thread ) +{ + ptx_reg_t src1_data, src2_data, data; + + const operand_info &dst = pI->dst(); + const operand_info &src1 = pI->src1(); + const operand_info &src2 = pI->src2(); + + src1_data = thread->get_operand_value(src1); + src2_data = thread->get_operand_value(src2); + src1_data.mask_and(0,0x00FFFFFF); + src2_data.mask_and(0,0x00FFFFFF); + + unsigned i_type = pI->get_type(); + + switch ( i_type ) { + case S32_TYPE: + if( src1_data.get_bit(23) ) + src1_data.mask_or(0xFFFFFFFF,0xFF000000); + if( src2_data.get_bit(23) ) + src2_data.mask_or(0xFFFFFFFF,0xFF000000); + data.s64 = src1_data.s64 * src2_data.s64; + break; + case U32_TYPE: + data.u64 = src1_data.u64 * src2_data.u64; + break; + default: + printf("GPGPU-Sim PTX: Execution error - type mismatch with instruction\n"); + assert(0); + break; + } + + if ( pI->is_hi() ) { + data.u64 = data.u64 >> 16; + data.mask_and(0,0xFFFFFFFF); + } else if (pI->is_lo()) { + data.mask_and(0,0xFFFFFFFF); + } + + thread->set_operand_value(dst,data); +} + +void mul_impl( const ptx_instruction *pI, ptx_thread_info *thread ) +{ + ptx_reg_t data; + + const operand_info &dst = pI->dst(); + const operand_info &src1 = pI->src1(); + const operand_info &src2 = pI->src2(); + ptx_reg_t d, t; + ptx_reg_t a = thread->get_operand_value(src1); + ptx_reg_t b = thread->get_operand_value(src2); + + unsigned i_type = pI->get_type(); + unsigned rounding_mode = pI->rounding_mode(); + + switch ( i_type ) { + case S16_TYPE: + t.s32 = ((int)a.s16) * ((int)b.s16); + if ( pI->is_wide() ) d.s32 = t.s32; + else if ( pI->is_hi() ) d.s16 = (t.s32>>16); + else if ( pI->is_lo() ) d.s16 = t.s16; + else assert(0); + break; + case S32_TYPE: + t.s64 = ((long long)a.s32) * ((long long)b.s32); + if ( pI->is_wide() ) d.s64 = t.s64; + else if ( pI->is_hi() ) d.s32 = (t.s64>>32); + else if ( pI->is_lo() ) d.s32 = t.s32; + else assert(0); + break; + case S64_TYPE: + t.s64 = a.s64 * b.s64; + assert( !pI->is_wide() ); + assert( !pI->is_hi() ); + if ( pI->is_lo() ) d.s64 = t.s64; + else assert(0); + break; + case U16_TYPE: + t.u32 = ((unsigned)a.u16) * ((unsigned)b.u16); + if ( pI->is_wide() ) d.u32 = t.u32; + else if ( pI->is_lo() ) d.u16 = t.u16; + else if ( pI->is_hi() ) d.u16 = (t.u32>>16); + else assert(0); + break; + case U32_TYPE: + t.u64 = ((unsigned long long)a.u32) * ((unsigned long long)b.u32); + if ( pI->is_wide() ) d.u64 = t.u64; + else if ( pI->is_lo() ) d.u32 = t.u32; + else if ( pI->is_hi() ) d.u32 = (t.u64>>32); + else assert(0); + break; + case U64_TYPE: + t.u64 = a.u64 * b.u64; + assert( !pI->is_wide() ); + assert( !pI->is_hi() ); + if ( pI->is_lo() ) d.u64 = t.u64; + else assert(0); + break; + case F16_TYPE: + assert(0); + break; + case F32_TYPE: { + int orig_rm = fegetround(); + switch ( rounding_mode ) { + case RN_OPTION: break; + case RZ_OPTION: fesetround( FE_TOWARDZERO ); break; + default: assert(0); break; + } + d.f32 = a.f32 * b.f32; + if ( pI->saturation_mode() ) { + if ( d.f32 < 0 ) d.f32 = 0; + else if ( d.f32 > 1.0f ) d.f32 = 1.0f; + } + fesetround( orig_rm ); + break; + } + case F64_TYPE: { + int orig_rm = fegetround(); + switch ( rounding_mode ) { + case RN_OPTION: break; + case RZ_OPTION: fesetround( FE_TOWARDZERO ); break; + default: assert(0); break; + } + d.f64 = a.f64 * b.f64; + if ( pI->saturation_mode() ) { + if ( d.f64 < 0 ) d.f64 = 0; + else if ( d.f64 > 1.0f ) d.f64 = 1.0; + } + fesetround( orig_rm ); + break; + } + default: + assert(0); + break; + } + thread->set_operand_value(dst,d); +} + +void neg_impl( const ptx_instruction *pI, ptx_thread_info *thread ) +{ + ptx_reg_t src1_data, src2_data, data; + + const operand_info &dst = pI->dst(); + const operand_info &src1 = pI->src1(); + + src1_data = thread->get_operand_value(src1); + + unsigned to_type = pI->get_type(); + switch ( to_type ) { + case S8_TYPE: + case S16_TYPE: + case S32_TYPE: + case S64_TYPE: + data.s64 = 0 - src1_data.s64; break; // seems buggy, but not (just ignore higher bits) + case U8_TYPE: + case U16_TYPE: + case U32_TYPE: + case U64_TYPE: + assert(0); break; + case F16_TYPE: assert(0); break; + case F32_TYPE: data.f32 = 0.0f - src1_data.f32; break; + case F64_TYPE: data.f64 = 0.0f - src1_data.f64; break; + default: assert(0); break; + } + + thread->set_operand_value(dst,data); +} + +void not_impl( const ptx_instruction *pI, ptx_thread_info *thread ) +{ + ptx_reg_t a, b, d; + const operand_info &dst = pI->dst(); + const operand_info &src1 = pI->src1(); + a = thread->get_operand_value(src1); + + unsigned i_type = pI->get_type(); + switch ( i_type ) { + case PRED_TYPE: d.pred = ~a.pred; break; + case B16_TYPE: d.u16 = ~a.u16; break; + case B32_TYPE: d.u32 = ~a.u32; break; + case B64_TYPE: d.u64 = ~a.u64; break; + default: + printf("Execution error: type mismatch with instruction\n"); + assert(0); + break; + } + + thread->set_operand_value(dst,d); +} + +void or_impl( const ptx_instruction *pI, ptx_thread_info *thread ) +{ + ptx_reg_t src1_data, src2_data, data; + const operand_info &dst = pI->dst(); + const operand_info &src1 = pI->src1(); + const operand_info &src2 = pI->src2(); + src1_data = thread->get_operand_value(src1); + src2_data = thread->get_operand_value(src2); + + data.u64 = src1_data.u64 | src2_data.u64; + + thread->set_operand_value(dst,data); +} + +void rcp_impl( const ptx_instruction *pI, ptx_thread_info *thread ) +{ + ptx_reg_t src1_data, src2_data, data; + const operand_info &dst = pI->dst(); + const operand_info &src1 = pI->src1(); + src1_data = thread->get_operand_value(src1); + + unsigned i_type = pI->get_type(); + switch ( i_type ) { + case F32_TYPE: + data.f32 = 1.0f / src1_data.f32; + break; + case F64_TYPE: + data.f64 = 1.0f / src1_data.f64; + break; + default: + printf("Execution error: type mismatch with instruction\n"); + assert(0); + break; + } + + thread->set_operand_value(dst,data); +} + +void red_impl( const ptx_instruction *pI, ptx_thread_info *thread ) { inst_not_implemented(pI); } + +void rem_impl( const ptx_instruction *pI, ptx_thread_info *thread ) +{ + ptx_reg_t src1_data, src2_data, data; + + const operand_info &dst = pI->dst(); + const operand_info &src1 = pI->src1(); + const operand_info &src2 = pI->src2(); + + src1_data = thread->get_operand_value(src1); + src2_data = thread->get_operand_value(src2); + + data.u64 = src1_data.u64 % src2_data.u64; + + thread->set_operand_value(dst,data); +} + +void ret_impl( const ptx_instruction *pI, ptx_thread_info *thread ) +{ + bool empty = thread->callstack_pop(); + if( empty ) { + core_t *sc = thread->get_core(); + unsigned warp_id = thread->get_hw_wid(); + sc->warp_exit(warp_id); + thread->m_cta_info->register_thread_exit(thread); + thread->set_done(); + } +} + +void rsqrt_impl( const ptx_instruction *pI, ptx_thread_info *thread ) +{ + ptx_reg_t a, d; + const operand_info &dst = pI->dst(); + const operand_info &src1 = pI->src1(); + a = thread->get_operand_value(src1); + + unsigned i_type = pI->get_type(); + switch ( i_type ) { + case F32_TYPE: + if ( a.f32 < 0 ) { + d.u64 = 0; + d.u64 = 0x7fc00000; // NaN + } else if ( a.f32 == 0 ) { + d.u64 = 0; + d.u32 = 0x7f800000; // Inf + } else + d.f32 = cuda_math::__internal_accurate_fdividef(1.0f, sqrtf(a.f32)); + break; + case F64_TYPE: + if ( a.f32 < 0 ) { + d.u64 = 0; + d.u32 = 0x7fc00000; // NaN + float x = d.f32; + d.f64 = (double)x; + } else if ( a.f32 == 0 ) { + d.u64 = 0; + d.u32 = 0x7f800000; // Inf + float x = d.f32; + d.f64 = (double)x; + } else + d.f64 = 1.0 / sqrt(a.f64); + break; + default: + printf("Execution error: type mismatch with instruction\n"); + assert(0); + break; + } + + thread->set_operand_value(dst,d); +} + +#define SAD(d,a,b,c) d = c + ((a<b) ? (b-a) : (a-b)) + +void sad_impl( const ptx_instruction *pI, ptx_thread_info *thread ) +{ + ptx_reg_t a, b, c, d; + const operand_info &dst = pI->dst(); + const operand_info &src1 = pI->src1(); + const operand_info &src2 = pI->src1(); + const operand_info &src3 = pI->src1(); + a = thread->get_operand_value(src1); + b = thread->get_operand_value(src2); + c = thread->get_operand_value(src3); + + unsigned i_type = pI->get_type(); + switch ( i_type ) { + case U16_TYPE: SAD(d.u16,a.u16,b.u16,c.u16); break; + case U32_TYPE: SAD(d.u32,a.u32,b.u32,c.u32); break; + case U64_TYPE: SAD(d.u64,a.u64,b.u64,c.u64); break; + case S16_TYPE: SAD(d.s16,a.s16,b.s16,c.s16); break; + case S32_TYPE: SAD(d.s32,a.s32,b.s32,c.s32); break; + case S64_TYPE: SAD(d.s64,a.s64,b.s64,c.s64); break; + case F32_TYPE: SAD(d.f32,a.f32,b.f32,c.f32); break; + case F64_TYPE: SAD(d.f64,a.f64,b.f64,c.f64); break; + default: + printf("Execution error: type mismatch with instruction\n"); + assert(0); + break; + } + + thread->set_operand_value(dst,d); +} + +void selp_impl( const ptx_instruction *pI, ptx_thread_info *thread ) +{ + const operand_info &dst = pI->dst(); + const operand_info &src1 = pI->src1(); + const operand_info &src2 = pI->src2(); + const operand_info &src3 = pI->src3(); + + ptx_reg_t a, b, c, d; + + a = thread->get_operand_value(src1); + b = thread->get_operand_value(src2); + c = thread->get_operand_value(src3); + + d = (c.pred)?a:b; + + thread->set_operand_value(dst,d); +} + +bool isFloat(int type) +{ + switch ( type ) { + case F16_TYPE: + case F32_TYPE: + case F64_TYPE: + return true; + default: + return false; + } +} + +bool CmpOp( int type, ptx_reg_t a, ptx_reg_t b, unsigned cmpop ) +{ + bool t = false; + + switch ( type ) { + case B16_TYPE: + switch (cmpop) { + case EQ_OPTION: t = (a.u16 == b.u16); break; + case NE_OPTION: t = (a.u16 != b.u16); break; + default: + assert(0); + } + + case B32_TYPE: + switch (cmpop) { + case EQ_OPTION: t = (a.u32 == b.u32); break; + case NE_OPTION: t = (a.u32 != b.u32); break; + default: + assert(0); + } + case B64_TYPE: + switch (cmpop) { + case EQ_OPTION: t = (a.u64 == b.u64); break; + case NE_OPTION: t = (a.u64 != b.u64); break; + default: + assert(0); + } + break; + case S8_TYPE: + case S16_TYPE: + switch (cmpop) { + case EQ_OPTION: t = (a.s16 == b.s16); break; + case NE_OPTION: t = (a.s16 != b.s16); break; + case LT_OPTION: t = (a.s16 < b.s16); break; + case LE_OPTION: t = (a.s16 <= b.s16); break; + case GT_OPTION: t = (a.s16 > b.s16); break; + case GE_OPTION: t = (a.s16 >= b.s16); break; + default: + assert(0); + } + break; + case S32_TYPE: + switch (cmpop) { + case EQ_OPTION: t = (a.s32 == b.s32); break; + case NE_OPTION: t = (a.s32 != b.s32); break; + case LT_OPTION: t = (a.s32 < b.s32); break; + case LE_OPTION: t = (a.s32 <= b.s32); break; + case GT_OPTION: t = (a.s32 > b.s32); break; + case GE_OPTION: t = (a.s32 >= b.s32); break; + default: + assert(0); + } + break; + case S64_TYPE: + switch (cmpop) { + case EQ_OPTION: t = (a.s64 == b.s64); break; + case NE_OPTION: t = (a.s64 != b.s64); break; + case LT_OPTION: t = (a.s64 < b.s64); break; + case LE_OPTION: t = (a.s64 <= b.s64); break; + case GT_OPTION: t = (a.s64 > b.s64); break; + case GE_OPTION: t = (a.s64 >= b.s64); break; + default: + assert(0); + } + break; + case U8_TYPE: + case U16_TYPE: + switch (cmpop) { + case EQ_OPTION: t = (a.u16 == b.u16); break; + case NE_OPTION: t = (a.u16 != b.u16); break; + case LT_OPTION: t = (a.u16 < b.u16); break; + case LE_OPTION: t = (a.u16 <= b.u16); break; + case GT_OPTION: t = (a.u16 > b.u16); break; + case GE_OPTION: t = (a.u16 >= b.u16); break; + case LO_OPTION: t = (a.u16 < b.u16); break; + case LS_OPTION: t = (a.u16 <= b.u16); break; + case HI_OPTION: t = (a.u16 > b.u16); break; + case HS_OPTION: t = (a.u16 >= b.u16); break; + default: + assert(0); + } + break; + case U32_TYPE: + switch (cmpop) { + case EQ_OPTION: t = (a.u32 == b.u32); break; + case NE_OPTION: t = (a.u32 != b.u32); break; + case LT_OPTION: t = (a.u32 < b.u32); break; + case LE_OPTION: t = (a.u32 <= b.u32); break; + case GT_OPTION: t = (a.u32 > b.u32); break; + case GE_OPTION: t = (a.u32 >= b.u32); break; + case LO_OPTION: t = (a.u32 < b.u32); break; + case LS_OPTION: t = (a.u32 <= b.u32); break; + case HI_OPTION: t = (a.u32 > b.u32); break; + case HS_OPTION: t = (a.u32 >= b.u32); break; + default: + assert(0); + } + break; + case U64_TYPE: + switch (cmpop) { + case EQ_OPTION: t = (a.u64 == b.u64); break; + case NE_OPTION: t = (a.u64 != b.u64); break; + case LT_OPTION: t = (a.u64 < b.u64); break; + case LE_OPTION: t = (a.u64 <= b.u64); break; + case GT_OPTION: t = (a.u64 > b.u64); break; + case GE_OPTION: t = (a.u64 >= b.u64); break; + case LO_OPTION: t = (a.u64 < b.u64); break; + case LS_OPTION: t = (a.u64 <= b.u64); break; + case HI_OPTION: t = (a.u64 > b.u64); break; + case HS_OPTION: t = (a.u64 >= b.u64); break; + default: + assert(0); + } + break; + case F16_TYPE: assert(0); break; + case F32_TYPE: + switch (cmpop) { + case EQ_OPTION: t = (a.f32 == b.f32) && !isNaN(a.f32) && !isNaN(b.f32); break; + case NE_OPTION: t = (a.f32 != b.f32) && !isNaN(a.f32) && !isNaN(b.f32); break; + case LT_OPTION: t = (a.f32 < b.f32 ) && !isNaN(a.f32) && !isNaN(b.f32); break; + case LE_OPTION: t = (a.f32 <= b.f32) && !isNaN(a.f32) && !isNaN(b.f32); break; + case GT_OPTION: t = (a.f32 > b.f32 ) && !isNaN(a.f32) && !isNaN(b.f32); break; + case GE_OPTION: t = (a.f32 >= b.f32) && !isNaN(a.f32) && !isNaN(b.f32); break; + case EQU_OPTION: t = (a.f32 == b.f32) || isNaN(a.f32) || isNaN(b.f32); break; + case NEU_OPTION: t = (a.f32 != b.f32) || isNaN(a.f32) || isNaN(b.f32); break; + case LTU_OPTION: t = (a.f32 < b.f32 ) || isNaN(a.f32) || isNaN(b.f32); break; + case LEU_OPTION: t = (a.f32 <= b.f32) || isNaN(a.f32) || isNaN(b.f32); break; + case GTU_OPTION: t = (a.f32 > b.f32 ) || isNaN(a.f32) || isNaN(b.f32); break; + case GEU_OPTION: t = (a.f32 >= b.f32) || isNaN(a.f32) || isNaN(b.f32); break; + case NUM_OPTION: t = !isNaN(a.f32) && !isNaN(b.f32); break; + case NAN_OPTION: t = isNaN(a.f32) || isNaN(b.f32); break; + default: + assert(0); + } + break; + case F64_TYPE: + switch (cmpop) { + case EQ_OPTION: t = (a.f64 == b.f64) && !isNaN(a.f64) && !isNaN(b.f64); break; + case NE_OPTION: t = (a.f64 != b.f64) && !isNaN(a.f64) && !isNaN(b.f64); break; + case LT_OPTION: t = (a.f64 < b.f64 ) && !isNaN(a.f64) && !isNaN(b.f64); break; + case LE_OPTION: t = (a.f64 <= b.f64) && !isNaN(a.f64) && !isNaN(b.f64); break; + case GT_OPTION: t = (a.f64 > b.f64 ) && !isNaN(a.f64) && !isNaN(b.f64); break; + case GE_OPTION: t = (a.f64 >= b.f64) && !isNaN(a.f64) && !isNaN(b.f64); break; + case EQU_OPTION: t = (a.f64 == b.f64) || isNaN(a.f64) || isNaN(b.f64); break; + case NEU_OPTION: t = (a.f64 != b.f64) || isNaN(a.f64) || isNaN(b.f64); break; + case LTU_OPTION: t = (a.f64 < b.f64 ) || isNaN(a.f64) || isNaN(b.f64); break; + case LEU_OPTION: t = (a.f64 <= b.f64) || isNaN(a.f64) || isNaN(b.f64); break; + case GTU_OPTION: t = (a.f64 > b.f64 ) || isNaN(a.f64) || isNaN(b.f64); break; + case GEU_OPTION: t = (a.f64 >= b.f64) || isNaN(a.f64) || isNaN(b.f64); break; + case NUM_OPTION: t = !isNaN(a.f64) && !isNaN(b.f64); break; + case NAN_OPTION: t = isNaN(a.f64) || isNaN(b.f64); break; + default: + assert(0); + } + break; + default: assert(0); break; + } + + return t; +} + +void setp_impl( const ptx_instruction *pI, ptx_thread_info *thread ) +{ + ptx_reg_t a, b; + + int t=0; + const operand_info &dst = pI->dst(); + const operand_info &src1 = pI->src1(); + const operand_info &src2 = pI->src2(); + + assert( pI->get_num_operands() < 4 ); // or need to deal with "c" operand / boolOp + + a = thread->get_operand_value(src1); + b = thread->get_operand_value(src2); + + unsigned type = pI->get_type(); + unsigned cmpop = pI->get_cmpop(); + + t = CmpOp(type,a,b,cmpop); + + ptx_reg_t data; + data.pred = (t!=0); + + thread->set_operand_value(dst,data); +} + +void set_impl( const ptx_instruction *pI, ptx_thread_info *thread ) +{ + ptx_reg_t a, b; + + int t=0; + const operand_info &dst = pI->dst(); + const operand_info &src1 = pI->src1(); + const operand_info &src2 = pI->src2(); + + assert( pI->get_num_operands() < 4 ); // or need to deal with "c" operand / boolOp + + a = thread->get_operand_value(src1); + b = thread->get_operand_value(src2); + + unsigned src_type = pI->get_type2(); + unsigned cmpop = pI->get_cmpop(); + + t = CmpOp(src_type,a,b,cmpop); + + ptx_reg_t data; + if ( isFloat(pI->get_type()) ) { + data.f32 = (t!=0)?1.0f:0.0f; + } else { + data.u32 = (t!=0)?0xFFFFFFFF:0; + } + + thread->set_operand_value(dst,data); + +} + +void shl_impl( const ptx_instruction *pI, ptx_thread_info *thread ) +{ + ptx_reg_t a, b, d; + const operand_info &dst = pI->dst(); + const operand_info &src1 = pI->src1(); + const operand_info &src2 = pI->src2(); + a = thread->get_operand_value(src1); + b = thread->get_operand_value(src2); + + + + unsigned i_type = pI->get_type(); + switch ( i_type ) { + case B16_TYPE: + if ( b.u16 >= 16 ) + d.u16 = 0; + else + d.u16 = (unsigned short) ((a.u16 << b.u16) & 0xFFFF); + break; + case B32_TYPE: + if ( b.u32 >= 32 ) + d.u32 = 0; + else + d.u32 = (unsigned) ((a.u32 << b.u32) & 0xFFFFFFFF); + break; + case B64_TYPE: + if ( b.u32 >= 64 ) + d.u64 = 0; + else + d.u64 = (a.u64 << b.u64); + break; + default: + printf("Execution error: type mismatch with instruction\n"); + assert(0); + break; + } + + thread->set_operand_value(dst,d); +} + +void shr_impl( const ptx_instruction *pI, ptx_thread_info *thread ) +{ + ptx_reg_t a, b, d; + const operand_info &dst = pI->dst(); + const operand_info &src1 = pI->src1(); + const operand_info &src2 = pI->src2(); + a = thread->get_operand_value(src1); + b = thread->get_operand_value(src2); + + unsigned i_type = pI->get_type(); + switch ( i_type ) { + case U16_TYPE: + case B16_TYPE: + if ( b.u16 < 16 ) + d.u16 = (unsigned short) ((a.u16 >> b.u16) & 0xFFFF); + else + d.u16 = 0; + break; + case U32_TYPE: + case B32_TYPE: + if ( b.u32 < 32 ) + d.u32 = (unsigned) ((a.u32 >> b.u32) & 0xFFFFFFFF); + else + d.u32 = 0; + break; + case U64_TYPE: + case B64_TYPE: + if ( b.u32 < 64 ) + d.u64 = (a.u64 >> b.u64); + else + d.u64 = 0; + break; + case S16_TYPE: + if ( b.u16 < 16 ) + d.s64 = (a.s16 >> b.s16); + else { + if ( a.s16 < 0 ) { + d.s64 = -1; + } else { + d.s64 = 0; + } + } + break; + case S32_TYPE: + if ( b.u32 < 32 ) + d.s64 = (a.s32 >> b.s32); + else { + if ( a.s32 < 0 ) { + d.s64 = -1; + } else { + d.s64 = 0; + } + } + break; + case S64_TYPE: + if ( b.u64 < 64 ) + d.s64 = (a.s64 >> b.u64); + else { + if ( a.s64 < 0 ) { + if ( b.s32 < 0 ) { + d.u64 = -1; + d.s32 = 0; + } else { + d.s64 = -1; + } + } else { + d.s64 = 0; + } + } + break; + default: + printf("Execution error: type mismatch with instruction\n"); + assert(0); + break; + } + + thread->set_operand_value(dst,d); +} + +void sin_impl( const ptx_instruction *pI, ptx_thread_info *thread ) +{ + ptx_reg_t a, d; + const operand_info &dst = pI->dst(); + const operand_info &src1 = pI->src1(); + a = thread->get_operand_value(src1); + + unsigned i_type = pI->get_type(); + switch ( i_type ) { + case F32_TYPE: + d.f32 = sin(a.f32); + break; + default: + printf("Execution error: type mismatch with instruction\n"); + assert(0); + break; + } + + thread->set_operand_value(dst,d); +} + +void slct_impl( const ptx_instruction *pI, ptx_thread_info *thread ) +{ + const operand_info &dst = pI->dst(); + const operand_info &src1 = pI->src1(); + const operand_info &src2 = pI->src2(); + const operand_info &src3 = pI->src3(); + + ptx_reg_t a, b, c, d; + + a = thread->get_operand_value(src1); + b = thread->get_operand_value(src2); + c = thread->get_operand_value(src3); + + bool t = false; + unsigned c_type = pI->get_type2(); + switch ( c_type ) { + case S32_TYPE: t = c.s32 >= 0; break; + case F32_TYPE: t = c.f32 >= 0; break; + default: assert(0); + } + + unsigned i_type = pI->get_type(); + + switch ( i_type ) { + case B16_TYPE: + case U16_TYPE: d.u16 = t?a.u16:b.u16; break; + case F32_TYPE: + case B32_TYPE: + case U32_TYPE: d.u32 = t?a.u32:b.u32; break; + case F64_TYPE: + case B64_TYPE: + case U64_TYPE: d.u64 = t?a.u64:b.u64; break; + default: assert(0); + } + + thread->set_operand_value(dst,d); +} + +void sqrt_impl( const ptx_instruction *pI, ptx_thread_info *thread ) +{ + ptx_reg_t a, d; + const operand_info &dst = pI->dst(); + const operand_info &src1 = pI->src1(); + a = thread->get_operand_value(src1); + + unsigned i_type = pI->get_type(); + switch ( i_type ) { + case F32_TYPE: + if ( a.f32 < 0 ) + d.f32 = nanf(""); + else + d.f32 = sqrt(a.f32); break; + case F64_TYPE: + if ( a.f64 < 0 ) + d.f64 = nan(""); + else + d.f64 = sqrt(a.f64); break; + default: + printf("Execution error: type mismatch with instruction\n"); + assert(0); + break; + } + + thread->set_operand_value(dst,d); +} + +void st_impl( const ptx_instruction *pI, ptx_thread_info *thread ) +{ + const operand_info &dst = pI->dst(); + const operand_info &src1 = pI->src1(); //may be scalar or vector of regs + ptx_reg_t addr = thread->get_operand_value(dst); + ptx_reg_t data; + unsigned space = pI->get_space(); + unsigned vector_spec = pI->get_vector(); + unsigned type = pI->get_type(); + memory_space *mem = NULL; + + switch ( space ) { + case GLOBAL_DIRECTIVE: mem = g_global_mem; break; + case LOCAL_DIRECTIVE: mem = thread->m_local_mem; break; + case TEX_DIRECTIVE: mem = g_tex_mem; break; + case SURF_DIRECTIVE: mem = g_surf_mem; break; + case PARAM_DIRECTIVE: mem = g_param_mem; break; + case SHARED_DIRECTIVE: mem = thread->m_shared_mem; break; + default: + assert(0); + break; + } + + size_t size; + int t; + type_decode(type,size,t); + + if (!vector_spec) { + data = thread->get_operand_value(src1); + mem->write(addr.u32,size/8,&data.s64); + } else { + if (vector_spec == V2_TYPE) { + ptx_reg_t* ptx_regs = new ptx_reg_t[2]; + thread->get_vector_operand_values(src1, ptx_regs, 2); + mem->write(addr.u32,size/8,&ptx_regs[0].s64); + mem->write(addr.u32+size/8,size/8,&ptx_regs[1].s64); + free(ptx_regs); + } + if (vector_spec == V3_TYPE) { + ptx_reg_t* ptx_regs = new ptx_reg_t[3]; + thread->get_vector_operand_values(src1, ptx_regs, 3); + mem->write(addr.u32,size/8,&ptx_regs[0].s64); + mem->write(addr.u32+size/8,size/8,&ptx_regs[1].s64); + mem->write(addr.u32+2*size/8,size/8,&ptx_regs[2].s64); + free(ptx_regs); + } + if (vector_spec == V4_TYPE) { + ptx_reg_t* ptx_regs = new ptx_reg_t[4]; + thread->get_vector_operand_values(src1, ptx_regs, 4); + mem->write(addr.u32,size/8,&ptx_regs[0].s64); + mem->write(addr.u32+size/8,size/8,&ptx_regs[1].s64); + mem->write(addr.u32+2*size/8,size/8,&ptx_regs[2].s64); + mem->write(addr.u32+3*size/8,size/8,&ptx_regs[3].s64); + free(ptx_regs); + } + } + thread->m_last_effective_address = addr.u32; + thread->m_last_memory_space = space; +} + +void sub_impl( const ptx_instruction *pI, ptx_thread_info *thread ) +{ + ptx_reg_t data; + + const operand_info &dst = pI->dst(); + const operand_info &src1 = pI->src1(); + const operand_info &src2 = pI->src2(); + + ptx_reg_t src1_data = thread->get_operand_value(src1); + ptx_reg_t src2_data = thread->get_operand_value(src2); + + unsigned i_type = pI->get_type(); + + switch ( i_type ) { + case S8_TYPE: + case S16_TYPE: + case S32_TYPE: + case S64_TYPE: + data.s64 = src1_data.s64 - src2_data.s64; break; + case U8_TYPE: + case U16_TYPE: + case U32_TYPE: + case U64_TYPE: + case B8_TYPE: + case B16_TYPE: + case B32_TYPE: + case B64_TYPE: + data.u64 = src1_data.u64 - src2_data.u64; break; + case F16_TYPE: assert(0); break; + case F32_TYPE: data.f32 = src1_data.f32 - src2_data.f32; break; + case F64_TYPE: data.f64 = src1_data.f64 - src2_data.f64; break; + default: assert(0); break; + } + + thread->set_operand_value(dst,data); +} + +ptx_reg_t* ptx_tex_regs = NULL; + +union intfloat { + int a; + float b; +}; + +float reduce_precision( float x, unsigned bits ) +{ + intfloat tmp; + tmp.b = x; + int v = tmp.a; + int man = v & ((1<<23)-1); + int mask = ((1<<bits)-1) << (23-bits); + int nv = (v & ((-1)-((1<<23)-1))) | (mask&man); + tmp.a = nv; + float result = tmp.b; + return result; +} + +unsigned wrap( unsigned x, unsigned y, unsigned mx, unsigned my ) +{ + unsigned nx = (mx+x)%mx; + unsigned ny = (my+y)%my; + return nx + mx*ny; +} + +void tex_impl( const ptx_instruction *pI, ptx_thread_info *thread ) +{ + cudasim_n_tex_insn++; + unsigned dimension = pI->dimension(); + const operand_info &dst = pI->dst(); //the registers to which fetched texel will be placed + const operand_info &src1 = pI->src1(); //the name of the texture + const operand_info &src2 = pI->src2(); //the vector registers containing coordinates of the texel to be fetched + + std::string texname = src1.name(); + unsigned to_type = pI->get_type(); + fflush(stdout); + ptx_reg_t data1, data2, data3, data4; + if (!ptx_tex_regs) ptx_tex_regs = new ptx_reg_t[4]; + thread->get_vector_operand_values(src2, ptx_tex_regs, 4); //ptx_reg should be 4 entry vector type...coordinates into texture + + assert(NameToTextureMap.find(texname) != NameToTextureMap.end());//use map to find texturerefence, then use map to find pointer to array + struct textureReference* texref = NameToTextureMap[texname]; + assert(TextureToArrayMap.find(texref) != TextureToArrayMap.end()); + struct cudaArray* cuArray = TextureToArrayMap[texref]; + assert(TextureToInfoMap.find(texref) != TextureToInfoMap.end()); + struct textureInfo* texInfo = TextureToInfoMap[texref]; + + //assume always 2D f32 input + //access array with src2 coordinates + memory_space *mem = g_global_mem; + float x_f32, y_f32; + size_t size; + int t; + unsigned tex_array_base; + unsigned int width = 0, height = 0; + int x = 0; + int y = 0; + unsigned tex_array_index; + float alpha=0, beta=0; + + type_decode(to_type,size,t); + tex_array_base = cuArray->devPtr32; + + switch (dimension) { + case GEOM_MODIFIER_1D: + x = ptx_tex_regs[0].u64; + x *= (cuArray->desc.w+cuArray->desc.x+cuArray->desc.y+cuArray->desc.z)/8; + tex_array_index = tex_array_base + x; + + break; + case GEOM_MODIFIER_2D: + width = cuArray->width; + height = cuArray->height; + if (texref->normalized) { + x_f32 = reduce_precision(ptx_tex_regs[0].f32,16); + y_f32 = reduce_precision(ptx_tex_regs[1].f32,15); + + if (texref->addressMode[0]) {//clamp + if (x_f32<0) x_f32 = 0; + if (x_f32>=1) x_f32 = 1 - 1/x_f32; + } else {//wrap + x_f32 = x_f32 - floor(x_f32); + } + if (texref->addressMode[1]) {//clamp + if (y_f32<0) y_f32 = 0; + if (y_f32>=1) y_f32 = 1 - 1/y_f32; + } else {//wrap + y_f32 = y_f32 - floor(y_f32); + } + + if( texref->filterMode == cudaFilterModeLinear ) { + float xb = x_f32 * width - 0.5; + float yb = y_f32 * height - 0.5; + alpha = xb - floor(xb); + beta = yb - floor(yb); + alpha = reduce_precision(alpha,9); + beta = reduce_precision(beta,9); + + x = (int)floor(xb); + y = (int)floor(yb); + } else { + x = (int) floor(x_f32 * width); + y = (int) floor(y_f32 * height); + } + } else { + x_f32 = ptx_tex_regs[0].f32; + y_f32 = ptx_tex_regs[1].f32; + + alpha = x_f32 - floor(x_f32); + beta = y_f32 - floor(y_f32); + + x = (int) x_f32; + y = (int) y_f32; + if (texref->addressMode[0]) {//clamp + if (x<0) x = 0; + if (x>= (int)width) x = width-1; + } else {//wrap + x = x % width; + if (x < 0) x*= -1; + } + if (texref->addressMode[1]) {//clamp + if (y<0) y = 0; + if (y>= (int)height) y = height -1; + } else {//wrap + y = y % height; + if (y < 0) y *= -1; + } + } + + width *= (cuArray->desc.w+cuArray->desc.x+cuArray->desc.y+cuArray->desc.z)/8; + x *= (cuArray->desc.w+cuArray->desc.x+cuArray->desc.y+cuArray->desc.z)/8; + tex_array_index = tex_array_base + (x + width*y); + break; + default: + assert(0); break; + } + switch ( to_type ) { + case U8_TYPE: + case U16_TYPE: + case U32_TYPE: + case S8_TYPE: + case S16_TYPE: + case S32_TYPE: + mem->read( tex_array_index, cuArray->desc.x/8, &data1.u32); + if (cuArray->desc.y) { + mem->read( tex_array_index+4, cuArray->desc.y/8, &data2.u32); + if (cuArray->desc.z) { + mem->read( tex_array_index+8, cuArray->desc.z/8, &data3.u32); + if (cuArray->desc.w) + mem->read( tex_array_index+12, cuArray->desc.w/8, &data4.u32); + } + } + break; + case U64_TYPE: + case S64_TYPE: + mem->read( tex_array_index, 8, &data1.u64); + if (cuArray->desc.y) { + mem->read( tex_array_index+4, 8, &data2.u64); + if (cuArray->desc.z) { + mem->read( tex_array_index+8, 8, &data3.u64); + if (cuArray->desc.w) + mem->read( tex_array_index+12, 8, &data4.u64); + } + } + break; + case F16_TYPE: assert(0); break; + case F32_TYPE: { + if( texref->filterMode == cudaFilterModeLinear ) { + float Tij; + float Ti1j; + float Tij1; + float Ti1j1; + + mem->read(tex_array_base + wrap(x,y,width,height), 4, &Tij); + mem->read(tex_array_base + wrap(x+4,y,width,height), 4, &Ti1j); + mem->read(tex_array_base + wrap(x,y+1,width,height), 4, &Tij1); + mem->read(tex_array_base + wrap(x+4,y+1,width,height), 4, &Ti1j1); + + data1.f32 = (1-alpha)*(1-beta)*Tij + + alpha*(1-beta)*Ti1j + + (1-alpha)*beta*Tij1 + + alpha*beta*Ti1j1; + + } else { + mem->read( tex_array_index, cuArray->desc.x/8, &data1.f32); + if (cuArray->desc.y) { + mem->read( tex_array_index+4, cuArray->desc.y/8, &data2.f32); + if (cuArray->desc.z) { + mem->read( tex_array_index+8, cuArray->desc.z/8, &data3.f32); + if (cuArray->desc.w) + mem->read( tex_array_index+12, cuArray->desc.w/8, &data4.f32); + } + } + } + } break; + case F64_TYPE: + mem->read( tex_array_index, 8, &data1.f64); + if (cuArray->desc.y) { + mem->read( tex_array_index+8, 8, &data2.f64); + if (cuArray->desc.z) { + mem->read( tex_array_index+16, 8, &data3.f64); + if (cuArray->desc.w) + mem->read( tex_array_index+24, 8, &data4.f64); + } + } + break; + default: assert(0); break; + } + int x_block_coord, y_block_coord, memreqindex, blockoffset; + + switch (dimension) { + case GEOM_MODIFIER_1D: + thread->m_last_effective_address = tex_array_index; + break; + case GEOM_MODIFIER_2D: + x_block_coord = x; + x_block_coord = x_block_coord >> (texInfo->Tx_numbits + texInfo->texel_size_numbits); + + y_block_coord = y; + y_block_coord = y_block_coord >> texInfo->Ty_numbits; + + memreqindex = ((y_block_coord*cuArray->width/texInfo->Tx)+x_block_coord)<<6; + + blockoffset = (x%(texInfo->Tx*texInfo->texel_size) + (y%(texInfo->Ty)<<(texInfo->Tx_numbits + texInfo->texel_size_numbits))); + memreqindex += blockoffset; + thread->m_last_effective_address = tex_array_base + memreqindex;//tex_array_index; + break; + default: + assert(0); + } + thread->m_last_memory_space = TEX_DIRECTIVE; + thread->set_vector_operand_values(dst,data1,data2,data3,data4,4); +} +void trap_impl( const ptx_instruction *pI, ptx_thread_info *thread ) { inst_not_implemented(pI); } + +extern unsigned g_warp_active_mask; + +void vote_impl( const ptx_instruction *pI, ptx_thread_info *thread ) +{ + static bool first_in_warp = true; + static bool and_all; + static bool or_all; + static std::list<ptx_thread_info*> threads_in_warp; + static unsigned last_tid; + + if( first_in_warp ) { + first_in_warp = false; + threads_in_warp.clear(); + and_all = true; + or_all = false; + unsigned mask=0x80000000; + unsigned offset=31; + while( mask && ((mask & g_warp_active_mask)==0) ) { + mask = mask>>1; + offset--; + } + last_tid = (thread->get_hw_tid() - (thread->get_hw_tid()%pI->warp_size())) + offset; + } + + ptx_reg_t src1_data; + const operand_info &src1 = pI->src1(); + src1_data = thread->get_operand_value(src1); + + bool pred_value = src1_data.pred; + bool invert = src1.is_neg_pred(); + + threads_in_warp.push_back(thread); + and_all &= (invert ^ pred_value); + or_all |= (invert ^ pred_value); + + // TODO: determine last active thread in warp... + if( thread->get_hw_tid() == last_tid ) { + bool pred_value = false; + + switch( pI->vote_mode() ) { + case ptx_instruction::vote_any: pred_value = or_all; break; + case ptx_instruction::vote_all: pred_value = and_all; break; + case ptx_instruction::vote_uni: pred_value = (or_all ^ and_all); break; + default: + abort(); + } + ptx_reg_t data; + data.pred = pred_value?1:0; + + for( std::list<ptx_thread_info*>::iterator t=threads_in_warp.begin(); t!=threads_in_warp.end(); ++t ) { + const operand_info &dst = pI->dst(); + (*t)->set_operand_value(dst,data); + } + first_in_warp = true; + } +} + +void xor_impl( const ptx_instruction *pI, ptx_thread_info *thread ) +{ + ptx_reg_t src1_data, src2_data, data; + + const operand_info &dst = pI->dst(); + const operand_info &src1 = pI->src1(); + const operand_info &src2 = pI->src2(); + + src1_data = thread->get_operand_value(src1); + src2_data = thread->get_operand_value(src2); + + data.u64 = src1_data.u64 ^ src2_data.u64; + + thread->set_operand_value(dst,data); +} + +void inst_not_implemented( const ptx_instruction * pI ) +{ + printf("Execution error (%s:%u): instruction \"%s\" not (yet) implemented\n", + pI->source_file(), + pI->source_line(), + pI->get_opcode_cstr() ); + abort(); +} + +void print_instruction(const ptx_instruction *instruction) +{ +} + diff --git a/src/cuda-sim/memory.cc b/src/cuda-sim/memory.cc new file mode 100644 index 0000000..c6a1a9b --- /dev/null +++ b/src/cuda-sim/memory.cc @@ -0,0 +1,154 @@ +/* + * memory.cc + * + * Copyright © 2009 by Tor M. Aamodt, Wilson W. L. Fung and the University of + * British Columbia, Vancouver, BC V6T 1Z4, All Rights Reserved. + * + * THIS IS A LEGAL DOCUMENT BY DOWNLOADING GPGPU-SIM, YOU ARE AGREEING TO THESE + * TERMS AND CONDITIONS. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * NOTE: The files libcuda/cuda_runtime_api.c and src/cuda-sim/cuda-math.h + * are derived from the CUDA Toolset available from http://www.nvidia.com/cuda + * (property of NVIDIA). The files benchmarks/BlackScholes/ and + * benchmarks/template/ are derived from the CUDA SDK available from + * http://www.nvidia.com/cuda (also property of NVIDIA). The files from + * src/intersim/ are derived from Booksim (a simulator provided with the + * textbook "Principles and Practices of Interconnection Networks" available + * from http://cva.stanford.edu/books/ppin/). As such, those files are bound by + * the corresponding legal terms and conditions set forth separately (original + * copyright notices are left in files from these sources and where we have + * modified a file our copyright notice appears before the original copyright + * notice). + * + * Using this version of GPGPU-Sim requires a complete installation of CUDA + * which is distributed seperately by NVIDIA under separate terms and + * conditions. To use this version of GPGPU-Sim with OpenCL requires a + * recent version of NVIDIA's drivers which support OpenCL. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the University of British Columbia nor the names of + * its contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * 4. This version of GPGPU-SIM is distributed freely for non-commercial use only. + * + * 5. No nonprofit user may place any restrictions on the use of this software, + * including as modified by the user, by any other authorized user. + * + * 6. GPGPU-SIM was developed primarily by Tor M. Aamodt, Wilson W. L. Fung, + * Ali Bakhoda, George L. Yuan, at the University of British Columbia, + * Vancouver, BC V6T 1Z4 + */ + +#include "memory.h" +#include <stdlib.h> + +template<unsigned BSIZE> memory_space_impl<BSIZE>::memory_space_impl( std::string name, unsigned hash_size ) +{ + m_name = name; + MEM_MAP_RESIZE(hash_size); + + m_log2_block_size = -1; + for( unsigned n=0, mask=1; mask != 0; mask <<= 1, n++ ) { + if( BSIZE & mask ) { + assert( m_log2_block_size == (unsigned)-1 ); + m_log2_block_size = n; + } + } + assert( m_log2_block_size != (unsigned)-1 ); +} + +template<unsigned BSIZE> void memory_space_impl<BSIZE>::write( mem_addr_t addr, size_t length, const void *data ) +{ + mem_addr_t index = addr >> m_log2_block_size; + unsigned offset = addr & (BSIZE-1); + unsigned nbytes = length; + assert( (addr+length) <= (index+1)*BSIZE ); + m_data[index].write(offset,nbytes,(const unsigned char*)data); +} + +template<unsigned BSIZE> void memory_space_impl<BSIZE>::read( mem_addr_t addr, size_t length, void *data ) const +{ + mem_addr_t index = addr >> m_log2_block_size; + unsigned offset = addr & (BSIZE-1); + unsigned nbytes = length; + typename map_t::const_iterator i = m_data.find(index); + assert( (addr+length) <= (index+1)*BSIZE ); + if( i == m_data.end() ) { + for( size_t n=0; n < length; n++ ) + ((unsigned char*)data)[n] = (unsigned char) 0; + //printf("GPGPU-Sim PTX: WARNING reading %zu bytes from unititialized memory at address 0x%x in space %s\n", length, addr, m_name.c_str() ); + } else { + i->second.read(offset,nbytes,(unsigned char*)data); + } +} + +template class memory_space_impl<32>; +template class memory_space_impl<64>; +template class memory_space_impl<8192>; +template class memory_space_impl<16*1024>; + + +#ifdef UNIT_TEST + +int main(int argc, char *argv[] ) +{ + int errors_found=0; + memory_space *mem = new memory_space_impl<32>("test",4); + // write address to [address] + for( mem_addr_t addr=0; addr < 16*1024; addr+=4) + mem->write(addr,4,&addr); + + for( mem_addr_t addr=0; addr < 16*1024; addr+=4) { + unsigned tmp=0; + mem->read(addr,4,&tmp); + if( tmp != addr ) { + errors_found=1; + printf("ERROR ** mem[0x%x] = 0x%x, expected 0x%x\n", addr, tmp, addr ); + } + } + + for( mem_addr_t addr=0; addr < 16*1024; addr+=1) { + unsigned char val = (addr + 128) % 256; + mem->write(addr,1,&val); + } + + for( mem_addr_t addr=0; addr < 16*1024; addr+=1) { + unsigned tmp=0; + mem->read(addr,1,&tmp); + unsigned char val = (addr + 128) % 256; + if( tmp != val ) { + errors_found=1; + printf("ERROR ** mem[0x%x] = 0x%x, expected 0x%x\n", addr, tmp, (unsigned)val ); + } + } + + if( errors_found ) { + printf("SUMMARY: ERRORS FOUND\n"); + } else { + printf("SUMMARY: UNIT TEST PASSED\n"); + } +} + +#endif diff --git a/src/cuda-sim/memory.h b/src/cuda-sim/memory.h new file mode 100644 index 0000000..6eebcf4 --- /dev/null +++ b/src/cuda-sim/memory.h @@ -0,0 +1,148 @@ +/* + * Copyright © 2009 by Tor M. Aamodt, Wilson W. L. Fung and the University of + * British Columbia, Vancouver, BC V6T 1Z4, All Rights Reserved. + * + * THIS IS A LEGAL DOCUMENT BY DOWNLOADING GPGPU-SIM, YOU ARE AGREEING TO THESE + * TERMS AND CONDITIONS. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * NOTE: The files libcuda/cuda_runtime_api.c and src/cuda-sim/cuda-math.h + * are derived from the CUDA Toolset available from http://www.nvidia.com/cuda + * (property of NVIDIA). The files benchmarks/BlackScholes/ and + * benchmarks/template/ are derived from the CUDA SDK available from + * http://www.nvidia.com/cuda (also property of NVIDIA). The files from + * src/intersim/ are derived from Booksim (a simulator provided with the + * textbook "Principles and Practices of Interconnection Networks" available + * from http://cva.stanford.edu/books/ppin/). As such, those files are bound by + * the corresponding legal terms and conditions set forth separately (original + * copyright notices are left in files from these sources and where we have + * modified a file our copyright notice appears before the original copyright + * notice). + * + * Using this version of GPGPU-Sim requires a complete installation of CUDA + * which is distributed seperately by NVIDIA under separate terms and + * conditions. To use this version of GPGPU-Sim with OpenCL requires a + * recent version of NVIDIA's drivers which support OpenCL. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the University of British Columbia nor the names of + * its contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * 4. This version of GPGPU-SIM is distributed freely for non-commercial use only. + * + * 5. No nonprofit user may place any restrictions on the use of this software, + * including as modified by the user, by any other authorized user. + * + * 6. GPGPU-SIM was developed primarily by Tor M. Aamodt, Wilson W. L. Fung, + * Ali Bakhoda, George L. Yuan, at the University of British Columbia, + * Vancouver, BC V6T 1Z4 + */ + +#ifndef memory_h_INCLUDED +#define memory_h_INCLUDED + +#ifdef __GNUC__ +#if __GNUC__ >= 4 && __GNUC_MINOR__ >= 3 + #include <unordered_map> + #define mem_map std::unordered_map + #define MEM_MAP_RESIZE(hash_size) m_data.rehash(hash_size) +#else + #include <ext/hash_map> + namespace std { + using namespace __gnu_cxx; + } + #define mem_map std::hash_map + #define MEM_MAP_RESIZE(hash_size) m_data.resize(hash_size) +#endif +#else + #include <map> + #define mem_map std::map + #define MEM_MAP_RESIZE(hash_size) +#endif + +#include <assert.h> +#include <string.h> +#include <string> +#include "../util.h" + +typedef address_type mem_addr_t; + +#define MEM_BLOCK_SIZE (4*1024) + +template<unsigned BSIZE> class mem_storage { +public: + mem_storage( const mem_storage &another ) + { + m_data = new unsigned char[ BSIZE ]; + memcpy(m_data,another.m_data,BSIZE); + } + mem_storage() + { + m_data = new unsigned char[ BSIZE ]; + } + ~mem_storage() + { + delete[] m_data; + } + + void write( unsigned offset, size_t length, const unsigned char *data ) + { + assert( offset + length <= BSIZE ); + memcpy(m_data+offset,data,length); + } + + void read( unsigned offset, size_t length, unsigned char *data ) const + { + assert( offset + length <= BSIZE ); + memcpy(data,m_data+offset,length); + } + +private: + unsigned m_nbytes; + unsigned char *m_data; +}; + +class memory_space +{ +public: + virtual ~memory_space() {} + virtual void write( mem_addr_t addr, size_t length, const void *data ) = 0; + virtual void read( mem_addr_t addr, size_t length, void *data ) const = 0;; +}; + +template<unsigned BSIZE> class memory_space_impl : public memory_space { +public: + memory_space_impl( std::string name, unsigned hash_size ); + + virtual void write( mem_addr_t addr, size_t length, const void *data ); + virtual void read( mem_addr_t addr, size_t length, void *data ) const; + +private: + std::string m_name; + unsigned m_log2_block_size; + typedef mem_map<mem_addr_t,mem_storage<BSIZE> > map_t; + map_t m_data; +}; + +#endif diff --git a/src/cuda-sim/opcodes.def b/src/cuda-sim/opcodes.def new file mode 100644 index 0000000..ba6657f --- /dev/null +++ b/src/cuda-sim/opcodes.def @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2009 by Tor M. Aamodt, Wilson W. L. Fung, Ali Bakhoda, + * George L. Yuan and the University of British Columbia + * Vancouver, BC V6T 1Z4 + * All Rights Reserved. + * + * THIS IS A LEGAL DOCUMENT BY DOWNLOADING GPGPU-SIM, YOU ARE AGREEING TO THESE + * TERMS AND CONDITIONS. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * NOTE: The files libcuda/cuda_runtime_api.c and src/cuda-sim/cuda-math.h + * are derived from the CUDA Toolset available from http://www.nvidia.com/cuda + * (property of NVIDIA). The files benchmarks/BlackScholes/ and + * benchmarks/template/ are derived from the CUDA SDK available from + * http://www.nvidia.com/cuda (also property of NVIDIA). The files from + * src/intersim/ are derived from Booksim (a simulator provided with the + * textbook "Principles and Practices of Interconnection Networks" available + * from http://cva.stanford.edu/books/ppin/). As such, those files are bound by + * the corresponding legal terms and conditions set forth separately (original + * copyright notices are left in files from these sources and where we have + * modified a file our copyright notice appears before the original copyright + * notice). + * + * Using this version of GPGPU-Sim requires a complete installation of CUDA + * which is distributed seperately by NVIDIA under separate terms and + * conditions. To use this version of GPGPU-Sim with OpenCL requires a + * recent version of NVIDIA's drivers which support OpenCL. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the University of British Columbia nor the names of + * its contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * 4. This version of GPGPU-SIM is distributed freely for non-commercial use only. + * + * 5. No nonprofit user may place any restrictions on the use of this software, + * including as modified by the user, by any other authorized user. + * + * 6. GPGPU-SIM was developed primarily by Tor M. Aamodt, Wilson W. L. Fung, + * Ali Bakhoda, George L. Yuan, at the University of British Columbia, + * Vancouver, BC V6T 1Z4 + */ + +/*6th operand of each OP_DEF reflects its classification */ +/*Type +ALU 1 +MAD 2 +Control 3 +SFU 4 +Mem(except Tex) 5 +Tex 6 +*/ +OP_DEF(ABS_OP,abs_impl,"abs",1,1) +OP_DEF(ADD_OP,add_impl,"add",1,1) +OP_DEF(AND_OP,and_impl,"and",1,1) +OP_DEF(ATOM_OP,atom_impl,"atom",0,3) +OP_DEF(BAR_OP,bar_sync_impl,"bar.sync",1,3) +OP_DEF(BRA_OP,bra_impl,"bra",0,3) +OP_DEF(CALL_OP,call_impl,"call",1,3) /*call may actually return an value if the syntax is call (ret-param),fname,(param-list)*/ +OP_DEF(CNOT_OP,cnot_impl,"cnot",1,1) +OP_DEF(COS_OP,cos_impl,"cos",1,4) +OP_DEF(CVT_OP,cvt_impl,"cvt",1,1) +OP_DEF(DIV_OP,div_impl,"div",1,1) +OP_DEF(EX2_OP,ex2_impl,"ex2",1,4) +OP_DEF(EXIT_OP,exit_impl,"exit",1,3) +OP_DEF(LD_OP,ld_impl,"ld",1,5) +OP_DEF(LG2_OP,lg2_impl,"lg2",1,4) +OP_DEF(MAD24_OP,mad24_impl,"mad24",1,2) +OP_DEF(MAD_OP,mad_impl,"mad",1,2) +OP_DEF(MAX_OP,max_impl,"max",1,1) +OP_DEF(MIN_OP,min_impl,"min",1,1) +OP_DEF(MOV_OP,mov_impl,"mov",1,1) +OP_DEF(MUL24_OP,mul24_impl,"mul24",1,1) +OP_DEF(MUL_OP,mul_impl,"mul",1,1) +OP_DEF(NEG_OP,neg_impl,"neg",1,1) +OP_DEF(NOT_OP,not_impl,"not",1,1) +OP_DEF(OR_OP,or_impl,"or",1,1) +OP_DEF(RCP_OP,rcp_impl,"rcp",1,4) +OP_DEF(REM_OP,rem_impl,"rem",1,1) +OP_DEF(RET_OP,ret_impl,"ret",0,3) +OP_DEF(RSQRT_OP,rsqrt_impl,"rsqrt",1,4) +OP_DEF(SAD_OP,sad_impl,"sad",1,1) +OP_DEF(SELP_OP,selp_impl,"selp",1,1) +OP_DEF(SETP_OP,setp_impl,"setp",1,1) +OP_DEF(SET_OP,set_impl,"set",1,1) +OP_DEF(SHL_OP,shl_impl,"shl",1,1) +OP_DEF(SHR_OP,shr_impl,"shr",1,1) +OP_DEF(SIN_OP,sin_impl,"sin",1,4) +OP_DEF(SLCT_OP,slct_impl,"slct",1,1) +OP_DEF(SQRT_OP,sqrt_impl,"sqrt",1,4) +OP_DEF(ST_OP,st_impl,"st",0,5) +OP_DEF(SUB_OP,sub_impl,"sub",1,1) +OP_DEF(TEX_OP,tex_impl,"tex",1,6) +OP_DEF(TRAP_OP,trap_impl,"trap",1,3) +OP_DEF(VOTE_OP,vote_impl,"vote",0,3) +OP_DEF(XOR_OP,xor_impl,"xor",1,1) +OP_DEF(MEMBAR_OP,membar_impl,"membar",1,3) +OP_DEF(RED_OP,red_impl,"red",1,7) +OP_DEF(ADDC_OP,addc_impl,"addc",1,1) +OP_DEF(BRKPT_OP,brkpt_impl,"brkpt",1,9) diff --git a/src/cuda-sim/opcodes.h b/src/cuda-sim/opcodes.h new file mode 100644 index 0000000..221bf2e --- /dev/null +++ b/src/cuda-sim/opcodes.h @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2009 by Tor M. Aamodt and the University of British Columbia + * Vancouver, BC V6T 1Z4 + * All Rights Reserved. + * + * THIS IS A LEGAL DOCUMENT BY DOWNLOADING GPGPU-SIM, YOU ARE AGREEING TO THESE + * TERMS AND CONDITIONS. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * NOTE: The files libcuda/cuda_runtime_api.c and src/cuda-sim/cuda-math.h + * are derived from the CUDA Toolset available from http://www.nvidia.com/cuda + * (property of NVIDIA). The files benchmarks/BlackScholes/ and + * benchmarks/template/ are derived from the CUDA SDK available from + * http://www.nvidia.com/cuda (also property of NVIDIA). The files from + * src/intersim/ are derived from Booksim (a simulator provided with the + * textbook "Principles and Practices of Interconnection Networks" available + * from http://cva.stanford.edu/books/ppin/). As such, those files are bound by + * the corresponding legal terms and conditions set forth separately (original + * copyright notices are left in files from these sources and where we have + * modified a file our copyright notice appears before the original copyright + * notice). + * + * Using this version of GPGPU-Sim requires a complete installation of CUDA + * which is distributed seperately by NVIDIA under separate terms and + * conditions. To use this version of GPGPU-Sim with OpenCL requires a + * recent version of NVIDIA's drivers which support OpenCL. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the University of British Columbia nor the names of + * its contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * 4. This version of GPGPU-SIM is distributed freely for non-commercial use only. + * + * 5. No nonprofit user may place any restrictions on the use of this software, + * including as modified by the user, by any other authorized user. + * + * 6. GPGPU-SIM was developed primarily by Tor M. Aamodt, Wilson W. L. Fung, + * Ali Bakhoda, George L. Yuan, at the University of British Columbia, + * Vancouver, BC V6T 1Z4 + */ + +#ifndef opcodes_h_included +#define opcodes_h_included + +enum opcode_t { +#define OP_DEF(OP,FUNC,STR,DST,CLASSIFICATION) OP, +#include "opcodes.def" + NUM_OPCODES +#undef OP_DEF +}; + +enum special_regs { + CLOCK_ID, + CTA_ID, + NTID_ID, + GRIDID_ID, + NCTAID_ID, + TID_ID +}; + +#endif diff --git a/src/cuda-sim/ptx-stats.cc b/src/cuda-sim/ptx-stats.cc new file mode 100644 index 0000000..2d18286 --- /dev/null +++ b/src/cuda-sim/ptx-stats.cc @@ -0,0 +1,298 @@ +/* + * Copyright © 2009 by Wilson W. L. Fung and the University of British Columbia, + * Vancouver, BC V6T 1Z4, All Rights Reserved. + * + * THIS IS A LEGAL DOCUMENT BY DOWNLOADING GPGPU-SIM, YOU ARE AGREEING TO THESE + * TERMS AND CONDITIONS. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * NOTE: The files libcuda/cuda_runtime_api.c and src/cuda-sim/cuda-math.h + * are derived from the CUDA Toolset available from http://www.nvidia.com/cuda + * (property of NVIDIA). The files benchmarks/BlackScholes/ and + * benchmarks/template/ are derived from the CUDA SDK available from + * http://www.nvidia.com/cuda (also property of NVIDIA). The files from + * src/intersim/ are derived from Booksim (a simulator provided with the + * textbook "Principles and Practices of Interconnection Networks" available + * from http://cva.stanford.edu/books/ppin/). As such, those files are bound by + * the corresponding legal terms and conditions set forth separately (original + * copyright notices are left in files from these sources and where we have + * modified a file our copyright notice appears before the original copyright + * notice). + * + * Using this version of GPGPU-Sim requires a complete installation of CUDA + * which is distributed seperately by NVIDIA under separate terms and + * conditions. To use this version of GPGPU-Sim with OpenCL requires a + * recent version of NVIDIA's drivers which support OpenCL. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the University of British Columbia nor the names of + * its contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * 4. This version of GPGPU-SIM is distributed freely for non-commercial use only. + * + * 5. No nonprofit user may place any restrictions on the use of this software, + * including as modified by the user, by any other authorized user. + * + * 6. GPGPU-SIM was developed primarily by Tor M. Aamodt, Wilson W. L. Fung, + * Ali Bakhoda, George L. Yuan, at the University of British Columbia, + * Vancouver, BC V6T 1Z4 + */ + +#include "ptx_ir.h" +#include "ptx_sim.h" +#include "ptx-stats.h" +#include "../option_parser.h" +#include <stdio.h> +#include <map> + +// external dependencies +extern function_info *g_func_info; + +// options +int enable_ptx_file_line_stats; +char * ptx_line_stats_filename = NULL; + +void ptx_file_line_stats_options(option_parser_t opp) +{ + option_parser_register(opp, "-enable_ptx_file_line_stats", OPT_BOOL, + &enable_ptx_file_line_stats, + "Turn on PTX source line statistic profiling. (1 = On)", "1"); + option_parser_register(opp, "-ptx_line_stats_filename", OPT_CSTR, + &ptx_line_stats_filename, + "Output file for PTX source line statistics.", "gpgpu_inst_stats.txt"); +} + +// implementations + +// defining a PTX source line = filename + line number +class ptx_file_line +{ +public: + ptx_file_line(const char* s, int l){ + if( s == NULL ) + st = "NULL_NAME"; + else + st = s; + line = l; + } + + bool operator<(const ptx_file_line &other) const { + if( st == other.st ) { + if( line < other.line ) + return true; + else + return false; + } else { + return st < other.st; + } + } + + bool operator==(const ptx_file_line &other) const { + return (st==other.st) and (line==other.line); + } + + std::string st; + unsigned line; +}; + +// holds all statistics collected for a singe PTX source line +class ptx_file_line_stats +{ +public: + ptx_file_line_stats() + : exec_count(0), latency(0), dram_traffic(0), + smem_n_way_bank_conflict_total(0), smem_warp_count(0), + gmem_n_access_total(0), gmem_warp_count(0), exposed_latency(0), + warp_divergence(0) + { } + + unsigned long exec_count; + unsigned long long latency; + unsigned long long dram_traffic; + unsigned long long smem_n_way_bank_conflict_total; // total number of banks accessed by this instruction + unsigned long smem_warp_count; // number of warps accessing shared memory + unsigned long long gmem_n_access_total; // number of uncoalesced access in total from this instruction + unsigned long gmem_warp_count; // number of warps causing these uncoalesced access + unsigned long long exposed_latency; // latency exposed as pipeline bubbles (attributed to this instruction) + unsigned long long warp_divergence; // number of warp divergence occured at this instruction +}; + +static std::map<ptx_file_line, ptx_file_line_stats> ptx_file_line_stats_tracker; + +// output statistics to a file +void ptx_file_line_stats_write_file() +{ + // check if stat collection is turned on + if (enable_ptx_file_line_stats == 0) return; + + std::map<ptx_file_line, ptx_file_line_stats>::iterator it; + FILE * pfile; + + pfile = fopen(ptx_line_stats_filename, "w"); + fprintf(pfile,"kernel line : count latency dram_traffic smem_bk_conflicts smem_warp gmem_access_generated gmem_warp exposed_latency warp_divergence\n"); + for( it=ptx_file_line_stats_tracker.begin(); it != ptx_file_line_stats_tracker.end(); it++ ) { + fprintf(pfile, "%s %i : ", it->first.st.c_str(), it->first.line); + fprintf(pfile, "%lu ", it->second.exec_count); + fprintf(pfile, "%llu ", it->second.latency); + fprintf(pfile, "%llu ", it->second.dram_traffic); + fprintf(pfile, "%llu ", it->second.smem_n_way_bank_conflict_total); + fprintf(pfile, "%lu ", it->second.smem_warp_count); + fprintf(pfile, "%llu ", it->second.gmem_n_access_total); + fprintf(pfile, "%lu ", it->second.gmem_warp_count); + fprintf(pfile, "%llu ", it->second.exposed_latency); + fprintf(pfile, "%llu ", it->second.warp_divergence); + fprintf(pfile, "\n"); + } + fflush(pfile); + fclose(pfile); +} + +// attribute one more execution count to this ptx instruction +// counting the number of threads (not warps) executing this instruction +void ptx_file_line_stats_add_exec_count(const ptx_instruction *pInsn) +{ + ptx_file_line_stats_tracker[ptx_file_line(pInsn->source_file(), pInsn->source_line())].exec_count += 1; +} + +// attribute pipeline latency to this ptx instruction (specified by the pc) +// pipeline latency is the number of cycles a warp with this instruction spent in the pipeline +void ptx_file_line_stats_add_latency(void * ptx_thd, unsigned pc, unsigned latency) +{ + const ptx_instruction *pInsn = function_info::pc_to_instruction(pc); + + ptx_file_line_stats_tracker[ptx_file_line(pInsn->source_file(), pInsn->source_line())].latency += latency; +} + +// attribute dram traffic to this ptx instruction (specified by the pc) +// dram traffic is counted in number of requests +void ptx_file_line_stats_add_dram_traffic(unsigned pc, unsigned dram_traffic) +{ + const ptx_instruction *pInsn = function_info::pc_to_instruction(pc); + + ptx_file_line_stats_tracker[ptx_file_line(pInsn->source_file(), pInsn->source_line())].dram_traffic += dram_traffic; +} + +// attribute the number of shared memory access cycles to a ptx instruction +// counts both the number of warps doing shared memory access and the number of cycles involved +void ptx_file_line_stats_add_smem_bank_conflict(void * ptx_thd, unsigned pc, unsigned n_way_bkconflict) +{ + const ptx_instruction *pInsn = function_info::pc_to_instruction(pc); + + ptx_file_line_stats& line_stats = ptx_file_line_stats_tracker[ptx_file_line(pInsn->source_file(), pInsn->source_line())]; + line_stats.smem_n_way_bank_conflict_total += n_way_bkconflict; + line_stats.smem_warp_count += 1; +} + +// attribute a non-coalesced mem access to a ptx instruction +// counts both the number of warps causing this and the number of memory requests generated +void ptx_file_line_stats_add_uncoalesced_gmem(void * ptx_thd, unsigned pc, unsigned n_access) +{ + const ptx_instruction *pInsn = function_info::pc_to_instruction(pc); + + ptx_file_line_stats& line_stats = ptx_file_line_stats_tracker[ptx_file_line(pInsn->source_file(), pInsn->source_line())]; + line_stats.gmem_n_access_total += n_access; + line_stats.gmem_warp_count += 1; +} + +// a class that tracks the inflight memory instructions of a shader core +// and attributes exposed latency to those instructions when signaled to do so +class ptx_inflight_memory_insn_tracker +{ +public: + typedef std::map<const ptx_instruction *, int> insn_count_map; + + void add_count(const ptx_instruction * pInsn, int count = 1) + { + ptx_inflight_memory_insns[pInsn] += count; + } + + void sub_count(const ptx_instruction * pInsn, int count = 1) + { + insn_count_map::iterator i_insncount; + i_insncount = ptx_inflight_memory_insns.find(pInsn); + + assert(i_insncount != ptx_inflight_memory_insns.end()); + + i_insncount->second -= count; + + if (i_insncount->second <= 0) { + ptx_inflight_memory_insns.erase(i_insncount); + } + } + + void attribute_exposed_latency(int count = 1) + { + insn_count_map &exlat_insnmap = ptx_inflight_memory_insns; + insn_count_map::const_iterator i_exlatinsn; + + i_exlatinsn = exlat_insnmap.begin(); + for (; i_exlatinsn != exlat_insnmap.end(); ++i_exlatinsn) { + const ptx_instruction *pInsn = i_exlatinsn->first; + ptx_file_line_stats& line_stats = ptx_file_line_stats_tracker[ptx_file_line(pInsn->source_file(), pInsn->source_line())]; + line_stats.exposed_latency += count; + } + } + + insn_count_map ptx_inflight_memory_insns; +}; + +static ptx_inflight_memory_insn_tracker *inflight_mem_tracker = NULL; + +void ptx_file_line_stats_create_exposed_latency_tracker(int n_shader_cores) +{ + inflight_mem_tracker = new ptx_inflight_memory_insn_tracker[n_shader_cores]; +} + +// add an inflight memory instruction +void ptx_file_line_stats_add_inflight_memory_insn(int sc_id, unsigned pc) +{ + const ptx_instruction *pInsn = function_info::pc_to_instruction(pc); + + inflight_mem_tracker[sc_id].add_count(pInsn); +} + +// remove an inflight memory instruction +void ptx_file_line_stats_sub_inflight_memory_insn(int sc_id, unsigned pc) +{ + const ptx_instruction *pInsn = function_info::pc_to_instruction(pc); + + inflight_mem_tracker[sc_id].sub_count(pInsn); +} + +// attribute an empty cycle in the pipeline (exposed latency) to the ptx memory instructions in flight +void ptx_file_line_stats_commit_exposed_latency(int sc_id, int exposed_latency) +{ + assert(exposed_latency > 0); + inflight_mem_tracker[sc_id].attribute_exposed_latency(exposed_latency); +} + +// attribute the number of warp divergence to a ptx instruction +void ptx_file_line_stats_add_warp_divergence(unsigned pc, unsigned n_way_divergence) +{ + const ptx_instruction *pInsn = function_info::pc_to_instruction(pc); + + ptx_file_line_stats& line_stats = ptx_file_line_stats_tracker[ptx_file_line(pInsn->source_file(), pInsn->source_line())]; + line_stats.warp_divergence += n_way_divergence; +} + diff --git a/src/cuda-sim/ptx-stats.h b/src/cuda-sim/ptx-stats.h new file mode 100644 index 0000000..200f7a5 --- /dev/null +++ b/src/cuda-sim/ptx-stats.h @@ -0,0 +1,92 @@ +/* + * Copyright © 2009 by Wilson W. L. Fung and the University of British Columbia, + * Vancouver, BC V6T 1Z4, All Rights Reserved. + * + * THIS IS A LEGAL DOCUMENT BY DOWNLOADING GPGPU-SIM, YOU ARE AGREEING TO THESE + * TERMS AND CONDITIONS. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * NOTE: The files libcuda/cuda_runtime_api.c and src/cuda-sim/cuda-math.h + * are derived from the CUDA Toolset available from http://www.nvidia.com/cuda + * (property of NVIDIA). The files benchmarks/BlackScholes/ and + * benchmarks/template/ are derived from the CUDA SDK available from + * http://www.nvidia.com/cuda (also property of NVIDIA). The files from + * src/intersim/ are derived from Booksim (a simulator provided with the + * textbook "Principles and Practices of Interconnection Networks" available + * from http://cva.stanford.edu/books/ppin/). As such, those files are bound by + * the corresponding legal terms and conditions set forth separately (original + * copyright notices are left in files from these sources and where we have + * modified a file our copyright notice appears before the original copyright + * notice). + * + * Using this version of GPGPU-Sim requires a complete installation of CUDA + * which is distributed seperately by NVIDIA under separate terms and + * conditions. To use this version of GPGPU-Sim with OpenCL requires a + * recent version of NVIDIA's drivers which support OpenCL. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the University of British Columbia nor the names of + * its contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * 4. This version of GPGPU-SIM is distributed freely for non-commercial use only. + * + * 5. No nonprofit user may place any restrictions on the use of this software, + * including as modified by the user, by any other authorized user. + * + * 6. GPGPU-SIM was developed primarily by Tor M. Aamodt, Wilson W. L. Fung, + * Ali Bakhoda, George L. Yuan, at the University of British Columbia, + * Vancouver, BC V6T 1Z4 + */ + +#pragma once + +#include "../option_parser.h" + +extern int enable_ptx_file_line_stats; + +// set options +void ptx_file_line_stats_options(option_parser_t opp); + +// output stats to a file +void ptx_file_line_stats_write_file(); + +#ifdef __cplusplus +// stat collection interface to cuda-sim +class ptx_instruction; +void ptx_file_line_stats_add_exec_count(const ptx_instruction *pInsn); +#endif + +// stat collection interface to gpgpu-sim +void ptx_file_line_stats_add_latency(void * ptx_thd, unsigned pc, unsigned latency); +void ptx_file_line_stats_add_dram_traffic(unsigned pc, unsigned dram_traffic); +void ptx_file_line_stats_add_smem_bank_conflict(void * ptx_thd, unsigned pc, unsigned n_way_bkconflict); +void ptx_file_line_stats_add_uncoalesced_gmem(void * ptx_thd, unsigned pc, unsigned n_access); + +void ptx_file_line_stats_create_exposed_latency_tracker(int n_shader_cores); +void ptx_file_line_stats_add_inflight_memory_insn(int sc_id, unsigned pc); +void ptx_file_line_stats_sub_inflight_memory_insn(int sc_id, unsigned pc); +void ptx_file_line_stats_commit_exposed_latency(int sc_id, int exposed_latency); + +void ptx_file_line_stats_add_warp_divergence(unsigned pc, unsigned n_way_divergence); + diff --git a/src/cuda-sim/ptx.l b/src/cuda-sim/ptx.l new file mode 100644 index 0000000..d764c7f --- /dev/null +++ b/src/cuda-sim/ptx.l @@ -0,0 +1,336 @@ +/* + * Copyright (c) 2009 by Tor M. Aamodt and the University of British Columbia + * Vancouver, BC V6T 1Z4 + * All Rights Reserved. + * + * THIS IS A LEGAL DOCUMENT BY DOWNLOADING GPGPU-SIM, YOU ARE AGREEING TO THESE + * TERMS AND CONDITIONS. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * NOTE: The files libcuda/cuda_runtime_api.c and src/cuda-sim/cuda-math.h + * are derived from the CUDA Toolset available from http://www.nvidia.com/cuda + * (property of NVIDIA). The files benchmarks/BlackScholes/ and + * benchmarks/template/ are derived from the CUDA SDK available from + * http://www.nvidia.com/cuda (also property of NVIDIA). The files from + * src/intersim/ are derived from Booksim (a simulator provided with the + * textbook "Principles and Practices of Interconnection Networks" available + * from http://cva.stanford.edu/books/ppin/). As such, those files are bound by + * the corresponding legal terms and conditions set forth separately (original + * copyright notices are left in files from these sources and where we have + * modified a file our copyright notice appears before the original copyright + * notice). + * + * Using this version of GPGPU-Sim requires a complete installation of CUDA + * which is distributed seperately by NVIDIA under separate terms and + * conditions. To use this version of GPGPU-Sim with OpenCL requires a + * recent version of NVIDIA's drivers which support OpenCL. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the University of British Columbia nor the names of + * its contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * 4. This version of GPGPU-SIM is distributed freely for non-commercial use only. + * + * 5. No nonprofit user may place any restrictions on the use of this software, + * including as modified by the user, by any other authorized user. + * + * 6. GPGPU-SIM was developed primarily by Tor M. Aamodt, Wilson W. L. Fung, + * Ali Bakhoda, George L. Yuan, at the University of British Columbia, + * Vancouver, BC V6T 1Z4 + */ + +%option noyywrap +%option yylineno +%option prefix="ptx_" +%{ +#include "opcodes.h" +#include "ptx.tab.h" +#include <string.h> + +char linebuf[1024]; +unsigned col = 0; +#define TC col+=strlen(ptx_text); +int ptx_error( const char *s ); +%} + +%s IN_STRING +%s IN_COMMENT +%x NOT_OPCODE +%% + +abs TC; ptx_lval.int_value = ABS_OP; return OPCODE; +add TC; ptx_lval.int_value = ADD_OP; return OPCODE; +and TC; ptx_lval.int_value = AND_OP; return OPCODE; +atom TC; ptx_lval.int_value = ATOM_OP; return OPCODE; +bar.sync TC; ptx_lval.int_value = BAR_OP; return OPCODE; +bra TC; ptx_lval.int_value = BRA_OP; return OPCODE; +call TC; ptx_lval.int_value = CALL_OP; return OPCODE; +cnot TC; ptx_lval.int_value = CNOT_OP; return OPCODE; +cos TC; ptx_lval.int_value = COS_OP; return OPCODE; +cvt TC; ptx_lval.int_value = CVT_OP; return OPCODE; +div TC; ptx_lval.int_value = DIV_OP; return OPCODE; +ex2 TC; ptx_lval.int_value = EX2_OP; return OPCODE; +exit TC; ptx_lval.int_value = EXIT_OP; return OPCODE; +ld TC; ptx_lval.int_value = LD_OP; return OPCODE; +ld.volatile TC; ptx_lval.int_value = LD_OP; return OPCODE; +lg2 TC; ptx_lval.int_value = LG2_OP; return OPCODE; +mad24 TC; ptx_lval.int_value = MAD24_OP; return OPCODE; +mad TC; ptx_lval.int_value = MAD_OP; return OPCODE; +max TC; ptx_lval.int_value = MAX_OP; return OPCODE; +min TC; ptx_lval.int_value = MIN_OP; return OPCODE; +mov TC; ptx_lval.int_value = MOV_OP; return OPCODE; +mul24 TC; ptx_lval.int_value = MUL24_OP; return OPCODE; +mul TC; ptx_lval.int_value = MUL_OP; return OPCODE; +neg TC; ptx_lval.int_value = NEG_OP; return OPCODE; +not TC; ptx_lval.int_value = NOT_OP; return OPCODE; +or TC; ptx_lval.int_value = OR_OP; return OPCODE; +rcp TC; ptx_lval.int_value = RCP_OP; return OPCODE; +rem TC; ptx_lval.int_value = REM_OP; return OPCODE; +ret TC; ptx_lval.int_value = RET_OP; return OPCODE; +rsqrt TC; ptx_lval.int_value = RSQRT_OP; return OPCODE; +sad TC; ptx_lval.int_value = SAD_OP; return OPCODE; +selp TC; ptx_lval.int_value = SELP_OP; return OPCODE; +setp TC; ptx_lval.int_value = SETP_OP; return OPCODE; +set TC; ptx_lval.int_value = SET_OP; return OPCODE; +shl TC; ptx_lval.int_value = SHL_OP; return OPCODE; +shr TC; ptx_lval.int_value = SHR_OP; return OPCODE; +sin TC; ptx_lval.int_value = SIN_OP; return OPCODE; +slct TC; ptx_lval.int_value = SLCT_OP; return OPCODE; +sqrt TC; ptx_lval.int_value = SQRT_OP; return OPCODE; +st TC; ptx_lval.int_value = ST_OP; return OPCODE; +st.volatile TC; ptx_lval.int_value = ST_OP; return OPCODE; +sub TC; ptx_lval.int_value = SUB_OP; return OPCODE; +tex TC; BEGIN(NOT_OPCODE); ptx_lval.int_value = TEX_OP; return OPCODE; +trap TC; ptx_lval.int_value = TRAP_OP; return OPCODE; +vote TC; ptx_lval.int_value = VOTE_OP; return OPCODE; +xor TC; ptx_lval.int_value = XOR_OP; return OPCODE; +membar TC; ptx_lval.int_value = MEMBAR_OP; return OPCODE; +red TC; ptx_lval.int_value = RED_OP; return OPCODE; +brkpt TC; ptx_lval.int_value = BRKPT_OP; return OPCODE; +addc TC; ptx_lval.int_value = ADDC_OP; return OPCODE; + +<INITIAL,NOT_OPCODE>{ + +\.align TC; return ALIGN_DIRECTIVE; +\.byte TC; return BYTE_DIRECTIVE; +\.const\[[0-9]+\] TC; return CONST_DIRECTIVE; +\.const TC; return CONST_DIRECTIVE; +\.entry TC; return ENTRY_DIRECTIVE; +\.extern TC; return EXTERN_DIRECTIVE; +\.file TC; return FILE_DIRECTIVE; +\.func TC; return FUNC_DIRECTIVE; +\.global TC; return GLOBAL_DIRECTIVE; +\.local TC; return LOCAL_DIRECTIVE; +\.loc TC; return LOC_DIRECTIVE; +\.param TC; return PARAM_DIRECTIVE; +\.reg TC; return REG_DIRECTIVE; +\.section TC; return SECTION_DIRECTIVE; +\.shared TC; return SHARED_DIRECTIVE; +\.sreg TC; return SREG_DIRECTIVE; +\.struct TC; return STRUCT_DIRECTIVE; +\.surf TC; return SURF_DIRECTIVE; +\.target TC; return TARGET_DIRECTIVE; +\.tex TC; BEGIN(NOT_OPCODE); return TEX_DIRECTIVE; +\.union TC; return UNION_DIRECTIVE; +\.version TC; return VERSION_DIRECTIVE; +\.visible TC; return VISIBLE_DIRECTIVE; + +\.maxntid TC; return MAXNTID_DIRECTIVE; + +"%clock" TC; ptx_lval.int_value = CLOCK_ID; return SPECIAL_REGISTER; +"%ctaid" TC; ptx_lval.int_value = CTA_ID; return SPECIAL_REGISTER; +"%ntid" TC; ptx_lval.int_value = NTID_ID; return SPECIAL_REGISTER; +"%gridid" TC; ptx_lval.int_value = GRIDID_ID; return SPECIAL_REGISTER; +"%nctaid" TC; ptx_lval.int_value = NCTAID_ID; return SPECIAL_REGISTER; +"%tid" TC; ptx_lval.int_value = TID_ID; return SPECIAL_REGISTER; + +[_A-Za-z$%][_0-9A-Za-z$]* TC; ptx_lval.string_value = strdup(yytext); /*printf("\n<<identifier=\"%s\">>\n", yytext); fflush(stdout);*/ return IDENTIFIER; + +[0-9]+\.[0-9]+ TC; sscanf(yytext,"%lf", &ptx_lval.double_value); return DOUBLE_OPERAND; +[-]{0,1}[0-9]+ TC; ptx_lval.int_value = atoi(yytext); return INT_OPERAND; +0[xX][0-9]+ TC; sscanf(yytext,"%x", &ptx_lval.int_value); return INT_OPERAND; +0[fF][0-9a-fA-F]{8} TC; sscanf(yytext+2,"%x", (unsigned*)(void*)&ptx_lval.float_value); return FLOAT_OPERAND; +0[dD][0-9a-fA-F]{16} TC; sscanf(yytext+2,"%Lx", (unsigned long long*)(void*)&ptx_lval.double_value); return DOUBLE_OPERAND; + +\.s8 TC; return S8_TYPE; +\.s16 TC; return S16_TYPE; +\.s32 TC; return S32_TYPE; +\.s64 TC; return S64_TYPE; +\.u8 TC; return U8_TYPE; +\.u16 TC; return U16_TYPE; +\.u32 TC; return U32_TYPE; +\.u64 TC; return U64_TYPE; +\.f16 TC; return F16_TYPE; +\.f32 TC; return F32_TYPE; +\.f64 TC; return F64_TYPE; +\.b8 TC; return B8_TYPE; +\.b16 TC; return B16_TYPE; +\.b32 TC; return B32_TYPE; +\.b64 TC; return B64_TYPE; +\.pred TC; return PRED_TYPE; + +\.v2 TC; return V2_TYPE; +\.v3 TC; return V3_TYPE; +\.v4 TC; return V4_TYPE; + +\.equ TC; return EQU_OPTION; +\.neu TC; return NEU_OPTION; +\.ltu TC; return LTU_OPTION; +\.leu TC; return LEU_OPTION; +\.gtu TC; return GTU_OPTION; +\.geu TC; return GEU_OPTION; +\.num TC; return NUM_OPTION; +\.nan TC; return NAN_OPTION; + +\.sat TC; return SAT_OPTION; + +\.eq TC; return EQ_OPTION; +\.ne TC; return NE_OPTION; +\.lt TC; return LT_OPTION; +\.le TC; return LE_OPTION; +\.gt TC; return GT_OPTION; +\.ge TC; return GE_OPTION; + +\.lo TC; return LO_OPTION; +\.ls TC; return LS_OPTION; +\.hi TC; return HI_OPTION; +\.hs TC; return HS_OPTION; + + +\.rni TC; return RNI_OPTION; +\.rzi TC; return RZI_OPTION; +\.rmi TC; return RMI_OPTION; +\.rpi TC; return RPI_OPTION; + +\.rn TC; return RN_OPTION; +\.rz TC; return RZ_OPTION; +\.rm TC; return RM_OPTION; +\.rp TC; return RP_OPTION; + +\.ftz TC; return FTZ_OPTION; + +\.wide TC; return WIDE_OPTION; +\.uni TC; return UNI_OPTION; + +\.approx TC; return APPROX_OPTION; +\.full TC; return FULL_OPTION; + +\.any TC; return ANY_OPTION; +\.all TC; return ALL_OPTION; +\.gl TC; return GLOBAL_OPTION; +\.cta TC; return CTA_OPTION; + +\.and TC; return ATOMIC_AND; +\.or TC; return ATOMIC_OR; +\.xor TC; return ATOMIC_XOR; +\.cas TC; return ATOMIC_CAS; +\.exch TC; return ATOMIC_EXCH; +\.add TC; return ATOMIC_ADD; +\.inc TC; return ATOMIC_INC; +\.dec TC; return ATOMIC_DEC; +\.min TC; return ATOMIC_MIN; +\.max TC; return ATOMIC_MAX; + +\.1d TC; return GEOM_MODIFIER_1D; +\.2d TC; return GEOM_MODIFIER_2D; +\.3d TC; return GEOM_MODIFIER_3D; + +\.0 TC; ptx_lval.int_value = 0; return DIMENSION_MODIFIER; +\.1 TC; ptx_lval.int_value = 1; return DIMENSION_MODIFIER; +\.2 TC; ptx_lval.int_value = 2; return DIMENSION_MODIFIER; +\.x TC; ptx_lval.int_value = 0; return DIMENSION_MODIFIER; +\.y TC; ptx_lval.int_value = 1; return DIMENSION_MODIFIER; +\.z TC; ptx_lval.int_value = 2; return DIMENSION_MODIFIER; + +"+" TC; return PLUS; +"," TC; return COMMA; +"@" TC; return PRED; +"[" TC; return LEFT_SQUARE_BRACKET; +"]" TC; return RIGHT_SQUARE_BRACKET; +"<" TC; return LEFT_ANGLE_BRACKET; +">" TC; return RIGHT_ANGLE_BRACKET; +"(" TC; return LEFT_PAREN; +")" TC; return RIGHT_PAREN; +":" TC; return COLON; +";" TC; BEGIN(INITIAL); return SEMI_COLON; +"!" TC; return EXCLAMATION; +"=" TC; return EQUALS; +"{" TC; return LEFT_BRACE; +"}" TC; return RIGHT_BRACE; +\. TC; return PERIOD; + +"//"[^\n]* TC; // eat single + +\n.* col=0; strncpy(linebuf, yytext + 1, 1024); yyless( 1 ); + +" " TC; +"\t" TC; + + +} + +<INITIAL>{ +"/*" BEGIN(IN_COMMENT); +} +<IN_COMMENT>{ +"*/" BEGIN(INITIAL); +[^*\n]+ // eat comment in chunks +"*" // eat the lone star +\n TC; +} + +<INITIAL>{ +"\"" BEGIN(IN_STRING); +} +<IN_STRING>{ +"\"" TC; BEGIN(INITIAL); return STRING; +[^\"]* TC; ptx_lval.string_value = strdup(yytext); +} + +<INITIAL,NOT_OPCODE>. TC; ptx_error((const char*)NULL); + +%% + +extern int g_error_detected; +extern const char *g_filename; + +int ptx_error( const char *s ) +{ + int i; + g_error_detected = 1; + fflush(stdout); + if( s != NULL ) + printf("%s:%u: Syntax error:\n\n", g_filename, ptx_lineno ); + printf(" %s\n", linebuf ); + printf(" "); + for( i=0; i < col-1; i++ ) { + if( linebuf[i] == '\t' ) printf("\t"); + else printf(" "); + } + + printf("^\n\n"); + fflush(stdout); + //exit(1); + return 0; +} diff --git a/src/cuda-sim/ptx.y b/src/cuda-sim/ptx.y new file mode 100644 index 0000000..2e1cb0f --- /dev/null +++ b/src/cuda-sim/ptx.y @@ -0,0 +1,470 @@ +/* + * Copyright (c) 2009 by Tor M. Aamodt, George L. Yuan and the + * University of British Columbia + * Vancouver, BC V6T 1Z4 + * All Rights Reserved. + * + * THIS IS A LEGAL DOCUMENT BY DOWNLOADING GPGPU-SIM, YOU ARE AGREEING TO THESE + * TERMS AND CONDITIONS. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * NOTE: The files libcuda/cuda_runtime_api.c and src/cuda-sim/cuda-math.h + * are derived from the CUDA Toolset available from http://www.nvidia.com/cuda + * (property of NVIDIA). The files benchmarks/BlackScholes/ and + * benchmarks/template/ are derived from the CUDA SDK available from + * http://www.nvidia.com/cuda (also property of NVIDIA). The files from + * src/intersim/ are derived from Booksim (a simulator provided with the + * textbook "Principles and Practices of Interconnection Networks" available + * from http://cva.stanford.edu/books/ppin/). As such, those files are bound by + * the corresponding legal terms and conditions set forth separately (original + * copyright notices are left in files from these sources and where we have + * modified a file our copyright notice appears before the original copyright + * notice). + * + * Using this version of GPGPU-Sim requires a complete installation of CUDA + * which is distributed seperately by NVIDIA under separate terms and + * conditions. To use this version of GPGPU-Sim with OpenCL requires a + * recent version of NVIDIA's drivers which support OpenCL. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the University of British Columbia nor the names of + * its contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * 4. This version of GPGPU-SIM is distributed freely for non-commercial use only. + * + * 5. No nonprofit user may place any restrictions on the use of this software, + * including as modified by the user, by any other authorized user. + * + * 6. GPGPU-SIM was developed primarily by Tor M. Aamodt, Wilson W. L. Fung, + * Ali Bakhoda, George L. Yuan, at the University of British Columbia, + * Vancouver, BC V6T 1Z4 + */ + +%union { + double double_value; + float float_value; + int int_value; + char * string_value; + void * ptr_value; +} + +%token <string_value> STRING +%token <int_value> OPCODE +%token ALIGN_DIRECTIVE +%token BYTE_DIRECTIVE +%token CONST_DIRECTIVE +%token ENTRY_DIRECTIVE +%token EXTERN_DIRECTIVE +%token FILE_DIRECTIVE +%token FUNC_DIRECTIVE +%token GLOBAL_DIRECTIVE +%token LOCAL_DIRECTIVE +%token LOC_DIRECTIVE +%token PARAM_DIRECTIVE +%token REG_DIRECTIVE +%token SECTION_DIRECTIVE +%token SHARED_DIRECTIVE +%token SREG_DIRECTIVE +%token STRUCT_DIRECTIVE +%token SURF_DIRECTIVE +%token TARGET_DIRECTIVE +%token TEX_DIRECTIVE +%token UNION_DIRECTIVE +%token VERSION_DIRECTIVE +%token VISIBLE_DIRECTIVE +%token MAXNTID_DIRECTIVE +%token <string_value> IDENTIFIER +%token <int_value> INT_OPERAND +%token <float_value> FLOAT_OPERAND +%token <double_value> DOUBLE_OPERAND +%token S8_TYPE +%token S16_TYPE +%token S32_TYPE +%token S64_TYPE +%token U8_TYPE +%token U16_TYPE +%token U32_TYPE +%token U64_TYPE +%token F16_TYPE +%token F32_TYPE +%token F64_TYPE +%token B8_TYPE +%token B16_TYPE +%token B32_TYPE +%token B64_TYPE +%token PRED_TYPE +%token V2_TYPE +%token V3_TYPE +%token V4_TYPE +%token COMMA +%token PRED +%token EQ_OPTION +%token NE_OPTION +%token LT_OPTION +%token LE_OPTION +%token GT_OPTION +%token GE_OPTION +%token LO_OPTION +%token LS_OPTION +%token HI_OPTION +%token HS_OPTION +%token EQU_OPTION +%token NEU_OPTION +%token LTU_OPTION +%token LEU_OPTION +%token GTU_OPTION +%token GEU_OPTION +%token NUM_OPTION +%token NAN_OPTION +%token LEFT_SQUARE_BRACKET +%token RIGHT_SQUARE_BRACKET +%token WIDE_OPTION +%token <int_value> SPECIAL_REGISTER +%token PLUS +%token COLON +%token SEMI_COLON +%token EXCLAMATION +%token RIGHT_BRACE +%token LEFT_BRACE +%token EQUALS +%token PERIOD +%token <int_value> DIMENSION_MODIFIER +%token RN_OPTION +%token RZ_OPTION +%token RM_OPTION +%token RP_OPTION +%token RNI_OPTION +%token RZI_OPTION +%token RMI_OPTION +%token RPI_OPTION +%token UNI_OPTION +%token GEOM_MODIFIER_1D +%token GEOM_MODIFIER_2D +%token GEOM_MODIFIER_3D +%token SAT_OPTION +%token FTZ_OPTION +%token ATOMIC_AND +%token ATOMIC_OR +%token ATOMIC_XOR +%token ATOMIC_CAS +%token ATOMIC_EXCH +%token ATOMIC_ADD +%token ATOMIC_INC +%token ATOMIC_DEC +%token ATOMIC_MIN +%token ATOMIC_MAX +%token LEFT_ANGLE_BRACKET +%token RIGHT_ANGLE_BRACKET +%token LEFT_PAREN +%token RIGHT_PAREN +%token APPROX_OPTION +%token FULL_OPTION +%token ANY_OPTION +%token ALL_OPTION +%token GLOBAL_OPTION +%token CTA_OPTION +%type <int_value> function_decl_header +%type <ptr_value> function_decl + +%{ + #include "ptx_ir.h" + #include <stdlib.h> + #include <string.h> + #include <math.h> + void syntax_not_implemented(); + extern int g_func_decl; + int ptx_lex(void); + int ptx_error(const char *); +%} + +%% + +input: /* empty */ + | input directive_statement + | input function_defn + | input function_decl + ; + +function_defn: function_decl { set_symtab($1); } LEFT_BRACE statement_list RIGHT_BRACE { end_function(); } + | function_decl { set_symtab($1); } block_spec LEFT_BRACE statement_list RIGHT_BRACE { end_function(); } + ; + +block_spec: MAXNTID_DIRECTIVE INT_OPERAND COMMA INT_OPERAND COMMA INT_OPERAND + ; + +function_decl: function_decl_header LEFT_PAREN { start_function($1); } param_entry RIGHT_PAREN function_ident_param { $$ = reset_symtab(); } + | function_decl_header { start_function($1); } function_ident_param { $$ = reset_symtab(); } + | function_decl_header { start_function($1); add_function_name(""); g_func_decl=0; $$ = reset_symtab(); } + ; + +function_ident_param: IDENTIFIER { add_function_name($1); } LEFT_PAREN param_list RIGHT_PAREN { g_func_decl=0; } + | IDENTIFIER { add_function_name($1); g_func_decl=0; } + ; + +function_decl_header: ENTRY_DIRECTIVE { $$ = 1; g_func_decl=1; } + | FUNC_DIRECTIVE { $$ = 0; g_func_decl=1; } + ; + +param_list: param_entry { add_directive(); } + | param_list COMMA param_entry { add_directive(); } + +param_entry: PARAM_DIRECTIVE { add_space_spec(PARAM_DIRECTIVE); } variable_spec identifier_spec { add_function_arg(); } + | REG_DIRECTIVE { add_space_spec(REG_DIRECTIVE); } variable_spec identifier_spec { add_function_arg(); } + +statement_list: directive_statement { add_directive(); } + | instruction_statement { add_instruction(); } + | statement_list directive_statement { add_directive(); } + | statement_list instruction_statement { add_instruction(); } + ; + +directive_statement: variable_declaration SEMI_COLON + | VERSION_DIRECTIVE DOUBLE_OPERAND + | TARGET_DIRECTIVE IDENTIFIER COMMA IDENTIFIER + | TARGET_DIRECTIVE IDENTIFIER + | FILE_DIRECTIVE INT_OPERAND STRING { add_file($2,$3); } + | LOC_DIRECTIVE INT_OPERAND INT_OPERAND INT_OPERAND + ; + +variable_declaration: variable_spec identifier_list { add_variables(); } + | variable_spec identifier_spec EQUALS initializer_list { add_variables(); } + | variable_spec identifier_spec EQUALS literal_operand { add_variables(); } + ; + +variable_spec: var_spec_list { set_variable_type(); } + +identifier_list: identifier_spec + | identifier_list COMMA identifier_spec; + +identifier_spec: IDENTIFIER { add_identifier($1,0,NON_ARRAY_IDENTIFIER); } + | IDENTIFIER LEFT_ANGLE_BRACKET INT_OPERAND RIGHT_ANGLE_BRACKET { + int i,lbase,l; + char *id = NULL; + lbase = strlen($1); + for( i=0; i < $3; i++ ) { + l = lbase + (int)log10(i+1)+10; + id = malloc(l); + snprintf(id,l,"%s%u",$1,i); + add_identifier(id,0,NON_ARRAY_IDENTIFIER); + } + free($1); + } + | IDENTIFIER LEFT_SQUARE_BRACKET RIGHT_SQUARE_BRACKET { add_identifier($1,0,ARRAY_IDENTIFIER_NO_DIM); } + | IDENTIFIER LEFT_SQUARE_BRACKET INT_OPERAND RIGHT_SQUARE_BRACKET { add_identifier($1,$3,ARRAY_IDENTIFIER); } + ; + +var_spec_list: var_spec + | var_spec_list var_spec; + +var_spec: space_spec + | type_spec + | align_spec + | EXTERN_DIRECTIVE { add_extern_spec(); } + ; + +align_spec: ALIGN_DIRECTIVE INT_OPERAND { add_alignment_spec($2); } + +space_spec: REG_DIRECTIVE { add_space_spec(REG_DIRECTIVE); } + | SREG_DIRECTIVE { add_space_spec(SREG_DIRECTIVE); } + | addressable_spec + ; + +addressable_spec: CONST_DIRECTIVE { add_space_spec(CONST_DIRECTIVE); } + | GLOBAL_DIRECTIVE { add_space_spec(GLOBAL_DIRECTIVE); } + | LOCAL_DIRECTIVE { add_space_spec(LOCAL_DIRECTIVE); } + | PARAM_DIRECTIVE { add_space_spec(PARAM_DIRECTIVE); } + | SHARED_DIRECTIVE { add_space_spec(SHARED_DIRECTIVE); } + | SURF_DIRECTIVE { add_space_spec(SURF_DIRECTIVE); } + | TEX_DIRECTIVE { add_space_spec(TEX_DIRECTIVE); } + ; + +type_spec: scalar_type + | vector_spec scalar_type + ; + +vector_spec: V2_TYPE { add_option(V2_TYPE); } + | V3_TYPE { add_option(V3_TYPE); } + | V4_TYPE { add_option(V4_TYPE); } + ; + +scalar_type: S8_TYPE { add_scalar_type_spec( S8_TYPE ); } + | S16_TYPE { add_scalar_type_spec( S16_TYPE ); } + | S32_TYPE { add_scalar_type_spec( S32_TYPE ); } + | S64_TYPE { add_scalar_type_spec( S64_TYPE ); } + | U8_TYPE { add_scalar_type_spec( U8_TYPE ); } + | U16_TYPE { add_scalar_type_spec( U16_TYPE ); } + | U32_TYPE { add_scalar_type_spec( U32_TYPE ); } + | U64_TYPE { add_scalar_type_spec( U64_TYPE ); } + | F16_TYPE { add_scalar_type_spec( F16_TYPE ); } + | F32_TYPE { add_scalar_type_spec( F32_TYPE ); } + | F64_TYPE { add_scalar_type_spec( F64_TYPE ); } + | B8_TYPE { add_scalar_type_spec( B8_TYPE ); } + | B16_TYPE { add_scalar_type_spec( B16_TYPE ); } + | B32_TYPE { add_scalar_type_spec( B32_TYPE ); } + | B64_TYPE { add_scalar_type_spec( B64_TYPE ); } + | PRED_TYPE { add_scalar_type_spec( PRED_TYPE ); } + ; + +initializer_list: LEFT_BRACE literal_list RIGHT_BRACE { add_array_initializer(); } + | LEFT_BRACE initializer_list RIGHT_BRACE { syntax_not_implemented(); } + +literal_list: literal_operand + | literal_operand COMMA literal_list; + +instruction_statement: instruction SEMI_COLON + | IDENTIFIER COLON { add_label($1); } + | pred_spec instruction SEMI_COLON; + +instruction: opcode_spec LEFT_PAREN operand RIGHT_PAREN { set_return(); } COMMA operand COMMA LEFT_PAREN operand_list RIGHT_PAREN + | opcode_spec operand COMMA LEFT_PAREN operand_list RIGHT_PAREN + | opcode_spec operand_list + | opcode_spec + ; + +opcode_spec: OPCODE { add_opcode($1); } option_list + | OPCODE { add_opcode($1); } + +pred_spec: PRED IDENTIFIER { add_pred($2,0); } + | PRED EXCLAMATION IDENTIFIER { add_pred($3,1); } + ; + +option_list: option + | option option_list ; + +option: type_spec + | compare_spec + | addressable_spec + | rounding_mode + | UNI_OPTION { add_option(UNI_OPTION); } + | WIDE_OPTION { add_option(WIDE_OPTION); } + | ANY_OPTION { add_option(ANY_OPTION); } + | ALL_OPTION { add_option(ALL_OPTION); } + | GLOBAL_OPTION { add_option(GLOBAL_OPTION); } + | CTA_OPTION { add_option(CTA_OPTION); } + | GEOM_MODIFIER_1D { add_option(GEOM_MODIFIER_1D); } + | GEOM_MODIFIER_2D { add_option(GEOM_MODIFIER_2D); } + | GEOM_MODIFIER_3D { add_option(GEOM_MODIFIER_3D); } + | SAT_OPTION { add_option(SAT_OPTION); } + | FTZ_OPTION { add_option(FTZ_OPTION); } + | APPROX_OPTION { add_option(APPROX_OPTION); } + | FULL_OPTION { add_option(FULL_OPTION); } + | atomic_operation_spec ; + +atomic_operation_spec: ATOMIC_AND { add_option(ATOMIC_AND); } + | ATOMIC_OR { add_option(ATOMIC_OR); } + | ATOMIC_XOR { add_option(ATOMIC_XOR); } + | ATOMIC_CAS { add_option(ATOMIC_CAS); } + | ATOMIC_EXCH { add_option(ATOMIC_EXCH); } + | ATOMIC_ADD { add_option(ATOMIC_ADD); } + | ATOMIC_INC { add_option(ATOMIC_INC); } + | ATOMIC_DEC { add_option(ATOMIC_DEC); } + | ATOMIC_MIN { add_option(ATOMIC_MIN); } + | ATOMIC_MAX { add_option(ATOMIC_MAX); } + ; + +rounding_mode: floating_point_rounding_mode + | integer_rounding_mode; + +floating_point_rounding_mode: RN_OPTION { add_option(RN_OPTION); } + | RZ_OPTION { add_option(RZ_OPTION); } + | RM_OPTION { add_option(RM_OPTION); } + | RP_OPTION { add_option(RP_OPTION); } + ; + +integer_rounding_mode: RNI_OPTION { add_option(RNI_OPTION); } + | RZI_OPTION { add_option(RZI_OPTION); } + | RMI_OPTION { add_option(RMI_OPTION); } + | RPI_OPTION { add_option(RPI_OPTION); } + ; + +compare_spec:EQ_OPTION { add_option(EQ_OPTION); } + | NE_OPTION { add_option(NE_OPTION); } + | LT_OPTION { add_option(LT_OPTION); } + | LE_OPTION { add_option(LE_OPTION); } + | GT_OPTION { add_option(GT_OPTION); } + | GE_OPTION { add_option(GE_OPTION); } + | LO_OPTION { add_option(LO_OPTION); } + | LS_OPTION { add_option(LS_OPTION); } + | HI_OPTION { add_option(HI_OPTION); } + | HS_OPTION { add_option(HS_OPTION); } + | EQU_OPTION { add_option(EQU_OPTION); } + | NEU_OPTION { add_option(NEU_OPTION); } + | LTU_OPTION { add_option(LTU_OPTION); } + | LEU_OPTION { add_option(LEU_OPTION); } + | GTU_OPTION { add_option(GTU_OPTION); } + | GEU_OPTION { add_option(GEU_OPTION); } + | NUM_OPTION { add_option(NUM_OPTION); } + | NAN_OPTION { add_option(NAN_OPTION); } + ; + +operand_list: operand + | operand COMMA operand_list; + +operand: IDENTIFIER { add_scalar_operand( $1 ); } + | EXCLAMATION IDENTIFIER { add_neg_pred_operand( $2 ); } + | memory_operand + | literal_operand + | builtin_operand + | vector_operand + | tex_operand + | IDENTIFIER PLUS INT_OPERAND { add_address_operand($1,$3); } + ; + +vector_operand: LEFT_BRACE IDENTIFIER COMMA IDENTIFIER RIGHT_BRACE { add_2vector_operand($2,$4); } + | LEFT_BRACE IDENTIFIER COMMA IDENTIFIER COMMA IDENTIFIER RIGHT_BRACE { add_3vector_operand($2,$4,$6); } + | LEFT_BRACE IDENTIFIER COMMA IDENTIFIER COMMA IDENTIFIER COMMA IDENTIFIER RIGHT_BRACE { add_4vector_operand($2,$4,$6,$8); } + ; + +tex_operand: LEFT_SQUARE_BRACKET IDENTIFIER COMMA + LEFT_BRACE IDENTIFIER COMMA IDENTIFIER COMMA IDENTIFIER COMMA IDENTIFIER RIGHT_BRACE + RIGHT_SQUARE_BRACKET { + add_scalar_operand($2); + add_4vector_operand($5,$7,$9,$11); + } + +builtin_operand: SPECIAL_REGISTER DIMENSION_MODIFIER { add_builtin_operand($1,$2); } + | SPECIAL_REGISTER { add_builtin_operand($1,-1); } + ; + +memory_operand : LEFT_SQUARE_BRACKET address_expression RIGHT_SQUARE_BRACKET { add_memory_operand(); } + +literal_operand : INT_OPERAND { add_literal_int($1); } + | FLOAT_OPERAND { add_literal_float($1); } + | DOUBLE_OPERAND { add_literal_double($1); } + ; + +address_expression: IDENTIFIER { add_address_operand($1,0); } + | IDENTIFIER PLUS INT_OPERAND { add_address_operand($1,$3); } + ; + +%% + +extern int ptx_lineno; +extern const char *g_filename; + +void syntax_not_implemented() +{ + printf("Parse error (%s:%u): this syntax is not (yet) implemented:\n",g_filename,ptx_lineno); + ptx_error(NULL); + abort(); +} diff --git a/src/cuda-sim/ptx_ir.cc b/src/cuda-sim/ptx_ir.cc new file mode 100644 index 0000000..542426a --- /dev/null +++ b/src/cuda-sim/ptx_ir.cc @@ -0,0 +1,1283 @@ +/* + * Copyright (c) 2009 by Tor M. Aamodt, Wilson W. L. Fung, Ali Bakhoda, + * George L. Yuan and the University of British Columbia + * Vancouver, BC V6T 1Z4 + * All Rights Reserved. + * + * THIS IS A LEGAL DOCUMENT BY DOWNLOADING GPGPU-SIM, YOU ARE AGREEING TO THESE + * TERMS AND CONDITIONS. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * NOTE: The files libcuda/cuda_runtime_api.c and src/cuda-sim/cuda-math.h + * are derived from the CUDA Toolset available from http://www.nvidia.com/cuda + * (property of NVIDIA). The files benchmarks/BlackScholes/ and + * benchmarks/template/ are derived from the CUDA SDK available from + * http://www.nvidia.com/cuda (also property of NVIDIA). The files from + * src/intersim/ are derived from Booksim (a simulator provided with the + * textbook "Principles and Practices of Interconnection Networks" available + * from http://cva.stanford.edu/books/ppin/). As such, those files are bound by + * the corresponding legal terms and conditions set forth separately (original + * copyright notices are left in files from these sources and where we have + * modified a file our copyright notice appears before the original copyright + * notice). + * + * Using this version of GPGPU-Sim requires a complete installation of CUDA + * which is distributed seperately by NVIDIA under separate terms and + * conditions. To use this version of GPGPU-Sim with OpenCL requires a + * recent version of NVIDIA's drivers which support OpenCL. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the University of British Columbia nor the names of + * its contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * 4. This version of GPGPU-SIM is distributed freely for non-commercial use only. + * + * 5. No nonprofit user may place any restrictions on the use of this software, + * including as modified by the user, by any other authorized user. + * + * 6. GPGPU-SIM was developed primarily by Tor M. Aamodt, Wilson W. L. Fung, + * Ali Bakhoda, George L. Yuan, at the University of British Columbia, + * Vancouver, BC V6T 1Z4 + */ + +#include "ptx_ir.h" +#include "ptx.tab.h" +#include "opcodes.h" +#include <stdio.h> +#include <stdlib.h> +#include <list> +#include <assert.h> +#include <algorithm> +#include <stdarg.h> + +extern unsigned g_max_regs_per_thread; +extern "C" int ptx_error( const char *s ); +void gpgpu_ptx_sim_bindTextureToArray(const struct textureReference* texref, struct cudaArray* array); //texture functions +struct cudaArray* gpgpu_ptx_sim_accessArrayofTexture(struct textureReference* texref); +void gpgpu_ptx_sim_bindNameToTexture(const char* name, struct textureReference* texref); +struct textureReference* gpgpu_ptx_sim_accessTextureofName(const char* name); +const char* gpgpu_ptx_sim_findNamefromTexture(const struct textureReference* texref); +int gpgpu_ptx_sim_sizeofTexture(const char* name); + +// the program intermediate representation... +symbol_table *g_global_symbol_table = NULL; +symbol_table *g_current_symbol_table = NULL; +std::list<ptx_instruction*> g_instructions; +symbol *g_last_symbol = NULL; +std::map<std::string,symbol_table*> g_sym_name_to_symbol_table; + +int g_error_detected = 0; + +// type specifier stuff: +int g_space_spec = -1; +int g_scalar_type_spec = -1; +int g_vector_spec = -1; +int g_alignment_spec = -1; +int g_extern_spec = 0; + +// variable declaration stuff: +type_info *g_var_type = NULL; + +// instruction definition stuff: +const symbol *g_pred; +int g_neg_pred; +symbol *g_label; +int g_opcode = -1; +std::list<operand_info> g_operands; +std::list<int> g_options; +std::list<int> g_scalar_type; + +extern bool g_debug_ir_generation; +extern const char *g_filename; +extern int ptx_lineno; +extern int g_debug_execution; +extern int g_debug_thread_uid; + + +#define DPRINTF(...) \ + if( g_debug_ir_generation ) { \ + printf(" %s:%u => ",g_filename,ptx_lineno); \ + printf(" (%s:%u) ", __FILE__, __LINE__); \ + printf(__VA_ARGS__); \ + printf("\n"); \ + fflush(stdout); \ + } + +unsigned g_entry_func_param_index=0; +function_info *g_func_info = NULL; +function_info *g_entrypoint_func_info = NULL; +symbol_table *g_entrypoint_symbol_table = NULL; +std::map<unsigned,std::string> g_ptx_token_decode; +operand_info g_return_var; + +void init_parser() +{ + g_global_symbol_table = g_current_symbol_table = new symbol_table("global",0,NULL); + ptx_lineno = 1; + +#define DEF(X,Y) g_ptx_token_decode[X] = Y; +#include "ptx_parser_decode.def" +#undef DEF +} + +void init_directive_state() +{ + DPRINTF("init_directive_state"); + g_space_spec=-1; + g_scalar_type_spec=-1; + g_vector_spec=-1; + g_opcode=-1; + g_alignment_spec = -1; + g_extern_spec = 0; + g_scalar_type.clear(); + g_operands.clear(); + g_last_symbol = NULL; +} + +void init_instruction_state() +{ + DPRINTF("init_instruction_state"); + g_pred = NULL; + g_neg_pred = 0; + g_label = NULL; + g_opcode = -1; + g_options.clear(); + g_return_var = operand_info(); + init_directive_state(); +} + +extern void register_ptx_function( const char *name, function_info *impl, symbol_table *symtab ); + +static int g_entry_point; + +void start_function( int entry_point ) +{ + DPRINTF("start_function"); + init_directive_state(); + init_instruction_state(); + g_entry_point = entry_point; + g_func_info = NULL; + g_entry_func_param_index=0; +} + +char *g_add_identifier_cached__identifier = NULL; +int g_add_identifier_cached__array_dim; +int g_add_identifier_cached__array_ident; + +void add_function_name( const char *name ) +{ + DPRINTF("add_function_name %s %s", name, ((g_entry_point)?"(entrypoint)":"")); + bool prior_decl = g_global_symbol_table->add_function_decl( name, g_entry_point, &g_func_info, &g_current_symbol_table ); + if( g_entry_point ) { + g_entrypoint_func_info = g_func_info; + g_entrypoint_symbol_table = g_current_symbol_table; + } + if( g_add_identifier_cached__identifier ) { + add_identifier( g_add_identifier_cached__identifier, + g_add_identifier_cached__array_dim, + g_add_identifier_cached__array_ident ); + free( g_add_identifier_cached__identifier ); + g_add_identifier_cached__identifier = NULL; + g_func_info->add_return_var( g_last_symbol ); + init_directive_state(); + } + if( prior_decl ) { + g_func_info->remove_args(); + } + g_global_symbol_table->add_function( g_func_info ); +} + +void add_directive() +{ + DPRINTF("add_directive"); + init_directive_state(); +} + +#define mymax(a,b) ((a)>(b)?(a):(b)) + +void gpgpu_ptx_assemble( std::string kname, void *kinfo ); + +void end_function() +{ + DPRINTF("end_function"); + + init_directive_state(); + init_instruction_state(); + g_max_regs_per_thread = mymax( g_max_regs_per_thread, (g_current_symbol_table->next_reg_num()-1)); + g_func_info->add_inst( g_instructions ); + g_instructions.clear(); + gpgpu_ptx_assemble( g_func_info->get_name(), g_func_info ); + + DPRINTF("function %s, PC = %d\n", g_func_info->get_name().c_str(), g_func_info->get_start_PC()); +} + +extern int ptx_lineno; +extern const char *g_filename; + +#define parse_error(msg, ...) parse_error_impl(__FILE__,__LINE__, msg, ##__VA_ARGS__) +#define parse_assert(cond,msg, ...) parse_assert_impl((cond),__FILE__,__LINE__, msg, ##__VA_ARGS__) + +void parse_error_impl( const char *file, unsigned line, const char *msg, ... ) +{ + va_list ap; + char buf[1024]; + va_start(ap,msg); + vsnprintf(buf,1024,msg,ap); + va_end(ap); + + g_error_detected = 1; + printf("%s:%u: Parse error: %s (%s:%u)\n\n", g_filename, ptx_lineno, buf, file, line); + ptx_error(NULL); + abort(); + exit(1); +} + +void parse_assert_impl( int test_value, const char *file, unsigned line, const char *msg, ... ) +{ + va_list ap; + char buf[1024]; + va_start(ap,msg); + vsnprintf(buf,1024,msg,ap); + va_end(ap); + + if ( test_value == 0 ) + parse_error_impl(file,line, msg); +} + +extern "C" char linebuf[1024]; + + +void set_return() +{ + parse_assert( (g_opcode == CALL_OP), "only call can have return value"); + g_operands.front().set_return(); + g_return_var = g_operands.front(); +} + +std::map<std::string,std::map<unsigned,const ptx_instruction*> > g_inst_lookup; + +const ptx_instruction *ptx_instruction_lookup( const char *filename, unsigned linenumber ) +{ + std::map<std::string,std::map<unsigned,const ptx_instruction*> >::iterator f=g_inst_lookup.find(filename); + if( f == g_inst_lookup.end() ) + return NULL; + std::map<unsigned,const ptx_instruction*>::iterator l=f->second.find(linenumber); + if( l == f->second.end() ) + return NULL; + return l->second; +} + +void add_instruction() +{ + DPRINTF("add_instruction: %s", ((g_opcode>0)?g_opcode_string[g_opcode]:"<label>") ); + ptx_instruction *i = new ptx_instruction( g_opcode, + g_pred, + g_neg_pred, + g_label, + g_operands, + g_return_var, + g_options, + g_scalar_type, + g_space_spec, + g_filename, + ptx_lineno, + linebuf ); + g_instructions.push_back(i); + g_inst_lookup[g_filename][ptx_lineno] = i; + init_instruction_state(); +} + +void add_variables() +{ + DPRINTF("add_variables"); + if ( !g_operands.empty() ) { + assert( g_last_symbol != NULL ); + g_last_symbol->add_initializer(g_operands); + } + init_directive_state(); +} + +void set_variable_type() +{ + DPRINTF("set_variable_type space_spec=%s scalar_type_spec=%s", + g_ptx_token_decode[g_space_spec].c_str(), + g_ptx_token_decode[g_scalar_type_spec].c_str() ); + parse_assert( g_space_spec != -1, "variable has no space specification" ); + parse_assert( g_scalar_type_spec != -1, "variable has no type information" ); // need to extend for structs? + g_var_type = g_current_symbol_table->add_type( g_space_spec, + g_scalar_type_spec, + g_vector_spec, + g_alignment_spec, + g_extern_spec ); +} + +bool check_for_duplicates( const char *identifier ) +{ + const symbol *s = g_current_symbol_table->lookup(identifier); + return ( s != NULL ); +} + +extern std::set<std::string> g_globals; +extern std::set<std::string> g_constants; + +int g_func_decl = 0; +int g_ident_add_uid = 0; +unsigned g_const_alloc = 1; + +void add_identifier( const char *identifier, int array_dim, unsigned array_ident ) +{ + if( g_func_decl && (g_func_info == NULL) ) { + // return variable decl... + assert( g_add_identifier_cached__identifier == NULL ); + g_add_identifier_cached__identifier = strdup(identifier); + g_add_identifier_cached__array_dim = array_dim; + g_add_identifier_cached__array_ident = array_ident; + return; + } + DPRINTF("add_identifier \"%s\" (%u)", identifier, g_ident_add_uid); + g_ident_add_uid++; + type_info *type = g_var_type; + type_info_key ti = type->get_key(); + int basic_type; + int regnum; + size_t num_bits; + unsigned addr, addr_pad; + type_decode(ti.scalar_type(),num_bits,basic_type); + + bool duplicates = check_for_duplicates( identifier ); + if( duplicates ) { + symbol *s = g_current_symbol_table->lookup(identifier); + g_last_symbol = s; + if( g_func_decl ) + return; + std::string msg = std::string(identifier) + " was delcared previous at " + s->decl_location(); + parse_error(msg.c_str()); + } + + assert( g_var_type != NULL ); + switch ( array_ident ) { + case ARRAY_IDENTIFIER: + type = g_current_symbol_table->get_array_type(type,array_dim); + num_bits = array_dim * num_bits; + break; + case ARRAY_IDENTIFIER_NO_DIM: + type = g_current_symbol_table->get_array_type(type,(unsigned)-1); + num_bits = 0; + break; + default: + break; + } + g_last_symbol = g_current_symbol_table->add_variable(identifier,type,g_filename,ptx_lineno); + switch ( g_space_spec ) { + case REG_DIRECTIVE: { + regnum = g_current_symbol_table->next_reg_num(); + int arch_regnum = -1; + for (int d = 0; d < strlen(identifier); d++) { + if (isdigit(identifier[d])) { + sscanf(identifier + d, "%d", &arch_regnum); + break; + } + } + if (strcmp(identifier, "%sp") == 0) { + arch_regnum = 0; + } + g_last_symbol->set_regno(regnum, arch_regnum); + } break; + case SHARED_DIRECTIVE: + printf("GPGPU-Sim PTX: allocating shared region for \"%s\" from 0x%x to 0x%lx (shared memory space)\n", + identifier, + g_current_symbol_table->get_shared_next(), + g_current_symbol_table->get_shared_next() + num_bits/8 ); + fflush(stdout); + assert( (num_bits%8) == 0 ); + addr = g_current_symbol_table->get_shared_next(); + addr_pad = num_bits ? (((num_bits/8) - (addr % (num_bits/8))) % (num_bits/8)) : 0; + g_last_symbol->set_address( addr+addr_pad ); + g_current_symbol_table->alloc_shared( num_bits/8 + addr_pad ); + break; + case CONST_DIRECTIVE: + if( array_ident == ARRAY_IDENTIFIER_NO_DIM ) { + printf("GPGPU-Sim PTX: deferring allocation of constant region for \"%s\" (need size information)\n", identifier ); + } else { + printf("GPGPU-Sim PTX: allocating constant region for \"%s\" from 0x%x to 0x%lx (global memory space) %u\n", + identifier, + g_current_symbol_table->get_global_next(), + g_current_symbol_table->get_global_next() + num_bits/8, + g_const_alloc++ ); + fflush(stdout); + assert( (num_bits%8) == 0 ); + addr = g_current_symbol_table->get_global_next(); + addr_pad = num_bits ? (((num_bits/8) - (addr % (num_bits/8))) % (num_bits/8)) : 0; + g_last_symbol->set_address( addr + addr_pad ); + g_current_symbol_table->alloc_global( num_bits/8 + addr_pad ); + } + if( g_current_symbol_table == g_global_symbol_table ) { + g_constants.insert( identifier ); + } + assert( g_current_symbol_table != NULL ); + g_sym_name_to_symbol_table[ identifier ] = g_current_symbol_table; + break; + case GLOBAL_DIRECTIVE: + printf("GPGPU-Sim PTX: allocating global region for \"%s\" from 0x%x to 0x%lx (global memory space)\n", + identifier, + g_current_symbol_table->get_global_next(), + g_current_symbol_table->get_global_next() + num_bits/8 ); + fflush(stdout); + assert( (num_bits%8) == 0 ); + addr = g_current_symbol_table->get_global_next(); + addr_pad = num_bits ? (((num_bits/8) - (addr % (num_bits/8))) % (num_bits/8)) : 0; + g_last_symbol->set_address( addr+addr_pad ); + g_current_symbol_table->alloc_global( num_bits/8 + addr_pad ); + g_globals.insert( identifier ); + assert( g_current_symbol_table != NULL ); + g_sym_name_to_symbol_table[ identifier ] = g_current_symbol_table; + break; + case LOCAL_DIRECTIVE: + printf("GPGPU-Sim PTX: allocating local region for \"%s\" from 0x%x to 0x%lx (local memory space)\n", + identifier, + g_current_symbol_table->get_local_next(), + g_current_symbol_table->get_local_next() + num_bits/8 ); + fflush(stdout); + assert( (num_bits%8) == 0 ); + g_last_symbol->set_address( g_current_symbol_table->get_local_next() ); + g_current_symbol_table->alloc_local( num_bits/8 ); + break; + case TEX_DIRECTIVE: + printf("GPGPU-Sim PTX: encountered texture directive %s.\n", identifier); + break; + default: + break; + } + + + if ( ti.is_param() ) { + g_func_info->add_param_name_and_type(g_entry_func_param_index,identifier, ti.scalar_type() ); + g_entry_func_param_index++; + } +} + +void add_function_arg() +{ + if( g_func_info ) { + DPRINTF("add_function_arg \"%s\"", g_last_symbol->name().c_str() ); + g_func_info->add_arg(g_last_symbol); + } +} + +void add_extern_spec() +{ + DPRINTF("add_extern_spec"); + g_extern_spec = 1; +} + +void add_alignment_spec( int spec ) +{ + DPRINTF("add_alignment_spec"); + parse_assert( g_alignment_spec == -1, "multiple .align specifiers per variable declaration not allowed." ); + g_alignment_spec = spec; +} + +void add_space_spec( int spec ) +{ + DPRINTF("add_space_spec \"%s\"", g_ptx_token_decode[spec].c_str() ); + parse_assert( g_space_spec == -1, "multiple space specifiers not allowed." ); + g_space_spec = spec; +} + +void add_vector_spec(int spec ) +{ + DPRINTF("add_vector_spec"); + parse_assert( g_vector_spec == -1, "multiple vector specifiers not allowed." ); + g_vector_spec = spec; +} + +void add_scalar_type_spec( int type_spec ) +{ + DPRINTF("add_scalar_type_spec \"%s\"", g_ptx_token_decode[type_spec].c_str()); + g_scalar_type.push_back( type_spec ); + if ( g_scalar_type.size() > 1 ) { + parse_assert( (g_opcode == -1) || (g_opcode == CVT_OP) || (g_opcode == SET_OP) || (g_opcode == SLCT_OP) + || (g_opcode == TEX_OP), + "only cvt, set, slct, and tex can have more than one type specifier."); + } + g_scalar_type_spec = type_spec; +} + +void add_label( const char *identifier ) +{ + DPRINTF("add_label"); + symbol *s = g_current_symbol_table->lookup(identifier); + if ( s != NULL ) { + g_label = s; + } else { + g_label = g_current_symbol_table->add_variable(identifier,NULL,g_filename,ptx_lineno); + } +} + +void add_opcode( int opcode ) +{ + g_opcode = opcode; +} + +void add_pred( const char *identifier, int neg ) +{ + DPRINTF("add_pred"); + const symbol *s = g_current_symbol_table->lookup(identifier); + if ( s == NULL ) { + std::string msg = std::string("predicate \"") + identifier + "\" has no declaration."; + parse_error( msg.c_str() ); + } + g_pred = s; + g_neg_pred = neg; +} + +void add_option( int option ) +{ + DPRINTF("add_option"); + g_options.push_back( option ); +} + +void add_2vector_operand( const char *d1, const char *d2 ) +{ + DPRINTF("add_2vector_operand"); + const symbol *s1 = g_current_symbol_table->lookup(d1); + const symbol *s2 = g_current_symbol_table->lookup(d2); + parse_assert( s1 != NULL && s2 != NULL, "v2 component(s) missing declarations."); + g_operands.push_back( operand_info(s1,s2,NULL,NULL) ); +} + +void add_3vector_operand( const char *d1, const char *d2, const char *d3 ) +{ + DPRINTF("add_3vector_operand"); + const symbol *s1 = g_current_symbol_table->lookup(d1); + const symbol *s2 = g_current_symbol_table->lookup(d2); + const symbol *s3 = g_current_symbol_table->lookup(d3); + parse_assert( s1 != NULL && s2 != NULL && s3 != NULL, "v3 component(s) missing declarations."); + g_operands.push_back( operand_info(s1,s2,s3,NULL) ); +} + +void add_4vector_operand( const char *d1, const char *d2, const char *d3, const char *d4 ) +{ + DPRINTF("add_4vector_operand"); + const symbol *s1 = g_current_symbol_table->lookup(d1); + const symbol *s2 = g_current_symbol_table->lookup(d2); + const symbol *s3 = g_current_symbol_table->lookup(d3); + const symbol *s4 = g_current_symbol_table->lookup(d4); + parse_assert( s1 != NULL && s2 != NULL && s3 != NULL && s4 != NULL, "v4 component(s) missing declarations."); + g_operands.push_back( operand_info(s1,s2,s3,s4) ); +} + +void add_builtin_operand( int builtin, int dim_modifier ) +{ + DPRINTF("add_builtin_operand"); + g_operands.push_back( operand_info(builtin,dim_modifier) ); +} + +void add_memory_operand() +{ + DPRINTF("add_memory_operand"); + assert( !g_operands.empty() ); + g_operands.back().make_memory_operand(); +} + +void add_literal_int( int value ) +{ + DPRINTF("add_literal_int"); + g_operands.push_back( operand_info(value) ); +} + +void add_literal_float( float value ) +{ + DPRINTF("add_literal_float"); + g_operands.push_back( operand_info(value) ); +} + +void add_literal_double( double value ) +{ + DPRINTF("add_literal_double"); + g_operands.push_back( operand_info(value) ); +} + +void add_scalar_operand( const char *identifier ) +{ + DPRINTF("add_scalar_operand"); + const symbol *s = g_current_symbol_table->lookup(identifier); + if ( s == NULL ) { + if ( g_opcode == BRA_OP ) { + // forward branch target... + s = g_current_symbol_table->add_variable(identifier,NULL,g_filename,ptx_lineno); + } else { + std::string msg = std::string("operand \"") + identifier + "\" has no declaration."; + parse_error( msg.c_str() ); + } + } + g_operands.push_back( operand_info(s) ); +} + +void add_neg_pred_operand( const char *identifier ) +{ + DPRINTF("add_neg_pred_operand"); + const symbol *s = g_current_symbol_table->lookup(identifier); + if ( s == NULL ) { + s = g_current_symbol_table->add_variable(identifier,NULL,g_filename,ptx_lineno); + } + operand_info op(s); + op.set_neg_pred(); + g_operands.push_back( op ); +} + +void add_address_operand( const char *identifier, int offset ) +{ + DPRINTF("add_address_operand"); + const symbol *s = g_current_symbol_table->lookup(identifier); + if ( s == NULL ) { + std::string msg = std::string("operand \"") + identifier + "\" has no declaration."; + parse_error( msg.c_str() ); + } + g_operands.push_back( operand_info(s,offset) ); +} + +unsigned symbol::sm_next_uid = 1; + +unsigned symbol::get_uid() +{ + unsigned result = sm_next_uid++; + return result; +} + +void symbol::add_initializer( const std::list<operand_info> &init ) +{ + m_initializer = init; +} + +void symbol::print_info(FILE *fp) const +{ + fprintf(fp,"uid:%u, decl:%s, type:%p, ", m_uid, m_decl_location.c_str(), m_type ); + if( m_address_valid ) + fprintf(fp,"<address valid>, "); + if( m_is_label ) + fprintf(fp," is_label "); + if( m_is_shared ) + fprintf(fp," is_shared "); + if( m_is_const ) + fprintf(fp," is_const "); + if( m_is_global ) + fprintf(fp," is_global "); + if( m_is_local ) + fprintf(fp," is_local "); + if( m_is_tex ) + fprintf(fp," is_tex "); + if( m_is_func_addr ) + fprintf(fp," is_func_addr "); + if( m_function ) + fprintf(fp," %p ", m_function ); +} + +symbol_table::symbol_table() +{ + assert(0); +} + +symbol_table::symbol_table( const char *scope_name, unsigned entry_point, symbol_table *parent ) +{ + m_scope_name = std::string(scope_name); + m_reg_allocator=0; + m_shared_next = 0x100; // for debug with valgrind: make zero imply undefined address + m_const_next = 0x100; // for debug with valgrind: make zero imply undefined address + m_global_next = 0x100; // for debug with valgrind: make zero imply undefined address + m_local_next = 0x100; // for debug with valgrind: make zero imply undefined address + m_parent = parent; + if ( m_parent ) { + m_shared_next = m_parent->m_shared_next; + m_global_next = m_parent->m_global_next; + } +} + +void symbol_table::set_name( const char *name ) +{ + m_scope_name = std::string(name); +} + +symbol *symbol_table::lookup( const char *identifier ) +{ + std::string key(identifier); + std::map<std::string, symbol *>::iterator i = m_symbols.find(key); + if ( i != m_symbols.end() ) { + return i->second; + } + if ( m_parent ) { + return m_parent->lookup(identifier); + } + return NULL; +} + +symbol *symbol_table::add_variable( const char *identifier, const type_info *type, const char *filename, unsigned line ) +{ + char buf[1024]; + std::string key(identifier); + assert( m_symbols.find(key) == m_symbols.end() ); + snprintf(buf,1024,"%s:%u",filename,line); + symbol *s = new symbol(identifier,type,buf); + m_symbols[ key ] = s; + + if ( type != NULL && type->get_key().is_param() ) { + m_params.push_back(s); + } + if ( type != NULL && type->get_key().is_global() ) { + m_globals.push_back(s); + } + if ( type != NULL && type->get_key().is_const() ) { + m_consts.push_back(s); + } + + return s; +} + +void symbol_table::add_function( function_info *func ) +{ + std::map<std::string, symbol *>::iterator i = m_symbols.find( func->get_name() ); + if( i != m_symbols.end() ) + return; + char buf[1024]; + snprintf(buf,1024,"%s:%u",g_filename,ptx_lineno); + type_info *type = add_type( func ); + symbol *s = new symbol(func->get_name().c_str(),type,buf); + s->set_function(func); + m_symbols[ func->get_name() ] = s; +} + +bool symbol_table::add_function_decl( const char *name, int entry_point, function_info **func_info, symbol_table **sym_table ) +{ + std::string key = std::string(name); + bool prior_decl = false; + if( m_function_info_lookup.find(key) != m_function_info_lookup.end() ) { + *func_info = m_function_info_lookup[key]; + prior_decl = true; + } else { + *func_info = new function_info(entry_point); + (*func_info)->set_name(name); + m_function_info_lookup[key] = *func_info; + } + + if( m_function_symtab_lookup.find(key) != m_function_symtab_lookup.end() ) { + assert( prior_decl ); + *sym_table = m_function_symtab_lookup[key]; + } else { + assert( !prior_decl ); + *sym_table = new symbol_table( "", entry_point, g_global_symbol_table ); + symbol *null_reg = (*sym_table)->add_variable("_",NULL,"",0); + null_reg->set_regno(0, 0); + (*sym_table)->set_name(name); + (*func_info)->set_symtab(*sym_table); + m_function_symtab_lookup[key] = *sym_table; + register_ptx_function(name,*func_info,*sym_table); + } + return prior_decl; +} + +type_info *symbol_table::add_type( int space_spec, int scalar_type_spec, int vector_spec, int alignment_spec, int extern_spec ) +{ + type_info_key t(space_spec,scalar_type_spec,vector_spec,alignment_spec,extern_spec,0); + type_info *pt; + pt = new type_info(this,t); + return pt; +} + +type_info *symbol_table::add_type( function_info *func ) +{ + type_info_key t; + type_info *pt; + t.set_is_func(); + pt = new type_info(this,t); + return pt; +} + +type_info *symbol_table::get_array_type( type_info *base_type, unsigned array_dim ) +{ + type_info_key t = base_type->get_key(); + t.set_array_dim(array_dim); + type_info *pt; + pt = m_types[t] = new type_info(this,t); + return pt; +} + +void symbol_table::set_label_address( const symbol *label, unsigned addr ) +{ + std::map<std::string, symbol *>::iterator i=m_symbols.find(label->name()); + assert( i != m_symbols.end() ); + symbol *s = i->second; + s->set_label_address(addr); +} + +void symbol_table::dump() +{ + printf("\n\n"); + printf("Symbol table for \"%s\":\n", m_scope_name.c_str() ); + std::map<std::string, symbol *>::iterator i; + for( i=m_symbols.begin(); i!=m_symbols.end(); i++ ) { + printf("%30s : ", i->first.c_str() ); + if( i->second ) + i->second->print_info(stdout); + else + printf(" <no symbol object> "); + printf("\n"); + } + printf("\n"); +} + +unsigned operand_info::sm_next_uid=1; + +unsigned operand_info::get_uid() +{ + unsigned result = sm_next_uid++; + return result; +} + +void add_array_initializer() +{ + g_last_symbol->add_initializer(g_operands); +} + + +std::list<ptx_instruction*>::iterator function_info::find_next_real_instruction( std::list<ptx_instruction*>::iterator i) +{ + while( (i != m_instructions.end()) && (*i)->is_label() ) + i++; + return i; +} + +void function_info::create_basic_blocks() +{ + std::list<ptx_instruction*> leaders; + std::list<ptx_instruction*>::iterator i, l; + + // first instruction is a leader + i=m_instructions.begin(); + leaders.push_back(*i); + i++; + while( i!=m_instructions.end() ) { + ptx_instruction *pI = *i; + if( pI->is_label() ) { + leaders.push_back(pI); + i = find_next_real_instruction(++i); + } else { + switch( pI->get_opcode() ) { + case BRA_OP: case RET_OP: case EXIT_OP: + i++; + if( i != m_instructions.end() ) + leaders.push_back(*i); + i = find_next_real_instruction(i); + break; + case CALL_OP: + if( pI->has_pred() ) { + printf("GPGPU-Sim PTX: Warning found predicated call\n"); + i++; + if( i != m_instructions.end() ) + leaders.push_back(*i); + i = find_next_real_instruction(i); + } else i++; + break; + default: + i++; + } + } + } + + if( leaders.empty() ) { + printf("GPGPU-Sim PTX: Function \'%s\' has no basic blocks\n", m_name.c_str()); + return; + } + + unsigned bb_id = 0; + l=leaders.begin(); + i=m_instructions.begin(); + m_basic_blocks.push_back( new basic_block_t(bb_id++,*find_next_real_instruction(i),NULL,1,0) ); + ptx_instruction *last_real_inst=*(l++); + + for( ; i!=m_instructions.end(); i++ ) { + ptx_instruction *pI = *i; + if( l != leaders.end() && *i == *l ) { + // found start of next basic block + m_basic_blocks.back()->ptx_end = last_real_inst; + if( find_next_real_instruction(i) != m_instructions.end() ) { // if not bogus trailing label + m_basic_blocks.push_back( new basic_block_t(bb_id++,*find_next_real_instruction(i),NULL,0,0) ); + last_real_inst = *find_next_real_instruction(i); + } + // start search for next leader + l++; + } + pI->assign_bb( m_basic_blocks.back() ); + if( !pI->is_label() ) last_real_inst = pI; + } + m_basic_blocks.back()->ptx_end = last_real_inst; + m_basic_blocks.push_back( /*exit basic block*/ new basic_block_t(bb_id,NULL,NULL,0,1) ); +} + +void function_info::print_basic_blocks() +{ + printf("Printing basic blocks for function \'%s\':\n", m_name.c_str() ); + std::list<ptx_instruction*>::iterator ptx_itr; + unsigned last_bb=0; + for (ptx_itr = m_instructions.begin();ptx_itr != m_instructions.end(); ptx_itr++) { + if( (*ptx_itr)->get_bb() ) { + if( (*ptx_itr)->get_bb()->bb_id != last_bb ) { + printf("\n"); + last_bb = (*ptx_itr)->get_bb()->bb_id; + } + printf("bb_%02u\t: ", (*ptx_itr)->get_bb()->bb_id); + (*ptx_itr)->print_insn(); + printf("\n"); + } + } + printf("\nSummary of basic blocks for \'%s\':\n", m_name.c_str() ); + std::vector<basic_block_t*>::iterator bb_itr; + for (bb_itr = m_basic_blocks.begin();bb_itr != m_basic_blocks.end(); bb_itr++) { + printf("bb_%02u\t:", (*bb_itr)->bb_id); + if ((*bb_itr)->ptx_begin) + printf(" first: %s\t", ((*bb_itr)->ptx_begin)->get_opcode_cstr()); + else printf(" first: NULL\t"); + if ((*bb_itr)->ptx_end) { + printf(" last: %s\t", ((*bb_itr)->ptx_end)->get_opcode_cstr()); + } else printf(" last: NULL\t"); + printf("\n"); + } + printf("\n"); +} + +void function_info::print_basic_block_links() +{ + printf("Printing basic blocks links for function \'%s\':\n", m_name.c_str() ); + std::vector<basic_block_t*>::iterator bb_itr; + for (bb_itr = m_basic_blocks.begin();bb_itr != m_basic_blocks.end(); bb_itr++) { + printf("ID: %d\t:", (*bb_itr)->bb_id); + if ( !(*bb_itr)->predecessor_ids.empty() ) { + printf("Predecessors:"); + std::set<int>::iterator p; + for (p= (*bb_itr)->predecessor_ids.begin();p != (*bb_itr)->predecessor_ids.end();p++) { + printf(" %d", *p); + } + printf("\t"); + } + if ( !(*bb_itr)->successor_ids.empty() ) { + printf("Successors:"); + std::set<int>::iterator s; + for (s= (*bb_itr)->successor_ids.begin();s != (*bb_itr)->successor_ids.end();s++) { + printf(" %d", *s); + } + } + printf("\n"); + } +} +void function_info::connect_basic_blocks( ) //iterate across m_basic_blocks of function, connecting basic blocks together +{ + std::vector<basic_block_t*>::iterator bb_itr; + std::vector<basic_block_t*>::iterator bb_target_itr; + basic_block_t* exit_bb = m_basic_blocks.back(); + + //start from first basic block, which we know is the entry point + bb_itr = m_basic_blocks.begin(); + for (bb_itr = m_basic_blocks.begin();bb_itr != m_basic_blocks.end(); bb_itr++) { + ptx_instruction *pI = (*bb_itr)->ptx_end; + if ((*bb_itr)->is_exit) //reached last basic block, no successors to link + continue; + if (pI->get_opcode() == RET_OP || pI->get_opcode() == EXIT_OP ) { + (*bb_itr)->successor_ids.insert(exit_bb->bb_id); + exit_bb->predecessor_ids.insert((*bb_itr)->bb_id); + if( pI->has_pred() ) { + printf("GPGPU-Sim PTX: Warning detected predicated return/exit.\n"); + // if predicated, add link to next block + unsigned next_addr = pI->get_m_instr_mem_index() + 1; + if( next_addr < m_instr_mem_size && m_instr_mem[next_addr] ) { + basic_block_t *next_bb = m_instr_mem[next_addr]->get_bb(); + (*bb_itr)->successor_ids.insert(next_bb->bb_id); + next_bb->predecessor_ids.insert((*bb_itr)->bb_id); + } + } + continue; + } else if (pI->get_opcode() == BRA_OP) { + //find successor and link that basic_block to this one + operand_info &target = pI->dst(); //get operand, e.g. target name + unsigned addr = labels[ target.name() ]; + ptx_instruction *target_pI = m_instr_mem[addr]; + basic_block_t *target_bb = target_pI->get_bb(); + (*bb_itr)->successor_ids.insert(target_bb->bb_id); + target_bb->predecessor_ids.insert((*bb_itr)->bb_id); + } + if ( !(pI->get_opcode()==BRA_OP && (!pI->has_pred())) ) { + // if basic block does not end in an unpredicated branch, + // then next basic block is also successor + // (this is better than testing for .uni) + unsigned next_addr = pI->get_m_instr_mem_index() + 1; + basic_block_t *next_bb = m_instr_mem[next_addr]->get_bb(); + (*bb_itr)->successor_ids.insert(next_bb->bb_id); + next_bb->predecessor_ids.insert((*bb_itr)->bb_id); + } else + assert(pI->get_opcode() == BRA_OP); + } +} + +void intersect( std::set<int> &A, const std::set<int> &B ) +{ + // return intersection of A and B in A + for( std::set<int>::iterator a=A.begin(); a!=A.end(); ) { + std::set<int>::iterator a_next = a; + a_next++; + if( B.find(*a) == B.end() ) { + A.erase(*a); + a = a_next; + } else + a++; + } +} + +bool is_equal( const std::set<int> &A, const std::set<int> &B ) +{ + if( A.size() != B.size() ) + return false; + for( std::set<int>::iterator b=B.begin(); b!=B.end(); b++ ) + if( A.find(*b) == A.end() ) + return false; + return true; +} + +void print_set(const std::set<int> &A) +{ + std::set<int>::iterator a; + for (a= A.begin(); a != A.end(); a++) { + printf("%d ", (*a)); + } + printf("\n"); +} + +void function_info::find_postdominators( ) +{ + // find postdominators using algorithm of Muchnick's Adv. Compiler Design & Implemmntation Fig 7.14 + printf("GPGPU-Sim PTX: Finding postdominators for \'%s\'...\n", m_name.c_str() ); + fflush(stdout); + assert( m_basic_blocks.size() >= 2 ); // must have a distinquished exit block + std::vector<basic_block_t*>::reverse_iterator bb_itr = m_basic_blocks.rbegin(); + (*bb_itr)->postdominator_ids.insert((*bb_itr)->bb_id); // the only postdominator of the exit block is the exit + for (++bb_itr;bb_itr != m_basic_blocks.rend();bb_itr++) { //copy all basic blocks to all postdominator lists EXCEPT for the exit block + for (unsigned i=0; i<m_basic_blocks.size(); i++) + (*bb_itr)->postdominator_ids.insert(i); + } + bool change = true; + while (change) { + change = false; + for ( int h = m_basic_blocks.size()-2/*skip exit*/; h >= 0 ; --h ) { + assert( m_basic_blocks[h]->bb_id == (unsigned)h ); + std::set<int> T; + for (unsigned i=0;i< m_basic_blocks.size();i++) + T.insert(i); + for ( std::set<int>::iterator s = m_basic_blocks[h]->successor_ids.begin();s != m_basic_blocks[h]->successor_ids.end();s++) + intersect(T, m_basic_blocks[*s]->postdominator_ids); + T.insert(h); + if (!is_equal(T,m_basic_blocks[h]->postdominator_ids)) { + change = true; + m_basic_blocks[h]->postdominator_ids = T; + } + } + } +} + +void function_info::find_ipostdominators( ) +{ + // find immediate postdominator blocks, using algorithm of + // Muchnick's Adv. Compiler Design & Implemmntation Fig 7.15 + printf("GPGPU-Sim PTX: Finding immediate postdominators for \'%s\'...\n", m_name.c_str() ); + fflush(stdout); + assert( m_basic_blocks.size() >= 2 ); // must have a distinquished exit block + for (unsigned i=0; i<m_basic_blocks.size(); i++) { //initialize Tmp(n) to all pdoms of n except for n + m_basic_blocks[i]->Tmp_ids = m_basic_blocks[i]->postdominator_ids; + assert( m_basic_blocks[i]->bb_id == i ); + m_basic_blocks[i]->Tmp_ids.erase(i); + } + for ( int n = m_basic_blocks.size()-2; n >=0;--n) { + // point iterator to basic block before the exit + for( std::set<int>::iterator s=m_basic_blocks[n]->Tmp_ids.begin(); s != m_basic_blocks[n]->Tmp_ids.end(); s++ ) { + int bb_s = *s; + for( std::set<int>::iterator t=m_basic_blocks[n]->Tmp_ids.begin(); t != m_basic_blocks[n]->Tmp_ids.end(); ) { + std::set<int>::iterator t_next = t; t_next++; // might erase thing pointed to be t, invalidating iterator t + if( *s == *t ) { + t = t_next; + continue; + } + int bb_t = *t; + if( m_basic_blocks[bb_s]->postdominator_ids.find(bb_t) != m_basic_blocks[bb_s]->postdominator_ids.end() ) + m_basic_blocks[n]->Tmp_ids.erase(bb_t); + t = t_next; + } + } + } + unsigned num_ipdoms=0; + for ( int n = m_basic_blocks.size()-1; n >=0;--n) { + assert( m_basic_blocks[n]->Tmp_ids.size() <= 1 ); + // if the above assert fails we have an error in either postdominator + // computation, the flow graph does not have a unique exit, or some other error + if( !m_basic_blocks[n]->Tmp_ids.empty() ) { + m_basic_blocks[n]->immediatepostdominator_id = *m_basic_blocks[n]->Tmp_ids.begin(); + num_ipdoms++; + } + } + assert( num_ipdoms == m_basic_blocks.size()-1 ); + // the exit node does not have an immediate post dominator, but everyone else should +} + +void function_info::print_postdominators() +{ + printf("Printing postdominators for function \'%s\':\n", m_name.c_str() ); + std::vector<int>::iterator bb_itr; + for (unsigned i = 0; i < m_basic_blocks.size(); i++) { + printf("ID: %d\t:", i); + for( std::set<int>::iterator j=m_basic_blocks[i]->postdominator_ids.begin(); j!=m_basic_blocks[i]->postdominator_ids.end(); j++) + printf(" %d", *j ); + printf("\n"); + } +} + +void function_info::print_ipostdominators() +{ + printf("Printing immediate postdominators for function \'%s\':\n", m_name.c_str() ); + std::vector<int>::iterator bb_itr; + for (unsigned i = 0; i < m_basic_blocks.size(); i++) { + printf("ID: %d\t:", i); + printf("%d\n", m_basic_blocks[i]->immediatepostdominator_id); + } +} + +unsigned function_info::get_num_reconvergence_pairs() +{ + if (!num_reconvergence_pairs) { + for (unsigned i=0; i< (m_basic_blocks.size()-1); i++) { //last basic block containing exit obviously won't have a pair + if (m_basic_blocks[i]->ptx_end->get_opcode() == BRA_OP) { + num_reconvergence_pairs++; + } + } + } + return num_reconvergence_pairs; +} + +void function_info::get_reconvergence_pairs(gpgpu_recon_t* recon_points) +{ + unsigned idx=0; //array index + for (unsigned i=0; i< (m_basic_blocks.size()-1); i++) { //last basic block containing exit obviously won't have a pair +#ifdef DEBUG_GET_RECONVERG_PAIRS + printf("i=%d\n", i); fflush(stdout); +#endif + if (m_basic_blocks[i]->ptx_end->get_opcode() == BRA_OP) { +#ifdef DEBUG_GET_RECONVERG_PAIRS + printf("\tbranch!\n"); + printf("\tbb_id=%d; ipdom=%d\n", m_basic_blocks[i]->bb_id, m_basic_blocks[i]->immediatepostdominator_id); + printf("\tm_instr_mem index=%d\n", m_basic_blocks[i]->ptx_end->get_m_instr_mem_index()); + fflush(stdout); +#endif + recon_points[idx].source_pc = m_basic_blocks[i]->ptx_end->get_PC(); +#ifdef DEBUG_GET_RECONVERG_PAIRS + printf("\trecon_points[idx].source_pc=%d\n", recon_points[idx].source_pc); +#endif + if( m_basic_blocks[m_basic_blocks[i]->immediatepostdominator_id]->ptx_begin ) { + recon_points[idx].target_pc = m_basic_blocks[m_basic_blocks[i]->immediatepostdominator_id]->ptx_begin->get_PC(); + } else { + // reconverge after function return + recon_points[idx].target_pc = -2; + } +#ifdef DEBUG_GET_RECONVERG_PAIRS + m_basic_blocks[m_basic_blocks[i]->immediatepostdominator_id]->ptx_begin->print_insn(); + printf("\trecon_points[idx].target_pc=%d\n", recon_points[idx].target_pc); fflush(stdout); +#endif + idx++; + } + } +} + +// interface with graphviz (print the graph in DOT language) for plotting +void function_info::print_basic_block_dot() +{ + printf("Basic Block in DOT\n"); + printf("digraph %s {\n", m_name.c_str()); + std::vector<basic_block_t*>::iterator bb_itr; + for (bb_itr = m_basic_blocks.begin();bb_itr != m_basic_blocks.end(); bb_itr++) { + printf("\t"); + std::set<int>::iterator s; + for (s = (*bb_itr)->successor_ids.begin();s != (*bb_itr)->successor_ids.end();s++) { + unsigned succ_bb = *s; + printf("%d -> %d; ", (*bb_itr)->bb_id, succ_bb ); + } + printf("\n"); + } + printf("}\n"); +} + +extern "C" void add_file( unsigned num, const char *filename ) +{ + if( g_filename == NULL ) { + char *b = strdup(filename); + char *l=b; + char *n=b; + while( *n != '\0' ) { + if( *n == '/' ) + l = n+1; + n++; + } + + char *p = strtok(l,"."); + char buf[1024]; + snprintf(buf,1024,"%s.ptx",p); + + char *q = strtok(NULL,"."); + if( q && !strcmp(q,"cu") ) { + g_filename = strdup(buf); + } + + free( b ); + } + + g_current_symbol_table = g_global_symbol_table; +} + +extern "C" void *reset_symtab() +{ + void *result = g_current_symbol_table; + g_current_symbol_table = g_global_symbol_table; + return result; +} + +extern "C" void set_symtab(void*symtab) +{ + g_current_symbol_table = (symbol_table*)symtab; +} + +unsigned ptx_kernel_shmem_size( void *kernel_impl ) +{ + function_info *f = (function_info*)kernel_impl; + const struct gpgpu_ptx_sim_kernel_info *kernel_info = f->get_kernel_info(); + return kernel_info->smem; +} + +unsigned ptx_kernel_nregs( void *kernel_impl ) +{ + function_info *f = (function_info*)kernel_impl; + const struct gpgpu_ptx_sim_kernel_info *kernel_info = f->get_kernel_info(); + return kernel_info->regs; +} diff --git a/src/cuda-sim/ptx_ir.h b/src/cuda-sim/ptx_ir.h new file mode 100644 index 0000000..82d5b4c --- /dev/null +++ b/src/cuda-sim/ptx_ir.h @@ -0,0 +1,1223 @@ +/* + * Copyright (c) 2009 by Tor M. Aamodt, Wilson W. L. Fung, Ali Bakhoda, + * George L. Yuan, Dan O'Connor, Joey Ting, Henry Wong and the + * University of British Columbia + * Vancouver, BC V6T 1Z4 + * All Rights Reserved. + * + * THIS IS A LEGAL DOCUMENT BY DOWNLOADING GPGPU-SIM, YOU ARE AGREEING TO THESE + * TERMS AND CONDITIONS. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * NOTE: The files libcuda/cuda_runtime_api.c and src/cuda-sim/cuda-math.h + * are derived from the CUDA Toolset available from http://www.nvidia.com/cuda + * (property of NVIDIA). The files benchmarks/BlackScholes/ and + * benchmarks/template/ are derived from the CUDA SDK available from + * http://www.nvidia.com/cuda (also property of NVIDIA). The files from + * src/intersim/ are derived from Booksim (a simulator provided with the + * textbook "Principles and Practices of Interconnection Networks" available + * from http://cva.stanford.edu/books/ppin/). As such, those files are bound by + * the corresponding legal terms and conditions set forth separately (original + * copyright notices are left in files from these sources and where we have + * modified a file our copyright notice appears before the original copyright + * notice). + * + * Using this version of GPGPU-Sim requires a complete installation of CUDA + * which is distributed seperately by NVIDIA under separate terms and + * conditions. To use this version of GPGPU-Sim with OpenCL requires a + * recent version of NVIDIA's drivers which support OpenCL. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the University of British Columbia nor the names of + * its contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * 4. This version of GPGPU-SIM is distributed freely for non-commercial use only. + * + * 5. No nonprofit user may place any restrictions on the use of this software, + * including as modified by the user, by any other authorized user. + * + * 6. GPGPU-SIM was developed primarily by Tor M. Aamodt, Wilson W. L. Fung, + * Ali Bakhoda, George L. Yuan, at the University of British Columbia, + * Vancouver, BC V6T 1Z4 + */ + +#ifndef ptx_ir_INCLUDED +#define ptx_ir_INCLUDED + +#ifdef __cplusplus + + #include <cstdlib> + #include <cstring> + #include <string> + #include <list> + #include <map> + #include <vector> + #include <assert.h> + + #include "ptx.tab.h" + #include "ptx_sim.h" + #include "dram_callback.h" + #include "../util.h" + + #include "memory.h" + +enum space_type { + undefined, inst_space +}; + + +class addr { /* need this because there are many distinct address spaces (global, local, param, tex, surf, shared) */ +public: + + addr() { m_space=undefined; m_addr = 0;} + void set_space( enum space_type space ); + operator unsigned() { return m_addr;} + +private: + enum space_type m_space; + unsigned m_addr; +}; + + +class type_info_key { +public: + type_info_key() + { + m_init = false; + } + type_info_key( int space_spec, int scalar_type_spec, int vector_spec, int alignment_spec, int extern_spec, int array_dim ) + { + m_init = true; + m_space_spec = space_spec; + m_scalar_type_spec = scalar_type_spec; + m_vector_spec = vector_spec; + m_alignment_spec = alignment_spec; + m_extern_spec = extern_spec; + m_array_dim = array_dim; + m_is_function = 0; + } + void set_is_func() + { + assert(!m_init); + m_init = true; + m_space_spec = 0; + m_scalar_type_spec = 0; + m_vector_spec = 0; + m_alignment_spec = 0; + m_extern_spec = 0; + m_array_dim = 0; + m_is_function = 1; + } + + void set_array_dim( int array_dim ) + { + m_array_dim = array_dim; + } + + bool is_reg() const { return m_space_spec == REG_DIRECTIVE;} + bool is_param() const { return m_space_spec == PARAM_DIRECTIVE;} + bool is_global() const { return m_space_spec == GLOBAL_DIRECTIVE;} + bool is_local() const { return m_space_spec == LOCAL_DIRECTIVE;} + bool is_shared() const { return m_space_spec == SHARED_DIRECTIVE;} + bool is_const() const { return m_space_spec == CONST_DIRECTIVE;} + bool is_tex() const { return m_space_spec == TEX_DIRECTIVE;} + bool is_func_addr() const { return m_is_function?true:false; } + int scalar_type() const { return m_scalar_type_spec;} +private: + bool m_init; + int m_space_spec; + int m_scalar_type_spec; + int m_vector_spec; + int m_alignment_spec; + int m_extern_spec; + int m_array_dim; + int m_is_function; + + friend class type_info_key_compare; +}; + +class symbol_table; + +struct type_info_key_compare { + bool operator()( const type_info_key &a, const type_info_key &b ) const + { + assert( a.m_init && b.m_init ); + if ( a.m_space_spec < b.m_space_spec ) return true; + if ( a.m_scalar_type_spec < b.m_scalar_type_spec ) return true; + if ( a.m_vector_spec < b.m_vector_spec ) return true; + if ( a.m_alignment_spec < b.m_alignment_spec ) return true; + if ( a.m_extern_spec < b.m_extern_spec ) return true; + if ( a.m_array_dim < b.m_array_dim ) return true; + if ( a.m_is_function < b.m_is_function ) return true; + + return false; + } +}; + +class type_info { +public: + type_info( symbol_table *scope, type_info_key t ) + { + m_type_info = t; + } + const type_info_key &get_key() const { return m_type_info;} + +private: + symbol_table *m_scope; + type_info_key m_type_info; +}; + +enum operand_type { + reg_t, vector_t, builtin_t, address_t, memory_t, float_op_t, double_op_t, int_t, + unsigned_t, symbolic_t, label_t, v_reg_t, v_float_op_t, v_double_op_t, + v_int_t, v_unsigned_t +}; + +class operand_info; + +class symbol { +public: + symbol( const char *name, const type_info *type, const char *location ) + { + m_uid = get_uid(); + m_name = name; + m_decl_location = location; + m_type = type; + m_address_valid = false; + m_is_label = false; + m_is_shared = false; + m_is_const = false; + m_is_global = false; + m_is_local = false; + m_is_tex = false; + m_is_func_addr = false; + m_reg_num_valid = false; + m_function = NULL; + if ( type ) m_is_shared = type->get_key().is_shared(); + if ( type ) m_is_const = type->get_key().is_const(); + if ( type ) m_is_global = type->get_key().is_global(); + if ( type ) m_is_local = type->get_key().is_local(); + if ( type ) m_is_tex = type->get_key().is_tex(); + if ( type ) m_is_func_addr = type->get_key().is_func_addr(); + } + const std::string &name() const { return m_name;} + const std::string &decl_location() const { return m_decl_location;} + const type_info *type() const { return m_type;} + addr_t get_address() const + { + assert( m_is_label || !m_type->get_key().is_reg() ); // todo : other assertions + assert( m_address_valid ); + return m_address; + } + function_info *get_pc() const + { + return m_function; + } + void set_regno( unsigned regno, unsigned arch_regno ) + { + m_reg_num_valid = true; + m_reg_num = regno; + m_arch_reg_num = arch_regno; + } + + void set_address( addr_t addr ) + { + m_address_valid = true; + m_address = addr; + } + void set_label_address( addr_t addr) + { + m_address_valid = true; + m_address = addr; + m_is_label = true; + } + void set_function( function_info *func ) + { + m_function = func; + m_is_func_addr = true; + } + + bool is_label() const { return m_is_label;} + bool is_shared() const { return m_is_shared;} + bool is_const() const { return m_is_const;} + bool is_global() const { return m_is_global;} + bool is_local() const { return m_is_local;} + bool is_tex() const { return m_is_tex;} + bool is_func_addr() const { return m_is_func_addr; } + + void add_initializer( const std::list<operand_info> &init ); + bool has_initializer() const + { + return m_initializer.size() > 0; + } + std::list<operand_info> get_initializer() const + { + return m_initializer; + } + unsigned reg_num() const + { + assert( m_reg_num_valid ); + return m_reg_num; + } + unsigned arch_reg_num() const + { + assert( m_reg_num_valid ); + return m_arch_reg_num; + } + void print_info(FILE *fp) const; + +private: + unsigned get_uid(); + unsigned m_uid; + const type_info *m_type; + std::string m_name; + std::string m_decl_location; + + unsigned m_address; + function_info *m_function; // used for function symbols + + bool m_address_valid; + bool m_is_label; + bool m_is_shared; + bool m_is_const; + bool m_is_global; + bool m_is_local; + bool m_is_tex; + bool m_is_func_addr; + unsigned m_reg_num; + unsigned m_arch_reg_num; + bool m_reg_num_valid; + + std::list<operand_info> m_initializer; + static unsigned sm_next_uid; +}; + + +class symbol_table { +public: + symbol_table(); + symbol_table( const char *scope_name, unsigned entry_point, symbol_table *parent ); + void set_name( const char *name ); + symbol* lookup( const char *identifier ); + std::string get_scope_name() const { return m_scope_name; } + symbol *add_variable( const char *identifier, const type_info *type, const char *filename, unsigned line ); + void add_function( function_info *func ); + bool add_function_decl( const char *name, int entry_point, function_info **func_info, symbol_table **symbol_table ); + type_info *add_type( int space_spec, int scalar_type_spec, int vector_spec, int alignment_spec, int extern_spec ); + type_info *add_type( function_info *func ); + type_info *get_array_type( type_info *base_type, unsigned array_dim ); + void set_label_address( const symbol *label, unsigned addr ); + unsigned next_reg_num() { return ++m_reg_allocator;} + addr_t get_shared_next() { return m_shared_next;} + addr_t get_global_next() { return m_global_next;} + addr_t get_local_next() { return m_local_next;} + addr_t get_tex_next() { return m_tex_next;} + void alloc_shared( unsigned num_bytes ) { m_shared_next += num_bytes;} + void alloc_global( unsigned num_bytes ) { m_global_next += num_bytes;} + void alloc_local( unsigned num_bytes ) { m_local_next += num_bytes;} + void alloc_tex( unsigned num_bytes ) { m_tex_next += num_bytes;} + + typedef std::list<symbol*>::iterator iterator; + + iterator param_iterator_begin() { return m_params.begin();} + iterator param_iterator_end() { return m_params.end();} + + iterator global_iterator_begin() { return m_globals.begin();} + iterator global_iterator_end() { return m_globals.end();} + + iterator const_iterator_begin() { return m_consts.begin();} + iterator const_iterator_end() { return m_consts.end();} + + void dump(); +private: + unsigned m_reg_allocator; + unsigned m_shared_next; + unsigned m_const_next; + unsigned m_global_next; + unsigned m_local_next; + unsigned m_tex_next; + + symbol_table *m_parent; + std::string m_scope_name; + std::map<std::string, symbol *> m_symbols; //map from name of register to pointers to the registers + std::map<type_info_key,type_info*,type_info_key_compare> m_types; + std::list<symbol*> m_params; + std::list<symbol*> m_globals; + std::list<symbol*> m_consts; + std::map<std::string,function_info*> m_function_info_lookup; + std::map<std::string,symbol_table*> m_function_symtab_lookup; +}; + +class operand_info { +public: + operand_info() + { + m_uid = get_uid(); + m_valid = false; + } + operand_info( const symbol *addr ) + { + m_uid = get_uid(); + m_valid = true; + if ( addr->is_label() ) { + m_type = label_t; + } else if ( addr->is_shared() ) { + m_type = symbolic_t; + } else if ( addr->is_const() ) { + m_type = symbolic_t; + } else if ( addr->is_global() ) { + m_type = symbolic_t; + } else if ( addr->is_local() ) { + m_type = symbolic_t; + } else if ( addr->is_tex() ) { + m_type = symbolic_t; + } else if ( addr->is_func_addr() ) { + m_type = symbolic_t; + } else { + m_type = reg_t; + } + m_value.m_symbolic = addr; + m_addr_offset = 0; + m_vector = false; + m_neg_pred = false; + m_is_return_var = false; + } + operand_info( int builtin_id, int dim_mod ) + { + m_uid = get_uid(); + m_valid = true; + m_vector = false; + m_type = builtin_t; + m_value.m_int = builtin_id; + m_addr_offset = dim_mod; + m_neg_pred = false; + m_is_return_var = false; + } + operand_info( const symbol *addr, int offset ) + { + m_uid = get_uid(); + m_valid = true; + m_vector = false; + m_type = address_t; + m_value.m_symbolic = addr; + m_addr_offset = offset; + m_neg_pred = false; + m_is_return_var = false; + } + operand_info( unsigned x ) + { + m_uid = get_uid(); + m_valid = true; + m_vector = false; + m_type = unsigned_t; + m_value.m_unsigned = x; + m_addr_offset = 0; + m_neg_pred = false; + m_is_return_var = false; + } + operand_info( int x ) + { + m_uid = get_uid(); + m_valid = true; + m_vector = false; + m_type = int_t; + m_value.m_int = x; + m_addr_offset = 0; + m_neg_pred = false; + m_is_return_var = false; + } + operand_info( float x ) + { + m_uid = get_uid(); + m_valid = true; + m_vector = false; + m_type = float_op_t; + m_value.m_float = x; + m_addr_offset = 0; + m_neg_pred = false; + m_is_return_var = false; + } + operand_info( double x ) + { + m_uid = get_uid(); + m_valid = true; + m_vector = false; + m_type = double_op_t; + m_value.m_double = x; + m_addr_offset = 0; + m_neg_pred = false; + m_is_return_var = false; + } + operand_info( const symbol *s1, const symbol *s2, const symbol *s3, const symbol *s4 ) + { + m_uid = get_uid(); + m_valid = true; + m_vector = true; + m_type = vector_t; + m_value.m_vector_symbolic = new const symbol*[4]; + m_value.m_vector_symbolic[0] = s1; + m_value.m_vector_symbolic[1] = s2; + m_value.m_vector_symbolic[2] = s3; + m_value.m_vector_symbolic[3] = s4; + m_addr_offset = 0; + m_neg_pred = false; + m_is_return_var = false; + } + + void make_memory_operand() { m_type = memory_t;} + void set_return() { m_is_return_var = true; } + + const std::string &name() const + { + assert( m_type == symbolic_t || m_type == reg_t || m_type == address_t || m_type == memory_t || m_type == label_t); + return m_value.m_symbolic->name(); + } + + unsigned get_vect_nelem() const + { + assert( is_vector() ); + if( !m_value.m_vector_symbolic[0] ) return 0; + if( !m_value.m_vector_symbolic[1] ) return 1; + if( !m_value.m_vector_symbolic[2] ) return 2; + if( !m_value.m_vector_symbolic[3] ) return 3; + return 4; + } + const std::string &vec_name1() const + { + assert( m_type == vector_t); + return m_value.m_vector_symbolic[0]->name(); + } + + const std::string &vec_name2() const + { + assert( m_type == vector_t); + return m_value.m_vector_symbolic[1]->name(); + } + + const std::string &vec_name3() const + { + assert( m_type == vector_t); + return m_value.m_vector_symbolic[2]->name(); + } + + const std::string &vec_name4() const + { + assert( m_type == vector_t); + return m_value.m_vector_symbolic[3]->name(); + } + + bool is_reg() const + { + if ( m_type == reg_t ) { + return true; + } + if ( m_type != symbolic_t ) { + return false; + } + return m_value.m_symbolic->type()->get_key().is_reg(); + } + + bool is_vector() const + { + if ( m_vector) return true; + return false; + } + int reg_num() const { return m_value.m_symbolic->reg_num();} + int reg1_num() const { return m_value.m_vector_symbolic[0]->reg_num();} + int reg2_num() const { return m_value.m_vector_symbolic[1]->reg_num();} + int reg3_num() const { return m_value.m_vector_symbolic[2]?m_value.m_vector_symbolic[2]->reg_num():0; } + int reg4_num() const { return m_value.m_vector_symbolic[3]?m_value.m_vector_symbolic[3]->reg_num():0; } + int arch_reg_num() const { return m_value.m_symbolic->arch_reg_num(); } + int arch_reg_num(unsigned n) const { return (m_value.m_vector_symbolic[n])? m_value.m_vector_symbolic[n]->arch_reg_num() : -1; } + bool is_label() const { return m_type == label_t;} + bool is_builtin() const { return m_type == builtin_t;} + bool is_memory_operand() const { return m_type == memory_t;} + bool is_literal() const { return m_type == int_t || + m_type == float_op_t || + m_type == double_op_t || + m_type == unsigned_t;} + bool is_shared() const { + if ( !(m_type == symbolic_t || m_type == address_t || m_type == memory_t) ) { + return false; + } + return m_value.m_symbolic->is_shared(); + } + bool is_const() const { return m_value.m_symbolic->is_const();} + bool is_global() const { return m_value.m_symbolic->is_global();} + bool is_local() const { return m_value.m_symbolic->is_local();} + bool is_tex() const { return m_value.m_symbolic->is_tex();} + bool is_return_var() const { return m_is_return_var; } + + bool is_function_address() const + { + if( m_type != symbolic_t ) { + return false; + } + return m_value.m_symbolic->is_func_addr(); + } + + ptx_reg_t get_literal_value() const + { + ptx_reg_t result; + switch ( m_type ) { + case int_t: result.s32 = m_value.m_int; break; + case float_op_t: result.f32 = m_value.m_float; break; + case double_op_t: result.f64 = m_value.m_double; break; + case unsigned_t: result.u32 = m_value.m_unsigned; break; + default: + assert(0); + break; + } + return result; + } + int get_int() const { return m_value.m_int;} + int get_addr_offset() const { return m_addr_offset;} + const symbol *get_symbol() const { return m_value.m_symbolic;} + void set_type( enum operand_type type ) + { + m_type = type; + } + enum operand_type get_type() const { + return m_type; + } + void set_neg_pred() + { + assert( m_valid ); + m_neg_pred = true; + } + bool is_neg_pred() const { return m_neg_pred; } + bool is_valid() const { return m_valid; } + +private: + unsigned m_uid; + bool m_valid; + bool m_vector; + enum operand_type m_type; + + union { + int m_int; + unsigned int m_unsigned; + float m_float; + double m_double; + int m_vint[4]; + unsigned int m_vunsigned[4]; + float m_vfloat[4]; + double m_vdouble[4]; + const symbol* m_symbolic; + const symbol** m_vector_symbolic; + } m_value; + + int m_addr_offset; + + bool m_neg_pred; + bool m_is_return_var; + + static unsigned sm_next_uid; + unsigned get_uid(); +}; + +extern const char *g_opcode_string[]; +extern unsigned g_num_ptx_inst_uid; +struct basic_block_t { + basic_block_t( unsigned ID, ptx_instruction *begin, ptx_instruction *end, bool entry, bool ex) + { + bb_id = ID; + ptx_begin = begin; + ptx_end = end; + is_entry=entry; + is_exit=ex; + immediatepostdominator_id = -1; + } + + ptx_instruction* ptx_begin; + ptx_instruction* ptx_end; + std::set<int> predecessor_ids; //indices of other basic blocks in m_basic_blocks array + std::set<int> successor_ids; + std::set<int> postdominator_ids; + std::set<int> dominator_ids; + std::set<int> Tmp_ids; + int immediatepostdominator_id; + bool is_entry; + bool is_exit; + unsigned bb_id; +}; + +struct gpgpu_recon_t { + address_type source_pc; + address_type target_pc; +}; + +class ptx_instruction { +public: + ptx_instruction( int opcode, + const symbol *pred, + int neg_pred, + symbol *label, + const std::list<operand_info> &operands, + const operand_info &return_var, + const std::list<int> &options, + const std::list<int> &scalar_type, + int space_spec, + const char *file, + unsigned line, + const char *source ); + + void print_insn() const; + void print_insn( FILE *fp ) const; + unsigned uid() const { return m_uid;} + int get_opcode() { return m_opcode;} + const char *get_opcode_cstr() const + { + if ( m_opcode != -1 ) { + return g_opcode_string[m_opcode]; + } else { + return "label"; + } + } + const char *source_file() const { return m_source_file.c_str();} + unsigned source_line() const { return m_source_line;} + unsigned get_num_operands() const { return m_operands.size();} + bool has_pred() const { return m_pred != NULL;} + operand_info get_pred() const { return operand_info( m_pred );} + bool get_pred_neg() const { return m_neg_pred;} + const char *get_source() const { return m_source.c_str();} + + typedef std::vector<operand_info>::const_iterator const_iterator; + + const_iterator op_iter_begin() const + { + return m_operands.begin(); + } + + const_iterator op_iter_end() const + { + return m_operands.end(); + } + + const operand_info &dst() const + { + assert( !m_operands.empty() ); + return m_operands[0]; + } + + const operand_info &func_addr() const + { + assert( !m_operands.empty() ); + if( !m_operands[0].is_return_var() ) { + return m_operands[0]; + } else { + assert( m_operands.size() >= 2 ); + return m_operands[1]; + } + } + + operand_info &dst() + { + assert( !m_operands.empty() ); + return m_operands[0]; + } + + const operand_info &src1() const + { + assert( m_operands.size() > 1 ); + return m_operands[1]; + } + + const operand_info &src2() const + { + assert( m_operands.size() > 2 ); + return m_operands[2]; + } + + const operand_info &src3() const + { + assert( m_operands.size() > 3 ); + return m_operands[3]; + } + + const operand_info &operand_lookup( unsigned n ) const + { + assert( n < m_operands.size() ); + return m_operands[n]; + } + bool has_return() const + { + return m_return_var.is_valid(); + } + + unsigned get_space() const { return m_space_spec;} + unsigned get_vector() const { return m_vector_spec;} + unsigned get_atomic() const { return m_atomic_spec;} + + int get_type() const + { + assert( !m_scalar_type.empty() ); + return m_scalar_type.front(); + } + + int get_type2() const + { + assert( m_scalar_type.size()==2 ); + return m_scalar_type.back(); + } + + void assign_bb(basic_block_t* basic_block) //assign instruction to a basic block + { + m_basic_block = basic_block; + } + basic_block_t* get_bb() { return m_basic_block;} + void set_m_instr_mem_index(unsigned index) { + m_instr_mem_index = index; + } + void set_PC( addr_t PC ) + { + m_PC = PC; + } + addr_t get_PC() const + { + return m_PC; + } + + unsigned get_m_instr_mem_index() { return m_instr_mem_index;} + unsigned get_cmpop() const { return m_compare_op;} + const symbol *get_label() const { return m_label;} + bool is_label() const { if(m_label){ assert(m_opcode==-1);return true;} return false;} + bool is_hi() const { return m_hi;} + bool is_lo() const { return m_lo;} + bool is_wide() const { return m_wide;} + bool is_uni() const { return m_uni;} + unsigned rounding_mode() const { return m_rounding_mode;} + unsigned saturation_mode() const { return m_saturation_mode;} + unsigned dimension() const { return m_geom_spec;} + enum vote_mode_t { vote_any, vote_all, vote_uni }; + enum vote_mode_t vote_mode() const { return m_vote_mode; } + + unsigned warp_size() const { return m_warp_size; } + int membar_level() const { return m_membar_level; } +private: + basic_block_t *m_basic_block; + unsigned m_uid; + addr_t m_PC; + std::string m_source_file; + unsigned m_source_line; + std::string m_source; + unsigned m_warp_size; + + const symbol *m_pred; + bool m_neg_pred; + int m_opcode; + const symbol *m_label; + std::vector<operand_info> m_operands; + operand_info m_return_var; + + std::list<int> m_options; + bool m_wide; + bool m_hi; + bool m_lo; + bool m_uni; //if branch instruction, this evaluates to true for uniform branches (ie jumps) + unsigned m_rounding_mode; + unsigned m_compare_op; + unsigned m_saturation_mode; + + std::list<int> m_scalar_type; + int m_space_spec; + int m_geom_spec; + int m_vector_spec; + int m_atomic_spec; + enum vote_mode_t m_vote_mode; + int m_membar_level; + int m_instr_mem_index; //index into m_instr_mem array +}; + +class param_info { +public: + param_info() { m_valid = false; m_value_set=false;} + param_info( unsigned index, std::string name, int type ) + { + m_valid = true; + m_value_set = false; + m_index = index; + m_name = name; + m_type = type; + } + void add_data( param_t v ) { + m_value_set = true; + m_value = v; + } + std::string get_name() const { return m_name; } + int get_type() const { return m_type; } + param_t get_value() const { assert(m_value_set); return m_value; } +private: + bool m_valid; + unsigned m_index; + std::string m_name; + int m_type; + bool m_value_set; + param_t m_value; +}; + +class function_info { +public: + function_info(int entry_point ) + { + m_entry_point = entry_point?true:false; + num_reconvergence_pairs = 0; + m_symtab = NULL; + m_assembled = false; + m_return_var_sym = NULL; + m_kernel_info.cmem = 0; + m_kernel_info.lmem = 0; + m_kernel_info.regs = 0; + m_kernel_info.smem = 0; + } + void set_name(const char *name) + { + m_name = name; + } + void set_symtab(symbol_table *symtab ) + { + m_symtab = symtab; + } + std::string get_name() + { + return m_name; + } + void print_insn( unsigned pc, FILE * fp ) const; + void add_inst( const std::list<ptx_instruction*> &instructions ) + { + m_instructions = instructions; + } + std::list<ptx_instruction*>::iterator find_next_real_instruction( std::list<ptx_instruction*>::iterator i ); + void create_basic_blocks( ); + + void print_basic_blocks(); + + void print_basic_block_links(); + void print_basic_block_dot(); + + void connect_basic_blocks( ); //iterate across m_basic_blocks of function, connecting basic blocks together + + //iterate across m_basic_blocks of function, + //finding postdominator blocks, using algorithm of + //Muchnick's Adv. Compiler Design & Implemmntation Fig 7.14 + void find_postdominators( ); + + //iterate across m_basic_blocks of function, + //finding immediate postdominator blocks, using algorithm of + //Muchnick's Adv. Compiler Design & Implemmntation Fig 7.15 + void find_ipostdominators( ); + + void print_postdominators(); + + void print_ipostdominators(); + + unsigned get_num_reconvergence_pairs(); + + void get_reconvergence_pairs(gpgpu_recon_t* recon_points); + + unsigned get_function_size() { return m_instructions.size();} + + void ptx_assemble(); + void ptx_decode_inst( ptx_thread_info *thd, + unsigned *op_type, + int *i1, + int *i2, + int *i3, + int *i4, + int *o1, + int *o2, + int *o3, + int *o4, + int *vectorin, + int *vectorout, + int *arch_reg ); + unsigned ptx_get_inst_op( ptx_thread_info *thread ); + void ptx_exec_inst( ptx_thread_info *thd, addr_t *addr, unsigned *space, unsigned *data_size, dram_callback_t* callback, unsigned warp_active_mask ); + void add_param( const char *name, struct param_t value ) + { + m_params[ name ] = value; + } + void add_param_name_and_type( unsigned index, std::string name, int type ); + void add_param_data( unsigned argn, struct gpgpu_ptx_sim_arg *args ); + void add_return_var( const symbol *rv ) + { + m_return_var_sym = rv; + } + void add_arg( const symbol *arg ) + { + assert( arg != NULL ); + m_args.push_back(arg); + } + void remove_args() + { + m_args.clear(); + } + unsigned num_args() const + { + return m_args.size(); + } + const symbol* get_arg( unsigned n ) const + { + assert( n < m_args.size() ); + return m_args[n]; + } + bool has_return() const + { + return m_return_var_sym != NULL; + } + const symbol *get_return_var() const + { + return m_return_var_sym; + } + const ptx_instruction *get_instruction( unsigned PC ) const + { + unsigned index = PC - m_start_PC; + if( index < m_instr_mem_size ) + return m_instr_mem[index]; + return NULL; + } + addr_t get_start_PC() const + { + return m_start_PC; + } + + void finalize( memory_space *param_mem, symbol_table *symtab ) + { + unsigned param_address = 0; + for( std::map<unsigned,param_info>::iterator i=m_ptx_param_info.begin(); i!=m_ptx_param_info.end(); i++ ) { + param_info &p = i->second; + std::string name = p.get_name(); + int type = p.get_type(); + param_t value = p.get_value(); + value.type = type; + symbol *param = symtab->lookup(name.c_str()); + unsigned xtype = param->type()->get_key().scalar_type(); + assert(xtype==(unsigned)type); + int tmp; + size_t size; + type_decode(xtype,size,tmp); + param_mem->write(param_address,size/8,&value); + param->set_address(param_address); + param_address += 8;//align to 64 bits so mem_access doesn't complain (before was size/8); + } + } + ptx_reg_t get_param( const std::string &name ) const + { + std::map<std::string,param_t>::const_iterator i = m_params.find(name); + if ( i == m_params.end() ) { + printf("Loader error: parameter \"%s\" value not defined in configuration\n", name.c_str() ); + abort(); + } else { + param_t x = i->second; + ptx_reg_t y; + switch ( x.type ) { + case S8_TYPE: + case S16_TYPE: + case S32_TYPE: + case S64_TYPE: + case B8_TYPE: + case B16_TYPE: + case B32_TYPE: + case B64_TYPE: + case U8_TYPE: + case U16_TYPE: + case U32_TYPE: + case U64_TYPE: + y.u64 = x.data.int_value; + break; + case F16_TYPE: + assert(0); + case F32_TYPE: + y.f32 = x.data.float_value; + break; + case F64_TYPE: + y.f64 = x.data.double_value; + break; + } + return y; + } + } + + const struct gpgpu_ptx_sim_kernel_info* get_kernel_info () { + return &m_kernel_info; + } + + const void set_kernel_info (const struct gpgpu_ptx_sim_kernel_info *info) { + m_kernel_info = *info; + } + symbol_table *get_symtab() + { + return m_symtab; + } + + static const ptx_instruction* pc_to_instruction(unsigned pc) + { + assert(pc > 0); + assert(pc <= s_g_pc_to_insn.size()); + return s_g_pc_to_insn[pc - 1]; + } + +private: + bool m_entry_point; + bool m_assembled; + std::string m_name; + ptx_instruction **m_instr_mem; + unsigned m_start_PC; + unsigned m_instr_mem_size; + std::map<std::string,param_t> m_params; + std::map<unsigned,param_info> m_ptx_param_info; + const symbol *m_return_var_sym; + std::vector<const symbol*> m_args; + std::list<ptx_instruction*> m_instructions; + std::vector<basic_block_t*> m_basic_blocks; + std::list<std::pair<unsigned, unsigned> > m_back_edges; + std::map<std::string,unsigned> labels; + unsigned num_reconvergence_pairs; + + //Registers/shmem/etc. used (from ptxas -v), loaded from ___.ptxinfo along with ___.ptx + struct gpgpu_ptx_sim_kernel_info m_kernel_info; + + symbol_table *m_symtab; + + static std::vector<ptx_instruction*> s_g_pc_to_insn; // a direct mapping from PC to instruction +}; + +/*******************************/ +// These declarations should be identical to those in ./../../cuda-sim-dev/libcuda/texture_types.h +enum cudaChannelFormatKind { + cudaChannelFormatKindSigned, + cudaChannelFormatKindUnsigned, + cudaChannelFormatKindFloat +}; + +struct cudaChannelFormatDesc { + int x; + int y; + int z; + int w; + enum cudaChannelFormatKind f; +}; + +struct cudaArray { + void *devPtr; + int devPtr32; + struct cudaChannelFormatDesc desc; + int width; + int height; + int size; //in bytes + unsigned dimensions; +}; + +enum cudaTextureAddressMode { + cudaAddressModeWrap, + cudaAddressModeClamp +}; + +enum cudaTextureFilterMode { + cudaFilterModePoint, + cudaFilterModeLinear +}; + +enum cudaTextureReadMode { + cudaReadModeElementType, + cudaReadModeNormalizedFloat +}; + +struct textureReference { + int normalized; + enum cudaTextureFilterMode filterMode; + enum cudaTextureAddressMode addressMode[2]; + struct cudaChannelFormatDesc channelDesc; +}; + +/**********************************/ + +struct textureInfo { + unsigned int texel_size; //size in bytes, e.g. (channelDesc.x+y+z+w)/8 + unsigned int Tx,Ty; //tiling factor dimensions of layout of texels per 64B cache block + unsigned int Tx_numbits,Ty_numbits; //log2(T) + unsigned int texel_size_numbits; //log2(texel_size) +}; + + +extern function_info *g_func_info; + +extern int g_error_detected; +extern bool g_debug_ir_generation; +extern std::list<ptx_instruction*> g_instructions; +extern symbol_table *g_current_symbol_table; +extern symbol_table *g_entrypoint_symbol_table; +extern function_info *g_entrypoint_func_info; +extern symbol_table *g_global_symbol_table; +void init_parser(); + +extern "C" { +#endif + + void start_function( int entry_point ); + void add_function_name( const char *fname ); + void init_directive_state(); + void add_directive(); + void end_function(); + void add_identifier( const char *s, int array_dim, unsigned array_ident ); + void add_function_arg(); + void add_scalar_type_spec( int type_spec ); + void add_scalar_operand( const char *identifier ); + void add_neg_pred_operand( const char *identifier ); + void add_variables(); + void set_variable_type(); + void add_opcode( int opcode ); + void add_pred( const char *identifier, int negate ); + void add_2vector_operand( const char *d1, const char *d2 ); + void add_3vector_operand( const char *d1, const char *d2, const char *d3 ); + void add_4vector_operand( const char *d1, const char *d2, const char *d3, const char *d4 ); + void add_option(int option ); + void add_builtin_operand( int builtin, int dim_modifier ); + void add_memory_operand( ); + void add_literal_int( int value ); + void add_literal_float( float value ); + void add_literal_double( double value ); + void add_address_operand( const char *identifier, int offset ); + void add_label( const char *idenfiier ); + void add_vector_spec(int spec ); + void add_space_spec(int spec ); + void add_extern_spec(); + void add_instruction(); + void set_return(); + void add_alignment_spec( int spec ); + void add_array_initializer(); + void add_file( unsigned num, const char *filename ); + void *reset_symtab(); + void set_symtab(void*); + + +#define NON_ARRAY_IDENTIFIER 1 +#define ARRAY_IDENTIFIER_NO_DIM 2 +#define ARRAY_IDENTIFIER 3 + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/cuda-sim/ptx_sim.cc b/src/cuda-sim/ptx_sim.cc new file mode 100644 index 0000000..8ec60df --- /dev/null +++ b/src/cuda-sim/ptx_sim.cc @@ -0,0 +1,432 @@ +/* + * Copyright (c) 2009 by Tor M. Aamodt, Ali Bakhoda, George L. Yuan, + * Dan O'Connor, and the University of British Columbia + * Vancouver, BC V6T 1Z4 + * All Rights Reserved. + * + * THIS IS A LEGAL DOCUMENT BY DOWNLOADING GPGPU-SIM, YOU ARE AGREEING TO THESE + * TERMS AND CONDITIONS. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * NOTE: The files libcuda/cuda_runtime_api.c and src/cuda-sim/cuda-math.h + * are derived from the CUDA Toolset available from http://www.nvidia.com/cuda + * (property of NVIDIA). The files benchmarks/BlackScholes/ and + * benchmarks/template/ are derived from the CUDA SDK available from + * http://www.nvidia.com/cuda (also property of NVIDIA). The files from + * src/intersim/ are derived from Booksim (a simulator provided with the + * textbook "Principles and Practices of Interconnection Networks" available + * from http://cva.stanford.edu/books/ppin/). As such, those files are bound by + * the corresponding legal terms and conditions set forth separately (original + * copyright notices are left in files from these sources and where we have + * modified a file our copyright notice appears before the original copyright + * notice). + * + * Using this version of GPGPU-Sim requires a complete installation of CUDA + * which is distributed seperately by NVIDIA under separate terms and + * conditions. To use this version of GPGPU-Sim with OpenCL requires a + * recent version of NVIDIA's drivers which support OpenCL. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the University of British Columbia nor the names of + * its contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * 4. This version of GPGPU-SIM is distributed freely for non-commercial use only. + * + * 5. No nonprofit user may place any restrictions on the use of this software, + * including as modified by the user, by any other authorized user. + * + * 6. GPGPU-SIM was developed primarily by Tor M. Aamodt, Wilson W. L. Fung, + * Ali Bakhoda, George L. Yuan, at the University of British Columbia, + * Vancouver, BC V6T 1Z4 + */ + +#include "ptx_sim.h" +#include <string> +#include "ptx_ir.h" + +std::set<unsigned long long> g_ptx_cta_info_sm_idx_used; +unsigned long long g_ptx_cta_info_uid = 1; +extern int gpgpu_option_spread_blocks_across_cores; + +ptx_cta_info::ptx_cta_info( unsigned sm_idx ) +{ + assert( g_ptx_cta_info_sm_idx_used.find(sm_idx) == g_ptx_cta_info_sm_idx_used.end() ); + g_ptx_cta_info_sm_idx_used.insert(sm_idx); + + m_sm_idx = sm_idx; + m_uid = g_ptx_cta_info_uid++; +} + +void ptx_cta_info::add_thread( ptx_thread_info *thd ) +{ + m_threads_in_cta.insert(thd); +} + +void ptx_cta_info::add_to_barrier( ptx_thread_info *thd ) +{ + m_threads_waiting_at_barrier.insert(thd); +} + +void ptx_cta_info::release_barrier() +{ + assert( m_threads_waiting_at_barrier.size() == m_threads_in_cta.size() ); + m_threads_waiting_at_barrier.clear(); +} + +bool ptx_cta_info::all_at_barrier() const +{ + return(m_threads_waiting_at_barrier.size() == m_threads_in_cta.size()); +} + +unsigned ptx_cta_info::num_threads() const +{ + return m_threads_in_cta.size(); +} + +void ptx_cta_info::check_cta_thread_status_and_reset() +{ + bool fail = false; + if ( m_threads_that_have_exited.size() != m_threads_in_cta.size() ) { + printf("\n\n"); + printf("Execution error: Some threads still running in CTA during CTA reallocation! (1)\n"); + printf(" CTA uid = %Lu (sm_idx = %u) : %lu running out of %zu total\n", + m_uid, + m_sm_idx, + (m_threads_in_cta.size() - m_threads_that_have_exited.size()), m_threads_in_cta.size() ); + printf(" These are the threads that are still running:\n"); + std::set<ptx_thread_info*>::iterator t_iter; + for ( t_iter=m_threads_in_cta.begin(); t_iter != m_threads_in_cta.end(); ++t_iter ) { + ptx_thread_info *t = *t_iter; + if ( m_threads_that_have_exited.find(t) == m_threads_that_have_exited.end() ) { + if ( m_dangling_pointers.find(t) != m_dangling_pointers.end() ) { + printf(" <thread deleted>\n"); + } else { + printf(" [done=%c] : ", (t->is_done()?'Y':'N') ); + t->print_insn( t->get_pc(), stdout ); + printf("\n"); + } + } + } + printf("\n\n"); + fail = true; + } + assert_barrier_empty(true); + if ( fail ) { + abort(); + } + + bool fail2 = false; + std::set<ptx_thread_info*>::iterator t_iter; + for ( t_iter=m_threads_in_cta.begin(); t_iter != m_threads_in_cta.end(); ++t_iter ) { + ptx_thread_info *t = *t_iter; + if ( m_dangling_pointers.find(t) == m_dangling_pointers.end() ) { + if ( !t->is_done() ) { + if ( !fail2 ) { + printf("Execution error: Some threads still running in CTA during CTA reallocation! (2)\n"); + printf(" CTA uid = %Lu (sm_idx = %u) :\n", m_uid, m_sm_idx ); + fail2 = true; + } + printf(" "); + t->print_insn( t->get_pc(), stdout ); + printf("\n"); + } + } + } + if ( fail2 ) { + abort(); + } + m_threads_in_cta.clear(); + m_threads_that_have_exited.clear(); + m_dangling_pointers.clear(); +} + +void ptx_cta_info::assert_barrier_empty( bool called_from_delete_threads ) const +{ + if ( !m_threads_waiting_at_barrier.empty() ) { + printf( "\n\n" ); + printf( "Execution error: Some threads at barrier at %s:\n", + (called_from_delete_threads?"CTA re-init":"thread exit") ); + printf( " CTA uid = %Lu (sm_idx = %u)\n", m_uid, m_sm_idx ); + std::set<ptx_thread_info*>::iterator b; + for ( b=m_threads_waiting_at_barrier.begin(); b != m_threads_waiting_at_barrier.end(); ++b ) { + ptx_thread_info *t = *b; + if ( m_dangling_pointers.find(t) != m_dangling_pointers.end() ) { + printf(" <thread deleted>\n"); + } else { + printf(" "); + t->print_insn( t->get_pc(), stdout ); + printf("\n"); + } + } + printf("\n\n"); + abort(); + } +} + +void ptx_cta_info::register_thread_exit( ptx_thread_info *thd ) +{ + assert( m_threads_that_have_exited.find(thd) == m_threads_that_have_exited.end() ); + m_threads_that_have_exited.insert(thd); +} + +void ptx_cta_info::register_deleted_thread( ptx_thread_info *thd ) +{ + m_dangling_pointers.insert(thd); +} + +unsigned ptx_cta_info::get_sm_idx() const +{ + return m_sm_idx; +} + +unsigned g_ptx_thread_info_uid_next=1; +unsigned g_ptx_thread_info_delete_count=0; + +ptx_thread_info::~ptx_thread_info() +{ + g_ptx_thread_info_delete_count++; +} + +ptx_thread_info::ptx_thread_info() +{ + m_uid = g_ptx_thread_info_uid_next++; + m_core = NULL; + m_barrier_num = -1; + m_at_barrier = false; + m_valid = false; + m_gridid = 0; + m_thread_done = false; + m_cycle_done = 0; + m_PC=0; + m_icount = 0; + m_last_effective_address = 0; + m_last_memory_space = 0; + m_branch_taken = 0; + m_shared_mem = NULL; + m_cta_info = NULL; + m_local_mem = NULL; + m_symbol_table = NULL; + m_func_info = NULL; + m_hw_tid = -1; + m_hw_wid = -1; + m_hw_sid = -1; + m_last_dram_callback.function = NULL; + m_last_dram_callback.instruction = NULL; + m_regs.push_back( std::map<std::string,ptx_reg_t>() ); + m_debug_trace_regs_modified.push_back( std::map<std::string,ptx_reg_t>() ); + m_callstack.push_back( stack_entry() ); + m_RPC = -1; + m_RPC_updated = false; + m_last_was_call = false; +} + +extern unsigned long long gpu_sim_cycle; + +void ptx_thread_info::set_done() +{ + assert( !m_at_barrier ); + m_thread_done = true; + m_cycle_done = gpu_sim_cycle; +} + +extern unsigned long long gpu_sim_cycle; +extern signed long long gpu_tot_sim_cycle; + +unsigned ptx_thread_info::get_builtin( int builtin_id, unsigned dim_mod ) +{ + assert( m_valid ); + switch (builtin_id) { + case NTID_ID: + assert( dim_mod < 3 ); + return m_ntid[dim_mod]; + case CLOCK_ID: + return gpu_sim_cycle + gpu_tot_sim_cycle; + case CTA_ID: + assert( dim_mod < 3 ); + return m_ctaid[dim_mod]; + case GRIDID_ID: + return m_gridid; + case NCTAID_ID: + assert( dim_mod < 3 ); + return m_nctaid[dim_mod]; + case TID_ID: + assert( dim_mod < 3 ); + return m_tid[dim_mod]; + default: + assert(0); + } + return 0; +} + +void ptx_thread_info::set_info( symbol_table *symtab, function_info *func ) +{ + m_symbol_table = symtab; + m_func_info = func; + m_PC = func->get_start_PC(); +} + +void ptx_thread_info::print_insn( unsigned pc, FILE * fp ) const +{ + m_func_info->print_insn(pc,fp); +} + +static void print_reg( std::string name, ptx_reg_t value ) +{ + const symbol *sym = g_current_symbol_table->lookup(name.c_str()); + printf(" %8s ", name.c_str() ); + if( sym == NULL ) { + printf("<unknown type> 0x%llx\n", (unsigned long long ) value.u64 ); + return; + } + const type_info *t = sym->type(); + type_info_key ti = t->get_key(); + + switch ( ti.scalar_type() ) { + case S8_TYPE: printf(".s8 %d\n", value.s8 ); break; + case S16_TYPE: printf(".s16 %d\n", value.s16 ); break; + case S32_TYPE: printf(".s32 %d\n", value.s32 ); break; + case S64_TYPE: printf(".s64 %Ld\n", value.s64 ); break; + case U8_TYPE: printf(".u8 0x%02x\n", (unsigned) value.u8 ); break; + case U16_TYPE: printf(".u16 0x%04x\n", (unsigned) value.u16 ); break; + case U32_TYPE: printf(".u32 0x%08x\n", (unsigned) value.u32 ); break; + case U64_TYPE: printf(".u64 0x%llx\n", value.u64 ); break; + case F16_TYPE: printf(".f16 %f [0x%04x]\n", value.f16, (unsigned) value.u16 ); break; + case F32_TYPE: printf(".f32 %.15lf [0x%08x]\n", value.f32, value.u32 ); break; + case F64_TYPE: printf(".f64 %.15le [0x%016llx]\n", value.f64, value.u64 ); break; + case B8_TYPE: printf(".b8 0x%02x\n", (unsigned) value.u8 ); break; + case B16_TYPE: printf(".b16 0x%04x\n", (unsigned) value.u16 ); break; + case B32_TYPE: printf(".b32 0x%08x\n", (unsigned) value.u32 ); break; + case B64_TYPE: printf(".b64 0x%llx\n", (unsigned long long ) value.u64 ); break; + case PRED_TYPE: printf(".pred %u\n", (unsigned) value.pred ); break; + default: + printf( "non-scalar type\n" ); + break; + } +} + +void ptx_thread_info::callstack_push( unsigned pc, unsigned rpc, const symbol *return_var_src, const symbol *return_var_dst, unsigned call_uid ) +{ + m_RPC = -1; + m_RPC_updated = true; + m_last_was_call = true; + m_callstack.push_back( stack_entry(m_symbol_table,m_func_info,pc,rpc,return_var_src,return_var_dst,call_uid) ); + m_regs.push_back( std::map<std::string,ptx_reg_t>() ); + m_debug_trace_regs_modified.push_back( std::map<std::string,ptx_reg_t>() ); +} + +#define POST_DOMINATOR 1 /* must match definition in shader.h */ +extern int gpgpu_simd_model; +extern ptx_reg_t get_operand_value( const symbol *reg ); +extern void set_operand_value( const symbol *dst, const ptx_reg_t &data ); + +bool ptx_thread_info::callstack_pop() +{ + assert( !m_callstack.empty() ); + m_func_info = m_callstack.back().m_func_info; + m_symbol_table = m_callstack.back().m_symbol_table; + m_NPC = m_callstack.back().m_PC; + m_RPC_updated = true; + m_last_was_call = false; + m_RPC = m_callstack.back().m_RPC; + const symbol *rv_src = m_callstack.back().m_return_var_src; + const symbol *rv_dst = m_callstack.back().m_return_var_dst; + assert( !((rv_src != NULL) ^ (rv_dst != NULL)) ); + ptx_reg_t rval; + if( rv_src != NULL ) + rval = get_operand_value(rv_src); + m_callstack.pop_back(); + m_regs.pop_back(); + m_debug_trace_regs_modified.pop_back(); + if( rv_dst != NULL ) + set_operand_value(rv_dst,rval); + return m_callstack.empty(); +} + +void ptx_thread_info::dump_callstack() const +{ + std::list<stack_entry>::const_iterator c=m_callstack.begin(); + std::list<std::map<std::string,ptx_reg_t> >::const_iterator r=m_regs.begin(); + + printf("\n\n"); + printf("Call stack for thread uid = %u (sc=%u, hwtid=%u)\n", m_uid, m_hw_sid, m_hw_tid ); + while( c != m_callstack.end() && r != m_regs.end() ) { + const stack_entry &c_e = *c; + const std::map<std::string,ptx_reg_t> ®s = *r; + if( !c_e.m_valid ) { + printf(" <entry> #regs = %zu\n", regs.size() ); + } else { + printf(" %20s PC=%3u RV= (callee=\'%s\',caller=\'%s\') #regs = %zu\n", + c_e.m_func_info->get_name().c_str(), c_e.m_PC, + c_e.m_return_var_src->name().c_str(), + c_e.m_return_var_dst->name().c_str(), + regs.size() ); + } + c++; + r++; + } + if( c != m_callstack.end() || r != m_regs.end() ) { + printf(" *** mismatch in m_regs and m_callstack sizes ***\n" ); + } + printf("\n\n"); +} + +std::string ptx_thread_info::get_location() const +{ + const ptx_instruction *pI = m_func_info->get_instruction(m_PC); + char buf[1024]; + snprintf(buf,1024,"%s:%u", pI->source_file(), pI->source_line() ); + return std::string(buf); +} + +void ptx_thread_info::dump_regs() +{ + printf("Register File Contents:\n"); + std::map<std::string,ptx_reg_t>::const_iterator r; + for ( r=m_regs.back().begin(); r != m_regs.back().end(); ++r ) { + std::string name = r->first; + ptx_reg_t value = r->second; + print_reg(name,value); + + } +} + +void ptx_thread_info::dump_modifiedregs() +{ + if( m_debug_trace_regs_modified.empty() ) + return; + printf("Modified Registers:\n"); + std::map<std::string,ptx_reg_t>::const_iterator r; + for ( r=m_debug_trace_regs_modified.back().begin(); r != m_debug_trace_regs_modified.back().end(); ++r ) { + std::string name = r->first; + ptx_reg_t value = r->second; + print_reg(name,value); + } +} + +void ptx_thread_info::set_npc( const function_info *f ) +{ + m_NPC = f->get_start_PC(); + m_func_info = const_cast<function_info*>( f ); + m_symbol_table = m_func_info->get_symtab(); +} diff --git a/src/cuda-sim/ptx_sim.h b/src/cuda-sim/ptx_sim.h new file mode 100644 index 0000000..17eb250 --- /dev/null +++ b/src/cuda-sim/ptx_sim.h @@ -0,0 +1,457 @@ +/* + * Copyright (c) 2009 by Tor M. Aamodt, Wilson W. L. Fung, Ali Bakhoda, + * George L. Yuan, Dan O'Connor, Henry Wong and the + * University of British Columbia + * Vancouver, BC V6T 1Z4 + * All Rights Reserved. + * + * THIS IS A LEGAL DOCUMENT BY DOWNLOADING GPGPU-SIM, YOU ARE AGREEING TO THESE + * TERMS AND CONDITIONS. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * NOTE: The files libcuda/cuda_runtime_api.c and src/cuda-sim/cuda-math.h + * are derived from the CUDA Toolset available from http://www.nvidia.com/cuda + * (property of NVIDIA). The files benchmarks/BlackScholes/ and + * benchmarks/template/ are derived from the CUDA SDK available from + * http://www.nvidia.com/cuda (also property of NVIDIA). The files from + * src/intersim/ are derived from Booksim (a simulator provided with the + * textbook "Principles and Practices of Interconnection Networks" available + * from http://cva.stanford.edu/books/ppin/). As such, those files are bound by + * the corresponding legal terms and conditions set forth separately (original + * copyright notices are left in files from these sources and where we have + * modified a file our copyright notice appears before the original copyright + * notice). + * + * Using this version of GPGPU-Sim requires a complete installation of CUDA + * which is distributed seperately by NVIDIA under separate terms and + * conditions. To use this version of GPGPU-Sim with OpenCL requires a + * recent version of NVIDIA's drivers which support OpenCL. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the University of British Columbia nor the names of + * its contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * 4. This version of GPGPU-SIM is distributed freely for non-commercial use only. + * + * 5. No nonprofit user may place any restrictions on the use of this software, + * including as modified by the user, by any other authorized user. + * + * 6. GPGPU-SIM was developed primarily by Tor M. Aamodt, Wilson W. L. Fung, + * Ali Bakhoda, George L. Yuan, at the University of British Columbia, + * Vancouver, BC V6T 1Z4 + */ + +#ifndef ptx_sim_h_INCLUDED +#define ptx_sim_h_INCLUDED + +#include <stdlib.h> + +#include "dram_callback.h" +#include "../util.h" +#include "../abstract_hardware_model.h" + +typedef address_type addr_t; + +struct dim3 { + unsigned int x, y, z; +}; + +struct gpgpu_ptx_sim_arg { + const void *m_start; + size_t m_nbytes; + size_t m_offset; + struct gpgpu_ptx_sim_arg *m_next; +}; + +//Holds properties of the kernel (Kernel's resource use). These will be zero if +//the ptxinfo file is not present. +struct gpgpu_ptx_sim_kernel_info { + int lmem; + int smem; + int cmem; + int regs; +}; + +#include <assert.h> +#include "opcodes.h" + +struct param_t { + union { + float float_value; + int int_value; + double double_value; + unsigned long long ptr_value; + } data; + int type; +}; + +#ifdef __cplusplus + + #include <string> + #include <map> + #include <set> + #include <list> + +#include "memory.h" + +union ptx_reg_t { + ptx_reg_t() { + bits.ms = 0; + bits.ls = 0; + } + ptx_reg_t(unsigned x) + { + bits.ms = 0; + bits.ls = 0; + u32 = x; + } + operator unsigned int() { return u32;} + operator unsigned short() { return u16;} + operator unsigned char() { return u8;} + operator unsigned long long() { return u64;} + + void mask_and( unsigned ms, unsigned ls ) + { + bits.ms &= ms; + bits.ls &= ls; + } + + void mask_or( unsigned ms, unsigned ls ) + { + bits.ms |= ms; + bits.ls |= ls; + } + int get_bit( unsigned bit ) + { + if ( bit < 32 ) + return(bits.ls >> bit) & 1; + else + return(bits.ms >> (bit-32)) & 1; + } + + signed char s8; + signed short s16; + signed int s32; + signed long long s64; + unsigned char u8; + unsigned short u16; + unsigned int u32; + unsigned long long u64; + float f16; + float f32; + double f64; + struct { + unsigned ls; + unsigned ms; + } bits; + unsigned pred : 1; + +}; + +class ptx_instruction; +class operand_info; +class symbol_table; +class function_info; +class ptx_thread_info; + +class ptx_cta_info { +public: + ptx_cta_info( unsigned sm_idx ); + void add_thread( ptx_thread_info *thd ); + void add_to_barrier( ptx_thread_info *thd ); + bool all_at_barrier() const; + void release_barrier(); + unsigned num_threads() const; + void check_cta_thread_status_and_reset(); + void assert_barrier_empty( bool called_from_delete_threads = false ) const; + void register_thread_exit( ptx_thread_info *thd ); + void register_deleted_thread( ptx_thread_info *thd ); + unsigned get_sm_idx() const; + +private: + unsigned long long m_uid; + unsigned m_sm_idx; + std::set<ptx_thread_info*> m_threads_in_cta; + std::set<ptx_thread_info*> m_threads_waiting_at_barrier; + std::set<ptx_thread_info*> m_threads_that_have_exited; + std::set<ptx_thread_info*> m_dangling_pointers; +}; + +class symbol; + +struct stack_entry { + stack_entry() { + m_symbol_table=NULL; + m_func_info=NULL; + m_PC=0; + m_RPC=-1; + m_return_var_src = NULL; + m_return_var_dst = NULL; + m_call_uid = 0; + m_valid = false; + } + stack_entry( symbol_table *s, function_info *f, unsigned pc, unsigned rpc, const symbol *return_var_src, const symbol *return_var_dst, unsigned call_uid ) + { + m_symbol_table=s; + m_func_info=f; + m_PC=pc; + m_RPC=rpc; + m_return_var_src = return_var_src; + m_return_var_dst = return_var_dst; + m_call_uid = call_uid; + m_valid = true; + } + + bool m_valid; + symbol_table *m_symbol_table; + function_info *m_func_info; + unsigned m_PC; + unsigned m_RPC; + const symbol *m_return_var_src; + const symbol *m_return_var_dst; + unsigned m_call_uid; +}; + +class ptx_thread_info { +public: + ~ptx_thread_info(); + ptx_thread_info(); + + ptx_reg_t get_operand_value( const symbol *reg ); + ptx_reg_t get_operand_value( const operand_info &op ); + void set_operand_value( const operand_info &dst, const ptx_reg_t &data ); + void set_operand_value( const symbol *dst, const ptx_reg_t &data ); + void get_vector_operand_values( const operand_info &op, ptx_reg_t* ptx_regs, unsigned num_elements ); + void set_vector_operand_values( const operand_info &dst, + const ptx_reg_t &data1, + const ptx_reg_t &data2, + const ptx_reg_t &data3, + const ptx_reg_t &data4, + unsigned num_elements ); + + function_info *func_info() + { + return m_func_info; + } + void print_insn( unsigned pc, FILE * fp ) const; + void set_info( symbol_table *symtab, function_info *func ); + unsigned get_uid() const + { + return m_uid; + } + + dim3 get_ctaid() const + { + dim3 r; + r.x = m_ctaid[0]; + r.y = m_ctaid[1]; + r.z = m_ctaid[2]; + return r; + } + dim3 get_tid() const + { + dim3 r; + r.x = m_tid[0]; + r.y = m_tid[1]; + r.z = m_tid[2]; + return r; + } + unsigned get_hw_tid() const { return m_hw_tid;} + unsigned get_hw_ctaid() const { return m_hw_ctaid;} + unsigned get_hw_wid() const { return m_hw_wid;} + unsigned get_hw_sid() const { return m_hw_sid;} + void set_hw_tid(unsigned tid) { m_hw_tid=tid;} + void set_hw_wid(unsigned wid) { m_hw_wid=wid;} + void set_hw_sid(unsigned sid) { m_hw_sid=sid;} + void set_hw_ctaid(unsigned cta_id) { m_hw_ctaid=cta_id;} + void set_core(core_t *core) { m_core = core; } + core_t *get_core() { return m_core; } + + unsigned get_icount() const { return m_icount;} + void set_valid() { m_valid = true;} + addr_t last_eaddr() const { return m_last_effective_address;} + unsigned last_space() const { return m_last_memory_space;} + dram_callback_t last_callback() const { return m_last_dram_callback;} + void set_at_barrier( int barrier_num ) + { + m_barrier_num = barrier_num; + m_at_barrier = true; + m_cta_info->add_to_barrier(this); + } + bool is_at_barrier() const { return m_at_barrier;} + bool all_at_barrier() const { return m_cta_info->all_at_barrier();} + unsigned long long get_cta_uid() { return m_cta_info->get_sm_idx();} + void clear_barrier( ) + { + m_barrier_num = -1; + m_at_barrier = false; + } + void release_barrier() { m_cta_info->release_barrier();} + + void set_single_thread_single_block() + { + m_ntid[0] = 1; + m_ntid[1] = 1; + m_ntid[2] = 1; + m_ctaid[0] = 0; + m_ctaid[1] = 0; + m_ctaid[2] = 0; + m_tid[0] = 0; + m_tid[1] = 0; + m_tid[2] = 0; + m_nctaid[0] = 1; + m_nctaid[1] = 1; + m_nctaid[2] = 1; + m_gridid = 0; + m_valid = true; + } + void set_tid( int x, int y, int z) + { + m_tid[0] = x; + m_tid[1] = y; + m_tid[2] = z; + } + void set_ctaid( int x, int y, int z) + { + m_ctaid[0] = x; + m_ctaid[1] = y; + m_ctaid[2] = z; + } + void set_ntid( int x, int y, int z) + { + m_ntid[0] = x; + m_ntid[1] = y; + m_ntid[2] = z; + } + void set_nctaid( int x, int y, int z) + { + m_nctaid[0] = x; + m_nctaid[1] = y; + m_nctaid[2] = z; + } + + unsigned get_builtin( int builtin_id, unsigned dim_mod ); + + void set_done(); + bool is_done() { return m_thread_done;} + unsigned donecycle() const { return m_cycle_done; } + + unsigned next_instr() + { + m_NPC = m_PC+1; // increment to next instruction in case of no branch + m_icount++; + m_branch_taken = false; + return m_PC; + } + bool branch_taken() const + { + return m_branch_taken; + } + unsigned get_pc() const + { + return m_PC; + } + void set_npc( unsigned npc ) + { + m_NPC = npc; + } + void set_npc( const function_info *f ); + void callstack_push( unsigned npc, unsigned rpc, const symbol *return_var_src, const symbol *return_var_dst, unsigned call_uid ); + bool callstack_pop(); + void dump_callstack() const; + std::string get_location() const; + bool rpc_updated() const { return m_RPC_updated; } + bool last_was_call() const { return m_last_was_call; } + unsigned get_rpc() const { return m_RPC; } + void clearRPC() + { + m_RPC = -1; + m_RPC_updated = false; + m_last_was_call = false; + } + unsigned get_return_PC() + { + return m_callstack.back().m_PC; + } + void update_pc() + { + m_PC = m_NPC; + } + void dump_regs(); + void dump_modifiedregs(); + void clear_modifiedregs() { m_debug_trace_regs_modified.back().clear();} + function_info *get_finfo() { return m_func_info; } + +public: + addr_t m_last_effective_address; + bool m_branch_taken; + unsigned m_last_memory_space; + dram_callback_t m_last_dram_callback; + memory_space *m_shared_mem; + memory_space *m_local_mem; + ptx_cta_info *m_cta_info; + ptx_reg_t m_last_set_operand_value; + +private: + unsigned m_uid; + core_t *m_core; + bool m_valid; + unsigned m_ntid[3]; + unsigned m_tid[3]; + unsigned m_nctaid[3]; + unsigned m_ctaid[3]; + unsigned m_gridid; + bool m_thread_done; + unsigned m_hw_sid; + unsigned m_hw_tid; + unsigned m_hw_wid; + unsigned m_hw_ctaid; + + unsigned m_icount; + unsigned m_PC; + unsigned m_NPC; + unsigned m_RPC; + bool m_RPC_updated; + bool m_last_was_call; + unsigned m_cycle_done; + + int m_barrier_num; + bool m_at_barrier; + + symbol_table *m_symbol_table; + function_info *m_func_info; + + std::list<stack_entry> m_callstack; + + std::list<std::map<std::string,ptx_reg_t> > m_regs; + std::list<std::map<std::string,ptx_reg_t> > m_debug_trace_regs_modified; +}; + +unsigned type_decode( unsigned type, size_t &size, int &t ); + +#endif + +#define MAX_REG_OPERANDS 8 + +#endif diff --git a/src/cuda-sim/ptxinfo.l b/src/cuda-sim/ptxinfo.l new file mode 100644 index 0000000..3a2ab52 --- /dev/null +++ b/src/cuda-sim/ptxinfo.l @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2009 by Tor M. Aamodt and the University of British Columbia + * Vancouver, BC V6T 1Z4 + * All Rights Reserved. + * + * THIS IS A LEGAL DOCUMENT BY DOWNLOADING GPGPU-SIM, YOU ARE AGREEING TO THESE + * TERMS AND CONDITIONS. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * NOTE: The files libcuda/cuda_runtime_api.c and src/cuda-sim/cuda-math.h + * are derived from the CUDA Toolset available from http://www.nvidia.com/cuda + * (property of NVIDIA). The files benchmarks/BlackScholes/ and + * benchmarks/template/ are derived from the CUDA SDK available from + * http://www.nvidia.com/cuda (also property of NVIDIA). The files from + * src/intersim/ are derived from Booksim (a simulator provided with the + * textbook "Principles and Practices of Interconnection Networks" available + * from http://cva.stanford.edu/books/ppin/). As such, those files are bound by + * the corresponding legal terms and conditions set forth separately (original + * copyright notices are left in files from these sources and where we have + * modified a file our copyright notice appears before the original copyright + * notice). + * + * Using this version of GPGPU-Sim requires a complete installation of CUDA + * which is distributed seperately by NVIDIA under separate terms and + * conditions. To use this version of GPGPU-Sim with OpenCL requires a + * recent version of NVIDIA's drivers which support OpenCL. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the University of British Columbia nor the names of + * its contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * 4. This version of GPGPU-SIM is distributed freely for non-commercial use only. + * + * 5. No nonprofit user may place any restrictions on the use of this software, + * including as modified by the user, by any other authorized user. + * + * 6. GPGPU-SIM was developed primarily by Tor M. Aamodt, Wilson W. L. Fung, + * Ali Bakhoda, George L. Yuan, at the University of British Columbia, + * Vancouver, BC V6T 1Z4 + */ + +%option noyywrap +%option yylineno +%option prefix="ptxinfo_" +%{ +#include "ptxinfo.tab.h" +#include <string.h> + +char ptxinfo_linebuf[1024]; +unsigned ptxinfo_col = 0; +#define TC ptxinfo_col+=strlen(ptxinfo_text); +%} + +%% + +"warning"[^\n]* TC; return WARNING; +"ptxas" TC; return HEADER; +"info" TC; return INFO; +"Compiling entry function" TC; return FUNC; +"Used" TC; return USED; +"registers" TC; return REGS; +"bytes" TC; return BYTES; +"lmem" TC; return LMEM; +"smem" TC; return SMEM; +"cmem" TC; return CMEM; +"line" TC; return LINE; + +[_A-Za-z$%][_0-9A-Za-z$]* TC; ptxinfo_lval.string_value = strdup(yytext); return IDENTIFIER; +[-]{0,1}[0-9]+ TC; ptxinfo_lval.int_value = atoi(yytext); return INT_OPERAND; + +"+" TC; return PLUS; +"," TC; return COMMA; +"[" TC; return LEFT_SQUARE_BRACKET; +"]" TC; return RIGHT_SQUARE_BRACKET; +":" TC; return COLON; +";" TC; return SEMICOLON; +"'" TC; return QUOTE; +" " TC; +"\t" TC; + +\n.* ptxinfo_col=0; strncpy(ptxinfo_linebuf, yytext + 1, 1024); yyless( 1 ); + +%% + +extern int g_ptxinfo_error_detected; +extern const char *g_filename; +extern const char *g_ptxinfo_filename; + +int ptxinfo_error( const char *s ) +{ + int i; + g_ptxinfo_error_detected = 1; + fflush(stdout); + if( s != NULL ) + printf("ptxas -v %s (%s:%u) Syntax error:\n\n", g_filename, g_ptxinfo_filename, ptxinfo_lineno ); + printf(" %s\n", ptxinfo_linebuf ); + printf(" "); + for( i=0; i < ptxinfo_col-1; i++ ) { + if( ptxinfo_linebuf[i] == '\t' ) printf("\t"); + else printf(" "); + } + + printf("^\n\n"); + fflush(stdout); + exit(43); + return 0; +} diff --git a/src/cuda-sim/ptxinfo.y b/src/cuda-sim/ptxinfo.y new file mode 100644 index 0000000..c9c84b8 --- /dev/null +++ b/src/cuda-sim/ptxinfo.y @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2009 by Tor M. Aamodt, George L. Yuan and the + * University of British Columbia + * Vancouver, BC V6T 1Z4 + * All Rights Reserved. + * + * THIS IS A LEGAL DOCUMENT BY DOWNLOADING GPGPU-SIM, YOU ARE AGREEING TO THESE + * TERMS AND CONDITIONS. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * NOTE: The files libcuda/cuda_runtime_api.c and src/cuda-sim/cuda-math.h + * are derived from the CUDA Toolset available from http://www.nvidia.com/cuda + * (property of NVIDIA). The files benchmarks/BlackScholes/ and + * benchmarks/template/ are derived from the CUDA SDK available from + * http://www.nvidia.com/cuda (also property of NVIDIA). The files from + * src/intersim/ are derived from Booksim (a simulator provided with the + * textbook "Principles and Practices of Interconnection Networks" available + * from http://cva.stanford.edu/books/ppin/). As such, those files are bound by + * the corresponding legal terms and conditions set forth separately (original + * copyright notices are left in files from these sources and where we have + * modified a file our copyright notice appears before the original copyright + * notice). + * + * Using this version of GPGPU-Sim requires a complete installation of CUDA + * which is distributed seperately by NVIDIA under separate terms and + * conditions. To use this version of GPGPU-Sim with OpenCL requires a + * recent version of NVIDIA's drivers which support OpenCL. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the University of British Columbia nor the names of + * its contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * 4. This version of GPGPU-SIM is distributed freely for non-commercial use only. + * + * 5. No nonprofit user may place any restrictions on the use of this software, + * including as modified by the user, by any other authorized user. + * + * 6. GPGPU-SIM was developed primarily by Tor M. Aamodt, Wilson W. L. Fung, + * Ali Bakhoda, George L. Yuan, at the University of British Columbia, + * Vancouver, BC V6T 1Z4 + */ + +%union { + int int_value; + char * string_value; +} + +%token <int_value> INT_OPERAND +%token HEADER +%token INFO +%token FUNC +%token USED +%token REGS +%token BYTES +%token LMEM +%token SMEM +%token CMEM +%token <string_value> IDENTIFIER +%token PLUS +%token COMMA +%token LEFT_SQUARE_BRACKET +%token RIGHT_SQUARE_BRACKET +%token COLON +%token SEMICOLON +%token QUOTE +%token LINE +%token WARNING + +%{ + #include <stdlib.h> + #include <string.h> + + static unsigned g_declared; + static unsigned g_system; + int ptxinfo_lex(void); + void ptxinfo_addinfo(); + void ptxinfo_function(const char *fname ); + void ptxinfo_regs( unsigned nregs ); + void ptxinfo_lmem( unsigned declared, unsigned system ); + void ptxinfo_smem( unsigned declared, unsigned system ); + void ptxinfo_cmem( unsigned nbytes, unsigned bank ); + int ptxinfo_error(const char*); +%} + +%% + +input: /* empty */ + | input line + ; + +line: HEADER INFO COLON line_info + | HEADER IDENTIFIER COMMA LINE INT_OPERAND SEMICOLON WARNING + ; + +line_info: function_name + | function_info { ptxinfo_addinfo(); } + ; + +function_name: FUNC QUOTE IDENTIFIER QUOTE { ptxinfo_function($3); } + +function_info: info + | function_info COMMA info + ; + +info: USED INT_OPERAND REGS { ptxinfo_regs($2); } + | tuple LMEM { ptxinfo_lmem(g_declared,g_system); } + | tuple SMEM { ptxinfo_smem(g_declared,g_system); } + | INT_OPERAND BYTES CMEM LEFT_SQUARE_BRACKET INT_OPERAND RIGHT_SQUARE_BRACKET { ptxinfo_cmem($1,$5); } + | INT_OPERAND BYTES LMEM { ptxinfo_lmem($1,0); } + | INT_OPERAND BYTES SMEM { ptxinfo_smem($1,0); } + | INT_OPERAND BYTES CMEM { ptxinfo_cmem($1,0); } + | INT_OPERAND REGS { ptxinfo_regs($1); } + ; + +tuple: INT_OPERAND PLUS INT_OPERAND BYTES { g_declared=$1; g_system=$3; } + +%% + + diff --git a/src/gpgpu-sim/Makefile b/src/gpgpu-sim/Makefile new file mode 100644 index 0000000..2b1412a --- /dev/null +++ b/src/gpgpu-sim/Makefile @@ -0,0 +1,116 @@ +# Copyright (c) 2009 by Tor M. Aamodt, Wilson W. L. Fung, Ali Bakhoda, +# George L. Yuan, Ivan Sham and the +# University of British Columbia +# Vancouver, BC V6T 1Z4 +# All Rights Reserved. +# +# THIS IS A LEGAL DOCUMENT BY DOWNLOADING GPGPU-SIM, YOU ARE AGREEING TO THESE +# TERMS AND CONDITIONS. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNERS OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# +# NOTE: The files libcuda/cuda_runtime_api.c and src/cuda-sim/cuda-math.h +# are derived from the CUDA Toolset available from http://www.nvidia.com/cuda +# (property of NVIDIA). The files benchmarks/BlackScholes/* and +# benchmarks/template/* are derived from the CUDA SDK available from +# http://www.nvidia.com/cuda (also property of NVIDIA). The files +# src/gpgpusim_entrypoint.c and src/simplesim-3.0/* are derived from the +# SimpleScalar Toolset available from http://www.simplescalar.com/ +# (property of SimpleScalar LLC) and the files src/intersim/* are derived +# from Booksim (Simulator provided with the textbook "Principles and +# Practices of Interconnection Networks" available from +# http://cva.stanford.edu/books/ppin/). As such, those files are bound by +# the corresponding legal terms and conditions set forth separately (original +# copyright notices are left in files from these sources and where we have +# modified a file our copyright notice appears before the original copyright +# notice). +# +# Using this version of GPGPU-Sim requires a complete installation of CUDA +# version 1.1, which is distributed seperately by NVIDIA under separate terms +# and conditions. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# 3. Neither the name of the University of British Columbia nor the names of +# its contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# 4. This version of GPGPU-SIM is distributed freely for non-commercial use only. +# +# 5. No nonprofit user may place any restrictions on the use of this software, +# including as modified by the user, by any other authorized user. +# +# 6. GPGPU-SIM was developed primarily by Tor M. Aamodt, Wilson W. L. Fung, +# Ali Bakhoda, George L. Yuan, at the University of British Columbia, +# Vancouver, BC V6T 1Z4 + +# GPGPU-Sim Makefile + +DEBUG=0 + +CXXFLAGS = -Wall -DDEBUG +CXXFLAGS_L2CACHE = -Wall -DDEBUG + +GNUC_CPP0X := $(shell gcc --version | perl -ne 'if (/gcc\s+\(.*\)\s+([0-9.]+)/){ if($$1 >= 4.3) {$$n=1} else {$$n=0;} } END { print $$n; }') +ifeq ($(GNUC_CPP0X), 1) + CXXFLAGS += -std=c++0x + CXXFLAGS_L2CACHE += -std=c++0x +endif + +ifneq ($(DEBUG),1) + OPTFLAGS += -O3 +else + CXXFLAGS_L2CACHE += -DGLIBCXX_DEBUG_PEDANTIC + CXXFLAGS += -D_GLIBCXX_DEBUG -DGLIBCXX_DEBUG_PEDANTIC +endif + +OPTFLAGS += -g3 -fPIC + +CPP = g++ $(SNOW) +OEXT = o + +SRCS = $(shell ls *.cc) +OBJS = $(SRCS:.cc=.$(OEXT)) + +libgpu_uarch_sim.a:$(OBJS) + ar rcs libgpu_uarch_sim.a $(OBJS) + +depend: + makedepend $(SRCS) 2> /dev/null + +l2cache.$(OEXT): l2cache.cc + $(CPP) $(OPTFLAGS) $(CXXFLAGS_L2CACHE) -o $*.$(OEXT) -c $*.cc + +.cc.$(OEXT): + $(CPP) $(OPTFLAGS) $(CXXFLAGS) -o $*.$(OEXT) -c $*.cc + +clean: + rm -f *.o core *~ *.a + +option_parser.$(OEXT): option_parser.h + +dram_sched.$(OEXT): ../cuda-sim/ptx.tab.h + +../cuda-sim/ptx.tab.h: + make -C ../cuda-sim/ ptx.tab.c + +# DO NOT DELETE + diff --git a/src/gpgpu-sim/addrdec.cc b/src/gpgpu-sim/addrdec.cc new file mode 100644 index 0000000..333d6de --- /dev/null +++ b/src/gpgpu-sim/addrdec.cc @@ -0,0 +1,471 @@ +/* + * addrdec.c + * + * Copyright (c) 2009 by Tor M. Aamodt, Wilson W. L. Fung, Ali Bakhoda, + * George L. Yuan and the University of British Columbia + * Vancouver, BC V6T 1Z4 + * All Rights Reserved. + * + * THIS IS A LEGAL DOCUMENT BY DOWNLOADING GPGPU-SIM, YOU ARE AGREEING TO THESE + * TERMS AND CONDITIONS. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * NOTE: The files libcuda/cuda_runtime_api.c and src/cuda-sim/cuda-math.h + * are derived from the CUDA Toolset available from http://www.nvidia.com/cuda + * (property of NVIDIA). The files benchmarks/BlackScholes/ and + * benchmarks/template/ are derived from the CUDA SDK available from + * http://www.nvidia.com/cuda (also property of NVIDIA). The files from + * src/intersim/ are derived from Booksim (a simulator provided with the + * textbook "Principles and Practices of Interconnection Networks" available + * from http://cva.stanford.edu/books/ppin/). As such, those files are bound by + * the corresponding legal terms and conditions set forth separately (original + * copyright notices are left in files from these sources and where we have + * modified a file our copyright notice appears before the original copyright + * notice). + * + * Using this version of GPGPU-Sim requires a complete installation of CUDA + * which is distributed seperately by NVIDIA under separate terms and + * conditions. To use this version of GPGPU-Sim with OpenCL requires a + * recent version of NVIDIA's drivers which support OpenCL. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the University of British Columbia nor the names of + * its contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * 4. This version of GPGPU-SIM is distributed freely for non-commercial use only. + * + * 5. No nonprofit user may place any restrictions on the use of this software, + * including as modified by the user, by any other authorized user. + * + * 6. GPGPU-SIM was developed primarily by Tor M. Aamodt, Wilson W. L. Fung, + * Ali Bakhoda, George L. Yuan, at the University of British Columbia, + * Vancouver, BC V6T 1Z4 + */ + +#include <string.h> +#include "addrdec.h" +//#include "gpu-sim.h" +#include "../option_parser.h" + +int ADDR_CHIP_S = 10; +extern int gpgpu_mem_address_mask; + +long int powli( long int x, long int y ) // compute x to the y +{ + long int r = 1; + int i; + for (i = 0; i < y; ++i ) { + r *= x; + } + return r; +} + +void addrdec_display(addrdec_t *a) { + //printf("DRAM: unused:%x chip:%x row:%x col:%x bk:%x\n", + // a.dram.unused, a.dram.chip, GET_ROW(a), GET_COL(a), a.dram.bk); + + if (a->chip) printf("\tchip:%x ", a->chip); + if (a->row) printf("\trow:%x ", a->row); + if (a->col) printf("\tcol:%x ", a->col); + if (a->bk) printf("\tbk:%x ", a->bk); + if (a->burst) printf("\tburst:%x ", a->burst); +} + +unsigned long long int addrdec_packbits(unsigned long long int mask, + unsigned long long int val, + unsigned char high, unsigned char low) +{ + int i, pos; + unsigned long long int out; + out = 0; + pos = 0; + for (i=low;i<high;i++) { + if ((mask & ((unsigned long long int)1<<i)) != 0) { + out |= ((val & ((unsigned long long int)1<<i)) >> i) << pos; + pos++; + } + // printf("%02d: %016llx %d\n",i,out,pos); + } + + return out; +} + +unsigned long long int addrdec_mask[5] = { + 0x0000000000001C00, + 0x0000000000000300, + 0x000000000FFF0000, + 0x000000000000E0FF, + 0x000000000000000F +}; + +void addrdec_getmasklimit(unsigned long long int mask, unsigned char *high, unsigned char *low) +{ + *high = 64; + *low = 0; + int i; + int low_found = 0; + + for (i=0;i<64;i++) { + if ((mask & ((unsigned long long int)1<<i)) != 0) { + if (low_found) { + *high = i + 1; + } else { + *high = i + 1; + *low = i; + low_found = 1; + } + } + // printf("%02d: %016llx %d\n",i,out,pos); + } +} + +unsigned char addrdec_mklow[5] = { 0, 0, 0, 0, 0}; +unsigned char addrdec_mkhigh[5] = { 64, 64, 64, 64, 64}; + +static unsigned int gap; +static int Nchips; + +void addrdec_tlx(unsigned long long int addr, addrdec_t *tlx) +{ + unsigned long long int addr_for_chip,rest_of_addr; + if (!gap) { + tlx->chip = addrdec_packbits(addrdec_mask[CHIP], addr, addrdec_mkhigh[CHIP], addrdec_mklow[CHIP]); + tlx->bk = addrdec_packbits(addrdec_mask[BK], addr, addrdec_mkhigh[BK], addrdec_mklow[BK]); + tlx->row = addrdec_packbits(addrdec_mask[ROW], addr, addrdec_mkhigh[ROW], addrdec_mklow[ROW]); + tlx->col = addrdec_packbits(addrdec_mask[COL], addr, addrdec_mkhigh[COL], addrdec_mklow[COL]); + tlx->burst= addrdec_packbits(addrdec_mask[BURST], addr, addrdec_mkhigh[BURST], addrdec_mklow[BURST]); + } else { + addr_for_chip= ( (addr>>ADDR_CHIP_S) % Nchips) << ADDR_CHIP_S; + rest_of_addr= ( (addr>>ADDR_CHIP_S) / Nchips) << ADDR_CHIP_S; + + tlx->chip = addrdec_packbits(addrdec_mask[CHIP], addr_for_chip, addrdec_mkhigh[CHIP], addrdec_mklow[CHIP]); + if (addrdec_mask[BK] > addrdec_mask[CHIP]) { + tlx->bk = addrdec_packbits(addrdec_mask[BK], rest_of_addr, addrdec_mkhigh[BK], addrdec_mklow[BK]); + } else { + tlx->bk = addrdec_packbits(addrdec_mask[BK], addr, addrdec_mkhigh[BK], addrdec_mklow[BK]); + } + if (addrdec_mask[ROW] > addrdec_mask[CHIP]) { + tlx->row = addrdec_packbits(addrdec_mask[ROW], rest_of_addr, addrdec_mkhigh[ROW], addrdec_mklow[ROW]); + } else { + tlx->row = addrdec_packbits(addrdec_mask[ROW], addr, addrdec_mkhigh[ROW], addrdec_mklow[ROW]); + } + tlx->col = addrdec_packbits(addrdec_mask[COL], addr, addrdec_mkhigh[COL], addrdec_mklow[COL]); + tlx->burst= addrdec_packbits(addrdec_mask[BURST], addr, addrdec_mkhigh[BURST], addrdec_mklow[BURST]); + } +} + +unsigned int LOGB2_32( unsigned int v ) { + unsigned int shift; + unsigned int r; + + r = 0; + + shift = (( v & 0xFFFF0000) != 0 ) << 4; v >>= shift; r |= shift; + shift = (( v & 0xFF00 ) != 0 ) << 3; v >>= shift; r |= shift; + shift = (( v & 0xF0 ) != 0 ) << 2; v >>= shift; r |= shift; + shift = (( v & 0xC ) != 0 ) << 1; v >>= shift; r |= shift; + shift = (( v & 0x2 ) != 0 ) << 0; v >>= shift; r |= shift; + + return r; +} + + +static char *addrdec_option = NULL; +void addrdec_setoption(option_parser_t opp) +{ + option_parser_register(opp, "-gpgpu_mem_addr_mapping", OPT_CSTR, &addrdec_option, + "mapping memory address to dram model {dramid@<start bit>;<memory address map>}", + NULL); +} + +void addrdec_parseoption(const char *option) +{ + unsigned int dramid_start = 0; + int dramid_parsed = sscanf(option, "dramid@%d", &dramid_start); + if (dramid_parsed == 1) { + ADDR_CHIP_S = dramid_start; + } else { + ADDR_CHIP_S = -1; + } + + const char *cmapping = strchr(option, ';'); + if (cmapping == NULL) { + cmapping = option; + } else { + cmapping += 1; + } + + addrdec_mask[CHIP] = 0x0; + addrdec_mask[BK] = 0x0; + addrdec_mask[ROW] = 0x0; + addrdec_mask[COL] = 0x0; + addrdec_mask[BURST]= 0x0; + + int ofs = 63; + while ((*cmapping) != '\0') { + switch (*cmapping) { + case 'D': case 'd': + assert(dramid_parsed != 1); addrdec_mask[CHIP] |= (1ULL << ofs); ofs--; break; + case 'B': case 'b': addrdec_mask[BK] |= (1ULL << ofs); ofs--; break; + case 'R': case 'r': addrdec_mask[ROW] |= (1ULL << ofs); ofs--; break; + case 'C': case 'c': addrdec_mask[COL] |= (1ULL << ofs); ofs--; break; + case 'S': case 's': addrdec_mask[BURST] |= (1ULL << ofs); addrdec_mask[COL] |= (1ULL << ofs); ofs--; break; + // ignore bit + case '0': ofs--; break; + // ignore character + case '|': + case ' ': + case '.': break; + default: + fprintf(stderr, "ERROR: Invalid address mapping character '%c' in option '%s'\n", *cmapping, option); + } + cmapping += 1; + } + + if (ofs != -1) { + fprintf(stderr, "ERROR: Invalid address mapping length (%d) in option '%s'\n", 63 - ofs, option); + assert(ofs == -1); + } +} + + +void addrdec_setnchip(unsigned int nchips) +{ + unsigned i; + unsigned long long int mask; + unsigned int nchipbits = LOGB2_32(nchips); + Nchips = nchips; + + gap = (nchips - powli(2,nchipbits)); + if (gap) { + nchipbits++; + } + switch (gpgpu_mem_address_mask) { + case 0: + //old, added 2row bits, use #define ADDR_CHIP_S 10 + ADDR_CHIP_S = 10; + addrdec_mask[CHIP] = 0x0000000000000000; + addrdec_mask[BK] = 0x0000000000000300; + addrdec_mask[ROW] = 0x0000000007FFE000; + addrdec_mask[COL] = 0x0000000000001CFF; + break; + case 1: + ADDR_CHIP_S = 13; + addrdec_mask[CHIP] = 0x0000000000000000; + addrdec_mask[BK] = 0x0000000000001800; + addrdec_mask[ROW] = 0x0000000007FFE000; + addrdec_mask[COL] = 0x00000000000007FF; + break; + case 2: + ADDR_CHIP_S = 11; + addrdec_mask[CHIP] = 0x0000000000000000; + addrdec_mask[BK] = 0x0000000000001800; + addrdec_mask[ROW] = 0x0000000007FFE000; + addrdec_mask[COL] = 0x00000000000007FF; + break; + case 3: + ADDR_CHIP_S = 11; + addrdec_mask[CHIP] = 0x0000000000000000; + addrdec_mask[BK] = 0x0000000000001800; + addrdec_mask[ROW] = 0x000000000FFFE000; + addrdec_mask[COL] = 0x00000000000007FF; + break; + + case 14: + ADDR_CHIP_S = 14; + addrdec_mask[CHIP] = 0x0000000000000000; + addrdec_mask[BK] = 0x0000000000001800; + addrdec_mask[ROW] = 0x0000000007FFE000; + addrdec_mask[COL] = 0x00000000000007FF; + break; + case 15: + ADDR_CHIP_S = 15; + addrdec_mask[CHIP] = 0x0000000000000000; + addrdec_mask[BK] = 0x0000000000001800; + addrdec_mask[ROW] = 0x0000000007FFE000; + addrdec_mask[COL] = 0x00000000000007FF; + break; + case 16: + ADDR_CHIP_S = 16; + addrdec_mask[CHIP] = 0x0000000000000000; + addrdec_mask[BK] = 0x0000000000001800; + addrdec_mask[ROW] = 0x0000000007FFE000; + addrdec_mask[COL] = 0x00000000000007FF; + break; + case 6: + ADDR_CHIP_S = 6; + addrdec_mask[CHIP] = 0x0000000000000000; + addrdec_mask[BK] = 0x0000000000001800; + addrdec_mask[ROW] = 0x0000000007FFE000; + addrdec_mask[COL] = 0x00000000000007FF; + break; + case 5: + ADDR_CHIP_S = 5; + addrdec_mask[CHIP] = 0x0000000000000000; + addrdec_mask[BK] = 0x0000000000001800; + addrdec_mask[ROW] = 0x0000000007FFE000; + addrdec_mask[COL] = 0x00000000000007FF; + break; + case 100: + ADDR_CHIP_S = 1; + addrdec_mask[CHIP] = 0x0000000000000000; + addrdec_mask[BK] = 0x0000000000000003; + addrdec_mask[ROW] = 0x0000000007FFE000; + addrdec_mask[COL] = 0x0000000000001FFC; + break; + case 103: + ADDR_CHIP_S = 3; + addrdec_mask[CHIP] = 0x0000000000000000; + addrdec_mask[BK] = 0x0000000000000003; + addrdec_mask[ROW] = 0x0000000007FFE000; + addrdec_mask[COL] = 0x0000000000001FFC; + break; + case 106: + ADDR_CHIP_S = 6; + addrdec_mask[CHIP] = 0x0000000000000000; + addrdec_mask[BK] = 0x0000000000001800; + addrdec_mask[ROW] = 0x0000000007FFE000; + addrdec_mask[COL] = 0x00000000000007FF; + break; + case 160: + //old, added 2row bits, use #define ADDR_CHIP_S 10 + ADDR_CHIP_S = 6; + addrdec_mask[CHIP] = 0x0000000000000000; + addrdec_mask[BK] = 0x0000000000000300; + addrdec_mask[ROW] = 0x0000000007FFE000; + addrdec_mask[COL] = 0x0000000000001CFF; + + default: + break; + } + + if (addrdec_option != NULL) { + addrdec_parseoption(addrdec_option); + } + + if (ADDR_CHIP_S != -1) { + mask = ((unsigned long long int)1 << ADDR_CHIP_S) - 1; + addrdec_mask[BK] = ((addrdec_mask[BK] & ~mask) << nchipbits) | (addrdec_mask[BK] & mask); + addrdec_mask[ROW] = ((addrdec_mask[ROW] & ~mask) << nchipbits) | (addrdec_mask[ROW] & mask); + addrdec_mask[COL] = ((addrdec_mask[COL] & ~mask) << nchipbits) | (addrdec_mask[COL] & mask); + + for (i=ADDR_CHIP_S;i<(ADDR_CHIP_S+nchipbits);i++) { + mask = (unsigned long long int)1 << i; + addrdec_mask[CHIP] |= mask; + } + } else { + // make sure nchips is power of two when explicit dram id mask is used + assert((nchips & (nchips - 1)) == 0); + } + + addrdec_getmasklimit(addrdec_mask[CHIP], &addrdec_mkhigh[CHIP], &addrdec_mklow[CHIP] ); + addrdec_getmasklimit(addrdec_mask[BK], &addrdec_mkhigh[BK], &addrdec_mklow[BK] ); + addrdec_getmasklimit(addrdec_mask[ROW], &addrdec_mkhigh[ROW], &addrdec_mklow[ROW] ); + addrdec_getmasklimit(addrdec_mask[COL], &addrdec_mkhigh[COL], &addrdec_mklow[COL] ); + addrdec_getmasklimit(addrdec_mask[BURST], &addrdec_mkhigh[BURST], &addrdec_mklow[BURST]); + + printf("addr_dec_mask[CHIP] = %016llx \thigh:%d low:%d\n", addrdec_mask[CHIP], addrdec_mkhigh[CHIP], addrdec_mklow[CHIP] ); + printf("addr_dec_mask[BK] = %016llx \thigh:%d low:%d\n", addrdec_mask[BK], addrdec_mkhigh[BK], addrdec_mklow[BK] ); + printf("addr_dec_mask[ROW] = %016llx \thigh:%d low:%d\n", addrdec_mask[ROW], addrdec_mkhigh[ROW], addrdec_mklow[ROW] ); + printf("addr_dec_mask[COL] = %016llx \thigh:%d low:%d\n", addrdec_mask[COL], addrdec_mkhigh[COL], addrdec_mklow[COL] ); + printf("addr_dec_mask[BURST] = %016llx \thigh:%d low:%d\n", addrdec_mask[BURST], addrdec_mkhigh[BURST], addrdec_mklow[BURST]); +} + +#ifdef UNIT_TEST + +int main () { + unsigned int tb = 1; + unsigned pos; + addrdec_t_o tlx; + + printf("DRAM: %d %d %d %d %d %d %d\n", + D_COLL, D_BK, D_COLU, D_ROWL, D_CHIP, D_ROWU, D_UNUSED); + for (tb=1, pos=0; tb!=0; tb <<= 1, pos++) { + printf("%08lx|%02d =>", tb,pos); + tlx.plain = tb; + addrdec_fetch(tlx); + addrdec_dram(tlx); + printf("\n"); + } + + unsigned long long int packed; + packed = addrdec_packbits(0xFFFF0000FFFF0000, 0x2244113322441133); + assert (packed == 0x22442244); + printf("%016llx\n", packed); + + packed = addrdec_packbits(0x5555555555555555, 0x3333333333333333); + assert (packed == 0x55555555); + printf("%016llx\n", packed); + + packed = addrdec_packbits(0x5555555555555555, 0x6363636363636363); + assert (packed == 0x99999999); + printf("%016llx\n", packed); + + addrdec_t tls; + for (tb=1, pos=0; tb!=0; tb <<= 1, pos++) { + printf("%08lx|%02d =>", tb,pos); + addrdec_tlx(tb, &tls); + addrdec_display(&tls); + printf("\n"); + } + + addrdec_setnchip(32); + for (tb=1, pos=0; tb!=0; tb <<= 1, pos++) { + printf("%08lx|%02d =>", tb,pos); + addrdec_tlx(tb, &tls); + addrdec_display(&tls); + printf("\n"); + } + addrdec_setnchip(16); + for (tb=1, pos=0; tb!=0; tb <<= 1, pos++) { + printf("%08lx|%02d =>", tb,pos); + addrdec_tlx(tb, &tls); + addrdec_display(&tls); + printf("\n"); + } + addrdec_setnchip(8); + for (tb=1, pos=0; tb!=0; tb <<= 1, pos++) { + printf("%08lx|%02d =>", tb,pos); + addrdec_tlx(tb, &tls); + addrdec_display(&tls); + printf("\n"); + } + addrdec_setnchip(7); + for (tb=1, pos=0; tb!=0; tb <<= 1, pos++) { + printf("%08lx|%02d =>", tb,pos); + addrdec_tlx(tb, &tls); + addrdec_display(&tls); + printf("\n"); + } +/* addrdec_setnchip(6); + for(tb=1, pos=0; tb!=0; tb ++, pos++) { + printf("%08lx|%02d =>", tb,pos); + addrdec_tlx(tb, &tls); + addrdec_display(&tls); + printf("\n"); + }*/ + return 0; +} + +#endif diff --git a/src/gpgpu-sim/addrdec.h b/src/gpgpu-sim/addrdec.h new file mode 100644 index 0000000..ba28c05 --- /dev/null +++ b/src/gpgpu-sim/addrdec.h @@ -0,0 +1,96 @@ +/* + * addrdec.h + * + * Copyright (c) 2009 by Tor M. Aamodt, Wilson W. L. Fung and the University of + * British Columbia + * Vancouver, BC V6T 1Z4 + * All Rights Reserved. + * + * THIS IS A LEGAL DOCUMENT BY DOWNLOADING GPGPU-SIM, YOU ARE AGREEING TO THESE + * TERMS AND CONDITIONS. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * NOTE: The files libcuda/cuda_runtime_api.c and src/cuda-sim/cuda-math.h + * are derived from the CUDA Toolset available from http://www.nvidia.com/cuda + * (property of NVIDIA). The files benchmarks/BlackScholes/ and + * benchmarks/template/ are derived from the CUDA SDK available from + * http://www.nvidia.com/cuda (also property of NVIDIA). The files from + * src/intersim/ are derived from Booksim (a simulator provided with the + * textbook "Principles and Practices of Interconnection Networks" available + * from http://cva.stanford.edu/books/ppin/). As such, those files are bound by + * the corresponding legal terms and conditions set forth separately (original + * copyright notices are left in files from these sources and where we have + * modified a file our copyright notice appears before the original copyright + * notice). + * + * Using this version of GPGPU-Sim requires a complete installation of CUDA + * which is distributed seperately by NVIDIA under separate terms and + * conditions. To use this version of GPGPU-Sim with OpenCL requires a + * recent version of NVIDIA's drivers which support OpenCL. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the University of British Columbia nor the names of + * its contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * 4. This version of GPGPU-SIM is distributed freely for non-commercial use only. + * + * 5. No nonprofit user may place any restrictions on the use of this software, + * including as modified by the user, by any other authorized user. + * + * 6. GPGPU-SIM was developed primarily by Tor M. Aamodt, Wilson W. L. Fung, + * Ali Bakhoda, George L. Yuan, at the University of British Columbia, + * Vancouver, BC V6T 1Z4 + */ + +#include <stdio.h> +#include <stdlib.h> +#include <assert.h> +#include "../option_parser.h" + +#ifndef ADDRDEC_H +#define ADDRDEC_H + +enum { + CHIP = 0, + BK = 1, + ROW = 2, + COL = 3, + BURST = 4, + N_ADDRDEC +}; + +typedef struct { + unsigned chip; + unsigned bk; + unsigned row; + unsigned col; + unsigned burst; +} addrdec_t; + +void addrdec_tlx(unsigned long long int addr, addrdec_t *tlx); +void addrdec_setnchip(unsigned int nchips); +void addrdec_setoption(option_parser_t opp); +void addrdec_parseoption(const char *option); + +#endif diff --git a/src/gpgpu-sim/cflogger.h b/src/gpgpu-sim/cflogger.h new file mode 100644 index 0000000..bdf57e5 --- /dev/null +++ b/src/gpgpu-sim/cflogger.h @@ -0,0 +1,121 @@ +/* + * cflogger.h + * + * Copyright (c) 2009 by Tor M. Aamodt, Wilson W. L. Fung, and the + * University of British Columbia + * Vancouver, BC V6T 1Z4 + * All Rights Reserved. + * + * THIS IS A LEGAL DOCUMENT BY DOWNLOADING GPGPU-SIM, YOU ARE AGREEING TO THESE + * TERMS AND CONDITIONS. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * NOTE: The files libcuda/cuda_runtime_api.c and src/cuda-sim/cuda-math.h + * are derived from the CUDA Toolset available from http://www.nvidia.com/cuda + * (property of NVIDIA). The files benchmarks/BlackScholes/ and + * benchmarks/template/ are derived from the CUDA SDK available from + * http://www.nvidia.com/cuda (also property of NVIDIA). The files from + * src/intersim/ are derived from Booksim (a simulator provided with the + * textbook "Principles and Practices of Interconnection Networks" available + * from http://cva.stanford.edu/books/ppin/). As such, those files are bound by + * the corresponding legal terms and conditions set forth separately (original + * copyright notices are left in files from these sources and where we have + * modified a file our copyright notice appears before the original copyright + * notice). + * + * Using this version of GPGPU-Sim requires a complete installation of CUDA + * which is distributed seperately by NVIDIA under separate terms and + * conditions. To use this version of GPGPU-Sim with OpenCL requires a + * recent version of NVIDIA's drivers which support OpenCL. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the University of British Columbia nor the names of + * its contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * 4. This version of GPGPU-SIM is distributed freely for non-commercial use only. + * + * 5. No nonprofit user may place any restrictions on the use of this software, + * including as modified by the user, by any other authorized user. + * + * 6. GPGPU-SIM was developed primarily by Tor M. Aamodt, Wilson W. L. Fung, + * Ali Bakhoda, George L. Yuan, at the University of British Columbia, + * Vancouver, BC V6T 1Z4 + */ + +#ifndef CFLOGGER_H +#define CFLOGGER_H + +void try_snap_shot (unsigned long long current_cycle); +void set_spill_interval (unsigned long long interval); +void spill_log_to_file (FILE *fout, int final, unsigned long long current_cycle); + +void create_thread_CFlogger( int n_loggers, int n_threads, int n_insn, address_type start_pc, unsigned long long logging_interval); +void destroy_thread_CFlogger( ); +void cflog_update_thread_pc( int logger_id, int thread_id, address_type pc ); +void cflog_snapshot( int logger_id, unsigned long long cycle ); +void cflog_print(FILE *fout); +void cflog_print_path_expression(FILE *fout); +void cflog_visualizer_print(FILE *fout); + + +void insn_warp_occ_create( int n_loggers, int simd_width, int n_insn ); +void insn_warp_occ_log( int logger_id, address_type pc, int warp_occ ); +void insn_warp_occ_print( FILE *fout ); + + +void shader_warp_occ_create( int n_loggers, int simd_width, unsigned long long logging_interval ); +void shader_warp_occ_log( int logger_id, int warp_occ ); +void shader_warp_occ_snapshot( int logger_id, unsigned long long current_cycle ); +void shader_warp_occ_print( FILE *fout ); + + +void shader_mem_acc_create( int n_loggers, int n_dram, int n_bank, unsigned long long logging_interval ); +void shader_mem_acc_log( int logger_id, int dram_id, int bank, char rw ); +void shader_mem_acc_snapshot( int logger_id, unsigned long long current_cycle ); +void shader_mem_acc_print( FILE *fout ); + + +void shader_mem_lat_create( int n_loggers, unsigned long long logging_interval ); +void shader_mem_lat_log( int logger_id, int latency ); +void shader_mem_lat_snapshot( int logger_id, unsigned long long current_cycle ); +void shader_mem_lat_print( FILE *fout ); + + +int get_shader_normal_cache_id(); +int get_shader_texture_cache_id(); +int get_shader_constant_cache_id(); +void shader_cache_access_create( int n_loggers, int n_types, unsigned long long logging_interval ); +void shader_cache_access_log( int logger_id, int type, int miss); +void shader_cache_access_unlog( int logger_id, int type, int miss); +void shader_cache_access_print( FILE *fout ); + + +void shader_CTA_count_create( int n_shaders, unsigned long long logging_interval); +void shader_CTA_count_log( int shader_id, int nCTAadded ); +void shader_CTA_count_unlog( int shader_id, int nCTAdone ); +void shader_CTA_count_resetnow( ); +void shader_CTA_count_print( FILE *fout ); +void shader_CTA_count_visualizer_print( FILE *fout ); + +#endif /* CFLOGGER_H */ diff --git a/src/gpgpu-sim/delayqueue.cc b/src/gpgpu-sim/delayqueue.cc new file mode 100644 index 0000000..dc870d0 --- /dev/null +++ b/src/gpgpu-sim/delayqueue.cc @@ -0,0 +1,468 @@ +/* + * delayqueue.c + * + * Copyright (c) 2009 by Tor M. Aamodt, Wilson W. L. Fung, Ali Bakhoda, + * Ivan Sham, Henry Tran and the University of British Columbia + * Vancouver, BC V6T 1Z4 + * All Rights Reserved. + * + * THIS IS A LEGAL DOCUMENT BY DOWNLOADING GPGPU-SIM, YOU ARE AGREEING TO THESE + * TERMS AND CONDITIONS. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * NOTE: The files libcuda/cuda_runtime_api.c and src/cuda-sim/cuda-math.h + * are derived from the CUDA Toolset available from http://www.nvidia.com/cuda + * (property of NVIDIA). The files benchmarks/BlackScholes/ and + * benchmarks/template/ are derived from the CUDA SDK available from + * http://www.nvidia.com/cuda (also property of NVIDIA). The files from + * src/intersim/ are derived from Booksim (a simulator provided with the + * textbook "Principles and Practices of Interconnection Networks" available + * from http://cva.stanford.edu/books/ppin/). As such, those files are bound by + * the corresponding legal terms and conditions set forth separately (original + * copyright notices are left in files from these sources and where we have + * modified a file our copyright notice appears before the original copyright + * notice). + * + * Using this version of GPGPU-Sim requires a complete installation of CUDA + * which is distributed seperately by NVIDIA under separate terms and + * conditions. To use this version of GPGPU-Sim with OpenCL requires a + * recent version of NVIDIA's drivers which support OpenCL. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the University of British Columbia nor the names of + * its contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * 4. This version of GPGPU-SIM is distributed freely for non-commercial use only. + * + * 5. No nonprofit user may place any restrictions on the use of this software, + * including as modified by the user, by any other authorized user. + * + * 6. GPGPU-SIM was developed primarily by Tor M. Aamodt, Wilson W. L. Fung, + * Ali Bakhoda, George L. Yuan, at the University of British Columbia, + * Vancouver, BC V6T 1Z4 + */ + +#include "delayqueue.h" +#include "gpu-misc.h" +#include "../intersim/statwraper.h" + +extern unsigned long long gpu_sim_cycle; //for stat collection + +unsigned char dq_full( delay_queue* dq ) +{ + if (dq->max_len && dq->length >= dq->max_len) + return 1; + return 0; +} + +unsigned char dq_empty(delay_queue* dq ) +{ + return(dq->head == NULL)?1:0; +} + +unsigned int dq_n_element(delay_queue* dq ) +{ + return(dq->n_element); +} + +unsigned char dq_push(delay_queue* dq, void* data) { + if (dq->max_len) assert(dq->length < dq->max_len); + if (dq->head) { + if (dq->tail->data || dq->length < dq->min_len) { + dq->tail->next = (delay_data*) malloc(sizeof(delay_data)); + dq->tail = dq->tail->next; + dq->length++; + dq->n_element++; + } + } else { + dq->head = dq->tail = (delay_data*) malloc(sizeof(delay_data)); + dq->length++; + dq->n_element++; + } + dq->tail->next = NULL; + dq->tail->time_elapsed = dq->latency; + dq->tail->data = (void*)data; + dq->tail->push_time = gpu_sim_cycle; + return 1; +} + +void* dq_top(delay_queue* dq) { + if (dq->head) { + return dq->head->data; + } else { + return NULL; + } +} + +void* dq_pop(delay_queue* dq) { + delay_data* next; + void* data; + if (dq->head) { + if (dq->head->time_elapsed) { + dq->head->time_elapsed--; + data = NULL; + } else { + next = dq->head->next; + data = dq->head->data; + StatAddSample(dq->lat_stat, LOGB2 (gpu_sim_cycle - dq->head->push_time)); + if ( dq->head == dq->tail ) { + assert( next == NULL ); + dq->tail = NULL; + } + free(dq->head); + dq->head = next; + dq->length--; + if (dq->length == 0) { + assert( dq->head == NULL ); + dq->tail = dq->head; + } + dq->n_element--; + } + if (dq->min_len && dq->length < dq->min_len) { + dq_push(dq,NULL); + dq->n_element--; // uncount NULL elements inserted to create delays + } + } else { + data = NULL; + } + return data; +} + +void dq_set_min_length(delay_queue* dq, unsigned int new_min_len) { + if (new_min_len == dq->min_len) return; + + if (new_min_len > dq->min_len) { + dq->min_len = new_min_len; + while (dq->length < dq->min_len) { + dq_push(dq,NULL); + dq->n_element--; // uncount NULL elements inserted to create delays + } + } else { + // in this branch imply that the original min_len is larger then 0 + // ie. dq->head != 0 + assert(dq->head); + dq->min_len = new_min_len; + while ((dq->length > dq->min_len) && (dq->tail->data == 0)) { + delay_data *iter; + iter = dq->head; + while (iter && (iter->next != dq->tail)) + iter = iter->next; + if (!iter) { + // there is only one node, and that node is empty + assert(dq->head->data == 0); + dq_pop(dq); + } else { + // there are more than one node, and tail node is empty + assert(iter->next == dq->tail); + free(dq->tail); + dq->tail = iter; + dq->tail->next = 0; + dq->length--; + } + } + } +} + +void dq_remove(void* data, delay_queue* dq) +{ + // removes an item from the queue without deallocating the memory + delay_data* ptr = NULL; + delay_data* temp = NULL; + + assert(dq); + assert(data); + + ptr = dq->head; + if (ptr) { + if (ptr->data == data) { + StatAddSample(dq->lat_stat, LOGB2 (gpu_sim_cycle - ptr->push_time)); + dq->head = ptr->next; + if ( dq->head == NULL ) + dq->tail = NULL; + dq->length--; + return; + } + while (ptr->next) { + if (ptr->next->data == data) { + temp = ptr->next; + StatAddSample(dq->lat_stat, LOGB2 (gpu_sim_cycle - temp->push_time)); + if ( ptr->next == dq->tail ) { + dq->tail = ptr; + } + ptr->next = ptr->next->next; + dq->length--; + return; + } + ptr = ptr->next; + } + } +} + +void removeEntry(void* data, delay_queue** dqq, int size_dq) +{ + int i; + delay_data* ptr = NULL; + delay_queue* dq = NULL; + delay_data* temp = NULL; + + assert(dqq); + assert(data); + + + for (i = 0; i<size_dq; i++) { + dq = dqq[i]; + ptr = dq->head; + if (ptr) { + if (ptr->data == data) { + dq->head = ptr->next; + if ( dq->head == NULL ) + dq->tail = NULL; + StatAddSample(dq->lat_stat, LOGB2 (gpu_sim_cycle - ptr->push_time)); + free(ptr); + dq->length--; + return; + } + while (ptr->next) { + if (ptr->next->data == data) { + temp = ptr->next; + if ( ptr->next == dq->tail ) { + dq->tail = ptr; + } + ptr->next = ptr->next->next; + StatAddSample(dq->lat_stat, LOGB2 (gpu_sim_cycle - temp->push_time)); + free(temp); + dq->length--; + return; + } + ptr = ptr->next; + } + } + + } +} + +static int dq_uid_counter = 0; + +delay_queue* dq_create(const char* name, unsigned int latency, unsigned int min_len, unsigned int max_len) { + unsigned i; + delay_queue* dq; + dq = (delay_queue*) malloc(sizeof(delay_queue)); + dq->name = name; + dq->latency = latency; + dq->min_len = min_len; + dq->max_len = max_len; + dq->length = 0; + dq->n_element = 0; + dq->head = NULL; + dq->tail = NULL; + for (i=0;i<min_len;i++) dq_push(dq,NULL); + dq->uid = dq_uid_counter; + dq_uid_counter++; + if (1) { + dq->lat_stat = StatCreate(dq->name,1,32); + } + dq->max_size_stat = 0; + dq->avg_size_stat =0.0 ; + return dq; +} + +void dq_print(delay_queue* dq) { + delay_data* ddp = dq->head; + printf("%s(%d): ", dq->name, dq->length); + while (ddp) { + printf("%p ", ddp->data); + ddp = ddp->next; + } + printf("\n"); +} + +void dq_free(delay_queue* dq) { + while (dq->head) { + dq->tail = dq->head; + dq->head = dq->head->next; + free(dq->tail); + } + free(dq); + dq = NULL; +} + +void dq_update_stat(delay_queue* dq) { + if (dq->n_element > dq->max_size_stat) { + dq->max_size_stat = dq->n_element; + } + dq->avg_size_stat = (dq->avg_size_stat*dq->n_stat_samples + dq->n_element)/(++dq->n_stat_samples); +} +void dq_print_stat(delay_queue* dq) { + printf("Max Length: %d, Average Length: %f\n",dq->max_size_stat,dq->avg_size_stat ); +} + + +#ifdef TEST_DQ + +void regresstion_test01() { + delay_queue *dqa, *dqb; + int i; + int a[7]; + for (i=0;i<7;i++) a[i]=i; + + dqa = dq_create("dqa", 0, 7, 0); + for (i=0;i<3;i++) dq_push(dqa, &a[i]); + + for (i=0;i<6;i++) { + dq_print(dqa); + assert(dq_pop(dqa) == 0); + } + dq_print(dqa); + assert(dq_pop(dqa) == &a[0]); + + // shortening queue + dq_print(dqa); + dq_set_min_length(dqa, 4); + // see if data in the queue still persist + dq_print(dqa); + assert(dq_pop(dqa) == &a[1]); + // see if the queue behave with min length = 4 + dq_push(dqa, &a[3]); + dq_print(dqa); + assert(dq_pop(dqa) == &a[2]); + for (i=0;i<2;i++) { + dq_print(dqa); + assert(dq_pop(dqa) == 0); + } + dq_print(dqa); + assert(dq_pop(dqa) == &a[3]); + + // lengthening queue + dq_set_min_length(dqa, 6); + dq_push(dqa, &a[4]); + dq_push(dqa, &a[5]); + for (i=0;i<5;i++) { + dq_print(dqa); + assert(dq_pop(dqa) == 0); + } + dq_print(dqa); + assert(dq_pop(dqa) == &a[4]); + + // queue with no min length + dq_set_min_length(dqa, 0); + dq_print(dqa); + assert(dq_pop(dqa) == &a[5]); + dq_print(dqa); + dq_push(dqa, &a[6]); + dq_print(dqa); + assert(dq_pop(dqa) == &a[6]); + + // lengthening the queue, then shorten it again, + // but with some data exceeding the new min length + // the data should retain. + dq_print(dqa); + dq_set_min_length(dqa, 7); + dq_print(dqa); + dq_push(dqa, &a[0]); + assert(dq_pop(dqa) == 0); + dq_print(dqa); + dq_set_min_length(dqa, 4); + dq_print(dqa); + assert(dq_pop(dqa) == 0); + assert(dq_pop(dqa) == 0); + assert(dq_pop(dqa) == 0); + assert(dq_pop(dqa) == 0); + assert(dq_pop(dqa) == 0); + // This is the 7th pop: min-length is obeyed + assert(dq_pop(dqa) == &a[0]); + dq_print(dqa); + + // Shortening a queue with null entry only + dq_set_min_length(dqa, 0); + assert(dqa->length == 0); + dq_print(dqa); + + // Lengthening + dq_set_min_length(dqa, 6); + assert(dqa->length == 6); + dq_print(dqa); + + // Shortening a queue with null entry only + dq_set_min_length(dqa, 3); + assert(dqa->length == 3); + dq_print(dqa); + + dq_free(dqa); + printf("regression test 01 passed!\n"); +} + +int regresstion_test00() { + delay_queue *dqa, *dqb, *dqc, *dqd; + int i; + int a[4]; + int *b; + for (i=0;i<4;i++) a[i]=i; + dqa = dq_create("dqa", 0, 4, 0); + dqb = dq_create("dqb", 0, 10, 0); + dq_print(dqa); + dq_print(dqb); + dq_push(dqa,a); + dq_print(dqa); + dq_pop(dqa); + dq_print(dqa); + dq_push(dqa,a); + dq_print(dqa); + dq_pop(dqa); + dq_print(dqa); + dq_pop(dqa); + dq_print(dqa); + b = dq_pop(dqa); + dq_print(dqa); + for (i=0;i<4;i++) printf("%d\n",b[i]); + dqc = dq_create("dqc", 0, 0, 3); + for (i=0;i<4;i++) { + if (!dq_push(dqc,&a[i])) printf("cannot push.\n"); + dq_print(dqc); + } + dqd = dq_create("dqd", 0, 2, 3); + if (!dq_push(dqd,&a[0])) printf("cannot push.\n"); + dq_print(dqd); + if (!dq_push(dqd,&a[1])) printf("cannot push.\n"); + dq_print(dqd); + if (!dq_push(dqd,&a[2])) printf("cannot push.\n"); + dq_print(dqd); + dq_pop(dqd); + if (!dq_push(dqd,&a[3])) printf("cannot push.\n"); + dq_print(dqd); + + dq_free(dqa); + dq_free(dqb); + dq_free(dqc); + dq_free(dqd); + + return 0; +} + +int main() { + regresstion_test01(); + return 0; +} + + +#endif diff --git a/src/gpgpu-sim/delayqueue.h b/src/gpgpu-sim/delayqueue.h new file mode 100644 index 0000000..baa3892 --- /dev/null +++ b/src/gpgpu-sim/delayqueue.h @@ -0,0 +1,122 @@ +/* + * delayqueue.h + * + * Copyright (c) 2009 by Tor M. Aamodt, Wilson W. L. Fung, Ali Bakhoda, + * Ivan Sham, Henry Tran and the University of British Columbia + * Vancouver, BC V6T 1Z4 + * All Rights Reserved. + * + * THIS IS A LEGAL DOCUMENT BY DOWNLOADING GPGPU-SIM, YOU ARE AGREEING TO THESE + * TERMS AND CONDITIONS. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * NOTE: The files libcuda/cuda_runtime_api.c and src/cuda-sim/cuda-math.h + * are derived from the CUDA Toolset available from http://www.nvidia.com/cuda + * (property of NVIDIA). The files benchmarks/BlackScholes/ and + * benchmarks/template/ are derived from the CUDA SDK available from + * http://www.nvidia.com/cuda (also property of NVIDIA). The files from + * src/intersim/ are derived from Booksim (a simulator provided with the + * textbook "Principles and Practices of Interconnection Networks" available + * from http://cva.stanford.edu/books/ppin/). As such, those files are bound by + * the corresponding legal terms and conditions set forth separately (original + * copyright notices are left in files from these sources and where we have + * modified a file our copyright notice appears before the original copyright + * notice). + * + * Using this version of GPGPU-Sim requires a complete installation of CUDA + * which is distributed seperately by NVIDIA under separate terms and + * conditions. To use this version of GPGPU-Sim with OpenCL requires a + * recent version of NVIDIA's drivers which support OpenCL. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the University of British Columbia nor the names of + * its contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * 4. This version of GPGPU-SIM is distributed freely for non-commercial use only. + * + * 5. No nonprofit user may place any restrictions on the use of this software, + * including as modified by the user, by any other authorized user. + * + * 6. GPGPU-SIM was developed primarily by Tor M. Aamodt, Wilson W. L. Fung, + * Ali Bakhoda, George L. Yuan, at the University of British Columbia, + * Vancouver, BC V6T 1Z4 + */ + +#include <stdio.h> +#include <assert.h> +#include <stdlib.h> + +#ifndef DELAYQUEUE_H +#define DELAYQUEUE_H + +#include "../util.h" + +typedef struct delay_data_t delay_data; +struct delay_data_t { + void *data; + unsigned int time_elapsed; + delay_data *next; + unsigned long long push_time; //for stat collection +}; + +typedef struct { + const char* name; + int uid; + + unsigned int latency; + unsigned int min_len; + unsigned int max_len; + unsigned int length; + unsigned int n_element; + + delay_data *head; + delay_data *tail; + + void* lat_stat; //a pointer to latency stats distribution structure + //occupancy stat + unsigned int max_size_stat; + unsigned int n_stat_samples; + float avg_size_stat; +} delay_queue; + +unsigned char dq_full(delay_queue* dq ); +unsigned char dq_empty(delay_queue* dq ); +unsigned int dq_n_element(delay_queue* dq ); +unsigned char dq_push(delay_queue* dq, void* data); +void* dq_pop(delay_queue* dq); +void dq_set_min_length(delay_queue* dq, unsigned int new_min_len); +void removeEntry(void* data, delay_queue** dq, int size_dq); +delay_queue* dq_create( const char* name, + unsigned int latency, + unsigned int min_len, + unsigned int max_len); +void dq_remove(void* data, delay_queue* dq); +void dq_print(delay_queue* dq); +void dq_free(delay_queue* dq); +void* dq_top(delay_queue* dq);//return the data in the head without poping the queue + +void dq_update_stat(delay_queue* dq); +void dq_print_stat(delay_queue* dq); + +#endif diff --git a/src/gpgpu-sim/dram.cc b/src/gpgpu-sim/dram.cc new file mode 100644 index 0000000..9f76b32 --- /dev/null +++ b/src/gpgpu-sim/dram.cc @@ -0,0 +1,604 @@ +/* + * dram.c + * + * Copyright (c) 2009 by Tor M. Aamodt, Wilson W. L. Fung, George L. Yuan, + * Ivan Sham, Justin Kwong, Dan O'Connor and the + * University of British Columbia + * Vancouver, BC V6T 1Z4 + * All Rights Reserved. + * + * THIS IS A LEGAL DOCUMENT BY DOWNLOADING GPGPU-SIM, YOU ARE AGREEING TO THESE + * TERMS AND CONDITIONS. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * NOTE: The files libcuda/cuda_runtime_api.c and src/cuda-sim/cuda-math.h + * are derived from the CUDA Toolset available from http://www.nvidia.com/cuda + * (property of NVIDIA). The files benchmarks/BlackScholes/ and + * benchmarks/template/ are derived from the CUDA SDK available from + * http://www.nvidia.com/cuda (also property of NVIDIA). The files from + * src/intersim/ are derived from Booksim (a simulator provided with the + * textbook "Principles and Practices of Interconnection Networks" available + * from http://cva.stanford.edu/books/ppin/). As such, those files are bound by + * the corresponding legal terms and conditions set forth separately (original + * copyright notices are left in files from these sources and where we have + * modified a file our copyright notice appears before the original copyright + * notice). + * + * Using this version of GPGPU-Sim requires a complete installation of CUDA + * which is distributed seperately by NVIDIA under separate terms and + * conditions. To use this version of GPGPU-Sim with OpenCL requires a + * recent version of NVIDIA's drivers which support OpenCL. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the University of British Columbia nor the names of + * its contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * 4. This version of GPGPU-SIM is distributed freely for non-commercial use only. + * + * 5. No nonprofit user may place any restrictions on the use of this software, + * including as modified by the user, by any other authorized user. + * + * 6. GPGPU-SIM was developed primarily by Tor M. Aamodt, Wilson W. L. Fung, + * Ali Bakhoda, George L. Yuan, at the University of British Columbia, + * Vancouver, BC V6T 1Z4 + */ + +//#include "gpu-sim.h" +#include "gpu-misc.h" +//#include "shader.h" +#include "dram.h" + +extern unsigned long long gpu_sim_cycle; +extern signed long long gpu_tot_sim_cycle; +extern int gpgpu_memlatency_stat; +extern unsigned max_dq_latency; +extern unsigned dq_lat_table[24]; +extern unsigned max_dq_latency; +extern unsigned dq_lat_table[24]; +unsigned int gpu_n_warps; +extern unsigned int gpu_n_mem_per_ctrlr; +extern unsigned int recent_dram_util; + +#ifdef DRAM_VERIFY +int PRINT_CYCLE = 0; +#endif + +dram_t* dram_create( unsigned int id, unsigned int nbk, + unsigned int tCCD, unsigned int tRRD, + unsigned int tRCD, unsigned int tRAS, + unsigned int tRP, unsigned int tRC, + unsigned int CL, unsigned int WL, + unsigned int BL, unsigned int tWTR, + unsigned int busW, unsigned int queue_limit, + unsigned char scheduler_type ) +{ + dram_t *dm; + unsigned i; + + dm = (dram_t*)calloc(1,sizeof(dram_t)); + + dm->id = id; + + dm->nbk = nbk; + dm->tCCD = tCCD; + dm->tRRD = tRRD; + dm->tRCD = tRCD; + dm->tRCDWR = tRCD - (WL + 1); //formula given in datasheet + dm->tRAS = tRAS; + dm->tRP = tRP; + dm->tRC = tRC; + dm->CL = CL; + dm->WL = WL; + dm->BL = BL; + + dm->tRTW = (CL+(BL/2)+2-WL); //read to write time according to datasheet + dm->tWTR = tWTR; + + dm->busW = busW; + + dm->CCDc = 0; + dm->RRDc = 0; + dm->RTWc = 0; + dm->WTRc = 0; + + dm->rw = READ; //read mode is default + + dm->bk = (bank_t**) calloc(sizeof(bank_t*),dm->nbk); + dm->bk[0] = (bank_t*) calloc(sizeof(bank_t),dm->nbk); + for (i=1;i<dm->nbk;i++) { + dm->bk[i] = dm->bk[0] + i; + } + for (i=0;i<dm->nbk;i++) { + dm->bk[i]->state = BANK_IDLE; + } + dm->prio = 0; + dm->rwq = dq_create("rwq",0,dm->CL,dm->CL+1); + dm->mrqq = dq_create("mrqq",0,0,0); + dm->queue_limit = queue_limit; + + dm->returnq = dq_create("dramreturnq",0,0, queue_limit); + + dm->m_fast_ideal_scheduler = NULL; + if ( scheduler_type == DRAM_IDEAL_FAST ) + dm->m_fast_ideal_scheduler = alloc_fast_ideal_scheduler(dm); + + + dm->n_cmd = 0; + dm->n_activity = 0; + dm->n_nop = 0; + dm->n_act = 0; + dm->n_pre = 0; + dm->n_rd = 0; + dm->n_wr = 0; + dm->n_req = 0; + dm->max_mrqs_temp = 0; + + dm->bwutil = 0; + + dm->max_mrqs = 0; + + dm->scheduler_type = scheduler_type; + + dm->realistic_scheduler_mode = READ; //realistic scheduler defaults to read + for (i=0;i<10;i++) { + dm->dram_util_bins[i]=0; + dm->dram_eff_bins[i]=0; + } + dm->last_n_cmd = dm->last_n_activity = dm->last_bwutil = 0; + + dm->n_cmd_partial = 0; + dm->n_activity_partial = 0; + dm->n_nop_partial = 0; + dm->n_act_partial = 0; + dm->n_pre_partial = 0; + dm->n_req_partial = 0; + dm->ave_mrqs_partial = 0; + dm->bwutil_partial = 0; + return dm; +} + +void dram_free( dram_t *dm ) +{ + dq_free(dm->mrqq); + dq_free(dm->rwq); + dq_free( dm->returnq ); + + free(dm->bk[0]); + free(dm->bk); + free(dm); +} + +int dram_full( dram_t *dm ) +{ + int full = 0; + if ( dm->queue_limit == 0 ) return 0; + if ( dm->scheduler_type == DRAM_IDEAL_FAST ) { + unsigned nreqs = fast_scheduler_queue_length(dm) + dq_n_element(dm->mrqq); + full = (nreqs >= dm->queue_limit); + } else { + full = (dm->mrqq->length >= dm->queue_limit); + } + + return full; +} + +unsigned int dram_que_length( dram_t *dm ) +{ + unsigned nreqs = 0; + if (dm->scheduler_type == DRAM_IDEAL_FAST ) { + nreqs = fast_scheduler_queue_length(dm); + } else { + nreqs = dm->mrqq->length ; + } + return nreqs; +} + +void dram_push( dram_t *dm, unsigned int bank, + unsigned int row, unsigned int col, + unsigned int nbytes, unsigned int write, + unsigned int wid, + unsigned int sid, int cache_hits_waiting, unsigned long long addr, + void *data ) +{ + dram_req_t *mrq; + + if (bank>=dm->nbk) printf("ERROR: no such bank exist in DRAM %d\n", bank); + + mrq = (dram_req_t *) malloc(sizeof(dram_req_t)); + + mrq->bk = bank; + mrq->row = row; + mrq->col = col; + mrq->nbytes = nbytes; + mrq->txbytes = 0; + mrq->dqbytes = 0; + mrq->data = data; + mrq->timestamp = gpu_tot_sim_cycle + gpu_sim_cycle; + mrq->cache_hits_waiting = cache_hits_waiting; + mrq->addr = addr; + mrq->insertion_time = (unsigned) gpu_sim_cycle; + + if (!write) { + mrq->rw = READ; //request is a read + } else { + mrq->rw = WRITE; //request is a write + } + + dq_push(dm->mrqq,mrq); + dm->n_req += 1; + dm->n_req_partial += 1; + + if ( dm->scheduler_type == DRAM_IDEAL_FAST ) { + unsigned nreqs = fast_scheduler_queue_length(dm); + if ( nreqs > dm->max_mrqs_temp) + dm->max_mrqs_temp = nreqs; + } else { + dm->max_mrqs_temp = (dm->max_mrqs_temp > dm->mrqq->length)? dm->max_mrqs_temp : dm->mrqq->length; + } +} + +void scheduler_fifo(dram_t* dm) +{ + if (dm->mrqq->head) { + dram_req_t *head_mrqq; + unsigned int bkn; + head_mrqq = (dram_req_t *)dm->mrqq->head->data; + bkn = head_mrqq->bk; + if (!dm->bk[bkn]->mrq) { + dm->bk[bkn]->mrq = (dram_req_t*) dq_pop(dm->mrqq); + } + } +} + + +#define DEC2ZERO(x) x = (x)? (x-1) : 0; +#define SWAP(a,b) a ^= b; b ^= a; a ^= b; + +void dram_issueCMD (dram_t* dm) +{ + unsigned i,j,k; + unsigned char issued; + issued = 0; + + /* check if the upcoming request is on an idle bank */ + /* Should we modify this so that multiple requests are checked? */ + + switch (dm->scheduler_type) { + case DRAM_FIFO: + scheduler_fifo(dm); + break; + case DRAM_IDEAL_FAST: + fast_scheduler_ideal(dm); + break; + default: + printf("Error: Unknown DRAM scheduler type\n"); + assert(0); + } + if ( dm->scheduler_type == DRAM_IDEAL_FAST ) { + unsigned nreqs = fast_scheduler_queue_length(dm); + if ( nreqs > dm->max_mrqs) { + dm->max_mrqs = nreqs; + } + dm->ave_mrqs += nreqs; + dm->ave_mrqs_partial += nreqs; + } else { + if (dm->mrqq->length > dm->max_mrqs) { + dm->max_mrqs = dm->mrqq->length; + } + dm->ave_mrqs += dm->mrqq->length; + dm->ave_mrqs_partial += dm->mrqq->length; + } + k=dm->nbk; + // check if any bank is ready to issue a new read + for (i=0;i<dm->nbk;i++) { + j = (i + dm->prio) % dm->nbk; + if (dm->bk[j]->mrq) { //if currently servicing a memory request + // correct row activated for a READ + if ( !issued && !dm->CCDc && !dm->bk[j]->RCDc && + (dm->bk[j]->curr_row == dm->bk[j]->mrq->row) && + (dm->bk[j]->mrq->rw == READ) && (dm->WTRc == 0 ) && + (dm->bk[j]->state == BANK_ACTIVE) && + !dq_full(dm->rwq) ) { + if (dm->rw==WRITE) { + dm->rw=READ; + dq_set_min_length(dm->rwq, dm->CL); + } + dq_push(dm->rwq,(void*)dm->bk[j]->mrq); //only push when rwq empty? + dm->bk[j]->mrq->txbytes += dm->BL * dm->busW * gpu_n_mem_per_ctrlr; //16 bytes + dm->CCDc = dm->tCCD; + dm->RTWc = dm->tRTW; + issued = 1; + dm->n_rd++; + //printf("\tn_rd++ Bank: %d Row: %d Col: %d\n", j, dm->bk[j]->mrq->row, dm->bk[j]->mrq->col); + dm->bwutil+= dm->BL/2; + dm->bwutil_partial += dm->BL/2; + dm->bk[j]->n_access++; +#ifdef DRAM_VERIFY + PRINT_CYCLE=1; + printf("\tRD Bk:%d Row:%03x Col:%03x \n", + j, dm->bk[j]->curr_row, + dm->bk[j]->mrq->col+dm->bk[j]->mrq->txbytes-dm->BL*dm->busW); +#endif + // transfer done + if ( !(dm->bk[j]->mrq->txbytes < dm->bk[j]->mrq->nbytes) ) { + dm->bk[j]->mrq = NULL; + } + } else + // correct row activated for a WRITE + if ( !issued && !dm->CCDc && !dm->bk[j]->RCDWRc && + (dm->bk[j]->curr_row == dm->bk[j]->mrq->row) && + (dm->bk[j]->mrq->rw == WRITE) && (dm->RTWc == 0 ) && + (dm->bk[j]->state == BANK_ACTIVE) && + !dq_full(dm->rwq) ) { + if (dm->rw==READ) { + dm->rw=WRITE; + dq_set_min_length(dm->rwq, dm->WL); + } + dq_push(dm->rwq,(void*)dm->bk[j]->mrq); + + dm->bk[j]->mrq->txbytes += dm->BL * dm->busW * gpu_n_mem_per_ctrlr; /*16 bytes*/ + dm->CCDc = dm->tCCD; + issued = 1; + dm->n_wr++; + dm->bwutil+=2; + dm->bwutil_partial += dm->BL/2; +#ifdef DRAM_VERIFY + PRINT_CYCLE=1; + printf("\tWR Bk:%d Row:%03x Col:%03x \n", + j, dm->bk[j]->curr_row, + dm->bk[j]->mrq->col+dm->bk[j]->mrq->txbytes-dm->BL*dm->busW); +#endif + // transfer done + if ( !(dm->bk[j]->mrq->txbytes < dm->bk[j]->mrq->nbytes) ) { + dm->bk[j]->mrq = NULL; + } + } + + else + // bank is idle + if ( !issued && !dm->RRDc && + (dm->bk[j]->state == BANK_IDLE) && + !dm->bk[j]->RPc && !dm->bk[j]->RCc ) { +#ifdef DRAM_VERIFY + PRINT_CYCLE=1; + printf("\tACT BK:%d NewRow:%03x From:%03x \n", + j,dm->bk[j]->mrq->row,dm->bk[j]->curr_row); +#endif + // activate the row with current memory request + dm->bk[j]->curr_row = dm->bk[j]->mrq->row; + dm->bk[j]->state = BANK_ACTIVE; + dm->RRDc = dm->tRRD; + dm->bk[j]->RCDc = dm->tRCD; + dm->bk[j]->RCDWRc = dm->tRCDWR; + dm->bk[j]->RASc = dm->tRAS; + dm->bk[j]->RCc = dm->tRC; + dm->prio = (j + 1) % dm->nbk; + issued = 1; + dm->n_act_partial++; + dm->n_act++; + } + + else + // different row activated + if ( (!issued) && + (dm->bk[j]->curr_row != dm->bk[j]->mrq->row) && + (dm->bk[j]->state == BANK_ACTIVE) && + (!dm->bk[j]->RASc) ) { + //printf("\tRASc: %d \n", dm->bk[j]->RASc); + // make the bank idle again + dm->bk[j]->state = BANK_IDLE; + dm->bk[j]->RPc = dm->tRP; + dm->prio = (j + 1) % dm->nbk; + issued = 1; + dm->n_pre++; + dm->n_pre_partial++; +#ifdef DRAM_VERIFY + PRINT_CYCLE=1; + printf("\tPRE BK:%d Row:%03x \n", j,dm->bk[j]->curr_row); + //printf("\tRASc: %d \n", dm->bk[j]->RASc); +#endif + } + } else { + if (!dm->CCDc && !dm->RRDc && !dm->RTWc && !dm->WTRc && !dm->bk[j]->RCDc && !dm->bk[j]->RASc + && !dm->bk[j]->RCc && !dm->bk[j]->RPc && !dm->bk[j]->RCDWRc) k--; + dm->bk[i]->n_idle++; + } + } + if (!issued) { + dm->n_nop++; + dm->n_nop_partial++; +#ifdef DRAM_VIEWCMD + printf("\tNOP "); +#endif + } + if (k) { + dm->n_activity++; + dm->n_activity_partial++; + } + dm->n_cmd++; + dm->n_cmd_partial++; + + // decrements counters once for each time dram_issueCMD is called + DEC2ZERO(dm->RRDc); + DEC2ZERO(dm->CCDc); + DEC2ZERO(dm->RTWc); + DEC2ZERO(dm->WTRc); + for (j=0;j<dm->nbk;j++) { + DEC2ZERO(dm->bk[j]->RCDc); + DEC2ZERO(dm->bk[j]->RASc); + DEC2ZERO(dm->bk[j]->RCc); + DEC2ZERO(dm->bk[j]->RPc); + DEC2ZERO(dm->bk[j]->RCDWRc); + } + +#ifdef DRAM_VISUALIZE + dram_visualize(dm); +#endif +} + +//if mrq is being serviced by dram, gets popped after CL latency fulfilled +void* dram_pop( dram_t *dm ) +{ + dram_req_t *mrq; + void *data; + unsigned dq_latency; + + data = NULL; + mrq = (dram_req_t*)dq_pop(dm->rwq); + if (mrq) { + // data = mrq->data; +#ifdef DRAM_VIEWCMD + printf("\tDQ: BK%d Row:%03x Col:%03x", + mrq->bk, mrq->row, mrq->col + mrq->dqbytes); +#endif + mrq->dqbytes += dm->BL * dm->busW * gpu_n_mem_per_ctrlr; /*16 bytes*/ + if (mrq->dqbytes >= mrq->nbytes) { + + if (gpgpu_memlatency_stat) { + dq_latency = gpu_sim_cycle + gpu_tot_sim_cycle - mrq->timestamp; + dq_lat_table[LOGB2(dq_latency)]++; + if (dq_latency > max_dq_latency) + max_dq_latency = dq_latency; + } + data = mrq->data; + + free(mrq); + } + } +#ifdef DRAM_VIEWCMD + printf("\n"); +#endif + + return data; +} + +// a hack to allow peeking into what memory request will be serviced. +void* dram_top( dram_t *dm ) +{ + dram_req_t *mrq; + void *data; + + data = NULL; + mrq = (dram_req_t*)dq_top(dm->rwq); + if (mrq) { + // number of bytes returned from dram if this is ever popped + unsigned tobe_dqbytes = mrq->dqbytes + dm->BL * dm->busW * gpu_n_mem_per_ctrlr; + if (tobe_dqbytes >= mrq->nbytes) { + data = mrq->data; + } + } + + return data; +} + +void dram_print( dram_t* dm, FILE* simFile) +{ + unsigned i; + fprintf(simFile,"DRAM[%d]: %d bks, busW=%d BL=%d CL=%d, ", + dm->id, dm->nbk, dm->busW, dm->BL, dm->CL ); + fprintf(simFile,"tRRD=%d tCCD=%d, tRCD=%d tRAS=%d tRP=%d tRC=%d\n", + dm->tCCD, dm->tRRD, dm->tRCD, dm->tRAS, dm->tRP, dm->tRC ); + fprintf(simFile,"n_cmd=%d n_nop=%d n_act=%d n_pre=%d n_req=%d n_rd=%d n_write=%d bw_util=%.4g\n", + dm->n_cmd, dm->n_nop, dm->n_act, dm->n_pre, dm->n_req, dm->n_rd, dm->n_wr, + (float)dm->bwutil/dm->n_cmd); + fprintf(simFile,"n_activity=%d dram_eff=%.4g\n", + dm->n_activity, (float)dm->bwutil/dm->n_activity); + for (i=0;i<dm->nbk;i++) { + fprintf(simFile, "bk%d: %da %di ",i,dm->bk[i]->n_access,dm->bk[i]->n_idle); + } + fprintf(simFile, "\n"); + fprintf(simFile, "dram_util_bins:"); + for (i=0;i<10;i++) fprintf(simFile, " %d", dm->dram_util_bins[i]); + fprintf(simFile, "\ndram_eff_bins:"); + for (i=0;i<10;i++) fprintf(simFile, " %d", dm->dram_eff_bins[i]); + fprintf(simFile, "\n"); + /* + { + delay_data* mrq; + mrq = dm->mrqq->head; + while (mrq) { + printf("%d",((dram_req_t*)mrq->data)->bk); + mrq = mrq->next; + } + printf("\n"); + } + */ + fprintf(simFile, "mrqq: max=%d avg=%g\n", dm->max_mrqs, (float)dm->ave_mrqs/dm->n_cmd); +} + +void dram_visualize( dram_t* dm ) +{ + unsigned i; + + printf("RRDc=%d CCDc=%d mrqq.Length=%d rwq.Length=%d\n", + dm->RRDc, dm->CCDc, dm->mrqq->length,dm->rwq->length); + for (i=0;i<dm->nbk;i++) { + printf("BK%d: state=%c curr_row=%03x, %2d %2d %2d %2d %p ", + i, dm->bk[i]->state, dm->bk[i]->curr_row, + dm->bk[i]->RCDc, dm->bk[i]->RASc, + dm->bk[i]->RPc, dm->bk[i]->RCc, + dm->bk[i]->mrq ); + if (dm->bk[i]->mrq) + printf("txf: %d %d", dm->bk[i]->mrq->nbytes, dm->bk[i]->mrq->txbytes); + printf("\n"); + } + if ( dm->m_fast_ideal_scheduler ) { + dump_fast_ideal_scheduler( dm ); + } + +} + +void dram_print_stat( dram_t* dm, FILE* simFile ) +{ + int i; + fprintf(simFile,"DRAM (%d): n_cmd=%d n_nop=%d n_act=%d n_pre=%d n_req=%d n_rd=%d n_write=%d bw_util=%.4g ", + dm->id, dm->n_cmd, dm->n_nop, dm->n_act, dm->n_pre, dm->n_req, dm->n_rd, dm->n_wr, + (float)dm->bwutil/dm->n_cmd); + fprintf(simFile, "mrqq: %d %.4g mrqsmax=%d ", dm->max_mrqs, (float)dm->ave_mrqs/dm->n_cmd, dm->max_mrqs_temp); + fprintf(simFile, "\n"); + fprintf(simFile, "dram_util_bins:"); + for (i=0;i<10;i++) fprintf(simFile, " %d", dm->dram_util_bins[i]); + fprintf(simFile, "\ndram_eff_bins:"); + for (i=0;i<10;i++) fprintf(simFile, " %d", dm->dram_eff_bins[i]); + fprintf(simFile, "\n"); + dm->max_mrqs_temp = 0; +} + + +unsigned dram_busy( dram_t* dm) +{ + unsigned busy = 0; + + switch (dm->scheduler_type) { + case DRAM_FIFO: + busy = (dm->mrqq->length > 0); + break; + case DRAM_IDEAL_FAST: + busy = (fast_scheduler_queue_length(dm) > 0) || (dm->mrqq->length > 0); + break; + } + + return busy; +} + diff --git a/src/gpgpu-sim/dram.h b/src/gpgpu-sim/dram.h new file mode 100644 index 0000000..fff23f6 --- /dev/null +++ b/src/gpgpu-sim/dram.h @@ -0,0 +1,237 @@ +/* + * dram.c + * + * Copyright (c) 2009 by Tor M. Aamodt, Wilson W. L. Fung, George L. Yuan, + * Ivan Sham, Justin Kwong, Dan O'Connor and the + * University of British Columbia + * Vancouver, BC V6T 1Z4 + * All Rights Reserved. + * + * THIS IS A LEGAL DOCUMENT BY DOWNLOADING GPGPU-SIM, YOU ARE AGREEING TO THESE + * TERMS AND CONDITIONS. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * NOTE: The files libcuda/cuda_runtime_api.c and src/cuda-sim/cuda-math.h + * are derived from the CUDA Toolset available from http://www.nvidia.com/cuda + * (property of NVIDIA). The files benchmarks/BlackScholes/ and + * benchmarks/template/ are derived from the CUDA SDK available from + * http://www.nvidia.com/cuda (also property of NVIDIA). The files + * src/gpgpusim_entrypoint.c and src/simplesim-3.0/ are derived from the + * SimpleScalar Toolset available from http://www.simplescalar.com/ + * (property of SimpleScalar LLC) and the files src/intersim/ are derived + * from Booksim (Simulator provided with the textbook "Principles and + * Practices of Interconnection Networks" available from + * http://cva.stanford.edu/books/ppin/). As such, those files are bound by + * the corresponding legal terms and conditions set forth separately (original + * copyright notices are left in files from these sources and where we have + * modified a file our copyright notice appears before the original copyright + * notice). + * + * Using this version of GPGPU-Sim requires a complete installation of CUDA + * which is distributed seperately by NVIDIA under separate terms and + * conditions. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the University of British Columbia nor the names of + * its contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * 4. This version of GPGPU-SIM is distributed freely for non-commercial use only. + * + * 5. No nonprofit user may place any restrictions on the use of this software, + * including as modified by the user, by any other authorized user. + * + * 6. GPGPU-SIM was developed primarily by Tor M. Aamodt, Wilson W. L. Fung, + * Ali Bakhoda, George L. Yuan, at the University of British Columbia, + * Vancouver, BC V6T 1Z4 + */ + +#include <stdio.h> +#include <stdlib.h> + +#include "delayqueue.h" +#include "../cuda-sim/dram_callback.h" + +#ifndef DRAM_H +#define DRAM_H + +#define FIFO_AGE_LIMIT 50 //used for both BANK_CONF and REALISTIC schedulers +#define FIFO_NUM_WRITE_LIMIT 3 //used for both BANK_CONF and REALISTIC schedulers +#define LOOKAHEAD_VALUE 10 //used for REALISTIC scheduler ONLY + +enum { + DRAM_FIFO, + DRAM_IDEAL_FAST, + DRAM_NUM_HANDLES +}; + + +#define READ 'R' //define read and write states +#define WRITE 'W' +typedef struct { + unsigned int row; + unsigned int col; + unsigned int bk; + unsigned int nbytes; + unsigned int txbytes; + unsigned int dqbytes; + unsigned int age; + unsigned int timestamp; + unsigned char rw; //is the request a read or a write? + unsigned long long int addr; + unsigned int insertion_time; + void* data; + + int cache_hits_waiting; +} dram_req_t; + +#define BANK_IDLE 'I' +#define BANK_ACTIVE 'A' + +typedef struct { + unsigned int RCDc; + unsigned int RCDWRc; + unsigned int RASc; + unsigned int RPc; + unsigned int RCc; + + unsigned char rw; //is the bank reading or writing? + unsigned char state; //is the bank active or idle? + unsigned int curr_row; + + dram_req_t *mrq; + + unsigned int n_access; + unsigned int n_writes; + unsigned int n_idle; +} bank_t; + +typedef struct { + unsigned int id; + + unsigned int tCCD; //column to column delay + unsigned int tRRD; //minimal time required between activation of rows in different banks + unsigned int tRCD; //row to column delay - time required to activate a row before a read + unsigned int tRCDWR;//row to column delay for a write command + unsigned int tRAS; //time needed to activate row + unsigned int tRP; //row precharge ie. deactivate row + unsigned int tRC; //row cycle time ie. precharge current, then activate different row + + unsigned int CL; //CAS latency + unsigned int WL; //WRITE latency + unsigned int BL; //Burst Length in bytes (we're using 4? could be 8) + unsigned int tRTW; //time to switch from read to write + unsigned int tWTR; //time to switch from write to read 5? look in datasheet + unsigned int busW; + + unsigned int nbk; + bank_t **bk; + unsigned int prio; + + unsigned int RRDc; + unsigned int CCDc; + unsigned int RTWc; //read to write penalty applies across banks + unsigned int WTRc; //write to read penalty applies across banks + + unsigned char rw; //was last request a read or write? (important for RTW, WTR) + + unsigned int pending_writes; + unsigned char realistic_scheduler_mode; + + delay_queue *rwq; + delay_queue *mrqq; + //buffer to hold packets when DRAM processing is over + //should be filled with dram clock and popped with l2or icnt clock + delay_queue *returnq; + + + unsigned int dram_util_bins[10]; + unsigned int dram_eff_bins[10]; + unsigned int last_n_cmd, last_n_activity, last_bwutil; + + unsigned int queue_limit; + + unsigned int n_cmd; + unsigned int n_activity; + unsigned int n_nop; + unsigned int n_act; + unsigned int n_pre; + unsigned int n_rd; + unsigned int n_wr; + unsigned int n_req; + unsigned int max_mrqs_temp; + + unsigned int bwutil; + unsigned int max_mrqs; + unsigned int ave_mrqs; + unsigned char scheduler_type; + + void *m_fast_ideal_scheduler; + + void *m_L2cache; + + unsigned int n_cmd_partial; + unsigned int n_activity_partial; + unsigned int n_nop_partial; + unsigned int n_act_partial; + unsigned int n_pre_partial; + unsigned int n_req_partial; + unsigned int ave_mrqs_partial; + unsigned int bwutil_partial; + + void * req_hist; +} dram_t; + + +dram_t* dram_create( unsigned int id, unsigned int nbk, + unsigned int tCCD, unsigned int tRRD, + unsigned int tRCD, unsigned int tRAS, + unsigned int tRP, unsigned int tRC, + unsigned int CL, unsigned int WL, + unsigned int BL, unsigned int tWTR, + unsigned int busW, unsigned int queue_limit, + unsigned char scheduler_type ); +void dram_free( dram_t *dm ); +int dram_full( dram_t *dm ); +void dram_push( dram_t *dm, unsigned int bank, + unsigned int row, unsigned int col, + unsigned int nbytes, unsigned int write, + unsigned int wid, unsigned int sid, int cache_hits_waiting, unsigned long long addr, + void *data ); +void scheduler_fifo(dram_t* dm); +void dram_issueCMD (dram_t* dm); +void* dram_pop( dram_t *dm ); +void* dram_top( dram_t *dm ); +unsigned dram_busy( dram_t *dm); +void dram_print( dram_t* dm, FILE* simFile ); +void dram_visualize( dram_t* dm ); +void dram_print_stat( dram_t* dm, FILE* simFile ); +void fast_scheduler_ideal(dram_t* dm); +void* alloc_fast_ideal_scheduler(dram_t *dm); +void dump_fast_ideal_scheduler(dram_t *dm); +unsigned fast_scheduler_queue_length(dram_t *dm); + +//supposed to return the current queue length for all memory scheduler types. +unsigned int dram_que_length( dram_t *dm ); + +#endif /*DRAM_H*/ diff --git a/src/gpgpu-sim/dram_sched.cc b/src/gpgpu-sim/dram_sched.cc new file mode 100644 index 0000000..24b518f --- /dev/null +++ b/src/gpgpu-sim/dram_sched.cc @@ -0,0 +1,247 @@ +/* + * dram_sched.cc + * + * Copyright (c) 2009 by Tor M. Aamodt, Wilson W. L. Fung, Ali Bakhoda, + * George L. Yuan and the + * University of British Columbia + * Vancouver, BC V6T 1Z4 + * All Rights Reserved. + * + * THIS IS A LEGAL DOCUMENT BY DOWNLOADING GPGPU-SIM, YOU ARE AGREEING TO THESE + * TERMS AND CONDITIONS. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * NOTE: The files libcuda/cuda_runtime_api.c and src/cuda-sim/cuda-math.h + * are derived from the CUDA Toolset available from http://www.nvidia.com/cuda + * (property of NVIDIA). The files benchmarks/BlackScholes/ and + * benchmarks/template/ are derived from the CUDA SDK available from + * http://www.nvidia.com/cuda (also property of NVIDIA). The files from + * src/intersim/ are derived from Booksim (a simulator provided with the + * textbook "Principles and Practices of Interconnection Networks" available + * from http://cva.stanford.edu/books/ppin/). As such, those files are bound by + * the corresponding legal terms and conditions set forth separately (original + * copyright notices are left in files from these sources and where we have + * modified a file our copyright notice appears before the original copyright + * notice). + * + * Using this version of GPGPU-Sim requires a complete installation of CUDA + * which is distributed seperately by NVIDIA under separate terms and + * conditions. To use this version of GPGPU-Sim with OpenCL requires a + * recent version of NVIDIA's drivers which support OpenCL. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the University of British Columbia nor the names of + * its contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * 4. This version of GPGPU-SIM is distributed freely for non-commercial use only. + * + * 5. No nonprofit user may place any restrictions on the use of this software, + * including as modified by the user, by any other authorized user. + * + * 6. GPGPU-SIM was developed primarily by Tor M. Aamodt, Wilson W. L. Fung, + * Ali Bakhoda, George L. Yuan, at the University of British Columbia, + * Vancouver, BC V6T 1Z4 + */ + +#include "dram_sched.h" +#include "gpu-misc.h" +#include "gpu-sim.h" +#include "../util.h" + +extern unsigned long long gpu_sim_cycle; +extern signed long long gpu_tot_sim_cycle; +extern unsigned max_mrq_latency; +extern unsigned mrq_lat_table[24]; +extern int gpgpu_memlatency_stat; +extern int gpgpu_dram_sched_queue_size; +extern unsigned int **concurrent_row_access; //concurrent_row_access[dram chip id][bank id] +extern unsigned int **row_access; //concurrent_row_access[dram chip id][bank id] +extern unsigned int **num_activates; //num_activates[dram chip id][bank id] +extern unsigned int **max_conc_access2samerow; //max_conc_access2samerow[dram chip id][bank id] +extern unsigned int **max_servicetime2samerow; + +ideal_dram_scheduler::ideal_dram_scheduler( dram_t *dm ) +{ + + m_num_pending = 0; + m_dram = dm; + m_queue = new std::list<dram_req_t*>[dm->nbk]; + m_bins = new std::map<unsigned,std::list<std::list<dram_req_t*>::iterator> >[ dm->nbk ]; + m_last_row = new std::list<std::list<dram_req_t*>::iterator>*[ dm->nbk ]; + curr_row_service_time = new unsigned[dm->nbk]; + row_service_timestamp = new unsigned[dm->nbk]; + for ( unsigned i=0; i < dm->nbk; i++ ) { + m_queue[i].clear(); + m_bins[i].clear(); + m_last_row[i] = NULL; + curr_row_service_time[i] = 0; + row_service_timestamp[i] = 0; + } + +} + +void ideal_dram_scheduler::add_req( dram_req_t *req ) +{ + m_num_pending++; + + m_queue[req->bk].push_front(req); + std::list<dram_req_t*>::iterator ptr = m_queue[req->bk].begin(); + + m_bins[req->bk][req->row].push_front( ptr ); //newest reqs to the front + + +} + + +inline void ideal_dram_scheduler::data_collection(unsigned int bank) +{ + if (gpu_sim_cycle > row_service_timestamp[bank]) { + curr_row_service_time[bank] = gpu_sim_cycle - row_service_timestamp[bank]; + if (curr_row_service_time[bank] > max_servicetime2samerow[m_dram->id][bank]) + max_servicetime2samerow[m_dram->id][bank] = curr_row_service_time[bank]; + } + curr_row_service_time[bank] = 0; + row_service_timestamp[bank] = gpu_sim_cycle; + if (concurrent_row_access[m_dram->id][bank] > max_conc_access2samerow[m_dram->id][bank]) { + max_conc_access2samerow[m_dram->id][bank] = concurrent_row_access[m_dram->id][bank]; + } + concurrent_row_access[m_dram->id][bank] = 0; + num_activates[m_dram->id][bank]++; +} + +dram_req_t *ideal_dram_scheduler::schedule( unsigned bank, unsigned curr_row ) +{ + int row_hit = 0; + if ( m_last_row[bank] == NULL ) { + if ( m_queue[bank].empty() ) + return NULL; + + std::map<unsigned,std::list<std::list<dram_req_t*>::iterator> >::iterator bin_ptr = m_bins[bank].find( curr_row ); + if ( bin_ptr == m_bins[bank].end()) { + dram_req_t *req = m_queue[bank].back(); + bin_ptr = m_bins[bank].find( req->row ); + assert( bin_ptr != m_bins[bank].end() ); // where did the request go??? + m_last_row[bank] = &(bin_ptr->second); + data_collection(bank); + } else { + m_last_row[bank] = &(bin_ptr->second); + + } + } + row_hit=1; + std::list<dram_req_t*>::iterator next = m_last_row[bank]->back(); + dram_req_t *req = (*next); + + concurrent_row_access[m_dram->id][bank]++; + row_access[m_dram->id][bank]++; + m_last_row[bank]->pop_back(); + + m_queue[bank].erase(next); + if ( m_last_row[bank]->empty() ) { + m_bins[bank].erase( req->row ); + m_last_row[bank] = NULL; + } +#ifdef DEBUG_FAST_IDEAL_SCHED + if ( req ) + printf("%08u : DRAM(%u) scheduling memory request to bank=%u, row=%u\n", + (unsigned)gpu_sim_cycle, m_dram->id, req->bk, req->row ); +#endif + assert( req != NULL && m_num_pending != 0 ); + m_num_pending--; + + return req; +} + + +void ideal_dram_scheduler::print( FILE *fp ) +{ + for ( unsigned b=0; b < m_dram->nbk; b++ ) { + printf(" %u: queue length = %u\n", b, (unsigned)m_queue[b].size() ); + } +} + +void* alloc_fast_ideal_scheduler(dram_t *dm) +{ + return new ideal_dram_scheduler(dm); +} + +void fast_scheduler_ideal(dram_t* dm) +{ + + + unsigned mrq_latency; + // replacement for scheduler_ideal() + + ideal_dram_scheduler *sched = reinterpret_cast<ideal_dram_scheduler*>( dm->m_fast_ideal_scheduler ); + while ( !dq_empty(dm->mrqq) && (!gpgpu_dram_sched_queue_size || sched->num_pending() < (unsigned) gpgpu_dram_sched_queue_size)) { + dram_req_t *req = (dram_req_t*)dq_pop(dm->mrqq); + sched->add_req(req); + } + + dram_req_t *req; + unsigned i; + for ( i=0; i < dm->nbk; i++ ) { + unsigned b = (i+dm->prio)%dm->nbk; + if ( !dm->bk[b]->mrq ) { + + req = sched->schedule(b, dm->bk[b]->curr_row); + + if ( req ) { + dm->prio = (dm->prio+1)%dm->nbk; + dm->bk[b]->mrq = req; + if (gpgpu_memlatency_stat) { + mrq_latency = gpu_sim_cycle + gpu_tot_sim_cycle - dm->bk[b]->mrq->timestamp; + dm->bk[b]->mrq->timestamp = gpu_tot_sim_cycle + gpu_sim_cycle; + mrq_lat_table[LOGB2(mrq_latency)]++; + if (mrq_latency > max_mrq_latency) { + max_mrq_latency = mrq_latency; + } + } + + break; + } + } + } +} + + + +void dump_fast_ideal_scheduler( dram_t *dm ) +{ + ideal_dram_scheduler *sched = reinterpret_cast<ideal_dram_scheduler*>( dm->m_fast_ideal_scheduler ); + sched->print(stdout); +} + +unsigned fast_scheduler_queue_length(dram_t *dm) +{ + if (dm->m_fast_ideal_scheduler ) { + ideal_dram_scheduler *sched = reinterpret_cast<ideal_dram_scheduler*>( dm->m_fast_ideal_scheduler ); + return sched->num_pending(); + } else { + printf("fast_scheduler_queue_length(): Where did the scheduler go?\n"); + exit(1); + } +} + + diff --git a/src/gpgpu-sim/dram_sched.h b/src/gpgpu-sim/dram_sched.h new file mode 100644 index 0000000..49cae22 --- /dev/null +++ b/src/gpgpu-sim/dram_sched.h @@ -0,0 +1,99 @@ +/* + * dram_sched.h + * + * Copyright (c) 2009 by Tor M. Aamodt, Wilson W. L. Fung, Ali Bakhoda, + * George L. Yuan and the + * University of British Columbia + * Vancouver, BC V6T 1Z4 + * All Rights Reserved. + * + * THIS IS A LEGAL DOCUMENT BY DOWNLOADING GPGPU-SIM, YOU ARE AGREEING TO THESE + * TERMS AND CONDITIONS. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * NOTE: The files libcuda/cuda_runtime_api.c and src/cuda-sim/cuda-math.h + * are derived from the CUDA Toolset available from http://www.nvidia.com/cuda + * (property of NVIDIA). The files benchmarks/BlackScholes/ and + * benchmarks/template/ are derived from the CUDA SDK available from + * http://www.nvidia.com/cuda (also property of NVIDIA). The files from + * src/intersim/ are derived from Booksim (a simulator provided with the + * textbook "Principles and Practices of Interconnection Networks" available + * from http://cva.stanford.edu/books/ppin/). As such, those files are bound by + * the corresponding legal terms and conditions set forth separately (original + * copyright notices are left in files from these sources and where we have + * modified a file our copyright notice appears before the original copyright + * notice). + * + * Using this version of GPGPU-Sim requires a complete installation of CUDA + * which is distributed seperately by NVIDIA under separate terms and + * conditions. To use this version of GPGPU-Sim with OpenCL requires a + * recent version of NVIDIA's drivers which support OpenCL. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the University of British Columbia nor the names of + * its contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * 4. This version of GPGPU-SIM is distributed freely for non-commercial use only. + * + * 5. No nonprofit user may place any restrictions on the use of this software, + * including as modified by the user, by any other authorized user. + * + * 6. GPGPU-SIM was developed primarily by Tor M. Aamodt, Wilson W. L. Fung, + * Ali Bakhoda, George L. Yuan, at the University of British Columbia, + * Vancouver, BC V6T 1Z4 + */ + +#ifndef dram_sched_h_INCLUDED +#define dram_sched_h_INCLUDED + +#include "dram.h" +#include "shader.h" +#include "gpu-sim.h" +#include "gpu-misc.h" +#include <list> +#include <map> + +class ideal_dram_scheduler { +public: + ideal_dram_scheduler( dram_t *dm ); + void add_req( dram_req_t *req ); + std::list<dram_req_t*>::iterator binarysort_VFTF(dram_req_t *req); + std::list<dram_req_t*>::iterator sort_VFTF(dram_req_t *req); + inline void data_collection(unsigned bank); + dram_req_t *schedule( unsigned bank, unsigned curr_row ); + void print( FILE *fp ); + unsigned num_pending() const { return m_num_pending;} + +private: + + dram_t *m_dram; + unsigned m_num_pending; + std::list<dram_req_t*> *m_queue; + std::map<unsigned,std::list<std::list<dram_req_t*>::iterator> > *m_bins; + std::list<std::list<dram_req_t*>::iterator> **m_last_row; + unsigned *curr_row_service_time; //one set of variables for each bank. + unsigned *row_service_timestamp; //tracks when scheduler began servicing current row +}; + +#endif diff --git a/src/gpgpu-sim/dwf.cc b/src/gpgpu-sim/dwf.cc new file mode 100644 index 0000000..cd66084 --- /dev/null +++ b/src/gpgpu-sim/dwf.cc @@ -0,0 +1,2609 @@ +/* + * dwf.cc + * + * Copyright (c) 2009 by Tor M. Aamodt, Wilson W. L. Fung, and the + * University of British Columbia + * Vancouver, BC V6T 1Z4 + * All Rights Reserved. + * + * THIS IS A LEGAL DOCUMENT BY DOWNLOADING GPGPU-SIM, YOU ARE AGREEING TO THESE + * TERMS AND CONDITIONS. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * NOTE: The files libcuda/cuda_runtime_api.c and src/cuda-sim/cuda-math.h + * are derived from the CUDA Toolset available from http://www.nvidia.com/cuda + * (property of NVIDIA). The files benchmarks/BlackScholes/ and + * benchmarks/template/ are derived from the CUDA SDK available from + * http://www.nvidia.com/cuda (also property of NVIDIA). The files from + * src/intersim/ are derived from Booksim (a simulator provided with the + * textbook "Principles and Practices of Interconnection Networks" available + * from http://cva.stanford.edu/books/ppin/). As such, those files are bound by + * the corresponding legal terms and conditions set forth separately (original + * copyright notices are left in files from these sources and where we have + * modified a file our copyright notice appears before the original copyright + * notice). + * + * Using this version of GPGPU-Sim requires a complete installation of CUDA + * which is distributed seperately by NVIDIA under separate terms and + * conditions. To use this version of GPGPU-Sim with OpenCL requires a + * recent version of NVIDIA's drivers which support OpenCL. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the University of British Columbia nor the names of + * its contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * 4. This version of GPGPU-SIM is distributed freely for non-commercial use only. + * + * 5. No nonprofit user may place any restrictions on the use of this software, + * including as modified by the user, by any other authorized user. + * + * 6. GPGPU-SIM was developed primarily by Tor M. Aamodt, Wilson W. L. Fung, + * Ali Bakhoda, George L. Yuan, at the University of British Columbia, + * Vancouver, BC V6T 1Z4 + */ + + +#include "dwf.h" +#include "histogram.h" +#include <map> +#include <set> +#include <deque> +#include <queue> +#include <string.h> + +using namespace std; + +unsigned int gpgpu_dwf_regbk = 1; +unsigned int gpgpu_dwf_heuristic = 0; +enum { + MAJORITY = 0, + MINORITY = 1, + FIFO = 2, + PDOMPRIO = 3, + PC = 4, + MAJORITY_MAXHEAP = 5, + N_DWFMODE +}; + +typedef struct warp_entry { + address_type pc; + int* tid; // thread id's + int occ; // occupancy vector + int pdom_prio; // pdom_priority + int pdom_occ; // pdom_priority's aux data + int next_warp; // index to next warp in an implicit queue + void* lut_ptr; // pointer to the warp lut entry that last update this warp (a hack), done to decouple warp lut and warp pool + int uid; // unique id of a warp +} warp_entry_t; + +class issue_warp_majority { +public: + + virtual void add_threads( address_type pc, int *tid) = 0; + virtual void push_warp( address_type pc, int idx) = 0; + virtual int pop_warp( ) = 0; + virtual void print( FILE *fout ) = 0; + virtual ~issue_warp_majority( ) {} +}; + +typedef struct maxheap_lut_entry { + address_type pc; // pc of the warps + int maxheap_idx; // index to the max heap +} maxheap_lut_entry_t; + +typedef struct maxheap_entry { + address_type pc; // pc of the warps + int n_thds; // number of threads with this pc (from lut) + int wpool_head; // the first warp with this pc + int wpool_tail; // the last warp with this pc + int lut_idx; // reverse index to the lut (for update in entry movement) +} maxheap_entry_t; + +class mh_lut_class { +private: + + maxheap_lut_entry_t *lut_data; + list<int> *lru_stack; // front = LRU + int n_set; + int insn_size_lgb2; + +public: + + int size; + int assoc; + int n_read; + int n_write; + int n_read_per_cycle; + int n_write_per_cycle; + + int n_aliased; + static maxheap_lut_entry_t clean_entry; + + mh_lut_class (int size, int assoc, int n_read_per_cycle, int n_write_per_cycle ) { + int i; + + this->size = size; + this->assoc = assoc; + lut_data = new maxheap_lut_entry_t[size]; + + for (i=0; i<size; i++) { + lut_data[i] = clean_entry; + } + + n_set = size/assoc; + assert(n_set && !((n_set - 1) & n_set)); // make sure n_set is a power of 2 + + insn_size_lgb2 = 0; + + lru_stack = new list<int>[n_set]; + for (i=0; i<n_set; i++) { + int j; + for (j=0; j<assoc; j++) { + lru_stack[i].push_back(i * assoc + j); + } + } + + this->n_read_per_cycle = n_read_per_cycle; + this->n_write_per_cycle = n_write_per_cycle; + this->n_read = 0; + this->n_write = 0; + this->n_aliased = 0; + } + + ~mh_lut_class ( ) { + delete[] lut_data; + } + + // obtain entry at a known location + maxheap_lut_entry_t get( int lut_idx ) { + assert(lut_idx < size); + n_read++; + return lut_data[lut_idx]; + } + + // modify an entry at a known location + void set( int lut_idx, maxheap_lut_entry_t lut_entry ) { + n_write++; + lut_data[lut_idx] = lut_entry; + } + + // update a lut entry with a new index + void update_mh_idx( int lut_idx, int mh_idx ) { + n_write++; + lut_data[lut_idx].maxheap_idx = mh_idx; + } + + // lookup an entry with a pc + int lookup( address_type pc ) { + int i; + int lut_idx = -1; + int set_start_idx = get_set(pc) * assoc; + + // look for the matched entry within the set + for (i = set_start_idx; i < (set_start_idx + assoc); i++) { + if (lut_data[i].pc == pc) { + lut_idx = i; + } + } + + // update lru stack if hit + if (lut_idx != -1) { + int set_idx = set_start_idx / assoc; + list<int>::iterator it; + it = find(lru_stack[set_idx].begin(), lru_stack[set_idx].end(), lut_idx); + + if (it != lru_stack[set_idx].end()) { + lru_stack[set_idx].erase(it); + lru_stack[set_idx].push_back(lut_idx); + } + } + + return lut_idx; + } + + void free(int lut_idx) { + set(lut_idx, clean_entry); + + int set_idx = lut_idx / assoc; + list<int>::iterator it; + it = find(lru_stack[set_idx].begin(), lru_stack[set_idx].end(), lut_idx); + + if (it != lru_stack[set_idx].end()) { + lru_stack[set_idx].erase(it); + lru_stack[set_idx].push_front(lut_idx); + } + } + + // find the LRU entry to be replaced + int find_lru( maxheap_lut_entry_t lut_entry ) { + int set_idx = get_set(lut_entry.pc); + int lru_idx = lru_stack[set_idx].front(); + + return lru_idx; + } + + // actually replacing the LRU entry + int replace_lru( maxheap_lut_entry_t lut_entry ) { + int set_idx = get_set(lut_entry.pc); + int lru_idx = lru_stack[set_idx].front(); + lru_stack[set_idx].pop_front(); + + // counting the number of overwritten entries + if (lut_data[lru_idx].maxheap_idx != 0) n_aliased++; + + set(lru_idx, lut_entry); + lru_stack[set_idx].push_back(lru_idx); + + return lru_idx; + } + + // reset the number of accesses to zero + void reset_access( ) { + n_read = 0; + n_write = 0; + } + + // clear the number of accesses - done at the end of scheduler cycle + void clear_access( ) { + n_read -= n_read_per_cycle; + n_read = (n_read >= 0)? n_read : 0; + n_write -= n_write_per_cycle; + n_write = (n_write >= 0)? n_write : 0; + } + + // test if the structure is done with all the required accesses + int all_access_done( ) { + return(n_read == 0 && n_write == 0); + } + + void print_lut_e(FILE *fout, maxheap_lut_entry_t lut_e) { + fprintf(fout, "[%08x]mh%02d", + lut_e.pc, lut_e.maxheap_idx); + } + + void print(FILE *fout) { + int i, j; + for (i=0; i<n_set; i++) { + fprintf(fout, "S%02d", i); + for (j=0; j<assoc; j++) { + fprintf(fout, " |%02d:", i * assoc + j); + print_lut_e(fout, lut_data[i * assoc + j]); + } + fprintf(fout, " "); + list<int>::iterator it = lru_stack[i].begin(); + for (; it != lru_stack[i].end(); it++) { + fprintf(fout, "%02d-", *it); + } + fprintf(fout, "\n"); + } + } + +private: + + inline int get_set(address_type pc) { + return((pc >> insn_size_lgb2) & (n_set - 1)); + } +}; + +maxheap_lut_entry_t mh_lut_class::clean_entry = {0xDEADBEEF, 0}; + +// A class tracking the number of accesses done to the maxheap structure +// and the index ranges from 1..n_entries with 1 being the root +class maxheap_class { +private: + + maxheap_entry_t *maxheap_data; + mh_lut_class *lut; + +public: + + int n_read; + int n_write; + int n_entries; + int size; + int n_read_per_cycle; + int n_write_per_cycle; + + int max_n_entries; + static maxheap_entry_t clean_entry; + + maxheap_class( int size, mh_lut_class *lut, int n_read_per_cycle, int n_write_per_cycle ) { + n_read = 0; + n_write = 0; + n_entries = 0; // index to the last element + this->size = size; + maxheap_data = new maxheap_entry_t[size]; + + for (int i=0; i<size; i++) { + maxheap_data[i] = clean_entry; + } + + this->lut = lut; + + this->n_read_per_cycle = n_read_per_cycle; + this->n_write_per_cycle = n_write_per_cycle; + this->n_read = 0; + this->n_write = 0; + this->max_n_entries = 0; + } + + ~maxheap_class( ) { + delete[] maxheap_data; + } + + // insert a new entry into the maxheap + // return: the index to the new entry + int insert( maxheap_entry_t mh_entry ) { + assert(n_entries + 1 < size); + n_write++; + n_entries++; + maxheap_data[n_entries] = mh_entry; + max_n_entries = (max_n_entries >= n_entries)? max_n_entries : n_entries; + return n_entries; + } + + // retrieve the max heap entry at index [mh_idx] + maxheap_entry_t get( int mh_idx ) { + assert(mh_idx > 0); + assert(mh_idx <= n_entries); + n_read++; + return maxheap_data[mh_idx]; + } + + // replace the max heap entry at index [mh_idx] + void set( int mh_idx, maxheap_entry_t mh_entry ) { + assert(mh_idx > 0); + assert(mh_idx <= n_entries); + n_write++; + maxheap_data[mh_idx] = mh_entry; + } + + // a special version of set that only reset the lut_idx + void remove_lut_idx( int mh_idx ) { + assert(mh_idx > 0); + assert(mh_idx <= n_entries); + n_write++; + maxheap_data[mh_idx].lut_idx = -1; + } + + // read both childrens of a given node, count as one read + // return the number of child read + int get_childof(int mh_idx, maxheap_entry_t *child) { + int child_idx = childof(mh_idx); + int child_read = 0; + + if (child_idx <= n_entries) { + n_read++; + child[0] = maxheap_data[child_idx]; + child_read++; + } + if (child_idx + 1 <= n_entries) { + child[1] = maxheap_data[child_idx + 1]; + child_read++; + } + + return child_read; + } + + // pop the root entry of max heap + maxheap_entry_t pop_root( ) { + maxheap_entry_t old_root = get(1); + maxheap_entry_t curr_mhe[3]; + curr_mhe[0] = get(n_entries); + + set(1, curr_mhe[0]); + if (curr_mhe[0].lut_idx >= 0) + lut->update_mh_idx(curr_mhe[0].lut_idx, 1); + + n_entries--; + + int curr_node = 1; + int n_child = 0; + + n_child = get_childof(curr_node, curr_mhe + 1); + while (n_child > 0) { + int max_child = 0; + int i; + for (i = 1; i < n_child + 1; i++) { + if (cmp_mh(curr_mhe[i], curr_mhe[max_child])) { + max_child = i; + } + } + + n_child = 0; + if (max_child > 0) { + int max_child_node = childof(curr_node) + max_child - 1; + set(curr_node, curr_mhe[max_child]); + set(max_child_node, curr_mhe[0]); + + // update the lut for this swap + if (curr_mhe[max_child].lut_idx >= 0) + lut->update_mh_idx(curr_mhe[max_child].lut_idx, curr_node); + if (curr_mhe[0].lut_idx >= 0) + lut->update_mh_idx(curr_mhe[0].lut_idx, max_child_node); + + // get the next child + curr_node = max_child_node; + n_child = get_childof(curr_node, curr_mhe + 1); + } + } + + return old_root; + } + + // probe if the maxheap is empty + int empty( ) { + return(n_entries == 0); + } + + // reset the number of accesses to zero + void reset_access( ) { + n_read = 0; + n_write = 0; + } + + // clear the number of accesses - done at the end of scheduler cycle + void clear_access( ) { + n_read -= n_read_per_cycle; + n_read = (n_read >= 0)? n_read : 0; + n_write -= n_write_per_cycle; + n_write = (n_write >= 0)? n_write : 0; + } + + // test if the structure is done with all the required accesses + int all_access_done( ) { + return(n_read == 0 && n_write == 0); + } + + // sort the max heap again starting from start_idx + // (this entry can only go up in the tree to the root) + void sort_bottomup(int start_idx) { + maxheap_entry_t mh_entry; + maxheap_entry_t mh_parent; + + if (start_idx == 1) return; // no need to resort if the root is incremented + + int curr_idx = start_idx; + int parent_idx = parentof(start_idx); + + int continue_sort = 1; + while (curr_idx > 1 && continue_sort) { + mh_entry = get(curr_idx); + mh_parent = get(parent_idx); + + // swap the entries if it is now larger than it's parent + if (cmp_mh(mh_entry, mh_parent)) { + set(parent_idx, mh_entry); + set(curr_idx, mh_parent); + + // update the lut for this swap + if (mh_entry.lut_idx >= 0) + lut->update_mh_idx(mh_entry.lut_idx, parent_idx); + if (mh_parent.lut_idx >= 0) + lut->update_mh_idx(mh_parent.lut_idx, curr_idx); + + // update index for next iteration + curr_idx = parent_idx; + parent_idx = parentof(curr_idx); + } else { + // swap did not happen, no need to sort anymore + continue_sort = 0; + } + } + } + + void print_mh_e(FILE *fout, maxheap_entry_t mh_e) { + fprintf(fout, "[%08x]%03d(H%03dT%03d)p%02d | ", + mh_e.pc, mh_e.n_thds, mh_e.wpool_head, mh_e.wpool_tail, mh_e.lut_idx); + } + + void print(FILE *fout) { + fprintf(fout, "MaxHeap: "); + fprintf(fout, "N_entries = %d\n", n_entries); + for (int i=0; i<n_entries; i++) { + print_mh_e(fout, maxheap_data[i + 1]); + if (!((i + 2) & (i + 1))) fprintf(fout, "\n"); + } + fprintf(fout, "\n"); + } + +private: + + static inline int parentof(int mh_idx) { + assert(mh_idx > 0); + return(mh_idx / 2); + } + + static inline int childof(int mh_idx) { + return(mh_idx * 2); + } + + static inline int cmp_mh(maxheap_entry_t &a, maxheap_entry_t &b) { + if (a.n_thds > b.n_thds) return 1; + if (a.n_thds == b.n_thds) { + if (a.pc < b.pc) return 1; + } + return 0; + } + +}; + +maxheap_entry_t maxheap_class::clean_entry = {0, 0, -1, -1, -1}; + +typedef struct mh_update_struct { + int n_maxheap_read; + int n_maxheap_write; + int n_mhlut_read; + int n_mhlut_write; +} mh_update; + +// heap implementation of majority policy +class issue_warp_majority_heap : public issue_warp_majority { +public: + + mh_lut_class mh_lut; + maxheap_class maxheap; + + maxheap_lut_entry_t major_lut_e; + maxheap_entry_t major_mh_e; + + vector<warp_entry_t> *warp_pool; + int simd_width; + + int n_stall_on_maxheap; + + queue<mh_update> update_queue; + static pow2_histogram n_pending_updates_histo; + + issue_warp_majority_heap (int simd_width = 0, vector<warp_entry_t> *bp = NULL, + int lut_size = 32, int lut_assoc = 4, int maxheap_size = 128, + int n_read_lut = 4, int n_write_lut = 4, + int n_read_mh = 4, int n_write_mh = 4) + : mh_lut(lut_size, lut_assoc, n_read_lut, n_write_lut), + maxheap(maxheap_size, &mh_lut, n_read_mh, n_write_mh) + { + this->simd_width = simd_width; + this->warp_pool = bp; + + this->major_lut_e = mh_lut_class::clean_entry; + this->major_mh_e = maxheap_class::clean_entry; + + this->n_stall_on_maxheap = 0; + } + + // adding more threads to a specify pc + // these threads may end up in different warpes + void add_threads( address_type pc, int *tid) { + int i; + int n_thds = 0; + for (i=0; i<simd_width; i++) { + if (tid[i] >= 0) n_thds++; + } + + // handle special case with adding threads to current majority pc + if (major_lut_e.pc == pc) { + assert(major_mh_e.pc == pc); + major_mh_e.n_thds += n_thds; + return; + } + + maxheap_lut_entry_t lut_e; + maxheap_entry_t mh_entry; + + // snapshot the current maxheap read/write demand + mh_update new_mh_update; + new_mh_update.n_maxheap_read = maxheap.n_read; + new_mh_update.n_maxheap_write = maxheap.n_write; + new_mh_update.n_mhlut_read = mh_lut.n_read; + new_mh_update.n_mhlut_write = mh_lut.n_write; + + int lut_idx = mh_lut.lookup(pc); + + int sort_from_idx = 0; + + if (lut_idx >= 0) { + // obtain the entry + lut_e = mh_lut.get(lut_idx); + + // get the maxheap entry and update its number of threads + mh_entry = maxheap.get(lut_e.maxheap_idx); + mh_entry.n_thds += n_thds; + maxheap.set(lut_e.maxheap_idx, mh_entry); + + // sort from this specific entry + sort_from_idx = lut_e.maxheap_idx; + } else { + // create a new lut entry + lut_e = mh_lut_class::clean_entry; + lut_e.pc = pc; + + // get index to the LRU lut entry in this set + lut_idx = mh_lut.find_lru(lut_e); + + // get the replaced lut entry and remove its link with the maxheap entry + maxheap_lut_entry_t lut_old = mh_lut.get(lut_idx); + if (lut_old.maxheap_idx > 0) maxheap.remove_lut_idx(lut_old.maxheap_idx); + + // create a new maxheap entry + mh_entry = maxheap_class::clean_entry; + mh_entry.pc = pc; + mh_entry.n_thds = n_thds; + mh_entry.lut_idx = lut_idx; + + // push the new entry into the maxheap and lut respectively + lut_e.maxheap_idx = maxheap.insert(mh_entry); + mh_lut.replace_lru(lut_e); + + // start sorting from the bottom? + sort_from_idx = lut_e.maxheap_idx; + } + + maxheap.sort_bottomup(sort_from_idx); + + // record the newly generated maxheap read/write demand from this update + new_mh_update.n_maxheap_read = maxheap.n_read - new_mh_update.n_maxheap_read; + new_mh_update.n_maxheap_write = maxheap.n_write - new_mh_update.n_maxheap_write; + new_mh_update.n_mhlut_read = mh_lut.n_read - new_mh_update.n_mhlut_read; + new_mh_update.n_mhlut_write = mh_lut.n_write - new_mh_update.n_mhlut_write; + + update_queue.push(new_mh_update); + } + + // call this when a new warp allocated for a specific pc + void push_warp( address_type pc, int idx) { + maxheap_entry_t *p_mh_e = NULL; + maxheap_entry_t mh_e; + maxheap_lut_entry_t lut_e = mh_lut_class::clean_entry; + int lut_idx = -1; + + if (major_mh_e.pc == pc) { + p_mh_e = &major_mh_e; + } else { + lut_idx = mh_lut.lookup(pc); + assert(lut_idx >= 0); // if it is a miss, a new entry should have been created already + lut_e = mh_lut.get(lut_idx); + mh_e = maxheap.get(lut_e.maxheap_idx); + p_mh_e = &mh_e; + + // discounting these 'gets' + // because they should be combined with the 'gets' in add_threads() + mh_lut.n_read--; + maxheap.n_read--; + } + + if (p_mh_e->wpool_head == -1) { + p_mh_e->wpool_head = idx; + p_mh_e->wpool_tail = idx; + } else { + (*warp_pool)[p_mh_e->wpool_tail].next_warp = idx; + p_mh_e->wpool_tail = idx; + } + + if (major_mh_e.pc == pc) { + } else { + maxheap.set(lut_e.maxheap_idx, mh_e); + // discounting this 'set' + // because it should be combined with the 'set' in add_threads() + maxheap.n_write--; + } + } + + // obtain a warp index from this issue logic + int pop_warp( ) { + int bidx = -1; + if (major_mh_e.wpool_head == -1 && !maxheap.empty()) { + if (this->all_access_done( )) { + // pop the majority PC from max heap + major_mh_e = maxheap.pop_root(); + + // pop its corresponding entry from the lut as well (if it exists) + if (major_mh_e.lut_idx >= 0) { + major_lut_e = mh_lut.get(major_mh_e.lut_idx); + mh_lut.free(major_mh_e.lut_idx); + } else { + major_lut_e = mh_lut_class::clean_entry; + } + } else { + n_stall_on_maxheap += 1; + bidx = -1; + return bidx; + } + } + + // just pop and entry to from the virtual queue (and set the head pointer to next warp) + bidx = major_mh_e.wpool_head; + if (bidx >= 0) { + major_mh_e.wpool_head = (*warp_pool)[major_mh_e.wpool_head].next_warp; + } + + return bidx; + } + + void reset_access( ) { + maxheap.reset_access(); + mh_lut.reset_access(); + + while (!update_queue.empty()) { + update_queue.pop(); + } + } + + inline void consume_access( int &req_acc, int &avl_acc) { + if (req_acc > avl_acc) { + req_acc -= avl_acc; + avl_acc = 0; + } else { + avl_acc -= req_acc; + req_acc = 0; + } + } + + void clear_access( ) { + maxheap.clear_access(); + mh_lut.clear_access(); + + int n_maxheap_read_bw = maxheap.n_read_per_cycle; + int n_maxheap_write_bw = maxheap.n_write_per_cycle; + int n_mhlut_read_bw = mh_lut.n_read_per_cycle; + int n_mhlut_write_bw = mh_lut.n_write_per_cycle; + + while ((n_maxheap_read_bw > 0 || n_maxheap_read_bw > 0 || + n_mhlut_read_bw > 0 || n_mhlut_write_bw > 0) && !update_queue.empty()) { + mh_update &c_update = update_queue.front(); + + consume_access (c_update.n_maxheap_read, n_maxheap_read_bw); + consume_access (c_update.n_maxheap_write, n_maxheap_write_bw); + consume_access (c_update.n_mhlut_read, n_mhlut_read_bw); + consume_access (c_update.n_mhlut_write, n_mhlut_write_bw); + + if (c_update.n_maxheap_read == 0 && c_update.n_maxheap_write == 0 && + c_update.n_mhlut_read == 0 && c_update.n_mhlut_write == 0) { + update_queue.pop(); + } else { + break; + } + } + + n_pending_updates_histo.add2bin(update_queue.size()); + } + + void print( FILE *fout ) { + fprintf(fout, "LUT: "); + mh_lut.print_lut_e(fout, major_lut_e); + fprintf(fout, " \tMH: "); + maxheap.print_mh_e(fout, major_mh_e); + fprintf(fout, "\n"); + mh_lut.print(fout); + maxheap.print(fout); + } + + static void print_stat( FILE *fout) { + fprintf(fout, "n_pending_maxheap_updates = "); + n_pending_updates_histo.fprint(fout); + fprintf(fout, "\n"); + } + +private: + + int all_access_done( ) { + return(maxheap.all_access_done() && mh_lut.all_access_done()); + + } +}; +pow2_histogram issue_warp_majority_heap::n_pending_updates_histo; + +class warp_queue { +public: + int m_pc; + int n_thds; + int simd_width; + deque<int> idx_queue; + + warp_queue( address_type pc, int simd_width) { + this->m_pc = pc; + this->n_thds = 0; + this->simd_width = simd_width; + } + + // called right after a lut_entry is looked up + void add_threads( int *tid ) { + for (int i=0; i<simd_width; i++) { + if (tid[i] >= 0) this->n_thds++; + } + } + + // called right after a warp is issued + void sub_threads( int *tid ) { + for (int i=0; i<simd_width; i++) { + if (tid[i] >= 0) this->n_thds--; + } + } + + // if other warp queue should be ahead + bool operator<(const warp_queue& other) const { + if (n_thds == other.n_thds) { + return(m_pc > other.m_pc); // smaller pc first + } else { + return(n_thds < other.n_thds); + } + } + bool operator>(const warp_queue& other) const { + if (n_thds == other.n_thds) { + return(m_pc > other.m_pc); // smaller pc first + } else { + return(n_thds > other.n_thds); + } + } + + void print( FILE *fout ) { + fprintf(fout, "0x%08x(%03d)=[", m_pc, n_thds); + deque<int>::iterator dit = idx_queue.begin(); + for (; dit != idx_queue.end(); dit++) { + fprintf(fout, "%03d ", *dit); + } + fprintf(fout, "]\n"); + } +}; + +bool minor_warp( const warp_queue* a, const warp_queue* b ) { + return(*a<*b); +} + +// queue implementation of majority scheduling policy +class issue_warp_majority_queue : public issue_warp_majority { +public: + map<address_type, warp_queue* > majority_map; + set<warp_queue*> warpq_set; + warp_queue* maj_warp; + + vector<warp_entry_t> *warp_pool; + int simd_width; + + issue_warp_majority_queue(int simd_width = 0, vector<warp_entry_t> *bp = NULL) { + this->maj_warp = NULL; + this->simd_width = simd_width; + this->warp_pool = bp; + } + + // adding more threads to a specify pc + // these threads may end up in different warps + void add_threads( address_type pc, int *tid) { + warp_queue* bq = majority_map[pc]; + if (bq == NULL) { + bq = new warp_queue(pc,simd_width); + warpq_set.insert(bq); + majority_map[pc] = bq; + } + bq->add_threads(tid); + } + + // call this when a new warp allocated for a specific pc + void push_warp( address_type pc, int idx) { + warp_queue* bq = majority_map[pc]; + assert(bq != NULL); + bool check_redundant_idx = false; + if (check_redundant_idx) { + deque<int>::iterator dit = find(bq->idx_queue.begin(), bq->idx_queue.end(), idx); + assert(dit == bq->idx_queue.end()); + } + bq->idx_queue.push_back(idx); + } + + // obtain a warp index from this issue logic + int pop_warp( ) { + int bidx = -1; + + // find the new majority pc if it didn't exist + if (maj_warp == NULL && warpq_set.size()) { + maj_warp = *max_element(warpq_set.begin(), warpq_set.end(), minor_warp); + } + + // if a majority pc indeed exist + if (maj_warp) { + assert(!maj_warp->idx_queue.empty()); + bidx = maj_warp->idx_queue.front(); + maj_warp->idx_queue.pop_front(); + maj_warp->sub_threads((*warp_pool)[bidx].tid); + + // when the majority pc runs out of thread + if (maj_warp->n_thds == 0) { + // remove that warp queue + warpq_set.erase(maj_warp); + majority_map.erase(maj_warp->m_pc); + delete maj_warp; + maj_warp = NULL; + } + } + + return bidx; + } + + void print( FILE *fout ) { + fprintf(fout, "issue_warp_majority:\n"); + set<warp_queue*>::iterator dit = warpq_set.begin(); + for (; dit != warpq_set.end(); dit++) { + fprintf(fout, " %c ", ((*dit)==maj_warp)? 'M':' '); + (*dit)->print(fout); + } + } + + void check_consistency( ) { + set<warp_queue*>::iterator set_it = warpq_set.begin(); + for (; set_it != warpq_set.end(); set_it++) { + warp_queue* bq = (*set_it); + + int real_nthds = 0; + deque<int>::iterator dit = bq->idx_queue.begin(); + for (; dit != bq->idx_queue.end(); dit++) { + int *tid = (*warp_pool)[*dit].tid; + for (int i = 0; i < simd_width; i++) { + real_nthds += (tid[i] >= 0)? 1 : 0; + } + } + + assert(real_nthds == bq->n_thds); + } + } +}; + +// pdom priority +class lesspdom_first { +public: + vector<warp_entry_t> *warp_pool; + lesspdom_first( vector<warp_entry_t> *bp=NULL ) { + this->warp_pool = bp; + } + bool operator() (const int &idx_a, const int &idx_b) const { + if ((*warp_pool)[idx_a].pdom_prio != (*warp_pool)[idx_b].pdom_prio) { + return((*warp_pool)[idx_a].pdom_prio < (*warp_pool)[idx_b].pdom_prio); + } else { + return((*warp_pool)[idx_a].occ > (*warp_pool)[idx_b].occ); + } + } +}; + + +class issue_warp_pdom_prio { +public: + vector<warp_entry_t> *warp_pool; + int* thd_pdom_prio; + int simd_width; + int n_threads; + + int resort_needed; + list<int> pdom_pqueue; //the queue holding all index + + lesspdom_first lesspdom_cmp; + + static set<address_type> reconvgence_pt; //table holding all recvg pt + + issue_warp_pdom_prio (int simd_width = 0, vector<warp_entry_t> *bp = NULL, + int n_threads = 0) + : lesspdom_cmp(bp) + { + this->simd_width = simd_width; + this->warp_pool = bp; + this->n_threads = n_threads; + this->thd_pdom_prio = new int[n_threads]; + memset(this->thd_pdom_prio, 0, sizeof(int)*n_threads); + this->resort_needed = 0; + } + + ~issue_warp_pdom_prio( ) { + delete[] this->thd_pdom_prio; + } + + void reinit( ) { + memset(this->thd_pdom_prio, 0, sizeof(int)*n_threads); + } + + // adding more threads to a warp + void add_threads( int idx, address_type pc) { + assert((*warp_pool)[idx].pc == pc); + + // check to see if this is a newly allocated warp + bool check_pdom = false; + if ((*warp_pool)[idx].pdom_prio == -1) { + check_pdom = true; + } + + // check for newly assigned threads to the warp + int pdom_occ = (*warp_pool)[idx].pdom_occ; + int *tid = (*warp_pool)[idx].tid; + for (int i=0; i<simd_width; i++) { + if (tid[i] >= 0 && !(pdom_occ & (1<<i))) { + if ((*warp_pool)[idx].pdom_prio < thd_pdom_prio[tid[i]]) { + (*warp_pool)[idx].pdom_prio = thd_pdom_prio[tid[i]]; + resort_needed = 1; + } + pdom_occ |= (1<<i); + } + } + if (check_pdom) { + if (reconvgence_pt.find(pc) != reconvgence_pt.end()) { + (*warp_pool)[idx].pdom_prio += 1; + } + } + } + + // call this when a new warp allocated for a specific pc + void push_warp( address_type pc, int idx ) { + assert((*warp_pool)[idx].pc == pc); + // initialize the pdom_prio for this newly allocated warp + (*warp_pool)[idx].pdom_prio = -1; + (*warp_pool)[idx].pdom_occ = 0; + pdom_pqueue.push_back(idx); + } + + // obtain a warp index from this issue logic + int front_warp( ) { + int bidx = -1; + + if (!pdom_pqueue.empty()) { + if (resort_needed) { + pdom_pqueue.sort(lesspdom_cmp); + resort_needed = 0; + } + + bidx = pdom_pqueue.front(); + } + + return bidx; + } + + int size( ) { + return pdom_pqueue.size(); + } + + void enforce_resort( ) { + resort_needed = 1; + } + + int pop_warp( ) { + int bidx = -1; + + if (!pdom_pqueue.empty()) { + if (resort_needed) { + pdom_pqueue.sort(lesspdom_cmp); + resort_needed = 0; + } + + bidx = pdom_pqueue.front(); + pdom_pqueue.pop_front(); + + // update the pdom prio of each thread inside a warp + for (int i=0; i<simd_width; i++) { + if ((*warp_pool)[bidx].tid[i] >= 0) { + thd_pdom_prio[(*warp_pool)[bidx].tid[i]] = (*warp_pool)[bidx].pdom_prio; + } + } + } + + return bidx; + } + +}; + +set<address_type> issue_warp_pdom_prio::reconvgence_pt = set<address_type>(); +//*/ + + +class npc_tracker_class { +public: + map<address_type, unsigned> pc_count; + unsigned* acc_pc_count; + int simd_width; + static map<unsigned, unsigned> histogram; + + npc_tracker_class( ) { + this->acc_pc_count = NULL; + this->simd_width = 0; + } + + npc_tracker_class(unsigned* acc_pc_count, int simd_width) { + this->acc_pc_count = acc_pc_count; + this->simd_width = simd_width; + } + + void add_threads( int *tid, address_type pc ) { + for (int i=0; i<simd_width; i++) { + if (tid[i] != -1) pc_count[pc] += 1; // automatically create a new entry if not exist + } + } + + void sub_threads( int *tid, address_type pc ) { + for (int i=0; i<simd_width; i++) { + if (tid[i] != -1) { + pc_count[pc] -= 1; + assert((int)pc_count[pc] >= 0); + if (pc_count[pc] == 0) pc_count.erase(pc); // manually erasing entries with 0 count + } + } + } + + void update_acc_count( ) { + (*acc_pc_count) += pc_count.size(); + histogram[pc_count.size()] += 1; + } + + unsigned count( ) { return pc_count.size();} + + static void histo_print( FILE* fout ) { + map<unsigned, unsigned>::iterator i; + fprintf(fout, "DYHW nPC Histogram: "); + for (i = histogram.begin(); i != histogram.end(); i++) { + fprintf(fout, "%d:%d ", i->first, i->second); + } + fprintf(fout, "\n"); + } +}; + +map<unsigned, unsigned> npc_tracker_class::histogram; + +class pc_tag { +private: + + address_type m_pc; + +public: + + pc_tag () { + this->reset(); + } + + pc_tag (const pc_tag& p) { this->m_pc = p.m_pc;} + pc_tag (const address_type& other_pc) { this->m_pc = other_pc;} + + pc_tag& operator=(const pc_tag& p) { m_pc = p.m_pc; return *this;} + pc_tag& operator=(const address_type& other_pc) { m_pc = other_pc; return *this;} + + inline bool operator==(const pc_tag& p) const { return(m_pc == p.m_pc);} + inline bool operator==(const address_type& other_pc) const { return(m_pc == other_pc);} + + inline bool operator!=(const pc_tag& p) const { return(m_pc != p.m_pc);} + inline bool operator!=(const address_type& other_pc) const { return(m_pc != other_pc);} + + inline bool operator<(const pc_tag& p) const { return(m_pc < p.m_pc);} + + inline void reset() { + m_pc = -1; + } + + inline address_type get_pc() const { return m_pc;} + + // the hash function to warp LUT + inline unsigned lut_hash( int insn_size_lgb2, int lut_nsets ) const { + return(m_pc >> insn_size_lgb2) & (lut_nsets - 1); + } + + inline void to_print(char *buffer, unsigned length) { + snprintf(buffer, length, "0x%08x", m_pc); + } +}; + +template <class Tag> +class tag2warp_entry_t { +public: + + Tag tag; + int idx; // pointing to warp pool + int occ; // occupancy vector + int accessed; // is the entry accessed this cycle + + tag2warp_entry_t () { + this->reset(); + } + + ~tag2warp_entry_t () {} + + tag2warp_entry_t (const tag2warp_entry_t& p) { + this->tag = p.tag; + this->idx = p.idx; + this->occ = p.occ; + this->accessed = p.accessed; + } + + tag2warp_entry_t& operator=(const tag2warp_entry_t& p) { + if (this != &p) { + tag = p.tag; + idx = p.idx; + occ = p.occ; + accessed = p.accessed; + } + return *this; + } + + inline bool operator==(const tag2warp_entry_t& p) const { + return(tag == p.tag); + } + + inline bool operator==(const Tag& test_tag) const { + return(tag == test_tag); + } + + inline bool operator()(const tag2warp_entry_t& p) const { + return(tag == p.tag); + } + + inline void reset() { + tag.reset(); + idx = 0; + occ = 0; + accessed = 0; + } + + void print( FILE *fout ) { + static char buffer[20]; + tag.to_print(buffer,20); + fprintf(fout, "\t%s->%03d (%02x)\n", buffer, idx, occ); + } + +}; + +template <class Tag> +class tag2warp_set { +public: + vector< tag2warp_entry_t<Tag> > entry; + list< tag2warp_entry_t<Tag>* > lru_stack; + + tag2warp_set(int assoc = 0) : entry(assoc) { + for (unsigned j=0; j<this->entry.size(); j++) { + this->lru_stack.push_back(&(this->entry[j])); + } + } + + tag2warp_set(const tag2warp_set& other) : entry(other.entry.size()) { + for (unsigned j=0; j<this->entry.size(); j++) { + this->lru_stack.push_back(&(this->entry[j])); + } + } + + tag2warp_set& operator=(const tag2warp_set& p) { + printf("tag2warp_set assignment operator called!\n"); + return *this; + } + + ~tag2warp_set() {} +}; + +template <class Tag> +class warp_lut { +public: + virtual ~warp_lut() {} + virtual tag2warp_entry_t<Tag>* lookup_pc2warp( const Tag& tag, bool& lut_missed ) = 0; + virtual void invalidate_entry( tag2warp_entry_t<Tag>* lut_entry, int warp_idx ) = 0; + virtual void clear_accessed( ) = 0; + virtual void print( FILE* fout) = 0; +}; + +template <class Tag> +class warp_lut_sa : public warp_lut<Tag> { +private: + int lut_size; + int lut_assoc; + vector< tag2warp_set<Tag> > tag2warp_lut; + int insn_size_lgb2; + + queue< tag2warp_entry_t<Tag>* > lut_accessed_q; // store accessed lut entry for clear + + struct same_tag { + Tag tag; + bool operator()(tag2warp_entry_t<Tag>* a) { + return(a->tag == tag); + } + }; + + static unsigned int lut_aliased; + +public: + warp_lut_sa(int lut_size, int lut_assoc, int insn_size) { + this->lut_size = lut_size; + this->lut_assoc = lut_assoc; + + // optimize for LUT hash function + insn_size_lgb2 = 0; + while ( (1 << insn_size_lgb2) < insn_size ) insn_size_lgb2++; + + // initialize the pc2warp LUT + // note: lut_size is the absolute size of LUT regardless of assoc. + this->tag2warp_lut.assign(lut_size/lut_assoc, tag2warp_set<Tag>(lut_assoc)); + + // assert on #set in LUT to be power of 2 + int lut_nset_pow2 = 1; + while ( lut_nset_pow2 < (int)tag2warp_lut.size() ) lut_nset_pow2 <<= 1; + assert((int)tag2warp_lut.size() == lut_nset_pow2); + } + + tag2warp_entry_t<Tag>* lookup_pc2warp( const Tag& tag, bool& lut_missed ); + void invalidate_entry( tag2warp_entry_t<Tag>* lut_entry, int warp_idx ) { + if (lut_entry != NULL) { // check for warp lut entry invalidation + if (lut_entry->idx == warp_idx) { + lut_entry->reset(); + } + } + } + + void clear_accessed( ); + + void print( FILE* fout) { + for (unsigned i=0; i< tag2warp_lut.size(); i++) { + for (unsigned j=0; j< tag2warp_lut[i].entry.size(); j++) { + fprintf(fout, "lut%03d-%02d:", i, j); + tag2warp_lut[i].entry[j].print(fout); + } + } + } + + static void print_stats ( FILE* fout ) { + fprintf( fout, "lut_aliased = %d\n", lut_aliased); + } +}; +template <class Tag> unsigned int warp_lut_sa<Tag>::lut_aliased = 0; + + +// lookup function in LUT +// may return an entry that has different PC for replacement +// or return a NULL pointer to indicate that the entry is accessed by another port +template <class Tag> +tag2warp_entry_t<Tag>* warp_lut_sa<Tag>::lookup_pc2warp( const Tag &tag, bool &lut_missed ) +{ + tag2warp_entry_t<Tag>* lut_entry = NULL; + unsigned hashed_pc = tag.lut_hash(insn_size_lgb2, tag2warp_lut.size()); + list< tag2warp_entry_t<Tag>* > &hashed_lru_stack = tag2warp_lut.at(hashed_pc).lru_stack; + struct same_tag same_tag_f; + + same_tag_f.tag = tag; + typename list< tag2warp_entry_t<Tag>* >::iterator lut_it; + lut_it = find_if(hashed_lru_stack.begin(), + hashed_lru_stack.end(), + same_tag_f); + if (lut_it != hashed_lru_stack.end()) { + lut_entry = *lut_it; + lut_entry->accessed = 1; + lut_accessed_q.push(lut_entry); + hashed_lru_stack.splice(hashed_lru_stack.end(), hashed_lru_stack, lut_it); + assert(lut_entry == hashed_lru_stack.back()); + lut_missed = false; + } else { + assert(!hashed_lru_stack.empty()); + lut_entry = hashed_lru_stack.front(); + if (lut_entry->accessed) { + lut_entry = NULL; + } else { + lut_entry->accessed = 1; + lut_accessed_q.push(lut_entry); + hashed_lru_stack.splice(hashed_lru_stack.end(), hashed_lru_stack, hashed_lru_stack.begin()); + assert(lut_entry == hashed_lru_stack.back()); + lut_aliased++; + } + lut_missed = true; + } + assert(hashed_lru_stack.size() == tag2warp_lut[hashed_pc].entry.size()); + + return lut_entry; +} + +template <class Tag> +void warp_lut_sa<Tag>::clear_accessed( ) { + while ( !lut_accessed_q.empty() ) { + lut_accessed_q.front()->accessed = 0; + lut_accessed_q.pop(); + } +} + +// a perfect warp lut that never misses. +template <class Tag> +class warp_lut_perfect : public warp_lut<Tag> { +private: + typedef map< Tag, tag2warp_entry_t<Tag>* > warp_map_t; + warp_map_t m_tag2entry_map; + + static unsigned int lut_max_size; +public: + warp_lut_perfect() {} + ~warp_lut_perfect() { + typename warp_map_t::iterator mit = m_tag2entry_map.begin(); + for (; mit != m_tag2entry_map.end(); mit++) { + delete mit->second; + } + } + + // idealistic implementation of lookup: the entry is never aliased, + // and a new one is created automatically if it does not exist + tag2warp_entry_t<Tag>* lookup_pc2warp( const Tag& tag, bool& lut_missed ) { + typename warp_map_t::iterator mit = m_tag2entry_map.find(tag); + + tag2warp_entry_t<Tag>* lut_entry = NULL; + if (mit != m_tag2entry_map.end()) { + lut_entry = mit->second; + assert(lut_entry->tag == tag); + } else { + lut_entry = new tag2warp_entry_t<Tag>(); + m_tag2entry_map.insert(make_pair(tag, lut_entry)); + } + + lut_missed = false; + lut_max_size = (lut_max_size < m_tag2entry_map.size())? m_tag2entry_map.size() : lut_max_size; + + return lut_entry; + } + + void invalidate_entry( tag2warp_entry_t<Tag>* lut_entry, int warp_idx ) { + if (lut_entry == NULL) return; + if (lut_entry->idx != warp_idx) return; + + typename warp_map_t::iterator mit = m_tag2entry_map.find(lut_entry->tag); + if (mit != m_tag2entry_map.end()) { + assert(mit->second == lut_entry); + mit->second->reset(); + delete mit->second; + m_tag2entry_map.erase(mit); + } + } + + void clear_accessed( ) {} + + void print( FILE* fout) { + typename warp_map_t::iterator mit = m_tag2entry_map.begin(); + for (; mit != m_tag2entry_map.end(); mit++) { + mit->second->print(fout); + } + } + + static void print_stats ( FILE* fout ) { + fprintf( fout, "lut_max_size = %d\n", lut_max_size); + } +}; +template <class Tag> unsigned int warp_lut_perfect<Tag>::lut_max_size = 0; + + +typedef tag2warp_entry_t<pc_tag> warplut_entry_t; +typedef pc_tag warp_tag_t; + +class dwf_hw_sche_class { +public: + int m_id; + warp_lut<pc_tag> *warp_lut_pc; + vector<warp_entry_t> warp_pool; + deque<int> free_warp_q; // the warp allocator + int simd_width; + int regf_width; + int insn_size_lgb2; + bool just_resume; + + vector<char> m_req; // request vector from incoming warp + vector<char> m_occ_new; // occupancy vector of the new warp, double as conflict vector + vector<char> m_occ_upd; // occupancy vector of the updated existing warp + vector<char> m_occ_ext; // occupancy vector of the existing warp + + dwf_hw_sche_class( int lut_size, int lut_assoc, + int simd_width, int regf_width, + int n_threads, int insn_size, + int heuristic, int id, + char *policy_opt = NULL ); + ~dwf_hw_sche_class(); + + warplut_entry_t* lookup_pc2warp( const warp_tag_t& lookup_tag ); + int update_warp( int* tid, address_type pc ); + + // barrier handling + int m_nbarriers; + class dwf_barrier { + public: + bool m_release; // see if a barrier is to be released (ie. all warp in cta hit already) + deque<int> m_queue; // queue storing warps currently hitting a barrier, skipping warplut and scheduler + + dwf_barrier() : m_release(false) {} + dwf_barrier(const dwf_barrier& that) + : m_release(that.m_release), m_queue(that.m_queue) {} + bool ready_to_issue() { + return(m_release && !m_queue.empty()); + } + }; + set< int > m_cta_released_barrier; // set of cta with released barrier + map< int, dwf_barrier > m_barrier; // map <barrier id == cta id, barrier> + int update_warp_at_barrier( int* tid, address_type pc, int cta_id, int barrier_num = 0 ); + void hit_barrier( int cta_id, int barrier_num = 0 ); + void release_barrier( int cta_id, int barrier_num = 0 ); + + int allocate_warp( address_type pc, bool update_scheduler = true ); + void free_warp( int idx, bool update_warplut = true ); + + void issue_warp( int *tid, address_type *pc ); + + void clear_accessed( ) { + warp_lut_pc->clear_accessed(); + } + + void init_cta(int start_thread, int cta_size, address_type start_pc); + + void print_pc2warp_lut( FILE *fout ); + void print_warp_pool( FILE *fout ); + void print_free_warp_q( FILE *fout ); + + int heuristic; + + // FIFO warp issue logic + queue<int> issue_warp_FIFO_q; + + // PC warp issue logic + class pc_first { + public: + vector<warp_entry_t> &warp_pool; + pc_first( vector<warp_entry_t> &bp ) : warp_pool(bp) {} + bool operator() (const int &idx_a, const int &idx_b) const { + if (warp_pool[idx_a].pc != warp_pool[idx_b].pc) { + return(warp_pool[idx_a].pc > warp_pool[idx_b].pc); + } else { + return(warp_pool[idx_a].occ < warp_pool[idx_b].occ); + } + } + }; + pc_first mypc_first; + priority_queue<int, vector<int>, pc_first > issue_warp_PC_q; + + // Majority warp issue logic + issue_warp_majority *issue_warp_MAJ; + void clear_policy_access( ); + void reset_policy_access( ); + + // PDOM Priority issue logic + issue_warp_pdom_prio issue_warp_pdom; + + // statistics + npc_tracker_class npc_tracker; + int max_warppool_occ; + int *warppool_occ_histo; // histogram of warppool occupancy + static unsigned int lut_realmiss; + static unsigned int uid_cnt; + static unsigned int warp_fragmentation; + static unsigned int warp_merge_conflict; + static void print_stats ( FILE* fout ) { + warp_lut_perfect<warp_tag_t>::print_stats( fout ); + warp_lut_sa<warp_tag_t>::print_stats( fout ); + fprintf( fout, "lut_realmiss = %d\n", lut_realmiss); + fprintf( fout, "warp_fragmentation = %d\n", warp_fragmentation); + fprintf( fout, "warp_merge_conflict = %d\n", warp_merge_conflict); + } +}; + +unsigned int dwf_hw_sche_class::lut_realmiss = 0; +unsigned int dwf_hw_sche_class::uid_cnt = 0; +unsigned int dwf_hw_sche_class::warp_fragmentation = 0; +unsigned int dwf_hw_sche_class::warp_merge_conflict = 0; + + +dwf_hw_sche_class::dwf_hw_sche_class( int lut_size, int lut_assoc, + int simd_width, int regf_width, + int n_threads, int insn_size, + int heuristic, int id, + char *policy_opt ) +: m_id(id), +// WarpLUT w/ pc tag +warp_lut_pc( (lut_size == 0)? (warp_lut<pc_tag> *) new warp_lut_perfect<pc_tag>() : + (warp_lut<pc_tag> *) new warp_lut_sa<pc_tag>(lut_size, lut_assoc, insn_size) ), +m_nbarriers(1), // for barrier +mypc_first( warp_pool ), issue_warp_PC_q( mypc_first ), // DPC +issue_warp_pdom(simd_width, &warp_pool, n_threads), // DPdPri +npc_tracker( NULL, simd_width ) +{ + unsigned i; + + this->simd_width = simd_width; + this->regf_width = regf_width; + this->m_req.resize(regf_width); + this->m_occ_new.resize(regf_width); + this->m_occ_upd.resize(regf_width); + this->m_occ_ext.resize(regf_width); + + // initialize the warp pool + // (make sure the thread id's are init to -1) + this->warp_pool.resize(n_threads); + for (i=0; i<warp_pool.size(); i++) { + warp_pool[i].pc = -1; + warp_pool[i].tid = new int[simd_width]; + memset(warp_pool[i].tid, -1, sizeof(int)*simd_width); + warp_pool[i].occ = 0; + warp_pool[i].next_warp = -1; + + // push the index to the warp allocator + free_warp_q.push_back(i); + } + + // setup for various heuristics + this->heuristic = heuristic; + switch (heuristic) { + case MAJORITY: + issue_warp_MAJ = new issue_warp_majority_queue(simd_width, &warp_pool); + break; + case MAJORITY_MAXHEAP: { + int mh_lut_size = 32; + int mh_lut_assoc = 4; + int n_reads_per_cycle_lut = 4; + int n_writes_per_cycle_lut = 4; + int mh_size = 128; + int n_reads_per_cycle_mh = 4; + int n_writes_per_cycle_mh = 4; + if (policy_opt != NULL) { + sscanf(policy_opt, ";LUT=%d:%dr%dw%d;MH=%dr%dw%d", + &mh_lut_size, &mh_lut_assoc, &n_reads_per_cycle_lut, &n_writes_per_cycle_lut, + &mh_size, &n_reads_per_cycle_mh, &n_writes_per_cycle_mh); + } + issue_warp_MAJ = new issue_warp_majority_heap(simd_width, &warp_pool, + mh_lut_size, mh_lut_assoc, mh_size, + n_reads_per_cycle_lut, n_writes_per_cycle_lut, + n_reads_per_cycle_mh, n_writes_per_cycle_mh); + } + break; + } + + this->just_resume = false; + + this->max_warppool_occ = 0; + this->warppool_occ_histo = new int[n_threads]; + memset(this->warppool_occ_histo, 0, n_threads*sizeof(int)); +} + +// should never be called (only at exit?) +dwf_hw_sche_class::~dwf_hw_sche_class( ) +{ + unsigned i; + + for (i=0; i<warp_pool.size(); i++) { + free(warp_pool[i].tid); + } + + delete[] this->warppool_occ_histo; + + delete warp_lut_pc; +} + +// allocate a new warp in warp pool +int dwf_hw_sche_class::allocate_warp( address_type pc, bool update_scheduler ) +{ + int idx; + assert(!free_warp_q.empty()); + idx = free_warp_q.front(); + free_warp_q.pop_front(); + warp_pool[idx].uid = uid_cnt; + uid_cnt++; + warp_pool[idx].pc = pc; + warp_pool[idx].next_warp = -1; + warp_pool[idx].lut_ptr = NULL; + + if (update_scheduler) { + if (heuristic == FIFO) issue_warp_FIFO_q.push(idx); + if (heuristic == PC) issue_warp_PC_q.push(idx); + if (heuristic == MAJORITY || heuristic == MAJORITY_MAXHEAP) + issue_warp_MAJ->push_warp(pc, idx); + if (heuristic == PDOMPRIO) issue_warp_pdom.push_warp(pc, idx); + } + + return idx; +} + +// free a warp in warp pool +// it will reset the content of the warp entry as well +void dwf_hw_sche_class::free_warp( int idx, bool update_warplut ) +{ + bool redundant_idx_check = false; + if (redundant_idx_check) { + deque<int>::iterator dit = find(free_warp_q.begin(), free_warp_q.end(), idx); + assert(dit == free_warp_q.end()); + } + + warp_pool[idx].pc = -1; + memset(warp_pool[idx].tid, -1, sizeof(int)*simd_width); + warp_pool[idx].occ = 0; + warp_pool[idx].next_warp = -1; + if (update_warplut) { + warp_lut_pc->invalidate_entry( (warplut_entry_t*)warp_pool[idx].lut_ptr, idx ); + } + + free_warp_q.push_back(idx); + assert(free_warp_q.size() <= warp_pool.size()); +} + +warplut_entry_t* dwf_hw_sche_class::lookup_pc2warp( const warp_tag_t& lookup_tag ) +{ + bool lut_missed = false; + + warplut_entry_t* lut_entry; + lut_entry = warp_lut_pc->lookup_pc2warp( lookup_tag, lut_missed ); + + if (!lut_missed) { + if (lut_entry->tag != warp_pool[lut_entry->idx].pc) lut_missed = true; + } + + if (lut_missed) { + if (npc_tracker.pc_count.find(lookup_tag.get_pc()) != npc_tracker.pc_count.end()) { + lut_realmiss++; // ie. the incoming warp lost an opportunity to merge + } + } + + return lut_entry; +} + + +void fill_all (vector<char>& container, const char& value) +{ + fill(container.begin(), container.end(), value); +} + +int regfile_hash(signed istream_number, unsigned simd_size, unsigned n_banks); +int dwf_hw_sche_class::update_warp( int *tid, address_type pc ) +{ + int i; + bool newwarp = false; + bool newwarp_alloc = false; + warplut_entry_t* lut_entry; + warp_tag_t warp_tag(pc); + lut_entry = lookup_pc2warp(warp_tag); + + // no LUT entry returned, stall + if (!lut_entry) { + assert(0); + } + + if (heuristic == MAJORITY || heuristic == MAJORITY_MAXHEAP) { + issue_warp_MAJ->add_threads(pc, tid); + } + + npc_tracker.add_threads( tid, pc ); + + // if the pc of the LUT entry does not match, + // allocate a new entry + if (lut_entry->tag != warp_tag) { + lut_entry->idx = allocate_warp(pc); + lut_entry->tag = warp_tag; + lut_entry->occ = 0; + assert(warp_pool[lut_entry->idx].pc == pc); + newwarp = true; + newwarp_alloc = true; + } + + // create the request vector + bool tid_has_valid_entry = false; + fill_all(m_req, 0); + for (i = 0; i<simd_width; i++) { + if (tid[i] != -1) { + int lane = regfile_hash(tid[i],simd_width,regf_width); + // make sure we are not having two threads going to same lane + assert(lane < regf_width); + m_req[lane] += 1; + tid_has_valid_entry = true; + } + } + assert(tid_has_valid_entry); + + // read the old idx pointing to an existing warp + int old_idx = lut_entry->idx; + + // create the conflict vector + fill_all(m_occ_ext, 0); + int regf_mask = regf_width - 1; + for (i = 0; i<simd_width; i++) { + m_occ_ext[i & regf_mask] += ((lut_entry->occ & (1 << i)) == 0)? 0 : 1; + } + fill_all(m_occ_upd, 0); + fill_all(m_occ_new, 0); + int n_regf_slot = simd_width / regf_width; + bool conflict = false; + for (i = 0; i<regf_width; i++) { + if (m_occ_ext[i] + m_req[i] > n_regf_slot) { + m_occ_new[i] = m_occ_ext[i] + m_req[i] - n_regf_slot; + m_occ_upd[i] = n_regf_slot - m_occ_ext[i]; + conflict = true; + } else { + m_occ_upd[i] = m_req[i]; + } + } + + // if the pc of the warp mismatch with lut, + // set conflict vector to all one. + // that force all threads to the newly allocated warp + if (warp_pool[old_idx].pc != pc) { + conflict = true; + for (i = 0; i<regf_width; i++) { + m_occ_new[i] = m_req[i]; + m_occ_upd[i] = 0; + m_occ_ext[i] = n_regf_slot; + } + } + + // if there are conflicted entries, get a new warp + int new_idx = -1; + if (conflict) { + new_idx = allocate_warp(pc); + lut_entry->idx = new_idx; + lut_entry->occ = 0; //update the lut_entry + assert(warp_pool[new_idx].pc == pc); + + int total_occ = 0; + for (i = 0; i < regf_width; i++) + total_occ += m_occ_ext[i] + m_req[i]; + if (total_occ <= simd_width) warp_fragmentation += 1; + warp_merge_conflict += 1; + + newwarp_alloc = true; + } + + // update the warp as indicated by the LUT + // if the lane is conflicted, or the old warp is just not + // write to the new warp + int new_occ = 0; + fill_all(m_occ_new, 0); + for (i = 0; i<simd_width; i++) { + if (tid[i] != -1) { + int rfbank = regfile_hash(tid[i],simd_width,regf_width); + int lane = -1; + if ((m_occ_ext[rfbank] < n_regf_slot) || newwarp) { + lane = rfbank + m_occ_ext[rfbank] * regf_width; + assert(lane < simd_width); + warp_pool[old_idx].tid[lane] = tid[i]; + warp_pool[old_idx].occ++; + lut_entry->occ |= (1<<lane); + m_occ_ext[rfbank]++; + } else { + lane = rfbank + m_occ_new[rfbank] * regf_width; + assert(lane < simd_width); + warp_pool[new_idx].tid[lane] = tid[i]; + warp_pool[new_idx].occ++; + new_occ |= (1<<lane); + m_occ_new[rfbank]++; + assert(m_occ_new[rfbank] <= n_regf_slot); + } + } + } + + // to cover the case where the pc of the warp mismatch with lut + // (because the warp is issued) + if (warp_pool[old_idx].pc == pc) { + issue_warp_pdom.add_threads(old_idx, pc); + } + if (conflict) { + lut_entry->occ = new_occ; + issue_warp_pdom.add_threads(new_idx, pc); + } + + warp_pool[lut_entry->idx].lut_ptr = lut_entry; // link up the lut entry and warp + + bool scheduler_consistency_check = false; + if (scheduler_consistency_check && heuristic == MAJORITY) { + ((issue_warp_majority_queue*)issue_warp_MAJ)->check_consistency(); + } + + return 1; +} + +// called AFTER threads hit a barrier to insert them into the barrier queue +// ASSUME: threads from released barrier are not hitting second barrier right away +int dwf_hw_sche_class::update_warp_at_barrier( int* tid, address_type pc, int cta_id, int barrier_num ) +{ + assert(barrier_num < m_nbarriers); + assert(cta_id >= 0); + + int i; + int warp_index = 0xDEADBEEF; + + npc_tracker.add_threads( tid, pc ); + + // always allocate new warp + warp_index = allocate_warp(pc, false); + assert(warp_pool[warp_index].pc == pc); + + // no need to create the request vector + // no need to create the conflict vector + + // assign threads into the new warp + fill_all(m_occ_ext, 0); + int max_nthreads_per_rfbank = simd_width / regf_width; + for (i = 0; i<simd_width; i++) { + if (tid[i] != -1) { + int rfbank = regfile_hash(tid[i],simd_width,regf_width); + int lane = -1; + + assert(m_occ_ext[rfbank] < max_nthreads_per_rfbank); + lane = rfbank + m_occ_ext[rfbank] * regf_width; + assert(lane < simd_width); + warp_pool[warp_index].tid[lane] = tid[i]; + warp_pool[warp_index].occ++; + m_occ_ext[rfbank]++; + } + } + + warp_pool[warp_index].lut_ptr = NULL; // no link to any lut entry + + // put the warp id into barrier queue + m_barrier[cta_id].m_queue.push_back(warp_index); + + // notify issue module to check this barrier at issue + if ( m_barrier[cta_id].ready_to_issue() ) { + m_cta_released_barrier.insert(cta_id); + } + + return 1; +} + +// called at decode stage when thread hit a barrier +// ASSUME: threads from released barrier are not hitting second barrier right away +void dwf_hw_sche_class::hit_barrier( int cta_id, int barrier_num ) +{ + assert(barrier_num < m_nbarriers); + assert(cta_id >= 0); + + m_barrier[cta_id].m_release = false; +} + +// called at decode stage when all thread in cta hit the barrier +// ASSUME: threads from released barrier are not hitting second barrier right away +void dwf_hw_sche_class::release_barrier( int cta_id, int barrier_num ) +{ + assert(barrier_num < m_nbarriers); + assert(cta_id >= 0); + + map<int, dwf_barrier>::iterator i_barrier = m_barrier.find(cta_id); + assert(i_barrier != m_barrier.end()); // barrier has to exists in the first place! + i_barrier->second.m_release = true; +} + +void dwf_hw_sche_class::issue_warp( int *tid, address_type *pc ) +{ + int i; + bool warp_issued = false; + + // scan the released barriers for ready warp + // TODO: arbitrate between different queues? + set<int>::iterator i_ctabar = m_cta_released_barrier.begin(); + for (; i_ctabar != m_cta_released_barrier.end(); ++i_ctabar) { + int cta_id = *i_ctabar; + map<int, dwf_barrier>::iterator i_barrier = m_barrier.find(cta_id); + + if ( i_barrier->second.ready_to_issue() ) { + int warp_idx = i_barrier->second.m_queue.front(); + + for (i = 0; i < simd_width; i++) { + tid[i] = warp_pool[warp_idx].tid[i]; + } + *pc = warp_pool[warp_idx].pc; + + i_barrier->second.m_queue.pop_front(); + free_warp(warp_idx, false); // don't update warplut as the warp is not linked to it + + // remove cta from checking list if the queue is emptied + // (if the last threads haven't made it back to scheduler in time, + // update_warp_at_barrier will insert the cta id again) + if (i_barrier->second.m_queue.empty()) { + m_cta_released_barrier.erase(i_ctabar); + } + + warp_issued = true; + + break; + } + } + + if (!warp_issued) { + switch (heuristic) { + case FIFO: + // Oldest warp are issued first + if (!issue_warp_FIFO_q.empty()) { + int idx = issue_warp_FIFO_q.front(); + for (i = 0; i < simd_width; i++) { + tid[i] = warp_pool[idx].tid[i]; + } + *pc = warp_pool[idx].pc; + + issue_warp_FIFO_q.pop(); + free_warp(idx); + } else { + memset(tid, -1, sizeof(int)*simd_width); + *pc = -1; + } + break; + case PC: + // lowest PC warp are issued first + if (!issue_warp_PC_q.empty()) { + int idx = issue_warp_PC_q.top(); + for (i = 0; i < simd_width; i++) { + tid[i] = warp_pool[idx].tid[i]; + } + *pc = warp_pool[idx].pc; + + issue_warp_PC_q.pop(); + free_warp(idx); + } else { + memset(tid, -1, sizeof(int)*simd_width); + *pc = -1; + } + break; + case MAJORITY: + case MAJORITY_MAXHEAP: + // issue the most common PC first + { + int idx = issue_warp_MAJ->pop_warp(); + if (idx >= 0) { + for (i = 0; i < simd_width; i++) { + tid[i] = warp_pool[idx].tid[i]; + } + *pc = warp_pool[idx].pc; + free_warp(idx); + } else { + memset(tid, -1, sizeof(int)*simd_width); + *pc = -1; + } + } + break; + case PDOMPRIO: + // issue the warp with lowest PDOM count + { + int idx = issue_warp_pdom.front_warp(); + if (idx >= 0) { + issue_warp_pdom.pop_warp(); + + for (i = 0; i < simd_width; i++) { + tid[i] = warp_pool[idx].tid[i]; + } + *pc = warp_pool[idx].pc; + free_warp(idx); + + just_resume = false; + } else { + memset(tid, -1, sizeof(int)*simd_width); + *pc = -1; + } + } + break; + default: + printf("Unsupported Heuristics!\n"); + abort(); + break; + } + } + + npc_tracker.sub_threads( tid, *pc ); + + int warppool_occ = warp_pool.size() - free_warp_q.size(); + if (max_warppool_occ < warppool_occ) { + max_warppool_occ = warppool_occ; + } + warppool_occ_histo[warppool_occ] += 1; +} + +void dwf_hw_sche_class::init_cta(int start_thread, int cta_size, address_type start_pc) +{ + assert((start_thread % simd_width) == 0); // thread id starting at a warp + + int n_warp_2assign = cta_size / simd_width; + n_warp_2assign += (cta_size % simd_width)? 1 : 0; // round up + + static int *thd_id = NULL; + if (thd_id == NULL) thd_id = new int[simd_width]; + + for (int w = 0; w < n_warp_2assign; w++) { + // generate the warp update register for each warp + fill_n(thd_id, simd_width, -1); + int warp_start_tid = start_thread + w * simd_width; + for (int i = 0; (i < simd_width) && (warp_start_tid + i) < (start_thread + cta_size); i++) { + thd_id[i] = warp_start_tid + i; + } + + // push these warps into DWF scheduler + update_warp( thd_id, start_pc ); + } +} + +void dwf_hw_sche_class::print_free_warp_q( FILE *fout ) +{ + fprintf(fout, "free_node_q (%zd)= ", free_warp_q.size() ); + deque<int>::iterator dit = free_warp_q.begin(); + for (; dit != free_warp_q.end(); dit++) { + fprintf(fout, "%03d ", *dit); + } + fprintf(fout, "\n"); +} + +void print_warp( FILE *fout, warp_entry_t warp_e, int simd_width ) +{ + fprintf(fout, "\t%02d 0x%08x: (", warp_e.pdom_prio, warp_e.pc ); + for (int i=0;i<simd_width;i++) { + fprintf(fout, "%03d ", warp_e.tid[i]); + } + fprintf(fout, ")\n"); +} + +void dwf_hw_sche_class::print_warp_pool( FILE *fout ) +{ + for (unsigned i=0; i< warp_pool.size(); i++) { + if (warp_pool[i].pc != (address_type)-1) { + fprintf(fout, "bp%03d:", i); + print_warp(fout, warp_pool[i], simd_width); + } + } +} + +void dwf_hw_sche_class::clear_policy_access( ) { + if (heuristic == MAJORITY_MAXHEAP) { + ((issue_warp_majority_heap*)issue_warp_MAJ)->clear_access( ); + } +} + +void dwf_hw_sche_class::reset_policy_access( ) { + if (heuristic == MAJORITY_MAXHEAP) { + ((issue_warp_majority_heap*)issue_warp_MAJ)->reset_access( ); + } +} + +/////////////////////////////////////////////////////////////////////////// +// c-wrapper interface +/////////////////////////////////////////////////////////////////////////// + +int dwf_hw_n_sche = 0; +dwf_hw_sche_class **dwf_hw_sche; +unsigned *acc_dyn_pcs = NULL; + +void create_dwf_schedulers( int n_shaders, + int lut_size, int lut_assoc, + int simd_width, int regf_width, + int n_threads, int insn_size, + int heuristic, + char *policy_opt ) +{ + dwf_hw_n_sche = n_shaders; + dwf_hw_sche = new dwf_hw_sche_class*[n_shaders]; + for (int i=0; i<n_shaders; i++) { + dwf_hw_sche[i] = new dwf_hw_sche_class( lut_size, lut_assoc, + simd_width, regf_width, + n_threads, insn_size, + heuristic, i, + policy_opt ); + } + + if (acc_dyn_pcs == NULL) { + acc_dyn_pcs = new unsigned[n_shaders]; + std::fill_n(acc_dyn_pcs, n_shaders, 0); + } + for (int i=0; i<n_shaders; i++) { + dwf_hw_sche[i]->npc_tracker.acc_pc_count = &acc_dyn_pcs[i]; + } +} + +int dwf_update_warp( int shd_id, int* tid, address_type pc ) +{ + return dwf_hw_sche[shd_id]->update_warp( tid, pc ); +} + +int dwf_update_warp_at_barrier( int shd_id, int* tid, address_type pc, int cta_id ) +{ + return dwf_hw_sche[shd_id]->update_warp_at_barrier( tid, pc, cta_id); +} + +void dwf_hit_barrier( int shd_id, int cta_id ) +{ + dwf_hw_sche[shd_id]->hit_barrier( cta_id ); +} + +void dwf_release_barrier( int shd_id, int cta_id ) +{ + dwf_hw_sche[shd_id]->release_barrier( cta_id ); +} + +void dwf_issue_warp( int shd_id, int *tid, address_type *pc ) +{ + dwf_hw_sche[shd_id]->issue_warp( tid, pc ); +} + +void dwf_clear_accessed( int shd_id ) +{ + dwf_hw_sche[shd_id]->clear_accessed( ); +} + +void dwf_clear_policy_access( int shd_id ) +{ + dwf_hw_sche[shd_id]->clear_policy_access( ); +} + +void dwf_reset_policy_access( int shd_id ) +{ + dwf_hw_sche[shd_id]->reset_policy_access( ); +} + +void dwf_init_CTA(int shd_id, int start_thread, int cta_size, address_type start_pc) +{ + dwf_hw_sche[shd_id]->init_cta(start_thread, cta_size, start_pc); + dwf_hw_sche[shd_id]->clear_accessed( ); + dwf_hw_sche[shd_id]->clear_policy_access( ); +} + +void dwf_print_stat( FILE* fout ) +{ + dwf_hw_sche_class::print_stats( fout ); + npc_tracker_class::histo_print( fout ); + fprintf(fout, "max_warppool_occ = "); + for (int i=0; i<dwf_hw_n_sche; i++) { + fprintf(fout, "%d ", dwf_hw_sche[i]->max_warppool_occ); + } + fprintf(fout, "\n"); + for (int i=0; i<dwf_hw_n_sche; i++) { + fprintf(fout, "warppool_occ[%d] = ", i); + for (int j=0; j<dwf_hw_sche[i]->max_warppool_occ; j++) { + fprintf(fout, "%d ", dwf_hw_sche[i]->warppool_occ_histo[j]); + } + fprintf(fout, "\n"); + } + if (dwf_hw_sche[0]->heuristic == MAJORITY_MAXHEAP) { + fprintf(fout, "n_stall_on_maxheap = "); + for (int i=0; i<dwf_hw_n_sche; i++) { + fprintf(fout, "%d ", + ((issue_warp_majority_heap*)dwf_hw_sche[i]->issue_warp_MAJ)->n_stall_on_maxheap); + } + fprintf(fout, "\n"); + fprintf(fout, "maxheap_n_entries = "); + for (int i=0; i<dwf_hw_n_sche; i++) { + fprintf(fout, "%d ", + ((issue_warp_majority_heap*)dwf_hw_sche[i]->issue_warp_MAJ)->maxheap.max_n_entries); + } + fprintf(fout, "\n"); + fprintf(fout, "maxheap_lut_n_aliased = "); + for (int i=0; i<dwf_hw_n_sche; i++) { + fprintf(fout, "%d ", + ((issue_warp_majority_heap*)dwf_hw_sche[i]->issue_warp_MAJ)->mh_lut.n_aliased); + } + fprintf(fout, "\n"); + issue_warp_majority_heap::print_stat(fout); + } +} + +void dwf_reset_reconv_pt() +{ + issue_warp_pdom_prio::reconvgence_pt.clear(); +} + +void dwf_insert_reconv_pt(address_type pc) +{ + issue_warp_pdom_prio::reconvgence_pt.insert(pc); +} + +void dwf_reinit_schedulers( int n_shaders ) +{ + for (int i=0; i<n_shaders; i++) { + dwf_hw_sche[i]->issue_warp_pdom.reinit(); + } +} + +void dwf_update_statistics( int shader_id ) +{ + dwf_hw_sche[shader_id]->npc_tracker.update_acc_count(); +} + +void g_print_dmaj_scheduler(int sid) { + dwf_hw_sche[sid]->issue_warp_MAJ->print(stdout); +} + +void g_print_warp_lut(int sid) { + dwf_hw_sche[sid]->warp_lut_pc->print(stdout); +} + +void g_print_free_warp_q(int sid) { + dwf_hw_sche[sid]->print_free_warp_q(stdout); +} + +void g_print_warp_pool(int sid) { + dwf_hw_sche[sid]->print_warp_pool(stdout); +} + +void g_print_max_heap(int sid) { + dwf_hw_sche[sid]->issue_warp_MAJ->print(stdout); +} + +#ifdef UNIT_TEST + + #undef UNIT_TEST + #include "stat-tool.cc" + +unsigned gpgpu_thread_swizzling = 0; +unsigned long long gpu_sim_cycle = 0; + +int regfile_hash(signed istream_number, unsigned simd_size, unsigned n_banks) { + if (gpgpu_thread_swizzling) { + signed warp_ID = istream_number / simd_size; + return((istream_number + warp_ID) % n_banks); + } else { + return(istream_number % n_banks); + } +} + +int log2i(int n) { + int lg; + lg = -1; + while (n) { + n>>=1;lg++; + } + return lg; +} + +int test_FIFO() +{ + dwf_hw_sche_class *dwf_sche; + int i; + int tid[6][4] = { + { 0, 1, 2, 3}, + { 4, 5, 6, 7}, + { 8,-1,10,-1}, + {-1, 1,-1, 3}, + { 4, 9,-1,11}, + {-1,13,14,-1} + }; + + int expect_out[12][4] = { + { 0, 1, 2, 3}, + { 0, 1, 2, 3}, + { 0, 1, 2, 3}, + { 4, 5, 6, 7}, + { 8, 1,10, 3}, + { 4, 9,14,11}, + {-1,13,-1,-1}, + { 4, 9,-1,11}, + {-1,13,14,-1}, + { 8,-1,10,-1}, + { 4, 9,14,11}, + { 8,13,10,-1} + }; + + int tid_out[4]; + address_type pc_out; + + dwf_sche = new dwf_hw_sche_class(16, 2, 4, 4, 16, 1, FIFO); + + // same threads - different pc + dwf_sche->clear_accessed(); + dwf_sche->update_warp(tid[0], 0x409a80); + dwf_sche->update_warp(tid[0], 0x409a88); + + // different threads - different pc + dwf_sche->clear_accessed(); + dwf_sche->update_warp(tid[0], 0x409a90); + dwf_sche->update_warp(tid[1], 0x409a80); + + // different threads - same pc + // expect two warp to merge into one as there is no lane conflict + dwf_sche->clear_accessed(); + dwf_sche->update_warp(tid[2], 0x409a90); + dwf_sche->clear_accessed(); + dwf_sche->update_warp(tid[3], 0x409a90); + + // same as above, but with lane conflict + // expect a new warp allocated, + // but only the conflicting threads goes to new warp + dwf_sche->clear_accessed(); + dwf_sche->update_warp(tid[4], 0x409a80); + dwf_sche->clear_accessed(); + dwf_sche->update_warp(tid[5], 0x409a80); + + // different threads - different pc + // purposely try to alias an existing mapping + dwf_sche->clear_accessed(); + dwf_sche->update_warp(tid[4], 0x410a80); + dwf_sche->update_warp(tid[5], 0x411a80); + + // going back to that mapping + // a new warp should be allocated (despite lack of conflict) + dwf_sche->clear_accessed(); + dwf_sche->update_warp(tid[2], 0x409a80); + + // testing the occupancy vector + dwf_sche->clear_accessed(); + dwf_sche->update_warp(tid[4], 0x409aa0); + dwf_sche->clear_accessed(); + dwf_sche->update_warp(tid[5], 0x409aa0); + dwf_sche->clear_accessed(); + dwf_sche->update_warp(tid[2], 0x409aa0); + + // fill the warp pool up + for (i=12; i<64; ) { + dwf_sche->clear_accessed(); + dwf_sche->update_warp(tid[1], 0x409a80 + 8 * i++); + dwf_sche->update_warp(tid[4], 0x409a80 + 8 * i++); + } + // issue all the warp (do some auto checking on the way) + for (i=0; i<64; i++) { + dwf_sche->issue_warp(tid_out, &pc_out); + printf("0x%08x [%d %d %d %d]\n", pc_out, tid_out[0], tid_out[1], tid_out[2], tid_out[3]); + if (i<12) { + if ( memcmp(tid_out, expect_out[i], 4*sizeof(int) ) ) { + printf("%d warp mismatches\n", i); + assert(0); + } + } + } + + // now that all warpes are issue, no entries in the lut is valid + // updating warp with an old address that remains in the lut + // to see if detects the invalid lut entry + dwf_sche->clear_accessed(); + dwf_sche->update_warp(tid[2], 0x409a80 + 8 * 63); + dwf_sche->update_warp(tid[3], 0x409a80 + 8 * 62); + dwf_sche->issue_warp(tid_out, &pc_out); + assert(!memcmp(tid_out, tid[2], 4*sizeof(int) )); + dwf_sche->issue_warp(tid_out, &pc_out); + assert(!memcmp(tid_out, tid[3], 4*sizeof(int) )); + + dwf_sche->print_warp_pool(stdout); + dwf_sche->warp_lut_pc->print(stdout); + dwf_hw_sche_class::print_stats(stdout); + + delete dwf_sche; + + return 0; +} + +int test_PC () +{ + dwf_hw_sche_class *dwf_sche; + int i; + int tid[4][4] = { + { 0, 1, 2, 3}, + { 4, 5, 6, 7}, + { 8,-1,10,-1}, + {-1,13,14,-1} + }; + + int tid_out[4]; + address_type pc_out; + + dwf_sche = new dwf_hw_sche_class(16, 2, 4, 4, 16, 1, PC); + + // fill the warp pool up in reverse PC order + for (i=0; i<4; i++) { + for (int j=0; j<4; j++) { + dwf_sche->clear_accessed(); + dwf_sche->update_warp(tid[j], 0x409a80 - 8 * i); + } + } + + // issue the warps, expect them to be in PC order, with higher occ warp issued first + printf("PC Issue Logic:\n"); + for (i=0; i<4; i++) { + for (int j=0; j<4; j++) { + dwf_sche->issue_warp(tid_out, &pc_out); + printf("0x%08x [%d %d %d %d]\n", pc_out, tid_out[0], tid_out[1], tid_out[2], tid_out[3]); + } + } + +} + +int test_MAJ () +{ + dwf_hw_sche_class *dwf_sche; + int i; + int tid[4][4] = { + { 0, 1, 2, 3}, + { 4, 5, 6, 7}, + { 8,-1,10,-1}, + {-1,13,14,-1} + }; + + int tid_out[4]; + address_type pc_out; + + dwf_sche = new dwf_hw_sche_class(16, 2, 4, 4, 16, 1, MAJORITY); + + // fill the warp pool up in reverse PC order + for (i=0; i<4; i++) { + for (int j=0; j<(4-i); j++) { + dwf_sche->clear_accessed(); + dwf_sche->update_warp(tid[j], 0x409a80 - 8 * i); + } + } + + // issue the warps, expect them to be in PC order, with higher occ warp issued first + printf("Majority Issue Logic:\n"); + for (i=0; i<4; i++) { + for (int j=0; j<4; j++) { + dwf_sche->issue_warp(tid_out, &pc_out); + printf("0x%08x [%d %d %d %d]\n", pc_out, tid_out[0], tid_out[1], tid_out[2], tid_out[3]); + } + } +} + +int test_MAJ_HEAP () +{ + printf("\ntest_MAJ_HEAP:\n"); + dwf_hw_sche_class *dwf_sche; + int i; + int tid[4][4] = { + { 0, 1, 2, 3}, + { 4, 5, 6, 7}, + { 8,-1,10,-1}, + {-1,13,14,-1} + }; + + int tid_out[4]; + address_type pc_out; + + dwf_sche = new dwf_hw_sche_class(16, 2, 4, 4, 16, 1, MAJORITY_MAXHEAP); + + // fill the warp pool up in reverse PC order + for (i=0; i<4; i++) { + for (int j=0; j<(i+1); j++) { + dwf_sche->clear_accessed(); + dwf_sche->update_warp(tid[j], 0x409a80 + 8 * i); + } + } + + dwf_sche->reset_policy_access(); + dwf_sche->issue_warp_MAJ->print(stdout); + + // issue the warps, expect them to be in PC order, with higher occ warp issued first + printf("Majority (Max Heap) Issue Logic:\n"); + for (i=0; i<4; i++) { + for (int j=0; j<4; j++) { + dwf_sche->issue_warp(tid_out, &pc_out); + printf("0x%08x [%d %d %d %d]\n", pc_out, tid_out[0], tid_out[1], tid_out[2], tid_out[3]); + } + dwf_sche->reset_policy_access(); + } +} + +void test_warp_lut_pc () +{ + printf("\ntest_warp_lut_pc:\n"); + warp_lut_sa<pc_tag> warp_lut_pc(16, // size + 4, // assoc + 1); // insn_size + + address_type pc_value[] = {0, 4, 0, 8, 12, 16, 20, 8, 8, 0}; + int n_entry = sizeof(pc_value) / sizeof(address_type); + vector<pc_tag> pc_stream(pc_value, pc_value + n_entry); + + int misses = 0; + for (int n = 0; n < n_entry * 100; n++) { + int i = n % n_entry; + tag2warp_entry_t<pc_tag> *lut_entry = NULL; + bool lut_miss = false; + + lut_entry = warp_lut_pc.lookup_pc2warp(pc_stream[i], lut_miss); + + if (lut_entry->tag != pc_stream[i]) { + lut_entry->tag = pc_stream[i]; + lut_entry->occ = 1; + misses += 1; + } + warp_lut_pc.clear_accessed(); + lut_entry->accessed = 0; + } + + printf("Number of Miss = %d\n", misses); +} + +int main () { + //test_FIFO(); + //test_PC(); + //test_MAJ(); + test_MAJ_HEAP(); + test_warp_lut_pc(); + return 0; +} + +#endif diff --git a/src/gpgpu-sim/dwf.h b/src/gpgpu-sim/dwf.h new file mode 100644 index 0000000..6328f1a --- /dev/null +++ b/src/gpgpu-sim/dwf.h @@ -0,0 +1,118 @@ +/* + * dwf.h + * + * Copyright (c) 2009 by Tor M. Aamodt, Wilson W. L. Fung, and the + * University of British Columbia + * Vancouver, BC V6T 1Z4 + * All Rights Reserved. + * + * THIS IS A LEGAL DOCUMENT BY DOWNLOADING GPGPU-SIM, YOU ARE AGREEING TO THESE + * TERMS AND CONDITIONS. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * NOTE: The files libcuda/cuda_runtime_api.c and src/cuda-sim/cuda-math.h + * are derived from the CUDA Toolset available from http://www.nvidia.com/cuda + * (property of NVIDIA). The files benchmarks/BlackScholes/ and + * benchmarks/template/ are derived from the CUDA SDK available from + * http://www.nvidia.com/cuda (also property of NVIDIA). The files from + * src/intersim/ are derived from Booksim (a simulator provided with the + * textbook "Principles and Practices of Interconnection Networks" available + * from http://cva.stanford.edu/books/ppin/). As such, those files are bound by + * the corresponding legal terms and conditions set forth separately (original + * copyright notices are left in files from these sources and where we have + * modified a file our copyright notice appears before the original copyright + * notice). + * + * Using this version of GPGPU-Sim requires a complete installation of CUDA + * which is distributed seperately by NVIDIA under separate terms and + * conditions. To use this version of GPGPU-Sim with OpenCL requires a + * recent version of NVIDIA's drivers which support OpenCL. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the University of British Columbia nor the names of + * its contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * 4. This version of GPGPU-SIM is distributed freely for non-commercial use only. + * + * 5. No nonprofit user may place any restrictions on the use of this software, + * including as modified by the user, by any other authorized user. + * + * 6. GPGPU-SIM was developed primarily by Tor M. Aamodt, Wilson W. L. Fung, + * Ali Bakhoda, George L. Yuan, at the University of British Columbia, + * Vancouver, BC V6T 1Z4 + */ + +#ifndef dwf_h_INCLUDED +#define dwf_h_INCLUDED + +#ifdef __cplusplus + + #include <cstdio> + #include <cstdlib> + #include <cassert> + #include <vector> + #include <queue> + #include <list> + #include <algorithm> + +#endif + +#include "../util.h" + +extern unsigned *acc_dyn_pcs; + +void create_dwf_schedulers( int n_shaders, + int lut_size, int lut_assoc, + int simd_width, int regf_width, + int n_threads, int insn_size, + int heuristic, + char *policy_opt ); + +int dwf_update_warp( int shd_id, int* tid, address_type pc ); + +void dwf_issue_warp( int shd_id, int *tid, address_type *pc ); + +void dwf_clear_accessed( int shd_id ); + +void dwf_clear_policy_access( int shd_id ); +void dwf_reset_policy_access( int shd_id ); + +int dwf_update_warp_at_barrier( int shd_id, int* tid, address_type pc, int cta_id ); +void dwf_hit_barrier( int shd_id, int cta_id ); +void dwf_release_barrier( int shd_id, int cta_id ); + +void dwf_init_CTA(int shd_id, int start_thread, int cta_size, address_type start_pc); + +void dwf_print_stat( FILE* fout ); + +void dwf_reset_reconv_pt(); +void dwf_insert_reconv_pt(address_type pc); + +void dwf_reinit_schedulers( int n_shaders ); + +void dwf_set_accPC( int n_shaders, unsigned *acc_pc_count ); + +void dwf_update_statistics( int shader_id ); + +#endif diff --git a/src/gpgpu-sim/gpu-cache.cc b/src/gpgpu-sim/gpu-cache.cc new file mode 100644 index 0000000..679d1d2 --- /dev/null +++ b/src/gpgpu-sim/gpu-cache.cc @@ -0,0 +1,609 @@ +/* + * gpu-cache.c + * + * Copyright (c) 2009 by Tor M. Aamodt, Wilson W. L. Fung, Ali Bakhoda, + * George L. Yuan and the + * University of British Columbia + * Vancouver, BC V6T 1Z4 + * All Rights Reserved. + * + * THIS IS A LEGAL DOCUMENT BY DOWNLOADING GPGPU-SIM, YOU ARE AGREEING TO THESE + * TERMS AND CONDITIONS. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * NOTE: The files libcuda/cuda_runtime_api.c and src/cuda-sim/cuda-math.h + * are derived from the CUDA Toolset available from http://www.nvidia.com/cuda + * (property of NVIDIA). The files benchmarks/BlackScholes/ and + * benchmarks/template/ are derived from the CUDA SDK available from + * http://www.nvidia.com/cuda (also property of NVIDIA). The files from + * src/intersim/ are derived from Booksim (a simulator provided with the + * textbook "Principles and Practices of Interconnection Networks" available + * from http://cva.stanford.edu/books/ppin/). As such, those files are bound by + * the corresponding legal terms and conditions set forth separately (original + * copyright notices are left in files from these sources and where we have + * modified a file our copyright notice appears before the original copyright + * notice). + * + * Using this version of GPGPU-Sim requires a complete installation of CUDA + * which is distributed seperately by NVIDIA under separate terms and + * conditions. To use this version of GPGPU-Sim with OpenCL requires a + * recent version of NVIDIA's drivers which support OpenCL. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the University of British Columbia nor the names of + * its contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * 4. This version of GPGPU-SIM is distributed freely for non-commercial use only. + * + * 5. No nonprofit user may place any restrictions on the use of this software, + * including as modified by the user, by any other authorized user. + * + * 6. GPGPU-SIM was developed primarily by Tor M. Aamodt, Wilson W. L. Fung, + * Ali Bakhoda, George L. Yuan, at the University of British Columbia, + * Vancouver, BC V6T 1Z4 + */ + +#include "gpu-cache.h" +#include "gpu-misc.h" +#include "addrdec.h" +#include <assert.h> +#include <string.h> + +// both shd_cache_access and shd_cache_probe functions use +// shd_cache_access_internal to access/probe cache +shd_cache_line_t* shd_cache_access_internal( shd_cache_t *cp, + unsigned long long int addr, + unsigned int nbytes, + unsigned char write, + unsigned int sim_cycle, + unsigned int real_access); + +shd_cache_t * shd_cache_create( char *name, + unsigned int nset, + unsigned int assoc, + unsigned int line_sz, + unsigned char policy, + unsigned int hit_latency, + unsigned long long int bank_mask, + enum cache_write_policy wp) { + + shd_cache_t *cp; + unsigned int nlines; + + unsigned int i; + + if (!nset || !assoc) { + printf("Creating non-existing cache!\n"); + return 0; + } + + nlines = nset * assoc; + cp = (shd_cache_t*) calloc(1, sizeof(shd_cache_t)); + + cp->bank_mask = bank_mask; + cp->name = (char*) malloc(sizeof(char) * (strlen(name) + 1)); + strcpy(cp->name, name); + cp->nset = nset; + cp->nset_log2 = LOGB2(nset); + cp->assoc = assoc; + cp->line_sz = line_sz; + cp->line_sz_log2 = LOGB2(line_sz); + cp->policy = policy; + cp->hit_latency = hit_latency; + cp->lines = (shd_cache_line_t*) calloc(nlines, sizeof(shd_cache_line_t)); + cp->write_policy = wp; + + for (i=0; i<nlines; i++) { + cp->lines[i].line_sz = line_sz; + cp->lines[i].status = 0; + } + + // don't hook up with any logger + cp->core_id = -1; + cp->type_id = -1; + + // initialize snapshot counters for visualizer + cp->prev_snapshot_access = 0; + cp->prev_snapshot_miss = 0; + cp->prev_snapshot_merge_hit = 0; + +// printf("%s: %d(%x) x %d x %d(%x) %c, %d\n", +// cp->name, cp->nset, cp->nset_log2, cp->assoc, cp->line_sz, +// cp->line_sz_log2, cp->policy, nlines); + + return cp; +} + +void shd_cache_destroy( shd_cache_t* cp ) { + + free(cp->lines); + free(cp); +} + +extern void shader_cache_miss_log( int logger_id, int type ); +// hook up with shader core logger +void shd_cache_bind_logger(shd_cache_t* cp, int core_id, int type_id) { + cp->core_id = core_id; + cp->type_id = type_id; +} + +extern unsigned long long int addrdec_packbits(unsigned long long int mask, + unsigned long long int val, + unsigned char high, unsigned char low); +extern void shader_cache_access_log( int logger_id, int type, int miss); +extern void shader_cache_access_unlog( int logger_id, int type, int miss); + +shd_cache_line_t* shd_cache_access_internal( shd_cache_t *cp, + unsigned long long int addr, + unsigned int nbytes, + unsigned char write, + unsigned int sim_cycle, + unsigned int real_access) { + //if real_access==0 then its only a cache probe stats and LRU tags should not be updated + unsigned int i; + unsigned int set; + unsigned long long int tag; + unsigned long long int packed_addr; + shd_cache_line_t *pline; + + if (cp->bank_mask) + packed_addr = addrdec_packbits(cp->bank_mask, addr, 64, 0); + else + packed_addr = addr; + + set = (packed_addr >> cp->line_sz_log2) & ( (1<<cp->nset_log2) - 1 ); + tag = packed_addr >> (cp->line_sz_log2 + cp->nset_log2); + + if (real_access) { + cp->access++; + shader_cache_access_log(cp->core_id, cp->type_id, 0); + } + + for (i=0; i<cp->assoc; i++) { + pline = &(cp->lines[set*cp->assoc+i] ); + if (pline->status & VALID) { + if (pline->tag == tag) { + //printf("Cache Hit! Addr=%08x Set=%x Way=%x Tag=%x\n", packed_addr, set, i, tag); + if (real_access) { + pline->last_used = sim_cycle; + if (write) { + pline->status |= DIRTY; + } + } + return pline; + } + } + } + if (real_access) { + cp->miss++; + shader_cache_access_log(cp->core_id, cp->type_id, 1); + } + return 0; +} + +shd_cache_line_t* shd_cache_access( shd_cache_t *cp, + unsigned long long int addr, + unsigned int nbytes, + unsigned char write, + unsigned int sim_cycle ) +{ + return shd_cache_access_internal(cp,addr,nbytes,write,sim_cycle,1/*this is a real access*/); +} + +extern int gpgpu_cache_wt_through; +shd_cache_t *test = NULL; +enum cache_request_status shd_cache_access_wb( shd_cache_t *cp, + unsigned long long int addr, + unsigned int nbytes, + unsigned char write, + unsigned int sim_cycle, address_type *wb_address) +{ + unsigned int i; + unsigned int set; + unsigned long long int tag; + unsigned long long int packed_addr; + shd_cache_line_t *pline; + + unsigned already_reserved = 0; + unsigned all_reserved = 1; + shd_cache_line_t *free_line = NULL; + + if (cp->bank_mask) + packed_addr = addrdec_packbits(cp->bank_mask, addr, 64, 0); + else + packed_addr = addr; + + set = (packed_addr >> cp->line_sz_log2) & ( (1<<cp->nset_log2) - 1 ); + tag = packed_addr >> (cp->line_sz_log2 + cp->nset_log2); + + cp->access++; + shader_cache_access_log(cp->core_id, cp->type_id, 0); + + for (i=0; i<cp->assoc; i++) { + pline = &(cp->lines[set*cp->assoc+i] ); + if (pline->tag == tag) { + if (pline->status & RESERVED) { + already_reserved = 1; + break; + } else if (pline->status & VALID) { + //printf("Cache Hit! Addr=%08x Set=%x Way=%x Tag=%x\n", packed_addr, set, i, tag); + pline->last_used = sim_cycle; + if (write) { + pline->status |= DIRTY; + } + //return pline; + if (cp->write_policy == write_through) return HIT_W_WT; + return HIT; + } + } + if (!(pline->status & RESERVED)) { + all_reserved = 0; + if (!(pline->status & VALID)) { + free_line = pline; + } + } + } + cp->miss++; + shader_cache_access_log(cp->core_id, cp->type_id, 1); + + if (already_reserved || cp->write_policy != write_back || write) { + //not in cache yet, but no wb as place is reserved. + //or wt caches never nead to worry about it (as do no_write caches) + //or is a write + if (already_reserved && write) { + //write the data into the cache line, make it dirty + //up to mshrs to save write mask to not overwrite this data when the read returns + pline->status |= DIRTY; + return WB_HIT_ON_MISS; + } else if (already_reserved) { + return WB_HIT_ON_MISS; + } + return MISS_NO_WB; + } + + //if not in cache, and a write back cache, and not already allocated, need to allocate a place for this request + + if (all_reserved) { + //cannot service this request, because we can't garantee that we have room for the line when it comes back + return RESERVATION_FAIL; + } + + //printf("RESRV %d\n",tag); + + if (free_line) { + //reserve fo this request + free_line->status |= RESERVED; + free_line->tag = tag; + //no writeback + return MISS_NO_WB; + } + + // need to kick a line out to reserve a spot + shd_cache_line_t *rline = NULL; + + for (i=0; i<cp->assoc; i++) { + pline = &(cp->lines[set*cp->assoc+i] ); + if (pline->status & VALID && !(pline->status & RESERVED)) { + if (!rline) { + rline = pline; //select first available for ejection for later comparison + continue; + } + switch (cp->policy) { + case LRU: + if (pline->last_used < rline->last_used) + rline = pline; + break; + case FIFO: + if (pline->fetch_time < rline->fetch_time) + rline = pline; + break; + default: + rline = pline; //pick one, ie. the last valied one. + } + } + } + assert(rline); //ensure we actually found one. + + unsigned needs_wb = (rline->status & (DIRTY|VALID)) == (DIRTY|VALID); + /* Set the replaced cache line address */ + if (needs_wb) { + *wb_address = rline->addr; + } + + /* reserve this new line */ + rline->status |= RESERVED; + rline->status &= ~VALID; + rline->status &= ~DIRTY; + rline->tag = tag; + + /* printf("Fetching! Addr=%08x ReplAddr=%08x(%d) Set=%x Tag=%x\n", + packed_addr, repl_addr, nofreeslot, set, tag); + */ + if (needs_wb) { + return MISS_W_WB; + } else { + return MISS_NO_WB; + } +} + +shd_cache_line_t* shd_cache_probe( shd_cache_t *cp, + unsigned long long int addr) +{ + return shd_cache_access_internal(cp,addr, + 1,0,0, /*do not matter*/ + 0/*this is just a probe*/); +} + +void shd_cache_undo_stats( shd_cache_t *cp, int miss ) +{ + if (miss) { + cp->miss--; + shader_cache_access_unlog(cp->core_id, cp->type_id, 1); + } + cp->access--; + shader_cache_access_unlog(cp->core_id, cp->type_id, 0); +} + +// Obtain the windowed cache miss rate for visualizer +float shd_cache_windowed_cache_miss_rate( shd_cache_t *cp, int minus_merge_hit ) +{ + unsigned int n_access = cp->access - cp->prev_snapshot_access; + unsigned int n_miss = cp->miss - cp->prev_snapshot_miss; + unsigned int n_merge_hit = cp->merge_hit - cp->prev_snapshot_merge_hit; + + if (minus_merge_hit) { + n_miss -= n_merge_hit; + } + float missrate = 0.0f; + if (n_access != 0) { + missrate = (float) n_miss / n_access; + } + + return missrate; +} + +// start a new sampling window +void shd_cache_new_window( shd_cache_t *cp ) +{ + cp->prev_snapshot_access = cp->access; + cp->prev_snapshot_miss = cp->miss; + cp->prev_snapshot_merge_hit = cp->merge_hit; +} + +unsigned long long int L2_shd_cache_fill( shd_cache_t *cp, + unsigned long long int addr, + unsigned int sim_cycle ) { + unsigned long long int result = shd_cache_fill(cp, addr, sim_cycle); + return result; +} + +static unsigned int _n_line_existed = 0; // debug counter + +// Fetch requested data into cache line. +// Returning address on the replaced line if it is dirty, or -1 if it is clean +// Assume the line is filled all at once. +unsigned long long int shd_cache_fill( shd_cache_t *cp, + unsigned long long int addr, + unsigned int sim_cycle ) { + + unsigned int i; + unsigned int set; + unsigned long long int tag; + unsigned long long int packed_addr; + unsigned long long int repl_addr; + + unsigned char nofreeslot; + unsigned char line_exists; + unsigned int base = 0 ; + unsigned int maxway = cp->assoc ; + + shd_cache_line_t *pline, *cline; + + if (cp->bank_mask) + packed_addr = addrdec_packbits(cp->bank_mask, addr, 64, 0); + else + packed_addr = addr; + set = (packed_addr >> cp->line_sz_log2) & ( (1<<cp->nset_log2) - 1 ); + tag = packed_addr >> (cp->line_sz_log2 + cp->nset_log2); + + if (cp->write_policy == write_back) { + //this request must have a reserved spot + cline = NULL; + for (i=base; i<maxway; i++) { + pline = &(cp->lines[set*cp->assoc+i] ); + if ((pline->tag == tag) && (pline->status & RESERVED)) { + cline = pline; + break; + } + if ((pline->tag == tag) && (pline->status & VALID)) { + //A second fill has returned to a line in the cache + //discard it as line in cache may have been modified, or is the same + _n_line_existed++; + + return -1; + } + } + + //if (cline) printf("FOUND %d\n",tag); + //else printf("UNFOUND!!! %d\n", tag); + + if (!cline) printf("----!!! about to abort - this probably happened because global memory msrh merging is not enabled with a writeback cache !!!----\n"); + + assert(cline); //error if it doesn't have a reserved space + + /* Fetch data into block */ + cline->status &= ~RESERVED; + cline->status |= VALID; + //cline->status &= ~DIRTY; Don't clear dirty bit, as might be dirty from write. + cline->tag = tag; + cline->addr = addr; + cline->last_used = sim_cycle; + cline->fetch_time = sim_cycle; + + // no wb, already handled. + return -1; + } + + //behavior unchanged for write through cache... probably not all necessary. + + // Look for any free slots and the possibility that the line is in the cache already + nofreeslot = 1; + line_exists = 0; + for (i=base; i<maxway; i++) { + pline = &(cp->lines[set*cp->assoc+i] ); + if (!(pline->status & VALID)) { + cline = pline; + nofreeslot = 0; + break; + } else if (pline->tag == tag) { + cline = pline; + line_exists = 1; + break; + } + } + + if (line_exists) { + _n_line_existed += 1; + return -1; // don't need to spill any line, nor it needs to be filled + } + + if (nofreeslot) { + cline = &(cp->lines[set*cp->assoc+base] ); + for (i=1+base; i<maxway; i++) { + pline = &(cp->lines[set*cp->assoc+i] ); + if (pline->status & VALID) { + switch (cp->policy) { + case LRU: + if (pline->last_used < cline->last_used) + cline = pline; + break; + case FIFO: + if (pline->fetch_time < cline->fetch_time) + cline = pline; + break; + default: + break; + } + } + } + } + + /* Set the replaced cache line address */ + if ((cline->status & (DIRTY|VALID)) == (DIRTY|VALID)) { + repl_addr = cline->addr; + } else { + repl_addr = -1; + } + + /* Fetch data into block */ + cline->status |= VALID; + cline->status &= ~DIRTY; + cline->tag = tag; + cline->addr = addr; + cline->last_used = sim_cycle; + cline->fetch_time = sim_cycle; + +/* printf("Fetching! Addr=%08x ReplAddr=%08x(%d) Set=%x Tag=%x\n", + packed_addr, repl_addr, nofreeslot, set, tag); + */ + return repl_addr; +} + +void shd_cache_mergehit( shd_cache_t *cp, unsigned long long int addr ) +{ + cp->merge_hit += 1; +} + +void shd_cache_print( shd_cache_t *cp, FILE *stream) { + fprintf( stream, "Cache %s:\t", cp->name); + fprintf( stream, "Size = %d B (%d Set x %d-way x %d byte line)\n", + cp->line_sz * cp->nset * cp->assoc, + cp->nset, cp->assoc, cp->line_sz ); + fprintf( stream, "\t\tAccess = %d, Miss = %d (%.3g), -MgHts = %d (%.3g)\n", + cp->access, cp->miss, (float) cp->miss / cp->access, + cp->miss - cp->merge_hit, (float) (cp->miss - cp->merge_hit) / cp->access); +} + +#ifdef UNIT_TEST + +int main() { + shd_cache_t *cp[3]; + unsigned int addr, i; + unsigned int cachenum; + unsigned int sim_cycle; + + unsigned int test_addrs[8] = { 0x100, 0x200, 0x300, 0x400, + 0x104, 0x204, 0x500, 0x100}; + unsigned int repl_addr[8] = {0,0,0,0,0,0,0,0}; + unsigned int rdwr[8] = {0,1,0,0,0,0,0,0}; + + sim_cycle = 0; + cp[0] = shd_cache_create ("cp1", 16, 4, 16, LRU, 1); + cp[1] = shd_cache_create ("cp2", 16, 4, 16, FIFO, 1); + + for (cachenum = 0; cachenum<2; cachenum++) + for (i=0; i<8; i++) { + if ( !shd_cache_access(cp[cachenum], test_addrs[i], 4, rdwr[i], sim_cycle) ) { + repl_addr[i] = shd_cache_fill(cp[cachenum], test_addrs[i], sim_cycle); + shd_cache_access(cp[cachenum], test_addrs[i], 4, rdwr[i], sim_cycle); + } + sim_cycle++; + } + + printf("replaced address:"); + for (i=0; i<8; i++) { + printf("0x%x ", repl_addr[i]); + } + printf("\n"); + shd_cache_print(cp[0],stdout); + shd_cache_print(cp[1],stdout); + + shd_cache_fill(cp[0], 0x104b3ecb0, sim_cycle); + printf("Accessing 64-bit address tag: %d\n", + shd_cache_access(cp[0], 0x104b3ecb2, 4, 0, sim_cycle)); + printf("Accessing 64-bit address tag: %d\n", + shd_cache_access(cp[0], 0x103433330, 4, 0, sim_cycle)); + + + shd_set_coherency_policy(2); + cp[2] = shd_cache_create("cp2", 16, 4, 16, LRU, 1); + shd_cache_fill(cp[2], 0x12345000, 0); + shd_cache_access(cp[2], 0x12345000, 4, 1, 0); + shd_cache_access(cp[2], 0x12345004, 4, 0, 0); + shd_cache_access(cp[2], 0x12345008, 4, 0, 0); + shd_cache_access(cp[2], 0x1234500C, 4, 1, 0); + printf("Checking Dirty Vector %x, Result = %d (Expect %d)\n", 0xf, + shd_cache_linedirty(cp[2], 0x12345000, 0xf), 1 ); + printf("Checking Dirty Vector %x, Result = %d (Expect %d)\n", 0x6, + shd_cache_linedirty(cp[2], 0x12345000, 0x6), 0 ); + printf("Checking Dirty Vector %x, Result = %d (Expect %d)\n", 0x1, + shd_cache_linedirty(cp[2], 0x12345000, 0x1), 1 ); + printf("Checking Dirty Vector %x, Result = %d (Expect %d)\n", 0x8, + shd_cache_linedirty(cp[2], 0x12345000, 0x8), 1 ); + printf("Checking Dirty Vector %x, Result = %d (Expect %d)\n", 0x9, + shd_cache_linedirty(cp[2], 0x12345000, 0x9), 1 ); + +} + +#endif diff --git a/src/gpgpu-sim/gpu-cache.h b/src/gpgpu-sim/gpu-cache.h new file mode 100644 index 0000000..e82ad05 --- /dev/null +++ b/src/gpgpu-sim/gpu-cache.h @@ -0,0 +1,191 @@ +/* + * gpu-cache.c + * + * Copyright (c) 2009 by Tor M. Aamodt, Wilson W. L. Fung, Ali Bakhoda, + * George L. Yuan and the + * University of British Columbia + * Vancouver, BC V6T 1Z4 + * All Rights Reserved. + * + * THIS IS A LEGAL DOCUMENT BY DOWNLOADING GPGPU-SIM, YOU ARE AGREEING TO THESE + * TERMS AND CONDITIONS. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * NOTE: The files libcuda/cuda_runtime_api.c and src/cuda-sim/cuda-math.h + * are derived from the CUDA Toolset available from http://www.nvidia.com/cuda + * (property of NVIDIA). The files benchmarks/BlackScholes/ and + * benchmarks/template/ are derived from the CUDA SDK available from + * http://www.nvidia.com/cuda (also property of NVIDIA). The files from + * src/intersim/ are derived from Booksim (a simulator provided with the + * textbook "Principles and Practices of Interconnection Networks" available + * from http://cva.stanford.edu/books/ppin/). As such, those files are bound by + * the corresponding legal terms and conditions set forth separately (original + * copyright notices are left in files from these sources and where we have + * modified a file our copyright notice appears before the original copyright + * notice). + * + * Using this version of GPGPU-Sim requires a complete installation of CUDA + * which is distributed seperately by NVIDIA under separate terms and + * conditions. To use this version of GPGPU-Sim with OpenCL requires a + * recent version of NVIDIA's drivers which support OpenCL. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the University of British Columbia nor the names of + * its contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * 4. This version of GPGPU-SIM is distributed freely for non-commercial use only. + * + * 5. No nonprofit user may place any restrictions on the use of this software, + * including as modified by the user, by any other authorized user. + * + * 6. GPGPU-SIM was developed primarily by Tor M. Aamodt, Wilson W. L. Fung, + * Ali Bakhoda, George L. Yuan, at the University of British Columbia, + * Vancouver, BC V6T 1Z4 + */ + +#include <stdio.h> +#include <stdlib.h> +#include "../util.h" + +#ifndef GPU_CACHE_H +#define GPU_CACHE_H + +#define VALID 0x01 +#define DIRTY 0x02 +#define RESERVED 0x04 + +enum cache_request_status { + HIT, + HIT_W_WT, /* Hit, but write through cache, still needs to send to memory */ + MISS_NO_WB, /* miss, but witeback not necessary*/ + MISS_W_WB, /* miss, must do writeback */ + WB_HIT_ON_MISS, /* request hit on a reservation in wb cache*/ + RESERVATION_FAIL, + NUM_CACHE_REQUEST_STATUS +}; + + +typedef struct { + unsigned long long int tag; + unsigned long long int addr; + unsigned int set; + unsigned int line_sz; /* bytes */ + unsigned int fetch_time; + unsigned int last_used; + unsigned char status; /* valid, dirty... etc */ +} shd_cache_line_t; + + +#define LRU 'L' +#define FIFO 'F' +#define RANDOM 'R' + +enum cache_write_policy{ + no_writes, //line replacement when new line arrives + write_back, //line replacement when new line arrives + write_through //reservation based, use much handle reservation full error. +}; + +typedef struct { + + char *name; + + shd_cache_line_t *lines; /* nset x assoc lines in total */ + unsigned int nset; + unsigned int nset_log2; + unsigned int assoc; + unsigned int line_sz; // bytes + unsigned int line_sz_log2; + enum cache_write_policy write_policy; + unsigned char policy; + unsigned int hit_latency; + + unsigned int access; + unsigned int miss; + unsigned int merge_hit; // number of cache miss that hit the same line (and merged as a result) + + // performance counters for calculating the amount of misses within a time window + unsigned int prev_snapshot_access; + unsigned int prev_snapshot_miss; + unsigned int prev_snapshot_merge_hit; + + int core_id; // which shader core is using this + int type_id; // what kind of cache is this (normal, texture, constant) + + unsigned long long int bank_mask; + +} shd_cache_t; + +shd_cache_t * shd_cache_create( char *name, + unsigned int nset, + unsigned int assoc, + unsigned int line_sz, + unsigned char policy, + unsigned int hit_latency, + unsigned long long int bank_mask, + enum cache_write_policy wp); + +void shd_cache_destroy( shd_cache_t* cp ); + +// hook up with shader core logger +void shd_cache_bind_logger(shd_cache_t* cp, int core_id, int type_id); + +//depercated, use _wb +shd_cache_line_t* shd_cache_access( shd_cache_t *cp, + unsigned long long int addr, + unsigned int nbytes, + unsigned char write, + unsigned int sim_cycle ); + +//cache check checks for wb and forwards information over. +enum cache_request_status shd_cache_access_wb( shd_cache_t *cp, + unsigned long long int addr, + unsigned int nbytes, + unsigned char write, + unsigned int sim_cycle, + address_type *wb_address); + + +//just probe the tag array to see if addr is in the cache or not +//does not update LRU or stats... +shd_cache_line_t* shd_cache_probe( shd_cache_t *cp, + unsigned long long int addr); + +// undo the statistic record when the memory access is stalled/squashed and will try again next cycle +void shd_cache_undo_stats( shd_cache_t *cp, int miss ); + +void shd_cache_mergehit( shd_cache_t *cp, unsigned long long int addr ); + +unsigned long long int shd_cache_fill( shd_cache_t *cp, + unsigned long long int addr, + unsigned int sim_cycle ); + +unsigned long long int L2_shd_cache_fill( shd_cache_t *cp, + unsigned long long int addr, + unsigned int sim_cycle ); + +void shd_cache_print( shd_cache_t *cp, FILE *stream); + + +#endif diff --git a/src/gpgpu-sim/gpu-misc.cc b/src/gpgpu-sim/gpu-misc.cc new file mode 100644 index 0000000..06a0f68 --- /dev/null +++ b/src/gpgpu-sim/gpu-misc.cc @@ -0,0 +1,96 @@ +/* + * gpu-misc.c + * + * Copyright (c) 2009 by Tor M. Aamodt, Wilson W. L. Fung, Ali Bakhoda, + * George L. Yuan and the + * University of British Columbia + * Vancouver, BC V6T 1Z4 + * All Rights Reserved. + * + * THIS IS A LEGAL DOCUMENT BY DOWNLOADING GPGPU-SIM, YOU ARE AGREEING TO THESE + * TERMS AND CONDITIONS. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * NOTE: The files libcuda/cuda_runtime_api.c and src/cuda-sim/cuda-math.h + * are derived from the CUDA Toolset available from http://www.nvidia.com/cuda + * (property of NVIDIA). The files benchmarks/BlackScholes/ and + * benchmarks/template/ are derived from the CUDA SDK available from + * http://www.nvidia.com/cuda (also property of NVIDIA). The files from + * src/intersim/ are derived from Booksim (a simulator provided with the + * textbook "Principles and Practices of Interconnection Networks" available + * from http://cva.stanford.edu/books/ppin/). As such, those files are bound by + * the corresponding legal terms and conditions set forth separately (original + * copyright notices are left in files from these sources and where we have + * modified a file our copyright notice appears before the original copyright + * notice). + * + * Using this version of GPGPU-Sim requires a complete installation of CUDA + * which is distributed seperately by NVIDIA under separate terms and + * conditions. To use this version of GPGPU-Sim with OpenCL requires a + * recent version of NVIDIA's drivers which support OpenCL. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the University of British Columbia nor the names of + * its contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * 4. This version of GPGPU-SIM is distributed freely for non-commercial use only. + * + * 5. No nonprofit user may place any restrictions on the use of this software, + * including as modified by the user, by any other authorized user. + * + * 6. GPGPU-SIM was developed primarily by Tor M. Aamodt, Wilson W. L. Fung, + * Ali Bakhoda, George L. Yuan, at the University of British Columbia, + * Vancouver, BC V6T 1Z4 + */ + +#include "gpu-misc.h" + +unsigned int LOGB2( unsigned int v ) { + unsigned int shift; + unsigned int r; + + r = 0; + + shift = (( v & 0xFFFF0000) != 0 ) << 4; v >>= shift; r |= shift; + shift = (( v & 0xFF00 ) != 0 ) << 3; v >>= shift; r |= shift; + shift = (( v & 0xF0 ) != 0 ) << 2; v >>= shift; r |= shift; + shift = (( v & 0xC ) != 0 ) << 1; v >>= shift; r |= shift; + shift = (( v & 0x2 ) != 0 ) << 0; v >>= shift; r |= shift; + + return r; +} + +unsigned int MAX2NUM( unsigned int a, unsigned int b ) { + if (a > b) { + return a; + } else + return b; +} + +unsigned int MIN2NUM( unsigned int a, unsigned int b ) { + if (a < b) { + return a; + } else + return b; +} diff --git a/src/gpgpu-sim/gpu-misc.h b/src/gpgpu-sim/gpu-misc.h new file mode 100644 index 0000000..3d07d77 --- /dev/null +++ b/src/gpgpu-sim/gpu-misc.h @@ -0,0 +1,106 @@ +/* + * gpu-misc.h + * + * Copyright (c) 2009 by Tor M. Aamodt, Wilson W. L. Fung, Ali Bakhoda, + * George L. Yuan and the + * University of British Columbia + * Vancouver, BC V6T 1Z4 + * All Rights Reserved. + * + * THIS IS A LEGAL DOCUMENT BY DOWNLOADING GPGPU-SIM, YOU ARE AGREEING TO THESE + * TERMS AND CONDITIONS. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * NOTE: The files libcuda/cuda_runtime_api.c and src/cuda-sim/cuda-math.h + * are derived from the CUDA Toolset available from http://www.nvidia.com/cuda + * (property of NVIDIA). The files benchmarks/BlackScholes/ and + * benchmarks/template/ are derived from the CUDA SDK available from + * http://www.nvidia.com/cuda (also property of NVIDIA). The files from + * src/intersim/ are derived from Booksim (a simulator provided with the + * textbook "Principles and Practices of Interconnection Networks" available + * from http://cva.stanford.edu/books/ppin/). As such, those files are bound by + * the corresponding legal terms and conditions set forth separately (original + * copyright notices are left in files from these sources and where we have + * modified a file our copyright notice appears before the original copyright + * notice). + * + * Using this version of GPGPU-Sim requires a complete installation of CUDA + * which is distributed seperately by NVIDIA under separate terms and + * conditions. To use this version of GPGPU-Sim with OpenCL requires a + * recent version of NVIDIA's drivers which support OpenCL. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the University of British Columbia nor the names of + * its contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * 4. This version of GPGPU-SIM is distributed freely for non-commercial use only. + * + * 5. No nonprofit user may place any restrictions on the use of this software, + * including as modified by the user, by any other authorized user. + * + * 6. GPGPU-SIM was developed primarily by Tor M. Aamodt, Wilson W. L. Fung, + * Ali Bakhoda, George L. Yuan, at the University of British Columbia, + * Vancouver, BC V6T 1Z4 + */ + +#ifndef GPU_MISC_H +#define GPU_MISC_H + +#define CONSTC 100 +#define DCACHE 200 +#define TEXTC 300 +#define SHD_CACHE_TAG(x,shdr) ((x) & (~((unsigned long long int)shdr->L1cache->line_sz - 1))) +#define SHD_TEXCACHE_TAG(x,shdr) ((x) & (~((unsigned long long int)shdr->L1texcache->line_sz - 1))) +#define SHD_CONSTCACHE_TAG(x,shdr) ((x) & (~((unsigned long long int)shdr->L1constcache->line_sz - 1))) +#define CACHE_TAG_OF(x,cache) ((x) & (~((unsigned long long int)cache->line_sz - 1))) +#define CACHE_TAG_OF_64(x) ((x) & (~((unsigned long long int)64 - 1))) + +#define ispowerof2(x) ((((x) - 1) & (x)) == 0) +#define powerof2(x) (1 << (x)) + + +enum mem_space { //used for cudasim + SHARED_SPACE, + CONST_SPACE, + GLOBAL_SPACE, + LOCAL_SPACE, + TEX_SPACE +}; +//enables a verbose printout of all L1 cache misses and all MSHR status changes +//good for a single shader configuration +#define DEBUGL1MISS 0 + +unsigned int LOGB2( unsigned int v ); + +unsigned int MAX2NUM( unsigned int a, unsigned int b ); + +unsigned int MIN2NUM( unsigned int a, unsigned int b ); + + +#define gs_max2(a,b) (((a)>(b))?(a):(b)) +#define gs_min2(a,b) (((a)<(b))?(a):(b)) +#define min3(x,y,z) (((x)<(y) && (x)<(z))?(x):(gs_min2((y),(z)))) + +#endif + diff --git a/src/gpgpu-sim/gpu-sim.cc b/src/gpgpu-sim/gpu-sim.cc new file mode 100644 index 0000000..6ae7430 --- /dev/null +++ b/src/gpgpu-sim/gpu-sim.cc @@ -0,0 +1,1752 @@ +/* + * gpu-sim.c + * + * Copyright (c) 2009 by Tor M. Aamodt, Wilson W. L. Fung, Ali Bakhoda, + * George L. Yuan, Ivan Sham, Henry Wong, Dan O'Connor and the + * University of British Columbia + * Vancouver, BC V6T 1Z4 + * All Rights Reserved. + * + * THIS IS A LEGAL DOCUMENT BY DOWNLOADING GPGPU-SIM, YOU ARE AGREEING TO THESE + * TERMS AND CONDITIONS. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * NOTE: The files libcuda/cuda_runtime_api.c and src/cuda-sim/cuda-math.h + * are derived from the CUDA Toolset available from http://www.nvidia.com/cuda + * (property of NVIDIA). The files benchmarks/BlackScholes/ and + * benchmarks/template/ are derived from the CUDA SDK available from + * http://www.nvidia.com/cuda (also property of NVIDIA). The files from + * src/intersim/ are derived from Booksim (a simulator provided with the + * textbook "Principles and Practices of Interconnection Networks" available + * from http://cva.stanford.edu/books/ppin/). As such, those files are bound by + * the corresponding legal terms and conditions set forth separately (original + * copyright notices are left in files from these sources and where we have + * modified a file our copyright notice appears before the original copyright + * notice). + * + * Using this version of GPGPU-Sim requires a complete installation of CUDA + * which is distributed seperately by NVIDIA under separate terms and + * conditions. To use this version of GPGPU-Sim with OpenCL requires a + * recent version of NVIDIA's drivers which support OpenCL. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the University of British Columbia nor the names of + * its contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * 4. This version of GPGPU-SIM is distributed freely for non-commercial use only. + * + * 5. No nonprofit user may place any restrictions on the use of this software, + * including as modified by the user, by any other authorized user. + * + * 6. GPGPU-SIM was developed primarily by Tor M. Aamodt, Wilson W. L. Fung, + * Ali Bakhoda, George L. Yuan, at the University of British Columbia, + * Vancouver, BC V6T 1Z4 + */ + +#include "gpu-sim.h" + +#include <time.h> +#include "gpu-cache.h" +#include "gpu-misc.h" +#include "delayqueue.h" +#include "shader.h" +#include "icnt_wrapper.h" +#include "dram.h" +#include "addrdec.h" +#include "dwf.h" +#include "warp_tracker.h" +#include "cflogger.h" +#include "l2cache.h" + +#include "../cuda-sim/ptx-stats.h" +#include "../intersim/statwraper.h" +#include "../abstract_hardware_model.h" + +#include <stdio.h> +#include <string.h> +#define MAX(a,b) (((a)>(b))?(a):(b)) + +extern unsigned L2_write_miss; +extern unsigned L2_write_hit; +extern unsigned L2_read_hit; +extern unsigned L2_read_miss; +unsigned made_read_mfs = 0; +unsigned made_write_mfs = 0; +unsigned freed_read_mfs = 0; +unsigned freed_L1write_mfs = 0; +unsigned freed_L2write_mfs = 0; +unsigned freed_dummy_read_mfs = 0; +unsigned long long gpu_sim_cycle = 0; +unsigned long long gpu_sim_insn = 0; +unsigned long long gpu_sim_insn_no_ld_const = 0; +unsigned long long gpu_sim_prev_insn = 0; +unsigned long long gpu_sim_insn_last_update = 0; +unsigned long long gpu_tot_sim_cycle = 0; +unsigned long long gpu_tot_sim_insn = 0; +unsigned long long gpu_last_sim_cycle = 0; +unsigned long long gpu_completed_thread = 0; +unsigned long long gpu_tot_issued_cta = 0; +unsigned long long gpu_tot_completed_thread = 0; + +unsigned int **concurrent_row_access; //concurrent_row_access[dram chip id][bank id] +unsigned int **num_activates; //num_activates[dram chip id][bank id] +unsigned int **row_access; //row_access[dram chip id][bank id] +unsigned int **max_conc_access2samerow; //max_conc_access2samerow[dram chip id][bank id] +unsigned int **max_servicetime2samerow; //max_servicetime2samerow[dram chip id][bank id] +unsigned int mergemiss = 0; +unsigned int L1_read_miss = 0; +unsigned int L1_write_miss = 0; +unsigned int L1_write_hit_on_miss = 0; +unsigned int L1_writeback = 0; +unsigned int L1_texture_miss = 0; +unsigned int L1_const_miss = 0; +unsigned int gpgpu_n_sent_writes = 0; +unsigned int gpgpu_n_processed_writes = 0; +unsigned int *max_return_queue_length; + +// performance counter for stalls due to congestion. +unsigned int gpu_stall_shd_mem = 0; +unsigned int gpu_stall_wr_back = 0; +unsigned int gpu_stall_dramfull = 0; +unsigned int gpu_stall_icnt2sh = 0; +unsigned int gpu_stall_by_MSHRwb = 0; + +//shader cannot send to icnt because icnt buffer is full +//Note: it is accumulative for all shaders and is never reset +//so it might increase 8 times in a cycle if we have 8 shaders +unsigned int gpu_stall_sh2icnt = 0; +// performance counters to account for instruction distribution +extern unsigned int gpgpu_n_load_insn; +extern unsigned int gpgpu_n_store_insn; +extern unsigned int gpgpu_n_shmem_insn; +extern unsigned int gpgpu_n_tex_insn; +extern unsigned int gpgpu_n_const_insn; +extern unsigned int gpgpu_multi_unq_fetches; +char *gpgpu_runtime_stat; +int gpu_stat_sample_freq = 10000; +int gpu_runtime_stat_flag = 0; +extern int gpgpu_warpdistro_shader; + +// GPGPU options +unsigned long long gpu_max_cycle = 0; +unsigned long long gpu_max_insn = 0; +int gpu_max_cycle_opt = 0; +int gpu_max_insn_opt = 0; +int gpu_max_cta_opt = 0; +int gpu_deadlock_detect = 0; +int gpu_deadlock = 0; +static unsigned long long last_gpu_sim_insn = 0; +int gpgpu_dram_scheduler = DRAM_FIFO; +int g_save_embedded_ptx = 0; +int gpgpu_simd_model = 0; +int gpgpu_no_dl1 = 0; +char *gpgpu_cache_texl1_opt; +char *gpgpu_cache_constl1_opt; +char *gpgpu_cache_dl1_opt; +char *gpgpu_cache_dl2_opt; +extern int gpgpu_l2_readoverwrite; +int gpgpu_partial_write_mask = 0; + +int gpgpu_perfect_mem = FALSE; +char *gpgpu_shader_core_pipeline_opt; +extern unsigned int *requests_by_warp; +unsigned int gpgpu_dram_buswidth = 4; +unsigned int gpgpu_dram_burst_length = 4; +int gpgpu_dram_sched_queue_size = 0; +char * gpgpu_dram_timing_opt; +int gpgpu_flush_cache = 0; +int gpgpu_mem_address_mask = 0; +unsigned int recent_dram_util = 0; + +int gpgpu_cflog_interval = 0; + +unsigned int finished_trace = 0; + +unsigned g_next_request_uid = 1; + +extern struct regs_t regs; + +extern long int gpu_reads; + +void ptx_dump_regs( void *thd ); + +int g_nthreads_issued; +int g_total_cta_left; + + +unsigned ptx_kernel_program_size(); +void visualizer_printstat(); +void time_vector_create(int ld_size,int st_size); +void time_vector_print(void); +void time_vector_update(unsigned int uid,int slot ,long int cycle,int type); +void check_time_vector_update(unsigned int uid,int slot ,long int latency,int type); +void node_req_hist_clear(void *p); +void node_req_hist_dump(void *p); +void node_req_hist_update(void * p,int node, long long cycle); + +/* functionally simulated memory */ +extern struct mem_t *mem; + +/* Defining Clock Domains +basically just the ratio is important */ + +#define CORE 0x01 +#define L2 0x02 +#define DRAM 0x04 +#define ICNT 0x08 + +double core_time=0; +double icnt_time=0; +double dram_time=0; +double l2_time=0; + +#define MhZ *1000000 +double core_freq=2 MhZ; +double icnt_freq=2 MhZ; +double dram_freq=2 MhZ; +double l2_freq=2 MhZ; + +double core_period = 1 /( 2 MhZ); +double icnt_period = 1 /( 2 MhZ); +double dram_period = 1 /( 2 MhZ); +double l2_period = 1 / (2 MhZ); + +char * gpgpu_clock_domains; + +/* GPU uArch parameters */ +unsigned int gpu_n_mem = 8; +unsigned int gpu_mem_n_bk = 4; +unsigned int gpu_n_mem_per_ctrlr = 1; +unsigned int gpu_n_shader = 8; +int gpu_concentration = 1; +int gpu_n_tpc = 8; +unsigned int gpu_n_mshr_per_shader; +unsigned int gpu_n_thread_per_shader = 128; +unsigned int gpu_n_warp_per_shader; +unsigned int gpu_n_mshr_per_thread = 1; + +extern int gpgpu_interwarp_mshr_merge ; + +extern unsigned int gpgpu_shmem_size; +extern unsigned int gpgpu_shader_registers; +extern unsigned int gpgpu_shader_cta; +extern int gpgpu_shmem_bkconflict; +extern int gpgpu_cache_bkconflict; +extern int gpgpu_n_cache_bank; +extern unsigned int warp_size; +extern int pipe_simd_width; +extern unsigned int gpgpu_dwf_heuristic; +extern unsigned int gpgpu_dwf_regbk; +int gpgpu_reg_bankconflict = FALSE; +extern int gpgpu_shmem_port_per_bank; +extern int gpgpu_cache_port_per_bank; +extern int gpgpu_const_port_per_bank; +extern int gpgpu_shmem_pipe_speedup; +extern int gpgpu_reg_bank_conflict_model; +extern int gpgpu_num_reg_banks; + +extern unsigned int gpu_max_cta_per_shader; +extern unsigned int gpu_padded_cta_size; +extern int gpgpu_local_mem_map; + +unsigned int gpgpu_pre_mem_stages = 0; +unsigned int gpgpu_no_divg_load = 0; +char *gpgpu_dwf_hw_opt; +unsigned int gpgpu_thread_swizzling = 0; +unsigned int gpgpu_strict_simd_wrbk = 0; + +int pdom_sched_type = 0; +int n_pdom_sc_orig_stat = 0; //the selected pdom schedular is used +int n_pdom_sc_single_stat = 0; //only a single warp is ready to go in that cycle. +int *num_warps_issuable; +int *num_warps_issuable_pershader; + +// Thread Dispatching Unit option +int gpgpu_cuda_sim = 1; +int gpgpu_spread_blocks_across_cores = 1; + +/* GPU uArch structures */ +shader_core_ctx_t **sc; +dram_t **dram; +unsigned int common_clock = 0; +unsigned int more_thread = 1; +extern unsigned int n_regconflict_stall; +unsigned int warp_conflict_at_writeback = 0; +unsigned int gpgpu_commit_pc_beyond_two = 0; +extern int g_network_mode; +int gpgpu_cache_wt_through = 0; + + +//memory access classification +int gpgpu_n_mem_read_local = 0; +int gpgpu_n_mem_write_local = 0; +int gpgpu_n_mem_texture = 0; +int gpgpu_n_mem_const = 0; +int gpgpu_n_mem_read_global = 0; +int gpgpu_n_mem_write_global = 0; + +#define MEM_LATENCY_STAT_IMPL +#include "mem_latency_stat.h" + +unsigned char fq_has_buffer(unsigned long long int addr, int bsize, bool write, int sid ); +unsigned char fq_push(unsigned long long int addr, int bsize, unsigned char write, partial_write_mask_t partial_write_mask, + int sid, int wid, mshr_entry* mshr, int cache_hits_waiting, + enum mem_access_type mem_acc, address_type pc); +int issue_mf_from_fq(mem_fetch_t *mf); +unsigned char single_check_icnt_has_buffer(int chip, int sid, unsigned char is_write ); +unsigned char fq_pop(int tpc_id); +void fill_shd_L1_with_new_line(shader_core_ctx_t * sc, mem_fetch_t * mf); + +void set_option_gpgpu_spread_blocks_across_cores(int option); +void set_param_gpgpu_num_shaders(int num_shaders); +unsigned ptx_sim_grid_size(); +void icnt_init_grid(); +void interconnect_stats(); +void icnt_overal_stat(); +unsigned ptx_sim_cta_size(); +unsigned ptx_sim_init_thread( void** thread_info, int sid, unsigned tid,unsigned threads_left,unsigned num_threads, core_t *core, unsigned hw_cta_id, unsigned hw_warp_id ); + +void gpu_sim_loop( int grid_num ); + +void print_shader_cycle_distro( FILE *fout ) ; +void find_reconvergence_points(); +void dwf_process_reconv_pts(); + +extern int gpgpu_ptx_instruction_classification ; +extern int g_ptx_sim_mode; + +extern int gpgpu_coalesce_arch; + +#define CREATELOG 111 +#define SAMPLELOG 222 +#define DUMPLOG 333 +void L2c_log(int task); +void dram_log(int task); + +void visualizer_options(option_parser_t opp); +void gpu_reg_options(option_parser_t opp) +{ + option_parser_register(opp, "-save_embedded_ptx", OPT_BOOL, &g_save_embedded_ptx, + "saves ptx files embedded in binary as <n>.ptx", + "0"); + option_parser_register(opp, "-gpgpu_simd_model", OPT_INT32, &gpgpu_simd_model, + "0 = no recombination, 1 = post-dominator, 2 = MIMD, 3 = dynamic warp formation", "0"); + option_parser_register(opp, "-gpgpu_dram_scheduler", OPT_INT32, &gpgpu_dram_scheduler, + "0 = fifo (default), 1 = fast ideal", "0"); + + option_parser_register(opp, "-gpgpu_max_cycle", OPT_INT32, &gpu_max_cycle_opt, + "terminates gpu simulation early (0 = no limit)", + "0"); + option_parser_register(opp, "-gpgpu_max_insn", OPT_INT32, &gpu_max_insn_opt, + "terminates gpu simulation early (0 = no limit)", + "0"); + option_parser_register(opp, "-gpgpu_max_cta", OPT_INT32, &gpu_max_cta_opt, + "terminates gpu simulation early (0 = no limit)", + "0"); + + option_parser_register(opp, "-gpgpu_tex_cache:l1", OPT_CSTR, &gpgpu_cache_texl1_opt, + "per-shader L1 texture cache (READ-ONLY) config, i.e., {<nsets>:<linesize>:<assoc>:<repl>|none}", + "512:64:2:L"); + + option_parser_register(opp, "-gpgpu_const_cache:l1", OPT_CSTR, &gpgpu_cache_constl1_opt, + "per-shader L1 constant memory cache (READ-ONLY) config, i.e., {<nsets>:<linesize>:<assoc>:<repl>|none}", + "64:64:2:L"); + + option_parser_register(opp, "-gpgpu_no_dl1", OPT_BOOL, &gpgpu_no_dl1, + "no dl1 cache (voids -gpgpu_cache:dl1 option)", + "0"); + + option_parser_register(opp, "-gpgpu_cache:dl1", OPT_CSTR, &gpgpu_cache_dl1_opt, + "shader L1 data cache config, i.e., {<nsets>:<bsize>:<assoc>:<repl>|none}", + "256:128:1:L"); + + option_parser_register(opp, "-gpgpu_cache:dl2", OPT_CSTR, &gpgpu_cache_dl2_opt, + "unified banked L2 data cache config, i.e., {<nsets>:<bsize>:<assoc>:<repl>|none}; disabled by default", + NULL); + + option_parser_register(opp, "-gpgpu_perfect_mem", OPT_BOOL, &gpgpu_perfect_mem, + "enable perfect memory mode (no cache miss)", + "0"); + + option_parser_register(opp, "-gpgpu_shader_core_pipeline", OPT_CSTR, &gpgpu_shader_core_pipeline_opt, + "shader core pipeline config, i.e., {<nthread>:<warpsize>:<pipe_simd_width>}", + "256:32:32"); + + option_parser_register(opp, "-gpgpu_shader_registers", OPT_UINT32, &gpgpu_shader_registers, + "Number of registers per shader core. Limits number of concurrent CTAs. (default 8192)", + "8192"); + + option_parser_register(opp, "-gpgpu_shader_cta", OPT_UINT32, &gpgpu_shader_cta, + "Maximum number of concurrent CTAs in shader (default 8)", + "8"); + + option_parser_register(opp, "-gpgpu_n_shader", OPT_UINT32, &gpu_n_shader, + "number of shaders in gpu", + "8"); + option_parser_register(opp, "-gpgpu_n_mem", OPT_UINT32, &gpu_n_mem, + "number of memory modules (e.g. memory controllers) in gpu", + "8"); + option_parser_register(opp, "-gpgpu_n_mem_per_ctrlr", OPT_UINT32, &gpu_n_mem_per_ctrlr, + "number of memory chips per memory controller", + "1"); + option_parser_register(opp, "-gpgpu_runtime_stat", OPT_CSTR, &gpgpu_runtime_stat, + "display runtime statistics such as dram utilization {<freq>:<flag>}", + "10000:0"); + + option_parser_register(opp, "-gpgpu_dwf_heuristic", OPT_UINT32, &gpgpu_dwf_heuristic, + "DWF scheduling heuristic: 0 = majority, 1 = minority, 2 = timestamp, 3 = pdom priority, 4 = pc-based, 5 = max-heap", + "0"); + + option_parser_register(opp, "-gpgpu_reg_bankconflict", OPT_BOOL, &gpgpu_reg_bankconflict, + "Check for bank conflict in the pipeline", + "0"); + + option_parser_register(opp, "-gpgpu_dwf_regbk", OPT_BOOL, (int*)&gpgpu_dwf_regbk, + "Have dwf scheduler to avoid bank conflict", + "1"); + + option_parser_register(opp, "-gpgpu_memlatency_stat", OPT_INT32, &gpgpu_memlatency_stat, + "track and display latency statistics 0x2 enables MC, 0x4 enables queue logs", + "0"); + + option_parser_register(opp, "-gpu_n_mshr_per_shader", OPT_UINT32, &gpu_n_mshr_per_shader, + "Number of MSHRs per shader", + "64"); + + option_parser_register(opp, "-gpgpu_interwarp_mshr_merge", OPT_INT32, &gpgpu_interwarp_mshr_merge, + "interwarp coalescing", + "0"); + + option_parser_register(opp, "-gpgpu_dram_sched_queue_size", OPT_INT32, &gpgpu_dram_sched_queue_size, + "0 = unlimited (default); # entries per chip", + "0"); + + option_parser_register(opp, "-gpgpu_dram_buswidth", OPT_UINT32, &gpgpu_dram_buswidth, + "default = 4 bytes (8 bytes per cycle at DDR)", + "4"); + + option_parser_register(opp, "-gpgpu_dram_burst_length", OPT_UINT32, &gpgpu_dram_burst_length, + "Burst length of each DRAM request (default = 4 DDR cycle)", + "4"); + + option_parser_register(opp, "-gpgpu_dram_timing_opt", OPT_CSTR, &gpgpu_dram_timing_opt, + "DRAM timing parameters = {nbk:tCCD:tRRD:tRCD:tRAS:tRP:tRC:CL:WL:tWTR}", + "4:2:8:12:21:13:34:9:4:5"); + + + option_parser_register(opp, "-gpgpu_mem_address_mask", OPT_INT32, &gpgpu_mem_address_mask, + "0 = old addressing mask, 1 = new addressing mask, 2 = new add. mask + flipped bank sel and chip sel bits", + "0"); + + option_parser_register(opp, "-gpgpu_flush_cache", OPT_BOOL, &gpgpu_flush_cache, + "Flush cache at the end of each kernel call", + "0"); + + option_parser_register(opp, "-gpgpu_pre_mem_stages", OPT_UINT32, &gpgpu_pre_mem_stages, + "default = 0 pre-memory pipeline stages", + "0"); + + option_parser_register(opp, "-gpgpu_no_divg_load", OPT_BOOL, (int*)&gpgpu_no_divg_load, + "Don't allow divergence on load", + "0"); + + option_parser_register(opp, "-gpgpu_dwf_hw", OPT_CSTR, &gpgpu_dwf_hw_opt, + "dynamic warp formation hw config, i.e., {<#LUT_entries>:<associativity>|none}", + "32:2"); + + option_parser_register(opp, "-gpgpu_thread_swizzling", OPT_BOOL, (int*)&gpgpu_thread_swizzling, + "Thread Swizzling (1=on, 0=off)", + "0"); + + option_parser_register(opp, "-gpgpu_strict_simd_wrbk", OPT_BOOL, (int*)&gpgpu_strict_simd_wrbk, + "Applying Strick SIMD WriteBack Stage (1=on, 0=off)", + "0"); + + option_parser_register(opp, "-gpgpu_shmem_size", OPT_UINT32, &gpgpu_shmem_size, + "Size of shared memory per shader core (default 16kB)", + "16384"); + + option_parser_register(opp, "-gpgpu_shmem_bkconflict", OPT_BOOL, &gpgpu_shmem_bkconflict, + "Turn on bank conflict check for shared memory", + "0"); + + option_parser_register(opp, "-gpgpu_shmem_pipe_speedup", OPT_INT32, &gpgpu_shmem_pipe_speedup, + "Number of groups each warp is divided for shared memory bank conflict check", + "2"); + + option_parser_register(opp, "-gpgpu_cache_wt_through", OPT_BOOL, &gpgpu_cache_wt_through, + "L1 cache become write through (1=on, 0=off)", + "0"); + + option_parser_register(opp, "-gpgpu_deadlock_detect", OPT_BOOL, &gpu_deadlock_detect, + "Stop the simulation at deadlock (1=on (default), 0=off)", + "1"); + + option_parser_register(opp, "-gpgpu_cache_bkconflict", OPT_BOOL, &gpgpu_cache_bkconflict, + "Turn on bank conflict check for L1 cache access", + "0"); + + option_parser_register(opp, "-gpgpu_n_cache_bank", OPT_INT32, &gpgpu_n_cache_bank, + "Number of banks in L1 cache, also for memory coalescing stall", + "1"); + + option_parser_register(opp, "-gpgpu_warpdistro_shader", OPT_INT32, &gpgpu_warpdistro_shader, + "Specify which shader core to collect the warp size distribution from", + "-1"); + + + option_parser_register(opp, "-gpgpu_pdom_sched_type", OPT_INT32, &pdom_sched_type, + "0 = first ready warp found, 1 = random, 8 = loose round robin", + "8"); + + option_parser_register(opp, "-gpgpu_spread_blocks_across_cores", OPT_BOOL, + &gpgpu_spread_blocks_across_cores, + "Spread block-issuing across all cores instead of filling up core by core (do NOT disable)", + "1"); + + option_parser_register(opp, "-gpgpu_cuda_sim", OPT_BOOL, &gpgpu_cuda_sim, + "use PTX instruction set", + "1"); + option_parser_register(opp, "-gpgpu_ptx_instruction_classification", OPT_INT32, + &gpgpu_ptx_instruction_classification, + "if enabled will classify ptx instruction types per kernel (Max 255 kernels now)", + "0"); + option_parser_register(opp, "-gpgpu_ptx_sim_mode", OPT_INT32, &g_ptx_sim_mode, + "Select between Performance (default) or Functional simulation (1)", + "0"); + option_parser_register(opp, "-gpgpu_clock_domains", OPT_CSTR, &gpgpu_clock_domains, + "Clock Domain Frequencies in MhZ {<Core Clock>:<ICNT Clock>:<L2 Clock>:<DRAM Clock>}", + "500.0:2000.0:2000.0:2000.0"); + + option_parser_register(opp, "-gpgpu_shmem_port_per_bank", OPT_INT32, &gpgpu_shmem_port_per_bank, + "Number of access processed by a shared memory bank per cycle (default = 2)", + "2"); + option_parser_register(opp, "-gpgpu_cache_port_per_bank", OPT_INT32, &gpgpu_cache_port_per_bank, + "Number of access processed by a cache bank per cycle (default = 2)", + "2"); + option_parser_register(opp, "-gpgpu_const_port_per_bank", OPT_INT32, &gpgpu_const_port_per_bank, + "Number of access processed by a constant cache bank per cycle (default = 2)", + "2"); + option_parser_register(opp, "-gpgpu_cflog_interval", OPT_INT32, &gpgpu_cflog_interval, + "Interval between each snapshot in control flow logger", + "0"); + option_parser_register(opp, "-gpgpu_partial_write_mask", OPT_INT32, &gpgpu_partial_write_mask, + "use partial write mask to filter memory requests <1>No extra reads(use this!)<2>extra reads generated for partial chunks", + "0"); + option_parser_register(opp, "-gpu_concentration", OPT_INT32, &gpu_concentration, + "Number of shader cores per interconnection port (default = 1)", + "1"); + option_parser_register(opp, "-gpgpu_local_mem_map", OPT_INT32, &gpgpu_local_mem_map, + "Mapping from local memory space address to simulated GPU physical address space (default = 1)", + "1"); + option_parser_register(opp, "-gpgpu_reg_bank_conflict_model", OPT_BOOL, &gpgpu_reg_bank_conflict_model, + "Turn on register bank conflict model (default = off)", + "0"); + option_parser_register(opp, "-gpgpu_num_reg_banks", OPT_INT32, &gpgpu_num_reg_banks, + "Number of register banks (default = 8)", + "8"); + option_parser_register(opp, "-gpgpu_coalesce_arch", OPT_INT32, &gpgpu_coalesce_arch, + "Coalescing arch (default = 13, anything else is off for now)", + "13"); + addrdec_setoption(opp); + L2c_options(opp); + visualizer_options(opp); + ptx_file_line_stats_options(opp); +} + +///////////////////////////////////////////////////////////////////////////// + +inline int mem2device(int memid) { + return memid + gpu_n_tpc; +} + +///////////////////////////////////////////////////////////////////////////// + + +/* Allocate memory for uArch structures */ +void init_gpu () +{ + int i; + + gpu_max_cycle = gpu_max_cycle_opt; + gpu_max_insn = gpu_max_insn_opt; + + i = sscanf(gpgpu_shader_core_pipeline_opt,"%d:%d:%d", + &gpu_n_thread_per_shader, &warp_size, &pipe_simd_width); + gpu_n_warp_per_shader = gpu_n_thread_per_shader / warp_size; + num_warps_issuable = (int*) calloc(gpu_n_warp_per_shader+1, sizeof(int)); + num_warps_issuable_pershader = (int*) calloc(gpu_n_shader, sizeof(int)); + if (i == 2) { + pipe_simd_width = warp_size; + } else if (i == 3) { + assert(warp_size % pipe_simd_width == 0); + } + + sscanf(gpgpu_runtime_stat, "%d:%x", + &gpu_stat_sample_freq, &gpu_runtime_stat_flag); + + sc = (shader_core_ctx_t**) calloc(gpu_n_shader, sizeof(shader_core_ctx_t*)); + int mshr_que = gpu_n_mshr_per_thread; + for (i=0;(unsigned)i<gpu_n_shader;i++) { + sc[i] = shader_create("sh", i, /* shader id*/ + gpu_n_thread_per_shader, /* number of threads */ + mshr_que, /* number of MSHR per threads */ + fq_push, fq_has_buffer, gpgpu_simd_model); + } + + ptx_file_line_stats_create_exposed_latency_tracker(gpu_n_shader); + + // initialize dynamic warp formation scheduler + int dwf_lut_size, dwf_lut_assoc; + sscanf(gpgpu_dwf_hw_opt,"%d:%d", &dwf_lut_size, &dwf_lut_assoc); + char *dwf_hw_policy_opt = strchr(gpgpu_dwf_hw_opt, ';'); + int insn_size = 1; // for cuda-sim + create_dwf_schedulers(gpu_n_shader, dwf_lut_size, dwf_lut_assoc, + warp_size, pipe_simd_width, + gpu_n_thread_per_shader, insn_size, + gpgpu_dwf_heuristic, dwf_hw_policy_opt ); + + gpgpu_no_divg_load = gpgpu_no_divg_load && (gpgpu_simd_model == DWF); + // always use no diverge on load for PDOM and NAIVE + gpgpu_no_divg_load = gpgpu_no_divg_load || (gpgpu_simd_model == POST_DOMINATOR || gpgpu_simd_model == NO_RECONVERGE); + if (gpgpu_no_divg_load) + init_warp_tracker(); + + assert(gpu_n_shader % gpu_concentration == 0); + gpu_n_tpc = gpu_n_shader / gpu_concentration; + + dram = (dram_t**) calloc(gpu_n_mem, sizeof(dram_t*)); + // L2request = (mem_fetch_t**) calloc(gpu_n_mem, sizeof(mem_fetch_t*)); + addrdec_setnchip(gpu_n_mem); + unsigned int nbk,tCCD,tRRD,tRCD,tRAS,tRP,tRC,CL,WL,tWTR; + sscanf(gpgpu_dram_timing_opt,"%d:%d:%d:%d:%d:%d:%d:%d:%d:%d",&nbk,&tCCD,&tRRD,&tRCD,&tRAS,&tRP,&tRC,&CL,&WL,&tWTR); + gpu_mem_n_bk = nbk; + for (i=0;(unsigned)i<gpu_n_mem;i++) { + dram[i] = dram_create(i, nbk, tCCD, tRRD, tRCD, tRAS, tRP, tRC, + CL, WL, gpgpu_dram_burst_length/*BL*/, tWTR, gpgpu_dram_buswidth/*busW*/, + gpgpu_dram_sched_queue_size, gpgpu_dram_scheduler); + if (gpgpu_cache_dl2_opt) + L2c_create(dram[i], gpgpu_cache_dl2_opt); + } + dram_log(CREATELOG); + if (gpgpu_cache_dl2_opt && 1) { + L2c_log(CREATELOG); + } + concurrent_row_access = (unsigned int**) calloc(gpu_n_mem, sizeof(unsigned int*)); + num_activates = (unsigned int**) calloc(gpu_n_mem, sizeof(unsigned int*)); + row_access = (unsigned int**) calloc(gpu_n_mem, sizeof(unsigned int*)); + max_conc_access2samerow = (unsigned int**) calloc(gpu_n_mem, sizeof(unsigned int*)); + max_servicetime2samerow = (unsigned int**) calloc(gpu_n_mem, sizeof(unsigned int*)); + + for (i=0;(unsigned)i<gpu_n_mem ;i++ ) { + concurrent_row_access[i] = (unsigned int*) calloc(gpu_mem_n_bk, sizeof(unsigned int)); + row_access[i] = (unsigned int*) calloc(gpu_mem_n_bk, sizeof(unsigned int)); + num_activates[i] = (unsigned int*) calloc(gpu_mem_n_bk, sizeof(unsigned int)); + max_conc_access2samerow[i] = (unsigned int*) calloc(gpu_mem_n_bk, sizeof(unsigned int)); + max_servicetime2samerow[i] = (unsigned int*) calloc(gpu_mem_n_bk, sizeof(unsigned int)); + } + + memlatstat_init(); + + L2c_init_stat(); + max_return_queue_length = (unsigned int*) calloc(gpu_n_shader, sizeof(unsigned int)); + icnt_init(gpu_n_tpc, gpu_n_mem); + + common_clock = 0; + + time_vector_create(NUM_MEM_REQ_STAT,MR_2SH_ICNT_INJECTED); +} + + + +void gpu_print_stat(); + +void init_clock_domains(void ) { + sscanf(gpgpu_clock_domains,"%lf:%lf:%lf:%lf", + &core_freq, &icnt_freq, &l2_freq, &dram_freq); + core_freq = core_freq MhZ; + icnt_freq = icnt_freq MhZ; + l2_freq = l2_freq MhZ; + dram_freq = dram_freq MhZ; + core_period = 1/core_freq; + icnt_period = 1/icnt_freq; + dram_period = 1/dram_freq; + l2_period = 1/l2_freq; + core_time = 0 ; + dram_time = 0 ; + icnt_time = 0; + l2_time = 0; + printf("GPGPU-Sim uArch: clock freqs: %lf:%lf:%lf:%lf\n",core_freq,icnt_freq,l2_freq,dram_freq); + printf("GPGPU-Sim uArch: clock periods: %.20lf:%.20lf:%.20lf:%.20lf\n",core_period,icnt_period,l2_period,dram_period); +} + +void reinit_clock_domains(void) +{ + core_time = 0 ; + dram_time = 0 ; + icnt_time = 0; + l2_time = 0; +} + +void init_once(void ) { + init_clock_domains(); +} + +// return the number of cycle required to run all the trace on the gpu +unsigned int run_gpu_sim(int grid_num) +{ + + int not_completed; + int mem_busy; + int icnt2mem_busy; + + gpu_sim_cycle = 0; + not_completed = 1; + mem_busy = 1; + icnt2mem_busy = 1; + finished_trace = 0; + g_next_request_uid = 1; + more_thread = 1; + gpu_sim_insn = 0; + gpu_sim_insn_no_ld_const = 0; + + gpu_completed_thread = 0; + + g_nthreads_issued = 0; + + static int one_time_inits_done = 0 ; + if (!one_time_inits_done ) { + init_once(); + } + reinit_clock_domains(); + assert(gpgpu_spread_blocks_across_cores); // this seems to be required, so let's make it explicit + set_option_gpgpu_spread_blocks_across_cores(gpgpu_spread_blocks_across_cores); + set_param_gpgpu_num_shaders(gpu_n_shader); + for (unsigned i=0;i<gpu_n_shader;i++) { + sc[i]->not_completed = 0; + shader_reinit(sc[i],0,sc[i]->n_threads); + } + if (gpu_max_cta_opt != 0) { + g_total_cta_left = gpu_max_cta_opt; + } else { + g_total_cta_left = ptx_sim_grid_size(); + } + if (gpu_max_cta_opt != 0) { + // the maximum number of CTA has been reached, stop any further simulation + if (gpu_tot_issued_cta >= (unsigned)gpu_max_cta_opt) { + return 0; + } + } + + if (gpu_max_cycle && (gpu_tot_sim_cycle + gpu_sim_cycle) >= gpu_max_cycle) { + return gpu_sim_cycle; + } + if (gpu_max_insn && (gpu_tot_sim_insn + gpu_sim_insn) >= gpu_max_insn) { + return gpu_sim_cycle; + } + + // refind the diverge/reconvergence pairs + dwf_reset_reconv_pt(); + find_reconvergence_points(); + + dwf_process_reconv_pts(); + dwf_reinit_schedulers(gpu_n_shader); + + // initialize the control-flow, memory access, memory latency logger + create_thread_CFlogger( gpu_n_shader, gpu_n_thread_per_shader, ptx_kernel_program_size(), 0, gpgpu_cflog_interval ); + shader_CTA_count_create( gpu_n_shader, gpgpu_cflog_interval); + if (gpgpu_cflog_interval != 0) { + insn_warp_occ_create( gpu_n_shader, warp_size, ptx_kernel_program_size() ); + shader_warp_occ_create( gpu_n_shader, warp_size, gpgpu_cflog_interval); + shader_mem_acc_create( gpu_n_shader, gpu_n_mem, 4, gpgpu_cflog_interval); + shader_mem_lat_create( gpu_n_shader, gpgpu_cflog_interval); + shader_cache_access_create( gpu_n_shader, 3, gpgpu_cflog_interval); + set_spill_interval (gpgpu_cflog_interval * 40); + } + + // calcaulte the max cta count and cta size for local memory address mapping + gpu_max_cta_per_shader = max_cta_per_shader(sc[0]); + //gpu_max_cta_per_shader is limited by number of CTAs if not enough + if (ptx_sim_grid_size() < gpu_max_cta_per_shader*gpu_n_shader) { + gpu_max_cta_per_shader = (ptx_sim_grid_size() / gpu_n_shader); + if (ptx_sim_grid_size() % gpu_n_shader) + gpu_max_cta_per_shader++; + } + unsigned int gpu_cta_size = ptx_sim_cta_size(); + gpu_padded_cta_size = (gpu_cta_size%32) ? 32*((gpu_cta_size/32)+1) : gpu_cta_size; + + if (g_network_mode) { + icnt_init_grid(); + } + last_gpu_sim_insn = 0; + // add this condition as well? (gpgpu_n_processed_writes < gpgpu_n_sent_writes) + while (not_completed || mem_busy || icnt2mem_busy) { + gpu_sim_loop(grid_num); + + not_completed = 0; + for (unsigned i=0;i<gpu_n_shader;i++) { + not_completed += sc[i]->not_completed; + } + // dram_busy just check the request queue length into the dram + // to make sure all the memory requests (esp the writes) are done + mem_busy = 0; + for (unsigned i=0;i<gpu_n_mem;i++) { + mem_busy += dram_busy(dram[i]); + } + // icnt to the memory should clean of any pending tranfers as well + icnt2mem_busy = icnt_busy( ); + + if (gpu_max_cycle && (gpu_tot_sim_cycle + gpu_sim_cycle) >= gpu_max_cycle) { + break; + } + if (gpu_max_insn && (gpu_tot_sim_insn + gpu_sim_insn) >= gpu_max_insn) { + break; + } + if (gpu_deadlock_detect && gpu_deadlock) { + break; + } + + } + memlatstat_lat_pw(); + gpu_tot_sim_cycle += gpu_sim_cycle; + gpu_tot_sim_insn += gpu_sim_insn; + gpu_tot_completed_thread += gpu_completed_thread; + + ptx_file_line_stats_write_file(); + + printf("stats for grid: %d\n", grid_num); + gpu_print_stat(); + if (g_network_mode) { + interconnect_stats(); + printf("----------------------------Interconnect-DETAILS---------------------------------" ); + icnt_overal_stat(); + printf("----------------------------END-of-Interconnect-DETAILS-------------------------" ); + } + if (gpgpu_memlatency_stat & GPU_MEMLATSTAT_QUEUELOGS ) { + dramqueue_latency_log_dump(); + dram_log(DUMPLOG); + if (gpgpu_cache_dl2_opt) { + L2c_log(DUMPLOG); + L2c_latency_log_dump(); + } + } + +#define DEADLOCK 0 + if (gpu_deadlock_detect && gpu_deadlock) { + fflush(stdout); + printf("ERROR ** deadlock detected: last writeback @ gpu_sim_cycle %u (+ gpu_tot_sim_cycle %u) (%u cycles ago)\n", + (unsigned) gpu_sim_insn_last_update, (unsigned) (gpu_tot_sim_cycle-gpu_sim_cycle), + (unsigned) (gpu_sim_cycle - gpu_sim_insn_last_update )); + fflush(stdout); + assert(DEADLOCK); + } + return gpu_sim_cycle; +} + +extern void ** g_inst_classification_stat; +extern void ** g_inst_op_classification_stat; +extern int g_ptx_kernel_count; // used for classification stat collection purposes + +extern unsigned get_max_mshr_used(shader_core_ctx_t* shader); + +void gpu_print_stat() +{ + unsigned i; + int j,k; + + printf("gpu_sim_cycle = %lld\n", gpu_sim_cycle); + printf("gpu_sim_insn = %lld\n", gpu_sim_insn); + printf("gpu_sim_no_ld_const_insn = %lld\n", gpu_sim_insn_no_ld_const); + printf("gpu_ipc = %12.4f\n", (float)gpu_sim_insn / gpu_sim_cycle); + printf("gpu_completed_thread = %lld\n", gpu_completed_thread); + printf("gpu_tot_sim_cycle = %lld\n", gpu_tot_sim_cycle); + printf("gpu_tot_sim_insn = %lld\n", gpu_tot_sim_insn); + printf("gpu_tot_ipc = %12.4f\n", (float)gpu_tot_sim_insn / gpu_tot_sim_cycle); + printf("gpu_tot_completed_thread = %lld\n", gpu_tot_completed_thread); + printf("gpu_tot_issued_cta = %lld\n", gpu_tot_issued_cta); + printf("gpgpu_n_sent_writes = %d\n", gpgpu_n_sent_writes); + printf("gpgpu_n_processed_writes = %d\n", gpgpu_n_processed_writes); + + // performance counter for stalls due to congestion. + printf("gpu_stall_by_MSHRwb= %d\n", gpu_stall_by_MSHRwb); + printf("gpu_stall_shd_mem = %d\n", gpu_stall_shd_mem ); + printf("gpu_stall_wr_back = %d\n", gpu_stall_wr_back ); + printf("gpu_stall_dramfull = %d\n", gpu_stall_dramfull); + printf("gpu_stall_icnt2sh = %d\n", gpu_stall_icnt2sh ); + printf("gpu_stall_sh2icnt = %d\n", gpu_stall_sh2icnt ); + // performance counter that are not local to one shader + shader_print_accstats(stdout); + + memlatstat_print(); + printf("max return queue length = "); + for (unsigned i=0;i<gpu_n_shader;i++) { + printf("%d ", max_return_queue_length[i]); + } + printf("\n"); + // merge misses + printf("merge misses = %d\n", mergemiss); + printf("L1 read misses = %d\n", L1_read_miss); + printf("L1 write misses = %d\n", L1_write_miss); + printf("L1 write hit on misses = %d\n", L1_write_hit_on_miss); + printf("L1 writebacks = %d\n", L1_writeback); + printf("L1 texture misses = %d\n", L1_texture_miss); + printf("L1 const misses = %d\n", L1_const_miss); + printf("L2_write_miss = %d\n", L2_write_miss); + printf("L2_write_hit = %d\n", L2_write_hit); + printf("L2_read_miss = %d\n", L2_read_miss); + printf("L2_read_hit = %d\n", L2_read_hit); + printf("made_read_mfs = %d\n", made_read_mfs); + printf("made_write_mfs = %d\n", made_write_mfs); + printf("freed_read_mfs = %d\n", freed_read_mfs); + printf("freed_L1write_mfs = %d\n", freed_L1write_mfs); + printf("freed_L2write_mfs = %d\n", freed_L2write_mfs); + printf("freed_dummy_read_mfs = %d\n", freed_dummy_read_mfs); + + printf("gpgpu_n_mem_read_local = %d\n", gpgpu_n_mem_read_local); + printf("gpgpu_n_mem_write_local = %d\n", gpgpu_n_mem_write_local); + printf("gpgpu_n_mem_read_global = %d\n", gpgpu_n_mem_read_global); + printf("gpgpu_n_mem_write_global = %d\n", gpgpu_n_mem_write_global); + printf("gpgpu_n_mem_texture = %d\n", gpgpu_n_mem_texture); + printf("gpgpu_n_mem_const = %d\n", gpgpu_n_mem_const); + + printf("max_n_mshr_used = "); + for (unsigned i=0; i< gpu_n_shader; i++) printf("%d ", get_max_mshr_used(sc[i])); + printf("\n"); + + if (gpgpu_cache_dl2_opt) { + L2c_print_stat( ); + } + for (unsigned i=0;i<gpu_n_mem;i++) { + dram_print(dram[i],stdout); + } + + for (i=0, j=0, k=0;i<gpu_n_shader;i++) { + shd_cache_print(sc[i]->L1cache,stdout); + j+=sc[i]->L1cache->miss; + k+=sc[i]->L1cache->access; + } + printf("L1 Data Cache Total Miss Rate = %0.3f\n", (float)j/k); + + for (i=0,j=0,k=0;i<gpu_n_shader;i++) { + shd_cache_print(sc[i]->L1texcache,stdout); + j+=sc[i]->L1texcache->miss; + k+=sc[i]->L1texcache->access; + } + printf("L1 Texture Cache Total Miss Rate = %0.3f\n", (float)j/k); + + for (i=0,j=0,k=0;i<gpu_n_shader;i++) { + shd_cache_print(sc[i]->L1constcache,stdout); + j+=sc[i]->L1constcache->miss; + k+=sc[i]->L1constcache->access; + } + printf("L1 Const Cache Total Miss Rate = %0.3f\n", (float)j/k); + + if (gpgpu_cache_dl2_opt) { + L2c_print_cache_stat(); + } + printf("n_regconflict_stall = %d\n", n_regconflict_stall); + + if (gpgpu_simd_model == DWF) { + dwf_print_stat(stdout); + } + + if (gpgpu_simd_model == POST_DOMINATOR) { + printf("num_warps_issuable:"); + for (unsigned i=0;i<(gpu_n_warp_per_shader+1);i++) { + printf("%d ", num_warps_issuable[i]); + } + printf("\n"); + } + if (gpgpu_strict_simd_wrbk) { + printf("warp_conflict_at_writeback = %d\n", warp_conflict_at_writeback); + } + + printf("gpgpu_commit_pc_beyond_two = %d\n", gpgpu_commit_pc_beyond_two); + + print_shader_cycle_distro( stdout ); + + print_thread_pc_histogram( stdout ); + + if (gpgpu_cflog_interval != 0) { + spill_log_to_file (stdout, 1, gpu_sim_cycle); + insn_warp_occ_print(stdout); + } + if ( gpgpu_ptx_instruction_classification ) { + StatDisp( g_inst_classification_stat[g_ptx_kernel_count]); + StatDisp( g_inst_op_classification_stat[g_ptx_kernel_count]); + } + time_vector_print(); + + fflush(stdout); +} + +//////////////////////////////////////////////////////////////////////////////////// +// Wrapper function for shader cores' memory system: +//////////////////////////////////////////////////////////////////////////////////// + +// a hack to make the size of a packet discrete multiples of the interconnect's flit_size. +static inline +unsigned int fill_to_next_flit(unsigned int size) +{ + assert (g_network_mode == INTERSIM); + return size; +} + + + +unsigned char check_icnt_has_buffer(unsigned long long int *addr, int *bsize, + int n_addr, int sid ) +{ + addrdec_t tlx; + static unsigned int *req_buffer = NULL; + //the req_buf size can be equal to gpu_n_mem ; gpu_n_shader is added to make it compatible + //with the case where a mem controller is sending to shd + if (!req_buffer) req_buffer = (unsigned int*)malloc((gpu_n_mem+gpu_n_tpc)*sizeof(unsigned int)); + memset(req_buffer, 0, (gpu_n_mem+gpu_n_tpc)*sizeof(unsigned int)); + + // aggregate all buffer requirement of all memory accesses by dram chips + for (int i=0; i< n_addr; i++) { + addrdec_tlx(addr[i],&tlx); + req_buffer[tlx.chip] += fill_to_next_flit(bsize[i]); + } + + int tpc_id = sid / gpu_concentration; + + return icnt_has_buffer(tpc_id, req_buffer); +} + +unsigned char single_check_icnt_has_buffer(int chip, int sid, unsigned char is_write ) +{ + static unsigned int *req_buffer = NULL; + //the req_buf size can be equal to gpu_n_mem ; gpu_n_shader is added to make it compatible + //with the case where a mem controller is sending to shd + if (!req_buffer) req_buffer = (unsigned int*)malloc((gpu_n_mem+gpu_n_tpc)*sizeof(unsigned int)); + memset(req_buffer, 0, (gpu_n_mem+gpu_n_tpc)*sizeof(unsigned int)); + + // aggregate all buffer requirement of all memory accesses by dram chips + + int b_size; + if (is_write) + b_size = sc[sid]->L1cache->line_sz; + else + b_size = READ_PACKET_SIZE; + req_buffer[chip] += fill_to_next_flit(b_size); + + int tpc_id = sid / gpu_concentration; + + return icnt_has_buffer(tpc_id, req_buffer); +} + +int max_n_addr = 0; + +// Check the memory system for buffer availability +unsigned char fq_has_buffer(unsigned long long int addr, int bsize, bool write, int sid ) +{ + //requests should be single always now + int rsize = bsize; + //maintain similar functionality with fq_push, if its a read, bsize is the load size, not the request's size + if (!write) { + rsize = READ_PACKET_SIZE; + } + return check_icnt_has_buffer(&addr, &rsize, 1, sid); +} + +// Takes in memory address and their parameters and pushes to the fetch queue +unsigned char fq_push(unsigned long long int addr, int bsize, unsigned char write, partial_write_mask_t partial_write_mask, + int sid, int wid, mshr_entry* mshr, int cache_hits_waiting, + enum mem_access_type mem_acc, address_type pc) +{ + mem_fetch_t *mf; + + mf = (mem_fetch_t*) calloc(1,sizeof(mem_fetch_t)); + mf->request_uid = g_next_request_uid++; + mf->addr = addr; + mf->nbytes_L1 = bsize; + mf->sid = sid; + mf->source_node = sid / gpu_concentration; + mf->wid = wid; + mf->cache_hits_waiting = cache_hits_waiting; + mf->txbytes_L1 = 0; + mf->rxbytes_L1 = 0; + mf->mshr = mshr; + if (mshr) mshr->mf = (void*)mf; // for debugging + mf->write = write; + + if (write) + made_write_mfs++; + else + made_read_mfs++; + memlatstat_start(mf); + addrdec_tlx(addr,&mf->tlx); + mf->bank = mf->tlx.bk; + mf->chip = mf->tlx.chip; + if (gpgpu_cache_dl2_opt) + mf->nbytes_L2 = L2c_get_linesize( dram[mf->tlx.chip] ); + else + mf->nbytes_L2 = 0; + mf->txbytes_L2 = 0; + mf->rxbytes_L2 = 0; + + mf->write_mask = partial_write_mask; + if (!write) assert(partial_write_mask == NO_PARTIAL_WRITE); + + // stat collection codes + mf->mem_acc = mem_acc; + mf->pc = pc; + + switch (mem_acc) { + case CONST_ACC_R: gpgpu_n_mem_const++; break; + case TEXTURE_ACC_R: gpgpu_n_mem_texture++; break; + case GLOBAL_ACC_R: gpgpu_n_mem_read_global++; break; + case GLOBAL_ACC_W: gpgpu_n_mem_write_global++; break; + case LOCAL_ACC_R: gpgpu_n_mem_read_local++; break; + case LOCAL_ACC_W: gpgpu_n_mem_write_local++; break; + default: assert(0); + } + + return(issue_mf_from_fq(mf)); + +} + +int issue_mf_from_fq(mem_fetch_t *mf){ + int destination; // where is the next level of memory? + destination = mf->tlx.chip; + int tpc_id = mf->sid / gpu_concentration; + + if (mf->mshr) mshr_update_status(mf->mshr,IN_ICNT2MEM); + if (!mf->write) { + mf->type = RD_REQ; + assert( mf->timestamp == (gpu_sim_cycle+gpu_tot_sim_cycle) ); + time_vector_update(mf->mshr->insts[0].uid, MR_ICNT_PUSHED, gpu_sim_cycle+gpu_tot_sim_cycle, mf->type ); + icnt_push(tpc_id, mem2device(destination), (void*)mf, READ_PACKET_SIZE); + } else { + mf->type = WT_REQ; + icnt_push(tpc_id, mem2device(destination), (void*)mf, mf->nbytes_L1); + gpgpu_n_sent_writes++; + assert( mf->timestamp == (gpu_sim_cycle+gpu_tot_sim_cycle) ); + time_vector_update(mf->request_uid, MR_ICNT_PUSHED, gpu_sim_cycle+gpu_tot_sim_cycle, mf->type ) ; + } + + return 0; +} + +extern void mshr_return_from_mem(shader_core_ctx_t * shader, mshr_entry_t* mshr); + +inline void fill_shd_L1_with_new_line(shader_core_ctx_t * sc, mem_fetch_t * mf) { + unsigned long long int repl_addr = -1; + // When the data arrives, it flags all the appropriate MSHR + // entries accordingly (by checking the address in each entry ) + memlatstat_read_done(mf); + + mshr_return_from_mem(sc, mf->mshr); + + if (mf->mshr->istexture) { + shd_cache_fill(sc->L1texcache,mf->addr,sc->gpu_cycle); + repl_addr = -1; + } else if (mf->mshr->isconst) { + shd_cache_fill(sc->L1constcache,mf->addr,sc->gpu_cycle); + repl_addr = -1; + } else { + if (!gpgpu_no_dl1) { + //if we are doing a writeback cache we may have marked off a mask in the mshr + //only write into the cache unmasked bytes. + //since this doesn't affect timing we don't actually do it. + repl_addr = shd_cache_fill(sc->L1cache,mf->addr,sc->gpu_cycle); + } + } + + freed_read_mfs++; + free(mf); +} + +unsigned char fq_pop(int tpc_id) +{ + mem_fetch_t *mf; + + mf = (mem_fetch_t*) icnt_pop(tpc_id); + + // if there is a memory fetch request coming back, forward it to the proper shader core + if (mf) { + assert(mf->type == REPLY_DATA); + time_vector_update(mf->mshr->insts[0].uid ,MR_2SH_FQ_POP,gpu_sim_cycle+gpu_tot_sim_cycle, mf->type ) ; + fill_shd_L1_with_new_line(sc[mf->sid], mf); + } + return 0; +} + +//////////////////////////////////////////////////////////////////////////////////////////////// + +int issue_block2core( shader_core_ctx_t *shdr, int grid_num ) +{ + int tid, nthreads_2beissued, more_threads; + int nthreads_in_block= 0; + int start_thread = 0; + int end_thread = shdr->n_threads; + int cta_id=-1; + int cta_size=0; + int padded_cta_size; + + cta_size = ptx_sim_cta_size(); + padded_cta_size = cta_size; + + assert(gpgpu_spread_blocks_across_cores); //should be if muliple CTA per shader supported + + for (unsigned i=0;i<max_cta_per_shader(shdr);i++ ) { //try to find next empty cta slot + if (shdr->cta_status[i]==0) { // + cta_id=i; + break; + } + } + assert( cta_id!=-1);//must have found a CTA to run + if (padded_cta_size%warp_size) { + padded_cta_size = ((padded_cta_size/warp_size)+1)*(warp_size); + } + start_thread = cta_id * padded_cta_size; + end_thread = start_thread + cta_size; + shader_reinit(shdr,start_thread, end_thread); + + // issue threads in blocks (if it is specified) + warp_set_t warps; + for (int i = start_thread; i<end_thread; i++) { //setup the block + unsigned warp_id = i/warp_size; + shdr->thread[i].cta_id = cta_id; + nthreads_in_block += ptx_sim_init_thread(&shdr->thread[i].ptx_thd_info,shdr->sid,i,cta_size-(i-start_thread),shdr->n_threads/*cta_size*/,shdr,cta_id,warp_id); + warps.set( warp_id ); + } + shdr->allocate_barrier( cta_id, warps ); + + shader_init_CTA(shdr, start_thread, end_thread); + nthreads_2beissued = nthreads_in_block; + shdr->cta_status[cta_id]+=nthreads_2beissued; + assert( nthreads_2beissued ); //we should have not reached this point if there is no more thread to - + + assert( (unsigned) nthreads_2beissued <= shdr->n_threads); //confirm threads to be issued is less than or equal to number of threads supported by microarchitecture + + int n_cta_issued= nthreads_2beissued/cta_size ;//+ nthreads_2beissued%cta_size; + shdr->n_active_cta += n_cta_issued; + shader_CTA_count_log(shdr->sid, n_cta_issued); + g_total_cta_left-= n_cta_issued; + + more_threads = 1; + if (gpgpu_spread_blocks_across_cores) { + nthreads_2beissued += start_thread; + } + printf("Shader %d initializing CTA #%d with hw tids from %d to %d @(%lld,%lld)", + shdr->sid, cta_id, start_thread, nthreads_2beissued, gpu_sim_cycle, gpu_tot_sim_cycle ); + printf(" shdr->not_completed = %d\n", shdr->not_completed); + + for (tid=start_thread;tid<nthreads_2beissued;tid++) { + + // reset complete flag for stream + shdr->not_completed += 1; + assert( shdr->warp[tid/warp_size].n_completed > 0 ); + assert( shdr->warp[tid/warp_size].n_completed <= warp_size); + shdr->warp[tid/warp_size].n_completed--; + + // set avail4fetch flag to ready + shdr->thread[tid].avail4fetch = 1; + assert( shdr->warp[tid/warp_size].n_avail4fetch < warp_size ); + shdr->warp[tid/warp_size].n_avail4fetch++; + + g_nthreads_issued++; + } + + if (!nthreads_in_block) more_threads = 0; + return more_threads; //if there are no more threads to be issued, return 0 +} + +/////////////////////////////////////////////////////////////////////////////////////////// +// wrapper code to to create an illusion of a memory controller with L2 cache. +// +int mem_ctrl_full( int mc_id ) +{ + if (gpgpu_cache_dl2_opt) { + return L2c_full( dram[mc_id] ); + } else { + return( gpgpu_dram_sched_queue_size && dram_full(dram[mc_id]) ); + } +} + +//#define DEBUG_PARTIAL_WRITES +void mem_ctrl_push( int mc_id, mem_fetch_t* mf ) +{ + if (gpgpu_cache_dl2_opt) { + L2c_push(dram[mc_id], mf); + } else { + addrdec_t tlx; + addrdec_tlx(mf->addr, &tlx); +#if 0 //old chunking no longer valid. + if (gpgpu_partial_write_mask && mf->write) { + assert( gpgpu_no_dl1 ); // gpgpu_partial_write_mask is not supported with caches for now + } +#endif //#if 0 //old chunking no longer valid + dram_push(dram[mc_id], + tlx.bk, tlx.row, tlx.col, + mf->nbytes_L1, mf->write, + mf->wid, mf->sid, mf->cache_hits_waiting, mf->addr, mf); + memlatstat_dram_access(mf, mc_id, tlx.bk); + if (mf->mshr) mshr_update_status(mf->mshr,IN_DRAM_REQ_QUEUE); + } +} + +void* mem_ctrl_pop( int mc_id ) +{ + mem_fetch_t* mf; + if (gpgpu_cache_dl2_opt) { + mf = L2c_pop(dram[mc_id]); + if (mf && mf->mshr && mf->mshr->insts[0].callback.function) { + dram_callback_t* cb = &(mf->mshr->insts[0].callback); + cb->function(cb->instruction, cb->thread); + } + return mf; + } else { + mf = static_cast<mem_fetch_t*> (dq_pop(dram[mc_id]->returnq)); //dram_pop(dram[mc_id]); + if (mf) mf->type = REPLY_DATA; + if (mf && mf->mshr && mf->mshr->insts[0].callback.function) { + dram_callback_t* cb = &(mf->mshr->insts[0].callback); + cb->function(cb->instruction, cb->thread); + } + return mf; + } +} + +void* mem_ctrl_top( int mc_id ) +{ + mem_fetch_t* mf; + if (gpgpu_cache_dl2_opt) { + return L2c_top(dram[mc_id]); + } else { + mf = static_cast<mem_fetch_t*> (dq_top(dram[mc_id]->returnq));//dram_top(dram[mc_id]); + if (mf) mf->type = REPLY_DATA; + return mf ;//dram_top(dram[mc_id]); + } +} + +void get_dram_output ( dram_t* dram_p ) +{ + mem_fetch_t* mf; + mem_fetch_t* mf_top; + mf_top = (mem_fetch_t*) dram_top(dram_p); //test + if (mf_top) { + if (mf_top->type == DUMMY_READ) { + dram_pop(dram_p); + free(mf_top); + freed_dummy_read_mfs++; + return; + } + } + if (gpgpu_cache_dl2_opt) { + L2c_get_dram_output( dram_p ); + } else { + if ( dq_full(dram_p->returnq) ) return; + mf = (mem_fetch_t*) dram_pop(dram_p); + assert (mf_top==mf ); + if (mf) { + dq_push(dram_p->returnq, mf); + if (mf->mshr) mshr_update_status(mf->mshr,IN_DRAMRETURN_Q); + } + } +} + +void dram_log (int task ) { + static void ** mrqq_Dist; //memory request queue inside DRAM + if (task == CREATELOG) { + mrqq_Dist = (void **) calloc(gpu_n_mem,sizeof(void*)); + for (unsigned i=0;i<gpu_n_mem;i++) { + if (dram[i]->queue_limit) + mrqq_Dist[i] = StatCreate("mrqq_length",1,dram[i]->queue_limit); + else //queue length is unlimited; + mrqq_Dist[i] = StatCreate("mrqq_length",1,64); //track up to 64 entries + } + } else if (task == SAMPLELOG) { + for (unsigned i=0;i<gpu_n_mem;i++) { + StatAddSample(mrqq_Dist[i], dram_que_length(dram[i])); + } + } else if (task == DUMPLOG) { + for (unsigned i=0;i<gpu_n_mem;i++) { + printf ("Queue Length DRAM[%d] ",i);StatDisp(mrqq_Dist[i]); + } + } +} + +void dramqueue_latency_log_dump() +{ + for (unsigned i=0;i<gpu_n_mem;i++) { + printf ("(LOGB2)Latency DRAM[%d] ",i);StatDisp(dram[i]->mrqq->lat_stat); + printf ("(LOGB2)Latency DRAM[%d] ",i);StatDisp(dram[i]->rwq->lat_stat); + } +} + +//Find next clock domain and increment its time +inline int next_clock_domain(void) +{ + double smallest = min3(core_time,icnt_time,dram_time); + int mask = 0x00; + if (gpgpu_cache_dl2_opt //when no-L2 it will never be L2's turn + && ( l2_time <= smallest) ) { + smallest = l2_time; + mask |= L2 ; + l2_time += l2_period; + } + if ( icnt_time <= smallest ) { + mask |= ICNT; + icnt_time += icnt_period; + } + if ( dram_time <= smallest ) { + mask |= DRAM; + dram_time += dram_period; + } + if ( core_time <= smallest ) { + mask |= CORE; + core_time += core_period; + } + return mask; +} + +extern time_t simulation_starttime; +void gpu_sim_loop( int grid_num ) +{ + int clock_mask = next_clock_domain(); + + // shader core loading (pop from ICNT into shader core) follows CORE clock + if (clock_mask & CORE ) { + for (int i=0;i<gpu_n_tpc;i++) { + fq_pop(i); + } + } + + if (clock_mask & ICNT) { + // pop from memory controller to interconnect + static unsigned int *rt_size = NULL; + if (!rt_size) rt_size = (unsigned int*) malloc ((gpu_n_tpc+gpu_n_mem)*sizeof(unsigned int)); + memset(rt_size, 0, (gpu_n_tpc+gpu_n_mem)*sizeof(unsigned int)); + + for (unsigned i=0;i<gpu_n_mem;i++) { + + mem_fetch_t* mf; + + mf = (mem_fetch_t*) mem_ctrl_top(i); //(returns L2_top or DRAM returnq top) + + if (mf) { + mf->source_node = mem2device(i); + assert( mf->type != RD_REQ && mf->type != WT_REQ ); // never should a request come out from L2 or dram + if (!mf->write) { + int return_dev = -1; + return_dev = mf->sid / gpu_concentration; + assert(return_dev != -1); + // check icnt resource for READ data return + rt_size[return_dev] = mf->nbytes_L1; + if ( icnt_has_buffer( mem2device(i), rt_size) ) { + if (mf->mshr) mshr_update_status(mf->mshr,IN_ICNT2SHADER); + memlatstat_icnt2sh_push(mf); + time_vector_update(mf->mshr->insts[0].uid ,MR_2SH_ICNT_PUSHED,gpu_sim_cycle+gpu_tot_sim_cycle,RD_REQ); + icnt_push( mem2device(i), return_dev, mf, mf->nbytes_L1); + mem_ctrl_pop(i); + } else { + gpu_stall_icnt2sh++; + } + rt_size[return_dev] = 0; // clean up for the next dram_pop + } else { + time_vector_update(mf->request_uid ,MR_2SH_ICNT_PUSHED,gpu_sim_cycle+gpu_tot_sim_cycle,WT_REQ ) ; + mem_ctrl_pop(i); + free(mf); + freed_L1write_mfs++; + gpgpu_n_processed_writes++; + } + } + } + } + + if (clock_mask & DRAM) { + for (unsigned i=0;i<gpu_n_mem;i++) { + get_dram_output ( dram[i] ); + } + // Issue the dram command (scheduler + delay model) + for (unsigned i=0;i<gpu_n_mem;i++) { + dram_issueCMD(dram[i]); + } + dram_log(SAMPLELOG); + } + + // L2 operations follow L2 clock domain + if (clock_mask & L2) { + for (unsigned i=0;i<gpu_n_mem;i++) { + L2c_process_dram_output ( dram[i], i ); // pop from dram + L2c_push_miss_to_dram ( dram[i] ); //push to dram + L2c_service_mem_req ( dram[i], i ); // pop(push) from(to) icnt2l2(l2toicnt) queues; service l2 requests + } + if (gpgpu_cache_dl2_opt) { // L2 cache enabled + for (unsigned i=0;i<gpu_n_mem;i++) { + L2c_update_stat( dram[i] ); + } + } + if (gpgpu_cache_dl2_opt) { //take a sample of l2c queue lengths + L2c_log(SAMPLELOG); + } + } + + if (clock_mask & ICNT) { + // pop memory request from ICNT and + // push it to the proper memory controller (L2 or DRAM controller) + for (unsigned i=0;i<gpu_n_mem;i++) { + + if ( mem_ctrl_full(i) ) { + gpu_stall_dramfull++; + continue; + } + + mem_fetch_t* mf; + mf = (mem_fetch_t*) icnt_pop( mem2device(i) ); + + if (mf) { + if (mf->type==RD_REQ) { + time_vector_update(mf->mshr->insts[0].uid ,MR_DRAMQ,gpu_sim_cycle+gpu_tot_sim_cycle,mf->type ) ; + } else { + time_vector_update(mf->request_uid ,MR_DRAMQ,gpu_sim_cycle+gpu_tot_sim_cycle,mf->type ) ; + } + memlatstat_icnt2mem_pop(mf); + mem_ctrl_push( i, mf ); + } + } + icnt_transfer( ); + } + + if (clock_mask & CORE) { + // L1 cache + shader core pipeline stages + for (unsigned i=0;i<gpu_n_shader;i++) { + if (sc[i]->not_completed || more_thread) + shader_cycle(sc[i], i, grid_num); + sc[i]->gpu_cycle++; + } + gpu_sim_cycle++; + + for (unsigned i=0;i<gpu_n_shader && more_thread;i++) { + if (gpgpu_spread_blocks_across_cores) { + int cta_issue_count = 1; + if ( ( (unsigned) (sc[i]->n_active_cta + cta_issue_count) <= max_cta_per_shader(sc[i]) ) + && g_total_cta_left ) { + int j; + for (j=0;j<cta_issue_count;j++) { + issue_block2core(sc[i], grid_num); + } + if (!g_total_cta_left) { + more_thread = 0; + } + assert( g_total_cta_left > -1 ); + } + } else { + if (!(sc[i]->not_completed)) + more_thread = issue_block2core(sc[i], grid_num); + } + } + + + // Flush the caches once all of threads are completed. + if (gpgpu_flush_cache) { + int all_threads_complete = 1 ; + for (unsigned i=0;i<gpu_n_shader;i++) { + if (sc[i]->not_completed == 0) { + shader_cache_flush(sc[i]); + } else { + all_threads_complete = 0 ; + } + } + if (all_threads_complete) { + printf("Flushed L1 caches...\n"); + if (gpgpu_cache_dl2_opt) { + int dlc = 0; + for (unsigned i=0;i<gpu_n_mem;i++) { + dlc = L2c_cache_flush(dram[i]); + printf("Dirty lines flushed from L2 %d is %d \n", i, dlc ); + } + } + } + } + + if (!(gpu_sim_cycle % gpu_stat_sample_freq)) { + time_t days, hrs, minutes, sec; + time_t curr_time; + time(&curr_time); + unsigned long long elapsed_time = MAX(curr_time - simulation_starttime, 1); + days = elapsed_time/(3600*24); + hrs = elapsed_time/3600 - 24*days; + minutes = elapsed_time/60 - 60*(hrs + 24*days); + sec = elapsed_time - 60*(minutes + 60*(hrs + 24*days)); + printf("cycles: %lld inst.: %lld (ipc=%4.1f) sim_rate=%u (inst/sec) elapsed = %u:%u:%02u:%02u / %s", + gpu_tot_sim_cycle + gpu_sim_cycle, gpu_tot_sim_insn + gpu_sim_insn, + (double)gpu_sim_insn/(double)gpu_sim_cycle, + (unsigned)((gpu_tot_sim_insn+gpu_sim_insn) / elapsed_time), + (unsigned)days,(unsigned)hrs,(unsigned)minutes,(unsigned)sec, + ctime(&curr_time)); + fflush(stdout); + memlatstat_lat_pw(); + visualizer_printstat(); + if (gpgpu_runtime_stat && (gpu_runtime_stat_flag != 0) ) { + if (gpu_runtime_stat_flag & GPU_RSTAT_BW_STAT) { + for (unsigned i=0;i<gpu_n_mem;i++) { + dram_print_stat(dram[i],stdout); + } + printf("maxmrqlatency = %d \n", max_mrq_latency); + printf("maxmflatency = %d \n", max_mf_latency); + } + if (gpu_runtime_stat_flag & GPU_RSTAT_DWF_MAP) { + printf("DWF_MS: "); + for (unsigned i=0;i<gpu_n_shader;i++) { + printf("%u ",acc_dyn_pcs[i]); + } + printf("\n"); + print_thread_pc( stdout ); + } + if (gpu_runtime_stat_flag & GPU_RSTAT_SHD_INFO) { + shader_print_runtime_stat( stdout ); + } + if (gpu_runtime_stat_flag & GPU_RSTAT_WARP_DIS) { + print_shader_cycle_distro( stdout ); + } + if (gpu_runtime_stat_flag & GPU_RSTAT_L1MISS) { + shader_print_l1_miss_stat( stdout ); + } + if (gpu_runtime_stat_flag & GPU_RSTAT_PDOM ) { + if (pdom_sched_type) { + printf ("pdom_original_warps_count %d \n",n_pdom_sc_orig_stat ); + printf ("pdom_single_warps_count %d \n",n_pdom_sc_single_stat ); + } + } + if (gpu_runtime_stat_flag & GPU_RSTAT_SCHED ) { + printf("Average Num. Warps Issuable per Shader:\n"); + for (unsigned i=0;i<gpu_n_shader;i++) { + printf("%2.2f ", (float) num_warps_issuable_pershader[i]/ gpu_stat_sample_freq); + num_warps_issuable_pershader[i] = 0; + } + printf("\n"); + } + } + } + + for (unsigned i=0;i<gpu_n_mem;i++) { + acc_mrq_length[i] += dram_que_length(dram[i]); + } + if (!(gpu_sim_cycle % 20000)) { + // deadlock detection + if (gpu_deadlock_detect && gpu_sim_insn == last_gpu_sim_insn) { + gpu_deadlock = 1; + } else { + last_gpu_sim_insn = gpu_sim_insn; + } + } + try_snap_shot(gpu_sim_cycle); + spill_log_to_file (stdout, 0, gpu_sim_cycle); + } +} + +void dump_regs(unsigned sid, unsigned tid) +{ + if ( sid >= gpu_n_shader ) { + printf("shader %u is out of range\n",sid); + return; + } + if ( tid >= gpu_n_thread_per_shader ) { + printf("thread %u is out of range\n",tid); + return; + } + + shader_core_ctx_t *s = sc[sid]; + + ptx_dump_regs( s->thread[tid].ptx_thd_info ); +} + +int ptx_thread_done( void *thr ); + +void shader_dump_istream_state(shader_core_ctx_t *shader, FILE *fout ) +{ + fprintf( fout, "\n"); + for (unsigned t=0; t < gpu_n_thread_per_shader/warp_size; t++ ) { + int tid = t*warp_size; + if ( shader->warp[t].n_completed < warp_size ) { + fprintf( fout, " %u:%3u fetch state = c:%u a4f:%u bw:%u (completed: ", shader->sid, tid, + shader->warp[t].n_completed, + shader->warp[t].n_avail4fetch, + shader->warp[t].n_waiting_at_barrier ); + + for (unsigned i = tid; i < (t+1)*warp_size; i++ ) { + if ( ptx_thread_done(shader->thread[i].ptx_thd_info) ) { + fprintf(fout,"1"); + } else { + fprintf(fout,"0"); + } + if ( (((i+1)%4) == 0) && (i+1) < (t+1)*warp_size ) { + fprintf(fout,","); + } + } + fprintf(fout,")\n"); + } + } +} + +void dump_pipeline_impl( int mask, int s, int m ) +{ +/* + You may want to use this function while running GPGPU-Sim in gdb. + One way to do that is add the following to your .gdbinit file: + + define dp + call dump_pipeline_impl((0x40|0x4|0x1),$arg0,0) + end + + Then, typing "dp 3" will show the contents of the pipeline for shader core 3. +*/ + + printf("Dumping pipeline state...\n"); + if(!mask) mask = 0xFFFFFFFF; + for (unsigned i=0;i<gpu_n_shader;i++) { + if(s != -1) { + i = s; + } + if(mask&1) shader_display_pipeline(sc[i], stdout, 1, mask & 0x2E ); + if(mask&0x40) shader_dump_istream_state(sc[i], stdout); + if(mask&0x100) mshr_print(stdout, sc[i]); + if(s != -1) { + break; + } + } + if(mask&0x10000) { + for (unsigned i=0;i<gpu_n_mem;i++) { + if(m != -1) { + i=m; + } + printf("DRAM / memory controller %u:\n", i); + if(mask&0x100000) dram_print_stat(dram[i],stdout); + if(mask&0x1000000) dram_visualize( dram[i] ); + if(m != -1) { + break; + } + } + } + fflush(stdout); +} + +void dump_pipeline() +{ + dump_pipeline_impl(0,-1,-1); +} diff --git a/src/gpgpu-sim/gpu-sim.h b/src/gpgpu-sim/gpu-sim.h new file mode 100644 index 0000000..e30797e --- /dev/null +++ b/src/gpgpu-sim/gpu-sim.h @@ -0,0 +1,116 @@ +/* + * gpu-sim.h + * + * Copyright (c) 2009 by Tor M. Aamodt, Wilson W. L. Fung, Ali Bakhoda, + * George L. Yuan, Ivan Sham and the + * University of British Columbia + * Vancouver, BC V6T 1Z4 + * All Rights Reserved. + * + * THIS IS A LEGAL DOCUMENT BY DOWNLOADING GPGPU-SIM, YOU ARE AGREEING TO THESE + * TERMS AND CONDITIONS. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * NOTE: The files libcuda/cuda_runtime_api.c and src/cuda-sim/cuda-math.h + * are derived from the CUDA Toolset available from http://www.nvidia.com/cuda + * (property of NVIDIA). The files benchmarks/BlackScholes/ and + * benchmarks/template/ are derived from the CUDA SDK available from + * http://www.nvidia.com/cuda (also property of NVIDIA). The files + * src/gpgpusim_entrypoint.c and src/simplesim-3.0/ are derived from the + * SimpleScalar Toolset available from http://www.simplescalar.com/ + * (property of SimpleScalar LLC) and the files src/intersim/ are derived + * from Booksim (Simulator provided with the textbook "Principles and + * Practices of Interconnection Networks" available from + * http://cva.stanford.edu/books/ppin/). As such, those files are bound by + * the corresponding legal terms and conditions set forth separately (original + * copyright notices are left in files from these sources and where we have + * modified a file our copyright notice appears before the original copyright + * notice). + * + * Using this version of GPGPU-Sim requires a complete installation of CUDA + * which is distributed seperately by NVIDIA under separate terms and + * conditions. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the University of British Columbia nor the names of + * its contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * 4. This version of GPGPU-SIM is distributed freely for non-commercial use only. + * + * 5. No nonprofit user may place any restrictions on the use of this software, + * including as modified by the user, by any other authorized user. + * + * 6. GPGPU-SIM was developed primarily by Tor M. Aamodt, Wilson W. L. Fung, + * Ali Bakhoda, George L. Yuan, at the University of British Columbia, + * Vancouver, BC V6T 1Z4 + */ + +#include <stdio.h> +#include <stdlib.h> +#include <math.h> +#include "zlib.h" + +#include "../option_parser.h" +#include "shader.h" +#include "dram.h" +#include "mem_fetch.h" + +#ifndef GPU_SIM_H +#define GPU_SIM_H + +#define NUM_SHADERS 8 +#define STREAMS_PER_FILE 128 + +//unsigned int run_gpu_sim(int grid_num); + +unsigned int get_converge_point(unsigned int pc, void *thd); + +#define GPU_RSTAT_SHD_INFO 0x1 +#define GPU_RSTAT_BW_STAT 0x2 +#define GPU_RSTAT_WARP_DIS 0x4 +#define GPU_RSTAT_DWF_MAP 0x8 + +//gpgpu_interwarp_mshr_merge +#define TEX_MSHR_MERGE 0x4 +#define CONST_MSHR_MERGE 0x2 +#define GLOBAL_MSHR_MERGE 0x1 + +//Prints out a verbose L1 miss rate per thread for shader 0 +#define GPU_RSTAT_L1MISS 0x10 +#define GPU_RSTAT_PDOM 0x20 +#define GPU_RSTAT_SCHED 0x40 + +//options for gpgpu_memlatency_stat +#define GPU_MEMLATSTAT_MC 0x2 +#define GPU_MEMLATSTAT_QUEUELOGS 0x4 + +//void gpu_reg_options(option_parser_t opp); +//void init_gpu(); +void gpu_print_stat(); + +int mem_ctrl_full( int mc_id ); + +void dramqueue_latency_log_dump(); + +#endif diff --git a/src/gpgpu-sim/histogram.h b/src/gpgpu-sim/histogram.h new file mode 100644 index 0000000..2d39410 --- /dev/null +++ b/src/gpgpu-sim/histogram.h @@ -0,0 +1,128 @@ +/* + * histogram.h + * + * Copyright (c) 2009 by Tor M. Aamodt, Wilson W. L. Fung and the + * University of British Columbia + * Vancouver, BC V6T 1Z4 + * All Rights Reserved. + * + * THIS IS A LEGAL DOCUMENT BY DOWNLOADING GPGPU-SIM, YOU ARE AGREEING TO THESE + * TERMS AND CONDITIONS. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * NOTE: The files libcuda/cuda_runtime_api.c and src/cuda-sim/cuda-math.h + * are derived from the CUDA Toolset available from http://www.nvidia.com/cuda + * (property of NVIDIA). The files benchmarks/BlackScholes/ and + * benchmarks/template/ are derived from the CUDA SDK available from + * http://www.nvidia.com/cuda (also property of NVIDIA). The files from + * src/intersim/ are derived from Booksim (a simulator provided with the + * textbook "Principles and Practices of Interconnection Networks" available + * from http://cva.stanford.edu/books/ppin/). As such, those files are bound by + * the corresponding legal terms and conditions set forth separately (original + * copyright notices are left in files from these sources and where we have + * modified a file our copyright notice appears before the original copyright + * notice). + * + * Using this version of GPGPU-Sim requires a complete installation of CUDA + * which is distributed seperately by NVIDIA under separate terms and + * conditions. To use this version of GPGPU-Sim with OpenCL requires a + * recent version of NVIDIA's drivers which support OpenCL. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the University of British Columbia nor the names of + * its contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * 4. This version of GPGPU-SIM is distributed freely for non-commercial use only. + * + * 5. No nonprofit user may place any restrictions on the use of this software, + * including as modified by the user, by any other authorized user. + * + * 6. GPGPU-SIM was developed primarily by Tor M. Aamodt, Wilson W. L. Fung, + * Ali Bakhoda, George L. Yuan, at the University of British Columbia, + * Vancouver, BC V6T 1Z4 + */ + +#ifndef HISTOGRAM_H +#define HISTOGRAM_H + +#ifdef __cplusplus + +#include <string> + +class binned_histogram { +protected: + + std::string m_name; + int m_nbins; + int *m_bins; // bin boundaries + int *m_bin_cnts; // counters + int m_maximum; // the maximum sample + +public: + + binned_histogram (std::string name = "", int nbins = 32, int* bins = NULL); + + binned_histogram (const binned_histogram& other); + + void reset_bins (); + + void add2bin (int sample); + + void fprint (FILE *fout); + + virtual ~binned_histogram (); + +}; + +class pow2_histogram : public binned_histogram { + +public: + + pow2_histogram ( std::string name = "", int nbins = 32, int* bins = NULL); + + ~pow2_histogram() {} + + void add2bin (int sample); + +}; + +class linear_histogram : public binned_histogram { + +private: + + int m_stride; + +public: + + linear_histogram (int stride = 1, const char *name = NULL, int nbins = 32, int* bins = NULL); + + ~linear_histogram() {} + + void add2bin (int sample); + +}; + +#endif + +#endif /* HISTOGRAM_H */ diff --git a/src/gpgpu-sim/icnt_wrapper.cc b/src/gpgpu-sim/icnt_wrapper.cc new file mode 100644 index 0000000..3e38269 --- /dev/null +++ b/src/gpgpu-sim/icnt_wrapper.cc @@ -0,0 +1,97 @@ +/* + * icnt_wrapper.c + * + * Copyright (c) 2009 by Tor M. Aamodt, Wilson W. L. Fung, Ali Bakhoda, + * George L. Yuan and the + * University of British Columbia + * Vancouver, BC V6T 1Z4 + * All Rights Reserved. + * + * THIS IS A LEGAL DOCUMENT BY DOWNLOADING GPGPU-SIM, YOU ARE AGREEING TO THESE + * TERMS AND CONDITIONS. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * NOTE: The files libcuda/cuda_runtime_api.c and src/cuda-sim/cuda-math.h + * are derived from the CUDA Toolset available from http://www.nvidia.com/cuda + * (property of NVIDIA). The files benchmarks/BlackScholes/ and + * benchmarks/template/ are derived from the CUDA SDK available from + * http://www.nvidia.com/cuda (also property of NVIDIA). The files from + * src/intersim/ are derived from Booksim (a simulator provided with the + * textbook "Principles and Practices of Interconnection Networks" available + * from http://cva.stanford.edu/books/ppin/). As such, those files are bound by + * the corresponding legal terms and conditions set forth separately (original + * copyright notices are left in files from these sources and where we have + * modified a file our copyright notice appears before the original copyright + * notice). + * + * Using this version of GPGPU-Sim requires a complete installation of CUDA + * which is distributed seperately by NVIDIA under separate terms and + * conditions. To use this version of GPGPU-Sim with OpenCL requires a + * recent version of NVIDIA's drivers which support OpenCL. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the University of British Columbia nor the names of + * its contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * 4. This version of GPGPU-SIM is distributed freely for non-commercial use only. + * + * 5. No nonprofit user may place any restrictions on the use of this software, + * including as modified by the user, by any other authorized user. + * + * 6. GPGPU-SIM was developed primarily by Tor M. Aamodt, Wilson W. L. Fung, + * Ali Bakhoda, George L. Yuan, at the University of British Columbia, + * Vancouver, BC V6T 1Z4 + */ + +#include "icnt_wrapper.h" +#include <assert.h> +#include "../intersim/interconnect_interface.h" + +icnt_has_buffer_p icnt_has_buffer; +icnt_push_p icnt_push; +icnt_pop_p icnt_pop; +icnt_transfer_p icnt_transfer; +icnt_busy_p icnt_busy; + +extern int g_network_mode; +extern char* g_network_config_filename; + +void icnt_init( unsigned int n_shader, unsigned int n_mem ) +{ + switch (g_network_mode) { + + case INTERSIM: + init_interconnect(g_network_config_filename ,n_shader, n_mem); + icnt_has_buffer = interconnect_has_buffer; + icnt_push = interconnect_push; + icnt_pop = interconnect_pop; + icnt_transfer = advance_interconnect; + icnt_busy = interconnect_busy; + break; + + default: + assert(0); + break; + } +} diff --git a/src/gpgpu-sim/icnt_wrapper.h b/src/gpgpu-sim/icnt_wrapper.h new file mode 100644 index 0000000..998a4f3 --- /dev/null +++ b/src/gpgpu-sim/icnt_wrapper.h @@ -0,0 +1,100 @@ +/* + * icnt_wrapper.h + * + * Copyright (c) 2009 by Tor M. Aamodt, Wilson W. L. Fung, Ali Bakhoda, + * George L. Yuan and the + * University of British Columbia + * Vancouver, BC V6T 1Z4 + * All Rights Reserved. + * + * THIS IS A LEGAL DOCUMENT BY DOWNLOADING GPGPU-SIM, YOU ARE AGREEING TO THESE + * TERMS AND CONDITIONS. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * NOTE: The files libcuda/cuda_runtime_api.c and src/cuda-sim/cuda-math.h + * are derived from the CUDA Toolset available from http://www.nvidia.com/cuda + * (property of NVIDIA). The files benchmarks/BlackScholes/ and + * benchmarks/template/ are derived from the CUDA SDK available from + * http://www.nvidia.com/cuda (also property of NVIDIA). The files from + * src/intersim/ are derived from Booksim (a simulator provided with the + * textbook "Principles and Practices of Interconnection Networks" available + * from http://cva.stanford.edu/books/ppin/). As such, those files are bound by + * the corresponding legal terms and conditions set forth separately (original + * copyright notices are left in files from these sources and where we have + * modified a file our copyright notice appears before the original copyright + * notice). + * + * Using this version of GPGPU-Sim requires a complete installation of CUDA + * which is distributed seperately by NVIDIA under separate terms and + * conditions. To use this version of GPGPU-Sim with OpenCL requires a + * recent version of NVIDIA's drivers which support OpenCL. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the University of British Columbia nor the names of + * its contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * 4. This version of GPGPU-SIM is distributed freely for non-commercial use only. + * + * 5. No nonprofit user may place any restrictions on the use of this software, + * including as modified by the user, by any other authorized user. + * + * 6. GPGPU-SIM was developed primarily by Tor M. Aamodt, Wilson W. L. Fung, + * Ali Bakhoda, George L. Yuan, at the University of British Columbia, + * Vancouver, BC V6T 1Z4 + */ + +#ifndef ICNT_WRAPPER_H +#define ICNT_WRAPPER_H + +// functional interface to the interconnect +typedef int (*icnt_has_buffer_p)(unsigned int input, unsigned int *size); +typedef void (*icnt_push_p)(unsigned int input, unsigned int output, void* data, unsigned int size); +typedef void* (*icnt_pop_p)(unsigned int output); +typedef void (*icnt_transfer_p)( ); +typedef unsigned (*icnt_busy_p)( ); +typedef void (*icnt_drain_p)( ); + + +extern icnt_has_buffer_p icnt_has_buffer; +extern icnt_push_p icnt_push; +extern icnt_pop_p icnt_pop; +extern icnt_transfer_p icnt_transfer; +extern icnt_busy_p icnt_busy; +extern icnt_drain_p icnt_drain; + +/* +// definition of valid gpu network mode. +extern enum { + INTERSIM = 1, + N_NETWORK_MODE +} gpu_network_mode; +*/ +enum network_mode { + INTERSIM = 1, + N_NETWORK_MODE +}; + +void icnt_init( unsigned int n_shader, unsigned int n_mem ); + +#endif diff --git a/src/gpgpu-sim/l2cache.cc b/src/gpgpu-sim/l2cache.cc new file mode 100644 index 0000000..aef837b --- /dev/null +++ b/src/gpgpu-sim/l2cache.cc @@ -0,0 +1,999 @@ +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include <list> +#include <set> +#include "../tr1_hash_map.h" // for unordered_map failback + +#include "../option_parser.h" +#include "mem_fetch.h" +#include "dram.h" +#include "gpu-cache.h" +#include "histogram.h" +#include "l2cache.h" +#include "../intersim/statwraper.h" + +class L2c_mshr; +class L2c_miss_tracker; +class L2c_access_locality; + +mem_fetch_t* g_debug_mf = NULL; + +// L2 cache block (include the cache model + flow controls) +struct L2cacheblk +{ + shd_cache_t *L2cache; + + delay_queue *cbtoL2queue; //latency 10 + delay_queue *cbtoL2writequeue; + delay_queue *dramtoL2queue; //latency 10 + delay_queue *dramtoL2writequeue; + delay_queue *L2todramqueue; //latency 0 + delay_queue *L2todram_wbqueue; + delay_queue *L2tocbqueue; //latency 0 + + mem_fetch_t *L2request; //request currently being serviced by the L2 Cache + + L2c_mshr *m_mshr; // mshr model + L2c_miss_tracker *m_missTracker; // tracker observing for redundant misses + L2c_access_locality *m_accessLocality; // tracking true locality of L2 Cache access + + L2cacheblk(size_t linesize); + ~L2cacheblk(); +}; + +// external dependencies +extern unsigned long long int addrdec_mask[5]; +extern dram_t **dram; +extern int gpgpu_dram_sched_queue_size; +extern unsigned int gpu_n_shader; +extern unsigned int gpu_n_mem; +extern unsigned long long gpu_sim_cycle; +extern unsigned made_write_mfs; +extern unsigned freed_L1write_mfs; +extern unsigned freed_L2write_mfs; +extern unsigned int gpgpu_n_sent_writes; +extern unsigned int gpgpu_n_processed_writes; +extern unsigned g_next_request_uid; + +void memlatstat_icnt2sh_push(mem_fetch_t *mf); +void memlatstat_dram_access(mem_fetch_t *mf, unsigned dram_id, unsigned bank); +void memlatstat_start(mem_fetch_t *mf); +unsigned memlatstat_done(mem_fetch_t *mf); + +// option +char *gpgpu_L2_queue_config; +int gpgpu_l2_readoverwrite = 0; +int l2_ideal = 0; + +void L2c_options(option_parser_t opp) +{ + option_parser_register(opp, "-gpgpu_L2_queue", OPT_CSTR, &gpgpu_L2_queue_config, + "L2 data cache queue length and latency config", + "0:0:0:0:0:0:10:10"); + + option_parser_register(opp, "-gpgpu_l2_readoverwrite", OPT_BOOL, &gpgpu_l2_readoverwrite, + "Prioritize read requests over write requests for L2", + "0"); + + option_parser_register(opp, "-l2_ideal", OPT_BOOL, &l2_ideal, + "Use a ideal L2 cache that always hit", + "0"); +} + +// stats +unsigned L2_write_miss = 0; +unsigned L2_write_hit = 0; +unsigned L2_read_hit = 0; +unsigned L2_read_miss = 0; +unsigned int *L2_cbtoL2length; +unsigned int *L2_cbtoL2writelength; +unsigned int *L2_L2tocblength; +unsigned int *L2_dramtoL2length; +unsigned int *L2_dramtoL2writelength; +unsigned int *L2_L2todramlength; + +//////////////////////////////////////////////// +// L2 MSHR model + +class L2c_mshr +{ +private: + typedef std::list<const mem_fetch_t*> mem_fetch_list; + typedef tr1_hash_map<address_type, mem_fetch_list> L2missGroup; + L2missGroup m_L2missgroup; // structure tracking redundant dram access + + struct active_chain { + address_type cacheTag; + mem_fetch_list *list; + active_chain() : cacheTag(0xDEADBEEF), list(NULL) { } + }; + active_chain m_active_mshr_chain; + size_t m_linesize; // L2 cache line size + + const size_t m_n_entries; // total number of entries available + size_t m_entries_used; // number of entries in use + + int m_n_miss; + int m_n_miss_serviced_by_dram; + int m_n_mshr_hits; + size_t m_max_entries_used; + + address_type cache_tag(const mem_fetch_t *mf) const + { + // return mf->addr; + return (mf->addr & ~(m_linesize - 1)); + } + +public: + L2c_mshr(size_t linesize, size_t n_entries = 64) + : m_linesize(linesize), m_n_entries(n_entries), m_entries_used(0), + m_n_miss(0), m_n_miss_serviced_by_dram(0), m_n_mshr_hits(0), m_max_entries_used(0) { } + + // add a cache miss to MSHR, return true if this access is hit another existing entry and merges with it + bool new_miss(const mem_fetch_t *mf); + + // notify MSHR that a new cache line has been fetched, activate the associated MSHR chain + void miss_serviced(const mem_fetch_t *mf); + + // probe if there are pending hits left in this MSHR chain + bool mshr_chain_empty(); + + // peek the first entry in the active MSHR chain + mem_fetch_t *mshr_chain_top(); + + // pop the first entry in the active MSHR chain + void mshr_chain_pop(); + + void print(FILE *fout = stdout); + void print_stat(FILE *fout = stdout); +}; + +bool L2c_mshr::new_miss(const mem_fetch_t *mf) +{ + address_type cacheTag = cache_tag(mf); + mem_fetch_list &missGroup = m_L2missgroup[cacheTag]; + + bool mshr_hit = not missGroup.empty(); + + missGroup.push_front(mf); + + m_n_miss += 1; + if (mshr_hit) + m_n_mshr_hits += 1; + m_entries_used += 1; + m_max_entries_used = std::max(m_max_entries_used, m_entries_used); + + return mshr_hit; +} + +void L2c_mshr::miss_serviced(const mem_fetch_t *mf) +{ + assert(m_active_mshr_chain.list == NULL); + address_type cacheTag = cache_tag(mf); + L2missGroup::iterator missGroup = m_L2missgroup.find(cacheTag); + if (missGroup == m_L2missgroup.end() || mf->type == L2_WTBK_DATA) { + assert(mf->type == L2_WTBK_DATA); // only this returning mem req can be missed by the MSHR + return; + } + assert(missGroup->first == cacheTag); + + m_active_mshr_chain.cacheTag = cacheTag; + m_active_mshr_chain.list = &(missGroup->second); + + m_n_miss_serviced_by_dram += 1; +} + +bool L2c_mshr::mshr_chain_empty() +{ + return (m_active_mshr_chain.list == NULL); +} + +mem_fetch_t *L2c_mshr::mshr_chain_top() +{ + const mem_fetch_t *mf = m_active_mshr_chain.list->back(); + assert(cache_tag(mf) == m_active_mshr_chain.cacheTag); + + return const_cast<mem_fetch_t*>(mf); +} + +void L2c_mshr::mshr_chain_pop() +{ + m_entries_used -= 1; + m_active_mshr_chain.list->pop_back(); + if (m_active_mshr_chain.list->empty()) { + address_type cacheTag = m_active_mshr_chain.cacheTag; + m_L2missgroup.erase(cacheTag); + m_active_mshr_chain.list = NULL; + } +} + +void L2c_mshr::print(FILE *fout) +{ + fprintf(fout, "L2c MSHR: n_entries_used = %zu\n", m_entries_used); + L2missGroup::iterator missGroup; + for (missGroup = m_L2missgroup.begin(); missGroup != m_L2missgroup.end(); ++missGroup) { + fprintf(fout, "%#08x: ", missGroup->first); + mem_fetch_list &mf_list = missGroup->second; + for (mem_fetch_list::iterator imf = mf_list.begin(); imf != mf_list.end(); ++imf) { + fprintf(fout, "%p:%d ", *imf, (*imf)->request_uid); + } + fprintf(fout, "\n"); + } +} + +void L2c_mshr::print_stat(FILE *fout) +{ + fprintf(fout, "L2c MSHR: max_entry = %zu, n_miss = %d, n_mshr_hits = %d, n_serviced_by_dram %d\n", + m_max_entries_used, m_n_miss, m_n_mshr_hits, m_n_miss_serviced_by_dram); +} + +//////////////////////////////////////////////// +// track redundant dram access generated by L2 cache +class L2c_miss_tracker +{ +private: + typedef std::set<mem_fetch_t*> mem_fetch_set; + typedef tr1_hash_map<address_type, mem_fetch_set> L2missGroup; + L2missGroup m_L2missgroup; // structure tracking redundant dram access + size_t m_linesize; // L2 cache line size + + typedef tr1_hash_map<address_type, int> L2redundantCnt; + L2redundantCnt m_L2redundantCnt; + + int m_totalL2redundantAcc; + + address_type cache_tag(const mem_fetch_t *mf) const + { + // return mf->addr; + return (mf->addr & ~(m_linesize - 1)); + } + +public: + L2c_miss_tracker(size_t linesize) : m_linesize(linesize), m_totalL2redundantAcc(0) { } + void new_miss(mem_fetch_t *mf); + void miss_serviced(mem_fetch_t *mf); + + void print(FILE *fout, bool brief = true); + void print_stat(FILE *fout, bool brief = true); + +}; + +void L2c_miss_tracker::new_miss(mem_fetch_t *mf) +{ + address_type cacheTag = cache_tag(mf); + mem_fetch_set &missGroup = m_L2missgroup[cacheTag]; + + if (missGroup.size() != 0) { + m_L2redundantCnt[cacheTag] += 1; + m_totalL2redundantAcc += 1; + } + + missGroup.insert(mf); +} + +void L2c_miss_tracker::miss_serviced(mem_fetch_t *mf) +{ + address_type cacheTag = cache_tag(mf); + L2missGroup::iterator iMissGroup = m_L2missgroup.find(cacheTag); + if (iMissGroup == m_L2missgroup.end()) return; // this is possible for write miss + mem_fetch_set &missGroup = iMissGroup->second; + + missGroup.erase(mf); + + // remove the miss group if it goes empty + if (missGroup.empty()) { + m_L2missgroup.erase(iMissGroup); + } +} + +void L2c_miss_tracker::print(FILE *fout, bool brief) +{ + L2missGroup::iterator iMissGroup; + for (iMissGroup = m_L2missgroup.begin(); iMissGroup != m_L2missgroup.end(); ++iMissGroup) { + fprintf(fout, "%#08x: ", iMissGroup->first); + for (mem_fetch_set::iterator iMemSet = iMissGroup->second.begin(); iMemSet != iMissGroup->second.end(); ++iMemSet) { + fprintf(fout, "%p ", *iMemSet); + } + fprintf(fout, "\n"); + } +} + +void L2c_miss_tracker::print_stat(FILE *fout, bool brief) +{ + fprintf(fout, "RedundantMiss = %d\n", m_totalL2redundantAcc); + if (brief == true) return; + fprintf(fout, " Detail:"); + for (L2redundantCnt::iterator iL2rc = m_L2redundantCnt.begin(); iL2rc != m_L2redundantCnt.end(); ++iL2rc) { + fprintf(fout, "%#08x:%d ", iL2rc->first, iL2rc->second); + } + fprintf(fout, "\n"); +} + +//////////////////////////////////////////////// +// track all locality of L2 cache access +class L2c_access_locality +{ +private: + size_t m_linesize; // L2 cache line size + + typedef tr1_hash_map<address_type, int> L2accCnt; + L2accCnt m_L2accCnt; + + int m_totalL2accAcc; + + address_type cache_tag(const mem_fetch_t *mf) const + { + // return mf->addr; + return (mf->addr & ~(m_linesize - 1)); + } + +public: + L2c_access_locality(size_t linesize) : m_linesize(linesize), m_totalL2accAcc(0) { } + void access(mem_fetch_t *mf); + + void print_stat(FILE *fout, bool brief = true); + +}; + +void L2c_access_locality::access(mem_fetch_t *mf) +{ + address_type cacheTag = cache_tag(mf); + m_L2accCnt[cacheTag] += 1; + m_totalL2accAcc += 1; +} + +void L2c_access_locality::print_stat(FILE *fout, bool brief) +{ + float access_locality = (float) m_totalL2accAcc / m_L2accCnt.size(); + fprintf(fout, "Access Locality = %d / %zu (%f) \n", m_totalL2accAcc, m_L2accCnt.size(), access_locality); + if (brief == true) return; + fprintf(fout, " Detail:"); + pow2_histogram locality_histo(" Hits"); + for (L2accCnt::iterator iL2rc = m_L2accCnt.begin(); iL2rc != m_L2accCnt.end(); ++iL2rc) { + locality_histo.add2bin(iL2rc->second); + // fprintf(fout, "%#08x:%d\n", iL2rc->first, iL2rc->second); + } + locality_histo.fprint(fout); + fprintf(fout, "\n"); +} + +L2cacheblk::L2cacheblk(size_t linesize) +: m_mshr(new L2c_mshr(linesize)), + m_missTracker(new L2c_miss_tracker(linesize)), + m_accessLocality(new L2c_access_locality(linesize)) +{ } + +L2cacheblk::~L2cacheblk() +{ + delete m_mshr; + delete m_missTracker; + delete m_accessLocality; +} + + +//////////////////////////////////////////////// +// L2 access functions + +// L2 Cache Creation +void L2c_create ( dram_t* dram_p, const char* cache_opt ) +{ + unsigned int shd_n_set; + unsigned int shd_linesize; + unsigned int shd_n_assoc; + unsigned char shd_policy; + + unsigned int L2c_cb_L2_length; + unsigned int L2c_cb_L2w_length; + unsigned int L2c_L2_dm_length; + unsigned int L2c_dm_L2_length; + unsigned int L2c_dm_L2w_length; + unsigned int L2c_L2_cb_length; + unsigned int L2c_L2_cb_minlength; + unsigned int L2c_L2_dm_minlength; + + sscanf(cache_opt,"%d:%d:%d:%c", + &shd_n_set, &shd_linesize, &shd_n_assoc, &shd_policy); + + L2cacheblk *p_L2c = new L2cacheblk(shd_linesize); + + char L2c_name[32]; + snprintf(L2c_name, 32, "L2c_%03d", dram_p->id); + p_L2c->L2cache = shd_cache_create(L2c_name, + shd_n_set, shd_n_assoc, shd_linesize, + shd_policy, 16, ~addrdec_mask[CHIP], + write_through); //write_through maintains old behavior for now + + sscanf(gpgpu_L2_queue_config,"%d:%d:%d:%d:%d:%d:%d:%d", + &L2c_cb_L2_length, &L2c_cb_L2w_length, &L2c_L2_dm_length, + &L2c_dm_L2_length, &L2c_dm_L2w_length, &L2c_L2_cb_length, + &L2c_L2_cb_minlength, &L2c_L2_dm_minlength ); + //(<name>,<latency>,<min_length>,<max_length>) + p_L2c->cbtoL2queue = dq_create("cbtoL2queue", 0,0,L2c_cb_L2_length); + p_L2c->cbtoL2writequeue = dq_create("cbtoL2writequeue", 0,0,L2c_cb_L2w_length); + p_L2c->L2todramqueue = dq_create("L2todramqueue", 0,L2c_L2_dm_minlength,L2c_L2_dm_length); + p_L2c->dramtoL2queue = dq_create("dramtoL2queue", 0,0,L2c_dm_L2_length); + p_L2c->dramtoL2writequeue = dq_create("dramtoL2writequeue",0,0,L2c_dm_L2w_length); + p_L2c->L2tocbqueue = dq_create("L2tocbqueue", 0,L2c_L2_cb_minlength,L2c_L2_cb_length); + + p_L2c->L2todram_wbqueue = dq_create("L2todram_wbqueue", 0,L2c_L2_dm_minlength, + L2c_L2_dm_minlength + gpgpu_dram_sched_queue_size + L2c_dm_L2_length); + + p_L2c->L2request = NULL; + + assert(dram_p->m_L2cache == NULL); + dram_p->m_L2cache = reinterpret_cast<void*>(p_L2c); +} + +unsigned L2c_get_linesize( dram_t *dram_p ) +{ + assert(dram_p->m_L2cache != NULL); + L2cacheblk *p_L2c = reinterpret_cast<L2cacheblk*>(dram_p->m_L2cache); + return p_L2c->L2cache->line_sz; +} + +int L2c_full( dram_t *dram_p ) +{ + assert(dram_p->m_L2cache != NULL); + L2cacheblk *p_L2c = reinterpret_cast<L2cacheblk*>(dram_p->m_L2cache); + return(dq_full(p_L2c->cbtoL2queue) || dq_full(p_L2c->cbtoL2writequeue)); +} + +void L2c_push( dram_t *dram_p, mem_fetch_t *mf ) +{ + assert(dram_p->m_L2cache != NULL); + L2cacheblk *p_L2c = reinterpret_cast<L2cacheblk*>(dram_p->m_L2cache); + + if (gpgpu_l2_readoverwrite && mf->write) + dq_push(p_L2c->cbtoL2writequeue, mf); + else + dq_push(p_L2c->cbtoL2queue, mf); + p_L2c->m_accessLocality->access(mf); + if (mf->mshr) mshr_update_status(mf->mshr, IN_CBTOL2QUEUE); +} + +mem_fetch_t* L2c_pop( dram_t *dram_p ) +{ + assert(dram_p->m_L2cache != NULL); + L2cacheblk *p_L2c = reinterpret_cast<L2cacheblk*>(dram_p->m_L2cache); + + mem_fetch_t *mf; + mf = (mem_fetch_t*)dq_pop(p_L2c->L2tocbqueue); + + return mf; +} + +mem_fetch_t* L2c_top( dram_t *dram_p ) +{ + assert(dram_p->m_L2cache != NULL); + L2cacheblk *p_L2c = reinterpret_cast<L2cacheblk*>(dram_p->m_L2cache); + + return (mem_fetch_t*)dq_top(p_L2c->L2tocbqueue); +} + +void L2c_qlen ( dram_t *dram_p ) +{ + assert(dram_p->m_L2cache != NULL); + L2cacheblk *p_L2c = reinterpret_cast<L2cacheblk*>(dram_p->m_L2cache); + + printf("\n"); + printf("cb->L2{%d}\tcb->L2w{%d}\tL2->cb{%d}\n", + p_L2c->cbtoL2queue->length, + p_L2c->cbtoL2writequeue->length, + p_L2c->L2tocbqueue->length); + printf("dm->L2{%d}\tdm->L2w{%d}\tL2->dm{%d}\tL2->wb_dm{%d}\n", + p_L2c->dramtoL2queue->length, + p_L2c->dramtoL2writequeue->length, + p_L2c->L2todramqueue->length, + p_L2c->L2todram_wbqueue->length); +} + +// service memory request in icnt-to-L2 queue, writing to L2 as necessary +// (if L2 writeback miss, writeback to memory) +void L2c_service_mem_req ( dram_t* dram_p, int dm_id ) +{ + assert(dram_p->m_L2cache != NULL); + L2cacheblk *p_L2c = reinterpret_cast<L2cacheblk*>(dram_p->m_L2cache); + + mem_fetch_t* mf; + + if (!p_L2c->L2request) { + //if not servicing L2 cache request.. + p_L2c->L2request = (mem_fetch_t*) dq_pop(p_L2c->cbtoL2queue); //..then get one + if (!p_L2c->L2request) { + p_L2c->L2request = (mem_fetch_t*) dq_pop(p_L2c->cbtoL2writequeue); + } + } + + mf = p_L2c->L2request; + + if (!mf) return; + + switch (mf->type) { + case RD_REQ: + case WT_REQ: { + shd_cache_line_t *hit_cacheline = shd_cache_access(p_L2c->L2cache, + mf->addr, + 4, mf->write, + gpu_sim_cycle); + + if (hit_cacheline || l2_ideal) { //L2 Cache Hit; reads are sent as a single command and need to be stored + if (!mf->write) { //L2 Cache Read + if ( dq_full(p_L2c->L2tocbqueue) ) { + p_L2c->L2cache->access--; + } else { + mf->type = REPLY_DATA; + dq_push(p_L2c->L2tocbqueue, mf); + // at this point, should first check if earlier L2 miss is ready to be serviced + // if so, service earlier L2 miss first + p_L2c->L2request = NULL; //finished servicing + L2_read_hit++; + memlatstat_icnt2sh_push(mf); + if (mf->mshr) mshr_update_status(mf->mshr, IN_L2TOCBQUEUE_HIT); + } + } else { //L2 Cache Write aka servicing L1 Writeback + p_L2c->L2request = NULL; + L2_write_hit++; + freed_L1write_mfs++; + free(mf); //writeback from L1 successful + gpgpu_n_processed_writes++; + } + } else { + // L2 Cache Miss; issue commands accordingly + if ( dq_full(p_L2c->L2todramqueue) ) { + p_L2c->L2cache->miss--; + p_L2c->L2cache->access--; + } else { + // if a miss hit the mshr, that means there is another inflight request for the same data + // this miss just need to access the cache later when this request is serviced + bool mshr_hit = p_L2c->m_mshr->new_miss(mf); + if (not mshr_hit) { + if (!mf->write) { + dq_push(p_L2c->L2todramqueue, mf); + } else { + // if request is writeback from L1 and misses, + // then redirect mf writes to dram (no write allocate) + mf->nbytes_L2 = mf->nbytes_L1 - READ_PACKET_SIZE; + dq_push(p_L2c->L2todramqueue, mf); + } + } + if (mf->mshr) mshr_update_status(mf->mshr, IN_L2TODRAMQUEUE); + p_L2c->L2request = NULL; + } + } + } + break; + default: assert(0); + } +} + +// service memory request in L2todramqueue, pushing to dram +void L2c_push_miss_to_dram ( dram_t* dram_p ) +{ + assert(dram_p->m_L2cache != NULL); + L2cacheblk *p_L2c = reinterpret_cast<L2cacheblk*>(dram_p->m_L2cache); + + mem_fetch_t* mf; + + if ( gpgpu_dram_sched_queue_size && dram_full(dram_p) ) return; + + mf = (mem_fetch_t*) dq_pop(p_L2c->L2todram_wbqueue); //prioritize writeback + if (!mf) mf = (mem_fetch_t*) dq_pop(p_L2c->L2todramqueue); + if (mf) { + if (mf->write) { + L2_write_miss++; + } else { + L2_read_miss++; + } + p_L2c->m_missTracker->new_miss(mf); + memlatstat_dram_access(mf, dram_p->id, mf->tlx.bk); + dram_push(dram_p, + mf->tlx.bk, mf->tlx.row, mf->tlx.col, + mf->nbytes_L2, mf->write, + mf->wid, mf->sid, mf->cache_hits_waiting, mf->addr, mf); + if (mf->mshr) mshr_update_status(mf->mshr, IN_DRAM_REQ_QUEUE); + } +} + +//Service writes that are finished in Dram +//only updates the stats and frees the mf +void dramtoL2_service_write(mem_fetch_t * mf) { + freed_L2write_mfs++; + free(mf); + gpgpu_n_processed_writes++; +} + +// pop completed memory request from dram and push it to dram-to-L2 queue +void L2c_get_dram_output ( dram_t* dram_p ) +{ + assert(dram_p->m_L2cache != NULL); + L2cacheblk *p_L2c = reinterpret_cast<L2cacheblk*>(dram_p->m_L2cache); + + mem_fetch_t* mf; + mem_fetch_t* mf_top; + if ( dq_full(p_L2c->dramtoL2queue) || dq_full(p_L2c->dramtoL2writequeue) ) return; + mf_top = (mem_fetch_t*) dram_top(dram_p); //test + mf = (mem_fetch_t*) dram_pop(dram_p); + assert (mf_top==mf ); + if (mf) { + if (gpgpu_l2_readoverwrite && mf->write) + dq_push(p_L2c->dramtoL2writequeue, mf); + else + dq_push(p_L2c->dramtoL2queue, mf); + if (mf->mshr) mshr_update_status(mf->mshr, IN_DRAMTOL2QUEUE); + } +} + +// service memory request in dramtoL2queue, writing to L2 as necessary +// (may cause cache eviction and subsequent writeback) +void L2c_process_dram_output ( dram_t* dram_p, int dm_id ) +{ + assert(dram_p->m_L2cache != NULL); + L2cacheblk *p_L2c = reinterpret_cast<L2cacheblk*>(dram_p->m_L2cache); + + static mem_fetch_t **L2dramout = NULL; + static unsigned long long int *wb_addr = NULL; + if (!L2dramout) L2dramout = (mem_fetch_t**)calloc(gpu_n_mem, sizeof(mem_fetch_t*)); + if (!wb_addr) { + wb_addr = (unsigned long long int*)calloc(gpu_n_mem, sizeof(unsigned long long int)); + for (unsigned i = 0; i < gpu_n_mem; i++) wb_addr[i] = -1; + } + + if (L2dramout[dm_id] == NULL) { + // pop from mshr chain if it is not empty, otherwise, pop a new cacheline from dram output queue + if (p_L2c->m_mshr->mshr_chain_empty() == false) { + L2dramout[dm_id] = p_L2c->m_mshr->mshr_chain_top(); + p_L2c->m_mshr->mshr_chain_pop(); + } else { + L2dramout[dm_id] = (mem_fetch_t*) dq_pop(p_L2c->dramtoL2queue); + if (!L2dramout[dm_id]) L2dramout[dm_id] = (mem_fetch_t*) dq_pop(p_L2c->dramtoL2writequeue); + + if (L2dramout[dm_id] != NULL) { + p_L2c->m_mshr->miss_serviced(L2dramout[dm_id]); + + if (p_L2c->m_mshr->mshr_chain_empty() == false) { // possible if this is a L2 writeback + L2dramout[dm_id] = p_L2c->m_mshr->mshr_chain_top(); + p_L2c->m_mshr->mshr_chain_pop(); + } + } + } + } + + mem_fetch_t* mf = L2dramout[dm_id]; + if (mf) { + if (!mf->write) { //service L2 read miss + + // it is a pre-fill dramout mf + if (wb_addr[dm_id] == (unsigned long long int)-1) { + if ( dq_full(p_L2c->L2tocbqueue) ) goto RETURN; + + if (mf->mshr) mshr_update_status(mf->mshr, IN_L2TOCBQUEUE_MISS); + + //only transfer across icnt once the whole line has been received by L2 cache + mf->type = REPLY_DATA; + dq_push(p_L2c->L2tocbqueue, mf); + + assert(mf->sid <= (int)gpu_n_shader); + shd_cache_line_t *fetch_line_exist = shd_cache_probe(p_L2c->L2cache, mf->addr); + if (fetch_line_exist == NULL) { + wb_addr[dm_id] = L2_shd_cache_fill(p_L2c->L2cache, mf->addr, gpu_sim_cycle ); + } + } + // only perform a write on cache eviction (write-back policy) + // it is the 1st or nth time trial to writeback + if (wb_addr[dm_id] != (unsigned long long int)-1) { + // performing L2 writeback (no false sharing for memory-side cache) + int wb_succeed = L2c_write_back(wb_addr[dm_id], p_L2c->L2cache->line_sz, dm_id ); + if (!wb_succeed) goto RETURN; //try again next cycle + } + + p_L2c->m_missTracker->miss_serviced(mf); + L2dramout[dm_id] = NULL; + wb_addr[dm_id] = -1; + } else { //service L2 write miss + p_L2c->m_missTracker->miss_serviced(mf); + dramtoL2_service_write(mf); + L2dramout[dm_id] = NULL; + wb_addr[dm_id] = -1; + } + } + RETURN: + assert (L2dramout[dm_id] || wb_addr[dm_id] == (unsigned long long int)-1); +} + +// Writeback from L2 to DRAM: +// - Takes in memory address and their parameters and pushes to dram request queue +// - This is used only for L2 writeback +unsigned char L2c_write_back(unsigned long long int addr, int bsize, int dram_id ) +{ + addrdec_t tlx; + addrdec_tlx(addr,&tlx); + + assert(dram[dram_id]->m_L2cache != NULL); + L2cacheblk *p_L2c = reinterpret_cast<L2cacheblk*>(dram[dram_id]->m_L2cache); + + if ( dq_full(p_L2c->L2todram_wbqueue) ) return 0; + + mem_fetch_t *mf; + + mf = (mem_fetch_t*) malloc(sizeof(mem_fetch_t)); + made_write_mfs++; + mf->request_uid = g_next_request_uid++; + mf->addr = addr; + mf->nbytes_L1 = bsize + READ_PACKET_SIZE; + mf->txbytes_L1 = 0; + mf->rxbytes_L1 = 0; + mf->nbytes_L2 = bsize; + mf->sid = gpu_n_shader; // (gpu_n_shader+1); + mf->wid = 0; + mf->txbytes_L2 = 0; + mf->rxbytes_L2 = 0; + mf->mshr = NULL; + mf->pc = -1; // disable ptx_file_line_stats + mf->write = 1; // it is writeback + mf->mem_acc = L2_WRBK_ACC; + memlatstat_start(mf); + mf->tlx = tlx; + mf->bank = mf->tlx.bk; + mf->chip = mf->tlx.chip; + + + //writeback + mf->type = L2_WTBK_DATA; + if (!dq_push(p_L2c->L2todram_wbqueue, mf)) assert(0); + gpgpu_n_sent_writes++; + return 1; +} + +unsigned int L2c_cache_flush ( dram_t* dram_p) { + assert(dram_p->m_L2cache != NULL); + L2cacheblk *p_L2c = reinterpret_cast<L2cacheblk*>(dram_p->m_L2cache); + + shd_cache_t *cp = p_L2c->L2cache; + int dirty_lines_flushed = 0 ; + for (unsigned i = 0; i < cp->nset * cp->assoc ; i++) { + if ( (cp->lines[i].status & (DIRTY|VALID)) == (DIRTY|VALID) ) { + dirty_lines_flushed++; + } + cp->lines[i].status &= ~VALID; + cp->lines[i].status &= ~DIRTY; + } + return dirty_lines_flushed; +} + +void L2c_init_stat() +{ + L2_cbtoL2length = (unsigned int*) calloc(gpu_n_mem, sizeof(unsigned int)); + L2_cbtoL2writelength = (unsigned int*) calloc(gpu_n_mem, sizeof(unsigned int)); + L2_L2tocblength = (unsigned int*) calloc(gpu_n_mem, sizeof(unsigned int)); + L2_dramtoL2length = (unsigned int*) calloc(gpu_n_mem, sizeof(unsigned int)); + L2_dramtoL2writelength = (unsigned int*) calloc(gpu_n_mem, sizeof(unsigned int)); + L2_L2todramlength = (unsigned int*) calloc(gpu_n_mem, sizeof(unsigned int)); +} + +void L2c_update_stat( dram_t* dram_p) +{ + assert(dram_p->m_L2cache != NULL); + L2cacheblk *p_L2c = reinterpret_cast<L2cacheblk*>(dram_p->m_L2cache); + + int i = dram_p->id; + + if (p_L2c->cbtoL2queue->length > L2_cbtoL2length[i]) + L2_cbtoL2length[i] = p_L2c->cbtoL2queue->length; + if (p_L2c->cbtoL2writequeue->length > L2_cbtoL2writelength[i]) + L2_cbtoL2writelength[i] = p_L2c->cbtoL2writequeue->length; + if (p_L2c->L2tocbqueue->length > L2_L2tocblength[i]) + L2_L2tocblength[i] = p_L2c->L2tocbqueue->length; + if (p_L2c->dramtoL2queue->length > L2_dramtoL2length[i]) + L2_dramtoL2length[i] = p_L2c->dramtoL2queue->length; + if (p_L2c->dramtoL2writequeue->length > L2_dramtoL2writelength[i]) + L2_dramtoL2writelength[i] = p_L2c->dramtoL2writequeue->length; + if (p_L2c->L2todramqueue->length > L2_L2todramlength[i]) + L2_L2todramlength[i] = p_L2c->L2todramqueue->length; +} + +void L2c_print_stat( ) +{ + unsigned i; + + printf(" "); + for (i=0;i<gpu_n_mem;i++) { + printf(" dram[%d]", i); + } + printf("\n"); + + printf("cbtoL2 queue maximum length ="); + for (i=0;i<gpu_n_mem;i++) { + printf("%8d", L2_cbtoL2length[i]); + } + printf("\n"); + + printf("cbtoL2 write queue maximum length ="); + for (i=0;i<gpu_n_mem;i++) { + printf("%8d", L2_cbtoL2writelength[i]); + } + printf("\n"); + + printf("L2tocb queue maximum length ="); + for (i=0;i<gpu_n_mem;i++) { + printf("%8d", L2_L2tocblength[i]); + } + printf("\n"); + + printf("dramtoL2 queue maximum length ="); + for (i=0;i<gpu_n_mem;i++) { + printf("%8d", L2_dramtoL2length[i]); + } + printf("\n"); + + printf("dramtoL2 write queue maximum length ="); + for (i=0;i<gpu_n_mem;i++) { + printf("%8d", L2_dramtoL2writelength[i]); + } + printf("\n"); + + printf("L2todram queue maximum length ="); + for (i=0;i<gpu_n_mem;i++) { + printf("%8d", L2_L2todramlength[i]); + } + printf("\n"); +} + +void L2c_print_cache_stat() +{ + unsigned i; + int j, k; + for (i=0,j=0,k=0;i<gpu_n_mem;i++) { + assert(dram[i]->m_L2cache != NULL); + L2cacheblk *p_L2c = reinterpret_cast<L2cacheblk*>(dram[i]->m_L2cache); + + shd_cache_print(p_L2c->L2cache,stdout); + j += p_L2c->L2cache->miss; + k += p_L2c->L2cache->access; + p_L2c->m_mshr->print_stat(stdout); + p_L2c->m_missTracker->print_stat(stdout); + p_L2c->m_accessLocality->print_stat(stdout, false); + } + printf("L2 Cache Total Miss Rate = %0.3f\n", (float)j/k); +} + +void L2c_print_debug( ) +{ + unsigned i; + + printf(" "); + for (i=0;i<gpu_n_mem;i++) { + printf(" dram[%d]", i); + } + printf("\n"); + + printf("cbtoL2 queue length ="); + for (i=0;i<gpu_n_mem;i++) { + L2cacheblk *p_L2c = reinterpret_cast<L2cacheblk*>(dram[i]->m_L2cache); + printf("%8d", p_L2c->cbtoL2queue->length); + } + printf("\n"); + + printf("cbtoL2 write queue length ="); + for (i=0;i<gpu_n_mem;i++) { + L2cacheblk *p_L2c = reinterpret_cast<L2cacheblk*>(dram[i]->m_L2cache); + printf("%8d", p_L2c->cbtoL2writequeue->length); + } + printf("\n"); + + printf("L2tocb queue length ="); + for (i=0;i<gpu_n_mem;i++) { + L2cacheblk *p_L2c = reinterpret_cast<L2cacheblk*>(dram[i]->m_L2cache); + printf("%8d", p_L2c->L2tocbqueue->length); + } + printf("\n"); + + printf("dramtoL2 queue length ="); + for (i=0;i<gpu_n_mem;i++) { + L2cacheblk *p_L2c = reinterpret_cast<L2cacheblk*>(dram[i]->m_L2cache); + printf("%8d", p_L2c->dramtoL2queue->length); + } + printf("\n"); + + printf("dramtoL2 write queue length ="); + for (i=0;i<gpu_n_mem;i++) { + L2cacheblk *p_L2c = reinterpret_cast<L2cacheblk*>(dram[i]->m_L2cache); + printf("%8d", p_L2c->dramtoL2writequeue->length); + } + printf("\n"); + + printf("L2todram queue length ="); + for (i=0;i<gpu_n_mem;i++) { + L2cacheblk *p_L2c = reinterpret_cast<L2cacheblk*>(dram[i]->m_L2cache); + printf("%8d", p_L2c->L2todramqueue->length); + } + printf("\n"); + + printf("L2todram writeback queue length ="); + for (i=0;i<gpu_n_mem;i++) { + L2cacheblk *p_L2c = reinterpret_cast<L2cacheblk*>(dram[i]->m_L2cache); + printf("%8d", p_L2c->L2todram_wbqueue->length); + } + printf("\n"); +} + +#define CREATELOG 111 +#define SAMPLELOG 222 +#define DUMPLOG 333 + +void L2c_log(int task) +{ + unsigned i; + static void ** cbtol2_Dist ; + static void ** cbtoL2wr_Dist ; + static void ** L2tocb_Dist ; + static void ** dramtoL2_Dist ; + static void ** dramtoL2wr_Dist ; + static void ** L2todram_Dist ; + static void ** L2todram_wb_Dist ; + if (task == CREATELOG) { + cbtol2_Dist = (void **) calloc(gpu_n_mem,sizeof(void*)); + cbtoL2wr_Dist = (void **) calloc(gpu_n_mem,sizeof(void*)); + L2tocb_Dist = (void **) calloc(gpu_n_mem,sizeof(void*)); + dramtoL2_Dist = (void **)calloc(gpu_n_mem,sizeof(void*)); + dramtoL2wr_Dist = (void **)calloc(gpu_n_mem,sizeof(void*)); + L2todram_Dist = (void **)calloc(gpu_n_mem,sizeof(void*)); + L2todram_wb_Dist = (void **)calloc(gpu_n_mem,sizeof(void*)); + + for (i=0;i<gpu_n_mem;i++) { + assert(dram[i]->m_L2cache != NULL); + L2cacheblk *p_L2c = reinterpret_cast<L2cacheblk*>(dram[i]->m_L2cache); + + cbtol2_Dist[i] = StatCreate("cbtoL2",1,p_L2c->cbtoL2queue->max_len); + cbtoL2wr_Dist[i] = StatCreate("cbtoL2write",1,p_L2c->cbtoL2writequeue->max_len); + L2tocb_Dist[i] = StatCreate("L2tocb",1,p_L2c->L2tocbqueue->max_len); + dramtoL2_Dist[i] = StatCreate("dramtoL2",1,p_L2c->dramtoL2queue->max_len); + dramtoL2wr_Dist[i] = StatCreate("dramtoL2write",1,p_L2c->dramtoL2writequeue->max_len); + L2todram_Dist[i] = StatCreate("L2todram",1,p_L2c->L2todramqueue->max_len); + L2todram_wb_Dist[i] = StatCreate("L2todram_wb",1,p_L2c->L2todram_wbqueue->max_len); + } + } else if (task == SAMPLELOG) { + for (i=0;i<gpu_n_mem;i++) { + assert(dram[i]->m_L2cache != NULL); + L2cacheblk *p_L2c = reinterpret_cast<L2cacheblk*>(dram[i]->m_L2cache); + + StatAddSample(cbtol2_Dist[i], p_L2c->cbtoL2queue->length); + StatAddSample(cbtoL2wr_Dist[i], p_L2c->cbtoL2writequeue->length); + StatAddSample(L2tocb_Dist[i], p_L2c->L2tocbqueue->length); + StatAddSample(dramtoL2_Dist[i], p_L2c->dramtoL2queue->length); + StatAddSample(dramtoL2wr_Dist[i], p_L2c->dramtoL2writequeue->length); + StatAddSample(L2todram_Dist[i], p_L2c->L2todramqueue->length); + StatAddSample(L2todram_wb_Dist[i], p_L2c->L2todram_wbqueue->length); + } + } else if (task == DUMPLOG) { + for (i=0;i<gpu_n_mem;i++) { + printf ("Queue Length DRAM[%d] ",i); StatDisp(cbtol2_Dist[i]); + printf ("Queue Length DRAM[%d] ",i); StatDisp(cbtoL2wr_Dist[i]); + printf ("Queue Length DRAM[%d] ",i); StatDisp(L2tocb_Dist[i]); + printf ("Queue Length DRAM[%d] ",i); StatDisp(dramtoL2_Dist[i]); + printf ("Queue Length DRAM[%d] ",i); StatDisp(dramtoL2wr_Dist[i]); + printf ("Queue Length DRAM[%d] ",i); StatDisp(L2todram_Dist[i]); + printf ("Queue Length DRAM[%d] ",i); StatDisp(L2todram_wb_Dist[i]); + } + } +} + +void L2c_latency_log_dump() +{ + unsigned i; + for (i=0;i<gpu_n_mem;i++) { + assert(dram[i]->m_L2cache != NULL); + L2cacheblk *p_L2c = reinterpret_cast<L2cacheblk*>(dram[i]->m_L2cache); + + printf ("(LOGB2)Latency DRAM[%d] ",i); StatDisp(p_L2c->cbtoL2queue->lat_stat); + printf ("(LOGB2)Latency DRAM[%d] ",i); StatDisp(p_L2c->cbtoL2writequeue->lat_stat); + printf ("(LOGB2)Latency DRAM[%d] ",i); StatDisp(p_L2c->L2tocbqueue->lat_stat); + printf ("(LOGB2)Latency DRAM[%d] ",i); StatDisp(p_L2c->dramtoL2queue->lat_stat); + printf ("(LOGB2)Latency DRAM[%d] ",i); StatDisp(p_L2c->dramtoL2writequeue->lat_stat); + printf ("(LOGB2)Latency DRAM[%d] ",i); StatDisp(p_L2c->L2todramqueue->lat_stat); + printf ("(LOGB2)Latency DRAM[%d] ",i); StatDisp(p_L2c->L2todram_wbqueue->lat_stat); + } +} + + diff --git a/src/gpgpu-sim/l2cache.h b/src/gpgpu-sim/l2cache.h new file mode 100644 index 0000000..449d47a --- /dev/null +++ b/src/gpgpu-sim/l2cache.h @@ -0,0 +1,48 @@ +#pragma once + +#include "dram.h" + +// L2 Cache Creation +void L2c_create ( dram_t* dram_p, const char* cache_opt ); + +void L2c_qlen ( dram_t *dram_p ); + +// service memory request in icnt-to-L2 queue, writing to L2 as necessary +// (if L2 writeback miss, writeback to memory) +void L2c_service_mem_req ( dram_t* dram_p, int dm_id ); + +// service memory request in L2todramqueue, pushing to dram +void L2c_push_miss_to_dram ( dram_t* dram_p ); + +// pop completed memory request from dram and push it to dram-to-L2 queue +void L2c_get_dram_output ( dram_t* dram_p ); + +// service memory request in dramtoL2queue, writing to L2 as necessary +// (may cause cache eviction and subsequent writeback) +void L2c_process_dram_output ( dram_t* dram_p, int dm_id ); + +// Writeback from L2 to DRAM: +// - Takes in memory address and their parameters and pushes to dram request queue +// - This is used only for L2 writeback +unsigned char L2c_write_back(unsigned long long int addr, int bsize, int dram_id ); + +unsigned int L2c_cache_flush ( dram_t* dram_p); + +unsigned L2c_get_linesize( dram_t *dram_p ); + +// probe L2 cache for fullness +int L2c_full( dram_t *dram_p ); +void L2c_push( dram_t *dram_p, mem_fetch_t *mf ); +mem_fetch_t* L2c_pop( dram_t *dram_p ); +mem_fetch_t* L2c_top( dram_t *dram_p ); + +void L2c_init_stat(); +void L2c_update_stat( dram_t* dram_p); +void L2c_print_stat(); +void L2c_print_cache_stat(); +void L2c_print_debug(); +void L2c_log(int task); +void L2c_latency_log_dump(); + +void L2c_options(option_parser_t opp); + diff --git a/src/gpgpu-sim/mem_fetch.h b/src/gpgpu-sim/mem_fetch.h new file mode 100644 index 0000000..0e12a2d --- /dev/null +++ b/src/gpgpu-sim/mem_fetch.h @@ -0,0 +1,110 @@ +/* + * mem_fetch.h + * + * Copyright (c) 2009 by Tor M. Aamodt, Wilson W. L. Fung, Ali Bakhoda, + * George L. Yuan and the + * University of British Columbia + * Vancouver, BC V6T 1Z4 + * All Rights Reserved. + * + * THIS IS A LEGAL DOCUMENT BY DOWNLOADING GPGPU-SIM, YOU ARE AGREEING TO THESE + * TERMS AND CONDITIONS. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * NOTE: The files libcuda/cuda_runtime_api.c and src/cuda-sim/cuda-math.h + * are derived from the CUDA Toolset available from http://www.nvidia.com/cuda + * (property of NVIDIA). The files benchmarks/BlackScholes/ and + * benchmarks/template/ are derived from the CUDA SDK available from + * http://www.nvidia.com/cuda (also property of NVIDIA). The files from + * src/intersim/ are derived from Booksim (a simulator provided with the + * textbook "Principles and Practices of Interconnection Networks" available + * from http://cva.stanford.edu/books/ppin/). As such, those files are bound by + * the corresponding legal terms and conditions set forth separately (original + * copyright notices are left in files from these sources and where we have + * modified a file our copyright notice appears before the original copyright + * notice). + * + * Using this version of GPGPU-Sim requires a complete installation of CUDA + * which is distributed seperately by NVIDIA under separate terms and + * conditions. To use this version of GPGPU-Sim with OpenCL requires a + * recent version of NVIDIA's drivers which support OpenCL. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the University of British Columbia nor the names of + * its contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * 4. This version of GPGPU-SIM is distributed freely for non-commercial use only. + * + * 5. No nonprofit user may place any restrictions on the use of this software, + * including as modified by the user, by any other authorized user. + * + * 6. GPGPU-SIM was developed primarily by Tor M. Aamodt, Wilson W. L. Fung, + * Ali Bakhoda, George L. Yuan, at the University of British Columbia, + * Vancouver, BC V6T 1Z4 + */ + +#ifndef MEM_FETCH_H +#define MEM_FETCH_H + +#include "shader.h" +#include "addrdec.h" + +enum mf_type { + RD_REQ = 0, + WT_REQ, + REPLY_DATA, // send to shader + L2_WTBK_DATA, + DUMMY_READ, //used in write mask + N_MF_TYPE +}; + +typedef struct { + unsigned request_uid; + unsigned long long int addr; + int nbytes_L1; + int txbytes_L1; + int rxbytes_L1; + int nbytes_L2; + int txbytes_L2; + int rxbytes_L2; + int sid; //shader core id + int wid; //warp id + int cache_hits_waiting; + mshr_entry* mshr; + address_type pc; + unsigned char write; + enum mem_access_type mem_acc; + unsigned int timestamp; //set to gpu_sim_cycle at struct creation + unsigned int timestamp2; //set to gpu_sim_cycle when pushed onto icnt to shader; only used for reads + unsigned int icnt_receive_time; //set to gpu_sim_cycle + interconnect_latency when fixed icnt latency mode is enabled + unsigned char bank; + unsigned char chip; + addrdec_t tlx; + enum mf_type type; + partial_write_mask_t write_mask; + int source_node; //memory node id when sending from mem to shader + //same as sid when sending from shader 2 mem +} mem_fetch_t; + +#endif diff --git a/src/gpgpu-sim/mem_latency_stat.h b/src/gpgpu-sim/mem_latency_stat.h new file mode 100644 index 0000000..2fa6b1c --- /dev/null +++ b/src/gpgpu-sim/mem_latency_stat.h @@ -0,0 +1,544 @@ +/* + * mem_latency_stat.h + * + * Copyright (c) 2009 by Tor M. Aamodt, Wilson W. L. Fung, Ali Bakhoda, + * George L. Yuan and the + * University of British Columbia + * Vancouver, BC V6T 1Z4 + * All Rights Reserved. + * + * THIS IS A LEGAL DOCUMENT BY DOWNLOADING GPGPU-SIM, YOU ARE AGREEING TO THESE + * TERMS AND CONDITIONS. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * NOTE: The files libcuda/cuda_runtime_api.c and src/cuda-sim/cuda-math.h + * are derived from the CUDA Toolset available from http://www.nvidia.com/cuda + * (property of NVIDIA). The files benchmarks/BlackScholes/ and + * benchmarks/template/ are derived from the CUDA SDK available from + * http://www.nvidia.com/cuda (also property of NVIDIA). The files from + * src/intersim/ are derived from Booksim (a simulator provided with the + * textbook "Principles and Practices of Interconnection Networks" available + * from http://cva.stanford.edu/books/ppin/). As such, those files are bound by + * the corresponding legal terms and conditions set forth separately (original + * copyright notices are left in files from these sources and where we have + * modified a file our copyright notice appears before the original copyright + * notice). + * + * Using this version of GPGPU-Sim requires a complete installation of CUDA + * which is distributed seperately by NVIDIA under separate terms and + * conditions. To use this version of GPGPU-Sim with OpenCL requires a + * recent version of NVIDIA's drivers which support OpenCL. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the University of British Columbia nor the names of + * its contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * 4. This version of GPGPU-SIM is distributed freely for non-commercial use only. + * + * 5. No nonprofit user may place any restrictions on the use of this software, + * including as modified by the user, by any other authorized user. + * + * 6. GPGPU-SIM was developed primarily by Tor M. Aamodt, Wilson W. L. Fung, + * Ali Bakhoda, George L. Yuan, at the University of British Columbia, + * Vancouver, BC V6T 1Z4 + */ + + +#ifndef MEM_LATENCY_STAT_H +#define MEM_LATENCY_STAT_H + +extern unsigned long long gpu_sim_cycle; +extern unsigned int gpu_n_mem; +extern unsigned int gpu_n_shader; +extern int gpgpu_dram_sched_queue_size; +extern int gpgpu_dram_scheduler; +extern unsigned int gpu_mem_n_bk; +#ifdef MEM_LATENCY_STAT_IMPL + #define EXTERN_DEF +#else + #define EXTERN_DEF extern +#endif + +EXTERN_DEF int gpgpu_memlatency_stat = FALSE; + +EXTERN_DEF unsigned max_mrq_latency; +EXTERN_DEF unsigned max_dq_latency; +EXTERN_DEF unsigned max_mf_latency; +EXTERN_DEF unsigned max_icnt2mem_latency; +EXTERN_DEF unsigned max_icnt2sh_latency; +EXTERN_DEF unsigned mrq_lat_table[32]; +EXTERN_DEF unsigned dq_lat_table[32]; +EXTERN_DEF unsigned mf_lat_table[32]; +EXTERN_DEF unsigned icnt2mem_lat_table[24]; +EXTERN_DEF unsigned icnt2sh_lat_table[24]; +EXTERN_DEF unsigned mf_lat_pw_table[32]; //table storing values of mf latency Per Window +EXTERN_DEF unsigned mf_num_lat_pw; +EXTERN_DEF unsigned mf_tot_lat_pw; //total latency summed up per window. divide by mf_num_lat_pw to obtain average latency Per Window +EXTERN_DEF unsigned long long int mf_total_lat; +EXTERN_DEF unsigned long long int ** mf_total_lat_table; //mf latency sums[dram chip id][bank id] +EXTERN_DEF unsigned ** mf_max_lat_table; //mf latency sums[dram chip id][bank id] +EXTERN_DEF unsigned num_mfs; +EXTERN_DEF unsigned int ***bankwrites; //bankwrites[shader id][dram chip id][bank id] +EXTERN_DEF unsigned int ***bankreads; //bankreads[shader id][dram chip id][bank id] +EXTERN_DEF unsigned int **totalbankwrites; //bankwrites[dram chip id][bank id] +EXTERN_DEF unsigned int **totalbankreads; //bankreads[dram chip id][bank id] +EXTERN_DEF unsigned int **totalbankaccesses; //bankaccesses[dram chip id][bank id] +EXTERN_DEF unsigned int *requests_by_warp; +EXTERN_DEF unsigned int *MCB_accesses; //upon cache miss, tracks which memory controllers accessed by a warp +EXTERN_DEF unsigned int *num_MCBs_accessed; //tracks how many memory controllers are accessed whenever any thread in a warp misses in cache +EXTERN_DEF unsigned int *position_of_mrq_chosen; //position of mrq in m_queue chosen +EXTERN_DEF unsigned *mf_num_lat_pw_perwarp; +EXTERN_DEF unsigned *mf_tot_lat_pw_perwarp; //total latency summed up per window per warp. divide by mf_num_lat_pw_perwarp to obtain average latency Per Window +EXTERN_DEF unsigned long long int *mf_total_lat_perwarp; +EXTERN_DEF unsigned *num_mfs_perwarp; +EXTERN_DEF unsigned *acc_mrq_length; + +EXTERN_DEF unsigned ***mem_access_type_stats; // dram access type classification + + +void memlatstat_init( ) +{ + unsigned i,j; + + max_mrq_latency = 0; + max_dq_latency = 0; + max_mf_latency = 0; + max_icnt2mem_latency = 0; + max_icnt2sh_latency = 0; + memset(mrq_lat_table, 0, sizeof(unsigned)*32); + memset(dq_lat_table, 0, sizeof(unsigned)*32); + memset(mf_lat_table, 0, sizeof(unsigned)*32); + memset(icnt2mem_lat_table, 0, sizeof(unsigned)*24); + memset(icnt2sh_lat_table, 0, sizeof(unsigned)*24); + memset(mf_lat_pw_table, 0, sizeof(unsigned)*32); + mf_num_lat_pw = 0; + mf_num_lat_pw_perwarp = (unsigned *) calloc((gpu_n_shader * gpu_n_thread_per_shader / warp_size)+1, sizeof(unsigned int)); + mf_tot_lat_pw_perwarp = (unsigned *) calloc((gpu_n_shader * gpu_n_thread_per_shader / warp_size)+1, sizeof(unsigned int)); + mf_total_lat_perwarp = (unsigned long long int *) calloc((gpu_n_shader * gpu_n_thread_per_shader / warp_size)+1, sizeof(unsigned long long int)); + num_mfs_perwarp = (unsigned *) calloc((gpu_n_shader * gpu_n_thread_per_shader / warp_size)+1, sizeof(unsigned int)); + acc_mrq_length = (unsigned *) calloc(gpu_n_mem, sizeof(unsigned int)); + mf_tot_lat_pw = 0; //total latency summed up per window. divide by mf_num_lat_pw to obtain average latency Per Window + mf_total_lat = 0; + num_mfs = 0; + printf("*** Initializing Memory Statistics ***\n"); + requests_by_warp = (unsigned int*) calloc((gpu_n_shader * gpu_n_thread_per_shader / warp_size)+1, sizeof(unsigned int)); + totalbankreads = (unsigned int**) calloc(gpu_n_mem, sizeof(unsigned int*)); + totalbankwrites = (unsigned int**) calloc(gpu_n_mem, sizeof(unsigned int*)); + totalbankaccesses = (unsigned int**) calloc(gpu_n_mem, sizeof(unsigned int*)); + mf_total_lat_table = (unsigned long long int **) calloc(gpu_n_mem, sizeof(unsigned long long *)); + mf_max_lat_table = (unsigned **) calloc(gpu_n_mem, sizeof(unsigned *)); + bankreads = (unsigned int***) calloc(gpu_n_shader, sizeof(unsigned int**)); + bankwrites = (unsigned int***) calloc(gpu_n_shader, sizeof(unsigned int**)); + MCB_accesses = (unsigned int*) calloc(gpu_n_mem*4, sizeof(unsigned int)); + num_MCBs_accessed = (unsigned int*) calloc(gpu_n_mem*4+1, sizeof(unsigned int)); + if (gpgpu_dram_sched_queue_size) { + position_of_mrq_chosen = (unsigned int*) calloc(gpgpu_dram_sched_queue_size, sizeof(unsigned int)); + } else + position_of_mrq_chosen = (unsigned int*) calloc(1024, sizeof(unsigned int)); + for (i=0;i<gpu_n_shader ;i++ ) { + bankreads[i] = (unsigned int**) calloc(gpu_n_mem, sizeof(unsigned int*)); + bankwrites[i] = (unsigned int**) calloc(gpu_n_mem, sizeof(unsigned int*)); + for (j=0;j<gpu_n_mem ;j++ ) { + bankreads[i][j] = (unsigned int*) calloc(gpu_mem_n_bk, sizeof(unsigned int)); + bankwrites[i][j] = (unsigned int*) calloc(gpu_mem_n_bk, sizeof(unsigned int)); + } + } + + for (i=0;i<gpu_n_mem ;i++ ) { + totalbankreads[i] = (unsigned int*) calloc(gpu_mem_n_bk, sizeof(unsigned int)); + totalbankwrites[i] = (unsigned int*) calloc(gpu_mem_n_bk, sizeof(unsigned int)); + totalbankaccesses[i] = (unsigned int*) calloc(gpu_mem_n_bk, sizeof(unsigned int)); + mf_total_lat_table[i] = (unsigned long long int*) calloc(gpu_mem_n_bk, sizeof(unsigned long long int)); + mf_max_lat_table[i] = (unsigned *) calloc(gpu_mem_n_bk, sizeof(unsigned)); + } + + mem_access_type_stats = (unsigned ***) malloc(NUM_MEM_ACCESS_TYPE * sizeof(unsigned **)); + for (i = 0; i < NUM_MEM_ACCESS_TYPE; i++) { + int j; + mem_access_type_stats[i] = (unsigned **) calloc(gpu_n_mem, sizeof(unsigned*)); + for (j=0; (unsigned) j< gpu_n_mem; j++) { + mem_access_type_stats[i][j] = (unsigned *) calloc((gpu_mem_n_bk+1), sizeof(unsigned*)); + } + } +} + +void memlatstat_start(mem_fetch_t *mf) +{ + mf->timestamp = gpu_sim_cycle + gpu_tot_sim_cycle; + mf->timestamp2 = 0; +} + +// recorder the total latency +unsigned memlatstat_done(mem_fetch_t *mf) +{ + unsigned mf_latency; + unsigned wid = mf->sid*gpu_n_warp_per_shader + mf->wid; + mf_latency = (gpu_sim_cycle+gpu_tot_sim_cycle) - mf->timestamp; + mf_num_lat_pw++; + mf_num_lat_pw_perwarp[wid]++; + mf_tot_lat_pw_perwarp[wid] += mf_latency; + mf_tot_lat_pw += mf_latency; + check_time_vector_update(mf->mshr->insts[0].uid,MR_2SH_FQ_POP,mf_latency, mf->type ) ; + mf_lat_table[LOGB2(mf_latency)]++; + shader_mem_lat_log(mf->sid, mf_latency); + mf_total_lat_table[mf->chip][mf->bank] += mf_latency; + if (mf_latency > max_mf_latency) + max_mf_latency = mf_latency; + return mf_latency; +} + +void memlatstat_icnt2sh_push(mem_fetch_t *mf) +{ + mf->timestamp2 = gpu_sim_cycle+gpu_tot_sim_cycle; +} + +void memlatstat_read_done(mem_fetch_t *mf) +{ + if (gpgpu_memlatency_stat) { + unsigned mf_latency = memlatstat_done(mf); + + if (mf_latency > mf_max_lat_table[mf->chip][mf->bank]) { + mf_max_lat_table[mf->chip][mf->bank] = mf_latency; + } + + unsigned icnt2sh_latency; + icnt2sh_latency = (gpu_tot_sim_cycle+gpu_sim_cycle) - mf->timestamp2; + icnt2sh_lat_table[LOGB2(icnt2sh_latency)]++; + if (icnt2sh_latency > max_icnt2sh_latency) + max_icnt2sh_latency = icnt2sh_latency; + } +} + +void memlatstat_dram_access(mem_fetch_t *mf, unsigned dram_id, unsigned bank) +{ + assert(dram_id < gpu_n_mem); + assert(bank < gpu_mem_n_bk); + if (gpgpu_memlatency_stat) { + if (mf->write) { + if ( (unsigned) mf->sid < gpu_n_shader ) { //do not count L2_writebacks here + bankwrites[mf->sid][dram_id][bank]++; + shader_mem_acc_log( mf->sid, dram_id, bank, 'w'); + } + totalbankwrites[dram_id][bank]++; + } else { + bankreads[mf->sid][dram_id][bank]++; + shader_mem_acc_log( mf->sid, dram_id, bank, 'r'); + totalbankreads[dram_id][bank]++; + } + + if (mf->pc != (unsigned) -1) { + ptx_file_line_stats_add_dram_traffic(mf->pc, 1); + } + + mem_access_type_stats[mf->mem_acc][dram_id][bank]++; + } +} + +void memlatstat_icnt2mem_pop(mem_fetch_t *mf) +{ + if (gpgpu_memlatency_stat) { + unsigned icnt2mem_latency; + icnt2mem_latency = (gpu_tot_sim_cycle+gpu_sim_cycle) - mf->timestamp; + icnt2mem_lat_table[LOGB2(icnt2mem_latency)]++; + if (icnt2mem_latency > max_icnt2mem_latency) + max_icnt2mem_latency = icnt2mem_latency; + } +} + +void memlatstat_lat_pw( ) +{ + unsigned i; + if (mf_num_lat_pw && gpgpu_memlatency_stat) { + assert(mf_tot_lat_pw); + mf_total_lat = mf_tot_lat_pw; + num_mfs = mf_num_lat_pw; + mf_lat_pw_table[LOGB2(mf_tot_lat_pw/mf_num_lat_pw)]++; + mf_tot_lat_pw = 0; + mf_num_lat_pw = 0; + } + for (i=0;i < ((gpu_n_shader * gpu_n_thread_per_shader / warp_size)+1); i++) { + if (mf_num_lat_pw_perwarp[i] && gpgpu_memlatency_stat) { + assert(mf_tot_lat_pw_perwarp[i]); + mf_total_lat_perwarp[i] += mf_tot_lat_pw_perwarp[i]; + num_mfs_perwarp[i] += mf_num_lat_pw_perwarp[i]; + //mf_lat_pw_table[LOGB2(mf_tot_lat_pw/mf_num_lat_pw)]++; + mf_tot_lat_pw_perwarp[i] = 0; + mf_num_lat_pw_perwarp[i] = 0; + } + } +} + + +void memlatstat_print( ) +{ + unsigned i,j,k,l,m; + unsigned max_bank_accesses, min_bank_accesses, max_chip_accesses, min_chip_accesses; + + if (gpgpu_memlatency_stat) { + printf("maxmrqlatency = %d \n", max_mrq_latency); + printf("maxdqlatency = %d \n", max_dq_latency); + printf("maxmflatency = %d \n", max_mf_latency); + if (num_mfs) { + printf("averagemflatency = %lld \n", mf_total_lat/num_mfs); + } + printf("max_icnt2mem_latency = %d \n", max_icnt2mem_latency); + printf("max_icnt2sh_latency = %d \n", max_icnt2sh_latency); + printf("mrq_lat_table:"); + for (i=0; i< 32; i++) { + printf("%d \t", mrq_lat_table[i]); + } + printf("\n"); + printf("dq_lat_table:"); + for (i=0; i< 32; i++) { + printf("%d \t", dq_lat_table[i]); + } + printf("\n"); + printf("mf_lat_table:"); + for (i=0; i< 32; i++) { + printf("%d \t", mf_lat_table[i]); + } + printf("\n"); + printf("icnt2mem_lat_table:"); + for (i=0; i< 24; i++) { + printf("%d \t", icnt2mem_lat_table[i]); + } + printf("\n"); + printf("icnt2sh_lat_table:"); + for (i=0; i< 24; i++) { + printf("%d \t", icnt2sh_lat_table[i]); + } + printf("\n"); + printf("mf_lat_pw_table:"); + for (i=0; i< 32; i++) { + printf("%d \t", mf_lat_pw_table[i]); + } + printf("\n"); + + /*MAXIMUM CONCURRENT ACCESSES TO SAME ROW*/ + printf("maximum concurrent accesses to same row:\n"); + for (i=0;i<gpu_n_mem ;i++ ) { + printf("dram[%d]: ", i); + for (j=0;j<4 ;j++ ) { + printf("%9d ",max_conc_access2samerow[i][j]); + } + printf("\n"); + } + + /*MAXIMUM SERVICE TIME TO SAME ROW*/ + printf("maximum service time to same row:\n"); + for (i=0;i<gpu_n_mem ;i++ ) { + printf("dram[%d]: ", i); + for (j=0;j<4 ;j++ ) { + printf("%9d ",max_servicetime2samerow[i][j]); + } + printf("\n"); + } + + /*AVERAGE ROW ACCESSES PER ACTIVATE*/ + int total_row_accesses = 0; + int total_num_activates = 0; + printf("average row accesses per activate:\n"); + for (i=0;i<gpu_n_mem ;i++ ) { + printf("dram[%d]: ", i); + for (j=0;j<4 ;j++ ) { + total_row_accesses += row_access[i][j]; + total_num_activates += num_activates[i][j]; + printf("%9f ",(float) row_access[i][j]/num_activates[i][j]); + } + printf("\n"); + } + printf("average row locality = %d/%d = %f\n", total_row_accesses, total_num_activates, (float)total_row_accesses/total_num_activates); + /*MEMORY ACCESSES*/ + k = 0; + l = 0; + m = 0; + max_bank_accesses = 0; + max_chip_accesses = 0; + min_bank_accesses = 0xFFFFFFFF; + min_chip_accesses = 0xFFFFFFFF; + printf("number of total memory accesses made:\n"); + for (i=0;i<gpu_n_mem ;i++ ) { + printf("dram[%d]: ", i); + for (j=0;j<4 ;j++ ) { + l = totalbankaccesses[i][j]; + if (l < min_bank_accesses) + min_bank_accesses = l; + if (l > max_bank_accesses) + max_bank_accesses = l; + k += l; + m += l; + printf("%9d ",l); + } + if (m < min_chip_accesses) + min_chip_accesses = m; + if (m > max_chip_accesses) + max_chip_accesses = m; + m = 0; + printf("\n"); + } + printf("total accesses: %d\n", k); + if (min_bank_accesses) + printf("bank skew: %d/%d = %4.2f\n", max_bank_accesses, min_bank_accesses, (float)max_bank_accesses/min_bank_accesses); + else + printf("min_bank_accesses = 0!\n"); + if (min_chip_accesses) + printf("chip skew: %d/%d = %4.2f\n", max_chip_accesses, min_chip_accesses, (float)max_chip_accesses/min_chip_accesses); + else + printf("min_chip_accesses = 0!\n"); + + /*READ ACCESSES*/ + k = 0; + l = 0; + m = 0; + max_bank_accesses = 0; + max_chip_accesses = 0; + min_bank_accesses = 0xFFFFFFFF; + min_chip_accesses = 0xFFFFFFFF; + printf("number of total read accesses:\n"); + for (i=0;i<gpu_n_mem ;i++ ) { + printf("dram[%d]: ", i); + for (j=0;j<4 ;j++ ) { + l = totalbankreads[i][j]; + if (l < min_bank_accesses) + min_bank_accesses = l; + if (l > max_bank_accesses) + max_bank_accesses = l; + k += l; + m += l; + printf("%9d ",l); + } + if (m < min_chip_accesses) + min_chip_accesses = m; + if (m > max_chip_accesses) + max_chip_accesses = m; + m = 0; + printf("\n"); + } + printf("total reads: %d\n", k); + if (min_bank_accesses) + printf("bank skew: %d/%d = %4.2f\n", max_bank_accesses, min_bank_accesses, (float)max_bank_accesses/min_bank_accesses); + else + printf("min_bank_accesses = 0!\n"); + if (min_chip_accesses) + printf("chip skew: %d/%d = %4.2f\n", max_chip_accesses, min_chip_accesses, (float)max_chip_accesses/min_chip_accesses); + else + printf("min_chip_accesses = 0!\n"); + + /*WRITE ACCESSES*/ + k = 0; + l = 0; + m = 0; + max_bank_accesses = 0; + max_chip_accesses = 0; + min_bank_accesses = 0xFFFFFFFF; + min_chip_accesses = 0xFFFFFFFF; + printf("number of total write accesses:\n"); + for (i=0;i<gpu_n_mem ;i++ ) { + printf("dram[%d]: ", i); + for (j=0;j<4 ;j++ ) { + l = totalbankwrites[i][j]; + if (l < min_bank_accesses) + min_bank_accesses = l; + if (l > max_bank_accesses) + max_bank_accesses = l; + k += l; + m += l; + printf("%9d ",l); + } + if (m < min_chip_accesses) + min_chip_accesses = m; + if (m > max_chip_accesses) + max_chip_accesses = m; + m = 0; + printf("\n"); + } + printf("total reads: %d\n", k); + if (min_bank_accesses) + printf("bank skew: %d/%d = %4.2f\n", max_bank_accesses, min_bank_accesses, (float)max_bank_accesses/min_bank_accesses); + else + printf("min_bank_accesses = 0!\n"); + if (min_chip_accesses) + printf("chip skew: %d/%d = %4.2f\n", max_chip_accesses, min_chip_accesses, (float)max_chip_accesses/min_chip_accesses); + else + printf("min_chip_accesses = 0!\n"); + + + /*AVERAGE MF LATENCY PER BANK*/ + printf("average mf latency per bank:\n"); + for (i=0;i<gpu_n_mem ;i++ ) { + printf("dram[%d]: ", i); + for (j=0;j<4 ;j++ ) { + k = totalbankwrites[i][j] + totalbankreads[i][j]; + if (k) + printf("%10lld", mf_total_lat_table[i][j] / k); + else + printf(" none "); + } + printf("\n"); + } + + /*MAXIMUM MF LATENCY PER BANK*/ + printf("maximum mf latency per bank:\n"); + for (i=0;i<gpu_n_mem ;i++ ) { + printf("dram[%d]: ", i); + for (j=0;j<4 ;j++ ) { + printf("%10d", mf_max_lat_table[i][j]); + } + printf("\n"); + } + } + + if (gpgpu_memlatency_stat & GPU_MEMLATSTAT_MC) { + printf("\nNumber of Memory Banks Accessed per Memory Operation per Warp (from 0):\n"); + unsigned long long accum_MCBs_accessed = 0; + unsigned long long tot_mem_ops_per_warp = 0; + for (i=0;i<= gpu_n_mem*4 ; i++ ) { + accum_MCBs_accessed += i*num_MCBs_accessed[i]; + tot_mem_ops_per_warp += num_MCBs_accessed[i]; + printf("%d\t", num_MCBs_accessed[i]); + } + + printf("\nAverage # of Memory Banks Accessed per Memory Operation per Warp=%f\n", (float)accum_MCBs_accessed/tot_mem_ops_per_warp); + + //printf("\nAverage Difference Between First and Last Response from Memory System per warp = "); + + + printf("\nposition of mrq chosen\n"); + + if (!gpgpu_dram_sched_queue_size) + j = 1024; + else + j = gpgpu_dram_sched_queue_size; + k=0;l=0; + for (i=0;i< j; i++ ) { + printf("%d\t", position_of_mrq_chosen[i]); + k += position_of_mrq_chosen[i]; + l += i*position_of_mrq_chosen[i]; + } + printf("\n"); + printf("\naverage position of mrq chosen = %f\n", (float)l/k); + } +} + +#endif /*MEM_LATENCY_STAT_H*/ diff --git a/src/gpgpu-sim/shader.cc b/src/gpgpu-sim/shader.cc new file mode 100644 index 0000000..8f3f700 --- /dev/null +++ b/src/gpgpu-sim/shader.cc @@ -0,0 +1,3811 @@ +/* + * shader.c + * + * Copyright (c) 2009 by Tor M. Aamodt, Wilson W. L. Fung, Ali Bakhoda, + * George L. Yuan, Ivan Sham, Henry Wong, Dan O'Connor, Henry Tran and the + * University of British Columbia + * Vancouver, BC V6T 1Z4 + * All Rights Reserved. + * + * THIS IS A LEGAL DOCUMENT BY DOWNLOADING GPGPU-SIM, YOU ARE AGREEING TO THESE + * TERMS AND CONDITIONS. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * NOTE: The files libcuda/cuda_runtime_api.c and src/cuda-sim/cuda-math.h + * are derived from the CUDA Toolset available from http://www.nvidia.com/cuda + * (property of NVIDIA). The files benchmarks/BlackScholes/ and + * benchmarks/template/ are derived from the CUDA SDK available from + * http://www.nvidia.com/cuda (also property of NVIDIA). The files from + * src/intersim/ are derived from Booksim (a simulator provided with the + * textbook "Principles and Practices of Interconnection Networks" available + * from http://cva.stanford.edu/books/ppin/). As such, those files are bound by + * the corresponding legal terms and conditions set forth separately (original + * copyright notices are left in files from these sources and where we have + * modified a file our copyright notice appears before the original copyright + * notice). + * + * Using this version of GPGPU-Sim requires a complete installation of CUDA + * which is distributed seperately by NVIDIA under separate terms and + * conditions. To use this version of GPGPU-Sim with OpenCL requires a + * recent version of NVIDIA's drivers which support OpenCL. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the University of British Columbia nor the names of + * its contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * 4. This version of GPGPU-SIM is distributed freely for non-commercial use only. + * + * 5. No nonprofit user may place any restrictions on the use of this software, + * including as modified by the user, by any other authorized user. + * + * 6. GPGPU-SIM was developed primarily by Tor M. Aamodt, Wilson W. L. Fung, + * Ali Bakhoda, George L. Yuan, at the University of British Columbia, + * Vancouver, BC V6T 1Z4 + */ + +#include <float.h> +#include "shader.h" +#include "gpu-sim.h" +#include "addrdec.h" +#include "dram.h" +#include "dwf.h" +#include "warp_tracker.h" +#include "cflogger.h" +#include "gpu-misc.h" +#include "../cuda-sim/ptx_sim.h" +#include "../cuda-sim/ptx-stats.h" +#include "../cuda-sim/dram_callback.h" +#include "mem_fetch.h" +#include <string.h> + +#define PRIORITIZE_MSHR_OVER_WB 1 +#define MAX(a,b) (((a)>(b))?(a):(b)) + +extern unsigned int mergemiss; +extern unsigned int L1_write_miss; +extern unsigned int L1_read_miss; +extern unsigned int L1_write_hit_on_miss; +extern unsigned int L1_writeback; +extern unsigned int L1_texture_miss; +extern unsigned int L1_const_miss; + +extern unsigned int finished_trace; +extern int gpgpu_perfect_mem; +extern int gpgpu_no_dl1; +extern char *gpgpu_cache_texl1_opt; +extern char *gpgpu_cache_constl1_opt; +extern char *gpgpu_cache_dl1_opt; +extern unsigned int gpu_n_thread_per_shader; +extern unsigned int gpu_n_mshr_per_shader; +extern unsigned int gpu_n_shader; +extern unsigned int gpu_n_mem; +extern int gpgpu_reg_bankconflict; +extern int gpgpu_dram_sched_queue_size; +extern int gpgpu_memlatency_stat; +extern dram_t **dram; +extern int *num_warps_issuable; +extern int *num_warps_issuable_pershader; + +extern unsigned long long gpu_sim_insn; +extern unsigned long long gpu_sim_insn_no_ld_const; +extern unsigned long long gpu_sim_insn_last_update; +extern unsigned long long gpu_completed_thread; +extern unsigned long long gpu_sim_cycle; +extern shader_core_ctx_t **sc; +extern unsigned int gpgpu_pre_mem_stages; +extern int gpgpu_no_divg_load; +extern unsigned int gpgpu_thread_swizzling; +extern unsigned int gpgpu_strict_simd_wrbk; +extern unsigned int warp_conflict_at_writeback; +extern unsigned int gpgpu_commit_pc_beyond_two; +extern int gpgpu_spread_blocks_across_cores; +extern int gpgpu_cflog_interval; + +extern unsigned int gpu_stall_by_MSHRwb; +extern unsigned int gpu_stall_shd_mem; +extern unsigned int gpu_stall_sh2icnt; + +enum mem_stage_access_type { + C_MEM, + T_MEM, + S_MEM, + G_MEM_LD, + L_MEM_LD, + G_MEM_ST, + L_MEM_ST, + N_MEM_STAGE_ACCESS_TYPE +}; + +enum mem_stage_stall_type { + NO_RC_FAIL = 0, + BK_CONF, + MSHR_RC_FAIL, + ICNT_RC_FAIL, + COAL_STALL, + WB_ICNT_RC_FAIL, + WB_CACHE_RSRV_FAIL, + N_MEM_STAGE_STALL_TYPE +}; +unsigned int gpu_stall_shd_mem_breakdown[N_MEM_STAGE_ACCESS_TYPE][N_MEM_STAGE_STALL_TYPE] = { {0} }; + +unsigned warp_size = 4; +int pipe_simd_width; +extern unsigned int **totalbankaccesses; //bankaccesses[shader id][dram chip id][bank id] +extern unsigned int *MCB_accesses; //upon cache miss, tracks which memory controllers accessed by a warp +extern unsigned int *num_MCBs_accessed; //tracks how many memory controllers are accessed whenever any thread in a warp misses in cache +extern unsigned int *max_return_queue_length; + +unsigned int *shader_cycle_distro; + +unsigned int g_waiting_at_barrier = 0; + +unsigned int gpgpu_shmem_size = 16384; +unsigned int gpgpu_shader_registers = 8192; +unsigned int gpgpu_shader_cta = 8; + +unsigned int gpgpu_n_load_insn = 0; +unsigned int gpgpu_n_store_insn = 0; +unsigned int gpgpu_n_shmem_insn = 0; +unsigned int gpgpu_n_tex_insn = 0; +unsigned int gpgpu_n_const_insn = 0; +unsigned int gpgpu_n_param_insn = 0; +unsigned int gpgpu_multi_unq_fetches = 0; + +extern int gpgpu_cache_wt_through; + +int gpgpu_shmem_bkconflict = 0; +unsigned int gpgpu_n_shmem_bkconflict = 0; +int gpgpu_n_shmem_bank = 16; + +int gpgpu_cache_bkconflict = 0; +unsigned int gpgpu_n_cache_bkconflict = 0; +unsigned int gpgpu_n_cmem_portconflict = 0; +int gpgpu_n_cache_bank = 16; + +extern int gpu_runtime_stat_flag; +int gpgpu_warpdistro_shader = -1; + +int gpgpu_interwarp_mshr_merge = 0; +int gpgpu_n_intrawarp_mshr_merge = 0; + +extern int gpgpu_partial_write_mask; +int gpgpu_n_partial_writes = 0; + +extern int gpgpu_n_mem_write_local; +extern int gpgpu_n_mem_write_global; + +#ifndef MhZ + #define MhZ *1000000 +#endif +extern double core_freq; +extern double icnt_freq; +extern double dram_freq; +extern double l2_freq; + +int gpgpu_shmem_port_per_bank = 4; +int gpgpu_cache_port_per_bank = 4; +int gpgpu_const_port_per_bank = 4; +int gpgpu_shmem_pipe_speedup = 2; + +unsigned int gpu_max_cta_per_shader = 8; +unsigned int gpu_padded_cta_size = 32; +int gpgpu_local_mem_map = 1; + + +extern int pdom_sched_type; +extern int n_pdom_sc_orig_stat; +extern int n_pdom_sc_single_stat; +extern int gpgpu_cuda_sim; +extern unsigned long long gpu_tot_sim_cycle; + +extern unsigned g_max_regs_per_thread; +void ptx_decode_inst( void *thd, unsigned *op, int *i1, int *i2, int *i3, int *i4, int *o1, int *o2, int *o3, int *o4, int *vectorin, int *vectorout, int *arch_reg ); +unsigned ptx_get_inst_op( void *thd); +void ptx_exec_inst( void *thd, address_type *addr, unsigned *space, unsigned *data_size, dram_callback_t* callback, unsigned warp_active_mask); +int ptx_branch_taken( void *thd ); +void ptx_sim_free_sm( void** thread_info ); +unsigned ptx_sim_init_thread( void** thread_info, int sid, unsigned tid,unsigned threads_left,unsigned num_threads, core_t *core, unsigned hw_cta_id, unsigned hw_warp_id); +unsigned ptx_sim_cta_size(); +const struct gpgpu_ptx_sim_kernel_info* ptx_sim_kernel_info(); +void set_option_gpgpu_spread_blocks_across_cores(int option); +int ptx_thread_done( void *thr ); +unsigned ptx_thread_donecycle( void *thr ); +int ptx_thread_get_next_pc( void *thd ); +void* ptx_thread_get_next_finfo( void *thd ); +int ptx_thread_at_barrier( void *thd ); +int ptx_thread_all_at_barrier( void *thd ); +unsigned long long ptx_thread_get_cta_uid( void *thd ); +void ptx_thread_reset_barrier( void *thd ); +void ptx_thread_release_barrier( void *thd ); +void ptx_print_insn( address_type pc, FILE *fp ); +int ptx_set_tex_cache_linesize( unsigned linesize); +void time_vector_update(unsigned int uid,int slot ,long int cycle,int type); + + + +///////////////////////////////////////////////////////////////////////////// +/*-------------------------------------------------------------------------*/ +/*-------------------------------------------------------------------------*/ + +static const char* MSHR_Status_str[] = { + "INITIALIZED", + "IN_ICNT2MEM", + "IN_ICNTOL2QUEUE", + "IN_L2TODRAMQUEUE", + "IN_DRAM_REQ_QUEUE", + "IN_DRAMTOL2QUEUE", + "IN_L2TOICNTQUEUE_HIT", + "IN_L2TOICNTQUEUE_MISS", + "IN_ICNT2SHADER", + "FETCHED", +}; + +// a helper function that deduce if a mshr contains an atomic operation +bool isatomic(mshr_entry_t *mshr) +{ + return (mshr->insts[0].callback.function != NULL); +} + +#include <map> +#include <utility> +#include <algorithm> +// a class that speeds up mshr lookup with a C++ multimap +class mshr_lookup { +private: + typedef std::multimap<unsigned long long int, mshr_entry*> mshr_lut_t; + mshr_lut_t m_lut; // multiple mshr entries can have the same tag +private: + void insert(mshr_entry* mshr) + { + using namespace std; + unsigned long long int tag_addr = mshr->addr; + m_lut.insert(make_pair(tag_addr, mshr)); + } + + mshr_entry* lookup(unsigned long long int addr) const + { + using namespace std; + // mshr_lut_t::const_iterator i_lut = m_lut.find(tag_addr); + pair<mshr_lut_t::const_iterator, mshr_lut_t::const_iterator> i_range = m_lut.equal_range(addr); + if (i_range.first == i_range.second) { + return NULL; + } else { + mshr_lut_t::const_iterator i_lut = i_range.first; + mshr_entry* mshr_hit = i_lut->second; + //follow match to end of merge chain: + //this won't really work for different sized requests, ie can't merge a 64b request to a 32b + while (mshr_hit->merged_requests) { + mshr_hit = mshr_hit->merged_requests; + } + return mshr_hit; + } + } + + void remove(mshr_entry* mshr) + { + using namespace std; + std::pair<mshr_lut_t::iterator, mshr_lut_t::iterator> i_range = m_lut.equal_range(mshr->addr); + + assert(i_range.first != i_range.second); + + for (mshr_lut_t::iterator i_lut = i_range.first; i_lut != i_range.second; ++i_lut) { + if (i_lut->second == mshr) { + m_lut.erase(i_lut); + break; + } + } + } +public: + //checks if we should do mshr merging for this mshr + bool can_merge(mshr_entry_t * mshr) + { + if (mshr->iswrite) return false; // can't merge a write + if (isatomic(mshr)) return false; // can't merge a atomic operation + bool interwarp_mshr_merge = gpgpu_interwarp_mshr_merge & GLOBAL_MSHR_MERGE; + if (mshr->istexture) { + interwarp_mshr_merge = gpgpu_interwarp_mshr_merge & TEX_MSHR_MERGE; + } else if (mshr->isconst) { + interwarp_mshr_merge = gpgpu_interwarp_mshr_merge & CONST_MSHR_MERGE; + } + return interwarp_mshr_merge; + } + + void mshr_fast_lookup_insert(mshr_entry* mshr) + { + if (!can_merge(mshr)) return; + insert(mshr); + } + + void mshr_fast_lookup_remove(mshr_entry* mshr) + { + if (!can_merge(mshr)) return; + remove(mshr); + } + + mshr_entry* shader_get_mergeable_mshr(mshr_entry_t* mshr) + { + if (!can_merge(mshr)) return NULL; + return lookup(mshr->addr); + } +}; + +class mem_access_t; +int is_tex ( int space ); +int is_const ( int space ); +int is_local ( int space ); +#include <iostream> +class mshr_shader_unit{ +public: + mshr_shader_unit(unsigned max_mshr): m_max_mshr(max_mshr), m_max_mshr_used(0){ + m_mshrs.resize(max_mshr); + for (std::vector<mshr_entry_t>::iterator i = m_mshrs.begin(); i != m_mshrs.end(); i++) m_free_list.push_back(i); + } + bool has_mshr(unsigned num){return (num <= m_free_list.size());} + unsigned mshr_used(){ return m_max_mshr - m_free_list.size();} + mshr_entry_t* add_mshr(mem_access_t &access, inst_t* warp); + + //return queue access; (includes texture pipeline return) + mshr_entry_t* return_head(){ + if (has_return()) + return &(*(choose_return_queue().front())); + else + return NULL; + } + //return queue pop; (includes texture pipeline return) + void pop_return_head() { + free_mshr(return_head()->this_mshr); + choose_return_queue().pop_front(); + } + + static void mshr_update_status(mshr_entry *mshr, enum mshr_status new_status ); + void mshr_return_from_mem(mshr_entry *mshr); + void check_mshr(mshr_entry *mshr){ + assert(find(m_free_list.begin(),m_free_list.end(),mshr->this_mshr)==m_free_list.end()); + assert(mshr->insts.size()); + } + unsigned get_max_mshr_used(){return m_max_mshr_used;} + void print(FILE* fp, shader_core_ctx_t* shader); +private: + typedef std::vector<mshr_entry_t> mshr_storage_type;//list might be less complicated, but slower? + mshr_storage_type m_mshrs; + std::deque< mshr_storage_type::iterator > m_free_list; + std::deque< mshr_storage_type::iterator > m_mshr_return_queue; + std::deque< mshr_storage_type::iterator > m_texture_mshr_pipeline; + unsigned m_max_mshr; + unsigned m_max_mshr_used; + mshr_lookup m_mshr_lookup; + + mshr_entry_t *alloc_free_mshr(bool istexture){ + assert(!m_free_list.empty()); + std::vector<mshr_entry_t>::iterator i = m_free_list.back(); + m_free_list.pop_back(); + i->this_mshr = i; + if (istexture) { + //put in texture pipeline + m_texture_mshr_pipeline.push_back(i); + } + if (mshr_used() > m_max_mshr_used) m_max_mshr_used = mshr_used(); + return &(*i); + } + void free_mshr(std::vector<mshr_entry_t>::iterator &i){ + //clean up up for next time, since not reallocating memory. + m_mshr_lookup.mshr_fast_lookup_remove(&(*i)); //need to remove before clearing insts, as they are accessed + i->insts.clear(); //add expects this to be clear + m_free_list.push_back(i); + } + bool has_return() { return (not m_mshr_return_queue.empty()) or ((not m_texture_mshr_pipeline.empty()) and m_texture_mshr_pipeline.front()->fetched());} + std::deque< std::vector<mshr_entry_t>::iterator > & choose_return_queue() { + //prioritize a ready texture over a global/const... + if ((not m_texture_mshr_pipeline.empty()) and m_texture_mshr_pipeline.front()->fetched()) return m_texture_mshr_pipeline; + assert(!m_mshr_return_queue.empty()); + return m_mshr_return_queue; + } +}; + + + +void mshr_shader_unit::mshr_update_status(mshr_entry *mshr, enum mshr_status new_status ) { + mshr->status = new_status; +#if DEBUGL1MISS + printf("cycle %d Addr %x %d \n",gpu_sim_cycle,CACHE_TAG_OF_64(mshr->addr),new_status); +#endif + mshr_entry * merged_req = mshr->merged_requests; + while (merged_req) { + merged_req->status = new_status; + merged_req = merged_req->merged_requests; + } +} + +inline void mshr_shader_unit::mshr_return_from_mem(mshr_entry *mshr){ + mshr_update_status(mshr, FETCHED); + if (not mshr->istexture) { + //place in return queue + m_mshr_return_queue.push_back(mshr->this_mshr); + //place all merged requests in return queue + mshr_entry * merged_req = mshr->merged_requests; + while (merged_req) { + m_mshr_return_queue.push_back(merged_req->this_mshr); + merged_req = merged_req->merged_requests; + } + } +} + +void mshr_return_from_mem(shader_core_ctx_t * shader, mshr_entry_t* mshr){ + shader->mshr_unit->mshr_return_from_mem(mshr); +} + +unsigned get_max_mshr_used(shader_core_ctx_t * shader){ + return shader->mshr_unit->get_max_mshr_used(); +} + + +void mshr_print(FILE* fp, shader_core_ctx_t *shader) { + shader->mshr_unit->print(fp, shader); +} + +void mshr_shader_unit::print(FILE* fp, shader_core_ctx_t* shader){ + for (mshr_storage_type::iterator it = m_mshrs.begin(); it != m_mshrs.end(); it++) { + //valid if not in free list; + if (find(m_free_list.begin(),m_free_list.end(), it) == m_free_list.end()) { + mshr_entry *mshr = &(*it); + fprintf(fp, "MSHR(%d): %s Addr:0x%llx Fetched:%d Merged:%d Status:%s\n", + shader->sid, + (mshr->iswrite)? "=>" : "<=", + mshr->addr, mshr->fetched(), + (mshr->merged_requests != NULL or mshr->merged_on_other_reqest), MSHR_Status_str[mshr->status]); + for (unsigned i = 0; i < mshr->insts.size(); i++) { + fprintf(fp,"\tthread: UID:%d HW:%d ReqAddr:0x%llx\n", mshr->insts[i].uid, mshr->insts[i].hw_thread_id, mshr->insts[i].memreqaddr); + } + } + } +} + +void mshr_update_status(mshr_entry* mshr, enum mshr_status new_status) { + mshr_entry *merged_req; + mshr->status = new_status; +#if DEBUGL1MISS + printf("cycle %d Addr %x %d \n",gpu_sim_cycle,CACHE_TAG_OF_64(mshr->addr),new_status); +#endif + merged_req = mshr->merged_requests; + while (merged_req) { + merged_req->status = new_status; + merged_req = merged_req->merged_requests; + } +} + +///////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////// +/*-------------------------------------------------------------------------*/ + +inst_t create_nop_inst() // just because C++ does not have designated initializer list.... +{ + inst_t nop_inst; + nop_inst.pc = 0; + nop_inst.op=NO_OP; + memset(nop_inst.out, 0, sizeof(nop_inst.out)); + memset(nop_inst.in, 0, sizeof(nop_inst.in)); + nop_inst.is_vectorin=0; + nop_inst.is_vectorout=0; + nop_inst.memreqaddr=0; + nop_inst.reg_bank_access_pending=0; + nop_inst.reg_bank_conflict_stall_checked=0, + nop_inst.hw_thread_id=-1; + nop_inst.wlane=-1; + nop_inst.uid = (unsigned)-1; + nop_inst.priority = (unsigned)-1; + nop_inst.ptx_thd_info = NULL; + nop_inst.warp_active_mask = 0; + nop_inst.ts_cycle = 0; + nop_inst.id_cycle = 0; + nop_inst.ex_cycle = 0; + nop_inst.mm_cycle = 0; + return nop_inst; +} + +static inst_t nop_inst = create_nop_inst(); + +int log2i(int n) { + int lg; + lg = -1; + while (n) { + n>>=1;lg++; + } + return lg; +} + +extern unsigned int gpu_n_warp_per_shader; + +shader_core_ctx_t* shader_create( const char *name, int sid, + unsigned int n_threads, + unsigned int n_mshr, + fq_push_t fq_push, + fq_has_buffer_t fq_has_buffer, + unsigned int model ) +{ + shader_core_ctx_t *sc; + int i; + unsigned int shd_n_set; + unsigned int shd_linesize; + unsigned int shd_n_assoc; + unsigned char shd_policy; + + unsigned int l1tex_cache_n_set; //L1 texture cache parameters + unsigned int l1tex_cache_linesize; + unsigned int l1tex_cache_n_assoc; + unsigned char l1tex_cache_policy; + + unsigned int l1const_cache_n_set; //L1 constant cache parameters + unsigned int l1const_cache_linesize; + unsigned int l1const_cache_n_assoc; + unsigned char l1const_cache_policy; + + if ( gpgpu_cuda_sim ) { + unsigned cta_size = ptx_sim_cta_size(); + if ( cta_size > n_threads ) { + printf("Execution error: Shader kernel CTA (block) size is too large for microarch config.\n"); + printf(" This can cause problems with applications that use __syncthreads.\n"); + printf(" CTA size (x*y*z) = %u, n_threads = %u\n", cta_size, n_threads ); + printf(" => either change -gpgpu_shader argument in gpgpusim.config file or\n"); + printf(" modify the CUDA source to decrease the kernel block size.\n"); + abort(); + } + } + + sc = new shader_core_ctx_t( gpu_n_warp_per_shader, gpgpu_shader_cta ); + + sc->name = name; + sc->sid = sid; + + sc->RR_k = 0; + + sc->model = model; + + sc->pipeline_reg = (inst_t**) calloc(N_PIPELINE_STAGES, sizeof(inst_t*)); + for (int j = 0; j<N_PIPELINE_STAGES; j++) { + sc->pipeline_reg[j] = (inst_t*) calloc(warp_size, sizeof(inst_t)); + for (unsigned i=0; i<warp_size; i++) { + sc->pipeline_reg[j][i] = nop_inst; + } + } + + if (gpgpu_pre_mem_stages) { + sc->pre_mem_pipeline = (inst_t**) calloc(gpgpu_pre_mem_stages+1, sizeof(inst_t*)); + for (unsigned j = 0; j<=gpgpu_pre_mem_stages; j++) { + sc->pre_mem_pipeline[j] = (inst_t*) calloc(pipe_simd_width, sizeof(inst_t)); + for (int i=0; i<pipe_simd_width; i++) { + sc->pre_mem_pipeline[j][i] = nop_inst; + } + } + } + sc->n_threads = n_threads; + sc->thread = (thread_ctx_t*) calloc(sizeof(thread_ctx_t), n_threads); + sc->not_completed = 0; + + unsigned n_warp = (n_threads/warp_size) + ((n_threads%warp_size)?1:0); + sc->warp.resize(n_warp, shd_warp_t(warp_size)); + for (unsigned j = 0; j < n_warp; j++) { + sc->warp[j].wid = j; + } + + sc->n_active_cta = 0; + for (i = 0; i<MAX_CTA_PER_SHADER; i++ ) { + sc->cta_status[i]=0; + } + //Warp variable initializations + sc->next_warp = 0; + sc->branch_priority = 0; + sc->max_branch_priority = (int*) malloc(sizeof(int)*n_threads); + + + for (unsigned i = 0; i<n_threads; i++) { + sc->max_branch_priority[i] = INT_MAX; + sc->thread[i].id = i; + + sc->thread[i].warp_priority = sc->max_branch_priority[i]; + sc->thread[i].avail4fetch = 0; + sc->thread[i].m_waiting_at_barrier = 0; + + sc->thread[i].ptx_thd_info = NULL; + sc->thread[i].cta_id = -1; + } + + sscanf(gpgpu_cache_dl1_opt,"%d:%d:%d:%c", + &shd_n_set, &shd_linesize, &shd_n_assoc, &shd_policy); + sscanf(gpgpu_cache_texl1_opt,"%d:%d:%d:%c", + &l1tex_cache_n_set, &l1tex_cache_linesize, &l1tex_cache_n_assoc, &l1tex_cache_policy); + sscanf(gpgpu_cache_constl1_opt,"%d:%d:%d:%c", + &l1const_cache_n_set, &l1const_cache_linesize, &l1const_cache_n_assoc, &l1const_cache_policy); +#define STRSIZE 32 + char L1c_name[STRSIZE]; + char L1texc_name[STRSIZE]; + char L1constc_name[STRSIZE]; + snprintf(L1c_name, STRSIZE, "L1c_%03d", sc->sid); + sc->L1cache = shd_cache_create(L1c_name,shd_n_set,shd_n_assoc,shd_linesize,shd_policy,1,0, + gpgpu_cache_wt_through?write_through:write_back); + shd_cache_bind_logger(sc->L1cache, sc->sid, get_shader_normal_cache_id()); + snprintf(L1texc_name, STRSIZE, "L1texc_%03d", sc->sid); + sc->L1texcache = shd_cache_create(L1texc_name,l1tex_cache_n_set,l1tex_cache_n_assoc,l1tex_cache_linesize,l1tex_cache_policy,1,0, no_writes ); + shd_cache_bind_logger(sc->L1texcache, sc->sid, get_shader_texture_cache_id()); + snprintf(L1constc_name, STRSIZE, "L1constc_%03d", sc->sid); + sc->L1constcache = shd_cache_create(L1constc_name,l1const_cache_n_set,l1const_cache_n_assoc,l1const_cache_linesize,l1const_cache_policy,1,0, no_writes ); + shd_cache_bind_logger(sc->L1constcache, sc->sid, get_shader_constant_cache_id()); + //at this point, should set the parameters used by addressing schemes of all textures + ptx_set_tex_cache_linesize(l1tex_cache_linesize); + + sc->mshr_unit = new mshr_shader_unit(gpu_n_mshr_per_shader); + + sc->fq_push = fq_push; + sc->fq_has_buffer = fq_has_buffer; + + sc->pdom_warp = (pdom_warp_ctx_t*)calloc(n_threads / warp_size, sizeof(pdom_warp_ctx_t)); + for (unsigned i = 0; i < n_threads / warp_size; ++i) { + sc->pdom_warp[i].m_stack_top = 0; + sc->pdom_warp[i].m_pc = (address_type*)calloc(warp_size * 2, sizeof(address_type)); + sc->pdom_warp[i].m_calldepth = (unsigned int*)calloc(warp_size * 2, sizeof(unsigned int)); + sc->pdom_warp[i].m_active_mask = (unsigned int*)calloc(warp_size * 2, sizeof(unsigned int)); + sc->pdom_warp[i].m_recvg_pc = (address_type*)calloc(warp_size * 2, sizeof(address_type)); + sc->pdom_warp[i].m_branch_div_cycle = (unsigned long long *)calloc(warp_size * 2, sizeof(unsigned long long )); + + memset(sc->pdom_warp[i].m_pc, -1, warp_size * 2 * sizeof(address_type)); + memset(sc->pdom_warp[i].m_calldepth, 0, warp_size * 2 * sizeof(unsigned int)); + memset(sc->pdom_warp[i].m_active_mask, 0, warp_size * 2 * sizeof(unsigned int)); + memset(sc->pdom_warp[i].m_recvg_pc, -1, warp_size * 2 * sizeof(address_type)); + } + + sc->waiting_at_barrier = 0; + + sc->last_issued_thread = sc->n_threads - 1; + + sc->using_dwf = (sc->model == DWF); + + sc->using_rrstage = (sc->model == DWF); + + sc->using_commit_queue = (sc->model == DWF + || sc->model == POST_DOMINATOR || sc->model == NO_RECONVERGE); + + if (sc->using_commit_queue) { + sc->thd_commit_queue = dq_create("thd_commit_queue", 0, 0, 0); + } + + sc->shmem_size = gpgpu_shmem_size; + sc->n_registers = gpgpu_shader_registers; + sc->n_cta = gpgpu_shader_cta; + + sc->shader_memory_new_instruction_processed = false; + + return sc; +} + + +unsigned shader_reinit(shader_core_ctx_t *sc, int start_thread, int end_thread ) +{ + int i; + unsigned result=0; + + if ( gpgpu_cuda_sim ) { + unsigned cta_size = ptx_sim_cta_size(); + if ( cta_size > sc->n_threads ) { + printf("Execution error: Shader kernel CTA (block) size is too large for microarch config.\n"); + printf(" This can cause problems with applications that use __syncthreads.\n"); + printf(" CTA size (x*y*z) = %u, n_threads = %u\n", cta_size, sc->n_threads ); + printf(" => either change -gpgpu_shader argument in gpgpusim.config file or\n"); + printf(" modify the CUDA source to decrease the kernel block size.\n"); + abort(); + } + } + + sc->next_warp = 0; + sc->branch_priority = 0; + + for (i = start_thread; i<end_thread; i++) + ptx_sim_free_sm(&sc->thread[i].ptx_thd_info); + + for (i = start_thread; i<end_thread; i++) { + sc->max_branch_priority[i] = INT_MAX; + sc->thread[i].warp_priority = sc->max_branch_priority[i]; + sc->thread[i].n_insn = 0; + sc->thread[i].cta_id = -1; + } + + for (unsigned i = start_thread / warp_size; i < end_thread / warp_size; ++i) { + sc->warp[i].reset(warp_size); + sc->pdom_warp[i].m_stack_top = 0; + memset(sc->pdom_warp[i].m_pc, -1, warp_size * 2 * sizeof(address_type)); + memset(sc->pdom_warp[i].m_calldepth, 0, warp_size * 2 * sizeof(unsigned int)); + memset(sc->pdom_warp[i].m_active_mask, 0, warp_size * 2 * sizeof(unsigned int)); + memset(sc->pdom_warp[i].m_recvg_pc, -1, warp_size * 2 * sizeof(address_type)); + memset(sc->pdom_warp[i].m_branch_div_cycle, 0, warp_size * 2 * sizeof(unsigned long long )); + } + + sc->waiting_at_barrier = 0; + sc->last_issued_thread = end_thread - 1; + + if (sc->using_commit_queue) { + if (!gpgpu_spread_blocks_across_cores) //assertion no longer holds with multiple blocks per core + assert(dq_empty(sc->thd_commit_queue)); + } + sc->pending_shmem_bkacc = 0; + sc->pending_cache_bkacc = 0; + sc->pending_cmem_acc = 0; + + //do not reset this here, shader memory may be in the middle of processing another cta's instruction. + //sc->shader_memory_new_instruction_processed = false; + + return result; +} + +// initialize a CTA in the shader core, currently only useful for PDOM and DWF + +void shader_init_CTA(shader_core_ctx_t *shader, int start_thread, int end_thread) +{ + int i; + int n_thread = end_thread - start_thread; + address_type start_pc = ptx_thread_get_next_pc(shader->thread[start_thread].ptx_thd_info); + if (shader->model == POST_DOMINATOR) { + int start_warp = start_thread / warp_size; + int end_warp = end_thread / warp_size + ((end_thread % warp_size)? 1 : 0); + for (i = start_warp; i < end_warp; ++i) { + shader->pdom_warp[i].m_stack_top = 0; + memset(shader->pdom_warp[i].m_pc, -1, warp_size * 2 * sizeof(address_type)); + memset(shader->pdom_warp[i].m_calldepth, 0, warp_size * 2 * sizeof(unsigned int)); + memset(shader->pdom_warp[i].m_active_mask, 0, warp_size * 2 * sizeof(unsigned int)); + memset(shader->pdom_warp[i].m_recvg_pc, -1, warp_size * 2 * sizeof(address_type)); + memset(shader->pdom_warp[i].m_branch_div_cycle, 0, warp_size * 2 * sizeof(unsigned long long )); + shader->pdom_warp[i].m_pc[0] = start_pc; + shader->pdom_warp[i].m_calldepth[0] = 1; + int t = 0; + for (t = 0; t < (int)warp_size; t++) { + if ( i * (int)warp_size + t < end_thread ) { + shader->pdom_warp[i].m_active_mask[0] |= (1 << t); + } + } + } + } else if (shader->model == DWF) { + dwf_init_CTA(shader->sid, start_thread, n_thread, start_pc); + } + + for (i = start_thread; i<end_thread; i++) { + shader->thread[i].in_scheduler = 1; + } +} + + + + +// register id for unused register slot in instruction +#define DNA (0) + +unsigned g_next_shader_inst_uid=1; + +// check to see if the fetch stage need to be stalled +int shader_fetch_stalled(shader_core_ctx_t *shader) +{ + int n_warp_parts = warp_size/pipe_simd_width; + + if (shader->warp_part2issue < n_warp_parts) { + return 1; + } + + for (unsigned i=0; i<warp_size; i++) { + if (shader->pipeline_reg[TS_IF][i].hw_thread_id != -1 ) { + return 1; // stalled + } + } + for (int i=0; i<pipe_simd_width; i++) { + if (shader->pipeline_reg[IF_ID][i].hw_thread_id != -1 ) { + return 1; // stalled + } + } + + shader->warp_part2issue = 0; // reset pointer to first warp part + shader->new_warp_TS = 1; + + return 0; // not stalled +} + +// initalize the pipeline stage register to nops +void shader_clear_stage_reg(shader_core_ctx_t *shader, int stage) +{ + for (unsigned i=0; i<warp_size; i++) { + shader->pipeline_reg[stage][i] = nop_inst; + } +} + +// return the next pc of a thread +address_type shader_thread_nextpc(shader_core_ctx_t *shader, int tid) +{ + assert( gpgpu_cuda_sim ); + address_type pc = ptx_thread_get_next_pc( shader->thread[tid].ptx_thd_info ); + return pc; +} + +// issue thread to the warp +// tid - thread id, warp_id - used by PDOM, wlane - position in warp +void shader_issue_thread(shader_core_ctx_t *shader, int tid, int wlane, unsigned active_mask ) +{ + if ( gpgpu_cuda_sim ) { + shader->pipeline_reg[TS_IF][wlane].hw_thread_id = tid; + shader->pipeline_reg[TS_IF][wlane].wlane = wlane; + shader->pipeline_reg[TS_IF][wlane].pc = ptx_thread_get_next_pc( shader->thread[tid].ptx_thd_info ); + shader->pipeline_reg[TS_IF][wlane].ptx_thd_info = shader->thread[tid].ptx_thd_info; + shader->pipeline_reg[TS_IF][wlane].memreqaddr = 0; + shader->pipeline_reg[TS_IF][wlane].reg_bank_conflict_stall_checked = 0; + shader->pipeline_reg[TS_IF][wlane].reg_bank_access_pending = 0; + shader->pipeline_reg[TS_IF][wlane].uid = g_next_shader_inst_uid++; + shader->pipeline_reg[TS_IF][wlane].warp_active_mask = active_mask; + shader->pipeline_reg[TS_IF][wlane].ts_cycle = gpu_tot_sim_cycle + gpu_sim_cycle; + } + assert( shader->thread[tid].avail4fetch > 0 ); + shader->thread[tid].avail4fetch--; + assert( shader->warp[wid_from_hw_tid(tid,warp_size)].n_avail4fetch > 0 ); + shader->warp[wid_from_hw_tid(tid,warp_size)].n_avail4fetch--; +} + +void update_max_branch_priority(shader_core_ctx_t *shader, unsigned warp_hw_id, unsigned grid_num ) +{ + int temp_max = 0; + // This means that a group of threads has completed, + // hence need to update max_priority + for (unsigned i = 0; i<warp_size; i++) { + if ( !ptx_thread_done( shader->thread[hw_tid_from_wid(warp_hw_id,warp_size,i)].ptx_thd_info ) ) { + if (shader->thread[hw_tid_from_wid(warp_hw_id,warp_size,i)].warp_priority>=temp_max) { + temp_max = shader->thread[hw_tid_from_wid(warp_hw_id,warp_size,i)].warp_priority; + } + } + } + for (unsigned i = 0; i<warp_size; i++) { + shader->max_branch_priority[hw_tid_from_wid(warp_hw_id,warp_size,i)] = temp_max; + } +} + +void shader_fetch_simd_no_reconverge(shader_core_ctx_t *shader, unsigned int shader_number, int grid_num ) +{ + int i; + int tid; + int new_tid = 0; + address_type pc = 0; + int warp_ok = 0; + int n_warp = shader->n_threads/warp_size; + int complete = 0; + + assert(gpgpu_cuda_sim); + + // First, check to see if entire program is completed, + // if it is, then break out of loop + for (unsigned i=0; i<shader->n_threads; i++) { + if (!ptx_thread_done( shader->thread[i].ptx_thd_info )) { + complete = 0; + break; + } else { + complete = 1; + } + } + if (complete) { + // printf("Shader has completed program.\n"); + return; + } + + if (shader_fetch_stalled(shader)) { + return; + } + shader_clear_stage_reg(shader, TS_IF); + + // Finds a warp where all threads in it are available for fetching + // simultaneously(all threads are not yet in pipeline, or, the ones + // that are not available, are completed already + for (i=0; i<n_warp; i++) { + int n_completed = shader->warp[shader->next_warp].n_completed; + int n_avail4fetch = shader->warp[shader->next_warp].n_avail4fetch; + if (((n_completed) == (int)warp_size) || + ((n_completed + n_avail4fetch) < (int)warp_size) ) { + //All threads in this warp have completed, hence go to next warp + //Or, some of the threads are still in pipeline + warp_ok = 0; // hey look, it's a silent register update / store instruction! (this operation is redundant) + shader->next_warp = (shader->next_warp+1)%n_warp; + } else { + int n_waiting_at_barrier = shader->warp[shader->next_warp].n_waiting_at_barrier; + if ( n_waiting_at_barrier >= (int)warp_size ) { + warp_ok = 0; // hey look, it's a silent register update / store instruction! (this operation is redundant) + continue; + } + warp_ok = 1; + break; + } + } + // None of the instructions from inside the warp can be scheduled -> should + // probably just stall, ie nops into pipeline + if (!warp_ok) { + shader_clear_stage_reg(shader, TS_IF); // NOTE: is this needed? + shader->next_warp = (shader->next_warp+1)%n_warp; // NOTE: this is not round-robin. + return; + } + + tid = warp_size*shader->next_warp; + + for (i = 0; i<(int)warp_size; i++) { + if (shader->thread[tid+i].warp_priority == shader->max_branch_priority[tid+i]) { + pc = shader_thread_nextpc(shader, tid+i); + new_tid = tid+i; + break; + } + } + //Determine which instructions inside this 'warp' will be scheduled together at this run + //If they are cannot be scheduled together then 'save' their branch priority + for (i = 0; i<(int)warp_size; i++) { + if (!ptx_thread_done( shader->thread[tid+i].ptx_thd_info )) { + address_type next_pc; + next_pc = shader_thread_nextpc(shader, tid+i); + if (next_pc != pc || + shader->thread[tid+i].warp_priority != shader->max_branch_priority[tid+i] || + shader->thread[tid+i].m_waiting_at_barrier) { + if (!ptx_thread_done( shader->thread[tid+i].ptx_thd_info )) { + if ( !shader->thread[tid + i].m_waiting_at_barrier ) { + shader->thread[tid + i].warp_priority = shader->branch_priority; + } + } + } else { + shader_issue_thread(shader, tid+i, i,(unsigned)-1); + } + } + } + shader->branch_priority++; + + shader->next_warp = (shader->next_warp+1)%n_warp; +} + +int pdom_sched_find_next_warp (shader_core_ctx_t *shader,int pdom_sched_policy, int* ready_warps + , int ready_warp_count, int* last_warp, int w_comp_c, int w_pipe_c, int w_barr_c) +{ + int n_warp = shader->n_threads/warp_size; + int i=0; + int selected_warp = ready_warps[0]; + int found =0; + + switch (pdom_sched_policy) { + case 0: + selected_warp = ready_warps[0]; //first ok warp found + found=1; + break; + case 1 ://random + selected_warp = ready_warps[rand()%ready_warp_count]; + found=1; + break; + case 8 :// execute the first available warp which is after the warp execued last time + found=0; + selected_warp = (last_warp[shader->sid] + 1 ) % n_warp; + while (!found) { + for (i=0;i<ready_warp_count;i++) { + if (selected_warp==ready_warps[i]) { + found=1; + } + } + if (!found) + selected_warp = (selected_warp + 1 ) % n_warp; + } + break; + default: + assert(0); + } + if (found) { + if (ready_warp_count==1) { + n_pdom_sc_single_stat++; + } else { + n_pdom_sc_orig_stat++; + } + return selected_warp; + } else { + return -1; + } +} + +void shader_fetch_simd_postdominator(shader_core_ctx_t *shader, unsigned int shader_number, int grid_num) { + int i; + int warp_ok = 0; + int n_warp = shader->n_threads/warp_size; + int complete = 0; + int tmp_warp; + int warp_id; + + address_type check_pc = -1; + + assert(gpgpu_cuda_sim); + + // First, check to see if entire program is completed, + // if it is, then break out of loop + for (unsigned i=0; i<shader->n_threads; i++) { + if (!ptx_thread_done( shader->thread[i].ptx_thd_info )) { + complete = 0; + break; + } else { + complete = 1; + } + } + if (complete) { + return; + } + + if (shader_fetch_stalled(shader)) { + return; + } + shader_clear_stage_reg(shader, TS_IF); + + int ready_warp_count = 0; + int w_comp_c = 0 ; + int w_pipe_c = 0 ; + int w_barr_c = 0 ; + static int * ready_warps = NULL; + static int * tmp_ready_warps = NULL; + if (!ready_warps) { + ready_warps = (int*)calloc(n_warp,sizeof(int)); + } + if (!tmp_ready_warps) { + tmp_ready_warps = (int*)calloc(n_warp,sizeof(int)); + } + for (i=0; i<n_warp; i++) { + ready_warps[i]=-1; + tmp_ready_warps[i]=-1; + } + + static int* last_warp; //keeps track of last warp issued per shader + if (!last_warp) { + last_warp = (int*)calloc(gpu_n_shader,sizeof(int)); + } + + + // Finds a warp where all threads in it are available for fetching + // simultaneously(all threads are not yet in pipeline, or, the ones + // that are not available, are completed already + for (i=0; i<n_warp; i++) { + int n_completed = shader->warp[shader->next_warp].n_completed; + int n_avail4fetch = shader->warp[shader->next_warp].n_avail4fetch; + + if ((n_completed) == (int)warp_size) { + //All threads in this warp have completed + w_comp_c++; + } else if ((n_completed+n_avail4fetch) < (int)warp_size) { + //some of the threads are still in pipeline + w_pipe_c++; + } else if ( shader->warp_waiting_at_barrier(shader->next_warp) ) { + w_barr_c++; + } else { + // A valid warp is found at this point + tmp_ready_warps[ready_warp_count] = shader->next_warp; + ready_warp_count++; + } + shader->next_warp = (shader->next_warp + 1) % n_warp; + } + for (i=0;i<ready_warp_count;i++) { + ready_warps[i]=tmp_ready_warps[i]; + } + + num_warps_issuable[ready_warp_count]++; + num_warps_issuable_pershader[shader->sid]+= ready_warp_count; + + if (ready_warp_count) { + tmp_warp = pdom_sched_find_next_warp (shader, pdom_sched_type ,ready_warps + , ready_warp_count, last_warp, w_comp_c, w_pipe_c ,w_barr_c); + if (tmp_warp != -1) { + shader->next_warp = tmp_warp; + warp_ok=1; + } + } + + static int no_warp_issued; + // None of the instructions from inside the warp can be scheduled -> should + // probably just stall, ie nops into pipeline + if (!warp_ok) { + shader_clear_stage_reg(shader, TS_IF); + shader->next_warp = (shader->next_warp+1) % n_warp; + no_warp_issued = 1 ; + return; + } + + /************************************************************/ + //at this point we have a warp to execute which is pointed to by + //shader->next_warp + + warp_id = shader->next_warp; + last_warp[shader->sid] = warp_id; + int wtid = warp_size*shader->next_warp; + + pdom_warp_ctx_t *scheduled_warp = &(shader->pdom_warp[warp_id]); + + int stack_top = scheduled_warp->m_stack_top; + + address_type top_pc = scheduled_warp->m_pc[stack_top]; + unsigned int top_active_mask = scheduled_warp->m_active_mask[stack_top]; + address_type top_recvg_pc = scheduled_warp->m_recvg_pc[stack_top]; + + assert(top_active_mask != 0); + + const address_type null_pc = 0; + int warp_diverged = 0; + address_type new_recvg_pc = null_pc; + while (top_active_mask != 0) { + + // extract a group of threads with the same next PC among the active threads in the warp + address_type tmp_next_pc = null_pc; + unsigned int tmp_active_mask = 0; + void *first_active_thread=NULL; + for (i = warp_size - 1; i >= 0; i--) { + unsigned int mask = (1 << i); + if ((top_active_mask & mask) == mask) { // is this thread active? + if (ptx_thread_done( shader->thread[wtid+i].ptx_thd_info )) { + top_active_mask &= ~mask; // remove completed thread from active mask + } else if (tmp_next_pc == null_pc) { + first_active_thread = shader->thread[wtid+i].ptx_thd_info; + tmp_next_pc = shader_thread_nextpc(shader, wtid+i); + tmp_active_mask |= mask; + top_active_mask &= ~mask; + } else if (tmp_next_pc == shader_thread_nextpc(shader, wtid+i)) { + tmp_active_mask |= mask; + top_active_mask &= ~mask; + } + } + } + + // discard the new entry if its PC matches with reconvergence PC + // that automatically reconverges the entry + if (tmp_next_pc == top_recvg_pc) continue; + + // this new entry is not converging + // if this entry does not include thread from the warp, divergence occurs + if (top_active_mask != 0 && warp_diverged == 0) { + warp_diverged = 1; + // modify the existing top entry into a reconvergence entry in the pdom stack + new_recvg_pc = get_converge_point(top_pc,first_active_thread); + if (new_recvg_pc != top_recvg_pc) { + scheduled_warp->m_pc[stack_top] = new_recvg_pc; + scheduled_warp->m_branch_div_cycle[stack_top] = gpu_sim_cycle; + stack_top += 1; + scheduled_warp->m_branch_div_cycle[stack_top] = 0; + } + } + + // discard the new entry if its PC matches with reconvergence PC + if (warp_diverged && tmp_next_pc == new_recvg_pc) continue; + + // update the current top of pdom stack + scheduled_warp->m_pc[stack_top] = tmp_next_pc; + scheduled_warp->m_active_mask[stack_top] = tmp_active_mask; + if (warp_diverged) { + scheduled_warp->m_calldepth[stack_top] = 0; + scheduled_warp->m_recvg_pc[stack_top] = new_recvg_pc; + } else { + scheduled_warp->m_recvg_pc[stack_top] = top_recvg_pc; + } + stack_top += 1; // set top to next entry in the pdom stack + } + scheduled_warp->m_stack_top = stack_top - 1; + + assert(scheduled_warp->m_stack_top >= 0); + assert(scheduled_warp->m_stack_top < (int)warp_size * 2); + + // schedule threads according to active mask on the top of pdom stack + for (i = 0; i < (int)warp_size; i++) { + unsigned int mask = (1 << i); + if ((scheduled_warp->m_active_mask[scheduled_warp->m_stack_top] & mask) == mask) { + assert (!ptx_thread_done( shader->thread[wtid+i].ptx_thd_info )); + shader_issue_thread(shader, wtid+i, i, scheduled_warp->m_active_mask[scheduled_warp->m_stack_top]); + } + } + shader->next_warp = (shader->next_warp+1)%n_warp; + + // check if all issued threads have the same pc + for (i = 0; i < (int) warp_size; i++) { + if ( shader->pipeline_reg[TS_IF][i].hw_thread_id != -1 ) { + if ( check_pc == (unsigned)-1 ) { + check_pc = shader->pipeline_reg[TS_IF][i].pc; + } else { + assert( check_pc == shader->pipeline_reg[TS_IF][i].pc ); + } + } + } +} + + +void get_pdom_stack_top_info( unsigned sid, unsigned tid, unsigned *pc, unsigned *rpc ) +{ + unsigned warp_id = tid/warp_size; + pdom_warp_ctx_t *warp_info = &(sc[sid]->pdom_warp[warp_id]); + unsigned idx = warp_info->m_stack_top; + *pc = warp_info->m_pc[idx]; + *rpc = warp_info->m_recvg_pc[idx]; +} + +void shader_fetch_mimd( shader_core_ctx_t *shader, unsigned int shader_number ) +{ + unsigned int last_issued_thread = 0; + + if (shader_fetch_stalled(shader)) { + return; + } + shader_clear_stage_reg(shader, TS_IF); + + // some form of barrel processing: + // - checking availability from the thread after the last issued thread + for (int i=0, j=0;i<(int)shader->n_threads && j< (int) warp_size;i++) { + int thd_id = (i + shader->last_issued_thread + 1) % shader->n_threads; + if (shader->thread[thd_id].avail4fetch && !shader->thread[thd_id].m_waiting_at_barrier ) { + shader_issue_thread(shader, thd_id, j,(unsigned)-1); + last_issued_thread = thd_id; + j++; + } + } + shader->last_issued_thread = last_issued_thread; +} + +// seperate the incoming warp into multiple warps with seperate pcs +int split_warp_by_pc(int *tid_in, shader_core_ctx_t *shader, int **tid_split, address_type *pc) { + unsigned n_pc = 0; + static int *pc_cnt = NULL; // count the number of threads with the same pc + + assert(tid_in); + assert(tid_split); + assert(pc); + memset(pc,0,sizeof(address_type)*warp_size); + + if (!pc_cnt) pc_cnt = (int*) malloc(sizeof(int)*warp_size); + memset(pc_cnt,0,sizeof(int)*warp_size); + + // go through each thread in the given warp + for (unsigned i=0; i< warp_size; i++) { + if (tid_in[i] < 0) continue; + int matched = 0; + address_type thd_pc; + thd_pc = shader_thread_nextpc(shader, tid_in[i]); + + // check to see if the pc has occured before + for (unsigned j=0; j<n_pc; j++) { + if (thd_pc == pc[j]) { + tid_split[j][pc_cnt[j]] = tid_in[i]; + pc_cnt[j]++; + matched = 1; + break; + } + } + // if not, put the tid in a seperate warp + if (!matched) { + assert(n_pc < warp_size); + tid_split[n_pc][0] = tid_in[i]; + pc[n_pc] = thd_pc; + pc_cnt[n_pc] = 1; + n_pc++; + } + } + return n_pc; +} + +// see if this warp just executed the barrier instruction +int warp_reached_barrier(int *tid_in, shader_core_ctx_t *shader) +{ + int reached_barrier = 0; + for (unsigned i=0; i<warp_size; i++) { + if (tid_in[i] < 0) continue; + if (shader->thread[tid_in[i]].m_reached_barrier) { + reached_barrier = 1; + break; + } + } + return reached_barrier; +} + +// seperate the incoming warp into multiple warps with seperate pcs and cta +int split_warp_by_cta(int *tid_in, shader_core_ctx_t *shader, int **tid_split, address_type *pc, int *cta) { + unsigned n_pc = 0; + static int *pc_cnt = NULL; // count the number of threads with the same pc + + assert(tid_in); + assert(tid_split); + assert(pc); + memset(pc,0,sizeof(address_type)*warp_size); + + if (!pc_cnt) pc_cnt = (int*) malloc(sizeof(int)*warp_size); + memset(pc_cnt,0,sizeof(int)*warp_size); + + // go through each thread in the given warp + for (unsigned i=0; i<warp_size; i++) { + if (tid_in[i] < 0) continue; + int matched = 0; + address_type thd_pc; + thd_pc = shader_thread_nextpc(shader, tid_in[i]); + + int thd_cta = ptx_thread_get_cta_uid( shader->thread[tid_in[i]].ptx_thd_info ); + + // check to see if the pc has occured before + for (unsigned j=0; j<n_pc; j++) { + if (thd_pc == pc[j] && thd_cta == cta[j]) { + tid_split[j][pc_cnt[j]] = tid_in[i]; + pc_cnt[j]++; + matched = 1; + break; + } + } + // if not, put the tid in a seperate warp + if (!matched) { + assert(n_pc < warp_size); + tid_split[n_pc][0] = tid_in[i]; + pc[n_pc] = thd_pc; + cta[n_pc] = thd_cta; + pc_cnt[n_pc] = 1; + n_pc++; + } + } + return n_pc; +} + +void shader_fetch_simd_dwf( shader_core_ctx_t *shader, unsigned int shader_number ) { + + static int *tid_in = NULL; + static int *tid_out = NULL; + + if (!tid_in) { + tid_in = (int*) malloc(sizeof(int)*warp_size); + memset(tid_in, -1, sizeof(int)*warp_size); + } + if (!tid_out) { + tid_out = (int*) malloc(sizeof(int)*warp_size); + memset(tid_out, -1, sizeof(int)*warp_size); + } + + + static int **tid_split = NULL; + if (!tid_split) { + tid_split = (int**)malloc(sizeof(int*)*warp_size); + tid_split[0] = (int*)malloc(sizeof(int)*warp_size*warp_size); + for (unsigned i=1; i<warp_size; i++) { + tid_split[i] = tid_split[0] + warp_size * i; + } + } + + static address_type *thd_pc = NULL; + if (!thd_pc) thd_pc = (address_type*)malloc(sizeof(address_type)*warp_size); + static int *thd_cta = NULL; + if (!thd_cta) thd_cta = (int*)malloc(sizeof(int)*warp_size); + + int warpupdate_bw = 1; + while (!dq_empty(shader->thd_commit_queue) && warpupdate_bw > 0) { + // grab a committed warp, split it into multiple BRUs (tid_split) by PC + int *tid_commit = (int*)dq_pop(shader->thd_commit_queue); + memset(tid_split[0], -1, sizeof(int)*warp_size*warp_size); + memset(thd_pc, 0, sizeof(address_type)*warp_size); + memset(thd_cta, -1, sizeof(int)*warp_size); + + int reached_barrier = warp_reached_barrier(tid_commit, shader); + + unsigned n_warp_update; + if (reached_barrier) { + n_warp_update = split_warp_by_cta(tid_commit, shader, tid_split, thd_pc, thd_cta); + } else { + n_warp_update = split_warp_by_pc(tid_commit, shader, tid_split, thd_pc); + } + + if (n_warp_update > 2) gpgpu_commit_pc_beyond_two++; + warpupdate_bw -= n_warp_update; + // put the splitted warp updates into the DWF scheduler + for (unsigned i=0;i<n_warp_update;i++) { + for (unsigned j=0;j<warp_size;j++) { + if (tid_split[i][j] < 0) continue; + assert(shader->thread[tid_split[i][j]].avail4fetch); + assert(!shader->thread[tid_split[i][j]].in_scheduler); + shader->thread[tid_split[i][j]].in_scheduler = 1; + } + dwf_clear_accessed(shader->sid); + if (reached_barrier) { + dwf_update_warp_at_barrier(shader->sid, tid_split[i], thd_pc[i], thd_cta[i]); + } else { + dwf_update_warp(shader->sid, tid_split[i], thd_pc[i]); + } + } + + free_commit_warp(tid_commit); + } + + // Track the #PC right after the warps are input to the scheduler + dwf_update_statistics(shader->sid); + dwf_clear_policy_access(shader->sid); + + if (shader_fetch_stalled(shader)) { + return; + } + shader_clear_stage_reg(shader, TS_IF); + + address_type scheduled_pc; + dwf_issue_warp(shader->sid, tid_out, &scheduled_pc); + + for (unsigned i=0; i<warp_size; i++) { + int issue_tid = tid_out[i]; + if (issue_tid >= 0) { + shader_issue_thread(shader, issue_tid, i, (unsigned)-1); + shader->thread[issue_tid].in_scheduler = 0; + shader->thread[issue_tid].m_reached_barrier = 0; + shader->last_issued_thread = issue_tid; + assert(shader->pipeline_reg[TS_IF][i].pc == scheduled_pc); + } + } +} + +void print_shader_cycle_distro( FILE *fout ) +{ + fprintf(fout, "Warp Occupancy Distribution:\n"); + fprintf(fout, "Stall:%d\t", shader_cycle_distro[0]); + fprintf(fout, "W0_Idle:%d\t", shader_cycle_distro[1]); + fprintf(fout, "W0_Mem:%d", shader_cycle_distro[2]); + for (unsigned i = 3; i < warp_size + 3; i++) { + fprintf(fout, "\tW%d:%d", i-2, shader_cycle_distro[i]); + } + fprintf(fout, "\n"); +} + +void inflight_memory_insn_add( shader_core_ctx_t *shader, inst_t *mem_insn) +{ + if (enable_ptx_file_line_stats) { + ptx_file_line_stats_add_inflight_memory_insn(shader->sid, mem_insn->pc); + } +} + +void inflight_memory_insn_sub( shader_core_ctx_t *shader, inst_t *mem_insn) +{ + if (enable_ptx_file_line_stats) { + ptx_file_line_stats_sub_inflight_memory_insn(shader->sid, mem_insn->pc); + } +} + +void report_exposed_memory_latency( shader_core_ctx_t *shader ) +{ + if (enable_ptx_file_line_stats) { + ptx_file_line_stats_commit_exposed_latency(shader->sid, 1); + } +} + +static int gpgpu_warp_occ_detailed = 0; +static int **warp_occ_detailed = NULL; + +void check_stage_pcs( shader_core_ctx_t *shader, unsigned stage ); +void check_pm_stage_pcs( shader_core_ctx_t *shader, unsigned stage ); + +void shader_fetch( shader_core_ctx_t *shader, unsigned int shader_number, int grid_num ) +{ + assert(shader->model < NUM_SIMD_MODEL); + int n_warp_parts = warp_size/pipe_simd_width; + + // check if decode stage is stalled + int decode_stalled = 0; + for (int i = 0; i < pipe_simd_width; i++) { + if (shader->pipeline_reg[IF_ID][i].hw_thread_id != -1 ) + decode_stalled = 1; + } + + if (shader->gpu_cycle % n_warp_parts == 0) { + + if (shader->model == POST_DOMINATOR || shader->model == NO_RECONVERGE) { + int warpupdate_bw = 1; // number of warps to be unlocked per scheduler cycle + while (!dq_empty(shader->thd_commit_queue) && warpupdate_bw > 0) { + // grab a committed warp and unlock it here + int *tid_commit = (int*)dq_pop(shader->thd_commit_queue); + for (unsigned i = 0; i < warp_size; i++) { + if (tid_commit[i] >= 0) { + shader->thread[tid_commit[i]].avail4fetch++; + assert(shader->thread[tid_commit[i]].avail4fetch <= 1); + assert( shader->warp[wid_from_hw_tid(tid_commit[i],warp_size)].n_avail4fetch < (unsigned)warp_size ); + shader->warp[wid_from_hw_tid(tid_commit[i],warp_size)].n_avail4fetch++; + } + } + warpupdate_bw -= 1; + free_commit_warp(tid_commit); + } + } + + switch (shader->model) { + case NO_RECONVERGE: + shader_fetch_simd_no_reconverge(shader, shader_number, grid_num ); + break; + case POST_DOMINATOR: + shader_fetch_simd_postdominator(shader, shader_number, grid_num); + break; + case MIMD: + shader_fetch_mimd(shader, shader_number); + break; + case DWF: + shader_fetch_simd_dwf(shader, shader_number); + break; + default: + fprintf(stderr, "Unknown scheduler: %d\n", shader->model); + assert(0); + break; + } + + static int *tid_out = NULL; + if (!tid_out) { + tid_out = (int*) malloc(sizeof(int) * warp_size); + } + memset(tid_out, -1, sizeof(int)*warp_size); + + if (!shader_cycle_distro) { + shader_cycle_distro = (unsigned int*) calloc(warp_size + 3, sizeof(unsigned int)); + } + + if (gpgpu_no_divg_load && shader->new_warp_TS && !decode_stalled) { + int n_thd_in_warp = 0; + address_type pc_out = 0xDEADBEEF; + for (unsigned i=0; i<warp_size; i++) { + tid_out[i] = shader->pipeline_reg[TS_IF][i].hw_thread_id; + if (tid_out[i] >= 0) { + n_thd_in_warp += 1; + pc_out = shader->pipeline_reg[TS_IF][i].pc; + } + } + wpt_register_warp(tid_out, shader); + if (gpu_runtime_stat_flag & GPU_RSTAT_DWF_MAP) { + track_thread_pc( shader->sid, tid_out, pc_out ); + } + if (gpgpu_cflog_interval != 0) { + insn_warp_occ_log( shader->sid, pc_out, n_thd_in_warp); + shader_warp_occ_log( shader->sid, n_thd_in_warp); + } + if ( gpgpu_warpdistro_shader < 0 || shader->sid == gpgpu_warpdistro_shader ) { + shader_cycle_distro[n_thd_in_warp + 2] += 1; + if (n_thd_in_warp == 0) { + if (shader->pending_mem_access == 0) shader_cycle_distro[1]++; + } + } + shader->new_warp_TS = 0; + + if (enable_ptx_file_line_stats && n_thd_in_warp > 0) { + //ptx_file_line_stats_add_warp_issued(pc_out); + //ptx_file_line_stats_add_warp_occ_total(pc_out, n_thd_in_warp); + } + + if ( gpgpu_warp_occ_detailed && + n_thd_in_warp && (shader->model == POST_DOMINATOR) ) { + int n_warp = gpu_n_thread_per_shader / warp_size; + if (!warp_occ_detailed) { + warp_occ_detailed = (int**) malloc(sizeof(int*) * gpu_n_shader * n_warp); + warp_occ_detailed[0] = (int*) calloc(sizeof(int), gpu_n_shader * n_warp * warp_size); + for (unsigned i = 0; i < n_warp * gpu_n_shader; i++) { + warp_occ_detailed[i] = warp_occ_detailed[0] + i * warp_size; + } + } + + int wid = -1; + for (unsigned i=0; i<warp_size; i++) { + if (tid_out[i] >= 0) wid = tid_out[i] / warp_size; + } + assert(wid != -1); + warp_occ_detailed[shader->sid * n_warp + wid][n_thd_in_warp - 1] += 1; + + if (shader->sid == 0 && wid == 16 && 0) { + printf("wtrace[%08x] ", pc_out); + for (unsigned i=0; i<warp_size; i++) { + printf("%03d ", tid_out[i]); + } + printf("\n"); + } + } + } else { + if ( gpgpu_warpdistro_shader < 0 || shader->sid == gpgpu_warpdistro_shader ) { + shader_cycle_distro[0] += 1; + } + } + + if (!decode_stalled) { + for (unsigned i = 0; i < warp_size; i++) { + int tid_tsif = shader->pipeline_reg[TS_IF][i].hw_thread_id; + address_type pc_out = shader->pipeline_reg[TS_IF][i].pc; + cflog_update_thread_pc(shader->sid, tid_tsif, pc_out); + } + } + + if (enable_ptx_file_line_stats && !decode_stalled) { + int TS_stage_empty = 1; + for (unsigned i = 0; i < warp_size; i++) { + if (shader->pipeline_reg[TS_IF][i].hw_thread_id >= 0) { + TS_stage_empty = 0; + break; + } + } + if (TS_stage_empty) { + report_exposed_memory_latency(shader); + } + } + } + + // if not, send the warp part to decode stage + if (!decode_stalled && shader->warp_part2issue < n_warp_parts) { + check_stage_pcs(shader,TS_IF); + for (int i = 0; i < pipe_simd_width; i++) { + int wlane_idx = shader->warp_part2issue * pipe_simd_width + i; + shader->pipeline_reg[IF_ID][i] = shader->pipeline_reg[TS_IF][wlane_idx]; + shader->pipeline_reg[IF_ID][i].if_cycle = gpu_tot_sim_cycle + gpu_sim_cycle; + shader->pipeline_reg[TS_IF][wlane_idx] = nop_inst; + } + shader->warp_part2issue += 1; + } +} + +inline int is_load ( op_type op ) { + return op == LOAD_OP; +} + +inline int is_store ( op_type op ) { + return op == STORE_OP; +} + +inline int is_tex ( int space ) { + return((space) == TEX_DIRECTIVE); +} + +inline int is_const ( int space ) { + return((space) == CONST_DIRECTIVE || (space) == PARAM_DIRECTIVE); +} + +inline int is_local ( int space ) { + return((space) == LOCAL_DIRECTIVE); +} + +inline int is_param ( int space ) { + return((space) == PARAM_DIRECTIVE); +} + +inline int is_shared ( int space ) { + return((space) == SHARED_DIRECTIVE); +} + +inline int shmem_bank ( address_type addr ) { + return((int)(addr/((address_type)WORD_SIZE)) % gpgpu_n_shmem_bank); +} + +inline int cache_bank ( address_type addr, shader_core_ctx_t *shader ) { + return(int)( addr >> (address_type)shader->L1cache->line_sz_log2 ) & ( gpgpu_n_cache_bank - 1 ); +} + +inline address_type coalesced_segment(address_type addr, unsigned segment_size_lg2bytes) +{ + return (addr >> segment_size_lg2bytes); +} + + +inline address_type translate_local_memaddr(address_type localaddr, shader_core_ctx_t *shader, int tid) +{ + // During functional execution, each thread sees its own memory space for local memory, but these + // need to be mapped to a shared address space for timing simulation. We do that mapping here. + localaddr -= 0x100; + localaddr /=4; + if (gpgpu_local_mem_map) { + // Dnew = D*nTpC*nCpS*nS + nTpC*C + T%nTpC + // C = S + nS*(T/nTpC) + // D = data index; T = thread; C = CTA; S = shader core; p = per + // keep threads in a warp contiguous + // then distribute across memory space by CTAs from successive shader cores first, + // then by successive CTA in same shader core + localaddr *= gpu_padded_cta_size * gpu_max_cta_per_shader * gpu_n_shader; + localaddr += gpu_padded_cta_size * (shader->sid + gpu_n_shader * (tid / gpu_padded_cta_size)); + localaddr += tid % gpu_padded_cta_size; + } else { + // legacy mapping that maps the same address in the local memory space of all threads + // to a single contiguous address region + localaddr *= gpu_n_shader * gpu_n_thread_per_shader; + localaddr += (gpu_n_thread_per_shader*shader->sid) + tid; + } + localaddr *= 4; + localaddr += 0x100; + + return localaddr; +} + +///////////////////////////////////////////////////////////////////////////////////////// +// Register Bank Conflict Structures + +int gpgpu_reg_bank_conflict_model = 0; + +#define MAX_REG_BANKS 32 +unsigned int gpgpu_num_reg_banks=8; // this needs to be less than MAX_REG_BANKS + +#define MAX_BANK_CONFLICT 8 /* tex can have four source and four destination regs */ + +class reg_bank_access { +public: + reg_bank_access():tot(0),rd(0),wr(0){ + for (unsigned i = 0; i < 4; i++) rd_regs[i] = -1; + } + unsigned tot; + unsigned rd; + unsigned wr; + int rd_regs[4]; +}; + +reg_bank_access g_reg_bank_access[MAX_REG_BANKS]; + +// just to use as "shorthand" for clearing accesses each cycle +static const struct reg_bank_access empty_reg_bank_access; + +unsigned int gpu_reg_bank_conflict_stalls = 0; +///////////////////////////////////////////////////////////////////////////////////////// + +void shader_decode( shader_core_ctx_t *shader, + unsigned int shader_number, + unsigned int grid_num ) { + + address_type addr; + dram_callback_t callback; + op_type op = NO_OP; + register int is_write; + int tid; + int i1, i2, i3, i4, o1, o2, o3, o4; //4 outputs needed for texture fetches in cuda-sim + int i; + int touched_priority=0; + int warp_tid=0; + unsigned space, data_size; + int vectorin, vectorout; + int arch_reg[MAX_REG_OPERANDS] = { -1 }; + address_type regs_regs_PC = 0xDEADBEEF; + address_type warp_current_pc = 0x600DBEEF; + address_type warp_next_pc = 0x600DBEEF; + int warp_diverging = 0; + unsigned warp_id = -1; + unsigned cta_id = -1; + + // stalling for register bank conflict + if ( gpgpu_reg_bank_conflict_model ) { + for (i=0; i<pipe_simd_width;i++) { + if ( shader->pipeline_reg[IF_ID][i].reg_bank_conflict_stall_checked ) { + if ( shader->pipeline_reg[IF_ID][i].reg_bank_access_pending > 0 ) { + assert( shader->pipeline_reg[IF_ID][i].reg_bank_access_pending <= 8 ); + shader->pipeline_reg[IF_ID][i].reg_bank_access_pending--; + gpu_reg_bank_conflict_stalls++; + return; + } + } + } + } + + for (i=0; i<pipe_simd_width;i++) { + int next_stage = (shader->using_rrstage)? ID_RR:ID_EX; + if (shader->pipeline_reg[next_stage][i].hw_thread_id != -1 ) { + return; /* stalled */ + } + } + + check_stage_pcs(shader,IF_ID); + + // decode the instruction + int first_valid_thread = -1; + for (i=0; i<pipe_simd_width;i++) { + + if (shader->pipeline_reg[IF_ID][i].hw_thread_id == -1 ) + continue; /* bubble */ + + /* get the next instruction to execute from fetch stage */ + tid = shader->pipeline_reg[IF_ID][i].hw_thread_id; + if (first_valid_thread == -1) { + first_valid_thread = i; + warp_id = tid/warp_size; + assert( !shader->warp_waiting_at_barrier(warp_id) ); + cta_id = shader->thread[tid].cta_id; + } + + if ( gpgpu_cuda_sim ) { + ptx_decode_inst( shader->thread[tid].ptx_thd_info, (unsigned*)&op, &i1, &i2, &i3, &i4, &o1, &o2, &o3, &o4, &vectorin, &vectorout, arch_reg); + shader->pipeline_reg[IF_ID][i].op = op; + shader->pipeline_reg[IF_ID][i].pc = ptx_thread_get_next_pc( shader->thread[tid].ptx_thd_info ); + shader->pipeline_reg[IF_ID][i].ptx_thd_info = shader->thread[tid].ptx_thd_info; + + } else { + abort(); + } + // put the info into the shader instruction structure + // - useful in tracking instruction dependency (not needed for now) + shader->pipeline_reg[IF_ID][i].in[0] = i1; + shader->pipeline_reg[IF_ID][i].in[1] = i2; + shader->pipeline_reg[IF_ID][i].in[2] = i3; + shader->pipeline_reg[IF_ID][i].in[3] = i4; + shader->pipeline_reg[IF_ID][i].out[0] = o1; + shader->pipeline_reg[IF_ID][i].out[1] = o2; + shader->pipeline_reg[IF_ID][i].out[2] = o3; + shader->pipeline_reg[IF_ID][i].out[3] = o4; + + } + + // checking for register bank conflict and stall accordingly + if ( gpgpu_reg_bank_conflict_model && + first_valid_thread != -1 && + !shader->pipeline_reg[first_valid_thread][IF_ID].reg_bank_conflict_stall_checked ) + { + for (i = 4; i < 8; i++) { + if( arch_reg[i] != -1 ) { + assert( arch_reg[i] >=0 ); + assert( gpgpu_num_reg_banks <= MAX_REG_BANKS ); + int skip = 0; + int bank = arch_reg[i] % gpgpu_num_reg_banks; + int opndreg = shader->pipeline_reg[first_valid_thread][IF_ID].in[i-4]; + assert(opndreg >= 0); + int j; + for (j = 0; j < 4; j++) { + if (g_reg_bank_access[bank].rd_regs[j] == -1) + break; + else if (g_reg_bank_access[bank].rd_regs[j] == opndreg) { + // two operands reading from same register in same bank, can be merged into a single read + skip = 1; + break; + } + } + if (!skip) { + g_reg_bank_access[bank].tot++; + g_reg_bank_access[bank].rd++; + g_reg_bank_access[bank].rd_regs[j] = opndreg; + } + } + } + + unsigned max_access=0; + inst_t* conflict_inst = &shader->pipeline_reg[first_valid_thread][IF_ID]; + for(unsigned r = 0; r < gpgpu_num_reg_banks; r++ ) { + if( g_reg_bank_access[r].tot > max_access ) + max_access = g_reg_bank_access[r].tot; + g_reg_bank_access[r] = empty_reg_bank_access; + } + if( max_access >= 1 ) { + assert( max_access <= MAX_REG_OPERANDS ); + conflict_inst->reg_bank_access_pending = max_access - 1; + if( max_access > 1 ) { + conflict_inst->reg_bank_conflict_stall_checked = 1; + return; // stall pipeline + } + } + shader->pipeline_reg[first_valid_thread][IF_ID].reg_bank_conflict_stall_checked = 1; + } + + // execute the instruction functionally + for (i=0; i<pipe_simd_width;i++) { + if (shader->pipeline_reg[IF_ID][i].hw_thread_id == -1 ) + continue; /* bubble */ + /* get the next instruction to execute from fetch stage */ + tid = shader->pipeline_reg[IF_ID][i].hw_thread_id; + if ( gpgpu_cuda_sim ) { + int arch_reg[MAX_REG_OPERANDS]; + ptx_decode_inst( shader->thread[tid].ptx_thd_info, (unsigned*)&op, &i1, &i2, &i3, &i4, &o1, &o2, &o3, &o4, &vectorin, &vectorout, arch_reg ); + ptx_exec_inst( shader->thread[tid].ptx_thd_info, &addr, &space, &data_size, &callback, shader->pipeline_reg[IF_ID][i].warp_active_mask ); + shader->pipeline_reg[IF_ID][i].callback = callback; + shader->pipeline_reg[IF_ID][i].space = space; + if (is_local(space) && (is_load(op) || is_store(op))) { + addr = translate_local_memaddr(addr, shader, tid); + } + shader->pipeline_reg[IF_ID][i].is_vectorin = vectorin; + shader->pipeline_reg[IF_ID][i].is_vectorout = vectorout; + shader->pipeline_reg[IF_ID][i].data_size = data_size; + warp_current_pc = shader->pipeline_reg[IF_ID][i].pc; + memcpy( shader->pipeline_reg[IF_ID][i].arch_reg, arch_reg, sizeof(arch_reg) ); + regs_regs_PC = ptx_thread_get_next_pc( shader->thread[tid].ptx_thd_info ); + } + + shader->pipeline_reg[IF_ID][i].memreqaddr = addr; + if ( op == LOAD_OP ) { + shader->pipeline_reg[IF_ID][i].inst_type = LOAD_OP; + } else if ( op == STORE_OP ) { + shader->pipeline_reg[IF_ID][i].inst_type = STORE_OP; + } + + if ( gpgpu_cuda_sim && ptx_thread_at_barrier( shader->thread[tid].ptx_thd_info ) ) { + if (shader->model == DWF) { + shader->thread[tid].m_waiting_at_barrier=1; + shader->thread[tid].m_reached_barrier=1; // not reset at barrier release, but at the issue after that + shader->warp[wid_from_hw_tid(tid,warp_size)].n_waiting_at_barrier++; + shader->waiting_at_barrier++; + int cta_uid = ptx_thread_get_cta_uid( shader->thread[tid].ptx_thd_info ); + dwf_hit_barrier( shader->sid, cta_uid ); + + int release = ptx_thread_all_at_barrier( shader->thread[tid].ptx_thd_info ); //test if all threads arrived at the barrier + if ( release ) { //All threads arrived at barrier...releasing + int cta_uid = ptx_thread_get_cta_uid( shader->thread[tid].ptx_thd_info ); + for ( unsigned t=0; t < gpu_n_thread_per_shader; ++t ) { + if ( !ptx_thread_at_barrier( shader->thread[t].ptx_thd_info ) ) + continue; + int other_cta_uid = ptx_thread_get_cta_uid( shader->thread[t].ptx_thd_info ); + if ( other_cta_uid == cta_uid ) { //reseting @barrier tracking info + shader->warp[wid_from_hw_tid(t,warp_size)].n_waiting_at_barrier=0; + shader->thread[t].m_waiting_at_barrier=0; + ptx_thread_reset_barrier( shader->thread[t].ptx_thd_info ); + shader->waiting_at_barrier--; + } + } + if (shader->model == DWF) { + dwf_release_barrier( shader->sid, cta_uid ); + } + ptx_thread_release_barrier( shader->thread[tid].ptx_thd_info ); + } + } + } else { + assert( !shader->thread[tid].m_waiting_at_barrier ); + } + + // put the info into the shader instruction structure + // - useful in tracking instruction dependency (not needed for now) + shader->pipeline_reg[IF_ID][i].in[0] = i1; + shader->pipeline_reg[IF_ID][i].in[1] = i2; + shader->pipeline_reg[IF_ID][i].in[2] = i3; + shader->pipeline_reg[IF_ID][i].in[3] = i4; + shader->pipeline_reg[IF_ID][i].out[0] = o1; + shader->pipeline_reg[IF_ID][i].out[1] = o2; + shader->pipeline_reg[IF_ID][i].out[2] = o3; + shader->pipeline_reg[IF_ID][i].out[3] = o4; + + if ( op == STORE_OP ) { + is_write = TRUE; + } + + if ( op == BRANCH_OP ) { + int taken=0; + assert( gpgpu_cuda_sim ); + taken = ptx_branch_taken(shader->thread[tid].ptx_thd_info); + } + + // go to the next instruction + // - done implicitly in ptx_exec_inst() + + // branch divergence detection + if (warp_next_pc != regs_regs_PC) { + if (warp_next_pc == 0x600DBEEF) { + warp_next_pc = regs_regs_PC; + } else { + warp_diverging = 1; + } + } + + // direct the instruction to the appropriate next stage (config dependent) + int next_stage = (shader->using_rrstage)? ID_RR:ID_EX; + shader->pipeline_reg[next_stage][i] = shader->pipeline_reg[IF_ID][i]; + shader->pipeline_reg[next_stage][i].id_cycle = gpu_tot_sim_cycle + gpu_sim_cycle; + shader->pipeline_reg[IF_ID][i] = nop_inst; + } + + if( op == BARRIER_OP ) { + shader->set_at_barrier(cta_id,warp_id); + } + + if ( shader->model == NO_RECONVERGE && touched_priority ) { + update_max_branch_priority(shader,warp_tid,grid_num); + } + shader->n_diverge += warp_diverging; + if (warp_diverging == 1) { + assert(warp_current_pc != 0x600DBEEF); // guard against empty warp causing warp divergence + ptx_file_line_stats_add_warp_divergence(warp_current_pc, 1); + } +} + +unsigned int n_regconflict_stall = 0; + + +int regfile_hash(signed thread_number, unsigned simd_size, unsigned n_banks) { + if (gpgpu_thread_swizzling) { + signed warp_ID = thread_number / simd_size; + return((thread_number + warp_ID) % n_banks); + } else { + return(thread_number % n_banks); + } +} + +int gpgpu_n_reg_banks = 8; +void shader_preexecute( shader_core_ctx_t *shader, + unsigned int shader_number ) { + int i; + static int *thread_warp = NULL; + int n_access_per_cycle = pipe_simd_width / gpgpu_n_reg_banks; + + if (!thread_warp) { + thread_warp = (int*) malloc(sizeof(int) * pipe_simd_width); + } + + for (i=0; i<pipe_simd_width; i++) { + if (shader->pipeline_reg[RR_EX][i].hw_thread_id != -1 ) { + //stalled, but can still service a register read + if (shader->RR_k) { + shader->RR_k--; + } + return; // stalled + } + } + + // if there is still register read to service, stall + if (shader->RR_k > 1) { + shader->RR_k--; + return; + } + + // if RR_k == 1, it was stalled previously and the register read is now done + if (!shader->RR_k && gpgpu_reg_bankconflict) { + int max_reg_bank_acc = 0; + for (i=0; i<pipe_simd_width; i++) { + thread_warp[i] = 0; + } + for (i=0; i<pipe_simd_width; i++) { + if (shader->pipeline_reg[ID_RR][i].hw_thread_id != -1 ) + thread_warp[regfile_hash(shader->pipeline_reg[ID_RR][i].hw_thread_id, + warp_size, gpgpu_n_reg_banks)]++; + } + for (i=0; i<pipe_simd_width; i++) { + if (thread_warp[i] > max_reg_bank_acc ) { + max_reg_bank_acc = thread_warp[i]; + } + } + // calculate the number of cycles needed for each register bank to fulfill all accesses + shader->RR_k = (max_reg_bank_acc / n_access_per_cycle) + ((max_reg_bank_acc % n_access_per_cycle)? 1 : 0); + } + + // if there are more than one access cycle needed at a bank, stall + if (shader->RR_k > 1) { + n_regconflict_stall++; + shader->RR_k--; + return; + } + + check_stage_pcs(shader,ID_RR); + + shader->RR_k = 0; //setting RR_k to 0 to indicate RF conflict check next cycle + for (i=0; i<pipe_simd_width;i++) { + if (shader->pipeline_reg[ID_RR][i].hw_thread_id == -1 ) + continue; //bubble + shader->pipeline_reg[ID_EX][i] = shader->pipeline_reg[ID_RR][i]; + shader->pipeline_reg[ID_RR][i] = nop_inst; + } + +} + + +void shader_execute( shader_core_ctx_t *shader, + unsigned int shader_number ) { + + int i; + + for (i=0; i<pipe_simd_width; i++) { + if (gpgpu_pre_mem_stages) { + if (shader->pre_mem_pipeline[0][i].hw_thread_id != -1 ) { + //printf("stalled in shader_execute\n"); + return; // stalled + } + } else { + if (shader->pipeline_reg[EX_MM][i].hw_thread_id != -1 ) + return; // stalled + } + } + + check_stage_pcs(shader,ID_EX); + + for (i=0; i<pipe_simd_width; i++) { + if (shader->pipeline_reg[ID_EX][i].hw_thread_id == -1 ) + continue; // bubble + if (gpgpu_pre_mem_stages) { + shader->pre_mem_pipeline[0][i] = shader->pipeline_reg[ID_EX][i]; + shader->pre_mem_pipeline[0][i].ex_cycle = gpu_tot_sim_cycle + gpu_sim_cycle; + } else { + shader->pipeline_reg[EX_MM][i] = shader->pipeline_reg[ID_EX][i]; + shader->pipeline_reg[EX_MM][i].ex_cycle = gpu_tot_sim_cycle + gpu_sim_cycle; + } + shader->pipeline_reg[ID_EX][i] = nop_inst; + } + + if (!gpgpu_pre_mem_stages) { + // inform memory stage that a new instruction has arrived + shader->shader_memory_new_instruction_processed = 0; + } +} + +void shader_pre_memory( shader_core_ctx_t *shader, + unsigned int shader_number ) { + int i,j; + + + for (j = gpgpu_pre_mem_stages; j > 0; j--) { + for (i=0; i<pipe_simd_width; i++) { + if (shader->pre_mem_pipeline[j][i].hw_thread_id != -1 ) { + return; + } + } + check_pm_stage_pcs(shader,j-1); + for (i=0; i<pipe_simd_width; i++) { + shader->pre_mem_pipeline[j][i] = shader->pre_mem_pipeline[j - 1][i]; + shader->pre_mem_pipeline[j - 1][i] = nop_inst; + } + } + check_pm_stage_pcs(shader,gpgpu_pre_mem_stages); + for (i=0;i<pipe_simd_width ;i++ ) + shader->pipeline_reg[EX_MM][i] = shader->pre_mem_pipeline[gpgpu_pre_mem_stages][i]; + + // inform memory stage that a new instruction has arrived + shader->shader_memory_new_instruction_processed = 0; + + if (gpgpu_pre_mem_stages) { + for (i=0; i<pipe_simd_width; i++) + shader->pre_mem_pipeline[0][i] = nop_inst; + } +} + +int gpgpu_coalesce_arch = 13; + +enum memory_path { + NO_MEM_PATH = 0, + SHARED_MEM_PATH, + GLOBAL_MEM_PATH, + TEXTURE_MEM_PATH, + CONSTANT_MEM_PATH, + NUM_MEM_PATHS //not a mem path +}; + +static unsigned next_access_uid = 0; + +class mem_access_t{ +public: + mem_access_t(): uid(next_access_uid++),addr(0),req_size(0),order(0),_quarter_count_all(0),warp_indices(),space(0),path(NO_MEM_PATH),isatomic(false),cache_hit(false),cache_checked(false),recheck_cache(false),iswrite(false),need_wb(false),wb_addr(0),reserved_mshr(NULL){}; + bool operator<(const mem_access_t &other) const {return (order > other.order);}//this is reverse + unsigned uid; + address_type addr; //address of the segment to load. + unsigned req_size; //bytes + unsigned order; // order of accesses, based on banks. + union{ + unsigned _quarter_count_all; + char quarter_count[4]; //access counts to each quarter of segment, for compaction; + }; + std::vector<unsigned> warp_indices; //warp indicies for this request. + unsigned space; + memory_path path; + bool isatomic; + bool cache_hit; + bool cache_checked; + bool recheck_cache; + bool iswrite; + bool need_wb; + address_type wb_addr; //address to wb too if necessary. + mshr_entry_t* reserved_mshr; +}; + +mshr_entry_t* mshr_shader_unit::add_mshr(mem_access_t &access, inst_t* warp) +{ + static unsigned next_request_uid = 1; + mshr_entry_t* mshr = alloc_free_mshr(is_tex(access.space)); + //note no constructor was called, all entries must be reinitialized! + mshr->request_uid = next_request_uid++; + mshr->status = INITIALIZED; + mshr->addr = access.addr; + mshr->mf = NULL; + mshr->merged_on_other_reqest = false; + mshr->merged_requests =NULL; + mshr->iswrite = access.iswrite; + assert(access.warp_indices.size()); //code assumes at least one instruction attached to mshr. + for (unsigned i = 0; i < access.warp_indices.size(); i++) { + mshr->insts.push_back(warp[access.warp_indices[i]]); + } + mshr->islocal = is_local(access.space); + mshr->isconst = is_const(access.space); + mshr->istexture = is_tex(access.space); + if (gpgpu_interwarp_mshr_merge) { + mshr_entry_t* mergehit = m_mshr_lookup.shader_get_mergeable_mshr(mshr); + if (mergehit) { + //merge this request; + mergehit->merged_requests = mshr; + mshr->merged_on_other_reqest = true; + if (mergehit->fetched()) mshr_return_from_mem(mshr); + } + m_mshr_lookup.mshr_fast_lookup_insert(mshr); + } + return mshr; +} + + +inline address_type line_size_based_tag_func(address_type address, unsigned line_size) +{ + return ((address) & (~((address_type)line_size - 1))); +} + +inline address_type null_tag_func(address_type address, unsigned line_size){ + return address; //no modification: each address is its own tag. Equivalent to line_size_based_tag_func(address,1), but line_size ignored. +} + +// only 1 bank +inline int null_bank_func(address_type add, unsigned line_size) +{ + return 1; +} + +inline int shmem_bank_func(address_type add, unsigned line_size) +{ + return shmem_bank(add); +} + +inline int dcache_bank_func(address_type add, unsigned line_size) +{ + if (gpgpu_no_dl1) return 1; //no banks + else return (add / line_size) & (gpgpu_n_cache_bank - 1); +} + +#include <bitset> +void check_accessq( shader_core_ctx_t *shader, std::vector<mem_access_t> &accessq ){ + std::bitset<32> check = 0; + for (unsigned i = 0; i < accessq.size(); i++) { + if (shader) { + std::cout << shader->sid << ":" << i << " space " << accessq[i].space << " " << gpu_sim_cycle << std::endl; + assert(accessq[i].space == (unsigned) shader->pipeline_reg[EX_MM][accessq[i].warp_indices[0]].space); + } + for (unsigned j = 0; j < accessq[i].warp_indices.size(); j++) { + if (check[accessq[i].warp_indices[j]]) { + std::cout << "OOOPS" << std::endl; //good line for breakpoint + }else{check[accessq[i].warp_indices[j]] = true;} + } + } +} + +// This speciallized function calculates the list of independant memory accesses, sorted by access order +// Acesses to same tag line are coalesced. +// will neither coalesce nor overlap bank accesses accross warp parts. +template < int (*bank_func)(address_type add, unsigned line_size), address_type (*tag_func)(address_type add, unsigned line_size) > +inline void get_memory_access_list(inst_t* insns, unsigned char* paths, memory_path path, unsigned warp_parts, unsigned line_size, bool limit_broadcast,std::vector<mem_access_t> &accessq) +{ + // calculates the memory accesses for a generic cache with banks and tags. + // can be used for coalesescing + + //tracks bank accesses for sorting into generations; + static std::map<unsigned,unsigned> bank_accs; + bank_accs.clear(); + //keep track of broadcasts with unique orders if limit_broadcast + //the normally calculated orders will never be greater than pipe_simd_width; + unsigned broadcast_order = pipe_simd_width; + + unsigned qbegin = accessq.size(); + unsigned qpartbegin = qbegin; + unsigned mem_pipe_size = pipe_simd_width / warp_parts; + for (unsigned part = 0; part < (unsigned)pipe_simd_width; part += mem_pipe_size) { + for (unsigned i = part; i < part + mem_pipe_size; i++) { + if (paths[i] != path) continue; //skip instructions from other memory paths + address_type segment = (*tag_func)(insns[i].memreqaddr, line_size); + unsigned quarter=0; + if ( line_size>=4 ) { + quarter = (insns[i].memreqaddr / (line_size/4)) & 3; + } + //check if we are already loading this segment. + bool isatomic = (insns[i].callback.function != NULL); + unsigned match = 0; + if (not isatomic) { //atomics must have own request + for (unsigned j = qpartbegin; j < accessq.size(); j++) { + if (segment == accessq[j].addr and not accessq[j].isatomic) { + //match + accessq[j].quarter_count[quarter]++; + accessq[j].warp_indices.push_back(i); + if (limit_broadcast) accessq[j].order = ++broadcast_order; //do proadcast in its own cycle. + match = 1; + break; + } + } + } + if (!match) { + //needs its own request + accessq.push_back(mem_access_t()); + accessq.back().addr = segment; + accessq.back().space = insns[i].space; + accessq.back().path = path; + accessq.back().isatomic = isatomic; + accessq.back().iswrite = is_store(insns[i].op); + accessq.back().req_size = line_size; + accessq.back().quarter_count[quarter]++; + accessq.back().warp_indices.push_back(i); + + //Determine Bank Conflicts. + unsigned bank = (*bank_func)(insns[i].memreqaddr, line_size); + //ensure no concurrent bank access accross warp parts. + // ie. order will be less than part for all previous loads in previous parts, so: + if (bank_accs[bank] < part) bank_accs[bank]=part; + accessq.back().order = bank_accs[bank]; + bank_accs[bank]++; + } + } + qpartbegin = accessq.size(); //don't coalesce accross warp parts + } + //sort requests into order accorting to order (orders will not necessarily be consequtive if multiple parts) + std::stable_sort(accessq.begin()+qbegin,accessq.end()); //this is a reverse sort, least order last, but doesn't really matter where consumed. +} + + +void shader_memory_shared_process_inst(shader_core_ctx_t * shader, unsigned char* paths, std::vector<mem_access_t> &accessq) +{ + get_memory_access_list<&shmem_bank_func, &null_tag_func>(shader->pipeline_reg[EX_MM], paths, SHARED_MEM_PATH, + gpgpu_shmem_pipe_speedup, + 1, //shared memory doesn't care about line_size, needs to be at least 1; + true, //limit broadcasts to single cycle. + accessq); + //thats it :) +} + +void shader_memory_const_process_inst(shader_core_ctx_t * shader, unsigned char* paths, std::vector<mem_access_t> &accessq) +{ + unsigned qbegin = accessq.size(); + get_memory_access_list<&null_bank_func, &line_size_based_tag_func>(shader->pipeline_reg[EX_MM], paths, CONSTANT_MEM_PATH, + 1, //warp parts + shader->L1constcache->line_sz, + false, //no broadcast limit. + accessq); + //do cache checks here for each request, could be done later for more accurate timing of cache accesses, but probably uneccesary; + for (unsigned i = qbegin; i < accessq.size(); i++) { + if (is_param(accessq[i].space)) { + accessq[i].cache_hit = true; + } else { + cache_request_status status = shd_cache_access_wb(shader->L1constcache, + accessq[i].addr, + WORD_SIZE, //this field is ingored. + 0, //should always be a read + shader->gpu_cycle, + NULL/*should never writeback*/); + accessq[i].cache_hit = (status == HIT); + if (gpgpu_perfect_mem) accessq[i].cache_hit = true; + if (accessq[i].cache_hit) L1_const_miss++; + } + accessq[i].cache_checked = true; + } +} + +void shader_memory_texture_process_inst(shader_core_ctx_t * shader, unsigned char* paths, std::vector<mem_access_t> &accessq) +{ + unsigned qbegin = accessq.size(); + get_memory_access_list<&null_bank_func, &line_size_based_tag_func>(shader->pipeline_reg[EX_MM], paths, TEXTURE_MEM_PATH, + 1, //warp parts + shader->L1texcache->line_sz, + false, //no broadcast limit. + accessq); + //do cache checks here for each request, could be done later for more accurate timing of cache accesses, but probably uneccesary; + for (unsigned i = qbegin; i < accessq.size(); i++) { + cache_request_status status = shd_cache_access_wb(shader->L1texcache, + accessq[i].addr, + WORD_SIZE, //this field is ignored. + 0, //should always be a read + shader->gpu_cycle, + NULL /*should never writeback*/); + accessq[i].cache_hit = (status == HIT); + if (gpgpu_perfect_mem) accessq[i].cache_hit = true; + if (accessq[i].cache_hit) L1_texture_miss++; + accessq[i].cache_checked = true; + } +} + +void shader_memory_global_process_inst(shader_core_ctx_t * shader, unsigned char* paths, std::vector<mem_access_t> &accessq) +{ + unsigned qbegin = accessq.size(); + unsigned warp_parts = 1; + unsigned line_size = shader->L1cache->line_sz; + if (gpgpu_coalesce_arch == 13) { + warp_parts = 2; + if(gpgpu_no_dl1) { + int valindex = -1; + for (int i = 0; i < pipe_simd_width; i++) { + if (paths[i] == GLOBAL_MEM_PATH) { + valindex = i; + break; + } + } + assert(valindex != -1); + // line size is dependant on instruction; + //assume first valid thread instruction is the same as the rest. + switch (shader->pipeline_reg[EX_MM][valindex].data_size) { + case 1: + line_size = 32; + break; + case 2: + line_size = 64; + break; + case 4: + case 8: + line_size = 128; + break; + default: + assert(0); + } + } + } + get_memory_access_list<&dcache_bank_func, &line_size_based_tag_func>(shader->pipeline_reg[EX_MM], paths, GLOBAL_MEM_PATH, + warp_parts, //warp parts + line_size, + false, //no broadcast limit. + accessq); + + for (unsigned i = qbegin; i < accessq.size(); i++) { + if (gpgpu_coalesce_arch == 13 and gpgpu_no_dl1) { + //if there is no l1 cache it makes sense to do coalescing here. + //reduce memory request sizes. + char* quarter_counts = accessq[i].quarter_count; + bool low = quarter_counts[0] or quarter_counts[1]; + bool high = quarter_counts[2] or quarter_counts[3]; + if (accessq[i].req_size == 128) { + if (low xor high) { //can reduce size + accessq[i].req_size = 64; + if (high) accessq[i].addr += 64; + low = quarter_counts[0] or quarter_counts[2]; //set low and high for next pass + high = quarter_counts[1] or quarter_counts[3]; + } + } + if (accessq[i].req_size == 64) { + if (low xor high) { //can reduce size + accessq[i].req_size = 32; + if (high) accessq[i].addr += 32; + } + } + } + } +} + + + +mem_stage_stall_type send_mem_request(shader_core_ctx_t *shader, mem_access_t &access){ + inst_t* warp = shader->pipeline_reg[EX_MM]; + inst_t* req_head = warp + access.warp_indices[0]; + + if (access.need_wb) { + //fill out and send a writeback + unsigned req_size = shader->L1cache->line_sz + WRITE_PACKET_SIZE; + if (!(shader->fq_has_buffer(access.wb_addr, req_size, true, shader->sid))) { + gpu_stall_sh2icnt++; + return WB_ICNT_RC_FAIL; + } + + shader->fq_push( access.wb_addr, + req_size, + true, NO_PARTIAL_WRITE, shader->sid, -1, NULL, + 0, + is_local(access.space)?LOCAL_ACC_W:GLOBAL_ACC_W, //space of cache line is same as new request + -1); + L1_writeback++; + access.need_wb = false; + } + + bool requires_mshr = (shader->model != MIMD) and (not access.iswrite); + + //this decoding here might belong elsewhere + unsigned code; + mem_access_type access_type; + switch(access.space) { + case CONST_DIRECTIVE: + case PARAM_DIRECTIVE: + code = CONSTC; + access_type = CONST_ACC_R; + break; + case TEX_DIRECTIVE: + code = TEXTC; + access_type = TEXTURE_ACC_R; + break; + case GLOBAL_DIRECTIVE: + code = DCACHE; + access_type = (access.iswrite)? GLOBAL_ACC_W: GLOBAL_ACC_R; + break; + case LOCAL_DIRECTIVE: + code = DCACHE; + access_type = (access.iswrite)? LOCAL_ACC_W: LOCAL_ACC_R; + break; + default: + assert(0); // NOT A MEM SPACE; + break; + } + + //reserve mshr + if (requires_mshr and not access.reserved_mshr) { + + // can allocate mshr? + if (not shader->mshr_unit->has_mshr(1)) { + //no mshr available; + return MSHR_RC_FAIL; + } + + access.reserved_mshr = shader->mshr_unit->add_mshr(access, warp); + access.recheck_cache = false; //we have an mshr now, so have checked cache in same cycle as checking mshrs, so have merged if necessary. + } + + //require inct if access is this far without reserved mshr, or has and mshr but not merged with another request + bool requires_icnt = (not access.reserved_mshr) or (not access.reserved_mshr->merged_on_other_reqest); + + if (requires_icnt) { + + //calculate request size for icnt check (and later send); + unsigned request_size = access.req_size; + if (access.iswrite) { + if (requires_mshr) { + //needs information for a load back into cache. + request_size += READ_PACKET_SIZE + WRITE_MASK_SIZE; + } else { + //plain write + request_size += WRITE_PACKET_SIZE + WRITE_MASK_SIZE; + } + } + + + // can allocate icnt? + //unsigned char fq_has_buffer(unsigned long long int addr, int bsize, bool write, int sid); + if (!(shader->fq_has_buffer(access.addr, request_size, access.iswrite, shader->sid))) { + gpu_stall_sh2icnt++; + //std::cout<< "failed to push " << request_size << " bytes" << std::endl; + return ICNT_RC_FAIL; + } + + //send over interconnect + + unsigned cache_hits_waiting = 0; //fixme do we really want to be passing this in? + + partial_write_mask_t write_mask = NO_PARTIAL_WRITE; + if (access.iswrite) { + for (unsigned i=0;i < access.warp_indices.size();i++) { + unsigned w = access.warp_indices[i]; + int data_offset = warp[w].memreqaddr & ((unsigned long long int)access.req_size - 1); + for (unsigned b = data_offset; b < data_offset + warp[w].data_size; b++) write_mask.set(b); + } + if (write_mask.count() != access.req_size) { + gpgpu_n_partial_writes++; + } + } + + //typedef unsigned char (*fq_push_t)(unsigned long long int addr, int bsize, unsigned char readwrite, + // unsigned long long int partial_write_mask, + // int sid, int wid, mshr_entry* mshr, int cache_hits_waiting, + // enum mem_access_type mem_acc, address_type pc); + shader->fq_push( access.addr, + request_size, + access.iswrite, write_mask, shader->sid, req_head->hw_thread_id/warp_size, access.reserved_mshr, + cache_hits_waiting, access_type, req_head->pc); + + } + + + //book keeping for mshr since this request is done (sent/accounted for) at this point; + if (requires_mshr) { + + for (unsigned i = 0; i < access.warp_indices.size(); i++) { + unsigned o = access.warp_indices[i]; + shader->pending_mem_access++; + inflight_memory_insn_add(shader, &warp[o]); + +#if 0 //old stats + if (i > 0) { //maintain old stats (yes/no?) + shader->thread[warp[o].hw_thread_id].n_l1_mrghit_ac++; + shd_cache_mergehit(shader->L1texcache, warp[o].memreqaddr); //fixme; + } +#endif + } + + if (not access.iswrite) { + // set the pipeline instructions in this request to noops, they all wait for memory; + for (unsigned i = 0; i < access.warp_indices.size(); i++) { + unsigned o = access.warp_indices[i]; + shader->pipeline_reg[EX_MM][o] = nop_inst; + + } + } + } + + return NO_RC_FAIL; +} + + +bool shader_memory_shared_cycle( shader_core_ctx_t *shader, std::vector<mem_access_t> &accessq, + mem_stage_stall_type &rc_fail, mem_stage_access_type &fail_type){ + //consume port number orders from the top of the queue; + for (unsigned i = 0; i < (unsigned) gpgpu_shmem_port_per_bank; i++) { + if (accessq.empty()) break; + unsigned current_order = accessq.back().order; + //consume all requests of the same order (concurrent bank requests) + while ((not accessq.empty()) and accessq.back().order == current_order) accessq.pop_back(); + } + if (not accessq.empty()) { + rc_fail = BK_CONF; + fail_type = S_MEM; + gpgpu_n_shmem_bkconflict++; + } + return accessq.empty(); //done if empty. +} + +//generic memory access queue processing, accessq must be sorted by order +//--that is, requests of similar order are expected to be contiguous in the queueu. +//if you want to use this for shared memory, make sure they are marked as cashe hits (not default) +// cycle_exec may be called multiple times if memory fails. typically used for cache checks +template < mem_stage_stall_type (*cycle_exec)(shader_core_ctx_t*, mem_access_t&) > +inline mem_stage_stall_type shader_memory_generic_process_queue( shader_core_ctx_t *shader, + unsigned ports_per_bank, unsigned memory_send_max, + std::vector<mem_access_t> &accessq ){ + mem_stage_stall_type rc_fail = NO_RC_FAIL; + // number of requests to sent to memory this cycle + unsigned mem_req_count = 0; + //consume port number orders from the top of the queue; + for (unsigned i = 0; i < ports_per_bank; i++) { + if (accessq.empty()) break; + unsigned current_order = accessq.back().order; + //consume all requests of the same order (concurrent bank requests) + //stop when things that go to memory exceed a per cycle limit. + while ((not accessq.empty()) and accessq.back().order == current_order and rc_fail == NO_RC_FAIL) { + rc_fail = (*cycle_exec)(shader, accessq.back()); + if (rc_fail != NO_RC_FAIL) break; //can't complete this request this cycle. + if (not accessq.back().cache_hit){ + if (mem_req_count < memory_send_max) { + mem_req_count++; + rc_fail = send_mem_request(shader, accessq.back()); //try to get mshr, icnt, send; + } + else { + rc_fail = COAL_STALL; //not really a coal stall, its a too many memory request stall; + } + if (rc_fail != NO_RC_FAIL) break; //can't complete this request this cycle. + } + accessq.pop_back(); + } + } + if (not accessq.empty() and rc_fail == NO_RC_FAIL) { + //no resource failed so must be a bank comflict. + rc_fail = BK_CONF; + } + return rc_fail; +} + +mem_stage_stall_type ccache_check(shader_core_ctx_t *shader, mem_access_t& access){ /*done in process queue*/ return NO_RC_FAIL;} + +bool shader_memory_constant_cycle( shader_core_ctx_t *shader, std::vector<mem_access_t> &accessq, + mem_stage_stall_type &rc_fail, mem_stage_access_type &fail_type){ + + mem_stage_stall_type fail = shader_memory_generic_process_queue<ccache_check>( shader, gpgpu_const_port_per_bank, + 1, //memory send max per cycle + accessq ); + if (fail != NO_RC_FAIL){ + rc_fail = fail; //keep other fails if this didn't fail. + fail_type = C_MEM; + if (rc_fail == BK_CONF or rc_fail == COAL_STALL) { + gpgpu_n_cmem_portconflict++; //coal stalls aren't really a bank conflict, but this maintains previous behavior. + } + } + return accessq.empty(); //done if empty. +} + +mem_stage_stall_type tcache_check(shader_core_ctx_t *shader, mem_access_t& access){ /*done in process queue*/ return NO_RC_FAIL;} + +bool shader_memory_texture_cycle( shader_core_ctx_t *shader, std::vector<mem_access_t> &accessq, + mem_stage_stall_type &rc_fail, mem_stage_access_type &fail_type){ + + mem_stage_stall_type fail = shader_memory_generic_process_queue<tcache_check>(shader, 1, //how is tex memory banked? + 1, //memory send max per cycle + accessq ); + if (fail != NO_RC_FAIL){ + rc_fail = fail; //keep other fails if this didn't fail. + fail_type = T_MEM; + } + return accessq.empty(); //done if empty. +} + + +mem_stage_stall_type dcache_check(shader_core_ctx_t *shader, mem_access_t& access){ + if (access.cache_checked and not access.recheck_cache) return NO_RC_FAIL; + if (!gpgpu_no_dl1 && !gpgpu_perfect_mem) { + //check cache + cache_request_status status = shd_cache_access_wb(shader->L1cache, + access.addr, + WORD_SIZE, //this field is ignored. + access.iswrite, + shader->gpu_cycle, + &access.wb_addr); + if (status == RESERVATION_FAIL) { + access.cache_checked = false; + return WB_CACHE_RSRV_FAIL; + } + access.cache_hit = (status == HIT); //if HIT_W_WT then still send to memory so "MISS" + if (status == MISS_W_WB) access.need_wb = true; + if (status == WB_HIT_ON_MISS and access.iswrite) + { + //write has hit a reserved cache line + //it has writen its data into the cache line, so no need to go to memory + access.cache_hit = true; + L1_write_hit_on_miss++; + // here we would search the MSHRs for the originating read, + // and mask off the writen bytes, so they are not overwritten in the cache when it comes back + // --- don't actually do this since we are pretending. + // MSHR will still forward the unmasked value to its dependant reads. + // if doing stall on use, must stall this thread after this write (otherwise, inproper values may be forwarded to future reads). + } + if (status == WB_HIT_ON_MISS and not access.iswrite) { + //read has hit on a reserved cache line, + //we need to make sure cache check happens on same cycle as a mshr merge happens, otherwise we might miss it coming back + access.recheck_cache = true; + } + access.cache_checked = true; + } else { + access.cache_hit = false; + } + + if (gpgpu_perfect_mem) access.cache_hit = true; + + //atomics always go to memory + if (access.isatomic) { + if (!gpgpu_perfect_mem) { + access.cache_hit = false; + } else { + //unless perfect mem, in which case, the callback can only be done here + dram_callback_t &atom_exec = shader->pipeline_reg[EX_MM][access.warp_indices[0]].callback; + atom_exec.function(atom_exec.instruction, atom_exec.thread); + } + } + + if (!access.cache_hit) { + if (access.iswrite) L1_write_miss++; + else L1_read_miss++; + } + return NO_RC_FAIL; +} + +bool shader_memory_global_cycle( shader_core_ctx_t *shader, std::vector<mem_access_t> &accessq, + mem_stage_stall_type &rc_fail, mem_stage_access_type &fail_type){ + mem_stage_stall_type fail = shader_memory_generic_process_queue<&dcache_check>(shader, gpgpu_cache_port_per_bank, + 1, //memory send max per cycle + accessq ); + if (fail != NO_RC_FAIL) { + rc_fail = fail; //keep other fails if this didn't fail. + //need to determine load/store, local/global: + bool iswrite = accessq.back().iswrite; + if (is_local(accessq.back().space)) { + fail_type = (iswrite)?L_MEM_ST:L_MEM_LD; + } else { + fail_type = (iswrite)?G_MEM_ST:G_MEM_LD; + } + + if (rc_fail == BK_CONF or rc_fail == COAL_STALL) { + gpgpu_n_cache_bkconflict++; + } + } + return accessq.empty(); //done if empty. +} + +inline void mem_instruction_stats(inst_t* warp){ + //there must be a better way to count these + for (unsigned i=0; i< (unsigned) pipe_simd_width; i++) { + if (warp[i].hw_thread_id == -1) continue; //bubble + //this breaks some encapsulation: the is_[space] functions, if you change those, change this. + bool store = is_store(warp[i].op); + switch (warp[i].space) { + case SHARED_DIRECTIVE: + gpgpu_n_shmem_insn++; + break; + case CONST_DIRECTIVE: + gpgpu_n_const_insn++; + break; + case PARAM_DIRECTIVE: + gpgpu_n_param_insn++; + break; + case TEX_DIRECTIVE: + gpgpu_n_tex_insn++; + break; + case GLOBAL_DIRECTIVE: + case LOCAL_DIRECTIVE: + if (store){ + gpgpu_n_store_insn++; + } else { + gpgpu_n_load_insn++; + } + break; + default: + //assert(0); //unknown mem space. + break; //not a mem instruction + } + } +} + +struct shader_queues_t{ + std::vector<mem_access_t> shared; + std::vector<mem_access_t> constant; + std::vector<mem_access_t> texture; + std::vector<mem_access_t> global; +}; + +void shader_memory_queue(shader_core_ctx_t *shader, shader_queues_t *accessqs) +{ + //classify memory according to type; + static unsigned char *path = NULL; + if (!path) path = (unsigned char*)malloc(pipe_simd_width * sizeof(unsigned char)); + memset(path, 0, pipe_simd_width * sizeof(unsigned char)); + //static std::vector<char> path; + //path.clear(); path.resize(p, NO_MEM_PATH); + + static unsigned type_counts[NUM_MEM_PATHS]; + memset(type_counts, 0, sizeof(type_counts)); + //static std::vector<unsigned> type_counts; + //type_counts.clear(); type_counts.resize(NUM_MEM_PATHS); + + for (unsigned i=0; i< (unsigned) pipe_simd_width; i++) { + if (shader->pipeline_reg[EX_MM][i].hw_thread_id == -1) continue; //bubble + //this breaks some encapsulation: the is_[space] functions; if you change those, change this. + switch (shader->pipeline_reg[EX_MM][i].space) { + case SHARED_DIRECTIVE: + path[i] = SHARED_MEM_PATH; + type_counts[SHARED_MEM_PATH]++; + break; + case CONST_DIRECTIVE: + case PARAM_DIRECTIVE: + path[i] = CONSTANT_MEM_PATH; + type_counts[CONSTANT_MEM_PATH]++; + break; + case TEX_DIRECTIVE: + path[i] = TEXTURE_MEM_PATH; + type_counts[TEXTURE_MEM_PATH]++; + break; + case GLOBAL_DIRECTIVE: + case LOCAL_DIRECTIVE: + path[i] = GLOBAL_MEM_PATH; + type_counts[GLOBAL_MEM_PATH]++; + break; + default: + //path[i] = NO_MEM_PATH; + break; //not a mem instruction + } + } + + //instruction counting: + mem_instruction_stats(shader->pipeline_reg[EX_MM]); + + + if (type_counts[SHARED_MEM_PATH]) shader_memory_shared_process_inst(shader, path, accessqs->shared); + if (type_counts[CONSTANT_MEM_PATH]) shader_memory_const_process_inst(shader, path, accessqs->constant); + if (type_counts[TEXTURE_MEM_PATH]) shader_memory_texture_process_inst(shader, path, accessqs->texture); + if (type_counts[GLOBAL_MEM_PATH]) shader_memory_global_process_inst(shader, path, accessqs->global); + +} + + +void shader_memory( shader_core_ctx_t *shader, unsigned int shader_number ) +{ + enum mem_stage_stall_type rc_fail = NO_RC_FAIL; // resource allocation + + //these should be local to the shader structure but can't because it is included in non c++ files. + //so provide static storage for it here + static std::vector<shader_queues_t> shader_memory_queues; + if (shader_memory_queues.size() == 0) { + shader_memory_queues.resize(gpu_n_shader); + for (unsigned i = 0; i < gpu_n_shader; i++) { + shader_memory_queues[i].shared.reserve(pipe_simd_width); + shader_memory_queues[i].constant.reserve(pipe_simd_width); + shader_memory_queues[i].texture.reserve(pipe_simd_width); + shader_memory_queues[i].global.reserve(pipe_simd_width); + } + } + shader_queues_t *accessqs = &(shader_memory_queues[shader->sid]); + + if (shader->shader_memory_new_instruction_processed == 0) { + shader->shader_memory_new_instruction_processed = 1; //only do this once per pipeline occupant + shader_memory_queue(shader, accessqs); + } + + bool done = true; + mem_stage_access_type type; + + done &= shader_memory_shared_cycle(shader, accessqs->shared, rc_fail, type); + done &= shader_memory_constant_cycle(shader, accessqs->constant, rc_fail, type); + done &= shader_memory_texture_cycle(shader, accessqs->texture, rc_fail, type); + done &= shader_memory_global_cycle(shader, accessqs->global, rc_fail, type); + + //wb stalled? + int wb_stalled = 0; // check if next stage is stalled + for (unsigned i=0; i< (unsigned) pipe_simd_width; i++) { + if (shader->pipeline_reg[MM_WB][i].hw_thread_id != -1 ) { + wb_stalled = 1; + break; + } + } + + if (!done) { + assert(rc_fail != NO_RC_FAIL); + //log stall types + gpu_stall_shd_mem++; + gpu_stall_shd_mem_breakdown[type][rc_fail]++; + } + + if (!done or wb_stalled) return; + + // this memory stage is done and not stalled by wb + // pipeline forward + + check_stage_pcs(shader,EX_MM); + // and pass instruction from EX_MM to MM_WB for cache hit + for (unsigned i=0; i< (unsigned) pipe_simd_width; i++) { + if (shader->pipeline_reg[EX_MM][i].hw_thread_id == -1 ) + continue; // bubble + shader->pipeline_reg[MM_WB][i] = shader->pipeline_reg[EX_MM][i]; + shader->pipeline_reg[MM_WB][i].mm_cycle = gpu_tot_sim_cycle + gpu_sim_cycle; + shader->pipeline_reg[EX_MM][i] = nop_inst; + } + + // reflect the change to EX|MM pipeline register to the pre_mem stage + if (gpgpu_pre_mem_stages) { + check_stage_pcs(shader,EX_MM); + for (unsigned i=0;i< (unsigned)pipe_simd_width ;i++ ) + shader->pre_mem_pipeline[gpgpu_pre_mem_stages][i] = shader->pipeline_reg[EX_MM][i]; + } +} + +int writeback_l1_miss =0 ; + + +void register_cta_thread_exit(shader_core_ctx_t *shader, int tid ) +{ + if (gpgpu_cuda_sim && gpgpu_spread_blocks_across_cores) { + unsigned padded_cta_size = ptx_sim_cta_size(); + if (padded_cta_size%warp_size) { + padded_cta_size = ((padded_cta_size/warp_size)+1)*(warp_size); + } + int cta_num = tid/padded_cta_size; + assert( shader->cta_status[cta_num] > 0 ); + shader->cta_status[cta_num]--; + if (!shader->cta_status[cta_num]) { + shader->n_active_cta--; + shader->deallocate_barrier(cta_num); + shader_CTA_count_unlog(shader->sid, 1); + printf("Shader %d finished CTA #%d (%lld,%lld)\n", shader->sid, cta_num, gpu_sim_cycle, gpu_tot_sim_cycle ); + } + } +} + +#if 0 +//this function is unecessary, cache is properly dirtied by fill with cache line function in gpu-sim.cc +void dirty_cache_lines(shader_core_ctx_t *shader, mshr_entry_t* mshr){ + shd_cache_line_t *hit_cacheline; + if (mshr->istexture) { + hit_cacheline = shd_cache_access(shader->L1texcache, + mshr->addr, WORD_SIZE, + mshr->iswrite, //should always be 0 + shader->gpu_cycle); + shd_cache_undo_stats( shader->L1texcache, !hit_cacheline ); + } else if (mshr->isconst) { + hit_cacheline = shd_cache_access(shader->L1constcache, + mshr->addr, WORD_SIZE, + mshr->iswrite, //should always be 0 + shader->gpu_cycle); + shd_cache_undo_stats( shader->L1constcache, !hit_cacheline ); + } else if (!gpgpu_no_dl1) { + hit_cacheline = shd_cache_access(shader->L1cache, + mshr->addr, WORD_SIZE, + mshr->iswrite, + shader->gpu_cycle); + shd_cache_undo_stats( shader->L1constcache, !hit_cacheline ); + if (!hit_cacheline) { + writeback_l1_miss++; + } + } +} +#endif + +typedef struct { + unsigned pc; + unsigned long latency; + void *ptx_thd_info; +} insn_latency_info; + +void obtain_insn_latency_info(insn_latency_info *latinfo, inst_t *insn) +{ + latinfo->pc = insn->pc; + latinfo->latency = gpu_tot_sim_cycle + gpu_sim_cycle - insn->ts_cycle; + latinfo->ptx_thd_info = insn->ptx_thd_info; +} + +unsigned gpu_n_max_mshr_writeback=1; +void shader_writeback( shader_core_ctx_t *shader, unsigned int shader_number, int grid_num ) +{ + static int *unlock_tid = NULL; + static int *freed_warp = NULL; + static int *mshr_tid = NULL; + static int *pl_tid = NULL; + static int *done_tid = NULL; + insn_latency_info *unlock_lat_info = NULL; + static insn_latency_info *mshr_lat_info = NULL; + static insn_latency_info *pl_lat_info = NULL; + + mshr_entry *mshr_head = NULL; + + int tid; + op_type op; + int o1, o2, o3, o4; + bool stalled_by_MSHR = false; + bool writeback_by_MSHR = false; + bool w2rf = false; + + if ( unlock_tid == NULL ) { + unlock_tid = (int*) malloc(sizeof(int)*pipe_simd_width); + mshr_tid = (int*) malloc(sizeof(int)*pipe_simd_width); + pl_tid = (int*) malloc(sizeof(int)*pipe_simd_width); + done_tid = (int*) malloc(sizeof(int)*pipe_simd_width); + freed_warp = (int *) malloc(sizeof(int)*pipe_simd_width); + mshr_lat_info = (insn_latency_info*) malloc(sizeof(insn_latency_info) * pipe_simd_width); + pl_lat_info = (insn_latency_info*) malloc(sizeof(insn_latency_info) * pipe_simd_width); + } + memset(unlock_tid, -1, sizeof(int)*pipe_simd_width); + memset(mshr_tid, -1, sizeof(int)*pipe_simd_width); + memset(pl_tid, -1, sizeof(int)*pipe_simd_width); + memset(done_tid, -1, sizeof(int)*pipe_simd_width); + memset(freed_warp, 0, sizeof(int)*pipe_simd_width); + unlock_lat_info = NULL; + + check_stage_pcs(shader,MM_WB); + + /* Generate Condition for instruction writeback to register file. + A load miss *instruction* does not reach writeback until the data is fetched */ + for (int i=0; i<pipe_simd_width; i++) { + tid = shader->pipeline_reg[MM_WB][i].hw_thread_id; + w2rf |= (tid >= 0); + pl_tid[i] = tid; + } + + //check mshrs for commit; + //in future do req bank checking here; + unsigned mshr_threads_unlocked = 0; + for (unsigned i = 0; i < gpu_n_max_mshr_writeback; i++) { + mshr_head = shader->mshr_unit->return_head(); + if (mshr_head) { + //bail if we can't unlock anymore threads, needs to be implemented better. + if (mshr_threads_unlocked + mshr_head->insts.size() > (unsigned) pipe_simd_width) break;//todo, do this right + assert(!gpgpu_strict_simd_wrbk);//implementation commented out below +/* + //for stalling in the middle of an mshr writeback due to mshr having threads from multiple warps, does this happen anymore? + static unsigned next_mshr_index = 0; + int mshr_warpid = -1; + bool mshr_not_blocked_by_samewarp = true; + //use below: for (j = next_mshr_index; ... +*/ + assert (mshr_head->insts.size()); + for (unsigned j = 0; j < mshr_head->insts.size(); j++) { + inst_t &insn = mshr_head->insts[j]; + time_vector_update(insn.uid,MR_WRITEBACK,gpu_sim_cycle+gpu_tot_sim_cycle,RD_REQ); + obtain_insn_latency_info(&mshr_lat_info[mshr_threads_unlocked], &(mshr_head->insts[j])); + inflight_memory_insn_sub(shader, &mshr_head->insts[j]); + assert (insn.hw_thread_id >= 0); + unlock_tid[mshr_threads_unlocked] = insn.hw_thread_id; + shader->pending_mem_access--; + // for ensuring that we don't unlock more than the code allows, needs to be fixed. + mshr_threads_unlocked++; +/* + next_mshr_index++; + if ((shader->model == POST_DOMINATOR || shader->model == NO_RECONVERGE) && gpgpu_strict_simd_wrbk) { + // restricting the threads from mshr to be in the same warp + if (mshr_warpid == -1) { + mshr_warpid = mshr_head->insts[j].hw_thread_id / warp_size; + } else if ((unsigned)mshr_warpid != (mshr_head->insts[j].hw_thread_id / warp_size)) { + warp_conflict_at_writeback++; + mshr_not_blocked_by_samewarp = false; + break; + } + } +*/ + } + //done with it since garanteed to wb +/* + if (mshr_not_blocked_by_samewarp) { +*/ + //this mshr is done this cycle, can pop it + shader->mshr_unit->pop_return_head(); +/* + //reset for next mshr + next_mshr_index = 0; + } +*/ + writeback_by_MSHR = true; + unlock_lat_info = mshr_lat_info; + if (w2rf) { + stalled_by_MSHR = true; + } + assert(mshr_threads_unlocked); + } + } + if (stalled_by_MSHR) { + gpu_stall_by_MSHRwb++; + } + + if (!writeback_by_MSHR) { //!writeback_by_MSHR + for (int i=0; i<pipe_simd_width; i++) { + op = shader->pipeline_reg[MM_WB][i].op; + tid = shader->pipeline_reg[MM_WB][i].hw_thread_id; + o1 = shader->pipeline_reg[MM_WB][i].out[0]; + o2 = shader->pipeline_reg[MM_WB][i].out[1]; + o3 = shader->pipeline_reg[MM_WB][i].out[2]; + o4 = shader->pipeline_reg[MM_WB][i].out[3]; + + unlock_tid[i] = pl_tid[i]; + obtain_insn_latency_info(&pl_lat_info[i], &shader->pipeline_reg[MM_WB][i]); + } + unlock_lat_info = pl_lat_info; + } + int thd_unlocked = 0; + for (int i=0; i<pipe_simd_width; i++) { + // NOTE: no need to check for next-stage stall at the last stage + if (unlock_tid[i] >= 0 ) { // not unlocking an invalid thread (ie. due to a bubble) + // thread completed if it is going to fetching beyond code boundry + if ( gpgpu_cuda_sim && ptx_thread_done(shader->thread[unlock_tid[i]].ptx_thd_info) ) { + + finished_trace += 1; + shader->not_completed -= 1; + gpu_completed_thread += 1; + + int warp_id = wid_from_hw_tid(unlock_tid[i],warp_size); + if (!(shader->warp[warp_id].n_completed < (unsigned)warp_size)) { + printf("shader[%d]->warp[%d].n_completed = %d; warp_size = %d\n", + shader->sid,warp_id, shader->warp[warp_id].n_completed, warp_size); + } + assert( shader->warp[warp_id].n_completed < (unsigned)warp_size ); + shader->warp[warp_id].n_completed++; + if ( shader->model == NO_RECONVERGE ) { + update_max_branch_priority(shader,warp_id,grid_num); + } + + if (gpgpu_no_divg_load) { + int amask = wpt_signal_complete(unlock_tid[i], shader); + freed_warp[i] = (amask != 0)? 1 : 0; + } else { + register_cta_thread_exit(shader, unlock_tid[i] ); + } + } else { //thread is not finished yet + // program is not finished yet, allow more fetch + if (gpgpu_no_divg_load) { + freed_warp[i] = wpt_signal_avail(unlock_tid[i], shader); + } else { + shader->thread[unlock_tid[i]].avail4fetch++; + assert(shader->thread[unlock_tid[i]].avail4fetch <= 1); + assert( shader->warp[wid_from_hw_tid(unlock_tid[i],warp_size)].n_avail4fetch < (unsigned)warp_size ); + shader->warp[wid_from_hw_tid(unlock_tid[i],warp_size)].n_avail4fetch++; + thd_unlocked = 1; + } + } + + // At any rate, a real instruction is committed + // - don't count cache miss + if ( shader->pipeline_reg[MM_WB][i].inst_type != NO_OP_FLAG ) { + gpu_sim_insn++; + if ( !is_const(shader->pipeline_reg[MM_WB][i].space) ) + gpu_sim_insn_no_ld_const++; + gpu_sim_insn_last_update = gpu_sim_cycle; + shader->num_sim_insn++; + shader->thread[unlock_tid[i]].n_insn++; + shader->thread[unlock_tid[i]].n_insn_ac++; + } + + if (enable_ptx_file_line_stats) { + unsigned pc = unlock_lat_info[i].pc; + unsigned long latency = unlock_lat_info[i].latency; + ptx_file_line_stats_add_latency(unlock_lat_info[i].ptx_thd_info, pc, latency); + } + } + } + if (shader->using_commit_queue && thd_unlocked) { + int *tid_unlocked = alloc_commit_warp(); + memcpy(tid_unlocked, unlock_tid, sizeof(int)*pipe_simd_width); //NOTE: this maybe warp_size + dq_push(shader->thd_commit_queue,(void*)tid_unlocked); + } + + + /* The pipeline can be stalled by MSHR */ + if (!stalled_by_MSHR) { + for (int i=0; i<pipe_simd_width; i++) { + shader->pipeline_reg[WB_RT][i] = shader->pipeline_reg[MM_WB][i]; + shader->pipeline_reg[MM_WB][i] = nop_inst; + } + } +} + + +void shader_print_runtime_stat( FILE *fout ) { + unsigned i; + + fprintf(fout, "SHD_INSN: "); + for (i=0;i<gpu_n_shader;i++) { + fprintf(fout, "%u ",sc[i]->num_sim_insn); + } + fprintf(fout, "\n"); + fprintf(fout, "SHD_THDS: "); + for (i=0;i<gpu_n_shader;i++) { + fprintf(fout, "%u ",sc[i]->not_completed); + } + fprintf(fout, "\n"); + fprintf(fout, "SHD_DIVG: "); + for (i=0;i<gpu_n_shader;i++) { + fprintf(fout, "%u ",sc[i]->n_diverge); + } + fprintf(fout, "\n"); + + fprintf(fout, "THD_INSN: "); + for (i=0; i<gpu_n_thread_per_shader; i++) { + fprintf(fout, "%d ", sc[0]->thread[i].n_insn); + } + fprintf(fout, "\n"); +} + + +void shader_print_l1_miss_stat( FILE *fout ) { + unsigned i; + + fprintf(fout, "THD_INSN_AC: "); + for (i=0; i<gpu_n_thread_per_shader; i++) { + fprintf(fout, "%d ", sc[0]->thread[i].n_insn_ac); + } + fprintf(fout, "\n"); + + fprintf(fout, "T_L1_Mss: "); //l1 miss rate per thread + for (i=0; i<gpu_n_thread_per_shader; i++) { + fprintf(fout, "%d ", sc[0]->thread[i].n_l1_mis_ac); + } + fprintf(fout, "\n"); + + fprintf(fout, "T_L1_Mgs: "); //l1 merged miss rate per thread + for (i=0; i<gpu_n_thread_per_shader; i++) { + fprintf(fout, "%d ", sc[0]->thread[i].n_l1_mis_ac - sc[0]->thread[i].n_l1_mrghit_ac); + } + fprintf(fout, "\n"); + + fprintf(fout, "T_L1_Acc: "); //l1 access per thread + for (i=0; i<gpu_n_thread_per_shader; i++) { + fprintf(fout, "%d ", sc[0]->thread[i].n_l1_access_ac); + } + fprintf(fout, "\n"); + + //per warp + int temp =0; + fprintf(fout, "W_L1_Mss: "); //l1 miss rate per warp + for (i=0; i<gpu_n_thread_per_shader; i++) { + temp += sc[0]->thread[i].n_l1_mis_ac; + if (i%warp_size == (unsigned)(warp_size-1)) { + fprintf(fout, "%d ", temp); + temp = 0; + } + } + fprintf(fout, "\n"); + temp=0; + fprintf(fout, "W_L1_Mgs: "); //l1 merged miss rate per warp + for (i=0; i<gpu_n_thread_per_shader; i++) { + temp += (sc[0]->thread[i].n_l1_mis_ac - sc[0]->thread[i].n_l1_mrghit_ac); + if (i%warp_size == (unsigned)(warp_size-1)) { + fprintf(fout, "%d ", temp); + temp = 0; + } + } + fprintf(fout, "\n"); + temp =0; + fprintf(fout, "W_L1_Acc: "); //l1 access per warp + for (i=0; i<gpu_n_thread_per_shader; i++) { + temp += sc[0]->thread[i].n_l1_access_ac; + if (i%warp_size == (unsigned)(warp_size-1)) { + fprintf(fout, "%d ", temp); + temp = 0; + } + } + fprintf(fout, "\n"); + +} + +void shader_print_stage(shader_core_ctx_t *shader, unsigned int stage, + FILE *fout, int stage_width, int print_mem, int mask ) +{ + int i, j, warp_id = -1; + + for (i=0; i<stage_width; i++) { + if (shader->pipeline_reg[stage][i].hw_thread_id > -1) { + warp_id = shader->pipeline_reg[stage][i].hw_thread_id / warp_size; + break; + } + } + i = (i>=stage_width)? 0 : i; + + fprintf(fout,"0x%04x ", shader->pipeline_reg[stage][i].pc ); + + if( mask & 2 ) { + fprintf(fout, "(" ); + for (j=0; j<stage_width; j++) + fprintf(fout, "%03d ", shader->pipeline_reg[stage][j].hw_thread_id); + fprintf(fout, "): "); + } else { + fprintf(fout, "w%02d[", warp_id); + for (j=0; j<stage_width; j++) + fprintf(fout, "%c", ((shader->pipeline_reg[stage][j].hw_thread_id != -1)?'1':'0') ); + fprintf(fout, "]: "); + } + + if( warp_id != -1 && shader->model == POST_DOMINATOR ) { + pdom_warp_ctx_t *warp=&(shader->pdom_warp[warp_id]); + if( warp->m_recvg_pc[warp->m_stack_top] == (unsigned)-1 ) { + fprintf(fout," rp:--- "); + } else { + fprintf(fout," rp:0x%03x ", warp->m_recvg_pc[warp->m_stack_top] ); + } + } + + ptx_print_insn( shader->pipeline_reg[stage][i].pc, fout ); + + if( mask & 0x10 ) { + if ( (shader->pipeline_reg[stage][i].op == STORE_OP || + shader->pipeline_reg[stage][i].op == LOAD_OP) && print_mem ) + fprintf(fout, " mem: 0x%016llx", shader->pipeline_reg[stage][i].memreqaddr); + } + fprintf(fout, "\n"); +} + +void shader_print_pre_mem_stages(shader_core_ctx_t *shader, FILE *fout, int print_mem, int mask ) +{ + int i, j; + int warp_id; + + if (!gpgpu_pre_mem_stages) return; + + for (unsigned pms = 0; pms <= gpgpu_pre_mem_stages - 1; pms++) { + fprintf(fout, "PM[%01d] = ", pms); + + warp_id = -1; + + for (i=0; i<pipe_simd_width; i++) { + if (shader->pre_mem_pipeline[pms][i].hw_thread_id > -1) { + warp_id = shader->pre_mem_pipeline[pms][i].hw_thread_id / warp_size; + break; + } + } + i = (i>=pipe_simd_width)? 0 : i; + + fprintf(fout,"0x%04x ", shader->pre_mem_pipeline[pms][i].pc ); + + if( mask & 2 ) { + fprintf(fout, "(" ); + for (j=0; j<pipe_simd_width; j++) + fprintf(fout, "%03d ", shader->pre_mem_pipeline[pms][j].hw_thread_id); + fprintf(fout, "): "); + } else { + fprintf(fout, "w%02d[", warp_id); + for (j=0; j<pipe_simd_width; j++) + fprintf(fout, "%c", ((shader->pre_mem_pipeline[pms][j].hw_thread_id != -1)?'1':'0') ); + fprintf(fout, "]: "); + } + + if( warp_id != -1 && shader->model == POST_DOMINATOR ) { + pdom_warp_ctx_t *warp=&(shader->pdom_warp[warp_id]); + if( warp->m_recvg_pc[warp->m_stack_top] == (unsigned)-1 ) { + printf(" rp:--- "); + } else { + printf(" rp:0x%03x ", warp->m_recvg_pc[warp->m_stack_top] ); + } + } + + ptx_print_insn( shader->pre_mem_pipeline[pms][i].pc, fout ); + + if( mask & 0x10 ) { + if ( ( shader->pre_mem_pipeline[pms][i].op == LOAD_OP || + shader->pre_mem_pipeline[pms][i].op == STORE_OP ) && print_mem ) + fprintf(fout, " mem: 0x%016llx", shader->pre_mem_pipeline[pms][i].memreqaddr); + } + fprintf(fout, "\n"); + } +} + +const char * ptx_get_fname( unsigned PC ); + +void shader_display_pipeline(shader_core_ctx_t *shader, FILE *fout, int print_mem, int mask ) +{ + // call this function from within gdb to print out status of pipeline + // if you encounter a bug, or to visualize pipeline operation + // (this is a good way to "verify" your pipeline model makes sense!) + + fprintf(fout, "=================================================\n"); + fprintf(fout, "shader %u at cycle %Lu+%Lu (%u threads running)\n", shader->sid, + gpu_tot_sim_cycle, gpu_sim_cycle, shader->not_completed); + fprintf(fout, "=================================================\n"); + + if ( (mask & 4) && shader->model == POST_DOMINATOR ) { + fprintf(fout,"warp status:\n"); + unsigned n = shader->n_threads / warp_size; + for (unsigned i=0; i < n; i++) { + unsigned nactive = 0; + for (unsigned j=0; j<warp_size; j++ ) { + unsigned tid = i*warp_size + j; + int done = ptx_thread_done( shader->thread[tid].ptx_thd_info ); + nactive += (ptx_thread_done( shader->thread[tid].ptx_thd_info )?0:1); + if ( done && (mask & 8) ) { + unsigned done_cycle = ptx_thread_donecycle( shader->thread[tid].ptx_thd_info ); + if ( done_cycle ) { + printf("\n w%02u:t%03u: done @ cycle %u", i, tid, done_cycle ); + } + } + } + if ( nactive == 0 ) { + continue; + } + pdom_warp_ctx_t *warp=&(shader->pdom_warp[i]); + for ( int k=0; k <= warp->m_stack_top; k++ ) { + if ( k==0 ) { + fprintf(fout, "w%02d (%2u thds active): %2u ", i, nactive, k ); + } else { + fprintf(fout, " %2u ", k ); + } + for (unsigned m=1,j=0; j<warp_size; j++, m<<=1) + fprintf(fout, "%c", ((warp->m_active_mask[k] & m)?'1':'0') ); + fprintf(fout, " pc: %4u", warp->m_pc[k] ); + if ( warp->m_recvg_pc[k] == (unsigned)-1 ) { + fprintf(fout," rp: ---- cd: %2u ", warp->m_calldepth[k] ); + } else { + fprintf(fout," rp: %4u cd: %2u ", warp->m_recvg_pc[k], warp->m_calldepth[k] ); + } + if ( warp->m_branch_div_cycle[k] != 0 ) { + fprintf(fout," bd@%6u ", (unsigned) warp->m_branch_div_cycle[k] ); + } else { + fprintf(fout," " ); + } + //fprintf(fout," func=\'%s\' ", ptx_get_fname( warp->m_pc[k] ) ); + ptx_print_insn( warp->m_pc[k], fout ); + fprintf(fout,"\n"); + } + } + fprintf(fout,"\n"); + } + + if ( mask & 0x20 ) { + fprintf(fout, "TS/IF = "); + shader_print_stage(shader, TS_IF, fout, warp_size, print_mem, mask); + } + + fprintf(fout, "IF/ID = "); + shader_print_stage(shader, IF_ID, fout, pipe_simd_width, print_mem, mask ); + + if (shader->using_rrstage) { + fprintf(fout, "ID/RR = "); + shader_print_stage(shader, ID_RR, fout, pipe_simd_width, print_mem, mask); + } + + fprintf(fout, "ID/EX = "); + shader_print_stage(shader, ID_EX, fout, pipe_simd_width, print_mem, mask); + + shader_print_pre_mem_stages(shader, fout, print_mem, mask); + + if (!gpgpu_pre_mem_stages) + fprintf(fout, "EX/MEM= "); + else + fprintf(fout, "PM/MEM= "); + shader_print_stage(shader, EX_MM, fout, pipe_simd_width, print_mem, mask); + + fprintf(fout, "MEM/WB= "); + shader_print_stage(shader, MM_WB, fout, pipe_simd_width, print_mem, mask); + + fprintf(fout, "\n"); +} + +void shader_dump_thread_state(shader_core_ctx_t *shader, FILE *fout ) +{ + fprintf( fout, "\n"); + for ( unsigned w = 0; w < gpu_n_thread_per_shader/warp_size; w++ ) { + int tid = w*warp_size; + if ( shader->warp[w].n_completed < (unsigned)warp_size ) { + fprintf( fout, " %u:%3u fetch state = c:%u a4f:%u bw:%u (completed: ", shader->sid, tid, + shader->warp[w].n_completed, + shader->warp[w].n_avail4fetch, + shader->warp[w].n_waiting_at_barrier ); + + for ( unsigned i = tid; i < (w+1)*warp_size; i++ ) { + if ( gpgpu_cuda_sim && ptx_thread_done(shader->thread[i].ptx_thd_info) ) { + fprintf(fout,"1"); + } else { + fprintf(fout,"0"); + } + if ( (((i+1)%4) == 0) && (i+1) < (w+1)*warp_size ) { + fprintf(fout,","); + } + } + fprintf(fout,")\n"); + } + } +} + +void shader_dp(shader_core_ctx_t *shader, int print_mem) { + shader_display_pipeline(shader, stdout, print_mem, 7 ); +} + + +unsigned int max_cta_per_shader( shader_core_ctx_t *shader) +{ + unsigned int result; + unsigned int padded_cta_size; + + padded_cta_size = ptx_sim_cta_size(); + if (padded_cta_size%warp_size) { + padded_cta_size = ((padded_cta_size/warp_size)+1)*(warp_size); + //printf("padded_cta_size=%u\n", padded_cta_size); + } + + //Limit by n_threads/shader + unsigned int result_thread = shader->n_threads / padded_cta_size; + + const struct gpgpu_ptx_sim_kernel_info *kernel_info = ptx_sim_kernel_info(); + + //Limit by shmem/shader + unsigned int result_shmem = (unsigned)-1; + if (kernel_info->smem > 0) + result_shmem = shader->shmem_size / kernel_info->smem; + + //Limit by register count, rounded up to multiple of 4. + unsigned int result_regs = (unsigned)-1; + if (kernel_info->regs > 0) + result_regs = shader->n_registers / (padded_cta_size * ((kernel_info->regs+3)&~3)); + + //Limit by CTA + unsigned int result_cta = shader->n_cta; + + result = result_thread; + result = gs_min2(result, result_shmem); + result = gs_min2(result, result_regs); + result = gs_min2(result, result_cta); + + static const struct gpgpu_ptx_sim_kernel_info* last_kinfo = NULL; + if (last_kinfo != kernel_info) { //Only print out stats if kernel_info struct changes + last_kinfo = kernel_info; + printf ("CTA/core = %u, limited by:", result); + if (result == result_thread) printf (" threads"); + if (result == result_shmem) printf (" shmem"); + if (result == result_regs) printf (" regs"); + if (result == result_cta) printf (" cta_limit"); + printf ("\n"); + } + + if (result < 1) { + printf ("Error: max_cta_per_shader(\"%s\") returning %d. Kernel requires more resources than shader has?\n", shader->name, result); + abort(); + } + return result; +} + +void shader_cycle( shader_core_ctx_t *shader, + unsigned int shader_number, + int grid_num ) +{ + + // last pipeline stage + shader_writeback(shader, shader_number, grid_num); + + // three parallel stages (only one does something on a given cycle) + //shader_const_memory (shader, shader_number); + shader_memory (shader, shader_number); + //shader_texture_memory (shader, shader_number); + + // empty stage + if (gpgpu_pre_mem_stages) + shader_pre_memory(shader, shader_number); + + shader_execute (shader, shader_number); + if (shader->using_rrstage) { + // model register bank conflicts + // (see Fung et al. MICRO'07 paper or ACM TACO paper) + shader_preexecute (shader, shader_number); + } + + shader_decode (shader, shader_number, grid_num); + + shader_fetch (shader, shader_number, grid_num); +} + +// performance counter that are not local to one shader +void shader_print_accstats( FILE* fout ) +{ + fprintf(fout, "gpgpu_n_load_insn = %d\n", gpgpu_n_load_insn); + fprintf(fout, "gpgpu_n_store_insn = %d\n", gpgpu_n_store_insn); + fprintf(fout, "gpgpu_n_shmem_insn = %d\n", gpgpu_n_shmem_insn); + fprintf(fout, "gpgpu_n_tex_insn = %d\n", gpgpu_n_tex_insn); + fprintf(fout, "gpgpu_n_const_mem_insn = %d\n", gpgpu_n_const_insn); + fprintf(fout, "gpgpu_n_param_mem_insn = %d\n", gpgpu_n_param_insn); + + fprintf(fout, "gpgpu_n_shmem_bkconflict = %d\n", gpgpu_n_shmem_bkconflict); + fprintf(fout, "gpgpu_n_cache_bkconflict = %d\n", gpgpu_n_cache_bkconflict); + + fprintf(fout, "gpgpu_n_intrawarp_mshr_merge = %d\n", gpgpu_n_intrawarp_mshr_merge); + fprintf(fout, "gpgpu_n_cmem_portconflict = %d\n", gpgpu_n_cmem_portconflict); + + fprintf(fout, "gpgpu_n_writeback_l1_miss = %d\n", writeback_l1_miss); + + fprintf(fout, "gpgpu_n_partial_writes = %d\n", gpgpu_n_partial_writes); + + fprintf(fout, "gpgpu_stall_shd_mem[c_mem][bk_conf] = %d\n", gpu_stall_shd_mem_breakdown[C_MEM][BK_CONF]); + fprintf(fout, "gpgpu_stall_shd_mem[c_mem][mshr_rc] = %d\n", gpu_stall_shd_mem_breakdown[C_MEM][MSHR_RC_FAIL]); + fprintf(fout, "gpgpu_stall_shd_mem[c_mem][icnt_rc] = %d\n", gpu_stall_shd_mem_breakdown[C_MEM][ICNT_RC_FAIL]); + fprintf(fout, "gpgpu_stall_shd_mem[t_mem][mshr_rc] = %d\n", gpu_stall_shd_mem_breakdown[T_MEM][MSHR_RC_FAIL]); + fprintf(fout, "gpgpu_stall_shd_mem[t_mem][icnt_rc] = %d\n", gpu_stall_shd_mem_breakdown[T_MEM][ICNT_RC_FAIL]); + fprintf(fout, "gpgpu_stall_shd_mem[s_mem][bk_conf] = %d\n", gpu_stall_shd_mem_breakdown[S_MEM][BK_CONF]); + fprintf(fout, "gpgpu_stall_shd_mem[g_mem][coal_stall] = %d\n", gpu_stall_shd_mem_breakdown[G_MEM_LD][COAL_STALL]); + fprintf(fout, "gpgpu_stall_shd_mem[g_mem_ld][mshr_rc] = %d\n", gpu_stall_shd_mem_breakdown[G_MEM_LD][MSHR_RC_FAIL]); + fprintf(fout, "gpgpu_stall_shd_mem[g_mem_ld][icnt_rc] = %d\n", gpu_stall_shd_mem_breakdown[G_MEM_LD][ICNT_RC_FAIL]); + fprintf(fout, "gpgpu_stall_shd_mem[g_mem_ld][wb_icnt_rc] = %d\n", gpu_stall_shd_mem_breakdown[G_MEM_LD][WB_ICNT_RC_FAIL]); + fprintf(fout, "gpgpu_stall_shd_mem[g_mem_ld][wb_rsrv_fail] = %d\n", gpu_stall_shd_mem_breakdown[G_MEM_LD][WB_CACHE_RSRV_FAIL]); + fprintf(fout, "gpgpu_stall_shd_mem[g_mem_st][mshr_rc] = %d\n", gpu_stall_shd_mem_breakdown[G_MEM_ST][MSHR_RC_FAIL]); + fprintf(fout, "gpgpu_stall_shd_mem[g_mem_st][icnt_rc] = %d\n", gpu_stall_shd_mem_breakdown[G_MEM_ST][ICNT_RC_FAIL]); + fprintf(fout, "gpgpu_stall_shd_mem[g_mem_st][wb_icnt_rc] = %d\n", gpu_stall_shd_mem_breakdown[G_MEM_LD][WB_ICNT_RC_FAIL]); + fprintf(fout, "gpgpu_stall_shd_mem[g_mem_st][wb_rsrv_fail] = %d\n", gpu_stall_shd_mem_breakdown[G_MEM_LD][WB_CACHE_RSRV_FAIL]); + fprintf(fout, "gpgpu_stall_shd_mem[l_mem_ld][mshr_rc] = %d\n", gpu_stall_shd_mem_breakdown[L_MEM_LD][MSHR_RC_FAIL]); + fprintf(fout, "gpgpu_stall_shd_mem[l_mem_ld][icnt_rc] = %d\n", gpu_stall_shd_mem_breakdown[L_MEM_LD][ICNT_RC_FAIL]); + fprintf(fout, "gpgpu_stall_shd_mem[l_mem_ld][wb_icnt_rc] = %d\n", gpu_stall_shd_mem_breakdown[L_MEM_LD][WB_ICNT_RC_FAIL]); + fprintf(fout, "gpgpu_stall_shd_mem[l_mem_ld][wb_rsrv_fail] = %d\n", gpu_stall_shd_mem_breakdown[L_MEM_LD][WB_CACHE_RSRV_FAIL]); + fprintf(fout, "gpgpu_stall_shd_mem[l_mem_st][mshr_rc] = %d\n", gpu_stall_shd_mem_breakdown[L_MEM_ST][MSHR_RC_FAIL]); + fprintf(fout, "gpgpu_stall_shd_mem[l_mem_st][icnt_rc] = %d\n", gpu_stall_shd_mem_breakdown[L_MEM_ST][ICNT_RC_FAIL]); + fprintf(fout, "gpgpu_stall_shd_mem[l_mem_ld][wb_icnt_rc] = %d\n", gpu_stall_shd_mem_breakdown[L_MEM_ST][WB_ICNT_RC_FAIL]); + fprintf(fout, "gpgpu_stall_shd_mem[l_mem_ld][wb_rsrv_fail] = %d\n", gpu_stall_shd_mem_breakdown[L_MEM_ST][WB_CACHE_RSRV_FAIL]); + + fprintf(fout, "gpu_reg_bank_conflict_stalls = %d\n", gpu_reg_bank_conflict_stalls); + + if (warp_occ_detailed) { + int n_warp = gpu_n_thread_per_shader / warp_size; + + for (unsigned s = 0; s<gpu_n_shader; s++) + for (int w = 0; w<n_warp; w++) { + fprintf(fout, "wod[%d][%d]=", s, w); + for (unsigned t = 0; t<warp_size; t++) { + fprintf(fout, "%d ", warp_occ_detailed[s * n_warp + w][t]); + } + fprintf(fout, "\n"); + } + } +} + +// Flushes all content of the cache to memory + +void shader_cache_flush(shader_core_ctx_t* sc) +{ + unsigned int i; + unsigned int set; + unsigned long long int flush_addr; + + shd_cache_t *cp = sc->L1cache; + shd_cache_line_t *pline; + + for (i=0; i<cp->nset*cp->assoc; i++) { + pline = &(cp->lines[i]); + set = i / cp->assoc; + if ((pline->status & (DIRTY|VALID)) == (DIRTY|VALID)) { + flush_addr = pline->addr; + + sc->fq_push(flush_addr, sc->L1cache->line_sz, 1, NO_PARTIAL_WRITE, sc->sid, 0, NULL, 0, GLOBAL_ACC_W, -1); + + pline->status &= ~VALID; + pline->status &= ~DIRTY; + } else if (pline->status & VALID) { + pline->status &= ~VALID; + } + } +} + +barrier_set_t::barrier_set_t( unsigned max_warps_per_core, unsigned max_cta_per_core ) +{ + m_max_warps_per_core = max_warps_per_core; + m_max_cta_per_core = max_cta_per_core; + if( max_warps_per_core > WARP_PER_CTA_MAX ) { + printf("ERROR ** increase WARP_PER_CTA_MAX in shader.h from %u to >= %u or warps per cta in gpgpusim.config\n", + WARP_PER_CTA_MAX, max_warps_per_core ); + exit(1); + } + m_warp_active.reset(); + m_warp_at_barrier.reset(); +} + +// during cta allocation +void barrier_set_t::allocate_barrier( unsigned cta_id, warp_set_t warps ) +{ + assert( cta_id < m_max_cta_per_core ); + cta_to_warp_t::iterator w=m_cta_to_warps.find(cta_id); + assert( w == m_cta_to_warps.end() ); // cta should not already be active or allocated barrier resources + m_cta_to_warps[cta_id] = warps; + assert( m_cta_to_warps.size() <= m_max_cta_per_core ); // catch cta's that were not properly deallocated + + m_warp_active |= warps; + m_warp_at_barrier &= ~warps; +} + +// during cta deallocation +void barrier_set_t::deallocate_barrier( unsigned cta_id ) +{ + cta_to_warp_t::iterator w=m_cta_to_warps.find(cta_id); + if( w == m_cta_to_warps.end() ) + return; + warp_set_t warps = w->second; + warp_set_t at_barrier = warps & m_warp_at_barrier; + assert( at_barrier.any() == false ); // no warps stuck at barrier + warp_set_t active = warps & m_warp_active; + assert( active.any() == false ); // no warps in CTA still running + m_warp_active &= ~warps; + m_warp_at_barrier &= ~warps; + m_cta_to_warps.erase(w); +} + +// individual warp hits barrier +void barrier_set_t::warp_reaches_barrier( unsigned cta_id, unsigned warp_id ) +{ + cta_to_warp_t::iterator w=m_cta_to_warps.find(cta_id); + + if( w == m_cta_to_warps.end() ) { // cta is active + printf("ERROR ** cta_id %u not found in barrier set on cycle %llu+%llu...\n", cta_id, gpu_tot_sim_cycle, gpu_sim_cycle ); + dump(); + abort(); + } + assert( w->second.test(warp_id) == true ); // warp is in cta + + m_warp_at_barrier.set(warp_id); + + warp_set_t warps_in_cta = w->second; + warp_set_t at_barrier = warps_in_cta & m_warp_at_barrier; + warp_set_t active = warps_in_cta & m_warp_active; + + if( at_barrier == active ) { + // all warps have reached barrier, so release waiting warps... + m_warp_at_barrier &= ~at_barrier; + } +} + +// fetching a warp +bool barrier_set_t::available_for_fetch( unsigned warp_id ) const +{ + return m_warp_active.test(warp_id) && m_warp_at_barrier.test(warp_id); +} + +// warp reaches exit +void barrier_set_t::warp_exit( unsigned warp_id ) +{ + // caller needs to verify all threads in warp are done, e.g., by checking PDOM stack to + // see it has only one entry during exit_impl() + m_warp_active.reset(warp_id); +} + +// assertions +bool barrier_set_t::warp_waiting_at_barrier( unsigned warp_id ) +{ + return m_warp_at_barrier.test(warp_id); +} + +void barrier_set_t::dump() const +{ + printf( "barrier set information\n"); + printf( " m_max_cta_per_core = %u\n", m_max_cta_per_core ); + printf( " m_max_warps_per_core = %u\n", m_max_warps_per_core ); + printf( " cta_to_warps:\n"); + + cta_to_warp_t::const_iterator i; + for( i=m_cta_to_warps.begin(); i!=m_cta_to_warps.end(); i++ ) { + unsigned cta_id = i->first; + warp_set_t warps = i->second; + printf(" cta_id %u : %s\n", cta_id, warps.to_string().c_str() ); + } + printf(" warp_active: %s\n", m_warp_active.to_string().c_str() ); + printf(" warp_at_barrier: %s\n", m_warp_at_barrier.to_string().c_str() ); + fflush(stdout); +} + +shader_core_ctx::shader_core_ctx( unsigned max_warps_per_cta, unsigned max_cta_per_core ) + : m_barriers( max_warps_per_cta, max_cta_per_core ) +{ +} + +void shader_core_ctx::set_at_barrier( unsigned cta_id, unsigned warp_id ) +{ + m_barriers.warp_reaches_barrier(cta_id,warp_id); +} + +void shader_core_ctx::warp_exit( unsigned warp_id ) +{ + m_barriers.warp_exit( warp_id ); +} + +bool shader_core_ctx::warp_waiting_at_barrier( unsigned warp_id ) +{ + return m_barriers.warp_waiting_at_barrier(warp_id); +} + +void shader_core_ctx::allocate_barrier( unsigned cta_id, warp_set_t warps ) +{ + m_barriers.allocate_barrier(cta_id,warps); +} + +void shader_core_ctx::deallocate_barrier( unsigned cta_id ) +{ + m_barriers.deallocate_barrier(cta_id); +} diff --git a/src/gpgpu-sim/shader.h b/src/gpgpu-sim/shader.h new file mode 100644 index 0000000..8333202 --- /dev/null +++ b/src/gpgpu-sim/shader.h @@ -0,0 +1,524 @@ +/* + * shader.h + * + * Copyright (c) 2009 by Tor M. Aamodt, Wilson W. L. Fung, Ali Bakhoda, + * George L. Yuan, Ivan Sham, Henry Wong, Dan O'Connor, Henry Tran and the + * University of British Columbia + * Vancouver, BC V6T 1Z4 + * All Rights Reserved. + * + * THIS IS A LEGAL DOCUMENT BY DOWNLOADING GPGPU-SIM, YOU ARE AGREEING TO THESE + * TERMS AND CONDITIONS. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * NOTE: The files libcuda/cuda_runtime_api.c and src/cuda-sim/cuda-math.h + * are derived from the CUDA Toolset available from http://www.nvidia.com/cuda + * (property of NVIDIA). The files benchmarks/BlackScholes/ and + * benchmarks/template/ are derived from the CUDA SDK available from + * http://www.nvidia.com/cuda (also property of NVIDIA). The files from + * src/intersim/ are derived from Booksim (a simulator provided with the + * textbook "Principles and Practices of Interconnection Networks" available + * from http://cva.stanford.edu/books/ppin/). As such, those files are bound by + * the corresponding legal terms and conditions set forth separately (original + * copyright notices are left in files from these sources and where we have + * modified a file our copyright notice appears before the original copyright + * notice). + * + * Using this version of GPGPU-Sim requires a complete installation of CUDA + * which is distributed seperately by NVIDIA under separate terms and + * conditions. To use this version of GPGPU-Sim with OpenCL requires a + * recent version of NVIDIA's drivers which support OpenCL. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the University of British Columbia nor the names of + * its contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * 4. This version of GPGPU-SIM is distributed freely for non-commercial use only. + * + * 5. No nonprofit user may place any restrictions on the use of this software, + * including as modified by the user, by any other authorized user. + * + * 6. GPGPU-SIM was developed primarily by Tor M. Aamodt, Wilson W. L. Fung, + * Ali Bakhoda, George L. Yuan, at the University of British Columbia, + * Vancouver, BC V6T 1Z4 + */ + +#include <stdio.h> +#include <stdlib.h> +#include <math.h> +#include <limits.h> +#include <assert.h> +#include <map> + +#include "../cuda-sim/ptx.tab.h" +#include "../cuda-sim/dram_callback.h" + +#include "gpu-cache.h" +#include "delayqueue.h" +#include "stack.h" +#include "dram.h" +#include "../abstract_hardware_model.h" + +#ifndef SHADER_H +#define SHADER_H + +#define NO_OP_FLAG 0xFF + +//READ_PACKET_SIZE: bytes: 6 address (flit can specify chanel so this gives up to ~2GB/channel, so good for now), 2 bytes [shaderid + mshrid](14 bits) + req_size(0-2 bits (if req_size variable) - so up to 2^14 = 16384 mshr total +#define READ_PACKET_SIZE 8 +//WRITE_PACKET_SIZE: bytes: 6 address, 2 miscelaneous. +#define WRITE_PACKET_SIZE 8 + +#include <bitset> +const unsigned partial_write_mask_bits = 128; //must be at least size of largest memory access. +typedef std::bitset<partial_write_mask_bits> partial_write_mask_t; + +#define WRITE_MASK_SIZE 8 +#define NO_PARTIAL_WRITE (partial_write_mask_t()) + +//this is used a lot of places where it maybe should be more variable? +#define WORD_SIZE 4 + +//Set a hard limit of 32 CTAs per shader [cuda only has 8] +#define MAX_CTA_PER_SHADER 32 + +typedef unsigned op_type; + +enum { + NO_RECONVERGE = 0, + POST_DOMINATOR = 1, + MIMD = 2, + DWF = 3, + NUM_SIMD_MODEL +}; + +//Defines number of threads grouped together to be executed together + + +typedef struct { + + address_type pc; + + op_type op; + int space; + + unsigned long long int memreqaddr; + //Each instruction keeps track of which hardware thread it came from + short hw_thread_id; + short wlane; + + /* reg label of the instruction */ + unsigned out[4]; + unsigned in[4]; + unsigned char is_vectorin; + unsigned char is_vectorout; + int arch_reg[8]; // register number for bank conflict evaluation + unsigned data_size; // what is the size of the word being operated on? + + int reg_bank_access_pending; + int reg_bank_conflict_stall_checked; // flag to turn off register bank conflict checker to avoid double stalling + + unsigned char inst_type; + + unsigned priority; + + unsigned uid; + + void *ptx_thd_info; + dram_callback_t callback; + unsigned warp_active_mask; + unsigned long long ts_cycle; + unsigned long long if_cycle; + unsigned long long id_cycle; + unsigned long long ex_cycle; + unsigned long long mm_cycle; + +} inst_t; + +typedef struct { + + void *ptx_thd_info; // pointer to the functional state of the thread in cuda-sim + + int avail4fetch; // 1 if its instrucion can be fetch into the pipeline, 0 otherwise + int warp_priority; + + int id; + + //unsigned n_completed; // number of threads in warp completed -- set for first thread in each warp + //unsigned n_avail4fetch; // number of threads in warp available to fetch -- set for first thread in each warp + //int n_waiting_at_barrier; // number of threads in warp that have reached the barrier + unsigned in_scheduler; // used by dynamic warp formation for error check + + int m_waiting_at_barrier; + int m_reached_barrier; + + unsigned n_insn; + unsigned n_insn_ac; + unsigned n_l1_mis_ac, + n_l1_mrghit_ac, + n_l1_access_ac; //used to collect "per thread" l1 miss statistics + // ac stands for accumulative. + unsigned cta_id; // which hardware CTA does this thread belong to? +} thread_ctx_t; + +struct shd_warp_t +{ + shd_warp_t(unsigned warp_size){reset(warp_size); assert(warp_size <= bitset_size);} + void reset(unsigned warp_size){n_completed = warp_size; n_avail4fetch = n_waiting_at_barrier = 0; threads_completed.reset(); threads_functionally_executed.reset();} + + unsigned wid; + unsigned n_completed; // number of threads in warp completed + unsigned n_avail4fetch; // number of threads in warp available to fetch + int n_waiting_at_barrier; // number of threads in warp that have reached the barrier + + const static unsigned bitset_size = 32; + std::bitset<bitset_size> threads_completed; + std::bitset<bitset_size> threads_functionally_executed; +}; + +inline unsigned hw_tid_from_wid(unsigned wid, unsigned warp_size, unsigned i){return wid * warp_size + i;}; +inline unsigned wid_from_hw_tid(unsigned tid, unsigned warp_size){return tid/warp_size;}; + +typedef struct { + + int m_stack_top; + + address_type *m_pc; + unsigned int *m_active_mask; + address_type *m_recvg_pc; + unsigned int *m_calldepth; + + unsigned long long *m_branch_div_cycle; + +} pdom_warp_ctx_t; // bounded stack that implements pdom reconvergence (see MICRO'07 paper) + + +enum mshr_status { + INITIALIZED = 0, + IN_ICNT2MEM, + IN_CBTOL2QUEUE, + IN_L2TODRAMQUEUE, + IN_DRAM_REQ_QUEUE, + IN_DRAMRETURN_Q, + IN_DRAMTOL2QUEUE, + IN_L2TOCBQUEUE_HIT, + IN_L2TOCBQUEUE_MISS, + IN_ICNT2SHADER, + FETCHED, + NUM_MSHR_STATUS +}; + +//used to stages that time_vector will keep track of their timing +enum mem_req_stat { + MR_UNUSED, + MR_FQPUSHED, + MR_ICNT_PUSHED, + MR_ICNT_INJECTED, + MR_ICNT_AT_DEST, + MR_DRAMQ, //icnt_pop at dram side and mem_ctrl_push + MR_DRAM_PROCESSING_START, + MR_DRAM_PROCESSING_END, + MR_DRAM_OUTQ, + MR_2SH_ICNT_PUSHED, // icnt_push and mem_ctl_pop //STORES END HERE! + MR_2SH_ICNT_INJECTED, + MR_2SH_ICNT_AT_DEST, + MR_2SH_FQ_POP, //icnt_pop called inside fq_pop + MR_RETURN_Q, + MR_WRITEBACK, //done + NUM_MEM_REQ_STAT +}; +#include <vector> +typedef struct mshr_entry_t { +#ifdef _GLIBCXX_DEBUG + //satisfy cxx debug conditions on iterators, needs to be nonsingular to copy, which messes completely with structures containing them. + mshr_entry_t(){ + static std::vector<mshr_entry_t> dummy_vector; + this_mshr = dummy_vector.begin(); //initialize it to something nonsingular so it can be copied. + } +#endif +private: + friend class mshr_shader_unit; + std::vector<mshr_entry_t>::iterator this_mshr; //to ease tracking and update. +public: + unsigned request_uid; + + /* memory address of the data */ + unsigned long long int addr; + + // instructions are stored here. + std::vector<inst_t> insts; + + /* Current stage of the load: fetched or not? */ + bool fetched(){return status == FETCHED;}; + + bool iswrite; + + bool merged_on_other_reqest; //true if waiting for another mshr - this mshr doesn't send a memory request + struct mshr_entry_t *merged_requests; //mshrs waiting on this mshr + + enum mshr_status status; + + void *mf; // link to corresponding memory fetch structure + + //unsigned space; //does below. + bool istexture; //if it's a request from the texture cache + bool isconst; //if it's a request from the constant cache + bool islocal; //if it's a request to the local memory of a thread + + bool wt_no_w2cache; //in write_through, sometimes need to prevent writing back returning data into cache, because its been written in the meantime. +} mshr_entry; + +enum mem_access_type { + GLOBAL_ACC_R = 0, + LOCAL_ACC_R = 1, + CONST_ACC_R = 2, + TEXTURE_ACC_R = 3, + GLOBAL_ACC_W = 4, + LOCAL_ACC_W = 5, + L2_WRBK_ACC = 6, + NUM_MEM_ACCESS_TYPE = 7 +}; + + +/* A pointer to the function that glues the shader with the memory hiearchy */ +typedef unsigned char (*fq_push_t)(unsigned long long int addr, int bsize, unsigned char readwrite, + partial_write_mask_t, + int sid, int wid, mshr_entry* mshr, int cache_hits_waiting, + enum mem_access_type mem_acc, address_type pc); + +typedef unsigned char (*fq_has_buffer_t)(unsigned long long int addr, int bsize, bool write, int sid); + +const unsigned WARP_PER_CTA_MAX = 32; +typedef std::bitset<WARP_PER_CTA_MAX> warp_set_t; + +class barrier_set_t { +public: + barrier_set_t( unsigned max_warps_per_core, unsigned max_cta_per_core ); + + // during cta allocation + void allocate_barrier( unsigned cta_id, warp_set_t warps ); + + // during cta deallocation + void deallocate_barrier( unsigned cta_id ); + + typedef std::map<unsigned, warp_set_t > cta_to_warp_t; + + // individual warp hits barrier + void warp_reaches_barrier( unsigned cta_id, unsigned warp_id ); + + // fetching a warp + bool available_for_fetch( unsigned warp_id ) const; + + // warp reaches exit + void warp_exit( unsigned warp_id ); + + // assertions + bool warp_waiting_at_barrier( unsigned warp_id ); + + // debug + void dump() const; + +private: + unsigned m_max_cta_per_core; + unsigned m_max_warps_per_core; + + cta_to_warp_t m_cta_to_warps; + warp_set_t m_warp_active; + warp_set_t m_warp_at_barrier; +}; + +class mshr_shader_unit; + +extern unsigned int warp_size; + +typedef struct shader_core_ctx : public core_t +{ + shader_core_ctx( unsigned max_warps_per_cta, unsigned max_cta_per_core ); + + virtual void set_at_barrier( unsigned cta_id, unsigned warp_id ); + virtual void warp_exit( unsigned warp_id ); + virtual bool warp_waiting_at_barrier( unsigned warp_id ); + void allocate_barrier( unsigned cta_id, warp_set_t warps ); + void deallocate_barrier( unsigned cta_id ); + +//// + + const char *name; + int sid; + + // array of the threads running on this shader core + thread_ctx_t *thread; + unsigned int n_threads; + unsigned int last_issued_thread; + + //per warp information array + std::vector<shd_warp_t> warp; + + barrier_set_t m_barriers; + + //Keeps track of which warp of instructions to fetch/execute + int next_warp; + + // number of threads to be completed ( ==0 when all thread on this core completed) + int not_completed; + // number of Cuda Thread Arrays (blocks) currently running on this shader. + int n_active_cta; + //Keep track of multiple CTAs in shader + int cta_status[MAX_CTA_PER_SHADER]; + // registers holding the instruction between pipeline stages. + // see below for definition of pipeline stages + inst_t** pipeline_reg; + inst_t** pre_mem_pipeline; + int warp_part2issue; // which part of warp to issue to pipeline + int new_warp_TS; // new warp at TS pipeline register + + shd_cache_t *L1cache; + shd_cache_t *L1texcache; + shd_cache_t *L1constcache; + + // pointer to memory access wrapping function + fq_push_t fq_push; + fq_has_buffer_t fq_has_buffer; + + // simulation cycles happened to the shader, kept for cacheline replacement + unsigned int gpu_cycle; + // number of instructions committed by this shader core + unsigned int num_sim_insn; + + // reconvergence + unsigned int model; + + // Structure is used to keep track of the branching within the warp of instructions. + // As a group of instructions is grouped together from different threads to be executed, when + // a branch does occur, then the sub-set that does not get run will be given the value of warp_priority, + // and warp_priority will increase. Each time a sub-set branches further, a similar scheme is used. + // When a sub-set completes fully, then this table will determine which next sub-set to finish, which + // will be the next largest value in the table. + int branch_priority; + int* max_branch_priority; //Keeps track of the maximum priority of the threads running within a warp. need n_threads number of these + + // pdom reconvergence context for each warp + pdom_warp_ctx_t *pdom_warp; + + int waiting_at_barrier; // number of threads current waiting at a barrier in this shader. + int RR_k; //counter for register read pipeline + + int using_dwf; //is the scheduler using dynamic warp formation + int using_rrstage; //is the pipeline using an extra stage for register read + int using_commit_queue; //is the scheduler using commit_queue? + + delay_queue *thd_commit_queue; + + int pending_shmem_bkacc; // 0 = check conflict for new insn + int pending_cache_bkacc; // 0 = check conflict for new insn + + bool shader_memory_new_instruction_processed; + + int pending_mem_access; // number of memory access to be serviced (use for W0 classification) + + int pending_cmem_acc; //number of accesses to differrnt addresses in the constant memory cache + + unsigned int n_diverge; // number of divergence occurred in this shader + + //Shader core resources + unsigned int shmem_size; + unsigned int n_registers; //registers available in the shader core + unsigned int n_cta; //Limit on number of concurrent CTAs in shader core + + //void *req_hist; //not used anywhere + + mshr_shader_unit *mshr_unit; +} shader_core_ctx_t; + + +shader_core_ctx_t* shader_create( const char *name, int sid, unsigned int n_threads, + unsigned int n_mshr, fq_push_t fq_push, fq_has_buffer_t fq_has_buffer, unsigned int model); +unsigned shader_reinit(shader_core_ctx_t *sc, int start_thread, int end_thread); +void shader_init_CTA(shader_core_ctx_t *shader, int start_thread, int end_thread); + +void shader_fetch( shader_core_ctx_t *shader, + unsigned int shader_number, + int grid_num ); +void shader_decode( shader_core_ctx_t *shader, + unsigned int shader_number, + unsigned int grid_num ); +void shader_preexecute( shader_core_ctx_t *shader, + unsigned int shader_number ); +void shader_execute( shader_core_ctx_t *shader, + unsigned int shader_number ); +void shader_pre_memory( shader_core_ctx_t *shader, + unsigned int shader_number ); +void shader_const_memory( shader_core_ctx_t *shader, + unsigned int shader_number ); +void shader_texture_memory( shader_core_ctx_t *shader, + unsigned int shader_number ); +void shader_memory( shader_core_ctx_t *shader, + unsigned int shader_number ); +void shader_writeback( shader_core_ctx_t *shader, + unsigned int shader_number, + int grid_num ); + +void shader_display_pipeline(shader_core_ctx_t *shader, FILE *fout, int print_mem, int mask3bit ); +void shader_dump_thread_state(shader_core_ctx_t *shader, FILE *fout ); +void shader_cycle( shader_core_ctx_t *shader, + unsigned int shader_number, + int grid_num ); + +void mshr_print(FILE *fp, shader_core_ctx_t *shader); + +void mshr_update_status(mshr_entry* mshr, enum mshr_status new_status); + +mshr_entry* fetchMSHR(delay_queue** mshr, shader_core_ctx_t* sc); +mshr_entry* shader_check_mshr4tag(shader_core_ctx_t* sc, unsigned long long int addr,int mem_type); +void shader_update_mshr(shader_core_ctx_t* sc, unsigned long long int fetched_addr, unsigned int mshr_idx, int mem_type ); +void shader_visualizer_dump(FILE *fp, shader_core_ctx_t* sc); + +void init_mshr_pool(); +mshr_entry* alloc_mshr_entry(); +void free_mshr_entry( mshr_entry * ); + +void shader_clean(shader_core_ctx_t *sc, unsigned int n_threads); +void shader_cache_flush(shader_core_ctx_t* sc); + +// print out the accumulative statistics for shaders (those that are not local to one shader) +void shader_print_accstats( FILE* fout ); +void shader_print_runtime_stat( FILE *fout ); +void shader_print_l1_miss_stat( FILE *fout ); + +//return the maximum CTAs that can be running at the same on shader +//based on on the current kernel's CTA size and is 1 if mutiple CTA per block is not supported +unsigned int max_cta_per_shader( shader_core_ctx_t *shader); + +#define N_PIPELINE_STAGES 7 +#define TS_IF 0 +#define IF_ID 1 +#define ID_RR 2 +#define ID_EX 3 +#define RR_EX 3 +#define EX_MM 4 +#define MM_WB 5 +#define WB_RT 6 + + +#endif /* SHADER_H */ diff --git a/src/gpgpu-sim/stack.cc b/src/gpgpu-sim/stack.cc new file mode 100644 index 0000000..7f7cc70 --- /dev/null +++ b/src/gpgpu-sim/stack.cc @@ -0,0 +1,127 @@ +/* + * stack.c + * + * Copyright (c) 2009 by Tor M. Aamodt, Wilson W. L. Fung, Ali Bakhoda, + * Ivan Sham and the + * University of British Columbia + * Vancouver, BC V6T 1Z4 + * All Rights Reserved. +* + * THIS IS A LEGAL DOCUMENT BY DOWNLOADING GPGPU-SIM, YOU ARE AGREEING TO THESE + * TERMS AND CONDITIONS. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * NOTE: The files libcuda/cuda_runtime_api.c and src/cuda-sim/cuda-math.h + * are derived from the CUDA Toolset available from http://www.nvidia.com/cuda + * (property of NVIDIA). The files benchmarks/BlackScholes/ and + * benchmarks/template/ are derived from the CUDA SDK available from + * http://www.nvidia.com/cuda (also property of NVIDIA). The files from + * src/intersim/ are derived from Booksim (a simulator provided with the + * textbook "Principles and Practices of Interconnection Networks" available + * from http://cva.stanford.edu/books/ppin/). As such, those files are bound by + * the corresponding legal terms and conditions set forth separately (original + * copyright notices are left in files from these sources and where we have + * modified a file our copyright notice appears before the original copyright + * notice). + * + * Using this version of GPGPU-Sim requires a complete installation of CUDA + * which is distributed seperately by NVIDIA under separate terms and + * conditions. To use this version of GPGPU-Sim with OpenCL requires a + * recent version of NVIDIA's drivers which support OpenCL. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the University of British Columbia nor the names of + * its contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * 4. This version of GPGPU-SIM is distributed freely for non-commercial use only. + * + * 5. No nonprofit user may place any restrictions on the use of this software, + * including as modified by the user, by any other authorized user. + * + * 6. GPGPU-SIM was developed primarily by Tor M. Aamodt, Wilson W. L. Fung, + * Ali Bakhoda, George L. Yuan, at the University of British Columbia, + * Vancouver, BC V6T 1Z4 + */ + +#include "stack.h" + +#include <stdlib.h> +#include <assert.h> + +void push_stack(Stack *S, address_type val) { + assert(S->top < S->max_size); + S->v[S->top] = val; + (S->top)++; + +} + +address_type pop_stack(Stack *S) { + (S->top)--; + return(S->v[S->top]); +} + +address_type top_stack(Stack *S) { + assert(S->top >= 1); + return(S->v[S->top - 1]); +} + +Stack* new_stack(int size) { + Stack* S; + S = (Stack*)malloc(sizeof(Stack)); + S->max_size = size; + S->top = 0; + S->v = (address_type*)calloc(size, sizeof(address_type)); + return S; +} + +void free_stack(Stack *S) { + free(S->v); + free(S); +} + +int size_stack(Stack *S) { + return S->top; +} + +int full_stack(Stack *S) { + return S->top >= S->max_size; +} + +int empty_stack(Stack *S) { + return S->top == 0; +} + +int element_exist_stack(Stack *S, address_type value) { + int i; + for (i = 0; i < S->top; ++i) { + if (value == S->v[i]) { + return 1; + } + } + return 0; +} + +void reset_stack(Stack *S) { + S->top = 0; +} diff --git a/src/gpgpu-sim/stack.h b/src/gpgpu-sim/stack.h new file mode 100644 index 0000000..b00e25b --- /dev/null +++ b/src/gpgpu-sim/stack.h @@ -0,0 +1,90 @@ +/* + * stack.h + * + * Copyright (c) 2009 by Tor M. Aamodt, Wilson W. L. Fung, Ali Bakhoda, + * Ivan Sham and the + * University of British Columbia + * Vancouver, BC V6T 1Z4 + * All Rights Reserved. + * + * THIS IS A LEGAL DOCUMENT BY DOWNLOADING GPGPU-SIM, YOU ARE AGREEING TO THESE + * TERMS AND CONDITIONS. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * NOTE: The files libcuda/cuda_runtime_api.c and src/cuda-sim/cuda-math.h + * are derived from the CUDA Toolset available from http://www.nvidia.com/cuda + * (property of NVIDIA). The files benchmarks/BlackScholes/ and + * benchmarks/template/ are derived from the CUDA SDK available from + * http://www.nvidia.com/cuda (also property of NVIDIA). The files + * src/gpgpusim_entrypoint.c and src/simplesim-3.0/ are derived from the + * SimpleScalar Toolset available from http://www.simplescalar.com/ + * (property of SimpleScalar LLC) and the files src/intersim/ are derived + * from Booksim (Simulator provided with the textbook "Principles and + * Practices of Interconnection Networks" available from + * http://cva.stanford.edu/books/ppin/). As such, those files are bound by + * the corresponding legal terms and conditions set forth separately (original + * copyright notices are left in files from these sources and where we have + * modified a file our copyright notice appears before the original copyright + * notice). + * + * Using this version of GPGPU-Sim requires a complete installation of CUDA + * which is distributed seperately by NVIDIA under separate terms and + * conditions. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the University of British Columbia nor the names of + * its contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * 4. This version of GPGPU-SIM is distributed freely for non-commercial use only. + * + * 5. No nonprofit user may place any restrictions on the use of this software, + * including as modified by the user, by any other authorized user. + * + * 6. GPGPU-SIM was developed primarily by Tor M. Aamodt, Wilson W. L. Fung, + * Ali Bakhoda, George L. Yuan, at the University of British Columbia, + * Vancouver, BC V6T 1Z4 + */ + +#ifndef _MY_STACK_ +#define _MY_STACK_ + +#include "../util.h" + +typedef struct { + address_type *v; + int max_size; + int top; +} Stack; + +void push_stack(Stack *S, address_type val); +address_type pop_stack(Stack *S); +address_type top_stack(Stack *S); +Stack* new_stack(int size); +void free_stack(Stack *S); +int size_stack(Stack *S); +int full_stack(Stack *S); +int empty_stack(Stack *S); +int element_exist_stack(Stack *S, address_type value); +void reset_stack(Stack *S); +#endif // _MY_STACK_ diff --git a/src/gpgpu-sim/stat-tool.cc b/src/gpgpu-sim/stat-tool.cc new file mode 100644 index 0000000..2dc9c0b --- /dev/null +++ b/src/gpgpu-sim/stat-tool.cc @@ -0,0 +1,1081 @@ +/* + * stat-tool.cc + * + * Copyright © 2009 by Tor M. Aamodt, Wilson W. L. Fung, Ali Bakhoda, + * George L. Yuan and the University of British Columbia, Vancouver, + * BC V6T 1Z4, All Rights Reserved. + * + * THIS IS A LEGAL DOCUMENT BY DOWNLOADING GPGPU-SIM, YOU ARE AGREEING TO THESE + * TERMS AND CONDITIONS. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * NOTE: The files libcuda/cuda_runtime_api.c and src/cuda-sim/cuda-math.h + * are derived from the CUDA Toolset available from http://www.nvidia.com/cuda + * (property of NVIDIA). The files benchmarks/BlackScholes/ and + * benchmarks/template/ are derived from the CUDA SDK available from + * http://www.nvidia.com/cuda (also property of NVIDIA). The files from + * src/intersim/ are derived from Booksim (a simulator provided with the + * textbook "Principles and Practices of Interconnection Networks" available + * from http://cva.stanford.edu/books/ppin/). As such, those files are bound by + * the corresponding legal terms and conditions set forth separately (original + * copyright notices are left in files from these sources and where we have + * modified a file our copyright notice appears before the original copyright + * notice). + * + * Using this version of GPGPU-Sim requires a complete installation of CUDA + * which is distributed seperately by NVIDIA under separate terms and + * conditions. To use this version of GPGPU-Sim with OpenCL requires a + * recent version of NVIDIA's drivers which support OpenCL. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the University of British Columbia nor the names of + * its contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * 4. This version of GPGPU-SIM is distributed freely for non-commercial use only. + * + * 5. No nonprofit user may place any restrictions on the use of this software, + * including as modified by the user, by any other authorized user. + * + * 6. GPGPU-SIM was developed primarily by Tor M. Aamodt, Wilson W. L. Fung, + * Ali Bakhoda, George L. Yuan, at the University of British Columbia, + * Vancouver, BC V6T 1Z4 + */ + +#include <stdio.h> +#include <stdlib.h> +#include <assert.h> +#include <zlib.h> +#include <string> + +// detect gcc 4.3 and use unordered map (part of c++0x) +// unordered map doesn't play nice with _GLIBCXX_DEBUG, just use a map if its enabled. +#if defined( __GNUC__ ) and not defined( _GLIBCXX_DEBUG ) +#if __GNUC__ >= 4 && __GNUC_MINOR__ >= 3 + #include <unordered_map> + #define my_hash_map std::unordered_map +#else + #include <ext/hash_map> + namespace std { + using namespace __gnu_cxx; + } + #define my_hash_map std::hash_map +#endif +#else + #include <map> + #define my_hash_map std::map + #define USE_MAP +#endif + +#include "histogram.h" + +binned_histogram::binned_histogram (std::string name, int nbins, int* bins) + : m_name(name), m_nbins(nbins), m_bins(NULL), m_bin_cnts(new int[m_nbins]), m_maximum(0) +{ + if (bins) { + m_bins = new int[m_nbins]; + for (int i = 0; i < nbins; i++) { + m_bins[i] = bins[i]; + } + } + + reset_bins(); +} + +binned_histogram::binned_histogram (const binned_histogram& other) + : m_name(other.m_name), m_nbins(other.m_nbins), m_bins(NULL), + m_bin_cnts(new int[m_nbins]), m_maximum(0) +{ + for (int i = 0; i < m_nbins; i++) { + m_bin_cnts[i] = other.m_bin_cnts[i]; + } +} + +void binned_histogram::reset_bins () { + for (int i = 0; i < m_nbins; i++) { + m_bin_cnts[i] = 0; + } +} + +void binned_histogram::add2bin (int sample) { + assert(0); + m_maximum = (sample > m_maximum)? sample : m_maximum; +} + +void binned_histogram::fprint (FILE *fout) { + if (m_name.c_str() != NULL) fprintf(fout, "%s = ", m_name.c_str()); + for (int i = 0; i < m_nbins; i++) { + fprintf(fout, "%d ", m_bin_cnts[i]); + } + fprintf(fout, "max=%d ", m_maximum); +} + +binned_histogram::~binned_histogram () { + if (m_bins) delete[] m_bins; + delete[] m_bin_cnts; +} + +pow2_histogram::pow2_histogram (std::string name, int nbins, int* bins) + : binned_histogram (name, nbins, bins) {} + +void pow2_histogram::add2bin (int sample) { + assert(sample >= 0); + + int bin; + int v = sample; + register unsigned int shift; + + bin = (v > 0xFFFF) << 4; v >>= bin; + shift = (v > 0xFF ) << 3; v >>= shift; bin |= shift; + shift = (v > 0xF ) << 2; v >>= shift; bin |= shift; + shift = (v > 0x3 ) << 1; v >>= shift; bin |= shift; + bin |= (v >> 1); + bin += (sample > 0)? 1:0; + + m_bin_cnts[bin] += 1; + + m_maximum = (sample > m_maximum)? sample : m_maximum; +} + +linear_histogram::linear_histogram (int stride, const char *name, int nbins, int* bins) + : binned_histogram (name, nbins, bins), m_stride(stride) +{ +} + +void linear_histogram::add2bin (int sample) { + assert(sample >= 0); + + int bin = sample / m_stride; + + m_bin_cnts[bin] += 1; + + m_maximum = (sample > m_maximum)? sample : m_maximum; +} + + +#include <list> +#include <vector> +#include <map> +#include <algorithm> +#include <string> +#include "../util.h" + +#include "cflogger.h" + +///////////////////////////////////////////////////////////////////////////////////// +// logger snapshot trigger: +// - automate the snap_shot part of loggers to avoid modifying simulation loop everytime +// a new time-dependent stat is added +///////////////////////////////////////////////////////////////////////////////////// + +class snap_shot_trigger { +protected: + unsigned long long m_snap_shot_interval; + +public: + snap_shot_trigger(unsigned long long interval) : m_snap_shot_interval(interval) {} + virtual ~snap_shot_trigger() {} + + const unsigned long long & get_interval() const { return m_snap_shot_interval;} + + void try_snap_shot(unsigned long long current_cycle) { + if ((current_cycle % m_snap_shot_interval == 0) && current_cycle != 0) { + snap_shot(current_cycle); + } + } + + virtual void snap_shot(unsigned long long current_cycle) = 0; +}; + +static unsigned long long min_snap_shot_interval = 0; +static unsigned long long next_snap_shot_cycle = 0; +static std::list<snap_shot_trigger*> list_ss_trigger; + +void add_snap_shot_trigger (snap_shot_trigger* ss_trigger) +{ + // quick optimization assuming that all snap shot intervals are perfect multiples of each other + if (min_snap_shot_interval == 0 || min_snap_shot_interval > ss_trigger->get_interval()) { + min_snap_shot_interval = ss_trigger->get_interval(); + next_snap_shot_cycle = min_snap_shot_interval; // assume that snap shots haven't started yet + } + list_ss_trigger.push_back(ss_trigger); +} + +void remove_snap_shot_trigger (snap_shot_trigger* ss_trigger) +{ + list_ss_trigger.remove(ss_trigger); +} + +void try_snap_shot (unsigned long long current_cycle) +{ + if (min_snap_shot_interval == 0) return; + if (current_cycle != next_snap_shot_cycle) return; + + std::list<snap_shot_trigger*>::iterator ss_trigger_iter = list_ss_trigger.begin(); + for(; ss_trigger_iter != list_ss_trigger.end(); ++ss_trigger_iter) { + (*ss_trigger_iter)->snap_shot(current_cycle); // WF: should be try_snap_shot + } + next_snap_shot_cycle = current_cycle + min_snap_shot_interval; // WF: stateful testing, maybe bad +} + +///////////////////////////////////////////////////////////////////////////////////// +// spill log interface: +// - unified interface to spill log to file to avoid infinite memory usage for logging +///////////////////////////////////////////////////////////////////////////////////// + +class spill_log_interface { + public: + spill_log_interface() {} + virtual ~spill_log_interface() {} + + virtual void spill(FILE *fout, bool final) = 0; +}; + +static unsigned long long spill_interval = 0; +static unsigned long long next_spill_cycle = 0; +static std::list<spill_log_interface*> list_spill_log; + +void add_spill_log (spill_log_interface* spill_log) +{ + list_spill_log.push_back(spill_log); +} + +void remove_spill_log (spill_log_interface* spill_log) +{ + list_spill_log.remove(spill_log); +} + +void set_spill_interval (unsigned long long interval) +{ + spill_interval = interval; + next_spill_cycle = spill_interval; +} + +void spill_log_to_file (FILE *fout, int final, unsigned long long current_cycle) +{ + if (!final && spill_interval == 0) return; + if (!final && current_cycle <= next_spill_cycle) return; + + fprintf(fout, "\n"); // ensure that the spill occurs at a new line + std::list<spill_log_interface*>::iterator i_spill_log = list_spill_log.begin(); + for(; i_spill_log != list_spill_log.end(); ++i_spill_log) { + (*i_spill_log)->spill(fout, final); + } + fflush(fout); + + next_spill_cycle = current_cycle + spill_interval; // WF: stateful testing, maybe bad +} + +///////////////////////////////////////////////////////////////////////////////////// +// thread control-flow locality logger +///////////////////////////////////////////////////////////////////////////////////// +unsigned translate_pc_to_ptxlineno(unsigned pc); +class thread_insn_span { +private: + + typedef my_hash_map<address_type, int> span_count_map; + unsigned long long m_cycle; + int m_n_insn; + span_count_map m_insn_span_count; + +public: + + thread_insn_span(unsigned long long cycle, int n_insn) + : m_cycle(cycle), m_n_insn(n_insn), +#ifdef USE_MAP + m_insn_span_count() +#else + m_insn_span_count(n_insn * 2) +#endif + { } + + ~thread_insn_span() { } + + thread_insn_span(const thread_insn_span& other) + : m_cycle(other.m_cycle), m_n_insn(other.m_n_insn), + m_insn_span_count(other.m_insn_span_count) + { } + + thread_insn_span& operator=(const thread_insn_span& other) + { + printf("thread_insn_span& operator=\n"); + if (this != &other && m_n_insn != other.m_n_insn) { + m_n_insn = other.m_n_insn; + m_insn_span_count = other.m_insn_span_count; + m_cycle = other.m_cycle; + } + return *this; + } + + thread_insn_span& operator+=(const thread_insn_span& other) + { + assert(m_n_insn == other.m_n_insn); // no way to aggregate if they are different programs + span_count_map::const_iterator i_sc = other.m_insn_span_count.begin(); + for (; i_sc != other.m_insn_span_count.end(); ++i_sc) { + m_insn_span_count[i_sc->first] += i_sc->second; + } + return *this; + } + + void set_span( address_type pc ) { + if( ((int)pc) >= 0 ) + m_insn_span_count[pc] += 1; + } + + void reset(unsigned long long cycle) { + m_cycle = cycle; + m_insn_span_count.clear(); + } + + void print_span(FILE *fout) { + fprintf(fout, "%d: ", (int)m_cycle); + span_count_map::const_iterator i_sc = m_insn_span_count.begin(); + for (; i_sc != m_insn_span_count.end(); ++i_sc) { + fprintf(fout, "%d ", i_sc->first); + } + fprintf(fout, "\n"); + } + + void print_histo(FILE *fout) { + fprintf(fout, "%d:", (int)m_cycle); + span_count_map::const_iterator i_sc = m_insn_span_count.begin(); + for (; i_sc != m_insn_span_count.end(); ++i_sc) { + fprintf(fout, "%d ", i_sc->second); + } + fprintf(fout, "\n"); + } + + void print_sparse_histo(FILE *fout) { + int n_printed_entries = 0; + span_count_map::const_iterator i_sc = m_insn_span_count.begin(); + for (; i_sc != m_insn_span_count.end(); ++i_sc) { + unsigned ptx_lineno = translate_pc_to_ptxlineno(i_sc->first); + fprintf(fout, "%u %d ", ptx_lineno, i_sc->second); + n_printed_entries++; + } + if (n_printed_entries == 0) { + fprintf(fout, "0 0 "); + } + fprintf(fout, "\n"); + } + + void print_sparse_histo(gzFile fout) { + int n_printed_entries = 0; + span_count_map::const_iterator i_sc = m_insn_span_count.begin(); + for (; i_sc != m_insn_span_count.end(); ++i_sc) { + unsigned ptx_lineno = translate_pc_to_ptxlineno(i_sc->first); + gzprintf(fout, "%u %d ", ptx_lineno, i_sc->second); + n_printed_entries++; + } + if (n_printed_entries == 0) { + gzprintf(fout, "0 0 "); + } + gzprintf(fout, "\n"); + } +}; + +class thread_CFlocality : public snap_shot_trigger, public spill_log_interface { +private: + + std::string m_name; + + int m_nthreads; + std::vector<address_type> m_thread_pc; + + unsigned long long m_cycle; + thread_insn_span m_thd_span; + std::list<thread_insn_span> m_thd_span_archive; + +public: + + thread_CFlocality(std::string name, unsigned long long snap_shot_interval, + int nthreads, int n_insn, address_type start_pc, unsigned long long start_cycle = 0) + : snap_shot_trigger(snap_shot_interval), m_name(name), + m_nthreads(nthreads), m_thread_pc(nthreads, start_pc), m_cycle(start_cycle), + m_thd_span(start_cycle, n_insn) + { + std::fill(m_thread_pc.begin(), m_thread_pc.end(), -1); // so that hw thread with no work assigned will not clobber results + } + + ~thread_CFlocality() {} + + void update_thread_pc( int thread_id, address_type pc ) { + m_thread_pc[thread_id] = pc; + m_thd_span.set_span(pc); + } + + void snap_shot(unsigned long long current_cycle) { + m_thd_span_archive.push_back(m_thd_span); + m_thd_span.reset(current_cycle); + for (int i = 0; i < (int)m_thread_pc.size(); i++) { + m_thd_span.set_span(m_thread_pc[i]); + } + } + + void spill(FILE *fout, bool final) { + std::list<thread_insn_span>::iterator lit = m_thd_span_archive.begin(); + for (; lit != m_thd_span_archive.end(); lit = m_thd_span_archive.erase(lit) ) { + fprintf(fout, "%s-", m_name.c_str()); + lit->print_histo(fout); + } + assert( m_thd_span_archive.empty() ); + if (final) { + fprintf(fout, "%s-", m_name.c_str()); + m_thd_span.print_histo(fout); + } + } + + void print_visualizer(FILE *fout) { + fprintf(fout, "%s: ", m_name.c_str()); + if (m_thd_span_archive.empty()) { + + // visualizer do no require snap_shots + m_thd_span.print_sparse_histo(fout); + + // clean the thread span + m_thd_span.reset(0); + for (int i = 0; i < (int)m_thread_pc.size(); i++) { + m_thd_span.set_span(m_thread_pc[i]); + } + } else { + assert(0); // TODO: implement fall back so that visualizer can work with snap shots + } + } + + void print_visualizer(gzFile fout) { + gzprintf(fout, "%s: ", m_name.c_str()); + if (m_thd_span_archive.empty()) { + + // visualizer do no require snap_shots + m_thd_span.print_sparse_histo(fout); + + // clean the thread span + m_thd_span.reset(0); + for (int i = 0; i < (int)m_thread_pc.size(); i++) { + m_thd_span.set_span(m_thread_pc[i]); + } + } else { + assert(0); // TODO: implement fall back so that visualizer can work with snap shots + } + } + + void print_span(FILE *fout) { + std::list<thread_insn_span>::iterator lit = m_thd_span_archive.begin(); + for (; lit != m_thd_span_archive.end(); ++lit) { + fprintf(fout, "%s-", m_name.c_str()); + lit->print_span(fout); + } + fprintf(fout, "%s-", m_name.c_str()); + m_thd_span.print_span(fout); + } + + void print_histo(FILE *fout) { + std::list<thread_insn_span>::iterator lit = m_thd_span_archive.begin(); + for (; lit != m_thd_span_archive.end(); ++lit) { + fprintf(fout, "%s-", m_name.c_str()); + lit->print_histo(fout); + } + fprintf(fout, "%s-", m_name.c_str()); + m_thd_span.print_histo(fout); + } +}; + +static int n_thread_CFloggers = 0; +static thread_CFlocality** thread_CFlogger = NULL; + +void create_thread_CFlogger( int n_loggers, int n_threads, int n_insn, address_type start_pc, unsigned long long logging_interval) +{ + destroy_thread_CFlogger(); + + n_thread_CFloggers = n_loggers; + thread_CFlogger = new thread_CFlocality*[n_loggers]; + + std::string name_tpl("CFLog"); + char buffer[32]; + for (int i = 0; i < n_thread_CFloggers; i++) { + snprintf(buffer, 32, "%02d", i); + thread_CFlogger[i] = new thread_CFlocality( name_tpl + buffer, logging_interval, n_threads, n_insn, start_pc); + if (logging_interval != 0) { + add_snap_shot_trigger(thread_CFlogger[i]); + add_spill_log(thread_CFlogger[i]); + } + } +} + +void destroy_thread_CFlogger( ) +{ + if (thread_CFlogger != NULL) { + for (int i = 0; i < n_thread_CFloggers; i++) { + remove_snap_shot_trigger(thread_CFlogger[i]); + remove_spill_log(thread_CFlogger[i]); + delete thread_CFlogger[i]; + } + delete thread_CFlogger; + thread_CFlogger = NULL; + } +} + +void cflog_update_thread_pc( int logger_id, int thread_id, address_type pc ) +{ + if (thread_id < 0) return; + thread_CFlogger[logger_id]->update_thread_pc(thread_id, pc); +} + +void cflog_snapshot( int logger_id, unsigned long long cycle ) +{ + thread_CFlogger[logger_id]->snap_shot(cycle); +} + +void cflog_print(FILE *fout) +{ + for (int i = 0; i < n_thread_CFloggers; i++) { + thread_CFlogger[i]->print_histo(fout); + } +} + +void cflog_visualizer_print(FILE *fout) +{ + for (int i = 0; i < n_thread_CFloggers; i++) { + thread_CFlogger[i]->print_visualizer(fout); + } +} + +void cflog_visualizer_gzprint(gzFile fout) +{ + for (int i = 0; i < n_thread_CFloggers; i++) { + thread_CFlogger[i]->print_visualizer(fout); + } +} + +///////////////////////////////////////////////////////////////////////////////////// +// per-insn active thread distribution (warp occ) logger +///////////////////////////////////////////////////////////////////////////////////// + +class insn_warp_occ_logger{ +private: + int m_simd_width; + std::vector<linear_histogram> m_insn_warp_occ; + int m_id; + static int s_ids; + +public: + insn_warp_occ_logger(int simd_width, int n_insn) + : m_simd_width(simd_width), + m_insn_warp_occ(n_insn, linear_histogram(1, "", m_simd_width)), + m_id(s_ids++) {} + + insn_warp_occ_logger(const insn_warp_occ_logger& other) + : m_simd_width(other.m_simd_width), + m_insn_warp_occ(other.m_insn_warp_occ.size(), linear_histogram(1, "", m_simd_width)), + m_id(s_ids++) {} + + insn_warp_occ_logger& operator=(const insn_warp_occ_logger& p) { + printf("insn_warp_occ_logger Operator= called: %02d \n", m_id); + assert(0); + return *this; + } + + ~insn_warp_occ_logger() {} + + void set_id(int id) { + m_id = id; + } + + void log(address_type pc, int warp_occ) { + m_insn_warp_occ[pc].add2bin(warp_occ - 1); + } + + void print(FILE *fout) { + for (unsigned i = 0; i < m_insn_warp_occ.size(); i++) { + fprintf(fout, "InsnWarpOcc%02d-%d", m_id, i); + m_insn_warp_occ[i].fprint(fout); + fprintf(fout, "\n"); + } + } +}; +int insn_warp_occ_logger::s_ids = 0; + +static std::vector<insn_warp_occ_logger> iwo_logger; + +void insn_warp_occ_create( int n_loggers, int simd_width, int n_insn) +{ + iwo_logger.clear(); + iwo_logger.assign(n_loggers, insn_warp_occ_logger(simd_width, n_insn)); + for (unsigned i = 0; i < iwo_logger.size(); i++) { + iwo_logger[i].set_id(i); + } +} + +void insn_warp_occ_log( int logger_id, address_type pc, int warp_occ) +{ + if (warp_occ <= 0) return; + iwo_logger[logger_id].log(pc, warp_occ); +} + +void insn_warp_occ_print( FILE *fout ) +{ + for (unsigned i = 0; i < iwo_logger.size(); i++) { + iwo_logger[i].print(fout); + } +} + +///////////////////////////////////////////////////////////////////////////////////// +// generic linear histogram logger +///////////////////////////////////////////////////////////////////////////////////// + +class linear_histogram_snapshot { +private: + unsigned long long m_cycle; + std::vector<int> m_linear_histogram; +public: + linear_histogram_snapshot(int n_bins, unsigned long long cycle) + : m_cycle(cycle), + m_linear_histogram(n_bins,0) + { } + + linear_histogram_snapshot(const linear_histogram_snapshot& other) + : m_cycle(other.m_cycle), + m_linear_histogram(other.m_linear_histogram) + { } + + ~linear_histogram_snapshot() { } + + void addsample(int pos) { + assert((size_t)pos < m_linear_histogram.size()); + m_linear_histogram[pos] += 1; + } + + void subsample(int pos) { + assert((size_t)pos < m_linear_histogram.size()); + m_linear_histogram[pos] -= 1; + } + + void reset(unsigned long long cycle) { + m_cycle = cycle; + m_linear_histogram.assign(m_linear_histogram.size(), 0); + } + + void set_cycle(unsigned long long cycle) { + m_cycle = cycle; + } + + void print(FILE *fout) { + fprintf(fout, "%d = ", (int)m_cycle); + for (unsigned int i = 0; i < m_linear_histogram.size(); i++) { + fprintf(fout, "%d ", m_linear_histogram[i]); + } + } + + void print_visualizer(FILE *fout) { + for (unsigned int i = 0; i < m_linear_histogram.size(); i++) { + fprintf(fout, "%d ", m_linear_histogram[i]); + } + } + + void print_visualizer(gzFile fout) { + for (unsigned int i = 0; i < m_linear_histogram.size(); i++) { + gzprintf(fout, "%d ", m_linear_histogram[i]); + } + } +}; + +class linear_histogram_logger : public snap_shot_trigger, public spill_log_interface { +private: + int m_n_bins; + linear_histogram_snapshot m_curr_lin_hist; + std::list<linear_histogram_snapshot> m_lin_hist_archive; + unsigned long long m_cycle; + bool m_reset_at_snap_shot; + std::string m_name; + int m_id; + static int s_ids; + +public: + linear_histogram_logger(int n_bins, + unsigned long long snap_shot_interval, + const char *name, + bool reset_at_snap_shot = true, + unsigned long long start_cycle = 0) + : snap_shot_trigger(snap_shot_interval), + m_n_bins(n_bins), + m_curr_lin_hist(m_n_bins, start_cycle), + m_lin_hist_archive(), + m_cycle(start_cycle), + m_reset_at_snap_shot(reset_at_snap_shot), + m_name(name), + m_id(s_ids++) {} + + linear_histogram_logger(const linear_histogram_logger& other) // WF: Buggy - Not really copying data over + : snap_shot_trigger(other.get_interval()), + m_n_bins(other.m_n_bins), + m_curr_lin_hist(m_n_bins, other.m_cycle), + m_lin_hist_archive(), + m_cycle(other.m_cycle), + m_reset_at_snap_shot(other.m_reset_at_snap_shot), + m_name(other.m_name), + m_id(s_ids++) {} + + // using default assignment operator! + + ~linear_histogram_logger() { + // printf("Destroyer called: %s%02d \n", m_name.c_str(), m_id); + remove_snap_shot_trigger(this); + remove_spill_log(this); + } + + void set_id(int id) { + m_id = id; + } + + void log(int pos) { + m_curr_lin_hist.addsample(pos); + } + + void unlog(int pos) { + m_curr_lin_hist.subsample(pos); + } + + void snap_shot(unsigned long long current_cycle) { + m_lin_hist_archive.push_back(m_curr_lin_hist); + if (m_reset_at_snap_shot) { + m_curr_lin_hist.reset(current_cycle); + } else { + m_curr_lin_hist.set_cycle(current_cycle); + } + } + + void spill(FILE *fout, bool final) { + std::list<linear_histogram_snapshot>::iterator iter = m_lin_hist_archive.begin(); + for (; iter != m_lin_hist_archive.end(); iter = m_lin_hist_archive.erase(iter) ) { + fprintf(fout, "%s%02d-", m_name.c_str(), (m_id >= 0)? m_id : 0); + iter->print(fout); + fprintf(fout, "\n"); + } + assert( m_lin_hist_archive.empty() ); + if (final) { + fprintf(fout, "%s%02d-", m_name.c_str(), (m_id >= 0)? m_id : 0); + m_curr_lin_hist.print(fout); + fprintf(fout, "\n"); + } + } + + void print(FILE *fout) { + std::list<linear_histogram_snapshot>::iterator iter = m_lin_hist_archive.begin(); + for (; iter != m_lin_hist_archive.end(); ++iter) { + fprintf(fout, "%s%02d-", m_name.c_str(), m_id); + iter->print(fout); + fprintf(fout, "\n"); + } + fprintf(fout, "%s%02d-", m_name.c_str(), m_id); + m_curr_lin_hist.print(fout); + fprintf(fout, "\n"); + } + + void print_visualizer(FILE *fout) { + assert(m_lin_hist_archive.empty()); // don't support snapshot for now + fprintf(fout, "%s", m_name.c_str()); + if (m_id >= 0) { + fprintf(fout, "%02d: ", m_id); + } else { + fprintf(fout, ": "); + } + m_curr_lin_hist.print_visualizer(fout); + fprintf(fout, "\n"); + if (m_reset_at_snap_shot) { + m_curr_lin_hist.reset(0); + } + } + + void print_visualizer(gzFile fout) { + assert(m_lin_hist_archive.empty()); // don't support snapshot for now + gzprintf(fout, "%s", m_name.c_str()); + if (m_id >= 0) { + gzprintf(fout, "%02d: ", m_id); + } else { + gzprintf(fout, ": "); + } + m_curr_lin_hist.print_visualizer(fout); + gzprintf(fout, "\n"); + if (m_reset_at_snap_shot) { + m_curr_lin_hist.reset(0); + } + } +}; +int linear_histogram_logger::s_ids = 0; + +///////////////////////////////////////////////////////////////////////////////////// +// per-shadercore active thread distribution (warp occ) logger +///////////////////////////////////////////////////////////////////////////////////// + +static std::vector<linear_histogram_logger> s_warp_occ_logger; + +void shader_warp_occ_create( int n_loggers, int simd_width, unsigned long long logging_interval) +{ + // simd_width + 1 to include the case with full warp + s_warp_occ_logger.assign(n_loggers, + linear_histogram_logger(simd_width + 1, logging_interval, "ShdrWarpOcc")); + for (unsigned i = 0; i < s_warp_occ_logger.size(); i++) { + s_warp_occ_logger[i].set_id(i); + add_snap_shot_trigger(&(s_warp_occ_logger[i])); + add_spill_log(&(s_warp_occ_logger[i])); + } +} + +void shader_warp_occ_log( int logger_id, int warp_occ) +{ + s_warp_occ_logger[logger_id].log(warp_occ); +} + +void shader_warp_occ_snapshot( int logger_id, unsigned long long current_cycle) +{ + s_warp_occ_logger[logger_id].snap_shot(current_cycle); +} + +void shader_warp_occ_print( FILE *fout ) +{ + for (unsigned i = 0; i < s_warp_occ_logger.size(); i++) { + s_warp_occ_logger[i].print(fout); + } +} + + +///////////////////////////////////////////////////////////////////////////////////// +// per-shadercore memory-access logger +///////////////////////////////////////////////////////////////////////////////////// + +static int s_mem_acc_logger_n_dram = 0; +static int s_mem_acc_logger_n_bank = 0; +static std::vector<linear_histogram_logger> s_mem_acc_logger; + +void shader_mem_acc_create( int n_loggers, int n_dram, int n_bank, unsigned long long logging_interval) +{ + // (n_bank + 1) to space data out; 2x to separate read and write + s_mem_acc_logger.assign(n_loggers, + linear_histogram_logger(2 * n_dram * (n_bank + 1), logging_interval, "ShdrMemAcc")); + + s_mem_acc_logger_n_dram = n_dram; + s_mem_acc_logger_n_bank = n_bank; + for (unsigned i = 0; i < s_mem_acc_logger.size(); i++) { + s_mem_acc_logger[i].set_id(i); + add_snap_shot_trigger(&(s_mem_acc_logger[i])); + add_spill_log(&(s_mem_acc_logger[i])); + } +} + +void shader_mem_acc_log( int logger_id, int dram_id, int bank, char rw) +{ + if (s_mem_acc_logger_n_dram == 0) return; + int write_offset = 0; + switch(rw) { + case 'r': write_offset = 0; break; + case 'w': write_offset = (s_mem_acc_logger_n_bank + 1) * s_mem_acc_logger_n_dram; break; + default: assert(0); break; + } + s_mem_acc_logger[logger_id].log(dram_id * s_mem_acc_logger_n_bank + bank + write_offset); +} + +void shader_mem_acc_snapshot( int logger_id, unsigned long long current_cycle) +{ + s_mem_acc_logger[logger_id].snap_shot(current_cycle); +} + +void shader_mem_acc_print( FILE *fout ) +{ + for (unsigned i = 0; i < s_mem_acc_logger.size(); i++) { + s_mem_acc_logger[i].print(fout); + } +} + + +///////////////////////////////////////////////////////////////////////////////////// +// per-shadercore memory-latency logger +///////////////////////////////////////////////////////////////////////////////////// + +static bool s_mem_lat_logger_used = false; +static int s_mem_lat_logger_nbins = 48; // up to 2^24 = 16M +static std::vector<linear_histogram_logger> s_mem_lat_logger; + +void shader_mem_lat_create( int n_loggers, unsigned long long logging_interval) +{ + s_mem_lat_logger.assign(n_loggers, + linear_histogram_logger(s_mem_lat_logger_nbins, logging_interval, "ShdrMemLat")); + + for (unsigned i = 0; i < s_mem_lat_logger.size(); i++) { + s_mem_lat_logger[i].set_id(i); + add_snap_shot_trigger(&(s_mem_lat_logger[i])); + add_spill_log(&(s_mem_lat_logger[i])); + } + + s_mem_lat_logger_used = true; +} + +void shader_mem_lat_log( int logger_id, int latency) +{ + if (s_mem_lat_logger_used == false) return; + if (latency > (1<<(s_mem_lat_logger_nbins/2))) assert(0); // guard for out of bound bin + assert(latency > 0); + + int latency_bin; + + int bin; // LOG_2(latency) + int v = latency; + register unsigned int shift; + + bin = (v > 0xFFFF) << 4; v >>= bin; + shift = (v > 0xFF ) << 3; v >>= shift; bin |= shift; + shift = (v > 0xF ) << 2; v >>= shift; bin |= shift; + shift = (v > 0x3 ) << 1; v >>= shift; bin |= shift; + bin |= (v >> 1); + latency_bin = 2 * bin; + if (bin > 0) { + latency_bin += ((latency & (1 << (bin - 1))) != 0)? 1 : 0; // approx. for LOG_sqrt2(latency) + } + + s_mem_lat_logger[logger_id].log(latency_bin); +} + +void shader_mem_lat_snapshot( int logger_id, unsigned long long current_cycle) +{ + s_mem_lat_logger[logger_id].snap_shot(current_cycle); +} + +void shader_mem_lat_print( FILE *fout ) +{ + for (unsigned i = 0; i < s_mem_lat_logger.size(); i++) { + s_mem_lat_logger[i].print(fout); + } +} + + +///////////////////////////////////////////////////////////////////////////////////// +// per-shadercore cache-miss logger +///////////////////////////////////////////////////////////////////////////////////// + +static int s_cache_access_logger_n_types = 0; +static std::vector<linear_histogram_logger> s_cache_access_logger; + +enum cache_access_logger_types { + NORMAL, TEXTURE, CONSTANT +}; + +int get_shader_normal_cache_id() { return NORMAL; } +int get_shader_texture_cache_id() { return TEXTURE; } +int get_shader_constant_cache_id() { return CONSTANT; } + +void shader_cache_access_create( int n_loggers, int n_types, unsigned long long logging_interval) +{ + // There are different type of cache (x2 for recording accesses and misses) + s_cache_access_logger.assign(n_loggers, + linear_histogram_logger(n_types * 2, logging_interval, "ShdrCacheMiss")); + + s_cache_access_logger_n_types = n_types; + for (unsigned i = 0; i < s_cache_access_logger.size(); i++) { + s_cache_access_logger[i].set_id(i); + add_snap_shot_trigger(&(s_cache_access_logger[i])); + add_spill_log(&(s_cache_access_logger[i])); + } +} + +void shader_cache_access_log( int logger_id, int type, int miss) +{ + if (s_cache_access_logger_n_types == 0) return; + if (logger_id < 0) return; + assert(type == NORMAL || type == TEXTURE || type == CONSTANT); + assert(miss == 0 || miss == 1); + + s_cache_access_logger[logger_id].log(2 * type + miss); +} + +void shader_cache_access_unlog( int logger_id, int type, int miss) +{ + if (s_cache_access_logger_n_types == 0) return; + if (logger_id < 0) return; + assert(type == NORMAL || type == TEXTURE || type == CONSTANT); + assert(miss == 0 || miss == 1); + + s_cache_access_logger[logger_id].unlog(2 * type + miss); +} + +void shader_cache_access_print( FILE *fout ) +{ + for (unsigned i = 0; i < s_cache_access_logger.size(); i++) { + s_cache_access_logger[i].print(fout); + } +} + + +///////////////////////////////////////////////////////////////////////////////////// +// per-shadercore CTA count logger (only make sense with gpgpu_spread_blocks_across_cores) +///////////////////////////////////////////////////////////////////////////////////// + +static linear_histogram_logger *s_CTA_count_logger = NULL; + +void shader_CTA_count_create( int n_shaders, unsigned long long logging_interval) +{ + // only need one logger to track all the shaders + if (s_CTA_count_logger != NULL) delete s_CTA_count_logger; + s_CTA_count_logger = new linear_histogram_logger(n_shaders, logging_interval, "ShdrCTACount", false); + + s_CTA_count_logger->set_id(-1); + if (logging_interval != 0) { + add_snap_shot_trigger(s_CTA_count_logger); + add_spill_log(s_CTA_count_logger); +} +} + +void shader_CTA_count_log( int shader_id, int nCTAadded ) +{ + if (s_CTA_count_logger == NULL) return; + + for (int i = 0; i < nCTAadded; i++) { + s_CTA_count_logger->log(shader_id); + } +} + +void shader_CTA_count_unlog( int shader_id, int nCTAdone ) +{ + if (s_CTA_count_logger == NULL) return; + + for (int i = 0; i < nCTAdone; i++) { + s_CTA_count_logger->unlog(shader_id); + } +} + +void shader_CTA_count_print( FILE *fout ) +{ + if (s_CTA_count_logger == NULL) return; + s_CTA_count_logger->print(fout); +} + +void shader_CTA_count_visualizer_print( FILE *fout ) +{ + if (s_CTA_count_logger == NULL) return; + s_CTA_count_logger->print_visualizer(fout); +} + +void shader_CTA_count_visualizer_gzprint( gzFile fout ) +{ + if (s_CTA_count_logger == NULL) return; + s_CTA_count_logger->print_visualizer(fout); +} + diff --git a/src/gpgpu-sim/visualizer.cc b/src/gpgpu-sim/visualizer.cc new file mode 100644 index 0000000..3772fc2 --- /dev/null +++ b/src/gpgpu-sim/visualizer.cc @@ -0,0 +1,602 @@ +/* + * Copyright © 2009 by Tor M. Aamodt, Wilson W. L. Fung and the University of + * British Columbia, Vancouver, BC V6T 1Z4, All Rights Reserved. + * + * THIS IS A LEGAL DOCUMENT BY DOWNLOADING GPGPU-SIM, YOU ARE AGREEING TO THESE + * TERMS AND CONDITIONS. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * NOTE: The files libcuda/cuda_runtime_api.c and src/cuda-sim/cuda-math.h + * are derived from the CUDA Toolset available from http://www.nvidia.com/cuda + * (property of NVIDIA). The files benchmarks/BlackScholes/ and + * benchmarks/template/ are derived from the CUDA SDK available from + * http://www.nvidia.com/cuda (also property of NVIDIA). The files from + * src/intersim/ are derived from Booksim (a simulator provided with the + * textbook "Principles and Practices of Interconnection Networks" available + * from http://cva.stanford.edu/books/ppin/). As such, those files are bound by + * the corresponding legal terms and conditions set forth separately (original + * copyright notices are left in files from these sources and where we have + * modified a file our copyright notice appears before the original copyright + * notice). + * + * Using this version of GPGPU-Sim requires a complete installation of CUDA + * which is distributed seperately by NVIDIA under separate terms and + * conditions. To use this version of GPGPU-Sim with OpenCL requires a + * recent version of NVIDIA's drivers which support OpenCL. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the University of British Columbia nor the names of + * its contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * 4. This version of GPGPU-SIM is distributed freely for non-commercial use only. + * + * 5. No nonprofit user may place any restrictions on the use of this software, + * including as modified by the user, by any other authorized user. + * + * 6. GPGPU-SIM was developed primarily by Tor M. Aamodt, Wilson W. L. Fung, + * Ali Bakhoda, George L. Yuan, at the University of British Columbia, + * Vancouver, BC V6T 1Z4 + */ + +#include "gpu-sim.h" +#include "../option_parser.h" +#include <time.h> +#include <string.h> +#include <zlib.h> + +extern unsigned int gpu_n_shader; +extern unsigned int gpu_n_mem; +extern unsigned int gpu_mem_n_bk; +extern shader_core_ctx_t **sc; +extern dram_t **dram; +extern unsigned int L1_read_miss; +extern unsigned int L1_write_miss; +extern unsigned int L1_texture_miss; +extern unsigned int L1_const_miss; +extern unsigned L2_write_miss; +extern unsigned L2_write_hit; +extern unsigned L2_read_hit; +extern unsigned L2_read_miss; +extern unsigned long long int mf_total_lat; +extern unsigned num_mfs; +extern unsigned long long gpu_sim_cycle; +extern unsigned long long gpu_sim_insn; +extern unsigned long long gpu_tot_sim_insn; +extern unsigned long long gpu_completed_thread; +extern unsigned int gpgpu_n_sent_writes; +extern unsigned int gpgpu_n_processed_writes; +extern unsigned int gpgpu_n_cache_bkconflict; +extern unsigned int gpgpu_n_shmem_bkconflict; +extern unsigned int gpu_stall_by_MSHRwb; +extern unsigned int *max_return_queue_length; +extern unsigned max_mrq_latency; +extern unsigned max_dq_latency; +extern unsigned max_mf_latency; +extern unsigned max_icnt2mem_latency; +extern unsigned max_icnt2sh_latency; +extern int gpgpu_warpdistro_shader; +extern unsigned ***mem_access_type_stats; + +extern unsigned int warp_size; +extern unsigned int *shader_cycle_distro; +void time_vector_print_interval2file(FILE *outfile); +void time_vector_print_interval2gzfile(gzFile outfile); +void cflog_visualizer_gzprint(gzFile fout); +void shader_CTA_count_visualizer_gzprint(gzFile fout); +float shd_cache_windowed_cache_miss_rate(shd_cache_t*, int); +void shd_cache_new_window(shd_cache_t*); + +int g_visualizer_enabled = 1; +char *g_visualizer_filename = NULL; +int g_visualizer_zlevel = 6; + +void visualizer_options(option_parser_t opp) +{ + option_parser_register(opp, "-visualizer_enabled", OPT_BOOL, + &g_visualizer_enabled, "Turn on visualizer output (1=On, 0=Off)", + "1"); + + option_parser_register(opp, "-visualizer_outputfile", OPT_CSTR, + &g_visualizer_filename, "Specifies the output log file for visualizer", + NULL); + + option_parser_register(opp, "-visualizer_zlevel", OPT_INT32, + &g_visualizer_zlevel, "Compression level of the visualizer output log (0=no comp, 9=highest)", + "6"); + +} + +void visualizer_printstat() +{ + static unsigned int *last_shader_cycle_distro = NULL; + gzFile visualizer_file = NULL; // gzFile is basically a pointer to a struct, so it is fine to initialize it as NULL + unsigned i; + if ( !g_visualizer_enabled ) + return; + if (!last_shader_cycle_distro) + last_shader_cycle_distro = (unsigned int*) calloc(warp_size + 3, sizeof(unsigned int)); + + if ( g_visualizer_filename == NULL ) { + time_t curr_time; + time(&curr_time); + char *date = ctime(&curr_time); + char *s = date; + while (*s) { + if (*s == ' ' || *s == '\t' || *s == ':') *s = '-'; + if (*s == '\n' || *s == '\r' ) *s = 0; + s++; + } + char buf[1024]; + snprintf(buf,1024,"gpgpusim_visualizer__%s.log.gz",date); + visualizer_file = gzopen(buf, "w"); + if (visualizer_file == NULL) { + printf("error - could not open visualizer trace file.\n"); + exit(1); + } + gzsetparams(visualizer_file, g_visualizer_zlevel, Z_DEFAULT_STRATEGY); + g_visualizer_filename = strdup(buf); + } else { + visualizer_file = gzopen(g_visualizer_filename,"a"); + if (visualizer_file == NULL) { + printf("error - could not open visualizer trace file.\n"); + exit(1); + } + gzsetparams(visualizer_file, g_visualizer_zlevel, Z_DEFAULT_STRATEGY); + } + + // instruction count per shader core + gzprintf(visualizer_file, "shaderinsncount: "); + for (i=0;i<gpu_n_shader;i++) { + gzprintf(visualizer_file, "%u ",sc[i]->num_sim_insn); + } + gzprintf(visualizer_file, "\n"); + + // warp divergence per shader core + gzprintf(visualizer_file, "shaderwarpdiv: "); + for (i=0;i<gpu_n_shader;i++) { + gzprintf(visualizer_file, "%u ", sc[i]->n_diverge); + } + gzprintf(visualizer_file, "\n"); + + cflog_visualizer_gzprint(visualizer_file); + shader_CTA_count_visualizer_gzprint(visualizer_file); + + // per shader core cache miss rate + gzprintf(visualizer_file, "CacheMissRate_GlobalLocalL1_All: "); + for (i=0;i<gpu_n_shader;i++) { + gzprintf(visualizer_file, "%0.4f ", shd_cache_windowed_cache_miss_rate(sc[i]->L1cache, 0)); + } + gzprintf(visualizer_file, "\n"); + + gzprintf(visualizer_file, "CacheMissRate_TextureL1_All: "); + for (i=0;i<gpu_n_shader;i++) { + gzprintf(visualizer_file, "%0.4f ", shd_cache_windowed_cache_miss_rate(sc[i]->L1texcache, 0)); + } + gzprintf(visualizer_file, "\n"); + + gzprintf(visualizer_file, "CacheMissRate_ConstL1_All: "); + for (i=0;i<gpu_n_shader;i++) { + gzprintf(visualizer_file, "%0.4f ", shd_cache_windowed_cache_miss_rate(sc[i]->L1constcache, 0)); + } + gzprintf(visualizer_file, "\n"); + + gzprintf(visualizer_file, "CacheMissRate_GlobalLocalL1_noMgHt: "); + for (i=0;i<gpu_n_shader;i++) { + gzprintf(visualizer_file, "%0.4f ", shd_cache_windowed_cache_miss_rate(sc[i]->L1cache, 1)); + } + gzprintf(visualizer_file, "\n"); + + gzprintf(visualizer_file, "CacheMissRate_TextureL1_noMgHt: "); + for (i=0;i<gpu_n_shader;i++) { + gzprintf(visualizer_file, "%0.4f ", shd_cache_windowed_cache_miss_rate(sc[i]->L1texcache, 1)); + } + gzprintf(visualizer_file, "\n"); + + gzprintf(visualizer_file, "CacheMissRate_ConstL1_noMgHt: "); + for (i=0;i<gpu_n_shader;i++) { + gzprintf(visualizer_file, "%0.4f ", shd_cache_windowed_cache_miss_rate(sc[i]->L1constcache, 1)); + } + gzprintf(visualizer_file, "\n"); + + // reset for next interval + for (i=0;i<gpu_n_shader;i++) { + shd_cache_new_window(sc[i]->L1cache); + shd_cache_new_window(sc[i]->L1texcache); + shd_cache_new_window(sc[i]->L1constcache); + } + + // dram specific statistics + for (i=0;i<gpu_n_mem;i++) { + gzprintf(visualizer_file, "dramncmd: %u %u\n",dram[i]->id, dram[i]->n_cmd_partial); + gzprintf(visualizer_file, "dramnop: %u %u\n",dram[i]->id,dram[i]->n_nop_partial); + gzprintf(visualizer_file,"dramnact: %u %u\n",dram[i]->id,dram[i]->n_act_partial); + gzprintf(visualizer_file,"dramnpre: %u %u\n",dram[i]->id,dram[i]->n_pre_partial); + gzprintf(visualizer_file,"dramnreq: %u %u\n",dram[i]->id,dram[i]->n_req_partial); + gzprintf(visualizer_file,"dramavemrqs: %u %u\n",dram[i]->id, + dram[i]->n_cmd_partial?(dram[i]->ave_mrqs_partial/dram[i]->n_cmd_partial ):0); + + // utilization and efficiency + gzprintf(visualizer_file,"dramutil: %u %u\n", + dram[i]->id,dram[i]->n_cmd_partial?100*dram[i]->bwutil_partial/dram[i]->n_cmd_partial:0); + gzprintf(visualizer_file,"drameff: %u %u\n", + dram[i]->id,dram[i]->n_activity_partial?100*dram[i]->bwutil_partial/dram[i]->n_activity_partial:0); + + // reset for next interval + dram[i]->bwutil_partial = 0; + dram[i]->n_activity_partial = 0; + dram[i]->ave_mrqs_partial = 0; + dram[i]->n_cmd_partial = 0; + dram[i]->n_nop_partial = 0; + dram[i]->n_act_partial = 0; + dram[i]->n_pre_partial = 0; + dram[i]->n_req_partial = 0; + } + + // dram access type classification + for (i=0;i<gpu_n_mem;i++) { + unsigned int j; + for (j = 0; j < gpu_mem_n_bk; j++) { + gzprintf(visualizer_file,"dramglobal_acc_r: %u %u %u\n", dram[i]->id, j, + mem_access_type_stats[GLOBAL_ACC_R][dram[i]->id][j]); + gzprintf(visualizer_file,"dramglobal_acc_w: %u %u %u\n", dram[i]->id, j, + mem_access_type_stats[GLOBAL_ACC_W][dram[i]->id][j]); + gzprintf(visualizer_file,"dramlocal_acc_r: %u %u %u\n", dram[i]->id, j, + mem_access_type_stats[LOCAL_ACC_R][dram[i]->id][j]); + gzprintf(visualizer_file,"dramlocal_acc_w: %u %u %u\n", dram[i]->id, j, + mem_access_type_stats[LOCAL_ACC_W][dram[i]->id][j]); + gzprintf(visualizer_file,"dramconst_acc_r: %u %u %u\n", dram[i]->id, j, + mem_access_type_stats[CONST_ACC_R][dram[i]->id][j]); + gzprintf(visualizer_file,"dramtexture_acc_r: %u %u %u\n", dram[i]->id, j, + mem_access_type_stats[TEXTURE_ACC_R][dram[i]->id][j]); + } + } + + // overall cache miss rates + gzprintf(visualizer_file, "Lonetexturemiss: %d\n", L1_texture_miss); + gzprintf(visualizer_file, "Loneconstmiss: %d\n", L1_const_miss); + gzprintf(visualizer_file, "Lonereadmiss: %d\n", L1_read_miss); + gzprintf(visualizer_file, "Lonewritemiss: %d\n", L1_write_miss); + gzprintf(visualizer_file, "Ltwowritemiss: %d\n", L2_write_miss); + gzprintf(visualizer_file, "Ltwowritehit: %d\n", L2_write_hit); + gzprintf(visualizer_file, "Ltworeadmiss: %d\n", L2_read_miss); + gzprintf(visualizer_file, "Ltworeadhit: %d\n", L2_read_hit); + + // latency stats + if (num_mfs) { + gzprintf(visualizer_file, "averagemflatency: %lld\n", mf_total_lat/num_mfs); + } + + // other parameters for graphing + gzprintf(visualizer_file, "globalcyclecount: %lld\n", gpu_sim_cycle); + gzprintf(visualizer_file, "globalinsncount: %lld\n", gpu_sim_insn); + gzprintf(visualizer_file, "globaltotinsncount: %lld\n", gpu_tot_sim_insn); + gzprintf(visualizer_file, "gpucompletedthreads: %lld\n", gpu_completed_thread); + gzprintf(visualizer_file, "gpgpunsentwrites: %d\n", gpgpu_n_sent_writes); + gzprintf(visualizer_file, "gpgpunprocessedwrites: %d\n", gpgpu_n_processed_writes); + gzprintf(visualizer_file, "gpgpu_n_cache_bkconflict: %d\n", gpgpu_n_cache_bkconflict); + gzprintf(visualizer_file, "gpgpu_n_shmem_bkconflict: %d\n", gpgpu_n_shmem_bkconflict); + gzprintf(visualizer_file, "gpu_stall_by_MSHRwb: %d\n", gpu_stall_by_MSHRwb); + + // warp divergence breakdown + time_vector_print_interval2gzfile(visualizer_file); + gzprintf(visualizer_file, "WarpDivergenceBreakdown:"); + unsigned int total=0; + unsigned int cf = (gpgpu_warpdistro_shader==-1)?gpu_n_shader:1; + gzprintf(visualizer_file, " %d", (shader_cycle_distro[0] - last_shader_cycle_distro[0]) / cf ); + gzprintf(visualizer_file, " %d", (shader_cycle_distro[2] - last_shader_cycle_distro[2]) / cf ); + for (i=0; i<warp_size+3; i++) { + if ( i>=3 ) { + total += (shader_cycle_distro[i] - last_shader_cycle_distro[i]); + if ( ((i-3) % (warp_size/8)) == ((warp_size/8)-1) ) { + gzprintf(visualizer_file, " %d", total / cf ); + total=0; + } + } + last_shader_cycle_distro[i] = shader_cycle_distro[i]; + } + gzprintf(visualizer_file,"\n"); + + gzclose(visualizer_file); +} + +#include <list> +#include <vector> +#include <iostream> +#include <map> +#include"../gpgpu-sim/shader.h" +class my_time_vector { +private: + std::map< unsigned int, std::vector<long int> > ld_time_map; + std::map< unsigned int, std::vector<long int> > st_time_map; + unsigned ld_vector_size; + unsigned st_vector_size; + std::vector<double> ld_time_dist; + std::vector<double> st_time_dist; + + std::vector<double> overal_ld_time_dist; + std::vector<double> overal_st_time_dist; + int overal_ld_count; + int overal_st_count; + +public: + my_time_vector(int ld_size,int st_size){ + ld_vector_size = ld_size; + st_vector_size = st_size; + ld_time_dist.resize(ld_size); + st_time_dist.resize(st_size); + overal_ld_time_dist.resize(ld_size); + overal_st_time_dist.resize(st_size); + overal_ld_count = 0; + overal_st_count= 0; + } + void update_ld(unsigned int uid,unsigned int slot, long int time) { + if ( ld_time_map.find( uid )!=ld_time_map.end() ) { + ld_time_map[uid][slot]=time; + } else if (slot <= MR_2SH_FQ_POP ) { + std::vector<long int> time_vec; + time_vec.resize(ld_vector_size); + time_vec[slot] = time; + ld_time_map[uid] = time_vec; + } else { + //It's a merged mshr! forget it + } + } + void update_st(unsigned int uid,unsigned int slot, long int time) { + if ( st_time_map.find( uid )!=st_time_map.end() ) { + st_time_map[uid][slot]=time; + } else { + std::vector<long int> time_vec; + time_vec.resize(st_vector_size); + time_vec[slot] = time; + st_time_map[uid] = time_vec; + } + } + void check_ld_update(unsigned int uid,unsigned int slot, long int latency) { + if ( ld_time_map.find( uid )!=ld_time_map.end() ) { + int our_latency = ld_time_map[uid][slot] - ld_time_map[uid][MR_ICNT_PUSHED]; + assert( our_latency == latency); + } else if (slot <= MR_2SH_FQ_POP ) { + abort(); + } + } + void check_st_update(unsigned int uid,unsigned int slot, long int latency) { + if ( st_time_map.find( uid )!=st_time_map.end() ) { + int our_latency = st_time_map[uid][slot] - st_time_map[uid][MR_ICNT_PUSHED]; + assert( our_latency == latency); + } else { + abort(); + } + } +private: + void calculate_ld_dist(void) { + unsigned i,first; + long int last_update,diff; + int finished_count=0; + ld_time_dist.clear(); + ld_time_dist.resize(ld_vector_size); + std::map< unsigned int, std::vector<long int> >::iterator iter, iter_temp; + iter =ld_time_map.begin() ; + while (iter != ld_time_map.end()) { + last_update=0; + first=-1; + if (!iter->second[MR_WRITEBACK]) { + //this request is not done yet skip it! + ++iter; + continue; + } + while ( !last_update ) { + first++; + assert( first < iter->second.size() ); + last_update = iter->second[first]; + } + + for ( i=first;i<ld_vector_size;i++ ) { + diff = iter->second[i] - last_update; + if ( diff>0 ) { + ld_time_dist[i]+=diff; + last_update = iter->second[i]; + } + } + iter_temp = iter; + iter++; + ld_time_map.erase(iter_temp); + finished_count++; + } + if ( finished_count ) { + for ( i=0;i<ld_vector_size;i++ ) { + overal_ld_time_dist[i] = (overal_ld_time_dist[i]*overal_ld_count + ld_time_dist[i]) / (overal_ld_count + finished_count); + } + overal_ld_count += finished_count; + for ( i=0;i<ld_vector_size;i++ ) { + ld_time_dist[i]/=finished_count; + } + } + } + + void calculate_st_dist(void) { + unsigned i,first; + long int last_update,diff; + int finished_count=0; + st_time_dist.clear(); + st_time_dist.resize(st_vector_size); + std::map< unsigned int, std::vector<long int> >::iterator iter,iter_temp; + iter =st_time_map.begin() ; + while ( iter != st_time_map.end() ) { + last_update=0; + first=-1; + if (!iter->second[MR_2SH_ICNT_PUSHED]) { + //this request is not done yet skip it! + ++iter; + continue; + } + while ( !last_update ) { + first++; + assert( first < iter->second.size() ); + last_update = iter->second[first]; + } + + for ( i=first;i<st_vector_size;i++ ) { + diff = iter->second[i] - last_update; + if ( diff>0 ) { + st_time_dist[i]+=diff; + last_update = iter->second[i]; + } + } + iter_temp = iter; + iter++; + st_time_map.erase(iter_temp); + finished_count++; + } + if ( finished_count ) { + for ( i=0;i<st_vector_size;i++ ) { + overal_st_time_dist[i] = (overal_st_time_dist[i]*overal_st_count + st_time_dist[i]) / (overal_st_count + finished_count); + } + overal_st_count += finished_count; + for ( i=0;i<st_vector_size;i++ ) { + st_time_dist[i]/=finished_count; + } + } + } + +public: + void clear_time_map_vectors(void) { + ld_time_map.clear(); + st_time_map.clear(); + } + void print_all_ld(void) { + unsigned i; + std::map< unsigned int, std::vector<long int> >::iterator iter; + for ( iter =ld_time_map.begin() ; iter != ld_time_map.end(); ++iter ) { + std::cout<<"ld_uid"<<iter->first; + for ( i=0;i<ld_vector_size;i++ ) { + std::cout<<" "<<iter->second[i]; + } + std::cout<< std::endl; + } + } + + void print_all_st(void) { + unsigned i; + std::map< unsigned int, std::vector<long int> >::iterator iter; + + for ( iter =st_time_map.begin() ; iter != st_time_map.end(); ++iter ) { + std::cout<<"st_uid"<<iter->first; + for ( i=0;i<st_vector_size;i++ ) { + std::cout<<" "<<iter->second[i]; + } + std::cout<<std::endl; + } + } + + void calculate_dist() { + calculate_ld_dist(); + calculate_st_dist(); + } + void print_dist(void) { + unsigned i; + calculate_dist(); + std::cout << "LD_mem_lat_dist " ; + for ( i=0;i<ld_vector_size;i++ ) { + std::cout <<" "<<(int)overal_ld_time_dist[i]; + } + std::cout << std::endl; + std::cout << "ST_mem_lat_dist " ; + for ( i=0;i<st_vector_size;i++ ) { + std::cout <<" "<<(int)overal_st_time_dist[i]; + } + std::cout << std::endl; + } + void print_to_file(FILE *outfile) { + unsigned i; + calculate_dist(); + fprintf (outfile,"LDmemlatdist:") ; + for ( i=0;i<ld_vector_size;i++ ) { + fprintf (outfile," %d", (int)ld_time_dist[i]); + } + fprintf (outfile,"\n") ; + fprintf (outfile,"STmemlatdist:") ; + for ( i=0;i<st_vector_size;i++ ) { + fprintf (outfile," %d", (int)st_time_dist[i]); + } + fprintf (outfile,"\n") ; + } + void print_to_gzfile(gzFile outfile) { + unsigned i; + calculate_dist(); + gzprintf (outfile,"LDmemlatdist:") ; + for ( i=0;i<ld_vector_size;i++ ) { + gzprintf (outfile," %d", (int)ld_time_dist[i]); + } + gzprintf (outfile,"\n") ; + gzprintf (outfile,"STmemlatdist:") ; + for ( i=0;i<st_vector_size;i++ ) { + gzprintf (outfile," %d", (int)st_time_dist[i]); + } + gzprintf (outfile,"\n") ; + } +}; + +my_time_vector* g_my_time_vector; + +void time_vector_create(int ld_size,int st_size) { + g_my_time_vector = new my_time_vector(ld_size,st_size); +} + + +void time_vector_print(void) { + g_my_time_vector->print_dist(); +} + + +void time_vector_print_interval2file(FILE *outfile) { + g_my_time_vector->print_to_file(outfile); +} + + +void time_vector_print_interval2gzfile(gzFile outfile) { + g_my_time_vector->print_to_gzfile(outfile); +} + +#include "../gpgpu-sim/mem_fetch.h" + +void time_vector_update(unsigned int uid,int slot ,long int cycle,int type) { + if ( (type == RD_REQ) || (type == REPLY_DATA) ) { + g_my_time_vector->update_ld( uid, slot,cycle); + } else if ( type == WT_REQ ) { + g_my_time_vector->update_st( uid, slot,cycle); + } else { + abort(); + } +} + +void check_time_vector_update(unsigned int uid,int slot ,long int latency,int type) +{ + if ( (type == RD_REQ) || (type == REPLY_DATA) ) { + g_my_time_vector->check_ld_update( uid, slot, latency ); + } else if ( type == WT_REQ ) { + g_my_time_vector->check_st_update( uid, slot, latency ); + } else { + abort(); + } +} diff --git a/src/gpgpu-sim/warp_tracker.cc b/src/gpgpu-sim/warp_tracker.cc new file mode 100644 index 0000000..19f0e12 --- /dev/null +++ b/src/gpgpu-sim/warp_tracker.cc @@ -0,0 +1,471 @@ +/* + * waro_tracker.cc + * + * Copyright (c) 2009 by Tor M. Aamodt, Wilson W. L. Fung, Ali Bakhoda and the + * University of British Columbia + * Vancouver, BC V6T 1Z4 + * All Rights Reserved. + * + * THIS IS A LEGAL DOCUMENT BY DOWNLOADING GPGPU-SIM, YOU ARE AGREEING TO THESE + * TERMS AND CONDITIONS. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * NOTE: The files libcuda/cuda_runtime_api.c and src/cuda-sim/cuda-math.h + * are derived from the CUDA Toolset available from http://www.nvidia.com/cuda + * (property of NVIDIA). The files benchmarks/BlackScholes/ and + * benchmarks/template/ are derived from the CUDA SDK available from + * http://www.nvidia.com/cuda (also property of NVIDIA). The files from + * src/intersim/ are derived from Booksim (a simulator provided with the + * textbook "Principles and Practices of Interconnection Networks" available + * from http://cva.stanford.edu/books/ppin/). As such, those files are bound by + * the corresponding legal terms and conditions set forth separately (original + * copyright notices are left in files from these sources and where we have + * modified a file our copyright notice appears before the original copyright + * notice). + * + * Using this version of GPGPU-Sim requires a complete installation of CUDA + * which is distributed seperately by NVIDIA under separate terms and + * conditions. To use this version of GPGPU-Sim with OpenCL requires a + * recent version of NVIDIA's drivers which support OpenCL. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the University of British Columbia nor the names of + * its contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * 4. This version of GPGPU-SIM is distributed freely for non-commercial use only. + * + * 5. No nonprofit user may place any restrictions on the use of this software, + * including as modified by the user, by any other authorized user. + * + * 6. GPGPU-SIM was developed primarily by Tor M. Aamodt, Wilson W. L. Fung, + * Ali Bakhoda, George L. Yuan, at the University of British Columbia, + * Vancouver, BC V6T 1Z4 + */ + +#include "warp_tracker.h" + +using namespace std; + +extern unsigned int warp_size; +extern unsigned int gpu_n_shader; +extern unsigned int gpu_n_thread_per_shader; + +#include <set> + +class warp_tracker { +public: + + int *tid; // the threads in this warp + int n_thd; // total number of threads in this warp + int n_notavail; // number of threads still not available + shader_core_ctx_t *shd; // reference to shader core + + warp_tracker () { + tid = new int[warp_size]; + memset(tid, -1, sizeof(int)*warp_size); + n_thd = 0; + n_notavail = 0; + shd = NULL; + } + warp_tracker( int *tid, shader_core_ctx_t * shd ) { + this->tid = new int[warp_size]; + memcpy(this->tid, tid, sizeof(int)*warp_size); + this->n_thd = 0; + this->n_notavail = 0; + for (unsigned i=0; i<warp_size; i++) { + if (this->tid[i] >= 0) { + this->n_thd++; + } + } + this->n_notavail = this->n_thd; + this->shd = shd; + } + ~warp_tracker () { + delete[] tid; + } + + // set the warp to be consist of the given threads + void set_warp ( int *tid, shader_core_ctx_t *shd) { + memcpy(this->tid, tid, sizeof(int)*warp_size); + this->n_thd = 0; + this->n_notavail = 0; + for (unsigned i=0; i<warp_size; i++) { + if (this->tid[i] >= 0) { + this->n_thd++; + } + } + this->n_notavail = this->n_thd; + this->shd = shd; + } + + // signal that this thread is available for fetch + // if all threads in the warp are available, change all their status + // and return true + bool avail_thd ( int tid_in ) { + n_notavail--; + if (n_notavail) { + return false; + } else { + + int thd_unlocked = 0; + if (shd->model == POST_DOMINATOR || shd->model == NO_RECONVERGE) { + thd_unlocked = 1; + } else { + // unlock the threads here if scheduler is not PDOM or NO-RECONV + for (unsigned i=0; i<warp_size; i++) { + if (this->tid[i] >= 0) { + shd->thread[tid[i]].avail4fetch++; + assert(shd->thread[tid[i]].avail4fetch <= 1); + assert( shd->warp[tid[i]/warp_size].n_avail4fetch < warp_size ); + shd->warp[tid[i]/warp_size].n_avail4fetch++; + thd_unlocked = 1; + } + } + } + if (shd->using_commit_queue && thd_unlocked) { + int *tid_unlocked = alloc_commit_warp(); + memcpy(tid_unlocked, this->tid, sizeof(int)*warp_size); + dq_push(shd->thd_commit_queue,(void*)tid_unlocked); + } + + return true; + } + } + + // a bookkeeping method to allow a warp to be deallocated + // when its threads have finished executing. + bool complete_thd ( int tid_in ) { + n_notavail--; + if (n_notavail) { + return false; + } else { + return true; + } + } +}; + +static warp_tracker ***warp_tracker_map; +static unsigned **g_warp_tracker_map_setl_cycle; +static warp_tracker *warp_tracker_pool = NULL; +static list<warp_tracker*> free_wpt; + +warp_tracker* alloc_warp_tracker( int *tid_in, shader_core_ctx_t *shd ) +{ + assert(!free_wpt.empty()); + warp_tracker* wpt = free_wpt.front(); + free_wpt.pop_front(); + + wpt->set_warp(tid_in, shd); + + return wpt; +} + +void free_warp_tracker(warp_tracker* wpt) +{ + free_wpt.push_back(wpt); +} + +void init_warp_tracker( ) +{ + unsigned int i; + + warp_tracker_map = (warp_tracker ***)calloc(gpu_n_shader, sizeof(warp_tracker **)); + g_warp_tracker_map_setl_cycle = (unsigned**)calloc(gpu_n_shader, sizeof(unsigned*)); + for (i=0; i<gpu_n_shader; i++) { + warp_tracker_map[i] = (warp_tracker **)calloc(gpu_n_thread_per_shader, sizeof(warp_tracker *)); + g_warp_tracker_map_setl_cycle[i] = (unsigned*)calloc(gpu_n_thread_per_shader, sizeof(unsigned)); + } + + // max possible number of warps is just when each thread has its own warp + warp_tracker_pool = new warp_tracker[gpu_n_shader * gpu_n_thread_per_shader]; + printf("%d %d %d %d\n", warp_size, gpu_n_shader, gpu_n_thread_per_shader, + warp_size * gpu_n_shader * gpu_n_thread_per_shader); + for (i=0; i<gpu_n_shader*gpu_n_thread_per_shader; i++) { + free_wpt.push_back(&(warp_tracker_pool[i])); + } + printf("%zd\n", free_wpt.size()); +} + +extern signed long long gpu_tot_sim_cycle; +extern signed long long gpu_sim_cycle; + +void wpt_register_warp( int *tid_in, shader_core_ctx_t *shd ) +{ + int sid = shd->sid; + unsigned i; + int n_thd = 0; + for (i=0; i<warp_size; i++) { + if (tid_in[i] >= 0) n_thd++; + } + + if (!n_thd) return; + + warp_tracker *wpt = alloc_warp_tracker(tid_in, shd); + + // assign the new warp_tracker to warp_tracker_map + for (i=0; i<warp_size; i++) { + if (tid_in[i] >= 0) { + assert( warp_tracker_map[sid][tid_in[i]] == NULL ); + warp_tracker_map[sid][tid_in[i]] = wpt; + g_warp_tracker_map_setl_cycle[sid][tid_in[i]] = gpu_tot_sim_cycle + gpu_sim_cycle; + } + } +} + +int wpt_signal_avail( int tid, shader_core_ctx_t *shd ) +{ + int sid = shd->sid; + warp_tracker *wpt = warp_tracker_map[sid][tid]; + assert(wpt != NULL); + + + // signal the warp tracker + if (wpt->avail_thd(tid)) { + // if the warp is ready to be fetched again, remove this warp_tracker + for (unsigned i=0; i<warp_size; i++) { + if (wpt->tid[i] >= 0) { + warp_tracker_map[sid][wpt->tid[i]] = NULL; + g_warp_tracker_map_setl_cycle[sid][wpt->tid[i]] = gpu_tot_sim_cycle + gpu_sim_cycle; + } + } + + free_warp_tracker( wpt ); + + return 1; + } else { + return 0; + } +} + +void register_cta_thread_exit(shader_core_ctx_t *shader, int cta_num ); + +int wpt_signal_complete( int tid, shader_core_ctx_t *shd ) +{ + int sid = shd->sid; + warp_tracker *wpt = warp_tracker_map[sid][tid]; + assert(wpt != NULL); + + // signal the warp tracker + if (wpt->complete_thd(tid)) { + // if the warp has completed execution, remove this warp_tracker + int warp_mask = 0; + for (unsigned i=0; i<warp_size; i++) { + if (wpt->tid[i] >= 0) { + register_cta_thread_exit(shd, wpt->tid[i] ); + warp_tracker_map[sid][wpt->tid[i]] = NULL; + g_warp_tracker_map_setl_cycle[sid][wpt->tid[i]] = gpu_tot_sim_cycle + gpu_sim_cycle; + warp_mask |= (1 << i); + } + } + + free_warp_tracker( wpt ); + + return warp_mask; + } else { + return 0; + } +} + +//------------------------------------------------------------------------------------ + +class thread_pc_tracker_class { +public: + address_type *thd_pc; // tracks the pc of each thread + map<address_type, unsigned> pc_count; + unsigned acc_pc_count; + int simd_width; + static map<unsigned, unsigned> histogram; + + thread_pc_tracker_class( ) { + this->acc_pc_count = 0; + this->simd_width = 0; + this->thd_pc = NULL; + } + + thread_pc_tracker_class(int simd_width, int thread_count) { + this->acc_pc_count = 0; + this->simd_width = simd_width; + this->thd_pc = new address_type[thread_count]; + memset( this->thd_pc, 0, sizeof(address_type)*thread_count); + } + + void add_threads( int *tid, address_type pc ) { + for (int i=0; i<simd_width; i++) { + if (tid[i] != -1) { + pc_count[pc] += 1; // automatically create a new entry if not exist + thd_pc[tid[i]] = pc; + } + } + } + + void sub_threads( int *tid ) { + for (int i=0; i<simd_width; i++) { + if (tid[i] != -1) { + address_type pc = thd_pc[tid[i]]; + if (pc == 0) break; + pc_count[pc] -= 1; + assert((int)pc_count[pc] >= 0); + if (pc_count[pc] == 0) pc_count.erase(pc); // manually erasing entries with 0 count + } + } + } + + void update_acc_count( ) { + acc_pc_count += pc_count.size(); + histogram[pc_count.size()] += 1; + } + + void set_threads_pc ( int *tid, address_type pc ) { + sub_threads(tid); + add_threads(tid, pc); + update_acc_count( ); + } + + unsigned get_acc_pc_count( ) { return acc_pc_count;} + + unsigned count( ) { return pc_count.size();} + + static void histo_print( FILE* fout ) { + if (histogram.empty()) return; // do not output anything if the histogram is empty + map<unsigned, unsigned>::iterator i; + fprintf(fout, "Thread PC Histogram: "); + for (i = histogram.begin(); i != histogram.end(); i++) { + fprintf(fout, "%d:%d ", i->first, i->second); + } + fprintf(fout, "\n"); + } +}; + +map<unsigned, unsigned> thread_pc_tracker_class::histogram; + +thread_pc_tracker_class *thread_pc_tracker = NULL; + +void print_thread_pc_histogram( FILE *fout ) +{ + thread_pc_tracker_class::histo_print(fout); +} + +void print_thread_pc( FILE *fout ) +{ + fprintf(fout, "SHD_PC_C: "); + for (unsigned i=0; i<gpu_n_shader; i++) { + fprintf(fout, "%d ", thread_pc_tracker[i].get_acc_pc_count() ); + } + fprintf(fout, "\n"); +} + +void track_thread_pc( int shader_id, int *tid, address_type pc ) +{ + if (!thread_pc_tracker) { + thread_pc_tracker = new thread_pc_tracker_class[gpu_n_shader]; + for (unsigned i=0; i<gpu_n_shader; i++) { + thread_pc_tracker[i] = thread_pc_tracker_class(warp_size, gpu_n_thread_per_shader); + } + } + thread_pc_tracker[shader_id].set_threads_pc( tid, pc ); +} + +//------------------------------------------------------------------------------------ + +static int *commit_warp_pool = NULL; +static queue<int*> free_commit_warp_q; + +void init_commit_warp( ) +{ + unsigned int num_warp = warp_size * gpu_n_shader * gpu_n_thread_per_shader; + commit_warp_pool = new int[num_warp]; + for (unsigned int i=0; i<num_warp; i+=warp_size) { + free_commit_warp_q.push(&(commit_warp_pool[i])); + } +} + +int* alloc_commit_warp( ) +{ + if (!commit_warp_pool) { + init_commit_warp( ); + } + + assert(!free_commit_warp_q.empty()); + int *new_commit_warp = free_commit_warp_q.front(); + free_commit_warp_q.pop(); + + return new_commit_warp; +} + +void free_commit_warp( int *commit_warp ) +{ + free_commit_warp_q.push(commit_warp); +} + + +extern int pipe_simd_width; + +// uncomment to enable checking for warp consistency +// #define CHECK_WARP_CONSISTENCY + +void check_stage_pcs( shader_core_ctx_t *shader, unsigned stage ) +{ +#ifdef CHECK_WARP_CONSISTENCY + address_type inst_pc = (address_type)-1; + unsigned tid; + if( shader->model == MIMD ) + return; + + std::set<unsigned> tids; + + for ( int i = 0; i < pipe_simd_width; i++) { + if (shader->pipeline_reg[i][stage].hw_thread_id == -1 ) + continue; + if ( inst_pc == (address_type)-1 ) + inst_pc = shader->pipeline_reg[i][stage].pc; + tid = shader->pipeline_reg[i][stage].hw_thread_id; + assert( tids.find(tid) == tids.end() ); + tids.insert(tid); + assert( inst_pc == shader->pipeline_reg[i][stage].pc ); + } +#endif +} + +void check_pm_stage_pcs( shader_core_ctx_t *shader, unsigned stage ) +{ +#ifdef CHECK_WARP_CONSISTENCY + address_type inst_pc = (address_type)-1; + unsigned tid; + if( shader->model == MIMD ) + return; + + std::set<unsigned> tids; + + for (int i = 0; i < pipe_simd_width; i++) { + if (shader->pre_mem_pipeline[i][stage].hw_thread_id == -1 ) + continue; + if ( inst_pc == (address_type)-1 ) + inst_pc = shader->pre_mem_pipeline[i][stage].pc; + tid = shader->pre_mem_pipeline[i][stage].hw_thread_id; + assert( tids.find(tid) == tids.end() ); + tids.insert(tid); + assert( inst_pc == shader->pre_mem_pipeline[i][stage].pc ); + } +#endif +} diff --git a/src/gpgpu-sim/warp_tracker.h b/src/gpgpu-sim/warp_tracker.h new file mode 100644 index 0000000..a4faa09 --- /dev/null +++ b/src/gpgpu-sim/warp_tracker.h @@ -0,0 +1,100 @@ +/* + * waro_tracker.h + * + * Copyright (c) 2009 by Tor M. Aamodt, Wilson W. L. Fung, Ali Bakhoda and the + * University of British Columbia + * Vancouver, BC V6T 1Z4 + * All Rights Reserved. + * + * THIS IS A LEGAL DOCUMENT BY DOWNLOADING GPGPU-SIM, YOU ARE AGREEING TO THESE + * TERMS AND CONDITIONS. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * NOTE: The files libcuda/cuda_runtime_api.c and src/cuda-sim/cuda-math.h + * are derived from the CUDA Toolset available from http://www.nvidia.com/cuda + * (property of NVIDIA). The files benchmarks/BlackScholes/ and + * benchmarks/template/ are derived from the CUDA SDK available from + * http://www.nvidia.com/cuda (also property of NVIDIA). The files from + * src/intersim/ are derived from Booksim (a simulator provided with the + * textbook "Principles and Practices of Interconnection Networks" available + * from http://cva.stanford.edu/books/ppin/). As such, those files are bound by + * the corresponding legal terms and conditions set forth separately (original + * copyright notices are left in files from these sources and where we have + * modified a file our copyright notice appears before the original copyright + * notice). + * + * Using this version of GPGPU-Sim requires a complete installation of CUDA + * which is distributed seperately by NVIDIA under separate terms and + * conditions. To use this version of GPGPU-Sim with OpenCL requires a + * recent version of NVIDIA's drivers which support OpenCL. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the University of British Columbia nor the names of + * its contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * 4. This version of GPGPU-SIM is distributed freely for non-commercial use only. + * + * 5. No nonprofit user may place any restrictions on the use of this software, + * including as modified by the user, by any other authorized user. + * + * 6. GPGPU-SIM was developed primarily by Tor M. Aamodt, Wilson W. L. Fung, + * Ali Bakhoda, George L. Yuan, at the University of British Columbia, + * Vancouver, BC V6T 1Z4 + */ + +#ifndef warp_tracker_h_INCLUDED +#define warp_tracker_h_INCLUDED + +#ifdef __cplusplus + + #include <cstdio> + #include <cstdlib> + #include <cstring> + #include <cassert> + #include <map> + #include <list> + #include <deque> + #include <queue> + +#endif + +#include "../util.h" +#include "shader.h" + +void init_warp_tracker( ); + +void wpt_register_warp( int *tid_in, shader_core_ctx_t *shd ); + +int wpt_signal_avail( int tid, shader_core_ctx_t *shd ); + +int wpt_signal_complete( int tid, shader_core_ctx_t *shd ); + +void print_thread_pc_histogram( FILE *fout ); +void print_thread_pc( FILE *fout ); +void track_thread_pc( int shader_id, int *tid, address_type pc ); + +int* alloc_commit_warp( ); +void free_commit_warp( int *commit_warp ); + +#endif diff --git a/src/gpgpusim_entrypoint.cc b/src/gpgpusim_entrypoint.cc new file mode 100644 index 0000000..b6770ea --- /dev/null +++ b/src/gpgpusim_entrypoint.cc @@ -0,0 +1,144 @@ +/* + * gpgpusim_entrypoint.c + * + * Copyright © 2009 by Tor M. Aamodt, Wilson W. L. Fung, Ali Bakhoda, + * George L. Yuan and the University of British Columbia, Vancouver, + * BC V6T 1Z4, All Rights Reserved. + * + * THIS IS A LEGAL DOCUMENT BY DOWNLOADING GPGPU-SIM, YOU ARE AGREEING TO THESE + * TERMS AND CONDITIONS. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * NOTE: The files libcuda/cuda_runtime_api.c and src/cuda-sim/cuda-math.h + * are derived from the CUDA Toolset available from http://www.nvidia.com/cuda + * (property of NVIDIA). The files benchmarks/BlackScholes/ and + * benchmarks/template/ are derived from the CUDA SDK available from + * http://www.nvidia.com/cuda (also property of NVIDIA). The files from + * src/intersim/ are derived from Booksim (a simulator provided with the + * textbook "Principles and Practices of Interconnection Networks" available + * from http://cva.stanford.edu/books/ppin/). As such, those files are bound by + * the corresponding legal terms and conditions set forth separately (original + * copyright notices are left in files from these sources and where we have + * modified a file our copyright notice appears before the original copyright + * notice). + * + * Using this version of GPGPU-Sim requires a complete installation of CUDA + * which is distributed seperately by NVIDIA under separate terms and + * conditions. To use this version of GPGPU-Sim with OpenCL requires a + * recent version of NVIDIA's drivers which support OpenCL. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the University of British Columbia nor the names of + * its contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * 4. This version of GPGPU-SIM is distributed freely for non-commercial use only. + * + * 5. No nonprofit user may place any restrictions on the use of this software, + * including as modified by the user, by any other authorized user. + * + * 6. GPGPU-SIM was developed primarily by Tor M. Aamodt, Wilson W. L. Fung, + * Ali Bakhoda, George L. Yuan, at the University of British Columbia, + * Vancouver, BC V6T 1Z4 + */ + +#include <stdio.h> +#include <time.h> + +#include "option_parser.h" +//#include "gpgpu-sim/gpu-sim.h" + +#define MAX(a,b) (((a)>(b))?(a):(b)) + +struct dim3 { + unsigned int x, y, z; +}; + +struct gpgpu_ptx_sim_arg *grid_params; +int g_grid_num=0; +int g_argc = 3; +const char *g_argv[] = {"", "-config","gpgpusim.config"}; + +unsigned int run_gpu_sim(int grid_num); +void gpgpu_ptx_sim_init_grid(const char *kernel_key,struct gpgpu_ptx_sim_arg *args, struct dim3 gridDim, struct dim3 blockDim ); + +int g_network_mode = 0; +char* g_network_config_filename; +option_parser_t opp; +extern void read_environment_variables(); +extern void print_splash(); + +extern void gpu_reg_options(option_parser_t opp); +extern void init_gpu(); + +time_t simulation_starttime; + +void gpgpu_ptx_sim_init_perf() +{ + print_splash(); + read_environment_variables(); + opp = option_parser_create(); + option_parser_register(opp, "-network_mode", OPT_INT32, &g_network_mode, "Interconnection network mode", "1"); + option_parser_register(opp, "-inter_config_file", OPT_CSTR, &g_network_config_filename, "Interconnection network config file", "mesh"); + gpu_reg_options(opp); // register GPU microrachitecture options + option_parser_cmdline(opp, g_argc, g_argv); // parse configuration options + + srand(1); + + fprintf(stdout, "GPGPU-Sim: Configuration options:\n\n"); + option_parser_print(opp, stdout); + init_gpu(); // initialize the GPU microarchitecture model + fprintf(stdout, "GPU performance model initialization complete.\n"); + + simulation_starttime = time((time_t *)NULL); +} + +extern unsigned long long gpu_tot_sim_insn; +extern unsigned long long gpu_tot_sim_cycle; + +int gpgpu_ptx_sim_main_perf( const char *kernel_key, struct dim3 gridDim, struct dim3 blockDim, struct gpgpu_ptx_sim_arg *grid_params ) +{ + time_t current_time, difference, d, h, m, s; + gpgpu_ptx_sim_init_grid(kernel_key,grid_params,gridDim,blockDim); + + run_gpu_sim(g_grid_num); // run a CUDA grid on the GPU microarchitecture simulator + + g_grid_num++; + + current_time = time((time_t *)NULL); + difference = MAX(current_time - simulation_starttime, 1); + + d = difference/(3600*24); + h = difference/3600 - 24*d; + m = difference/60 - 60*(h + 24*d); + s = difference - 60*(m + 60*(h + 24*d)); + + fflush(stderr); + printf("\n\ngpgpu_simulation_time = %u days, %u hrs, %u min, %u sec (%u sec)\n", + (unsigned)d, (unsigned)h, (unsigned)m, (unsigned)s, (unsigned)difference ); + printf("gpgpu_simulation_rate = %u (inst/sec)\n", (unsigned)(gpu_tot_sim_insn / difference) ); + printf("gpgpu_simulation_rate = %u (cycle/sec)\n", (unsigned)(gpu_tot_sim_cycle / difference) ); + fflush(stdout); + + return 0; +} 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 <iostream>
+#include <assert.h>
+
+#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<sRequest> [_inputs];
+ _out_req = new list<sRequest> [_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<sRequest>::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<sRequest>::iterator insert_point;
+ list<int>::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<sRequest>::iterator erase_point;
+ list<int>::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<sRequest>::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 <string>
+#include <list>
+
+#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<int> _in_occ;
+ list<int> _out_occ;
+
+ list<sRequest> *_in_req;
+ list<sRequest> *_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 <assert.h>
+
+#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<sRequest>::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<sRequest>::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<sRequest>::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 <list>
+
+#include "module.hpp"
+#include "config_utils.hpp"
+
+class Arbiter : public Module {
+protected:
+ const int _inputs;
+
+ struct sRequest {
+ int in;
+ int label;
+ int pri;
+ };
+
+ list<sRequest> _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 <string>
+
+#ifdef _WIN32_
+ #pragma warning (disable: 4786)
+ #include <ostream>
+#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 <iostream>
+#include <stdlib.h>
+#include <assert.h>
+
+#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 <stdlib.h>
+#include <string>
+#include <cstring>
+
+#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 <string>
+#include "config_utils.hpp"
+
+int configlex(void);
+void configerror(string msg);
+
+%}
+
+%union {
+ char *name;
+ unsigned int num;
+ double fnum;
+}
+
+%token <name> STR
+%token <num> NUM
+%token <fnum> 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 <iostream>
+#include <stdlib.h>
+#include <cstring>
+
+#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<string,char *>::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<string,unsigned int>::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<string,double>::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<string,char *>::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<string,unsigned int>::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<string,double>::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<stdio.h>
+#include<string>
+#include<map>
+
+extern int configparse( );
+
+class Configuration {
+ static Configuration *theConfig;
+ FILE *_config_file;
+
+protected:
+ map<string,char *> _str_map;
+ map<string,unsigned int> _int_map;
+ map<string,double> _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: [email protected]
+% ========================================================================
+% 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 <[email protected]>
+% 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 <char><set><truecase><falsecase>
+%
+\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<x><y><z> with <x> for [eo], <y> form [lcr]
+% and <z> 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 Binary files differnew file mode 100644 index 0000000..c12aa4a --- /dev/null +++ b/src/intersim/doc/manual.pdf 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 <string>
+#include <sstream>
+#include <iostream>
+#include <stdlib.h>
+#include <assert.h>
+
+#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<Flit>( this, "crossbar_pipeline", _outputs,
+ _st_prepare_delay + _st_final_delay );
+
+ _credit_pipe =
+ new PipelineFIFO<Credit>( this, "credit_pipeline", _inputs,
+ _credit_delay );
+
+ _arrival_pipe =
+ new PipelineFIFO<tArrivalEvent>( this, "arrival_pipeline", _inputs,
+ 0 /* FIX THIS EVENTUALLY */);
+
+ // Queues
+
+ _input_buffer = new queue<Flit *> [_inputs];
+ _output_buffer = new queue<Flit *> [_outputs];
+
+ _in_cred_buffer = new queue<Credit *> [_inputs];
+ _out_cred_buffer = new queue<Credit *> [_outputs];
+
+ _arrival_queue = new queue<tArrivalEvent *> [_inputs];
+ _transport_queue = new queue<tTransportEvent *> [_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<tWaiting *> [_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<tWaiting *>::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<tWaiting *>::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 <string>
+#include <queue>
+
+#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<tWaiting *> *_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<Flit> *_crossbar_pipe;
+ PipelineFIFO<Credit> *_credit_pipe;
+
+ queue<Flit *> *_input_buffer;
+ queue<Flit *> *_output_buffer;
+
+ queue<Credit *> *_in_cred_buffer;
+ queue<Credit *> *_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<tArrivalEvent> *_arrival_pipe;
+ queue<tArrivalEvent *> *_arrival_queue;
+ PriorityArbiter **_arrival_arbiter;
+
+ struct tTransportEvent {
+ int input;
+ int src_vc;
+ int dst_vc;
+
+ int id; // debug
+ bool watch; // debug
+ };
+
+ queue<tTransportEvent *> *_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 <iostream>
+
+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 <vector>
+#include <sstream>
+
+#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 <map>
+#include <assert.h>
+#include <cstdlib>
+#include "injection.hpp"
+#include "network.hpp"
+#include "random_utils.hpp"
+#include "misc_utils.hpp"
+
+map<string, tInjectionProcess> 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<string, tInjectionProcess>::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 <string> +#include <stdlib.h> +#include <string.h> +#include <assert.h> +#include <queue> + +#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 <string.h> + +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<Flit *> ** ejection_buf; +vector<int> 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<void *> buf; + queue<bool> 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<void * , vector<void* >, 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; j<memcount ; j++) { + if (memnodes[j]==i) { + memfound=1; + } + } + if (!memfound) { + node_map[k]=i; + k++; + } + } + for (int j = 0; j<memcount ; j++) { + node_map[k]=memnodes[j]; + k++; + } + assert(k==dim*dim); +} + +void display_map(int dim,int count){ + int i=0; + for (i=0;i<count;i++) { + printf("%d ",node_map[i]); + if (i%dim ==0) { + printf("\n"); + } + } +} + +void create_node_map(int n_shader, int n_mem, int size, int use_map) { + node_map = (int*)malloc((size)*sizeof(int)); + if (use_map) { + switch (size) { + case 16 : + { // good for 8 shaders and 8 memory cores + int newmap[] = { + 0, 2, 5, 7, + 8,10,13,15, + 1, 3, 4, 6, //memory nodes + 9,11,12,14 //memory nodes + }; + memcpy (node_map, newmap,16*sizeof(int)); + break; + } + case 64: + { // good for 56 shaders and 8 memory cores + int newmap[] = { + 0, 1, 2, 4, 5, 6, 7, 8, + 9, 10, 11, 12, 13, 14, 16, 18, + 19, 20, 21, 22, 23, 24, 25, 26, + 27, 28, 30, 31, 32, 33, 34, 35, + 37, 38, 39, 40, 41, 42, 43, 44, + 45, 46, 48, 50, 51, 52, 53, 54, + 55, 56, 57, 58, 59, 60, 62, 63, + 3, 15, 17, 29, 36, 47, 49, 61 //memory nodes are in this line + }; + memcpy (node_map, newmap,64*sizeof(int)); + break; + } + case 121: + { // good for 110 shaders and 11 memory cores + int newmap[] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, + 11, 13, 14, 15, 16, 17, 18, 19, 21, 22, 23, + 24, 26, 27, 29, 30, 31, 32, 33, 34, 35, 36, + 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, 56, 58, 59, + 61, 62, 64, 65, 66, 67, 68, 69, 70, 71, 72, + 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, + 84, 85, 86, 87, 88, 89, 90, 91, 93, 94, 96, + 97, 98, 99,101,102,103,104,105,106,107,109, + 110,111,112,113,114,115,116,117,118,119,120, + 12, 20, 25, 28, 57, 60, 63, 92, 95,100,108 //memory nodes are in this line + }; + memcpy (node_map, newmap,121*sizeof(int)); + break; + } + case 36: + { + int memnodes[8]={3,7,10,12,23,25,28,32}; + map_gen(6/*dim*/,8/*memcount*/,memnodes); + break; + } + default: + { + cout<<"WARNING !!! NO MAPPING IMPLEMENTED YET FOR THIS CONFIG"<<endl; + for (int i=0;i<size;i++) { + node_map[i]=i; + } + } + } + } else { // !use_map + for (int i=0;i<size;i++) { + node_map[i]=i; + } + } + reverse_map = (int*)malloc((size)*sizeof(int)); + for (int i = 0; i < size ; i++) { + for (int j = 0; j<size ; j++) { + if (node_map[j]==i) { + reverse_map[i]=j; + break; + } + } + } + printf("nodemap\n"); + display_map((int) sqrt(size),size); + +} + +int fixed_latency(int input, int output) +{ + int latency; + if (perfect_icnt) { + latency = 1; + } else { + int dim = icnt_config.GetInt( "k" ); + int xhops = abs ( input%dim - output%dim); + int yhops = abs ( input/dim - output/dim); + latency = ( (xhops+yhops)*fixed_lat_icnt ); + } + return latency; +} + +//This function gets a mapped node number and tells if it is a shader node or a memory node +static inline bool is_shd(int node) +{ + if (reverse_map[node] < (int) _n_shader) + return true; + else + return false; +} + +static inline bool is_mem(int node) +{ + return !is_shd(node); +} + +//////////////////// +void interconnect_stats() +{ + if (!fixed_lat_icnt) { + for (unsigned i=0; i<net_c;i++) { + cout <<"Traffic "<<i<< " Stat" << endl; + traffic[i]->ShowStats(); + if (icnt_config.GetInt("enable_link_stats")) { + cout << "%=================================" << endl; + cout <<"Traffic "<<i<< "Link utilizations:" << endl; + traffic[i]->_net->Display(); + } + } + } else { + //show max queue sizes + cout<<"Max Fixed Latency ICNT queue size for"<<endl; + for (unsigned i=0;i<(_n_mem + _n_shader);i++) { + cout<<" node[" << i <<"] is "<<max_fixedlat_buf_size[i]; + } + cout<<endl; + } +} + +void icnt_overal_stat() //should be called upon simulation exit to give an overal stat +{ + if (!fixed_lat_icnt) { + for (unsigned i=0; i<net_c;i++) { + traffic[i]->ShowOveralStat(); + } + } +} + +void icnt_init_grid (){ + for (unsigned i=0; i<net_c;i++) { + traffic[i]->IcntInitPerGrid(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: "<<input<<" output: "<<output<<endl; +#endif + + if (fixed_lat_icnt) { + ((mem_fetch_t *) data)->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 <<endl; +#endif + } + +} + +void* interconnect_pop(unsigned int output_node) +{ + int output = node_map[output_node]; +#if DEBUG + cout<<"Call interconnect POP " << output<<endl; +#endif + void* data = NULL; + if (fixed_lat_icnt) { + if (!out_buf_fixedlat_buf[output].empty()) { + if (((mem_fetch_t *)out_buf_fixedlat_buf[output].top())->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;(vc<g_num_vcs) && (data==NULL);vc++) { + if (clock_boundary_buf[output][turn].has_packet()) { + data = clock_boundary_buf[output][turn].pop_packet(); + } + turn++; + if (turn == g_num_vcs) turn = 0; + } + if (data) { + round_robin_turn[output] = turn; + } + } + return data; +} + +extern int MATLAB_OUTPUT ; +extern int DISPLAY_LAT_DIST ; +extern int DISPLAY_HOP_DIST ; +extern int DISPLAY_PAIR_LATENCY ; +extern unsigned int gpu_n_thread_per_shader; +extern char *gpgpu_cache_dl1_opt; +extern int gpgpu_no_dl1; + + +void init_interconnect (char* config_file, + unsigned int n_shader, unsigned int n_mem) +{ + _n_shader = n_shader; + _n_mem = n_mem; + if (! config_file ) { + cout << "Interconnect Requires a configfile" << endl; + exit (-1); + } + icnt_config.Parse( config_file ); + + net_c = icnt_config.GetInt( "network_count" ); + if (net_c==2) { + doub_net = true; + } else if (net_c<1 || net_c>2) { + cout <<net_c<<" Network_count less than 1 or more than 2 not supported."<<endl; + abort(); + } + + g_num_vcs = icnt_config.GetInt( "num_vcs" ); + InitializeRoutingMap( ); + InitializeTrafficMap( ); + InitializeInjectionMap( ); + + RandomSeed( icnt_config.GetInt("seed") ); + + Network **net; + + traffic = new TrafficManager *[net_c]; + net = new Network *[net_c]; + + for (unsigned i=0;i<net_c;i++) { + string topo; + + icnt_config.GetStr( "topology", topo ); + + if ( topo == "torus" ) { + net[i] = new KNCube( icnt_config, true ); + } else if ( topo =="mesh" ) { + net[i] = new KNCube( icnt_config, false ); + } else if ( topo == "fly" ) { + net[i] = new KNFly( icnt_config ); + } else if ( topo == "single" ) { + net[i] = new SingleNet( icnt_config ); + } else { + cerr << "Unknown topology " << topo << endl; + exit(-1); + } + + if ( icnt_config.GetInt( "link_failures" ) ) { + net[i]->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<net_c;i++) { + traffic[i]->_FirstStep(); + } +} + +void advance_interconnect () +{ + if (!fixed_lat_icnt) { + for (unsigned i=0;i<net_c;i++) { + traffic[i]->_Step( ); + } + } +} + +unsigned interconnect_busy() +{ + unsigned i,j; + for(i=0; i<net_c;i++) { + if (traffic[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<g_num_vcs;j++ ) { + if ( !ejection_buf[i][j].empty() || clock_boundary_buf[i][j].has_packet() ) { + return 1; + } + } + } + return 0; +} + +//create buffers for src_n nodes +void create_buf(int src_n,int warp_n,int vc_n) +{ + int i; + ejection_buf = new queue<Flit *>* [src_n]; + clock_boundary_buf = new boundary_buf* [src_n]; + round_robin_turn.resize( src_n ); + for (i=0;i<src_n;i++){ + ejection_buf[i]= new queue<Flit *>[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<void * , vector<void* >, mycomparison> [src_n]; + max_fixedlat_buf_size = new unsigned int [src_n]; + for (i=0;i<src_n;i++) { + max_fixedlat_buf_size[i]=0; + } + } + +} + +void write_out_buf(int output, Flit *flit) { + int vc = flit->vc; + 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; vc<g_num_vcs;vc++) { + if ( !ejection_buf[output][vc].empty() && !clock_boundary_buf[output][vc].is_full() ) { + flit = ejection_buf[output][vc].front(); + ejection_buf[output][vc].pop(); + clock_boundary_buf[output][vc].push_flit_data( flit->data, 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 " <<nc<<" push out flit to (mapped)" << output <<endl; +#endif + } + } +} + +void time_vector_update(unsigned int uid, int slot , long int cycle, int type); +extern unsigned long long gpu_tot_sim_cycle; +void time_vector_update_icnt_injected(void* data, int input) { + mem_fetch_t* mf = (mem_fetch_t*) data; + unsigned int uid = mf->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 <string>
+#include <sstream>
+#include <iostream>
+#include <stdlib.h>
+#include <assert.h>
+
+#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<Flit>( this, "crossbar_pipeline", _outputs*_output_speedup,
+ _st_prepare_delay + _st_final_delay );
+
+ _credit_pipe =
+ new PipelineFIFO<Credit>( this, "credit_pipeline", _inputs,
+ _credit_delay );
+
+ // Input and output queues
+
+ _input_buffer = new queue<Flit *> [_inputs];
+ _output_buffer = new queue<pair<Flit *, int> > [_outputs];
+
+ _in_cred_buffer = new queue<Credit *> [_inputs];
+ _out_cred_buffer = new queue<Credit *> [_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 <string>
+#include <queue>
+
+#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<Flit> *_crossbar_pipe;
+ PipelineFIFO<Credit> *_credit_pipe;
+
+ queue<Flit *> *_input_buffer;
+ queue<pair<Flit *,int> > *_output_buffer;
+
+ queue<Credit *> *_in_cred_buffer;
+ queue<Credit *> *_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 <iostream>
+
+#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<sRequest>::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 <vector>
+#include <sstream>
+
+#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 <iostream>
+
+#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 <iostream>
+
+#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 <iostream>
+#include <stdlib.h>
+
+#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<Module *>::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 <string>
+#include <vector>
+
+class Module {
+protected:
+ string _name;
+ string _fullname;
+
+ vector<Module *> _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 <assert.h>
+
+#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 <vector>
+
+#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 <assert.h>
+
+#include "outputset.hpp"
+
+OutputSet::OutputSet( int num_outputs )
+{
+ _num_outputs = num_outputs;
+
+ _outputs = new list<sSetElement> [_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<sSetElement>::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<sSetElement>::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<sSetElement>::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 <queue>
+#include <list>
+
+class OutputSet {
+ int _num_outputs;
+
+ struct sSetElement {
+ int vc_start;
+ int vc_end;
+ int pri;
+ };
+
+ list<sSetElement> *_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 <iostream>
+
+#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 T> 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<class T> PipelineFIFO<T>::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<class T> PipelineFIFO<T>::~PipelineFIFO( )
+{
+ for ( int l = 0; l < _lanes; ++l ) {
+ delete [] _data[l];
+ }
+ delete [] _data;
+}
+
+template<class T> void PipelineFIFO<T>::Write( T* val, int lane )
+{
+ _data[lane][_pipe_ptr] = val;
+}
+
+template<class T> void PipelineFIFO<T>::WriteAll( T* val )
+{
+ for ( int l = 0; l < _lanes; ++l ) {
+ _data[l][_pipe_ptr] = val;
+ }
+}
+
+template<class T> T* PipelineFIFO<T>::Read( int lane )
+{
+ return _data[lane][_pipe_ptr];
+}
+
+template<class T> void PipelineFIFO<T>::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
+ * (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<KK;j++) aa[j]=ran_x[j];
+ for (;j<n;j++) aa[j]=mod_diff(aa[j-KK],aa[j-LL]);
+ for (i=0;i<LL;i++,j++) ran_x[i]=mod_diff(aa[j-KK],aa[j-LL]);
+ for (;i<KK;i++,j++) ran_x[i]=mod_diff(aa[j-KK],ran_x[i-LL]);
+}
+
+/* the following routines are from exercise 3.6--15 */
+/* after calling ran_start, get new randoms by, e.g., "x=ran_arr_next()" */
+
+#define QUALITY 1009 /* recommended quality level for high-res use */
+long ran_arr_buf[QUALITY];
+long ran_arr_dummy=-1, ran_arr_started=-1;
+long *ran_arr_ptr=&ran_arr_dummy; /* the next random number, or -1 */
+
+#define TT 70 /* guaranteed separation between streams */
+#define is_odd(x) ((x)&1) /* units bit of x */
+
+#ifdef __STDC__
+void ran_start(long seed)
+#else
+void ran_start(seed) /* do this before using ran_array */
+long seed; /* selector for different streams */
+#endif
+{
+ register int t,j;
+ long x[KK+KK-1]; /* the preparation buffer */
+ register long ss=(seed+2)&(MM-2);
+ for (j=0;j<KK;j++) {
+ x[j]=ss; /* bootstrap the buffer */
+ ss<<=1; if (ss>=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<LL;j++) ran_x[j+KK-LL]=x[j];
+ for (;j<KK;j++) ran_x[j-LL]=x[j];
+ for (j=0;j<10;j++) ran_array(x,KK+KK-1); /* warm things up */
+ ran_arr_ptr=&ran_arr_started;
+}
+
+#define ran_arr_next() (*ran_arr_ptr>=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 <stdio.h>
+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
+ * (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<KK;j++) aa[j]=ran_u[j];
+ for (;j<n;j++) aa[j]=mod_sum(aa[j-KK],aa[j-LL]);
+ for (i=0;i<LL;i++,j++) ran_u[i]=mod_sum(aa[j-KK],aa[j-LL]);
+ for (;i<KK;i++,j++) ran_u[i]=mod_sum(aa[j-KK],ran_u[i-LL]);
+}
+
+/* the following routines are adapted from exercise 3.6--15 */
+/* after calling ranf_start, get new randoms by, e.g., "x=ranf_arr_next()" */
+
+#define QUALITY 1009 /* recommended quality level for high-res use */
+double ranf_arr_buf[QUALITY];
+double ranf_arr_dummy=-1.0, ranf_arr_started=-1.0;
+double *ranf_arr_ptr=&ranf_arr_dummy; /* the next random fraction, or -1 */
+
+#define TT 70 /* guaranteed separation between streams */
+#define is_odd(s) ((s)&1)
+
+#ifdef __STDC__
+void ranf_start(long seed)
+#else
+void ranf_start(seed) /* do this before using ranf_array */
+long seed; /* selector for different streams */
+#endif
+{
+ register int t,s,j;
+ double u[KK+KK-1];
+ double ulp=(1.0/(1L<<30))/(1L<<22); /* 2 to the -52 */
+ double ss=2.0*ulp*((seed&0x3fffffff)+2);
+
+ for (j=0;j<KK;j++) {
+ u[j]=ss; /* bootstrap the buffer */
+ ss+=ss; if (ss>=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<LL;j++) ran_u[j+KK-LL]=u[j];
+ for (;j<KK;j++) ran_u[j-LL]=u[j];
+ for (j=0;j<10;j++) ranf_array(u,KK+KK-1); /* warm things up */
+ ranf_arr_ptr=&ranf_arr_started;
+}
+
+#define ranf_arr_next() (*ranf_arr_ptr>=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 <stdio.h>
+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 <map>
+#include <stdlib.h>
+#include <assert.h>
+
+#include "routefunc.hpp"
+#include "kncube.hpp"
+#include "random_utils.hpp"
+
+map<string, tRoutingFunction> 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<string, tRoutingFunction>::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 <iostream>
+#include <assert.h>
+
+#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<Flit **>;
+ _input_credits = new vector<Credit **>;
+
+ _output_channels = new vector<Flit **>;
+ _output_credits = new vector<Credit **>;
+
+ _channel_faults = new vector<bool>;
+}
+
+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 <string>
+#include <vector>
+
+#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<Flit **> *_input_channels;
+ vector<Credit **> *_input_credits;
+ vector<Flit **> *_output_channels;
+ vector<Credit **> *_output_credits;
+ vector<bool> *_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 <iostream>
+
+#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<sRequest>::iterator p;
+ list<int>::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 <vector>
+
+#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 <math.h>
+#include <stdio.h>
+#include <iostream>
+
+#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 <stdio.h> + +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 <map>
+#include <stdlib.h>
+
+#include "traffic.hpp"
+#include "network.hpp"
+#include "random_utils.hpp"
+#include "misc_utils.hpp"
+
+map<string, tTrafficFunction> 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<string, tTrafficFunction>::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 <sstream>
+#include <math.h>
+#include <assert.h>
+
+#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<Flit *> * [_sources];
+ _active_list = new list<int> [_sources];
+ _active_vc = new bool * [_sources];
+ }
+
+ _partial_packets = new list<Flit *> * [_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<Flit *> [_dests];
+ _active_vc[s] = new bool [_dests];
+ }
+
+ _partial_packets[s] = new list<Flit *> [_classes];
+ }
+
+ _split_packets = config.GetInt( "split_packets" );
+
+ credit_return_queue = new queue<Flit *> [_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<Flit *, vector<Flit *>, 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<Flit *, vector<Flit *>, 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<int, bool>::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! " <<endl;
+ }
+ } else {
+ if ( f->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 "<<last_valid_flit->id<<"To output "<<output<< endl;
+ }
+ _RetireFlit(last_valid_flit, output );
+ if ( !_empty_network ) {
+ _accepted_packets[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<int, bool>::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["<<uid<<"]class" << c << " ======" << endl;
+
+ cout << "Traffic["<<uid<<"]class" << c << "Overall average latency = " << _overall_latency[c]->Average( )
+ << " (" << _overall_latency[c]->NumSamples( ) << " samples)" << endl;
+
+ cout << "Traffic["<<uid<<"]class" << c << "Overall average accepted rate = " << _overall_accepted->Average( )
+ << " (" << _overall_accepted->NumSamples( ) << " samples)" << endl;
+
+ cout << "Traffic["<<uid<<"]class" << c << "Overall min accepted rate = " << _overall_accepted_min->Average( )
+ << " (" << _overall_accepted_min->NumSamples( ) << " samples)" << endl;
+
+ if ( DISPLAY_LAT_DIST ) {
+ _latency_stats[c]->Display( );
+ }
+ }
+
+ if ( _reorder ) {
+ cout << "Traffic["<<uid<<"]class" << c << "Overall average reorder latency = " << _rob_latency->Average( ) << endl;
+ cout << "Traffic["<<uid<<"]class" << c << "Overall average reorder size - " << _rob_size->Average( ) << endl;
+
+ if ( DISPLAY_LAT_DIST ) {
+ _rob_latency->Display( );
+ _rob_size->Display( );
+ }
+ }
+
+ if ( DISPLAY_HOP_DIST ) {
+ cout << "Traffic["<<uid<<"]class" << c << "Average hops = " << _hop_stats->Average( )
+ << " (" << _hop_stats->NumSamples( ) << " samples)" << endl;
+
+ _hop_stats->Display( );
+ }
+
+ if ( DISPLAY_PAIR_LATENCY ) {
+ for ( int i = 0; i < _dests; ++i ) {
+ cout << "Traffic["<<uid<<"]class" << c << " Average to " << i << " = " << _pair_latency[i]->Average( ) << "( "
+ << _pair_latency[i]->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 <list>
+#include <map>
+#include <queue>
+
+#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<Flit *> **_partial_packets;
+
+ bool _use_lagging;
+
+ list<Flit *> **_voq;
+ list<int> *_active_list;
+ bool **_active_vc;
+
+ int _measured_in_flight;
+ int _total_in_flight;
+ map<int,bool> _in_flight;
+ bool _empty_network;
+
+ int _split_packets;
+
+ queue<Flit *> * 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<Flit *, vector<Flit *>, 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<Flit *> _used_flits;
+ list<Flit *> _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 <queue>
+
+#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<Flit *> _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 <iostream>
+
+#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
diff --git a/src/option_parser.cc b/src/option_parser.cc new file mode 100644 index 0000000..35ec34b --- /dev/null +++ b/src/option_parser.cc @@ -0,0 +1,514 @@ +/* + * option_parser.cc + * + * Copyright © 2009 by Wilson W. L. Fung and the University of British Columbia, + * Vancouver, BC V6T 1Z4, All Rights Reserved. + * + * THIS IS A LEGAL DOCUMENT BY DOWNLOADING GPGPU-SIM, YOU ARE AGREEING TO THESE + * TERMS AND CONDITIONS. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * NOTE: The files libcuda/cuda_runtime_api.c and src/cuda-sim/cuda-math.h + * are derived from the CUDA Toolset available from http://www.nvidia.com/cuda + * (property of NVIDIA). The files benchmarks/BlackScholes/ and + * benchmarks/template/ are derived from the CUDA SDK available from + * http://www.nvidia.com/cuda (also property of NVIDIA). The files from + * src/intersim/ are derived from Booksim (a simulator provided with the + * textbook "Principles and Practices of Interconnection Networks" available + * from http://cva.stanford.edu/books/ppin/). As such, those files are bound by + * the corresponding legal terms and conditions set forth separately (original + * copyright notices are left in files from these sources and where we have + * modified a file our copyright notice appears before the original copyright + * notice). + * + * Using this version of GPGPU-Sim requires a complete installation of CUDA + * which is distributed seperately by NVIDIA under separate terms and + * conditions. To use this version of GPGPU-Sim with OpenCL requires a + * recent version of NVIDIA's drivers which support OpenCL. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the University of British Columbia nor the names of + * its contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * 4. This version of GPGPU-SIM is distributed freely for non-commercial use only. + * + * 5. No nonprofit user may place any restrictions on the use of this software, + * including as modified by the user, by any other authorized user. + * + * 6. GPGPU-SIM was developed primarily by Tor M. Aamodt, Wilson W. L. Fung, + * Ali Bakhoda, George L. Yuan, at the University of British Columbia, + * Vancouver, BC V6T 1Z4 + */ + + +#include <stdio.h> +#include <stdlib.h> +#include <assert.h> +#include <string> +#include <iostream> +#include <iomanip> +#include <sstream> +#include <fstream> +#include <vector> +#include <list> +#include <map> +#include <string.h> + +using namespace std; + +// A generic option registry regardless of data type +class OptionRegistryInterface +{ +public: + OptionRegistryInterface(const string optionName, const string optionDesc) + : m_optionName(optionName), m_optionDesc(optionDesc) + {} + + virtual ~OptionRegistryInterface() {} + + const string& GetName() { return m_optionName; } + const string& GetDesc() { return m_optionDesc; } + virtual string toString() = 0; + virtual bool fromString(const string str) = 0; + virtual bool isFlag() = 0; + virtual bool assignDefault(const char *str) = 0; + +private: + string m_optionName; + string m_optionDesc; +}; + +// Template for option registry - class T = specify data type of the option +template <class T> +class OptionRegistry : public OptionRegistryInterface +{ +public: + OptionRegistry(const string name, const string desc, T &variable) + : OptionRegistryInterface(name, desc), m_variable(variable) + {} + + virtual ~OptionRegistry() {} + + virtual string toString() + { + stringstream ss; + ss << m_variable; + return ss.str(); + } + + virtual bool fromString(const string str) + { + stringstream ss(str); + ss.exceptions(stringstream::failbit | stringstream::badbit); + ss << setbase(10); + if (str.size() > 1 && str[0] == '0') { + if (str.size() > 2 && str[1] == 'x') { + ss.ignore(2); + ss << setbase(16); + } else { + ss.ignore(1); + ss << setbase(8); + } + } + try { + ss >> m_variable; + } catch (stringstream::failure &e) { + return false; + } + return true; + } + + virtual bool isFlag() { return false; } + virtual bool assignDefault(const char *str) { return fromString(str); } + + operator T() + { + return m_variable; + } + +private: + T &m_variable; +}; + +// specialized parser for string-type options +template<> +bool OptionRegistry<string>::fromString(const string str) +{ + m_variable = str; + return true; +} + +// specialized parser for c-string type options +template<> +bool OptionRegistry<char *>::fromString(const string str) +{ + m_variable = new char[str.size() + 1]; + strcpy(m_variable, str.c_str()); + return true; +} + +// specialized default assignment for c-string type option to allow NULL default +template<> +bool OptionRegistry<char *>::assignDefault(const char *str) +{ + m_variable = const_cast<char *>(str); // c-string options are not meant to be edited anyway + return true; +} + +// specialized default assignment for c-string type option to allow NULL default +template<> +string OptionRegistry<char *>::toString() +{ + stringstream ss; + if (m_variable != NULL) { + ss << m_variable; + } else { + ss << "NULL"; + } + return ss.str(); +} + +// specialized parser for boolean options +template<> +bool OptionRegistry<bool>::fromString(const string str) +{ + int value = 1; + bool parsed = true; + stringstream ss(str); + ss.exceptions(stringstream::failbit | stringstream::badbit); + try { + ss >> value; + } catch (stringstream::failure &ep) { + parsed = false; + } + assert(value == 0 or value == 1); // sanity check for boolean options (it can only be 1 or 0) + m_variable = (value != 0); + return parsed; +} + +// specializing a flag query function to identify boolean option +template<> +bool OptionRegistry<bool>::isFlag() { return true; } + +// class holding a collection of options and parse them from command line/configfile +class OptionParser +{ +public: + OptionParser() {} + ~OptionParser() + { + OptionCollection::iterator i_option; + for (i_option = m_optionReg.begin(); i_option != m_optionReg.end(); ++i_option) { + delete (*i_option); + } + } + + template<class T> + void Register(const string optionName, const string optionDesc, T &optionVariable, const char *optionDefault) + { + OptionRegistry<T> *p_option = new OptionRegistry<T>(optionName, optionDesc, optionVariable); + m_optionReg.push_back(p_option); + m_optionMap[optionName] = p_option; + p_option->assignDefault(optionDefault); + } + + void ParseCommandLine(int argc, const char * const argv[]) + { + for (int i = 1; i < argc; i++) { + OptionMap::iterator i_option; + bool optionFound = false; + + i_option = m_optionMap.find(argv[i]); + if (i_option != m_optionMap.end()) { + const char *argstr = (i + 1 < argc)? argv[i + 1] : ""; + OptionRegistryInterface *p_option = i_option->second; + if (p_option->isFlag()) { + if (p_option->fromString(argstr) == true) { + i += 1; + } + } else { + if (p_option->fromString(argstr) == false) { + fprintf(stderr, "\n\nGPGPU-Sim ** ERROR: Cannot parse value '%s' for option '%s'.\n", argstr, argv[i]); + exit(1); + } + i += 1; + } + optionFound = true; + } else if (string(argv[i]) == "-config") { + if (i + 1 >= argc) { + fprintf(stderr, "\n\nGPGPU-Sim ** ERROR: Missing filename for option '-config'.\n"); + exit(1); + } + ParseFile(argv[i + 1]); + i += 1; + optionFound = true; + } + + if (optionFound == false) { + fprintf(stderr, "\n\nGPGPU-Sim ** ERROR: Unknown Option: '%s' \n", argv[i]); + exit(1); + } + } + } + + void ParseFile(const char *filename) { + ifstream inputFile; + stringstream args; + + // open config file, stream every line into a continuous buffer + // get rid of comments in the process + inputFile.open(filename); + if (!inputFile.good()) { + fprintf(stderr, "\n\nGPGPU-Sim ** ERROR: Cannot open config file '%s'\n", filename); + exit(1); + } + while (inputFile.good()) { + string line; + getline(inputFile, line); + size_t commentStart = line.find_first_of("#"); + if (commentStart != line.npos) { + line.erase(commentStart); + } + args << line << ' '; + } + inputFile.close(); + + // extract non-whitespace string tokens + vector<char*> argv; + argv.push_back(new char[6]); + strcpy(argv[0], "dummy"); + while (args.good()) { + string argNew; + args >> argNew; + + if (argNew.size() == 0) continue; // this is probably the last token + + if (argNew[0] == '"') { + while (args.good() && argNew[argNew.size()-1] != '"') { + string argCont; + args >> argCont; + argNew += " " + argCont; + } + argNew.erase(0,1); + argNew.erase(argNew.size()-1); + } + + char *c_argNew = new char[argNew.size() + 1]; + strcpy(c_argNew, argNew.c_str()); + argv.push_back(c_argNew); + } + + // pass the string token into normal commandline parser + char **targv = (char**)calloc(argv.size(), sizeof(char*)); + for( unsigned k=0; k < argv.size(); k++ ) + targv[k] = argv[k]; + ParseCommandLine(argv.size(), targv); + free(targv); + for (size_t i = 0; i < argv.size(); i++) { + delete[] argv[i]; + } + } + + void Print(FILE *fout) + { + OptionCollection::iterator i_option; + for (i_option = m_optionReg.begin(); i_option != m_optionReg.end(); ++i_option) { + stringstream sout; + sout << setw(20) << left << (*i_option)->GetName() << " "; + sout << setw(20) << right << (*i_option)->toString() << " # "; + sout << left << (*i_option)->GetDesc(); + sout << std::endl; + fprintf(fout, "%s", sout.str().c_str()); + } + } + +private: + typedef list<OptionRegistryInterface*> OptionCollection; + OptionCollection m_optionReg; + typedef map<string, OptionRegistryInterface*> OptionMap; + OptionMap m_optionMap; +}; + +#include "option_parser.h" + +option_parser_t option_parser_create() +{ + OptionParser *p_opr = new OptionParser(); + return reinterpret_cast<option_parser_t>(p_opr); +} + +void option_parser_destroy(option_parser_t opp) +{ + OptionParser *p_opr = reinterpret_cast<OptionParser *>(opp); + delete p_opr; +} + +void option_parser_register(option_parser_t opp, + const char *name, + enum option_dtype type, + void *variable, + const char *desc, + const char *defaultvalue) +{ + OptionParser *p_opr = reinterpret_cast<OptionParser *>(opp); + switch (type) { + case OPT_INT32: p_opr->Register<int>(name, desc, *(int*)variable, defaultvalue); break; + case OPT_UINT32: p_opr->Register<unsigned int>(name, desc, *(unsigned int*)variable, defaultvalue); break; + case OPT_INT64: p_opr->Register<long long>(name, desc, *(long long*)variable, defaultvalue); break; + case OPT_UINT64: p_opr->Register<unsigned long long>(name, desc, *(unsigned long long*)variable, defaultvalue); break; + case OPT_BOOL: p_opr->Register<bool>(name, desc, *(bool*)variable, defaultvalue); break; + case OPT_FLOAT: p_opr->Register<float>(name, desc, *(float*)variable, defaultvalue); break; + case OPT_DOUBLE: p_opr->Register<double>(name, desc, *(double*)variable, defaultvalue); break; + case OPT_CSTR: p_opr->Register<char*>(name, desc, *(char**)variable, defaultvalue); break; + default: + fprintf(stderr, "\n\nGPGPU-Sim ** ERROR: option data type (%d) not supported!\n", type); + exit(1); + break; + } +} + +void option_parser_cmdline(option_parser_t opp, + int argc, const char *argv[]) +{ + OptionParser *p_opr = reinterpret_cast<OptionParser *>(opp); + p_opr->ParseCommandLine(argc,argv); +} + +void option_parser_cfgfile(option_parser_t opp, + const char *filename) +{ + OptionParser *p_opr = reinterpret_cast<OptionParser *>(opp); + p_opr->ParseFile(filename); +} + +void option_parser_print(option_parser_t opp, + FILE *fout) +{ + OptionParser *p_opr = reinterpret_cast<OptionParser *>(opp); + p_opr->Print(fout); +} + + + +// #define UNIT_TEST +#ifdef UNIT_TEST + +class testtype +{ +public: + int idata; + float fdata; + string sdata; + unsigned long long ulldata; + bool bdata; + unsigned int boolint; + char * coption; + + testtype() + : idata(0), + fdata(0.0f), + sdata(""), + ulldata(0), + bdata(false) + { } +}; + + +int cppinterfacetest(int argc, char *argv[]) +{ + testtype c; + OptionParser optionparser; + c.idata = 123; + c.fdata = 3249586.333; + c.sdata = string("haha"); + + optionparser.Register<int>("-idata", "integer data", c.idata, "-456"); + optionparser.Register<float>("-fdata", "floating point data", c.fdata, "0.001"); + optionparser.Register<string>("-sdata", "first string data", c.sdata, "hellow"); + optionparser.Register<unsigned long long>("-ulldata", "unsigned long long data", c.ulldata, "0x123456789abcdef1"); + optionparser.Register<bool>("-someflag", "first flag", c.bdata, "0"); + optionparser.Register<bool>("-otherflag", "second flag", (bool&)c.boolint, "1"); + optionparser.Register<char *>("-coption", "char * data", c.coption, NULL); + + cout << "Default: \n"; + optionparser.Print(stdout); + + optionparser.ParseCommandLine(argc, argv); + + cout << "Commandline Parse Results: \n"; + optionparser.Print(stdout); + + optionparser.ParseFile("test.config"); + cout << "File Parse Results: \n"; + optionparser.Print(stdout); + cout << c.sdata << ' ' << c.idata << endl; + + return 0; +} + +int cinterfacetest(int argc, char *argv[]) +{ + testtype c; + option_parser_t opp = option_parser_create(); + c.idata = 123; + c.fdata = 3249586.333; + c.sdata = string("haha"); + char *otherstr; + + option_parser_register(opp, "-idata", OPT_INT32, &c.idata, "integer data", "-456"); + option_parser_register(opp, "-fdata", OPT_FLOAT, &c.fdata, "floating point data", "0.001"); + option_parser_register(opp, "-sdata", OPT_CSTR, &otherstr, "first string data", "hellow"); + option_parser_register(opp, "-ulldata", OPT_UINT64, &c.ulldata, "unsigend long long data", "0x123456789abcdef1"); + option_parser_register(opp, "-someflag", OPT_BOOL, &c.bdata, "first flag", "0"); + option_parser_register(opp, "-otherflag", OPT_BOOL, &c.boolint, "second flag", "1"); + option_parser_register(opp, "-coption", OPT_CSTR, &c.coption, "char * data", NULL); + + printf("Default: \n"); + option_parser_print(opp, stdout); + + option_parser_cmdline(opp, argc, argv); + + printf("Commandline Parse Results: \n"); + option_parser_print(opp, stdout); + + option_parser_cfgfile(opp, "test.config"); + printf("File Parse Results: \n"); + option_parser_print(opp, stdout); + printf("%s %d\n", otherstr, c.idata); + + option_parser_destroy(opp); + + return 0; +} + +int main(int argc, char *argv[]) +{ + cppinterfacetest(argc,argv); + cinterfacetest(argc,argv); + + return 0; +} + +#endif + diff --git a/src/option_parser.h b/src/option_parser.h new file mode 100644 index 0000000..e14c08e --- /dev/null +++ b/src/option_parser.h @@ -0,0 +1,115 @@ +/* + * option_parser.h + * + * Copyright © 2009 by Wilson W. L. Fung and the University of British Columbia, + * Vancouver, BC V6T 1Z4, All Rights Reserved. + * + * THIS IS A LEGAL DOCUMENT BY DOWNLOADING GPGPU-SIM, YOU ARE AGREEING TO THESE + * TERMS AND CONDITIONS. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * NOTE: The files libcuda/cuda_runtime_api.c and src/cuda-sim/cuda-math.h + * are derived from the CUDA Toolset available from http://www.nvidia.com/cuda + * (property of NVIDIA). The files benchmarks/BlackScholes/ and + * benchmarks/template/ are derived from the CUDA SDK available from + * http://www.nvidia.com/cuda (also property of NVIDIA). The files from + * src/intersim/ are derived from Booksim (a simulator provided with the + * textbook "Principles and Practices of Interconnection Networks" available + * from http://cva.stanford.edu/books/ppin/). As such, those files are bound by + * the corresponding legal terms and conditions set forth separately (original + * copyright notices are left in files from these sources and where we have + * modified a file our copyright notice appears before the original copyright + * notice). + * + * Using this version of GPGPU-Sim requires a complete installation of CUDA + * which is distributed seperately by NVIDIA under separate terms and + * conditions. To use this version of GPGPU-Sim with OpenCL requires a + * recent version of NVIDIA's drivers which support OpenCL. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the University of British Columbia nor the names of + * its contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * 4. This version of GPGPU-SIM is distributed freely for non-commercial use only. + * + * 5. No nonprofit user may place any restrictions on the use of this software, + * including as modified by the user, by any other authorized user. + * + * 6. GPGPU-SIM was developed primarily by Tor M. Aamodt, Wilson W. L. Fung, + * Ali Bakhoda, George L. Yuan, at the University of British Columbia, + * Vancouver, BC V6T 1Z4 + */ + +#pragma once + +#include <stdio.h> +#include <stdlib.h> + +// pointer to C++ class +typedef void* option_parser_t; + +// data type of the option +enum option_dtype { + OPT_INT32, + OPT_UINT32, + OPT_INT64, + OPT_UINT64, + OPT_BOOL, + OPT_FLOAT, + OPT_DOUBLE, + OPT_CSTR +}; + +#ifdef __cplusplus +extern "C" { +#endif + +// create and destroy option parser +option_parser_t option_parser_create(); +void option_parser_destroy(option_parser_t opp); + +// register new option +void option_parser_register(option_parser_t opp, + const char *name, + enum option_dtype type, + void *variable, + const char *desc, + const char *defaultvalue); + +// parse command line +void option_parser_cmdline(option_parser_t opp, + int argc, const char *argv[]); + +// parse config file +void option_parser_cfgfile(option_parser_t opp, + const char *filename); + +// print options +void option_parser_print(option_parser_t opp, + FILE *fout); + +#ifdef __cplusplus +}; +#endif + diff --git a/src/tr1_hash_map.h b/src/tr1_hash_map.h new file mode 100644 index 0000000..3f31576 --- /dev/null +++ b/src/tr1_hash_map.h @@ -0,0 +1,19 @@ +#pragma once + +// detection and fallback for unordered_map in C++0x +#ifdef __cplusplus + #ifdef __GNUC__ + #if __GNUC__ >= 4 && __GNUC_MINOR__ >= 3 + #include <unordered_map> + #define tr1_hash_map std::unordered_map + #else + #include <map> + #define tr1_hash_map std::map + #endif + #else + #include <map> + #define tr1_hash_map std::map + #endif +#endif + + diff --git a/src/util.h b/src/util.h new file mode 100644 index 0000000..d021652 --- /dev/null +++ b/src/util.h @@ -0,0 +1,79 @@ +/* + * util.h + * + * Copyright (c) 2009 by Tor M. Aamodt and the University of British Columbia + * Vancouver, BC V6T 1Z4 + * All Rights Reserved. + * + * THIS IS A LEGAL DOCUMENT BY DOWNLOADING GPGPU-SIM, YOU ARE AGREEING TO THESE + * TERMS AND CONDITIONS. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * NOTE: The files libcuda/cuda_runtime_api.c and src/cuda-sim/cuda-math.h + * are derived from the CUDA Toolset available from http://www.nvidia.com/cuda + * (property of NVIDIA). The files benchmarks/BlackScholes/ and + * benchmarks/template/ are derived from the CUDA SDK available from + * http://www.nvidia.com/cuda (also property of NVIDIA). The files from + * src/intersim/ are derived from Booksim (a simulator provided with the + * textbook "Principles and Practices of Interconnection Networks" available + * from http://cva.stanford.edu/books/ppin/). As such, those files are bound by + * the corresponding legal terms and conditions set forth separately (original + * copyright notices are left in files from these sources and where we have + * modified a file our copyright notice appears before the original copyright + * notice). + * + * Using this version of GPGPU-Sim requires a complete installation of CUDA + * which is distributed seperately by NVIDIA under separate terms and + * conditions. To use this version of GPGPU-Sim with OpenCL requires a + * recent version of NVIDIA's drivers which support OpenCL. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the University of British Columbia nor the names of + * its contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * 4. This version of GPGPU-SIM is distributed freely for non-commercial use only. + * + * 5. No nonprofit user may place any restrictions on the use of this software, + * including as modified by the user, by any other authorized user. + * + * 6. GPGPU-SIM was developed primarily by Tor M. Aamodt, Wilson W. L. Fung, + * Ali Bakhoda, George L. Yuan, at the University of British Columbia, + * Vancouver, BC V6T 1Z4 + */ + +#ifndef util_h_INCLUDED +#define util_h_INCLUDED + +typedef unsigned address_type; + +#define NO_OP -1 +#define ALU_OP 1 +#define LOAD_OP 2 +#define STORE_OP 3 +#define BRANCH_OP 4 +#define BARRIER_OP 5 +#define TRUE 1 +#define FALSE 0 + +#endif |
