summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile108
-rw-r--r--src/abstract_hardware_model.h12
-rw-r--r--src/cuda-sim/Makefile170
-rw-r--r--src/cuda-sim/cuda-math.h81
-rw-r--r--src/cuda-sim/cuda-sim.cc2195
-rw-r--r--src/cuda-sim/dram_callback.h84
-rw-r--r--src/cuda-sim/instructions.cc2921
-rw-r--r--src/cuda-sim/memory.cc154
-rw-r--r--src/cuda-sim/memory.h148
-rw-r--r--src/cuda-sim/opcodes.def120
-rw-r--r--src/cuda-sim/opcodes.h82
-rw-r--r--src/cuda-sim/ptx-stats.cc298
-rw-r--r--src/cuda-sim/ptx-stats.h92
-rw-r--r--src/cuda-sim/ptx.l336
-rw-r--r--src/cuda-sim/ptx.y470
-rw-r--r--src/cuda-sim/ptx_ir.cc1283
-rw-r--r--src/cuda-sim/ptx_ir.h1223
-rw-r--r--src/cuda-sim/ptx_sim.cc432
-rw-r--r--src/cuda-sim/ptx_sim.h457
-rw-r--r--src/cuda-sim/ptxinfo.l128
-rw-r--r--src/cuda-sim/ptxinfo.y140
-rw-r--r--src/gpgpu-sim/Makefile116
-rw-r--r--src/gpgpu-sim/addrdec.cc471
-rw-r--r--src/gpgpu-sim/addrdec.h96
-rw-r--r--src/gpgpu-sim/cflogger.h121
-rw-r--r--src/gpgpu-sim/delayqueue.cc468
-rw-r--r--src/gpgpu-sim/delayqueue.h122
-rw-r--r--src/gpgpu-sim/dram.cc604
-rw-r--r--src/gpgpu-sim/dram.h237
-rw-r--r--src/gpgpu-sim/dram_sched.cc247
-rw-r--r--src/gpgpu-sim/dram_sched.h99
-rw-r--r--src/gpgpu-sim/dwf.cc2609
-rw-r--r--src/gpgpu-sim/dwf.h118
-rw-r--r--src/gpgpu-sim/gpu-cache.cc609
-rw-r--r--src/gpgpu-sim/gpu-cache.h191
-rw-r--r--src/gpgpu-sim/gpu-misc.cc96
-rw-r--r--src/gpgpu-sim/gpu-misc.h106
-rw-r--r--src/gpgpu-sim/gpu-sim.cc1752
-rw-r--r--src/gpgpu-sim/gpu-sim.h116
-rw-r--r--src/gpgpu-sim/histogram.h128
-rw-r--r--src/gpgpu-sim/icnt_wrapper.cc97
-rw-r--r--src/gpgpu-sim/icnt_wrapper.h100
-rw-r--r--src/gpgpu-sim/l2cache.cc999
-rw-r--r--src/gpgpu-sim/l2cache.h48
-rw-r--r--src/gpgpu-sim/mem_fetch.h110
-rw-r--r--src/gpgpu-sim/mem_latency_stat.h544
-rw-r--r--src/gpgpu-sim/shader.cc3811
-rw-r--r--src/gpgpu-sim/shader.h524
-rw-r--r--src/gpgpu-sim/stack.cc127
-rw-r--r--src/gpgpu-sim/stack.h90
-rw-r--r--src/gpgpu-sim/stat-tool.cc1081
-rw-r--r--src/gpgpu-sim/visualizer.cc602
-rw-r--r--src/gpgpu-sim/warp_tracker.cc471
-rw-r--r--src/gpgpu-sim/warp_tracker.h100
-rw-r--r--src/gpgpusim_entrypoint.cc144
-rw-r--r--src/intersim/Makefile119
-rw-r--r--src/intersim/allocator.cpp429
-rw-r--r--src/intersim/allocator.hpp122
-rw-r--r--src/intersim/arbiter.cpp143
-rw-r--r--src/intersim/arbiter.hpp51
-rw-r--r--src/intersim/booksim.hpp13
-rw-r--r--src/intersim/booksim_config.cpp140
-rw-r--r--src/intersim/booksim_config.hpp11
-rw-r--r--src/intersim/buffer_state.cpp145
-rw-r--r--src/intersim/buffer_state.hpp43
-rw-r--r--src/intersim/config.l51
-rw-r--r--src/intersim/config.y33
-rw-r--r--src/intersim/config_utils.cpp176
-rw-r--r--src/intersim/config_utils.hpp45
-rw-r--r--src/intersim/credit.cpp16
-rw-r--r--src/intersim/credit.hpp15
-rw-r--r--src/intersim/doc/fancyhdr.sty329
-rw-r--r--src/intersim/doc/manual.pdfbin0 -> 113212 bytes
-rw-r--r--src/intersim/doc/manual.tex687
-rw-r--r--src/intersim/event_router.cpp923
-rw-r--r--src/intersim/event_router.hpp151
-rw-r--r--src/intersim/examples/fly26_age41
-rw-r--r--src/intersim/examples/mesh40
-rw-r--r--src/intersim/examples/mesh241
-rw-r--r--src/intersim/examples/mesh441
-rw-r--r--src/intersim/examples/mesh88_lat40
-rw-r--r--src/intersim/examples/single20
-rw-r--r--src/intersim/examples/torus8814
-rw-r--r--src/intersim/flit.cpp12
-rw-r--r--src/intersim/flit.hpp46
-rw-r--r--src/intersim/fly.cpp133
-rw-r--r--src/intersim/fly.hpp26
-rw-r--r--src/intersim/injection.cpp100
-rw-r--r--src/intersim/injection.hpp12
-rw-r--r--src/intersim/interconnect_interface.cpp574
-rw-r--r--src/intersim/interconnect_interface.h35
-rw-r--r--src/intersim/iq_router.cpp633
-rw-r--r--src/intersim/iq_router.hpp75
-rw-r--r--src/intersim/islip.cpp185
-rw-r--r--src/intersim/islip.hpp22
-rw-r--r--src/intersim/kncube.cpp253
-rw-r--r--src/intersim/kncube.hpp33
-rw-r--r--src/intersim/loa.cpp102
-rw-r--r--src/intersim/loa.hpp23
-rw-r--r--src/intersim/maxsize.cpp181
-rw-r--r--src/intersim/maxsize.hpp22
-rw-r--r--src/intersim/misc_utils.cpp25
-rw-r--r--src/intersim/misc_utils.hpp7
-rw-r--r--src/intersim/module.cpp63
-rw-r--r--src/intersim/module.hpp33
-rw-r--r--src/intersim/network.cpp156
-rw-r--r--src/intersim/network.hpp72
-rw-r--r--src/intersim/outputset.cpp134
-rw-r--r--src/intersim/outputset.hpp36
-rw-r--r--src/intersim/pim.cpp98
-rw-r--r--src/intersim/pim.hpp20
-rw-r--r--src/intersim/pipefifo.hpp76
-rw-r--r--src/intersim/random_utils.cpp25
-rw-r--r--src/intersim/random_utils.hpp21
-rw-r--r--src/intersim/rng.cpp111
-rw-r--r--src/intersim/rng.hpp10
-rw-r--r--src/intersim/rng_double.cpp115
-rw-r--r--src/intersim/rng_double_wrapper.cpp9
-rw-r--r--src/intersim/rng_wrapper.cpp9
-rw-r--r--src/intersim/routefunc.cpp1045
-rw-r--r--src/intersim/routefunc.hpp14
-rw-r--r--src/intersim/router.cpp113
-rw-r--r--src/intersim/router.hpp63
-rw-r--r--src/intersim/selalloc.cpp207
-rw-r--r--src/intersim/selalloc.hpp22
-rw-r--r--src/intersim/singlenet.cpp43
-rw-r--r--src/intersim/singlenet.hpp17
-rw-r--r--src/intersim/stats.cpp119
-rw-r--r--src/intersim/stats.hpp39
-rw-r--r--src/intersim/statwraper.cpp64
-rw-r--r--src/intersim/statwraper.h13
-rw-r--r--src/intersim/traffic.cpp303
-rw-r--r--src/intersim/traffic.hpp15
-rw-r--r--src/intersim/trafficmanager.cpp1249
-rw-r--r--src/intersim/trafficmanager.hpp173
-rw-r--r--src/intersim/vc.cpp175
-rw-r--r--src/intersim/vc.hpp79
-rw-r--r--src/intersim/wavefront.cpp61
-rw-r--r--src/intersim/wavefront.hpp19
-rw-r--r--src/option_parser.cc514
-rw-r--r--src/option_parser.h115
-rw-r--r--src/tr1_hash_map.h19
-rw-r--r--src/util.h79
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> &regs = *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
new file mode 100644
index 0000000..c12aa4a
--- /dev/null
+++ b/src/intersim/doc/manual.pdf
Binary files differ
diff --git a/src/intersim/doc/manual.tex b/src/intersim/doc/manual.tex
new file mode 100644
index 0000000..2ec6726
--- /dev/null
+++ b/src/intersim/doc/manual.tex
@@ -0,0 +1,687 @@
+\documentclass[11pt]{article}
+\usepackage{fancyhdr}
+\usepackage[dvips]{graphicx}
+\usepackage{amsmath,amssymb}
+\usepackage{epsfig}
+\usepackage{calc}
+
+\newcommand{\simname}{BookSim~}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+% Setup the margin sizes.
+
+\evensidemargin = 0in
+\oddsidemargin = 0in
+\textwidth = 6.5in
+
+\topmargin = -0.5in
+\textheight = 9in
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\author{Brian Towles and William J. Dally}
+\title{\simname 1.0 User's Guide}
+
+\begin{document}
+
+\maketitle
+\tableofcontents
+
+\pagestyle{fancy}
+%\renewcommand{\chaptermark}[1]{\markboth{#1}{}}
+\renewcommand{\sectionmark}[1]{\markright{\thesection\ #1}}
+\fancyhf{} % delete current setting for header and footer
+\fancyhead[LE,RO]{\bfseries\thepage}
+\fancyhead[LO]{\bfseries\rightmark}
+\fancyhead[RE]{\bfseries\leftmark}
+\renewcommand{\headrulewidth}{0.5pt}
+\renewcommand{\footrulewidth}{0.5pt}
+\addtolength{\headheight}{0.5pt} % make space for the rule
+\cfoot{\small\today}
+\fancypagestyle{plain}{%
+ \fancyhf{} % get rid of headers on plain pages
+ \renewcommand{\headrulewidth}{0pt} % and the line
+ \renewcommand{\footrulewidth}{0pt} % and the line
+}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\newenvironment{opt_list}[1]{\begin{list}{}{\renewcommand{\makelabel}[1]%
+{\texttt{##1}\hfil}\settowidth{\labelwidth}{\texttt{#1}}\setlength{\leftmargin}%
+{\labelwidth+\labelsep}}}{\end{list}}
+
+\section{Introduction}
+
+This document describes the use of the \simname interconnection
+network simulator. The simulator is designed as a companion to the
+textbook ``Principles and Practices of Interconnection Networks''
+(PPIN) published by Morgan Kaufmann (ISBN: 0122007514) and it is
+assumed that is reader is familiar with the material covered in that
+text.
+
+This user guide is fairly brief as, with most simulators, the best way
+to learn and {\it understand} the simulator is to study the code.
+Most of the simulator's components are designed to be modular so tasks
+such as adding a new routing algorithm, topology, or router
+microarchitecture should not require a complete redesign of the code.
+Once you have downloaded the code, compiled it, and run a simple
+example (Section~\ref{sec:get_started}), the more detailed examples of
+Section~\ref{sec:examples} give a good overview of the capabilities of
+the simulator. A list of configuration options is provided in
+Section~\ref{sec:config_params} for reference.
+
+\section{Getting started}
+\label{sec:get_started}
+
+\subsection{Downloading and building the simulator}
+\label{sec:download}
+
+The latest version of the simulator is available from
+\texttt{http://cva.stanford.edu} as a compressed tar archive. UNIX/Linux
+users can extract this archive using the tar utility
+\begin{verbatim}
+ tar xvfz booksim-1.0.tar.gz
+\end{verbatim}
+Windows users can use a compression program such as WinZip to extract
+the archive.
+
+The simulator itself is written in C++ and has been specifically
+tested with GNU's G++ compiler (version $\ge3$). In addition, both a
+LEX and YACC tool (also known as FLEX and BISON) are needed to create
+the configuration parser. These are standard tools in any UNIX/Linux
+development environment. It is suggested that Windows users download
+the CYGWIN versions (\texttt{http://www.cygwin.com}) of these UNIX
+development tools to simplify their compilation process. The
+\texttt{Makefile} should be edited so that the first lines give the
+paths to the tools. At Stanford, for example, the compiler, YACC, and
+LEX are stored in the \texttt{/usr/pubsw/bin} directory. The
+\texttt{Makefile} reflects this:
+\begin{verbatim}
+CPP = /usr/pubsw/bin/g++
+YACC = /usr/pubsw/bin/byacc -d
+LEX = /usr/pubsw/bin/flex
+\end{verbatim}
+Then, the simulator can be compiled by running \texttt{make} in the
+directory that contains the \texttt{Makefile}.
+
+\subsection{Running a simulation}
+\label{sec:run_example}
+
+The syntax of the simulator is simply
+\begin{verbatim}
+ booksim [configfile]
+\end{verbatim}
+The optional parameter \texttt{configfile} is a file that contains
+configuration information for the simulator. So, for example, to
+simulate the performance of a simple $8 \times 8$ torus (8-ary 2-cube)
+network on uniform traffic, a configuration such as the one shown in
+Figure~\ref{fig:config_example} could be used. This particular
+configuration is stored in \texttt{examples/torus88}.
+
+\begin{figure}
+\begin{verbatim}
+ // Topology
+ topology = torus;
+ k = 8;
+ n = 2;
+
+ // Routing
+ routing_function = dim_order;
+
+ // Flow control
+ num_vcs = 2;
+
+ // Traffic
+ traffic = uniform;
+ injection_rate = 0.15;
+\end{verbatim}
+\caption{Example configuration file for simulating a 8-ary 2-cube
+network.}
+\label{fig:config_example}
+\end{figure}
+
+In addition to specifying the topology, the configuration file also
+contains basic information about the routing algorithm, flow control,
+and traffic. This simple example uses dimension-order routing and, to
+ensure deadlock-freedom of this routing function in the torus, two
+virtual channels are required. The \texttt{injection\_rate} parameter
+is added to tell the simulator to inject 0.15 flits per simulation
+cycle per node. Because the simulator operates at the flit level,
+most parameters are specified in units of flits as is the case with
+the \texttt{injection\_rate}. Also, any line of the configuration
+that begins with \texttt{//} is treated as a comment and ignored by
+the simulator. A detailed list of configuration parameters is given in
+Section~\ref{sec:config_params}.
+
+\subsection{Simulation output}
+
+Continuing our example, running the torus simulation produces the
+output shown in Figure~\ref{fig:sim_output}. Each simulation has
+three basic phases: warm up, measurement, and drain. The length of
+the warm up and measurement phases is a multiple of a basic sample
+period (defined by \texttt{sample\_period} in the configuration). As
+shown in the figure, the current latency and throughput (rate of
+accepted packets) for the simulation is printed after each sample
+period. The overall throughput is determined by the lowest throughput
+of all the destination in the network, but the average throughput is
+also displayed.
+
+\begin{figure}
+\begin{verbatim}
+%=================================
+% Average latency = 6.02008
+% Accepted packets = 0.11 at node 52 (avg = 0.147094)
+% latency change = 1
+% throughput change = 1
+
+...
+
+% Warmed up ...
+%=================================
+% Average latency = 6.0796
+% Accepted packets = 0.119 at node 5 (avg = 0.148266)
+% latency change = 0.00562457
+% throughput change = 0.00379387
+
+...
+
+% Draining all recorded packets ...
+% Draining remaining packets ...
+====== Traffic class 0 ======
+Overall average latency = 6.09083 (1 samples)
+Overall average accepted rate = 0.149475 (1 samples)
+Overall min accepted rate = 0.138551 (1 samples)
+\end{verbatim}
+\caption{Simulator output from running the \texttt{examples/torus88}
+configuration file.}
+\label{fig:sim_output}
+\end{figure}
+
+After the warm up periods have passed, the simulator prints the
+``\texttt{Warmed up}'' message and resets all the simulation statistics.
+Then, the measurement phase begins and statistics continue to be
+reported after each sample period. Once the measurement periods have
+passed, all the measurement packets are drained from the network
+before final latency and throughput numbers are reported. Details of
+the configuration parameters used to control the length of the
+simulation phases are covered in Section~\ref{sec:sim_params}.
+
+\section{Examples}
+\label{sec:examples}
+
+One of the most basic performance measures of any interconnection
+network is its latency versus offered load.
+Figure~\ref{fig:lat_vs_load} shows a simple configuration file for
+making this measurement in a 8-ary 2-mesh network under the transpose
+traffic pattern. This configuration was used to generate Figure 25.2
+in PPIN. The particular configuration accounts for some small delays
+and pipelining of the input-queued router and also introduces a small
+input speedup to account for any inefficiencies in allocation. By
+running simulations for many increments of \texttt{injection\_rate},
+the average latency curve can be found. Then, to compare the
+performance of dimension-order routing against several other routing
+algorithms, for example, the \texttt{routing\_function} option can be
+changed.
+
+\begin{figure}
+\begin{verbatim}
+// Topology
+
+topology = mesh;
+k = 8;
+n = 2;
+
+// Routing
+
+routing_function = dim_order;
+
+// Flow control
+
+num_vcs = 8;
+vc_buf_size = 8;
+
+wait_for_tail_credit = 1;
+
+// Router architecture
+
+vc_allocator = islip;
+sw_allocator = islip;
+alloc_iters = 1;
+
+credit_delay = 2;
+routing_delay = 1;
+vc_alloc_delay = 1;
+
+input_speedup = 2;
+output_speedup = 1;
+internal_speedup = 1.0;
+
+// Traffic
+
+traffic = transpose;
+const_flits_per_packet = 20;
+
+// Simulation
+
+sim_type = latency;
+injection_rate = 0.1;
+\end{verbatim}
+\caption{A typical configuration file (\texttt{examples/mesh88\_lat})
+for creating a latency versus offered load curve for a 8-ary 2-mesh
+network.}
+\label{fig:lat_vs_load}
+\end{figure}
+
+Figure~\ref{fig:fly_dist} shows a configuration file that can be used
+to determine the distribution of packet latencies in a 2-ary 6-fly
+network that uses age-based arbitration. Note the use of the
+\texttt{priority} configuration parameter along with the
+\texttt{select} allocators that account for packet priorities. The
+simulator does not output latency distributions by default, but by
+editing \texttt{trafficmanager.cpp}, setting the configuration
+variable \texttt{DISPLAY\_LAT\_DIST} to true, and recompiling, the
+distribution will be displayed at the end of the simulation. This
+technique was used to produced the distribution shown in Figure 25.12
+of PPIN.
+
+\begin{figure}
+\begin{verbatim}
+// Topology
+
+topology = fly;
+k = 2;
+n = 6;
+
+// Routing
+
+routing_function = dest_tag;
+
+// Flow control
+
+num_vcs = 8;
+vc_buf_size = 8;
+
+wait_for_tail_credit = 1;
+
+// Router architecture
+
+vc_allocator = select;
+sw_allocator = select;
+alloc_iters = 1;
+
+credit_delay = 2;
+routing_delay = 1;
+vc_alloc_delay = 1;
+
+input_speedup = 2;
+output_speedup = 1;
+internal_speedup = 1.0;
+
+// Traffic
+
+traffic = uniform;
+const_flits_per_packet = 20;
+priority = age;
+
+// Simulation
+
+sim_type = latency;
+injection_rate = 0.1;
+\end{verbatim}
+\caption{A configuration file (\texttt{examples/fly26\_age}) for
+finding the distribution of packet latencies using age-based
+arbitration.}
+\label{fig:fly_dist}
+\end{figure}
+
+As a final example, Figure~\ref{fig:single} shows the use of the
+special single-node topology to test the performance of a switch
+allocator --- in this case, the iSLIP allocator. The
+\texttt{in\_ports} and \texttt{out\_ports} options set up a simulation
+of an $8\times 8$ crossbar.
+
+\begin{figure}
+\begin{verbatim}
+// Topology
+
+topology = single;
+in_ports = 8;
+out_ports = 8;
+
+// Routing
+
+routing_function = single;
+
+// Flow control
+
+vc_allocator = islip;
+sw_allocator = islip;
+alloc_iters = 2;
+
+num_vcs = 8;
+vc_buf_size = 1000;
+
+wait_for_tail_credit = 0;
+
+// Simulation
+
+sim_type = latency;
+injection_rate = 0.1;
+\end{verbatim}
+\caption{A single-node configuration file (\texttt{examples/single})
+for testing the performance of a switch allocator.}
+\label{fig:single}
+\end{figure}
+
+\section{Configuration parameters}
+\label{sec:config_params}
+
+All information used to configure a simulation is passed through a
+configuration file as illustrated by the example in
+Section~\ref{sec:run_example}. This section lists the existing
+configuration parameters --- a user can incorporate additional options
+by changing the \texttt{booksim\_config.cpp} file.
+
+\subsection{Topologies}
+\label{sec:topos}
+
+The \texttt{topology} parameter determines the underlying topology of the
+network and the simulator supports four basic topologies:
+\begin{opt_list}{single}
+\item[fly] A $k$-ary $n$-fly (butterfly) topology. The \texttt{k}
+parameter determines the network's radix and the \texttt{n} parameter
+determines the network's dimension.
+
+\item[mesh] A $k$-ary $n$-mesh (mesh) topology. The \texttt{k}
+parameter determines the network's radix and the \texttt{n} parameter determines
+the network's dimension.
+
+\item[single] A network with a single node, used for testing single
+router performance. The number of input and output ports for the node
+is determined by the \texttt{in\_ports} and \texttt{out\_ports} parameters,
+respectively.
+
+\item[torus] A $k$-ary $n$-cube (torus) topology. The \texttt{k}
+parameter determines the network's radix and the \texttt{n} parameter determines
+the network's dimension.
+\end{opt_list}
+
+Both the \texttt{mesh} and \texttt{torus} topologies support the
+addition of random link failures with the \texttt{link\_failures}
+parameter. The value of \texttt{link\_failures} determines the number
+of channels that are randomly removed from the topology and are thus
+no longer available for forwarding packets. Moreover, the
+randomization for failed channels is controlled by selecting an
+integer value for the \texttt{fail\_seed} parameter --- a fixed seed
+gives a fixed set of failed channels, independent of other
+randomization in the simulation. Also, note that only certain routing
+functions support this feature (see Section~\ref{sec:routing_algs}).
+
+\subsection{Routing algorithms}
+\label{sec:routing_algs}
+
+The \texttt{routing\_function} parameter selects a routing algorithm
+for the topology. Many routing algorithms need multiple virtual
+channels for deadlock freedom (VCDF).
+
+\begin{opt_list}{dim\_order\_bal}
+
+\item[dim\_order] Dimension-order routing. Works for the
+\texttt{mesh} topology (1 VCDF) and for the \texttt{torus} topology (2
+VCDF).
+
+\item[dim\_order\_bal] Dimension-order routing for the
+\texttt{torus} topology with a more balanced use of VCs to
+avoid deadlock (2 VCDF).
+
+\item[dim\_order\_ni] A non-interfering version of
+dimension-order routing. Works on the \texttt{torus} or \texttt{mesh}
+topology and requires one VC per network terminal.
+
+\item[min\_adapt] A minimal adaptive routing algorithm for
+the \texttt{mesh} topology (2 VCDF) and for the \texttt{torus}
+topology (3 VCDF).
+
+\item[planar\_adapt] Planar-adaptive routing for the
+\texttt{mesh} topology (2 VCDF). Supports routing around failed channels.
+
+\item[romm] ROMM routing for the \texttt{mesh} (2 VCDF).
+Load is balanced by routing in two phases: one from the source to a
+random intermediate node in the minimal quadrant and a second from the
+intermediate to the destination.
+
+\item[romm\_ni] A non-interfering version of ROMM routing for
+the \texttt{mesh} that requires one VC per network terminal.
+
+\item[single] A dummy routing function used for the
+\texttt{single} topology.
+
+\item[valiant] Valiant's randomized routing algorithm for the
+\texttt{mesh} (2 VCDF) and \texttt{torus} (4 VCDF) topology.
+
+\item[valiant\_ni] A non-interfering version of Valiant's algorithm
+for the \texttt{torus} that requires 4 VCs per network terminal.
+
+\end{opt_list}
+
+Also, the simulator code is structured so that additional routing
+algorithms can be added with minimal changes to the overall simulator
+(see the \texttt{routefunc.cpp} file in the simulator's source code).
+
+\subsection{Flow control}
+
+The simulator supports basic virtual-channel flow control with
+credit-based backpressure.
+
+\begin{opt_list}{wait\_for\_tail\_credit}
+
+\item[num\_vcs] The number of virtual channels per physical channel.
+
+\item[vc\_buf\_size] The depth of each virtual in flits.
+
+\item[voq] If non-zero, use virtual-output queuing. With virtual
+output queuing, a separate virtual channel is assigned to each
+destination in the network. This option is most useful when used with
+a non-interfering routing algorithm (Section~\ref{sec:routing_algs}).
+
+\item[wait\_for\_tail\_credit] If non-zero, do not reallocate a virtual
+channel until the tail flit has left that virtual channel. This
+conservative approach prevents a dependency from being formed between
+two packets sharing the same virtual channel in succession.
+\end{opt_list}
+
+\subsection{Router organizations}
+
+The simulator also supports two different router microarchitectures.
+The input-queued router follows the general organization described in
+PPIN while the event-driven router is modeled after the router used in
+the Avici TSR and described in U.S. Patent 6,370,145. The
+microarchitecture is selected using the \texttt{router} option. Also,
+both routers share a small set of options.
+
+\begin{opt_list}{internal\_speedup}
+\item[credit\_delay] The processing delay (in cycles) for a credit.
+Does not include the wire delay for transmitting the credit.
+
+\item[internal\_speedup] An arbitrary speedup of the internals of the
+routers over the channel transmission rate. For example, a speedup
+1.5 means that, on average, 1.5 flits can be forwarded by the router
+in the time required for a single flit to be transmitted across a
+channel. Also, the configuration parser expects a floating point
+number for this field, so integer speedups should also include a
+decimal point (e.g. ``2.0'').
+
+\item[output\_delay] The processing delay incurred in the output queue
+of a router.
+\end{opt_list}
+
+\subsubsection{The input-queued router}
+\label{sec:iq_router}
+
+The input-queued router (\texttt{router = iq}) follows the pipeline
+described in PPIN of route computation, virtual-channel allocation,
+switch allocation, and switch traversal. There are several options
+specific to the input-queued router.
+
+\begin{opt_list}{st\_prepare\_delay}
+
+\item[input\_speedup] An integer speedup of the input ports in space.
+A speedup of 2, for example, gives each input two input ports into the
+crossbar. Access to these ports is statically allocated based on the
+virtual channel number: virtual channel $v$ at input $i$ is connected
+to port $i \cdot s + (v \mod s)$ for an input speedup of $s$.
+
+\item[output\_speedup] An integer speedup of the output ports in
+space. Similar to \texttt{input\_speedup}
+
+\item[routing\_delay] The delay (in cycles) of route computation.
+
+\item[sw\_allocator] The type of allocator used for switch allocation.
+See Section~\ref{sec:alloc} for a list of the possible allocators.
+
+\item[sw\_alloc\_delay] The delay (in cycles) of switch allocation.
+
+\item[vc\_allocator] The type of allocator used for virtual-channel
+allocation. See Section~\ref{sec:alloc} for a list of the possible
+allocators.
+
+\item[vc\_alloc\_delay] The delay (in cycles) of virtual-channel
+allocation.
+
+\end{opt_list}
+
+\subsubsection{The event-driven router}
+\label{sec:event_router}
+
+The event-driven router (\texttt{router = event}) is a
+microarchitecture designed specifically to support a large number of
+virtual channels (VCs) efficiently. Instead of continuously polling
+the state of the virtual channels, as in the input-queued router, only
+changes in VC state are tracked. The efficiency then comes from the
+fact that the number of state changes per cycle is constant and
+independent of the number of VCs.
+
+\subsection{Allocators}
+\label{sec:alloc}
+
+Many of the allocators used in the simulator are configurable (see
+the input-queued router in Section~\ref{sec:iq_router}) and several
+allocation algorithms are available.
+\begin{opt_list}{wavefront}
+
+\item[max\_size] Maximum-size matching.
+\item[islip] iSLIP separable allocator.
+\item[pim] Parallel iterative matching separable allocator.
+\item[loa] Lonely output allocator.
+\item[wavefront] Wavefront matching.
+\item[select] Priority-based allocator. Allocation is performed as in
+iSLIP, but with preference towards higher priority packets (see
+\texttt{priority} option in Section~\ref{sec:traffic}).
+
+\end{opt_list}
+
+Allocation can also be improved by performing multiple iterations of
+the algorithm and the number of iterations is controlled by the
+\texttt{alloc\_iters} parameter.
+
+\subsection{Traffic}
+\label{sec:traffic}
+
+The rate at which flits are injected into the simulator is set using
+the \texttt{injection\_rate} option. The simulator's cycle time is a
+flit cycle, the time it takes a single flit to be injected at a
+source, and the injection rate is specified in flits per flit cycle.
+For example, setting \texttt{injection\_rate = 0.25} means that each
+source injects a new flit one of every four simulator cycles. The
+injection process can also be specified as either Bernoulli
+(\texttt{injection\_process = bernoulli}) or an on-off process
+(\texttt{injection\_process = on\_off}). The burstiness of the latter
+injection process is controlled via the \texttt{burst\_alpha} and
+\texttt{burst\_beta} parameter. See PPIN Section 24.2.2 for a
+description of the on-off process and its parameters.
+
+The unit of injection is packets, which may be comprised of many
+flits. The number of flits per packet is set using the
+\texttt{const\_flits\_per\_packet} option. Each packet may also have an
+associated priority, either age-based (\texttt{age}) or none
+(\texttt{none}), as specified by the \texttt{priority} option.
+
+The simulator also supports several different traffic patterns that
+are specified using the \texttt{traffic} option. To describe these
+patterns, we use the same notation of PPIN Section 3.2: $s_i$ ($d_i$)
+denotes the $i^\textrm{th}$ bit of the source (destination) address
+whereas $s_x$ ($d_x$) denotes the $x^\textrm{th}$ radix-$k$ digit of
+the source (destination) address. The bit length of an address is $b
+= \log_2 N$, where $N$ is the number of nodes in the network.
+
+\begin{opt_list}{transpose}
+\item[uniform] Each source sends an equal amount of traffic to each
+destination (\texttt{traffic = uniform}).
+\item[bitcomp] Bit complement. $d_i = \neg s_i$.
+\item[bitrev] Bit reverse. $d_i = s_{b-i-1}$.
+\item[shuffle] $d_i = s_{i-1 \mod b}$.
+\item[transpose] $d_i = s_{i+b/2 \mod b}$.
+\item[tornado] $d_x = s_x + \lceil k/2 \rceil - 1 \mod k$.
+\item[neighbor] $d_x = s_x + 1 \mod k$.
+\item[randperm] Random permutation. A fixed permutation traffic
+pattern is chosen uniformly at random from the set of all
+permutations. The seed used to generate this permutation is set by
+the \texttt{perm\_seed} option. So, randomly selecting values for
+\texttt{perm\_seed} gives a random sampling of permutation while a
+fixed value of \texttt{perm\_seed} allows the same permutation to be
+used for several experiments.
+\end{opt_list}
+
+\subsection{Simulation parameters}
+\label{sec:sim_params}
+
+The duration and other aspects of a simulation are controlled using
+the set of simulation parameters.
+
+\begin{opt_list}{warmup\_periods}
+
+\item[sim\_type] A simulation can either focus on
+\texttt{throughput} or \texttt{latency}. The key difference between
+these two types is that a \texttt{latency} simulation will wait for
+all measurement packets to drain before ending the simulation to
+ensure an accurate latency measurement. In \texttt{throughput}
+simulations, this final drain step is eliminated to allow simulation
+of networks operating beyond their saturation point.
+
+\item[sample\_period] The sample period is expressed in simulator
+cycles and is used as a multiplier when specifying the warm-up length
+of a simulation and the maximum number of samples. Also, intermediate
+statistics are displayed once every \texttt{sample\_period} cycles.
+
+\item[warmup\_periods] The length of the simulator warm up expressed
+as a multiple of the \texttt{sample\_period}. After warming up, all
+statistics counters are reset.
+
+\item[max\_samples] The total length of simulation expressed as a
+multiple of the \texttt{sample\_period}.
+
+\item[latency\_thres] If the sampled latency of the current simulation
+exceeds \texttt{latency\_thres}, the simulation is immediately ended.
+
+\item[sim\_count] The number of back-to-back simulations to run for the
+given configuration. Useful for creating ensemble averages of
+particular statistics.
+
+\item[seed] A random seed for the simulation.
+
+\item[reorder] A non-zero value indicates that packet order should be
+maintained and reordering time is accounted for in the overall latency.
+
+\end{opt_list}
+
+\appendix
+
+\section{Random number generation}
+
+The simulator uses Knuth's integer and floating point pseudorandom
+number generators. These algorithms and their explanations appear in
+``The Art of Computer Programming: Seminumerical Algorithms''.
+
+\end{document} \ No newline at end of file
diff --git a/src/intersim/event_router.cpp b/src/intersim/event_router.cpp
new file mode 100644
index 0000000..09172cd
--- /dev/null
+++ b/src/intersim/event_router.cpp
@@ -0,0 +1,923 @@
+#include <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